Remove Qwen provider from application

- Remove Qwen from ModelProvider type
- Remove Qwen OAuth service and device flow
- Update store to only support Ollama and Z.AI
- Remove Qwen from all component provider selectors
- Delete qwen-oauth.ts service file
This commit is contained in:
Gemini AI
2025-12-25 22:11:12 +04:00
Unverified
parent 0c828461e4
commit 1e175ae514
7 changed files with 73 additions and 237 deletions

View File

@@ -1,6 +1,6 @@
"use client";
import { useState, useEffect } from "react";
import { useState } from "react";
import Sidebar from "@/components/Sidebar";
import type { View } from "@/components/Sidebar";
import PromptEnhancer from "@/components/PromptEnhancer";
@@ -8,59 +8,9 @@ import PRDGenerator from "@/components/PRDGenerator";
import ActionPlanGenerator from "@/components/ActionPlanGenerator";
import HistoryPanel from "@/components/HistoryPanel";
import SettingsPanel from "@/components/SettingsPanel";
import useStore from "@/lib/store";
import modelAdapter from "@/lib/services/adapter-instance";
export default function Home() {
const [currentView, setCurrentView] = useState<View>("enhance");
const { setQwenTokens, setApiKey } = useStore();
useEffect(() => {
// Handle OAuth callback
if (typeof window !== "undefined") {
const urlParams = new URLSearchParams(window.location.search);
const code = urlParams.get("code");
if (code) {
// In a real app, you would exchange the code for tokens here
// Since we don't have a backend or real client secret, we'll simulate it
console.log("OAuth code received:", code);
// Mock token exchange
const mockAccessToken = "mock_access_token_" + Math.random().toString(36).substr(2, 9);
const tokens = {
accessToken: mockAccessToken,
expiresAt: Date.now() + 3600 * 1000, // 1 hour
};
setQwenTokens(tokens);
modelAdapter.setQwenOAuthTokens(tokens.accessToken, undefined, 3600);
// Save to localStorage
localStorage.setItem("promptarch-qwen-tokens", JSON.stringify(tokens));
// Clear the code from URL
window.history.replaceState({}, document.title, window.location.pathname);
// Switch to settings to show success (optional)
setCurrentView("settings");
}
// Load tokens from localStorage on init
const savedTokens = localStorage.getItem("promptarch-qwen-tokens");
if (savedTokens) {
try {
const tokens = JSON.parse(savedTokens);
if (tokens.expiresAt > Date.now()) {
setQwenTokens(tokens);
modelAdapter.setQwenOAuthTokens(tokens.accessToken, tokens.refreshToken, (tokens.expiresAt - Date.now()) / 1000);
}
} catch (e) {
console.error("Failed to load Qwen tokens:", e);
}
}
}
}, []);
const renderContent = () => {
switch (currentView) {
@@ -80,12 +30,10 @@ export default function Home() {
};
return (
<div className="flex min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 dark:from-slate-900 dark:to-slate-800">
<div className="flex h-screen bg-background">
<Sidebar currentView={currentView} onViewChange={setCurrentView} />
<main className="flex-1 overflow-auto p-8">
<div className="mx-auto max-w-7xl">
{renderContent()}
</div>
<main className="flex-1 overflow-auto">
<div className="container mx-auto p-6">{renderContent()}</div>
</main>
</div>
);

View File

@@ -40,7 +40,6 @@ export default function ActionPlanGenerator() {
if (saved) {
try {
const keys = JSON.parse(saved);
if (keys.qwen) modelAdapter.updateQwenApiKey(keys.qwen);
if (keys.ollama) modelAdapter.updateOllamaApiKey(keys.ollama);
if (keys.zai) modelAdapter.updateZaiApiKey(keys.zai);
} catch (e) {
@@ -133,7 +132,7 @@ export default function ActionPlanGenerator() {
<div className="space-y-2">
<label className="text-sm font-medium">AI Provider</label>
<div className="flex gap-2">
{(["qwen", "ollama", "zai"] as const).map((provider) => (
{(["ollama", "zai"] as const).map((provider) => (
<Button
key={provider}
variant={selectedProvider === provider ? "default" : "outline"}
@@ -141,7 +140,7 @@ export default function ActionPlanGenerator() {
onClick={() => setSelectedProvider(provider)}
className="capitalize"
>
{provider === "qwen" ? "Qwen" : provider === "ollama" ? "Ollama" : "Z.AI"}
{provider === "ollama" ? "Ollama" : "Z.AI"}
</Button>
))}
</div>

View File

@@ -47,7 +47,6 @@ export default function PRDGenerator() {
if (saved) {
try {
const keys = JSON.parse(saved);
if (keys.qwen) modelAdapter.updateQwenApiKey(keys.qwen);
if (keys.ollama) modelAdapter.updateOllamaApiKey(keys.ollama);
if (keys.zai) modelAdapter.updateZaiApiKey(keys.zai);
} catch (e) {
@@ -147,7 +146,7 @@ export default function PRDGenerator() {
<div className="space-y-2">
<label className="text-sm font-medium">AI Provider</label>
<div className="flex gap-2">
{(["qwen", "ollama", "zai"] as const).map((provider) => (
{(["ollama", "zai"] as const).map((provider) => (
<Button
key={provider}
variant={selectedProvider === provider ? "default" : "outline"}
@@ -155,7 +154,7 @@ export default function PRDGenerator() {
onClick={() => setSelectedProvider(provider)}
className="capitalize"
>
{provider === "qwen" ? "Qwen" : provider === "ollama" ? "Ollama" : "Z.AI"}
{provider === "ollama" ? "Ollama" : "Z.AI"}
</Button>
))}
</div>

View File

@@ -40,7 +40,6 @@ export default function PromptEnhancer() {
if (saved) {
try {
const keys = JSON.parse(saved);
if (keys.qwen) modelAdapter.updateQwenApiKey(keys.qwen);
if (keys.ollama) modelAdapter.updateOllamaApiKey(keys.ollama);
if (keys.zai) modelAdapter.updateZaiApiKey(keys.zai);
} catch (e) {
@@ -124,7 +123,7 @@ export default function PromptEnhancer() {
<div className="space-y-2">
<label className="text-sm font-medium">AI Provider</label>
<div className="flex flex-wrap gap-2">
{(["qwen", "ollama", "zai"] as const).map((provider) => (
{(["ollama", "zai"] as const).map((provider) => (
<Button
key={provider}
variant={selectedProvider === provider ? "default" : "outline"}
@@ -135,7 +134,7 @@ export default function PromptEnhancer() {
selectedProvider === provider && "bg-primary text-primary-foreground"
)}
>
{provider === "qwen" ? "Qwen" : provider === "ollama" ? "Ollama" : "Z.AI"}
{provider === "ollama" ? "Ollama" : "Z.AI"}
</Button>
))}
</div>

View File

@@ -3,15 +3,19 @@
import { useState, useEffect } from "react";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Textarea } from "@/components/ui/textarea";
import { Input } from "@/components/ui/input";
import useStore from "@/lib/store";
import modelAdapter from "@/lib/services/adapter-instance";
import { Save, Key, Server, Eye, EyeOff } from "lucide-react";
import { cn } from "@/lib/utils";
export default function SettingsPanel() {
const { apiKeys, setApiKey, selectedProvider, setSelectedProvider, qwenTokens, setQwenTokens } = useStore();
const {
apiKeys,
setApiKey,
selectedProvider,
setSelectedProvider
} = useStore();
const [showApiKey, setShowApiKey] = useState<Record<string, boolean>>({});
const handleSave = () => {
@@ -27,10 +31,6 @@ export default function SettingsPanel() {
if (saved) {
try {
const keys = JSON.parse(saved);
if (keys.qwen) {
setApiKey("qwen", keys.qwen);
modelAdapter.updateQwenApiKey(keys.qwen);
}
if (keys.ollama) {
setApiKey("ollama", keys.ollama);
modelAdapter.updateOllamaApiKey(keys.ollama);
@@ -47,12 +47,9 @@ export default function SettingsPanel() {
};
const handleApiKeyChange = (provider: string, value: string) => {
setApiKey(provider as "qwen" | "ollama" | "zai", value);
setApiKey(provider as "ollama" | "zai", value);
switch (provider) {
case "qwen":
modelAdapter.updateQwenApiKey(value);
break;
case "ollama":
modelAdapter.updateOllamaApiKey(value);
break;
@@ -79,69 +76,6 @@ export default function SettingsPanel() {
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<div className="space-y-2">
<label className="flex items-center gap-2 text-sm font-medium">
<Server className="h-4 w-4" />
Qwen Code API Key
</label>
<div className="relative">
<Input
type={showApiKey.qwen ? "text" : "password"}
placeholder="Enter your Qwen API key"
value={apiKeys.qwen || ""}
onChange={(e) => handleApiKeyChange("qwen", e.target.value)}
className="font-mono text-sm"
/>
<Button
type="button"
variant="ghost"
size="icon"
className="absolute right-0 top-0 h-full"
onClick={() => setShowApiKey((prev) => ({ ...prev, qwen: !prev.qwen }))}
>
{showApiKey.qwen ? (
<EyeOff className="h-4 w-4" />
) : (
<Eye className="h-4 w-4" />
)}
</Button>
</div>
<div className="flex items-center gap-4">
<p className="text-xs text-muted-foreground flex-1">
Get API key from{" "}
<a
href="https://help.aliyun.com/zh/dashscope/"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
>
Alibaba DashScope
</a>
</p>
<Button
variant={qwenTokens ? "secondary" : "outline"}
size="sm"
className="h-8"
onClick={() => {
if (qwenTokens) {
setQwenTokens(undefined as any);
localStorage.removeItem("promptarch-qwen-tokens");
modelAdapter.updateQwenApiKey(apiKeys.qwen || "");
} else {
window.location.href = modelAdapter.getQwenAuthUrl();
}
}}
>
{qwenTokens ? "Logout from Qwen" : "Login with Qwen (OAuth)"}
</Button>
</div>
{qwenTokens && (
<p className="text-[10px] text-green-600 dark:text-green-400 font-medium">
Authenticated via OAuth (Expires: {new Date(qwenTokens.expiresAt || 0).toLocaleString()})
</p>
)}
</div>
<div className="space-y-2">
<label className="flex items-center gap-2 text-sm font-medium">
<Server className="h-4 w-4" />
@@ -238,7 +172,7 @@ export default function SettingsPanel() {
</CardHeader>
<CardContent className="space-y-4">
<div className="grid gap-3">
{(["qwen", "ollama", "zai"] as const).map((provider) => (
{(["ollama", "zai"] as const).map((provider) => (
<button
key={provider}
onClick={() => setSelectedProvider(provider)}
@@ -254,7 +188,6 @@ export default function SettingsPanel() {
<div className="flex-1">
<h3 className="font-medium capitalize">{provider}</h3>
<p className="text-sm text-muted-foreground">
{provider === "qwen" && "Alibaba DashScope API"}
{provider === "ollama" && "Ollama Cloud API"}
{provider === "zai" && "Z.AI Plan API"}
</p>
@@ -278,7 +211,7 @@ export default function SettingsPanel() {
<CardContent>
<div className="rounded-md border bg-muted/30 p-4">
<p className="text-sm">
All API keys are stored locally in your browser. Your prompts are sent directly to the selected AI provider and are not stored by PromptArch.
All API keys are stored locally in your browser. Your prompts are sent directly to selected AI provider and are not stored by PromptArch.
</p>
</div>
</CardContent>

View File

@@ -10,11 +10,6 @@ interface AppState {
selectedModels: Record<ModelProvider, string>;
availableModels: Record<ModelProvider, string[]>;
apiKeys: Record<ModelProvider, string>;
qwenTokens?: {
accessToken: string;
refreshToken?: string;
expiresAt?: number;
};
isProcessing: boolean;
error: string | null;
history: {
@@ -31,7 +26,6 @@ interface AppState {
setSelectedModel: (provider: ModelProvider, model: string) => void;
setAvailableModels: (provider: ModelProvider, models: string[]) => void;
setApiKey: (provider: ModelProvider, key: string) => void;
setQwenTokens: (tokens: { accessToken: string; refreshToken?: string; expiresAt?: number }) => void;
setProcessing: (processing: boolean) => void;
setError: (error: string | null) => void;
addToHistory: (prompt: string) => void;
@@ -44,26 +38,22 @@ const useStore = create<AppState>((set) => ({
enhancedPrompt: null,
prd: null,
actionPlan: null,
selectedProvider: "qwen",
selectedProvider: "ollama",
selectedModels: {
qwen: "qwen-coder-plus",
ollama: "gpt-oss:120b",
zai: "glm-4.7",
},
availableModels: {
qwen: ["qwen-coder-plus", "qwen-coder-turbo", "qwen-coder-lite"],
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"],
},
apiKeys: {
qwen: "",
ollama: "",
zai: "",
},
isProcessing: false,
error: null,
history: [],
setCurrentPrompt: (prompt) => set({ currentPrompt: prompt }),
setEnhancedPrompt: (enhanced) => set({ enhancedPrompt: enhanced }),
setPRD: (prd) => set({ prd }),
@@ -81,7 +71,6 @@ const useStore = create<AppState>((set) => ({
set((state) => ({
apiKeys: { ...state.apiKeys, [provider]: key },
})),
setQwenTokens: (tokens) => set({ qwenTokens: tokens }),
setProcessing: (processing) => set({ isProcessing: processing }),
setError: (error) => set({ error }),
addToHistory: (prompt) =>
@@ -102,6 +91,7 @@ const useStore = create<AppState>((set) => ({
enhancedPrompt: null,
prd: null,
actionPlan: null,
isProcessing: false,
error: null,
}),
}));

View File

@@ -1,84 +1,8 @@
export type ModelProvider = "qwen" | "ollama" | "zai";
export type ModelProvider = "ollama" | "zai";
export interface ModelConfig {
provider: ModelProvider;
model: string;
apiKey?: string;
endpoint?: string;
}
export interface PromptEnhancement {
original: string;
enhanced: string;
quality: number;
intent: string;
patterns: string[];
}
export interface PRD {
id: string;
title: string;
overview: string;
objectives: string[];
userPersonas: UserPersona[];
functionalRequirements: Requirement[];
nonFunctionalRequirements: Requirement[];
technicalArchitecture: string;
successMetrics: string[];
createdAt: Date;
updatedAt: Date;
}
export interface UserPersona {
name: string;
description: string;
goals: string[];
painPoints: string[];
}
export interface Requirement {
id: string;
title: string;
description: string;
priority: "high" | "medium" | "low";
status: "pending" | "in-progress" | "completed";
dependencies?: string[];
}
export interface ActionPlan {
id: string;
prdId: string;
tasks: Task[];
frameworks: FrameworkRecommendation[];
architecture: ArchitectureGuideline;
estimatedDuration: string;
createdAt: Date;
rawContent?: string;
}
export interface Task {
id: string;
title: string;
description: string;
priority: "high" | "medium" | "low";
estimatedHours: number;
dependencies: string[];
status: "pending" | "in-progress" | "completed";
assignee?: string;
}
export interface FrameworkRecommendation {
category: string;
recommendation: string;
rationale: string;
alternatives: string[];
}
export interface ArchitectureGuideline {
pattern: string;
structure: string;
technologies: string[];
bestPractices: string[];
export interface ChatMessage {
role: "system" | "user" | "assistant";
content: string;
}
export interface APIResponse<T> {
@@ -87,7 +11,51 @@ export interface APIResponse<T> {
error?: string;
}
export interface ChatMessage {
role: "system" | "user" | "assistant";
content: string;
export interface PromptEnhancement {
id: string;
originalPrompt: string;
enhancedPrompt: string;
provider: ModelProvider;
model: string;
timestamp: Date;
}
export interface PRD {
id: string;
title: string;
overview: string;
objectives: string[];
userPersonas: string[];
functionalRequirements: string[];
nonFunctionalRequirements: string[];
technicalArchitecture: string;
successMetrics: string[];
createdAt: Date;
updatedAt: Date;
}
export interface ActionPlan {
id: string;
prdId: string;
tasks: {
id: string;
title: string;
description: string;
priority: "high" | "medium" | "low";
dependencies: string[];
estimatedEffort: string;
}[];
frameworks: {
name: string;
reason: string;
}[];
architecture: {
pattern: string;
structure: string;
technologies: string[];
bestPractices: string[];
};
estimatedDuration: string;
createdAt: Date;
rawContent?: string;
}