maxpool 1.10.0 → 1.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/server.js +49 -3
package/package.json
CHANGED
package/src/server.js
CHANGED
|
@@ -1657,7 +1657,7 @@ function isContextLengthError(errorBody) {
|
|
|
1657
1657
|
return /exceeded model token limit|maximum context length|context length exceeded|context window (?:size )?(?:exceeded|too)|prompt is too long|input is too long|reduce the length of|too many (?:input )?tokens|request too large/i.test(errorBody);
|
|
1658
1658
|
}
|
|
1659
1659
|
|
|
1660
|
-
export const __serverTest = { unavailableMessage, computeQueueWindowMs, isRetriableUpstreamStatus, classifyEffortRejection, repairEffort, isCapacitySignalStatus, isStrippableThinkingBlock, stripForeignThinkingBlocks, parseRejectedBlockPath, stripRejectedBlockClass, peekRejectedBlockType, describeRejectedBlock, headerValue, getMaxpoolProfile, ensureQueueHeartbeat, clearQueueHeartbeat, commitStreamGraceHeartbeat, describeRequest, classifyRateLimit, detectTranscriptOrigin, isAnthropicIncompatBody, isContextLengthError, streamResponse, startIdleRequestReaper };
|
|
1660
|
+
export const __serverTest = { reanchorOrphanedSystemMessages, unavailableMessage, computeQueueWindowMs, isRetriableUpstreamStatus, classifyEffortRejection, repairEffort, isCapacitySignalStatus, isStrippableThinkingBlock, stripForeignThinkingBlocks, parseRejectedBlockPath, stripRejectedBlockClass, peekRejectedBlockType, describeRejectedBlock, headerValue, getMaxpoolProfile, ensureQueueHeartbeat, clearQueueHeartbeat, commitStreamGraceHeartbeat, describeRequest, classifyRateLimit, detectTranscriptOrigin, isAnthropicIncompatBody, isContextLengthError, streamResponse, startIdleRequestReaper };
|
|
1661
1661
|
|
|
1662
1662
|
async function readErrorBody(upstreamRes, limitBytes = 64 * 1024) {
|
|
1663
1663
|
if (!upstreamRes.body) return '';
|
|
@@ -1905,6 +1905,52 @@ function describeRejectedBlock(body, errorBody) {
|
|
|
1905
1905
|
}
|
|
1906
1906
|
}
|
|
1907
1907
|
|
|
1908
|
+
/** Anthropic (2026-08) accepts a mid-array `system` message under one positional rule,
|
|
1909
|
+
* stated verbatim in its own 400:
|
|
1910
|
+
* "role 'system' must precede an 'assistant' message or end the array; the
|
|
1911
|
+
* directive-only form (content: [] with output_config) is accepted at any position"
|
|
1912
|
+
*
|
|
1913
|
+
* Both transcript repairs DROP a turn whose content strips empty. When that turn is the
|
|
1914
|
+
* assistant anchoring a preceding system, the system is orphaned and the NEXT request
|
|
1915
|
+
* 400s — and because the repair LATCHES the session (markSessionThinkingContaminated →
|
|
1916
|
+
* the pre-strip at retryCount===0), the orphaning recurs on every later turn. The
|
|
1917
|
+
* ordering 400 is not an isSignatureRejection, so it never reaches the recovery branches
|
|
1918
|
+
* or the friendly give-up message: it surfaces raw and the session is bricked. Measured
|
|
1919
|
+
* 2026-08-24: "stripped 21 provider thinking block(s)" at 06:12:13.278Z → that exact 400
|
|
1920
|
+
* at 06:12:13.835Z, 4 occurrences in one day.
|
|
1921
|
+
*
|
|
1922
|
+
* RE-ANCHOR, never drop the system. A system message carries load-bearing directives;
|
|
1923
|
+
* deleting one silently changes the user's session with no signal — a worse defect than
|
|
1924
|
+
* the loud 400. The placeholder reuses the `(content removed)` string this file already
|
|
1925
|
+
* emits for the messages[0] guard, so it is an established shape here, not a new one.
|
|
1926
|
+
*
|
|
1927
|
+
* Runs as a POST-PASS over the rebuilt array so it fires only on real violations:
|
|
1928
|
+
* - a system that ENDS the array is legal ("or end the array") — untouched
|
|
1929
|
+
* - a system already followed by an assistant is legal — untouched
|
|
1930
|
+
* - the directive-only form (content: [] + output_config) is legal ANYWHERE — untouched
|
|
1931
|
+
* Idempotent: a second run finds no violation, so the latched re-strip cannot grow the
|
|
1932
|
+
* transcript turn after turn.
|
|
1933
|
+
*/
|
|
1934
|
+
function reanchorOrphanedSystemMessages(messages) {
|
|
1935
|
+
let inserted = 0;
|
|
1936
|
+
const out = [];
|
|
1937
|
+
for (let i = 0; i < messages.length; i++) {
|
|
1938
|
+
const msg = messages[i];
|
|
1939
|
+
out.push(msg);
|
|
1940
|
+
if (msg?.role !== 'system') continue;
|
|
1941
|
+
const next = messages[i + 1];
|
|
1942
|
+
if (next === undefined) continue; // ends the array — legal
|
|
1943
|
+
if (next?.role === 'assistant') continue; // already anchored — legal
|
|
1944
|
+
// The directive-only form is accepted at any position; re-anchoring it would mutate
|
|
1945
|
+
// a transcript the API already accepts.
|
|
1946
|
+
if (Array.isArray(msg.content) && msg.content.length === 0) continue;
|
|
1947
|
+
out.push({ role: 'assistant', content: [{ type: 'text', text: '(content removed)' }] });
|
|
1948
|
+
inserted++;
|
|
1949
|
+
}
|
|
1950
|
+
return { messages: out, inserted };
|
|
1951
|
+
}
|
|
1952
|
+
|
|
1953
|
+
|
|
1908
1954
|
/**
|
|
1909
1955
|
* Last-resort repair driven by the upstream's own coordinate, for a rejected block no
|
|
1910
1956
|
* shape heuristic here recognised. Removes every block sharing the rejected block's
|
|
@@ -1951,7 +1997,7 @@ function stripRejectedBlockClass(body, errorBody) {
|
|
|
1951
1997
|
messages.push({ ...msg, content: kept });
|
|
1952
1998
|
}
|
|
1953
1999
|
if (!removed) return { body: null, removed: 0, type };
|
|
1954
|
-
json.messages = messages;
|
|
2000
|
+
json.messages = reanchorOrphanedSystemMessages(messages).messages;
|
|
1955
2001
|
return { body: Buffer.from(JSON.stringify(json)), removed, type };
|
|
1956
2002
|
} catch {
|
|
1957
2003
|
return { body: null, removed: 0, type: null };
|
|
@@ -2129,7 +2175,7 @@ function stripForeignThinkingBlocks(body) {
|
|
|
2129
2175
|
messages.push({ ...msg, content: kept });
|
|
2130
2176
|
}
|
|
2131
2177
|
if (!removed && !converted) return { body: null, removed: 0, converted: 0 };
|
|
2132
|
-
json.messages = messages;
|
|
2178
|
+
json.messages = reanchorOrphanedSystemMessages(messages).messages;
|
|
2133
2179
|
return { body: Buffer.from(JSON.stringify(json)), removed, converted };
|
|
2134
2180
|
} catch {
|
|
2135
2181
|
return { body: null, removed: 0, converted: 0 }; // non-JSON / unparseable → no rewrite
|