@jterrazz/intelligence 3.0.1 → 4.0.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.
Files changed (34) hide show
  1. package/README.md +259 -55
  2. package/dist/index.cjs +594 -783
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.ts +352 -5
  5. package/dist/index.js +620 -5
  6. package/dist/index.js.map +1 -1
  7. package/package.json +26 -20
  8. package/dist/middleware/__tests__/logging.middleware.test.d.ts +0 -1
  9. package/dist/middleware/__tests__/logging.middleware.test.js +0 -390
  10. package/dist/middleware/__tests__/logging.middleware.test.js.map +0 -1
  11. package/dist/middleware/logging.middleware.d.ts +0 -21
  12. package/dist/middleware/logging.middleware.js +0 -296
  13. package/dist/middleware/logging.middleware.js.map +0 -1
  14. package/dist/parsing/__tests__/create-schema-prompt.test.d.ts +0 -1
  15. package/dist/parsing/__tests__/create-schema-prompt.test.js +0 -53
  16. package/dist/parsing/__tests__/create-schema-prompt.test.js.map +0 -1
  17. package/dist/parsing/__tests__/parse-object.test.d.ts +0 -1
  18. package/dist/parsing/__tests__/parse-object.test.js +0 -193
  19. package/dist/parsing/__tests__/parse-object.test.js.map +0 -1
  20. package/dist/parsing/__tests__/parse-text.test.d.ts +0 -1
  21. package/dist/parsing/__tests__/parse-text.test.js +0 -167
  22. package/dist/parsing/__tests__/parse-text.test.js.map +0 -1
  23. package/dist/parsing/create-schema-prompt.d.ts +0 -28
  24. package/dist/parsing/create-schema-prompt.js +0 -42
  25. package/dist/parsing/create-schema-prompt.js.map +0 -1
  26. package/dist/parsing/parse-object.d.ts +0 -33
  27. package/dist/parsing/parse-object.js +0 -360
  28. package/dist/parsing/parse-object.js.map +0 -1
  29. package/dist/parsing/parse-text.d.ts +0 -14
  30. package/dist/parsing/parse-text.js +0 -76
  31. package/dist/parsing/parse-text.js.map +0 -1
  32. package/dist/providers/openrouter.provider.d.ts +0 -36
  33. package/dist/providers/openrouter.provider.js +0 -58
  34. package/dist/providers/openrouter.provider.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,7 +1,622 @@
1
- export { createLoggingMiddleware } from './middleware/logging.middleware.js';
2
- export { createSchemaPrompt } from './parsing/create-schema-prompt.js';
3
- export { parseObject, ParseObjectError } from './parsing/parse-object.js';
4
- export { parseText } from './parsing/parse-text.js';
5
- export { createOpenRouterProvider } from './providers/openrouter.provider.js';
1
+ import { Langfuse } from "langfuse";
2
+ import { generateText } from "ai";
3
+ import { jsonrepair } from "jsonrepair";
4
+ import { z } from "zod/v4";
5
+ import { createOpenRouter } from "@openrouter/ai-sdk-provider";
6
6
 
