技能展示适配持久化 + 参考文献收集与新对话流式修复
- 后端新增 skill_display_adapters 表,持久化每个技能的 display 配置与 sample
payload;/api/skills 列表、详情、设置接口同步读写
- 新增 /api/skills/{name}/display-sample 端点,编辑器优先加载库中样例
- 前端展示适配面板改为从后端加载持久样例,无样例时提示"自动识别"
- 参考文献收集修复:仅在 bash/shell/python 等执行类工具上从参数推断技能
名,避免 read_file / web_fetch 误匹配;引用列表纳入最新一条助手消息
- ChatBox 改用「最新 human → 最新 ai」窗口收集引用,并仅保留 citation
模式
- 修复「新对话」点击后已发消息消失:useThreadStream 只在 threadId 切回
空值时重置/中止 SSE,避免 onCreated 分配 id 时把刚开始的流打断;
ChatPage 在 landing 状态隐藏 MessageList,移除 KeyedChatPage remount
- 技能详情对话框:所有者可查看源码与文件 tab(不再要求管理员)
- LocalSandbox:Windows PowerShell 5 路径下把 && 重写为 ;,并以
UTF-8 解码 subprocess 输出,避免中文乱码
- knowledge-search-v2 mock 样例改为中文固定测试数据
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
7adb93fb8e
commit
0256d527f0
16 files changed
+2722
-2134
No files matched your search
@@ -231,14 +231,25 @@ export default function ChatPage() {
|
||||
</header>
|
||||
<main className="flex min-h-0 max-w-full grow flex-col">
|
||||
<div className="flex size-full justify-center">
|
||||
<MessageList
|
||||
className={cn("size-full", !isLandingState && "pt-10")}
|
||||
threadId={threadId}
|
||||
thread={thread}
|
||||
paddingBottom={messageListPaddingBottom}
|
||||
tokenUsageEnabled={tokenUsageEnabled}
|
||||
onClarificationSubmit={handleClarificationSubmit}
|
||||
/>
|
||||
{/*
|
||||
Landing state covers two cases: (a) a brand-new chat that
|
||||
hasn't sent anything yet, and (b) the user clicking 「新对话」
|
||||
mid-stream. In (b) the underlying useStream's internal
|
||||
stream.values takes one or two effect ticks to clear, so
|
||||
rendering MessageList unconditionally would leak the previous
|
||||
conversation under the welcome card. Skip rendering during
|
||||
the landing state to guarantee a clean slate.
|
||||
*/}
|
||||
{!isLandingState && (
|
||||
<MessageList
|
||||
className={cn("size-full", "pt-10")}
|
||||
threadId={threadId}
|
||||
thread={thread}
|
||||
paddingBottom={messageListPaddingBottom}
|
||||
tokenUsageEnabled={tokenUsageEnabled}
|
||||
onClarificationSubmit={handleClarificationSubmit}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="absolute right-0 bottom-0 left-0 z-30 flex justify-center px-4">
|
||||
<div
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type { CSSProperties, ReactNode } from "react";
|
||||
import { Navigate, Route, Routes, useLocation } from "react-router-dom";
|
||||
import { Navigate, Route, Routes } from "react-router-dom";
|
||||
|
||||
import { getStoredAuth } from "@/core/auth";
|
||||
|
||||
@@ -89,20 +89,6 @@ export function ChatRuntime({ children }: { children: ReactNode }) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 让 ChatPage 在每次真正的 react-router navigation 上完整 remount。
|
||||
*
|
||||
* 背景:点「新对话」时即使路径不变,我们也会用 `navigate(..., { replace: true,
|
||||
* state: { resetAt } })` 强制刷新 location.key —— 用它作 key 能保证 useStream、
|
||||
* useThreadStream、optimistic state、refs 一次性全部丢弃,避免上一条流的
|
||||
* `stream.values` 残留把欢迎页底下的旧消息拖出来(SDK 的 stream.clear() 需要
|
||||
* 多次 effect tick 才会清干净,中间过渡帧用户能看见)。
|
||||
*/
|
||||
function KeyedChatPage({ render }: { render: () => ReactNode }) {
|
||||
const key = useLocation().key;
|
||||
return <ChatRuntime key={key}>{render()}</ChatRuntime>;
|
||||
}
|
||||
|
||||
export default function WorkspaceRoutes({
|
||||
pageSidebarCollapsed = false,
|
||||
pageSidebarHidden = false,
|
||||
@@ -137,7 +123,9 @@ export default function WorkspaceRoutes({
|
||||
path="chats/:thread_id"
|
||||
element={
|
||||
<WorkspaceLayout {...layoutProps}>
|
||||
<KeyedChatPage render={() => <ChatPage />} />
|
||||
<ChatRuntime>
|
||||
<ChatPage />
|
||||
</ChatRuntime>
|
||||
</WorkspaceLayout>
|
||||
}
|
||||
/>
|
||||
|
||||
Reference in new issue
Block a user