Skip to content

Model-Provider-Abstraktion

源码版本rust-v0.145.0

codex abstrahiert „mit wem gesprochen wird" in den ModelProvider-Trait: einmal konfiguriert, kann das Backend OpenAI, Amazon Bedrock, ein lokales Ollama / LM Studio oder ein nutzerdefinierter OpenAI-kompatibler Endpunkt sein. Rund um den Trait liegen model-provider-info (Konfigurationsserialisierung), backend-client (HTTP-Aufruf), ollama / lmstudio (lokale OSS-Adapter) und das Modellauswahl-Panel der TUI. Diese Schicht entscheidet, an wen der Agent den Request am Ende schickt, mit welchen Credentials und über welches Wire-Protokoll.

Verantwortlichkeiten

  1. Provider-Metadaten zur Laufzeit exponieren: info() liefert ModelProviderInfo mit base URL, wire API, env key, auth-Konfiguration usw. (codex-rs/model-provider/src/provider.rs:101-162).
  2. Authentifizierung klären: ChatGPT-Login, API key, bearer token aus einem Kommando oder AWS SigV4 — gebündelt in api_auth / api_auth_for_scope (codex-rs/model-provider/src/provider.rs:171-194).
  3. Fähigkeitsgrenzen melden: ProviderCapabilities teilt der oberen Schicht mit, ob image generation / web search / namespace tools möglich sind; der Provider hat ein Veto-Recht (codex-rs/model-provider/src/provider.rs:33-48).
  4. Modellkatalog-Manager bauen: models_manager entscheidet, ob der Katalog von /v1/models geladen wird oder eine statische Liste (Bedrock) genutzt wird (codex-rs/model-provider/src/provider.rs:197-215).
  5. Lokale OSS adaptieren: Die Crates ollama / lmstudio erkunden den lokalen Dienst, laden Modelle und mappen ihre OpenAI-kompatiblen Endpunkte auf ModelProviderInfo zurück (codex-rs/ollama/src/lib.rs:23-50).

Entwurfsbeweggründe

Früh sprach codex nur mit OpenAI; die Konfiguration war OPENAI_API_KEY + base_url. Sobald Nutzer Bedrock, lokale Inferenz-Engines oder selbstgebaute Gateways anzapften, leckte diese „OpenAI hartcodiert"-Schreibweise überall heraus. Der Trait wurde neu eingeführt, um „den OpenAI-kompatiblen Teil" in der Standard-Implementierung zu belassen und „die pro-Anbieter unterschiedlichen Stellen" auf Trait-Method-Overrides zu verdichten.

ConfiguredModelProvider ist die Standard-Implementierung — sieht das Backend wie OpenAI /v1/responses aus, lässt es sich per ModelProviderInfo konfigurieren, ohne einen neuen Trait-Impl zu schreiben. Nur Bedrock hat wegen SigV4-Signierung eine eigene Implementierung amazon_bedrock::AmazonBedrockModelProvider. Der Entwurfskompromiss: die Trait-Methoden auf etwa ein Dutzend zu begrenzen, mit Standard-Implementierungen über info() + auth_manager(), was Override-Haken bietet, ohne dass jeder neue Provider dutzende Methoden neu schreiben muss.

Ollama / LM Studio implementieren ModelProvider nicht selbst, sondern verpacken den lokalen Dienst als „OpenAI-kompatible base URL", füttern damit ConfiguredModelProvider und nutzen Hilfsfunktionen wie ensure_oss_ready für Diensterkennung und Modellabruf. So bleibt die Trait-Anzahl klein, und der lokale OSS-Pfad hat die höchste Wiederverwendung.

Wichtige Dateien

codex-rs/model-provider/src/provider.rs:101-162ModelProvider-Trait-Hauptkörper; Einstieg von info / auth / capabilities / models_manager.codex-rs/model-provider/src/provider.rs:232-241 — Factory create_model_provider; Bedrock separat, der Rest über ConfiguredModelProvider.codex-rs/model-provider-info/src/lib.rs:89-141ModelProviderInfo-Struktur; Serialisierungsform entspricht dem Abschnitt [model_providers.xxx] in config.toml.codex-rs/model-provider-info/src/lib.rs:524-544create_oss_provider_with_base_url; Standardkonstruktion für lokale OSS-Provider.codex-rs/model-provider/src/auth.rs:49-71ResolvedProviderAuth; verpackt Authentifizierungsergebnis zusammen mit Telemetrie für die Request-Schicht.codex-rs/backend-client/src/client.rs:124-183backend_client::Client; ChatGPT-Backend in WHAM / Codex-API-Doppelform.codex-rs/ollama/src/client.rs:25-78OllamaClient; leitet host root aus ModelProviderInfo ab und pingelt /api/tags oder /v1/models.codex-rs/tui/src/oss_selection.rs:316-370select_oss_provider; bei TUI-Start wird der Port geprüft, bei einer Instanz automatisch gewählt, bei zweien ein Auswahldialog.

