mirror of
https://github.com/Alishahryar1/free-claude-code.git
synced 2026-07-03 14:05:26 +02:00
58aef0dc8a
## Problem Provider construction, model discovery, validation, and cleanup lived in one registry module. API and admin routes depended on registry-shaped app state and legacy process-level provider helpers. ## Changes | Before | After | | --- | --- | | `providers.registry` mixed provider factories, config, cache, discovery, validation, and cleanup. | `providers.runtime` splits factories, config, cache, model cache, discovery, validation, and runtime orchestration. | | API and admin routes read `app.state.provider_registry` and sometimes created registries ad hoc. | API and admin routes use app-scoped `ProviderRuntime` through `app.state.provider_runtime`. | | `api.dependencies` kept process-global provider cache helpers. | `api.dependencies` resolves providers only through the app-scoped runtime. | | Registry-shaped tests preserved old internal boundaries. | Runtime-shaped tests assert provider config, construction, cache, discovery, validation, and import boundaries. | <!-- greptile_comment --> <details open><summary><h3>Greptile Summary</h3></summary> This PR moves provider lifecycle ownership from the old registry module into an app-scoped runtime package. The main changes are: - Split provider config, factory wiring, instance cache, model cache, discovery, validation, and cleanup into `providers.runtime` modules. - Updated API and admin routes to resolve providers and model metadata through `app.state.provider_runtime`. - Removed legacy process-global provider helpers and the deleted `providers.registry` module. - Updated docs, smoke metadata, import-boundary checks, and tests for the new runtime ownership model. - Bumped the package version and lockfile metadata for the production refactor. </details> <h3>Confidence Score: 5/5</h3> The provider runtime refactor appears merge-safe with no identified blocking issues. The changes consistently move provider ownership to app-scoped runtime modules and update API, admin, docs, smoke metadata, import-boundary checks, and tests around that architecture. <details><summary><h3><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="20" align="absmiddle"></a> T-Rex Logs</h3></summary> **What T-Rex did** - Ran a baseline and head comparison of provider registry and runtime states, verifying the after-state shows head state\_has\_provider\_registry=False and state\_has\_provider\_runtime=True, that GET /v1/models and admin endpoints respond with 200, and that provider\_resolver\_called via runtime, with assertions passing. - Verified that the four focused provider-runtime contract tests passed in both the before and after refactor runs, including runtime split checks, with exit code 0. - Identified environmental blockers that prevented the smoke-runtime workflow from running, including uv unavailability, missing pytest for /usr/local/bin/python, and Python 3.11 being used despite pyproject.toml requiring \>=3.14. <a href="https://app.greptile.com/trex/runs/12528505/artifacts"><picture><source media="(prefers-color-scheme: dark)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=1"><source media="(prefers-color-scheme: light)" srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=1"><img alt="View all artifacts" src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=1" height="32"></picture></a> <sub><a href="https://www.greptile.com/trex"><img alt="T-Rex" src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg" height="14" align="absmiddle"></a> Ran code and verified through T-Rex</sub> </details> <sub>Reviews (1): Last reviewed commit: ["Refactor provider runtime ownership"](https://github.com/alishahryar1/free-claude-code/commit/01d589488185c1f85112f1a49c47f04512846161) | [Re-trigger Greptile](https://app.greptile.com/api/retrigger?id=40312173)</sub> <!-- /greptile_comment -->
66 lines
2.6 KiB
Python
66 lines
2.6 KiB
Python
"""Provider configuration construction from neutral catalog metadata."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from config.provider_catalog import ProviderDescriptor
|
|
from config.settings import Settings
|
|
from providers.base import ProviderConfig
|
|
from providers.exceptions import AuthenticationError
|
|
|
|
|
|
def string_setting(settings: Settings, attr_name: str | None, default: str = "") -> str:
|
|
"""Return a string-valued settings attribute, ignoring non-string mocks."""
|
|
if attr_name is None:
|
|
return default
|
|
value = getattr(settings, attr_name, default)
|
|
return value if isinstance(value, str) else default
|
|
|
|
|
|
def provider_credential(descriptor: ProviderDescriptor, settings: Settings) -> str:
|
|
"""Return the configured credential for a provider descriptor."""
|
|
if descriptor.static_credential is not None:
|
|
return descriptor.static_credential
|
|
if descriptor.credential_attr:
|
|
return string_setting(settings, descriptor.credential_attr)
|
|
return ""
|
|
|
|
|
|
def require_provider_credential(
|
|
descriptor: ProviderDescriptor, credential: str
|
|
) -> None:
|
|
"""Raise a user-facing configuration error when a required key is missing."""
|
|
if descriptor.credential_env is None:
|
|
return
|
|
if credential and credential.strip():
|
|
return
|
|
message = f"{descriptor.credential_env} is not set. Add it to your .env file."
|
|
if descriptor.credential_url:
|
|
message = f"{message} Get a key at {descriptor.credential_url}"
|
|
raise AuthenticationError(message)
|
|
|
|
|
|
def build_provider_config(
|
|
descriptor: ProviderDescriptor, settings: Settings
|
|
) -> ProviderConfig:
|
|
"""Build shared provider configuration for one provider descriptor."""
|
|
credential = provider_credential(descriptor, settings)
|
|
require_provider_credential(descriptor, credential)
|
|
base_url = string_setting(
|
|
settings, descriptor.base_url_attr, descriptor.default_base_url or ""
|
|
)
|
|
proxy = string_setting(settings, descriptor.proxy_attr)
|
|
return ProviderConfig(
|
|
api_key=credential,
|
|
base_url=base_url or descriptor.default_base_url,
|
|
rate_limit=settings.provider_rate_limit,
|
|
rate_window=settings.provider_rate_window,
|
|
max_concurrency=settings.provider_max_concurrency,
|
|
http_read_timeout=settings.http_read_timeout,
|
|
http_write_timeout=settings.http_write_timeout,
|
|
http_connect_timeout=settings.http_connect_timeout,
|
|
enable_thinking=settings.enable_model_thinking,
|
|
proxy=proxy,
|
|
log_raw_sse_events=settings.log_raw_sse_events,
|
|
log_api_error_tracebacks=settings.log_api_error_tracebacks,
|
|
)
|