feat: expand intelligent investigation workspace
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
||||
useInteractionRequests,
|
||||
useAgentConnection,
|
||||
FrontendActionExecutor,
|
||||
AnswerViewStateScope,
|
||||
agentTaskDiscussionContextSchema,
|
||||
type AgentTaskDiscussionContext,
|
||||
agentBootstrapKey,
|
||||
@@ -103,6 +104,12 @@ export function useWorkbenchAgent({
|
||||
const projectScopeKey = `project:${getWorkbenchProject(getAccessToken)?.project_id ?? "default"}`;
|
||||
const scopeKey = projectScopeKey;
|
||||
const { connectionStatus, recheck: checkAgentConnection, recoveryVersion } = useAgentConnection(scopeClient, scopeKey);
|
||||
const answerStateScope = useMemo(() => new AnswerViewStateScope(`${resources.id}:${scopeKey}`, scopeClient), [resources.id, scopeClient, scopeKey]);
|
||||
useEffect(() => {
|
||||
answerStateScope.retain();
|
||||
resources.disposers.add(answerStateScope.dispose);
|
||||
return () => { resources.disposers.delete(answerStateScope.dispose); answerStateScope.release(); };
|
||||
}, [answerStateScope, resources]);
|
||||
const history = useAgentHistory(scopeClient, scopeKey);
|
||||
const topics = useAgentTopics(scopeClient, scopeKey);
|
||||
const runtimeSummaries: AgentChatSessionSummary[] = agentRuntimes(resources).filter((entry) => entry.scope === scopeKey && entry.sessionId && entry.purpose !== "task_execution")
|
||||
@@ -184,7 +191,7 @@ export function useWorkbenchAgent({
|
||||
try {
|
||||
await target.chat.sendMessage(alreadyPresent ? undefined : replyMessage, { body: {
|
||||
session_id: owningSessionId, interaction_id: interactionId, model: selectedModel || undefined,
|
||||
capabilities: frontendActionEnabledRef.current ? ["host-question@1", "frontend-action@1"] : ["host-question@1"],
|
||||
capabilities: frontendActionEnabledRef.current ? ["host-question@1", "frontend-action@2"] : ["host-question@1"],
|
||||
approval_mode: approvalMode, task_context: target.taskDiscussion?.context,
|
||||
} });
|
||||
} catch (error) { target.runStatus = "error"; throw error; }
|
||||
@@ -344,8 +351,14 @@ export function useWorkbenchAgent({
|
||||
[refreshInteractionRequests, refreshAgentMessageInbox, applySessionId, claimEnvelope, handleCredentialRefresh, handleFrontendAction, onUiEnvelope]
|
||||
);
|
||||
|
||||
const synchronizeModelCatalog = useCallback((catalog: Awaited<ReturnType<AgentApiClient["getModels"]>>) => {
|
||||
// A background request from an old project must not replace this project's controls.
|
||||
if (resources.disposed || resources.activeAgent?.scope !== scopeKey) return;
|
||||
setModelOptions(catalog.models);
|
||||
setSelectedModel((current) => resolveAgentModelSelection(catalog, current));
|
||||
}, [resources, scopeKey]);
|
||||
const transport = useMemo(() => createWorkbenchAgentTransport(scopeClient, getAccessToken,
|
||||
getProjectId), [getAccessToken, getProjectId, scopeClient]);
|
||||
getProjectId, synchronizeModelCatalog), [getAccessToken, getProjectId, scopeClient, synchronizeModelCatalog]);
|
||||
const defaultRuntime = useMemo(() => {
|
||||
const created = createAgentRuntime(resources, scopeKey, scopeClient, transport);
|
||||
return created;
|
||||
@@ -768,7 +781,7 @@ export function useWorkbenchAgent({
|
||||
const loaded = await projectClient.loadSession(id, { messageLimit: 40 });
|
||||
if (!loaded) throw new Error("任务协作会话未能打开,请重试。");
|
||||
target = createAgentRuntime(resources, projectScopeKey, projectClient,
|
||||
createWorkbenchAgentTransport(projectClient, getAccessToken, getProjectId), loaded);
|
||||
createWorkbenchAgentTransport(projectClient, getAccessToken, getProjectId, synchronizeModelCatalog), loaded);
|
||||
if (loaded.isStreaming || loaded.runStatus === "running") resumeAgentRuntime(resources, target);
|
||||
}
|
||||
const titleBeforeOpen = titlesBeforeOpen.get(target);
|
||||
@@ -781,7 +794,7 @@ export function useWorkbenchAgent({
|
||||
activateRuntime(target);
|
||||
setPromptFocusRequest((current) => current + 1);
|
||||
return id;
|
||||
}, [activateRuntime, getAccessToken, getProjectId, projectScopeKey, resources]);
|
||||
}, [activateRuntime, getAccessToken, getProjectId, projectScopeKey, resources, synchronizeModelCatalog]);
|
||||
|
||||
const openTaskExecutionHistory = useCallback(async (taskId: string, runId: string, executionSessionId: string) => {
|
||||
const normalizedTaskId = taskId.trim();
|
||||
@@ -809,7 +822,7 @@ export function useWorkbenchAgent({
|
||||
resources,
|
||||
projectScopeKey,
|
||||
projectClient,
|
||||
createWorkbenchAgentTransport(projectClient, getAccessToken, getProjectId),
|
||||
createWorkbenchAgentTransport(projectClient, getAccessToken, getProjectId, synchronizeModelCatalog),
|
||||
loaded
|
||||
);
|
||||
} else {
|
||||
@@ -833,7 +846,7 @@ export function useWorkbenchAgent({
|
||||
activateRuntime(target);
|
||||
setStatusLabel(loaded.runStatus === "running" ? "调查执行记录自动同步中" : "调查执行记录已打开");
|
||||
return loaded.sessionId;
|
||||
}, [activateRuntime, getAccessToken, getProjectId, projectScopeKey, resources]);
|
||||
}, [activateRuntime, getAccessToken, getProjectId, projectScopeKey, resources, synchronizeModelCatalog]);
|
||||
|
||||
useEffect(() => {
|
||||
const target = runtimeRef.current;
|
||||
@@ -1084,7 +1097,7 @@ export function useWorkbenchAgent({
|
||||
topic_id: !activeSessionId ? runtimeRef.current?.topicId ?? undefined : undefined,
|
||||
model: selectedModel || undefined,
|
||||
approval_mode: approvalMode,
|
||||
capabilities: frontendActionEnabledRef.current ? ["frontend-action@1", "host-question@1"] : ["host-question@1"],
|
||||
capabilities: frontendActionEnabledRef.current ? ["frontend-action@2", "host-question@1"] : ["host-question@1"],
|
||||
task_context: taskDiscussion?.sessionId === sessionIdRef.current ? taskDiscussion.context : undefined
|
||||
}
|
||||
}
|
||||
@@ -1108,7 +1121,7 @@ export function useWorkbenchAgent({
|
||||
setStatusLabel("Agent 正在调查水质投诉事件");
|
||||
void target.chat.sendMessage({ text: prompt }, { body: {
|
||||
session_id: id, model: selectedModel || undefined, approval_mode: approvalMode, trigger_context: trigger,
|
||||
capabilities: frontendActionEnabledRef.current ? ["frontend-action@1", "host-question@1"] : ["host-question@1"],
|
||||
capabilities: frontendActionEnabledRef.current ? ["frontend-action@2", "host-question@1"] : ["host-question@1"],
|
||||
} }).catch(() => {});
|
||||
return { sessionId: id, reused: reserved.event_duplicate === true };
|
||||
}, [activateRuntime, approvalMode, resources, scopeClient, scopeKey, selectedModel, transport]);
|
||||
@@ -1185,6 +1198,7 @@ export function useWorkbenchAgent({
|
||||
|
||||
return {
|
||||
apiClient: clientRef.current,
|
||||
answerStateScope,
|
||||
contentReference: runtime.reference,
|
||||
setContentReference: (value: import("@/features/agent").AnswerUiSelection | undefined) => {
|
||||
const active = resources.activeAgent;
|
||||
@@ -1313,7 +1327,7 @@ function prefersReducedMotion() {
|
||||
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||
}
|
||||
|
||||
function createWorkbenchAgentTransport(client: AgentApiClient, access?: WorkbenchAccess, getProjectId?: () => Promise<string>) {
|
||||
function createWorkbenchAgentTransport(client: AgentApiClient, access?: WorkbenchAccess, getProjectId?: () => Promise<string>, onModels?: (catalog: Awaited<ReturnType<AgentApiClient["getModels"]>>) => void) {
|
||||
return new ResponsesChatTransport<AgentUiMessage>({
|
||||
api: `${env.TJWATER_AGENT_URL.replace(/\/$/, "")}/api/v1/responses`,
|
||||
headers: async (): Promise<Record<string, string>> => {
|
||||
@@ -1323,12 +1337,13 @@ function createWorkbenchAgentTransport(client: AgentApiClient, access?: Workbenc
|
||||
},
|
||||
async prepareSendMessagesRequest({ messages, body }) {
|
||||
const catalog = await client.getModels();
|
||||
onModels?.(catalog);
|
||||
const model = resolveAgentModelSelection(catalog, readBodyString(body, "model") ?? "");
|
||||
if (!model) throw new Error("Agent 后端当前没有可用模型,请检查模型配置。");
|
||||
return { body: createResponseRequest(messages, {
|
||||
conversation: readBodyString(body, "session_id"), interactionId: readBodyString(body, "interaction_id"), model, topicId: readBodyString(body, "topic_id"),
|
||||
approvalMode: readBodyString(body, "approval_mode") ?? "request",
|
||||
capabilities: Array.isArray(body?.capabilities) ? body.capabilities.filter((value): value is string => value === "frontend-action@1" || value === "host-question@1") : [], triggerContext: body?.trigger_context, taskContext: body?.task_context,
|
||||
capabilities: Array.isArray(body?.capabilities) ? body.capabilities.filter((value): value is string => value === "frontend-action@2" || value === "host-question@1") : [], triggerContext: body?.trigger_context, taskContext: body?.task_context,
|
||||
}) };
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user