@jterrazz/intelligence 4.0.4 → 4.1.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/README.md CHANGED
@@ -15,26 +15,26 @@ npm install @jterrazz/intelligence ai zod
15
15
  Combines `generateText` + `parseObject` + error classification into a single function that returns a discriminated union result.
16
16
 
17
17
  ```typescript
18
- import { generateStructured, withObservability } from "@jterrazz/intelligence";
19
- import { z } from "zod";
18
+ import { generateStructured, withObservability } from '@jterrazz/intelligence';
19
+ import { z } from 'zod';
20
20
 
21
21
  const schema = z.object({
22
- sentiment: z.string(),
23
- score: z.number(),
22
+ sentiment: z.string(),
23
+ score: z.number(),
24
24
  });
25
25
 
26
26
  const result = await generateStructured({
27
- model,
28
- prompt: "Analyze this article...",
29
- schema,
30
- providerOptions: withObservability({ traceId: "trace-123" }),
27
+ model,
28
+ prompt: 'Analyze this article...',
29
+ schema,
30
+ providerOptions: withObservability({ traceId: 'trace-123' }),
31
31
  });
32
32
 
33
33
  if (result.success) {
34
- console.log(result.data.sentiment, result.data.score);
34
+ console.log(result.data.sentiment, result.data.score);
35
35
  } else {
36
- // Typed error with code: TIMEOUT | RATE_LIMITED | PARSING_FAILED | etc.
37
- console.error(result.error.code, result.error.message);
36
+ // Typed error with code: TIMEOUT | RATE_LIMITED | PARSING_FAILED | etc.
37
+ console.error(result.error.code, result.error.message);
38
38
  }
39
39
  ```
40
40
 
@@ -44,23 +44,23 @@ Discriminated union result type for explicit error handling.
44
44
 
45
45
  ```typescript
46
46
  import {
47
- generationSuccess,
48
- generationFailure,
49
- isSuccess,
50
- isFailure,
51
- unwrap,
52
- unwrapOr,
53
- classifyError,
54
- type GenerationResult,
55
- } from "@jterrazz/intelligence";
47
+ generationSuccess,
48
+ generationFailure,
49
+ isSuccess,
50
+ isFailure,
51
+ unwrap,
52
+ unwrapOr,
53
+ classifyError,
54
+ type GenerationResult,
55
+ } from '@jterrazz/intelligence';
56
56
 
57
57
  // Create results
58
- const success = generationSuccess({ data: "value" });
59
- const failure = generationFailure("TIMEOUT", "Request timed out");
58
+ const success = generationSuccess({ data: 'value' });
59
+ const failure = generationFailure('TIMEOUT', 'Request timed out');
60
60
 
61
61
  // Type guards
62
62
  if (isSuccess(result)) {
63
- console.log(result.data);
63
+ console.log(result.data);
64
64
  }
65
65
 
66
66
  // Unwrap with default
@@ -68,9 +68,9 @@ const value = unwrapOr(result, defaultValue);
68
68
 
69
69
  // Classify errors automatically
70
70
  try {
71
- await someOperation();
71
+ await someOperation();
72
72
  } catch (error) {
73
- const code = classifyError(error); // TIMEOUT, RATE_LIMITED, PARSING_FAILED, etc.
73
+ const code = classifyError(error); // TIMEOUT, RATE_LIMITED, PARSING_FAILED, etc.
74
74
  }
75
75
  ```
76
76
 
@@ -81,26 +81,26 @@ Composable middlewares that wrap AI SDK models. Stack them together for logging,
81
81
  ### Composing Middlewares
82
82
 
83
83
  ```typescript
84
- import { wrapLanguageModel } from "ai";
84
+ import { wrapLanguageModel } from 'ai';
85
85
  import {
86
- createLoggingMiddleware,
87
- createObservabilityMiddleware,
88
- LangfuseAdapter,
89
- OpenRouterMetadataAdapter,
90
- } from "@jterrazz/intelligence";
86
+ createLoggingMiddleware,
87
+ createObservabilityMiddleware,
88
+ LangfuseAdapter,
89
+ OpenRouterMetadataAdapter,
90
+ } from '@jterrazz/intelligence';
91
91
 
92
92
  const model = wrapLanguageModel({
93
- model: provider.model("anthropic/claude-sonnet-4-20250514"),
94
- middleware: [
95
- createLoggingMiddleware({ logger, include: { usage: true } }),
96
- createObservabilityMiddleware({
97
- observability: new LangfuseAdapter({
98
- secretKey: process.env.LANGFUSE_SECRET_KEY,
99
- publicKey: process.env.LANGFUSE_PUBLIC_KEY,
100
- }),
101
- providerMetadata: new OpenRouterMetadataAdapter(),
102
- }),
103
- ],
93
+ model: provider.model('anthropic/claude-sonnet-4-20250514'),
94
+ middleware: [
95
+ createLoggingMiddleware({ logger, include: { usage: true } }),
96
+ createObservabilityMiddleware({
97
+ observability: new LangfuseAdapter({
98
+ secretKey: process.env.LANGFUSE_SECRET_KEY,
99
+ publicKey: process.env.LANGFUSE_PUBLIC_KEY,
100
+ }),
101
+ providerMetadata: new OpenRouterMetadataAdapter(),
102
+ }),
103
+ ],
104
104
  });
105
105
  ```
106
106
 
