fix: truncate tool list when provider limit exceeds MAX_TOOLS_LIMIT (grok-cli 200)

- Add proactive PROVIDER_TOOL_LIMITS map with grok-cli: 200
- Fix regex to capture 'maximum is 200' (not '427 tools provided')
- Remove broken truncation gate that skipped limits >= MAX_TOOLS_LIMIT (128)
- Add tests for Grok regex, proactive limits, and limits above threshold

Refs #5563
This commit is contained in:
Choti Wongbussakorn
2026-06-30 14:55:38 +07:00
committed by Diego Rodrigues de Sa e Souza
parent 470220cee7
commit e8dbd276fe
3 changed files with 41 additions and 9 deletions

View File

@@ -16,7 +16,6 @@ import {
} from "../../services/payloadRules.ts";
import { getEffectiveToolLimit } from "../../services/toolLimitDetector.ts";
import { providerSupportsCaching } from "../../utils/cacheControlPolicy.ts";
import { MAX_TOOLS_LIMIT } from "../../config/constants.ts";
import { FORMATS } from "../../translator/formats.ts";
type LoggerLike = { debug?: (...args: unknown[]) => void } | null | undefined;
@@ -39,13 +38,13 @@ function buildAppliedRulesSummary(
.join(", ");
}
function truncateToolList(bodyToSend: Body, provider: string | null | undefined, log?: LoggerLike): Body {
function truncateToolList(
bodyToSend: Body,
provider: string | null | undefined,
log?: LoggerLike
): Body {
const effectiveToolLimit = getEffectiveToolLimit(provider);
if (
effectiveToolLimit < MAX_TOOLS_LIMIT &&
Array.isArray(bodyToSend.tools) &&
bodyToSend.tools.length > effectiveToolLimit
) {
if (Array.isArray(bodyToSend.tools) && bodyToSend.tools.length > effectiveToolLimit) {
const truncatedTools = bodyToSend.tools.slice(0, effectiveToolLimit);
bodyToSend = { ...bodyToSend, tools: truncatedTools };
log?.debug?.(
@@ -64,8 +63,7 @@ function backfillQwenOAuthUser(
credentials: CredentialsLike,
log?: LoggerLike
): Body {
const hasValidQwenUser =
typeof bodyToSend.user === "string" && bodyToSend.user.trim().length > 0;
const hasValidQwenUser = typeof bodyToSend.user === "string" && bodyToSend.user.trim().length > 0;
const isQwenOAuthRequest =
provider === "qwen" &&
!credentials?.apiKey &&

View File

@@ -4,6 +4,10 @@ const DETECTED_LIMITS = new Map<string, { limit: number; timestamp: number }>();
const TTL_MS = 24 * 60 * 60 * 1000;
const DEFAULT_LIMIT = MAX_TOOLS_LIMIT;
const PROVIDER_TOOL_LIMITS: Record<string, number> = {
"grok-cli": 200,
};
const _detectedLimitsSweep = setInterval(() => {
const now = Date.now();
for (const [key, entry] of DETECTED_LIMITS) {
@@ -15,6 +19,10 @@ if (typeof _detectedLimitsSweep === "object" && "unref" in _detectedLimitsSweep)
}
export function getEffectiveToolLimit(provider: string): number {
const proactiveLimit = PROVIDER_TOOL_LIMITS[provider];
if (proactiveLimit !== undefined) {
return proactiveLimit;
}
const cached = DETECTED_LIMITS.get(provider);
if (cached && Date.now() - cached.timestamp < TTL_MS) {
return cached.limit;
@@ -33,6 +41,7 @@ const TOOL_LIMIT_PATTERNS = [
/'tools':\s*maximum\s+number\s+of\s+items\s+is\s+(\d+)/i,
/Maximum\s+number\s+of\s+tools\s+(?:allowed\s+)?(?:is\s+)?(\d+)/i,
/Too\s+many\s+tools\.?\s*(?:Maximum\s+)?(\d+)/i,
/\d+\s+tools\s+have\s+been\s+provided\s+but\s+(?:the\s+)?maximum\s+is\s+(\d+)/i,
/tool.*limit.*(\d+)/i,
/tools.*exceeded.*(\d+)/i,
];

View File

@@ -48,6 +48,31 @@ describe("toolLimitDetector", () => {
assert.strictEqual(result, null);
});
it("should parse Grok-style error capturing the maximum (200), not the provided count (427)", () => {
const result = parseToolLimitFromError(
"Maximum tools limit reached. 427 tools have been provided but the maximum is 200."
);
assert.strictEqual(result, 200);
});
it("should parse Grok-style error without 'the' before maximum", () => {
const result = parseToolLimitFromError("427 tools have been provided but maximum is 150");
assert.strictEqual(result, 150);
});
it("should return proactive limit for grok-cli (200) without any detection", () => {
assert.strictEqual(getEffectiveToolLimit("grok-cli"), 200);
});
it("should not override proactive limit with setDetectedToolLimit", () => {
setDetectedToolLimit("grok-cli", 150);
assert.strictEqual(getEffectiveToolLimit("grok-cli"), 200);
});
it("should still return default (128) for unknown providers", () => {
assert.strictEqual(getEffectiveToolLimit("some-new-provider"), 128);
});
it("should detect tool limit errors for 400 status", () => {
assert.strictEqual(shouldDetectLimit("Maximum number of tools is 128", 400), true);
assert.strictEqual(shouldDetectLimit("Too many tools provided", 400), true);