diff --git a/env.example b/env.example index df9dcfc..81edc13 100644 --- a/env.example +++ b/env.example @@ -183,6 +183,7 @@ OPENAI_API_KEY="" REPLICATE_API_TOKEN="" GOOGLE_GENERATIVE_AI_API_KEY="" DEEPSEEK_API_KEY="" +OPENROUTER_API_KEY="" # ----------------------------------------------------------------------------- # Web Content Analyzer (Firecrawl) diff --git a/package.json b/package.json index 8bdc6a0..e65509a 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "@mendable/firecrawl-js": "^1.29.1", "@next/third-parties": "^15.3.0", "@openpanel/nextjs": "^1.0.7", + "@openrouter/ai-sdk-provider": "^1.0.0-beta.6", "@orama/orama": "^3.1.4", "@orama/tokenizers": "^3.1.4", "@radix-ui/react-accordion": "^1.2.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 217c320..5b817ad 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -59,6 +59,9 @@ importers: '@openpanel/nextjs': specifier: ^1.0.7 version: 1.0.7(next@15.2.1(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@openrouter/ai-sdk-provider': + specifier: ^1.0.0-beta.6 + version: 1.0.0-beta.6(ai@5.0.0(zod@4.0.14))(zod@4.0.14) '@orama/orama': specifier: ^3.1.4 version: 3.1.4 @@ -1730,6 +1733,13 @@ packages: '@openpanel/web@1.0.1': resolution: {integrity: sha512-cVZ7Kr9SicczJ/RDIfEtZs8+1iGDzwkabVA/j3NqSl8VSucsC8m1+LVbjmCDzCJNnK4yVn6tEcc9PJRi2rtllw==} + '@openrouter/ai-sdk-provider@1.0.0-beta.6': + resolution: {integrity: sha512-1cj8yUek4ib10MqZ+9fw1p1a3SR0Zhx3HH1J3+WQIXXuH/rUhiAkwovgPi1ADuC1QECPSPiwfhK2GecnDm8hGg==} + engines: {node: '>=18'} + peerDependencies: + ai: ^5.0.0-beta.12 + zod: ^3.24.1 || ^v4 + '@opentelemetry/api-logs@0.57.2': resolution: {integrity: sha512-uIX52NnTM0iBh84MShlpouI7UKqkZ7MrUszTmaypHBu4r7NofznSnQRfJ+uUeDtQDj6w8eFGg5KBLDAwAPz1+A==} engines: {node: '>=14'} @@ -7527,6 +7537,11 @@ snapshots: dependencies: '@openpanel/sdk': 1.0.0 + '@openrouter/ai-sdk-provider@1.0.0-beta.6(ai@5.0.0(zod@4.0.14))(zod@4.0.14)': + dependencies: + ai: 5.0.0(zod@4.0.14) + zod: 4.0.14 + '@opentelemetry/api-logs@0.57.2': dependencies: '@opentelemetry/api': 1.9.0 diff --git a/src/ai/text/components/url-input-form.tsx b/src/ai/text/components/url-input-form.tsx index 208b436..504d7de 100644 --- a/src/ai/text/components/url-input-form.tsx +++ b/src/ai/text/components/url-input-form.tsx @@ -164,6 +164,7 @@ export function UrlInputForm({ OpenAI GPT-4o Google Gemini DeepSeek + OpenRouter diff --git a/src/ai/text/utils/web-content-analyzer-config.ts b/src/ai/text/utils/web-content-analyzer-config.ts index 221e1b2..83b183c 100644 --- a/src/ai/text/utils/web-content-analyzer-config.ts +++ b/src/ai/text/utils/web-content-analyzer-config.ts @@ -117,6 +117,13 @@ export const webContentAnalyzerConfig = { temperature: 0.1, maxTokens: 2000, }, + openrouter: { + model: 'openrouter/horizon-beta', + // model: 'x-ai/grok-3-beta', + // model: 'openai/gpt-4o-mini', + temperature: 0.1, + maxTokens: 2000, + }, } as const; /** diff --git a/src/ai/text/utils/web-content-analyzer.ts b/src/ai/text/utils/web-content-analyzer.ts index 72d2b7d..9e9e07b 100644 --- a/src/ai/text/utils/web-content-analyzer.ts +++ b/src/ai/text/utils/web-content-analyzer.ts @@ -97,9 +97,8 @@ export interface LoadingStatesProps { // URL Validation Schema export const urlSchema = z - .string() + .url() .min(1, 'URL is required') - .url('Please enter a valid URL') .refine( (url) => url.startsWith('http://') || url.startsWith('https://'), 'URL must start with http:// or https://' @@ -114,13 +113,13 @@ export const analysisResultsSchema = z.object({ pricing: z.string().default('Not specified'), useCases: z.array(z.string()).default([]), url: urlSchema, - analyzedAt: z.string().datetime(), + analyzedAt: z.iso.datetime(), }); // API Request Schema export const analyzeContentRequestSchema = z.object({ url: urlSchema, - modelProvider: z.enum(['openai', 'gemini', 'deepseek']), + modelProvider: z.enum(['openai', 'gemini', 'deepseek', 'openrouter']), }); // API Response Schema diff --git a/src/app/api/analyze-content/route.ts b/src/app/api/analyze-content/route.ts index 094d365..91db24e 100644 --- a/src/app/api/analyze-content/route.ts +++ b/src/app/api/analyze-content/route.ts @@ -23,6 +23,7 @@ import { createDeepSeek } from '@ai-sdk/deepseek'; import { createGoogleGenerativeAI } from '@ai-sdk/google'; import { createOpenAI } from '@ai-sdk/openai'; import FirecrawlApp from '@mendable/firecrawl-js'; +import { createOpenRouter } from '@openrouter/ai-sdk-provider'; import { generateObject } from 'ai'; import { type NextRequest, NextResponse } from 'next/server'; import { z } from 'zod'; @@ -230,6 +231,13 @@ async function analyzeContent( temperature = webContentAnalyzerConfig.deepseek.temperature; maxTokens = webContentAnalyzerConfig.deepseek.maxTokens; break; + case 'openrouter': + model = createOpenRouter({ + apiKey: process.env.OPENROUTER_API_KEY, + }).chat(webContentAnalyzerConfig.openrouter.model); + temperature = webContentAnalyzerConfig.openrouter.temperature; + maxTokens = webContentAnalyzerConfig.openrouter.maxTokens; + break; default: throw new WebContentAnalyzerError( ErrorType.VALIDATION,