| import { AgentStep, AgentTrace, AgentTraceMetadata, FinalStep } from '@/types/agent';
|
| import { create } from 'zustand';
|
| import { devtools } from 'zustand/middleware';
|
|
|
| interface AgentState {
|
|
|
| trace?: AgentTrace;
|
| traceId: string | null;
|
| isAgentProcessing: boolean;
|
| isConnectingToE2B: boolean;
|
| vncUrl: string;
|
| selectedModelId: string;
|
| availableModels: string[];
|
| isLoadingModels: boolean;
|
| isConnected: boolean;
|
| error?: string;
|
| isDarkMode: boolean;
|
| selectedStepIndex: number | null;
|
| finalStep?: FinalStep;
|
|
|
|
|
| setTrace: (trace: AgentTrace | undefined) => void;
|
| setTraceId: (traceId: string | null) => void;
|
| updateTraceWithStep: (step: AgentStep, metadata: AgentTraceMetadata) => void;
|
| updateStepEvaluation: (stepId: string, evaluation: 'like' | 'dislike' | 'neutral') => void;
|
| updateTraceEvaluation: (evaluation: 'success' | 'failed' | 'not_evaluated') => void;
|
| completeTrace: (metadata: AgentTraceMetadata, finalState?: 'success' | 'stopped' | 'max_steps_reached' | 'error' | 'sandbox_timeout') => void;
|
| setIsAgentProcessing: (processing: boolean) => void;
|
| setIsConnectingToE2B: (connecting: boolean) => void;
|
| setVncUrl: (url: string) => void;
|
| setSelectedModelId: (modelId: string) => void;
|
| setAvailableModels: (models: string[]) => void;
|
| setIsLoadingModels: (loading: boolean) => void;
|
| setIsConnected: (connected: boolean) => void;
|
| setError: (error: string | undefined) => void;
|
| setSelectedStepIndex: (index: number | null) => void;
|
| toggleDarkMode: () => void;
|
| resetAgent: () => void;
|
| }
|
|
|
| const initialState = {
|
| trace: undefined,
|
| traceId: null,
|
| isAgentProcessing: false,
|
| isConnectingToE2B: false,
|
| vncUrl: '',
|
| selectedModelId: 'Qwen/Qwen3-VL-30B-A3B-Instruct',
|
| availableModels: [],
|
| isLoadingModels: false,
|
| isConnected: false,
|
| error: undefined,
|
| isDarkMode: true,
|
| selectedStepIndex: null,
|
| finalStep: undefined,
|
| };
|
|
|
| export const useAgentStore = create<AgentState>()(
|
| devtools(
|
| (set) => ({
|
| ...initialState,
|
|
|
|
|
| setTrace: (trace) =>
|
| set({ trace }, false, 'setTrace'),
|
|
|
|
|
| setTraceId: (traceId) =>
|
| set({ traceId }, false, 'setTraceId'),
|
|
|
|
|
| updateTraceWithStep: (step, metadata) =>
|
| set(
|
| (state) => {
|
| if (!state.trace) return state;
|
|
|
| const existingSteps = state.trace.steps || [];
|
| const stepExists = existingSteps.some((s) => s.stepId === step.stepId);
|
|
|
| if (stepExists) return state;
|
|
|
|
|
| const updatedMetadata = {
|
| ...metadata,
|
| maxSteps: metadata.maxSteps > 0
|
| ? metadata.maxSteps
|
| : (state.trace.traceMetadata?.maxSteps || 200),
|
| };
|
|
|
| return {
|
| trace: {
|
| ...state.trace,
|
| steps: [...existingSteps, step],
|
| traceMetadata: updatedMetadata,
|
| isRunning: true,
|
| },
|
| };
|
| },
|
| false,
|
| 'updateTraceWithStep'
|
| ),
|
|
|
|
|
| updateStepEvaluation: (stepId, evaluation) =>
|
| set(
|
| (state) => {
|
| if (!state.trace || !state.trace.steps) return state;
|
|
|
| const updatedSteps = state.trace.steps.map((step) =>
|
| step.stepId === stepId
|
| ? { ...step, step_evaluation: evaluation }
|
| : step
|
| );
|
|
|
| return {
|
| trace: {
|
| ...state.trace,
|
| steps: updatedSteps,
|
| },
|
| };
|
| },
|
| false,
|
| 'updateStepEvaluation'
|
| ),
|
|
|
|
|
| updateTraceEvaluation: (evaluation) =>
|
| set(
|
| (state) => {
|
| if (!state.trace || !state.trace.traceMetadata) return state;
|
|
|
| const updatedMetadata = {
|
| ...state.trace.traceMetadata,
|
| user_evaluation: evaluation,
|
| };
|
|
|
| return {
|
| trace: {
|
| ...state.trace,
|
| traceMetadata: updatedMetadata,
|
| },
|
|
|
| finalStep: state.finalStep ? {
|
| ...state.finalStep,
|
| metadata: {
|
| ...state.finalStep.metadata,
|
| user_evaluation: evaluation,
|
| },
|
| } : state.finalStep,
|
| };
|
| },
|
| false,
|
| 'updateTraceEvaluation'
|
| ),
|
|
|
|
|
| completeTrace: (metadata, finalState?: 'success' | 'stopped' | 'max_steps_reached' | 'error' | 'sandbox_timeout') =>
|
| set(
|
| (state) => {
|
| if (!state.trace) return state;
|
|
|
|
|
| const updatedMetadata = {
|
| ...metadata,
|
| maxSteps: metadata.maxSteps > 0
|
| ? metadata.maxSteps
|
| : (state.trace.traceMetadata?.maxSteps || 200),
|
| completed: true,
|
| };
|
|
|
|
|
| let stepType: 'success' | 'failure' | 'stopped' | 'max_steps_reached' | 'sandbox_timeout';
|
| let stepMessage: string | undefined;
|
|
|
| if (finalState === 'stopped') {
|
| stepType = 'stopped';
|
| stepMessage = 'Task stopped by user';
|
| } else if (finalState === 'max_steps_reached') {
|
| stepType = 'max_steps_reached';
|
| stepMessage = 'Maximum steps reached';
|
| } else if (finalState === 'sandbox_timeout') {
|
| stepType = 'sandbox_timeout';
|
| stepMessage = 'Sandbox timeout';
|
| } else if (finalState === 'error' || state.error) {
|
| stepType = 'failure';
|
| stepMessage = state.error || 'Task failed';
|
| } else {
|
| stepType = 'success';
|
| stepMessage = undefined;
|
| }
|
|
|
| const finalStep: FinalStep = {
|
| type: stepType,
|
| message: stepMessage,
|
| metadata: updatedMetadata,
|
| };
|
|
|
| return {
|
| trace: {
|
| ...state.trace,
|
| isRunning: false,
|
| traceMetadata: updatedMetadata,
|
| },
|
| finalStep,
|
|
|
| selectedStepIndex: null,
|
| };
|
| },
|
| false,
|
| 'completeTrace'
|
| ),
|
|
|
|
|
| setIsAgentProcessing: (isAgentProcessing) =>
|
| set({ isAgentProcessing }, false, 'setIsAgentProcessing'),
|
|
|
|
|
| setIsConnectingToE2B: (isConnectingToE2B) =>
|
| set({ isConnectingToE2B }, false, 'setIsConnectingToE2B'),
|
|
|
|
|
| setVncUrl: (vncUrl) =>
|
| set({ vncUrl }, false, 'setVncUrl'),
|
|
|
|
|
| setSelectedModelId: (selectedModelId) =>
|
| set({ selectedModelId }, false, 'setSelectedModelId'),
|
|
|
|
|
| setAvailableModels: (availableModels) =>
|
| set({ availableModels }, false, 'setAvailableModels'),
|
|
|
|
|
| setIsLoadingModels: (isLoadingModels) =>
|
| set({ isLoadingModels }, false, 'setIsLoadingModels'),
|
|
|
|
|
| setIsConnected: (isConnected) =>
|
| set({ isConnected }, false, 'setIsConnected'),
|
|
|
|
|
| setError: (error) =>
|
| set(
|
| (state) => {
|
|
|
| if (error && state.trace) {
|
| const metadata = state.trace.traceMetadata || {
|
| traceId: state.trace.id,
|
| inputTokensUsed: 0,
|
| outputTokensUsed: 0,
|
| duration: 0,
|
| numberOfSteps: state.trace.steps?.length || 0,
|
| maxSteps: 200,
|
| completed: false,
|
| final_state: null,
|
| user_evaluation: 'not_evaluated' as const,
|
| };
|
|
|
|
|
| const finalMetadata: AgentTraceMetadata = {
|
| ...metadata,
|
| maxSteps: metadata.maxSteps > 0 ? metadata.maxSteps : 200,
|
| final_state: metadata.final_state || null,
|
| user_evaluation: metadata.user_evaluation || 'not_evaluated',
|
| };
|
|
|
| const finalStep: FinalStep = {
|
| type: 'failure',
|
| message: error,
|
| metadata: finalMetadata,
|
| };
|
|
|
| return {
|
| error,
|
| finalStep,
|
| trace: {
|
| ...state.trace,
|
| isRunning: false,
|
| },
|
| selectedStepIndex: null,
|
| };
|
| }
|
| return { error };
|
| },
|
| false,
|
| 'setError'
|
| ),
|
|
|
|
|
| setSelectedStepIndex: (selectedStepIndex) =>
|
| set({ selectedStepIndex }, false, 'setSelectedStepIndex'),
|
|
|
|
|
| toggleDarkMode: () =>
|
| set((state) => ({ isDarkMode: !state.isDarkMode }), false, 'toggleDarkMode'),
|
|
|
|
|
| resetAgent: () =>
|
| set((state) => ({
|
| ...initialState,
|
| traceId: state.traceId,
|
| isDarkMode: state.isDarkMode,
|
| isConnected: state.isConnected,
|
| selectedModelId: state.selectedModelId,
|
| availableModels: state.availableModels,
|
| isLoadingModels: state.isLoadingModels
|
| }), false, 'resetAgent'),
|
| }),
|
| { name: 'AgentStore' }
|
| )
|
| );
|
|
|
|
|
| export const selectTrace = (state: AgentState) => state.trace;
|
| export const selectTraceId = (state: AgentState) => state.traceId;
|
| export const selectIsAgentProcessing = (state: AgentState) => state.isAgentProcessing;
|
| export const selectIsConnectingToE2B = (state: AgentState) => state.isConnectingToE2B;
|
| export const selectVncUrl = (state: AgentState) => state.vncUrl;
|
| export const selectSelectedModelId = (state: AgentState) => state.selectedModelId;
|
| export const selectAvailableModels = (state: AgentState) => state.availableModels;
|
| export const selectIsLoadingModels = (state: AgentState) => state.isLoadingModels;
|
| export const selectIsConnected = (state: AgentState) => state.isConnected;
|
| export const selectSteps = (state: AgentState) => state.trace?.steps;
|
| export const selectMetadata = (state: AgentState) => state.trace?.traceMetadata;
|
| export const selectError = (state: AgentState) => state.error;
|
| export const selectIsDarkMode = (state: AgentState) => state.isDarkMode;
|
| export const selectSelectedStepIndex = (state: AgentState) => state.selectedStepIndex;
|
| export const selectFinalStep = (state: AgentState) => state.finalStep;
|
|
|
|
|
| export const selectSelectedStep = (state: AgentState) => {
|
| const steps = state.trace?.steps;
|
| const selectedIndex = state.selectedStepIndex;
|
|
|
| if (selectedIndex === null || !steps || selectedIndex >= steps.length) {
|
| return null;
|
| }
|
|
|
| return steps[selectedIndex];
|
| };
|
|
|