mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-07-24 12:44:18 +08:00
Update Longcat presets to LongCat-2.0 (#4838)
* Update Longcat presets to LongCat-2.0 * fix(proxy): classify LongCat-2.0 as text-only for media sanitizer The Longcat presets now use LongCat-2.0, but the known_text_only_model allowlist still only matched the retired longcat-flash-chat tail. Without this, images pasted into a text-only LongCat-2.0 session are forwarded upstream instead of being replaced with the unsupported-image marker, causing a hard rejection. Add longcat-2.0 (keeping the retired name for saved configs) and a regression test. --------- Co-authored-by: chengzifeng <chengzifeng@meituan.com> Co-authored-by: Jason <farion1231@gmail.com>
This commit is contained in:
@@ -2610,7 +2610,7 @@ web_search = "disabled"
|
||||
for (model, host) in [
|
||||
("mimo-v2.5-pro", "https://api.xiaomimimo.com/v1"),
|
||||
("mimo-v2.5", "https://token-plan-cn.xiaomimimo.com/v1"),
|
||||
("LongCat-2.0-Preview", "https://api.longcat.chat/openai/v1"),
|
||||
("LongCat-2.0", "https://api.longcat.chat/openai/v1"),
|
||||
("MiniMax-M3", "https://api.minimax.io/v1"),
|
||||
("MiniMax-M3", "https://api.minimaxi.com/v1"),
|
||||
] {
|
||||
|
||||
@@ -257,6 +257,7 @@ fn known_text_only_model(model: &str) -> bool {
|
||||
"kat-coder-pro-v1",
|
||||
"kat-coder-pro-v2",
|
||||
"ling-2.5-1t",
|
||||
"longcat-2.0",
|
||||
"longcat-flash-chat",
|
||||
"mimo-v2.5-pro",
|
||||
"us.deepseek.r1-v1",
|
||||
@@ -484,6 +485,15 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn longcat_models_are_classified_text_only() {
|
||||
// LongCat-2.0 (like the retired Flash Chat) is a text-only model; the
|
||||
// preset ships it in mixed case, so the classifier must normalize first.
|
||||
assert!(known_text_only_model("LongCat-2.0"));
|
||||
assert!(known_text_only_model("longcat/LongCat-2.0"));
|
||||
assert!(known_text_only_model("LongCat-Flash-Chat"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn explicit_text_modalities_replace_images_before_send() {
|
||||
let provider = provider(json!({
|
||||
|
||||
@@ -577,11 +577,7 @@ export const claudeDesktopProviderPresets: ClaudeDesktopProviderPreset[] = [
|
||||
baseUrl: "https://api.longcat.chat/anthropic",
|
||||
mode: "proxy",
|
||||
apiFormat: "anthropic",
|
||||
modelRoutes: brandedRoutes(
|
||||
"LongCat-Flash-Chat",
|
||||
"LongCat-Flash-Chat",
|
||||
"LongCat-Flash-Chat",
|
||||
),
|
||||
modelRoutes: brandedRoutes("LongCat-2.0", "LongCat-2.0", "LongCat-2.0"),
|
||||
icon: "longcat",
|
||||
iconColor: "#29E154",
|
||||
},
|
||||
|
||||
@@ -587,11 +587,12 @@ export const providerPresets: ProviderPreset[] = [
|
||||
env: {
|
||||
ANTHROPIC_BASE_URL: "https://api.longcat.chat/anthropic",
|
||||
ANTHROPIC_AUTH_TOKEN: "",
|
||||
ANTHROPIC_MODEL: "LongCat-Flash-Chat",
|
||||
ANTHROPIC_DEFAULT_HAIKU_MODEL: "LongCat-Flash-Chat",
|
||||
ANTHROPIC_DEFAULT_SONNET_MODEL: "LongCat-Flash-Chat",
|
||||
ANTHROPIC_DEFAULT_OPUS_MODEL: "LongCat-Flash-Chat",
|
||||
CLAUDE_CODE_MAX_OUTPUT_TOKENS: "6000",
|
||||
ANTHROPIC_MODEL: "LongCat-2.0",
|
||||
ANTHROPIC_SMALL_FAST_MODEL: "LongCat-2.0",
|
||||
ANTHROPIC_DEFAULT_HAIKU_MODEL: "LongCat-2.0",
|
||||
ANTHROPIC_DEFAULT_SONNET_MODEL: "LongCat-2.0",
|
||||
ANTHROPIC_DEFAULT_OPUS_MODEL: "LongCat-2.0",
|
||||
CLAUDE_CODE_MAX_OUTPUT_TOKENS: "131072",
|
||||
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: 1,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -723,7 +723,7 @@ requires_openai_auth = true`,
|
||||
config: generateThirdPartyConfig(
|
||||
"longcat",
|
||||
"https://api.longcat.chat/openai/v1",
|
||||
"LongCat-2.0-Preview",
|
||||
"LongCat-2.0",
|
||||
),
|
||||
endpointCandidates: ["https://api.longcat.chat/openai/v1"],
|
||||
// 美团 LongCat 官方 Codex 文档用 wire_api=responses 对自家 base_url,原生 Responses,无需路由接管转换
|
||||
@@ -732,8 +732,8 @@ requires_openai_auth = true`,
|
||||
// 注:LongCat 的 /responses 工具类型契约文档化程度最低,建议真机冒烟一次
|
||||
modelCatalog: modelCatalog([
|
||||
{
|
||||
model: "LongCat-2.0-Preview",
|
||||
displayName: "LongCat 2.0 Preview",
|
||||
model: "LongCat-2.0",
|
||||
displayName: "LongCat 2.0",
|
||||
contextWindow: 1048576,
|
||||
},
|
||||
]),
|
||||
|
||||
@@ -779,13 +779,13 @@ export const hermesProviderPresets: HermesProviderPreset[] = [
|
||||
base_url: "https://api.longcat.chat/openai/v1",
|
||||
api_key: "",
|
||||
api_mode: "chat_completions",
|
||||
models: [{ id: "LongCat-Flash-Chat", name: "LongCat Flash Chat" }],
|
||||
models: [{ id: "LongCat-2.0", name: "LongCat 2.0" }],
|
||||
},
|
||||
category: "cn_official",
|
||||
icon: "longcat",
|
||||
iconColor: "#29E154",
|
||||
suggestedDefaults: {
|
||||
model: { default: "LongCat-Flash-Chat", provider: "longcat" },
|
||||
model: { default: "LongCat-2.0", provider: "longcat" },
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1068,15 +1068,19 @@ export const openclawProviderPresets: OpenClawProviderPreset[] = [
|
||||
websiteUrl: "https://longcat.chat/platform",
|
||||
apiKeyUrl: "https://longcat.chat/platform/api_keys",
|
||||
settingsConfig: {
|
||||
baseUrl: "https://api.longcat.chat/v1",
|
||||
baseUrl: "https://api.longcat.chat/openai/v1",
|
||||
apiKey: "",
|
||||
api: "openai-completions",
|
||||
authHeader: true,
|
||||
models: [
|
||||
{
|
||||
id: "LongCat-Flash-Chat",
|
||||
name: "LongCat Flash Chat",
|
||||
contextWindow: 128000,
|
||||
id: "LongCat-2.0",
|
||||
name: "LongCat 2.0",
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
contextWindow: 1048576,
|
||||
maxTokens: 131072,
|
||||
compat: { maxTokensField: "max_tokens" },
|
||||
cost: { input: 0.001, output: 0.004 },
|
||||
},
|
||||
],
|
||||
@@ -1087,8 +1091,8 @@ export const openclawProviderPresets: OpenClawProviderPreset[] = [
|
||||
templateValues: {
|
||||
baseUrl: {
|
||||
label: "Base URL",
|
||||
placeholder: "https://api.longcat.chat/v1",
|
||||
defaultValue: "https://api.longcat.chat/v1",
|
||||
placeholder: "https://api.longcat.chat/openai/v1",
|
||||
defaultValue: "https://api.longcat.chat/openai/v1",
|
||||
editorValue: "",
|
||||
},
|
||||
apiKey: {
|
||||
@@ -1098,8 +1102,8 @@ export const openclawProviderPresets: OpenClawProviderPreset[] = [
|
||||
},
|
||||
},
|
||||
suggestedDefaults: {
|
||||
model: { primary: "longcat/LongCat-Flash-Chat" },
|
||||
modelCatalog: { "longcat/LongCat-Flash-Chat": { alias: "LongCat" } },
|
||||
model: { primary: "longcat/LongCat-2.0" },
|
||||
modelCatalog: { "longcat/LongCat-2.0": { alias: "LongCat" } },
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1054,12 +1054,15 @@ export const opencodeProviderPresets: OpenCodeProviderPreset[] = [
|
||||
npm: "@ai-sdk/openai-compatible",
|
||||
name: "Longcat",
|
||||
options: {
|
||||
baseURL: "https://api.longcat.chat/v1",
|
||||
baseURL: "https://api.longcat.chat/openai/v1",
|
||||
apiKey: "",
|
||||
setCacheKey: true,
|
||||
},
|
||||
models: {
|
||||
"LongCat-Flash-Chat": { name: "LongCat Flash Chat" },
|
||||
"LongCat-2.0": {
|
||||
name: "LongCat 2.0",
|
||||
options: { thinking: { type: "disabled" } },
|
||||
},
|
||||
},
|
||||
},
|
||||
category: "cn_official",
|
||||
@@ -1068,8 +1071,8 @@ export const opencodeProviderPresets: OpenCodeProviderPreset[] = [
|
||||
templateValues: {
|
||||
baseURL: {
|
||||
label: "Base URL",
|
||||
placeholder: "https://api.longcat.chat/v1",
|
||||
defaultValue: "https://api.longcat.chat/v1",
|
||||
placeholder: "https://api.longcat.chat/openai/v1",
|
||||
defaultValue: "https://api.longcat.chat/openai/v1",
|
||||
editorValue: "",
|
||||
},
|
||||
apiKey: {
|
||||
|
||||
@@ -645,6 +645,9 @@ export interface OpenClawModel {
|
||||
};
|
||||
contextWindow?: number;
|
||||
maxTokens?: number; // 最大输出 token 数
|
||||
compat?: {
|
||||
maxTokensField?: string; // 最大输出 token 请求字段名(如 "max_tokens")
|
||||
};
|
||||
}
|
||||
|
||||
// OpenClaw 默认模型配置(agents.defaults.model)
|
||||
|
||||
@@ -163,7 +163,7 @@ describe("Codex Chat provider presets", () => {
|
||||
{ contextWindows: { "doubao-seed-2-1-pro-260628": 262144 } },
|
||||
],
|
||||
["Bailian", { contextWindows: { "qwen3-coder-plus": 1048576 } }],
|
||||
["Longcat", { contextWindows: { "LongCat-2.0-Preview": 1048576 } }],
|
||||
["Longcat", { contextWindows: { "LongCat-2.0": 1048576 } }],
|
||||
["MiniMax", { contextWindows: { "MiniMax-M3": 1000000 } }],
|
||||
["MiniMax en", { contextWindows: { "MiniMax-M3": 1000000 } }],
|
||||
[
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { claudeDesktopProviderPresets } from "@/config/claudeDesktopProviderPresets";
|
||||
import { providerPresets } from "@/config/claudeProviderPresets";
|
||||
import { codexProviderPresets } from "@/config/codexProviderPresets";
|
||||
import { hermesProviderPresets } from "@/config/hermesProviderPresets";
|
||||
import { openclawProviderPresets } from "@/config/openclawProviderPresets";
|
||||
import { opencodeProviderPresets } from "@/config/opencodeProviderPresets";
|
||||
|
||||
const LONGCAT_MODEL = "LongCat-2.0";
|
||||
const LONGCAT_DISPLAY_NAME = "LongCat 2.0";
|
||||
const LONGCAT_OPENAI_BASE_URL = "https://api.longcat.chat/openai/v1";
|
||||
const LONGCAT_BRAND = "LongCat";
|
||||
const FLASH_VARIANT = "Flash";
|
||||
const CHAT_SUFFIX = "Chat";
|
||||
const REMOVED_LONGCAT_NAMES = [
|
||||
`${LONGCAT_BRAND}-${FLASH_VARIANT}-${CHAT_SUFFIX}`,
|
||||
`${LONGCAT_BRAND} ${FLASH_VARIANT} ${CHAT_SUFFIX}`,
|
||||
`${LONGCAT_MODEL}-Preview`,
|
||||
`${LONGCAT_DISPLAY_NAME} Preview`,
|
||||
];
|
||||
|
||||
function findLongcatPreset<T extends { name: string }>(presets: T[]): T {
|
||||
const preset = presets.find((item) => item.name === "Longcat");
|
||||
|
||||
expect(preset).toBeDefined();
|
||||
return preset!;
|
||||
}
|
||||
|
||||
describe("Longcat provider presets", () => {
|
||||
it("uses the official LongCat 2.0 model for Claude Code", () => {
|
||||
const preset = findLongcatPreset(providerPresets);
|
||||
const env = (preset.settingsConfig as { env: Record<string, unknown> }).env;
|
||||
|
||||
expect(env).toMatchObject({
|
||||
ANTHROPIC_MODEL: LONGCAT_MODEL,
|
||||
ANTHROPIC_SMALL_FAST_MODEL: LONGCAT_MODEL,
|
||||
ANTHROPIC_DEFAULT_HAIKU_MODEL: LONGCAT_MODEL,
|
||||
ANTHROPIC_DEFAULT_SONNET_MODEL: LONGCAT_MODEL,
|
||||
ANTHROPIC_DEFAULT_OPUS_MODEL: LONGCAT_MODEL,
|
||||
CLAUDE_CODE_MAX_OUTPUT_TOKENS: "131072",
|
||||
});
|
||||
});
|
||||
|
||||
it("uses the official LongCat 2.0 model for Claude Desktop routes", () => {
|
||||
const preset = findLongcatPreset(claudeDesktopProviderPresets);
|
||||
|
||||
expect(
|
||||
preset.modelRoutes?.map((route) => ({
|
||||
upstreamModel: route.upstreamModel,
|
||||
labelOverride: route.labelOverride,
|
||||
})),
|
||||
).toEqual([
|
||||
{
|
||||
upstreamModel: LONGCAT_MODEL,
|
||||
labelOverride: LONGCAT_MODEL,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("uses the official LongCat 2.0 model for Hermes", () => {
|
||||
const preset = findLongcatPreset(hermesProviderPresets);
|
||||
|
||||
expect(preset.settingsConfig.models).toEqual([
|
||||
{ id: LONGCAT_MODEL, name: LONGCAT_DISPLAY_NAME },
|
||||
]);
|
||||
expect(preset.suggestedDefaults?.model).toEqual({
|
||||
default: LONGCAT_MODEL,
|
||||
provider: "longcat",
|
||||
});
|
||||
});
|
||||
|
||||
it("uses the official LongCat 2.0 model for OpenCode", () => {
|
||||
const preset = findLongcatPreset(opencodeProviderPresets);
|
||||
|
||||
expect(preset.settingsConfig.options?.baseURL).toBe(
|
||||
LONGCAT_OPENAI_BASE_URL,
|
||||
);
|
||||
expect(preset.templateValues?.baseURL.defaultValue).toBe(
|
||||
LONGCAT_OPENAI_BASE_URL,
|
||||
);
|
||||
expect(preset.templateValues?.baseURL.placeholder).toBe(
|
||||
LONGCAT_OPENAI_BASE_URL,
|
||||
);
|
||||
expect(preset.settingsConfig.models).toEqual({
|
||||
[LONGCAT_MODEL]: {
|
||||
name: LONGCAT_DISPLAY_NAME,
|
||||
options: { thinking: { type: "disabled" } },
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("uses the official LongCat 2.0 model for OpenClaw", () => {
|
||||
const preset = findLongcatPreset(openclawProviderPresets);
|
||||
|
||||
expect(preset.settingsConfig.baseUrl).toBe(LONGCAT_OPENAI_BASE_URL);
|
||||
expect(preset.templateValues?.baseUrl.defaultValue).toBe(
|
||||
LONGCAT_OPENAI_BASE_URL,
|
||||
);
|
||||
expect(preset.templateValues?.baseUrl.placeholder).toBe(
|
||||
LONGCAT_OPENAI_BASE_URL,
|
||||
);
|
||||
expect(preset.settingsConfig.models).toEqual([
|
||||
expect.objectContaining({
|
||||
id: LONGCAT_MODEL,
|
||||
name: LONGCAT_DISPLAY_NAME,
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
contextWindow: 1048576,
|
||||
maxTokens: 131072,
|
||||
compat: { maxTokensField: "max_tokens" },
|
||||
}),
|
||||
]);
|
||||
expect(preset.suggestedDefaults?.model).toEqual({
|
||||
primary: `longcat/${LONGCAT_MODEL}`,
|
||||
});
|
||||
expect(preset.suggestedDefaults?.modelCatalog).toEqual({
|
||||
[`longcat/${LONGCAT_MODEL}`]: { alias: "LongCat" },
|
||||
});
|
||||
});
|
||||
|
||||
it("uses the official LongCat 2.0 model for Codex", () => {
|
||||
const preset = findLongcatPreset(codexProviderPresets);
|
||||
|
||||
expect(preset.config).toContain(`model = "${LONGCAT_MODEL}"`);
|
||||
expect(preset.modelCatalog).toEqual([
|
||||
expect.objectContaining({
|
||||
model: LONGCAT_MODEL,
|
||||
displayName: LONGCAT_DISPLAY_NAME,
|
||||
contextWindow: 1048576,
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
it("does not keep retired or preview Longcat model names", () => {
|
||||
const longcatPresets = [
|
||||
findLongcatPreset(providerPresets),
|
||||
findLongcatPreset(claudeDesktopProviderPresets),
|
||||
findLongcatPreset(hermesProviderPresets),
|
||||
findLongcatPreset(opencodeProviderPresets),
|
||||
findLongcatPreset(openclawProviderPresets),
|
||||
findLongcatPreset(codexProviderPresets),
|
||||
];
|
||||
const serializedPresets = JSON.stringify(longcatPresets);
|
||||
|
||||
for (const removedName of REMOVED_LONGCAT_NAMES) {
|
||||
expect(serializedPresets).not.toContain(removedName);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user