/** * ResponseNormalizer - Schema-driven response transformation engine * * Provides a generic, configurable approach to normalizing protocol-specific * responses into the universal ContentItem[] format used by the harness. * * ## Architecture * Uses declarative configuration (ResponseNormalizerConfig) to define: * - How to extract content from responses (contentPath) * - How to normalize individual items (itemNormalizer) * - How to detect errors (errorPath, errorExtractor) * - Whether to flatten nested arrays (flattenArrays) * * ## Key Features * - **Generic**: Works with any protocol response type (MCP, HTTP, OpenAI) * - **Declarative**: Configuration-driven, not code-driven * - **Flexible**: PathExtractor supports strings, arrays, functions * - **Extensible**: Custom itemNormalizer for protocol-specific logic * - **Consistent**: Unified error detection and content extraction * * ## Benefits * - Eliminates code duplication across adapters * - Makes transformation logic visible at a glance * - Easy to add new response formats without copying code * - Consistent handling of edge cases (empty arrays, null values) * - Type-safe with generic type parameter * * @example MCP Tool Response * ```typescript * const normalizer = new ResponseNormalizer({ * contentPath: "content" * }); * const result = normalizer.normalize({ content: [{ type: "text", text: "Hi" }] }); * // => { content: [...], output_text: "Hi", isError: false } * ``` * * @example HTTP JSON Response * ```typescript * const normalizer = new ResponseNormalizer({ * contentPath: "data", * errorExtractor: (data) => data.status >= 400, * itemNormalizer: (item) => ({ * type: "text", * text: JSON.stringify(item, null, 2), * mimeType: "application/json" * }) * }); * ``` * * @example OpenAI Choice Array * ```typescript * const normalizer = new ResponseNormalizer({ * contentPath: (data) => data.choices.map(c => c.message.content), * flattenArrays: true * }); * ``` * * @module @wavespec/kit/response/normalizer */ import type { ContentItem } from "@wavespec/types"; import type { ResponseNormalizerConfig, NormalizedResponse } from "./types.js"; /** * Default item normalizer for common protocol content types * * Converts protocol-specific content items to universal ContentItem format. * Handles all common content types found in MCP, HTTP, OpenAI protocols. * * ## Supported Content Types * - **text**: `{ type: "text", text: string }` → text ContentItem * - **image**: `{ type: "image", data: string, mimeType: string }` → image ContentItem * - **audio**: `{ type: "audio", data: string, mimeType: string }` → resource ContentItem (normalized) * - **resource**: `{ type: "resource", data: string, mimeType: string }` → resource ContentItem * - **primitives**: string, number, boolean → text ContentItem (stringified) * - **objects**: {...} → text ContentItem (JSON stringified) * * ## Edge Cases * - Null/undefined items are filtered out (return null) * - Objects without type field are treated as text (JSON stringified) * - Audio content is normalized to resource type (harness doesn't have audio type) * - Both `data` and `blob` fields are supported for resources * * @param item - Protocol content item or primitive value * @returns ContentItem or null if item should be filtered out */ export declare function defaultItemNormalizer(item: unknown): ContentItem | null; /** * ResponseNormalizer Class * * Schema-driven response transformation engine that converts protocol-specific * responses into the universal ContentItem[] format. Eliminates boilerplate by * using declarative configuration instead of custom normalization code. * * ## Type Parameter * @template TProtocolResponse - The protocol's response type (e.g., CallToolResult, HttpResponse) * * ## Configuration * Pass a ResponseNormalizerConfig defining: * - `contentPath`: How to extract content items from response * - `itemNormalizer`: (Optional) How to normalize each item * - `flattenArrays`: (Optional) Whether to flatten nested arrays * - `errorPath`/`errorExtractor`: (Optional) How to detect errors * * ## Usage Pattern * 1. Create normalizer with config * 2. Call normalize(response) for each response * 3. Get back NormalizedResponse with content, output_text, isError * * @example Simple MCP tool response * ```typescript * const normalizer = new ResponseNormalizer({ * contentPath: "content" * }); * const result = normalizer.normalize({ content: [{ type: "text", text: "Hi" }] }); * ``` * * @example HTTP with error detection * ```typescript * const normalizer = new ResponseNormalizer({ * contentPath: "data", * errorExtractor: (data) => data.status >= 400 * }); * ``` * * @example OpenAI with custom extraction * ```typescript * const normalizer = new ResponseNormalizer({ * contentPath: (data) => data.choices.map(c => c.message), * flattenArrays: true * }); * ``` */ export declare class ResponseNormalizer { private readonly config; private readonly itemNormalizer; /** * Create a new ResponseNormalizer * * @param config - Normalization schema configuration */ constructor(config: ResponseNormalizerConfig); /** * Normalize a protocol response to universal format * * Extracts content, normalizes items, computes output_text, detects errors. * * @param response - Protocol-specific response * @returns NormalizedResponse with content, output_text, isError */ normalize(response: TProtocolResponse): NormalizedResponse; /** * Extract and normalize content items from response * * 1. Extract raw content using contentPath * 2. Ensure it's an array * 3. Flatten nested arrays if configured * 4. Normalize each item using itemNormalizer * 5. Filter out null results * * @param response - Protocol-specific response * @returns Array of normalized ContentItems */ private extractContent; /** * Extract error indicator from response * * Uses errorExtractor if provided, otherwise errorPath. * Returns false if no error detection is configured. * * @param response - Protocol-specific response * @returns true if response represents an error */ private extractError; /** * Extract value from nested object using path * * Supports three path formats: * - String: "data.items" → obj.data.items * - Array: ["data", "items"] → obj["data"]["items"] * - Function: (obj) => obj.data.items → custom extraction * * Handles null/undefined gracefully by returning undefined. * * @param data - Object to extract from * @param path - Path specification * @returns Extracted value or undefined */ private extractByPath; /** * Recursively flatten nested arrays * * Converts [[item1], [item2]] to [item1, item2]. * Non-array items are preserved as-is. * * @param items - Array to flatten * @returns Flattened array */ private flattenArray; } //# sourceMappingURL=normalizer.d.ts.map