pulse-ai-utils
Version:
Utility functions and helpers for AI-powered applications
192 lines (191 loc) • 6.14 kB
TypeScript
import OpenAI from 'openai';
import { z, ZodTypeAny } from 'zod';
import QueryCache from './query-cache';
export type WaterfallStreamEvent = {
type: 'strategy_start';
strategy: string;
timestamp: number;
} | {
type: 'data_item';
item: any;
strategy: string;
source: string;
} | {
type: 'cache_hit';
itemCount: number;
strategy: string;
cacheType: string;
} | {
type: 'web_search_performed';
itemCount: number;
strategy: string;
} | {
type: 'fallback_start';
strategy: string;
} | {
type: 'strategy_complete';
strategy: string;
} | {
type: 'error';
error: string;
strategy: string;
isTimeout?: boolean;
} | {
type: 'parallel_start';
strategies: string[];
timestamp: number;
} | {
type: 'duplicate_skipped';
itemId: string;
strategy: string;
};
export interface LLMConfig {
model: string;
apiKey?: string;
baseURL?: string;
headers?: Record<string, string>;
}
export declare abstract class LLMBase {
protected openai: OpenAI;
protected defaultModel: string;
protected cache: QueryCache;
constructor(config: LLMConfig, openaiInstance?: OpenAI, cache?: QueryCache);
protected abstract createOpenAIInstance(config: LLMConfig): OpenAI;
protected getApiKey(): string;
protected abstract getProviderName(): string;
protected isTestMode(): boolean;
get model(): string;
get client(): OpenAI;
protected parseModelResponse<T extends ZodTypeAny>(response: any, zodSchema: T): z.infer<T>;
protected enhanceWithImages(items: any[], responseFormatName: string): Promise<void>;
/**
* Fetches structured data from web search using streaming
* Uses chat completions API with streaming for better handling of large responses
*/
fetchStructuredDataFromWebStream<T extends ZodTypeAny>({ model, prompt, recommendedSources, zodSchema, userLocation, locationGranularity, systemPrompt, timeline, responseFormatName, customFormat, options }: {
model?: string;
prompt: string;
recommendedSources?: string[];
zodSchema: T;
userLocation: any;
locationGranularity: string;
systemPrompt?: string;
timeline?: string;
responseFormatName?: string;
customFormat?: (schema: ZodTypeAny, name: string) => any;
options?: Record<string, any>;
}): Promise<z.infer<T>>;
/**
* Generator version that yields items as they are parsed from the stream
*/
fetchStructuredDataFromWebStreamGenerator<T extends ZodTypeAny>({ model, prompt, recommendedSources, zodSchema, userLocation, locationGranularity, systemPrompt, timeline, responseFormatName, customFormat, options }: {
model?: string;
prompt: string;
recommendedSources?: string[];
zodSchema: T;
userLocation: any;
locationGranularity: string;
systemPrompt?: string;
timeline?: string;
responseFormatName?: string;
customFormat?: (schema: ZodTypeAny, name: string) => any;
options?: Record<string, any>;
}): AsyncGenerator<any>;
/**
* Non-streaming version using responses.parse API
*/
/**
* Build user prompt with date and location
*/
private buildUserPrompt;
/**
* Check if error is a JSON parsing error
*/
private isJsonParsingError;
/**
* Parse response with fallback to salvaging partial JSON
*/
private parseResponseWithFallback;
/**
* Process and cache results only if we have actual data
*/
private processAndCacheResults;
/**
* Helper method to parse streamed content
*/
private parseStreamedContent;
/**
* Fetches structured data from web search
*
* @param options.resultLimit - Maximum number of results to request (default: 20)
* @param options.useStreaming - Use streaming implementation (default: true for better reliability)
*
* Note: The responses.parse API doesn't support max_tokens parameter.
* Token limits are controlled by the model's context window.
* We use result limiting in the prompt to prevent response truncation.
*/
fetchStructuredData<T extends ZodTypeAny>({ model, prompt, html, zodSchema, responseFormatName, }: {
model?: string;
prompt: string;
html: string;
zodSchema: T;
responseFormatName?: string;
}): Promise<z.infer<T>>;
/**
* Search content chunks for longer documents
*/
searchChunks(query: string, limit?: number, threshold?: number): Promise<any[]>;
/**
* Generate embedding for a given text
* Used for custom vector operations
*/
generateEmbedding(text: string): Promise<number[]>;
/**
* Execute all strategies in parallel and stream results with deduplication
*/
executeParallelStrategyStream({ prompt, area, region, country, timeline, systemPrompt, options, enableWebSearchLLM, lat, lng, radius }: {
prompt: string;
area: string;
region?: string;
country?: string;
timeline?: string;
systemPrompt?: string;
options?: Record<string, any>;
enableWebSearchLLM?: boolean;
lat?: number;
lng?: number;
radius?: number;
}): AsyncGenerator<WaterfallStreamEvent>;
/**
* Get appropriate stream for each strategy
*/
private getStrategyStream;
/**
* Create a safe wrapper around strategy streams that catches all errors
*/
private createSafeStrategyStream;
/**
* Stream results from Firestore query cache
*/
private streamQueryCache;
/**
* Stream results from Supabase RAG cache
*/
private streamRAGCache;
/**
* Stream results from Supabase vector search
*/
private streamVectorSearch;
/**
* Stream results from Supabase hybrid search
*/
private streamHybridSearch;
/**
* Stream results from web search
*/
private streamWebSearch;
/**
* Infer timeline from query text
*/
private inferTimelineFromQuery;
}