Keep uncatalogued DeepSeek models on Chat

Remove version-prefix Responses inference while preserving exact catalog
protocols. Unknown preview IDs retain their wire model and Chat pass-through;
this adds no capability, pricing, context or output-limit claims.

Config library: 684 passed, 0 failed, 1 ignored. Restoring the old resolver
makes the exact-preview regression fail 0 passed / 1 failed; restoring the
fix passes 1 / 0. Config production and TUI test metadata compilation pass;
TUI executable acceptance remains for the root integration gate. Formatting
and diff checks pass. No provider inference or OS action performed.
This commit is contained in:
CodeWhale Bot committed 2026-09-08 04:47:03 -07:00
1 parent 647922b224
commit a4578184d8
3 files changed
+82 -23

No files matched your search

+2 -21
View File
@@ -250,18 +250,8 @@ impl RouteResolver {
require_catalog_match,
)?
};
if provider_kind == ProviderKind::Deepseek {
if custom_endpoint {
selected.endpoint_key = "chat".to_string();
} else if selected.canonical_model.is_none()
&& deepseek_versioned_model_prefers_responses(selected.wire_model_id.as_str())
{
// DeepSeek introduced its native agent wire on V4 Flash. Keep
// exact catalog rows authoritative (notably V4 Pro => Chat),
// while allowing future versioned direct models to adopt the
// new Responses surface without a Codewhale release.
selected.endpoint_key = "responses".to_string();
}
if provider_kind == ProviderKind::Deepseek && custom_endpoint {
selected.endpoint_key = "chat".to_string();
}
if custom_endpoint && !endpoint_catalog_authoritative {
// Capabilities and pricing belong to the exact provider endpoint
@@ -644,15 +634,6 @@ fn request_uses_custom_endpoint(
.is_some_and(|base_url| provider_preserves_custom_base_url_model(descriptor.kind, base_url))
}
fn deepseek_versioned_model_prefers_responses(model: &str) -> bool {
model
.trim()
.to_ascii_lowercase()
.strip_prefix("deepseek-v")
.and_then(|suffix| suffix.chars().next())
.is_some_and(|first| first.is_ascii_digit())
}
/// True when `base_url` is an `http://` endpoint whose host is NOT loopback
/// (#1519). Such an endpoint sends credentials in plaintext over the network;
/// loopback (`localhost` / `127.0.0.1` / `::1`) is exempt because local
+30 -2
View File
@@ -415,8 +415,8 @@ fn resolver_routes_only_official_deepseek_flash_over_responses() {
Some("deepseek-v5-future"),
))
.expect("unknown future model preserves direct-provider pass-through");
assert_eq!(future.protocol(), RequestProtocol::Responses);
assert_eq!(future.endpoint().endpoint_key, "responses");
assert_eq!(future.protocol(), RequestProtocol::ChatCompletions);
assert_eq!(future.endpoint().endpoint_key, "chat");
let legacy_unknown = resolver
.resolve(&req(
@@ -444,6 +444,34 @@ fn resolver_routes_only_official_deepseek_flash_over_responses() {
);
}
#[test]
fn resolver_uncatalogued_deepseek_preview_keeps_chat_and_verbatim_model() {
let model = "deepseek-v4.1-flash-expires-on-0910";
for base_url in [
"https://api.deepseek.com",
"https://api.deepseek.com/v1",
"https://api.deepseek.com/beta",
] {
let route = RouteResolver::new()
.resolve(&RouteRequest {
base_url_override: Some(base_url.to_string()),
..req(Some(ProviderKind::Deepseek), Some(model))
})
.expect("uncatalogued preview keeps direct-provider pass-through");
assert_eq!(route.provider_kind(), ProviderKind::Deepseek);
assert_eq!(route.wire_model_id().as_str(), model);
assert!(
route.canonical_model().is_none(),
"no fallback to a known model"
);
assert_eq!(route.protocol(), RequestProtocol::ChatCompletions);
assert_eq!(route.endpoint().endpoint_key, "chat");
assert_eq!(route.endpoint().base_url, base_url);
assert_eq!(route.capabilities().image_input, CapabilityState::Unknown);
assert_eq!(route.limits().output_tokens, None);
}
}
#[test]
fn resolver_routes_deepseek_vision_exp_over_chat_with_image_input() {
for base_url_override in [
+50
View File
@@ -13178,6 +13178,56 @@ mod tests {
assert_eq!(prepared.body["reasoning"]["effort"], "max");
}
#[test]
fn uncatalogued_deepseek_preview_binds_chat_request_without_model_fallback() {
let model = "deepseek-v4.1-flash-expires-on-0910";
let (_config, route) = deepseek_route_for_test("https://api.deepseek.com", model);
assert_eq!(route.candidate.protocol(), WireFormat::ChatCompletions);
assert_eq!(route.candidate.wire_model_id().as_str(), model);
assert!(route.candidate.canonical_model().is_none());
for client in [
DeepSeekClient::new(&route.config).expect("preview client resolves"),
DeepSeekClient::from_candidate(&route.config, &route.candidate)
.expect("preview client binds the admitted candidate"),
] {
assert_eq!(client.wire_format, WireFormat::ChatCompletions);
assert_eq!(client.default_model, model);
let prepared = client
.prepare_outbound_request(
MessageRequest {
model: model.to_string(),
messages: vec![Message {
role: Role::User,
content: vec![ContentBlock::Text {
text: "hello".to_string(),
cache_control: None,
}],
}],
max_tokens: 64,
system: None,
tools: None,
tool_choice: None,
metadata: None,
thinking: None,
reasoning_effort: None,
stream: Some(true),
temperature: None,
top_p: None,
},
true,
)
.expect("preview Chat request prepares");
assert_eq!(prepared.dialect, WireDialect::ChatCompletions);
assert_eq!(
prepared.endpoint.url,
"https://api.deepseek.com/chat/completions"
);
assert_eq!(prepared.body["model"], model);
assert_eq!(prepared.body["messages"][0]["content"], "hello");
}
}
#[test]
fn rebinding_a_chat_bound_client_for_flash_switches_to_responses() {
// #5042: fleet dispatch binds the child client before the profile