universal-llm-client 4.1.0 → 4.2.0
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/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/structured-output.d.ts +24 -1
- package/dist/structured-output.d.ts.map +1 -1
- package/dist/structured-output.js +58 -5
- package/dist/structured-output.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/structured-output.ts +63 -6
- package/src/tests/structured-output.test.ts +110 -0
package/dist/index.d.ts
CHANGED
|
@@ -13,5 +13,5 @@ export { type StreamDecoder, type DecodedEvent, type DecoderCallback, type Decod
|
|
|
13
13
|
export { ToolBuilder, ToolExecutor, createTimeTool, createRandomNumberTool, } from './tools.js';
|
|
14
14
|
export { httpRequest, httpStream, parseNDJSON, parseSSE, buildHeaders, type HttpRequestOptions, type HttpResponse, } from './http.js';
|
|
15
15
|
export { MCPToolBridge, type MCPBridgeConfig, type MCPServerConfig, type MCPTool, } from './mcp.js';
|
|
16
|
-
export { StructuredOutputError, type StructuredOutputErrorOptions, type StructuredOutputOptions, type StructuredOutputResult, type StructuredOutputSuccess, type StructuredOutputFailure, type JSONSchema, type SchemaProvider, type ProviderSchema, type SchemaConfig, isStructuredOutputSuccess, isStructuredOutputFailure, normalizeJsonSchema, convertToProviderSchema, stripUnsupportedFeatures, getJsonSchema, getJsonSchemaFromConfig, parseStructured, tryParseStructured, validateStructuredOutput, StreamingJsonParser, type StreamingStructuredResult, } from './structured-output.js';
|
|
16
|
+
export { StructuredOutputError, type StructuredOutputErrorOptions, type StructuredOutputOptions, type StructuredOutputResult, type StructuredOutputSuccess, type StructuredOutputFailure, type JSONSchema, type SchemaProvider, type ProviderSchema, type SchemaConfig, isStructuredOutputSuccess, isStructuredOutputFailure, normalizeJsonSchema, convertToProviderSchema, stripUnsupportedFeatures, getJsonSchema, getJsonSchemaFromConfig, parseStructured, tryParseStructured, validateStructuredOutput, stripJsonFences, StreamingJsonParser, type StreamingStructuredResult, } from './structured-output.js';
|
|
17
17
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAMxC,OAAO,EAEH,cAAc,EACd,WAAW,EAEX,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EAErB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,eAAe,EAEpB,KAAK,eAAe,EACpB,KAAK,cAAc,EAEnB,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EAEtB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,aAAa,EAElB,KAAK,aAAa,EAElB,WAAW,EACX,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,SAAS,EACT,YAAY,EACZ,QAAQ,GACX,MAAM,iBAAiB,CAAC;AAMzB,OAAO,EACH,KAAK,OAAO,EACZ,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,WAAW,EACX,cAAc,EACd,eAAe,GAClB,MAAM,cAAc,CAAC;AAMtB,OAAO,EACH,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,aAAa,EACb,eAAe,EACf,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,2BAA2B,GAC9B,MAAM,qBAAqB,CAAC;AAM7B,OAAO,EACH,WAAW,EACX,YAAY,EACZ,cAAc,EACd,sBAAsB,GACzB,MAAM,YAAY,CAAC;AAMpB,OAAO,EACH,WAAW,EACX,UAAU,EACV,WAAW,EACX,QAAQ,EACR,YAAY,EACZ,KAAK,kBAAkB,EACvB,KAAK,YAAY,GACpB,MAAM,WAAW,CAAC;AAMnB,OAAO,EACH,aAAa,EACb,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,OAAO,GACf,MAAM,UAAU,CAAC;AAMlB,OAAO,EACH,qBAAqB,EACrB,KAAK,4BAA4B,EACjC,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAC5B,KAAK,uBAAuB,EAC5B,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,yBAAyB,EACzB,yBAAyB,EAEzB,mBAAmB,EACnB,uBAAuB,EACvB,wBAAwB,EACxB,aAAa,EACb,uBAAuB,EAEvB,eAAe,EACf,kBAAkB,EAClB,wBAAwB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAMxC,OAAO,EAEH,cAAc,EACd,WAAW,EAEX,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EAErB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,eAAe,EAEpB,KAAK,eAAe,EACpB,KAAK,cAAc,EAEnB,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EAEtB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,aAAa,EAElB,KAAK,aAAa,EAElB,WAAW,EACX,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,SAAS,EACT,YAAY,EACZ,QAAQ,GACX,MAAM,iBAAiB,CAAC;AAMzB,OAAO,EACH,KAAK,OAAO,EACZ,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,WAAW,EACX,cAAc,EACd,eAAe,GAClB,MAAM,cAAc,CAAC;AAMtB,OAAO,EACH,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,aAAa,EACb,eAAe,EACf,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,2BAA2B,GAC9B,MAAM,qBAAqB,CAAC;AAM7B,OAAO,EACH,WAAW,EACX,YAAY,EACZ,cAAc,EACd,sBAAsB,GACzB,MAAM,YAAY,CAAC;AAMpB,OAAO,EACH,WAAW,EACX,UAAU,EACV,WAAW,EACX,QAAQ,EACR,YAAY,EACZ,KAAK,kBAAkB,EACvB,KAAK,YAAY,GACpB,MAAM,WAAW,CAAC;AAMnB,OAAO,EACH,aAAa,EACb,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,OAAO,GACf,MAAM,UAAU,CAAC;AAMlB,OAAO,EACH,qBAAqB,EACrB,KAAK,4BAA4B,EACjC,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAC5B,KAAK,uBAAuB,EAC5B,KAAK,UAAU,EACf,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,yBAAyB,EACzB,yBAAyB,EAEzB,mBAAmB,EACnB,uBAAuB,EACvB,wBAAwB,EACxB,aAAa,EACb,uBAAuB,EAEvB,eAAe,EACf,kBAAkB,EAClB,wBAAwB,EACxB,eAAe,EAEf,mBAAmB,EACnB,KAAK,yBAAyB,GACjC,MAAM,wBAAwB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -45,7 +45,7 @@ export { StructuredOutputError, isStructuredOutputSuccess, isStructuredOutputFai
|
|
|
45
45
|
// Schema conversion utilities
|
|
46
46
|
normalizeJsonSchema, convertToProviderSchema, stripUnsupportedFeatures, getJsonSchema, getJsonSchemaFromConfig,
|
|
47
47
|
// Validation functions
|
|
48
|
-
parseStructured, tryParseStructured, validateStructuredOutput,
|
|
48
|
+
parseStructured, tryParseStructured, validateStructuredOutput, stripJsonFences,
|
|
49
49
|
// Streaming parser
|
|
50
50
|
StreamingJsonParser, } from './structured-output.js';
|
|
51
51
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,+EAA+E;AAC/E,oCAAoC;AACpC,+EAA+E;AAE/E,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExC,+EAA+E;AAC/E,qBAAqB;AACrB,+EAA+E;AAE/E,OAAO;AACH,QAAQ;AACR,cAAc,EACd,WAAW;AA6BX,UAAU;AACV,WAAW,EACX,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,SAAS,EACT,YAAY,EACZ,QAAQ,GACX,MAAM,iBAAiB,CAAC;AAEzB,+EAA+E;AAC/E,gBAAgB;AAChB,+EAA+E;AAE/E,OAAO,EAIH,WAAW,EACX,cAAc,EACd,eAAe,GAClB,MAAM,cAAc,CAAC;AAEtB,+EAA+E;AAC/E,kBAAkB;AAClB,+EAA+E;AAE/E,OAAO,EAOH,aAAa,EACb,eAAe,EACf,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,2BAA2B,GAC9B,MAAM,qBAAqB,CAAC;AAE7B,+EAA+E;AAC/E,iBAAiB;AACjB,+EAA+E;AAE/E,OAAO,EACH,WAAW,EACX,YAAY,EACZ,cAAc,EACd,sBAAsB,GACzB,MAAM,YAAY,CAAC;AAEpB,+EAA+E;AAC/E,0CAA0C;AAC1C,+EAA+E;AAE/E,OAAO,EACH,WAAW,EACX,UAAU,EACV,WAAW,EACX,QAAQ,EACR,YAAY,GAGf,MAAM,WAAW,CAAC;AAEnB,+EAA+E;AAC/E,kBAAkB;AAClB,+EAA+E;AAE/E,OAAO,EACH,aAAa,GAIhB,MAAM,UAAU,CAAC;AAElB,+EAA+E;AAC/E,oBAAoB;AACpB,+EAA+E;AAE/E,OAAO,EACH,qBAAqB,EAUrB,yBAAyB,EACzB,yBAAyB;AACzB,8BAA8B;AAC9B,mBAAmB,EACnB,uBAAuB,EACvB,wBAAwB,EACxB,aAAa,EACb,uBAAuB;AACvB,uBAAuB;AACvB,eAAe,EACf,kBAAkB,EAClB,wBAAwB;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,+EAA+E;AAC/E,oCAAoC;AACpC,+EAA+E;AAE/E,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExC,+EAA+E;AAC/E,qBAAqB;AACrB,+EAA+E;AAE/E,OAAO;AACH,QAAQ;AACR,cAAc,EACd,WAAW;AA6BX,UAAU;AACV,WAAW,EACX,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,SAAS,EACT,YAAY,EACZ,QAAQ,GACX,MAAM,iBAAiB,CAAC;AAEzB,+EAA+E;AAC/E,gBAAgB;AAChB,+EAA+E;AAE/E,OAAO,EAIH,WAAW,EACX,cAAc,EACd,eAAe,GAClB,MAAM,cAAc,CAAC;AAEtB,+EAA+E;AAC/E,kBAAkB;AAClB,+EAA+E;AAE/E,OAAO,EAOH,aAAa,EACb,eAAe,EACf,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,2BAA2B,GAC9B,MAAM,qBAAqB,CAAC;AAE7B,+EAA+E;AAC/E,iBAAiB;AACjB,+EAA+E;AAE/E,OAAO,EACH,WAAW,EACX,YAAY,EACZ,cAAc,EACd,sBAAsB,GACzB,MAAM,YAAY,CAAC;AAEpB,+EAA+E;AAC/E,0CAA0C;AAC1C,+EAA+E;AAE/E,OAAO,EACH,WAAW,EACX,UAAU,EACV,WAAW,EACX,QAAQ,EACR,YAAY,GAGf,MAAM,WAAW,CAAC;AAEnB,+EAA+E;AAC/E,kBAAkB;AAClB,+EAA+E;AAE/E,OAAO,EACH,aAAa,GAIhB,MAAM,UAAU,CAAC;AAElB,+EAA+E;AAC/E,oBAAoB;AACpB,+EAA+E;AAE/E,OAAO,EACH,qBAAqB,EAUrB,yBAAyB,EACzB,yBAAyB;AACzB,8BAA8B;AAC9B,mBAAmB,EACnB,uBAAuB,EACvB,wBAAwB,EACxB,aAAa,EACb,uBAAuB;AACvB,uBAAuB;AACvB,eAAe,EACf,kBAAkB,EAClB,wBAAwB,EACxB,eAAe;AACf,mBAAmB;AACnB,mBAAmB,GAEtB,MAAM,wBAAwB,CAAC"}
|
|
@@ -285,11 +285,34 @@ export declare function stripUnsupportedFeatures(schema: JSONSchema, provider: S
|
|
|
285
285
|
* 3. Adds name/description for LLM guidance
|
|
286
286
|
*/
|
|
287
287
|
export declare function convertToProviderSchema<T>(provider: SchemaProvider, options: StructuredOutputOptions<T>): ProviderSchema;
|
|
288
|
+
/**
|
|
289
|
+
* Strip markdown code fences from raw LLM output.
|
|
290
|
+
*
|
|
291
|
+
* Some providers/models — notably Gemma variants via Ollama — wrap
|
|
292
|
+
* structured JSON output in ` ```json ... ``` ` fences even when a JSON
|
|
293
|
+
* schema is passed via the `format` field. This helper extracts the
|
|
294
|
+
* inner payload so it can be fed to `JSON.parse`.
|
|
295
|
+
*
|
|
296
|
+
* Behavior:
|
|
297
|
+
* - If the input contains no triple-backtick fences, the input is
|
|
298
|
+
* returned unchanged (zero risk of disturbing well-formed output).
|
|
299
|
+
* - Otherwise, the first fenced block is extracted and returned trimmed.
|
|
300
|
+
* Language tags (` ```json `, ` ```JSON `, etc.) are handled.
|
|
301
|
+
* - Callers should still treat the return value as untrusted — it may
|
|
302
|
+
* not be valid JSON (e.g. the model emitted prose, not JSON).
|
|
303
|
+
*
|
|
304
|
+
* This helper is idempotent: `stripJsonFences(stripJsonFences(x)) === stripJsonFences(x)`.
|
|
305
|
+
*
|
|
306
|
+
* @param rawOutput The raw LLM response text
|
|
307
|
+
* @returns Fence-stripped content if fences were present, otherwise `rawOutput` unchanged
|
|
308
|
+
*/
|
|
309
|
+
export declare function stripJsonFences(rawOutput: string): string;
|
|
288
310
|
/**
|
|
289
311
|
* Parse and validate structured output from raw LLM response text.
|
|
290
312
|
*
|
|
291
313
|
* This function:
|
|
292
|
-
* 1. Parses JSON from the raw output string
|
|
314
|
+
* 1. Parses JSON from the raw output string (with fallback fence stripping
|
|
315
|
+
* for models that wrap structured output in ` ```json ... ``` `)
|
|
293
316
|
* 2. Validates using the SchemaConfig's validate function (if provided)
|
|
294
317
|
* 3. Throws StructuredOutputError on failure
|
|
295
318
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"structured-output.d.ts","sourceRoot":"","sources":["../src/structured-output.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"structured-output.d.ts","sourceRoot":"","sources":["../src/structured-output.ts"],"names":[],"mappings":"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"}
|
|
@@ -183,11 +183,46 @@ export function convertToProviderSchema(provider, options) {
|
|
|
183
183
|
// ============================================================================
|
|
184
184
|
// Validation Functions
|
|
185
185
|
// ============================================================================
|
|
186
|
+
/**
|
|
187
|
+
* Strip markdown code fences from raw LLM output.
|
|
188
|
+
*
|
|
189
|
+
* Some providers/models — notably Gemma variants via Ollama — wrap
|
|
190
|
+
* structured JSON output in ` ```json ... ``` ` fences even when a JSON
|
|
191
|
+
* schema is passed via the `format` field. This helper extracts the
|
|
192
|
+
* inner payload so it can be fed to `JSON.parse`.
|
|
193
|
+
*
|
|
194
|
+
* Behavior:
|
|
195
|
+
* - If the input contains no triple-backtick fences, the input is
|
|
196
|
+
* returned unchanged (zero risk of disturbing well-formed output).
|
|
197
|
+
* - Otherwise, the first fenced block is extracted and returned trimmed.
|
|
198
|
+
* Language tags (` ```json `, ` ```JSON `, etc.) are handled.
|
|
199
|
+
* - Callers should still treat the return value as untrusted — it may
|
|
200
|
+
* not be valid JSON (e.g. the model emitted prose, not JSON).
|
|
201
|
+
*
|
|
202
|
+
* This helper is idempotent: `stripJsonFences(stripJsonFences(x)) === stripJsonFences(x)`.
|
|
203
|
+
*
|
|
204
|
+
* @param rawOutput The raw LLM response text
|
|
205
|
+
* @returns Fence-stripped content if fences were present, otherwise `rawOutput` unchanged
|
|
206
|
+
*/
|
|
207
|
+
export function stripJsonFences(rawOutput) {
|
|
208
|
+
if (!rawOutput.includes('```'))
|
|
209
|
+
return rawOutput;
|
|
210
|
+
// Match the first fenced block, optionally with a language tag.
|
|
211
|
+
// The non-greedy `[\s\S]*?` keeps us from swallowing content after
|
|
212
|
+
// the first closing fence. Whitespace/newlines around the payload
|
|
213
|
+
// are tolerated so we accept both inline and multi-line fences.
|
|
214
|
+
const fenceMatch = rawOutput.match(/```(?:[a-zA-Z0-9_-]*)[ \t]*\n?([\s\S]*?)\n?[ \t]*```/);
|
|
215
|
+
if (fenceMatch && fenceMatch[1] !== undefined) {
|
|
216
|
+
return fenceMatch[1].trim();
|
|
217
|
+
}
|
|
218
|
+
return rawOutput;
|
|
219
|
+
}
|
|
186
220
|
/**
|
|
187
221
|
* Parse and validate structured output from raw LLM response text.
|
|
188
222
|
*
|
|
189
223
|
* This function:
|
|
190
|
-
* 1. Parses JSON from the raw output string
|
|
224
|
+
* 1. Parses JSON from the raw output string (with fallback fence stripping
|
|
225
|
+
* for models that wrap structured output in ` ```json ... ``` `)
|
|
191
226
|
* 2. Validates using the SchemaConfig's validate function (if provided)
|
|
192
227
|
* 3. Throws StructuredOutputError on failure
|
|
193
228
|
*
|
|
@@ -197,17 +232,35 @@ export function convertToProviderSchema(provider, options) {
|
|
|
197
232
|
* @throws StructuredOutputError if JSON parsing fails or validation fails
|
|
198
233
|
*/
|
|
199
234
|
export function parseStructured(config, rawOutput) {
|
|
200
|
-
// Step 1: Parse JSON
|
|
235
|
+
// Step 1: Parse JSON. Try the raw output first — well-behaved providers
|
|
236
|
+
// return clean JSON and we don't want to pay any cost in the fast path.
|
|
237
|
+
// If that fails, fall back to stripping markdown code fences before
|
|
238
|
+
// re-trying; some models (e.g. Gemma via Ollama) wrap structured output
|
|
239
|
+
// in ` ```json ... ``` ` even when a JSON schema is requested.
|
|
201
240
|
let parsed;
|
|
241
|
+
let parseError;
|
|
202
242
|
try {
|
|
203
243
|
parsed = JSON.parse(rawOutput);
|
|
204
244
|
}
|
|
205
245
|
catch (error) {
|
|
206
|
-
|
|
207
|
-
const syntaxError = error instanceof SyntaxError
|
|
246
|
+
parseError = error instanceof SyntaxError
|
|
208
247
|
? error
|
|
209
248
|
: new SyntaxError(String(error));
|
|
210
|
-
|
|
249
|
+
const fenceStripped = stripJsonFences(rawOutput);
|
|
250
|
+
if (fenceStripped !== rawOutput) {
|
|
251
|
+
try {
|
|
252
|
+
parsed = JSON.parse(fenceStripped);
|
|
253
|
+
parseError = undefined;
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
// Fence stripping didn't help — fall through to throw the
|
|
257
|
+
// original parse error so error messages reflect what the
|
|
258
|
+
// model actually emitted, not the post-processed variant.
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if (parseError) {
|
|
263
|
+
throw new StructuredOutputError(`Failed to parse JSON: ${parseError.message}`, { rawOutput, cause: parseError });
|
|
211
264
|
}
|
|
212
265
|
// Step 2: Validate if validator is provided
|
|
213
266
|
if (config.validate) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"structured-output.js","sourceRoot":"","sources":["../src/structured-output.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"structured-output.js","sourceRoot":"","sources":["../src/structured-output.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "universal-llm-client",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A universal LLM client with transparent provider failover, streaming tool execution, pluggable reasoning, and native observability.",
|
|
6
6
|
"main": "./dist/index.js",
|
package/src/index.ts
CHANGED
package/src/structured-output.ts
CHANGED
|
@@ -449,11 +449,49 @@ export function convertToProviderSchema<T>(
|
|
|
449
449
|
// Validation Functions
|
|
450
450
|
// ============================================================================
|
|
451
451
|
|
|
452
|
+
/**
|
|
453
|
+
* Strip markdown code fences from raw LLM output.
|
|
454
|
+
*
|
|
455
|
+
* Some providers/models — notably Gemma variants via Ollama — wrap
|
|
456
|
+
* structured JSON output in ` ```json ... ``` ` fences even when a JSON
|
|
457
|
+
* schema is passed via the `format` field. This helper extracts the
|
|
458
|
+
* inner payload so it can be fed to `JSON.parse`.
|
|
459
|
+
*
|
|
460
|
+
* Behavior:
|
|
461
|
+
* - If the input contains no triple-backtick fences, the input is
|
|
462
|
+
* returned unchanged (zero risk of disturbing well-formed output).
|
|
463
|
+
* - Otherwise, the first fenced block is extracted and returned trimmed.
|
|
464
|
+
* Language tags (` ```json `, ` ```JSON `, etc.) are handled.
|
|
465
|
+
* - Callers should still treat the return value as untrusted — it may
|
|
466
|
+
* not be valid JSON (e.g. the model emitted prose, not JSON).
|
|
467
|
+
*
|
|
468
|
+
* This helper is idempotent: `stripJsonFences(stripJsonFences(x)) === stripJsonFences(x)`.
|
|
469
|
+
*
|
|
470
|
+
* @param rawOutput The raw LLM response text
|
|
471
|
+
* @returns Fence-stripped content if fences were present, otherwise `rawOutput` unchanged
|
|
472
|
+
*/
|
|
473
|
+
export function stripJsonFences(rawOutput: string): string {
|
|
474
|
+
if (!rawOutput.includes('```')) return rawOutput;
|
|
475
|
+
|
|
476
|
+
// Match the first fenced block, optionally with a language tag.
|
|
477
|
+
// The non-greedy `[\s\S]*?` keeps us from swallowing content after
|
|
478
|
+
// the first closing fence. Whitespace/newlines around the payload
|
|
479
|
+
// are tolerated so we accept both inline and multi-line fences.
|
|
480
|
+
const fenceMatch = rawOutput.match(
|
|
481
|
+
/```(?:[a-zA-Z0-9_-]*)[ \t]*\n?([\s\S]*?)\n?[ \t]*```/,
|
|
482
|
+
);
|
|
483
|
+
if (fenceMatch && fenceMatch[1] !== undefined) {
|
|
484
|
+
return fenceMatch[1].trim();
|
|
485
|
+
}
|
|
486
|
+
return rawOutput;
|
|
487
|
+
}
|
|
488
|
+
|
|
452
489
|
/**
|
|
453
490
|
* Parse and validate structured output from raw LLM response text.
|
|
454
491
|
*
|
|
455
492
|
* This function:
|
|
456
|
-
* 1. Parses JSON from the raw output string
|
|
493
|
+
* 1. Parses JSON from the raw output string (with fallback fence stripping
|
|
494
|
+
* for models that wrap structured output in ` ```json ... ``` `)
|
|
457
495
|
* 2. Validates using the SchemaConfig's validate function (if provided)
|
|
458
496
|
* 3. Throws StructuredOutputError on failure
|
|
459
497
|
*
|
|
@@ -466,18 +504,37 @@ export function parseStructured<T>(
|
|
|
466
504
|
config: SchemaConfig<T>,
|
|
467
505
|
rawOutput: string,
|
|
468
506
|
): T {
|
|
469
|
-
// Step 1: Parse JSON
|
|
507
|
+
// Step 1: Parse JSON. Try the raw output first — well-behaved providers
|
|
508
|
+
// return clean JSON and we don't want to pay any cost in the fast path.
|
|
509
|
+
// If that fails, fall back to stripping markdown code fences before
|
|
510
|
+
// re-trying; some models (e.g. Gemma via Ollama) wrap structured output
|
|
511
|
+
// in ` ```json ... ``` ` even when a JSON schema is requested.
|
|
470
512
|
let parsed: unknown;
|
|
513
|
+
let parseError: SyntaxError | undefined;
|
|
471
514
|
try {
|
|
472
515
|
parsed = JSON.parse(rawOutput);
|
|
473
516
|
} catch (error) {
|
|
474
|
-
|
|
475
|
-
const syntaxError = error instanceof SyntaxError
|
|
517
|
+
parseError = error instanceof SyntaxError
|
|
476
518
|
? error
|
|
477
519
|
: new SyntaxError(String(error));
|
|
520
|
+
|
|
521
|
+
const fenceStripped = stripJsonFences(rawOutput);
|
|
522
|
+
if (fenceStripped !== rawOutput) {
|
|
523
|
+
try {
|
|
524
|
+
parsed = JSON.parse(fenceStripped);
|
|
525
|
+
parseError = undefined;
|
|
526
|
+
} catch {
|
|
527
|
+
// Fence stripping didn't help — fall through to throw the
|
|
528
|
+
// original parse error so error messages reflect what the
|
|
529
|
+
// model actually emitted, not the post-processed variant.
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
if (parseError) {
|
|
478
535
|
throw new StructuredOutputError(
|
|
479
|
-
`Failed to parse JSON: ${
|
|
480
|
-
{ rawOutput, cause:
|
|
536
|
+
`Failed to parse JSON: ${parseError.message}`,
|
|
537
|
+
{ rawOutput, cause: parseError },
|
|
481
538
|
);
|
|
482
539
|
}
|
|
483
540
|
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
parseStructured,
|
|
27
27
|
tryParseStructured,
|
|
28
28
|
validateStructuredOutput,
|
|
29
|
+
stripJsonFences,
|
|
29
30
|
} from '../structured-output.js';
|
|
30
31
|
import { fromZod } from '../zod-adapter.js';
|
|
31
32
|
|
|
@@ -1032,6 +1033,115 @@ describe('Validation Logic (VAL-SCHEMA-005)', () => {
|
|
|
1032
1033
|
});
|
|
1033
1034
|
});
|
|
1034
1035
|
|
|
1036
|
+
describe('Markdown Fence Tolerance (VAL-SCHEMA-008)', () => {
|
|
1037
|
+
// Some providers/models wrap structured output in ```json ... ```
|
|
1038
|
+
// fences even when a JSON schema is supplied via the `format`
|
|
1039
|
+
// field (observed with Gemma variants via Ollama). parseStructured
|
|
1040
|
+
// must tolerate this so the first such response doesn't fail the
|
|
1041
|
+
// whole scoring run.
|
|
1042
|
+
|
|
1043
|
+
const schema = z.object({
|
|
1044
|
+
score: z.number(),
|
|
1045
|
+
reasoning: z.string(),
|
|
1046
|
+
correct: z.boolean(),
|
|
1047
|
+
});
|
|
1048
|
+
|
|
1049
|
+
it('parses JSON wrapped in ```json fences', () => {
|
|
1050
|
+
const rawOutput = '```json\n{"score": 0.9, "reasoning": "matches", "correct": true}\n```';
|
|
1051
|
+
const result = parseStructured(fromZod(schema), rawOutput);
|
|
1052
|
+
expect(result).toEqual({ score: 0.9, reasoning: 'matches', correct: true });
|
|
1053
|
+
});
|
|
1054
|
+
|
|
1055
|
+
it('parses JSON wrapped in bare ``` fences (no language tag)', () => {
|
|
1056
|
+
const rawOutput = '```\n{"score": 0.5, "reasoning": "partial", "correct": false}\n```';
|
|
1057
|
+
const result = parseStructured(fromZod(schema), rawOutput);
|
|
1058
|
+
expect(result).toEqual({ score: 0.5, reasoning: 'partial', correct: false });
|
|
1059
|
+
});
|
|
1060
|
+
|
|
1061
|
+
it('parses JSON wrapped in ```JSON (uppercase language tag)', () => {
|
|
1062
|
+
const rawOutput = '```JSON\n{"score": 1.0, "reasoning": "exact", "correct": true}\n```';
|
|
1063
|
+
const result = parseStructured(fromZod(schema), rawOutput);
|
|
1064
|
+
expect(result).toEqual({ score: 1.0, reasoning: 'exact', correct: true });
|
|
1065
|
+
});
|
|
1066
|
+
|
|
1067
|
+
it('parses JSON with inline fences on a single line', () => {
|
|
1068
|
+
const rawOutput = '```json{"score": 0.0, "reasoning": "wrong", "correct": false}```';
|
|
1069
|
+
const result = parseStructured(fromZod(schema), rawOutput);
|
|
1070
|
+
expect(result).toEqual({ score: 0.0, reasoning: 'wrong', correct: false });
|
|
1071
|
+
});
|
|
1072
|
+
|
|
1073
|
+
it('parses JSON with leading/trailing whitespace around fences', () => {
|
|
1074
|
+
const rawOutput = ' \n```json\n{"score": 0.7, "reasoning": "ok", "correct": true}\n```\n ';
|
|
1075
|
+
const result = parseStructured(fromZod(schema), rawOutput);
|
|
1076
|
+
expect(result).toEqual({ score: 0.7, reasoning: 'ok', correct: true });
|
|
1077
|
+
});
|
|
1078
|
+
|
|
1079
|
+
it('falls back to first fenced block when prose precedes the JSON', () => {
|
|
1080
|
+
const rawOutput = "Here's the evaluation:\n```json\n{\"score\": 0.3, \"reasoning\": \"weak\", \"correct\": false}\n```\nHope that helps!";
|
|
1081
|
+
const result = parseStructured(fromZod(schema), rawOutput);
|
|
1082
|
+
expect(result).toEqual({ score: 0.3, reasoning: 'weak', correct: false });
|
|
1083
|
+
});
|
|
1084
|
+
|
|
1085
|
+
it('does not disturb well-formed JSON without fences (fast path)', () => {
|
|
1086
|
+
const rawOutput = '{"score": 0.8, "reasoning": "good", "correct": true}';
|
|
1087
|
+
const result = parseStructured(fromZod(schema), rawOutput);
|
|
1088
|
+
expect(result).toEqual({ score: 0.8, reasoning: 'good', correct: true });
|
|
1089
|
+
});
|
|
1090
|
+
|
|
1091
|
+
it('preserves JSON string values that contain literal backticks', () => {
|
|
1092
|
+
// The reasoning field contains the sequence ``` inside a string
|
|
1093
|
+
// literal but there is no fenced block. parseStructured should
|
|
1094
|
+
// take the fast path and not touch the value.
|
|
1095
|
+
const rawOutput = '{"score": 0.5, "reasoning": "the model emitted ``` once", "correct": false}';
|
|
1096
|
+
const result = parseStructured(fromZod(schema), rawOutput);
|
|
1097
|
+
expect(result.reasoning).toBe('the model emitted ``` once');
|
|
1098
|
+
});
|
|
1099
|
+
|
|
1100
|
+
it('throws with the original rawOutput when fence stripping does not yield valid JSON', () => {
|
|
1101
|
+
const rawOutput = '```json\nthis is not json at all\n```';
|
|
1102
|
+
try {
|
|
1103
|
+
parseStructured(fromZod(schema), rawOutput);
|
|
1104
|
+
expect(true).toBe(false);
|
|
1105
|
+
} catch (error) {
|
|
1106
|
+
expect(error).toBeInstanceOf(StructuredOutputError);
|
|
1107
|
+
if (error instanceof StructuredOutputError) {
|
|
1108
|
+
// The error should report the original raw output so
|
|
1109
|
+
// users debugging can see exactly what the model sent.
|
|
1110
|
+
expect(error.rawOutput).toBe(rawOutput);
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
});
|
|
1114
|
+
});
|
|
1115
|
+
|
|
1116
|
+
describe('stripJsonFences helper', () => {
|
|
1117
|
+
it('returns input unchanged when no fences are present', () => {
|
|
1118
|
+
expect(stripJsonFences('{"a": 1}')).toBe('{"a": 1}');
|
|
1119
|
+
expect(stripJsonFences('plain text')).toBe('plain text');
|
|
1120
|
+
expect(stripJsonFences('')).toBe('');
|
|
1121
|
+
});
|
|
1122
|
+
|
|
1123
|
+
it('strips ```json fences', () => {
|
|
1124
|
+
expect(stripJsonFences('```json\n{"a": 1}\n```')).toBe('{"a": 1}');
|
|
1125
|
+
});
|
|
1126
|
+
|
|
1127
|
+
it('strips bare ``` fences', () => {
|
|
1128
|
+
expect(stripJsonFences('```\n{"a": 1}\n```')).toBe('{"a": 1}');
|
|
1129
|
+
});
|
|
1130
|
+
|
|
1131
|
+
it('is idempotent', () => {
|
|
1132
|
+
const once = stripJsonFences('```json\n{"a": 1}\n```');
|
|
1133
|
+
const twice = stripJsonFences(once);
|
|
1134
|
+
expect(twice).toBe(once);
|
|
1135
|
+
});
|
|
1136
|
+
|
|
1137
|
+
it('returns input unchanged if a stray ``` has no closing fence', () => {
|
|
1138
|
+
// No matched pair — leave the input alone so the JSON parser
|
|
1139
|
+
// sees the real output and produces a meaningful error.
|
|
1140
|
+
const input = '{"a": 1, "note": "stray ``` here"}';
|
|
1141
|
+
expect(stripJsonFences(input)).toBe(input);
|
|
1142
|
+
});
|
|
1143
|
+
});
|
|
1144
|
+
|
|
1035
1145
|
describe('Malformed JSON Handling (VAL-SCHEMA-006)', () => {
|
|
1036
1146
|
it('throws StructuredOutputError on malformed JSON', () => {
|
|
1037
1147
|
const schema = z.object({ name: z.string() });
|