diff --git a/langgraph/src/agent/enterprise/nodes/tool-executor.ts b/langgraph/src/agent/enterprise/nodes/tool-executor.ts index 961c558..c9a8d65 100644 --- a/langgraph/src/agent/enterprise/nodes/tool-executor.ts +++ b/langgraph/src/agent/enterprise/nodes/tool-executor.ts @@ -65,7 +65,7 @@ const ANALYSIS_KEYWORDS = /趋势|统计|分析|对比|汇总|多少|走势|变 function hasAnalysisIntent(messages: EnterpriseState["messages"]): boolean { // Walk backwards to find the last human message for (let i = messages.length - 1; i >= 0; i--) { - const m = messages[i] as Record; + const m = messages[i] as unknown as Record; const isHuman = m.role === "user" || (typeof m._getType === "function" && (m._getType as () => string)() === "human") || @@ -80,7 +80,7 @@ function hasAnalysisIntent(messages: EnterpriseState["messages"]): boolean { /** Map error to a suggestion type for the error-result card */ function errorSuggestion( - toolName: string, + _toolName: string, error: unknown, ): "retry" | "contact_admin" | "check_input" { const msg = error instanceof Error ? error.message : String(error); @@ -597,7 +597,7 @@ export async function toolExecutorNode( const priorityChart = Object.entries(priorityStats).map( ([cname, value]) => ({ name: cname, value }), ); - const charts: Array<{ chart_type: string; title: string; data: Array<{ name: string; value: number }> }> = [ + const charts: Array<{ chart_type: "bar" | "line" | "pie" | "area"; title: string; data: Array<{ name: string; value: number }> }> = [ { chart_type: "pie", title: "状态分布", data: statusChart }, { chart_type: "bar", title: "优先级分布", data: priorityChart }, ]; @@ -614,7 +614,7 @@ export async function toolExecutorNode( .map(([cname, value]) => ({ name: cname, value })); if (trendData.length >= 1) { charts.push({ - chart_type: "line", + chart_type: "line" as const, title: "工单创建时间趋势", data: trendData, }); diff --git a/langgraph/src/agent/enterprise/tools/soc-client.ts b/langgraph/src/agent/enterprise/tools/soc-client.ts index c0f8097..2d89525 100644 --- a/langgraph/src/agent/enterprise/tools/soc-client.ts +++ b/langgraph/src/agent/enterprise/tools/soc-client.ts @@ -324,11 +324,12 @@ export async function sandboxRun( throw new Error(`Unsupported language: ${language}. Supported: ${SUPPORTED_LANGUAGES.join(", ")}`); } const escaped = code.replace(/'/g, "'\\''"); - const cmd: Record = { + const cmdMap: Record = { python: `python3 -c '${escaped}'`, javascript: `node -e '${escaped}'`, bash: `bash -c '${escaped}'`, - }[language as SupportedLang]; + }; + const cmd = cmdMap[language as SupportedLang]; let execResp: Response; try { diff --git a/langgraph/src/main.tsx b/langgraph/src/main.tsx index f71632a..47a8ccb 100644 --- a/langgraph/src/main.tsx +++ b/langgraph/src/main.tsx @@ -145,6 +145,10 @@ function App() { // Pending retry tool name — set by soc:retry-tool, consumed on next submit const pendingRetryToolRef = useRef(null); + // ── Concurrent submit state ─────────────────────────────────────────────── + const [showConcurrentDialog, setShowConcurrentDialog] = useState(false); + const pendingSubmitRef = useRef<(() => void) | null>(null); + const thread = useStream<{ messages: Message[]; ui: UIMsgLocal[] }>({ apiUrl: LANGGRAPH_URL, assistantId: "agent", @@ -283,7 +287,19 @@ function App() { async function handleSubmit(e: React.FormEvent) { e.preventDefault(); const text = input.trim(); - if ((!text && !attachedFile) || thread.isLoading) return; + if (!text && !attachedFile) return; + + // If AI is still generating, show concurrent choice dialog + if (thread.isLoading) { + pendingSubmitRef.current = () => doSubmit(text); + setShowConcurrentDialog(true); + return; + } + + doSubmit(text); + } + + async function doSubmit(text: string) { setInput(""); setSourceLabel(null); // Clear draft and update lastActive for this thread @@ -354,6 +370,34 @@ function App() { } } + // ── Concurrent dialog handlers ──────────────────────────────────────────── + function handleConcurrentInterrupt() { + setShowConcurrentDialog(false); + thread.stop(); + const submit = pendingSubmitRef.current; + pendingSubmitRef.current = null; + setTimeout(() => submit?.(), 300); + } + + function handleConcurrentQueue() { + setShowConcurrentDialog(false); + const submit = pendingSubmitRef.current; + pendingSubmitRef.current = null; + const poll = () => { + if (!thread.isLoading) { + submit?.(); + } else { + setTimeout(poll, 500); + } + }; + setTimeout(poll, 500); + } + + function handleConcurrentCancel() { + setShowConcurrentDialog(false); + pendingSubmitRef.current = null; + } + // ── Regenerate last AI message ─────────────────────────────────────────── function handleRegenerate() { // Find last human message @@ -842,7 +886,7 @@ function App() { )}
- +