mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-03 22:02:08 +03:00
refactor(open-sse): extract safeParseJSON util, dedup tryParseJSON (#4735)
Integrated into release/v3.8.36
This commit is contained in:
committed by
GitHub
parent
6e28889aad
commit
4a1ae868ce
@@ -1,5 +1,7 @@
|
||||
// Gemini helper functions for translator
|
||||
|
||||
import { safeParseJSON } from "./jsonUtil.ts";
|
||||
|
||||
type JsonRecord = Record<string, unknown>;
|
||||
|
||||
// Unsupported JSON Schema constraints that should be removed for Antigravity.
|
||||
@@ -242,14 +244,9 @@ export function extractTextContent(content: unknown): string {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Try parse JSON safely
|
||||
// Try parse JSON safely (null fallback on parse error; re-export keeps legacy API).
|
||||
export function tryParseJSON(str: unknown): unknown {
|
||||
if (typeof str !== "string") return str;
|
||||
try {
|
||||
return JSON.parse(str);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
return safeParseJSON(str, null);
|
||||
}
|
||||
|
||||
// Generate request ID
|
||||
|
||||
18
open-sse/translator/helpers/jsonUtil.ts
Normal file
18
open-sse/translator/helpers/jsonUtil.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
// Safe JSON.parse helper shared by the translator layer.
|
||||
//
|
||||
// Behavior contract (must be preserved by all callers):
|
||||
// - non-string input is returned unchanged (passthrough)
|
||||
// - a valid JSON string is parsed and returned
|
||||
// - on parse error the caller-chosen `fallback` is returned
|
||||
//
|
||||
// The `fallback` is explicit so the two historical `tryParseJSON` variants
|
||||
// can keep their distinct semantics: geminiHelper returns `null` on error,
|
||||
// while openai-to-claude returns the raw input string (passthrough).
|
||||
export function safeParseJSON<TFallback>(str: unknown, fallback: TFallback): unknown {
|
||||
if (typeof str !== "string") return str;
|
||||
try {
|
||||
return JSON.parse(str);
|
||||
} catch {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import { FORMATS } from "../formats.ts";
|
||||
import { supportsClaudeMaxEffort, supportsXHighEffort } from "../../config/providerModels.ts";
|
||||
import { adjustMaxTokens } from "../helpers/maxTokensHelper.ts";
|
||||
import { sanitizeToolId } from "../helpers/schemaCoercion.ts";
|
||||
import { safeParseJSON } from "../helpers/jsonUtil.ts";
|
||||
import { DEFAULT_THINKING_CLAUDE_SIGNATURE } from "../../config/defaultThinkingSignature.ts";
|
||||
import { capMaxOutputTokens } from "../../../src/lib/modelCapabilities.ts";
|
||||
import { isAdaptiveThinkingOnly } from "../../../src/shared/constants/modelSpecs.ts";
|
||||
@@ -701,14 +702,9 @@ function extractTextContent(content) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// Try parse JSON
|
||||
function tryParseJSON(str) {
|
||||
if (typeof str !== "string") return str;
|
||||
try {
|
||||
return JSON.parse(str);
|
||||
} catch {
|
||||
return str;
|
||||
}
|
||||
// Try parse JSON (passthrough fallback: return the raw input string on parse error).
|
||||
function tryParseJSON(str: unknown): unknown {
|
||||
return safeParseJSON(str, str);
|
||||
}
|
||||
|
||||
function stripCacheControl(value: unknown): unknown {
|
||||
|
||||
45
tests/unit/translator-json-util.test.ts
Normal file
45
tests/unit/translator-json-util.test.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
// Unit coverage for the extracted safeParseJSON util and the two distinct
|
||||
// fallbacks its callers rely on:
|
||||
// - geminiHelper.tryParseJSON -> null fallback on parse error
|
||||
// - openai-to-claude tryParseJSON -> raw-string passthrough on parse error
|
||||
const { safeParseJSON } = await import("../../open-sse/translator/helpers/jsonUtil.ts");
|
||||
const gemini = await import("../../open-sse/translator/helpers/geminiHelper.ts");
|
||||
|
||||
test("safeParseJSON: non-string input is returned unchanged (passthrough)", () => {
|
||||
assert.equal(safeParseJSON(42, null), 42);
|
||||
assert.equal(safeParseJSON(null, "fb"), null);
|
||||
assert.equal(safeParseJSON(undefined, "fb"), undefined);
|
||||
const obj = { a: 1 };
|
||||
assert.equal(safeParseJSON(obj, null), obj);
|
||||
});
|
||||
|
||||
test("safeParseJSON: valid JSON strings parse regardless of fallback", () => {
|
||||
assert.deepEqual(safeParseJSON('{"a":1}', null), { a: 1 });
|
||||
assert.deepEqual(safeParseJSON("[1,2,3]", "ignored"), [1, 2, 3]);
|
||||
assert.equal(safeParseJSON("42", null), 42);
|
||||
assert.equal(safeParseJSON("true", null), true);
|
||||
assert.equal(safeParseJSON("null", "ignored"), null);
|
||||
});
|
||||
|
||||
test("safeParseJSON: invalid JSON returns the caller-chosen fallback", () => {
|
||||
// null fallback (geminiHelper semantics)
|
||||
assert.equal(safeParseJSON("not json", null), null);
|
||||
assert.equal(safeParseJSON("{broken}", null), null);
|
||||
assert.equal(safeParseJSON("", null), null);
|
||||
|
||||
// raw-string passthrough fallback (openai-to-claude semantics)
|
||||
assert.equal(safeParseJSON("not json", "not json"), "not json");
|
||||
assert.equal(safeParseJSON("{broken", "{broken"), "{broken");
|
||||
});
|
||||
|
||||
test("geminiHelper.tryParseJSON still returns null on parse error (re-export delegates)", () => {
|
||||
assert.deepEqual(gemini.tryParseJSON('{"ok":true}'), { ok: true });
|
||||
assert.equal(gemini.tryParseJSON("{broken"), null);
|
||||
assert.equal(gemini.tryParseJSON("not json"), null);
|
||||
assert.equal(gemini.tryParseJSON(""), null);
|
||||
// non-string passthrough preserved
|
||||
assert.equal(gemini.tryParseJSON(123), 123);
|
||||
});
|
||||
Reference in New Issue
Block a user