feat: add desktop ui foundation
This commit is contained in:
+15
-72
@@ -12,19 +12,15 @@ from pathlib import Path
|
||||
from javis.config.local_config import LocalConfigError, write_gemini_activation
|
||||
from javis.config.settings import ConfigurationError, Settings
|
||||
from javis.core.chat_service import ChatService, SessionProviderMismatchError
|
||||
from javis.core.provider_router import HybridProvider
|
||||
from javis.core.runtime import build_javis_runtime
|
||||
from javis.memory.sqlite_store import (
|
||||
AmbiguousSessionReferenceError,
|
||||
ChatSession,
|
||||
SessionNotFoundError,
|
||||
SessionStoreError,
|
||||
SQLiteSessionStore,
|
||||
)
|
||||
from javis.memory.usage_store import SQLiteUsageStore, UsageStoreError
|
||||
from javis.providers.base import MissingApiKeyError, ProviderError
|
||||
from javis.providers.gemini import GeminiProvider
|
||||
from javis.providers.ollama import OllamaProvider
|
||||
from javis.security.privacy import PrivacyDecision, PrivacyRouter
|
||||
from javis.memory.usage_store import UsageStoreError
|
||||
from javis.providers.base import ProviderError
|
||||
from javis.security.secrets import SecretProvider, SecretStoreError
|
||||
|
||||
InputFunction = Callable[[str], str]
|
||||
@@ -52,6 +48,7 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
subparsers = parser.add_subparsers(dest="command", required=True)
|
||||
chat_parser = subparsers.add_parser("chat", help="lokalen Textchat starten")
|
||||
chat_parser.add_argument("--session", help="vorhandene Sitzungs-ID laden")
|
||||
subparsers.add_parser("gui", help="Javis-Desktopoberfläche starten")
|
||||
configure_parser = subparsers.add_parser(
|
||||
"configure",
|
||||
help="nicht geheime lokale Einstellungen einrichten",
|
||||
@@ -385,79 +382,21 @@ def build_runtime(
|
||||
output: OutputFunction,
|
||||
secret_provider: SecretProvider | None = None,
|
||||
) -> tuple[ChatService, StatusFunction]:
|
||||
store = SQLiteSessionStore(settings.database_path)
|
||||
usage_store = SQLiteUsageStore(settings.usage_database_path)
|
||||
secrets = secret_provider or SecretProvider()
|
||||
local = OllamaProvider(
|
||||
settings.model,
|
||||
settings.ollama_base_url,
|
||||
settings.timeout_seconds,
|
||||
)
|
||||
|
||||
def approve(decision: PrivacyDecision) -> bool:
|
||||
answer = input_fn(
|
||||
f"Cloudfreigabe erforderlich ({decision.reason}). Einmalig erlauben? [j/N] "
|
||||
)
|
||||
def approve(_decision: object) -> bool:
|
||||
answer = input_fn("Cloudfreigabe erforderlich. Einmalig erlauben? [j/N] ")
|
||||
return _confirmed(answer)
|
||||
|
||||
def create_cloud_provider() -> GeminiProvider:
|
||||
try:
|
||||
key = secrets.get_gemini_key()
|
||||
except SecretStoreError as exc:
|
||||
raise MissingApiKeyError(
|
||||
"Der Gemini-Schlüssel konnte nicht sicher geladen werden."
|
||||
) from exc
|
||||
return GeminiProvider(
|
||||
model=settings.gemini_model,
|
||||
api_key=key,
|
||||
timeout_seconds=settings.gemini_timeout_seconds,
|
||||
max_output_tokens=settings.max_cloud_output_tokens,
|
||||
max_retries=settings.gemini_max_retries,
|
||||
)
|
||||
|
||||
hybrid = HybridProvider(
|
||||
local_provider=local,
|
||||
cloud_provider_factory=create_cloud_provider,
|
||||
cloud_model=settings.gemini_model,
|
||||
privacy_router=PrivacyRouter(),
|
||||
usage_store=usage_store,
|
||||
runtime = build_javis_runtime(
|
||||
settings,
|
||||
approval_callback=approve,
|
||||
notice_callback=output,
|
||||
mode=settings.provider_mode,
|
||||
cloud_enabled=settings.gemini_enabled,
|
||||
billing_confirmed_disabled=settings.billing_confirmed_disabled,
|
||||
free_only=settings.free_only,
|
||||
max_cloud_requests_per_day=settings.max_cloud_requests_per_day,
|
||||
max_cloud_input_chars=settings.max_cloud_input_chars,
|
||||
max_cloud_context_messages=settings.max_cloud_context_messages,
|
||||
secret_provider=secret_provider,
|
||||
)
|
||||
|
||||
def status() -> list[str]:
|
||||
ollama = local.probe()
|
||||
try:
|
||||
key_available = secrets.gemini_key_available()
|
||||
except SecretStoreError:
|
||||
key_available = False
|
||||
gemini_configured = (
|
||||
settings.gemini_enabled and settings.free_only and settings.billing_confirmed_disabled
|
||||
)
|
||||
try:
|
||||
cloud_requests = usage_store.cloud_requests_on()
|
||||
local_fallbacks = usage_store.local_fallbacks_on()
|
||||
except UsageStoreError:
|
||||
cloud_requests = local_fallbacks = -1
|
||||
return [
|
||||
f"Ollama erreichbar: {'ja' if ollama.reachable else 'nein'}",
|
||||
f"Lokales Modell vorhanden: {'ja' if ollama.model_available else 'nein'}",
|
||||
f"Gemini-Schlüssel vorhanden: {'ja' if key_available else 'nein'}",
|
||||
f"Gemini konfiguriert: {'ja' if gemini_configured else 'nein'}",
|
||||
f"Nullkostenmodus: {'aktiv' if settings.free_only else 'inaktiv'}",
|
||||
f"Aktiver Providermodus: {hybrid.mode}",
|
||||
f"Heutige Cloudanfragen: {cloud_requests if cloud_requests >= 0 else 'unbekannt'}",
|
||||
f"Heutige lokale Fallbacks: {local_fallbacks if local_fallbacks >= 0 else 'unbekannt'}",
|
||||
]
|
||||
return runtime.status().cli_lines()
|
||||
|
||||
return ChatService(store, hybrid), status
|
||||
return runtime.service, status
|
||||
|
||||
|
||||
def main(
|
||||
@@ -484,6 +423,10 @@ def main(
|
||||
input_fn=input_fn,
|
||||
output=output,
|
||||
)
|
||||
if args.command == "gui":
|
||||
from javis.ui.app import run_gui
|
||||
|
||||
return run_gui([])
|
||||
if args.command != "chat":
|
||||
return 2
|
||||
|
||||
|
||||
Reference in New Issue
Block a user