1"use strict";(self.webpackChunkspoonos_cookbook=self.webpackChunkspoonos_cookbook||[]).push([[5310],{13235:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>o,contentTitle:()=>l,default:()=>h,frontMatter:()=>i,metadata:()=>s,toc:()=>d});const s=JSON.parse('{"id":"graph-system/quick-start","title":"Quick Start","description":"Build your first LLM-powered graph in 2 minutes","source":"@site/docs/graph-system/quick-start.md","sourceDirName":"graph-system","slug":"/graph-system/quick-start","permalink":"/docs/graph-system/quick-start","draft":false,"unlisted":false,"editUrl":"https://github.com/XSpoonAi/spoon-doc/tree/main/docs/graph-system/quick-start.md","tags":[],"version":"current","sidebarPosition":2,"frontMatter":{"sidebar_position":2,"title":"Quick Start","description":"Build your first LLM-powered graph in 2 minutes"},"sidebar":"tutorialSidebar","previous":{"title":"Graph System Overview","permalink":"/docs/graph-system/"},"next":{"title":"Core Concepts","permalink":"/docs/graph-system/core-concepts"}}');var r=t(74848),a=t(28453);const i={sidebar_position:2,title:"Quick Start",description:"Build your first LLM-powered graph in 2 minutes"},l="Quick Start",o={},d=[{value:"Prerequisites",id:"prerequisites",level:2},{value:"2-Minute Hello World (no LLM)",id:"2-minute-hello-world-no-llm",level:2},{value:"Your First LLM Graph",id:"your-first-llm-graph",level:2},{value:"Understanding the Code",id:"understanding-the-code",level:2},{value:"1. Define Your State Schema",id:"1-define-your-state-schema",level:3},{value:"2. Create an LLM-Powered Node",id:"2-create-an-llm-powered-node",level:3},{value:"3. Build and Execute",id:"3-build-and-execute",level:3},{value:"Multi-Step LLM Workflow",id:"multi-step-llm-workflow",level:2},{value:"What Just Happened?",id:"what-just-happened",level:2},{value:"Quick Reference",id:"quick-reference",level:2},{value:"What's Next?",id:"whats-next",level:2},{value:"Core Concepts \u2192",id:"core-concepts-",level:3},{value:"Building Graphs \u2192",id:"building-graphs-",level:3},{value:"Examples \u2192",id:"examples-",level:3}];function c(e){const n={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",mermaid:"mermaid",ol:"ol",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,a.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.header,{children:(0,r.jsx)(n.h1,{id:"quick-start",children:"Quick Start"})}),"\n",(0,r.jsx)(n.p,{children:"Get up and running with the SpoonOS Graph System in under 2 minutes. This guide shows you how to create an LLM-powered graph workflow."}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"You will learn:"})," how to define state, add a node, run a graph, and retrieve a checkpoint\n",(0,r.jsx)(n.strong,{children:"Best for:"})," first-time users\n",(0,r.jsx)(n.strong,{children:"Time to complete:"})," ~2 minutes"]}),"\n",(0,r.jsx)(n.h2,{id:"prerequisites",children:"Prerequisites"}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:["Complete the steps in ",(0,r.jsx)(n.strong,{children:(0,r.jsx)(n.a,{href:"/docs/getting-started/installation",children:"Getting Started / Installation"})})]}),"\n",(0,r.jsxs)(n.li,{children:["Import public APIs from ",(0,r.jsx)(n.code,{children:"spoon_ai.graph"})," (e.g., ",(0,r.jsx)(n.code,{children:"from spoon_ai.graph import StateGraph, END"}),")"]}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"2-minute-hello-world-no-llm",children:"2-Minute Hello World (no LLM)"}),"\n",(0,r.jsxs)(n.p,{children:["The smallest runnable graph: one node, one edge, and a checkpoint read with ",(0,r.jsx)(n.code,{children:"thread_id"}),"."]}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:'import asyncio\nfrom typing import TypedDict\nfrom spoon_ai.graph import StateGraph, END\n\nclass HelloState(TypedDict):\n name: str\n message: str\n\nasync def say_hello(state: HelloState) -> dict:\n return {"message": f"Hello, {state[\'name\']}!"}
1\n\ngraph = StateGraph(HelloState)\ngraph.add_node("hello", say_hello)\ngraph.set_entry_point("hello")\ngraph.add_edge("hello", END)\napp = graph.compile()\n\nasync def main():\n config = {"configurable": {"thread_id": "hello-demo"}}\n result = await app.invoke({"name": "Graph", "message": ""}, config=config)\n print(result["message"]) # Hello, Graph!\n\n # Checkpoint read: requires thread_id (captures state before node execution)\n snapshot = graph.get_state(config)\n print("checkpoint values:", snapshot.values)\n\nif __name__ == "__main__":\n asyncio.run(main())\n'})}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"Run it:"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"python my_first_graph.py\n"})}),"\n",(0,r.jsx)(n.h2,{id:"your-first-llm-graph",children:"Your First LLM Graph"}),"\n",(0,r.jsx)(n.p,{children:"Here's a complete graph that uses LLM to analyze user queries:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:'import asyncio\nfrom typing import TypedDict, List\nfrom spoon_ai.graph import StateGraph, END\nfrom spoon_ai.llm import LLMManager\nfrom spoon_ai.schema import Message\n\nclass ChatState(TypedDict):\n messages: List[dict]\n user_query: str\n llm_response: str\n\n# Initialize LLM\nllm = LLMManager()\n\nasync def analyze_query(state: ChatState) -> dict:\n """Use LLM to analyze the user query."""\n response = await llm.chat([\n Message(role="system", content="You are a helpful crypto assistant."),\n Message(role="user", content=state["user_query"])\n ])\n return {"llm_response": response.content}\n\n# Build graph\ngraph = StateGraph(ChatState)\ngraph.add_node("analyze", analyze_query)\ngraph.set_entry_point("analyze")\ngraph.add_edge("analyze", END)\napp = graph.compile()\n\nasync def main():\n result = await app.invoke({\n "messages": [],\n "user_query": "What is Bitcoin?",\n "llm_response": ""\n })\n print(result["llm_response"])\n\nif __name__ == "__main__":\n asyncio.run(main())\n'})}),"\n",(0,r.jsx)(n.p,{children:(0,r.jsx)(n.strong,{children:"Run it:"})}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-bash",children:"python my_first_graph.py\n"})}),"\n",(0,r.jsx)(n.h2,{id:"understanding-the-code",children:"Understanding the Code"}),"\n",(0,r.jsx)(n.h3,{id:"1-define-your-state-schema",children:"1. Define Your State Schema"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:"from typing import Any, Dict, List, TypedDict\n\n\nclass ChatState(TypedDict):\n messages: List[Dict[str, Any]] # Conversation history\n user_query: str # User input\n llm_response: str # LLM output\n"})}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"State"})," flows through your entire graph. Every node can read from it and write to it."]}),"\n",(0,r.jsx)(n.admonition,{title:"TypedDict Benefits",type:"tip",children:(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsx)(n.li,{children:"IDE autocomplete for state fields"}),"\n",(0,r.jsx)(n.li,{children:"Type checking to catch bugs early"}),"\n",(0,r.jsx)(n.li,{children:"Self-documenting code"}),"\n"]})}),"\n",(0,r.jsx)(n.h3,{id:"2-create-an-llm-powered-node",children:"2. Create an LLM-Powered Node"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:'import os\nfrom typing import Any, Dict, List, TypedDict\n\nfrom spoon_ai.llm import LLMManager\nfrom spoon_ai.schema import Message\n\n\nclass ChatState(TypedDict, total=False):\n messages: List[Dict[str, Any]]\n user_query: str\n llm_response: str\n\n\nllm = LLMManager()\n\n\nasync def analyze_query(state: ChatState) -> dict:\n """Use LLM to analyze the user query."""\n if os.getenv("DOC_SNIPPET_MODE") == "1":\n return {"llm_response": f"(stub) analyzed: {state.get(\'user_query\', \'\')}"}\n\n response = await llm.chat([\n Message(role="system", content="You are a helpful crypto assistant."),\n Message(role="user", content=state["user_query"])\n ], max_tokens=200)\n return {"llm_response": response.content}\n'})}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Nodes"})," are async functions that:"]}),"\n",(0,r.jsxs)(n.ul,{children:["\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Input"}),": Receive the full state dictionary"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Process"}),": Call LLM, tools, or external APIs"]}),"\n",(0,r.jsxs)(n.li,{children:[(0,r.jsx)(n.strong,{children:"Output"}),": Return a partial update (only changed fields)"]}),"\n"]}),"\n",(0,r.jsx)(n.h3,{id:"3-build-and-execute",children:"3. Build and Execute"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:'from typing import Any, Dict, List, TypedDict\n\nfrom spoon_ai.graph import StateGraph, END\n\n\nclass ChatState(TypedDict, total=False):\n messages: List[Dict[str, Any]]\n user_query: str\n llm_response: str\n\n\nasync def analyze_query(state: ChatState) -> dict:\n # Keep this snippet runnable without requiring an LLM key.\n return {"llm_response": f"(stub) analyzed: {state.get(\'user_query\', \'\')}"}
1\n\n\ngraph = StateGraph(ChatState)\ngraph.add_node("analyze", analyze_query)\ngraph.set_entry_point("analyze")\ngraph.add_edge("analyze", END)\napp = graph.compile()\n'})}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.strong,{children:"Three essential steps"}),":"]}),"\n",(0,r.jsxs)(n.ol,{children:["\n",(0,r.jsxs)(n.li,{children:["Create ",(0,r.jsx)(n.code,{children:"StateGraph"})," with your state schema"]}),"\n",(0,r.jsxs)(n.li,{children:["Add nodes with ",(0,r.jsx)(n.code,{children:".add_node(name, function)"})]}),"\n",(0,r.jsx)(n.li,{children:"Set entry point and compile"}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"multi-step-llm-workflow",children:"Multi-Step LLM Workflow"}),"\n",(0,r.jsx)(n.p,{children:"Here's a more realistic example with multiple LLM calls:"}),"\n",(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:'import asyncio\nimport os\nfrom typing import TypedDict, List\nfrom spoon_ai.graph import StateGraph, END\nfrom spoon_ai.llm import LLMManager\nfrom spoon_ai.schema import Message\n\nclass AnalysisState(TypedDict):\n user_query: str\n intent: str\n analysis: str\n final_response: str\n\nllm = LLMManager()\n\nasync def classify_intent(state: AnalysisState) -> dict:\n """LLM classifies the user\'s intent."""\n if os.getenv("DOC_SNIPPET_MODE") == "1":\n return {"intent": "analysis_request"}\n response = await llm.chat([\n Message(role="system", content="""Classify the user query into one of:\n - price_query: asking about price\n - analysis_request: asking for market analysis\n - general_question: other questions\n Reply with only the category name."""),\n Message(role="user", content=state["user_query"])\n ])\n return {"intent": response.content.strip().lower()}\n\nasync def generate_analysis(state: AnalysisState) -> dict:\n """LLM generates detailed analysis."""\n if os.getenv("DOC_SNIPPET_MODE") == "1":\n return {"analysis": f"(stub) analysis for: {state[\'user_query\']}"}\n response = await llm.chat([\n Message(role="system", content="You are a crypto analyst. Provide detailed analysis."),\n Message(role="user", content=f"Analyze: {state[\'user_query\']}")\n ])\n return {"analysis": response.content}\n\nasync def format_response(state: AnalysisState) -> dict:\n """LLM formats the final response."""\n if os.getenv("DOC_SNIPPET_MODE") == "1":\n return {"final_response": f"(stub) summary: {state.get(\'analysis\', \'\')[:80]}..."}\n response = await llm.chat([\n Message(role="system", content="Format this analysis into a concise, user-friendly response."),\n Message(role="user", content=f"Intent: {state[\'intent\']}\\nAnalysis: {state[\'analysis\']}")\n ])\n return {"final_response": response.content}\n\n# Build graph: classify -> analyze ->
1 format\ngraph = StateGraph(AnalysisState)\ngraph.add_node("classify", classify_intent)\ngraph.add_node("analyze", generate_analysis)\ngraph.add_node("format", format_response)\n\ngraph.set_entry_point("classify")\ngraph.add_edge("classify", "analyze")\ngraph.add_edge("analyze", "format")\ngraph.add_edge("format", END)\n\napp = graph.compile()\n\nasync def main():\n result = await app.invoke({\n "user_query": "What do you think about Bitcoin\'s price trend?",\n "intent": "",\n "analysis": "",\n "final_response": ""\n })\n print(f"Intent: {result[\'intent\']}")\n print(f"Response: {result[\'final_response\']}")\n\nif __name__ == "__main__":\n asyncio.run(main())\n'})}),"\n",(0,r.jsx)(n.h2,{id:"what-just-happened",children:"What Just Happened?"}),"\n",(0,r.jsx)(n.mermaid,{value:"graph LR\n A[User Query] --\x3e B[Classify Intent<br/>LLM Call 1]\n B --\x3e C[Generate Analysis<br/>LLM Call 2]\n C --\x3e D[Format Response<br/>LLM Call 3]\n D --\x3e E[Final Output]\n\n style A fill:#e1f5fe\n style E fill:#c8e6c9\n style B fill:#fff3e0\n style C fill:#fff3e0\n style D fill:#fff3e0"}),"\n",(0,r.jsxs)(n.ol,{children:["\n",(0,r.jsx)(n.li,{children:"User provides a query"}),"\n",(0,r.jsx)(n.li,{children:"First LLM call classifies the intent"}),"\n",(0,r.jsx)(n.li,{children:"Second LLM call generates detailed analysis"}),"\n",(0,r.jsx)(n.li,{children:"Third LLM call formats the response"}),"\n",(0,r.jsx)(n.li,{children:"Final state contains all intermediate and final results"}),"\n"]}),"\n",(0,r.jsx)(n.h2,{id:"quick-reference",children:"Quick Reference"}),"\n",(0,r.jsxs)(n.table,{children:[(0,r.jsx)(n.thead,{children:(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.th,{children:"Component"}),(0,r.jsx)(n.th,{children:"Purpose"}),(0,r.jsx)(n.th,{children:"Example"})]})}),(0,r.jsxs)(n.tbody,{children:[(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"StateGraph(schema)"})}),(0,r.jsx)(n.td,{children:"Create a new graph"}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"graph = StateGraph(MyState)"})})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:".add_node(name, fn)"})}),(0,r.jsx)(n.td,{children:"Add an LLM-powered step"}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:'graph.add_node("analyze", llm_fn)'})})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:".add_edge(from, to)"})}),(0,r.jsx)(n.td,{children:"Connect nodes"}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:'graph.add_edge("a", "b")'})})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:".set_entry_point(name)"})}),(0,r.jsx)(n.td,{children:"Set starting node"}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:'graph.set_entry_point("start")'})})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:".compile()"})}),(0,r.jsx)(n.td,{children:"Prepare for execution"}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"app = graph.compile()"})})]}),(0,r.jsxs)(n.tr,{children:[(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:".invoke(state)"})}),(0,r.jsx)(n.td,{children:"Run the graph"}),(0,r.jsx)(n.td,{children:(0,r.jsx)(n.code,{children:"result = await app.invoke({...})"})})]})]})]}),"\n",(0,r.jsx)(n.h2,{id:"whats-next",children:"What's Next?"}),"\n",(0,r.jsx)(n.p,{children:"Now that you've built your first LLM-powered graph:"}),"\n",(0,r.jsx)(n.h3,{id:"core-concepts-",children:(0,r.jsx)(n.a,{href:"/docs/graph-system/core-concepts",children:"Core Concepts \u2192"})}),"\n",(0,r.jsx)(n.p,{children:"Deep dive into State, Nodes, Edges, and Checkpointing."}),"\n",(0,r.jsx)(n.h3,{id:"building-graphs-",children:(0,r.jsx)(n.a,{href:"/docs/graph-system/building-graphs",children:"Building Graphs \u2192"})}),"\n",(0,r.jsx)(n.p,{children:"Learn the three API styles: Imperative, Declarative, and High-Level."}),"\n",(0,r.jsx)(n.h3,{id:"examples-",children:(0,r.jsx)(n.a,{href:"/docs/graph-system/examples",children:"Examples \u2192"})}),"\n",(0,r.jsx)(n.p,{children:"See practical patterns like LLM routing, parallel execution, and human-in-the-loop."})]})}function h(e={}){const{wrapper:n}={...(0,a.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(c,{...e})}):c(e)}},28453:(e,n,t)=>{t.d(n,{R:()=>i,x:()=>l});var s=t(96540);const r={},a=s.createContext(r);function i(e){const n=s.useContext(a);return s.useMemo(function(){return"function"==typeof e?e(n):{...n,...e}},[n,e])}function l(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:i(e.components),s.createElement(a.Provider,{value:n},e.children)}}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.