UNPKG

weavebot-core

Version:

Generic content processing framework for web scraping and AI extraction

1 lines 40.6 kB
{"version":3,"sources":["../src/processors/web-scraper.ts","../src/types/index.ts","../src/interfaces/web-scraper-plugin.ts","../src/utils/index.ts","../src/processors/ai-extractor.ts"],"sourcesContent":["/**\n * Generic web scraper with plugin support\n * Platform-specific logic is handled by plugins\n */\n\nimport { chromium, Page, Browser } from \"playwright\";\nimport * as cheerio from \"cheerio\";\nimport {\n ProcessorPlugin,\n ProcessingStep,\n ProcessingContext,\n WebScrapingParams,\n ScrapedContent,\n ScrapingError,\n} from \"../types\";\nimport {\n WebScraperPlugin,\n WebScrapingConfig,\n PluginContext,\n WebScraperPluginRegistry,\n} from \"../interfaces/web-scraper-plugin\";\nimport { defaultLogger } from \"../utils\";\n\nexport interface WebScraperConfig {\n /**\n * Default timeout for page operations\n */\n defaultTimeout?: number;\n\n /**\n * Whether to run in headless mode\n */\n headless?: boolean;\n\n /**\n * Default viewport size\n */\n defaultViewport?: {\n width: number;\n height: number;\n };\n\n /**\n * Plugins to register\n */\n plugins?: WebScraperPlugin[];\n}\n\nexport class WebScraperProcessor implements ProcessorPlugin {\n public readonly name = \"web-scraper\";\n public readonly description = \"Scrapes web content with configurable strategies\";\n\n private config: WebScraperConfig;\n private pluginRegistry: WebScraperPluginRegistry;\n private browser: Browser | null = null;\n\n constructor(config: WebScraperConfig = {}) {\n this.config = config;\n this.pluginRegistry = new WebScraperPluginRegistry();\n\n // Register plugins\n if (config.plugins) {\n config.plugins.forEach((plugin) => this.pluginRegistry.register(plugin));\n }\n }\n\n async execute(\n params: WebScrapingParams,\n context: ProcessingContext = {}\n ): Promise<ScrapedContent> {\n const { url, strategy, options } = params;\n const startTime = Date.now();\n\n try {\n defaultLogger.info(\"Starting web scraping\", {\n url,\n strategy: strategy || \"auto\",\n ...context,\n });\n\n // Find applicable plugin\n const plugin = this.pluginRegistry.findPlugin(url);\n const pluginConfig = plugin?.getConfig(url);\n\n // Merge configurations\n const finalConfig: WebScrapingConfig = {\n strategy: strategy || pluginConfig?.strategy || \"auto\",\n timeout: options?.timeout || pluginConfig?.timeout || this.config.defaultTimeout || 30000,\n viewport: this.config.defaultViewport || pluginConfig?.viewport,\n waitSelectors: options?.waitForSelector ? [options.waitForSelector] : pluginConfig?.waitSelectors,\n headers: options?.userAgent ? { 'User-Agent': options.userAgent } : pluginConfig?.headers,\n blockResources: pluginConfig?.blockResources,\n additionalWait: pluginConfig?.additionalWait,\n };\n\n // Scrape content\n const content = await this.scrapeWithConfig(url, finalConfig, plugin);\n\n const duration = Date.now() - startTime;\n\n defaultLogger.info(\"Web scraping completed\", {\n url,\n plugin: plugin?.name || \"none\",\n duration,\n ...context,\n });\n\n return content;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n\n defaultLogger.error(\"Web scraping failed\", {\n error: errorMessage,\n url,\n ...context,\n });\n\n throw new ScrapingError(`Failed to scrape ${url}: ${errorMessage}`, {\n ...context,\n });\n }\n }\n\n private async scrapeWithConfig(\n url: string,\n config: WebScrapingConfig,\n plugin: WebScraperPlugin | null\n ): Promise<ScrapedContent> {\n const page = await this.getPage();\n\n try {\n // Set viewport if specified\n if (config.viewport) {\n await page.setViewportSize(config.viewport);\n }\n\n // Set custom headers if specified\n if (config.headers) {\n await page.setExtraHTTPHeaders(config.headers);\n }\n\n // Block resources if specified\n if (config.blockResources) {\n await page.route(\"**/*\", (route) => {\n const resourceType = route.request().resourceType();\n if ([\"image\", \"stylesheet\", \"font\", \"media\"].includes(resourceType)) {\n route.abort();\n } else {\n route.continue();\n }\n });\n }\n\n // Navigate to the page\n await page.goto(url, {\n waitUntil: config.strategy === \"spa\" ? \"networkidle\" : \"domcontentloaded\",\n timeout: config.timeout,\n });\n\n // Create plugin context\n const context: PluginContext = {\n url,\n logger: defaultLogger,\n state: {},\n };\n\n // Run plugin's beforeExtract if available\n if (plugin?.beforeExtract) {\n await plugin.beforeExtract(page, context);\n }\n\n // Wait for selectors if specified\n if (config.waitSelectors && config.waitSelectors.length > 0) {\n await Promise.all(\n config.waitSelectors.map((selector) =>\n page.waitForSelector(selector, { timeout: 5000 }).catch(() => {})\n )\n );\n }\n\n // Additional wait if specified\n if (config.additionalWait) {\n await page.waitForTimeout(config.additionalWait);\n }\n\n // Extract content\n const content = await this.extractContent(page, url);\n\n // Extract plugin metadata if available\n if (plugin?.extractMetadata) {\n const metadata = await plugin.extractMetadata(page, context);\n content.metadata = { ...(content.metadata || {}), ...metadata };\n }\n\n // Post-process with plugin if available\n const finalContent = plugin?.postProcess\n ? plugin.postProcess(content, context)\n : content;\n\n return finalContent;\n } finally {\n await page.close();\n }\n }\n\n private async extractContent(page: Page, url: string): Promise<ScrapedContent> {\n const html = await page.content();\n const $ = cheerio.load(html);\n\n // Remove script and style elements\n $(\"script, style, noscript\").remove();\n\n // Extract text content\n const text = $(\"body\")\n .text()\n .replace(/\\s+/g, \" \")\n .trim();\n\n // Extract title\n const title = $(\"title\").text() || $(\"h1\").first().text() || \"\";\n\n // Extract basic metadata\n const metadata: Record<string, any> = {\n description: $('meta[name=\"description\"]').attr(\"content\") || \"\",\n keywords: $('meta[name=\"keywords\"]').attr(\"content\") || \"\",\n author: $('meta[name=\"author\"]').attr(\"content\") || \"\",\n ogTitle: $('meta[property=\"og:title\"]').attr(\"content\") || \"\",\n ogDescription: $('meta[property=\"og:description\"]').attr(\"content\") || \"\",\n ogImage: $('meta[property=\"og:image\"]').attr(\"content\") || \"\",\n };\n\n // Extract JSON-LD if present\n const jsonLdScripts = $('script[type=\"application/ld+json\"]');\n if (jsonLdScripts.length > 0) {\n metadata.jsonLd = [];\n jsonLdScripts.each((_, element) => {\n try {\n const jsonLd = JSON.parse($(element).html() || \"{}\");\n metadata.jsonLd.push(jsonLd);\n } catch (e) {\n // Invalid JSON-LD, skip\n }\n });\n }\n\n return {\n url,\n title,\n text,\n html,\n metadata,\n extractedAt: new Date(),\n };\n }\n\n private async getPage(): Promise<Page> {\n if (!this.browser) {\n this.browser = await chromium.launch({\n headless: this.config.headless !== false,\n });\n }\n\n return await this.browser.newPage();\n }\n\n async cleanup(): Promise<void> {\n if (this.browser) {\n await this.browser.close();\n this.browser = null;\n }\n }\n\n /**\n * Register a plugin for platform-specific handling\n */\n registerPlugin(plugin: WebScraperPlugin): void {\n this.pluginRegistry.register(plugin);\n }\n}\n\n/**\n * Factory function to create a web scraper\n */\nexport function createWebScraper(\n config?: WebScraperConfig\n): WebScraperProcessor {\n return new WebScraperProcessor(config);\n}","import { z } from 'zod';\n\n// Core library configuration\nexport interface ProcessorConfig {\n defaultSchema?: string;\n processors?: Record<string, ProcessorPlugin>;\n storage?: Record<string, StorageAdapter>;\n logger?: Logger;\n}\n\n// Web scraping strategies\nexport type ScrapingStrategy = 'static' | 'spa' | 'auto';\n\nexport interface ScrapingOptions {\n strategy?: ScrapingStrategy;\n timeout?: number;\n waitForSelector?: string;\n userAgent?: string;\n enableJavaScript?: boolean;\n retries?: number;\n}\n\n// Processing pipeline types\nexport interface ProcessingContext {\n url?: string;\n data?: unknown;\n metadata?: Record<string, unknown>;\n userId?: string;\n sessionId?: string;\n stepIndex?: number;\n stepName?: string;\n pipeline?: string;\n [key: string]: unknown;\n}\n\nexport interface ProcessingStep {\n name: string;\n execute(input: unknown, context: ProcessingContext): Promise<unknown>;\n}\n\nexport interface ProcessorPlugin {\n name: string;\n description: string;\n execute(params: unknown, context: ProcessingContext): Promise<unknown>;\n}\n\n// Storage adapter interface\nexport interface StorageRecord {\n id: string;\n fields: Record<string, unknown>;\n createdTime: string;\n updatedTime?: string;\n}\n\nexport interface StorageAdapter {\n name: string;\n initialize(config: Record<string, unknown>): Promise<void>;\n create(data: unknown, schema?: string): Promise<StorageRecord>;\n update(id: string, data: Partial<unknown>): Promise<StorageRecord>;\n fetch(filter?: StorageFilter): Promise<StorageRecord[]>;\n delete(id: string): Promise<void>;\n createTable?(schema: z.ZodType, tableName: string): Promise<void>;\n}\n\nexport interface StorageFilter {\n schema?: string;\n limit?: number;\n offset?: number;\n where?: Record<string, unknown>;\n orderBy?: { field: string; direction: 'asc' | 'desc' }[];\n}\n\n// Pipeline orchestration\nexport interface Pipeline {\n name: string;\n steps: ProcessingStep[];\n execute(input: unknown, context: ProcessingContext): Promise<unknown>;\n}\n\nexport interface PipelineBuilder {\n addStep(step: ProcessingStep): PipelineBuilder;\n build(): Pipeline;\n}\n\n// Tool parameter types\nexport interface WebScrapingParams {\n url: string;\n strategy?: ScrapingStrategy;\n options?: ScrapingOptions;\n}\n\nexport interface AIExtractionParams {\n content: ScrapedContent;\n schema: string | z.ZodType;\n url?: string;\n model?: string;\n}\n\nexport interface StorageParams {\n data: unknown;\n schema: string;\n adapter?: string;\n}\n\n// Content processing types\nexport interface ScrapedContent {\n url: string;\n title: string;\n text: string;\n html: string;\n metadata: Record<string, any>;\n extractedAt: Date;\n}\n\n// Error types\nexport class WeaveBotError extends Error {\n public readonly type: string;\n public readonly context: Record<string, unknown>;\n\n constructor(message: string, type: string, context: Record<string, unknown> = {}) {\n super(message);\n this.name = 'WeaveBotError';\n this.type = type;\n this.context = context;\n }\n}\n\nexport class ScrapingError extends WeaveBotError {\n constructor(message: string, context: Record<string, unknown> = {}) {\n super(message, 'SCRAPING_FAILED', context);\n this.name = 'ScrapingError';\n }\n}\n\nexport class ExtractionError extends WeaveBotError {\n constructor(message: string, context: Record<string, unknown> = {}) {\n super(message, 'EXTRACTION_FAILED', context);\n this.name = 'ExtractionError';\n }\n}\n\nexport class ValidationError extends WeaveBotError {\n constructor(message: string, context: Record<string, unknown> = {}) {\n super(message, 'VALIDATION_FAILED', context);\n this.name = 'ValidationError';\n }\n}\n\nexport class StorageError extends WeaveBotError {\n constructor(message: string, context: Record<string, unknown> = {}) {\n super(message, 'STORAGE_FAILED', context);\n this.name = 'StorageError';\n }\n}\n\nexport class PipelineError extends WeaveBotError {\n constructor(message: string, context: Record<string, unknown> = {}) {\n super(message, 'PIPELINE_FAILED', context);\n this.name = 'PipelineError';\n }\n}\n\n// Logging interface\nexport interface Logger {\n debug(message: string, context?: Record<string, unknown>): void;\n info(message: string, context?: Record<string, unknown>): void;\n warn(message: string, context?: Record<string, unknown>): void;\n error(message: string, context?: Record<string, unknown>): void;\n time(operation: string, context?: Record<string, unknown>): () => void;\n}\n\nexport interface LogContext {\n userId?: string;\n sessionId?: string;\n url?: string;\n operation?: string;\n duration?: number;\n [key: string]: unknown;\n}\n\n// Input/Output types for processors\nexport interface ProcessorInput {\n type: 'url' | 'text' | 'file';\n data: string;\n schema?: string;\n options?: Record<string, unknown>;\n}\n\nexport interface ProcessorResult {\n success: boolean;\n data?: unknown;\n error?: {\n type: string;\n message: string;\n context?: Record<string, unknown>;\n };\n metadata: {\n processingTime: number;\n strategy?: string;\n model?: string;\n [key: string]: unknown;\n };\n}\n\n// Command system types\nexport interface CommandContext {\n userId: string;\n sessionId?: string;\n platform: string;\n args: string[];\n metadata?: Record<string, unknown>;\n}\n\nexport interface CommandHandler {\n command: string;\n description: string;\n execute(context: CommandContext): Promise<CommandResult>;\n}\n\nexport interface CommandResult {\n success: boolean;\n message: string;\n data?: unknown;\n error?: string;\n}\n\n// Newsletter generation types\nexport interface NewsletterOptions {\n startDate?: Date;\n endDate?: Date;\n maxEvents?: number;\n maxUpdates?: number;\n includeUpcoming?: boolean;\n template?: string;\n}","/**\n * Plugin interface for web scraper\n * Allows platform-specific handling without hardcoding in the core library\n */\n\nimport { Page } from \"playwright\";\n\nexport interface WebScraperPlugin {\n /**\n * Unique name for the plugin\n */\n name: string;\n\n /**\n * Check if this plugin should handle the given URL\n */\n canHandle(url: string): boolean;\n\n /**\n * Get scraping configuration for this URL\n */\n getConfig(url: string): WebScrapingConfig;\n\n /**\n * Optional: Handle page-specific interactions before content extraction\n */\n beforeExtract?(page: Page, context: PluginContext): Promise<void>;\n\n /**\n * Optional: Extract additional metadata specific to this platform\n */\n extractMetadata?(page: Page, context: PluginContext): Promise<Record<string, any>>;\n\n /**\n * Optional: Post-process the extracted content\n */\n postProcess?(content: ScrapedContent, context: PluginContext): ScrapedContent;\n}\n\nexport interface WebScrapingConfig {\n /**\n * Scraping strategy to use\n */\n strategy: \"static\" | \"spa\" | \"auto\";\n\n /**\n * Selectors to wait for before extraction\n */\n waitSelectors?: string[];\n\n /**\n * Maximum time to wait in milliseconds\n */\n timeout?: number;\n\n /**\n * Custom headers to send\n */\n headers?: Record<string, string>;\n\n /**\n * Viewport configuration\n */\n viewport?: {\n width: number;\n height: number;\n };\n\n /**\n * Whether to block resources (images, stylesheets, etc)\n */\n blockResources?: boolean;\n\n /**\n * Additional wait time after page load\n */\n additionalWait?: number;\n}\n\nexport interface PluginContext {\n /**\n * Original URL being scraped\n */\n url: string;\n\n /**\n * Logger instance\n */\n logger: any;\n\n /**\n * Shared state between plugins\n */\n state?: Record<string, any>;\n}\n\nexport interface ScrapedContent {\n url: string;\n title: string;\n text: string;\n html: string;\n metadata: Record<string, any>;\n extractedAt: Date;\n}\n\n/**\n * Registry for web scraper plugins\n */\nexport class WebScraperPluginRegistry {\n private plugins: WebScraperPlugin[] = [];\n\n register(plugin: WebScraperPlugin): void {\n this.plugins.push(plugin);\n }\n\n findPlugin(url: string): WebScraperPlugin | null {\n return this.plugins.find((plugin) => plugin.canHandle(url)) || null;\n }\n\n getAll(): WebScraperPlugin[] {\n return [...this.plugins];\n }\n}","import { Logger as LoggerInterface, LogContext } from '../types';\n\nexport enum LogLevel {\n DEBUG = 0,\n INFO = 1,\n WARN = 2,\n ERROR = 3,\n}\n\nexport interface LogEntry {\n timestamp: string;\n level: LogLevel;\n message: string;\n context?: LogContext;\n error?: Error;\n}\n\nexport class Logger implements LoggerInterface {\n private logLevel: LogLevel;\n\n constructor(level: string = 'info') {\n this.logLevel = this.parseLogLevel(level);\n \n // Configure console output for different environments\n if (process.env.NODE_ENV === 'production') {\n // In production, use structured JSON logging\n this.setupProductionLogging();\n }\n }\n\n private parseLogLevel(level: string): LogLevel {\n switch (level.toLowerCase()) {\n case 'debug': return LogLevel.DEBUG;\n case 'info': return LogLevel.INFO;\n case 'warn': return LogLevel.WARN;\n case 'error': return LogLevel.ERROR;\n default: return LogLevel.INFO;\n }\n }\n\n private setupProductionLogging() {\n // Override console methods for structured logging in production\n const originalLog = console.log;\n const originalWarn = console.warn;\n const originalError = console.error;\n\n console.log = (...args) => {\n if (typeof args[0] === 'object' && args[0].level !== undefined) {\n originalLog(JSON.stringify(args[0]));\n } else {\n originalLog(...args);\n }\n };\n\n console.warn = (...args) => {\n if (typeof args[0] === 'object' && args[0].level !== undefined) {\n originalWarn(JSON.stringify(args[0]));\n } else {\n originalWarn(...args);\n }\n };\n\n console.error = (...args) => {\n if (typeof args[0] === 'object' && args[0].level !== undefined) {\n originalError(JSON.stringify(args[0]));\n } else {\n originalError(...args);\n }\n };\n }\n\n private shouldLog(level: LogLevel): boolean {\n return level >= this.logLevel;\n }\n\n private createLogEntry(level: LogLevel, message: string, context?: LogContext, error?: Error): LogEntry {\n return {\n timestamp: new Date().toISOString(),\n level,\n message,\n context: context ? this.sanitizeContext(context) : undefined,\n error: error ? {\n name: error.name,\n message: error.message,\n stack: error.stack\n } as any : undefined\n };\n }\n\n private sanitizeContext(context: LogContext): LogContext {\n const sanitized = { ...context };\n \n // Remove sensitive information\n const sensitiveKeys = ['password', 'token', 'key', 'secret', 'apikey'];\n \n Object.keys(sanitized).forEach(key => {\n if (sensitiveKeys.some(sensitive => key.toLowerCase().includes(sensitive))) {\n sanitized[key] = '[REDACTED]';\n }\n });\n\n return sanitized;\n }\n\n private formatLogEntry(entry: LogEntry): string {\n const levelNames = {\n [LogLevel.DEBUG]: 'DEBUG',\n [LogLevel.INFO]: 'INFO',\n [LogLevel.WARN]: 'WARN',\n [LogLevel.ERROR]: 'ERROR'\n };\n\n let formatted = `[${entry.timestamp}] ${levelNames[entry.level]}: ${entry.message}`;\n \n if (entry.context && Object.keys(entry.context).length > 0) {\n formatted += ` | Context: ${JSON.stringify(entry.context)}`;\n }\n \n if (entry.error) {\n formatted += ` | Error: ${entry.error.message}`;\n if (entry.error.stack && this.logLevel === LogLevel.DEBUG) {\n formatted += `\\nStack: ${entry.error.stack}`;\n }\n }\n\n return formatted;\n }\n\n private output(entry: LogEntry) {\n if (process.env.NODE_ENV === 'production') {\n // Structured logging for production\n const structuredLog = {\n timestamp: entry.timestamp,\n level: entry.level,\n message: entry.message,\n ...entry.context,\n error: entry.error\n };\n\n switch (entry.level) {\n case LogLevel.ERROR:\n console.error(structuredLog);\n break;\n case LogLevel.WARN:\n console.warn(structuredLog);\n break;\n default:\n console.log(structuredLog);\n }\n } else {\n // Human-readable logging for development\n const formatted = this.formatLogEntry(entry);\n \n switch (entry.level) {\n case LogLevel.ERROR:\n console.error(`\\x1b[31m${formatted}\\x1b[0m`); // Red\n break;\n case LogLevel.WARN:\n console.warn(`\\x1b[33m${formatted}\\x1b[0m`); // Yellow\n break;\n case LogLevel.INFO:\n console.log(`\\x1b[36m${formatted}\\x1b[0m`); // Cyan\n break;\n case LogLevel.DEBUG:\n console.log(`\\x1b[90m${formatted}\\x1b[0m`); // Gray\n break;\n }\n }\n }\n\n debug(message: string, context?: LogContext) {\n if (this.shouldLog(LogLevel.DEBUG)) {\n const entry = this.createLogEntry(LogLevel.DEBUG, message, context);\n this.output(entry);\n }\n }\n\n info(message: string, context?: LogContext) {\n if (this.shouldLog(LogLevel.INFO)) {\n const entry = this.createLogEntry(LogLevel.INFO, message, context);\n this.output(entry);\n }\n }\n\n warn(message: string, context?: LogContext) {\n if (this.shouldLog(LogLevel.WARN)) {\n const entry = this.createLogEntry(LogLevel.WARN, message, context);\n this.output(entry);\n }\n }\n\n error(message: string, contextOrError?: LogContext | Error, error?: Error) {\n if (this.shouldLog(LogLevel.ERROR)) {\n let context: LogContext | undefined;\n let actualError: Error | undefined;\n\n if (contextOrError instanceof Error) {\n actualError = contextOrError;\n } else {\n context = contextOrError;\n actualError = error;\n }\n\n const entry = this.createLogEntry(LogLevel.ERROR, message, context, actualError);\n this.output(entry);\n }\n }\n\n // Performance logging utility\n time(label: string, context?: LogContext): () => void {\n const startTime = Date.now();\n \n this.debug(`Timer started: ${label}`, context);\n \n return () => {\n const duration = Date.now() - startTime;\n this.info(`Timer ended: ${label}`, { ...context, duration });\n };\n }\n}\n\n// Create default logger instance\nexport const createLogger = (level?: string): Logger => {\n return new Logger(level || process.env.LOG_LEVEL || 'info');\n};\n\n// Default logger instance\nexport const defaultLogger = createLogger();\n\n// Utility functions\nexport const isValidUrl = (string: string): boolean => {\n try {\n new URL(string);\n return true;\n } catch (_) {\n return false;\n }\n};\n\nexport const sanitizeInput = (input: string): string => {\n // Basic sanitization to prevent injection attacks\n return input\n .replace(/[<>]/g, '') // Remove potential HTML tags\n .replace(/javascript:/gi, '') // Remove javascript: protocols\n .trim();\n};\n\nexport const sleep = (ms: number): Promise<void> => {\n return new Promise(resolve => setTimeout(resolve, ms));\n};\n\nexport const retry = async <T>(\n fn: () => Promise<T>,\n retries: number = 3,\n delay: number = 1000\n): Promise<T> => {\n try {\n return await fn();\n } catch (error) {\n if (retries > 0) {\n await sleep(delay);\n return retry(fn, retries - 1, delay * 2); // Exponential backoff\n }\n throw error;\n }\n};\n\nexport const formatDuration = (ms: number): string => {\n if (ms < 1000) return `${ms}ms`;\n if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;\n return `${(ms / 60000).toFixed(1)}m`;\n};\n\nexport const truncateText = (text: string, maxLength: number = 100): string => {\n if (text.length <= maxLength) return text;\n return text.substring(0, maxLength - 3) + '...';\n};","/**\n * Generic, schema-driven AI extraction processor\n * This processor is completely agnostic to the type of content being extracted\n */\n\nimport { generateObject } from \"ai\";\nimport { openai } from \"@ai-sdk/openai\";\nimport { google } from \"@ai-sdk/google\";\nimport { z } from \"zod\";\nimport {\n ProcessorPlugin,\n ProcessingStep,\n ProcessingContext,\n AIExtractionParams,\n ScrapedContent,\n ExtractionError,\n} from \"../types\";\nimport { defaultLogger } from \"../utils\";\n\nexport interface AIExtractionConfig {\n provider?: \"openai\" | \"google\";\n apiKey?: string;\n openaiApiKey?: string; // Deprecated, kept for backward compatibility\n model?: string;\n temperature?: number;\n}\n\nexport interface GenericExtractionConfig {\n schema: z.ZodType<any>;\n systemPrompt: string;\n userPromptTemplate: string;\n examples?: Array<{\n input: string;\n output: any;\n }>;\n postProcess?: (data: any) => any;\n temperature?: number;\n}\n\nexport class AIExtractionProcessor implements ProcessorPlugin {\n public readonly name = \"ai-extractor\";\n public readonly description = \"Extracts structured data from content using AI models\";\n\n private model: any;\n private config: AIExtractionConfig;\n private extractionConfigs = new Map<string, GenericExtractionConfig>();\n\n constructor(config: AIExtractionConfig) {\n this.config = config;\n this.model = this.createModel(config);\n }\n\n private createModel(config: AIExtractionConfig): any {\n const provider = config.provider || \"openai\";\n const apiKey = config.apiKey || \"\";\n\n // Set environment variable for Google if needed\n if (provider === \"google\" && apiKey) {\n process.env.GOOGLE_GENERATIVE_AI_API_KEY = apiKey;\n }\n\n switch (provider) {\n case \"openai\":\n return openai(config.model || \"gpt-4o\");\n case \"google\":\n return google(config.model || \"gemini-2.5-flash\");\n default:\n throw new Error(`Unsupported AI provider: ${provider}`);\n }\n }\n\n /**\n * Register a custom extraction configuration for a schema type\n */\n registerExtractor(schemaName: string, config: GenericExtractionConfig): void {\n this.extractionConfigs.set(schemaName, config);\n }\n\n async execute(\n params: AIExtractionParams,\n context: ProcessingContext = {}\n ): Promise<unknown> {\n const { content, schema, url, model } = params;\n\n defaultLogger.info(\"Starting AI extraction\", {\n url: url || content.url,\n schema: typeof schema === \"string\" ? schema : \"custom\",\n ...context,\n });\n\n try {\n // If schema is a string, look up registered configuration\n if (typeof schema === \"string\") {\n const config = this.extractionConfigs.get(schema);\n if (!config) {\n throw new ExtractionError(\n `No extraction configuration registered for schema '${schema}'`,\n context\n );\n }\n return await this.extractWithConfig(content, config, url || content.url);\n }\n\n // Otherwise, use the provided schema directly (backward compatibility)\n // This is a minimal extraction without custom prompts\n const result = await generateObject({\n model: model ? this.createModel({ ...this.config, model }) : this.model,\n schema: schema as z.ZodType,\n prompt: `Extract structured data from this content:\\n\\n${content.text}`,\n temperature: this.config.temperature || 0.1,\n });\n\n return result.object;\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n defaultLogger.error(\"AI extraction failed\", {\n error: errorMessage,\n url: url || content.url,\n ...context,\n });\n throw new ExtractionError(`Failed to extract data: ${errorMessage}`, {\n ...context,\n });\n }\n }\n\n private async extractWithConfig(\n content: ScrapedContent,\n config: GenericExtractionConfig,\n url: string\n ): Promise<unknown> {\n // Build the user prompt with template interpolation\n const userPrompt = this.interpolatePrompt(config.userPromptTemplate, {\n content: content.text,\n url: url,\n title: content.title || \"\",\n html: content.html || \"\",\n examples: config.examples\n ? config.examples\n .map((ex) => `Input: ${ex.input}\\nOutput: ${JSON.stringify(ex.output)}`)\n .join(\"\\n\\n\")\n : \"\",\n });\n\n // Generate object using AI\n const result = await generateObject({\n model: this.model,\n schema: config.schema,\n prompt: userPrompt,\n system: config.systemPrompt,\n temperature: config.temperature ?? this.config.temperature ?? 0.1,\n });\n\n // Apply post-processing if provided\n return config.postProcess\n ? config.postProcess(result.object)\n : result.object;\n }\n\n private interpolatePrompt(template: string, values: Record<string, string>): string {\n return template.replace(/\\{\\{(\\w+)\\}\\}/g, (match, key) => {\n return values[key] || match;\n });\n }\n}\n\n/**\n * Factory function to create an AI extraction processor\n */\nexport function createAIExtractor(\n config: AIExtractionConfig\n): AIExtractionProcessor {\n return new AIExtractionProcessor(config);\n}"],"mappings":"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","names":[]}