Add OpenAI platform: live model fetching with enrichment (provider 1)

The 4th registry row: id "openai", OPENAI_API_KEY env > auth.json
"openai" scope (login picker/paste/validation all registry-generic —
zero TUI changes needed, pinned by the picker test), base
https://api.openai.com/v1 with KIGI_OPENAI_BASE_URL override, Responses
dialect via the new PlatformWireApi spec field, enrichment-backed
metadata (wire_serves_metadata=false).

OpenAI's GET /v1/models returns bare ids and is polluted with
tts/whisper/embeddings entries: the listing is restricted to
enrichment-known TOOL-CALLING models (review caught that membership
alone admitted models.dev-known embeddings models, which would 400 on
every agentic request; dropped ids are debug-logged for launch-day
diagnosability). Context windows, effort menus, display names, and
thinking capability come from the enrichment pipeline — wiremock e2e
pins the full contract: polluted live listing + models.dev →
one Responses-backed chat model with a 400k documented context window.

Responses max-effort wiring (closes the P0c-1 debt): canonical effort
rides a CreateResponseWrapper sidecar and patch_reasoning_effort writes
it onto the serialized body at both send sites (all seven levels pinned,
xhigh/max distinct, summary preserved); normalize_effort_echo drops
echoes async-openai's typed enum cannot represent at both the non-stream
and SSE parse seams; the dead typed to_responses_api converter is
deleted. Kimi/moonshot stay byte-identical (ChatCompletions untouched,
wire_api maps to the same default; kimi wire tests green).

kimi-import now recognizes ANY registry platform host as built-in
(was hardcoded moonshot), covering openai and future rows.
This commit is contained in:
2026-07-21 05:04:55 -04:00
parent fdf9b956f5
commit 23e94939c0
10 changed files with 436 additions and 58 deletions
@@ -57,8 +57,15 @@ fn registry_models_dev_ids() -> BTreeSet<&'static str> {
.collect()
}
/// Cache dir override (tests re-home the cache away from the real
/// `~/.kigi` — same pattern as `KIGI_MODELS_CACHE_DIR`).
pub(crate) const MODELS_DEV_CACHE_DIR_ENV: &str = "KIGI_MODELS_DEV_CACHE_DIR";
fn cache_path() -> std::path::PathBuf {
crate::util::kigi_home::kigi_home().join(CACHE_FILE)
match std::env::var(MODELS_DEV_CACHE_DIR_ENV) {
Ok(dir) if !dir.trim().is_empty() => std::path::PathBuf::from(dir).join(CACHE_FILE),
_ => crate::util::kigi_home::kigi_home().join(CACHE_FILE),
}
}
fn refresh_url() -> Option<String> {
@@ -198,13 +205,21 @@ mod tests {
/// Wire-served-only platform sets never trigger IO — and never force the
/// bundled parse (empty owned catalog; the merge branch is gated off).
/// Kimi/Moonshot users therefore keep a zero-egress, zero-cache fetch
/// path even now that enrichment-needing platforms (OpenAI) exist.
#[test]
fn wire_served_platforms_get_empty_catalog_without_io() {
assert!(!any_platform_needs_enrichment(
&kigi_models::PlatformId::ALL
));
let catalog = load_enrichment_catalog(&kigi_models::PlatformId::ALL);
let wire_served = [
kigi_models::PlatformId::KimiCode,
kigi_models::PlatformId::MoonshotCn,
kigi_models::PlatformId::MoonshotAi,
];
assert!(!any_platform_needs_enrichment(&wire_served));
let catalog = load_enrichment_catalog(&wire_served);
assert!(catalog.is_empty());
// The full registry now DOES need enrichment (OpenAI is
// wire_serves_metadata=false) — the fast path must not hide that.
assert!(any_platform_needs_enrichment(&kigi_models::PlatformId::ALL));
}
fn cache_file_in(dir: &tempfile::TempDir) -> std::path::PathBuf {