sync: Agent Architecture Overhaul - AI SDK 6 + workflow optimization
Synced from private repo (feature/ai-sdk-6-upgrade): - Upgrade AI SDK 5 → 6 (packages + API changes) - Add sqliteQuery tool for flexible read-only queries - New WorkflowExecutor with workflow-specific tools - 83% token reduction for workflow execution - Remove mini-rah dead code (simplified architecture) - Add context management usage endpoint - Fix Connect workflow instructions - Clickable node references in workflow UI Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
f9271aeeb4
commit
3f0426ecf4
@@ -7,6 +7,7 @@ import QuickAddStatus from './QuickAddStatus';
|
||||
import { Zap, Flame, Minimize2 } from 'lucide-react';
|
||||
import type { AgentDelegation } from '@/services/agents/delegation';
|
||||
import { Node } from '@/types/database';
|
||||
import { parseAndRenderContent } from '@/components/helpers/NodeLabelRenderer';
|
||||
|
||||
interface AgentsPanelProps {
|
||||
openTabsData: Node[];
|
||||
@@ -509,6 +510,7 @@ export default function AgentsPanel({ openTabsData, activeTabId, activeDimension
|
||||
return rest;
|
||||
});
|
||||
}}
|
||||
onNodeClick={onNodeClick}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@@ -522,6 +524,7 @@ export default function AgentsPanel({ openTabsData, activeTabId, activeDimension
|
||||
<DelegationSummaryView
|
||||
delegation={selectedDelegation}
|
||||
onBack={() => setActiveAgentTab('workflows')}
|
||||
onNodeClick={onNodeClick}
|
||||
/>
|
||||
) : (
|
||||
<DelegationDetailView
|
||||
@@ -647,7 +650,7 @@ export default function AgentsPanel({ openTabsData, activeTabId, activeDimension
|
||||
}
|
||||
|
||||
// Summary view for completed delegations with no messages
|
||||
function DelegationSummaryView({ delegation, onBack }: { delegation: AgentDelegation; onBack?: () => void }) {
|
||||
function DelegationSummaryView({ delegation, onBack, onNodeClick }: { delegation: AgentDelegation; onBack?: () => void; onNodeClick?: (nodeId: number) => void }) {
|
||||
const isSuccess = delegation.status === 'completed';
|
||||
const statusColor = isSuccess ? '#22c55e' : '#ff6b6b';
|
||||
const statusLabel = isSuccess ? 'Completed' : 'Failed';
|
||||
@@ -748,7 +751,7 @@ function DelegationSummaryView({ delegation, onBack }: { delegation: AgentDelega
|
||||
lineHeight: '1.6',
|
||||
whiteSpace: 'pre-wrap'
|
||||
}}>
|
||||
{delegation.summary}
|
||||
{parseAndRenderContent(delegation.summary || '', onNodeClick)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -771,11 +774,13 @@ function DelegationSummaryView({ delegation, onBack }: { delegation: AgentDelega
|
||||
function WorkflowsListView({
|
||||
delegations,
|
||||
onSelectDelegation,
|
||||
onDeleteDelegation
|
||||
onDeleteDelegation,
|
||||
onNodeClick
|
||||
}: {
|
||||
delegations: AgentDelegation[];
|
||||
onSelectDelegation: (sessionId: string) => void;
|
||||
onDeleteDelegation: (sessionId: string) => void;
|
||||
onNodeClick?: (nodeId: number) => void;
|
||||
}) {
|
||||
const activeDelegations = delegations.filter(d => d.status === 'queued' || d.status === 'in_progress');
|
||||
const completedDelegations = delegations.filter(d => d.status === 'completed' || d.status === 'failed');
|
||||
@@ -863,6 +868,7 @@ function WorkflowsListView({
|
||||
statusLabel={label}
|
||||
onSelect={() => onSelectDelegation(delegation.sessionId)}
|
||||
onDelete={() => onDeleteDelegation(delegation.sessionId)}
|
||||
onNodeClick={onNodeClick}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
@@ -887,6 +893,7 @@ function WorkflowsListView({
|
||||
statusLabel={label}
|
||||
onSelect={() => onSelectDelegation(delegation.sessionId)}
|
||||
onDelete={() => onDeleteDelegation(delegation.sessionId)}
|
||||
onNodeClick={onNodeClick}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
@@ -903,13 +910,15 @@ function WorkflowCard({
|
||||
statusColor,
|
||||
statusLabel,
|
||||
onSelect,
|
||||
onDelete
|
||||
onDelete,
|
||||
onNodeClick
|
||||
}: {
|
||||
delegation: AgentDelegation;
|
||||
statusColor: string;
|
||||
statusLabel: string;
|
||||
onSelect: () => void;
|
||||
onDelete: () => void;
|
||||
onNodeClick?: (nodeId: number) => void;
|
||||
}) {
|
||||
const isActive = delegation.status === 'in_progress' || delegation.status === 'queued';
|
||||
|
||||
@@ -1009,7 +1018,7 @@ function WorkflowCard({
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap'
|
||||
}}>
|
||||
{delegation.summary}
|
||||
{parseAndRenderContent(delegation.summary, onNodeClick)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -1084,7 +1093,7 @@ function DelegationDetailView({
|
||||
</div>
|
||||
|
||||
{/* RAHChat for streaming */}
|
||||
<div style={{ flex: 1 }}>
|
||||
<div style={{ flex: 1, overflow: 'hidden', minHeight: 0 }}>
|
||||
<RAHChat
|
||||
openTabsData={openTabsData}
|
||||
activeTabId={activeTabId}
|
||||
|
||||
@@ -32,11 +32,17 @@ interface SendParams {
|
||||
mode: 'easy' | 'hard';
|
||||
}
|
||||
|
||||
interface UsageData {
|
||||
inputTokens: number;
|
||||
outputTokens: number;
|
||||
}
|
||||
|
||||
interface UseSSEChatOptions {
|
||||
getAuthToken?: () => string | null | undefined;
|
||||
beforeRequest?: () => boolean;
|
||||
onRequestError?: (error: unknown, response?: Response) => boolean | void;
|
||||
onStreamComplete?: () => void | Promise<void>;
|
||||
onUsageUpdate?: (usage: UsageData) => void;
|
||||
}
|
||||
|
||||
export function useSSEChat(
|
||||
@@ -44,7 +50,7 @@ export function useSSEChat(
|
||||
setMessages: (updater: (prev: ChatMessage[]) => ChatMessage[]) => void,
|
||||
options: UseSSEChatOptions = {}
|
||||
) {
|
||||
const { getAuthToken, beforeRequest, onRequestError, onStreamComplete } = options;
|
||||
const { getAuthToken, beforeRequest, onRequestError, onStreamComplete, onUsageUpdate } = options;
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const abortControllerRef = useRef<AbortController | null>(null);
|
||||
@@ -182,6 +188,14 @@ export function useSSEChat(
|
||||
setMessages((prev) => [...prev, newAssistantMessage]);
|
||||
}
|
||||
}
|
||||
|
||||
// Capture usage data from finish event (if AI SDK sends it)
|
||||
if (data.type === 'finish' && data.usage) {
|
||||
onUsageUpdate?.({
|
||||
inputTokens: data.usage.promptTokens ?? data.usage.inputTokens ?? 0,
|
||||
outputTokens: data.usage.completionTokens ?? data.usage.outputTokens ?? 0
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
// ignore malformed lines
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ function NodeLabel({ id, title, dimensions, onNodeClick }: NodeLabelProps) {
|
||||
verticalAlign: 'baseline'
|
||||
}}
|
||||
>
|
||||
#{id}
|
||||
{id}
|
||||
</span>
|
||||
{/* Non-clickable title - bold and underlined */}
|
||||
<span style={{
|
||||
|
||||
Reference in New Issue
Block a user