mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-10 14:54:57 +00:00
Agents: preserve unsafe integer tool args in Ollama stream
This commit is contained in:
@@ -49,6 +49,130 @@ interface OllamaToolCall {
|
||||
};
|
||||
}
|
||||
|
||||
const MAX_SAFE_INTEGER_ABS_STR = String(Number.MAX_SAFE_INTEGER);
|
||||
|
||||
function isAsciiDigit(ch: string | undefined): boolean {
|
||||
return ch !== undefined && ch >= "0" && ch <= "9";
|
||||
}
|
||||
|
||||
function parseJsonNumberToken(
|
||||
input: string,
|
||||
start: number,
|
||||
): { token: string; end: number; isInteger: boolean } | null {
|
||||
let idx = start;
|
||||
if (input[idx] === "-") {
|
||||
idx += 1;
|
||||
}
|
||||
if (idx >= input.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (input[idx] === "0") {
|
||||
idx += 1;
|
||||
} else if (isAsciiDigit(input[idx]) && input[idx] !== "0") {
|
||||
while (isAsciiDigit(input[idx])) {
|
||||
idx += 1;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
let isInteger = true;
|
||||
if (input[idx] === ".") {
|
||||
isInteger = false;
|
||||
idx += 1;
|
||||
if (!isAsciiDigit(input[idx])) {
|
||||
return null;
|
||||
}
|
||||
while (isAsciiDigit(input[idx])) {
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (input[idx] === "e" || input[idx] === "E") {
|
||||
isInteger = false;
|
||||
idx += 1;
|
||||
if (input[idx] === "+" || input[idx] === "-") {
|
||||
idx += 1;
|
||||
}
|
||||
if (!isAsciiDigit(input[idx])) {
|
||||
return null;
|
||||
}
|
||||
while (isAsciiDigit(input[idx])) {
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
token: input.slice(start, idx),
|
||||
end: idx,
|
||||
isInteger,
|
||||
};
|
||||
}
|
||||
|
||||
function isUnsafeIntegerLiteral(token: string): boolean {
|
||||
const digits = token[0] === "-" ? token.slice(1) : token;
|
||||
if (digits.length < MAX_SAFE_INTEGER_ABS_STR.length) {
|
||||
return false;
|
||||
}
|
||||
if (digits.length > MAX_SAFE_INTEGER_ABS_STR.length) {
|
||||
return true;
|
||||
}
|
||||
return digits > MAX_SAFE_INTEGER_ABS_STR;
|
||||
}
|
||||
|
||||
function quoteUnsafeIntegerLiterals(input: string): string {
|
||||
let out = "";
|
||||
let inString = false;
|
||||
let escaped = false;
|
||||
let idx = 0;
|
||||
|
||||
while (idx < input.length) {
|
||||
const ch = input[idx] ?? "";
|
||||
if (inString) {
|
||||
out += ch;
|
||||
if (escaped) {
|
||||
escaped = false;
|
||||
} else if (ch === "\\") {
|
||||
escaped = true;
|
||||
} else if (ch === '"') {
|
||||
inString = false;
|
||||
}
|
||||
idx += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === '"') {
|
||||
inString = true;
|
||||
out += ch;
|
||||
idx += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === "-" || isAsciiDigit(ch)) {
|
||||
const parsed = parseJsonNumberToken(input, idx);
|
||||
if (parsed) {
|
||||
if (parsed.isInteger && isUnsafeIntegerLiteral(parsed.token)) {
|
||||
out += `"${parsed.token}"`;
|
||||
} else {
|
||||
out += parsed.token;
|
||||
}
|
||||
idx = parsed.end;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
out += ch;
|
||||
idx += 1;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
function parseJsonPreservingUnsafeIntegers(input: string): unknown {
|
||||
return JSON.parse(quoteUnsafeIntegerLiterals(input)) as unknown;
|
||||
}
|
||||
|
||||
// ── Ollama /api/chat response types ─────────────────────────────────────────
|
||||
|
||||
interface OllamaChatResponse {
|
||||
@@ -262,7 +386,7 @@ export async function* parseNdjsonStream(
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
yield JSON.parse(trimmed) as OllamaChatResponse;
|
||||
yield parseJsonPreservingUnsafeIntegers(trimmed) as OllamaChatResponse;
|
||||
} catch {
|
||||
log.warn(`Skipping malformed NDJSON line: ${trimmed.slice(0, 120)}`);
|
||||
}
|
||||
@@ -271,7 +395,7 @@ export async function* parseNdjsonStream(
|
||||
|
||||
if (buffer.trim()) {
|
||||
try {
|
||||
yield JSON.parse(buffer.trim()) as OllamaChatResponse;
|
||||
yield parseJsonPreservingUnsafeIntegers(buffer.trim()) as OllamaChatResponse;
|
||||
} catch {
|
||||
log.warn(`Skipping malformed trailing data: ${buffer.trim().slice(0, 120)}`);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user