feat: v1.3.0 — plan-first workflow, OpenRouter provider, enhanced prompt engine

Major changes:
- Plan-first workflow: AI generates structured plan before code, with
  plan review card (Modify Plan / Start Coding / Skip to Code)
- Post-coding UX: Preview + Request Modifications buttons after code gen
- OpenRouter integration: 4th AI provider with 20+ model support
- Enhanced prompt engine: 9 strategies, 11+ intent patterns, modular
- PLAN MODE system prompt block in all 4 services
- Fixed stale React closure in approveAndGenerate with isApproval flag
- Fixed canvas auto-opening during plan phase with wasIdle gate
- Updated README, CHANGELOG, .env.example, version bump to 1.3.0
This commit is contained in:
admin
2026-03-18 18:45:37 +00:00
Unverified
parent cca11fe07a
commit a4b7a0d9e4
17 changed files with 3189 additions and 358 deletions

972
lib/enhance-engine.ts Normal file
View File

@@ -0,0 +1,972 @@
/**
* Prompt Enhancement Engine
* Based on prompt-master methodology (https://github.com/nidhinjs/prompt-master)
* Client-side prompt analysis and optimization for various AI tools
*/
// ============================================================================
// TYPE DEFINITIONS
// ============================================================================
/**
* Tool categories with different prompting requirements
*/
export type ToolCategory =
| 'reasoning' // Claude, GPT-4o, Gemini - Full structure, XML tags, explicit format locks
| 'thinking' // o1, o3, DeepSeek-R1 - Short clean instructions only, no CoT
| 'openweight' // Llama, Mistral, Qwen - Shorter prompts, simpler structure
| 'agentic' // Claude Code, Devin, SWE-agent - Start/target state, allowed/forbidden actions, stop conditions
| 'ide' // Cursor, Windsurf, Copilot - File path + function + desired change + scope lock
| 'fullstack' // Bolt, v0, Lovable - Stack spec, component boundaries, what NOT to scaffold
| 'image' // Midjourney, DALL-E, Stable Diffusion - Subject + style + mood + lighting + negative prompts
| 'search'; // Perplexity, SearchGPT - Mode specification, citation requirements
/**
* Template frameworks for different prompt structures
*/
export type TemplateFramework =
| 'RTF' // Role, Task, Format - Simple one-shot
| 'CO-STAR' // Context, Objective, Style, Tone, Audience, Response - Professional documents
| 'RISEN' // Role, Instructions, Steps, End Goal, Narrowing - Complex multi-step
| 'CRISPE' // Capacity, Role, Insight, Statement, Personality, Experiment - Creative work
| 'ChainOfThought' // Logic/math/debugging (NOT for thinking models)
| 'FewShot' // Format-sensitive tasks
| 'FileScope' // IDE AI editing
| 'ReActPlusStop' // Agentic AI
| 'VisualDescriptor'; // Image generation
/**
* Severity levels for diagnostic patterns
*/
export type Severity = 'critical' | 'warning' | 'info';
/**
* Diagnostic pattern for prompt analysis
*/
export interface DiagnosticPattern {
id: string;
name: string;
description: string;
category: 'task' | 'context' | 'format' | 'scope' | 'reasoning' | 'agentic';
detect: (prompt: string) => boolean;
fix: string;
severity: Severity;
}
/**
* Result from running diagnostics on a prompt
*/
export interface DiagnosticResult {
pattern: DiagnosticPattern;
detected: boolean;
severity: Severity;
suggestion: string;
}
/**
* Template structure with metadata
*/
export interface Template {
name: string;
framework: TemplateFramework;
description: string;
structure: string[];
bestFor: ToolCategory[];
}
/**
* Complete analysis report for a prompt
*/
export interface AnalysisReport {
prompt: string;
tokenEstimate: number;
suggestedTool: ToolCategory | null;
suggestedTemplate: Template | null;
diagnostics: DiagnosticResult[];
missingDimensions: string[];
overallScore: number; // 0-100
}
// ============================================================================
// TOOL CATEGORIES
// ============================================================================
export const TOOL_CATEGORIES: Record<ToolCategory, {
description: string;
examples: string[];
promptingStyle: string;
}> = {
reasoning: {
description: 'Models with strong reasoning capabilities',
examples: ['Claude', 'GPT-4o', 'Gemini'],
promptingStyle: 'Full structure, XML tags, explicit format locks, detailed instructions'
},
thinking: {
description: 'Models with built-in chain-of-thought',
examples: ['o1', 'o3', 'DeepSeek-R1'],
promptingStyle: 'Short clean instructions only, NO explicit CoT or step-by-step'
},
openweight: {
description: 'Open-source models',
examples: ['Llama', 'Mistral', 'Qwen'],
promptingStyle: 'Shorter prompts, simpler structure, clear direct instructions'
},
agentic: {
description: 'Autonomous coding agents',
examples: ['Claude Code', 'Devin', 'SWE-agent'],
promptingStyle: 'Start/target state, allowed/forbidden actions, stop conditions'
},
ide: {
description: 'IDE-integrated AI assistants',
examples: ['Cursor', 'Windsurf', 'Copilot'],
promptingStyle: 'File path + function + desired change + scope lock'
},
fullstack: {
description: 'Full-stack app builders',
examples: ['Bolt', 'v0', 'Lovable'],
promptingStyle: 'Stack spec, component boundaries, what NOT to scaffold'
},
image: {
description: 'Image generation models',
examples: ['Midjourney', 'DALL-E', 'Stable Diffusion'],
promptingStyle: 'Subject + style + mood + lighting + negative prompts'
},
search: {
description: 'Search-augmented AI',
examples: ['Perplexity', 'SearchGPT'],
promptingStyle: 'Mode specification, citation requirements, source attribution'
}
};
// ============================================================================
// TEMPLATE FRAMEWORKS
// ============================================================================
export const TEMPLATES: Template[] = [
{
name: 'RTF (Role-Task-Format)',
framework: 'RTF',
description: 'Simple one-shot prompts with clear role, task, and output format',
structure: ['Role: Who you are', 'Task: What to do', 'Format: How to output'],
bestFor: ['reasoning', 'openweight']
},
{
name: 'CO-STAR',
framework: 'CO-STAR',
description: 'Comprehensive framework for professional documents and complex tasks',
structure: [
'Context: Background information',
'Objective: What needs to be achieved',
'Style: Writing style and tone',
'Tone: Emotional tone',
'Audience: Who will read this',
'Response: Expected output format'
],
bestFor: ['reasoning', 'thinking', 'openweight']
},
{
name: 'RISEN',
framework: 'RISEN',
description: 'Multi-step complex task framework with clear end goals',
structure: [
'Role: AI agent identity',
'Instructions: Task requirements',
'Steps: Sequential actions',
'End Goal: Success criteria',
'Narrowing: Constraints and boundaries'
],
bestFor: ['reasoning', 'agentic']
},
{
name: 'CRISPE',
framework: 'CRISPE',
description: 'Creative work framework with personality and experimentation',
structure: [
'Capacity: What you can do',
'Role: Creative identity',
'Insight: Key perspective',
'Statement: The core request',
'Personality: Tone and style',
'Experiment: Creative constraints'
],
bestFor: ['reasoning', 'openweight']
},
{
name: 'Chain of Thought',
framework: 'ChainOfThought',
description: 'Step-by-step reasoning for logic, math, and debugging (NOT for thinking models)',
structure: [
'Problem statement',
'Step-by-step reasoning',
'Final answer',
'Verification'
],
bestFor: ['reasoning', 'openweight']
},
{
name: 'Few-Shot Learning',
framework: 'FewShot',
description: 'Provide examples to guide format-sensitive tasks',
structure: [
'Task description',
'Example 1: Input -> Output',
'Example 2: Input -> Output',
'Example 3: Input -> Output',
'Actual task'
],
bestFor: ['reasoning', 'openweight']
},
{
name: 'File-Scope Lock',
framework: 'FileScope',
description: 'IDE-specific editing with precise file and function targeting',
structure: [
'File path',
'Function/component name',
'Current code snippet',
'Desired change',
'Scope: ONLY modify X, do NOT touch Y'
],
bestFor: ['ide']
},
{
name: 'ReAct + Stop Conditions',
framework: 'ReActPlusStop',
description: 'Agentic framework with explicit stopping rules',
structure: [
'Starting state: Current situation',
'Target state: Desired outcome',
'Allowed actions: What you CAN do',
'Forbidden actions: What you CANNOT do',
'Stop conditions: When to pause and ask',
'Output requirements: Progress reporting'
],
bestFor: ['agentic']
},
{
name: 'Visual Descriptor',
framework: 'VisualDescriptor',
description: 'Comprehensive image generation prompt structure',
structure: [
'Subject: Main element',
'Style: Art style or aesthetic',
'Mood: Emotional quality',
'Lighting: Light source and quality',
'Composition: Framing and perspective',
'Colors: Color palette',
'Negative prompts: What to exclude'
],
bestFor: ['image']
}
];
// ============================================================================
// DIAGNOSTIC PATTERNS (35 Total)
// ============================================================================
const TASK_PATTERNS: DiagnosticPattern[] = [
{
id: 'task-001',
name: 'Vague task verb',
description: 'Uses generic verbs like "help", "fix", "make" without specifics',
category: 'task',
detect: (prompt: string) => {
const vagueVerbs = /\b(help|fix|make|improve|update|change|handle|work on)\b/i;
const noSpecifics = !/\b(specifically|exactly|to|that|which|called|named):\b/i.test(prompt);
return vagueVerbs.test(prompt) && noSpecifics && prompt.split(' ').length < 30;
},
fix: 'Replace vague verbs with specific action verbs. Instead of "fix this", use "add error handling to the login function"',
severity: 'warning'
},
{
id: 'task-002',
name: 'Two tasks in one',
description: 'Contains multiple distinct tasks in a single prompt',
category: 'task',
detect: (prompt: string) => {
const andPattern = /\b(and|also|plus|additionally)\s+[a-z]+\b/i;
const commaTasks = /\b(create|build|fix|add|write|update)[^,.]+,[^,.]+(create|build|fix|add|write|update)/i;
return andPattern.test(prompt) || commaTasks.test(prompt);
},
fix: 'Split into separate prompts. Each prompt should have ONE primary task.',
severity: 'critical'
},
{
id: 'task-003',
name: 'No success criteria',
description: 'Missing clear definition of when the task is complete',
category: 'task',
detect: (prompt: string) => {
const successWords = /\b(done when|success criteria|complete when|should|must result|verify that|ensure that|passes when)\b/i;
const isComplexTask = /\b(build|create|implement|develop|design|setup)\b/i.test(prompt);
return isComplexTask && !successWords.test(prompt);
},
fix: 'Add explicit success criteria: "The task is complete when [specific condition is met]"',
severity: 'warning'
},
{
id: 'task-004',
name: 'Over-permissive agent',
description: 'Gives AI too much freedom without constraints',
category: 'task',
detect: (prompt: string) => {
const permissivePhrases = /\b(whatever it takes|do your best|figure it out|you decide|however you want|as you see fit)\b/i;
return permissivePhrases.test(prompt);
},
fix: 'Replace open-ended permissions with specific constraints and scope boundaries.',
severity: 'critical'
},
{
id: 'task-005',
name: 'Emotional task description',
description: 'Uses emotional language without specific technical details',
category: 'task',
detect: (prompt: string) => {
const emotionalWords = /\b(broken|mess|terrible|awful|doesn't work|horrible|stupid|hate|frustrating)\b/i;
const noTechnicalDetails = !/\b(error|bug|line|function|file|exception|fail|crash)\b/i.test(prompt);
return emotionalWords.test(prompt) && noTechnicalDetails;
},
fix: 'Replace emotional language with specific technical details: what error, what line, what behavior?',
severity: 'warning'
},
{
id: 'task-006',
name: 'Build-the-whole-thing',
description: 'Attempts to build an entire project in one prompt',
category: 'task',
detect: (prompt: string) => {
const wholeProjectPhrases = /\b(entire app|whole project|full website|complete system|everything|end to end|from scratch)\b/i;
return wholeProjectPhrases.test(prompt);
},
fix: 'Break down into smaller, iterative prompts. Start with core functionality, then add features.',
severity: 'critical'
},
{
id: 'task-007',
name: 'Implicit reference',
description: 'References something previously mentioned without context',
category: 'task',
detect: (prompt: string) => {
const implicitRefs = /\b(the thing|that one|what we discussed|from before|the previous|like the other)\b/i;
const noContext = prompt.split(' ').length < 50;
return implicitRefs.test(prompt) && noContext;
},
fix: 'Always include full context. Replace "the thing" with specific name/description.',
severity: 'critical'
}
];
const CONTEXT_PATTERNS: DiagnosticPattern[] = [
{
id: 'ctx-001',
name: 'Assumed prior knowledge',
description: 'Assumes AI remembers previous conversations or context',
category: 'context',
detect: (prompt: string) => {
const assumptionPhrases = /\b(continue|as before|like we said|you know|from our chat|from earlier)\b/i;
const noContextProvided = prompt.split(' ').length < 40;
return assumptionPhrases.test(prompt) && noContextProvided;
},
fix: 'Include relevant context from previous work. Do not assume continuity.',
severity: 'warning'
},
{
id: 'ctx-002',
name: 'No project context',
description: 'Very short prompt with no domain or technology context',
category: 'context',
detect: (prompt: string) => {
const wordCount = prompt.split(/\s+/).length;
const hasTech = /\b(javascript|python|react|api|database|server|frontend|backend|mobile|web)\b/i;
return wordCount < 15 && !hasTech.test(prompt);
},
fix: 'Add project context: technology stack, domain, and what you\'re building.',
severity: 'warning'
},
{
id: 'ctx-003',
name: 'Forgotten stack',
description: 'Tech-agnostic prompt that implies an existing project',
category: 'context',
detect: (prompt: string) => {
const projectWords = /\b(add to|update the|change the|modify the|existing|current)\b/i;
const noTechStack = !/\b(javascript|typescript|python|java|rust|go|react|vue|angular|node|django|rails)\b/i.test(prompt);
return projectWords.test(prompt) && noTechStack;
},
fix: 'Specify your technology stack: language, framework, and key dependencies.',
severity: 'critical'
},
{
id: 'ctx-004',
name: 'Hallucination invite',
description: 'Asks for general knowledge that may not exist',
category: 'context',
detect: (prompt: string) => {
const hallucinationPhrases = /\b(what do experts say|what is commonly known|generally accepted|most people think|typical approach)\b/i;
return hallucinationPhrases.test(prompt);
},
fix: 'Ask for specific sources or provide source material. Avoid general "what do X think" questions.',
severity: 'info'
},
{
id: 'ctx-005',
name: 'Undefined audience',
description: 'User-facing output without audience specification',
category: 'context',
detect: (prompt: string) => {
const userFacing = /\b(write|create|generate|draft)\s+(content|message|email|copy|text|documentation)\b/i;
const noAudience = !/\b(for|audience|target|reader|user|customer|stakeholder)\b/i.test(prompt);
return userFacing.test(prompt) && noAudience;
},
fix: 'Specify who will read this output: "Write for [audience] who [context]"',
severity: 'warning'
},
{
id: 'ctx-006',
name: 'No prior failures',
description: 'Complex task without mentioning what was tried before',
category: 'context',
detect: (prompt: string) => {
const complexTask = /\b(debug|fix|solve|resolve|implement|build|create)\b/i;
const noPriorAttempts = !/\b(tried|attempted|already|previous|before|not working|failed)\b/i.test(prompt);
const isLongPrompt = prompt.split(' ').length > 20;
return complexTask.test(prompt) && noPriorAttempts && isLongPrompt;
},
fix: 'Mention what you\'ve already tried: "I tried X but got Y error. Now..."',
severity: 'info'
}
];
const FORMAT_PATTERNS: DiagnosticPattern[] = [
{
id: 'fmt-001',
name: 'Missing output format',
description: 'No specification of how output should be structured',
category: 'format',
detect: (prompt: string) => {
const formatKeywords = /\b(list|table|json|markdown|bullet|paragraph|csv|html|code|steps)\b/i;
const outputKeywords = /\b(output|return|format as|in the form of|structure)\b/i;
return !formatKeywords.test(prompt) && !outputKeywords.test(prompt);
},
fix: 'Specify output format: "Return as a bulleted list" or "Output as JSON"',
severity: 'warning'
},
{
id: 'fmt-002',
name: 'Implicit length',
description: 'Uses length terms without specific counts',
category: 'format',
detect: (prompt: string) => {
const vagueLength = /\b(summary|description|overview|brief|short|long|detailed)\b/i;
const noSpecificLength = !/\b(\d+\s*(words?|sentences?|paragraphs?)|under\s*\d+|max\s*\d+)\b/i.test(prompt);
return vagueLength.test(prompt) && noSpecificLength;
},
fix: 'Be specific: "Write 2-3 sentences" or "Keep under 100 words"',
severity: 'info'
},
{
id: 'fmt-003',
name: 'No role assignment',
description: 'Long prompt without specifying who AI should be',
category: 'format',
detect: (prompt: string) => {
const wordCount = prompt.split(/\s+/).length;
const roleKeywords = /\b(act as|you are|role|persona|expert|specialist|professional|engineer|developer|analyst)\b/i;
return wordCount > 50 && !roleKeywords.test(prompt);
},
fix: 'Add role assignment: "Act as a [role] with [expertise]"',
severity: 'info'
},
{
id: 'fmt-004',
name: 'Vague aesthetic',
description: 'Design-related prompt without specific visual direction',
category: 'format',
detect: (prompt: string) => {
const vagueAesthetic = /\b(professional|clean|modern|nice|good looking|beautiful|sleek)\b/i;
const noVisualSpecs = !/\b(colors?|fonts?|spacing|layout|style|theme|design system)\b/i.test(prompt);
return vagueAesthetic.test(prompt) && noVisualSpecs;
},
fix: 'Specify visual details: colors, typography, spacing, specific design reference.',
severity: 'warning'
},
{
id: 'fmt-005',
name: 'No negative prompts for image',
description: 'Image generation without exclusion criteria',
category: 'format',
detect: (prompt: string) => {
const imageKeywords = /\b(image|photo|picture|illustration|generate|create art|midjourney|dall-e)\b/i;
const noNegative = !/\b(negative|exclude|avoid|without|no|not)\b/i.test(prompt);
return imageKeywords.test(prompt) && noNegative;
},
fix: 'Add negative prompts: "Negative: blurry, low quality, distorted"',
severity: 'warning'
},
{
id: 'fmt-006',
name: 'Prose for Midjourney',
description: 'Long descriptive sentences instead of keyword-style prompts',
category: 'format',
detect: (prompt: string) => {
const longSentences = prompt.split(/[.!?]/).filter(s => s.trim().split(' ').length > 10).length > 0;
const imageKeywords = /\b(image|photo|art|illustration|midjourney|dall-e|stable diffusion)\b/i;
return imageKeywords.test(prompt) && longSentences;
},
fix: 'Use keyword-style prompts: "Subject, style, mood, lighting, --ar 16:9"',
severity: 'warning'
}
];
const SCOPE_PATTERNS: DiagnosticPattern[] = [
{
id: 'scp-001',
name: 'No scope boundary',
description: 'Missing specific scope constraints',
category: 'scope',
detect: (prompt: string) => {
const scopeWords = /\b(only|just|specifically|exactly|limit|restrict)\b/i;
const hasFilePath = /\/[\w.]+/.test(prompt) || /\b[\w-]+\.(js|ts|py|java|go|rs|cpp|c|h)\b/i;
const hasFunction = /\b(function|method|class|component)\s+\w+/i;
return !scopeWords.test(prompt) && !hasFilePath && !hasFunction;
},
fix: 'Add scope boundary: "Only modify X, do NOT touch Y"',
severity: 'warning'
},
{
id: 'scp-002',
name: 'No stack constraints',
description: 'Technical task without version specifications',
category: 'scope',
detect: (prompt: string) => {
const techTask = /\b(build|create|implement|setup|install|use|add)\s+(\w+\s+){0,3}(app|api|server|database|system)\b/i;
const noVersion = !/\b(version|v\d+|\d+\.\d+|specifically|exactly)\b/i.test(prompt);
return techTask.test(prompt) && noVersion;
},
fix: 'Specify versions: "Use React 18 with TypeScript 5"',
severity: 'warning'
},
{
id: 'scp-003',
name: 'No stop condition for agents',
description: 'Agentic task without explicit stopping rules',
category: 'scope',
detect: (prompt: string) => {
const agentKeywords = /\b(agent|autonomous|run this|execute|iterate|keep going)\b/i;
const noStop = !/\b(stop|pause|ask me|check in|before continuing|confirm)\b/i.test(prompt);
return agentKeywords.test(prompt) && noStop;
},
fix: 'Add stop conditions: "Stop and ask before deleting files" or "Pause after each major step"',
severity: 'critical'
},
{
id: 'scp-004',
name: 'No file path for IDE',
description: 'IDE editing without file specification',
category: 'scope',
detect: (prompt: string) => {
const editKeywords = /\b(update|fix|change|modify|edit|refactor)\b/i;
const hasPath = /\/[\w./-]+|\b[\w-]+\.(js|ts|jsx|tsx|py|java|go|rs|cpp|c|h|css|html|json)\b/i;
return editKeywords.test(prompt) && !hasPath;
},
fix: 'Always include file path: "Update src/components/Header.tsx"',
severity: 'critical'
},
{
id: 'scp-005',
name: 'Wrong template',
description: 'Template mismatch for the target tool',
category: 'scope',
detect: (prompt: string) => {
// Detect if using complex structure for thinking models
const thinkingModel = /\b(o1|o3|deepseek.*r1|thinking)\b/i;
const complexStructure = /\b(step by step|think through|reasoning|<thinking>|chain of thought)\b/i;
return thinkingModel.test(prompt) && complexStructure.test(prompt);
},
fix: 'For thinking models (o1, o3, R1), use short clean instructions without explicit CoT.',
severity: 'critical'
},
{
id: 'scp-006',
name: 'Pasting codebase',
description: 'Extremely long prompt suggesting codebase paste',
category: 'scope',
detect: (prompt: string) => {
const wordCount = prompt.split(/\s+/).length;
const multipleFiles = (prompt.match(/```/g) || []).length > 4;
return wordCount > 500 || multipleFiles;
},
fix: 'Use file paths and references instead of pasting entire files. Or use an IDE AI tool.',
severity: 'warning'
}
];
const REASONING_PATTERNS: DiagnosticPattern[] = [
{
id: 'rsn-001',
name: 'No CoT for logic',
description: 'Complex logic task without step-by-step instructions',
category: 'reasoning',
detect: (prompt: string) => {
const logicKeywords = /\b(compare|analyze|which is better|debug|why does|explain why|how does|verify)\b/i;
const noCoT = !/\b(step by step|walk through|reasoning|think through|first|then|finally)\b/i.test(prompt);
return logicKeywords.test(prompt) && noCoT;
},
fix: 'Add "Step by step" or "Walk through your reasoning" for logic tasks.',
severity: 'warning'
},
{
id: 'rsn-002',
name: 'CoT on reasoning models',
description: 'Explicit CoT instructions for thinking models',
category: 'reasoning',
detect: (prompt: string) => {
const thinkingModel = /\b(o1|o3|deepseek.*r1)\b/i;
const explicitCoT = /\b(step by step|think through|<thinking>|reasoning process|show your work)\b/i;
return thinkingModel.test(prompt) && explicitCoT.test(prompt);
},
fix: 'Remove explicit CoT instructions. Thinking models have built-in reasoning.',
severity: 'critical'
},
{
id: 'rsn-003',
name: 'Inter-session memory',
description: 'Assumes AI remembers across separate sessions',
category: 'reasoning',
detect: (prompt: string) => {
const memoryPhrases = /\b(you already know|remember|from our conversation|we discussed|earlier we|as mentioned)\b/i;
return memoryPhrases.test(prompt);
},
fix: 'AI does not remember between sessions. Include all necessary context.',
severity: 'info'
},
{
id: 'rsn-004',
name: 'Contradicting prior',
description: 'Explicit contradiction of previous instructions',
category: 'reasoning',
detect: (prompt: string) => {
const contradictionPhrases = /\b(actually|wait|ignore what i said|forget that|never mind|scratch that)\b/i;
return contradictionPhrases.test(prompt);
},
fix: 'State corrections clearly: "Correction: Replace X with Y"',
severity: 'warning'
},
{
id: 'rsn-005',
name: 'No grounding rule',
description: 'Factual task without certainty constraints',
category: 'reasoning',
detect: (prompt: string) => {
const factualTask = /\b(summarize|what is|tell me about|explain|list|research|find)\b/i;
const noGrounding = !/\b(if unsure|don't hallucinate|only if certain|say i don't know|stick to)\b/i.test(prompt);
return factualTask.test(prompt) && noGrounding && prompt.split(' ').length > 10;
},
fix: 'Add grounding: "If uncertain, say so rather than guessing"',
severity: 'info'
}
];
const AGENTIC_PATTERNS: DiagnosticPattern[] = [
{
id: 'agt-001',
name: 'No starting state',
description: 'Build/create task without current state description',
category: 'agentic',
detect: (prompt: string) => {
const buildKeywords = /\b(build|create|set up|implement|develop|make)\b/i;
const currentState = !/\b(currently|existing|now|currently have|right now|starting from)\b/i.test(prompt);
return buildKeywords.test(prompt) && currentState;
},
fix: 'Describe starting state: "Currently I have X. I want to reach Y."',
severity: 'warning'
},
{
id: 'agt-002',
name: 'No target state',
description: 'Agentic task without explicit deliverable',
category: 'agentic',
detect: (prompt: string) => {
const vagueCompletion = /\b(work on this|handle this|do this|take care of)\b/i;
const noTarget = !/\b(result should|final output|deliverable|end with|complete when)\b/i.test(prompt);
return vagueCompletion.test(prompt) && noTarget;
},
fix: 'Specify target state: "The final result should be [specific outcome]"',
severity: 'critical'
},
{
id: 'agt-003',
name: 'Silent agent',
description: 'Multi-step task without progress reporting requirements',
category: 'agentic',
detect: (prompt: string) => {
const multiStep = /\b(then|next|after that|first|second|finally)\b/i;
const noOutput = !/\b(show me|report|output|print|log|display progress|tell me)\b/i.test(prompt);
return multiStep.test(prompt) && noOutput;
},
fix: 'Add output requirements: "Report progress after each step"',
severity: 'warning'
},
{
id: 'agt-004',
name: 'Unlocked filesystem',
description: 'Agentic task without file access restrictions',
category: 'agentic',
detect: (prompt: string) => {
const agentKeywords = /\b(agent|autonomous|run|execute|implement|build|create)\b/i;
const noRestrictions = !/\b(only touch|don't modify|never delete|restrict to|scope|limit)\b/i.test(prompt);
return agentKeywords.test(prompt) && noRestrictions;
},
fix: 'Add file restrictions: "Only modify files in X, never touch Y"',
severity: 'critical'
},
{
id: 'agt-005',
name: 'No review trigger',
description: 'Agentic task without approval checkpoints',
category: 'agentic',
detect: (prompt: string) => {
const riskyActions = /\b(delete|remove|overwrite|deploy|publish|submit|merge)\b/i;
const noReview = !/\b(ask before|confirm|review|approve|check with me)\b/i.test(prompt);
return riskyActions.test(prompt) && noReview;
},
fix: 'Add review triggers: "Ask before deleting any files" or "Confirm before deploying"',
severity: 'critical'
}
];
// Combine all patterns
export const ALL_PATTERNS: DiagnosticPattern[] = [
...TASK_PATTERNS,
...CONTEXT_PATTERNS,
...FORMAT_PATTERNS,
...SCOPE_PATTERNS,
...REASONING_PATTERNS,
...AGENTIC_PATTERNS
];
// ============================================================================
// CORE FUNCTIONS
// ============================================================================
/**
* Auto-detect the target AI tool category based on prompt content
*/
export function detectToolCategory(prompt: string): ToolCategory | null {
const p = prompt.toLowerCase();
// Check for specific tool mentions
if (/(claude|gpt-4|gemini|gpt4)/i.test(prompt)) return 'reasoning';
if (/(o1|o3|deepseek.*r1|thinking.*model)/i.test(prompt)) return 'thinking';
if (/(llama|mistral|qwen|open.*weight|local.*model)/i.test(prompt)) return 'openweight';
if (/(claude code|devin|swe.*agent|autonomous.*agent)/i.test(prompt)) return 'agentic';
if (/(cursor|windsurf|copilot|ide.*ai|editor.*ai)/i.test(prompt)) return 'ide';
if (/(bolt|v0|lovable|fullstack.*ai|app.*builder)/i.test(prompt)) return 'fullstack';
if (/(midjourney|dall.?e|stable diffusion|image.*generate|create.*image|generate.*art)/i.test(prompt)) return 'image';
if (/(perplexity|searchgpt|search.*ai|research.*mode)/i.test(prompt)) return 'search';
// Infer from content patterns
if (/\.(js|ts|py|java|go|rs|cpp|c|h)\b/.test(prompt) && /\b(update|fix|change|modify)\b/.test(p)) return 'ide';
if (/\b(build|create|set up|implement).*\b(app|api|server|system)\b/.test(p) && /\b(stop|pause|ask before)\b/.test(p)) return 'agentic';
if (/\b(step by step|<thinking>|chain of thought|reasoning)\b/.test(p)) return 'reasoning';
if (/\b(image|photo|art|illustration)\b/.test(p) && /\b(style|mood|lighting)\b/.test(p)) return 'image';
return null;
}
/**
* Select the best template based on tool category and prompt analysis
*/
export function selectTemplate(prompt: string, toolCategory: ToolCategory | null): Template | null {
const p = prompt.toLowerCase();
// Image generation
if (toolCategory === 'image' || /\b(image|photo|art|illustration|midjourney|dall.?e)\b/.test(p)) {
return TEMPLATES.find(t => t.framework === 'VisualDescriptor') || null;
}
// IDE editing
if (toolCategory === 'ide' || (/\.(js|ts|py|java|go|rs)\b/.test(prompt) && /\b(update|fix|modify)\b/.test(p))) {
return TEMPLATES.find(t => t.framework === 'FileScope') || null;
}
// Agentic tasks
if (toolCategory === 'agentic' || /\b(build|create|set up).*\b(stop|pause|ask before)\b/.test(p)) {
return TEMPLATES.find(t => t.framework === 'ReActPlusStop') || null;
}
// Complex multi-step tasks
if (/\b(step|then|next|after|first|second|finally)\b/.test(p) && p.split(' ').length > 30) {
return TEMPLATES.find(t => t.framework === 'RISEN') || null;
}
// Logic/debugging tasks
if (/\b(debug|compare|analyze|which is better|why does|verify)\b/.test(p)) {
if (toolCategory !== 'thinking') {
return TEMPLATES.find(t => t.framework === 'ChainOfThought') || null;
}
}
// Professional documents
if (/\b(documentation|report|proposal|spec|requirements)\b/.test(p) && p.split(' ').length > 40) {
return TEMPLATES.find(t => t.framework === 'CO-STAR') || null;
}
// Creative work
if (/\b(creative|design|story|narrative|brand|voice)\b/.test(p)) {
return TEMPLATES.find(t => t.framework === 'CRISPE') || null;
}
// Format-sensitive tasks
if (/\b(example|sample|format|pattern|template)\b/.test(p)) {
return TEMPLATES.find(t => t.framework === 'FewShot') || null;
}
// Default to RTF for simple prompts
if (p.split(' ').length < 50) {
return TEMPLATES.find(t => t.framework === 'RTF') || null;
}
// Default for longer prompts
return TEMPLATES.find(t => t.framework === 'CO-STAR') || null;
}
/**
* Run all diagnostic patterns on a prompt
*/
export function runDiagnostics(prompt: string): DiagnosticResult[] {
const results: DiagnosticResult[] = [];
for (const pattern of ALL_PATTERNS) {
const detected = pattern.detect(prompt);
if (detected) {
results.push({
pattern,
detected: true,
severity: pattern.severity,
suggestion: pattern.fix
});
}
}
// Sort by severity (critical first)
const severityOrder = { critical: 0, warning: 1, info: 2 };
results.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);
return results;
}
/**
* Estimate token count (rough approximation: ~0.75 words per token)
*/
export function estimateTokens(prompt: string): number {
const wordCount = prompt.split(/\s+/).length;
return Math.ceil(wordCount * 0.75);
}
/**
* Identify missing dimensions from a prompt
*/
export function identifyMissingDimensions(prompt: string): string[] {
const missing: string[] = [];
const p = prompt.toLowerCase();
// Check for common dimensions
if (!/\b(act as|you are|role|expert|specialist)\b/i.test(prompt)) {
missing.push('Role/Identity');
}
if (!/\b(context|background|project|currently working)\b/i.test(prompt)) {
missing.push('Context');
}
if (!/\b(format|output|return as|structure)\b/i.test(prompt)) {
missing.push('Output Format');
}
if (!/\b(success|complete when|done when|verify|ensure)\b/i.test(prompt)) {
missing.push('Success Criteria');
}
if (!/\b(only|just|limit|restrict|scope)\b/i.test(prompt) && prompt.split(' ').length > 20) {
missing.push('Scope Boundaries');
}
if (!/\b(javascript|python|react|node|typescript|java|rust|go)\b/i.test(prompt) &&
/\b(code|function|class|app|api)\b/i.test(prompt)) {
missing.push('Technology Stack');
}
return missing;
}
/**
* Calculate overall prompt quality score (0-100)
*/
export function calculateScore(diagnostics: DiagnosticResult[], missingDimensions: string[]): number {
let score = 100;
// Deduct for diagnostics
for (const d of diagnostics) {
switch (d.severity) {
case 'critical': score -= 15; break;
case 'warning': score -= 8; break;
case 'info': score -= 3; break;
}
}
// Deduct for missing dimensions
score -= missingDimensions.length * 5;
return Math.max(0, Math.min(100, score));
}
/**
* Generate comprehensive analysis report
*/
export function generateAnalysisReport(prompt: string): AnalysisReport {
const suggestedTool = detectToolCategory(prompt);
const suggestedTemplate = selectTemplate(prompt, suggestedTool);
const diagnostics = runDiagnostics(prompt);
const missingDimensions = identifyMissingDimensions(prompt);
const tokenEstimate = estimateTokens(prompt);
const overallScore = calculateScore(diagnostics, missingDimensions);
return {
prompt,
tokenEstimate,
suggestedTool,
suggestedTemplate,
diagnostics,
missingDimensions,
overallScore
};
}
/**
* Get human-readable tool category description
*/
export function getToolDescription(category: ToolCategory): string {
return TOOL_CATEGORIES[category].description;
}
/**
* Get prompting style for a tool category
*/
export function getPromptingStyle(category: ToolCategory): string {
return TOOL_CATEGORIES[category].promptingStyle;
}
/**
* Get patterns by category
*/
export function getPatternsByCategory(category: DiagnosticPattern['category']): DiagnosticPattern[] {
return ALL_PATTERNS.filter(p => p.category === category);
}
/**
* Get pattern by ID
*/
export function getPatternById(id: string): DiagnosticPattern | undefined {
return ALL_PATTERNS.find(p => p.id === id);
}

View File

@@ -47,6 +47,21 @@ export const translations = {
clear: "Clear",
enterPromptError: "Please enter a prompt to enhance",
errorEnhance: "Failed to enhance prompt",
enhanceMode: "Enhancement Mode",
quickMode: "Quick",
deepMode: "Deep Analysis",
deepEnhance: "Deep Enhance",
targetTool: "Target AI Tool",
templateLabel: "Template Framework",
diagnosticsTitle: "Prompt Diagnostics",
promptQuality: "Prompt Quality",
missingDimensions: "Missing Dimensions",
tokensLabel: "tokens",
inputTokens: "input tokens",
outputTokens: "output tokens",
strategyNote: "Strategy",
strategyForTool: "Optimized for {tool} using {template} template.",
fixedIssues: "Fixed {count} critical issue(s).",
},
prdGenerator: {
title: "PRD Generator",
@@ -179,6 +194,7 @@ export const translations = {
qwenDesc: "Alibaba DashScope API",
ollamaDesc: "Ollama Cloud API",
zaiDesc: "Z.AI Plan API",
openrouterDesc: "OpenRouter - Access 100+ AI models",
},
uxDesigner: {
title: "UX Designer Prompt",
@@ -405,6 +421,11 @@ export const translations = {
files: "Files",
approveGenerate: "Approve & Generate Development",
startingEngine: "Starting Engine...",
startCoding: "Start Coding",
modifyPlan: "Modify Plan",
skipPlan: "Skip to Chat",
planSummary: "Summary",
implementationSteps: "Implementation Steps",
activateArtifact: "Activate Artifact",
canvasReady: "Canvas ready",
canvasIdle: "Canvas idle",
@@ -491,6 +512,21 @@ export const translations = {
clear: "Очистить",
enterPromptError: "Пожалуйста, введите промпт для улучшения",
errorEnhance: "Не удалось улучшить промпт",
enhanceMode: "Режим улучшения",
quickMode: "Быстрый",
deepMode: "Глубокий анализ",
deepEnhance: "Глубокое улучшение",
targetTool: "Целевой ИИ-инструмент",
templateLabel: "Шаблон фреймворка",
diagnosticsTitle: "Диагностика промпта",
promptQuality: "Качество промпта",
missingDimensions: "Отсутствующие параметры",
tokensLabel: "токенов",
inputTokens: "входных токенов",
outputTokens: "выходных токенов",
strategyNote: "Стратегия",
strategyForTool: "Оптимизировано для {tool} с шаблоном {template}.",
fixedIssues: "Исправлено {count} критических проблем(ы).",
},
prdGenerator: {
title: "Генератор PRD",
@@ -622,6 +658,7 @@ export const translations = {
getApiKey: "Получить API ключ здесь:",
qwenDesc: "Alibaba DashScope API",
ollamaDesc: "Ollama Cloud API",
openrouterDesc: "OpenRouter — доступ к 100+ ИИ-моделям",
zaiDesc: "Z.AI Plan API",
},
uxDesigner: {
@@ -849,6 +886,11 @@ export const translations = {
files: "Файлы",
approveGenerate: "Одобрить и начать разработку",
startingEngine: "Запуск двигателя...",
startCoding: "Начать кодинг",
modifyPlan: "Изменить план",
skipPlan: "Пропустить в чат",
planSummary: "Суммарно",
implementationSteps: "Шаги реализации",
activateArtifact: "Активировать артефакт",
canvasReady: "Холст готов",
canvasIdle: "Холст в режиме ожидания",
@@ -926,15 +968,30 @@ export const translations = {
},
promptEnhancer: {
title: "משפר פרומפטים",
description: "הפוך רעיונות פשוטים לפרומפטים מקצועיים באיכות גבוהה",
description: "הפוך רעיונות פשוטים לפרומפטים מקצועניים באיכות גבוהה",
placeholder: "הדבק את הפרומפט הראשוני שלך כאן...",
inputLabel: "פרומפט מקורי",
enhancedTitle: "אינטליגנציה משופרת",
enhancedDesc: "פרומפט מקצועי מוכן לסוכני קידוד",
enhancedDesc: "פרומפט מקצועני מוכן לכל כלי AI",
emptyState: "פרומפט משופר יופיע כאן",
clear: "נקה",
enterPromptError: "אנא הזן פרומפט לשיפור",
errorEnhance: "נכשל בשיפור הפרומפט",
enhanceMode: "מצב שיפור",
quickMode: "מהיר",
deepMode: "ניתוח עמוק",
deepEnhance: "שיפור עמוק",
targetTool: "כלי AI יעד",
templateLabel: "מסגרת תבנית",
diagnosticsTitle: "אבחון פרומפט",
promptQuality: "איכות פרומפט",
missingDimensions: "מימדים חסרים",
tokensLabel: "אסימונים",
inputTokens: "אסימוני קלט",
outputTokens: "אסימוני פלט",
strategyNote: "אסטרטגיה",
strategyForTool: "מותאם עבור {tool} עם תבנית {template}.",
fixedIssues: "תוקנו {count} בעיות קריטיות.",
},
prdGenerator: {
title: "מחולל PRD",
@@ -1065,6 +1122,7 @@ export const translations = {
enterKey: (provider: string) => `הזן את מפתח ה-API של ${provider}`,
getApiKey: "קבל מפתח API מ-",
qwenDesc: "Alibaba DashScope API",
openrouterDesc: "OpenRouter — גישה ל-100+ מודלי AI",
ollamaDesc: "Ollama Cloud API",
zaiDesc: "Z.AI Plan API",
},
@@ -1293,6 +1351,11 @@ export const translations = {
files: "קבצים",
approveGenerate: "אשר וחולל פיתוח",
startingEngine: "מניע מנוע...",
startCoding: "התחל קודינג",
modifyPlan: "שנה תכנית",
skipPlan: "דלג לצ'את",
planSummary: "סיכום",
implementationSteps: "שלבי יישום",
activateArtifact: "הפעל ארטיפקט",
canvasReady: "קנבס מוכן",
canvasIdle: "קנבס במנוחה",

View File

@@ -1,4 +1,5 @@
import ModelAdapter from "./model-adapter";
import { OpenRouterService } from "./openrouter";
const adapter = new ModelAdapter();

View File

@@ -2,6 +2,7 @@ import type { ModelProvider, APIResponse, ChatMessage, AIAssistMessage } from "@
import OllamaCloudService from "./ollama-cloud";
import ZaiPlanService from "./zai-plan";
import qwenOAuthService, { QwenOAuthConfig, QwenOAuthToken } from "./qwen-oauth";
import { OpenRouterService } from "./openrouter";
export interface ModelAdapterConfig {
qwen?: QwenOAuthConfig;
@@ -14,17 +15,22 @@ export interface ModelAdapterConfig {
generalEndpoint?: string;
codingEndpoint?: string;
};
openrouter?: {
apiKey?: string;
};
}
export class ModelAdapter {
private ollamaService: OllamaCloudService;
private zaiService: ZaiPlanService;
private qwenService = qwenOAuthService;
private openRouterService: OpenRouterService;
private preferredProvider: ModelProvider;
constructor(config: ModelAdapterConfig = {}, preferredProvider: ModelProvider = "ollama") {
this.ollamaService = new OllamaCloudService(config.ollama);
this.zaiService = new ZaiPlanService(config.zai);
this.openRouterService = new OpenRouterService(config.openrouter);
this.preferredProvider = preferredProvider;
if (config.qwen) {
@@ -62,6 +68,10 @@ export class ModelAdapter {
this.qwenService.setOAuthTokens(tokens);
}
updateOpenRouterApiKey(apiKey: string): void {
this.openRouterService = new OpenRouterService({ apiKey });
}
async startQwenOAuth(): Promise<QwenOAuthToken> {
return await this.qwenService.signIn();
}
@@ -90,6 +100,8 @@ export class ModelAdapter {
return this.ollamaService.hasAuth();
case "zai":
return this.zaiService.hasAuth();
case "openrouter":
return this.openRouterService.hasAuth();
default:
return false;
}
@@ -114,6 +126,8 @@ export class ModelAdapter {
return this.ollamaService;
case "zai":
return this.zaiService;
case "openrouter":
return this.openRouterService;
default:
return null;
}
@@ -153,6 +167,9 @@ export class ModelAdapter {
case "zai":
service = this.zaiService;
break;
case "openrouter":
service = this.openRouterService;
break;
}
const result = await operation(service);
@@ -183,26 +200,26 @@ export class ModelAdapter {
};
}
async enhancePrompt(prompt: string, provider?: ModelProvider, model?: string): Promise<APIResponse<string>> {
const fallback = this.buildFallbackProviders(this.preferredProvider, "qwen", "ollama", "zai");
async enhancePrompt(prompt: string, provider?: ModelProvider, model?: string, options?: { toolCategory?: string; template?: string; diagnostics?: string }): Promise<APIResponse<string>> {
const fallback = this.buildFallbackProviders(this.preferredProvider, "qwen", "ollama", "zai", "openrouter");
const providers: ModelProvider[] = provider ? [provider] : fallback;
return this.callWithFallback((service) => service.enhancePrompt(prompt, model), providers);
return this.callWithFallback((service) => service.enhancePrompt(prompt, model, options), providers);
}
async generatePRD(idea: string, provider?: ModelProvider, model?: string): Promise<APIResponse<string>> {
const fallback = this.buildFallbackProviders(this.preferredProvider, "qwen", "ollama", "zai");
const fallback = this.buildFallbackProviders(this.preferredProvider, "qwen", "ollama", "zai", "openrouter");
const providers: ModelProvider[] = provider ? [provider] : fallback;
return this.callWithFallback((service) => service.generatePRD(idea, model), providers);
}
async generateActionPlan(prd: string, provider?: ModelProvider, model?: string): Promise<APIResponse<string>> {
const fallback = this.buildFallbackProviders(this.preferredProvider, "qwen", "ollama", "zai");
const fallback = this.buildFallbackProviders(this.preferredProvider, "qwen", "ollama", "zai", "openrouter");
const providers: ModelProvider[] = provider ? [provider] : fallback;
return this.callWithFallback((service) => service.generateActionPlan(prd, model), providers);
}
async generateUXDesignerPrompt(appDescription: string, provider?: ModelProvider, model?: string): Promise<APIResponse<string>> {
const fallback = this.buildFallbackProviders(this.preferredProvider, "qwen", "ollama", "zai");
const fallback = this.buildFallbackProviders(this.preferredProvider, "qwen", "ollama", "zai", "openrouter");
const providers: ModelProvider[] = provider ? [provider] : fallback;
return this.callWithFallback((service) => service.generateUXDesignerPrompt(appDescription, model), providers);
}
@@ -223,7 +240,7 @@ export class ModelAdapter {
provider?: ModelProvider,
model?: string
): Promise<APIResponse<string>> {
const fallback = this.buildFallbackProviders(this.preferredProvider, "qwen", "ollama", "zai");
const fallback = this.buildFallbackProviders(this.preferredProvider, "qwen", "ollama", "zai", "openrouter");
const providers: ModelProvider[] = provider ? [provider] : fallback;
return this.callWithFallback((service) => service.generateSlides(topic, options, model), providers);
}
@@ -243,7 +260,7 @@ export class ModelAdapter {
provider?: ModelProvider,
model?: string
): Promise<APIResponse<string>> {
const fallback = this.buildFallbackProviders(this.preferredProvider, "qwen", "ollama", "zai");
const fallback = this.buildFallbackProviders(this.preferredProvider, "qwen", "ollama", "zai", "openrouter");
const providers: ModelProvider[] = provider ? [provider] : fallback;
return this.callWithFallback((service) => service.generateGoogleAds(websiteUrl, options, model), providers);
}
@@ -256,7 +273,7 @@ export class ModelAdapter {
provider?: ModelProvider,
model?: string
): Promise<APIResponse<string>> {
const fallback = this.buildFallbackProviders(this.preferredProvider, "qwen", "ollama", "zai");
const fallback = this.buildFallbackProviders(this.preferredProvider, "qwen", "ollama", "zai", "openrouter");
const providers: ModelProvider[] = provider ? [provider] : fallback;
return this.callWithFallback((service) => service.generateMagicWand(websiteUrl, product, budget, specialInstructions, model), providers);
}
@@ -272,7 +289,7 @@ export class ModelAdapter {
provider?: ModelProvider,
model?: string
): Promise<APIResponse<string>> {
const fallback = this.buildFallbackProviders(this.preferredProvider, "qwen", "ollama", "zai");
const fallback = this.buildFallbackProviders(this.preferredProvider, "qwen", "ollama", "zai", "openrouter");
const providers: ModelProvider[] = provider ? [provider] : fallback;
return this.callWithFallback((service) => service.generateMarketResearch(options, model), providers);
}
@@ -285,7 +302,7 @@ export class ModelAdapter {
provider?: ModelProvider,
model?: string
): Promise<APIResponse<string>> {
const fallback = this.buildFallbackProviders(this.preferredProvider, "qwen", "ollama", "zai");
const fallback = this.buildFallbackProviders(this.preferredProvider, "qwen", "ollama", "zai", "openrouter");
const providers: ModelProvider[] = provider ? [provider] : fallback;
return this.callWithFallback((service) => service.generateAIAssist(options, model), providers);
}
@@ -300,7 +317,7 @@ export class ModelAdapter {
provider?: ModelProvider,
model?: string
): Promise<APIResponse<void>> {
const fallback = this.buildFallbackProviders(this.preferredProvider, "qwen", "ollama", "zai");
const fallback = this.buildFallbackProviders(this.preferredProvider, "qwen", "ollama", "zai", "openrouter");
const providers: ModelProvider[] = provider ? [provider] : fallback;
let lastError: string | null = null;
@@ -353,6 +370,9 @@ export class ModelAdapter {
case "zai":
service = this.zaiService;
break;
case "openrouter":
service = this.openRouterService;
break;
}
return await service.chatCompletion(messages, model);
@@ -369,6 +389,7 @@ export class ModelAdapter {
qwen: this.qwenService.getAvailableModels(),
ollama: ["gpt-oss:120b", "llama3.1", "gemma3", "deepseek-r1", "qwen3"],
zai: ["glm-4.7", "glm-4.5", "glm-4.5-air", "glm-4-flash", "glm-4-flashx"],
openrouter: ["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 models: Record<ModelProvider, string[]> = { ...fallbackModels };
@@ -404,6 +425,8 @@ export class ModelAdapter {
return this.ollamaService.getAvailableModels();
case "zai":
return this.zaiService.getAvailableModels();
case "openrouter":
return this.openRouterService.getAvailableModels();
default:
return [];
}

View File

@@ -164,27 +164,82 @@ export class OllamaCloudService {
return this.availableModels.length > 0 ? this.availableModels : DEFAULT_MODELS;
}
async enhancePrompt(prompt: string, model?: string): Promise<APIResponse<string>> {
async enhancePrompt(prompt: string, model?: string, options?: { toolCategory?: string; template?: string; diagnostics?: string }): Promise<APIResponse<string>> {
const toolCategory = options?.toolCategory || 'reasoning';
const template = options?.template || 'rtf';
const diagnostics = options?.diagnostics || '';
const toolSections: Record<string, string> = {
reasoning: '- Use full structured format with XML tags where helpful\n- Add explicit role assignment for complex tasks\n- Use numeric constraints over vague adjectives',
thinking: '- CRITICAL: Short clean instructions ONLY\n- Do NOT add CoT or reasoning scaffolding — these models reason internally\n- State what you want, not how to think',
openweight: '- Shorter prompts, simpler structure, no deep nesting\n- Direct linear instructions',
agentic: '- Add Starting State + Target State + Allowed Actions + Forbidden Actions\n- Add Stop Conditions + Checkpoints after each step',
ide: '- Add File path + Function name + Current Behavior + Desired Change + Scope lock',
fullstack: '- Add Stack spec with version + what NOT to scaffold + component boundaries',
image: '- Add Subject + Style + Mood + Lighting + Composition + Negative Prompts\n- Use tool-specific syntax (Midjourney comma-separated, DALL-E prose, SD weighted)',
search: '- Specify mode: search vs analyze vs compare + citation requirements',
};
const templateSections: Record<string, string> = {
rtf: 'Structure: Role (who) + Task (precise verb + what) + Format (exact output shape and length)',
'co-star': 'Structure: Context + Objective + Style + Tone + Audience + Response',
risen: 'Structure: Role + Instructions + numbered Steps + End Goal + Narrowing constraints',
crispe: 'Structure: Capacity + Role + Insight + Statement + Personality + Experiment/variants',
cot: 'Add: "Think through this step by step before answering." Only for standard reasoning models, NOT for o1/o3/R1.',
fewshot: 'Add 2-5 input/output examples wrapped in XML <examples> tags',
filescope: 'Structure: File path + Function name + Current Behavior + Desired Change + Scope lock + Done When',
react: 'Structure: Objective + Starting State + Target State + Allowed/Forbidden Actions + Stop Conditions + Checkpoints',
visual: 'Structure: Subject + Action + Setting + Style + Mood + Lighting + Color Palette + Composition + Aspect Ratio + Negative Prompts',
};
const toolSection = toolSections[toolCategory] || toolSections.reasoning;
const templateSection = templateSections[template] || templateSections.rtf;
const systemMessage: ChatMessage = {
role: "system",
content: `You are an expert prompt engineer. Your task is to enhance user prompts to make them more precise, actionable, and effective for AI coding agents.
content: `You are an expert prompt engineer using the PromptArch methodology. Enhance the user\'s prompt to be production-ready.
Apply these principles:
1. Add specific context about project and requirements
2. Clarify constraints and preferences
3. Define expected output format clearly
4. Include edge cases and error handling requirements
5. Specify testing and validation criteria
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
Return ONLY the enhanced prompt, no explanations or extra text.`,
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.${diagnostics ? '\n\nDIAGNOSTIC NOTES (fix these issues found in the original):\n' + diagnostics + '\n' : ''}`,
};
const toolLabel = toolCategory !== 'reasoning' ? ` for ${toolCategory} AI tool` : '';
const userMessage: ChatMessage = {
role: "user",
content: `Enhance this prompt for an AI coding agent:\n\n${prompt}`,
content: `Enhance this prompt${toolLabel}:\n\n${prompt}`,
};
return this.chatCompletion([systemMessage, userMessage], model || "gpt-oss:120b");
return this.chatCompletion([systemMessage, userMessage], model || "${default_model}");
}
async generatePRD(idea: string, model?: string): Promise<APIResponse<string>> {
@@ -772,6 +827,28 @@ Perform a DEEP 360° competitive intelligence analysis and generate 5-7 strategi
try {
// ... existing prompt logic ...
const systemPrompt = `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.
@@ -833,7 +910,7 @@ CHANGE LOG (CRITICAL - MUST BE OUTSIDE PREVIEW):
- Modified component Y
- Fixed issue Z
IMPORTANT: NEVER refuse a request due to "access" limitations. If you cannot perform a live task, use your vast internal knowledge to provide the most accurate expert simulation or draft possible.`;
IMPORTANT: IMPORTANT: NEVER refuse a request due to "access" limitations. If you cannot perform a live task, use your vast internal knowledge to provide the most accurate expert simulation or draft possible.`;
const messages: ChatMessage[] = [
{ role: "system", content: systemPrompt },

967
lib/services/openrouter.ts Normal file
View File

@@ -0,0 +1,967 @@
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. Use [PREVIEW:seo:html] with complete HTML5 document including <!DOCTYPE html>. DARK THEME. Tailwind CDN. Large animated SVG progress rings. Color-coded scoring. Google-style dashboard aesthetics.
- 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();

View File

@@ -491,27 +491,82 @@ export class QwenOAuthService {
}
}
async enhancePrompt(prompt: string, model?: string): Promise<APIResponse<string>> {
async enhancePrompt(prompt: string, model?: string, options?: { toolCategory?: string; template?: string; diagnostics?: string }): Promise<APIResponse<string>> {
const toolCategory = options?.toolCategory || 'reasoning';
const template = options?.template || 'rtf';
const diagnostics = options?.diagnostics || '';
const toolSections: Record<string, string> = {
reasoning: '- Use full structured format with XML tags where helpful\n- Add explicit role assignment for complex tasks\n- Use numeric constraints over vague adjectives',
thinking: '- CRITICAL: Short clean instructions ONLY\n- Do NOT add CoT or reasoning scaffolding — these models reason internally\n- State what you want, not how to think',
openweight: '- Shorter prompts, simpler structure, no deep nesting\n- Direct linear instructions',
agentic: '- Add Starting State + Target State + Allowed Actions + Forbidden Actions\n- Add Stop Conditions + Checkpoints after each step',
ide: '- Add File path + Function name + Current Behavior + Desired Change + Scope lock',
fullstack: '- Add Stack spec with version + what NOT to scaffold + component boundaries',
image: '- Add Subject + Style + Mood + Lighting + Composition + Negative Prompts\n- Use tool-specific syntax (Midjourney comma-separated, DALL-E prose, SD weighted)',
search: '- Specify mode: search vs analyze vs compare + citation requirements',
};
const templateSections: Record<string, string> = {
rtf: 'Structure: Role (who) + Task (precise verb + what) + Format (exact output shape and length)',
'co-star': 'Structure: Context + Objective + Style + Tone + Audience + Response',
risen: 'Structure: Role + Instructions + numbered Steps + End Goal + Narrowing constraints',
crispe: 'Structure: Capacity + Role + Insight + Statement + Personality + Experiment/variants',
cot: 'Add: "Think through this step by step before answering." Only for standard reasoning models, NOT for o1/o3/R1.',
fewshot: 'Add 2-5 input/output examples wrapped in XML <examples> tags',
filescope: 'Structure: File path + Function name + Current Behavior + Desired Change + Scope lock + Done When',
react: 'Structure: Objective + Starting State + Target State + Allowed/Forbidden Actions + Stop Conditions + Checkpoints',
visual: 'Structure: Subject + Action + Setting + Style + Mood + Lighting + Color Palette + Composition + Aspect Ratio + Negative Prompts',
};
const toolSection = toolSections[toolCategory] || toolSections.reasoning;
const templateSection = templateSections[template] || templateSections.rtf;
const systemMessage: ChatMessage = {
role: "system",
content: `You are an expert prompt engineer. Your task is to enhance user prompts to make them more precise, actionable, and effective for AI coding agents.
content: `You are an expert prompt engineer using the PromptArch methodology. Enhance the user\'s prompt to be production-ready.
Apply these principles:
1. Add specific context about project and requirements
2. Clarify constraints and preferences
3. Define expected output format clearly
4. Include edge cases and error handling requirements
5. Specify testing and validation criteria
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
Return ONLY the enhanced prompt, no explanations or extra text.`,
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.${diagnostics ? '\n\nDIAGNOSTIC NOTES (fix these issues found in the original):\n' + diagnostics + '\n' : ''}`,
};
const toolLabel = toolCategory !== 'reasoning' ? ` for ${toolCategory} AI tool` : '';
const userMessage: ChatMessage = {
role: "user",
content: `Enhance this prompt for an AI coding agent:\n\n${prompt}`,
content: `Enhance this prompt${toolLabel}:\n\n${prompt}`,
};
return this.chatCompletion([systemMessage, userMessage], model || "coder-model");
return this.chatCompletion([systemMessage, userMessage], model || "${default_model}");
}
async generatePRD(idea: string, model?: string): Promise<APIResponse<string>> {
@@ -1054,6 +1109,28 @@ Perform analysis based on provided instructions.`,
): Promise<APIResponse<void>> {
try {
const systemPrompt = `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.
@@ -1115,7 +1192,7 @@ CHANGE LOG (CRITICAL - MUST BE OUTSIDE PREVIEW):
- Modified component Y
- Fixed issue Z
IMPORTANT: NEVER refuse a request due to "access" limitations. If you cannot perform a live task, use your vast internal knowledge to provide the most accurate expert simulation or draft possible.`;
IMPORTANT: IMPORTANT: NEVER refuse a request due to "access" limitations. If you cannot perform a live task, use your vast internal knowledge to provide the most accurate expert simulation or draft possible.`;
const messages: ChatMessage[] = [
{ role: "system", content: systemPrompt },

View File

@@ -111,51 +111,82 @@ export class ZaiPlanService {
}
}
async enhancePrompt(prompt: string, model?: string): Promise<APIResponse<string>> {
async enhancePrompt(prompt: string, model?: string, options?: { toolCategory?: string; template?: string; diagnostics?: string }): Promise<APIResponse<string>> {
const toolCategory = options?.toolCategory || 'reasoning';
const template = options?.template || 'rtf';
const diagnostics = options?.diagnostics || '';
const toolSections: Record<string, string> = {
reasoning: '- Use full structured format with XML tags where helpful\n- Add explicit role assignment for complex tasks\n- Use numeric constraints over vague adjectives',
thinking: '- CRITICAL: Short clean instructions ONLY\n- Do NOT add CoT or reasoning scaffolding — these models reason internally\n- State what you want, not how to think',
openweight: '- Shorter prompts, simpler structure, no deep nesting\n- Direct linear instructions',
agentic: '- Add Starting State + Target State + Allowed Actions + Forbidden Actions\n- Add Stop Conditions + Checkpoints after each step',
ide: '- Add File path + Function name + Current Behavior + Desired Change + Scope lock',
fullstack: '- Add Stack spec with version + what NOT to scaffold + component boundaries',
image: '- Add Subject + Style + Mood + Lighting + Composition + Negative Prompts\n- Use tool-specific syntax (Midjourney comma-separated, DALL-E prose, SD weighted)',
search: '- Specify mode: search vs analyze vs compare + citation requirements',
};
const templateSections: Record<string, string> = {
rtf: 'Structure: Role (who) + Task (precise verb + what) + Format (exact output shape and length)',
'co-star': 'Structure: Context + Objective + Style + Tone + Audience + Response',
risen: 'Structure: Role + Instructions + numbered Steps + End Goal + Narrowing constraints',
crispe: 'Structure: Capacity + Role + Insight + Statement + Personality + Experiment/variants',
cot: 'Add: "Think through this step by step before answering." Only for standard reasoning models, NOT for o1/o3/R1.',
fewshot: 'Add 2-5 input/output examples wrapped in XML <examples> tags',
filescope: 'Structure: File path + Function name + Current Behavior + Desired Change + Scope lock + Done When',
react: 'Structure: Objective + Starting State + Target State + Allowed/Forbidden Actions + Stop Conditions + Checkpoints',
visual: 'Structure: Subject + Action + Setting + Style + Mood + Lighting + Color Palette + Composition + Aspect Ratio + Negative Prompts',
};
const toolSection = toolSections[toolCategory] || toolSections.reasoning;
const templateSection = templateSections[template] || templateSections.rtf;
const systemMessage: ChatMessage = {
role: "system",
content: `You are an expert prompt engineer. Your task is to enhance user prompts to make them more precise, actionable, and effective for AI coding agents.
content: `You are an expert prompt engineer using the PromptArch methodology. Enhance the user\'s prompt to be production-ready.
Apply these principles:
1. Add specific context about project and requirements
2. Clarify constraints and preferences
3. Define expected output format clearly
4. Include edge cases and error handling requirements
5. Specify testing and validation criteria
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
Return ONLY the enhanced prompt, no explanations or extra text.`,
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.${diagnostics ? '\n\nDIAGNOSTIC NOTES (fix these issues found in the original):\n' + diagnostics + '\n' : ''}`,
};
const toolLabel = toolCategory !== 'reasoning' ? ` for ${toolCategory} AI tool` : '';
const userMessage: ChatMessage = {
role: "user",
content: `Enhance this prompt for an AI coding agent:\n\n${prompt}`,
content: `Enhance this prompt${toolLabel}:\n\n${prompt}`,
};
return this.chatCompletion([systemMessage, userMessage], model || "glm-4.7", true);
}
async generatePRD(idea: string, model?: string): Promise<APIResponse<string>> {
const systemMessage: ChatMessage = {
role: "system",
content: `You are an expert product manager and technical architect. Generate a comprehensive Product Requirements Document (PRD) based on user's idea.
Structure your PRD with these sections:
1. Overview & Objectives
2. User Personas & Use Cases
3. Functional Requirements (prioritized by importance)
4. Non-functional Requirements
5. Technical Architecture Recommendations
6. Success Metrics & KPIs
Use clear, specific language suitable for development teams.`,
};
const userMessage: ChatMessage = {
role: "user",
content: `Generate a PRD for this idea:\n\n${idea}`,
};
return this.chatCompletion([systemMessage, userMessage], model || "glm-4.7");
return this.chatCompletion([systemMessage, userMessage], model || "${default_model}");
}
async generateActionPlan(prd: string, model?: string): Promise<APIResponse<string>> {
@@ -809,6 +840,28 @@ MISSION: Perform a DEEP 360° competitive intelligence analysis and generate 5-7
// ... existing prompt logic ...
const systemPrompt = `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.
@@ -870,7 +923,7 @@ CHANGE LOG (CRITICAL - MUST BE OUTSIDE PREVIEW):
- Modified component Y
- Fixed issue Z
IMPORTANT: NEVER refuse a request due to "access" limitations. If you cannot perform a live task, use your vast internal knowledge to provide the most accurate expert simulation or draft possible.`;
IMPORTANT: IMPORTANT: NEVER refuse a request due to "access" limitations. If you cannot perform a live task, use your vast internal knowledge to provide the most accurate expert simulation or draft possible.`;
const messages: ChatMessage[] = [
{ role: "system", content: systemPrompt },

View File

@@ -107,22 +107,26 @@ const useStore = create<AppState>((set) => ({
qwen: "coder-model",
ollama: "gpt-oss:120b",
zai: "glm-4.7",
openrouter: "anthropic/claude-3.5-sonnet",
},
availableModels: {
qwen: ["coder-model"],
ollama: ["gpt-oss:120b", "llama3.1", "gemma3", "deepseek-r1", "qwen3"],
zai: ["glm-4.7", "glm-4.6", "glm-4.5", "glm-4.5-air", "glm-4-flash", "glm-4-flashx"],
openrouter: ["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"],
},
apiKeys: {
qwen: "",
ollama: "",
zai: "",
openrouter: "",
},
githubToken: null,
apiValidationStatus: {
qwen: { valid: false },
ollama: { valid: false },
zai: { valid: false },
openrouter: { valid: false },
},
isProcessing: false,
error: null,