Die Factory nimmt Bedrock heraus; alles andere geht über ConfiguredModelProvider, das ModelProviderInfo als Standardkonfiguration frisst:

rust
// provider.rs:232-241 — 工厂按 provider 类型分流
pub fn create_model_provider(
    provider_info: ModelProviderInfo,
    auth_manager: Option<Arc<AuthManager>>,
) -> SharedModelProvider {
    if provider_info.is_amazon_bedrock() {
        Arc::new(AmazonBedrockModelProvider::new(provider_info, auth_manager))
    } else {
        Arc::new(ConfiguredModelProvider::new(provider_info, auth_manager))
    }
}

ModelProviderInfo entspricht direkt dem Abschnitt [model_providers.<id>] in config.toml; alle Felder sind Option und leer bedeutet „OpenAI-Standard":

rust
// model-provider-info/src/lib.rs:89-141 — provider 配置的序列化形态
pub struct ModelProviderInfo {
    pub name: String,
    pub base_url: Option<String>,
    pub env_key: Option<String>,
    pub env_key_instructions: Option<String>,
    pub experimental_bearer_token: Option<String>,
    pub auth: Option<ModelProviderAuthInfo>,
    pub aws: Option<ModelProviderAwsAuthInfo>,
    pub wire_api: WireApi,
    // ...还有 query_params / http_headers / stream_* 等字段
    pub requires_openai_auth: bool,
    pub supports_websockets: bool,
}

Bei der Ollama-Adaptation ist der Kern nicht „Trait implementieren", sondern den lokalen Dienst als OpenAI-kompatiblen Endpunkt zu verpacken und den Rest an ConfiguredModelProvider zu übergeben:

rust
// ollama/src/client.rs:59-78 — 从 provider 配置构造客户端并探活
pub(crate) async fn try_from_provider(provider: &ModelProviderInfo) -> io::Result<Self> {
    let base_url = provider.base_url.as_ref().expect("oss provider must have a base_url");
    let uses_openai_compat = is_openai_compatible_base_url(base_url);
    let host_root = base_url_to_host_root(base_url);
    let client = reqwest::Client::builder()
        .connect_timeout(std::time::Duration::from_secs(5))
        .build()
        .unwrap_or_else(|_| reqwest::Client::new());
    let client = Self { client, host_root, uses_openai_compat };
    client.probe_server().await?;
    Ok(client)
}

Datenfluss

Grenzen und Fehler

  • Mehrere auth-Felder schließen sich gegenseitig aus: ModelProviderInfo::validate verbietet, dass aws zusammen mit env_key / experimental_bearer_token / auth / requires_openai_auth auftritt, um konfligierende Konfigurationssemantik zu verhindern (codex-rs/model-provider-info/src/lib.rs:154-212).
  • Ollama-Versionsprüfung: ensure_responses_supported verlangt Ollama ≥ 0.13.4 für die Responses API; ältere Versionen brechen direkt ab (codex-rs/ollama/src/lib.rs:63-77).
  • Bedrock geht nicht über den OpenAI-auth-Pfad: Tests wie create_model_provider_builds_command_auth_manager stellen klar, dass der Bedrock-Provider einen übergebenen OpenAI-auth-manager ignoriert, um falsche Credentials zu vermeiden (codex-rs/model-provider/src/provider.rs:552-565).
  • First-party-auth über Sonderpfad: provider_uses_first_party_auth_path verlangt requires_openai_auth=true und das Fehlen jeglicher env_key/bearer/aws/auth-Felder; nur reines ChatGPT-Login geht über den scope-basierten Authentifizierungspfad (codex-rs/model-provider/src/provider.rs:223-229).
  • OSS-Erkennung nicht fatal: ensure_oss_ready gibt bei fetch_models-Fehlern nur tracing::warn und bricht nicht ab; die obere Schicht kollidiert beim Modelllauf mit dem echten Fehler (codex-rs/ollama/src/lib.rs:34-47).

Zusammenfassung

Der ModelProvider-Trait verdichtet „mit wem gesprochen wird" auf rund ein Dutzend Methoden; die Standard-Implementierung ConfiguredModelProvider deckt alle OpenAI-kompatiblen Backends ab, und Bedrock hat wegen SigV4 eine eigene Implementierung. Ollama / LM Studio implementieren den Trait nicht direkt, sondern nutzen lokale Erkennung + OpenAI-kompatible base URL, um die Standard-Implementierung wiederzuverwenden — die Trait-Anzahl bleibt klein. Die konkrete Konfigurationsform ist in ModelProviderInfo definiert und setzt Konfigurationssystem fort; die Cloud-Aufgabenausführung läuft über Cloud Tasks und liegt nicht in derselben Schicht wie der lokale Provider.