Technical documentation for the LangGraph-based agent execution system in Material Kai Vision Platform.
The platform uses LangGraph for agent orchestration, providing:
┌─────────────────────────────────────┐
│ User Request │
└──────────────┬──────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────┐ │ StateGraph │ │ ┌────────────────┐ ┌────────────────────┐ │ │ │ START │ │ END │ │ │ └───────┬────────┘ └────────────────────┘ │ │ │ ▲ │ │ ▼ │ │ │ ┌───────────────────┐ shouldContinue() ┌────────────┴───────┐ │ │ │ agentNode │──────────────────────────│ toolsNode │ │ │ │ (LLM invoke) │◄─────────────────────────│ (execute tools) │ │ │ └───────────────────┘ └────────────────────┘ │ │ │ │ │ │ (no tool calls) │ │ ▼ │ │ ┌───────────────────┐ │ │ │ END │ │ │ └───────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ AgentStateAnnotation │ │ │ │ - messages: BaseMessage[] (reducer: append) │ │ │ │ - systemPrompt: string │ │ │ │ - toolResults: any[] (reducer: append) │ │ │ │ - collectedProducts: any[] (reducer: append) │ │ │ │ - iteration: number │ │ │ │ - inputTokens/outputTokens: number (reducer: sum) │ │ │ │ - finalResponse: string | null │ │ │ │ - generationJob: any | null │ │ │ └─────────────────────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────────────────────┘ │ ▼ ┌────────────────────────────────────────────┐ │ SupabaseCheckpointer │ │ (agent_checkpoints table) │ └────────────────────────────────────────────┘ │ ▼ ┌────────────────────────────────────────────┐ │ LongTermMemory │ │ (agent_memories table) │ └────────────────────────────────────────────┘
The state schema defines what data flows through the graph. It uses Annotation.Root from LangGraph with the following fields:
BaseMessage[] with an append reducer ([...prev, ...next]). Accumulates all messages. Default: [].string with a replace reducer ((_, next) => next). Holds the current system prompt. Default: ''.any[] with an append reducer. Collects tool results during execution. Default: [].any[] with an append reducer. Products found during search/recommendations. Default: [].number with a replace reducer. Current iteration count. Default: 0.number with a sum reducer (prev + next). Accumulated input token count. Default: 0.number with a sum reducer. Accumulated output token count. Default: 0.number with a sum reducer. Turn count for billing. Default: 0.string | null with a replace reducer. Set when the agent produces its final answer. Default: null.any | null with a replace reducer. Set if a 3D generation job is triggered. Default: null.| Reducer | Behavior | Use Case |
|---|---|---|
| Append | [...prev, ...next] |
Accumulating messages, results |
| Replace | (_, next) => next |
Single values like systemPrompt |
| Sum | prev + next |
Token counts, turn counts |
The createAgentGraph function accepts a model, a tools array, and an optional onChunk streaming callback. It builds a StateGraph with AgentStateAnnotation, adds an agent node and a tools node, connects START to agent, adds a conditional edge from agent using shouldContinue, adds an edge from tools back to agent, and compiles the graph. The maximum iteration limit is set to 10.
The shouldContinue function examines the last message in the state. If the iteration count has reached the maximum (10), it returns END. If the last message contains tool calls, it returns 'tools'. Otherwise (no tool calls), it returns END.
Enables resumable conversations by persisting state to Supabase.
The agent_checkpoints table stores checkpoint data indexed by thread ID. It has a UUID primary key, a thread_id text field (unique), a checkpoint_data JSONB column, and created_at/updated_at timestamps. An index on thread_id supports fast lookup.
The SupabaseCheckpointer class provides three methods:
agent_checkpoints table for the given thread_id and returns the checkpoint_data JSONB, or null if not found.updated_at on conflict with thread_id.thread_id.Thread IDs are constructed as ${agentId}-${conversationId} when a conversationId is provided, or ${agentId}-${crypto.randomUUID()} for new conversations.
Stores user preferences, facts, and context across conversations.
The agent_memories table has a UUID primary key, foreign key references to auth.users(id) for user_id, a workspace_id UUID, an agent_id text field, a memory_type text field constrained to ('preference', 'fact', 'context', 'relationship'), a content text field, an optional conversation_id UUID, a metadata JSONB field, and a created_at timestamp. Indexes cover (user_id, workspace_id), memory_type, and agent_id.
| Type | Description | Example |
|---|---|---|
preference |
User preferences | "Prefers modern minimalist style" |
fact |
Factual information | "Working on hotel lobby project" |
context |
Conversational context | "Previously discussed marble flooring" |
relationship |
Entity relationships | "Client is ABC Corp" |
The LongTermMemory class provides three methods:
agent_memories with the provided agentId, type, content, optional conversationId, and optional metadata.agent_memories for the user and workspace, filtered by optional agentId and types array, ordered by created_at descending, limited to options.limit (default 20).After each agent turn, an extractAndStoreMemories function runs asynchronously (non-blocking). It constructs an extraction prompt with the user's input and the assistant's response, asks the LLM to identify notable information (preferences, facts, context, relationships), and stores any extracted memories. Only truly notable information is stored; the function returns an empty array if nothing is notable.
The executeAgent function orchestrates the full agent lifecycle:
AGENT_CONFIGS[agentId] and fetch the system prompt.agentId and conversationId (or a new UUID).createAgentGraph(selectedModel, tools, onChunk).HumanMessage, the enriched system prompt, and zeroed counters.extractAndStoreMemories asynchronously (errors are silently caught).The onChunk callback is invoked at multiple points during execution to provide real-time progress to the client:
For critical actions like 3D generation or large purchases, a future shouldRequireApproval conditional edge is planned. It would inspect the last message's tool calls for critical tool names (e.g., generate3D, createQuote, submitOrder) and route to a human_approval node instead of continuing directly to tools.
The edge function's deno.json imports include:
@langchain/anthropic — Anthropic LLM integration@langchain/core/tools — Tool base classes@langchain/core/messages — Message types (HumanMessage, etc.)@langchain/langgraph — StateGraph, START, END, Annotationzod — Schema validation for tool inputs@supabase/supabase-js@2 — Supabase clientToken usage is accumulated across all iterations using the sum reducers on inputTokens and outputTokens. In each agentNode invocation, the function reads response.response_metadata?.usage to extract input_tokens and output_tokens, then returns them as part of the state update. Because the reducers sum values across iterations, the final state contains the total tokens used for the entire conversation turn. The total is computed as finalState.inputTokens + finalState.outputTokens.
agent_checkpoints table permissionsagent_memories table existsmaxIterations limitshouldContinue logicLast Updated: January 31, 2026 Version: 1.0.0 Status: Production Maintainer: Development Team