Model-Provider-Abstraktion
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
- Provider-Metadaten zur Laufzeit exponieren:
info()liefertModelProviderInfomit base URL, wire API, env key, auth-Konfiguration usw. (codex-rs/model-provider/src/provider.rs:101-162). - 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). - Fähigkeitsgrenzen melden:
ProviderCapabilitiesteilt 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). - Modellkatalog-Manager bauen:
models_managerentscheidet, ob der Katalog von/v1/modelsgeladen wird oder eine statische Liste (Bedrock) genutzt wird (codex-rs/model-provider/src/provider.rs:197-215). - Lokale OSS adaptieren: Die Crates
ollama/lmstudioerkunden den lokalen Dienst, laden Modelle und mappen ihre OpenAI-kompatiblen Endpunkte aufModelProviderInfozurü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-162 — ModelProvider-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-141 — ModelProviderInfo-Struktur; Serialisierungsform entspricht dem Abschnitt [model_providers.xxx] in config.toml.codex-rs/model-provider-info/src/lib.rs:524-544 — create_oss_provider_with_base_url; Standardkonstruktion für lokale OSS-Provider.codex-rs/model-provider/src/auth.rs:49-71 — ResolvedProviderAuth; verpackt Authentifizierungsergebnis zusammen mit Telemetrie für die Request-Schicht.codex-rs/backend-client/src/client.rs:124-183 — backend_client::Client; ChatGPT-Backend in WHAM / Codex-API-Doppelform.codex-rs/ollama/src/client.rs:25-78 — OllamaClient; leitet host root aus ModelProviderInfo ab und pingelt /api/tags oder /v1/models.codex-rs/tui/src/oss_selection.rs:316-370 — select_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:
// 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":
// 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:
// 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::validateverbietet, dassawszusammen mitenv_key/experimental_bearer_token/auth/requires_openai_authauftritt, um konfligierende Konfigurationssemantik zu verhindern (codex-rs/model-provider-info/src/lib.rs:154-212). - Ollama-Versionsprüfung:
ensure_responses_supportedverlangt 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_managerstellen 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_pathverlangtrequires_openai_auth=trueund 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_readygibt beifetch_models-Fehlern nurtracing::warnund 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.