@@ -109,22 +109,22 @@ const model = wrapLanguageModel({
109
109
  Logs AI SDK requests with timing, usage, and optional content.
110
110
 
111
111
  ```typescript
112
- import { wrapLanguageModel, generateText } from "ai";
113
- import { createLoggingMiddleware } from "@jterrazz/intelligence";
112
+ import { wrapLanguageModel, generateText } from 'ai';
113
+ import { createLoggingMiddleware } from '@jterrazz/intelligence';
114
114
 
115
115
  const model = wrapLanguageModel({
116
- model: provider.model("anthropic/claude-sonnet-4-20250514"),
117
- middleware: createLoggingMiddleware({
118
- logger,
119
- include: {
120
- params: false, // Log request params
121
- content: false, // Log response content
122
- usage: true, // Log token usage (default: true)
123
- },
124
- }),
116
+ model: provider.model('anthropic/claude-sonnet-4-20250514'),
117
+ middleware: createLoggingMiddleware({
118
+ logger,
119
+ include: {
120
+ params: false, // Log request params
121
+ content: false, // Log response content
122
+ usage: true, // Log token usage (default: true)
123
+ },
124
+ }),
125
125
  });
126
126
 
127
- await generateText({ model, prompt: "Hello!" });
127
+ await generateText({ model, prompt: 'Hello!' });
128
128
  // Logs: ai.generate.start, ai.generate.complete (with durationMs, usage, etc.)
129
129
  ```
130
130
 
@@ -133,32 +133,32 @@ await generateText({ model, prompt: "Hello!" });
133
133
  Sends generation data to observability platforms (Langfuse, etc.).
134
134
 
135
135
  ```typescript
136
- import { wrapLanguageModel, generateText } from "ai";
136
+ import { wrapLanguageModel, generateText } from 'ai';
137
137
  import {
138
- createObservabilityMiddleware,
139
- withObservability,
140
- LangfuseAdapter,
141
- } from "@jterrazz/intelligence";
138
+ createObservabilityMiddleware,
139
+ withObservability,
140
+ LangfuseAdapter,
141
+ } from '@jterrazz/intelligence';
142
142
 
143
143
  const observability = new LangfuseAdapter({
144
- secretKey: process.env.LANGFUSE_SECRET_KEY,
145
- publicKey: process.env.LANGFUSE_PUBLIC_KEY,
144
+ secretKey: process.env.LANGFUSE_SECRET_KEY,
145
+ publicKey: process.env.LANGFUSE_PUBLIC_KEY,
146
146
  });
147
147
 
148
148
  const model = wrapLanguageModel({
149
- model: provider.model("anthropic/claude-sonnet-4-20250514"),
150
- middleware: createObservabilityMiddleware({ observability }),
149
+ model: provider.model('anthropic/claude-sonnet-4-20250514'),
150
+ middleware: createObservabilityMiddleware({ observability }),
151
151
  });
152
152
 
153
153
  // Use withObservability() helper for type-safe metadata
154
154
  await generateText({
155
- model,
156
- prompt: "Analyze this...",
157
- providerOptions: withObservability({
158
- traceId: "trace-123",
159
- name: "analyzer",
160
- metadata: { userId: "user-1" },
161
- }),
155
+ model,
156
+ prompt: 'Analyze this...',
157
+ providerOptions: withObservability({
158
+ traceId: 'trace-123',
159
+ name: 'analyzer',
160
+ metadata: { userId: 'user-1' },
161
+ }),
162
162
  });
163
163
  ```
164
164
 
@@ -192,12 +192,12 @@ class AnthropicMetadataAdapter implements ProviderMetadataPort {
192
192
  Extracts and validates JSON from messy AI outputs (markdown blocks, malformed syntax).
193
193
 
194
194
  ````typescript
195
- import { parseObject } from "@jterrazz/intelligence";
196
- import { z } from "zod";
195
+ import { parseObject } from '@jterrazz/intelligence';
196
+ import { z } from 'zod';
197
197
 
198
198
  const schema = z.object({
199
- title: z.string(),
200
- tags: z.array(z.string()),
199
+ title: z.string(),
200
+ tags: z.array(z.string()),
201
201
  });
202
202
 
203
203
  const text = '```json\n{"title": "Hello", "tags": ["ai"]}\n```';
@@ -210,16 +210,16 @@ const result = parseObject(text, schema);
210
210
  Creates system prompt instructions for models without native structured output.
211
211
 
212
212
  ```typescript
213
- import { generateText } from "ai";
214
- import { createSchemaPrompt, parseObject } from "@jterrazz/intelligence";
215
- import { z } from "zod";
213
+ import { generateText } from 'ai';
214
+ import { createSchemaPrompt, parseObject } from '@jterrazz/intelligence';
215
+ import { z } from 'zod';
216
216
 
217
217
  const schema = z.object({ summary: z.string(), score: z.number() });
218
218
 
219
219
  const { text } = await generateText({
220
- model,
221
- prompt: "Analyze this article...",
222
- system: createSchemaPrompt(schema),
220
+ model,
221
+ prompt: 'Analyze this article...',
222
+ system: createSchemaPrompt(schema),
223
223
  });
224
224
 
225
225
  const result = parseObject(text, schema);
@@ -230,7 +230,7 @@ const result = parseObject(text, schema);
230
230
  Removes invisible characters, normalizes typography, cleans AI artifacts.
231
231
 
232
232
  ```typescript
233
- import { parseText } from "@jterrazz/intelligence";
233
+ import { parseText } from '@jterrazz/intelligence';
234
234
 
235
235
  const clean = parseText(messyAiOutput);
236
236
  // Removes: BOM, zero-width chars, citation markers
@@ -242,22 +242,22 @@ const clean = parseText(messyAiOutput);
242
242
  ### `createOpenRouterProvider` - OpenRouter for AI SDK
243
243
 
244
244
  ```typescript
245
- import { generateText } from "ai";
246
- import { createOpenRouterProvider } from "@jterrazz/intelligence";
245
+ import { generateText } from 'ai';
246
+ import { createOpenRouterProvider } from '@jterrazz/intelligence';
247
247
 
248
248
  const provider = createOpenRouterProvider({
249
- apiKey: process.env.OPENROUTER_API_KEY,
249
+ apiKey: process.env.OPENROUTER_API_KEY,
250
250
  });
251
251
 
252
252
  const { text } = await generateText({
253
- model: provider.model("anthropic/claude-sonnet-4-20250514"),
254
- prompt: "Hello!",
253
+ model: provider.model('anthropic/claude-sonnet-4-20250514'),
254
+ prompt: 'Hello!',
255
255
  });
256
256
 
257
257
  // With reasoning models
258
- const reasoningModel = provider.model("anthropic/claude-sonnet-4-20250514", {
259
- maxTokens: 16000,
260
- reasoning: { effort: "high" },
258
+ const reasoningModel = provider.model('anthropic/claude-sonnet-4-20250514', {
259
+ maxTokens: 16000,
260
+ reasoning: { effort: 'high' },
261
261
  });
262
262
  ```
263
263
 
package/dist/index.cjs CHANGED
@@ -1,4 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_parse_text = require("./parse-text.cjs");
2
3
  let langfuse = require("langfuse");
3
4
  let ai = require("ai");
4
5
  let jsonrepair = require("jsonrepair");
@@ -517,55 +518,6 @@ Your response must be parseable JSON that validates against this schema. Do not
517
518
  </OUTPUT_FORMAT>`;
518
519
  }
519
520
  //#endregion
520
- //#region src/parsing/parse-text.ts
521
- const INVISIBLE_CHARS_RE = /[\u00AD\u180E\u200B-\u200C\u200E-\u200F\u202A-\u202E\u2060-\u2064\u2066-\u2069\uFEFF]/g;
522
- const ASCII_CTRL_RE = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g;
523
- const SPACE_LIKE_RE = /[\u00A0\u1680\u2000-\u200A\u202F\u205F\u3000]/g;
524
- const MULTIPLE_SPACES_RE = / {2,}/g;
525
- const CR_RE = /\r\n?/g;
526
- const CITATION_RE = / *\(oaicite:\d+\)\{index=\d+\}/g;
527
- const EM_DASH_SEPARATOR_RE = /\s*[—–―‒]\s*/g;
528
- const TYPOGRAPHY_REPLACEMENTS = [
529
- {
530
- pattern: /[\u2018\u2019\u201A]/g,
531
- replacement: "'"
532
- },
533
- {
534
- pattern: /[\u201C\u201D\u201E]/g,
535
- replacement: "\""
536
- },
537
- {
538
- pattern: /\u2026/g,
539
- replacement: "..."
540
- },
541
- {
542
- pattern: /[\u2022\u25AA-\u25AB\u25B8-\u25B9\u25CF]/g,
543
- replacement: "-"
544
- }
545
- ];
546
- /**
547
- * Parses and sanitizes text by removing AI artifacts and normalizing typography.
548
- *
549
- * @param text - The text to parse
550
- * @param options - Parsing options
551
- * @returns The cleaned text
552
- */
553
- function parseText(text, options = {}) {
554
- const { normalizeEmDashesToCommas = true, collapseSpaces = true } = options;
555
- if (!text) return "";
556
- let result = text;
557
- result = result.replace(CR_RE, "\n");
558
- result = result.replace(CITATION_RE, "");
559
- result = result.normalize("NFKC");
560
- result = result.replace(INVISIBLE_CHARS_RE, "");
561
- result = result.replace(ASCII_CTRL_RE, "");
562
- if (normalizeEmDashesToCommas) result = result.replace(EM_DASH_SEPARATOR_RE, ", ");
563
- result = result.replace(SPACE_LIKE_RE, " ");
564
- for (const { pattern, replacement } of TYPOGRAPHY_REPLACEMENTS) result = result.replace(pattern, replacement);
565
- if (collapseSpaces) result = result.replace(MULTIPLE_SPACES_RE, " ").trim();
566
- return result;
567
- }
568
- //#endregion
569
521
  //#region src/provider/openrouter.provider.ts
570
522
  /**
571
523
  * Creates an OpenRouter provider for AI SDK models.
@@ -660,7 +612,7 @@ exports.generationSuccess = generationSuccess;
660
612
  exports.isFailure = isFailure;
661
613
  exports.isSuccess = isSuccess;
662
614
  exports.parseObject = parseObject;
663
- exports.parseText = parseText;
615
+ exports.parseText = require_parse_text.parseText;
664
616
  exports.unwrap = unwrap;
665
617
  exports.unwrapOr = unwrapOr;
666
618
  exports.withObservability = withObservability;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["Langfuse","z","z"],"sources":["../src/logging/logging.middleware.ts","../src/observability/observability.middleware.ts","../src/observability/langfuse.adapter.ts","../src/observability/noop.adapter.ts","../src/result/result.ts","../src/parsing/parse-object.ts","../src/generation/generate-structured.ts","../src/parsing/create-schema-prompt.ts","../src/parsing/parse-text.ts","../src/provider/openrouter.provider.ts","../src/provider/openrouter-metadata.adapter.ts","../src/provider/openai-compatible.provider.ts","../src/provider/openai-compatible-metadata.adapter.ts"],"sourcesContent":["import type { LanguageModelV3StreamPart } from \"@ai-sdk/provider\";\nimport type { LoggerPort } from \"@jterrazz/logger\";\nimport type { LanguageModelMiddleware } from \"ai\";\n\nexport interface LoggingMiddlewareOptions {\n logger: LoggerPort;\n include?: {\n params?: boolean;\n content?: boolean;\n usage?: boolean;\n };\n}\n\n/**\n * Creates middleware that logs AI SDK requests and responses.\n */\nexport function createLoggingMiddleware(\n options: LoggingMiddlewareOptions,\n): LanguageModelMiddleware {\n const { logger, include = {} } = options;\n const { params: includeParams, content: includeContent, usage: includeUsage = true } = include;\n\n return {\n specificationVersion: \"v3\",\n wrapGenerate: async ({ doGenerate, params, model }) => {\n const startTime = Date.now();\n\n logger.debug(\"ai.generate.start\", {\n model: model.modelId,\n ...(includeParams && { params }),\n });\n\n try {\n const result = await doGenerate();\n\n // Extract text from content\n const textContent = result.content\n .filter((c): c is { type: \"text\"; text: string } => c.type === \"text\")\n .map((c) => c.text)\n .join(\"\");\n\n logger.debug(\"ai.generate.complete\", {\n model: model.modelId,\n durationMs: Date.now() - startTime,\n finishReason: result.finishReason,\n ...(includeUsage && { usage: result.usage }),\n ...(includeContent && { content: textContent }),\n });\n\n return result;\n } catch (error) {\n logger.error(\"ai.generate.error\", {\n model: model.modelId,\n durationMs: Date.now() - startTime,\n error: error instanceof Error ? error.message : \"Unknown error\",\n });\n throw error;\n }\n },\n\n wrapStream: async ({ doStream, params, model }) => {\n const startTime = Date.now();\n\n logger.debug(\"ai.stream.start\", {\n model: model.modelId,\n ...(includeParams && { params }),\n });\n\n try {\n const result = await doStream();\n\n const chunks: string[] = [];\n\n const transformStream = new TransformStream<\n LanguageModelV3StreamPart,\n LanguageModelV3StreamPart\n >({\n transform(chunk, controller) {\n if (includeContent && chunk.type === \"text-delta\") {\n chunks.push(chunk.delta);\n }\n controller.enqueue(chunk);\n },\n flush() {\n logger.debug(\"ai.stream.complete\", {\n model: model.modelId,\n durationMs: Date.now() - startTime,\n ...(includeContent && { content: chunks.join(\"\") }),\n });\n },\n });\n\n return {\n specificationVersion: \"v3\",\n ...result,\n stream: result.stream.pipeThrough(transformStream),\n };\n } catch (error) {\n logger.error(\"ai.stream.error\", {\n model: model.modelId,\n durationMs: Date.now() - startTime,\n error: error instanceof Error ? error.message : \"Unknown error\",\n });\n throw error;\n }\n },\n };\n}\n","import type { LanguageModelV3StreamPart, SharedV3ProviderOptions } from \"@ai-sdk/provider\";\nimport type { LanguageModelMiddleware } from \"ai\";\n\n// Ports\nimport type { ObservabilityPort } from \"../ports/observability.port.js\";\nimport type { ProviderMetadataPort } from \"../ports/provider-metadata.port.js\";\n/**\n * Metadata passed per-call via providerOptions\n */\nexport interface ObservabilityMetadata {\n traceId: string;\n name?: string;\n metadata?: Record<string, unknown>;\n}\nexport interface ObservabilityMiddlewareOptions {\n observability: ObservabilityPort;\n providerMetadata?: ProviderMetadataPort;\n}\n/**\n * Helper to create type-safe observability metadata for providerOptions\n */\nexport function withObservability(meta: ObservabilityMetadata): SharedV3ProviderOptions {\n return { observability: meta } as unknown as SharedV3ProviderOptions;\n}\n/**\n * Creates middleware that sends generation data to an observability platform.\n */\nexport function createObservabilityMiddleware(\n options: ObservabilityMiddlewareOptions,\n): LanguageModelMiddleware {\n const { observability, providerMetadata } = options;\n return {\n specificationVersion: \"v3\",\n wrapGenerate: async ({ doGenerate, params, model }) => {\n const startTime = new Date();\n const meta = params.providerOptions?.observability as ObservabilityMetadata | undefined;\n const result = await doGenerate();\n const endTime = new Date();\n if (meta?.traceId) {\n const extracted = providerMetadata?.extract(\n result.providerMetadata as Record<string, unknown> | undefined,\n );\n // Extract text from content\n const outputText = result.content\n .filter((c): c is { type: \"text\"; text: string } => c.type === \"text\")\n .map((c) => c.text)\n .join(\"\");\n observability.generation({\n traceId: meta.traceId,\n name: meta.name ?? \"generation\",\n model: model.modelId,\n input: params.prompt,\n output: outputText,\n startTime,\n endTime,\n usage: extracted?.usage,\n cost: extracted?.cost,\n metadata: meta.metadata,\n });\n }\n return result;\n },\n wrapStream: async ({ doStream, params, model }) => {\n const startTime = new Date();\n const meta = params.providerOptions?.observability as ObservabilityMetadata | undefined;\n const result = await doStream();\n if (!meta?.traceId) {\n return result;\n }\n const chunks: string[] = [];\n const transformStream = new TransformStream<\n LanguageModelV3StreamPart,\n LanguageModelV3StreamPart\n >({\n transform(chunk, controller) {\n if (chunk.type === \"text-delta\") {\n chunks.push(chunk.delta);\n }\n controller.enqueue(chunk);\n },\n flush() {\n const endTime = new Date();\n observability.generation({\n traceId: meta.traceId,\n name: meta.name ?? \"generation\",\n model: model.modelId,\n input: params.prompt,\n output: chunks.join(\"\"),\n startTime,\n endTime,\n metadata: meta.metadata,\n });\n },\n });\n return {\n specificationVersion: \"v3\",\n ...result,\n stream: result.stream.pipeThrough(transformStream),\n };\n },\n };\n}\n","import { Langfuse } from \"langfuse\";\n\n// Ports\nimport type {\n GenerationParams,\n ObservabilityPort,\n TraceParams,\n} from \"../ports/observability.port.js\";\n\nexport interface LangfuseConfig {\n secretKey: string;\n publicKey: string;\n baseUrl?: string;\n environment?: string;\n release?: string;\n}\n\n/**\n * Langfuse adapter implementing ObservabilityPort\n */\nexport class LangfuseAdapter implements ObservabilityPort {\n private readonly client: Langfuse;\n\n constructor(config: LangfuseConfig) {\n this.client = new Langfuse({\n secretKey: config.secretKey,\n publicKey: config.publicKey,\n baseUrl: config.baseUrl,\n environment: config.environment,\n release: config.release,\n });\n }\n\n async flush(): Promise<void> {\n await this.client.flushAsync();\n }\n\n generation(params: GenerationParams): void {\n const usageDetails = params.usage\n ? {\n input: params.usage.input,\n output: params.usage.output,\n total: params.usage.total ?? params.usage.input + params.usage.output,\n ...(params.usage.reasoning !== undefined && {\n reasoning: params.usage.reasoning,\n }),\n ...(params.usage.cacheRead !== undefined && {\n cache_read: params.usage.cacheRead,\n }),\n ...(params.usage.cacheWrite !== undefined && {\n cache_write: params.usage.cacheWrite,\n }),\n }\n : undefined;\n\n const costDetails = params.cost\n ? {\n total: params.cost.total,\n ...(params.cost.input !== undefined && { input: params.cost.input }),\n ...(params.cost.output !== undefined && { output: params.cost.output }),\n }\n : undefined;\n\n this.client.generation({\n traceId: params.traceId,\n name: params.name,\n model: params.model,\n input: params.input,\n output: params.output,\n startTime: params.startTime,\n endTime: params.endTime,\n usageDetails,\n costDetails,\n metadata: params.metadata,\n });\n }\n\n async shutdown(): Promise<void> {\n await this.client.shutdownAsync();\n }\n\n trace(params: TraceParams): void {\n this.client.trace({\n id: params.id,\n name: params.name,\n metadata: params.metadata,\n });\n }\n}\n","// Ports\nimport type {\n GenerationParams,\n ObservabilityPort,\n TraceParams,\n} from \"../ports/observability.port.js\";\n\n/**\n * No-op adapter that silently discards all observability data.\n * Useful for testing, development, or when observability is disabled.\n */\nexport class NoopObservabilityAdapter implements ObservabilityPort {\n async flush(): Promise<void> {\n // No-op\n }\n\n generation(_params: GenerationParams): void {\n // No-op\n }\n\n async shutdown(): Promise<void> {\n // No-op\n }\n\n trace(_params: TraceParams): void {\n // No-op\n }\n}\n","/**\n * Error codes for AI generation failures\n */\nexport type GenerationErrorCode =\n | \"AI_GENERATION_FAILED\"\n | \"EMPTY_RESULT\"\n | \"PARSING_FAILED\"\n | \"RATE_LIMITED\"\n | \"TIMEOUT\"\n | \"VALIDATION_FAILED\";\n\n/**\n * Structured error information from AI generation\n */\nexport interface GenerationError {\n code: GenerationErrorCode;\n message: string;\n cause?: unknown;\n}\n\n/**\n * Discriminated union result type for AI operations.\n * Forces explicit handling of both success and failure cases.\n */\nexport type GenerationResult<T> =\n | { success: false; error: GenerationError }\n | { success: true; data: T };\n\n/**\n * Create a successful result\n */\nexport function generationSuccess<T>(data: T): GenerationResult<T> {\n return { success: true, data };\n}\n\n/**\n * Create a failed result\n */\nexport function generationFailure<T>(\n code: GenerationErrorCode,\n message: string,\n cause?: unknown,\n): GenerationResult<T> {\n return { success: false, error: { code, message, cause } };\n}\n\n/**\n * Classify an error into a GenerationErrorCode\n */\nexport function classifyError(error: unknown): GenerationErrorCode {\n if (error instanceof Error) {\n const message = error.message.toLowerCase();\n\n if (message.includes(\"timeout\") || message.includes(\"timed out\")) {\n return \"TIMEOUT\";\n }\n if (message.includes(\"rate limit\") || message.includes(\"429\")) {\n return \"RATE_LIMITED\";\n }\n if (\n message.includes(\"parse\") ||\n message.includes(\"json\") ||\n message.includes(\"unexpected token\")\n ) {\n return \"PARSING_FAILED\";\n }\n if (message.includes(\"valid\") || message.includes(\"schema\") || message.includes(\"zod\")) {\n return \"VALIDATION_FAILED\";\n }\n }\n\n return \"AI_GENERATION_FAILED\";\n}\n\n/**\n * Check if a result is successful (type guard)\n */\nexport function isSuccess<T>(result: GenerationResult<T>): result is { success: true; data: T } {\n return result.success;\n}\n\n/**\n * Check if a result is a failure (type guard)\n */\nexport function isFailure<T>(\n result: GenerationResult<T>,\n): result is { success: false; error: GenerationError } {\n return !result.success;\n}\n\n/**\n * Unwrap a result, throwing if it fails\n */\nexport function unwrap<T>(result: GenerationResult<T>): T {\n if (result.success) {\n return result.data;\n }\n throw new Error(`${result.error.code}: ${result.error.message}`);\n}\n\n/**\n * Unwrap a result with a default value for failures\n */\nexport function unwrapOr<T>(result: GenerationResult<T>, defaultValue: T): T {\n return result.success ? result.data : defaultValue;\n}\n","import { jsonrepair } from \"jsonrepair\";\nimport { z } from \"zod/v4\";\n\nconst MARKDOWN_CODE_BLOCK_RE = /```(?:json)?\\r?\\n([^`]*?)\\r?\\n```/g;\n\n/**\n * Error thrown when object parsing fails.\n * Contains the original text for debugging purposes.\n */\nclass ParseObjectError extends Error {\n public readonly name = \"ParseObjectError\";\n public readonly cause?: unknown;\n public readonly text?: string;\n\n constructor(message: string, cause?: unknown, text?: string) {\n super(message);\n this.cause = cause;\n this.text = text;\n }\n}\n\nfunction convertToPrimitive(value: unknown, schema: z.ZodType): unknown {\n if (schema instanceof z.ZodBoolean) {\n return Boolean(value);\n }\n if (schema instanceof z.ZodNull) {\n return null;\n }\n if (schema instanceof z.ZodNumber) {\n return Number(value);\n }\n if (schema instanceof z.ZodString) {\n return String(value);\n }\n return value;\n}\n\nfunction extractArray(text: string, originalText: string): unknown {\n const start = text.indexOf(\"[\");\n const end = text.lastIndexOf(\"]\");\n\n if (start === -1 || end === -1) {\n throw new ParseObjectError(\"No array found in response\", undefined, originalText);\n }\n\n try {\n const raw = text.slice(start, end + 1);\n return JSON.parse(jsonrepair(raw));\n } catch (error) {\n throw new ParseObjectError(\"Failed to parse array JSON\", error, originalText);\n }\n}\n\nfunction extractObject(text: string, originalText: string): unknown {\n const start = text.indexOf(\"{\");\n const end = text.lastIndexOf(\"}\");\n\n if (start === -1 || end === -1) {\n throw new ParseObjectError(\"No object found in response\", undefined, originalText);\n }\n\n try {\n const raw = text.slice(start, end + 1);\n return JSON.parse(jsonrepair(raw));\n } catch (error) {\n throw new ParseObjectError(\"Failed to parse object JSON\", error, originalText);\n }\n}\n\nfunction extractPrimitive(text: string, schema: z.ZodType): unknown {\n const trimmed = text.trim();\n try {\n return convertToPrimitive(JSON.parse(trimmed), schema);\n } catch {\n return convertToPrimitive(trimmed, schema);\n }\n}\n\nfunction extractBySchemaType(text: string, schema: z.ZodType, originalText: string): unknown {\n if (schema instanceof z.ZodArray) {\n return extractArray(text, originalText);\n }\n\n if (schema instanceof z.ZodObject) {\n return extractObject(text, originalText);\n }\n\n if (\n schema instanceof z.ZodBoolean ||\n schema instanceof z.ZodNull ||\n schema instanceof z.ZodNumber ||\n schema instanceof z.ZodString\n ) {\n return extractPrimitive(text, schema);\n }\n\n // Handle union types - extract as object/array and let Zod validate which variant matches\n if (schema instanceof z.ZodUnion || schema instanceof z.ZodDiscriminatedUnion) {\n const objectStart = text.indexOf(\"{\");\n if (objectStart !== -1) {\n return extractObject(text, originalText);\n }\n const arrayStart = text.indexOf(\"[\");\n if (arrayStart !== -1) {\n return extractArray(text, originalText);\n }\n throw new ParseObjectError(\"No object or array found for union type\", undefined, originalText);\n }\n\n // Handle wrapper types - unwrap and delegate to inner type\n if (schema instanceof z.ZodOptional || schema instanceof z.ZodNullable) {\n return extractBySchemaType(text, schema.unwrap() as z.ZodType, originalText);\n }\n\n if (schema instanceof z.ZodDefault) {\n return extractBySchemaType(text, schema.def.innerType as z.ZodType, originalText);\n }\n\n // Handle .transform() which creates a ZodPipe in Zod v4\n if (schema instanceof z.ZodPipe) {\n return extractBySchemaType(text, schema.def.in as z.ZodType, originalText);\n }\n\n throw new ParseObjectError(\"Unsupported schema type\", undefined, originalText);\n}\n\nfunction extractJsonFromCodeBlock(block: string): null | string {\n const content = block.replace(/```(?:json)?\\r?\\n([^`]*?)\\r?\\n```/, \"$1\").trim();\n try {\n JSON.parse(content);\n return content;\n } catch {\n return null;\n }\n}\n\nfunction findLongestString(strings: string[]): string {\n return strings.reduce((longest, current) =>\n current.length > longest.length ? current : longest,\n );\n}\n\nfunction findJsonStructures(text: string): string[] {\n const matches: string[] = [];\n let depth = 0;\n let start = -1;\n\n for (let i = 0; i < text.length; i++) {\n const char = text[i];\n if (char === \"{\" || char === \"[\") {\n if (depth === 0) {\n start = i;\n }\n depth++;\n } else if (char === \"}\" || char === \"]\") {\n depth--;\n if (depth === 0 && start !== -1) {\n const candidate = text.slice(start, i + 1);\n try {\n JSON.parse(candidate);\n matches.push(candidate);\n } catch {\n // Invalid JSON, skip\n }\n }\n }\n }\n\n return matches;\n}\n\nfunction extractJsonString(text: string): string {\n const codeBlocks = text.match(MARKDOWN_CODE_BLOCK_RE);\n if (codeBlocks && codeBlocks.length > 0) {\n const validBlocks = codeBlocks\n .map((block) => extractJsonFromCodeBlock(block))\n .filter((block): block is string => block !== null);\n\n if (validBlocks.length > 0) {\n return findLongestString(validBlocks);\n }\n }\n\n const structures = findJsonStructures(text);\n if (structures.length > 0) {\n return findLongestString(structures);\n }\n\n return text.replace(/\\s+/g, \" \").trim();\n}\n\nfunction unescapeString(text: string): string {\n return text\n .replace(/\\\\\"/g, '\"')\n .replace(/\\\\n/g, \"\\n\")\n .replace(/\\\\r/g, \"\\r\")\n .replace(/\\\\t/g, \"\\t\")\n .replace(/\\\\\\\\/g, \"\\\\\")\n .replace(/\\\\u([0-9a-fA-F]{4})/g, (_, code) => String.fromCharCode(Number.parseInt(code, 16)));\n}\n\nfunction unescapeJsonValues(json: unknown): unknown {\n if (typeof json === \"string\") {\n return unescapeString(json);\n }\n if (Array.isArray(json)) {\n return json.map(unescapeJsonValues);\n }\n if (typeof json === \"object\" && json !== null) {\n return Object.fromEntries(\n Object.entries(json).map(([key, value]) => [key, unescapeJsonValues(value)]),\n );\n }\n return json;\n}\n\n/**\n * Parses AI-generated text into structured data validated against a Zod schema.\n *\n * Handles common AI response formats:\n * - JSON wrapped in markdown code blocks\n * - JSON embedded in prose text\n * - Malformed JSON (auto-repaired)\n * - Escaped unicode and special characters\n *\n * @param text - The raw AI response text\n * @param schema - A Zod schema to validate and type the result\n * @returns The parsed and validated data\n * @throws {ParseObjectError} When parsing or validation fails\n *\n * @example\n * ```ts\n * const schema = z.object({ title: z.string(), tags: z.array(z.string()) });\n * const result = parseObject(aiResponse, schema);\n * // result is typed as { title: string; tags: string[] }\n * ```\n */\nfunction parseObject<T>(text: string, schema: z.ZodSchema<T>): T {\n try {\n const jsonString = extractJsonString(text);\n const extracted = extractBySchemaType(jsonString, schema, text);\n const unescaped = unescapeJsonValues(extracted);\n return schema.parse(unescaped);\n } catch (error) {\n if (error instanceof ParseObjectError) {\n throw error;\n }\n if (error instanceof z.ZodError) {\n throw new ParseObjectError(\"Failed to validate response against schema\", error, text);\n }\n throw error;\n }\n}\n\nexport { parseObject, ParseObjectError };\n","import type { LanguageModelV3, SharedV3ProviderOptions } from \"@ai-sdk/provider\";\nimport { generateText } from \"ai\";\nimport type { Schema } from \"zod\";\n\nimport { parseObject, ParseObjectError } from \"../parsing/parse-object.js\";\nimport {\n classifyError,\n generationFailure,\n type GenerationResult,\n generationSuccess,\n} from \"../result/result.js\";\n\nexport interface GenerateStructuredOptions<T> {\n model: LanguageModelV3;\n prompt: string;\n system?: string;\n schema: Schema<T>;\n providerOptions?: SharedV3ProviderOptions;\n abortSignal?: AbortSignal;\n maxOutputTokens?: number;\n temperature?: number;\n}\n\n/**\n * Generate structured data from an AI model with automatic parsing and error handling.\n * Observability is handled by middleware - no metadata exposed to caller.\n */\nexport async function generateStructured<T>(\n options: GenerateStructuredOptions<T>,\n): Promise<GenerationResult<T>> {\n const {\n model,\n prompt,\n system,\n schema,\n providerOptions,\n abortSignal,\n maxOutputTokens,\n temperature,\n } = options;\n\n try {\n const response = await generateText({\n model,\n prompt,\n system,\n providerOptions,\n abortSignal,\n maxOutputTokens,\n temperature,\n });\n\n if (!response.text || response.text.trim() === \"\") {\n return generationFailure(\"EMPTY_RESULT\", \"AI returned empty response\");\n }\n\n try {\n const data = parseObject(response.text, schema);\n return generationSuccess(data);\n } catch (error) {\n if (error instanceof ParseObjectError) {\n return generationFailure(\"PARSING_FAILED\", error.message, error);\n }\n return generationFailure(\"VALIDATION_FAILED\", \"Schema validation failed\", error);\n }\n } catch (error) {\n const code = classifyError(error);\n const message = error instanceof Error ? error.message : \"Unknown error\";\n return generationFailure(code, message, error);\n }\n}\n","import { z } from \"zod/v4\";\n\n/**\n * Creates a system prompt that instructs the model to output structured data\n * matching the provided Zod schema.\n *\n * Use this with `generateText` when the provider doesn't support native\n * structured outputs, then parse the response with `parseObject`.\n *\n * @param schema - A Zod schema defining the expected output structure\n * @returns A system prompt string with JSON schema instructions\n *\n * @example\n * ```ts\n * import { generateText } from 'ai';\n * import { createSchemaPrompt, parseObject } from '@jterrazz/intelligence';\n *\n * const schema = z.object({ title: z.string(), tags: z.array(z.string()) });\n *\n * const { text } = await generateText({\n * model,\n * prompt: 'Generate an article about TypeScript',\n * system: createSchemaPrompt(schema),\n * });\n *\n * const result = parseObject(text, schema);\n * ```\n */\nexport function createSchemaPrompt<T>(schema: z.ZodType<T>): string {\n const jsonSchema = z.toJSONSchema(schema);\n const schemaJson = JSON.stringify(jsonSchema, null, 2);\n\n const isPrimitive = [\"boolean\", \"integer\", \"number\", \"string\"].includes(\n jsonSchema.type as string,\n );\n\n if (isPrimitive) {\n return `<OUTPUT_FORMAT>\nYou must respond with a ${jsonSchema.type} value that matches this schema:\n\n\\`\\`\\`json\n${schemaJson}\n\\`\\`\\`\n\nYour response should be only the ${jsonSchema.type} value, without any JSON wrapping or additional text.\n</OUTPUT_FORMAT>`;\n }\n\n return `<OUTPUT_FORMAT>\nYou must respond with valid JSON that matches this JSON schema:\n\n\\`\\`\\`json\n${schemaJson}\n\\`\\`\\`\n\nYour response must be parseable JSON that validates against this schema. Do not include any text outside the JSON.\n</OUTPUT_FORMAT>`;\n}\n","const INVISIBLE_CHARS_RE =\n /[\\u00AD\\u180E\\u200B-\\u200C\\u200E-\\u200F\\u202A-\\u202E\\u2060-\\u2064\\u2066-\\u2069\\uFEFF]/g;\n\n// oxlint-disable-next-line no-control-regex -- Intentionally matching control characters for sanitization\nconst ASCII_CTRL_RE = /[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F]/g;\n\nconst SPACE_LIKE_RE = /[\\u00A0\\u1680\\u2000-\\u200A\\u202F\\u205F\\u3000]/g;\nconst MULTIPLE_SPACES_RE = / {2,}/g;\nconst CR_RE = /\\r\\n?/g;\nconst CITATION_RE = / *\\(oaicite:\\d+\\)\\{index=\\d+\\}/g;\nconst EM_DASH_SEPARATOR_RE = /\\s*[—–―‒]\\s*/g;\n\nconst TYPOGRAPHY_REPLACEMENTS: Array<{ pattern: RegExp; replacement: string }> = [\n { pattern: /[\\u2018\\u2019\\u201A]/g, replacement: \"'\" },\n { pattern: /[\\u201C\\u201D\\u201E]/g, replacement: '\"' },\n { pattern: /\\u2026/g, replacement: \"...\" },\n { pattern: /[\\u2022\\u25AA-\\u25AB\\u25B8-\\u25B9\\u25CF]/g, replacement: \"-\" },\n];\n\nexport interface ParseTextOptions {\n /** Collapse multiple spaces into one (default: true) */\n collapseSpaces?: boolean;\n /** Convert em/en dashes with spaces to commas (default: true) */\n normalizeEmDashesToCommas?: boolean;\n}\n\n/**\n * Parses and sanitizes text by removing AI artifacts and normalizing typography.\n *\n * @param text - The text to parse\n * @param options - Parsing options\n * @returns The cleaned text\n */\nexport function parseText(text: string, options: ParseTextOptions = {}): string {\n const { normalizeEmDashesToCommas = true, collapseSpaces = true } = options;\n\n if (!text) {\n return \"\";\n }\n\n let result = text;\n\n result = result.replace(CR_RE, \"\\n\");\n result = result.replace(CITATION_RE, \"\");\n result = result.normalize(\"NFKC\");\n result = result.replace(INVISIBLE_CHARS_RE, \"\");\n result = result.replace(ASCII_CTRL_RE, \"\");\n\n if (normalizeEmDashesToCommas) {\n result = result.replace(EM_DASH_SEPARATOR_RE, \", \");\n }\n\n result = result.replace(SPACE_LIKE_RE, \" \");\n\n for (const { pattern, replacement } of TYPOGRAPHY_REPLACEMENTS) {\n result = result.replace(pattern, replacement);\n }\n\n if (collapseSpaces) {\n result = result.replace(MULTIPLE_SPACES_RE, \" \").trim();\n }\n\n return result;\n}\n","import { createOpenRouter } from \"@openrouter/ai-sdk-provider\";\nimport type { LanguageModel } from \"ai\";\n\nexport interface ModelOptions {\n /** Maximum tokens to generate */\n maxTokens?: number;\n /** Reasoning configuration for supported models */\n reasoning?: {\n effort?: \"high\" | \"low\" | \"medium\";\n exclude?: boolean;\n };\n}\n\nexport interface OpenRouterConfig {\n apiKey: string;\n metadata?: OpenRouterMetadata;\n}\n\nexport interface OpenRouterMetadata {\n /** Application name for X-Title header */\n application?: string;\n /** Website URL for HTTP-Referer header */\n website?: string;\n}\n\nexport interface OpenRouterProvider {\n /** Get a language model instance */\n model: (name: string, options?: ModelOptions) => LanguageModel;\n}\n\n/**\n * Creates an OpenRouter provider for AI SDK models.\n *\n * @example\n * ```ts\n * const provider = createOpenRouterProvider({ apiKey: process.env.OPENROUTER_API_KEY });\n * const model = provider.model('anthropic/claude-sonnet-4-20250514');\n *\n * const { text } = await generateText({ model, prompt: 'Hello!' });\n * ```\n */\nexport function createOpenRouterProvider(config: OpenRouterConfig): OpenRouterProvider {\n const openrouter = createOpenRouter({ apiKey: config.apiKey });\n\n return {\n model(name: string, options: ModelOptions = {}): LanguageModel {\n return openrouter(name, {\n ...(options.maxTokens !== undefined && {\n maxTokens: options.maxTokens,\n }),\n ...(options.reasoning && {\n extraBody: { reasoning: options.reasoning },\n }),\n });\n },\n };\n}\n","// Ports\nimport type {\n ExtractedProviderMetadata,\n ProviderMetadataPort,\n} from \"../ports/provider-metadata.port.js\";\n\ninterface OpenRouterUsage {\n promptTokens?: number;\n promptTokensDetails?: { cachedTokens?: number };\n completionTokens?: number;\n completionTokensDetails?: { reasoningTokens?: number };\n totalTokens?: number;\n cost?: number;\n}\n\ninterface OpenRouterProviderMetadata {\n provider?: string;\n usage?: OpenRouterUsage;\n}\n\n/**\n * OpenRouter adapter for extracting usage and cost from provider metadata\n */\nexport class OpenRouterMetadataAdapter implements ProviderMetadataPort {\n extract(providerMetadata: Record<string, unknown> | undefined): ExtractedProviderMetadata {\n const meta = providerMetadata?.openrouter as OpenRouterProviderMetadata | undefined;\n\n if (!meta?.usage) {\n return {};\n }\n\n const usage = meta.usage;\n\n return {\n usage: {\n input: usage.promptTokens ?? 0,\n output: usage.completionTokens ?? 0,\n total: usage.totalTokens,\n reasoning: usage.completionTokensDetails?.reasoningTokens,\n cacheRead: usage.promptTokensDetails?.cachedTokens,\n },\n cost: usage.cost !== undefined ? { total: usage.cost } : undefined,\n };\n }\n}\n","import { createOpenAI } from \"@ai-sdk/openai\";\nimport type { LanguageModel } from \"ai\";\n\nexport interface OpenAICompatibleModelOptions {\n /** Maximum tokens to generate */\n maxTokens?: number;\n}\n\nexport interface OpenAICompatibleConfig {\n /** API key for authentication */\n apiKey: string;\n /** Base URL of the OpenAI-compatible API */\n baseURL: string;\n /** Optional model name mapping */\n modelMapping?: Record<string, string>;\n}\n\nexport interface OpenAICompatibleProvider {\n /** Get a language model instance */\n model: (name: string, options?: OpenAICompatibleModelOptions) => LanguageModel;\n}\n\n/**\n * Creates a provider for OpenAI-compatible APIs.\n * Works with any API implementing the OpenAI chat completions spec.\n */\nexport function createOpenAICompatibleProvider(\n config: OpenAICompatibleConfig,\n): OpenAICompatibleProvider {\n const openai = createOpenAI({\n apiKey: config.apiKey,\n baseURL: config.baseURL,\n });\n\n return {\n model(name: string, _options: OpenAICompatibleModelOptions = {}): LanguageModel {\n const modelName = config.modelMapping?.[name] ?? name;\n return openai(modelName);\n },\n };\n}\n","import type {\n ExtractedProviderMetadata,\n ProviderMetadataPort,\n} from \"../ports/provider-metadata.port.js\";\n\ninterface OpenAIUsage {\n promptTokens?: number;\n completionTokens?: number;\n totalTokens?: number;\n}\n\ninterface OpenAIProviderMetadata {\n responseId?: string;\n usage?: OpenAIUsage;\n}\n\n/**\n * Metadata adapter for OpenAI-compatible APIs (including gateway-intelligence).\n * Extracts usage data from the standardized OpenAI response format.\n */\nexport class OpenAICompatibleMetadataAdapter implements ProviderMetadataPort {\n extract(providerMetadata: Record<string, unknown> | undefined): ExtractedProviderMetadata {\n const meta = providerMetadata?.openai as OpenAIProviderMetadata | undefined;\n\n if (!meta?.usage) {\n return {};\n }\n\n const usage = meta.usage;\n\n return {\n usage: {\n input: usage.promptTokens ?? 0,\n output: usage.completionTokens ?? 0,\n total: usage.totalTokens,\n },\n };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["Langfuse","z","z"],"sources":["../src/logging/logging.middleware.ts","../src/observability/observability.middleware.ts","../src/observability/langfuse.adapter.ts","../src/observability/noop.adapter.ts","../src/result/result.ts","../src/parsing/parse-object.ts","../src/generation/generate-structured.ts","../src/parsing/create-schema-prompt.ts","../src/provider/openrouter.provider.ts","../src/provider/openrouter-metadata.adapter.ts","../src/provider/openai-compatible.provider.ts","../src/provider/openai-compatible-metadata.adapter.ts"],"sourcesContent":["import type { LanguageModelV3StreamPart } from '@ai-sdk/provider';\nimport type { LoggerPort } from '@jterrazz/logger';\nimport type { LanguageModelMiddleware } from 'ai';\n\nexport interface LoggingMiddlewareOptions {\n logger: LoggerPort;\n include?: {\n params?: boolean;\n content?: boolean;\n usage?: boolean;\n };\n}\n\n/**\n * Creates middleware that logs AI SDK requests and responses.\n */\nexport function createLoggingMiddleware(\n options: LoggingMiddlewareOptions,\n): LanguageModelMiddleware {\n const { logger, include = {} } = options;\n const { params: includeParams, content: includeContent, usage: includeUsage = true } = include;\n\n return {\n specificationVersion: 'v3',\n wrapGenerate: async ({ doGenerate, params, model }) => {\n const startTime = Date.now();\n\n logger.debug('ai.generate.start', {\n model: model.modelId,\n ...(includeParams && { params }),\n });\n\n try {\n const result = await doGenerate();\n\n // Extract text from content\n const textContent = result.content\n .filter((c): c is { type: 'text'; text: string } => c.type === 'text')\n .map((c) => c.text)\n .join('');\n\n logger.debug('ai.generate.complete', {\n model: model.modelId,\n durationMs: Date.now() - startTime,\n finishReason: result.finishReason,\n ...(includeUsage && { usage: result.usage }),\n ...(includeContent && { content: textContent }),\n });\n\n return result;\n } catch (error) {\n logger.error('ai.generate.error', {\n model: model.modelId,\n durationMs: Date.now() - startTime,\n error: error instanceof Error ? error.message : 'Unknown error',\n });\n throw error;\n }\n },\n\n wrapStream: async ({ doStream, params, model }) => {\n const startTime = Date.now();\n\n logger.debug('ai.stream.start', {\n model: model.modelId,\n ...(includeParams && { params }),\n });\n\n try {\n const result = await doStream();\n\n const chunks: string[] = [];\n\n const transformStream = new TransformStream<\n LanguageModelV3StreamPart,\n LanguageModelV3StreamPart\n >({\n transform(chunk, controller) {\n if (includeContent && chunk.type === 'text-delta') {\n chunks.push(chunk.delta);\n }\n controller.enqueue(chunk);\n },\n flush() {\n logger.debug('ai.stream.complete', {\n model: model.modelId,\n durationMs: Date.now() - startTime,\n ...(includeContent && { content: chunks.join('') }),\n });\n },\n });\n\n return {\n specificationVersion: 'v3',\n ...result,\n stream: result.stream.pipeThrough(transformStream),\n };\n } catch (error) {\n logger.error('ai.stream.error', {\n model: model.modelId,\n durationMs: Date.now() - startTime,\n error: error instanceof Error ? error.message : 'Unknown error',\n });\n throw error;\n }\n },\n };\n}\n","import type { LanguageModelV3StreamPart, SharedV3ProviderOptions } from '@ai-sdk/provider';\nimport type { LanguageModelMiddleware } from 'ai';\n\n// Ports\nimport type { ObservabilityPort } from '../ports/observability.port.js';\nimport type { ProviderMetadataPort } from '../ports/provider-metadata.port.js';\n/**\n * Metadata passed per-call via providerOptions\n */\nexport interface ObservabilityMetadata {\n traceId: string;\n name?: string;\n metadata?: Record<string, unknown>;\n}\nexport interface ObservabilityMiddlewareOptions {\n observability: ObservabilityPort;\n providerMetadata?: ProviderMetadataPort;\n}\n/**\n * Helper to create type-safe observability metadata for providerOptions\n */\nexport function withObservability(meta: ObservabilityMetadata): SharedV3ProviderOptions {\n return { observability: meta } as unknown as SharedV3ProviderOptions;\n}\n/**\n * Creates middleware that sends generation data to an observability platform.\n */\nexport function createObservabilityMiddleware(\n options: ObservabilityMiddlewareOptions,\n): LanguageModelMiddleware {\n const { observability, providerMetadata } = options;\n return {\n specificationVersion: 'v3',\n wrapGenerate: async ({ doGenerate, params, model }) => {\n const startTime = new Date();\n const meta = params.providerOptions?.observability as ObservabilityMetadata | undefined;\n const result = await doGenerate();\n const endTime = new Date();\n if (meta?.traceId) {\n const extracted = providerMetadata?.extract(\n result.providerMetadata as Record<string, unknown> | undefined,\n );\n // Extract text from content\n const outputText = result.content\n .filter((c): c is { type: 'text'; text: string } => c.type === 'text')\n .map((c) => c.text)\n .join('');\n observability.generation({\n traceId: meta.traceId,\n name: meta.name ?? 'generation',\n model: model.modelId,\n input: params.prompt,\n output: outputText,\n startTime,\n endTime,\n usage: extracted?.usage,\n cost: extracted?.cost,\n metadata: meta.metadata,\n });\n }\n return result;\n },\n wrapStream: async ({ doStream, params, model }) => {\n const startTime = new Date();\n const meta = params.providerOptions?.observability as ObservabilityMetadata | undefined;\n const result = await doStream();\n if (!meta?.traceId) {\n return result;\n }\n const chunks: string[] = [];\n const transformStream = new TransformStream<\n LanguageModelV3StreamPart,\n LanguageModelV3StreamPart\n >({\n transform(chunk, controller) {\n if (chunk.type === 'text-delta') {\n chunks.push(chunk.delta);\n }\n controller.enqueue(chunk);\n },\n flush() {\n const endTime = new Date();\n observability.generation({\n traceId: meta.traceId,\n name: meta.name ?? 'generation',\n model: model.modelId,\n input: params.prompt,\n output: chunks.join(''),\n startTime,\n endTime,\n metadata: meta.metadata,\n });\n },\n });\n return {\n specificationVersion: 'v3',\n ...result,\n stream: result.stream.pipeThrough(transformStream),\n };\n },\n };\n}\n","import { Langfuse } from 'langfuse';\n\n// Ports\nimport type {\n GenerationParams,\n ObservabilityPort,\n TraceParams,\n} from '../ports/observability.port.js';\n\nexport interface LangfuseConfig {\n secretKey: string;\n publicKey: string;\n baseUrl?: string;\n environment?: string;\n release?: string;\n}\n\n/**\n * Langfuse adapter implementing ObservabilityPort\n */\nexport class LangfuseAdapter implements ObservabilityPort {\n private readonly client: Langfuse;\n\n constructor(config: LangfuseConfig) {\n this.client = new Langfuse({\n secretKey: config.secretKey,\n publicKey: config.publicKey,\n baseUrl: config.baseUrl,\n environment: config.environment,\n release: config.release,\n });\n }\n\n async flush(): Promise<void> {\n await this.client.flushAsync();\n }\n\n generation(params: GenerationParams): void {\n const usageDetails = params.usage\n ? {\n input: params.usage.input,\n output: params.usage.output,\n total: params.usage.total ?? params.usage.input + params.usage.output,\n ...(params.usage.reasoning !== undefined && {\n reasoning: params.usage.reasoning,\n }),\n ...(params.usage.cacheRead !== undefined && {\n cache_read: params.usage.cacheRead,\n }),\n ...(params.usage.cacheWrite !== undefined && {\n cache_write: params.usage.cacheWrite,\n }),\n }\n : undefined;\n\n const costDetails = params.cost\n ? {\n total: params.cost.total,\n ...(params.cost.input !== undefined && { input: params.cost.input }),\n ...(params.cost.output !== undefined && { output: params.cost.output }),\n }\n : undefined;\n\n this.client.generation({\n traceId: params.traceId,\n name: params.name,\n model: params.model,\n input: params.input,\n output: params.output,\n startTime: params.startTime,\n endTime: params.endTime,\n usageDetails,\n costDetails,\n metadata: params.metadata,\n });\n }\n\n async shutdown(): Promise<void> {\n await this.client.shutdownAsync();\n }\n\n trace(params: TraceParams): void {\n this.client.trace({\n id: params.id,\n name: params.name,\n metadata: params.metadata,\n });\n }\n}\n","// Ports\nimport type {\n GenerationParams,\n ObservabilityPort,\n TraceParams,\n} from '../ports/observability.port.js';\n\n/**\n * No-op adapter that silently discards all observability data.\n * Useful for testing, development, or when observability is disabled.\n */\nexport class NoopObservabilityAdapter implements ObservabilityPort {\n async flush(): Promise<void> {\n // No-op\n }\n\n generation(_params: GenerationParams): void {\n // No-op\n }\n\n async shutdown(): Promise<void> {\n // No-op\n }\n\n trace(_params: TraceParams): void {\n // No-op\n }\n}\n","/**\n * Error codes for AI generation failures\n */\nexport type GenerationErrorCode =\n | 'AI_GENERATION_FAILED'\n | 'EMPTY_RESULT'\n | 'PARSING_FAILED'\n | 'RATE_LIMITED'\n | 'TIMEOUT'\n | 'VALIDATION_FAILED';\n\n/**\n * Structured error information from AI generation\n */\nexport interface GenerationError {\n code: GenerationErrorCode;\n message: string;\n cause?: unknown;\n}\n\n/**\n * Discriminated union result type for AI operations.\n * Forces explicit handling of both success and failure cases.\n */\nexport type GenerationResult<T> =\n | { success: false; error: GenerationError }\n | { success: true; data: T };\n\n/**\n * Create a successful result\n */\nexport function generationSuccess<T>(data: T): GenerationResult<T> {\n return { success: true, data };\n}\n\n/**\n * Create a failed result\n */\nexport function generationFailure<T>(\n code: GenerationErrorCode,\n message: string,\n cause?: unknown,\n): GenerationResult<T> {\n return { success: false, error: { code, message, cause } };\n}\n\n/**\n * Classify an error into a GenerationErrorCode\n */\nexport function classifyError(error: unknown): GenerationErrorCode {\n if (error instanceof Error) {\n const message = error.message.toLowerCase();\n\n if (message.includes('timeout') || message.includes('timed out')) {\n return 'TIMEOUT';\n }\n if (message.includes('rate limit') || message.includes('429')) {\n return 'RATE_LIMITED';\n }\n if (\n message.includes('parse') ||\n message.includes('json') ||\n message.includes('unexpected token')\n ) {\n return 'PARSING_FAILED';\n }\n if (message.includes('valid') || message.includes('schema') || message.includes('zod')) {\n return 'VALIDATION_FAILED';\n }\n }\n\n return 'AI_GENERATION_FAILED';\n}\n\n/**\n * Check if a result is successful (type guard)\n */\nexport function isSuccess<T>(result: GenerationResult<T>): result is { success: true; data: T } {\n return result.success;\n}\n\n/**\n * Check if a result is a failure (type guard)\n */\nexport function isFailure<T>(\n result: GenerationResult<T>,\n): result is { success: false; error: GenerationError } {\n return !result.success;\n}\n\n/**\n * Unwrap a result, throwing if it fails\n */\nexport function unwrap<T>(result: GenerationResult<T>): T {\n if (result.success) {\n return result.data;\n }\n throw new Error(`${result.error.code}: ${result.error.message}`);\n}\n\n/**\n * Unwrap a result with a default value for failures\n */\nexport function unwrapOr<T>(result: GenerationResult<T>, defaultValue: T): T {\n return result.success ? result.data : defaultValue;\n}\n","import { jsonrepair } from 'jsonrepair';\nimport { z } from 'zod/v4';\n\nconst MARKDOWN_CODE_BLOCK_RE = /```(?:json)?\\r?\\n([^`]*?)\\r?\\n```/g;\n\n/**\n * Error thrown when object parsing fails.\n * Contains the original text for debugging purposes.\n */\nclass ParseObjectError extends Error {\n public readonly name = 'ParseObjectError';\n public readonly cause?: unknown;\n public readonly text?: string;\n\n constructor(message: string, cause?: unknown, text?: string) {\n super(message);\n this.cause = cause;\n this.text = text;\n }\n}\n\nfunction convertToPrimitive(value: unknown, schema: z.ZodType): unknown {\n if (schema instanceof z.ZodBoolean) {\n return Boolean(value);\n }\n if (schema instanceof z.ZodNull) {\n return null;\n }\n if (schema instanceof z.ZodNumber) {\n return Number(value);\n }\n if (schema instanceof z.ZodString) {\n return String(value);\n }\n return value;\n}\n\nfunction extractArray(text: string, originalText: string): unknown {\n const start = text.indexOf('[');\n const end = text.lastIndexOf(']');\n\n if (start === -1 || end === -1) {\n throw new ParseObjectError('No array found in response', undefined, originalText);\n }\n\n try {\n const raw = text.slice(start, end + 1);\n return JSON.parse(jsonrepair(raw));\n } catch (error) {\n throw new ParseObjectError('Failed to parse array JSON', error, originalText);\n }\n}\n\nfunction extractObject(text: string, originalText: string): unknown {\n const start = text.indexOf('{');\n const end = text.lastIndexOf('}');\n\n if (start === -1 || end === -1) {\n throw new ParseObjectError('No object found in response', undefined, originalText);\n }\n\n try {\n const raw = text.slice(start, end + 1);\n return JSON.parse(jsonrepair(raw));\n } catch (error) {\n throw new ParseObjectError('Failed to parse object JSON', error, originalText);\n }\n}\n\nfunction extractPrimitive(text: string, schema: z.ZodType): unknown {\n const trimmed = text.trim();\n try {\n return convertToPrimitive(JSON.parse(trimmed), schema);\n } catch {\n return convertToPrimitive(trimmed, schema);\n }\n}\n\nfunction extractBySchemaType(text: string, schema: z.ZodType, originalText: string): unknown {\n if (schema instanceof z.ZodArray) {\n return extractArray(text, originalText);\n }\n\n if (schema instanceof z.ZodObject) {\n return extractObject(text, originalText);\n }\n\n if (\n schema instanceof z.ZodBoolean ||\n schema instanceof z.ZodNull ||\n schema instanceof z.ZodNumber ||\n schema instanceof z.ZodString\n ) {\n return extractPrimitive(text, schema);\n }\n\n // Handle union types - extract as object/array and let Zod validate which variant matches\n if (schema instanceof z.ZodUnion || schema instanceof z.ZodDiscriminatedUnion) {\n const objectStart = text.indexOf('{');\n if (objectStart !== -1) {\n return extractObject(text, originalText);\n }\n const arrayStart = text.indexOf('[');\n if (arrayStart !== -1) {\n return extractArray(text, originalText);\n }\n throw new ParseObjectError(\n 'No object or array found for union type',\n undefined,\n originalText,\n );\n }\n\n // Handle wrapper types - unwrap and delegate to inner type\n if (schema instanceof z.ZodOptional || schema instanceof z.ZodNullable) {\n return extractBySchemaType(text, schema.unwrap() as z.ZodType, originalText);\n }\n\n if (schema instanceof z.ZodDefault) {\n return extractBySchemaType(text, schema.def.innerType as z.ZodType, originalText);\n }\n\n // Handle .transform() which creates a ZodPipe in Zod v4\n if (schema instanceof z.ZodPipe) {\n return extractBySchemaType(text, schema.def.in as z.ZodType, originalText);\n }\n\n throw new ParseObjectError('Unsupported schema type', undefined, originalText);\n}\n\nfunction extractJsonFromCodeBlock(block: string): null | string {\n const content = block.replace(/```(?:json)?\\r?\\n([^`]*?)\\r?\\n```/, '$1').trim();\n try {\n JSON.parse(content);\n return content;\n } catch {\n return null;\n }\n}\n\nfunction findLongestString(strings: string[]): string {\n return strings.reduce((longest, current) =>\n current.length > longest.length ? current : longest,\n );\n}\n\nfunction findJsonStructures(text: string): string[] {\n const matches: string[] = [];\n let depth = 0;\n let start = -1;\n\n for (let i = 0; i < text.length; i++) {\n const char = text[i];\n if (char === '{' || char === '[') {\n if (depth === 0) {\n start = i;\n }\n depth++;\n } else if (char === '}' || char === ']') {\n depth--;\n if (depth === 0 && start !== -1) {\n const candidate = text.slice(start, i + 1);\n try {\n JSON.parse(candidate);\n matches.push(candidate);\n } catch {\n // Invalid JSON, skip\n }\n }\n }\n }\n\n return matches;\n}\n\nfunction extractJsonString(text: string): string {\n const codeBlocks = text.match(MARKDOWN_CODE_BLOCK_RE);\n if (codeBlocks && codeBlocks.length > 0) {\n const validBlocks = codeBlocks\n .map((block) => extractJsonFromCodeBlock(block))\n .filter((block): block is string => block !== null);\n\n if (validBlocks.length > 0) {\n return findLongestString(validBlocks);\n }\n }\n\n const structures = findJsonStructures(text);\n if (structures.length > 0) {\n return findLongestString(structures);\n }\n\n return text.replace(/\\s+/g, ' ').trim();\n}\n\nfunction unescapeString(text: string): string {\n return text\n .replace(/\\\\\"/g, '\"')\n .replace(/\\\\n/g, '\\n')\n .replace(/\\\\r/g, '\\r')\n .replace(/\\\\t/g, '\\t')\n .replace(/\\\\\\\\/g, '\\\\')\n .replace(/\\\\u([0-9a-fA-F]{4})/g, (_, code) =>\n String.fromCharCode(Number.parseInt(code, 16)),\n );\n}\n\nfunction unescapeJsonValues(json: unknown): unknown {\n if (typeof json === 'string') {\n return unescapeString(json);\n }\n if (Array.isArray(json)) {\n return json.map(unescapeJsonValues);\n }\n if (typeof json === 'object' && json !== null) {\n return Object.fromEntries(\n Object.entries(json).map(([key, value]) => [key, unescapeJsonValues(value)]),\n );\n }\n return json;\n}\n\n/**\n * Parses AI-generated text into structured data validated against a Zod schema.\n *\n * Handles common AI response formats:\n * - JSON wrapped in markdown code blocks\n * - JSON embedded in prose text\n * - Malformed JSON (auto-repaired)\n * - Escaped unicode and special characters\n *\n * @param text - The raw AI response text\n * @param schema - A Zod schema to validate and type the result\n * @returns The parsed and validated data\n * @throws {ParseObjectError} When parsing or validation fails\n *\n * @example\n * ```ts\n * const schema = z.object({ title: z.string(), tags: z.array(z.string()) });\n * const result = parseObject(aiResponse, schema);\n * // result is typed as { title: string; tags: string[] }\n * ```\n */\nfunction parseObject<T>(text: string, schema: z.ZodSchema<T>): T {\n try {\n const jsonString = extractJsonString(text);\n const extracted = extractBySchemaType(jsonString, schema, text);\n const unescaped = unescapeJsonValues(extracted);\n return schema.parse(unescaped);\n } catch (error) {\n if (error instanceof ParseObjectError) {\n throw error;\n }\n if (error instanceof z.ZodError) {\n throw new ParseObjectError('Failed to validate response against schema', error, text);\n }\n throw error;\n }\n}\n\nexport { parseObject, ParseObjectError };\n","import type { LanguageModelV3, SharedV3ProviderOptions } from '@ai-sdk/provider';\nimport { generateText } from 'ai';\nimport type { Schema } from 'zod';\n\nimport { parseObject, ParseObjectError } from '../parsing/parse-object.js';\nimport {\n classifyError,\n generationFailure,\n type GenerationResult,\n generationSuccess,\n} from '../result/result.js';\n\nexport interface GenerateStructuredOptions<T> {\n model: LanguageModelV3;\n prompt: string;\n system?: string;\n schema: Schema<T>;\n providerOptions?: SharedV3ProviderOptions;\n abortSignal?: AbortSignal;\n maxOutputTokens?: number;\n temperature?: number;\n}\n\n/**\n * Generate structured data from an AI model with automatic parsing and error handling.\n * Observability is handled by middleware - no metadata exposed to caller.\n */\nexport async function generateStructured<T>(\n options: GenerateStructuredOptions<T>,\n): Promise<GenerationResult<T>> {\n const {\n model,\n prompt,\n system,\n schema,\n providerOptions,\n abortSignal,\n maxOutputTokens,\n temperature,\n } = options;\n\n try {\n const response = await generateText({\n model,\n prompt,\n system,\n providerOptions,\n abortSignal,\n maxOutputTokens,\n temperature,\n });\n\n if (!response.text || response.text.trim() === '') {\n return generationFailure('EMPTY_RESULT', 'AI returned empty response');\n }\n\n try {\n const data = parseObject(response.text, schema);\n return generationSuccess(data);\n } catch (error) {\n if (error instanceof ParseObjectError) {\n return generationFailure('PARSING_FAILED', error.message, error);\n }\n return generationFailure('VALIDATION_FAILED', 'Schema validation failed', error);\n }\n } catch (error) {\n const code = classifyError(error);\n const message = error instanceof Error ? error.message : 'Unknown error';\n return generationFailure(code, message, error);\n }\n}\n","import { z } from 'zod/v4';\n\n/**\n * Creates a system prompt that instructs the model to output structured data\n * matching the provided Zod schema.\n *\n * Use this with `generateText` when the provider doesn't support native\n * structured outputs, then parse the response with `parseObject`.\n *\n * @param schema - A Zod schema defining the expected output structure\n * @returns A system prompt string with JSON schema instructions\n *\n * @example\n * ```ts\n * import { generateText } from 'ai';\n * import { createSchemaPrompt, parseObject } from '@jterrazz/intelligence';\n *\n * const schema = z.object({ title: z.string(), tags: z.array(z.string()) });\n *\n * const { text } = await generateText({\n * model,\n * prompt: 'Generate an article about TypeScript',\n * system: createSchemaPrompt(schema),\n * });\n *\n * const result = parseObject(text, schema);\n * ```\n */\nexport function createSchemaPrompt<T>(schema: z.ZodType<T>): string {\n const jsonSchema = z.toJSONSchema(schema);\n const schemaJson = JSON.stringify(jsonSchema, null, 2);\n\n const isPrimitive = ['boolean', 'integer', 'number', 'string'].includes(\n jsonSchema.type as string,\n );\n\n if (isPrimitive) {\n return `<OUTPUT_FORMAT>\nYou must respond with a ${jsonSchema.type} value that matches this schema:\n\n\\`\\`\\`json\n${schemaJson}\n\\`\\`\\`\n\nYour response should be only the ${jsonSchema.type} value, without any JSON wrapping or additional text.\n</OUTPUT_FORMAT>`;\n }\n\n return `<OUTPUT_FORMAT>\nYou must respond with valid JSON that matches this JSON schema:\n\n\\`\\`\\`json\n${schemaJson}\n\\`\\`\\`\n\nYour response must be parseable JSON that validates against this schema. Do not include any text outside the JSON.\n</OUTPUT_FORMAT>`;\n}\n","import { createOpenRouter } from '@openrouter/ai-sdk-provider';\nimport type { LanguageModel } from 'ai';\n\nexport interface ModelOptions {\n /** Maximum tokens to generate */\n maxTokens?: number;\n /** Reasoning configuration for supported models */\n reasoning?: {\n effort?: 'high' | 'low' | 'medium';\n exclude?: boolean;\n };\n}\n\nexport interface OpenRouterConfig {\n apiKey: string;\n metadata?: OpenRouterMetadata;\n}\n\nexport interface OpenRouterMetadata {\n /** Application name for X-Title header */\n application?: string;\n /** Website URL for HTTP-Referer header */\n website?: string;\n}\n\nexport interface OpenRouterProvider {\n /** Get a language model instance */\n model: (name: string, options?: ModelOptions) => LanguageModel;\n}\n\n/**\n * Creates an OpenRouter provider for AI SDK models.\n *\n * @example\n * ```ts\n * const provider = createOpenRouterProvider({ apiKey: process.env.OPENROUTER_API_KEY });\n * const model = provider.model('anthropic/claude-sonnet-4-20250514');\n *\n * const { text } = await generateText({ model, prompt: 'Hello!' });\n * ```\n */\nexport function createOpenRouterProvider(config: OpenRouterConfig): OpenRouterProvider {\n const openrouter = createOpenRouter({ apiKey: config.apiKey });\n\n return {\n model(name: string, options: ModelOptions = {}): LanguageModel {\n return openrouter(name, {\n ...(options.maxTokens !== undefined && {\n maxTokens: options.maxTokens,\n }),\n ...(options.reasoning && {\n extraBody: { reasoning: options.reasoning },\n }),\n });\n },\n };\n}\n","// Ports\nimport type {\n ExtractedProviderMetadata,\n ProviderMetadataPort,\n} from '../ports/provider-metadata.port.js';\n\ninterface OpenRouterUsage {\n promptTokens?: number;\n promptTokensDetails?: { cachedTokens?: number };\n completionTokens?: number;\n completionTokensDetails?: { reasoningTokens?: number };\n totalTokens?: number;\n cost?: number;\n}\n\ninterface OpenRouterProviderMetadata {\n provider?: string;\n usage?: OpenRouterUsage;\n}\n\n/**\n * OpenRouter adapter for extracting usage and cost from provider metadata\n */\nexport class OpenRouterMetadataAdapter implements ProviderMetadataPort {\n extract(providerMetadata: Record<string, unknown> | undefined): ExtractedProviderMetadata {\n const meta = providerMetadata?.openrouter as OpenRouterProviderMetadata | undefined;\n\n if (!meta?.usage) {\n return {};\n }\n\n const usage = meta.usage;\n\n return {\n usage: {\n input: usage.promptTokens ?? 0,\n output: usage.completionTokens ?? 0,\n total: usage.totalTokens,\n reasoning: usage.completionTokensDetails?.reasoningTokens,\n cacheRead: usage.promptTokensDetails?.cachedTokens,\n },\n cost: usage.cost !== undefined ? { total: usage.cost } : undefined,\n };\n }\n}\n","import { createOpenAI } from '@ai-sdk/openai';\nimport type { LanguageModel } from 'ai';\n\nexport interface OpenAICompatibleModelOptions {\n /** Maximum tokens to generate */\n maxTokens?: number;\n}\n\nexport interface OpenAICompatibleConfig {\n /** API key for authentication */\n apiKey: string;\n /** Base URL of the OpenAI-compatible API */\n baseURL: string;\n /** Optional model name mapping */\n modelMapping?: Record<string, string>;\n}\n\nexport interface OpenAICompatibleProvider {\n /** Get a language model instance */\n model: (name: string, options?: OpenAICompatibleModelOptions) => LanguageModel;\n}\n\n/**\n * Creates a provider for OpenAI-compatible APIs.\n * Works with any API implementing the OpenAI chat completions spec.\n */\nexport function createOpenAICompatibleProvider(\n config: OpenAICompatibleConfig,\n): OpenAICompatibleProvider {\n const openai = createOpenAI({\n apiKey: config.apiKey,\n baseURL: config.baseURL,\n });\n\n return {\n model(name: string, _options: OpenAICompatibleModelOptions = {}): LanguageModel {\n const modelName = config.modelMapping?.[name] ?? name;\n return openai(modelName);\n },\n };\n}\n","import type {\n ExtractedProviderMetadata,\n ProviderMetadataPort,\n} from '../ports/provider-metadata.port.js';\n\ninterface OpenAIUsage {\n promptTokens?: number;\n completionTokens?: number;\n totalTokens?: number;\n}\n\ninterface OpenAIProviderMetadata {\n responseId?: string;\n usage?: OpenAIUsage;\n}\n\n/**\n * Metadata adapter for OpenAI-compatible APIs (including gateway-intelligence).\n * Extracts usage data from the standardized OpenAI response format.\n */\nexport class OpenAICompatibleMetadataAdapter implements ProviderMetadataPort {\n extract(providerMetadata: Record<string, unknown> | undefined): ExtractedProviderMetadata {\n const meta = providerMetadata?.openai as OpenAIProviderMetadata | undefined;\n\n if (!meta?.usage) {\n return {};\n }\n\n const usage = meta.usage;\n\n return {\n usage: {\n input: usage.promptTokens ?? 0,\n output: usage.completionTokens ?? 0,\n total: usage.totalTokens,\n },\n };\n }\n}\n"],"mappings":"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"}
package/dist/index.d.cts CHANGED
@@ -1,3 +1,4 @@
1
+ import { n as parseText, t as ParseTextOptions } from "./parse-text.cjs";
1
2
  import { LoggerPort } from "@jterrazz/logger";
2
3
  import { LanguageModel, LanguageModelMiddleware } from "ai";
3
4
  import { JSONSchema7 } from "json-schema";
@@ -1238,7 +1239,7 @@ declare class NoopObservabilityAdapter implements ObservabilityPort {
1238
1239
  /**
1239
1240
  * Error codes for AI generation failures
1240
1241
  */
1241
- type GenerationErrorCode = "AI_GENERATION_FAILED" | "EMPTY_RESULT" | "PARSING_FAILED" | "RATE_LIMITED" | "TIMEOUT" | "VALIDATION_FAILED";
1242
+ type GenerationErrorCode = 'AI_GENERATION_FAILED' | 'EMPTY_RESULT' | 'PARSING_FAILED' | 'RATE_LIMITED' | 'TIMEOUT' | 'VALIDATION_FAILED';
1242
1243
  /**
1243
1244
  * Structured error information from AI generation
1244
1245
  */
@@ -1373,29 +1374,13 @@ declare class ParseObjectError extends Error {
1373
1374
  */
1374
1375
  declare function parseObject<T>(text: string, schema: z.ZodSchema<T>): T;
1375
1376
  //#endregion
1376
- //#region src/parsing/parse-text.d.ts
1377
- interface ParseTextOptions {
1378
- /** Collapse multiple spaces into one (default: true) */
1379
- collapseSpaces?: boolean;
1380
- /** Convert em/en dashes with spaces to commas (default: true) */
1381
- normalizeEmDashesToCommas?: boolean;
1382
- }
1383
- /**
1384
- * Parses and sanitizes text by removing AI artifacts and normalizing typography.
1385
- *
1386
- * @param text - The text to parse
1387
- * @param options - Parsing options
1388
- * @returns The cleaned text
1389
- */
1390
- declare function parseText(text: string, options?: ParseTextOptions): string;
1391
- //#endregion
1392
1377
  //#region src/provider/openrouter.provider.d.ts
1393
1378
  interface ModelOptions {
1394
1379
  /** Maximum tokens to generate */
1395
1380
  maxTokens?: number;
1396
1381
  /** Reasoning configuration for supported models */
1397
1382
  reasoning?: {
1398
- effort?: "high" | "low" | "medium";
1383
+ effort?: 'high' | 'low' | 'medium';
1399
1384
  exclude?: boolean;
1400
1385
  };
1401
1386
  }
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { n as parseText, t as ParseTextOptions } from "./parse-text.js";
1
2
  import { LanguageModel, LanguageModelMiddleware } from "ai";
2
3
  import { z } from "zod/v4";
3
4
  import { LoggerPort } from "@jterrazz/logger";
@@ -1238,7 +1239,7 @@ declare class NoopObservabilityAdapter implements ObservabilityPort {
1238
1239
  /**
1239
1240
  * Error codes for AI generation failures
1240
1241
  */
1241
- type GenerationErrorCode = "AI_GENERATION_FAILED" | "EMPTY_RESULT" | "PARSING_FAILED" | "RATE_LIMITED" | "TIMEOUT" | "VALIDATION_FAILED";
1242
+ type GenerationErrorCode = 'AI_GENERATION_FAILED' | 'EMPTY_RESULT' | 'PARSING_FAILED' | 'RATE_LIMITED' | 'TIMEOUT' | 'VALIDATION_FAILED';
1242
1243
  /**
1243
1244
  * Structured error information from AI generation
1244
1245
  */
@@ -1373,29 +1374,13 @@ declare class ParseObjectError extends Error {
1373
1374
  */
1374
1375
  declare function parseObject<T>(text: string, schema: z.ZodSchema<T>): T;
1375
1376
  //#endregion
1376
- //#region src/parsing/parse-text.d.ts
1377
- interface ParseTextOptions {
1378
- /** Collapse multiple spaces into one (default: true) */
1379
- collapseSpaces?: boolean;
1380
- /** Convert em/en dashes with spaces to commas (default: true) */
1381
- normalizeEmDashesToCommas?: boolean;
1382
- }
1383
- /**
1384
- * Parses and sanitizes text by removing AI artifacts and normalizing typography.
1385
- *
1386
- * @param text - The text to parse
1387
- * @param options - Parsing options
1388
- * @returns The cleaned text
1389
- */
1390
- declare function parseText(text: string, options?: ParseTextOptions): string;
1391
- //#endregion
1392
1377
  //#region src/provider/openrouter.provider.d.ts
1393
1378
  interface ModelOptions {
1394
1379
  /** Maximum tokens to generate */
1395
1380
  maxTokens?: number;
1396
1381
  /** Reasoning configuration for supported models */
1397
1382
  reasoning?: {
1398
- effort?: "high" | "low" | "medium";
1383
+ effort?: 'high' | 'low' | 'medium';
1399
1384
  exclude?: boolean;
1400
1385
  };
1401
1386
  }
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { t as parseText } from "./parse-text.js";
1
2
  import { Langfuse } from "langfuse";
2
3
  import { generateText } from "ai";
3
4
  import { jsonrepair } from "jsonrepair";
@@ -516,55 +517,6 @@ Your response must be parseable JSON that validates against this schema. Do not
516
517
  </OUTPUT_FORMAT>`;
517
518
  }
518
519
  //#endregion
519
- //#region src/parsing/parse-text.ts
520
- const INVISIBLE_CHARS_RE = /[\u00AD\u180E\u200B-\u200C\u200E-\u200F\u202A-\u202E\u2060-\u2064\u2066-\u2069\uFEFF]/g;
521
- const ASCII_CTRL_RE = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g;
522
- const SPACE_LIKE_RE = /[\u00A0\u1680\u2000-\u200A\u202F\u205F\u3000]/g;
523
- const MULTIPLE_SPACES_RE = / {2,}/g;
524
- const CR_RE = /\r\n?/g;
525
- const CITATION_RE = / *\(oaicite:\d+\)\{index=\d+\}/g;
526
- const EM_DASH_SEPARATOR_RE = /\s*[—–―‒]\s*/g;
527
- const TYPOGRAPHY_REPLACEMENTS = [
528
- {
529
- pattern: /[\u2018\u2019\u201A]/g,
530
- replacement: "'"
531
- },
532
- {
533
- pattern: /[\u201C\u201D\u201E]/g,
534
- replacement: "\""
535
- },
536
- {
537
- pattern: /\u2026/g,
538
- replacement: "..."
539
- },
540
- {
541
- pattern: /[\u2022\u25AA-\u25AB\u25B8-\u25B9\u25CF]/g,
542
- replacement: "-"
543
- }
544
- ];
545
- /**
546
- * Parses and sanitizes text by removing AI artifacts and normalizing typography.
547
- *
548
- * @param text - The text to parse
549
- * @param options - Parsing options
550
- * @returns The cleaned text
551
- */
552
- function parseText(text, options = {}) {
553
- const { normalizeEmDashesToCommas = true, collapseSpaces = true } = options;
554
- if (!text) return "";
555
- let result = text;
556
- result = result.replace(CR_RE, "\n");
557
- result = result.replace(CITATION_RE, "");
558
- result = result.normalize("NFKC");
559
- result = result.replace(INVISIBLE_CHARS_RE, "");
560
- result = result.replace(ASCII_CTRL_RE, "");
561
- if (normalizeEmDashesToCommas) result = result.replace(EM_DASH_SEPARATOR_RE, ", ");
562
- result = result.replace(SPACE_LIKE_RE, " ");
563
- for (const { pattern, replacement } of TYPOGRAPHY_REPLACEMENTS) result = result.replace(pattern, replacement);
564
- if (collapseSpaces) result = result.replace(MULTIPLE_SPACES_RE, " ").trim();
565
- return result;
566
- }
567
- //#endregion
568
520
  //#region src/provider/openrouter.provider.ts
569
521
  /**
570
522
  * Creates an OpenRouter provider for AI SDK models.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/logging/logging.middleware.ts","../src/observability/observability.middleware.ts","../src/observability/langfuse.adapter.ts","../src/observability/noop.adapter.ts","../src/result/result.ts","../src/parsing/parse-object.ts","../src/generation/generate-structured.ts","../src/parsing/create-schema-prompt.ts","../src/parsing/parse-text.ts","../src/provider/openrouter.provider.ts","../src/provider/openrouter-metadata.adapter.ts","../src/provider/openai-compatible.provider.ts","../src/provider/openai-compatible-metadata.adapter.ts"],"sourcesContent":["import type { LanguageModelV3StreamPart } from \"@ai-sdk/provider\";\nimport type { LoggerPort } from \"@jterrazz/logger\";\nimport type { LanguageModelMiddleware } from \"ai\";\n\nexport interface LoggingMiddlewareOptions {\n logger: LoggerPort;\n include?: {\n params?: boolean;\n content?: boolean;\n usage?: boolean;\n };\n}\n\n/**\n * Creates middleware that logs AI SDK requests and responses.\n */\nexport function createLoggingMiddleware(\n options: LoggingMiddlewareOptions,\n): LanguageModelMiddleware {\n const { logger, include = {} } = options;\n const { params: includeParams, content: includeContent, usage: includeUsage = true } = include;\n\n return {\n specificationVersion: \"v3\",\n wrapGenerate: async ({ doGenerate, params, model }) => {\n const startTime = Date.now();\n\n logger.debug(\"ai.generate.start\", {\n model: model.modelId,\n ...(includeParams && { params }),\n });\n\n try {\n const result = await doGenerate();\n\n // Extract text from content\n const textContent = result.content\n .filter((c): c is { type: \"text\"; text: string } => c.type === \"text\")\n .map((c) => c.text)\n .join(\"\");\n\n logger.debug(\"ai.generate.complete\", {\n model: model.modelId,\n durationMs: Date.now() - startTime,\n finishReason: result.finishReason,\n ...(includeUsage && { usage: result.usage }),\n ...(includeContent && { content: textContent }),\n });\n\n return result;\n } catch (error) {\n logger.error(\"ai.generate.error\", {\n model: model.modelId,\n durationMs: Date.now() - startTime,\n error: error instanceof Error ? error.message : \"Unknown error\",\n });\n throw error;\n }\n },\n\n wrapStream: async ({ doStream, params, model }) => {\n const startTime = Date.now();\n\n logger.debug(\"ai.stream.start\", {\n model: model.modelId,\n ...(includeParams && { params }),\n });\n\n try {\n const result = await doStream();\n\n const chunks: string[] = [];\n\n const transformStream = new TransformStream<\n LanguageModelV3StreamPart,\n LanguageModelV3StreamPart\n >({\n transform(chunk, controller) {\n if (includeContent && chunk.type === \"text-delta\") {\n chunks.push(chunk.delta);\n }\n controller.enqueue(chunk);\n },\n flush() {\n logger.debug(\"ai.stream.complete\", {\n model: model.modelId,\n durationMs: Date.now() - startTime,\n ...(includeContent && { content: chunks.join(\"\") }),\n });\n },\n });\n\n return {\n specificationVersion: \"v3\",\n ...result,\n stream: result.stream.pipeThrough(transformStream),\n };\n } catch (error) {\n logger.error(\"ai.stream.error\", {\n model: model.modelId,\n durationMs: Date.now() - startTime,\n error: error instanceof Error ? error.message : \"Unknown error\",\n });\n throw error;\n }\n },\n };\n}\n","import type { LanguageModelV3StreamPart, SharedV3ProviderOptions } from \"@ai-sdk/provider\";\nimport type { LanguageModelMiddleware } from \"ai\";\n\n// Ports\nimport type { ObservabilityPort } from \"../ports/observability.port.js\";\nimport type { ProviderMetadataPort } from \"../ports/provider-metadata.port.js\";\n/**\n * Metadata passed per-call via providerOptions\n */\nexport interface ObservabilityMetadata {\n traceId: string;\n name?: string;\n metadata?: Record<string, unknown>;\n}\nexport interface ObservabilityMiddlewareOptions {\n observability: ObservabilityPort;\n providerMetadata?: ProviderMetadataPort;\n}\n/**\n * Helper to create type-safe observability metadata for providerOptions\n */\nexport function withObservability(meta: ObservabilityMetadata): SharedV3ProviderOptions {\n return { observability: meta } as unknown as SharedV3ProviderOptions;\n}\n/**\n * Creates middleware that sends generation data to an observability platform.\n */\nexport function createObservabilityMiddleware(\n options: ObservabilityMiddlewareOptions,\n): LanguageModelMiddleware {\n const { observability, providerMetadata } = options;\n return {\n specificationVersion: \"v3\",\n wrapGenerate: async ({ doGenerate, params, model }) => {\n const startTime = new Date();\n const meta = params.providerOptions?.observability as ObservabilityMetadata | undefined;\n const result = await doGenerate();\n const endTime = new Date();\n if (meta?.traceId) {\n const extracted = providerMetadata?.extract(\n result.providerMetadata as Record<string, unknown> | undefined,\n );\n // Extract text from content\n const outputText = result.content\n .filter((c): c is { type: \"text\"; text: string } => c.type === \"text\")\n .map((c) => c.text)\n .join(\"\");\n observability.generation({\n traceId: meta.traceId,\n name: meta.name ?? \"generation\",\n model: model.modelId,\n input: params.prompt,\n output: outputText,\n startTime,\n endTime,\n usage: extracted?.usage,\n cost: extracted?.cost,\n metadata: meta.metadata,\n });\n }\n return result;\n },\n wrapStream: async ({ doStream, params, model }) => {\n const startTime = new Date();\n const meta = params.providerOptions?.observability as ObservabilityMetadata | undefined;\n const result = await doStream();\n if (!meta?.traceId) {\n return result;\n }\n const chunks: string[] = [];\n const transformStream = new TransformStream<\n LanguageModelV3StreamPart,\n LanguageModelV3StreamPart\n >({\n transform(chunk, controller) {\n if (chunk.type === \"text-delta\") {\n chunks.push(chunk.delta);\n }\n controller.enqueue(chunk);\n },\n flush() {\n const endTime = new Date();\n observability.generation({\n traceId: meta.traceId,\n name: meta.name ?? \"generation\",\n model: model.modelId,\n input: params.prompt,\n output: chunks.join(\"\"),\n startTime,\n endTime,\n metadata: meta.metadata,\n });\n },\n });\n return {\n specificationVersion: \"v3\",\n ...result,\n stream: result.stream.pipeThrough(transformStream),\n };\n },\n };\n}\n","import { Langfuse } from \"langfuse\";\n\n// Ports\nimport type {\n GenerationParams,\n ObservabilityPort,\n TraceParams,\n} from \"../ports/observability.port.js\";\n\nexport interface LangfuseConfig {\n secretKey: string;\n publicKey: string;\n baseUrl?: string;\n environment?: string;\n release?: string;\n}\n\n/**\n * Langfuse adapter implementing ObservabilityPort\n */\nexport class LangfuseAdapter implements ObservabilityPort {\n private readonly client: Langfuse;\n\n constructor(config: LangfuseConfig) {\n this.client = new Langfuse({\n secretKey: config.secretKey,\n publicKey: config.publicKey,\n baseUrl: config.baseUrl,\n environment: config.environment,\n release: config.release,\n });\n }\n\n async flush(): Promise<void> {\n await this.client.flushAsync();\n }\n\n generation(params: GenerationParams): void {\n const usageDetails = params.usage\n ? {\n input: params.usage.input,\n output: params.usage.output,\n total: params.usage.total ?? params.usage.input + params.usage.output,\n ...(params.usage.reasoning !== undefined && {\n reasoning: params.usage.reasoning,\n }),\n ...(params.usage.cacheRead !== undefined && {\n cache_read: params.usage.cacheRead,\n }),\n ...(params.usage.cacheWrite !== undefined && {\n cache_write: params.usage.cacheWrite,\n }),\n }\n : undefined;\n\n const costDetails = params.cost\n ? {\n total: params.cost.total,\n ...(params.cost.input !== undefined && { input: params.cost.input }),\n ...(params.cost.output !== undefined && { output: params.cost.output }),\n }\n : undefined;\n\n this.client.generation({\n traceId: params.traceId,\n name: params.name,\n model: params.model,\n input: params.input,\n output: params.output,\n startTime: params.startTime,\n endTime: params.endTime,\n usageDetails,\n costDetails,\n metadata: params.metadata,\n });\n }\n\n async shutdown(): Promise<void> {\n await this.client.shutdownAsync();\n }\n\n trace(params: TraceParams): void {\n this.client.trace({\n id: params.id,\n name: params.name,\n metadata: params.metadata,\n });\n }\n}\n","// Ports\nimport type {\n GenerationParams,\n ObservabilityPort,\n TraceParams,\n} from \"../ports/observability.port.js\";\n\n/**\n * No-op adapter that silently discards all observability data.\n * Useful for testing, development, or when observability is disabled.\n */\nexport class NoopObservabilityAdapter implements ObservabilityPort {\n async flush(): Promise<void> {\n // No-op\n }\n\n generation(_params: GenerationParams): void {\n // No-op\n }\n\n async shutdown(): Promise<void> {\n // No-op\n }\n\n trace(_params: TraceParams): void {\n // No-op\n }\n}\n","/**\n * Error codes for AI generation failures\n */\nexport type GenerationErrorCode =\n | \"AI_GENERATION_FAILED\"\n | \"EMPTY_RESULT\"\n | \"PARSING_FAILED\"\n | \"RATE_LIMITED\"\n | \"TIMEOUT\"\n | \"VALIDATION_FAILED\";\n\n/**\n * Structured error information from AI generation\n */\nexport interface GenerationError {\n code: GenerationErrorCode;\n message: string;\n cause?: unknown;\n}\n\n/**\n * Discriminated union result type for AI operations.\n * Forces explicit handling of both success and failure cases.\n */\nexport type GenerationResult<T> =\n | { success: false; error: GenerationError }\n | { success: true; data: T };\n\n/**\n * Create a successful result\n */\nexport function generationSuccess<T>(data: T): GenerationResult<T> {\n return { success: true, data };\n}\n\n/**\n * Create a failed result\n */\nexport function generationFailure<T>(\n code: GenerationErrorCode,\n message: string,\n cause?: unknown,\n): GenerationResult<T> {\n return { success: false, error: { code, message, cause } };\n}\n\n/**\n * Classify an error into a GenerationErrorCode\n */\nexport function classifyError(error: unknown): GenerationErrorCode {\n if (error instanceof Error) {\n const message = error.message.toLowerCase();\n\n if (message.includes(\"timeout\") || message.includes(\"timed out\")) {\n return \"TIMEOUT\";\n }\n if (message.includes(\"rate limit\") || message.includes(\"429\")) {\n return \"RATE_LIMITED\";\n }\n if (\n message.includes(\"parse\") ||\n message.includes(\"json\") ||\n message.includes(\"unexpected token\")\n ) {\n return \"PARSING_FAILED\";\n }\n if (message.includes(\"valid\") || message.includes(\"schema\") || message.includes(\"zod\")) {\n return \"VALIDATION_FAILED\";\n }\n }\n\n return \"AI_GENERATION_FAILED\";\n}\n\n/**\n * Check if a result is successful (type guard)\n */\nexport function isSuccess<T>(result: GenerationResult<T>): result is { success: true; data: T } {\n return result.success;\n}\n\n/**\n * Check if a result is a failure (type guard)\n */\nexport function isFailure<T>(\n result: GenerationResult<T>,\n): result is { success: false; error: GenerationError } {\n return !result.success;\n}\n\n/**\n * Unwrap a result, throwing if it fails\n */\nexport function unwrap<T>(result: GenerationResult<T>): T {\n if (result.success) {\n return result.data;\n }\n throw new Error(`${result.error.code}: ${result.error.message}`);\n}\n\n/**\n * Unwrap a result with a default value for failures\n */\nexport function unwrapOr<T>(result: GenerationResult<T>, defaultValue: T): T {\n return result.success ? result.data : defaultValue;\n}\n","import { jsonrepair } from \"jsonrepair\";\nimport { z } from \"zod/v4\";\n\nconst MARKDOWN_CODE_BLOCK_RE = /```(?:json)?\\r?\\n([^`]*?)\\r?\\n```/g;\n\n/**\n * Error thrown when object parsing fails.\n * Contains the original text for debugging purposes.\n */\nclass ParseObjectError extends Error {\n public readonly name = \"ParseObjectError\";\n public readonly cause?: unknown;\n public readonly text?: string;\n\n constructor(message: string, cause?: unknown, text?: string) {\n super(message);\n this.cause = cause;\n this.text = text;\n }\n}\n\nfunction convertToPrimitive(value: unknown, schema: z.ZodType): unknown {\n if (schema instanceof z.ZodBoolean) {\n return Boolean(value);\n }\n if (schema instanceof z.ZodNull) {\n return null;\n }\n if (schema instanceof z.ZodNumber) {\n return Number(value);\n }\n if (schema instanceof z.ZodString) {\n return String(value);\n }\n return value;\n}\n\nfunction extractArray(text: string, originalText: string): unknown {\n const start = text.indexOf(\"[\");\n const end = text.lastIndexOf(\"]\");\n\n if (start === -1 || end === -1) {\n throw new ParseObjectError(\"No array found in response\", undefined, originalText);\n }\n\n try {\n const raw = text.slice(start, end + 1);\n return JSON.parse(jsonrepair(raw));\n } catch (error) {\n throw new ParseObjectError(\"Failed to parse array JSON\", error, originalText);\n }\n}\n\nfunction extractObject(text: string, originalText: string): unknown {\n const start = text.indexOf(\"{\");\n const end = text.lastIndexOf(\"}\");\n\n if (start === -1 || end === -1) {\n throw new ParseObjectError(\"No object found in response\", undefined, originalText);\n }\n\n try {\n const raw = text.slice(start, end + 1);\n return JSON.parse(jsonrepair(raw));\n } catch (error) {\n throw new ParseObjectError(\"Failed to parse object JSON\", error, originalText);\n }\n}\n\nfunction extractPrimitive(text: string, schema: z.ZodType): unknown {\n const trimmed = text.trim();\n try {\n return convertToPrimitive(JSON.parse(trimmed), schema);\n } catch {\n return convertToPrimitive(trimmed, schema);\n }\n}\n\nfunction extractBySchemaType(text: string, schema: z.ZodType, originalText: string): unknown {\n if (schema instanceof z.ZodArray) {\n return extractArray(text, originalText);\n }\n\n if (schema instanceof z.ZodObject) {\n return extractObject(text, originalText);\n }\n\n if (\n schema instanceof z.ZodBoolean ||\n schema instanceof z.ZodNull ||\n schema instanceof z.ZodNumber ||\n schema instanceof z.ZodString\n ) {\n return extractPrimitive(text, schema);\n }\n\n // Handle union types - extract as object/array and let Zod validate which variant matches\n if (schema instanceof z.ZodUnion || schema instanceof z.ZodDiscriminatedUnion) {\n const objectStart = text.indexOf(\"{\");\n if (objectStart !== -1) {\n return extractObject(text, originalText);\n }\n const arrayStart = text.indexOf(\"[\");\n if (arrayStart !== -1) {\n return extractArray(text, originalText);\n }\n throw new ParseObjectError(\"No object or array found for union type\", undefined, originalText);\n }\n\n // Handle wrapper types - unwrap and delegate to inner type\n if (schema instanceof z.ZodOptional || schema instanceof z.ZodNullable) {\n return extractBySchemaType(text, schema.unwrap() as z.ZodType, originalText);\n }\n\n if (schema instanceof z.ZodDefault) {\n return extractBySchemaType(text, schema.def.innerType as z.ZodType, originalText);\n }\n\n // Handle .transform() which creates a ZodPipe in Zod v4\n if (schema instanceof z.ZodPipe) {\n return extractBySchemaType(text, schema.def.in as z.ZodType, originalText);\n }\n\n throw new ParseObjectError(\"Unsupported schema type\", undefined, originalText);\n}\n\nfunction extractJsonFromCodeBlock(block: string): null | string {\n const content = block.replace(/```(?:json)?\\r?\\n([^`]*?)\\r?\\n```/, \"$1\").trim();\n try {\n JSON.parse(content);\n return content;\n } catch {\n return null;\n }\n}\n\nfunction findLongestString(strings: string[]): string {\n return strings.reduce((longest, current) =>\n current.length > longest.length ? current : longest,\n );\n}\n\nfunction findJsonStructures(text: string): string[] {\n const matches: string[] = [];\n let depth = 0;\n let start = -1;\n\n for (let i = 0; i < text.length; i++) {\n const char = text[i];\n if (char === \"{\" || char === \"[\") {\n if (depth === 0) {\n start = i;\n }\n depth++;\n } else if (char === \"}\" || char === \"]\") {\n depth--;\n if (depth === 0 && start !== -1) {\n const candidate = text.slice(start, i + 1);\n try {\n JSON.parse(candidate);\n matches.push(candidate);\n } catch {\n // Invalid JSON, skip\n }\n }\n }\n }\n\n return matches;\n}\n\nfunction extractJsonString(text: string): string {\n const codeBlocks = text.match(MARKDOWN_CODE_BLOCK_RE);\n if (codeBlocks && codeBlocks.length > 0) {\n const validBlocks = codeBlocks\n .map((block) => extractJsonFromCodeBlock(block))\n .filter((block): block is string => block !== null);\n\n if (validBlocks.length > 0) {\n return findLongestString(validBlocks);\n }\n }\n\n const structures = findJsonStructures(text);\n if (structures.length > 0) {\n return findLongestString(structures);\n }\n\n return text.replace(/\\s+/g, \" \").trim();\n}\n\nfunction unescapeString(text: string): string {\n return text\n .replace(/\\\\\"/g, '\"')\n .replace(/\\\\n/g, \"\\n\")\n .replace(/\\\\r/g, \"\\r\")\n .replace(/\\\\t/g, \"\\t\")\n .replace(/\\\\\\\\/g, \"\\\\\")\n .replace(/\\\\u([0-9a-fA-F]{4})/g, (_, code) => String.fromCharCode(Number.parseInt(code, 16)));\n}\n\nfunction unescapeJsonValues(json: unknown): unknown {\n if (typeof json === \"string\") {\n return unescapeString(json);\n }\n if (Array.isArray(json)) {\n return json.map(unescapeJsonValues);\n }\n if (typeof json === \"object\" && json !== null) {\n return Object.fromEntries(\n Object.entries(json).map(([key, value]) => [key, unescapeJsonValues(value)]),\n );\n }\n return json;\n}\n\n/**\n * Parses AI-generated text into structured data validated against a Zod schema.\n *\n * Handles common AI response formats:\n * - JSON wrapped in markdown code blocks\n * - JSON embedded in prose text\n * - Malformed JSON (auto-repaired)\n * - Escaped unicode and special characters\n *\n * @param text - The raw AI response text\n * @param schema - A Zod schema to validate and type the result\n * @returns The parsed and validated data\n * @throws {ParseObjectError} When parsing or validation fails\n *\n * @example\n * ```ts\n * const schema = z.object({ title: z.string(), tags: z.array(z.string()) });\n * const result = parseObject(aiResponse, schema);\n * // result is typed as { title: string; tags: string[] }\n * ```\n */\nfunction parseObject<T>(text: string, schema: z.ZodSchema<T>): T {\n try {\n const jsonString = extractJsonString(text);\n const extracted = extractBySchemaType(jsonString, schema, text);\n const unescaped = unescapeJsonValues(extracted);\n return schema.parse(unescaped);\n } catch (error) {\n if (error instanceof ParseObjectError) {\n throw error;\n }\n if (error instanceof z.ZodError) {\n throw new ParseObjectError(\"Failed to validate response against schema\", error, text);\n }\n throw error;\n }\n}\n\nexport { parseObject, ParseObjectError };\n","import type { LanguageModelV3, SharedV3ProviderOptions } from \"@ai-sdk/provider\";\nimport { generateText } from \"ai\";\nimport type { Schema } from \"zod\";\n\nimport { parseObject, ParseObjectError } from \"../parsing/parse-object.js\";\nimport {\n classifyError,\n generationFailure,\n type GenerationResult,\n generationSuccess,\n} from \"../result/result.js\";\n\nexport interface GenerateStructuredOptions<T> {\n model: LanguageModelV3;\n prompt: string;\n system?: string;\n schema: Schema<T>;\n providerOptions?: SharedV3ProviderOptions;\n abortSignal?: AbortSignal;\n maxOutputTokens?: number;\n temperature?: number;\n}\n\n/**\n * Generate structured data from an AI model with automatic parsing and error handling.\n * Observability is handled by middleware - no metadata exposed to caller.\n */\nexport async function generateStructured<T>(\n options: GenerateStructuredOptions<T>,\n): Promise<GenerationResult<T>> {\n const {\n model,\n prompt,\n system,\n schema,\n providerOptions,\n abortSignal,\n maxOutputTokens,\n temperature,\n } = options;\n\n try {\n const response = await generateText({\n model,\n prompt,\n system,\n providerOptions,\n abortSignal,\n maxOutputTokens,\n temperature,\n });\n\n if (!response.text || response.text.trim() === \"\") {\n return generationFailure(\"EMPTY_RESULT\", \"AI returned empty response\");\n }\n\n try {\n const data = parseObject(response.text, schema);\n return generationSuccess(data);\n } catch (error) {\n if (error instanceof ParseObjectError) {\n return generationFailure(\"PARSING_FAILED\", error.message, error);\n }\n return generationFailure(\"VALIDATION_FAILED\", \"Schema validation failed\", error);\n }\n } catch (error) {\n const code = classifyError(error);\n const message = error instanceof Error ? error.message : \"Unknown error\";\n return generationFailure(code, message, error);\n }\n}\n","import { z } from \"zod/v4\";\n\n/**\n * Creates a system prompt that instructs the model to output structured data\n * matching the provided Zod schema.\n *\n * Use this with `generateText` when the provider doesn't support native\n * structured outputs, then parse the response with `parseObject`.\n *\n * @param schema - A Zod schema defining the expected output structure\n * @returns A system prompt string with JSON schema instructions\n *\n * @example\n * ```ts\n * import { generateText } from 'ai';\n * import { createSchemaPrompt, parseObject } from '@jterrazz/intelligence';\n *\n * const schema = z.object({ title: z.string(), tags: z.array(z.string()) });\n *\n * const { text } = await generateText({\n * model,\n * prompt: 'Generate an article about TypeScript',\n * system: createSchemaPrompt(schema),\n * });\n *\n * const result = parseObject(text, schema);\n * ```\n */\nexport function createSchemaPrompt<T>(schema: z.ZodType<T>): string {\n const jsonSchema = z.toJSONSchema(schema);\n const schemaJson = JSON.stringify(jsonSchema, null, 2);\n\n const isPrimitive = [\"boolean\", \"integer\", \"number\", \"string\"].includes(\n jsonSchema.type as string,\n );\n\n if (isPrimitive) {\n return `<OUTPUT_FORMAT>\nYou must respond with a ${jsonSchema.type} value that matches this schema:\n\n\\`\\`\\`json\n${schemaJson}\n\\`\\`\\`\n\nYour response should be only the ${jsonSchema.type} value, without any JSON wrapping or additional text.\n</OUTPUT_FORMAT>`;\n }\n\n return `<OUTPUT_FORMAT>\nYou must respond with valid JSON that matches this JSON schema:\n\n\\`\\`\\`json\n${schemaJson}\n\\`\\`\\`\n\nYour response must be parseable JSON that validates against this schema. Do not include any text outside the JSON.\n</OUTPUT_FORMAT>`;\n}\n","const INVISIBLE_CHARS_RE =\n /[\\u00AD\\u180E\\u200B-\\u200C\\u200E-\\u200F\\u202A-\\u202E\\u2060-\\u2064\\u2066-\\u2069\\uFEFF]/g;\n\n// oxlint-disable-next-line no-control-regex -- Intentionally matching control characters for sanitization\nconst ASCII_CTRL_RE = /[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F]/g;\n\nconst SPACE_LIKE_RE = /[\\u00A0\\u1680\\u2000-\\u200A\\u202F\\u205F\\u3000]/g;\nconst MULTIPLE_SPACES_RE = / {2,}/g;\nconst CR_RE = /\\r\\n?/g;\nconst CITATION_RE = / *\\(oaicite:\\d+\\)\\{index=\\d+\\}/g;\nconst EM_DASH_SEPARATOR_RE = /\\s*[—–―‒]\\s*/g;\n\nconst TYPOGRAPHY_REPLACEMENTS: Array<{ pattern: RegExp; replacement: string }> = [\n { pattern: /[\\u2018\\u2019\\u201A]/g, replacement: \"'\" },\n { pattern: /[\\u201C\\u201D\\u201E]/g, replacement: '\"' },\n { pattern: /\\u2026/g, replacement: \"...\" },\n { pattern: /[\\u2022\\u25AA-\\u25AB\\u25B8-\\u25B9\\u25CF]/g, replacement: \"-\" },\n];\n\nexport interface ParseTextOptions {\n /** Collapse multiple spaces into one (default: true) */\n collapseSpaces?: boolean;\n /** Convert em/en dashes with spaces to commas (default: true) */\n normalizeEmDashesToCommas?: boolean;\n}\n\n/**\n * Parses and sanitizes text by removing AI artifacts and normalizing typography.\n *\n * @param text - The text to parse\n * @param options - Parsing options\n * @returns The cleaned text\n */\nexport function parseText(text: string, options: ParseTextOptions = {}): string {\n const { normalizeEmDashesToCommas = true, collapseSpaces = true } = options;\n\n if (!text) {\n return \"\";\n }\n\n let result = text;\n\n result = result.replace(CR_RE, \"\\n\");\n result = result.replace(CITATION_RE, \"\");\n result = result.normalize(\"NFKC\");\n result = result.replace(INVISIBLE_CHARS_RE, \"\");\n result = result.replace(ASCII_CTRL_RE, \"\");\n\n if (normalizeEmDashesToCommas) {\n result = result.replace(EM_DASH_SEPARATOR_RE, \", \");\n }\n\n result = result.replace(SPACE_LIKE_RE, \" \");\n\n for (const { pattern, replacement } of TYPOGRAPHY_REPLACEMENTS) {\n result = result.replace(pattern, replacement);\n }\n\n if (collapseSpaces) {\n result = result.replace(MULTIPLE_SPACES_RE, \" \").trim();\n }\n\n return result;\n}\n","import { createOpenRouter } from \"@openrouter/ai-sdk-provider\";\nimport type { LanguageModel } from \"ai\";\n\nexport interface ModelOptions {\n /** Maximum tokens to generate */\n maxTokens?: number;\n /** Reasoning configuration for supported models */\n reasoning?: {\n effort?: \"high\" | \"low\" | \"medium\";\n exclude?: boolean;\n };\n}\n\nexport interface OpenRouterConfig {\n apiKey: string;\n metadata?: OpenRouterMetadata;\n}\n\nexport interface OpenRouterMetadata {\n /** Application name for X-Title header */\n application?: string;\n /** Website URL for HTTP-Referer header */\n website?: string;\n}\n\nexport interface OpenRouterProvider {\n /** Get a language model instance */\n model: (name: string, options?: ModelOptions) => LanguageModel;\n}\n\n/**\n * Creates an OpenRouter provider for AI SDK models.\n *\n * @example\n * ```ts\n * const provider = createOpenRouterProvider({ apiKey: process.env.OPENROUTER_API_KEY });\n * const model = provider.model('anthropic/claude-sonnet-4-20250514');\n *\n * const { text } = await generateText({ model, prompt: 'Hello!' });\n * ```\n */\nexport function createOpenRouterProvider(config: OpenRouterConfig): OpenRouterProvider {\n const openrouter = createOpenRouter({ apiKey: config.apiKey });\n\n return {\n model(name: string, options: ModelOptions = {}): LanguageModel {\n return openrouter(name, {\n ...(options.maxTokens !== undefined && {\n maxTokens: options.maxTokens,\n }),\n ...(options.reasoning && {\n extraBody: { reasoning: options.reasoning },\n }),\n });\n },\n };\n}\n","// Ports\nimport type {\n ExtractedProviderMetadata,\n ProviderMetadataPort,\n} from \"../ports/provider-metadata.port.js\";\n\ninterface OpenRouterUsage {\n promptTokens?: number;\n promptTokensDetails?: { cachedTokens?: number };\n completionTokens?: number;\n completionTokensDetails?: { reasoningTokens?: number };\n totalTokens?: number;\n cost?: number;\n}\n\ninterface OpenRouterProviderMetadata {\n provider?: string;\n usage?: OpenRouterUsage;\n}\n\n/**\n * OpenRouter adapter for extracting usage and cost from provider metadata\n */\nexport class OpenRouterMetadataAdapter implements ProviderMetadataPort {\n extract(providerMetadata: Record<string, unknown> | undefined): ExtractedProviderMetadata {\n const meta = providerMetadata?.openrouter as OpenRouterProviderMetadata | undefined;\n\n if (!meta?.usage) {\n return {};\n }\n\n const usage = meta.usage;\n\n return {\n usage: {\n input: usage.promptTokens ?? 0,\n output: usage.completionTokens ?? 0,\n total: usage.totalTokens,\n reasoning: usage.completionTokensDetails?.reasoningTokens,\n cacheRead: usage.promptTokensDetails?.cachedTokens,\n },\n cost: usage.cost !== undefined ? { total: usage.cost } : undefined,\n };\n }\n}\n","import { createOpenAI } from \"@ai-sdk/openai\";\nimport type { LanguageModel } from \"ai\";\n\nexport interface OpenAICompatibleModelOptions {\n /** Maximum tokens to generate */\n maxTokens?: number;\n}\n\nexport interface OpenAICompatibleConfig {\n /** API key for authentication */\n apiKey: string;\n /** Base URL of the OpenAI-compatible API */\n baseURL: string;\n /** Optional model name mapping */\n modelMapping?: Record<string, string>;\n}\n\nexport interface OpenAICompatibleProvider {\n /** Get a language model instance */\n model: (name: string, options?: OpenAICompatibleModelOptions) => LanguageModel;\n}\n\n/**\n * Creates a provider for OpenAI-compatible APIs.\n * Works with any API implementing the OpenAI chat completions spec.\n */\nexport function createOpenAICompatibleProvider(\n config: OpenAICompatibleConfig,\n): OpenAICompatibleProvider {\n const openai = createOpenAI({\n apiKey: config.apiKey,\n baseURL: config.baseURL,\n });\n\n return {\n model(name: string, _options: OpenAICompatibleModelOptions = {}): LanguageModel {\n const modelName = config.modelMapping?.[name] ?? name;\n return openai(modelName);\n },\n };\n}\n","import type {\n ExtractedProviderMetadata,\n ProviderMetadataPort,\n} from \"../ports/provider-metadata.port.js\";\n\ninterface OpenAIUsage {\n promptTokens?: number;\n completionTokens?: number;\n totalTokens?: number;\n}\n\ninterface OpenAIProviderMetadata {\n responseId?: string;\n usage?: OpenAIUsage;\n}\n\n/**\n * Metadata adapter for OpenAI-compatible APIs (including gateway-intelligence).\n * Extracts usage data from the standardized OpenAI response format.\n */\nexport class OpenAICompatibleMetadataAdapter implements ProviderMetadataPort {\n extract(providerMetadata: Record<string, unknown> | undefined): ExtractedProviderMetadata {\n const meta = providerMetadata?.openai as OpenAIProviderMetadata | undefined;\n\n if (!meta?.usage) {\n return {};\n }\n\n const usage = meta.usage;\n\n return {\n usage: {\n input: usage.promptTokens ?? 0,\n output: usage.completionTokens ?? 0,\n total: usage.totalTokens,\n },\n };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/logging/logging.middleware.ts","../src/observability/observability.middleware.ts","../src/observability/langfuse.adapter.ts","../src/observability/noop.adapter.ts","../src/result/result.ts","../src/parsing/parse-object.ts","../src/generation/generate-structured.ts","../src/parsing/create-schema-prompt.ts","../src/provider/openrouter.provider.ts","../src/provider/openrouter-metadata.adapter.ts","../src/provider/openai-compatible.provider.ts","../src/provider/openai-compatible-metadata.adapter.ts"],"sourcesContent":["import type { LanguageModelV3StreamPart } from '@ai-sdk/provider';\nimport type { LoggerPort } from '@jterrazz/logger';\nimport type { LanguageModelMiddleware } from 'ai';\n\nexport interface LoggingMiddlewareOptions {\n logger: LoggerPort;\n include?: {\n params?: boolean;\n content?: boolean;\n usage?: boolean;\n };\n}\n\n/**\n * Creates middleware that logs AI SDK requests and responses.\n */\nexport function createLoggingMiddleware(\n options: LoggingMiddlewareOptions,\n): LanguageModelMiddleware {\n const { logger, include = {} } = options;\n const { params: includeParams, content: includeContent, usage: includeUsage = true } = include;\n\n return {\n specificationVersion: 'v3',\n wrapGenerate: async ({ doGenerate, params, model }) => {\n const startTime = Date.now();\n\n logger.debug('ai.generate.start', {\n model: model.modelId,\n ...(includeParams && { params }),\n });\n\n try {\n const result = await doGenerate();\n\n // Extract text from content\n const textContent = result.content\n .filter((c): c is { type: 'text'; text: string } => c.type === 'text')\n .map((c) => c.text)\n .join('');\n\n logger.debug('ai.generate.complete', {\n model: model.modelId,\n durationMs: Date.now() - startTime,\n finishReason: result.finishReason,\n ...(includeUsage && { usage: result.usage }),\n ...(includeContent && { content: textContent }),\n });\n\n return result;\n } catch (error) {\n logger.error('ai.generate.error', {\n model: model.modelId,\n durationMs: Date.now() - startTime,\n error: error instanceof Error ? error.message : 'Unknown error',\n });\n throw error;\n }\n },\n\n wrapStream: async ({ doStream, params, model }) => {\n const startTime = Date.now();\n\n logger.debug('ai.stream.start', {\n model: model.modelId,\n ...(includeParams && { params }),\n });\n\n try {\n const result = await doStream();\n\n const chunks: string[] = [];\n\n const transformStream = new TransformStream<\n LanguageModelV3StreamPart,\n LanguageModelV3StreamPart\n >({\n transform(chunk, controller) {\n if (includeContent && chunk.type === 'text-delta') {\n chunks.push(chunk.delta);\n }\n controller.enqueue(chunk);\n },\n flush() {\n logger.debug('ai.stream.complete', {\n model: model.modelId,\n durationMs: Date.now() - startTime,\n ...(includeContent && { content: chunks.join('') }),\n });\n },\n });\n\n return {\n specificationVersion: 'v3',\n ...result,\n stream: result.stream.pipeThrough(transformStream),\n };\n } catch (error) {\n logger.error('ai.stream.error', {\n model: model.modelId,\n durationMs: Date.now() - startTime,\n error: error instanceof Error ? error.message : 'Unknown error',\n });\n throw error;\n }\n },\n };\n}\n","import type { LanguageModelV3StreamPart, SharedV3ProviderOptions } from '@ai-sdk/provider';\nimport type { LanguageModelMiddleware } from 'ai';\n\n// Ports\nimport type { ObservabilityPort } from '../ports/observability.port.js';\nimport type { ProviderMetadataPort } from '../ports/provider-metadata.port.js';\n/**\n * Metadata passed per-call via providerOptions\n */\nexport interface ObservabilityMetadata {\n traceId: string;\n name?: string;\n metadata?: Record<string, unknown>;\n}\nexport interface ObservabilityMiddlewareOptions {\n observability: ObservabilityPort;\n providerMetadata?: ProviderMetadataPort;\n}\n/**\n * Helper to create type-safe observability metadata for providerOptions\n */\nexport function withObservability(meta: ObservabilityMetadata): SharedV3ProviderOptions {\n return { observability: meta } as unknown as SharedV3ProviderOptions;\n}\n/**\n * Creates middleware that sends generation data to an observability platform.\n */\nexport function createObservabilityMiddleware(\n options: ObservabilityMiddlewareOptions,\n): LanguageModelMiddleware {\n const { observability, providerMetadata } = options;\n return {\n specificationVersion: 'v3',\n wrapGenerate: async ({ doGenerate, params, model }) => {\n const startTime = new Date();\n const meta = params.providerOptions?.observability as ObservabilityMetadata | undefined;\n const result = await doGenerate();\n const endTime = new Date();\n if (meta?.traceId) {\n const extracted = providerMetadata?.extract(\n result.providerMetadata as Record<string, unknown> | undefined,\n );\n // Extract text from content\n const outputText = result.content\n .filter((c): c is { type: 'text'; text: string } => c.type === 'text')\n .map((c) => c.text)\n .join('');\n observability.generation({\n traceId: meta.traceId,\n name: meta.name ?? 'generation',\n model: model.modelId,\n input: params.prompt,\n output: outputText,\n startTime,\n endTime,\n usage: extracted?.usage,\n cost: extracted?.cost,\n metadata: meta.metadata,\n });\n }\n return result;\n },\n wrapStream: async ({ doStream, params, model }) => {\n const startTime = new Date();\n const meta = params.providerOptions?.observability as ObservabilityMetadata | undefined;\n const result = await doStream();\n if (!meta?.traceId) {\n return result;\n }\n const chunks: string[] = [];\n const transformStream = new TransformStream<\n LanguageModelV3StreamPart,\n LanguageModelV3StreamPart\n >({\n transform(chunk, controller) {\n if (chunk.type === 'text-delta') {\n chunks.push(chunk.delta);\n }\n controller.enqueue(chunk);\n },\n flush() {\n const endTime = new Date();\n observability.generation({\n traceId: meta.traceId,\n name: meta.name ?? 'generation',\n model: model.modelId,\n input: params.prompt,\n output: chunks.join(''),\n startTime,\n endTime,\n metadata: meta.metadata,\n });\n },\n });\n return {\n specificationVersion: 'v3',\n ...result,\n stream: result.stream.pipeThrough(transformStream),\n };\n },\n };\n}\n","import { Langfuse } from 'langfuse';\n\n// Ports\nimport type {\n GenerationParams,\n ObservabilityPort,\n TraceParams,\n} from '../ports/observability.port.js';\n\nexport interface LangfuseConfig {\n secretKey: string;\n publicKey: string;\n baseUrl?: string;\n environment?: string;\n release?: string;\n}\n\n/**\n * Langfuse adapter implementing ObservabilityPort\n */\nexport class LangfuseAdapter implements ObservabilityPort {\n private readonly client: Langfuse;\n\n constructor(config: LangfuseConfig) {\n this.client = new Langfuse({\n secretKey: config.secretKey,\n publicKey: config.publicKey,\n baseUrl: config.baseUrl,\n environment: config.environment,\n release: config.release,\n });\n }\n\n async flush(): Promise<void> {\n await this.client.flushAsync();\n }\n\n generation(params: GenerationParams): void {\n const usageDetails = params.usage\n ? {\n input: params.usage.input,\n output: params.usage.output,\n total: params.usage.total ?? params.usage.input + params.usage.output,\n ...(params.usage.reasoning !== undefined && {\n reasoning: params.usage.reasoning,\n }),\n ...(params.usage.cacheRead !== undefined && {\n cache_read: params.usage.cacheRead,\n }),\n ...(params.usage.cacheWrite !== undefined && {\n cache_write: params.usage.cacheWrite,\n }),\n }\n : undefined;\n\n const costDetails = params.cost\n ? {\n total: params.cost.total,\n ...(params.cost.input !== undefined && { input: params.cost.input }),\n ...(params.cost.output !== undefined && { output: params.cost.output }),\n }\n : undefined;\n\n this.client.generation({\n traceId: params.traceId,\n name: params.name,\n model: params.model,\n input: params.input,\n output: params.output,\n startTime: params.startTime,\n endTime: params.endTime,\n usageDetails,\n costDetails,\n metadata: params.metadata,\n });\n }\n\n async shutdown(): Promise<void> {\n await this.client.shutdownAsync();\n }\n\n trace(params: TraceParams): void {\n this.client.trace({\n id: params.id,\n name: params.name,\n metadata: params.metadata,\n });\n }\n}\n","// Ports\nimport type {\n GenerationParams,\n ObservabilityPort,\n TraceParams,\n} from '../ports/observability.port.js';\n\n/**\n * No-op adapter that silently discards all observability data.\n * Useful for testing, development, or when observability is disabled.\n */\nexport class NoopObservabilityAdapter implements ObservabilityPort {\n async flush(): Promise<void> {\n // No-op\n }\n\n generation(_params: GenerationParams): void {\n // No-op\n }\n\n async shutdown(): Promise<void> {\n // No-op\n }\n\n trace(_params: TraceParams): void {\n // No-op\n }\n}\n","/**\n * Error codes for AI generation failures\n */\nexport type GenerationErrorCode =\n | 'AI_GENERATION_FAILED'\n | 'EMPTY_RESULT'\n | 'PARSING_FAILED'\n | 'RATE_LIMITED'\n | 'TIMEOUT'\n | 'VALIDATION_FAILED';\n\n/**\n * Structured error information from AI generation\n */\nexport interface GenerationError {\n code: GenerationErrorCode;\n message: string;\n cause?: unknown;\n}\n\n/**\n * Discriminated union result type for AI operations.\n * Forces explicit handling of both success and failure cases.\n */\nexport type GenerationResult<T> =\n | { success: false; error: GenerationError }\n | { success: true; data: T };\n\n/**\n * Create a successful result\n */\nexport function generationSuccess<T>(data: T): GenerationResult<T> {\n return { success: true, data };\n}\n\n/**\n * Create a failed result\n */\nexport function generationFailure<T>(\n code: GenerationErrorCode,\n message: string,\n cause?: unknown,\n): GenerationResult<T> {\n return { success: false, error: { code, message, cause } };\n}\n\n/**\n * Classify an error into a GenerationErrorCode\n */\nexport function classifyError(error: unknown): GenerationErrorCode {\n if (error instanceof Error) {\n const message = error.message.toLowerCase();\n\n if (message.includes('timeout') || message.includes('timed out')) {\n return 'TIMEOUT';\n }\n if (message.includes('rate limit') || message.includes('429')) {\n return 'RATE_LIMITED';\n }\n if (\n message.includes('parse') ||\n message.includes('json') ||\n message.includes('unexpected token')\n ) {\n return 'PARSING_FAILED';\n }\n if (message.includes('valid') || message.includes('schema') || message.includes('zod')) {\n return 'VALIDATION_FAILED';\n }\n }\n\n return 'AI_GENERATION_FAILED';\n}\n\n/**\n * Check if a result is successful (type guard)\n */\nexport function isSuccess<T>(result: GenerationResult<T>): result is { success: true; data: T } {\n return result.success;\n}\n\n/**\n * Check if a result is a failure (type guard)\n */\nexport function isFailure<T>(\n result: GenerationResult<T>,\n): result is { success: false; error: GenerationError } {\n return !result.success;\n}\n\n/**\n * Unwrap a result, throwing if it fails\n */\nexport function unwrap<T>(result: GenerationResult<T>): T {\n if (result.success) {\n return result.data;\n }\n throw new Error(`${result.error.code}: ${result.error.message}`);\n}\n\n/**\n * Unwrap a result with a default value for failures\n */\nexport function unwrapOr<T>(result: GenerationResult<T>, defaultValue: T): T {\n return result.success ? result.data : defaultValue;\n}\n","import { jsonrepair } from 'jsonrepair';\nimport { z } from 'zod/v4';\n\nconst MARKDOWN_CODE_BLOCK_RE = /```(?:json)?\\r?\\n([^`]*?)\\r?\\n```/g;\n\n/**\n * Error thrown when object parsing fails.\n * Contains the original text for debugging purposes.\n */\nclass ParseObjectError extends Error {\n public readonly name = 'ParseObjectError';\n public readonly cause?: unknown;\n public readonly text?: string;\n\n constructor(message: string, cause?: unknown, text?: string) {\n super(message);\n this.cause = cause;\n this.text = text;\n }\n}\n\nfunction convertToPrimitive(value: unknown, schema: z.ZodType): unknown {\n if (schema instanceof z.ZodBoolean) {\n return Boolean(value);\n }\n if (schema instanceof z.ZodNull) {\n return null;\n }\n if (schema instanceof z.ZodNumber) {\n return Number(value);\n }\n if (schema instanceof z.ZodString) {\n return String(value);\n }\n return value;\n}\n\nfunction extractArray(text: string, originalText: string): unknown {\n const start = text.indexOf('[');\n const end = text.lastIndexOf(']');\n\n if (start === -1 || end === -1) {\n throw new ParseObjectError('No array found in response', undefined, originalText);\n }\n\n try {\n const raw = text.slice(start, end + 1);\n return JSON.parse(jsonrepair(raw));\n } catch (error) {\n throw new ParseObjectError('Failed to parse array JSON', error, originalText);\n }\n}\n\nfunction extractObject(text: string, originalText: string): unknown {\n const start = text.indexOf('{');\n const end = text.lastIndexOf('}');\n\n if (start === -1 || end === -1) {\n throw new ParseObjectError('No object found in response', undefined, originalText);\n }\n\n try {\n const raw = text.slice(start, end + 1);\n return JSON.parse(jsonrepair(raw));\n } catch (error) {\n throw new ParseObjectError('Failed to parse object JSON', error, originalText);\n }\n}\n\nfunction extractPrimitive(text: string, schema: z.ZodType): unknown {\n const trimmed = text.trim();\n try {\n return convertToPrimitive(JSON.parse(trimmed), schema);\n } catch {\n return convertToPrimitive(trimmed, schema);\n }\n}\n\nfunction extractBySchemaType(text: string, schema: z.ZodType, originalText: string): unknown {\n if (schema instanceof z.ZodArray) {\n return extractArray(text, originalText);\n }\n\n if (schema instanceof z.ZodObject) {\n return extractObject(text, originalText);\n }\n\n if (\n schema instanceof z.ZodBoolean ||\n schema instanceof z.ZodNull ||\n schema instanceof z.ZodNumber ||\n schema instanceof z.ZodString\n ) {\n return extractPrimitive(text, schema);\n }\n\n // Handle union types - extract as object/array and let Zod validate which variant matches\n if (schema instanceof z.ZodUnion || schema instanceof z.ZodDiscriminatedUnion) {\n const objectStart = text.indexOf('{');\n if (objectStart !== -1) {\n return extractObject(text, originalText);\n }\n const arrayStart = text.indexOf('[');\n if (arrayStart !== -1) {\n return extractArray(text, originalText);\n }\n throw new ParseObjectError(\n 'No object or array found for union type',\n undefined,\n originalText,\n );\n }\n\n // Handle wrapper types - unwrap and delegate to inner type\n if (schema instanceof z.ZodOptional || schema instanceof z.ZodNullable) {\n return extractBySchemaType(text, schema.unwrap() as z.ZodType, originalText);\n }\n\n if (schema instanceof z.ZodDefault) {\n return extractBySchemaType(text, schema.def.innerType as z.ZodType, originalText);\n }\n\n // Handle .transform() which creates a ZodPipe in Zod v4\n if (schema instanceof z.ZodPipe) {\n return extractBySchemaType(text, schema.def.in as z.ZodType, originalText);\n }\n\n throw new ParseObjectError('Unsupported schema type', undefined, originalText);\n}\n\nfunction extractJsonFromCodeBlock(block: string): null | string {\n const content = block.replace(/```(?:json)?\\r?\\n([^`]*?)\\r?\\n```/, '$1').trim();\n try {\n JSON.parse(content);\n return content;\n } catch {\n return null;\n }\n}\n\nfunction findLongestString(strings: string[]): string {\n return strings.reduce((longest, current) =>\n current.length > longest.length ? current : longest,\n );\n}\n\nfunction findJsonStructures(text: string): string[] {\n const matches: string[] = [];\n let depth = 0;\n let start = -1;\n\n for (let i = 0; i < text.length; i++) {\n const char = text[i];\n if (char === '{' || char === '[') {\n if (depth === 0) {\n start = i;\n }\n depth++;\n } else if (char === '}' || char === ']') {\n depth--;\n if (depth === 0 && start !== -1) {\n const candidate = text.slice(start, i + 1);\n try {\n JSON.parse(candidate);\n matches.push(candidate);\n } catch {\n // Invalid JSON, skip\n }\n }\n }\n }\n\n return matches;\n}\n\nfunction extractJsonString(text: string): string {\n const codeBlocks = text.match(MARKDOWN_CODE_BLOCK_RE);\n if (codeBlocks && codeBlocks.length > 0) {\n const validBlocks = codeBlocks\n .map((block) => extractJsonFromCodeBlock(block))\n .filter((block): block is string => block !== null);\n\n if (validBlocks.length > 0) {\n return findLongestString(validBlocks);\n }\n }\n\n const structures = findJsonStructures(text);\n if (structures.length > 0) {\n return findLongestString(structures);\n }\n\n return text.replace(/\\s+/g, ' ').trim();\n}\n\nfunction unescapeString(text: string): string {\n return text\n .replace(/\\\\\"/g, '\"')\n .replace(/\\\\n/g, '\\n')\n .replace(/\\\\r/g, '\\r')\n .replace(/\\\\t/g, '\\t')\n .replace(/\\\\\\\\/g, '\\\\')\n .replace(/\\\\u([0-9a-fA-F]{4})/g, (_, code) =>\n String.fromCharCode(Number.parseInt(code, 16)),\n );\n}\n\nfunction unescapeJsonValues(json: unknown): unknown {\n if (typeof json === 'string') {\n return unescapeString(json);\n }\n if (Array.isArray(json)) {\n return json.map(unescapeJsonValues);\n }\n if (typeof json === 'object' && json !== null) {\n return Object.fromEntries(\n Object.entries(json).map(([key, value]) => [key, unescapeJsonValues(value)]),\n );\n }\n return json;\n}\n\n/**\n * Parses AI-generated text into structured data validated against a Zod schema.\n *\n * Handles common AI response formats:\n * - JSON wrapped in markdown code blocks\n * - JSON embedded in prose text\n * - Malformed JSON (auto-repaired)\n * - Escaped unicode and special characters\n *\n * @param text - The raw AI response text\n * @param schema - A Zod schema to validate and type the result\n * @returns The parsed and validated data\n * @throws {ParseObjectError} When parsing or validation fails\n *\n * @example\n * ```ts\n * const schema = z.object({ title: z.string(), tags: z.array(z.string()) });\n * const result = parseObject(aiResponse, schema);\n * // result is typed as { title: string; tags: string[] }\n * ```\n */\nfunction parseObject<T>(text: string, schema: z.ZodSchema<T>): T {\n try {\n const jsonString = extractJsonString(text);\n const extracted = extractBySchemaType(jsonString, schema, text);\n const unescaped = unescapeJsonValues(extracted);\n return schema.parse(unescaped);\n } catch (error) {\n if (error instanceof ParseObjectError) {\n throw error;\n }\n if (error instanceof z.ZodError) {\n throw new ParseObjectError('Failed to validate response against schema', error, text);\n }\n throw error;\n }\n}\n\nexport { parseObject, ParseObjectError };\n","import type { LanguageModelV3, SharedV3ProviderOptions } from '@ai-sdk/provider';\nimport { generateText } from 'ai';\nimport type { Schema } from 'zod';\n\nimport { parseObject, ParseObjectError } from '../parsing/parse-object.js';\nimport {\n classifyError,\n generationFailure,\n type GenerationResult,\n generationSuccess,\n} from '../result/result.js';\n\nexport interface GenerateStructuredOptions<T> {\n model: LanguageModelV3;\n prompt: string;\n system?: string;\n schema: Schema<T>;\n providerOptions?: SharedV3ProviderOptions;\n abortSignal?: AbortSignal;\n maxOutputTokens?: number;\n temperature?: number;\n}\n\n/**\n * Generate structured data from an AI model with automatic parsing and error handling.\n * Observability is handled by middleware - no metadata exposed to caller.\n */\nexport async function generateStructured<T>(\n options: GenerateStructuredOptions<T>,\n): Promise<GenerationResult<T>> {\n const {\n model,\n prompt,\n system,\n schema,\n providerOptions,\n abortSignal,\n maxOutputTokens,\n temperature,\n } = options;\n\n try {\n const response = await generateText({\n model,\n prompt,\n system,\n providerOptions,\n abortSignal,\n maxOutputTokens,\n temperature,\n });\n\n if (!response.text || response.text.trim() === '') {\n return generationFailure('EMPTY_RESULT', 'AI returned empty response');\n }\n\n try {\n const data = parseObject(response.text, schema);\n return generationSuccess(data);\n } catch (error) {\n if (error instanceof ParseObjectError) {\n return generationFailure('PARSING_FAILED', error.message, error);\n }\n return generationFailure('VALIDATION_FAILED', 'Schema validation failed', error);\n }\n } catch (error) {\n const code = classifyError(error);\n const message = error instanceof Error ? error.message : 'Unknown error';\n return generationFailure(code, message, error);\n }\n}\n","import { z } from 'zod/v4';\n\n/**\n * Creates a system prompt that instructs the model to output structured data\n * matching the provided Zod schema.\n *\n * Use this with `generateText` when the provider doesn't support native\n * structured outputs, then parse the response with `parseObject`.\n *\n * @param schema - A Zod schema defining the expected output structure\n * @returns A system prompt string with JSON schema instructions\n *\n * @example\n * ```ts\n * import { generateText } from 'ai';\n * import { createSchemaPrompt, parseObject } from '@jterrazz/intelligence';\n *\n * const schema = z.object({ title: z.string(), tags: z.array(z.string()) });\n *\n * const { text } = await generateText({\n * model,\n * prompt: 'Generate an article about TypeScript',\n * system: createSchemaPrompt(schema),\n * });\n *\n * const result = parseObject(text, schema);\n * ```\n */\nexport function createSchemaPrompt<T>(schema: z.ZodType<T>): string {\n const jsonSchema = z.toJSONSchema(schema);\n const schemaJson = JSON.stringify(jsonSchema, null, 2);\n\n const isPrimitive = ['boolean', 'integer', 'number', 'string'].includes(\n jsonSchema.type as string,\n );\n\n if (isPrimitive) {\n return `<OUTPUT_FORMAT>\nYou must respond with a ${jsonSchema.type} value that matches this schema:\n\n\\`\\`\\`json\n${schemaJson}\n\\`\\`\\`\n\nYour response should be only the ${jsonSchema.type} value, without any JSON wrapping or additional text.\n</OUTPUT_FORMAT>`;\n }\n\n return `<OUTPUT_FORMAT>\nYou must respond with valid JSON that matches this JSON schema:\n\n\\`\\`\\`json\n${schemaJson}\n\\`\\`\\`\n\nYour response must be parseable JSON that validates against this schema. Do not include any text outside the JSON.\n</OUTPUT_FORMAT>`;\n}\n","import { createOpenRouter } from '@openrouter/ai-sdk-provider';\nimport type { LanguageModel } from 'ai';\n\nexport interface ModelOptions {\n /** Maximum tokens to generate */\n maxTokens?: number;\n /** Reasoning configuration for supported models */\n reasoning?: {\n effort?: 'high' | 'low' | 'medium';\n exclude?: boolean;\n };\n}\n\nexport interface OpenRouterConfig {\n apiKey: string;\n metadata?: OpenRouterMetadata;\n}\n\nexport interface OpenRouterMetadata {\n /** Application name for X-Title header */\n application?: string;\n /** Website URL for HTTP-Referer header */\n website?: string;\n}\n\nexport interface OpenRouterProvider {\n /** Get a language model instance */\n model: (name: string, options?: ModelOptions) => LanguageModel;\n}\n\n/**\n * Creates an OpenRouter provider for AI SDK models.\n *\n * @example\n * ```ts\n * const provider = createOpenRouterProvider({ apiKey: process.env.OPENROUTER_API_KEY });\n * const model = provider.model('anthropic/claude-sonnet-4-20250514');\n *\n * const { text } = await generateText({ model, prompt: 'Hello!' });\n * ```\n */\nexport function createOpenRouterProvider(config: OpenRouterConfig): OpenRouterProvider {\n const openrouter = createOpenRouter({ apiKey: config.apiKey });\n\n return {\n model(name: string, options: ModelOptions = {}): LanguageModel {\n return openrouter(name, {\n ...(options.maxTokens !== undefined && {\n maxTokens: options.maxTokens,\n }),\n ...(options.reasoning && {\n extraBody: { reasoning: options.reasoning },\n }),\n });\n },\n };\n}\n","// Ports\nimport type {\n ExtractedProviderMetadata,\n ProviderMetadataPort,\n} from '../ports/provider-metadata.port.js';\n\ninterface OpenRouterUsage {\n promptTokens?: number;\n promptTokensDetails?: { cachedTokens?: number };\n completionTokens?: number;\n completionTokensDetails?: { reasoningTokens?: number };\n totalTokens?: number;\n cost?: number;\n}\n\ninterface OpenRouterProviderMetadata {\n provider?: string;\n usage?: OpenRouterUsage;\n}\n\n/**\n * OpenRouter adapter for extracting usage and cost from provider metadata\n */\nexport class OpenRouterMetadataAdapter implements ProviderMetadataPort {\n extract(providerMetadata: Record<string, unknown> | undefined): ExtractedProviderMetadata {\n const meta = providerMetadata?.openrouter as OpenRouterProviderMetadata | undefined;\n\n if (!meta?.usage) {\n return {};\n }\n\n const usage = meta.usage;\n\n return {\n usage: {\n input: usage.promptTokens ?? 0,\n output: usage.completionTokens ?? 0,\n total: usage.totalTokens,\n reasoning: usage.completionTokensDetails?.reasoningTokens,\n cacheRead: usage.promptTokensDetails?.cachedTokens,\n },\n cost: usage.cost !== undefined ? { total: usage.cost } : undefined,\n };\n }\n}\n","import { createOpenAI } from '@ai-sdk/openai';\nimport type { LanguageModel } from 'ai';\n\nexport interface OpenAICompatibleModelOptions {\n /** Maximum tokens to generate */\n maxTokens?: number;\n}\n\nexport interface OpenAICompatibleConfig {\n /** API key for authentication */\n apiKey: string;\n /** Base URL of the OpenAI-compatible API */\n baseURL: string;\n /** Optional model name mapping */\n modelMapping?: Record<string, string>;\n}\n\nexport interface OpenAICompatibleProvider {\n /** Get a language model instance */\n model: (name: string, options?: OpenAICompatibleModelOptions) => LanguageModel;\n}\n\n/**\n * Creates a provider for OpenAI-compatible APIs.\n * Works with any API implementing the OpenAI chat completions spec.\n */\nexport function createOpenAICompatibleProvider(\n config: OpenAICompatibleConfig,\n): OpenAICompatibleProvider {\n const openai = createOpenAI({\n apiKey: config.apiKey,\n baseURL: config.baseURL,\n });\n\n return {\n model(name: string, _options: OpenAICompatibleModelOptions = {}): LanguageModel {\n const modelName = config.modelMapping?.[name] ?? name;\n return openai(modelName);\n },\n };\n}\n","import type {\n ExtractedProviderMetadata,\n ProviderMetadataPort,\n} from '../ports/provider-metadata.port.js';\n\ninterface OpenAIUsage {\n promptTokens?: number;\n completionTokens?: number;\n totalTokens?: number;\n}\n\ninterface OpenAIProviderMetadata {\n responseId?: string;\n usage?: OpenAIUsage;\n}\n\n/**\n * Metadata adapter for OpenAI-compatible APIs (including gateway-intelligence).\n * Extracts usage data from the standardized OpenAI response format.\n */\nexport class OpenAICompatibleMetadataAdapter implements ProviderMetadataPort {\n extract(providerMetadata: Record<string, unknown> | undefined): ExtractedProviderMetadata {\n const meta = providerMetadata?.openai as OpenAIProviderMetadata | undefined;\n\n if (!meta?.usage) {\n return {};\n }\n\n const usage = meta.usage;\n\n return {\n usage: {\n input: usage.promptTokens ?? 0,\n output: usage.completionTokens ?? 0,\n total: usage.totalTokens,\n },\n };\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ //#region src/parsing/parse-text.ts
2
+ const INVISIBLE_CHARS_RE = /[\u00AD\u180E\u200B-\u200C\u200E-\u200F\u202A-\u202E\u2060-\u2064\u2066-\u2069\uFEFF]/g;
3
+ const ASCII_CTRL_RE = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g;
4
+ const SPACE_LIKE_RE = /[\u00A0\u1680\u2000-\u200A\u202F\u205F\u3000]/g;
5
+ const MULTIPLE_SPACES_RE = / {2,}/g;
6
+ const CR_RE = /\r\n?/g;
7
+ const CITATION_RE = / *\(oaicite:\d+\)\{index=\d+\}/g;
8
+ const EM_DASH_SEPARATOR_RE = /\s*[—–―‒]\s*/g;
9
+ const TYPOGRAPHY_REPLACEMENTS = [
10
+ {
11
+ pattern: /[\u2018\u2019\u201A]/g,
12
+ replacement: "'"
13
+ },
14
+ {
15
+ pattern: /[\u201C\u201D\u201E]/g,
16
+ replacement: "\""
17
+ },
18
+ {
19
+ pattern: /\u2026/g,
20
+ replacement: "..."
21
+ },
22
+ {
23
+ pattern: /[\u2022\u25AA-\u25AB\u25B8-\u25B9\u25CF]/g,
24
+ replacement: "-"
25
+ }
26
+ ];
27
+ /**
28
+ * Parses and sanitizes text by removing AI artifacts and normalizing typography.
29
+ *
30
+ * @param text - The text to parse
31
+ * @param options - Parsing options
32
+ * @returns The cleaned text
33
+ */
34
+ function parseText(text, options = {}) {
35
+ const { normalizeEmDashesToCommas = true, collapseSpaces = true } = options;
36
+ if (!text) return "";
37
+ let result = text;
38
+ result = result.replace(CR_RE, "\n");
39
+ result = result.replace(CITATION_RE, "");
40
+ result = result.normalize("NFKC");
41
+ result = result.replace(INVISIBLE_CHARS_RE, "");
42
+ result = result.replace(ASCII_CTRL_RE, "");
43
+ if (normalizeEmDashesToCommas) result = result.replace(EM_DASH_SEPARATOR_RE, ", ");
44
+ result = result.replace(SPACE_LIKE_RE, " ");
45
+ for (const { pattern, replacement } of TYPOGRAPHY_REPLACEMENTS) result = result.replace(pattern, replacement);
46
+ if (collapseSpaces) result = result.replace(MULTIPLE_SPACES_RE, " ").trim();
47
+ return result;
48
+ }
49
+ //#endregion
50
+ Object.defineProperty(exports, "parseText", {
51
+ enumerable: true,
52
+ get: function() {
53
+ return parseText;
54
+ }
55
+ });
56
+
57
+ //# sourceMappingURL=parse-text.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-text.cjs","names":[],"sources":["../src/parsing/parse-text.ts"],"sourcesContent":["const INVISIBLE_CHARS_RE =\n /[\\u00AD\\u180E\\u200B-\\u200C\\u200E-\\u200F\\u202A-\\u202E\\u2060-\\u2064\\u2066-\\u2069\\uFEFF]/g;\n\n// oxlint-disable-next-line no-control-regex -- Intentionally matching control characters for sanitization\nconst ASCII_CTRL_RE = /[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F]/g;\n\nconst SPACE_LIKE_RE = /[\\u00A0\\u1680\\u2000-\\u200A\\u202F\\u205F\\u3000]/g;\nconst MULTIPLE_SPACES_RE = / {2,}/g;\nconst CR_RE = /\\r\\n?/g;\nconst CITATION_RE = / *\\(oaicite:\\d+\\)\\{index=\\d+\\}/g;\nconst EM_DASH_SEPARATOR_RE = /\\s*[—–―‒]\\s*/g;\n\nconst TYPOGRAPHY_REPLACEMENTS: Array<{ pattern: RegExp; replacement: string }> = [\n { pattern: /[\\u2018\\u2019\\u201A]/g, replacement: \"'\" },\n { pattern: /[\\u201C\\u201D\\u201E]/g, replacement: '\"' },\n { pattern: /\\u2026/g, replacement: '...' },\n { pattern: /[\\u2022\\u25AA-\\u25AB\\u25B8-\\u25B9\\u25CF]/g, replacement: '-' },\n];\n\nexport interface ParseTextOptions {\n /** Collapse multiple spaces into one (default: true) */\n collapseSpaces?: boolean;\n /** Convert em/en dashes with spaces to commas (default: true) */\n normalizeEmDashesToCommas?: boolean;\n}\n\n/**\n * Parses and sanitizes text by removing AI artifacts and normalizing typography.\n *\n * @param text - The text to parse\n * @param options - Parsing options\n * @returns The cleaned text\n */\nexport function parseText(text: string, options: ParseTextOptions = {}): string {\n const { normalizeEmDashesToCommas = true, collapseSpaces = true } = options;\n\n if (!text) {\n return '';\n }\n\n let result = text;\n\n result = result.replace(CR_RE, '\\n');\n result = result.replace(CITATION_RE, '');\n result = result.normalize('NFKC');\n result = result.replace(INVISIBLE_CHARS_RE, '');\n result = result.replace(ASCII_CTRL_RE, '');\n\n if (normalizeEmDashesToCommas) {\n result = result.replace(EM_DASH_SEPARATOR_RE, ', ');\n }\n\n result = result.replace(SPACE_LIKE_RE, ' ');\n\n for (const { pattern, replacement } of TYPOGRAPHY_REPLACEMENTS) {\n result = result.replace(pattern, replacement);\n }\n\n if (collapseSpaces) {\n result = result.replace(MULTIPLE_SPACES_RE, ' ').trim();\n }\n\n return result;\n}\n"],"mappings":";AAAA,MAAM,qBACF;AAGJ,MAAM,gBAAgB;AAEtB,MAAM,gBAAgB;AACtB,MAAM,qBAAqB;AAC3B,MAAM,QAAQ;AACd,MAAM,cAAc;AACpB,MAAM,uBAAuB;AAE7B,MAAM,0BAA2E;CAC7E;EAAE,SAAS;EAAyB,aAAa;EAAK;CACtD;EAAE,SAAS;EAAyB,aAAa;EAAK;CACtD;EAAE,SAAS;EAAW,aAAa;EAAO;CAC1C;EAAE,SAAS;EAA6C,aAAa;EAAK;CAC7E;;;;;;;;AAgBD,SAAgB,UAAU,MAAc,UAA4B,EAAE,EAAU;CAC5E,MAAM,EAAE,4BAA4B,MAAM,iBAAiB,SAAS;AAEpE,KAAI,CAAC,KACD,QAAO;CAGX,IAAI,SAAS;AAEb,UAAS,OAAO,QAAQ,OAAO,KAAK;AACpC,UAAS,OAAO,QAAQ,aAAa,GAAG;AACxC,UAAS,OAAO,UAAU,OAAO;AACjC,UAAS,OAAO,QAAQ,oBAAoB,GAAG;AAC/C,UAAS,OAAO,QAAQ,eAAe,GAAG;AAE1C,KAAI,0BACA,UAAS,OAAO,QAAQ,sBAAsB,KAAK;AAGvD,UAAS,OAAO,QAAQ,eAAe,IAAI;AAE3C,MAAK,MAAM,EAAE,SAAS,iBAAiB,wBACnC,UAAS,OAAO,QAAQ,SAAS,YAAY;AAGjD,KAAI,eACA,UAAS,OAAO,QAAQ,oBAAoB,IAAI,CAAC,MAAM;AAG3D,QAAO"}
@@ -0,0 +1,18 @@
1
+ //#region src/parsing/parse-text.d.ts
2
+ interface ParseTextOptions {
3
+ /** Collapse multiple spaces into one (default: true) */
4
+ collapseSpaces?: boolean;
5
+ /** Convert em/en dashes with spaces to commas (default: true) */
6
+ normalizeEmDashesToCommas?: boolean;
7
+ }
8
+ /**
9
+ * Parses and sanitizes text by removing AI artifacts and normalizing typography.
10
+ *
11
+ * @param text - The text to parse
12
+ * @param options - Parsing options
13
+ * @returns The cleaned text
14
+ */
15
+ declare function parseText(text: string, options?: ParseTextOptions): string;
16
+ //#endregion
17
+ export { parseText as n, ParseTextOptions as t };
18
+ //# sourceMappingURL=parse-text.d.cts.map
@@ -0,0 +1,18 @@
1
+ //#region src/parsing/parse-text.d.ts
2
+ interface ParseTextOptions {
3
+ /** Collapse multiple spaces into one (default: true) */
4
+ collapseSpaces?: boolean;
5
+ /** Convert em/en dashes with spaces to commas (default: true) */
6
+ normalizeEmDashesToCommas?: boolean;
7
+ }
8
+ /**
9
+ * Parses and sanitizes text by removing AI artifacts and normalizing typography.
10
+ *
11
+ * @param text - The text to parse
12
+ * @param options - Parsing options
13
+ * @returns The cleaned text
14
+ */
15
+ declare function parseText(text: string, options?: ParseTextOptions): string;
16
+ //#endregion
17
+ export { parseText as n, ParseTextOptions as t };
18
+ //# sourceMappingURL=parse-text.d.ts.map
@@ -0,0 +1,52 @@
1
+ //#region src/parsing/parse-text.ts
2
+ const INVISIBLE_CHARS_RE = /[\u00AD\u180E\u200B-\u200C\u200E-\u200F\u202A-\u202E\u2060-\u2064\u2066-\u2069\uFEFF]/g;
3
+ const ASCII_CTRL_RE = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g;
4
+ const SPACE_LIKE_RE = /[\u00A0\u1680\u2000-\u200A\u202F\u205F\u3000]/g;
5
+ const MULTIPLE_SPACES_RE = / {2,}/g;
6
+ const CR_RE = /\r\n?/g;
7
+ const CITATION_RE = / *\(oaicite:\d+\)\{index=\d+\}/g;
8
+ const EM_DASH_SEPARATOR_RE = /\s*[—–―‒]\s*/g;
9
+ const TYPOGRAPHY_REPLACEMENTS = [
10
+ {
11
+ pattern: /[\u2018\u2019\u201A]/g,
12
+ replacement: "'"
13
+ },
14
+ {
15
+ pattern: /[\u201C\u201D\u201E]/g,
16
+ replacement: "\""
17
+ },
18
+ {
19
+ pattern: /\u2026/g,
20
+ replacement: "..."
21
+ },
22
+ {
23
+ pattern: /[\u2022\u25AA-\u25AB\u25B8-\u25B9\u25CF]/g,
24
+ replacement: "-"
25
+ }
26
+ ];
27
+ /**
28
+ * Parses and sanitizes text by removing AI artifacts and normalizing typography.
29
+ *
30
+ * @param text - The text to parse
31
+ * @param options - Parsing options
32
+ * @returns The cleaned text
33
+ */
34
+ function parseText(text, options = {}) {
35
+ const { normalizeEmDashesToCommas = true, collapseSpaces = true } = options;
36
+ if (!text) return "";
37
+ let result = text;
38
+ result = result.replace(CR_RE, "\n");
39
+ result = result.replace(CITATION_RE, "");
40
+ result = result.normalize("NFKC");
41
+ result = result.replace(INVISIBLE_CHARS_RE, "");
42
+ result = result.replace(ASCII_CTRL_RE, "");
43
+ if (normalizeEmDashesToCommas) result = result.replace(EM_DASH_SEPARATOR_RE, ", ");
44
+ result = result.replace(SPACE_LIKE_RE, " ");
45
+ for (const { pattern, replacement } of TYPOGRAPHY_REPLACEMENTS) result = result.replace(pattern, replacement);
46
+ if (collapseSpaces) result = result.replace(MULTIPLE_SPACES_RE, " ").trim();
47
+ return result;
48
+ }
49
+ //#endregion
50
+ export { parseText as t };
51
+
52
+ //# sourceMappingURL=parse-text.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-text.js","names":[],"sources":["../src/parsing/parse-text.ts"],"sourcesContent":["const INVISIBLE_CHARS_RE =\n /[\\u00AD\\u180E\\u200B-\\u200C\\u200E-\\u200F\\u202A-\\u202E\\u2060-\\u2064\\u2066-\\u2069\\uFEFF]/g;\n\n// oxlint-disable-next-line no-control-regex -- Intentionally matching control characters for sanitization\nconst ASCII_CTRL_RE = /[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\u007F]/g;\n\nconst SPACE_LIKE_RE = /[\\u00A0\\u1680\\u2000-\\u200A\\u202F\\u205F\\u3000]/g;\nconst MULTIPLE_SPACES_RE = / {2,}/g;\nconst CR_RE = /\\r\\n?/g;\nconst CITATION_RE = / *\\(oaicite:\\d+\\)\\{index=\\d+\\}/g;\nconst EM_DASH_SEPARATOR_RE = /\\s*[—–―‒]\\s*/g;\n\nconst TYPOGRAPHY_REPLACEMENTS: Array<{ pattern: RegExp; replacement: string }> = [\n { pattern: /[\\u2018\\u2019\\u201A]/g, replacement: \"'\" },\n { pattern: /[\\u201C\\u201D\\u201E]/g, replacement: '\"' },\n { pattern: /\\u2026/g, replacement: '...' },\n { pattern: /[\\u2022\\u25AA-\\u25AB\\u25B8-\\u25B9\\u25CF]/g, replacement: '-' },\n];\n\nexport interface ParseTextOptions {\n /** Collapse multiple spaces into one (default: true) */\n collapseSpaces?: boolean;\n /** Convert em/en dashes with spaces to commas (default: true) */\n normalizeEmDashesToCommas?: boolean;\n}\n\n/**\n * Parses and sanitizes text by removing AI artifacts and normalizing typography.\n *\n * @param text - The text to parse\n * @param options - Parsing options\n * @returns The cleaned text\n */\nexport function parseText(text: string, options: ParseTextOptions = {}): string {\n const { normalizeEmDashesToCommas = true, collapseSpaces = true } = options;\n\n if (!text) {\n return '';\n }\n\n let result = text;\n\n result = result.replace(CR_RE, '\\n');\n result = result.replace(CITATION_RE, '');\n result = result.normalize('NFKC');\n result = result.replace(INVISIBLE_CHARS_RE, '');\n result = result.replace(ASCII_CTRL_RE, '');\n\n if (normalizeEmDashesToCommas) {\n result = result.replace(EM_DASH_SEPARATOR_RE, ', ');\n }\n\n result = result.replace(SPACE_LIKE_RE, ' ');\n\n for (const { pattern, replacement } of TYPOGRAPHY_REPLACEMENTS) {\n result = result.replace(pattern, replacement);\n }\n\n if (collapseSpaces) {\n result = result.replace(MULTIPLE_SPACES_RE, ' ').trim();\n }\n\n return result;\n}\n"],"mappings":";AAAA,MAAM,qBACF;AAGJ,MAAM,gBAAgB;AAEtB,MAAM,gBAAgB;AACtB,MAAM,qBAAqB;AAC3B,MAAM,QAAQ;AACd,MAAM,cAAc;AACpB,MAAM,uBAAuB;AAE7B,MAAM,0BAA2E;CAC7E;EAAE,SAAS;EAAyB,aAAa;EAAK;CACtD;EAAE,SAAS;EAAyB,aAAa;EAAK;CACtD;EAAE,SAAS;EAAW,aAAa;EAAO;CAC1C;EAAE,SAAS;EAA6C,aAAa;EAAK;CAC7E;;;;;;;;AAgBD,SAAgB,UAAU,MAAc,UAA4B,EAAE,EAAU;CAC5E,MAAM,EAAE,4BAA4B,MAAM,iBAAiB,SAAS;AAEpE,KAAI,CAAC,KACD,QAAO;CAGX,IAAI,SAAS;AAEb,UAAS,OAAO,QAAQ,OAAO,KAAK;AACpC,UAAS,OAAO,QAAQ,aAAa,GAAG;AACxC,UAAS,OAAO,UAAU,OAAO;AACjC,UAAS,OAAO,QAAQ,oBAAoB,GAAG;AAC/C,UAAS,OAAO,QAAQ,eAAe,GAAG;AAE1C,KAAI,0BACA,UAAS,OAAO,QAAQ,sBAAsB,KAAK;AAGvD,UAAS,OAAO,QAAQ,eAAe,IAAI;AAE3C,MAAK,MAAM,EAAE,SAAS,iBAAiB,wBACnC,UAAS,OAAO,QAAQ,SAAS,YAAY;AAGjD,KAAI,eACA,UAAS,OAAO,QAAQ,oBAAoB,IAAI,CAAC,MAAM;AAG3D,QAAO"}
package/dist/text.cjs ADDED
@@ -0,0 +1,3 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_parse_text = require("./parse-text.cjs");
3
+ exports.parseText = require_parse_text.parseText;
@@ -0,0 +1,2 @@
1
+ import { n as parseText, t as ParseTextOptions } from "./parse-text.cjs";
2
+ export { type ParseTextOptions, parseText };
package/dist/text.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import { n as parseText, t as ParseTextOptions } from "./parse-text.js";
2
+ export { type ParseTextOptions, parseText };
package/dist/text.js ADDED
@@ -0,0 +1,2 @@
1
+ import { t as parseText } from "./parse-text.js";
2
+ export { parseText };
package/package.json CHANGED
@@ -1,48 +1,62 @@
1
1
  {
2
- "name": "@jterrazz/intelligence",
3
- "version": "4.0.4",
4
- "author": "Jean-Baptiste Terrazzoni <contact@jterrazz.com>",
5
- "repository": {
6
- "type": "git",
7
- "url": "https://github.com/jterrazz/package-intelligence"
8
- },
9
- "files": [
10
- "dist"
11
- ],
12
- "type": "module",
13
- "exports": {
14
- ".": {
15
- "require": "./dist/index.cjs",
16
- "import": "./dist/index.js"
2
+ "name": "@jterrazz/intelligence",
3
+ "version": "4.1.0",
4
+ "author": "Jean-Baptiste Terrazzoni <contact@jterrazz.com>",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/jterrazz/package-intelligence"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "type": "module",
13
+ "sideEffects": false,
14
+ "exports": {
15
+ ".": {
16
+ "require": "./dist/index.cjs",
17
+ "import": "./dist/index.js"
18
+ },
19
+ "./text": {
20
+ "require": "./dist/text.cjs",
21
+ "import": "./dist/text.js"
22
+ }
23
+ },
24
+ "publishConfig": {
25
+ "registry": "https://registry.npmjs.org/"
26
+ },
27
+ "scripts": {
28
+ "build": "tsdown --config tsdown.config.ts",
29
+ "lint": "typescript check",
30
+ "lint:fix": "typescript fix",
31
+ "test": "vitest --run"
32
+ },
33
+ "dependencies": {
34
+ "@ai-sdk/openai": "^3.0.48",
35
+ "@jterrazz/logger": "^1.5.0",
36
+ "@openrouter/ai-sdk-provider": "^2.3.3",
37
+ "jsonrepair": "^3.13.3",
38
+ "langfuse": "^3.38.6"
39
+ },
40
+ "devDependencies": {
41
+ "@ai-sdk/provider": "^3.0.8",
42
+ "@jterrazz/test": "^5.3.2",
43
+ "@jterrazz/typescript": "^6.0.0",
44
+ "@types/node": "^25.5.0",
45
+ "ai": "^6.0.140",
46
+ "tsdown": "^0.21.5",
47
+ "vitest": "^4.1.2",
48
+ "zod": "^4.1.11"
49
+ },
50
+ "peerDependencies": {
51
+ "ai": ">=6.0.0",
52
+ "zod": ">=4.0.0"
53
+ },
54
+ "peerDependenciesMeta": {
55
+ "ai": {
56
+ "optional": true
57
+ },
58
+ "zod": {
59
+ "optional": true
60
+ }
17
61
  }
18
- },
19
- "publishConfig": {
20
- "registry": "https://registry.npmjs.org/"
21
- },
22
- "scripts": {
23
- "build": "typescript bundle",
24
- "lint": "codestyle check",
25
- "lint:fix": "codestyle fix",
26
- "test": "vitest --run"
27
- },
28
- "dependencies": {
29
- "@ai-sdk/openai": "^3.0.48",
30
- "@jterrazz/logger": "^1.5.0",
31
- "@openrouter/ai-sdk-provider": "^2.3.3",
32
- "jsonrepair": "^3.13.3",
33
- "langfuse": "^3.38.6"
34
- },
35
- "devDependencies": {
36
- "@ai-sdk/provider": "^3.0.8",
37
- "@jterrazz/codestyle": "^2.1.0",
38
- "@jterrazz/test": "^5.0.0",
39
- "@jterrazz/typescript": "^5.2.0",
40
- "@types/node": "^25.5.0",
41
- "ai": "^6.0.140",
42
- "vitest": "^4.1.2"
43
- },
44
- "peerDependencies": {
45
- "ai": ">=6.0.0",
46
- "zod": ">=4.0.0"
47
- }
48
62
  }