fix(provider/nous): inject required user tag into inference requests (#11861) (#12044)

Injeta a tag de usuário obrigatória nas requisições de inferência do provider Nous, com teste próprio ampliado. Validado no worktree combinado. Obrigado!
This commit is contained in:
Karan
2026-08-30 19:39:44 +05:30
committed by GitHub
parent 51e4930d05
commit ff4ac6c4d5
4 changed files with 93 additions and 6 deletions

View File

@@ -0,0 +1 @@
- **fix(provider/nous):** inject required user= tag into Nous Research inference requests to resolve upstream 400 "missing tags" error ([#11861](https://github.com/diegosouzapw/OmniRoute/issues/11861)) — thanks @Karan825

View File

@@ -639,8 +639,7 @@ export class DefaultExecutor extends BaseExecutor {
const record = body as Record<string, unknown>;
const rf = record.response_format as
| { type?: string; json_schema?: { schema?: unknown } }
| undefined;
{ type?: string; json_schema?: { schema?: unknown } } | undefined;
if (!rf) return body;
// openai-compatible-* providers accept json_object natively — only the
@@ -748,6 +747,38 @@ export class DefaultExecutor extends BaseExecutor {
delete withoutClientMetadata.client_metadata;
withDefaults = withoutClientMetadata;
}
// Nous Research inference gateway (portal.nousresearch.com) requires a top-level
// `tags` array containing at least a `user=` item on raw API-key requests (#11861).
// Without `tags`, upstream returns 400 "missing tags".
// Without `user=...`, upstream returns 400 "missing user tag".
if (
this.provider === "nous-research" &&
withDefaults &&
typeof withDefaults === "object" &&
!Array.isArray(withDefaults)
) {
const record = withDefaults as Record<string, unknown>;
const extraBody = record.extra_body as Record<string, unknown> | undefined;
const rawTags = Array.isArray(record.tags)
? (record.tags as unknown[])
: Array.isArray(extraBody?.tags)
? (extraBody.tags as unknown[])
: [];
const stringTags = rawTags.filter(
(t): t is string => typeof t === "string" && t.trim().length > 0
);
const hasUserTag = stringTags.some((t) => t.startsWith("user="));
if (!hasUserTag) {
const username =
typeof record.user === "string" && record.user.trim() ? record.user.trim() : "omniroute";
record.tags = [...stringTags, `user=${username}`];
} else {
record.tags = stringTags;
}
}
// 9router#1649: Mistral's API returns 422 (extra_forbidden) when an
// assistant message carries a `reasoning_content` field (replayed thinking

View File

@@ -631,6 +631,7 @@ export async function validateNousResearchProvider({ apiKey, providerSpecificDat
model: modelId,
messages: [{ role: "user", content: "test" }],
max_tokens: 1,
tags: ["user=omniroute"],
}),
});

View File

@@ -29,8 +29,62 @@ test("nous-research DefaultExecutor.buildUrl() targets the correct inference end
const url = executor.buildUrl("Hermes-4-70B", false, 0, null);
assert.equal(
url,
"https://inference-api.nousresearch.com/v1/chat/completions"
);
assert.equal(url, "https://inference-api.nousresearch.com/v1/chat/completions");
});
test("nous-research DefaultExecutor.transformRequest injects user=omniroute tag when tags is absent (#11861)", () => {
const executor = new DefaultExecutor("nous-research");
const transformed = executor.transformRequest(
"Hermes-4-70B",
{ model: "Hermes-4-70B", messages: [{ role: "user", content: "hello" }] },
false,
null
) as Record<string, unknown>;
assert.ok(Array.isArray(transformed.tags), "Expected tags array on nous-research body");
assert.deepEqual(transformed.tags, ["user=omniroute"]);
});
test("nous-research DefaultExecutor.transformRequest respects client-sent user in tags (#11861)", () => {
const executor = new DefaultExecutor("nous-research");
const transformed = executor.transformRequest(
"Hermes-4-70B",
{ model: "Hermes-4-70B", user: "dev-user", messages: [{ role: "user", content: "hello" }] },
false,
null
) as Record<string, unknown>;
assert.deepEqual(transformed.tags, ["user=dev-user"]);
});
test("nous-research DefaultExecutor.transformRequest preserves existing tags and appends user tag (#11861)", () => {
const executor = new DefaultExecutor("nous-research");
const transformed = executor.transformRequest(
"Hermes-4-70B",
{
model: "Hermes-4-70B",
tags: ["client=agent"],
messages: [{ role: "user", content: "hello" }],
},
false,
null
) as Record<string, unknown>;
assert.deepEqual(transformed.tags, ["client=agent", "user=omniroute"]);
});
test("nous-research DefaultExecutor.transformRequest does not duplicate user tag if already present (#11861)", () => {
const executor = new DefaultExecutor("nous-research");
const transformed = executor.transformRequest(
"Hermes-4-70B",
{
model: "Hermes-4-70B",
tags: ["user=custom-tag"],
messages: [{ role: "user", content: "hello" }],
},
false,
null
) as Record<string, unknown>;
assert.deepEqual(transformed.tags, ["user=custom-tag"]);
});