@@ -16,12 +16,10 @@ import {
1616} from "lucide-react" ;
1717import { ChangeEvent , FormEvent , useEffect , useRef , useState } from "react" ;
1818import {
19- callLocalKnowledge ,
2019 loadApiSnapshot ,
2120 loadRole ,
2221 loadWorkflowRun ,
2322 lookupNews ,
24- previewLocalKnowledge ,
2523 queryRag ,
2624 saveRuntimeSettings ,
2725 uploadDocuments
@@ -40,6 +38,7 @@ import { createEmptyRag, useChatController } from "./features/chat/chatControlle
4038import { ChatPanel } from "./features/single-chat/ChatPanel" ;
4139import { SESSION_STORAGE_KEY , seedMessages } from "./features/single-chat/chatHistory" ;
4240import { ToolPanel } from "./features/tools/ToolPanel" ;
41+ import { useToolController , type ToolController } from "./features/tools/toolController" ;
4342import { roleLabel , roleOptions } from "./features/roles/roleCatalog" ;
4443import { WechatPanel } from "./features/wechat-workspace/WechatPanel" ;
4544import { useGroupChatController } from "./features/group-chat/groupChatController" ;
@@ -56,7 +55,6 @@ import type {
5655 RagQueryResponse ,
5756 RagSettings ,
5857 RoleResponse ,
59- ToolInvocationResponse ,
6058 WorkspaceState ,
6159 WorkflowRunDetail
6260} from "./types" ;
@@ -533,15 +531,7 @@ function Inspector({
533531 selectedRun,
534532 loadingRunId,
535533 selectRun,
536- toolPreview,
537- toolCall,
538- previewTool,
539- callTool,
540- isPreviewing,
541- isCalling,
542- toolCanCall,
543- toolCallBlockedReason,
544- toolInvocationLabel,
534+ toolController,
545535 onRestoreSession,
546536 onArchiveSession,
547537 newsController,
@@ -575,15 +565,7 @@ function Inspector({
575565 selectedRun : WorkflowRunDetail | null ;
576566 loadingRunId : string ;
577567 selectRun : ( runId : string ) => void ;
578- toolPreview : ToolInvocationResponse | null ;
579- toolCall : ToolInvocationResponse | null ;
580- previewTool : ( ) => void ;
581- callTool : ( ) => void ;
582- isPreviewing : boolean ;
583- isCalling : boolean ;
584- toolCanCall : boolean ;
585- toolCallBlockedReason : string ;
586- toolInvocationLabel : string ;
568+ toolController : ToolController ;
587569 onRestoreSession : ( sessionId : string ) => void ;
588570 onArchiveSession : ( sessionId : string ) => void ;
589571 newsController : NewsController ;
@@ -643,15 +625,15 @@ function Inspector({
643625 />
644626 < ToolPanel
645627 toolCount = { snapshot . tools . length }
646- toolPreview = { toolPreview }
647- toolCall = { toolCall }
648- previewTool = { previewTool }
649- callTool = { callTool }
650- isPreviewing = { isPreviewing }
651- isCalling = { isCalling }
652- canCall = { toolCanCall }
653- callBlockedReason = { toolCallBlockedReason }
654- invocationLabel = { toolInvocationLabel }
628+ run = { toolController . run }
629+ error = { toolController . error }
630+ previewTool = { toolController . preview }
631+ callTool = { toolController . call }
632+ isPreviewing = { toolController . isPreviewing }
633+ isCalling = { toolController . isCalling }
634+ canCall = { toolController . canCall }
635+ callBlockedReason = { toolController . callBlockedReason }
636+ invocationLabel = { toolController . invocationLabel }
655637 />
656638 < SessionsPanel sessions = { snapshot . sessions } activeSessionId = { singleChatSessionId } isSending = { isSending } onRestore = { onRestoreSession } onArchive = { onArchiveSession } />
657639 < RoadmapPanel />
@@ -670,17 +652,14 @@ export default function App() {
670652 const [ keepCurrentRole , setKeepCurrentRole ] = useState ( false ) ;
671653 const [ conversationInstruction , setConversationInstruction ] = useState ( "" ) ;
672654 const [ isSavingSettings , setIsSavingSettings ] = useState ( false ) ;
673- const [ isPreviewing , setIsPreviewing ] = useState ( false ) ;
674- const [ isCalling , setIsCalling ] = useState ( false ) ;
675655 const [ isSearching , setIsSearching ] = useState ( false ) ;
676656 const wechatThreadId = workspaceRuntime . activeGroupThreadId ?? snapshot . wechat ?. group_thread_id ;
677657 const newsRunId = workspaceRuntime . activeNewsRunId ;
658+ const toolRunId = workspaceRuntime . activeToolRunId ;
678659 const setWechatThreadId = ( threadId ?: string ) => dispatchWorkspace ( { type : "SET_ACTIVE_GROUP_THREAD" , threadId } ) ;
679660 const setNewsRunId = ( runId ?: string ) => dispatchWorkspace ( { type : "SET_ACTIVE_NEWS_RUN" , runId } ) ;
661+ const setToolRunId = ( runId ?: string ) => dispatchWorkspace ( { type : "SET_ACTIVE_TOOL_RUN" , runId } ) ;
680662 const [ ragSearch , setRagSearch ] = useState < RagQueryResponse | null > ( null ) ;
681- const [ toolPreview , setToolPreview ] = useState < ToolInvocationResponse | null > ( null ) ;
682- const [ toolCall , setToolCall ] = useState < ToolInvocationResponse | null > ( null ) ;
683- const [ previewedInvocation , setPreviewedInvocation ] = useState < LocalKnowledgeInvocation | null > ( null ) ;
684663 const [ selectedRun , setSelectedRun ] = useState < WorkflowRunDetail | null > ( null ) ;
685664 const [ roleDetail , setRoleDetail ] = useState < RoleResponse | null > ( null ) ;
686665 const [ webLookup , setWebLookup ] = useState < NewsLookupResponse | null > ( null ) ;
@@ -752,17 +731,15 @@ export default function App() {
752731 setOperationError,
753732 clearChatArtifacts : ( ) => {
754733 setRagSearch ( null ) ;
755- setToolPreview ( null ) ;
756- setToolCall ( null ) ;
757- setPreviewedInvocation ( null ) ;
734+ operationRegistry . invalidate ( "tool" ) ;
735+ setToolRunId ( undefined ) ;
758736 setSelectedRun ( null ) ;
759737 } ,
760738 onWorkspaceCancelled : ( ) => {
761739 groupController . cancelWorkspace ( ) ;
762740 newsController . cancelWorkspace ( ) ;
741+ operationRegistry . invalidate ( "tool" ) ;
763742 setIsNewsBusy ( false ) ;
764- setIsPreviewing ( false ) ;
765- setIsCalling ( false ) ;
766743 } ,
767744 refresh,
768745 } ) ;
@@ -784,22 +761,14 @@ export default function App() {
784761 topK : ragSettings . chatTopK ,
785762 minScore : ragSettings . minScore
786763 } ;
787- const toolCanCall = Boolean (
788- toolPreview &&
789- previewedInvocation &&
790- previewedInvocation . query === currentToolInvocation . query &&
791- previewedInvocation . retrievalMode === currentToolInvocation . retrievalMode &&
792- previewedInvocation . topK === currentToolInvocation . topK &&
793- previewedInvocation . minScore === currentToolInvocation . minScore
794- ) ;
795- const toolCallBlockedReason = ! toolPreview
796- ? ""
797- : toolCanCall
798- ? ""
799- : "输入或 RAG 参数已变化,请重新预览后再调用。" ;
800- const toolInvocationLabel = previewedInvocation
801- ? `${ previewedInvocation . query } · ${ previewedInvocation . retrievalMode } · top_k=${ previewedInvocation . topK } · min_score=${ previewedInvocation . minScore } `
802- : "" ;
764+ const toolController = useToolController ( {
765+ invocation : currentToolInvocation ,
766+ activeRunId : toolRunId ,
767+ setActiveRunId : setToolRunId ,
768+ onCalled : async ( ) => {
769+ await refresh ( ) ;
770+ } ,
771+ } ) ;
803772
804773 useEffect ( ( ) => {
805774 const saved = window . localStorage . getItem ( SESSION_STORAGE_KEY ) ;
@@ -809,12 +778,16 @@ export default function App() {
809778 const restoredThreadId = String ( parsed . singleChatSessionId ?? parsed . sessionId ?? "" ) ;
810779 const restoredWechatThreadId = String ( parsed . wechatThreadId ?? "" ) ;
811780 const restoredNewsRunId = String ( parsed . newsRunId ?? "" ) ;
781+ const restoredToolRunId = String ( parsed . toolRunId ?? "" ) ;
812782 if ( restoredWechatThreadId ) {
813783 setWechatThreadId ( restoredWechatThreadId ) ;
814784 }
815785 if ( restoredNewsRunId ) {
816786 setNewsRunId ( restoredNewsRunId ) ;
817787 }
788+ if ( restoredToolRunId ) {
789+ setToolRunId ( restoredToolRunId ) ;
790+ }
818791 if ( parsed . chatSettings && typeof parsed . chatSettings === "object" ) {
819792 sessionSettingsRestoredRef . current = true ;
820793 setChatSettings ( { ...CHAT_SETTINGS_DEFAULTS , ...( parsed . chatSettings as ChatSettings ) } ) ;
@@ -896,6 +869,7 @@ export default function App() {
896869 singleChatSessionId,
897870 wechatThreadId,
898871 newsRunId,
872+ toolRunId,
899873 chatSettings,
900874 ragSettings,
901875 ragEnabled,
@@ -911,7 +885,7 @@ export default function App() {
911885 window . localStorage . setItem ( SESSION_STORAGE_KEY , payload ) ;
912886 } , isSending ? 800 : 200 ) ;
913887 return ( ) => window . clearTimeout ( timeout ) ;
914- } , [ singleChatMessages , singleChatSessionId , wechatThreadId , newsRunId , chatSettings , ragSettings , ragEnabled , keepCurrentRole , conversationInstruction , lastChat , isSending ] ) ;
888+ } , [ singleChatMessages , singleChatSessionId , wechatThreadId , newsRunId , toolRunId , chatSettings , ragSettings , ragEnabled , keepCurrentRole , conversationInstruction , lastChat , isSending ] ) ;
915889
916890 useEffect ( ( ) => {
917891 const flushSessionStorage = ( ) => {
@@ -983,35 +957,6 @@ export default function App() {
983957 }
984958 } ;
985959
986- const previewTool = async ( ) => {
987- const { operationId, generationId } = operationRegistry . start ( "tool" ) ;
988- setIsPreviewing ( true ) ;
989- setToolCall ( null ) ;
990- const invocation = { ...currentToolInvocation } ;
991- try {
992- const response = await previewLocalKnowledge ( invocation ) ;
993- if ( ! operationRegistry . isCurrent ( operationId , generationId ) ) return ;
994- setToolPreview ( response ) ;
995- setPreviewedInvocation ( { ...invocation , previewId : response . run_id } ) ;
996- } catch ( error ) {
997- if ( ! operationRegistry . isCurrent ( operationId , generationId ) ) return ;
998- setPreviewedInvocation ( null ) ;
999- setToolPreview ( {
1000- tool_name : "retrieve_local_knowledge" ,
1001- status : "failed" ,
1002- output : { } ,
1003- reason : error instanceof Error ? error . message : "预览失败" ,
1004- elapsed_ms : 0 ,
1005- run_id : ""
1006- } ) ;
1007- } finally {
1008- if ( operationRegistry . isCurrent ( operationId , generationId ) ) {
1009- setIsPreviewing ( false ) ;
1010- }
1011- operationRegistry . complete ( operationId ) ;
1012- }
1013- } ;
1014-
1015960 const saveSettings = async ( ) => {
1016961 setIsSavingSettings ( true ) ;
1017962 setOperationError ( "" ) ;
@@ -1063,38 +1008,6 @@ export default function App() {
10631008 }
10641009 } ;
10651010
1066- const callTool = async ( ) => {
1067- if ( ! previewedInvocation || ! toolCanCall || isCalling ) {
1068- return ;
1069- }
1070- const { operationId, generationId } = operationRegistry . start ( "tool" ) ;
1071- setIsCalling ( true ) ;
1072- try {
1073- const result = await callLocalKnowledge ( previewedInvocation ) ;
1074- if ( ! operationRegistry . isCurrent ( operationId , generationId ) ) return ;
1075- setToolCall ( result ) ;
1076- await refresh ( ) ;
1077- if ( result . run_id ) {
1078- await selectRun ( result . run_id ) ;
1079- }
1080- } catch ( error ) {
1081- if ( ! operationRegistry . isCurrent ( operationId , generationId ) ) return ;
1082- setToolCall ( {
1083- tool_name : "retrieve_local_knowledge" ,
1084- status : "failed" ,
1085- output : { } ,
1086- reason : error instanceof Error ? error . message : "调用失败" ,
1087- elapsed_ms : 0 ,
1088- run_id : ""
1089- } ) ;
1090- } finally {
1091- if ( operationRegistry . isCurrent ( operationId , generationId ) ) {
1092- setIsCalling ( false ) ;
1093- }
1094- operationRegistry . complete ( operationId ) ;
1095- }
1096- } ;
1097-
10981011 const handleLookupNews = async ( ) => {
10991012 const query = newsQuery . trim ( ) ;
11001013 if ( ! query || isNewsBusy ) {
@@ -1226,15 +1139,7 @@ export default function App() {
12261139 selectedRun = { selectedRun }
12271140 loadingRunId = { loadingRunId }
12281141 selectRun = { selectRun }
1229- toolPreview = { toolPreview }
1230- toolCall = { toolCall }
1231- previewTool = { previewTool }
1232- callTool = { callTool }
1233- isPreviewing = { isPreviewing }
1234- isCalling = { isCalling }
1235- toolCanCall = { toolCanCall }
1236- toolCallBlockedReason = { toolCallBlockedReason }
1237- toolInvocationLabel = { toolInvocationLabel }
1142+ toolController = { toolController }
12381143 onRestoreSession = { restoreSession }
12391144 onArchiveSession = { archiveCurrentSession }
12401145 newsController = { newsController }
0 commit comments