feat(model-catalog): add Claude 4.7 + GPT-5.5 sampling-params handling

This commit is contained in:
matevip 2026-04-26 08:32:45 +08:00
parent fac5ff2838
commit 23a6d16778
7 changed files with 164 additions and 15 deletions

View File

@ -98,36 +98,71 @@ public class AgentAnthropicChatModelBuilder implements ChatModelBuilder {
return builder.build();
}
/**
* Substrings used to detect Claude 4.7 model variants. Reference:
* hermes-agent {@code anthropic_adapter._NO_SAMPLING_PARAMS_SUBSTRINGS}.
* Claude 4.7 returns HTTP 400 if any of {@code temperature}, {@code top_p},
* or {@code top_k} are set to non-default values, AND introduces an
* "xhigh" thinking effort level between high and max.
*/
static boolean isClaude47(String modelName) {
if (modelName == null) return false;
String lower = modelName.toLowerCase();
// Require the "claude" token to avoid false positives like "gpt-4-7"
// matching. Match both hyphenated (claude-opus-4-7, anthropic/claude-opus-4-7)
// and dotted (claude-opus-4.7, anthropic/claude-opus-4.7 via OpenRouter)
// forms. Also tolerates date-stamped variants (claude-opus-4-7-20260415).
if (!lower.contains("claude")) return false;
return lower.contains("4-7") || lower.contains("4.7");
}
AnthropicChatOptions buildAnthropicOptions(ModelConfigEntity runtimeModel) {
AnthropicChatOptions.Builder builder = AnthropicChatOptions.builder();
if (StringUtils.hasText(runtimeModel.getModelName())) {
builder.model(runtimeModel.getModelName());
String modelName = runtimeModel.getModelName();
if (StringUtils.hasText(modelName)) {
builder.model(modelName);
}
boolean isClaude47 = isClaude47(modelName);
// Extended thinking request-level depth from ThinkingLevelHolder
String thinkingLevel = ThinkingLevelHolder.get();
boolean thinkingEnabled = thinkingLevel != null && !"off".equalsIgnoreCase(thinkingLevel);
if (thinkingEnabled) {
// Anthropic thinking mode constraints: temperature MUST be 1, top_p forbidden,
// max_tokens must accommodate budget_tokens + a buffer for the actual answer.
// Anthropic thinking-mode constraints (pre-4.7): temperature MUST be 1,
// top_p forbidden, max_tokens must accommodate budget_tokens + buffer.
// Claude 4.7 forbids temperature/top_p/top_k entirely (any non-null value
// HTTP 400) and adds an "xhigh" budget tier between high and max.
int budgetTokens = switch (thinkingLevel.toLowerCase()) {
case "low" -> 4096;
case "medium" -> 8192;
case "high" -> 16384;
case "xhigh" -> 24576; // 4.7 only between high (16k) and max (32k)
case "max" -> 32768;
default -> 16384;
};
builder.thinking(AnthropicApi.ThinkingType.ENABLED, budgetTokens);
builder.maxTokens(Math.max(budgetTokens + 4096,
runtimeModel.getMaxTokens() != null ? runtimeModel.getMaxTokens() : 8192));
builder.temperature(1.0);
// Claude 4.7: omit temperature entirely. Pre-4.7 thinking mode requires
// temperature=1 (Anthropic-mandated default for thinking).
if (!isClaude47) {
builder.temperature(1.0);
}
} else {
// Non-thinking: Anthropic refuses to accept temperature AND top_p simultaneously.
if (runtimeModel.getTemperature() != null) {
builder.temperature(runtimeModel.getTemperature());
} else if (runtimeModel.getTopP() != null) {
builder.topP(runtimeModel.getTopP());
// Non-thinking path.
// - Pre-4.7: Anthropic accepts EITHER temperature OR top_p (not both).
// - 4.7+: rejects all of temperature/top_p/top_k unless null/default. We
// omit them entirely so operators with legacy configs don't 400.
if (!isClaude47) {
if (runtimeModel.getTemperature() != null) {
builder.temperature(runtimeModel.getTemperature());
} else if (runtimeModel.getTopP() != null) {
builder.topP(runtimeModel.getTopP());
}
} else if (runtimeModel.getTemperature() != null || runtimeModel.getTopP() != null) {
log.debug("Ignoring temperature/top_p for Claude 4.7 model {} (API rejects sampling params)",
modelName);
}
// RFC-025: Anthropic rejects non-positive maxTokens clamp here so a bad config
// surfaces as a logged warning instead of an opaque API 400 mid-conversation.
@ -137,7 +172,7 @@ public class AgentAnthropicChatModelBuilder implements ChatModelBuilder {
} else {
if (configuredMax != null) {
log.warn("Ignoring non-positive Anthropic maxTokens={} for model {}; falling back to 4096",
configuredMax, runtimeModel.getModelName());
configuredMax, modelName);
}
builder.maxTokens(4096);
}

View File

@ -245,7 +245,20 @@ MERGE INTO mate_model_config (id, name, provider, model_name, description, tempe
(1000000235, 'Doubao 1.5 Thinking Lite', 'volcengine', 'doubao-1.5-thinking-lite', 'Doubao lite reasoning model', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000240, 'Kimi for Coding', 'kimi-code', 'kimi-for-coding', 'Kimi Code dedicated coding model', 0.2, 32768, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000250, 'GPT-5.4', 'openai-chatgpt', 'gpt-5.4', 'ChatGPT Plus/Pro member model (OAuth login)', NULL, 128000, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000251, 'GPT-5.4 Mini', 'openai-chatgpt', 'gpt-5.4-mini', 'ChatGPT member lightweight model', NULL, 128000, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0);
(1000000251, 'GPT-5.4 Mini', 'openai-chatgpt', 'gpt-5.4-mini', 'ChatGPT member lightweight model', NULL, 128000, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
-- GPT-5.5 series (direct OpenAI / Azure / OpenRouter / ChatGPT)
(1000000260, 'GPT-5.5', 'openai', 'gpt-5.5', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000261, 'GPT-5.5 Mini', 'openai', 'gpt-5.5-mini', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000262, 'GPT-5.5 Nano', 'openai', 'gpt-5.5-nano', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000263, 'GPT-5.5', 'azure-openai', 'gpt-5.5', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000264, 'GPT-5.5 Mini', 'azure-openai', 'gpt-5.5-mini', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000265, 'GPT-5.5', 'openrouter', 'openai/gpt-5.5', 'GPT-5.5 via OpenRouter', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
-- Claude 4.7 series (direct Anthropic + OpenRouter). Sonnet/Opus.
-- Note: Claude 4.7 forbids temperature/top_p/top_k — handled in AgentAnthropicChatModelBuilder.
(1000000270, 'Claude Opus 4.7', 'anthropic', 'claude-opus-4-7', 'Anthropic Claude Opus 4.7 (xhigh adaptive thinking)', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000271, 'Claude Sonnet 4.7', 'anthropic', 'claude-sonnet-4-7', 'Anthropic Claude Sonnet 4.7', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000272, 'Claude Opus 4.7', 'openrouter', 'anthropic/claude-opus-4-7', 'Claude Opus 4.7 via OpenRouter', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000273, 'Claude Sonnet 4.7', 'openrouter', 'anthropic/claude-sonnet-4-7', 'Claude Sonnet 4.7 via OpenRouter', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0);
-- Default system settings
MERGE INTO mate_system_setting (id, setting_key, setting_value, description, create_time, update_time)

View File

@ -285,7 +285,20 @@ VALUES
(1000000235, 'Doubao 1.5 Thinking Lite', 'volcengine', 'doubao-1.5-thinking-lite', 'Doubao lite reasoning model', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000240, 'Kimi for Coding', 'kimi-code', 'kimi-for-coding', 'Kimi Code dedicated coding model', 0.2, 32768, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000250, 'GPT-5.4', 'openai-chatgpt', 'gpt-5.4', 'ChatGPT Plus/Pro member model (OAuth login)', NULL, 128000, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000251, 'GPT-5.4 Mini', 'openai-chatgpt', 'gpt-5.4-mini', 'ChatGPT member lightweight model', NULL, 128000, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0)
(1000000251, 'GPT-5.4 Mini', 'openai-chatgpt', 'gpt-5.4-mini', 'ChatGPT member lightweight model', NULL, 128000, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
-- GPT-5.5 series (OpenAI / Azure / OpenRouter)
(1000000260, 'GPT-5.5', 'openai', 'gpt-5.5', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000261, 'GPT-5.5 Mini', 'openai', 'gpt-5.5-mini', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000262, 'GPT-5.5 Nano', 'openai', 'gpt-5.5-nano', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000263, 'GPT-5.5', 'azure-openai', 'gpt-5.5', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000264, 'GPT-5.5 Mini', 'azure-openai', 'gpt-5.5-mini', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000265, 'GPT-5.5', 'openrouter', 'openai/gpt-5.5', 'GPT-5.5 via OpenRouter', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
-- Claude 4.7 series (direct Anthropic + OpenRouter).
-- Note: Claude 4.7 forbids temperature/top_p/top_k — handled in AgentAnthropicChatModelBuilder.
(1000000270, 'Claude Opus 4.7', 'anthropic', 'claude-opus-4-7', 'Anthropic Claude Opus 4.7 (xhigh adaptive thinking)', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000271, 'Claude Sonnet 4.7', 'anthropic', 'claude-sonnet-4-7', 'Anthropic Claude Sonnet 4.7', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000272, 'Claude Opus 4.7', 'openrouter', 'anthropic/claude-opus-4-7', 'Claude Opus 4.7 via OpenRouter', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000273, 'Claude Sonnet 4.7', 'openrouter', 'anthropic/claude-sonnet-4-7', 'Claude Sonnet 4.7 via OpenRouter', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0)
ON DUPLICATE KEY UPDATE name=VALUES(name), provider=VALUES(provider), model_name=VALUES(model_name), description=VALUES(description), temperature=VALUES(temperature), max_tokens=VALUES(max_tokens), top_p=VALUES(top_p), builtin=VALUES(builtin), enabled=VALUES(enabled), is_default=VALUES(is_default), update_time=VALUES(update_time), deleted=VALUES(deleted);
-- Default system settings

View File

@ -285,7 +285,20 @@ VALUES
(1000000235, 'Doubao 1.5 Thinking Lite', 'volcengine', 'doubao-1.5-thinking-lite', '豆包轻量推理模型', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000240, 'Kimi for Coding', 'kimi-code', 'kimi-for-coding', 'Kimi Code 专用编码模型', 0.2, 32768, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000250, 'GPT-5.4', 'openai-chatgpt', 'gpt-5.4', 'ChatGPT Plus/Pro 会员模型OAuth 登录)', NULL, 128000, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000251, 'GPT-5.4 Mini', 'openai-chatgpt', 'gpt-5.4-mini', 'ChatGPT 会员轻量模型', NULL, 128000, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0)
(1000000251, 'GPT-5.4 Mini', 'openai-chatgpt', 'gpt-5.4-mini', 'ChatGPT 会员轻量模型', NULL, 128000, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
-- GPT-5.5 系列OpenAI / Azure / OpenRouter
(1000000260, 'GPT-5.5', 'openai', 'gpt-5.5', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000261, 'GPT-5.5 Mini', 'openai', 'gpt-5.5-mini', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000262, 'GPT-5.5 Nano', 'openai', 'gpt-5.5-nano', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000263, 'GPT-5.5', 'azure-openai', 'gpt-5.5', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000264, 'GPT-5.5 Mini', 'azure-openai', 'gpt-5.5-mini', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000265, 'GPT-5.5', 'openrouter', 'openai/gpt-5.5', 'OpenRouter 代理 GPT-5.5', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
-- Claude 4.7 系列(直连 Anthropic + OpenRouter
-- 注意Claude 4.7 禁止 temperature / top_p / top_k 参数,已在 AgentAnthropicChatModelBuilder 中适配
(1000000270, 'Claude Opus 4.7', 'anthropic', 'claude-opus-4-7', 'Anthropic Claude Opus 4.7xhigh 自适应思考)', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000271, 'Claude Sonnet 4.7', 'anthropic', 'claude-sonnet-4-7', 'Anthropic Claude Sonnet 4.7', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000272, 'Claude Opus 4.7', 'openrouter', 'anthropic/claude-opus-4-7', 'OpenRouter 代理 Claude Opus 4.7', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000273, 'Claude Sonnet 4.7', 'openrouter', 'anthropic/claude-sonnet-4-7', 'OpenRouter 代理 Claude Sonnet 4.7', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0)
ON DUPLICATE KEY UPDATE name=VALUES(name), provider=VALUES(provider), model_name=VALUES(model_name), description=VALUES(description), temperature=VALUES(temperature), max_tokens=VALUES(max_tokens), top_p=VALUES(top_p), builtin=VALUES(builtin), enabled=VALUES(enabled), is_default=VALUES(is_default), update_time=VALUES(update_time), deleted=VALUES(deleted);
-- 默认系统设置

View File

@ -251,7 +251,20 @@ MERGE INTO mate_model_config (id, name, provider, model_name, description, tempe
(1000000235, 'Doubao 1.5 Thinking Lite', 'volcengine', 'doubao-1.5-thinking-lite', '豆包轻量推理模型', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000240, 'Kimi for Coding', 'kimi-code', 'kimi-for-coding', 'Kimi Code 专用编码模型', 0.2, 32768, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000250, 'GPT-5.4', 'openai-chatgpt', 'gpt-5.4', 'ChatGPT Plus/Pro 会员模型OAuth 登录)', NULL, 128000, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000251, 'GPT-5.4 Mini', 'openai-chatgpt', 'gpt-5.4-mini', 'ChatGPT 会员轻量模型', NULL, 128000, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0);
(1000000251, 'GPT-5.4 Mini', 'openai-chatgpt', 'gpt-5.4-mini', 'ChatGPT 会员轻量模型', NULL, 128000, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
-- GPT-5.5 系列OpenAI / Azure / OpenRouter
(1000000260, 'GPT-5.5', 'openai', 'gpt-5.5', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000261, 'GPT-5.5 Mini', 'openai', 'gpt-5.5-mini', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000262, 'GPT-5.5 Nano', 'openai', 'gpt-5.5-nano', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000263, 'GPT-5.5', 'azure-openai', 'gpt-5.5', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000264, 'GPT-5.5 Mini', 'azure-openai', 'gpt-5.5-mini', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000265, 'GPT-5.5', 'openrouter', 'openai/gpt-5.5', 'OpenRouter 代理 GPT-5.5', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
-- Claude 4.7 系列(直连 Anthropic + OpenRouter
-- 注意Claude 4.7 禁止 temperature / top_p / top_k 参数,已在 AgentAnthropicChatModelBuilder 中适配
(1000000270, 'Claude Opus 4.7', 'anthropic', 'claude-opus-4-7', 'Anthropic Claude Opus 4.7xhigh 自适应思考)', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000271, 'Claude Sonnet 4.7', 'anthropic', 'claude-sonnet-4-7', 'Anthropic Claude Sonnet 4.7', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000272, 'Claude Opus 4.7', 'openrouter', 'anthropic/claude-opus-4-7', 'OpenRouter 代理 Claude Opus 4.7', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000273, 'Claude Sonnet 4.7', 'openrouter', 'anthropic/claude-sonnet-4-7', 'OpenRouter 代理 Claude Sonnet 4.7', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0);
-- 默认系统设置
MERGE INTO mate_system_setting (id, setting_key, setting_value, description, create_time, update_time)

View File

@ -0,0 +1,25 @@
-- Add Claude 4.7 + GPT-5.5 model entries to mate_model_config for existing
-- deployments. New installs pick these up via DatabaseBootstrapRunner from
-- data-{en,zh,mysql-en,mysql-zh}.sql; this migration covers operators who
-- already have V1 baseline + earlier versions applied.
--
-- MERGE INTO is the H2 idempotent upsert; running this twice is a no-op.
-- Same V number is used in mysql/ for cross-dialect parity.
MERGE INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, create_time, update_time, deleted)
KEY (id)
VALUES
-- GPT-5.5 series (OpenAI / Azure / OpenRouter)
(1000000260, 'GPT-5.5', 'openai', 'gpt-5.5', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000261, 'GPT-5.5 Mini', 'openai', 'gpt-5.5-mini', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000262, 'GPT-5.5 Nano', 'openai', 'gpt-5.5-nano', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000263, 'GPT-5.5', 'azure-openai', 'gpt-5.5', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000264, 'GPT-5.5 Mini', 'azure-openai', 'gpt-5.5-mini', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000265, 'GPT-5.5', 'openrouter', 'openai/gpt-5.5', 'GPT-5.5 via OpenRouter', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
-- Claude 4.7 series. NOTE: Claude 4.7 forbids temperature/top_p/top_k —
-- handled in AgentAnthropicChatModelBuilder. NULL temperature/top_p in seed
-- is the documented signal.
(1000000270, 'Claude Opus 4.7', 'anthropic', 'claude-opus-4-7', 'Anthropic Claude Opus 4.7 (xhigh adaptive thinking)', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000271, 'Claude Sonnet 4.7', 'anthropic', 'claude-sonnet-4-7', 'Anthropic Claude Sonnet 4.7', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000272, 'Claude Opus 4.7', 'openrouter', 'anthropic/claude-opus-4-7', 'Claude Opus 4.7 via OpenRouter', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000273, 'Claude Sonnet 4.7', 'openrouter', 'anthropic/claude-sonnet-4-7', 'Claude Sonnet 4.7 via OpenRouter', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0);

View File

@ -0,0 +1,37 @@
-- Add Claude 4.7 + GPT-5.5 model entries to mate_model_config for existing
-- deployments. New installs pick these up via DatabaseBootstrapRunner from
-- data-mysql-{en,zh}.sql; this migration covers operators who already have
-- earlier Flyway versions applied.
--
-- INSERT ... ON DUPLICATE KEY UPDATE is the MySQL idempotent upsert.
-- Same V number is used in h2/ for cross-dialect parity.
INSERT INTO mate_model_config (id, name, provider, model_name, description, temperature, max_tokens, top_p, builtin, enabled, is_default, create_time, update_time, deleted)
VALUES
-- GPT-5.5 series (OpenAI / Azure / OpenRouter)
(1000000260, 'GPT-5.5', 'openai', 'gpt-5.5', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000261, 'GPT-5.5 Mini', 'openai', 'gpt-5.5-mini', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000262, 'GPT-5.5 Nano', 'openai', 'gpt-5.5-nano', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000263, 'GPT-5.5', 'azure-openai', 'gpt-5.5', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000264, 'GPT-5.5 Mini', 'azure-openai', 'gpt-5.5-mini', '', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000265, 'GPT-5.5', 'openrouter', 'openai/gpt-5.5', 'GPT-5.5 via OpenRouter', 0.7, 4096, 0.8, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
-- Claude 4.7 series. NOTE: Claude 4.7 forbids temperature/top_p/top_k —
-- handled in AgentAnthropicChatModelBuilder. NULL temperature/top_p in seed
-- is the documented signal.
(1000000270, 'Claude Opus 4.7', 'anthropic', 'claude-opus-4-7', 'Anthropic Claude Opus 4.7 (xhigh adaptive thinking)', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000271, 'Claude Sonnet 4.7', 'anthropic', 'claude-sonnet-4-7', 'Anthropic Claude Sonnet 4.7', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000272, 'Claude Opus 4.7', 'openrouter', 'anthropic/claude-opus-4-7', 'Claude Opus 4.7 via OpenRouter', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
(1000000273, 'Claude Sonnet 4.7', 'openrouter', 'anthropic/claude-sonnet-4-7', 'Claude Sonnet 4.7 via OpenRouter', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0)
ON DUPLICATE KEY UPDATE
name = VALUES(name),
provider = VALUES(provider),
model_name = VALUES(model_name),
description = VALUES(description),
temperature = VALUES(temperature),
max_tokens = VALUES(max_tokens),
top_p = VALUES(top_p),
builtin = VALUES(builtin),
enabled = VALUES(enabled),
is_default = VALUES(is_default),
update_time = VALUES(update_time),
deleted = VALUES(deleted);