refactor: 静态变量提到对话外 + System Prompt 模板展开可视化

- 新建 SessionBar:会话变量独立于消息气泡,显示在对话顶部
- 重写 SystemPromptView:解析 {{var}} 占位符并内联展示模板→变量映射
- 重构 MessageList:提取 static_var 到 varMap,过滤后传入气泡
- 更新 SegmentRenderer + MessageBubble:传递 varMap 到 SystemPromptView
- 更新所有 Demo:static_var 从 user 消息迁移到 system 消息,使用真实会话配置(current_date、language、knowledge_cutoff)
- 更新导出逻辑:system 消息中收集 static_var 并在模板中展开 {{var}}
- 更新测试:新增模板展开用例,18 tests pass
This commit is contained in:
carry
2026-06-07 14:44:29 +08:00
parent 483b1a7f39
commit 92ecb139ad
10 changed files with 359 additions and 49 deletions
+53 -6
View File
@@ -1,12 +1,53 @@
import type { Message } from '../types/protocol'
import { useMemo } from 'react'
import type { Message, StaticVarSegment } from '../types/protocol'
import SessionBar from './SessionBar'
import MessageBubble from './MessageBubble'
interface MessageListProps {
messages: Message[]
}
/**
* 从所有 system 消息中提取 static_var 片段,
* 构建会话变量映射表并从消息体中移除这些片段。
*/
function extractSessionVars(messages: Message[]): {
variables: StaticVarSegment[]
varMap: Record<string, string>
cleanedMessages: Message[]
} {
const variables: StaticVarSegment[] = []
const varMap: Record<string, string> = {}
const cleanedMessages = messages.map((msg) => {
const staticVars: StaticVarSegment[] = []
const remaining = msg.segments.filter((seg) => {
if (seg.kind === 'static_var') {
staticVars.push(seg)
return false // 从消息体中移除
}
return true
})
// 收集变量
for (const v of staticVars) {
variables.push(v)
varMap[v.name] = v.value
}
return { ...msg, segments: remaining }
})
return { variables, varMap, cleanedMessages }
}
export default function MessageList({ messages }: MessageListProps) {
if (messages.length === 0) {
const { variables, varMap, cleanedMessages } = useMemo(
() => extractSessionVars(messages),
[messages]
)
if (cleanedMessages.length === 0) {
return (
<div className="flex-1 flex items-center justify-center text-gray-300 text-sm">
Demo
@@ -15,10 +56,16 @@ export default function MessageList({ messages }: MessageListProps) {
}
return (
<div className="flex-1 overflow-y-auto px-4 py-4 space-y-1">
{messages.map((msg) => (
<MessageBubble key={msg.id} message={msg} />
))}
<div className="flex-1 flex flex-col min-h-0">
{/* 会话变量横栏 —— 在对话气泡之外 */}
<SessionBar variables={variables} />
{/* 消息列表 */}
<div className="flex-1 overflow-y-auto px-4 py-4 space-y-1">
{cleanedMessages.map((msg) => (
<MessageBubble key={msg.id} message={msg} varMap={varMap} />
))}
</div>
</div>
)
}