Skip to Content
AgentPress is finally here! šŸŽ‰
ArchitectureState Management

State Management

This document describes the state management architecture used in the AgentPress monorepo.

Overview

AgentPress uses a layered state management approach:

LayerTechnologyPurpose
Global StateZustandApplication-wide state (user, agents, threads)
Server StateReact QueryData fetching, caching, synchronization
Component StateReact ContextsComponent-level shared state
Local StateuseState/useRefComponent-specific state

Zustand Stores

All stores are located in packages/lib/src/stores/.

Chat Store

File: packages/lib/src/stores/chatStore.ts

Manages agent configuration, tools, and chat threads.

interface ChatStore { agents: TAgentsConfig; // Array of agent configurations tools: ITool[]; // Available tools for agents activeThread: IThread | undefined; // Currently active chat thread adminThreads: IThread[]; // All threads visible to admin }

Actions:

  • updateAgent(agent) - Updates a single agent configuration
  • setAgents(agents) - Sets agents (supports updater functions)
  • setTools(tools) - Updates available tools
  • getTools() - Fetches tools from API
  • setActiveThread(thread) - Sets the current chat thread
  • setAdminThreads(threads) - Updates admin thread list
  • deleteThread(threadId) - Removes thread from admin list

Usage:

import { useChatStore } from "@agentpress/lib/stores"; function Component() { const { agents, activeThread, setActiveThread } = useChatStore(); // Select specific slice const agents = useChatStore((state) => state.agents); }

User Store

File: packages/lib/src/stores/userStore.ts

Manages authentication state and user session.

interface UserStore { user: IUser | undefined; // Current user object hasLoggedOut: boolean; // Flag for logout tracking }

Actions:

  • setUser(user) - Sets the authenticated user
  • logout() - Clears user and sets logout flag
  • resetLogoutFlag() - Resets the logout flag on re-login

RAG Store

File: packages/lib/src/stores/ragStore.ts

Manages knowledge bases and RAG resources.

interface RagStore { resources: Record<string, IResource[]>; // Resources by store name resourceGroups: IResourceGroupServer[]; // Knowledge base groups }

Task WebSocket Store

File: packages/lib/src/stores/taskWebSocketStore.ts

Manages task execution progress via WebSocket connections.

interface TaskWebSocketStore { activeTasks: TaskProgress[]; allTasks: TaskProgress[]; isConnected: boolean; finishedTaskNotificationQueue: Record<string, any>; }

React Query (Server State)

Used for server state synchronization and caching.

Query Pattern

import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; function Component() { const queryClient = useQueryClient(); // Query with conditional execution const { data, isLoading, error } = useQuery({ queryKey: ["embeddingModel"], queryFn: apiGetEmbeddingModel, enabled: !!user, // Only fetch when user exists }); // Mutation with cache invalidation const mutation = useMutation({ mutationFn: apiUpdateEmbeddingModel, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["embeddingModel"] }); toast.success("Updated successfully"); }, onError: (error) => { toast.error(`Failed: ${error.message}`); }, }); }

Common Query Keys

  • ["embeddingModel"] - Embedding model config
  • ["resourceGroups"] - Knowledge bases
  • ["evaluations"] - Evaluation definitions
  • ["agents"] - Agent configurations
  • ["threads"] - Conversation threads

React Context

Limited usage for component-level state sharing.

AdminHeaderContext

File: packages/ui/src/contexts/AdminHeaderContext.tsx

Manages dynamic page header content for admin pages.

interface PageHeaderActions { leftActions?: React.ReactNode; rightActions?: React.ReactNode; customTitle?: React.ReactNode; } // Usage import { useAdminHeader } from "@agentpress/ui/contexts"; function AdminPage() { const { setPageActions } = useAdminHeader(); useEffect(() => { setPageActions({ rightActions: <Button>Create</Button>, }); return () => setPageActions(null); }, []); }

API Request Pattern

File: packages/lib/src/backend/requests.ts

API functions directly update Zustand stores (side-effect-driven pattern).

export const apiGetTools = async () => { const { setTools } = useChatStore.getState(); try { const res = await fetch(`${getAgentPressConfig().apiHost}/chat/tools`); const data = await res.json(); setTools(data); // Direct store update } catch (e) { throw new Error(`Failed to fetch tools: ${e}`); } };

Key Characteristics:

  • Direct store access via .getState() (not hooks)
  • Side effects within request functions
  • Async/await pattern with error propagation

Provider Architecture

AgentPressProvider

File: packages/ui/src/core/providers/AgentPressProvider.tsx

export function AgentPressProvider({ children, queryClient }: Props) { const [queryClient] = useState( () => customQueryClient || new QueryClient() ); // Initialize WebSocket hook - populates Zustand store useTaskWebSocket(); return ( <AgentPressErrorBoundary> <QueryClientProvider client={queryClient}> <ThemeProvider attribute="class" enableSystem> <TooltipProvider delayDuration={0}> {children} </TooltipProvider> </ThemeProvider> </QueryClientProvider> </AgentPressErrorBoundary> ); }

Configuration Management

File: packages/lib/src/config.ts

Singleton configuration for library settings.

export interface LibConfig { apiHost: string; toolsUI: IToolsUI; isGuest?: boolean; appType: EAppType; voiceTranscription?: boolean; } export function setAgentPressConfig(config: Partial<LibConfig>): void; export function getAgentPressConfig(): LibConfig;

Important: Must be called in routes.tsx BEFORE app initialization.

Best Practices

1. Separation of Concerns

  • Global state in Zustand stores
  • Server state in React Query
  • Component state with hooks
  • API calls in dedicated requests module

2. Immutable Updates

// Zustand enforces immutability set((state) => ({ agents: state.agents.map(a => a.id === id ? { ...a, ...updates } : a ), }));

3. Performance Optimization

// Select specific slices to prevent unnecessary re-renders const agents = useChatStore((state) => state.agents); // Memoize expensive computations const filtered = useMemo(() => agents.filter(a => a.enabled), [agents] );

4. Error Handling

// Toast notifications for errors onError: (error) => { toast.error(`Failed: ${error.message}`); } // Error boundaries for React rendering <AgentPressErrorBoundary> {children} </AgentPressErrorBoundary>

Dependencies

PackageVersionPurpose
zustand^5.0.0Global state management
@tanstack/react-query^5.83.0Server state
@tanstack/react-router^1.130.0Routing
ai^5.0.59AI SDK with streaming
zod^4.1.12Runtime validation
Last updated on