From d795c32f79e3b6f308f33d67df1ee2b2f6532708 Mon Sep 17 00:00:00 2001 From: aliyanishfaq Date: Mon, 8 Sep 2025 15:48:24 -0700 Subject: [PATCH] code added --- CLAUDE.md | 367 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 367 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..e4bb474 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,367 @@ +# LangGraph Development Principles + +If you are coding with LangGraph, follow these principles and patterns. + +## Critical Structure Requirements + +### MANDATORY FIRST STEP +Before creating any files, **always search the codebase** for existing LangGraph-related files: +- Files with names like: `graph.py`, `main.py`, `app.py`, `agent.py`, `workflow.py` +- Files containing: `.compile()`, `StateGraph`, `create_react_agent`, `app =`, graph exports +- Any existing LangGraph imports or patterns + +**If any LangGraph files exist**: Follow the existing structure exactly. Do not create new agent.py files. + +**Only create agent.py when**: Building from completely empty directory with zero existing LangGraph files. + +- When starting from scratch, ensure all of the following: + 1. `agent.py` at project root with compiled graph exported as `app` + 2. `langgraph.json` configuration file in the same directory as the graph + 3. Proper state management defined with `TypedDict` or Pydantic `BaseModel` + 4. Test small components before building complex graphs + +## Deployment-First Principles + +**CRITICAL**: All LangGraph agents should be written for DEPLOYMENT unless otherwise specified. + +### Core Requirements: +- **NEVER ADD A CHECKPOINTER** unless explicitly requested by user +- Always export compiled graph as `app` +- Use prebuilt components when possible +- Follow model preference hierarchy: Anthropic > OpenAI > Google +- Keep state minimal (MessagesState usually sufficient) + +#### AVOID unless user specifically requests +```python +# Don't do this unless asked! +from langgraph.checkpoint.memory import MemorySaver +graph = create_react_agent(model, tools, checkpointer=MemorySaver()) +``` + +#### For existing codebases +- Always search for existing graph export patterns first +- Work within the established structure rather than imposing new patterns +- Do not create `agent.py` if graphs are already exported elsewhere + +### Standard Structure for New Projects: +``` +./agent.py # Main agent file, exports: app +./langgraph.json # LangGraph configuration +``` + +### Export Pattern: +```python +from langgraph.graph import StateGraph, START, END +# ... your state and node definitions ... + +# Build your graph +graph_builder = StateGraph(YourState) +# ... add nodes and edges ... + +# Export as 'app' for new agents from scratch +graph = graph_builder.compile() +app = graph # Required for new LangGraph agents +``` + +## Prefer Prebuilt Components + +**Always use prebuilt components when possible** - they are deployment-ready and well-tested. + +### Basic Agents - Use create_react_agent: +```python +from langgraph.prebuilt import create_react_agent + +# Simple, deployment-ready agent +graph = create_react_agent( + model=model, + tools=tools, + prompt="Your agent instructions here" +) +app = graph +``` + +### Multi-Agent Systems: + +#### Supervisor Pattern (central coordination): +```python +from langgraph_supervisor import create_supervisor + +supervisor = create_supervisor( + agents=[agent1, agent2], + model=model, + prompt="You coordinate between agents..." +) +app = supervisor.compile() +``` +Documentation: https://langchain-ai.github.io/langgraph/reference/supervisor/ + +#### Swarm Pattern (dynamic handoffs): +```python +from langgraph_swarm import create_swarm, create_handoff_tool + +alice = create_react_agent( + model, + [tools, create_handoff_tool(agent_name="Bob")], + prompt="You are Alice.", + name="Alice", +) + +workflow = create_swarm([alice, bob], default_active_agent="Alice") +app = workflow.compile() +``` +Documentation: https://langchain-ai.github.io/langgraph/reference/swarm/ + +### Only Build Custom StateGraph When: +- Prebuilt components don't fit the specific use case +- User explicitly asks for custom workflow +- Complex branching logic required +- Advanced streaming patterns needed + +## Model Preferences + +**LLM MODEL PRIORITY** (follow this order): +```python +# 1. PREFER: Anthropic +from langchain_anthropic import ChatAnthropic +model = ChatAnthropic(model="claude-3-5-sonnet-20241022") + +# 2. SECOND CHOICE: OpenAI +from langchain_openai import ChatOpenAI +model = ChatOpenAI(model="gpt-4o") + +# 3. THIRD CHOICE: Google +from langchain_google_genai import ChatGoogleGenerativeAI +model = ChatGoogleGenerativeAI(model="gemini-1.5-pro") +``` + +**NOTE**: Assume API keys are available in environment. +During development, ignore missing key errors. + +## Message and State Handling + +### CRITICAL: Extract Message Content Properly +```python +# CORRECT: Extract message content properly +result = agent.invoke({"messages": state["messages"]}) +if result.get("messages"): + final_message = result["messages"][-1] # This is a message object + content = final_message.content # This is the string content + +# WRONG: Treating message objects as strings +content = result["messages"][-1] # This is an object, not a string! +if content.startswith("Error"): # Will fail - objects don't have startswith() +``` + +### State Updates Must Be Dictionaries: +```python +def my_node(state: State) -> Dict[str, Any]: + # Do work... + return { + "field_name": extracted_string, # Always return dict updates + "messages": updated_message_list # Not the raw messages + } +``` + +## Streaming and Interrupts + +### Streaming Patterns: +- Interrupts only work with `stream_mode="updates"`, not `stream_mode="values"` +- In "updates" mode, events are structured as `{node_name: node_data, ...}` +- Check for `"__interrupt__"` key directly in the event object +- Iterate through `event.items()` to access individual node outputs + +- Interrupts appear as `event["__interrupt__"]` containing a tuple of `Interrupt` objects +- Access interrupt data via `interrupt_obj.value` where `interrupt_obj = event["__interrupt__"][0]` + +Documentation: +- LangGraph Streaming: https://langchain-ai.github.io/langgraph/how-tos/stream-updates/ +- SDK Streaming: https://langchain-ai.github.io/langgraph/cloud/reference/sdk/python_sdk_ref/#stream +- Concurrent Interrupts: https://docs.langchain.com/langgraph-platform/interrupt-concurrent + +### When to Use Interrupts: +Use `interrupt()` when you need: +- User approval for generated plans or proposed changes +- Human confirmation before executing potentially risky operations +- Additional clarification when the task is ambiguous +- User input data entry or for decision points that require human judgment +- Feedback on partially completed work before proceeding + +### Correct Interrupt Usage: +```python +# CORRECT: interrupt() pauses execution for human input +interrupt("Please confirm action") +# Execution resumes after human provides input through platform + +# AVOID: Treating interrupt() as synchronous +result = interrupt("Please confirm action") # Wrong - doesn't return values +if result == "yes": # This won't work + proceed() +``` + +## Common LangGraph Errors to Avoid + +- Incorrect `interrupt()` usage: It pauses execution, doesn't return values +- Refer to documentation for best interrupt handling practices, including waiting for user input and proper handling of it +- Wrong state update patterns: Return updates, not full state +- Missing state type annotations +- Missing state fields (current_field, user_input) +- Invalid edge conditions: Ensure all paths have valid transitions +- Not handling error states properly +- Not exporting graph as 'app' when creating new LangGraph agents from scratch +- Forgetting `langgraph.json` configuration +- **Type assumption errors**: Assuming message objects are strings, or that state fields are certain types +- **Chain operations without type checking**: Like `state.get("field", "")[-1].method()` without verifying types + +## Framework Integration Patterns + +### Integration Debugging +When building integrations, always start with debugging: + +```python +# Temporary debugging for new integrations +def my_integration_function(input_data, config): + print(f"=== DEBUG START ===") + print(f"Input type: {type(input_data)}") + print(f"Input data: {input_data}") + print(f"Config type: {type(config)}") + print(f"Config data: {config}") + + # Process... + result = process(input_data, config) + + print(f"Result type: {type(result)}") + print(f"Result data: {result}") + print(f"=== DEBUG END ===") + + return result +``` + +### Config Propagation Verification +Always verify the receiving end actually uses configuration: + +```python +# WRONG: Assuming config is used +def my_node(state: State) -> Dict[str, Any]: + response = llm.invoke(state["messages"]) + return {"messages": [response]} + +# CORRECT: Actually using config +def my_node(state: State, config: RunnableConfig) -> Dict[str, Any]: + # Extract configuration + configurable = config.get("configurable", {}) + system_prompt = configurable.get("system_prompt", "Default prompt") + + # Use configuration in messages + messages = [SystemMessage(content=system_prompt)] + state["messages"] + response = llm.invoke(messages) + return {"messages": [response]} +``` +## Patterns to Avoid + +### Don't Mix Responsibilities in Single Nodes: +```python +# AVOID: LLM call + tool execution in same node +def bad_node(state): + ai_response = model.invoke(state["messages"]) # LLM call + tool_result = tool_node.invoke({"messages": [ai_response]}) # Tool execution + return {"messages": [...]} # Mixed concerns! + +# PREFER: Separate nodes for separate concerns +def llm_node(state): + return {"messages": [model.invoke(state["messages"])]} + +def tool_node(state): + return ToolNode(tools).invoke(state) + +# Connect with edges +workflow.add_edge("llm", "tools") +``` + +### Overly Complex Agents When Simple Ones Suffice +```python +# AVOID: Unnecessary complexity +workflow = StateGraph(ComplexState) +workflow.add_node("agent", agent_node) +workflow.add_node("tools", tool_node) +# ... 20 lines of manual setup when create_react_agent would work +``` + +### Avoid Overly Complex State: +```python +# AVOID: Too many state fields +class State(TypedDict): + messages: List[BaseMessage] + user_input: str + current_step: int + metadata: Dict[str, Any] + history: List[Dict] + # ... many more fields + +# PREFER: Use MessagesState when sufficient +from langgraph.graph import MessagesState +``` + +### Wrong Export Patterns +```python +# AVOID: Wrong variable names or missing export +compiled_graph = workflow.compile() # Wrong name +# Missing: app = compiled_graph +``` + +### Incorrect interrupt() usage +```python +# AVOID: Treating interrupt() as synchronous +result = interrupt("Please confirm action") # Wrong - doesn't return values +if result == "yes": # This won't work + proceed() + +# CORRECT: interrupt() pauses execution for human input +interrupt("Please confirm action") +# Execution resumes after human provides input through platform +``` +Reference: https://langchain-ai.github.io/langgraph/concepts/streaming/#whats-possible-with-langgraph-streaming + +## LangGraph-Specific Coding Standards + +### Structured LLM Calls and Validation +When working with LangGraph nodes that involve LLM calls, always use structured output with Pydantic dataclasses: + +- Use `with_structured_output()` method for LLM calls that need specific response formats +- Define Pydantic BaseModel classes for all structured data (state schemas, LLM responses, tool inputs/outputs) +- Validate and parse LLM responses using Pydantic models +- For conditional nodes relying on LLM decisions, use structured output + +Example: `llm.with_structured_output(MyPydanticModel).invoke(messages)` instead of raw string parsing + +### General Guidelines: +- Test small components before building complex graphs +- **Avoid unnecessary complexity**: Consider if simpler approaches with prebuilt components would achieve the same goals +- Write concise and clear code without overly verbose implementations +- Only install trusted, well-maintained packages + +## Documentation Guidelines + +### When to Consult Documentation: +Always use documentation tools before implementing LangGraph code (the API evolves rapidly): +- Before creating new graph nodes or modifying existing ones +- When implementing state schemas or message passing patterns +- Before using LangGraph-specific decorators, annotations, or utilities +- When working with conditional edges, dynamic routing, or subgraphs +- Before implementing tool calling patterns within graph nodes + - When building applications that integrate multiple frameworks (e.g., LangGraph + Streamlit, LangGraph + Next.js/React), also consult the framework docs to ensure correct syntax and patterns + +### Key Documentation Resources: +- LangGraph Streaming: https://langchain-ai.github.io/langgraph/how-tos/stream-updates/ +- LangGraph Config: https://langchain-ai.github.io/langgraph/how-tos/pass-config-to-tools/ +- Supervisor Pattern: https://langchain-ai.github.io/langgraph/reference/supervisor/ +- Swarm Pattern: https://langchain-ai.github.io/langgraph/reference/swarm/ +- Agentic Concepts: https://langchain-ai.github.io/langgraph/concepts/agentic_concepts/ + +### Documentation Navigation +- Determine the base URL from the current documentation page +- For `../`, go one level up in the URL hierarchy +- For `../../`, go two levels up, then append the relative path +- Example: From `https://langchain-ai.github.io/langgraph/tutorials/get-started/langgraph-platform/setup/` with link `../../langgraph-platform/local-server` + - Go up two levels: `https://langchain-ai.github.io/langgraph/tutorials/get-started/` + - Append path: `https://langchain-ai.github.io/langgraph/tutorials/get-started/langgraph-platform/local-server` +- If you encounter an HTTP 404 error, the constructed URL is likely incorrect—rebuild it carefully \ No newline at end of file