@jterrazz/intelligence 3.0.2 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +260 -63
  2. package/dist/index.cjs +594 -808
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.ts +350 -5
  5. package/dist/index.js +620 -5
  6. package/dist/index.js.map +1 -1
  7. package/package.json +26 -20
  8. package/dist/middleware/__tests__/logging.middleware.test.d.ts +0 -1
  9. package/dist/middleware/__tests__/logging.middleware.test.js +0 -463
  10. package/dist/middleware/__tests__/logging.middleware.test.js.map +0 -1
  11. package/dist/middleware/logging.middleware.d.ts +0 -29
  12. package/dist/middleware/logging.middleware.js +0 -298
  13. package/dist/middleware/logging.middleware.js.map +0 -1
  14. package/dist/parsing/__tests__/create-schema-prompt.test.d.ts +0 -1
  15. package/dist/parsing/__tests__/create-schema-prompt.test.js +0 -53
  16. package/dist/parsing/__tests__/create-schema-prompt.test.js.map +0 -1
  17. package/dist/parsing/__tests__/parse-object.test.d.ts +0 -1
  18. package/dist/parsing/__tests__/parse-object.test.js +0 -433
  19. package/dist/parsing/__tests__/parse-object.test.js.map +0 -1
  20. package/dist/parsing/__tests__/parse-text.test.d.ts +0 -1
  21. package/dist/parsing/__tests__/parse-text.test.js +0 -167
  22. package/dist/parsing/__tests__/parse-text.test.js.map +0 -1
  23. package/dist/parsing/create-schema-prompt.d.ts +0 -28
  24. package/dist/parsing/create-schema-prompt.js +0 -42
  25. package/dist/parsing/create-schema-prompt.js.map +0 -1
  26. package/dist/parsing/parse-object.d.ts +0 -33
  27. package/dist/parsing/parse-object.js +0 -383
  28. package/dist/parsing/parse-object.js.map +0 -1
  29. package/dist/parsing/parse-text.d.ts +0 -14
  30. package/dist/parsing/parse-text.js +0 -76
  31. package/dist/parsing/parse-text.js.map +0 -1
  32. package/dist/providers/openrouter.provider.d.ts +0 -36
  33. package/dist/providers/openrouter.provider.js +0 -58
  34. package/dist/providers/openrouter.provider.js.map +0 -1
