bu-agent-sdk

repository·main·Indexed 20 days ago

https://github.com/browser-use/agent-sdk

A minimalist agent framework designed around a simple for-loop of tool calls. It provides a lightweight Agent class with native tool calling, support for multiple LLM providers (OpenAI, Anthropic, Google, Groq, Ollama, DeepSeek, Mistral), and features for context management including ephemeral messages and CompactionConfig. The SDK supports Pydantic models for tool parameters, FastAPI-style dependency injection via Depends, and real-time event streaming through agent.query_stream().

Tokens
2.7K
Snippets
14
Records
17
Agent score
72%

What's inside bu-agent-sdk

  1. Manage Multi-turn Conversations

    main

    The Agent preserves conversation history between consecutive .query() calls.

    • To maintain context: Simply call .query() multiple times.
    • To reset context: Call agent.clear_history() to wipe the conversation memory.
    await agent.query("My name is Alice")
    await agent.query("What's my name?")  # Remembers "Alice"
    agent.clear_history()  # Reset
  2. Use the Done Tool Pattern for explicit completion

    main

    To prevent agents from finishing prematurely, use the 'Done Tool Pattern'. Define a tool that raises TaskComplete and set require_done_tool=True in the Agent configuration. This forces the agent to use the explicit completion tool to finish.

    @tool("Signal completion")
    async def done(message: str) -> str:
        raise TaskComplete(message)
    
    agent = Agent(
        llm=llm,
        tools=[..., done],
        require_done_tool=True,  # Forces autonomous mode to use the done tool
    )
  3. How to use LangChain models with ChatLangchain

    main

    While not officially supported, you can use LangChain models by utilizing the ChatLangchain class. This is possible due to the underlying implementation of LLMs in the SDK.

    For a concrete implementation pattern, refer to the LangChain example in the repository: /examples/models/langchain/example.py.

  4. Quick Start with the Agent

    main

    The Agent provides a simple agentic loop with native tool calling. To use it, initialize an Agent with an LLM (e.g., ChatOpenAI) and a list of functions decorated with @tool.

    from bu_agent_sdk import Agent
    from bu_agent_sdk.llm import ChatOpenAI
    from bu_agent_sdk.tools import tool
    
    @tool("Add two numbers")
    async def add(a: int, b: int) -> int:
        return a + b
    
    agent = Agent(
        llm=ChatOpenAI(model="gpt-4o"),
        tools=[add],
    )
    
    result = await agent.query("What is 2 + 2?")
  5. Quick Start with Agent and Tools

    main

    To create a basic agent, define asynchronous functions decorated with @tool and pass them along with an LLM provider to the Agent class. Use agent.query() to execute a task.

    Note: To signal explicit task completion, raise a TaskComplete exception from within a tool.

    import asyncio
    from bu_agent_sdk import Agent, tool, TaskComplete
    from bu_agent_sdk.llm import ChatAnthropic
    
    @tool("Add two numbers")
    async def add(a: int, b: int) -> int:
        return a + b
    
    @tool("Signal task completion")
    async def done(message: str) -> str:
        raise TaskComplete(message)
    
    agent = Agent(
        llm=ChatAnthropic(model="claude-sonnet-4-20250514"),
        tools=[add, done],
    )
    
    async def main():
        result = await agent.query("What is 2 + 3?")
        print(result)
    
    asyncio.run(main())
  6. Configure Context Compaction

    main

    Use CompactionConfig to automatically summarize conversation history when the context approaches its limit. Set the threshold_ratio to determine when compaction should trigger.

    from bu_agent_sdk.agent import CompactionConfig
    
    agent = Agent(
        llm=llm,
        tools=tools,
        compaction=CompactionConfig(threshold_ratio=0.80),
    )
  7. Use Pydantic Models for Tool Parameters

    main

    To group related parameters for a tool, pass a Pydantic BaseModel as a single argument to the tool function. This allows for structured and validated tool inputs.

    from pydantic import BaseModel, Field
    
    class EmailParams(BaseModel):
        to: str = Field(description="Recipient")
        subject: str
        body: str
    
    @tool("Send an email")
    async def send_email(params: EmailParams) -> str:
        return f"Sent to {params.to}"
  8. Using Mistral LLMs with ChatMistral

    main

    To use Mistral, use the ChatMistral class.

    Configuration:

    • Requires the MISTRAL_API_KEY environment variable.
    • Supports an optional MISTRAL_BASE_URL environment variable.

    Behavioral Notes:

    • Structured Outputs: The SDK automatically strips unsupported JSON schema keywords such as minLength, maxLength, pattern, and format to ensure compatibility.
    • Generation: Uses max_tokens and supports an optional safe_prompt flag.
  9. Stream Agent Events

    main

    Use agent.query_stream() to iterate over real-time events occurring during the agent's execution. You can use pattern matching to handle different event types:

    • ToolCallEvent: Triggered when the agent decides to call a tool. Contains tool (name) and args.
    • ToolResultEvent: Triggered when a tool execution completes. Contains tool (name) and result.
    • FinalResponseEvent: Triggered when the agent provides its final answer. Contains content (text).
    from bu_agent_sdk.agent import ToolCallEvent, ToolResultEvent, FinalResponseEvent
    
    async for event in agent.query_stream("do something"):
        match event:
            case ToolCallEvent(tool=name, args=args):
                print(f"Calling {name}: {args}")
            case ToolResultEvent(tool=name, result=result):
                print(f"{name} returned: {result}")
            case FinalResponseEvent(content=text):
                print(f"Done: {text}")
  10. Manage context with Ephemeral Messages

    main

    To prevent large tool outputs (like browser states or screenshots) from consuming too much context, use the ephemeral parameter in the @tool decorator. This tells the agent to only keep the last N messages for that specific tool.

    @tool("Get browser state", ephemeral=3)  # Keep last 3 only
    async def get_state() -> str:
        return massive_dom_and_screenshot