@painitehq/structured-llm 0.1.0 → 0.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.js CHANGED
@@ -109,12 +109,6 @@ function repairJSON(raw) {
109
109
  candidate = candidate.replace(/,\s*([}\]])/g, "$1");
110
110
  candidate = candidate.replace(/'/g, '"');
111
111
  candidate = candidate.replace(/(\w+)\s*:/g, '"$1":');
112
- candidate = candidate.replace(/:\s*"([^"]*?)"/g, (match, content) => {
113
- if (content.includes('"')) {
114
- return match;
115
- }
116
- return match;
117
- });
118
112
  if (!candidate.startsWith("{") && !candidate.startsWith("[")) {
119
113
  const firstBrace = candidate.indexOf("{");
120
114
  const firstBracket = candidate.indexOf("[");
@@ -139,6 +133,128 @@ function repairJSON(raw) {
139
133
  }
140
134
  return null;
141
135
  }
136
+ function coerceType(value, targetType) {
137
+ if (value === void 0 || value === null) {
138
+ return { value, coerced: false };
139
+ }
140
+ switch (targetType) {
141
+ case "string":
142
+ if (typeof value === "string") return { value, coerced: false };
143
+ return { value: String(value), coerced: true };
144
+ case "number":
145
+ if (typeof value === "number") return { value, coerced: false };
146
+ if (typeof value === "string") {
147
+ const num = Number(value);
148
+ if (!isNaN(num)) return { value: num, coerced: true };
149
+ }
150
+ return { value, coerced: false };
151
+ case "boolean":
152
+ if (typeof value === "boolean") return { value, coerced: false };
153
+ if (typeof value === "string") {
154
+ const lower = value.toLowerCase();
155
+ if (lower === "true" || lower === "yes" || lower === "1") return { value: true, coerced: true };
156
+ if (lower === "false" || lower === "no" || lower === "0") return { value: false, coerced: true };
157
+ }
158
+ if (typeof value === "number") {
159
+ return { value: value !== 0, coerced: true };
160
+ }
161
+ return { value, coerced: false };
162
+ case "array":
163
+ if (Array.isArray(value)) return { value, coerced: false };
164
+ return { value: [value], coerced: true };
165
+ default:
166
+ return { value, coerced: false };
167
+ }
168
+ }
169
+ function repairData(data, fields, schemaName) {
170
+ const actions = [];
171
+ if (typeof data !== "object" || data === null || Array.isArray(data)) {
172
+ return { data, actions };
173
+ }
174
+ let obj = data;
175
+ if (schemaName in obj && typeof obj[schemaName] === "object" && obj[schemaName] !== null) {
176
+ actions.push({ attempt: 0, type: "unwrap", detail: `Unwrapped "${schemaName}" wrapper` });
177
+ obj = obj[schemaName];
178
+ }
179
+ for (const [key, field] of Object.entries(fields)) {
180
+ if (obj[key] === void 0) {
181
+ if (field.default !== void 0) {
182
+ obj[key] = field.default;
183
+ actions.push({ attempt: 0, type: "default_fill", detail: `Set "${key}" to default: ${JSON.stringify(field.default)}` });
184
+ } else if (field.type === "string") {
185
+ obj[key] = "";
186
+ actions.push({ attempt: 0, type: "missing_field", detail: `Added empty string for "${key}"` });
187
+ } else if (field.type === "number") {
188
+ obj[key] = 0;
189
+ actions.push({ attempt: 0, type: "missing_field", detail: `Added 0 for "${key}"` });
190
+ } else if (field.type === "boolean") {
191
+ obj[key] = false;
192
+ actions.push({ attempt: 0, type: "missing_field", detail: `Added false for "${key}"` });
193
+ } else if (field.type === "array") {
194
+ obj[key] = [];
195
+ actions.push({ attempt: 0, type: "missing_field", detail: `Added empty array for "${key}"` });
196
+ } else if (field.type === "object") {
197
+ obj[key] = {};
198
+ actions.push({ attempt: 0, type: "missing_field", detail: `Added empty object for "${key}"` });
199
+ }
200
+ }
201
+ if (obj[key] !== void 0) {
202
+ const coerced = coerceType(obj[key], field.type);
203
+ if (coerced.coerced) {
204
+ obj[key] = coerced.value;
205
+ actions.push({ attempt: 0, type: "type_coercion", detail: `Coerced "${key}" to ${field.type}: ${JSON.stringify(coerced.value)}` });
206
+ }
207
+ if (field.type === "array" && Array.isArray(obj[key]) && field.items) {
208
+ obj[key] = obj[key].map((item, i) => {
209
+ const itemResult = repairItem(item, field.items);
210
+ if (itemResult.actions.length > 0) {
211
+ actions.push(...itemResult.actions.map((a) => ({ ...a, detail: `[${i}] ${a.detail}` })));
212
+ }
213
+ return itemResult.data;
214
+ });
215
+ }
216
+ if (field.type === "object" && typeof obj[key] === "object" && obj[key] !== null && field.properties) {
217
+ const nested = repairData(obj[key], field.properties, "");
218
+ if (nested.actions.length > 0) {
219
+ actions.push(...nested.actions);
220
+ }
221
+ obj[key] = nested.data;
222
+ }
223
+ }
224
+ }
225
+ return { data: obj, actions };
226
+ }
227
+ function repairItem(item, field) {
228
+ const actions = [];
229
+ if (field.type === "object" && typeof item === "object" && item !== null && !Array.isArray(item) && field.properties) {
230
+ const obj = item;
231
+ for (const [key, prop] of Object.entries(field.properties)) {
232
+ if (obj[key] === void 0) {
233
+ if (prop.type === "string") {
234
+ obj[key] = "";
235
+ actions.push({ attempt: 0, type: "missing_field", detail: `Added empty string for "${key}"` });
236
+ } else if (prop.type === "number") {
237
+ obj[key] = 0;
238
+ actions.push({ attempt: 0, type: "missing_field", detail: `Added 0 for "${key}"` });
239
+ } else if (prop.type === "boolean") {
240
+ obj[key] = false;
241
+ actions.push({ attempt: 0, type: "missing_field", detail: `Added false for "${key}"` });
242
+ } else if (prop.type === "array") {
243
+ obj[key] = [];
244
+ actions.push({ attempt: 0, type: "missing_field", detail: `Added empty array for "${key}"` });
245
+ }
246
+ }
247
+ if (obj[key] !== void 0) {
248
+ const coerced = coerceType(obj[key], prop.type);
249
+ if (coerced.coerced) {
250
+ obj[key] = coerced.value;
251
+ actions.push({ attempt: 0, type: "type_coercion", detail: `Coerced "${key}" to ${prop.type}` });
252
+ }
253
+ }
254
+ }
255
+ }
256
+ return { data: item, actions };
257
+ }
142
258
  function validateAgainstSchema(data, schema) {
143
259
  if (typeof data !== "object" || data === null || Array.isArray(data)) {
144
260
  return { valid: false, errors: ["Root value must be an object"] };
@@ -192,40 +308,120 @@ function validateAgainstSchema(data, schema) {
192
308
  function defineSchema(name, fields) {
193
309
  return { name, fields };
194
310
  }
195
- function schemaToPrompt(schema) {
196
- const fieldDescriptions = Object.entries(schema.fields).map(([key, field]) => {
197
- const parts = [`"${key}": ${fieldTypeToExample(field)}`];
198
- if (field.description) {
199
- parts.push(`// ${field.description}`);
311
+ function schemaToJSONExample(fields) {
312
+ const result = {};
313
+ for (const [key, field] of Object.entries(fields)) {
314
+ switch (field.type) {
315
+ case "string":
316
+ result[key] = field.description || "...";
317
+ break;
318
+ case "number":
319
+ result[key] = 0;
320
+ break;
321
+ case "boolean":
322
+ result[key] = true;
323
+ break;
324
+ case "array":
325
+ if (field.items) {
326
+ result[key] = [schemaToJSONExample(field.items.properties || {})];
327
+ } else {
328
+ result[key] = [];
329
+ }
330
+ break;
331
+ case "object":
332
+ if (field.properties) {
333
+ result[key] = schemaToJSONExample(field.properties);
334
+ } else {
335
+ result[key] = {};
336
+ }
337
+ break;
200
338
  }
201
- return parts.join(" ");
202
- }).join(",\n ");
203
- return `{
204
- "${schema.name}": {
205
- ${fieldDescriptions}
206
339
  }
207
- }`;
340
+ return result;
208
341
  }
209
- function fieldTypeToExample(field) {
210
- switch (field.type) {
211
- case "string":
212
- return `"..."`;
213
- case "number":
214
- return "0";
215
- case "boolean":
216
- return "true";
217
- case "array":
218
- if (field.items) {
219
- return `[${fieldTypeToExample(field.items)}]`;
220
- }
221
- return "[]";
222
- case "object":
223
- if (field.properties) {
224
- const inner = Object.entries(field.properties).map(([k, v]) => `"${k}": ${fieldTypeToExample(v)}`).join(", ");
225
- return `{ ${inner} }`;
226
- }
227
- return "{}";
228
- }
342
+ function buildForcedPrompt(input, schema) {
343
+ const example = schemaToJSONExample(schema.fields);
344
+ const exampleStr = JSON.stringify({ [schema.name]: example }, null, 2);
345
+ return `TASK: Extract structured data from the text below into EXACTLY this JSON format.
346
+
347
+ CRITICAL RULES:
348
+ - Output ONLY valid JSON. No text before or after.
349
+ - No markdown. No code blocks. No explanations.
350
+ - Every field MUST be present with the correct type.
351
+ - String fields: use "" if unknown.
352
+ - Number fields: use 0 if unknown.
353
+ - Boolean fields: use true or false, never null.
354
+ - Array fields: use [] if empty, never null.
355
+ - Object fields: use {} if empty, never null.
356
+
357
+ REQUIRED JSON STRUCTURE:
358
+ ${exampleStr}
359
+
360
+ TEXT TO EXTRACT FROM:
361
+ """
362
+ ${input}
363
+ """
364
+
365
+ OUTPUT ONLY THE JSON OBJECT. NOTHING ELSE.`;
366
+ }
367
+ function buildRetryPrompt(input, schema, previousResponse, error) {
368
+ const example = schemaToJSONExample(schema.fields);
369
+ const exampleStr = JSON.stringify({ [schema.name]: example }, null, 2);
370
+ return `YOUR PREVIOUS RESPONSE WAS INVALID. You MUST fix it.
371
+
372
+ ERROR: ${error}
373
+
374
+ YOUR PREVIOUS RESPONSE:
375
+ ${previousResponse.slice(0, 1e3)}
376
+
377
+ WHAT YOU MUST DO NOW:
378
+ 1. Output ONLY valid JSON matching this EXACT structure
379
+ 2. No text before or after the JSON
380
+ 3. No markdown, no code blocks, no explanations
381
+ 4. Fix the errors listed above
382
+ 5. Every field MUST be present
383
+
384
+ REQUIRED JSON STRUCTURE:
385
+ ${exampleStr}
386
+
387
+ TEXT TO EXTRACT FROM:
388
+ """
389
+ ${input}
390
+ """
391
+
392
+ OUTPUT ONLY THE JSON OBJECT. NOTHING ELSE.`;
393
+ }
394
+ function buildFinalPrompt(input, schema, previousResponse, error) {
395
+ const example = schemaToJSONExample(schema.fields);
396
+ const exampleStr = JSON.stringify({ [schema.name]: example }, null, 2);
397
+ return `FINAL ATTEMPT. THIS IS YOUR LAST CHANCE.
398
+
399
+ YOUR PREVIOUS RESPONSE FAILED VALIDATION:
400
+ ${error}
401
+
402
+ YOUR PREVIOUS RESPONSE:
403
+ ${previousResponse.slice(0, 800)}
404
+
405
+ YOU MUST OUTPUT EXACTLY THIS STRUCTURE. NOTHING MORE. NOTHING LESS.
406
+ DO NOT ADD FIELDS THAT ARE NOT IN THE SCHEMA.
407
+ DO NOT OMIT ANY FIELDS.
408
+ DO NOT WRAP IN markdown OR code blocks.
409
+ DO NOT ADD ANY TEXT BEFORE OR AFTER THE JSON.
410
+
411
+ STRUCTURE:
412
+ ${exampleStr}
413
+
414
+ INPUT TEXT:
415
+ """
416
+ ${input}
417
+ """
418
+
419
+ OUTPUT ONLY:
420
+ `;
421
+ }
422
+ function schemaToPrompt(schema) {
423
+ const example = schemaToJSONExample(schema.fields);
424
+ return JSON.stringify({ [schema.name]: example }, null, 2);
229
425
  }
230
426
  function schemaToZodishString(schema) {
231
427
  const lines = [];
@@ -239,11 +435,16 @@ function schemaToZodishString(schema) {
239
435
 
240
436
  // src/extractor.ts
241
437
  var MAX_REPAIR_ATTEMPTS = 3;
242
- function unwrapNamedResponse(data, schemaName) {
243
- if (typeof data === "object" && data !== null && !Array.isArray(data) && schemaName in data && typeof data[schemaName] === "object") {
244
- return data[schemaName];
438
+ function calculateConfidence(attempts, repairActions, jsonValidFirstTry) {
439
+ let score = 100;
440
+ if (!jsonValidFirstTry) {
441
+ score -= 15;
245
442
  }
246
- return data;
443
+ score -= (attempts - 1) * 10;
444
+ score -= repairActions.filter((a) => a.type === "type_coercion").length * 5;
445
+ score -= repairActions.filter((a) => a.type === "missing_field").length * 8;
446
+ score -= repairActions.filter((a) => a.type === "default_fill").length * 3;
447
+ return Math.max(0, Math.min(100, score));
247
448
  }
248
449
  async function extract(input, schema, options = {}) {
249
450
  const config = {
@@ -257,59 +458,62 @@ async function extract(input, schema, options = {}) {
257
458
  "OpenRouter API key required. Pass it in options or set OPENROUTER_API_KEY environment variable."
258
459
  );
259
460
  }
260
- const schemaDescription = schemaToZodishString(schema);
261
- const exampleJSON = schemaToPrompt(schema);
262
- const userPrompt = `Extract structured data from the following text.
263
-
264
- ${schemaDescription}
265
-
266
- Return ONLY valid JSON matching this structure:
267
- ${exampleJSON}
268
-
269
- Text to extract from:
270
- """
271
- ${input}
272
- """`;
273
- const schemaFields = {};
274
- for (const [key, field] of Object.entries(schema.fields)) {
275
- schemaFields[key] = { type: field.type, required: field.required };
276
- }
461
+ const allRepairActions = [];
277
462
  let lastRaw = "";
278
463
  let lastError = "";
464
+ let jsonValidFirstTry = false;
465
+ let successAttempt = 0;
279
466
  for (let attempt = 0; attempt <= MAX_REPAIR_ATTEMPTS; attempt++) {
280
- const prompt = attempt === 0 ? userPrompt : `${userPrompt}
281
-
282
- IMPORTANT: Your previous response was invalid JSON. Here was the error:
283
- ${lastError}
284
-
285
- Previous raw response:
286
- ${lastRaw}
287
-
288
- Fix the JSON and return ONLY valid JSON. No explanations, no markdown, just the raw JSON object.`;
467
+ let prompt;
468
+ if (attempt === 0) {
469
+ prompt = buildForcedPrompt(input, schema);
470
+ } else if (attempt === 1) {
471
+ prompt = buildRetryPrompt(input, schema, lastRaw, lastError);
472
+ } else {
473
+ prompt = buildFinalPrompt(input, schema, lastRaw, lastError);
474
+ }
289
475
  const response = await callLLM(prompt, config);
290
476
  lastRaw = response.content;
291
477
  const extracted = extractJSON(response.content);
292
478
  if (!extracted) {
293
- lastError = "Could not extract JSON from response";
479
+ lastError = "Could not extract JSON from response. Your response contained no valid JSON.";
294
480
  continue;
295
481
  }
296
482
  const parsed = tryParseJSON(extracted);
297
483
  if (!parsed.success) {
298
- lastError = parsed.error;
484
+ lastError = `Invalid JSON: ${parsed.error}`;
299
485
  continue;
300
486
  }
487
+ if (attempt === 0) {
488
+ jsonValidFirstTry = true;
489
+ }
301
490
  const repaired = repairJSON(extracted);
302
- const finalData = repaired ? tryParseJSON(repaired).success ? JSON.parse(repaired) : parsed.data : parsed.data;
303
- const unwrapped = unwrapNamedResponse(finalData, schema.name);
304
- const validation = validateAgainstSchema(unwrapped, schemaFields);
491
+ let finalData = repaired ? tryParseJSON(repaired).success ? JSON.parse(repaired) : parsed.data : parsed.data;
492
+ const repairResult = repairData(finalData, schema.fields, schema.name);
493
+ finalData = repairResult.data;
494
+ allRepairActions.push(...repairResult.actions);
495
+ const schemaFields = {};
496
+ for (const [key, field] of Object.entries(schema.fields)) {
497
+ schemaFields[key] = field;
498
+ }
499
+ const validation = validateAgainstSchema(finalData, schemaFields);
305
500
  if (!validation.valid) {
306
- lastError = `Schema validation failed: ${validation.errors.join(", ")}`;
501
+ lastError = `Schema validation failed: ${validation.errors.join("; ")}`;
502
+ allRepairActions.push({
503
+ attempt,
504
+ type: "type_coercion",
505
+ detail: `Validation failed: ${validation.errors.join("; ")}`
506
+ });
307
507
  continue;
308
508
  }
509
+ successAttempt = attempt + 1;
309
510
  return {
310
- data: unwrapped,
511
+ data: finalData,
311
512
  raw: response.content,
312
513
  model: response.model,
514
+ confidence: calculateConfidence(successAttempt, allRepairActions, jsonValidFirstTry),
515
+ repairLog: allRepairActions,
516
+ attempts: successAttempt,
313
517
  usage: response.usage
314
518
  };
315
519
  }
@@ -318,6 +522,6 @@ Fix the JSON and return ONLY valid JSON. No explanations, no markdown, just the
318
522
  );
319
523
  }
320
524
 
321
- export { defineSchema, extract, extractJSON, repairJSON, schemaToPrompt, schemaToZodishString, tryParseJSON, validateAgainstSchema };
525
+ export { coerceType, defineSchema, extract, extractJSON, repairData, repairJSON, schemaToPrompt, schemaToZodishString, tryParseJSON, validateAgainstSchema };
322
526
  //# sourceMappingURL=index.js.map
323
527
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/llm-client.ts","../src/validator.ts","../src/schema.ts","../src/extractor.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["export interface LLMClientConfig {\n apiKey: string;\n model: string;\n temperature?: number;\n timeout?: number;\n}\n\nexport interface LLMResponse {\n content: string;\n model: string;\n usage?: {\n promptTokens: number;\n completionTokens: number;\n totalTokens: number;\n };\n}\n\ninterface OpenRouterChoice {\n message?: {\n content?: string;\n };\n}\n\ninterface OpenRouterResponse {\n choices?: OpenRouterChoice[];\n model?: string;\n usage?: {\n prompt_tokens: number;\n completion_tokens: number;\n total_tokens: number;\n };\n}\n\nconst DEFAULT_MODEL = \"openrouter/free\";\nconst DEFAULT_TEMPERATURE = 0;\nconst DEFAULT_TIMEOUT = 60000;\n\nexport async function callLLM(\n prompt: string,\n config: LLMClientConfig\n): Promise<LLMResponse> {\n const {\n apiKey,\n model = DEFAULT_MODEL,\n temperature = DEFAULT_TEMPERATURE,\n timeout = DEFAULT_TIMEOUT,\n } = config;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(\"https://openrouter.ai/api/v1/chat/completions\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n \"HTTP-Referer\": \"https://github.com/harshitduggal/structured-llm\",\n \"X-Title\": \"structured-llm\",\n },\n body: JSON.stringify({\n model,\n messages: [\n {\n role: \"system\",\n content: \"You are a precise data extraction assistant. You MUST respond with valid JSON only. No explanations, no markdown, no text outside the JSON. Just the raw JSON object.\",\n },\n {\n role: \"user\",\n content: prompt,\n },\n ],\n temperature,\n response_format: { type: \"json_object\" },\n }),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n const errorBody = await response.text();\n throw new Error(\n `OpenRouter API error: ${response.status} - ${errorBody}`\n );\n }\n\n const data = (await response.json()) as OpenRouterResponse;\n\n if (!data.choices || data.choices.length === 0) {\n throw new Error(\"No response from LLM\");\n }\n\n const content = data.choices[0].message?.content;\n if (!content) {\n throw new Error(\"Empty response from LLM\");\n }\n\n return {\n content,\n model: data.model || model,\n usage: data.usage\n ? {\n promptTokens: data.usage.prompt_tokens,\n completionTokens: data.usage.completion_tokens,\n totalTokens: data.usage.total_tokens,\n }\n : undefined,\n };\n } finally {\n clearTimeout(timeoutId);\n }\n}\n","export function tryParseJSON(raw: string): { success: true; data: unknown } | { success: false; error: string } {\n try {\n const data = JSON.parse(raw);\n return { success: true, data };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : \"Unknown parse error\" };\n }\n}\n\nexport function extractJSON(raw: string): string | null {\n const trimmed = raw.trim();\n\n if (tryParseJSON(trimmed).success) {\n return trimmed;\n }\n\n const codeBlockMatch = trimmed.match(/```(?:json)?\\s*\\n?([\\s\\S]*?)\\n?\\s*```/);\n if (codeBlockMatch) {\n const inner = codeBlockMatch[1].trim();\n if (tryParseJSON(inner).success) {\n return inner;\n }\n }\n\n const firstBrace = trimmed.indexOf(\"{\");\n const lastBrace = trimmed.lastIndexOf(\"}\");\n if (firstBrace !== -1 && lastBrace > firstBrace) {\n const candidate = trimmed.slice(firstBrace, lastBrace + 1);\n if (tryParseJSON(candidate).success) {\n return candidate;\n }\n }\n\n const firstBracket = trimmed.indexOf(\"[\");\n const lastBracket = trimmed.lastIndexOf(\"]\");\n if (firstBracket !== -1 && lastBracket > firstBracket) {\n const candidate = trimmed.slice(firstBracket, lastBracket + 1);\n if (tryParseJSON(candidate).success) {\n return candidate;\n }\n }\n\n return null;\n}\n\nexport function repairJSON(raw: string): string | null {\n let candidate = raw.trim();\n\n candidate = candidate.replace(/,\\s*([}\\]])/g, \"$1\");\n\n candidate = candidate.replace(/'/g, '\"');\n\n candidate = candidate.replace(/(\\w+)\\s*:/g, '\"$1\":');\n\n candidate = candidate.replace(/:\\s*\"([^\"]*?)\"/g, (match, content) => {\n if (content.includes('\"')) {\n return match;\n }\n return match;\n });\n\n if (!candidate.startsWith(\"{\") && !candidate.startsWith(\"[\")) {\n const firstBrace = candidate.indexOf(\"{\");\n const firstBracket = candidate.indexOf(\"[\");\n if (firstBrace !== -1 && (firstBracket === -1 || firstBrace < firstBracket)) {\n candidate = candidate.slice(firstBrace);\n } else if (firstBracket !== -1) {\n candidate = candidate.slice(firstBracket);\n }\n }\n\n if (!candidate.endsWith(\"}\") && !candidate.endsWith(\"]\")) {\n const lastBrace = candidate.lastIndexOf(\"}\");\n const lastBracket = candidate.lastIndexOf(\"]\");\n if (lastBrace !== -1 && (lastBracket === -1 || lastBrace > lastBracket)) {\n candidate = candidate.slice(0, lastBrace + 1);\n } else if (lastBracket !== -1) {\n candidate = candidate.slice(0, lastBracket + 1);\n }\n }\n\n const result = tryParseJSON(candidate);\n if (result.success) {\n return candidate;\n }\n\n return null;\n}\n\nexport function validateAgainstSchema(data: unknown, schema: Record<string, { type: string; required?: boolean }>): { valid: true; data: unknown } | { valid: false; errors: string[] } {\n if (typeof data !== \"object\" || data === null || Array.isArray(data)) {\n return { valid: false, errors: [\"Root value must be an object\"] };\n }\n\n const errors: string[] = [];\n const obj = data as Record<string, unknown>;\n\n for (const [key, field] of Object.entries(schema)) {\n const value = obj[key];\n\n if (value === undefined && field.required !== false) {\n errors.push(`Missing required field: \"${key}\"`);\n continue;\n }\n\n if (value === undefined) {\n continue;\n }\n\n switch (field.type) {\n case \"string\":\n if (typeof value !== \"string\") {\n errors.push(`Field \"${key}\" must be string, got ${typeof value}`);\n }\n break;\n case \"number\":\n if (typeof value !== \"number\") {\n errors.push(`Field \"${key}\" must be number, got ${typeof value}`);\n }\n break;\n case \"boolean\":\n if (typeof value !== \"boolean\") {\n errors.push(`Field \"${key}\" must be boolean, got ${typeof value}`);\n }\n break;\n case \"array\":\n if (!Array.isArray(value)) {\n errors.push(`Field \"${key}\" must be array, got ${typeof value}`);\n }\n break;\n case \"object\":\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n errors.push(`Field \"${key}\" must be object, got ${typeof value}`);\n }\n break;\n }\n }\n\n if (errors.length > 0) {\n return { valid: false, errors };\n }\n\n return { valid: true, data };\n}\n","import { ExtractionSchema, SchemaField } from \"./types\";\n\nexport function defineSchema(name: string, fields: Record<string, SchemaField>): ExtractionSchema {\n return { name, fields };\n}\n\nexport function schemaToPrompt(schema: ExtractionSchema): string {\n const fieldDescriptions = Object.entries(schema.fields)\n .map(([key, field]) => {\n const parts = [`\"${key}\": ${fieldTypeToExample(field)}`];\n if (field.description) {\n parts.push(`// ${field.description}`);\n }\n return parts.join(\" \");\n })\n .join(\",\\n \");\n\n return `{\n \"${schema.name}\": {\n ${fieldDescriptions}\n }\n}`;\n}\n\nfunction fieldTypeToExample(field: SchemaField): string {\n switch (field.type) {\n case \"string\":\n return `\"...\"`;\n case \"number\":\n return \"0\";\n case \"boolean\":\n return \"true\";\n case \"array\":\n if (field.items) {\n return `[${fieldTypeToExample(field.items)}]`;\n }\n return \"[]\";\n case \"object\":\n if (field.properties) {\n const inner = Object.entries(field.properties)\n .map(([k, v]) => `\"${k}\": ${fieldTypeToExample(v)}`)\n .join(\", \");\n return `{ ${inner} }`;\n }\n return \"{}\";\n }\n}\n\nexport function schemaToZodishString(schema: ExtractionSchema): string {\n const lines: string[] = [];\n lines.push(`Expected JSON structure for \"${schema.name}\":`);\n\n for (const [key, field] of Object.entries(schema.fields)) {\n const required = field.required !== false ? \"(required)\" : \"(optional)\";\n lines.push(` - \"${key}\": ${field.type} ${required}${field.description ? ` - ${field.description}` : \"\"}`);\n }\n\n return lines.join(\"\\n\");\n}\n","import { ExtractionSchema, ExtractionOptions, ExtractionResult } from \"./types\";\nimport { callLLM, LLMClientConfig } from \"./llm-client\";\nimport { extractJSON, repairJSON, tryParseJSON, validateAgainstSchema } from \"./validator\";\nimport { schemaToPrompt, schemaToZodishString } from \"./schema\";\n\n\nconst MAX_REPAIR_ATTEMPTS = 3;\n\nfunction unwrapNamedResponse(data: unknown, schemaName: string): unknown {\n if (\n typeof data === \"object\" &&\n data !== null &&\n !Array.isArray(data) &&\n schemaName in data &&\n typeof (data as Record<string, unknown>)[schemaName] === \"object\"\n ) {\n return (data as Record<string, unknown>)[schemaName];\n }\n return data;\n}\n\nexport async function extract<T = Record<string, unknown>>(\n input: string,\n schema: ExtractionSchema,\n options: ExtractionOptions = {}\n): Promise<ExtractionResult<T>> {\n const config: LLMClientConfig = {\n apiKey: options.apiKey || process.env.OPENROUTER_API_KEY || \"\",\n model: options.model || \"openrouter/free\",\n temperature: options.temperature ?? 0,\n timeout: options.timeout ?? 30000,\n };\n\n if (!config.apiKey || config.apiKey.trim() === \"\") {\n throw new Error(\n \"OpenRouter API key required. Pass it in options or set OPENROUTER_API_KEY environment variable.\"\n );\n }\n\n const schemaDescription = schemaToZodishString(schema);\n const exampleJSON = schemaToPrompt(schema);\n\n const userPrompt = `Extract structured data from the following text.\n\n${schemaDescription}\n\nReturn ONLY valid JSON matching this structure:\n${exampleJSON}\n\nText to extract from:\n\"\"\"\n${input}\n\"\"\"`;\n\n const schemaFields: Record<string, { type: string; required?: boolean }> = {};\n for (const [key, field] of Object.entries(schema.fields)) {\n schemaFields[key] = { type: field.type, required: field.required };\n }\n\n let lastRaw = \"\";\n let lastError = \"\";\n\n for (let attempt = 0; attempt <= MAX_REPAIR_ATTEMPTS; attempt++) {\n const prompt =\n attempt === 0\n ? userPrompt\n : `${userPrompt}\n\nIMPORTANT: Your previous response was invalid JSON. Here was the error:\n${lastError}\n\nPrevious raw response:\n${lastRaw}\n\nFix the JSON and return ONLY valid JSON. No explanations, no markdown, just the raw JSON object.`;\n\n const response = await callLLM(prompt, config);\n lastRaw = response.content;\n\n const extracted = extractJSON(response.content);\n if (!extracted) {\n lastError = \"Could not extract JSON from response\";\n continue;\n }\n\n const parsed = tryParseJSON(extracted);\n if (!parsed.success) {\n lastError = parsed.error;\n continue;\n }\n\n const repaired = repairJSON(extracted);\n const finalData = repaired\n ? tryParseJSON(repaired).success\n ? JSON.parse(repaired)\n : parsed.data\n : parsed.data;\n\n const unwrapped = unwrapNamedResponse(finalData, schema.name);\n\n const validation = validateAgainstSchema(unwrapped, schemaFields);\n if (!validation.valid) {\n lastError = `Schema validation failed: ${validation.errors.join(\", \")}`;\n continue;\n }\n\n return {\n data: unwrapped as T,\n raw: response.content,\n model: response.model,\n usage: response.usage,\n };\n }\n\n throw new Error(\n `Failed to extract valid JSON after ${MAX_REPAIR_ATTEMPTS + 1} attempts. Last error: ${lastError}`\n );\n}\n"]}
1
+ {"version":3,"sources":["../src/llm-client.ts","../src/validator.ts","../src/schema.ts","../src/extractor.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["export interface LLMClientConfig {\n apiKey: string;\n model: string;\n temperature?: number;\n timeout?: number;\n}\n\nexport interface LLMResponse {\n content: string;\n model: string;\n usage?: {\n promptTokens: number;\n completionTokens: number;\n totalTokens: number;\n };\n}\n\ninterface OpenRouterChoice {\n message?: {\n content?: string;\n };\n}\n\ninterface OpenRouterResponse {\n choices?: OpenRouterChoice[];\n model?: string;\n usage?: {\n prompt_tokens: number;\n completion_tokens: number;\n total_tokens: number;\n };\n}\n\nconst DEFAULT_MODEL = \"openrouter/free\";\nconst DEFAULT_TEMPERATURE = 0;\nconst DEFAULT_TIMEOUT = 60000;\n\nexport async function callLLM(\n prompt: string,\n config: LLMClientConfig\n): Promise<LLMResponse> {\n const {\n apiKey,\n model = DEFAULT_MODEL,\n temperature = DEFAULT_TEMPERATURE,\n timeout = DEFAULT_TIMEOUT,\n } = config;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n try {\n const response = await fetch(\"https://openrouter.ai/api/v1/chat/completions\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${apiKey}`,\n \"HTTP-Referer\": \"https://github.com/harshitduggal/structured-llm\",\n \"X-Title\": \"structured-llm\",\n },\n body: JSON.stringify({\n model,\n messages: [\n {\n role: \"system\",\n content: \"You are a precise data extraction assistant. You MUST respond with valid JSON only. No explanations, no markdown, no text outside the JSON. Just the raw JSON object.\",\n },\n {\n role: \"user\",\n content: prompt,\n },\n ],\n temperature,\n response_format: { type: \"json_object\" },\n }),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n const errorBody = await response.text();\n throw new Error(\n `OpenRouter API error: ${response.status} - ${errorBody}`\n );\n }\n\n const data = (await response.json()) as OpenRouterResponse;\n\n if (!data.choices || data.choices.length === 0) {\n throw new Error(\"No response from LLM\");\n }\n\n const content = data.choices[0].message?.content;\n if (!content) {\n throw new Error(\"Empty response from LLM\");\n }\n\n return {\n content,\n model: data.model || model,\n usage: data.usage\n ? {\n promptTokens: data.usage.prompt_tokens,\n completionTokens: data.usage.completion_tokens,\n totalTokens: data.usage.total_tokens,\n }\n : undefined,\n };\n } finally {\n clearTimeout(timeoutId);\n }\n}\n","import { SchemaField, RepairAction } from \"./types\";\n\nexport function tryParseJSON(raw: string): { success: true; data: unknown } | { success: false; error: string } {\n try {\n const data = JSON.parse(raw);\n return { success: true, data };\n } catch (e) {\n return { success: false, error: e instanceof Error ? e.message : \"Unknown parse error\" };\n }\n}\n\nexport function extractJSON(raw: string): string | null {\n const trimmed = raw.trim();\n\n if (tryParseJSON(trimmed).success) {\n return trimmed;\n }\n\n const codeBlockMatch = trimmed.match(/```(?:json)?\\s*\\n?([\\s\\S]*?)\\n?\\s*```/);\n if (codeBlockMatch) {\n const inner = codeBlockMatch[1].trim();\n if (tryParseJSON(inner).success) {\n return inner;\n }\n }\n\n const firstBrace = trimmed.indexOf(\"{\");\n const lastBrace = trimmed.lastIndexOf(\"}\");\n if (firstBrace !== -1 && lastBrace > firstBrace) {\n const candidate = trimmed.slice(firstBrace, lastBrace + 1);\n if (tryParseJSON(candidate).success) {\n return candidate;\n }\n }\n\n const firstBracket = trimmed.indexOf(\"[\");\n const lastBracket = trimmed.lastIndexOf(\"]\");\n if (firstBracket !== -1 && lastBracket > firstBracket) {\n const candidate = trimmed.slice(firstBracket, lastBracket + 1);\n if (tryParseJSON(candidate).success) {\n return candidate;\n }\n }\n\n return null;\n}\n\nexport function repairJSON(raw: string): string | null {\n let candidate = raw.trim();\n\n candidate = candidate.replace(/,\\s*([}\\]])/g, \"$1\");\n candidate = candidate.replace(/'/g, '\"');\n candidate = candidate.replace(/(\\w+)\\s*:/g, '\"$1\":');\n\n if (!candidate.startsWith(\"{\") && !candidate.startsWith(\"[\")) {\n const firstBrace = candidate.indexOf(\"{\");\n const firstBracket = candidate.indexOf(\"[\");\n if (firstBrace !== -1 && (firstBracket === -1 || firstBrace < firstBracket)) {\n candidate = candidate.slice(firstBrace);\n } else if (firstBracket !== -1) {\n candidate = candidate.slice(firstBracket);\n }\n }\n\n if (!candidate.endsWith(\"}\") && !candidate.endsWith(\"]\")) {\n const lastBrace = candidate.lastIndexOf(\"}\");\n const lastBracket = candidate.lastIndexOf(\"]\");\n if (lastBrace !== -1 && (lastBracket === -1 || lastBrace > lastBracket)) {\n candidate = candidate.slice(0, lastBrace + 1);\n } else if (lastBracket !== -1) {\n candidate = candidate.slice(0, lastBracket + 1);\n }\n }\n\n const result = tryParseJSON(candidate);\n if (result.success) {\n return candidate;\n }\n\n return null;\n}\n\nexport function coerceType(value: unknown, targetType: string): { value: unknown; coerced: boolean } {\n if (value === undefined || value === null) {\n return { value, coerced: false };\n }\n\n switch (targetType) {\n case \"string\":\n if (typeof value === \"string\") return { value, coerced: false };\n return { value: String(value), coerced: true };\n\n case \"number\":\n if (typeof value === \"number\") return { value, coerced: false };\n if (typeof value === \"string\") {\n const num = Number(value);\n if (!isNaN(num)) return { value: num, coerced: true };\n }\n return { value, coerced: false };\n\n case \"boolean\":\n if (typeof value === \"boolean\") return { value, coerced: false };\n if (typeof value === \"string\") {\n const lower = value.toLowerCase();\n if (lower === \"true\" || lower === \"yes\" || lower === \"1\") return { value: true, coerced: true };\n if (lower === \"false\" || lower === \"no\" || lower === \"0\") return { value: false, coerced: true };\n }\n if (typeof value === \"number\") {\n return { value: value !== 0, coerced: true };\n }\n return { value, coerced: false };\n\n case \"array\":\n if (Array.isArray(value)) return { value, coerced: false };\n return { value: [value], coerced: true };\n\n default:\n return { value, coerced: false };\n }\n}\n\nexport function repairData(data: unknown, fields: Record<string, SchemaField>, schemaName: string): { data: unknown; actions: RepairAction[] } {\n const actions: RepairAction[] = [];\n\n if (typeof data !== \"object\" || data === null || Array.isArray(data)) {\n return { data, actions };\n }\n\n let obj = data as Record<string, unknown>;\n\n if (schemaName in obj && typeof obj[schemaName] === \"object\" && obj[schemaName] !== null) {\n actions.push({ attempt: 0, type: \"unwrap\", detail: `Unwrapped \"${schemaName}\" wrapper` });\n obj = obj[schemaName] as Record<string, unknown>;\n }\n\n for (const [key, field] of Object.entries(fields)) {\n if (obj[key] === undefined) {\n if (field.default !== undefined) {\n obj[key] = field.default;\n actions.push({ attempt: 0, type: \"default_fill\", detail: `Set \"${key}\" to default: ${JSON.stringify(field.default)}` });\n } else if (field.type === \"string\") {\n obj[key] = \"\";\n actions.push({ attempt: 0, type: \"missing_field\", detail: `Added empty string for \"${key}\"` });\n } else if (field.type === \"number\") {\n obj[key] = 0;\n actions.push({ attempt: 0, type: \"missing_field\", detail: `Added 0 for \"${key}\"` });\n } else if (field.type === \"boolean\") {\n obj[key] = false;\n actions.push({ attempt: 0, type: \"missing_field\", detail: `Added false for \"${key}\"` });\n } else if (field.type === \"array\") {\n obj[key] = [];\n actions.push({ attempt: 0, type: \"missing_field\", detail: `Added empty array for \"${key}\"` });\n } else if (field.type === \"object\") {\n obj[key] = {};\n actions.push({ attempt: 0, type: \"missing_field\", detail: `Added empty object for \"${key}\"` });\n }\n }\n\n if (obj[key] !== undefined) {\n const coerced = coerceType(obj[key], field.type);\n if (coerced.coerced) {\n obj[key] = coerced.value;\n actions.push({ attempt: 0, type: \"type_coercion\", detail: `Coerced \"${key}\" to ${field.type}: ${JSON.stringify(coerced.value)}` });\n }\n\n if (field.type === \"array\" && Array.isArray(obj[key]) && field.items) {\n obj[key] = (obj[key] as unknown[]).map((item, i) => {\n const itemResult = repairItem(item, field.items!);\n if (itemResult.actions.length > 0) {\n actions.push(...itemResult.actions.map(a => ({ ...a, detail: `[${i}] ${a.detail}` })));\n }\n return itemResult.data;\n });\n }\n\n if (field.type === \"object\" && typeof obj[key] === \"object\" && obj[key] !== null && field.properties) {\n const nested = repairData(obj[key], field.properties, \"\");\n if (nested.actions.length > 0) {\n actions.push(...nested.actions);\n }\n obj[key] = nested.data;\n }\n }\n }\n\n return { data: obj, actions };\n}\n\nfunction repairItem(item: unknown, field: SchemaField): { data: unknown; actions: RepairAction[] } {\n const actions: RepairAction[] = [];\n\n if (field.type === \"object\" && typeof item === \"object\" && item !== null && !Array.isArray(item) && field.properties) {\n const obj = item as Record<string, unknown>;\n for (const [key, prop] of Object.entries(field.properties)) {\n if (obj[key] === undefined) {\n if (prop.type === \"string\") {\n obj[key] = \"\";\n actions.push({ attempt: 0, type: \"missing_field\", detail: `Added empty string for \"${key}\"` });\n } else if (prop.type === \"number\") {\n obj[key] = 0;\n actions.push({ attempt: 0, type: \"missing_field\", detail: `Added 0 for \"${key}\"` });\n } else if (prop.type === \"boolean\") {\n obj[key] = false;\n actions.push({ attempt: 0, type: \"missing_field\", detail: `Added false for \"${key}\"` });\n } else if (prop.type === \"array\") {\n obj[key] = [];\n actions.push({ attempt: 0, type: \"missing_field\", detail: `Added empty array for \"${key}\"` });\n }\n }\n\n if (obj[key] !== undefined) {\n const coerced = coerceType(obj[key], prop.type);\n if (coerced.coerced) {\n obj[key] = coerced.value;\n actions.push({ attempt: 0, type: \"type_coercion\", detail: `Coerced \"${key}\" to ${prop.type}` });\n }\n }\n }\n }\n\n return { data: item, actions };\n}\n\nexport function validateAgainstSchema(data: unknown, schema: Record<string, SchemaField>): { valid: true; data: unknown } | { valid: false; errors: string[] } {\n if (typeof data !== \"object\" || data === null || Array.isArray(data)) {\n return { valid: false, errors: [\"Root value must be an object\"] };\n }\n\n const errors: string[] = [];\n const obj = data as Record<string, unknown>;\n\n for (const [key, field] of Object.entries(schema)) {\n const value = obj[key];\n\n if (value === undefined && field.required !== false) {\n errors.push(`Missing required field: \"${key}\"`);\n continue;\n }\n\n if (value === undefined) {\n continue;\n }\n\n switch (field.type) {\n case \"string\":\n if (typeof value !== \"string\") {\n errors.push(`Field \"${key}\" must be string, got ${typeof value}`);\n }\n break;\n case \"number\":\n if (typeof value !== \"number\") {\n errors.push(`Field \"${key}\" must be number, got ${typeof value}`);\n }\n break;\n case \"boolean\":\n if (typeof value !== \"boolean\") {\n errors.push(`Field \"${key}\" must be boolean, got ${typeof value}`);\n }\n break;\n case \"array\":\n if (!Array.isArray(value)) {\n errors.push(`Field \"${key}\" must be array, got ${typeof value}`);\n }\n break;\n case \"object\":\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n errors.push(`Field \"${key}\" must be object, got ${typeof value}`);\n }\n break;\n }\n }\n\n if (errors.length > 0) {\n return { valid: false, errors };\n }\n\n return { valid: true, data };\n}\n","import { ExtractionSchema, SchemaField } from \"./types\";\n\nexport function defineSchema(name: string, fields: Record<string, SchemaField>): ExtractionSchema {\n return { name, fields };\n}\n\nfunction schemaToJSONExample(fields: Record<string, SchemaField>): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [key, field] of Object.entries(fields)) {\n switch (field.type) {\n case \"string\":\n result[key] = field.description || \"...\";\n break;\n case \"number\":\n result[key] = 0;\n break;\n case \"boolean\":\n result[key] = true;\n break;\n case \"array\":\n if (field.items) {\n result[key] = [schemaToJSONExample(field.items.properties || {})];\n } else {\n result[key] = [];\n }\n break;\n case \"object\":\n if (field.properties) {\n result[key] = schemaToJSONExample(field.properties);\n } else {\n result[key] = {};\n }\n break;\n }\n }\n return result;\n}\n\nexport function buildForcedPrompt(input: string, schema: ExtractionSchema): string {\n const example = schemaToJSONExample(schema.fields);\n const exampleStr = JSON.stringify({ [schema.name]: example }, null, 2);\n\n return `TASK: Extract structured data from the text below into EXACTLY this JSON format.\n\nCRITICAL RULES:\n- Output ONLY valid JSON. No text before or after.\n- No markdown. No code blocks. No explanations.\n- Every field MUST be present with the correct type.\n- String fields: use \"\" if unknown.\n- Number fields: use 0 if unknown.\n- Boolean fields: use true or false, never null.\n- Array fields: use [] if empty, never null.\n- Object fields: use {} if empty, never null.\n\nREQUIRED JSON STRUCTURE:\n${exampleStr}\n\nTEXT TO EXTRACT FROM:\n\"\"\"\n${input}\n\"\"\"\n\nOUTPUT ONLY THE JSON OBJECT. NOTHING ELSE.`;\n}\n\nexport function buildRetryPrompt(\n input: string,\n schema: ExtractionSchema,\n previousResponse: string,\n error: string\n): string {\n const example = schemaToJSONExample(schema.fields);\n const exampleStr = JSON.stringify({ [schema.name]: example }, null, 2);\n\n return `YOUR PREVIOUS RESPONSE WAS INVALID. You MUST fix it.\n\nERROR: ${error}\n\nYOUR PREVIOUS RESPONSE:\n${previousResponse.slice(0, 1000)}\n\nWHAT YOU MUST DO NOW:\n1. Output ONLY valid JSON matching this EXACT structure\n2. No text before or after the JSON\n3. No markdown, no code blocks, no explanations\n4. Fix the errors listed above\n5. Every field MUST be present\n\nREQUIRED JSON STRUCTURE:\n${exampleStr}\n\nTEXT TO EXTRACT FROM:\n\"\"\"\n${input}\n\"\"\"\n\nOUTPUT ONLY THE JSON OBJECT. NOTHING ELSE.`;\n}\n\nexport function buildFinalPrompt(\n input: string,\n schema: ExtractionSchema,\n previousResponse: string,\n error: string\n): string {\n const example = schemaToJSONExample(schema.fields);\n const exampleStr = JSON.stringify({ [schema.name]: example }, null, 2);\n\n return `FINAL ATTEMPT. THIS IS YOUR LAST CHANCE.\n\nYOUR PREVIOUS RESPONSE FAILED VALIDATION:\n${error}\n\nYOUR PREVIOUS RESPONSE:\n${previousResponse.slice(0, 800)}\n\nYOU MUST OUTPUT EXACTLY THIS STRUCTURE. NOTHING MORE. NOTHING LESS.\nDO NOT ADD FIELDS THAT ARE NOT IN THE SCHEMA.\nDO NOT OMIT ANY FIELDS.\nDO NOT WRAP IN markdown OR code blocks.\nDO NOT ADD ANY TEXT BEFORE OR AFTER THE JSON.\n\nSTRUCTURE:\n${exampleStr}\n\nINPUT TEXT:\n\"\"\"\n${input}\n\"\"\"\n\nOUTPUT ONLY:\n`;\n}\n\nexport function schemaToPrompt(schema: ExtractionSchema): string {\n const example = schemaToJSONExample(schema.fields);\n return JSON.stringify({ [schema.name]: example }, null, 2);\n}\n\nexport function schemaToZodishString(schema: ExtractionSchema): string {\n const lines: string[] = [];\n lines.push(`Expected JSON structure for \"${schema.name}\":`);\n\n for (const [key, field] of Object.entries(schema.fields)) {\n const required = field.required !== false ? \"(required)\" : \"(optional)\";\n lines.push(` - \"${key}\": ${field.type} ${required}${field.description ? ` - ${field.description}` : \"\"}`);\n }\n\n return lines.join(\"\\n\");\n}\n","import { ExtractionSchema, ExtractionOptions, ExtractionResult, RepairAction, SchemaField } from \"./types\";\nimport { callLLM, LLMClientConfig } from \"./llm-client\";\nimport { extractJSON, repairJSON, tryParseJSON, validateAgainstSchema, repairData } from \"./validator\";\nimport { buildForcedPrompt, buildRetryPrompt, buildFinalPrompt } from \"./schema\";\n\nconst MAX_REPAIR_ATTEMPTS = 3;\n\nfunction calculateConfidence(\n attempts: number,\n repairActions: RepairAction[],\n jsonValidFirstTry: boolean\n): number {\n let score = 100;\n\n if (!jsonValidFirstTry) {\n score -= 15;\n }\n\n score -= (attempts - 1) * 10;\n\n score -= repairActions.filter(a => a.type === \"type_coercion\").length * 5;\n score -= repairActions.filter(a => a.type === \"missing_field\").length * 8;\n score -= repairActions.filter(a => a.type === \"default_fill\").length * 3;\n\n return Math.max(0, Math.min(100, score));\n}\n\nexport async function extract<T = Record<string, unknown>>(\n input: string,\n schema: ExtractionSchema,\n options: ExtractionOptions = {}\n): Promise<ExtractionResult<T>> {\n const config: LLMClientConfig = {\n apiKey: options.apiKey || process.env.OPENROUTER_API_KEY || \"\",\n model: options.model || \"openrouter/free\",\n temperature: options.temperature ?? 0,\n timeout: options.timeout ?? 30000,\n };\n\n if (!config.apiKey || config.apiKey.trim() === \"\") {\n throw new Error(\n \"OpenRouter API key required. Pass it in options or set OPENROUTER_API_KEY environment variable.\"\n );\n }\n\n const allRepairActions: RepairAction[] = [];\n let lastRaw = \"\";\n let lastError = \"\";\n let jsonValidFirstTry = false;\n let successAttempt = 0;\n\n for (let attempt = 0; attempt <= MAX_REPAIR_ATTEMPTS; attempt++) {\n let prompt: string;\n\n if (attempt === 0) {\n prompt = buildForcedPrompt(input, schema);\n } else if (attempt === 1) {\n prompt = buildRetryPrompt(input, schema, lastRaw, lastError);\n } else {\n prompt = buildFinalPrompt(input, schema, lastRaw, lastError);\n }\n\n const response = await callLLM(prompt, config);\n lastRaw = response.content;\n\n const extracted = extractJSON(response.content);\n if (!extracted) {\n lastError = \"Could not extract JSON from response. Your response contained no valid JSON.\";\n continue;\n }\n\n const parsed = tryParseJSON(extracted);\n if (!parsed.success) {\n lastError = `Invalid JSON: ${parsed.error}`;\n continue;\n }\n\n if (attempt === 0) {\n jsonValidFirstTry = true;\n }\n\n const repaired = repairJSON(extracted);\n let finalData = repaired\n ? tryParseJSON(repaired).success\n ? JSON.parse(repaired)\n : parsed.data\n : parsed.data;\n\n const repairResult = repairData(finalData, schema.fields, schema.name);\n finalData = repairResult.data;\n allRepairActions.push(...repairResult.actions);\n\n const schemaFields: Record<string, SchemaField> = {};\n for (const [key, field] of Object.entries(schema.fields)) {\n schemaFields[key] = field;\n }\n\n const validation = validateAgainstSchema(finalData, schemaFields);\n if (!validation.valid) {\n lastError = `Schema validation failed: ${validation.errors.join(\"; \")}`;\n allRepairActions.push({\n attempt,\n type: \"type_coercion\",\n detail: `Validation failed: ${validation.errors.join(\"; \")}`,\n });\n continue;\n }\n\n successAttempt = attempt + 1;\n\n return {\n data: finalData as T,\n raw: response.content,\n model: response.model,\n confidence: calculateConfidence(successAttempt, allRepairActions, jsonValidFirstTry),\n repairLog: allRepairActions,\n attempts: successAttempt,\n usage: response.usage,\n };\n }\n\n throw new Error(\n `Failed to extract valid JSON after ${MAX_REPAIR_ATTEMPTS + 1} attempts. Last error: ${lastError}`\n );\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@painitehq/structured-llm",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Force LLM output into structured, type-safe JSON. Stop your app from crashing on malformed AI responses.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",