shin / src /free_claude_code /runtime /bootstrap.py
shinmentakezo07
feat: add key management and provider keypool (# new feature)
2c96ecb
Raw
History Blame Contribute Delete
3.85 kB
"""Single production composition root for the FCC server."""
import os
from functools import partial
from pathlib import Path
from free_claude_code.api.app import create_app
from free_claude_code.api.ports import ApiServices
from free_claude_code.config.logging_config import configure_logging
from free_claude_code.config.paths import server_log_path
from free_claude_code.config.settings import Settings
from free_claude_code.messaging.transcription import TranscriptionService
from free_claude_code.messaging.voice import Transcriber
from free_claude_code.providers.admission import ProviderAdmissionController
from free_claude_code.providers.base import BaseProvider, ProviderConfig
from free_claude_code.providers.nvidia_nim.voice import NvidiaNimTranscriber
from free_claude_code.providers.openai_codex import (
OpenAIAuthManager,
OpenAICodexProvider,
)
from free_claude_code.providers.runtime import ProviderRuntime
from free_claude_code.providers.runtime.config import split_api_key_pool
from free_claude_code.providers.runtime.factory import create_provider
from .application import ApplicationRuntime, RestartCallback
from .asgi import RuntimeASGIApp
from .codex_catalog import CodexModelCatalogPublisher
from .provider_manager import ProviderRuntimeManager
def build_asgi_app(
settings: Settings,
restart_callback: RestartCallback | None = None,
) -> RuntimeASGIApp:
"""Construct the complete server application and its resource owner."""
log_path = Path(os.getenv("LOG_FILE", server_log_path()))
configure_logging(
log_path,
level=settings.log_level,
verbose_third_party=settings.log_raw_api_payloads,
)
openai_auth = OpenAIAuthManager(proxy=settings.openai_proxy)
openai_factory = partial(_create_openai_provider, auth=openai_auth)
provider_constructor = partial(
create_provider,
injected_factories={"openai": openai_factory},
)
runtime_factory = partial(
ProviderRuntime,
provider_constructor=provider_constructor,
)
provider_manager = ProviderRuntimeManager(
settings,
runtime_factory=runtime_factory,
connected_provider_ids=openai_auth.connected_provider_ids,
model_catalog_publisher=CodexModelCatalogPublisher(),
)
runtime = ApplicationRuntime(
provider_manager,
transcriber=_create_transcriber(settings),
restart_callback=restart_callback,
connected_accounts={"openai": openai_auth},
)
services = ApiServices(
requests=provider_manager,
admin=runtime,
tasks=runtime,
)
return RuntimeASGIApp(create_app(services), runtime)
def _create_openai_provider(
config: ProviderConfig,
_settings: Settings,
admission: ProviderAdmissionController,
*,
auth: OpenAIAuthManager,
) -> BaseProvider:
return OpenAICodexProvider(config, auth=auth, admission=admission)
def _first_pool_key(value: str) -> str:
"""Return the first key of a comma-separated credential pool.
Provider chat credentials may be configured as a comma-separated pool for
round-robin rotation, but the voice transcribers consume a single key and
must never send the raw joined pool as one token.
"""
keys = split_api_key_pool(value)
return keys[0] if keys else ""
def _create_transcriber(settings: Settings) -> Transcriber | None:
if not settings.voice_note_enabled:
return None
if settings.whisper_device == "nvidia_nim":
return NvidiaNimTranscriber(
model=settings.whisper_model,
api_key=_first_pool_key(settings.nvidia_nim_api_key),
)
return TranscriptionService(
model=settings.whisper_model,
device=settings.whisper_device,
huggingface_api_key=_first_pool_key(settings.huggingface_api_key),
)