diff --git a/crates/buzz-agent/README.md b/crates/buzz-agent/README.md index f2d68d4d8c..6032dd1139 100644 --- a/crates/buzz-agent/README.md +++ b/crates/buzz-agent/README.md @@ -242,7 +242,7 @@ lifecycle hook — see [MCP_DRIVEN_HOOKS.md](../../docs/MCP_DRIVEN_HOOKS.md). | Block Gateway | `openai` | `POST {base}/chat/completions` | gpt-5, claude | | OpenRouter | `openrouter` | `POST {base}/chat/completions` | anything they route (extended-thinking replay, provider-agnostic tool calling) | | Databricks | `databricks` | `POST {host}/serving-endpoints/{model}/invocations` | goose-claude-4-6-sonnet | -| Databricks AI Gateway v2 | `databricks_v2` | `POST {host}/ai-gateway/{provider}/v1/...` | workspace endpoints and Unity Catalog model-service FQNs; UC FQNs use MLflow Chat Completions | +| Databricks AI Gateway v2 | `databricks_v2` | `POST {host}/ai-gateway/{provider}/v1/...` | workspace endpoints and Unity Catalog model-service FQNs; UC GPT-5+ services use OpenAI Responses; other UC FQNs use MLflow Chat Completions | The optional `DATABRICKS_MODEL_FILTER` applies only to model discovery. Each comma-separated entry is trimmed and matched against the complete raw ID with case-sensitive `*` (zero or more characters) and `?` (one Unicode character) semantics; patterns are OR-ed. Unset or blank preserves the full authenticated catalog. A nonblank value containing no usable patterns is rejected. This controls picker visibility only; Databricks and Unity Catalog permissions remain the authorization boundary. A filtered-empty result is authoritative and does not restore the built-in fallback models. diff --git a/crates/buzz-agent/src/llm.rs b/crates/buzz-agent/src/llm.rs index 1bac514774..c0eae9a4d2 100644 --- a/crates/buzz-agent/src/llm.rs +++ b/crates/buzz-agent/src/llm.rs @@ -2570,6 +2570,7 @@ fn apply_anthropic_cache_control(body: &mut serde_json::Map) { #[cfg(test)] mod tests { + include!("llm_fqn_tests.rs"); use super::*; use crate::config::{Config, HookServers, OpenAiApi, Provider, ThinkingSummary}; use crate::types::{HistoryItem, ToolCall, ToolResult, ToolResultContent}; diff --git a/crates/buzz-agent/src/llm_fqn_tests.rs b/crates/buzz-agent/src/llm_fqn_tests.rs new file mode 100644 index 0000000000..cf79358b86 --- /dev/null +++ b/crates/buzz-agent/src/llm_fqn_tests.rs @@ -0,0 +1,56 @@ +// Included in llm::tests to reuse the production-path HTTP capture fixture. +#[tokio::test] +async fn gpt_fqn_completion_and_summary_use_responses() { + let response = json!({"status":"completed", "output":[{ + "type":"message", "content":[{"type":"output_text", "text":"ok"}] + }]}); + let (base_url, captured) = spawn_sequence_stub(vec![ + StubHttpResponse::ok(response.clone()), + StubHttpResponse::ok(response), + ]) + .await; + let mut config = cfg(Provider::DatabricksV2); + config.base_url = base_url; + config.thinking_effort = Some(ThinkingEffort::High); + let model = "catalog.schema.goose-gpt-6-astra"; + let llm = Llm::new(&config).unwrap(); + let tools = vec![ToolDef { + name: "test_tool".into(), + description: "Test".into(), + input_schema: json!({"type":"object", "properties":{}}), + }]; + let result = llm + .complete( + &config, + "system", + &[HistoryItem::User("hello".into())], + &tools, + model, + ) + .await + .unwrap(); + assert_eq!(result.text, "ok"); + assert_eq!( + llm.summarize(&config, "system", "history", 128, model) + .await + .unwrap(), + "ok" + ); + let requests = captured.lock().await; + let posts: Vec<_> = requests.iter().filter(|r| r.method == "POST").collect(); + assert_eq!(posts.len(), 2); + for request in &posts { + assert_eq!(request.path, "/v1/ai-gateway/openai/v1/responses"); + let body = request.body.as_ref().unwrap(); + assert_eq!(body["model"], model); + assert!(body.get("input").is_some()); + assert!(body.get("messages").is_none()); + assert!(body.get("reasoning_effort").is_none()); + } + let completion = posts[0].body.as_ref().unwrap(); + assert!(completion["input"].is_array()); + assert_eq!(completion["reasoning"]["effort"], "high"); + assert_eq!(completion["tools"][0]["type"], "function"); + assert_eq!(completion["tools"][0]["name"], "test_tool"); + assert_eq!(posts[1].body.as_ref().unwrap()["max_output_tokens"], 128); +} diff --git a/crates/buzz-agent/src/model_capabilities.rs b/crates/buzz-agent/src/model_capabilities.rs index 53f2d290ff..18a4726012 100644 --- a/crates/buzz-agent/src/model_capabilities.rs +++ b/crates/buzz-agent/src/model_capabilities.rs @@ -292,8 +292,8 @@ fn prefix_matches(token: &str, s: &str) -> bool { /// /// Databricks Unity Catalog model-service names are catalog data, not model /// family hints. Both capability interpreters use this shape check before -/// family matching so suffixes such as `kimi-k3` cannot inherit endpoint -/// capabilities accidentally. +/// family matching so services cannot inherit endpoint capabilities accidentally. +/// GPT-5+ services have a route-only Responses exception. pub(crate) fn is_databricks_model_service_fqn(model: &str) -> bool { let mut components = model.split('.'); let (Some(catalog), Some(schema), Some(service)) = @@ -308,16 +308,35 @@ pub(crate) fn is_databricks_model_service_fqn(model: &str) -> bool { }) && components.next().is_none() } +/// Route GPT-5+ UC services to Responses without borrowing endpoint effort facts. +/// Match the first family token in the service only, preserving the existing +/// boundary semantics (e.g. `claude-gpt-5` is not a GPT service). +fn fqn_requires_responses(model: &str) -> bool { + let Some(service) = model.rsplit('.').next() else { + return false; + }; + let lower = service.to_ascii_lowercase(); + let stripped = strip_catalog_prefix(&lower, &manifest().family_tokens); + let Some(version) = stripped.strip_prefix("gpt-") else { + return false; + }; + let digits = version.bytes().take_while(u8::is_ascii_digit).count(); + let (major, suffix) = version.split_at(digits); + if suffix.starts_with(|c: char| c.is_ascii_alphanumeric()) { + return false; + } + major.parse::().is_ok_and(|major| major >= 5) +} + /// Resolve the capability profile for a `(provider, raw_model_id)` pair. pub fn resolve(provider: &str, raw_model_id: &str) -> CapabilityResult { let m = manifest(); let canon = canonical_provider(provider); let blank = raw_model_id.trim().is_empty(); - // Unity Catalog FQNs are neutral model-service identities. Resolve them - // through the concrete-unknown fallback before any suffix can match a - // provider family rule. Routing and effort normalization then share this - // one answer in Rust and TypeScript. + // FQNs keep neutral effort capabilities, but GPT-5+ service names need + // Responses for tools with reasoning. Only inspect the service component: + // catalog/schema names must never choose a model protocol. let model_service_fqn = canon == "databricks_v2" && is_databricks_model_service_fqn(raw_model_id); @@ -394,7 +413,11 @@ pub fn resolve(provider: &str, raw_model_id: &str) -> CapabilityResult { thinking_mode: state.thinking_mode, supported_efforts: &state.supported_efforts, default_effort: state.default_effort, - databricks_v2_wire_route: state.databricks_v2_wire_route, + databricks_v2_wire_route: if model_service_fqn && fqn_requires_responses(raw_model_id) { + DatabricksV2Route::OpenaiResponses + } else { + state.databricks_v2_wire_route + }, normalization_policy: state.normalization_policy, registry_label: None, } @@ -724,6 +747,25 @@ mod tests { Q::Vector { id: "boundary-claude-3-digit-run-anthropic-probe", provider: "anthropic", raw_model_id: "claude-35", note: Some("Probes whether the claude-3 prefix binds a longer digit run ('35').") }, Q::Vector { id: "boundary-claude-opus-4-70-anthropic-probe", provider: "anthropic", raw_model_id: "claude-opus-4-70", note: Some("Probes whether the claude-opus-4-7 prefix binds a longer digit run ('70').") }, Q::Vector { id: "boundary-gpt-5-1234-openai-probe", provider: "openai", raw_model_id: "gpt-5-1234", note: Some("Probes a 4-digit run after the gpt-5 stem.") }, + Q::Section { group: "Databricks FQN GPT-5+ Responses routing", note: Some("Only the service component selects Responses; effort capabilities remain neutral.") }, + Q::Vector { id: "dbv2-fqn-responses-0", provider: "databricks_v2", raw_model_id: "catalog.schema.goose-gpt-6-astra", note: None }, + Q::Vector { id: "dbv2-fqn-responses-1", provider: "databricks_v2", raw_model_id: "catalog.schema.goose-gpt-5", note: None }, + Q::Vector { id: "dbv2-fqn-responses-2", provider: "databricks_v2", raw_model_id: "catalog.schema.gpt-5-5", note: None }, + Q::Vector { id: "dbv2-fqn-responses-3", provider: "databricks_v2", raw_model_id: "catalog.schema.gpt-10", note: None }, + Q::Vector { id: "dbv2-fqn-responses-4", provider: "databricks_v2", raw_model_id: "catalog.schema.GOOSE-GPT-6-ASTRA", note: None }, + Q::Vector { id: "dbv2-fqn-responses-5", provider: "databricks_v2", raw_model_id: "gpt-6.schema.other", note: None }, + Q::Vector { id: "dbv2-fqn-responses-6", provider: "databricks_v2", raw_model_id: "catalog.gpt-5.other", note: None }, + Q::Vector { id: "dbv2-fqn-responses-7", provider: "databricks_v2", raw_model_id: "catalog.schema.claude-gpt-6", note: None }, + Q::Vector { id: "dbv2-fqn-responses-8", provider: "databricks_v2", raw_model_id: "catalog.schema.my-gpt-6-astra", note: None }, + Q::Vector { id: "dbv2-fqn-responses-9", provider: "databricks_v2", raw_model_id: "catalog.schema.mygpt-6-astra", note: None }, + Q::Vector { id: "dbv2-fqn-responses-10", provider: "databricks_v2", raw_model_id: "catalog.schema.gpt-4", note: None }, + Q::Vector { id: "dbv2-fqn-responses-11", provider: "databricks_v2", raw_model_id: "catalog.schema.gpt-4o", note: None }, + Q::Vector { id: "dbv2-fqn-responses-12", provider: "databricks_v2", raw_model_id: "catalog.schema.gpt-6x", note: None }, + Q::Vector { id: "dbv2-fqn-responses-13", provider: "databricks_v2", raw_model_id: "catalog.schema.gpt-oss-120b", note: None }, + Q::Vector { id: "dbv2-fqn-responses-14", provider: "databricks_v2", raw_model_id: "catalog.schema.gpt-", note: None }, + Q::Vector { id: "dbv2-fqn-responses-15", provider: "databricks_v2", raw_model_id: "catalog.schema.gpt-4294967296", note: None }, + Q::Vector { id: "dbv2-fqn-responses-16", provider: "databricks_v2", raw_model_id: "catalog.schema.gpt-6", note: None }, + Q::Vector { id: "dbv2-fqn-responses-17", provider: "databricks_v2", raw_model_id: "catalog.schema.kimi-k3", note: None }, Q::Section { group: "Databricks UC model-family humanization probes (#6918 follow-up)", note: Some("Exact-record and UC-FQN strip probes for the Gemini/DeepSeek/GLM/Grok/Llama/Qwen/Gemma/Inkling families surfaced by UC discovery.") }, Q::Vector { id: "dbv2-gemini-3-1-flash-image-exact-record-probe", provider: "databricks_v2", raw_model_id: "databricks-gemini-3-1-flash-image", note: Some("Probes the Gemini 3.1 Flash Image endpoint record and label.") }, Q::Vector { id: "dbv2-gemini-3-5-flash-exact-record-probe", provider: "databricks_v2", raw_model_id: "databricks-gemini-3-5-flash", note: Some("Probes the Gemini 3.5 Flash endpoint record and label.") }, @@ -844,7 +886,7 @@ mod tests { } #[test] - fn corpus_has_exactly_140_executable_vectors() { + fn corpus_has_exactly_158_executable_vectors() { // Locks the vector count so a silent INPUTS edit can't quietly drop // coverage; must equal the gate in the TS harness // (modelCapabilitiesCorpus.test.mjs). @@ -853,7 +895,7 @@ mod tests { .filter(|q| matches!(q, Q::Vector { .. })) .count(); assert_eq!( - vectors, 140, + vectors, 158, "corpus executable-vector count changed; update this gate deliberately" ); } diff --git a/crates/buzz-agent/tests/fqn_capabilities.rs b/crates/buzz-agent/tests/fqn_capabilities.rs new file mode 100644 index 0000000000..1c513dc011 --- /dev/null +++ b/crates/buzz-agent/tests/fqn_capabilities.rs @@ -0,0 +1,63 @@ +//! UC route selection must change only the route, never effort or model identity. +use buzz_agent::model_capabilities::{resolve, DatabricksV2Route}; + +#[test] +fn gpt_fqn_route_preserves_neutral_capabilities() { + let fallback = resolve("databricks_v2", "catalog.schema.unknown"); + for model in [ + "catalog.schema.goose-gpt-6-astra", + "catalog.schema.gpt-5", + "catalog.schema.goose-gpt-5-5", + "catalog.schema.gpt-10", + "catalog.schema.GOOSE-GPT-6-ASTRA", + "catalog.schema.team-gpt-6-astra", + ] { + let got = resolve(" DATABRICKS-V2 ", model); + assert_eq!( + got.databricks_v2_wire_route, + DatabricksV2Route::OpenaiResponses, + "{model}" + ); + assert_eq!(got.thinking_mode, fallback.thinking_mode, "{model}"); + assert_eq!(got.supported_efforts, fallback.supported_efforts, "{model}"); + assert_eq!(got.default_effort, fallback.default_effort, "{model}"); + assert_eq!( + got.normalization_policy, fallback.normalization_policy, + "{model}" + ); + assert_eq!(got.registry_label, None, "{model}"); + } +} + +#[test] +fn unrelated_fqns_do_not_select_responses() { + for model in [ + "gpt-6.schema.other", + "catalog.gpt-5.other", + "catalog.schema.claude-gpt-6", + "catalog.schema.mygpt-6-astra", + "catalog.schema.gpt-4", + "catalog.schema.gpt-4o", + "catalog.schema.gpt-6x", + "catalog.schema.gpt-oss-120b", + "catalog.schema.gpt-", + "catalog.schema.gpt-6", + "catalog.schema.gpt-4294967296", + "catalog.schema.kimi-k3", + ] { + assert_eq!( + resolve("databricks_v2", model).databricks_v2_wire_route, + DatabricksV2Route::MlflowChat, + "{model}" + ); + } + // The new rule is not an endpoint-family or cross-provider capability change. + assert_eq!( + resolve("databricks_v2", "goose-gpt-6-astra").databricks_v2_wire_route, + DatabricksV2Route::MlflowChat + ); + assert_eq!( + resolve("openai", "catalog.schema.goose-gpt-6-astra").databricks_v2_wire_route, + DatabricksV2Route::NotApplicable + ); +} diff --git a/desktop/src/features/agents/AGENTS.md b/desktop/src/features/agents/AGENTS.md index dfb9c0ed49..7f1f73b175 100644 --- a/desktop/src/features/agents/AGENTS.md +++ b/desktop/src/features/agents/AGENTS.md @@ -306,7 +306,7 @@ with a TypeScript lookup table or an id comparison in a component. refresh only local persona/team/managed-agent caches; they must never invalidate the remote relay directory. -17. **Databricks model discovery has one shared catalog authority.** Desktop and ACP call the shared `buzz-agent` discovery library; Desktop passes the effective merged `DATABRICKS_MODEL_FILTER` explicitly, and the library applies it to raw workspace endpoint IDs and Unity Catalog model-service FQNs after the additive union. A successful filtered-empty catalog is authoritative: it stays empty, disables switching, and never falls through to configured or known-model fallback. UC FQNs are catalog data and always use the MLflow Chat Completions route, regardless of family-looking text in their components. Global Defaults preserves the discovered model ID as the selected value while its closed trigger renders the provider-scoped display label; do not force the raw persisted ID over that label. +17. **Databricks model discovery has one shared catalog authority.** Desktop and ACP call the shared `buzz-agent` discovery library; Desktop passes the effective merged `DATABRICKS_MODEL_FILTER` explicitly, and the library applies it to raw workspace endpoint IDs and Unity Catalog model-service FQNs after the additive union. A successful filtered-empty catalog is authoritative: it stays empty, disables switching, and never falls through to configured or known-model fallback. UC FQNs retain neutral effort capabilities. A boundary-matched GPT-5-or-newer family in the service-name component selects OpenAI Responses so tools can coexist with reasoning; other FQNs use MLflow Chat Completions. Catalog/schema components never influence routing. Keep this route-only rule identical in the Rust and TypeScript capability interpreters. Global Defaults preserves the discovered model ID as the selected value while its closed trigger renders the provider-scoped display label; do not force the raw persisted ID over that label. ## Channel-only runtime controls diff --git a/desktop/src/features/agents/ui/modelCapabilities.ts b/desktop/src/features/agents/ui/modelCapabilities.ts index f2c8de6035..d6866c5c7e 100644 --- a/desktop/src/features/agents/ui/modelCapabilities.ts +++ b/desktop/src/features/agents/ui/modelCapabilities.ts @@ -302,6 +302,19 @@ function isDatabricksModelServiceFqn(model: string): boolean { ); } +// Mirror fqn_requires_responses: routing is the only inferred FQN capability. +function fqnRequiresResponses(model: string): boolean { + const service = model.split(".").at(-1) ?? ""; + const stripped = stripCatalogPrefix( + service.toLowerCase(), + MANIFEST.family_tokens, + ); + const match = /^gpt-([0-9]+)(?:$|[^a-z0-9])/.exec(stripped); + if (!match) return false; + const major = Number(match[1]); + return major >= 5 && major <= 0xffffffff; +} + /** * Resolve the capability profile for a `(provider, rawModelId)` pair. * @@ -315,8 +328,8 @@ export function resolveModelCapabilities( ): CapabilityResult { const canon = canonicalizeProvider(provider); const blank = rawModelId.trim().length === 0; - // Unity Catalog FQNs are neutral model-service identities. Resolve them - // through the concrete-unknown fallback before suffix family matching. + // FQNs keep neutral effort capabilities; only GPT-5+ service names + // select Responses. Catalog/schema names never choose the protocol. const modelServiceFqn = canon === "databricks_v2" && isDatabricksModelServiceFqn(rawModelId); @@ -370,7 +383,11 @@ export function resolveModelCapabilities( // 3. Provider fallback (blank vs. concrete-unknown); never carries a label. const pair = fallbackPair(canon); const state = blank ? pair.blank : pair.concrete_unknown; - return toResult(state, state.databricks_v2_wire_route, null); + const route = + modelServiceFqn && fqnRequiresResponses(rawModelId) + ? "openai-responses" + : state.databricks_v2_wire_route; + return toResult(state, route, null); } /** Authoritative list of known Databricks v2 model ids, sourced from the manifest. */ diff --git a/desktop/src/features/agents/ui/modelCapabilitiesCorpus.test.mjs b/desktop/src/features/agents/ui/modelCapabilitiesCorpus.test.mjs index e0d150cc4f..21baf6b64b 100644 --- a/desktop/src/features/agents/ui/modelCapabilitiesCorpus.test.mjs +++ b/desktop/src/features/agents/ui/modelCapabilitiesCorpus.test.mjs @@ -25,10 +25,10 @@ const corpus = JSON.parse(readFileSync(fileURLToPath(corpusUrl), "utf8")); // (`_group`) are skipped. Mirrors the Rust corpus filter. const executable = corpus.filter((entry) => entry.expect != null); -test("corpus has exactly 140 executable vectors", () => { +test("corpus has exactly 158 executable vectors", () => { // Locks the vector count so a silent corpus edit can't quietly drop coverage; // must equal the gate in the Rust suite (model_capabilities.rs). - assert.equal(executable.length, 140); + assert.equal(executable.length, 158); }); test("registry label aliases refuse an unprefixed query", () => { diff --git a/scripts/normative-corpus.json b/scripts/normative-corpus.json index b75e4eb0d5..cb0ff4ae27 100644 --- a/scripts/normative-corpus.json +++ b/scripts/normative-corpus.json @@ -2347,6 +2347,370 @@ "registry_label": null } }, + { + "_group": "Databricks FQN GPT-5+ Responses routing", + "_note": "Only the service component selects Responses; effort capabilities remain neutral." + }, + { + "id": "dbv2-fqn-responses-0", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.goose-gpt-6-astra", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "openai-responses", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-1", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.goose-gpt-5", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "openai-responses", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-2", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.gpt-5-5", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "openai-responses", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-3", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.gpt-10", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "openai-responses", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-4", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.GOOSE-GPT-6-ASTRA", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "openai-responses", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-5", + "provider": "databricks_v2", + "raw_model_id": "gpt-6.schema.other", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "mlflow-chat", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-6", + "provider": "databricks_v2", + "raw_model_id": "catalog.gpt-5.other", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "mlflow-chat", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-7", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.claude-gpt-6", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "mlflow-chat", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-8", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.my-gpt-6-astra", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "openai-responses", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-9", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.mygpt-6-astra", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "mlflow-chat", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-10", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.gpt-4", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "mlflow-chat", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-11", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.gpt-4o", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "mlflow-chat", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-12", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.gpt-6x", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "mlflow-chat", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-13", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.gpt-oss-120b", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "mlflow-chat", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-14", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.gpt-", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "mlflow-chat", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-15", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.gpt-4294967296", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "mlflow-chat", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-16", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.gpt-6", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "mlflow-chat", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, + { + "id": "dbv2-fqn-responses-17", + "provider": "databricks_v2", + "raw_model_id": "catalog.schema.kimi-k3", + "expect": { + "thinking_mode": "none", + "supported_efforts": [ + "none", + "minimal", + "low", + "medium", + "high", + "xhigh" + ], + "default_effort": "medium", + "databricks_v2_wire_route": "mlflow-chat", + "normalization_policy": "openai-clamp-max-to-xhigh", + "registry_label": null + } + }, { "_group": "Databricks UC model-family humanization probes (#6918 follow-up)", "_note": "Exact-record and UC-FQN strip probes for the Gemini/DeepSeek/GLM/Grok/Llama/Qwen/Gemma/Inkling families surfaced by UC discovery."