mirror of
https://github.com/musistudio/claude-code-router.git
synced 2026-07-09 17:18:24 +00:00
Sort subagent model descriptions deterministically
This commit is contained in:
parent
2cbaaded8f
commit
1adfbbf006
2 changed files with 107 additions and 8 deletions
|
|
@ -421,8 +421,7 @@ function claudeCodeAgentToolInstructions(config: AppConfig): { prompt: string; t
|
|||
}
|
||||
|
||||
function configuredSubagentModelDescriptionRows(config: AppConfig): string[] {
|
||||
const rows: string[] = [];
|
||||
const seen = new Set<string>();
|
||||
const candidates: Array<{ key: string; row: string; selector: string }> = [];
|
||||
for (const provider of config.Providers) {
|
||||
const providerName = provider.name?.trim();
|
||||
if (!providerName || !Array.isArray(provider.models)) {
|
||||
|
|
@ -436,18 +435,42 @@ function configuredSubagentModelDescriptionRows(config: AppConfig): string[] {
|
|||
}
|
||||
const selector = `${providerName}/${model}`;
|
||||
const key = selector.toLowerCase();
|
||||
if (seen.has(key)) {
|
||||
continue;
|
||||
}
|
||||
seen.add(key);
|
||||
const displayName = provider.modelDisplayNames?.[model]?.trim();
|
||||
const label = displayName && displayName !== model ? `${selector} (${displayName})` : selector;
|
||||
rows.push(`- ${label}: ${singleLineText(description, 320)}`);
|
||||
candidates.push({
|
||||
key,
|
||||
row: `- ${label}: ${singleLineText(description, 320)}`,
|
||||
selector
|
||||
});
|
||||
}
|
||||
}
|
||||
candidates.sort(compareSubagentModelDescriptionRows);
|
||||
|
||||
const rows: string[] = [];
|
||||
const seen = new Set<string>();
|
||||
for (const candidate of candidates) {
|
||||
if (seen.has(candidate.key)) {
|
||||
continue;
|
||||
}
|
||||
seen.add(candidate.key);
|
||||
rows.push(candidate.row);
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
function compareSubagentModelDescriptionRows(
|
||||
left: { key: string; row: string; selector: string },
|
||||
right: { key: string; row: string; selector: string }
|
||||
): number {
|
||||
return compareCodeUnitStrings(left.key, right.key) ||
|
||||
compareCodeUnitStrings(left.selector, right.selector) ||
|
||||
compareCodeUnitStrings(left.row, right.row);
|
||||
}
|
||||
|
||||
function compareCodeUnitStrings(left: string, right: string): number {
|
||||
return left < right ? -1 : left > right ? 1 : 0;
|
||||
}
|
||||
|
||||
function removeClaudeCodeBillingSystemHeader(body: Record<string, unknown>): void {
|
||||
const system = body.system;
|
||||
if (!Array.isArray(system) || system.length === 0) {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ function createRouterPlugin(options = {}) {
|
|||
const agent = options.agent ?? "claude-code";
|
||||
return new ClaudeCodeRouterPlugin({
|
||||
CUSTOM_ROUTER_PATH: "",
|
||||
Providers: [
|
||||
Providers: options.providers ?? [
|
||||
{
|
||||
modelDescriptions: options.modelDescriptions,
|
||||
modelDisplayNames: options.modelDisplayNames,
|
||||
|
|
@ -158,6 +158,82 @@ test("built-in Claude Code route injects subagent model instructions into Agent
|
|||
}
|
||||
});
|
||||
|
||||
test("built-in Claude Code route injects subagent model descriptions in stable order", async () => {
|
||||
const providersA = [
|
||||
{
|
||||
modelDescriptions: {
|
||||
"z-model": "Zeta model.",
|
||||
"a-model": "Alpha model."
|
||||
},
|
||||
models: ["z-model", "a-model"],
|
||||
name: "Zeta",
|
||||
type: "anthropic_messages"
|
||||
},
|
||||
{
|
||||
modelDescriptions: {
|
||||
"z-model": "Z model.",
|
||||
"a-model": "A model."
|
||||
},
|
||||
models: ["z-model", "a-model"],
|
||||
name: "Alpha",
|
||||
type: "anthropic_messages"
|
||||
}
|
||||
];
|
||||
const providersB = [
|
||||
{
|
||||
modelDescriptions: {
|
||||
"a-model": "A model.",
|
||||
"z-model": "Z model."
|
||||
},
|
||||
models: ["a-model", "z-model"],
|
||||
name: "Alpha",
|
||||
type: "anthropic_messages"
|
||||
},
|
||||
{
|
||||
modelDescriptions: {
|
||||
"a-model": "Alpha model.",
|
||||
"z-model": "Zeta model."
|
||||
},
|
||||
models: ["a-model", "z-model"],
|
||||
name: "Zeta",
|
||||
type: "anthropic_messages"
|
||||
}
|
||||
];
|
||||
const routeRequest = (plugin) => plugin.routeRequest({
|
||||
body: {
|
||||
messages: [],
|
||||
model: "claude-default",
|
||||
tools: [
|
||||
{
|
||||
description: "Start a subagent.",
|
||||
input_schema: {
|
||||
properties: {
|
||||
prompt: { description: "Task prompt.", type: "string" }
|
||||
},
|
||||
type: "object"
|
||||
},
|
||||
name: "Agent"
|
||||
}
|
||||
]
|
||||
},
|
||||
headers: {
|
||||
"user-agent": "Claude Code"
|
||||
},
|
||||
method: "POST",
|
||||
url: "/v1/messages"
|
||||
});
|
||||
|
||||
const pluginA = createRouterPlugin({ profileModel: "Alpha/a-model", providers: providersA });
|
||||
const pluginB = createRouterPlugin({ profileModel: "Alpha/a-model", providers: providersB });
|
||||
const [resultA, resultB] = await Promise.all([routeRequest(pluginA), routeRequest(pluginB)]);
|
||||
const description = resultA.body.tools[0].description;
|
||||
|
||||
assert.equal(description, resultB.body.tools[0].description);
|
||||
assert.ok(description.indexOf("- Alpha/a-model: A model.") < description.indexOf("- Alpha/z-model: Z model."));
|
||||
assert.ok(description.indexOf("- Alpha/z-model: Z model.") < description.indexOf("- Zeta/a-model: Alpha model."));
|
||||
assert.ok(description.indexOf("- Zeta/a-model: Alpha model.") < description.indexOf("- Zeta/z-model: Zeta model."));
|
||||
});
|
||||
|
||||
test("built-in Claude Code route injects workflow subagent model instructions into the Workflow tool", async () => {
|
||||
const plugin = createRouterPlugin({
|
||||
modelDescriptions: {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue