summaryrefslogtreecommitdiff
path: root/frontend/src/components/nodes
diff options
context:
space:
mode:
authorYurenHao0426 <blackhao0426@gmail.com>2026-02-13 21:43:34 +0000
committerYurenHao0426 <blackhao0426@gmail.com>2026-02-13 21:43:34 +0000
commit77be59bc0a6353e98846b9c9bfa2d566efea8b1f (patch)
treec0cc008b4705eb50616e6656f8fbc0e5b3475307 /frontend/src/components/nodes
parent30921396cb53f61eca90c85d692e0fc06d0f5ff4 (diff)
Add LLM Council mode for multi-model consensus
3-stage council orchestration: parallel model queries (Stage 1), anonymous peer ranking (Stage 2), and streamed chairman synthesis (Stage 3). Includes scope-aware file resolution for Google/Claude providers so upstream file attachments are visible to all providers. - Backend: council.py orchestrator, /api/run_council_stream endpoint, query_model_full() non-streaming wrapper, resolve_provider() helper, resolve_scoped_file_ids() for Google/Claude scope parity with OpenAI - Frontend: council toggle UI, model checkbox selector, chairman picker, SSE event parsing, tabbed Stage 1/2/3 response display - Canvas: amber council node indicator with Users icon Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'frontend/src/components/nodes')
-rw-r--r--frontend/src/components/nodes/LLMNode.tsx36
1 files changed, 25 insertions, 11 deletions
diff --git a/frontend/src/components/nodes/LLMNode.tsx b/frontend/src/components/nodes/LLMNode.tsx
index 7542860..adeb076 100644
--- a/frontend/src/components/nodes/LLMNode.tsx
+++ b/frontend/src/components/nodes/LLMNode.tsx
@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react';
import { Handle, Position, type NodeProps, useUpdateNodeInternals, useEdges } from 'reactflow';
import type { NodeData, MergedTrace, Trace } from '../../store/flowStore';
-import { Loader2, MessageSquare } from 'lucide-react';
+import { Loader2, MessageSquare, Users } from 'lucide-react';
import useFlowStore from '../../store/flowStore';
const LLMNode = ({ id, data, selected }: NodeProps<NodeData>) => {
@@ -96,13 +96,21 @@ const LLMNode = ({ id, data, selected }: NodeProps<NodeData>) => {
? isDark
? 'bg-gray-800 border-gray-600 opacity-50 cursor-not-allowed'
: 'bg-gray-100 border-gray-300 opacity-50 cursor-not-allowed'
- : selected
- ? isDark
- ? 'bg-gray-800 border-blue-400'
- : 'bg-white border-blue-500'
- : isDark
- ? 'bg-gray-800 border-gray-600'
- : 'bg-white border-gray-200'
+ : data.councilMode
+ ? selected
+ ? isDark
+ ? 'bg-gray-800 border-amber-400'
+ : 'bg-white border-amber-500'
+ : isDark
+ ? 'bg-gray-800 border-amber-600/60'
+ : 'bg-white border-amber-300'
+ : selected
+ ? isDark
+ ? 'bg-gray-800 border-blue-400'
+ : 'bg-white border-blue-500'
+ : isDark
+ ? 'bg-gray-800 border-gray-600'
+ : 'bg-white border-gray-200'
}`}
style={{ pointerEvents: isDisabled ? 'none' : 'auto', minHeight: minHandleHeight }}
onMouseEnter={() => setShowPreview(true)}
@@ -134,10 +142,12 @@ const LLMNode = ({ id, data, selected }: NodeProps<NodeData>) => {
}`}>
{data.status === 'loading' ? (
<Loader2 className="w-4 h-4 animate-spin text-blue-500" />
+ ) : data.councilMode ? (
+ <Users className={`w-4 h-4 ${isDark ? 'text-amber-400' : 'text-amber-600'}`} />
) : (
<MessageSquare className={`w-4 h-4 ${
- isDisabled
- ? 'text-gray-500'
+ isDisabled
+ ? 'text-gray-500'
: isDark ? 'text-gray-400' : 'text-gray-600'
}`} />
)}
@@ -151,7 +161,11 @@ const LLMNode = ({ id, data, selected }: NodeProps<NodeData>) => {
{data.label}
{isDisabled && <span className="text-xs ml-1">(disabled)</span>}
</div>
- <div className={`text-xs ${isDark ? 'text-gray-400' : 'text-gray-500'}`}>{data.model}</div>
+ <div className={`text-xs ${isDark ? 'text-gray-400' : 'text-gray-500'}`}>
+ {data.councilMode
+ ? `Council (${(data.councilModels || []).length})`
+ : data.model}
+ </div>
</div>
</div>