UNPKG

@tanstack/ai

Version:

Core TanStack AI library - Open source AI SDK

270 lines (240 loc) 7.07 kB
/** * Summarize Activity * * Generates summaries from text input. * This is a self-contained module with implementation, types, and JSDoc. */ import { aiEventClient } from '@tanstack/ai-event-client' import { streamGenerationResult } from '../stream-generation-result.js' import type { SummarizeAdapter } from './adapter' import type { StreamChunk, SummarizationOptions, SummarizationResult, } from '../../types' // =========================== // Activity Kind // =========================== /** The adapter kind this activity handles */ export const kind = 'summarize' as const // =========================== // Type Extraction Helpers // =========================== /** Extract provider options from a SummarizeAdapter via ~types */ export type SummarizeProviderOptions<TAdapter> = TAdapter extends SummarizeAdapter<any, any> ? TAdapter['~types']['providerOptions'] : object // =========================== // Activity Options Type // =========================== /** * Options for the summarize activity. * The model is extracted from the adapter's model property. * * @template TAdapter - The summarize adapter type * @template TStream - Whether to stream the output */ export interface SummarizeActivityOptions< TAdapter extends SummarizeAdapter<string, object>, TStream extends boolean = false, > { /** The summarize adapter to use (must be created with a model) */ adapter: TAdapter & { kind: typeof kind } /** The text to summarize */ text: string /** Maximum length of the summary (in words or characters, provider-dependent) */ maxLength?: number /** Style of summary to generate */ style?: 'bullet-points' | 'paragraph' | 'concise' /** Topics or aspects to focus on in the summary */ focus?: Array<string> /** Provider-specific options */ modelOptions?: SummarizeProviderOptions<TAdapter> /** * Whether to stream the summarization result. * When true, returns an AsyncIterable<StreamChunk> for streaming output. * When false or not provided, returns a Promise<SummarizationResult>. * * @default false */ stream?: TStream } // =========================== // Activity Result Type // =========================== /** * Result type for the summarize activity. * - If stream is true: AsyncIterable<StreamChunk> * - Otherwise: Promise<SummarizationResult> */ export type SummarizeActivityResult<TStream extends boolean> = TStream extends true ? AsyncIterable<StreamChunk> : Promise<SummarizationResult> // =========================== // Helper Functions // =========================== function createId(prefix: string): string { return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}` } // =========================== // Activity Implementation // =========================== /** * Summarize activity - generates summaries from text. * * Supports both streaming and non-streaming modes. * * @example Basic summarization * ```ts * import { summarize } from '@tanstack/ai' * import { openaiSummarize } from '@tanstack/ai-openai' * * const result = await summarize({ * adapter: openaiSummarize('gpt-4o-mini'), * text: 'Long article text here...' * }) * * console.log(result.summary) * ``` * * @example Summarization with style * ```ts * const result = await summarize({ * adapter: openaiSummarize('gpt-4o-mini'), * text: 'Long article text here...', * style: 'bullet-points', * maxLength: 100 * }) * ``` * * @example Focused summarization * ```ts * const result = await summarize({ * adapter: openaiSummarize('gpt-4o-mini'), * text: 'Long technical document...', * focus: ['key findings', 'methodology'] * }) * ``` * * @example Streaming summarization * ```ts * for await (const chunk of summarize({ * adapter: openaiSummarize('gpt-4o-mini'), * text: 'Long article text here...', * stream: true * })) { * if (chunk.type === 'content') { * process.stdout.write(chunk.delta) * } * } * ``` */ export function summarize< TAdapter extends SummarizeAdapter<string, object>, TStream extends boolean = false, >( options: SummarizeActivityOptions<TAdapter, TStream>, ): SummarizeActivityResult<TStream> { const { stream } = options if (stream) { return runStreamingSummarize( options as unknown as SummarizeActivityOptions< SummarizeAdapter<string, object>, true >, ) as SummarizeActivityResult<TStream> } return runSummarize( options as unknown as SummarizeActivityOptions< SummarizeAdapter<string, object>, false >, ) as SummarizeActivityResult<TStream> } /** * Run non-streaming summarization */ async function runSummarize( options: SummarizeActivityOptions<SummarizeAdapter<string, object>, false>, ): Promise<SummarizationResult> { const { adapter, text, maxLength, style, focus } = options const model = adapter.model const requestId = createId('summarize') const inputLength = text.length const startTime = Date.now() aiEventClient.emit('summarize:request:started', { requestId, provider: adapter.name, model, inputLength, timestamp: startTime, }) const summarizeOptions: SummarizationOptions = { model, text, maxLength, style, focus, } const result = await adapter.summarize(summarizeOptions) const duration = Date.now() - startTime const outputLength = result.summary.length aiEventClient.emit('summarize:request:completed', { requestId, provider: adapter.name, model, inputLength, outputLength, duration, timestamp: Date.now(), }) return result } /** * Run streaming summarization * Uses the adapter's native streaming if available, otherwise falls back * to non-streaming and yields the result as a single chunk. */ async function* runStreamingSummarize( options: SummarizeActivityOptions<SummarizeAdapter<string, object>, true>, ): AsyncIterable<StreamChunk> { const { adapter, text, maxLength, style, focus } = options const model = adapter.model const summarizeOptions: SummarizationOptions = { model, text, maxLength, style, focus, } // Use real streaming if the adapter supports it if (adapter.summarizeStream) { yield* adapter.summarizeStream(summarizeOptions) return } // Fall back to non-streaming — wrap result with streamGenerationResult yield* streamGenerationResult(() => adapter.summarize(summarizeOptions)) } // =========================== // Options Factory // =========================== /** * Create typed options for the summarize() function without executing. */ export function createSummarizeOptions< TAdapter extends SummarizeAdapter<string, object>, TStream extends boolean = false, >( options: SummarizeActivityOptions<TAdapter, TStream>, ): SummarizeActivityOptions<TAdapter, TStream> { return options } // Re-export adapter types export type { SummarizeAdapter, SummarizeAdapterConfig, AnySummarizeAdapter, } from './adapter' export { BaseSummarizeAdapter } from './adapter'