LlamaBot

repository·main·Indexed 41 days ago

https://github.com/ericmjl/llamabot

A Pythonic interface to LLMs version 0.19.16 that leverages LiteLLM to support various model providers, including OpenAI, Mistral, Ollama, and LMStudio. It provides specialized bot types such as SimpleBot for stateless tasks, ToolBot for function calling, QueryBot for RAG with document stores, and StructuredBot for Pydantic-validated outputs. Features include an Experiment context manager for prompt tracking, ImageBot for image generation, and CLI tools for blog summarization and documentation generation.

Tokens
65.4K
Snippets
223
Records
336
Agent score
50%

What's inside LlamaBot

  1. What is StructuredBot?

    main

    StructuredBot is a specialized bot class designed for scenarios requiring guaranteed structured outputs from LLMs. Unlike SimpleBot, which returns raw text, StructuredBot provides:

    • Pydantic schema validation: Enforces the structure defined by your Pydantic models.
    • Automatic retries: Automatically attempts to fix the output if the LLM produces invalid data.
    • Validated objects: Returns actual Pydantic model instances instead of strings.
    • Error reporting: Provides clear error messages when validation fails after all retry attempts.
  2. What is AgentBot and how does it work?

    main

    AgentBot is a graph-based agent that uses PocketFlow to orchestrate tool execution. Instead of using simple loops, it builds a flow graph consisting of:

    1. A decision node (DecideNode): Analyzes the conversation and selects which tool to execute.
    2. Tool nodes: Execute the selected tools.
    3. Loopback: Tools can loop back to the decision node (except terminal tools).
    4. Terminal nodes: The flow continues until a terminal node is reached.

    This approach allows for visual flow representation, flexible orchestration, and automatic tool wrapping where you provide plain callables.

  3. What is ToolBot and when to use it

    main

    ToolBot is a specialized bot class designed for tool selection and execution rather than multi-turn conversation. It analyzes user requests, selects the most appropriate tool from a provided toolkit, extracts necessary arguments, and returns a single function call with the proper arguments.

    Use ToolBot for:

    • Automation workflows: Executing specific functions based on intent.
    • Data analysis: Running custom code or analysis tools.
    • API integrations: Calling external services via function calling.
    • Single-turn scenarios: When you need a direct action rather than a chat.

    Comparison with other bots:

    • SimpleBot: Focuses on conversation and text generation.
    • AgentBot: Multi-turn planning and execution with multiple tools.
    • QueryBot: Document retrieval and question answering.
  4. How ChatMemory works: Linear vs Graph memory

    main

    LlamaBot provides a unified ChatMemory class that supports two distinct memory models depending on your needs:

    1. Linear Memory (lmb.ChatMemory()):

      • Behavior: Stores messages in a simple chronological order (e.g., H1 → A1 → H2 → A2).
      • Pros: Extremely fast, no additional LLM calls, low latency.
      • Best for: Simple Q&A, real-time chat, or performance-critical applications where recent history is sufficient.
    2. Graph Memory (lmb.ChatMemory.threaded(model="...")):

      • Behavior: Uses an LLM to intelligently connect messages into a conversation graph based on semantic content. This allows for multi-threaded discussions where the bot can jump between topics and return to them later.
      • Pros: Smart semantic threading, maintains context across complex, multi-topic discussions.
      • Cons: Slower, requires 1-2 LLM calls per message for threading/summarization.
      • Best for: Complex research, multi-topic analysis, or long-running conversations.
    import llamabot as lmb
    
    # Linear (Fast, no LLM calls)
    memory_linear = lmb.ChatMemory()
    
    # Graph (Intelligent threading, uses LLM)
    memory_graph = lmb.ChatMemory.threaded(model="gpt-4o-mini")
  5. Use advanced Jinja2 templating in prompts

    main

    Since @prompt uses Jinja2, you can use complex logic within your docstrings to create dynamic prompts:

    • Loops: Iterate over lists or complex data structures.
    • Conditionals: Include or exclude content based on function arguments.
    • Variables: Inject any function argument into the template.

    Note: The decorator automatically validates that every {{ variable }} in the docstring has a corresponding parameter in the function signature.

    @prompt(role="user")
    def complex_prompt(items, include_details=False):
        """Review the following items:
        {% for item in items %}
        - {{ item }}
        {% if include_details %}
          (Details included)
        {% endif %}
        {% endfor %}
        """
  6. Create terminal tools to end workflows

    main

    By default, tools loop back to a decision node (controlled by loopback_name="decide"). If you want a tool to signal the end of a bot's execution (e.g., sending a final response to a user), set loopback_name=None.

    This is required for tools like respond_to_user or return_object_to_user to prevent the bot from attempting to make further decisions after the final output is generated.

    from llamabot.components.tools import tool
    
    @tool(loopback_name=None)
    def respond_to_user(response: str) -> str:
        """Respond to the user with a message.
    
        :param response: The message to send
        :return: The response message
        """
        return response
  7. Configure ChatMemory node selection strategies

    main

    The behavior of ChatMemory during append and retrieve operations is determined by the node_selector provided during initialization.

    • LinearNodeSelector (Default): Messages are appended in a simple chronological sequence. append always connects the new message to the most recent assistant message. retrieve simply returns the $N$ most recent messages.
    • LLMNodeSelector: Uses an LLM to analyze the content of a new message and determine which existing conversation thread it belongs to. This allows for non-linear, branching conversation histories. retrieve uses semantic search to find relevant nodes and then traverses their parent paths to provide full context.
  8. Use AsyncSimpleBot for async streaming

    main
    The standard SimpleBot is synchronous and its __call__ method returns a synchronous generator when using certain stream targets. For async token streaming or await completion (especially in async HTTP servers like FastAPI), use AsyncSimpleBot instead. It shares the same initialization parameters as SimpleBot.
  9. How Agent orchestration works in LlamaBot

    main

    LlamaBot uses PocketFlow (Flow / AsyncFlow) for agent graph orchestration.

    Specifically, the AgentBot serves as a reference implementation of a PocketFlow graph that follows a decide $\rightarrow$ tool $\rightarrow$ loopback topology. While AgentBot provides a standard pattern, the library is designed to allow users to fork or replace components like DecideNode and edges to implement their own custom orchestration graphs.

  10. How LlamaBot resolves Git repository paths

    main

    LlamaBot's Git CLI commands (llamabot git hooks and llamabot git compose) are designed to work from any subdirectory within a Git worktree. The system automatically resolves the enclosing repository's Git directories, so you do not need to be at the repository root to run commands.

    Error Handling: If you attempt to run these commands outside of a Git worktree, the system will raise a RuntimeError with instructions to run the command inside a Git repository.

  11. Core concepts of LlamaBot

    main

    LlamaBot is a Python library designed for interacting with Large Language Models (LLMs) using a composable API. It leverages LiteLLM under the hood, allowing you to switch between different LLM providers and models without changing your core application logic.

    The library is built around three primary abstractions:

    1. Bots: Callable objects that encapsulate specific prompts and model interaction logic.
    2. Messages: The structured content used in conversations, categorized as system, human, or assistant content.
    3. Components: Optional extensions used to enhance bot capabilities, such as document stores for Retrieval-Augmented Generation (RAG).
  12. Add chat memory to SimpleBot

    main

    To enable conversational memory in a SimpleBot, pass a ChatMemory instance to the memory parameter. This allows the bot to maintain context across multiple interactions.

    LlamaBot supports two types of memory:

    1. Linear Memory: Fast, uses no LLM calls for memory management.
    2. Intelligent Threading: Uses an LLM to create smart connections between conversation topics (graph-based memory).

    Note: For RAG (Retrieval-Augmented Generation) with document stores, use QueryBot instead of SimpleBot with memory.

    import llamabot as lmb
    
    system_prompt = "You are Richard Feynman."
    
    # Option 1: Simple linear memory (fast)
    memory = lmb.ChatMemory()
    
    # Option 2: Intelligent threading (uses LLM)
    # memory = lmb.ChatMemory.threaded(model="gpt-4o-mini")
    
    feynman = lmb.SimpleBot(
        system_prompt,
        memory=memory,
        model_name="gpt-4.1-mini"
    )
    
    # The bot will remember context in subsequent calls
    response1 = feynman("Can you explain quantum mechanics?")
    response2 = feynman("Can you give me a simpler explanation?")
    print(response2)