7
+ //#region src/logging/logging.middleware.ts
8
+ /**
9
+ * Creates middleware that logs AI SDK requests and responses.
10
+ */
11
+ function createLoggingMiddleware(options) {
12
+ const { logger, include = {} } = options;
13
+ const { params: includeParams, content: includeContent, usage: includeUsage = true } = include;
14
+ return {
15
+ specificationVersion: "v3",
16
+ wrapGenerate: async ({ doGenerate, params, model }) => {
17
+ const startTime = Date.now();
18
+ logger.debug("ai.generate.start", {
19
+ model: model.modelId,
20
+ ...includeParams && { params }
21
+ });
22
+ try {
23
+ const result = await doGenerate();
24
+ const textContent = result.content.filter((c) => c.type === "text").map((c) => c.text).join("");
25
+ logger.debug("ai.generate.complete", {
26
+ model: model.modelId,
27
+ durationMs: Date.now() - startTime,
28
+ finishReason: result.finishReason,
29
+ ...includeUsage && { usage: result.usage },
30
+ ...includeContent && { content: textContent }
31
+ });
32
+ return result;
33
+ } catch (error) {
34
+ logger.error("ai.generate.error", {
35
+ model: model.modelId,
36
+ durationMs: Date.now() - startTime,
37
+ error: error instanceof Error ? error.message : "Unknown error"
38
+ });
39
+ throw error;
40
+ }
41
+ },
42
+ wrapStream: async ({ doStream, params, model }) => {
43
+ const startTime = Date.now();
44
+ logger.debug("ai.stream.start", {
45
+ model: model.modelId,
46
+ ...includeParams && { params }
47
+ });
48
+ try {
49
+ const result = await doStream();
50
+ const chunks = [];
51
+ const transformStream = new TransformStream({
52
+ transform(chunk, controller) {
53
+ if (includeContent && chunk.type === "text-delta") chunks.push(chunk.delta);
54
+ controller.enqueue(chunk);
55
+ },
56
+ flush() {
57
+ logger.debug("ai.stream.complete", {
58
+ model: model.modelId,
59
+ durationMs: Date.now() - startTime,
60
+ ...includeContent && { content: chunks.join("") }
61
+ });
62
+ }
63
+ });
64
+ return {
65
+ specificationVersion: "v3",
66
+ ...result,
67
+ stream: result.stream.pipeThrough(transformStream)
68
+ };
69
+ } catch (error) {
70
+ logger.error("ai.stream.error", {
71
+ model: model.modelId,
72
+ durationMs: Date.now() - startTime,
73
+ error: error instanceof Error ? error.message : "Unknown error"
74
+ });
75
+ throw error;
76
+ }
77
+ }
78
+ };
79
+ }
80
+
81
+ //#endregion
82
+ //#region src/observability/observability.middleware.ts
83
+ /**
84
+ * Helper to create type-safe observability metadata for providerOptions
85
+ */
86
+ function withObservability(meta) {
87
+ return { observability: meta };
88
+ }
89
+ /**
90
+ * Creates middleware that sends generation data to an observability platform.
91
+ */
92
+ function createObservabilityMiddleware(options) {
93
+ const { observability, providerMetadata } = options;
94
+ return {
95
+ specificationVersion: "v3",
96
+ wrapGenerate: async ({ doGenerate, params, model }) => {
97
+ const startTime = /* @__PURE__ */ new Date();
98
+ const meta = params.providerOptions?.observability;
99
+ const result = await doGenerate();
100
+ const endTime = /* @__PURE__ */ new Date();
101
+ if (meta?.traceId) {
102
+ const extracted = providerMetadata?.extract(result.providerMetadata);
103
+ const outputText = result.content.filter((c) => c.type === "text").map((c) => c.text).join("");
104
+ observability.generation({
105
+ traceId: meta.traceId,
106
+ name: meta.name ?? "generation",
107
+ model: model.modelId,
108
+ input: params.prompt,
109
+ output: outputText,
110
+ startTime,
111
+ endTime,
112
+ usage: extracted?.usage,
113
+ cost: extracted?.cost,
114
+ metadata: meta.metadata
115
+ });
116
+ }
117
+ return result;
118
+ },
119
+ wrapStream: async ({ doStream, params, model }) => {
120
+ const startTime = /* @__PURE__ */ new Date();
121
+ const meta = params.providerOptions?.observability;
122
+ const result = await doStream();
123
+ if (!meta?.traceId) return result;
124
+ const chunks = [];
125
+ const transformStream = new TransformStream({
126
+ transform(chunk, controller) {
127
+ if (chunk.type === "text-delta") chunks.push(chunk.delta);
128
+ controller.enqueue(chunk);
129
+ },
130
+ flush() {
131
+ const endTime = /* @__PURE__ */ new Date();
132
+ observability.generation({
133
+ traceId: meta.traceId,
134
+ name: meta.name ?? "generation",
135
+ model: model.modelId,
136
+ input: params.prompt,
137
+ output: chunks.join(""),
138
+ startTime,
139
+ endTime,
140
+ metadata: meta.metadata
141
+ });
142
+ }
143
+ });
144
+ return {
145
+ specificationVersion: "v3",
146
+ ...result,
147
+ stream: result.stream.pipeThrough(transformStream)
148
+ };
149
+ }
150
+ };
151
+ }
152
+
153
+ //#endregion
154
+ //#region src/observability/langfuse.adapter.ts
155
+ /**
156
+ * Langfuse adapter implementing ObservabilityPort
157
+ */
158
+ var LangfuseAdapter = class {
159
+ client;
160
+ constructor(config) {
161
+ this.client = new Langfuse({
162
+ secretKey: config.secretKey,
163
+ publicKey: config.publicKey,
164
+ baseUrl: config.baseUrl,
165
+ environment: config.environment,
166
+ release: config.release
167
+ });
168
+ }
169
+ async flush() {
170
+ await this.client.flushAsync();
171
+ }
172
+ generation(params) {
173
+ const usageDetails = params.usage ? {
174
+ input: params.usage.input,
175
+ output: params.usage.output,
176
+ total: params.usage.total ?? params.usage.input + params.usage.output,
177
+ ...params.usage.reasoning !== void 0 && { reasoning: params.usage.reasoning },
178
+ ...params.usage.cacheRead !== void 0 && { cache_read: params.usage.cacheRead },
179
+ ...params.usage.cacheWrite !== void 0 && { cache_write: params.usage.cacheWrite }
180
+ } : void 0;
181
+ const costDetails = params.cost ? {
182
+ total: params.cost.total,
183
+ ...params.cost.input !== void 0 && { input: params.cost.input },
184
+ ...params.cost.output !== void 0 && { output: params.cost.output }
185
+ } : void 0;
186
+ this.client.generation({
187
+ traceId: params.traceId,
188
+ name: params.name,
189
+ model: params.model,
190
+ input: params.input,
191
+ output: params.output,
192
+ startTime: params.startTime,
193
+ endTime: params.endTime,
194
+ usageDetails,
195
+ costDetails,
196
+ metadata: params.metadata
197
+ });
198
+ }
199
+ async shutdown() {
200
+ await this.client.shutdownAsync();
201
+ }
202
+ trace(params) {
203
+ this.client.trace({
204
+ id: params.id,
205
+ name: params.name,
206
+ metadata: params.metadata
207
+ });
208
+ }
209
+ };
210
+
211
+ //#endregion
212
+ //#region src/observability/noop.adapter.ts
213
+ /**
214
+ * No-op adapter that silently discards all observability data.
215
+ * Useful for testing, development, or when observability is disabled.
216
+ */
217
+ var NoopObservabilityAdapter = class {
218
+ async flush() {}
219
+ generation(_params) {}
220
+ async shutdown() {}
221
+ trace(_params) {}
222
+ };
223
+
224
+ //#endregion
225
+ //#region src/result/result.ts
226
+ /**
227
+ * Create a successful result
228
+ */
229
+ function generationSuccess(data) {
230
+ return {
231
+ success: true,
232
+ data
233
+ };
234
+ }
235
+ /**
236
+ * Create a failed result
237
+ */
238
+ function generationFailure(code, message, cause) {
239
+ return {
240
+ success: false,
241
+ error: {
242
+ code,
243
+ message,
244
+ cause
245
+ }
246
+ };
247
+ }
248
+ /**
249
+ * Classify an error into a GenerationErrorCode
250
+ */
251
+ function classifyError(error) {
252
+ if (error instanceof Error) {
253
+ const message = error.message.toLowerCase();
254
+ if (message.includes("timeout") || message.includes("timed out")) return "TIMEOUT";
255
+ if (message.includes("rate limit") || message.includes("429")) return "RATE_LIMITED";
256
+ if (message.includes("parse") || message.includes("json") || message.includes("unexpected token")) return "PARSING_FAILED";
257
+ if (message.includes("valid") || message.includes("schema") || message.includes("zod")) return "VALIDATION_FAILED";
258
+ }
259
+ return "AI_GENERATION_FAILED";
260
+ }
261
+ /**
262
+ * Check if a result is successful (type guard)
263
+ */
264
+ function isSuccess(result) {
265
+ return result.success;
266
+ }
267
+ /**
268
+ * Check if a result is a failure (type guard)
269
+ */
270
+ function isFailure(result) {
271
+ return !result.success;
272
+ }
273
+ /**
274
+ * Unwrap a result, throwing if it fails
275
+ */
276
+ function unwrap(result) {
277
+ if (result.success) return result.data;
278
+ throw new Error(`${result.error.code}: ${result.error.message}`);
279
+ }
280
+ /**
281
+ * Unwrap a result with a default value for failures
282
+ */
283
+ function unwrapOr(result, defaultValue) {
284
+ return result.success ? result.data : defaultValue;
285
+ }
286
+
287
+ //#endregion
288
+ //#region src/parsing/parse-object.ts
289
+ const MARKDOWN_CODE_BLOCK_RE = /```(?:json)?\r?\n([^`]*?)\r?\n```/g;
290
+ /**
291
+ * Error thrown when object parsing fails.
292
+ * Contains the original text for debugging purposes.
293
+ */
294
+ var ParseObjectError = class extends Error {
295
+ name = "ParseObjectError";
296
+ constructor(message, cause, text) {
297
+ super(message);
298
+ this.cause = cause;
299
+ this.text = text;
300
+ }
301
+ };
302
+ function convertToPrimitive(value, schema) {
303
+ if (schema instanceof z.ZodBoolean) return Boolean(value);
304
+ if (schema instanceof z.ZodNull) return null;
305
+ if (schema instanceof z.ZodNumber) return Number(value);
306
+ if (schema instanceof z.ZodString) return String(value);
307
+ return value;
308
+ }
309
+ function extractArray(text, originalText) {
310
+ const start = text.indexOf("[");
311
+ const end = text.lastIndexOf("]");
312
+ if (start === -1 || end === -1) throw new ParseObjectError("No array found in response", void 0, originalText);
313
+ try {
314
+ const raw = text.slice(start, end + 1);
315
+ return JSON.parse(jsonrepair(raw));
316
+ } catch (error) {
317
+ throw new ParseObjectError("Failed to parse array JSON", error, originalText);
318
+ }
319
+ }
320
+ function extractObject(text, originalText) {
321
+ const start = text.indexOf("{");
322
+ const end = text.lastIndexOf("}");
323
+ if (start === -1 || end === -1) throw new ParseObjectError("No object found in response", void 0, originalText);
324
+ try {
325
+ const raw = text.slice(start, end + 1);
326
+ return JSON.parse(jsonrepair(raw));
327
+ } catch (error) {
328
+ throw new ParseObjectError("Failed to parse object JSON", error, originalText);
329
+ }
330
+ }
331
+ function extractPrimitive(text, schema) {
332
+ const trimmed = text.trim();
333
+ try {
334
+ return convertToPrimitive(JSON.parse(trimmed), schema);
335
+ } catch {
336
+ return convertToPrimitive(trimmed, schema);
337
+ }
338
+ }
339
+ function extractBySchemaType(text, schema, originalText) {
340
+ if (schema instanceof z.ZodArray) return extractArray(text, originalText);
341
+ if (schema instanceof z.ZodObject) return extractObject(text, originalText);
342
+ if (schema instanceof z.ZodBoolean || schema instanceof z.ZodNull || schema instanceof z.ZodNumber || schema instanceof z.ZodString) return extractPrimitive(text, schema);
343
+ if (schema instanceof z.ZodUnion || schema instanceof z.ZodDiscriminatedUnion) {
344
+ if (text.indexOf("{") !== -1) return extractObject(text, originalText);
345
+ if (text.indexOf("[") !== -1) return extractArray(text, originalText);
346
+ throw new ParseObjectError("No object or array found for union type", void 0, originalText);
347
+ }
348
+ if (schema instanceof z.ZodOptional || schema instanceof z.ZodNullable) return extractBySchemaType(text, schema.unwrap(), originalText);
349
+ if (schema instanceof z.ZodDefault) return extractBySchemaType(text, schema.def.innerType, originalText);
350
+ if (schema instanceof z.ZodPipe) return extractBySchemaType(text, schema.def.in, originalText);
351
+ throw new ParseObjectError("Unsupported schema type", void 0, originalText);
352
+ }
353
+ function extractJsonFromCodeBlock(block) {
354
+ const content = block.replace(/```(?:json)?\r?\n([^`]*?)\r?\n```/, "$1").trim();
355
+ try {
356
+ JSON.parse(content);
357
+ return content;
358
+ } catch {
359
+ return null;
360
+ }
361
+ }
362
+ function findLongestString(strings) {
363
+ return strings.reduce((longest, current) => current.length > longest.length ? current : longest);
364
+ }
365
+ function findJsonStructures(text) {
366
+ const matches = [];
367
+ let depth = 0;
368
+ let start = -1;
369
+ for (let i = 0; i < text.length; i++) {
370
+ const char = text[i];
371
+ if (char === "{" || char === "[") {
372
+ if (depth === 0) start = i;
373
+ depth++;
374
+ } else if (char === "}" || char === "]") {
375
+ depth--;
376
+ if (depth === 0 && start !== -1) {
377
+ const candidate = text.slice(start, i + 1);
378
+ try {
379
+ JSON.parse(candidate);
380
+ matches.push(candidate);
381
+ } catch {}
382
+ }
383
+ }
384
+ }
385
+ return matches;
386
+ }
387
+ function extractJsonString(text) {
388
+ const codeBlocks = text.match(MARKDOWN_CODE_BLOCK_RE);
389
+ if (codeBlocks && codeBlocks.length > 0) {
390
+ const validBlocks = codeBlocks.map((block) => extractJsonFromCodeBlock(block)).filter((block) => block !== null);
391
+ if (validBlocks.length > 0) return findLongestString(validBlocks);
392
+ }
393
+ const structures = findJsonStructures(text);
394
+ if (structures.length > 0) return findLongestString(structures);
395
+ return text.replace(/\s+/g, " ").trim();
396
+ }
397
+ function unescapeString(text) {
398
+ return text.replace(/\\"/g, "\"").replace(/\\n/g, "\n").replace(/\\r/g, "\r").replace(/\\t/g, " ").replace(/\\\\/g, "\\").replace(/\\u([0-9a-fA-F]{4})/g, (_, code) => String.fromCharCode(Number.parseInt(code, 16)));
399
+ }
400
+ function unescapeJsonValues(json) {
401
+ if (typeof json === "string") return unescapeString(json);
402
+ if (Array.isArray(json)) return json.map(unescapeJsonValues);
403
+ if (typeof json === "object" && json !== null) return Object.fromEntries(Object.entries(json).map(([key, value]) => [key, unescapeJsonValues(value)]));
404
+ return json;
405
+ }
406
+ /**
407
+ * Parses AI-generated text into structured data validated against a Zod schema.
408
+ *
409
+ * Handles common AI response formats:
410
+ * - JSON wrapped in markdown code blocks
411
+ * - JSON embedded in prose text
412
+ * - Malformed JSON (auto-repaired)
413
+ * - Escaped unicode and special characters
414
+ *
415
+ * @param text - The raw AI response text
416
+ * @param schema - A Zod schema to validate and type the result
417
+ * @returns The parsed and validated data
418
+ * @throws {ParseObjectError} When parsing or validation fails
419
+ *
420
+ * @example
421
+ * ```ts
422
+ * const schema = z.object({ title: z.string(), tags: z.array(z.string()) });
423
+ * const result = parseObject(aiResponse, schema);
424
+ * // result is typed as { title: string; tags: string[] }
425
+ * ```
426
+ */
427
+ function parseObject(text, schema) {
428
+ try {
429
+ const unescaped = unescapeJsonValues(extractBySchemaType(extractJsonString(text), schema, text));
430
+ return schema.parse(unescaped);
431
+ } catch (error) {
432
+ if (error instanceof ParseObjectError) throw error;
433
+ if (error instanceof z.ZodError) throw new ParseObjectError("Failed to validate response against schema", error, text);
434
+ throw error;
435
+ }
436
+ }
437
+
438
+ //#endregion
439
+ //#region src/generation/generate-structured.ts
440
+ /**
441
+ * Generate structured data from an AI model with automatic parsing and error handling.
442
+ * Observability is handled by middleware - no metadata exposed to caller.
443
+ */
444
+ async function generateStructured(options) {
445
+ const { model, prompt, system, schema, providerOptions, abortSignal, maxOutputTokens, temperature } = options;
446
+ try {
447
+ const response = await generateText({
448
+ model,
449
+ prompt,
450
+ system,
451
+ providerOptions,
452
+ abortSignal,
453
+ maxOutputTokens,
454
+ temperature
455
+ });
456
+ if (!response.text || response.text.trim() === "") return generationFailure("EMPTY_RESULT", "AI returned empty response");
457
+ try {
458
+ return generationSuccess(parseObject(response.text, schema));
459
+ } catch (error) {
460
+ if (error instanceof ParseObjectError) return generationFailure("PARSING_FAILED", error.message, error);
461
+ return generationFailure("VALIDATION_FAILED", "Schema validation failed", error);
462
+ }
463
+ } catch (error) {
464
+ return generationFailure(classifyError(error), error instanceof Error ? error.message : "Unknown error", error);
465
+ }
466
+ }
467
+
468
+ //#endregion
469
+ //#region src/parsing/create-schema-prompt.ts
470
+ /**
471
+ * Creates a system prompt that instructs the model to output structured data
472
+ * matching the provided Zod schema.
473
+ *
474
+ * Use this with `generateText` when the provider doesn't support native
475
+ * structured outputs, then parse the response with `parseObject`.
476
+ *
477
+ * @param schema - A Zod schema defining the expected output structure
478
+ * @returns A system prompt string with JSON schema instructions
479
+ *
480
+ * @example
481
+ * ```ts
482
+ * import { generateText } from 'ai';
483
+ * import { createSchemaPrompt, parseObject } from '@jterrazz/intelligence';
484
+ *
485
+ * const schema = z.object({ title: z.string(), tags: z.array(z.string()) });
486
+ *
487
+ * const { text } = await generateText({
488
+ * model,
489
+ * prompt: 'Generate an article about TypeScript',
490
+ * system: createSchemaPrompt(schema),
491
+ * });
492
+ *
493
+ * const result = parseObject(text, schema);
494
+ * ```
495
+ */
496
+ function createSchemaPrompt(schema) {
497
+ const jsonSchema = z.toJSONSchema(schema);
498
+ const schemaJson = JSON.stringify(jsonSchema, null, 2);
499
+ if ([
500
+ "boolean",
501
+ "integer",
502
+ "number",
503
+ "string"
504
+ ].includes(jsonSchema.type)) return `<OUTPUT_FORMAT>
505
+ You must respond with a ${jsonSchema.type} value that matches this schema:
506
+
507
+ \`\`\`json
508
+ ${schemaJson}
509
+ \`\`\`
510
+
511
+ Your response should be only the ${jsonSchema.type} value, without any JSON wrapping or additional text.
512
+ </OUTPUT_FORMAT>`;
513
+ return `<OUTPUT_FORMAT>
514
+ You must respond with valid JSON that matches this JSON schema:
515
+
516
+ \`\`\`json
517
+ ${schemaJson}
518
+ \`\`\`
519
+
520
+ Your response must be parseable JSON that validates against this schema. Do not include any text outside the JSON.
521
+ </OUTPUT_FORMAT>`;
522
+ }
523
+
524
+ //#endregion
525
+ //#region src/parsing/parse-text.ts
526
+ const INVISIBLE_CHARS_RE = /[\u00AD\u180E\u200B-\u200C\u200E-\u200F\u202A-\u202E\u2060-\u2064\u2066-\u2069\uFEFF]/g;
527
+ const ASCII_CTRL_RE = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g;
528
+ const SPACE_LIKE_RE = /[\u00A0\u1680\u2000-\u200A\u202F\u205F\u3000]/g;
529
+ const MULTIPLE_SPACES_RE = / {2,}/g;
530
+ const CR_RE = /\r\n?/g;
531
+ const CITATION_RE = / *\(oaicite:\d+\)\{index=\d+\}/g;
532
+ const EM_DASH_SEPARATOR_RE = /\s*[—–―‒]\s*/g;
533
+ const TYPOGRAPHY_REPLACEMENTS = [
534
+ {
535
+ pattern: /[\u2018\u2019\u201A]/g,
536
+ replacement: "'"
537
+ },
538
+ {
539
+ pattern: /[\u201C\u201D\u201E]/g,
540
+ replacement: "\""
541
+ },
542
+ {
543
+ pattern: /\u2026/g,
544
+ replacement: "..."
545
+ },
546
+ {
547
+ pattern: /[\u2022\u25AA-\u25AB\u25B8-\u25B9\u25CF]/g,
548
+ replacement: "-"
549
+ }
550
+ ];
551
+ /**
552
+ * Parses and sanitizes text by removing AI artifacts and normalizing typography.
553
+ *
554
+ * @param text - The text to parse
555
+ * @param options - Parsing options
556
+ * @returns The cleaned text
557
+ */
558
+ function parseText(text, options = {}) {
559
+ const { normalizeEmDashesToCommas = true, collapseSpaces = true } = options;
560
+ if (!text) return "";
561
+ let result = text;
562
+ result = result.replace(CR_RE, "\n");
563
+ result = result.replace(CITATION_RE, "");
564
+ result = result.normalize("NFKC");
565
+ result = result.replace(INVISIBLE_CHARS_RE, "");
566
+ result = result.replace(ASCII_CTRL_RE, "");
567
+ if (normalizeEmDashesToCommas) result = result.replace(EM_DASH_SEPARATOR_RE, ", ");
568
+ result = result.replace(SPACE_LIKE_RE, " ");
569
+ for (const { pattern, replacement } of TYPOGRAPHY_REPLACEMENTS) result = result.replace(pattern, replacement);
570
+ if (collapseSpaces) result = result.replace(MULTIPLE_SPACES_RE, " ").trim();
571
+ return result;
572
+ }
573
+
574
+ //#endregion
575
+ //#region src/provider/openrouter.provider.ts
576
+ /**
577
+ * Creates an OpenRouter provider for AI SDK models.
578
+ *
579
+ * @example
580
+ * ```ts
581
+ * const provider = createOpenRouterProvider({ apiKey: process.env.OPENROUTER_API_KEY });
582
+ * const model = provider.model('anthropic/claude-sonnet-4-20250514');
583
+ *
584
+ * const { text } = await generateText({ model, prompt: 'Hello!' });
585
+ * ```
586
+ */
587
+ function createOpenRouterProvider(config) {
588
+ const openrouter = createOpenRouter({ apiKey: config.apiKey });
589
+ return { model(name, options = {}) {
590
+ return openrouter(name, {
591
+ ...options.maxTokens !== void 0 && { maxTokens: options.maxTokens },
592
+ ...options.reasoning && { extraBody: { reasoning: options.reasoning } }
593
+ });
594
+ } };
595
+ }
596
+
597
+ //#endregion
598
+ //#region src/provider/openrouter-metadata.adapter.ts
599
+ /**
600
+ * OpenRouter adapter for extracting usage and cost from provider metadata
601
+ */
602
+ var OpenRouterMetadataAdapter = class {
603
+ extract(providerMetadata) {
604
+ const meta = providerMetadata?.openrouter;
605
+ if (!meta?.usage) return {};
606
+ const usage = meta.usage;
607
+ return {
608
+ usage: {
609
+ input: usage.promptTokens ?? 0,
610
+ output: usage.completionTokens ?? 0,
611
+ total: usage.totalTokens,
612
+ reasoning: usage.completionTokensDetails?.reasoningTokens,
613
+ cacheRead: usage.promptTokensDetails?.cachedTokens
614
+ },
615
+ cost: usage.cost !== void 0 ? { total: usage.cost } : void 0
616
+ };
617
+ }
618
+ };
619
+
620
+ //#endregion
621
+ export { LangfuseAdapter, NoopObservabilityAdapter, OpenRouterMetadataAdapter, ParseObjectError, classifyError, createLoggingMiddleware, createObservabilityMiddleware, createOpenRouterProvider, createSchemaPrompt, generateStructured, generationFailure, generationSuccess, isFailure, isSuccess, parseObject, parseText, unwrap, unwrapOr, withObservability };
7
622
  //# sourceMappingURL=index.js.map