feat(页面): 添加秘塔AI配置界面和聊天页面增强
设置页面: - 新增秘塔AI配置区块 - 支持秘塔API Key的配置和清除 - 添加秘塔平台链接和工具使用说明 聊天页面: - 从数据库加载搜索图片和使用工具数据 - 将数据传递给 MessageBubble 组件展示
This commit is contained in:
parent
615a59567d
commit
8bfe420676
@ -89,10 +89,14 @@ export default function ChatPage({ params }: PageProps) {
|
|||||||
outputTokens: msg.outputTokens || undefined,
|
outputTokens: msg.outputTokens || undefined,
|
||||||
// 从数据库加载图片数据(代码执行产生的)
|
// 从数据库加载图片数据(代码执行产生的)
|
||||||
images: (msg.images as string[]) || undefined,
|
images: (msg.images as string[]) || undefined,
|
||||||
|
// 从数据库加载搜索到的图片(图片搜索工具结果)
|
||||||
|
searchImages: (msg.searchImages as { title: string; imageUrl: string; width: number; height: number; score: string; position: number; sourceUrl?: string }[]) || undefined,
|
||||||
// 从数据库加载用户上传的图片
|
// 从数据库加载用户上传的图片
|
||||||
uploadedImages: (msg.uploadedImages as string[]) || undefined,
|
uploadedImages: (msg.uploadedImages as string[]) || undefined,
|
||||||
// 从数据库加载用户上传的文档
|
// 从数据库加载用户上传的文档
|
||||||
uploadedDocuments: (msg.uploadedDocuments as { name: string; size: number; type: string; content: string }[]) || undefined,
|
uploadedDocuments: (msg.uploadedDocuments as { name: string; size: number; type: string; content: string }[]) || undefined,
|
||||||
|
// 从数据库加载使用的工具列表
|
||||||
|
usedTools: (msg.usedTools as string[]) || undefined,
|
||||||
}));
|
}));
|
||||||
setInitialMessages(historyMessages);
|
setInitialMessages(historyMessages);
|
||||||
}
|
}
|
||||||
@ -563,8 +567,10 @@ export default function ChatPage({ params }: PageProps) {
|
|||||||
isStreaming={message.status === 'streaming'}
|
isStreaming={message.status === 'streaming'}
|
||||||
error={message.error}
|
error={message.error}
|
||||||
images={message.images}
|
images={message.images}
|
||||||
|
searchImages={message.searchImages}
|
||||||
uploadedImages={message.uploadedImages}
|
uploadedImages={message.uploadedImages}
|
||||||
uploadedDocuments={message.uploadedDocuments}
|
uploadedDocuments={message.uploadedDocuments}
|
||||||
|
usedTools={message.usedTools}
|
||||||
pyodideStatus={message.pyodideStatus}
|
pyodideStatus={message.pyodideStatus}
|
||||||
onRegenerate={message.role === 'assistant' && !isStreaming ? handleRegenerate : undefined}
|
onRegenerate={message.role === 'assistant' && !isStreaming ? handleRegenerate : undefined}
|
||||||
onSaveToNote={message.role === 'assistant' && !isStreaming ? handleSaveToNote : undefined}
|
onSaveToNote={message.role === 'assistant' && !isStreaming ? handleSaveToNote : undefined}
|
||||||
|
|||||||
@ -56,6 +56,11 @@ export default function SettingsPage() {
|
|||||||
const [showApiKey, setShowApiKey] = useState(false);
|
const [showApiKey, setShowApiKey] = useState(false);
|
||||||
const [saveStatus, setSaveStatus] = useState<'idle' | 'saving' | 'saved' | 'error'>('idle');
|
const [saveStatus, setSaveStatus] = useState<'idle' | 'saving' | 'saved' | 'error'>('idle');
|
||||||
|
|
||||||
|
// 秘塔AI 配置状态
|
||||||
|
const [metasoApiKey, setMetasoApiKey] = useState('');
|
||||||
|
const [showMetasoApiKey, setShowMetasoApiKey] = useState(false);
|
||||||
|
const [metasoSaveStatus, setMetasoSaveStatus] = useState<'idle' | 'saving' | 'saved' | 'error'>('idle');
|
||||||
|
|
||||||
// AI 行为设置状态
|
// AI 行为设置状态
|
||||||
const [systemPrompt, setSystemPrompt] = useState('');
|
const [systemPrompt, setSystemPrompt] = useState('');
|
||||||
const [temperature, setTemperature] = useState('0.7');
|
const [temperature, setTemperature] = useState('0.7');
|
||||||
@ -107,6 +112,31 @@ export default function SettingsPage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 保存秘塔AI API Key
|
||||||
|
const handleSaveMetasoConfig = async () => {
|
||||||
|
setMetasoSaveStatus('saving');
|
||||||
|
try {
|
||||||
|
if (metasoApiKey) {
|
||||||
|
await updateSettings({ metasoApiKey });
|
||||||
|
}
|
||||||
|
setMetasoSaveStatus('saved');
|
||||||
|
setMetasoApiKey(''); // 清除输入的 API Key
|
||||||
|
setTimeout(() => setMetasoSaveStatus('idle'), 2000);
|
||||||
|
} catch {
|
||||||
|
setMetasoSaveStatus('error');
|
||||||
|
setTimeout(() => setMetasoSaveStatus('idle'), 2000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 清除秘塔 API Key
|
||||||
|
const handleClearMetasoApiKey = async () => {
|
||||||
|
try {
|
||||||
|
await updateSettings({ metasoApiKey: '' });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to clear Metaso API key:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 更新默认模型
|
// 更新默认模型
|
||||||
const handleModelChange = async (modelId: string) => {
|
const handleModelChange = async (modelId: string) => {
|
||||||
try {
|
try {
|
||||||
@ -476,6 +506,95 @@ export default function SettingsPage() {
|
|||||||
</div>
|
</div>
|
||||||
</SettingsSection>
|
</SettingsSection>
|
||||||
|
|
||||||
|
{/* 秘塔AI 配置 */}
|
||||||
|
<SettingsSection
|
||||||
|
title="秘塔AI 配置"
|
||||||
|
description="配置秘塔AI搜索和读取工具(可选)"
|
||||||
|
>
|
||||||
|
<SettingsItem
|
||||||
|
label="秘塔 API Key"
|
||||||
|
description={
|
||||||
|
settings?.metasoApiKeyConfigured ? (
|
||||||
|
'已配置秘塔 API Key'
|
||||||
|
) : (
|
||||||
|
<span>
|
||||||
|
配置后可使用 Metaso Search 和 Metaso Reader 工具。
|
||||||
|
<a
|
||||||
|
href="https://metaso.cn/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-[var(--color-primary)] hover:underline ml-1"
|
||||||
|
>
|
||||||
|
前往秘塔平台获取 API Key →
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
{settings?.metasoApiKeyConfigured ? (
|
||||||
|
<>
|
||||||
|
<span className="inline-flex items-center gap-1 text-sm text-green-600">
|
||||||
|
<Check size={14} />
|
||||||
|
已配置
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
onClick={handleClearMetasoApiKey}
|
||||||
|
className="btn-ghost text-red-600 hover:text-red-700 text-sm"
|
||||||
|
disabled={saving}
|
||||||
|
>
|
||||||
|
清除
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div className="relative">
|
||||||
|
<input
|
||||||
|
type={showMetasoApiKey ? 'text' : 'password'}
|
||||||
|
className="settings-input pr-10 w-80"
|
||||||
|
value={metasoApiKey}
|
||||||
|
onChange={(e) => setMetasoApiKey(e.target.value)}
|
||||||
|
placeholder="输入秘塔 API Key"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="absolute right-3 top-1/2 -translate-y-1/2 text-[var(--color-text-tertiary)] hover:text-[var(--color-text-primary)]"
|
||||||
|
onClick={() => setShowMetasoApiKey(!showMetasoApiKey)}
|
||||||
|
>
|
||||||
|
{showMetasoApiKey ? <EyeOff size={16} /> : <Eye size={16} />}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</SettingsItem>
|
||||||
|
|
||||||
|
{!settings?.metasoApiKeyConfigured && (
|
||||||
|
<div className="px-5 py-4 border-t border-[var(--color-border-light)]">
|
||||||
|
<button
|
||||||
|
onClick={handleSaveMetasoConfig}
|
||||||
|
disabled={metasoSaveStatus === 'saving' || !metasoApiKey}
|
||||||
|
className="btn-primary inline-flex items-center gap-2"
|
||||||
|
>
|
||||||
|
{metasoSaveStatus === 'saving' ? (
|
||||||
|
<Loader2 size={16} className="animate-spin" />
|
||||||
|
) : metasoSaveStatus === 'saved' ? (
|
||||||
|
<Check size={16} />
|
||||||
|
) : null}
|
||||||
|
{metasoSaveStatus === 'saving' ? '保存中...' : metasoSaveStatus === 'saved' ? '已保存' : '保存秘塔配置'}
|
||||||
|
</button>
|
||||||
|
{metasoSaveStatus === 'error' && (
|
||||||
|
<span className="ml-3 text-sm text-red-600">保存失败</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="px-5 py-3 bg-[var(--color-bg-tertiary)] border-t border-[var(--color-border-light)]">
|
||||||
|
<p className="text-xs text-[var(--color-text-tertiary)]">
|
||||||
|
💡 秘塔工具说明:Metaso Search 提供高质量中文搜索,Metaso Reader 可将网页转换为结构化 Markdown。
|
||||||
|
未配置 API Key 时,这些工具仍可选择但无法使用。
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</SettingsSection>
|
||||||
|
|
||||||
{/* 模型和工具设置 */}
|
{/* 模型和工具设置 */}
|
||||||
<SettingsSection
|
<SettingsSection
|
||||||
title="AI 配置"
|
title="AI 配置"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user