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:
@@ -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 [];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user