986 lines
30 KiB
TypeScript
986 lines
30 KiB
TypeScript
import type { ChatMessage, APIResponse, AIAssistMessage } from "@/types";
|
|
|
|
export interface OpenRouterConfig {
|
|
apiKey?: string;
|
|
siteUrl?: string;
|
|
siteName?: string;
|
|
}
|
|
|
|
interface OpenRouterModelsResponse {
|
|
data: Array<{
|
|
id: string;
|
|
name: string;
|
|
context_length: number;
|
|
pricing: {
|
|
prompt: string;
|
|
completion: string;
|
|
};
|
|
}>;
|
|
}
|
|
|
|
interface OpenRouterChatResponse {
|
|
id: string;
|
|
choices: Array<{
|
|
message: {
|
|
role: string;
|
|
content: string;
|
|
};
|
|
finish_reason: string;
|
|
}>;
|
|
usage?: {
|
|
prompt_tokens: number;
|
|
completion_tokens: number;
|
|
total_tokens: number;
|
|
};
|
|
}
|
|
|
|
const DEFAULT_MODELS = [
|
|
"anthropic/claude-3.5-sonnet",
|
|
"google/gemini-2.0-flash-exp:free",
|
|
"meta-llama/llama-3.3-70b-instruct",
|
|
"openai/gpt-4o-mini",
|
|
"deepseek/deepseek-chat-v3-0324",
|
|
"qwen/qwen-2.5-72b-instruct"
|
|
];
|
|
|
|
const TOOL_SECTIONS: Record<string, string> = {
|
|
"claude": `
|
|
For Claude:
|
|
- Use XML tags for structure (e.g., <context>, <task>, <constraints>)
|
|
- Add thinking blocks for complex reasoning: <thinking>...</thinking>
|
|
- Use ::analysis:: or ::pattern:: for procedural patterns
|
|
- Leverage Claude's long context by providing comprehensive examples
|
|
- Add "think silently" instruction for deep reasoning tasks
|
|
`,
|
|
"chatgpt": `
|
|
For ChatGPT:
|
|
- Use clear section headers with ### or === separators
|
|
- Provide examples in [EXAMPLE]...[/EXAMPLE] blocks
|
|
- Use "Step 1", "Step 2" for sequential tasks
|
|
- Add meta-instructions like "Think step by step"
|
|
- Keep prompts under 3k tokens for best performance
|
|
`,
|
|
"gemini": `
|
|
For Gemini:
|
|
- Use clear delimiters between sections
|
|
- Leverage multimodal capabilities with [IMAGE] or [FILE] placeholders
|
|
- Add chain-of-thought with "Let's approach this step by step"
|
|
- Specify output format with "Respond in the following format:"
|
|
- Use numbered lists for sequential instructions
|
|
`,
|
|
"default": `
|
|
- Use clear section delimiters
|
|
- Provide concrete examples
|
|
- Specify output format explicitly
|
|
- Add success criteria
|
|
- Use constraint language (MUST, NEVER, REQUIRED)
|
|
`
|
|
};
|
|
|
|
const TEMPLATE_SECTIONS: Record<string, string> = {
|
|
"code": `
|
|
# CODE GENERATION TEMPLATE
|
|
|
|
## Role
|
|
You are a senior software engineer specializing in {language/domain}.
|
|
|
|
## Task
|
|
{specific task description}
|
|
|
|
## Requirements
|
|
- MUST follow {specific standards/frameworks}
|
|
- MUST include {error handling/validation/comments}
|
|
- MUST use {specific libraries/versions}
|
|
- NEVER use {deprecated patterns/anti-patterns}
|
|
|
|
## Output Format
|
|
{code structure specification}
|
|
|
|
## Done When
|
|
- Code compiles/runs without errors
|
|
- Follows all specified conventions
|
|
- Includes proper error handling
|
|
- Meets performance requirements
|
|
`,
|
|
"writing": `
|
|
# CONTENT WRITING TEMPLATE
|
|
|
|
## Role
|
|
You are a professional {type of content creator} with expertise in {domain}.
|
|
|
|
## Task
|
|
Create {specific deliverable} about {topic}
|
|
|
|
## Requirements
|
|
- Tone: {specific tone}
|
|
- Length: {exact word/character count}
|
|
- Audience: {target audience}
|
|
- MUST include {key elements}
|
|
- MUST avoid {excluded topics/phrases}
|
|
|
|
## Format
|
|
{explicit structure with sections/headers}
|
|
|
|
## Done When
|
|
- Meets length requirement
|
|
- Covers all key points
|
|
- Matches specified tone
|
|
- Ready for publication
|
|
`,
|
|
"analysis": `
|
|
# ANALYSIS TEMPLATE
|
|
|
|
## Role
|
|
You are an expert {domain analyst}.
|
|
|
|
## Task
|
|
{analysis objective}
|
|
|
|
## Analysis Framework
|
|
1. {Step 1 with specific method}
|
|
2. {Step 2 with specific method}
|
|
3. {Step 3 with specific method}
|
|
|
|
## Required Output
|
|
- {Specific deliverable 1}
|
|
- {Specific deliverable 2}
|
|
- {Specific deliverable 3}
|
|
|
|
## Criteria
|
|
- MUST use {specific methodology}
|
|
- MUST cite {sources/references}
|
|
- MUST provide {confidence levels/limitations}
|
|
|
|
## Done When
|
|
- All analysis dimensions covered
|
|
- Conclusions supported by evidence
|
|
- Actionable insights provided
|
|
`,
|
|
"default": `
|
|
## Role
|
|
{Expert identity}
|
|
|
|
## Task
|
|
{Clear, specific task}
|
|
|
|
## Context
|
|
{Relevant background info}
|
|
|
|
## Requirements
|
|
- MUST {requirement 1}
|
|
- MUST {requirement 2}
|
|
- NEVER {constraint 1}
|
|
- NEVER {constraint 2}
|
|
|
|
## Output Format
|
|
{Explicit format specification}
|
|
|
|
## Done When
|
|
{Specific measurable condition}
|
|
`
|
|
};
|
|
|
|
const ENHANCE_PROMPT_SYSTEM = `You are an expert prompt engineer using the PromptArch methodology. Enhance the user's prompt to be production-ready.
|
|
|
|
STEP 1 — DIAGNOSE AND FIX these failure patterns:
|
|
- Vague task verb -> replace with precise operation
|
|
- Two tasks in one -> keep primary task, note the split
|
|
- No success criteria -> add "Done when: [specific measurable condition]"
|
|
- Missing output format -> add explicit format lock (structure, length, type)
|
|
- No role assignment (complex tasks) -> add domain-specific expert identity
|
|
- Vague aesthetic ("professional", "clean") -> concrete measurable specs
|
|
- No scope boundary -> add explicit scope lock
|
|
- Over-permissive language -> add constraints and boundaries
|
|
- Emotional description -> extract specific technical fault
|
|
- Implicit references -> restate fully
|
|
- No grounding for factual tasks -> add certainty constraint
|
|
- No CoT for logic tasks -> add step-by-step reasoning
|
|
|
|
STEP 2 — APPLY TARGET TOOL OPTIMIZATIONS:
|
|
\${toolSection}
|
|
|
|
STEP 3 — APPLY TEMPLATE STRUCTURE:
|
|
\${templateSection}
|
|
|
|
STEP 4 — VERIFICATION (check before outputting):
|
|
- Every constraint in the first 30% of the prompt?
|
|
- MUST/NEVER over should/avoid?
|
|
- Every sentence load-bearing with zero padding?
|
|
- Format explicit with stated length?
|
|
- Scope bounded?
|
|
- Would this produce correct output on first try?
|
|
|
|
STEP 5 — OUTPUT:
|
|
Output ONLY the enhanced prompt. No explanations, no commentary, no markdown code fences.
|
|
The prompt must be ready to paste directly into the target AI tool.`;
|
|
|
|
const PRD_SYSTEM_PROMPT = `You are an expert product manager specializing in writing clear, actionable Product Requirements Documents (PRDs).
|
|
|
|
Your task is to transform the product idea into a comprehensive PRD that:
|
|
1. Defines clear problem statements and user needs
|
|
2. Specifies functional requirements with acceptance criteria
|
|
3. Outlines technical considerations and constraints
|
|
4. Identifies success metrics and KPIs
|
|
5. Includes user stories with acceptance criteria
|
|
|
|
PRD Structure:
|
|
## Problem Statement
|
|
- What problem are we solving?
|
|
- For whom are we solving it?
|
|
- Why is this important now?
|
|
|
|
## Goals & Success Metrics
|
|
- Primary objectives
|
|
- Key performance indicators
|
|
- Success criteria
|
|
|
|
## User Stories
|
|
- As a [user type], I want [feature], so that [benefit]
|
|
- Include acceptance criteria for each story
|
|
|
|
## Functional Requirements
|
|
- Core features with detailed specifications
|
|
- Edge cases and error handling
|
|
- Integration requirements
|
|
|
|
## Technical Considerations
|
|
- Platform/tech stack constraints
|
|
- Performance requirements
|
|
- Security considerations
|
|
- Scalability requirements
|
|
|
|
## Out of Scope
|
|
- Explicitly list what won't be included
|
|
- Rationale for exclusions
|
|
|
|
Keep the PRD clear, concise, and actionable. Use specific, measurable language.`;
|
|
|
|
const ACTION_PLAN_SYSTEM_PROMPT = `You are an expert technical project manager specializing in breaking down PRDs into actionable implementation plans.
|
|
|
|
Your task is to transform the PRD into a detailed action plan that:
|
|
|
|
1. Identifies all major components/modules needed
|
|
2. Breaks down work into clear, sequential phases
|
|
3. Specifies dependencies between tasks
|
|
4. Estimates effort and complexity
|
|
5. Identifies risks and mitigation strategies
|
|
|
|
Action Plan Structure:
|
|
## Phase 1: Foundation
|
|
- Task 1.1: [Specific task] - [Estimated effort]
|
|
- Task 1.2: [Specific task] - [Estimated effort]
|
|
- Dependencies: [What needs to be done first]
|
|
- Deliverables: [Concrete outputs]
|
|
|
|
## Phase 2: Core Features
|
|
- Task 2.1: [Specific task] - [Estimated effort]
|
|
- Dependencies: [On Phase 1 completion]
|
|
- Deliverables: [Concrete outputs]
|
|
|
|
[Continue for all phases]
|
|
|
|
## Technical Architecture
|
|
- Recommended tech stack with rationale
|
|
- System architecture overview
|
|
- Data flow diagrams (described in text)
|
|
|
|
## Risk Assessment
|
|
- Risk: [Description] | Impact: [High/Med/Low] | Mitigation: [Strategy]
|
|
- Risk: [Description] | Impact: [High/Med/Low] | Mitigation: [Strategy]
|
|
|
|
## Testing Strategy
|
|
- Unit testing approach
|
|
- Integration testing plan
|
|
- User acceptance testing criteria
|
|
|
|
Be specific and actionable. Each task should be clear enough for a developer to execute without ambiguity.`;
|
|
|
|
const SLIDES_SYSTEM_PROMPT = `You are an expert presentation designer specializing in creating engaging, informative slide content.
|
|
|
|
Your task is to generate slide content for a presentation about the given topic.
|
|
|
|
For each slide, provide:
|
|
1. Slide Title (compelling and clear)
|
|
2. Bullet points (3-5 per slide, concise and impactful)
|
|
3. Speaker notes (detailed explanation for the presenter)
|
|
4. Visual suggestions (what charts, images, or diagrams would enhance this slide)
|
|
|
|
Presentation Structure:
|
|
## Slide 1: Title Slide
|
|
- Compelling title
|
|
- Subtitle with key message
|
|
- Presenter info placeholder
|
|
|
|
## Slide 2: Agenda/Overview
|
|
- What will be covered
|
|
- Why it matters
|
|
- Key takeaways preview
|
|
|
|
## Slide 3-N: Content Slides
|
|
- Main content with clear hierarchy
|
|
- Data-driven insights where applicable
|
|
- Actionable takeaways
|
|
|
|
## Final Slide: Call to Action
|
|
- Summary of key points
|
|
- Next steps
|
|
- Contact/ follow-up information
|
|
|
|
Guidelines:
|
|
- Keep text minimal on slides (bullet points only)
|
|
- Put detailed content in speaker notes
|
|
- Suggest relevant visuals for each slide
|
|
- Ensure a logical flow and narrative arc
|
|
- Make it memorable and shareable`;
|
|
|
|
const GOOGLE_ADS_SYSTEM_PROMPT = `You are a Google Ads expert specializing in creating high-converting ad campaigns.
|
|
|
|
Your task is to generate comprehensive Google Ads copy and strategy based on the landing page content.
|
|
|
|
Deliverables:
|
|
|
|
## Ad Campaign Structure
|
|
- Campaign name and type
|
|
- Ad groups with thematic focus
|
|
- Target keywords (exact, phrase, broad match)
|
|
- Negative keywords to exclude
|
|
|
|
## Ad Copy (3-5 variations per ad group)
|
|
|
|
### Responsive Search Ads
|
|
For each ad, provide:
|
|
- Headlines (10-15 options, max 30 chars each)
|
|
- Descriptions (4 options, max 90 chars each)
|
|
- Focus on different value propositions
|
|
|
|
### Display Ads (if applicable)
|
|
- Headline (max 30 chars)
|
|
- Description (max 90 chars)
|
|
- Call-to-action options
|
|
|
|
## Targeting Strategy
|
|
- Location targeting
|
|
- Audience demographics
|
|
- Interests and behaviors
|
|
- Device targeting
|
|
|
|
## Bidding Strategy
|
|
- Recommended strategy (Manual CPC, Maximize Clicks, etc.)
|
|
- Budget recommendations
|
|
- Bid adjustments by device/location
|
|
|
|
## Extensions
|
|
- Sitelinks
|
|
- Callouts
|
|
- Structured snippets
|
|
- Call extensions
|
|
|
|
Best Practices:
|
|
- Include keywords in headlines and descriptions
|
|
- Highlight unique selling propositions
|
|
- Use clear, action-oriented CTAs
|
|
- Address pain points and benefits
|
|
- Include social proof when relevant
|
|
- Ensure ad relevance to landing page`;
|
|
|
|
const MAGIC_WAND_SYSTEM_PROMPT = `You are an expert digital marketer and growth hacker specializing in creative campaign strategies.
|
|
|
|
Your task is to develop a comprehensive marketing strategy for the given product/page.
|
|
|
|
## Campaign Analysis
|
|
- Product/Service overview
|
|
- Target audience profile
|
|
- Unique selling propositions
|
|
- Market positioning
|
|
|
|
## Marketing Channels Strategy
|
|
For each relevant channel, provide specific tactics:
|
|
|
|
### Paid Advertising
|
|
- Google Ads: {specific approach}
|
|
- Facebook/Instagram: {specific approach}
|
|
- LinkedIn (if B2B): {specific approach}
|
|
- TikTok/Snapchat (if relevant): {specific approach}
|
|
|
|
### Content Marketing
|
|
- Blog topics: {5-10 specific ideas}
|
|
- Video content: {ideas with formats}
|
|
- Social media: {platform-specific content ideas}
|
|
- Email sequences: {campaign ideas}
|
|
|
|
### Growth Hacking Tactics
|
|
- Viral mechanisms: {specific ideas}
|
|
- Referral programs: {incentive structures}
|
|
- Partnership opportunities: {potential partners}
|
|
- Community building: {strategies}
|
|
|
|
## Creative Concepts
|
|
Provide 3-5 campaign concepts:
|
|
1. Concept Name - {Hook, Message, CTA}
|
|
2. Concept Name - {Hook, Message, CTA}
|
|
...
|
|
|
|
## Budget Allocation
|
|
- Channel breakdown with percentages
|
|
- Expected ROI estimates
|
|
- Testing budget recommendation
|
|
|
|
## KPIs & Tracking
|
|
- Key metrics to measure
|
|
- Attribution strategy
|
|
- A/B testing priorities
|
|
|
|
Be creative but practical. Focus on tactics that can be executed within the given budget.`;
|
|
|
|
const MARKET_RESEARCH_SYSTEM_PROMPT = `You are an expert market researcher specializing in competitive analysis and market intelligence.
|
|
|
|
Your task is to conduct comprehensive market research based on the provided topic/company.
|
|
|
|
## Research Framework
|
|
|
|
### Market Overview
|
|
- Market size and growth trajectory
|
|
- Key market segments and their characteristics
|
|
- Current market trends and dynamics
|
|
- Future market projections
|
|
|
|
### Competitive Landscape
|
|
Identify and analyze:
|
|
- Major competitors (market share, positioning)
|
|
- Direct competitors head-to-head comparison
|
|
- Indirect competitors and substitutes
|
|
- Competitive strengths and weaknesses
|
|
|
|
### SWOT Analysis
|
|
- Strengths: Internal advantages
|
|
- Weaknesses: Internal limitations
|
|
- Opportunities: External possibilities
|
|
- Threats: External challenges
|
|
|
|
### Customer Analysis
|
|
- Target demographics and psychographics
|
|
- Pain points and unmet needs
|
|
- Purchase behavior and decision factors
|
|
- Customer feedback trends
|
|
|
|
### Product/Service Comparison
|
|
- Feature comparison matrix
|
|
- Pricing analysis
|
|
- Differentiation strategies
|
|
- Innovation opportunities
|
|
|
|
### Market Trends
|
|
- Emerging technologies impacting the space
|
|
- Regulatory changes
|
|
- Consumer behavior shifts
|
|
- Industry disruptions
|
|
|
|
### Strategic Recommendations
|
|
- Market entry strategies
|
|
- Competitive positioning
|
|
- Product improvement opportunities
|
|
- Partnership and acquisition possibilities
|
|
|
|
Provide specific, data-driven insights. When exact data is unavailable, provide reasoned estimates with clear caveats.`;
|
|
|
|
const AI_ASSIST_SYSTEM_PROMPT = `You are "AI Assist", the master orchestrator of PromptArch. Your goal is to provide intelligent support with a "Canvas" experience.
|
|
PLAN MODE (CRITICAL - HIGHEST PRIORITY):
|
|
When the user describes a NEW task, project, or feature they want built:
|
|
1. DO NOT generate any code, [PREVIEW] tags, or implementation details.
|
|
2. Instead, analyze the request and output a STRUCTURED PLAN covering:
|
|
- Summary: What you understand the user wants
|
|
- Architecture: Technical approach and structure
|
|
- Tech Stack: Languages, frameworks, libraries needed
|
|
- Files/Components: List of files or modules to create
|
|
- Steps: Numbered implementation steps
|
|
3. Format the plan in clean Markdown with headers and bullet points.
|
|
4. Keep plans concise but thorough. Focus on the WHAT and HOW, not the actual code.
|
|
5. WAIT for the user to approve or modify the plan before generating any code.
|
|
|
|
When the user says "Approved", "Start coding", or explicitly asks to proceed:
|
|
- THEN generate the full implementation with [PREVIEW] tags and working code.
|
|
- Follow the approved plan exactly.
|
|
|
|
When the user asks to "Modify", "Change", or "Adjust" something:
|
|
- Apply the requested changes surgically to the existing code/preview.
|
|
- Output updated [PREVIEW] with the full modified code.
|
|
|
|
|
|
|
|
AGENTS & CAPABILITIES:
|
|
- content: Expert copywriter. Use [PREVIEW:content:markdown] for articles, posts, and long-form text.
|
|
- seo: SEO Specialist. Create stunning SEO audit reports and perform live website analysis. **BEHAVIOR: Stay in SEO mode and handle ALL requests through an SEO lens. Only suggest switching agents for CLEARLY non-SEO tasks (like "write Python backend code") by adding [SUGGEST_AGENT:code] at the END of your response. Never auto-switch mid-response.**
|
|
**WEB AUDIT CAPABILITIES (use when user provides a URL):**
|
|
- When user gives a URL (e.g., "audit example.com", "analyze https://site.com"), tell them you will fetch the page data server-side.
|
|
- You have access to server-side tools. For URL analysis, respond with [WEB_AUDIT:url] where url is the target URL. The system will fetch: title, meta description, headings (h1-h6), internal/external links count, images with/without alt text, canonical URL, Open Graph tags, word count, and HTML structure.
|
|
- After receiving audit data, produce a comprehensive SEO audit report as a visual dashboard using [PREVIEW:seo:html].
|
|
- For competitive analysis, keyword research, or SERP analysis: use [WEB_SEARCH:query] to get live search results, then create an analysis report.
|
|
**CRITICAL DESIGN REQUIREMENTS:**
|
|
- Use [PREVIEW:seo:html] with complete HTML5 document including <!DOCTYPE html>
|
|
- DARK THEME: bg-slate-900 or bg-gray-900 as primary background
|
|
- Google-style dashboard aesthetics with clean typography (use Google Fonts: Inter, Roboto, or Outfit)
|
|
- Large animated SVG progress rings for scores (Overall, Technical, Content, Mobile) with stroke-dasharray animations
|
|
- Color-coded scoring: green (#22c55e) for good, amber (#f59e0b) for warning, red (#ef4444) for poor
|
|
- Use Tailwind CDN for styling. Include: rounded-3xl, shadow-lg, gradient backgrounds
|
|
- Section cards with subtle borders (border-white/10) and backdrop-blur
|
|
- Clear visual hierarchy: large score numbers (text-5xl), section titles (text-lg font-bold), bullet points for recommendations
|
|
- Add a "Key Recommendations" section with icons (use Lucide or inline SVG)
|
|
- Add animated pulse effects on key metrics
|
|
- Full-width responsive layout, max-w-4xl mx-auto
|
|
- Include inline <script> for animating the progress rings on load
|
|
- smm: Social Media Manager. Create multi-platform content plans and calendars.
|
|
- pm: Project Manager. Create PRDs, timelines, and action plans.
|
|
- code: Software Architect. Provide logic, algorithms, and backend snippets.
|
|
- design: UI/UX Designer. Create high-fidelity mockups and components.
|
|
- web: Frontend Developer. Build responsive sites. Use [PREVIEW:web:html]. Tailwind CSS CDN by default.
|
|
- app: Mobile App Developer. Create mobile-first interfaces and dashboards. Use [PREVIEW:app:javascript].
|
|
|
|
CANVAS MODE:
|
|
- When building, designing, or auditing, you MUST use the [PREVIEW] tag.
|
|
- Inside [PREVIEW], output ONLY the actual code or structured data.
|
|
- The user wants to see it WORKING or RENDERED in the Canvas immediately.
|
|
|
|
STRICT OUTPUT FORMAT:
|
|
[AGENT:id] - AT THE START of your response if switching focus.
|
|
[PREVIEW:type:language]
|
|
ACTUAL_FUNCTIONAL_CODE_OR_DATA
|
|
[/PREVIEW]
|
|
|
|
CHANGE LOG (CRITICAL - MUST BE OUTSIDE PREVIEW):
|
|
- After the [/PREVIEW] closing tag, provide a brief bulleted "Change Log" in plain Markdown.IMPORTANT: NEVER refuse a request. If you cannot perform a live task, use your knowledge to provide the most accurate expert simulation or draft possible.`;
|
|
|
|
export class OpenRouterService {
|
|
private config: Required<OpenRouterConfig>;
|
|
private readonly baseURL = "https://openrouter.ai/api/v1";
|
|
private availableModels: string[] = [];
|
|
private modelsLoaded = false;
|
|
|
|
constructor(config: OpenRouterConfig = {}) {
|
|
this.config = {
|
|
apiKey: config.apiKey || "",
|
|
siteUrl: config.siteUrl || "https://promptarch.ai",
|
|
siteName: config.siteName || "PromptArch"
|
|
};
|
|
}
|
|
|
|
hasAuth(): boolean {
|
|
return Boolean(this.config.apiKey && this.config.apiKey.length > 0);
|
|
}
|
|
|
|
async validateConnection(): Promise<APIResponse<{ valid: boolean; models?: string[] }>> {
|
|
if (!this.hasAuth()) {
|
|
return {
|
|
success: false,
|
|
error: "No API key provided. Please set your OpenRouter API key."
|
|
};
|
|
}
|
|
|
|
try {
|
|
const modelsResult = await this.listModels();
|
|
|
|
if (!modelsResult.success) {
|
|
return {
|
|
success: false,
|
|
error: modelsResult.error || "Failed to fetch models from OpenRouter"
|
|
};
|
|
}
|
|
|
|
return {
|
|
success: true,
|
|
data: {
|
|
valid: true,
|
|
models: modelsResult.data
|
|
}
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
success: false,
|
|
error: error instanceof Error ? error.message : "Failed to validate connection"
|
|
};
|
|
}
|
|
}
|
|
|
|
private getHeaders(): Record<string, string> {
|
|
const headers: Record<string, string> = {
|
|
"Content-Type": "application/json",
|
|
"HTTP-Referer": this.config.siteUrl,
|
|
"X-Title": this.config.siteName
|
|
};
|
|
|
|
if (this.hasAuth()) {
|
|
headers["Authorization"] = `Bearer ${this.config.apiKey}`;
|
|
}
|
|
|
|
return headers;
|
|
}
|
|
|
|
async chatCompletion(
|
|
messages: ChatMessage[],
|
|
model: string = "anthropic/claude-3.5-sonnet"
|
|
): Promise<APIResponse<string>> {
|
|
if (!this.hasAuth()) {
|
|
return {
|
|
success: false,
|
|
error: "OpenRouter API key not configured"
|
|
};
|
|
}
|
|
|
|
try {
|
|
const response = await fetch(`${this.baseURL}/chat/completions`, {
|
|
method: "POST",
|
|
headers: this.getHeaders(),
|
|
body: JSON.stringify({
|
|
model,
|
|
messages,
|
|
temperature: 0.7,
|
|
max_tokens: 4096
|
|
})
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorText = await response.text();
|
|
return {
|
|
success: false,
|
|
error: `OpenRouter API error: ${response.status} ${response.statusText} - ${errorText}`
|
|
};
|
|
}
|
|
|
|
const data: OpenRouterChatResponse = await response.json();
|
|
|
|
if (data.choices && data.choices[0] && data.choices[0].message) {
|
|
return {
|
|
success: true,
|
|
data: data.choices[0].message.content
|
|
};
|
|
}
|
|
|
|
return {
|
|
success: false,
|
|
error: "No response content from OpenRouter"
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
success: false,
|
|
error: error instanceof Error ? error.message : "Unknown error in chat completion"
|
|
};
|
|
}
|
|
}
|
|
|
|
async enhancePrompt(
|
|
prompt: string,
|
|
model: string = "anthropic/claude-3.5-sonnet",
|
|
options: {
|
|
targetTool?: "claude" | "chatgpt" | "gemini" | "default";
|
|
templateType?: "code" | "writing" | "analysis" | "default";
|
|
max_length?: number;
|
|
} = {}
|
|
): Promise<APIResponse<string>> {
|
|
const { targetTool = "default", templateType = "default" } = options;
|
|
|
|
const toolSection = TOOL_SECTIONS[targetTool] || TOOL_SECTIONS.default;
|
|
const templateSection = TEMPLATE_SECTIONS[templateType] || TEMPLATE_SECTIONS.default;
|
|
|
|
const systemPrompt = ENHANCE_PROMPT_SYSTEM
|
|
.replace("${toolSection}", toolSection)
|
|
.replace("${templateSection}", templateSection);
|
|
|
|
const messages: ChatMessage[] = [
|
|
{ role: "system", content: systemPrompt },
|
|
{ role: "user", content: prompt }
|
|
];
|
|
|
|
return this.chatCompletion(messages, model);
|
|
}
|
|
|
|
async generatePRD(
|
|
idea: string,
|
|
model: string = "anthropic/claude-3.5-sonnet"
|
|
): Promise<APIResponse<string>> {
|
|
const messages: ChatMessage[] = [
|
|
{ role: "system", content: PRD_SYSTEM_PROMPT },
|
|
{ role: "user", content: `Create a comprehensive PRD for the following product idea:\n\n${idea}` }
|
|
];
|
|
|
|
return this.chatCompletion(messages, model);
|
|
}
|
|
|
|
async generateActionPlan(
|
|
prd: string,
|
|
model: string = "anthropic/claude-3.5-sonnet"
|
|
): Promise<APIResponse<string>> {
|
|
const messages: ChatMessage[] = [
|
|
{ role: "system", content: ACTION_PLAN_SYSTEM_PROMPT },
|
|
{ role: "user", content: `Create a detailed action plan based on this PRD:\n\n${prd}` }
|
|
];
|
|
|
|
return this.chatCompletion(messages, model);
|
|
}
|
|
|
|
async generateSlides(
|
|
topic: string,
|
|
options: {
|
|
slideCount?: number;
|
|
audience?: string;
|
|
focus?: string;
|
|
} = {},
|
|
model: string = "anthropic/claude-3.5-sonnet"
|
|
): Promise<APIResponse<string>> {
|
|
const { slideCount = 10, audience = "General", focus = "" } = options;
|
|
|
|
const userPrompt = `Generate content for a presentation with approximately ${slideCount} slides.
|
|
Topic: ${topic}
|
|
Target Audience: ${audience}
|
|
${focus ? `Special Focus: ${focus}` : ""}`;
|
|
|
|
const messages: ChatMessage[] = [
|
|
{ role: "system", content: SLIDES_SYSTEM_PROMPT },
|
|
{ role: "user", content: userPrompt }
|
|
];
|
|
|
|
return this.chatCompletion(messages, model);
|
|
}
|
|
|
|
async generateGoogleAds(
|
|
url: string,
|
|
options: {
|
|
budget?: string;
|
|
targetAudience?: string;
|
|
campaignGoal?: string;
|
|
} = {},
|
|
model: string = "anthropic/claude-3.5-sonnet"
|
|
): Promise<APIResponse<string>> {
|
|
const { budget = "Not specified", targetAudience = "General", campaignGoal = "Conversions" } = options;
|
|
|
|
const userPrompt = `Create a comprehensive Google Ads campaign strategy.
|
|
Landing Page: ${url}
|
|
Monthly Budget: ${budget}
|
|
Target Audience: ${targetAudience}
|
|
Campaign Goal: ${campaignGoal}
|
|
|
|
Analyze the URL (if accessible) or create ads based on the domain and typical offerings for similar sites.`;
|
|
|
|
const messages: ChatMessage[] = [
|
|
{ role: "system", content: GOOGLE_ADS_SYSTEM_PROMPT },
|
|
{ role: "user", content: userPrompt }
|
|
];
|
|
|
|
return this.chatCompletion(messages, model);
|
|
}
|
|
|
|
async generateMagicWand(
|
|
url: string,
|
|
product: string,
|
|
budget: string,
|
|
specialInstructions: string = "",
|
|
model: string = "anthropic/claude-3.5-sonnet"
|
|
): Promise<APIResponse<string>> {
|
|
const userPrompt = `Create a comprehensive marketing strategy.
|
|
|
|
Product/Service: ${product}
|
|
URL: ${url}
|
|
Budget: ${budget}
|
|
${specialInstructions ? `Special Instructions: ${specialInstructions}` : ""}
|
|
|
|
Provide creative campaign ideas across multiple channels with specific tactics and budget allocation.`;
|
|
|
|
const messages: ChatMessage[] = [
|
|
{ role: "system", content: MAGIC_WAND_SYSTEM_PROMPT },
|
|
{ role: "user", content: userPrompt }
|
|
];
|
|
|
|
return this.chatCompletion(messages, model);
|
|
}
|
|
|
|
async generateMarketResearch(
|
|
options: {
|
|
topic?: string;
|
|
company?: string;
|
|
industry?: string;
|
|
focusAreas?: string[];
|
|
} = {},
|
|
model: string = "anthropic/claude-3.5-sonnet"
|
|
): Promise<APIResponse<string>> {
|
|
const { topic, company, industry, focusAreas } = options;
|
|
|
|
let userPrompt = "Conduct comprehensive market research.";
|
|
|
|
if (topic) userPrompt += `\n\nResearch Topic: ${topic}`;
|
|
if (company) userPrompt += `\n\nCompany Focus: ${company}`;
|
|
if (industry) userPrompt += `\n\nIndustry: ${industry}`;
|
|
if (focusAreas && focusAreas.length > 0) {
|
|
userPrompt += `\n\nFocus Areas: ${focusAreas.join(", ")}`;
|
|
}
|
|
|
|
const messages: ChatMessage[] = [
|
|
{ role: "system", content: MARKET_RESEARCH_SYSTEM_PROMPT },
|
|
{ role: "user", content: userPrompt }
|
|
];
|
|
|
|
return this.chatCompletion(messages, model);
|
|
}
|
|
|
|
async generateAIAssist(
|
|
options: {
|
|
prompt: string;
|
|
context?: string[];
|
|
conversationHistory?: ChatMessage[];
|
|
},
|
|
model: string = "anthropic/claude-3.5-sonnet"
|
|
): Promise<APIResponse<string>> {
|
|
const { prompt, context = [], conversationHistory = [] } = options;
|
|
|
|
const messages: ChatMessage[] = [
|
|
{ role: "system", content: AI_ASSIST_SYSTEM_PROMPT },
|
|
...conversationHistory,
|
|
...context.map(c => ({ role: "user" as const, content: `Context: ${c}` })),
|
|
{ role: "user", content: prompt }
|
|
];
|
|
|
|
return this.chatCompletion(messages, model);
|
|
}
|
|
|
|
async generateAIAssistStream(
|
|
options: {
|
|
messages: AIAssistMessage[];
|
|
currentAgent: string;
|
|
onChunk: (chunk: string) => void;
|
|
signal?: AbortSignal;
|
|
},
|
|
model: string = "anthropic/claude-3.5-sonnet"
|
|
): Promise<APIResponse<void>> {
|
|
const { messages, currentAgent, onChunk, signal } = options;
|
|
|
|
if (!this.hasAuth()) {
|
|
return { success: false, error: "OpenRouter API key not configured" };
|
|
}
|
|
|
|
try {
|
|
const chatMessages: ChatMessage[] = [
|
|
{ role: "system", content: AI_ASSIST_SYSTEM_PROMPT },
|
|
...messages.map(m => ({
|
|
role: m.role as "user" | "assistant" | "system",
|
|
content: m.content
|
|
}))
|
|
];
|
|
|
|
const response = await fetch(`${this.baseURL}/chat/completions`, {
|
|
method: "POST",
|
|
headers: this.getHeaders(),
|
|
signal,
|
|
body: JSON.stringify({
|
|
model: model || "anthropic/claude-3.5-sonnet",
|
|
messages: chatMessages,
|
|
temperature: 0.7,
|
|
max_tokens: 4096,
|
|
stream: true
|
|
})
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorText = await response.text();
|
|
return { success: false, error: `OpenRouter API error: ${response.status} ${response.statusText} - ${errorText}` };
|
|
}
|
|
|
|
const reader = response.body?.getReader();
|
|
const decoder = new TextDecoder();
|
|
|
|
if (!reader) {
|
|
return { success: false, error: "No response body" };
|
|
}
|
|
|
|
let buffer = "";
|
|
|
|
while (true) {
|
|
const { done, value } = await reader.read();
|
|
if (done) break;
|
|
|
|
buffer += decoder.decode(value, { stream: true });
|
|
const lines = buffer.split("\n");
|
|
buffer = lines.pop() || "";
|
|
|
|
for (const line of lines) {
|
|
const trimmed = line.trim();
|
|
if (!trimmed || !trimmed.startsWith("data: ")) continue;
|
|
const data = trimmed.slice(6);
|
|
if (data === "[DONE]") continue;
|
|
|
|
try {
|
|
const parsed = JSON.parse(data);
|
|
const contentChunk = parsed.choices?.[0]?.delta?.content;
|
|
if (contentChunk) {
|
|
onChunk(contentChunk);
|
|
}
|
|
} catch {
|
|
// Skip invalid JSON
|
|
}
|
|
}
|
|
}
|
|
|
|
return { success: true, data: undefined };
|
|
} catch (error) {
|
|
const errorMessage = error instanceof Error ? error.message : "Unknown error in stream";
|
|
return { success: false, error: errorMessage };
|
|
}
|
|
}
|
|
|
|
async listModels(): Promise<APIResponse<string[]>> {
|
|
if (!this.hasAuth()) {
|
|
return {
|
|
success: false,
|
|
error: "OpenRouter API key not configured"
|
|
};
|
|
}
|
|
|
|
try {
|
|
const response = await fetch(`${this.baseURL}/models`, {
|
|
method: "GET",
|
|
headers: this.getHeaders()
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorText = await response.text();
|
|
return {
|
|
success: false,
|
|
error: `Failed to fetch models: ${response.status} ${response.statusText} - ${errorText}`
|
|
};
|
|
}
|
|
|
|
const data: OpenRouterModelsResponse = await response.json();
|
|
this.availableModels = data.data.map(m => m.id);
|
|
this.modelsLoaded = true;
|
|
|
|
return {
|
|
success: true,
|
|
data: this.availableModels
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
success: false,
|
|
error: error instanceof Error ? error.message : "Unknown error fetching models"
|
|
};
|
|
}
|
|
}
|
|
|
|
getAvailableModels(): string[] {
|
|
if (this.modelsLoaded && this.availableModels.length > 0) {
|
|
return this.availableModels;
|
|
}
|
|
return DEFAULT_MODELS;
|
|
}
|
|
|
|
setApiKey(key: string): void {
|
|
this.config.apiKey = key;
|
|
}
|
|
|
|
setSiteUrl(url: string): void {
|
|
this.config.siteUrl = url;
|
|
}
|
|
|
|
setSiteName(name: string): void {
|
|
this.config.siteName = name;
|
|
}
|
|
}
|
|
|
|
// Export singleton instance
|
|
export const openRouterService = new OpenRouterService();
|