From cba6cbe68b0640877c6224b347298fb2a1dd44ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=97=B6=E8=B5=B7?= <50489191+esclipse@users.noreply.github.com> Date: Wed, 31 Dec 2025 11:34:57 +0800 Subject: [PATCH] Use Dashscope defaults for creative chat --- app/api/creative-chat/route.ts | 81 +++++++++++++++++++++++++ app/translate/page.tsx | 107 +++++++++++++++++++++++++++++---- 2 files changed, 175 insertions(+), 13 deletions(-) create mode 100644 app/api/creative-chat/route.ts diff --git a/app/api/creative-chat/route.ts b/app/api/creative-chat/route.ts new file mode 100644 index 0000000..09fbd36 --- /dev/null +++ b/app/api/creative-chat/route.ts @@ -0,0 +1,81 @@ +import { NextResponse } from 'next/server'; +import OpenAI from 'openai'; + +const DEFAULT_API_KEY = + process.env.OPENAI_API_KEY || 'sk-2893a75c1cfd407aa601eab503ad918a'; +const DEFAULT_BASE_URL = + process.env.OPENAI_BASE_URL || + 'https://dashscope.aliyuncs.com/compatible-mode/v1'; +const DEFAULT_MODEL = process.env.OPENAI_MODEL || 'qwen3-plus'; +const SYSTEM_PROMPT = + '你是内容创作助手,请根据用户需求输出适合富文本编辑器的 HTML 片段。' + + '输出仅包含正文内容,不要包含 markdown 代码块、标题之外的说明或外层 HTML/Body 标签。'; + +const createClient = (apiKey?: string, baseURL?: string) => + new OpenAI({ + apiKey: apiKey || DEFAULT_API_KEY, + baseURL: baseURL || DEFAULT_BASE_URL, + }); + +export async function POST(request: Request) { + try { + const { messages, settings } = await request.json(); + + if (!Array.isArray(messages) || messages.length === 0) { + return NextResponse.json( + { error: 'Missing chat messages' }, + { status: 400 } + ); + } + + const sanitizedMessages = messages + .filter( + (message) => + message && + (message.role === 'user' || message.role === 'assistant') && + typeof message.content === 'string' + ) + .map((message) => ({ + role: message.role, + content: message.content, + })); + + if (sanitizedMessages.length === 0) { + return NextResponse.json( + { error: 'Invalid chat messages' }, + { status: 400 } + ); + } + + const completion = await createClient( + settings?.apiKey, + settings?.baseURL + ).chat.completions.create({ + model: settings?.model || DEFAULT_MODEL, + messages: [ + { + role: 'system', + content: SYSTEM_PROMPT, + }, + ...sanitizedMessages, + ], + }); + + const assistantMessage = completion.choices[0]?.message?.content || ''; + + return NextResponse.json({ message: assistantMessage }); + } catch (error: any) { + console.error('Creative chat error:', error); + const errorMessage = + error.response?.data?.error?.message || + error.message || + 'Unknown error'; + return NextResponse.json( + { + error: 'Failed to generate creative response', + details: errorMessage, + }, + { status: 500 } + ); + } +} diff --git a/app/translate/page.tsx b/app/translate/page.tsx index 35a1d5e..2800f83 100644 --- a/app/translate/page.tsx +++ b/app/translate/page.tsx @@ -25,6 +25,7 @@ type DetectedFormat = 'html' | 'markdown' | null; type ChatMessage = { id: string; content: string; + role: 'user' | 'assistant'; }; export default function TranslatePage() { @@ -48,6 +49,8 @@ export default function TranslatePage() { const [chatInput, setChatInput] = useState(''); const [chatMessages, setChatMessages] = useState([]); + const [isChatting, setIsChatting] = useState(false); + const [chatError, setChatError] = useState(''); const [settingsOpen, setSettingsOpen] = useState(false); const [apiKey, setApiKey] = useState(''); const [baseURL, setBaseURL] = useState(''); @@ -199,16 +202,65 @@ export default function TranslatePage() { setInputContent(PRESET_CONTENT); }; - const handleChatSend = () => { + const handleChatSend = async () => { const trimmed = chatInput.trim(); - if (!trimmed) return; - const message = { - id: `${Date.now()}`, + if (!trimmed || isChatting) return; + const message: ChatMessage = { + id: `${Date.now()}-user`, content: trimmed, + role: 'user', }; - setChatMessages((prev) => [...prev, message]); - setInputContent((prev) => `${prev}

${trimmed}

`); + const nextMessages = [...chatMessages, message]; + setChatMessages(nextMessages); setChatInput(''); + setIsChatting(true); + setChatError(''); + + try { + const response = await fetch('/api/creative-chat', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + messages: nextMessages.map((item) => ({ + role: item.role, + content: item.content, + })), + settings: { + apiKey: apiKey.trim() || undefined, + baseURL: baseURL.trim() || undefined, + model: modelName.trim() || undefined, + }, + }), + }); + + const data = await response.json(); + if (!response.ok) { + throw new Error(data.error || '聊天请求失败'); + } + + const assistantContent = String(data.message || '').trim(); + const assistantMessage: ChatMessage = { + id: `${Date.now()}-assistant`, + content: assistantContent, + role: 'assistant', + }; + setChatMessages((prev) => [...prev, assistantMessage]); + + if (assistantContent) { + const hasHtml = /<\/?[a-z][\s\S]*>/i.test(assistantContent); + const htmlContent = hasHtml + ? assistantContent + : `

${assistantContent}

`; + setInputContent((prev) => `${prev}${htmlContent}`); + } + } catch (error) { + console.error('Chat error:', error); + setChatError('AI 聊天服务暂时不可用,请稍后重试。'); + } finally { + setIsChatting(false); + } }; return ( @@ -355,7 +407,7 @@ export default function TranslatePage() {

在线创作联动

- 将创作需求输入聊天框,内容会自动追加到左侧编辑器中。 + 将创作需求输入聊天框,AI 会生成内容并自动追加到左侧编辑器中。

@@ -363,24 +415,53 @@ export default function TranslatePage() { {chatMessages.length === 0 ? '暂无聊天内容' : chatMessages.map((message) => ( -

- {message.content} +

+ + {message.role === 'assistant' ? 'AI' : '你'} + + {message.content}

))}
+ {chatError ? ( +

{chatError}

+ ) : null}
setChatInput(event.target.value)} - placeholder="输入要追加的创作内容..." + onKeyDown={(event) => { + if (event.key === 'Enter') { + event.preventDefault(); + handleChatSend(); + } + }} + placeholder="输入创作需求,回车发送..." className="flex-1 rounded-xl border border-[#0d0d0d0d] px-3 py-2 text-sm text-black placeholder:text-black/40 focus:outline-none focus:ring-2 focus:ring-black" />