mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 03:13:41 +08:00
fix(anthropic): bidirectional mcp_ tool-name prefix on OAuth requests
This commit is contained in:
parent
44548e3010
commit
ae6467a5dc
@ -1,50 +1,67 @@
|
||||
package vip.mate.agent.chatmodel;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.ai.anthropic.AnthropicChatOptions;
|
||||
import org.springframework.ai.chat.messages.AssistantMessage;
|
||||
import org.springframework.ai.chat.messages.Message;
|
||||
import org.springframework.ai.chat.messages.SystemMessage;
|
||||
import org.springframework.ai.chat.metadata.ChatGenerationMetadata;
|
||||
import org.springframework.ai.chat.model.ChatModel;
|
||||
import org.springframework.ai.chat.model.ChatResponse;
|
||||
import org.springframework.ai.chat.model.Generation;
|
||||
import org.springframework.ai.chat.prompt.ChatOptions;
|
||||
import org.springframework.ai.chat.prompt.Prompt;
|
||||
import org.springframework.ai.chat.model.ToolContext;
|
||||
import org.springframework.ai.tool.ToolCallback;
|
||||
import org.springframework.ai.tool.definition.DefaultToolDefinition;
|
||||
import org.springframework.ai.tool.definition.ToolDefinition;
|
||||
import org.springframework.ai.tool.metadata.ToolMetadata;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* RFC-062: Claude Code OAuth identity transform applied to every Anthropic
|
||||
* request when the underlying auth is a Claude Code OAuth token.
|
||||
*
|
||||
* <p>Anthropic's OAuth edge enforces an anti-abuse path that rate-limits
|
||||
* (and intermittently 5xxs) requests whose system prompt does NOT claim
|
||||
* Claude Code identity. Symptoms include:
|
||||
* (and intermittently 5xxs) requests claiming Claude Code identity but
|
||||
* shaped differently from real Claude Code traffic. Symptoms:
|
||||
*
|
||||
* <ul>
|
||||
* <li>HTTP 429 with {@code rate_limit_error} on quiet accounts that haven't
|
||||
* come close to their token budget — the give-away is a body of just
|
||||
* come close to their token budget — give-away is a body of just
|
||||
* {@code {"type":"error","error":{"type":"rate_limit_error","message":"Error"}}}
|
||||
* (genuine quota exhaustion includes a descriptive message).</li>
|
||||
* (genuine quota exhaustion carries a descriptive message).</li>
|
||||
* <li>Sporadic 500s on the first call after a long idle period.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>Reference: hermes-agent {@code anthropic_adapter._build_anthropic_messages_request}
|
||||
* lines 1571-1607 — applies the same transforms unconditionally on
|
||||
* lines 1571-1607 — same transforms applied unconditionally on
|
||||
* {@code is_oauth=True} requests.
|
||||
*
|
||||
* <h2>Transforms applied per call</h2>
|
||||
* <ol>
|
||||
* <li>Prepend {@code "You are Claude Code, Anthropic's official CLI for Claude."}
|
||||
* to the system prompt. If no system message is present we insert one.</li>
|
||||
* <li>Scrub MateClaw branding from system text — replace
|
||||
* {@code "MateClaw"} → {@code "Claude Code"} and a few common variants —
|
||||
* so server-side content filters don't fire on the spoofed identity.</li>
|
||||
* <li><b>System prompt prefix</b>: prepend
|
||||
* {@code "You are Claude Code, Anthropic's official CLI for Claude."}.
|
||||
* Insert a new SystemMessage if none exists.</li>
|
||||
* <li><b>Brand scrub</b>: replace {@code "MateClaw"}/{@code "mateclaw"}
|
||||
* in system text with their Claude Code equivalents — Anthropic's
|
||||
* content filter flags identity contradictions.</li>
|
||||
* <li><b>Tool {@code mcp_} prefix (outgoing)</b>: every tool definition
|
||||
* sent to Anthropic is renamed {@code mcp_<orig>} — Claude Code
|
||||
* runs all tools through MCP servers, so real Claude Code traffic
|
||||
* always has the prefix. Mismatch trips anti-abuse.</li>
|
||||
* <li><b>History tool_use prefix</b>: previously-issued tool calls in
|
||||
* AssistantMessage history get the prefix re-applied (we strip on
|
||||
* response, so they're stored unprefixed).</li>
|
||||
* <li><b>Tool {@code mcp_} prefix (incoming)</b>: ChatResponse tool_use
|
||||
* names are stripped of the {@code mcp_} prefix so MateClaw's tool
|
||||
* registry can resolve them.</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p><b>Tool-name {@code mcp_} prefix</b> (hermes lines 1593-1607) is a
|
||||
* separate concern: it requires bidirectional rewriting (out + back) and
|
||||
* touches Spring AI's tool-callback layer. Deferred to a follow-up — current
|
||||
* symptom is rate-limit on memory analysis (which uses no tools).
|
||||
*/
|
||||
@Slf4j
|
||||
public class ClaudeCodeIdentityChatModelDecorator implements ChatModel {
|
||||
@ -53,6 +70,9 @@ public class ClaudeCodeIdentityChatModelDecorator implements ChatModel {
|
||||
static final String CLAUDE_CODE_SYSTEM_PREFIX =
|
||||
"You are Claude Code, Anthropic's official CLI for Claude.";
|
||||
|
||||
/** Tool-name prefix Claude Code uses for all MCP-routed tools. */
|
||||
static final String MCP_TOOL_PREFIX = "mcp_";
|
||||
|
||||
private final ChatModel delegate;
|
||||
|
||||
public ClaudeCodeIdentityChatModelDecorator(ChatModel delegate) {
|
||||
@ -61,12 +81,12 @@ public class ClaudeCodeIdentityChatModelDecorator implements ChatModel {
|
||||
|
||||
@Override
|
||||
public ChatResponse call(Prompt prompt) {
|
||||
return delegate.call(transform(prompt));
|
||||
return stripToolPrefixes(delegate.call(transform(prompt)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ChatResponse> stream(Prompt prompt) {
|
||||
return delegate.stream(transform(prompt));
|
||||
return delegate.stream(transform(prompt)).map(this::stripToolPrefixes);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -74,6 +94,10 @@ public class ClaudeCodeIdentityChatModelDecorator implements ChatModel {
|
||||
return delegate.getDefaultOptions();
|
||||
}
|
||||
|
||||
/* ====================================================================== */
|
||||
/* Outbound transform: Prompt → Prompt with identity + tool prefix */
|
||||
/* ====================================================================== */
|
||||
|
||||
/**
|
||||
* Build a new {@link Prompt} with the OAuth identity transforms applied.
|
||||
* Package-private for unit tests.
|
||||
@ -88,40 +112,145 @@ public class ClaudeCodeIdentityChatModelDecorator implements ChatModel {
|
||||
boolean systemSeen = false;
|
||||
for (Message msg : source) {
|
||||
if (msg instanceof SystemMessage sm && !systemSeen) {
|
||||
// Only mutate the FIRST system message — multiple system messages
|
||||
// are rare in practice but preserve the second-onward verbatim.
|
||||
rewritten.add(new SystemMessage(prependIdentity(sanitizeBranding(sm.getText()))));
|
||||
systemSeen = true;
|
||||
} else if (msg instanceof AssistantMessage am && am.hasToolCalls()) {
|
||||
// Re-prefix tool_use names in history. We strip on response, so
|
||||
// by the time MateClaw stores the AssistantMessage the names
|
||||
// are unprefixed — must put the prefix back when echoing the
|
||||
// history to Anthropic for it to match its own prior turn.
|
||||
rewritten.add(rebuildAssistantMessage(am, true));
|
||||
} else {
|
||||
rewritten.add(msg);
|
||||
}
|
||||
}
|
||||
if (!systemSeen) {
|
||||
// No system message at all → insert one with just the identity prefix.
|
||||
rewritten.add(0, new SystemMessage(CLAUDE_CODE_SYSTEM_PREFIX));
|
||||
}
|
||||
return new Prompt(rewritten, original.getOptions());
|
||||
|
||||
ChatOptions transformedOptions = transformOptions(original.getOptions());
|
||||
return new Prompt(rewritten, transformedOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap each tool callback in the options so its {@code getToolDefinition().name()}
|
||||
* returns {@code mcp_<orig>}. Spring AI sends those names verbatim to Anthropic.
|
||||
* Other tool fields (description, schema) untouched. Returns {@code null} for
|
||||
* non-Anthropic options so we don't accidentally drop them on a custom subclass.
|
||||
*/
|
||||
private ChatOptions transformOptions(ChatOptions options) {
|
||||
if (!(options instanceof AnthropicChatOptions anthropicOpts)) {
|
||||
return options;
|
||||
}
|
||||
List<ToolCallback> originalCallbacks = anthropicOpts.getToolCallbacks();
|
||||
Set<String> originalToolNames = anthropicOpts.getToolNames();
|
||||
|
||||
boolean hasCallbacks = originalCallbacks != null && !originalCallbacks.isEmpty();
|
||||
boolean hasToolNames = originalToolNames != null && !originalToolNames.isEmpty();
|
||||
if (!hasCallbacks && !hasToolNames) {
|
||||
return options;
|
||||
}
|
||||
|
||||
AnthropicChatOptions copy = AnthropicChatOptions.fromOptions(anthropicOpts);
|
||||
if (hasCallbacks) {
|
||||
List<ToolCallback> wrapped = new ArrayList<>(originalCallbacks.size());
|
||||
for (ToolCallback cb : originalCallbacks) {
|
||||
wrapped.add(cb instanceof PrefixedToolCallback ? cb : new PrefixedToolCallback(cb));
|
||||
}
|
||||
copy.setToolCallbacks(wrapped);
|
||||
}
|
||||
if (hasToolNames) {
|
||||
// toolNames is a set used by Spring AI's tool resolver to filter from
|
||||
// a wider registry. If MateClaw populates it (most paths use callbacks
|
||||
// directly so this is rare), prefix the names so they line up with
|
||||
// the wrapped callbacks above.
|
||||
Set<String> prefixed = new LinkedHashSet<>(originalToolNames.size());
|
||||
for (String n : originalToolNames) {
|
||||
prefixed.add(n.startsWith(MCP_TOOL_PREFIX) ? n : MCP_TOOL_PREFIX + n);
|
||||
}
|
||||
copy.setToolNames(prefixed);
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
/* ====================================================================== */
|
||||
/* Inbound transform: ChatResponse → strip tool prefix */
|
||||
/* ====================================================================== */
|
||||
|
||||
ChatResponse stripToolPrefixes(ChatResponse response) {
|
||||
if (response == null) {
|
||||
return null;
|
||||
}
|
||||
List<Generation> origGens = response.getResults();
|
||||
if (origGens == null || origGens.isEmpty()) {
|
||||
return response;
|
||||
}
|
||||
List<Generation> rewritten = null;
|
||||
for (int i = 0; i < origGens.size(); i++) {
|
||||
Generation g = origGens.get(i);
|
||||
AssistantMessage am = g.getOutput();
|
||||
if (am == null || !am.hasToolCalls()) continue;
|
||||
boolean changed = false;
|
||||
for (AssistantMessage.ToolCall tc : am.getToolCalls()) {
|
||||
if (tc.name() != null && tc.name().startsWith(MCP_TOOL_PREFIX)) {
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!changed) continue;
|
||||
|
||||
if (rewritten == null) {
|
||||
rewritten = new ArrayList<>(origGens);
|
||||
}
|
||||
AssistantMessage stripped = rebuildAssistantMessage(am, false);
|
||||
ChatGenerationMetadata meta = g.getMetadata();
|
||||
rewritten.set(i, new Generation(stripped, meta));
|
||||
}
|
||||
if (rewritten == null) {
|
||||
return response; // no tool_use blocks needed rewriting
|
||||
}
|
||||
return new ChatResponse(rewritten, response.getMetadata());
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild an AssistantMessage with tool_call names prefixed (when
|
||||
* {@code prefix=true}) or stripped (when {@code prefix=false}).
|
||||
*/
|
||||
private AssistantMessage rebuildAssistantMessage(AssistantMessage original, boolean prefix) {
|
||||
List<AssistantMessage.ToolCall> rebuilt = new ArrayList<>(original.getToolCalls().size());
|
||||
for (AssistantMessage.ToolCall tc : original.getToolCalls()) {
|
||||
String name = tc.name();
|
||||
String newName;
|
||||
if (prefix) {
|
||||
newName = (name == null || name.startsWith(MCP_TOOL_PREFIX)) ? name : MCP_TOOL_PREFIX + name;
|
||||
} else {
|
||||
newName = (name != null && name.startsWith(MCP_TOOL_PREFIX))
|
||||
? name.substring(MCP_TOOL_PREFIX.length()) : name;
|
||||
}
|
||||
rebuilt.add(new AssistantMessage.ToolCall(tc.id(), tc.type(), newName, tc.arguments()));
|
||||
}
|
||||
return AssistantMessage.builder()
|
||||
.content(original.getText())
|
||||
.properties(original.getMetadata())
|
||||
.toolCalls(rebuilt)
|
||||
.media(original.getMedia())
|
||||
.build();
|
||||
}
|
||||
|
||||
/* ====================================================================== */
|
||||
/* String helpers (system prompt + branding) */
|
||||
/* ====================================================================== */
|
||||
|
||||
private static String prependIdentity(String existingSystem) {
|
||||
if (existingSystem == null || existingSystem.isBlank()) {
|
||||
return CLAUDE_CODE_SYSTEM_PREFIX;
|
||||
}
|
||||
if (existingSystem.startsWith(CLAUDE_CODE_SYSTEM_PREFIX)) {
|
||||
// Already prefixed (defensive — protects against double-wrapping
|
||||
// if a caller invokes the decorator twice).
|
||||
return existingSystem;
|
||||
}
|
||||
return CLAUDE_CODE_SYSTEM_PREFIX + "\n\n" + existingSystem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace MateClaw / agent-specific branding tokens with their Claude Code
|
||||
* equivalents. Same idea as hermes-agent's
|
||||
* {@code text.replace("Hermes Agent", "Claude Code")} chain — Anthropic's
|
||||
* server-side filter flags requests where the spoofed identity contradicts
|
||||
* itself ("You are Claude Code … built by MateClaw").
|
||||
*/
|
||||
static String sanitizeBranding(String text) {
|
||||
if (text == null || text.isEmpty()) {
|
||||
return text;
|
||||
@ -131,4 +260,57 @@ public class ClaudeCodeIdentityChatModelDecorator implements ChatModel {
|
||||
.replace("mateclaw", "claude-code")
|
||||
.replace("Mate Claw", "Claude Code");
|
||||
}
|
||||
|
||||
/* ====================================================================== */
|
||||
/* PrefixedToolCallback — wraps a ToolCallback to expose the mcp_ name */
|
||||
/* ====================================================================== */
|
||||
|
||||
/**
|
||||
* Wraps a {@link ToolCallback} so its {@code getToolDefinition().name()}
|
||||
* returns {@code mcp_<orig>}, while {@code call(...)} forwards verbatim
|
||||
* to the underlying tool. Anthropic sees the prefixed name on the wire;
|
||||
* MateClaw's tool implementation never sees the prefix.
|
||||
*/
|
||||
static final class PrefixedToolCallback implements ToolCallback {
|
||||
|
||||
private final ToolCallback delegate;
|
||||
private final ToolDefinition prefixedDefinition;
|
||||
|
||||
PrefixedToolCallback(ToolCallback delegate) {
|
||||
this.delegate = delegate;
|
||||
ToolDefinition orig = delegate.getToolDefinition();
|
||||
String origName = orig.name();
|
||||
String prefixed = (origName != null && origName.startsWith(MCP_TOOL_PREFIX))
|
||||
? origName : MCP_TOOL_PREFIX + origName;
|
||||
this.prefixedDefinition = DefaultToolDefinition.builder()
|
||||
.name(prefixed)
|
||||
.description(orig.description())
|
||||
.inputSchema(orig.inputSchema())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ToolDefinition getToolDefinition() {
|
||||
return prefixedDefinition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ToolMetadata getToolMetadata() {
|
||||
return delegate.getToolMetadata();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String call(String input) {
|
||||
return delegate.call(input);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String call(String input, ToolContext context) {
|
||||
return delegate.call(input, context);
|
||||
}
|
||||
|
||||
ToolCallback unwrap() {
|
||||
return delegate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -264,12 +264,13 @@ MERGE INTO mate_model_config (id, name, provider, model_name, description, tempe
|
||||
-- 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),
|
||||
-- Anthropic only released Opus 4.7 — Sonnet stays at 4.6 until further notice.
|
||||
(1000000271, 'Claude Sonnet 4.6', 'anthropic', 'claude-sonnet-4-6', 'Anthropic Claude Sonnet 4.6 (latest Sonnet — 4.7 not yet released)', 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),
|
||||
(1000000273, 'Claude Sonnet 4.6', 'openrouter', 'anthropic/claude-sonnet-4-6', 'Claude Sonnet 4.6 via OpenRouter', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
|
||||
-- RFC-062: Claude 4.7 via Claude Code OAuth subscription (Pro/Max plan).
|
||||
(1000000280, 'Claude Opus 4.7', 'anthropic-claude-code', 'claude-opus-4-7', 'Claude Opus 4.7 via Claude Code Pro/Max subscription', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
|
||||
(1000000281, 'Claude Sonnet 4.7', 'anthropic-claude-code', 'claude-sonnet-4-7', 'Claude Sonnet 4.7 via Claude Code Pro/Max subscription', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0);
|
||||
(1000000281, 'Claude Sonnet 4.6', 'anthropic-claude-code', 'claude-sonnet-4-6', 'Claude Sonnet 4.6 via Claude Code Pro/Max subscription', 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)
|
||||
|
||||
@ -302,12 +302,13 @@ VALUES
|
||||
-- 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),
|
||||
-- Anthropic only released Opus 4.7 — Sonnet stays at 4.6 until further notice.
|
||||
(1000000271, 'Claude Sonnet 4.6', 'anthropic', 'claude-sonnet-4-6', 'Anthropic Claude Sonnet 4.6 (latest Sonnet — 4.7 not yet released)', 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),
|
||||
(1000000273, 'Claude Sonnet 4.6', 'openrouter', 'anthropic/claude-sonnet-4-6', 'Claude Sonnet 4.6 via OpenRouter', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
|
||||
-- RFC-062: Claude 4.7 via Claude Code OAuth subscription (Pro/Max plan).
|
||||
(1000000280, 'Claude Opus 4.7', 'anthropic-claude-code', 'claude-opus-4-7', 'Claude Opus 4.7 via Claude Code Pro/Max subscription', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
|
||||
(1000000281, 'Claude Sonnet 4.7', 'anthropic-claude-code', 'claude-sonnet-4-7', 'Claude Sonnet 4.7 via Claude Code Pro/Max subscription', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0)
|
||||
(1000000281, 'Claude Sonnet 4.6', 'anthropic-claude-code', 'claude-sonnet-4-6', 'Claude Sonnet 4.6 via Claude Code Pro/Max subscription', 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
|
||||
|
||||
@ -302,12 +302,13 @@ VALUES
|
||||
-- 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.7(xhigh 自适应思考)', 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),
|
||||
-- Anthropic 仅发布了 Opus 4.7,Sonnet 暂时仍是 4.6
|
||||
(1000000271, 'Claude Sonnet 4.6', 'anthropic', 'claude-sonnet-4-6', 'Anthropic 最新 Sonnet (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),
|
||||
(1000000273, 'Claude Sonnet 4.6', 'openrouter', 'anthropic/claude-sonnet-4-6', 'OpenRouter 代理 Claude Sonnet 4.6', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
|
||||
-- RFC-062:通过 Claude Code Pro/Max 订阅调用 Claude 4.7
|
||||
(1000000280, 'Claude Opus 4.7', 'anthropic-claude-code', 'claude-opus-4-7', '通过 Claude Code Pro/Max 订阅调用 Claude Opus 4.7', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
|
||||
(1000000281, 'Claude Sonnet 4.7', 'anthropic-claude-code', 'claude-sonnet-4-7', '通过 Claude Code Pro/Max 订阅调用 Claude Sonnet 4.7', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0)
|
||||
(1000000281, 'Claude Sonnet 4.6', 'anthropic-claude-code', 'claude-sonnet-4-6', '通过 Claude Code Pro/Max 订阅调用 Claude Sonnet 4.6', 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);
|
||||
|
||||
-- 默认系统设置
|
||||
|
||||
@ -268,12 +268,13 @@ MERGE INTO mate_model_config (id, name, provider, model_name, description, tempe
|
||||
-- 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.7(xhigh 自适应思考)', 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),
|
||||
-- Anthropic 仅发布了 Opus 4.7,Sonnet 暂时仍是 4.6
|
||||
(1000000271, 'Claude Sonnet 4.6', 'anthropic', 'claude-sonnet-4-6', 'Anthropic 最新 Sonnet (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),
|
||||
(1000000273, 'Claude Sonnet 4.6', 'openrouter', 'anthropic/claude-sonnet-4-6', 'OpenRouter 代理 Claude Sonnet 4.6', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
|
||||
-- RFC-062:通过 Claude Code Pro/Max 订阅调用 Claude 4.7
|
||||
(1000000280, 'Claude Opus 4.7', 'anthropic-claude-code', 'claude-opus-4-7', '通过 Claude Code Pro/Max 订阅调用 Claude Opus 4.7', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0),
|
||||
(1000000281, 'Claude Sonnet 4.7', 'anthropic-claude-code', 'claude-sonnet-4-7', '通过 Claude Code Pro/Max 订阅调用 Claude Sonnet 4.7', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0);
|
||||
(1000000281, 'Claude Sonnet 4.6', 'anthropic-claude-code', 'claude-sonnet-4-6', '通过 Claude Code Pro/Max 订阅调用 Claude Sonnet 4.6', NULL, 4096, NULL, TRUE, TRUE, FALSE, NOW(), NOW(), 0);
|
||||
|
||||
-- 默认系统设置
|
||||
MERGE INTO mate_system_setting (id, setting_key, setting_value, description, create_time, update_time)
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
-- Repair migration for V42/V43: Anthropic only released Opus 4.7 — there
|
||||
-- is no claude-sonnet-4-7 model. Calls return HTTP 404 with body
|
||||
-- {"type":"not_found_error","message":"model: claude-sonnet-4-7"}.
|
||||
--
|
||||
-- Reference: hermes-agent anthropic_adapter.py _ANTHROPIC_OUTPUT_LIMITS
|
||||
-- (lines 65-93) lists claude-opus-4-7 but no claude-sonnet-4-7. The latest
|
||||
-- released Sonnet remains claude-sonnet-4-6 (released alongside Opus 4.6).
|
||||
--
|
||||
-- Strategy: rename in place — preserve ids 1000000271, 1000000273, 1000000281
|
||||
-- so user-customised settings (default flag, enabled flag) survive.
|
||||
-- When/if Anthropic ships Sonnet 4.7, a future migration can switch back.
|
||||
|
||||
UPDATE mate_model_config
|
||||
SET name = 'Claude Sonnet 4.6',
|
||||
model_name = 'claude-sonnet-4-6',
|
||||
description = 'Anthropic Claude Sonnet 4.6 (latest Sonnet — 4.7 not yet released)',
|
||||
update_time = NOW()
|
||||
WHERE id = 1000000271 AND model_name = 'claude-sonnet-4-7';
|
||||
|
||||
UPDATE mate_model_config
|
||||
SET name = 'Claude Sonnet 4.6',
|
||||
model_name = 'anthropic/claude-sonnet-4-6',
|
||||
description = 'Claude Sonnet 4.6 via OpenRouter',
|
||||
update_time = NOW()
|
||||
WHERE id = 1000000273 AND model_name = 'anthropic/claude-sonnet-4-7';
|
||||
|
||||
UPDATE mate_model_config
|
||||
SET name = 'Claude Sonnet 4.6',
|
||||
model_name = 'claude-sonnet-4-6',
|
||||
description = 'Claude Sonnet 4.6 via Claude Code Pro/Max subscription',
|
||||
update_time = NOW()
|
||||
WHERE id = 1000000281 AND model_name = 'claude-sonnet-4-7';
|
||||
@ -0,0 +1,31 @@
|
||||
-- Repair migration for V42/V43: Anthropic only released Opus 4.7 — there
|
||||
-- is no claude-sonnet-4-7 model. Calls return HTTP 404 with body
|
||||
-- {"type":"not_found_error","message":"model: claude-sonnet-4-7"}.
|
||||
--
|
||||
-- Reference: hermes-agent anthropic_adapter.py _ANTHROPIC_OUTPUT_LIMITS
|
||||
-- (lines 65-93) lists claude-opus-4-7 but no claude-sonnet-4-7. The latest
|
||||
-- released Sonnet remains claude-sonnet-4-6 (released alongside Opus 4.6).
|
||||
--
|
||||
-- Strategy: rename in place — preserve ids 1000000271, 1000000273, 1000000281
|
||||
-- so user-customised settings (default flag, enabled flag) survive.
|
||||
|
||||
UPDATE mate_model_config
|
||||
SET name = 'Claude Sonnet 4.6',
|
||||
model_name = 'claude-sonnet-4-6',
|
||||
description = 'Anthropic Claude Sonnet 4.6 (latest Sonnet — 4.7 not yet released)',
|
||||
update_time = NOW()
|
||||
WHERE id = 1000000271 AND model_name = 'claude-sonnet-4-7';
|
||||
|
||||
UPDATE mate_model_config
|
||||
SET name = 'Claude Sonnet 4.6',
|
||||
model_name = 'anthropic/claude-sonnet-4-6',
|
||||
description = 'Claude Sonnet 4.6 via OpenRouter',
|
||||
update_time = NOW()
|
||||
WHERE id = 1000000273 AND model_name = 'anthropic/claude-sonnet-4-7';
|
||||
|
||||
UPDATE mate_model_config
|
||||
SET name = 'Claude Sonnet 4.6',
|
||||
model_name = 'claude-sonnet-4-6',
|
||||
description = 'Claude Sonnet 4.6 via Claude Code Pro/Max subscription',
|
||||
update_time = NOW()
|
||||
WHERE id = 1000000281 AND model_name = 'claude-sonnet-4-7';
|
||||
Loading…
Reference in New Issue
Block a user