diff options
| author | YurenHao0426 <blackhao0426@gmail.com> | 2026-02-13 21:43:34 +0000 |
|---|---|---|
| committer | YurenHao0426 <blackhao0426@gmail.com> | 2026-02-13 21:43:34 +0000 |
| commit | 77be59bc0a6353e98846b9c9bfa2d566efea8b1f (patch) | |
| tree | c0cc008b4705eb50616e6656f8fbc0e5b3475307 /frontend/src/components/nodes/LLMNode.tsx | |
| parent | 30921396cb53f61eca90c85d692e0fc06d0f5ff4 (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/LLMNode.tsx')
| -rw-r--r-- | frontend/src/components/nodes/LLMNode.tsx | 36 |
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> |
