146 lines
5.3 KiB
Python
146 lines
5.3 KiB
Python
"""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,
|
|
)
|