feat: add desktop ui foundation
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
"""Shared runtime assembly for CLI and desktop interfaces."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
from javis.config.settings import Settings
|
||||
from javis.core.chat_service import ChatService
|
||||
from javis.core.provider_router import ApprovalCallback, HybridProvider, NoticeCallback
|
||||
from javis.memory.sqlite_store import SQLiteSessionStore
|
||||
from javis.memory.usage_store import SQLiteUsageStore, UsageStoreError
|
||||
from javis.providers.base import MissingApiKeyError
|
||||
from javis.providers.gemini import GeminiProvider
|
||||
from javis.providers.ollama import OllamaProvider
|
||||
from javis.security.privacy import PrivacyRouter
|
||||
from javis.security.secrets import SecretProvider, SecretStoreError
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class RuntimeStatus:
|
||||
ollama_reachable: bool
|
||||
local_model_available: bool
|
||||
local_model: str
|
||||
gemini_key_available: bool
|
||||
gemini_configured: bool
|
||||
free_only: bool
|
||||
provider_mode: str
|
||||
cloud_requests_today: int | None
|
||||
local_fallbacks_today: int | None
|
||||
data_dir: str
|
||||
|
||||
def cli_lines(self) -> list[str]:
|
||||
cloud_requests = (
|
||||
self.cloud_requests_today if self.cloud_requests_today is not None else "unbekannt"
|
||||
)
|
||||
local_fallbacks = (
|
||||
self.local_fallbacks_today if self.local_fallbacks_today is not None else "unbekannt"
|
||||
)
|
||||
return [
|
||||
f"Ollama erreichbar: {'ja' if self.ollama_reachable else 'nein'}",
|
||||
f"Lokales Modell vorhanden: {'ja' if self.local_model_available else 'nein'}",
|
||||
f"Gemini-Schlüssel vorhanden: {'ja' if self.gemini_key_available else 'nein'}",
|
||||
f"Gemini konfiguriert: {'ja' if self.gemini_configured else 'nein'}",
|
||||
f"Nullkostenmodus: {'aktiv' if self.free_only else 'inaktiv'}",
|
||||
f"Aktiver Providermodus: {self.provider_mode}",
|
||||
f"Heutige Cloudanfragen: {cloud_requests}",
|
||||
f"Heutige lokale Fallbacks: {local_fallbacks}",
|
||||
]
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class JavisRuntime:
|
||||
settings: Settings
|
||||
service: ChatService
|
||||
local_provider: OllamaProvider
|
||||
hybrid_provider: HybridProvider
|
||||
usage_store: SQLiteUsageStore
|
||||
secrets: SecretProvider
|
||||
|
||||
def status(self) -> RuntimeStatus:
|
||||
ollama = self.local_provider.probe()
|
||||
try:
|
||||
key_available = self.secrets.gemini_key_available()
|
||||
except SecretStoreError:
|
||||
key_available = False
|
||||
try:
|
||||
cloud_requests: int | None = self.usage_store.cloud_requests_on()
|
||||
local_fallbacks: int | None = self.usage_store.local_fallbacks_on()
|
||||
except UsageStoreError:
|
||||
cloud_requests = local_fallbacks = None
|
||||
return RuntimeStatus(
|
||||
ollama_reachable=ollama.reachable,
|
||||
local_model_available=ollama.model_available,
|
||||
local_model=self.local_provider.model,
|
||||
gemini_key_available=key_available,
|
||||
gemini_configured=(
|
||||
self.settings.gemini_enabled
|
||||
and self.settings.free_only
|
||||
and self.settings.billing_confirmed_disabled
|
||||
),
|
||||
free_only=self.settings.free_only,
|
||||
provider_mode=self.hybrid_provider.mode,
|
||||
cloud_requests_today=cloud_requests,
|
||||
local_fallbacks_today=local_fallbacks,
|
||||
data_dir=str(self.settings.data_dir),
|
||||
)
|
||||
|
||||
|
||||
def build_javis_runtime(
|
||||
settings: Settings,
|
||||
*,
|
||||
approval_callback: ApprovalCallback,
|
||||
notice_callback: NoticeCallback,
|
||||
secret_provider: SecretProvider | None = None,
|
||||
) -> JavisRuntime:
|
||||
"""Assemble the existing core once for any user interface."""
|
||||
|
||||
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 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,
|
||||
approval_callback=approval_callback,
|
||||
notice_callback=notice_callback,
|
||||
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,
|
||||
)
|
||||
return JavisRuntime(
|
||||
settings=settings,
|
||||
service=ChatService(store, hybrid),
|
||||
local_provider=local,
|
||||
hybrid_provider=hybrid,
|
||||
usage_store=usage_store,
|
||||
secrets=secrets,
|
||||
)
|
||||
Reference in New Issue
Block a user