Files
Jarvis-Ai/src/javis/core/runtime.py
T

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,
)