@@ -0,0 +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"],"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// 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\n constructor(\n message: string,\n public readonly cause?: unknown,\n public readonly text?: string,\n ) {\n super(message);\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"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,350 @@
1
- export { createLoggingMiddleware, type LoggingMiddlewareOptions, } from './middleware/logging.middleware.js';
2
- export { createSchemaPrompt } from './parsing/create-schema-prompt.js';
3
- export { parseObject, ParseObjectError } from './parsing/parse-object.js';
4
- export { parseText, type ParseTextOptions } from './parsing/parse-text.js';
5
- export { createOpenRouterProvider, type ModelOptions, type OpenRouterConfig, type OpenRouterMetadata, type OpenRouterProvider, } from './providers/openrouter.provider.js';
1
+ import { LanguageModel, LanguageModelMiddleware } from "ai";
2
+ import { z } from "zod/v4";
3
+ import { LoggerPort } from "@jterrazz/logger";
4
+ import { LanguageModelV3, SharedV3ProviderOptions } from "@ai-sdk/provider";
5
+ import { Schema } from "zod";
6
+
7
+ //#region src/logging/logging.middleware.d.ts
8
+ interface LoggingMiddlewareOptions {
9
+ logger: LoggerPort;
10
+ include?: {
11
+ params?: boolean;
12
+ content?: boolean;
13
+ usage?: boolean;
14
+ };
15
+ }
16
+ /**
17
+ * Creates middleware that logs AI SDK requests and responses.
18
+ */
19
+ declare function createLoggingMiddleware(options: LoggingMiddlewareOptions): LanguageModelMiddleware;
20
+ //#endregion
21
+ //#region src/ports/observability.port.d.ts
22
+ /**
23
+ * Usage details for LLM generations
24
+ */
25
+ interface UsageDetails {
26
+ input: number;
27
+ output: number;
28
+ total?: number;
29
+ reasoning?: number;
30
+ cacheRead?: number;
31
+ cacheWrite?: number;
32
+ }
33
+ /**
34
+ * Cost details for LLM generations
35
+ */
36
+ interface CostDetails {
37
+ total: number;
38
+ input?: number;
39
+ output?: number;
40
+ }
41
+ /**
42
+ * Parameters for creating a trace
43
+ */
44
+ interface TraceParams {
45
+ id: string;
46
+ name: string;
47
+ metadata?: Record<string, unknown>;
48
+ }
49
+ /**
50
+ * Parameters for recording a generation
51
+ */
52
+ interface GenerationParams {
53
+ traceId: string;
54
+ name: string;
55
+ model: string;
56
+ input: unknown;
57
+ output: string;
58
+ startTime: Date;
59
+ endTime: Date;
60
+ usage?: UsageDetails;
61
+ cost?: CostDetails;
62
+ metadata?: Record<string, unknown>;
63
+ }
64
+ /**
65
+ * Port for observability integrations (Langfuse, Datadog, etc.)
66
+ */
67
+ interface ObservabilityPort {
68
+ trace(params: TraceParams): void;
69
+ generation(params: GenerationParams): void;
70
+ flush(): Promise<void>;
71
+ shutdown(): Promise<void>;
72
+ }
73
+ //#endregion
74
+ //#region src/ports/provider-metadata.port.d.ts
75
+ /**
76
+ * Extracted metadata from a provider response
77
+ */
78
+ interface ExtractedProviderMetadata {
79
+ usage?: UsageDetails;
80
+ cost?: CostDetails;
81
+ }
82
+ /**
83
+ * Port for extracting usage and cost data from provider-specific metadata.
84
+ * Implement this interface for each AI provider (OpenRouter, Anthropic, etc.)
85
+ */
86
+ interface ProviderMetadataPort {
87
+ /**
88
+ * Extract usage and cost data from provider metadata
89
+ * @param metadata - The raw provider metadata from AI SDK response
90
+ * @returns Extracted usage and cost details, or undefined values if not available
91
+ */
92
+ extract(metadata: Record<string, unknown> | undefined): ExtractedProviderMetadata;
93
+ }
94
+ //#endregion
95
+ //#region src/observability/observability.middleware.d.ts
96
+ /**
97
+ * Metadata passed per-call via providerOptions
98
+ */
99
+ interface ObservabilityMetadata {
100
+ traceId: string;
101
+ name?: string;
102
+ metadata?: Record<string, unknown>;
103
+ }
104
+ interface ObservabilityMiddlewareOptions {
105
+ observability: ObservabilityPort;
106
+ providerMetadata?: ProviderMetadataPort;
107
+ }
108
+ /**
109
+ * Helper to create type-safe observability metadata for providerOptions
110
+ */
111
+ declare function withObservability(meta: ObservabilityMetadata): SharedV3ProviderOptions;
112
+ /**
113
+ * Creates middleware that sends generation data to an observability platform.
114
+ */
115
+ declare function createObservabilityMiddleware(options: ObservabilityMiddlewareOptions): LanguageModelMiddleware;
116
+ //#endregion
117
+ //#region src/observability/langfuse.adapter.d.ts
118
+ interface LangfuseConfig {
119
+ secretKey: string;
120
+ publicKey: string;
121
+ baseUrl?: string;
122
+ environment?: string;
123
+ release?: string;
124
+ }
125
+ /**
126
+ * Langfuse adapter implementing ObservabilityPort
127
+ */
128
+ declare class LangfuseAdapter implements ObservabilityPort {
129
+ private readonly client;
130
+ constructor(config: LangfuseConfig);
131
+ flush(): Promise<void>;
132
+ generation(params: GenerationParams): void;
133
+ shutdown(): Promise<void>;
134
+ trace(params: TraceParams): void;
135
+ }
136
+ //#endregion
137
+ //#region src/observability/noop.adapter.d.ts
138
+ /**
139
+ * No-op adapter that silently discards all observability data.
140
+ * Useful for testing, development, or when observability is disabled.
141
+ */
142
+ declare class NoopObservabilityAdapter implements ObservabilityPort {
143
+ flush(): Promise<void>;
144
+ generation(_params: GenerationParams): void;
145
+ shutdown(): Promise<void>;
146
+ trace(_params: TraceParams): void;
147
+ }
148
+ //#endregion
149
+ //#region src/result/result.d.ts
150
+ /**
151
+ * Error codes for AI generation failures
152
+ */
153
+ type GenerationErrorCode = "AI_GENERATION_FAILED" | "EMPTY_RESULT" | "PARSING_FAILED" | "RATE_LIMITED" | "TIMEOUT" | "VALIDATION_FAILED";
154
+ /**
155
+ * Structured error information from AI generation
156
+ */
157
+ interface GenerationError {
158
+ code: GenerationErrorCode;
159
+ message: string;
160
+ cause?: unknown;
161
+ }
162
+ /**
163
+ * Discriminated union result type for AI operations.
164
+ * Forces explicit handling of both success and failure cases.
165
+ */
166
+ type GenerationResult<T> = {
167
+ success: false;
168
+ error: GenerationError;
169
+ } | {
170
+ success: true;
171
+ data: T;
172
+ };
173
+ /**
174
+ * Create a successful result
175
+ */
176
+ declare function generationSuccess<T>(data: T): GenerationResult<T>;
177
+ /**
178
+ * Create a failed result
179
+ */
180
+ declare function generationFailure<T>(code: GenerationErrorCode, message: string, cause?: unknown): GenerationResult<T>;
181
+ /**
182
+ * Classify an error into a GenerationErrorCode
183
+ */
184
+ declare function classifyError(error: unknown): GenerationErrorCode;
185
+ /**
186
+ * Check if a result is successful (type guard)
187
+ */
188
+ declare function isSuccess<T>(result: GenerationResult<T>): result is {
189
+ success: true;
190
+ data: T;
191
+ };
192
+ /**
193
+ * Check if a result is a failure (type guard)
194
+ */
195
+ declare function isFailure<T>(result: GenerationResult<T>): result is {
196
+ success: false;
197
+ error: GenerationError;
198
+ };
199
+ /**
200
+ * Unwrap a result, throwing if it fails
201
+ */
202
+ declare function unwrap<T>(result: GenerationResult<T>): T;
203
+ /**
204
+ * Unwrap a result with a default value for failures
205
+ */
206
+ declare function unwrapOr<T>(result: GenerationResult<T>, defaultValue: T): T;
207
+ //#endregion
208
+ //#region src/generation/generate-structured.d.ts
209
+ interface GenerateStructuredOptions<T> {
210
+ model: LanguageModelV3;
211
+ prompt: string;
212
+ system?: string;
213
+ schema: Schema<T>;
214
+ providerOptions?: SharedV3ProviderOptions;
215
+ abortSignal?: AbortSignal;
216
+ maxOutputTokens?: number;
217
+ temperature?: number;
218
+ }
219
+ /**
220
+ * Generate structured data from an AI model with automatic parsing and error handling.
221
+ * Observability is handled by middleware - no metadata exposed to caller.
222
+ */
223
+ declare function generateStructured<T>(options: GenerateStructuredOptions<T>): Promise<GenerationResult<T>>;
224
+ //#endregion
225
+ //#region src/parsing/create-schema-prompt.d.ts
226
+ /**
227
+ * Creates a system prompt that instructs the model to output structured data
228
+ * matching the provided Zod schema.
229
+ *
230
+ * Use this with `generateText` when the provider doesn't support native
231
+ * structured outputs, then parse the response with `parseObject`.
232
+ *
233
+ * @param schema - A Zod schema defining the expected output structure
234
+ * @returns A system prompt string with JSON schema instructions
235
+ *
236
+ * @example
237
+ * ```ts
238
+ * import { generateText } from 'ai';
239
+ * import { createSchemaPrompt, parseObject } from '@jterrazz/intelligence';
240
+ *
241
+ * const schema = z.object({ title: z.string(), tags: z.array(z.string()) });
242
+ *
243
+ * const { text } = await generateText({
244
+ * model,
245
+ * prompt: 'Generate an article about TypeScript',
246
+ * system: createSchemaPrompt(schema),
247
+ * });
248
+ *
249
+ * const result = parseObject(text, schema);
250
+ * ```
251
+ */
252
+ declare function createSchemaPrompt<T>(schema: z.ZodType<T>): string;
253
+ //#endregion
254
+ //#region src/parsing/parse-object.d.ts
255
+ /**
256
+ * Error thrown when object parsing fails.
257
+ * Contains the original text for debugging purposes.
258
+ */
259
+ declare class ParseObjectError extends Error {
260
+ readonly cause?: unknown;
261
+ readonly text?: string | undefined;
262
+ readonly name = "ParseObjectError";
263
+ constructor(message: string, cause?: unknown, text?: string | undefined);
264
+ }
265
+ /**
266
+ * Parses AI-generated text into structured data validated against a Zod schema.
267
+ *
268
+ * Handles common AI response formats:
269
+ * - JSON wrapped in markdown code blocks
270
+ * - JSON embedded in prose text
271
+ * - Malformed JSON (auto-repaired)
272
+ * - Escaped unicode and special characters
273
+ *
274
+ * @param text - The raw AI response text
275
+ * @param schema - A Zod schema to validate and type the result
276
+ * @returns The parsed and validated data
277
+ * @throws {ParseObjectError} When parsing or validation fails
278
+ *
279
+ * @example
280
+ * ```ts
281
+ * const schema = z.object({ title: z.string(), tags: z.array(z.string()) });
282
+ * const result = parseObject(aiResponse, schema);
283
+ * // result is typed as { title: string; tags: string[] }
284
+ * ```
285
+ */
286
+ declare function parseObject<T>(text: string, schema: z.ZodSchema<T>): T;
287
+ //#endregion
288
+ //#region src/parsing/parse-text.d.ts
289
+ interface ParseTextOptions {
290
+ /** Collapse multiple spaces into one (default: true) */
291
+ collapseSpaces?: boolean;
292
+ /** Convert em/en dashes with spaces to commas (default: true) */
293
+ normalizeEmDashesToCommas?: boolean;
294
+ }
295
+ /**
296
+ * Parses and sanitizes text by removing AI artifacts and normalizing typography.
297
+ *
298
+ * @param text - The text to parse
299
+ * @param options - Parsing options
300
+ * @returns The cleaned text
301
+ */
302
+ declare function parseText(text: string, options?: ParseTextOptions): string;
303
+ //#endregion
304
+ //#region src/provider/openrouter.provider.d.ts
305
+ interface ModelOptions {
306
+ /** Maximum tokens to generate */
307
+ maxTokens?: number;
308
+ /** Reasoning configuration for supported models */
309
+ reasoning?: {
310
+ effort?: "high" | "low" | "medium";
311
+ exclude?: boolean;
312
+ };
313
+ }
314
+ interface OpenRouterConfig {
315
+ apiKey: string;
316
+ metadata?: OpenRouterMetadata;
317
+ }
318
+ interface OpenRouterMetadata {
319
+ /** Application name for X-Title header */
320
+ application?: string;
321
+ /** Website URL for HTTP-Referer header */
322
+ website?: string;
323
+ }
324
+ interface OpenRouterProvider {
325
+ /** Get a language model instance */
326
+ model: (name: string, options?: ModelOptions) => LanguageModel;
327
+ }
328
+ /**
329
+ * Creates an OpenRouter provider for AI SDK models.
330
+ *
331
+ * @example
332
+ * ```ts
333
+ * const provider = createOpenRouterProvider({ apiKey: process.env.OPENROUTER_API_KEY });
334
+ * const model = provider.model('anthropic/claude-sonnet-4-20250514');
335
+ *
336
+ * const { text } = await generateText({ model, prompt: 'Hello!' });
337
+ * ```
338
+ */
339
+ declare function createOpenRouterProvider(config: OpenRouterConfig): OpenRouterProvider;
340
+ //#endregion
341
+ //#region src/provider/openrouter-metadata.adapter.d.ts
342
+ /**
343
+ * OpenRouter adapter for extracting usage and cost from provider metadata
344
+ */
345
+ declare class OpenRouterMetadataAdapter implements ProviderMetadataPort {
346
+ extract(providerMetadata: Record<string, unknown> | undefined): ExtractedProviderMetadata;
347
+ }
348
+ //#endregion
349
+ export { type CostDetails, type ExtractedProviderMetadata, type GenerateStructuredOptions, type GenerationError, type GenerationErrorCode, type GenerationParams, type GenerationResult, LangfuseAdapter, type LangfuseConfig, type LoggingMiddlewareOptions, type ModelOptions, NoopObservabilityAdapter, type ObservabilityMetadata, type ObservabilityMiddlewareOptions, type ObservabilityPort, type OpenRouterConfig, type OpenRouterMetadata, OpenRouterMetadataAdapter, type OpenRouterProvider, ParseObjectError, type ParseTextOptions, type ProviderMetadataPort, type TraceParams, type UsageDetails, classifyError, createLoggingMiddleware, createObservabilityMiddleware, createOpenRouterProvider, createSchemaPrompt, generateStructured, generationFailure, generationSuccess, isFailure, isSuccess, parseObject, parseText, unwrap, unwrapOr, withObservability };
350
+ //# sourceMappingURL=index.d.ts.map