Skip to main content

A Python framework for building AI agents with multi-provider LLM support, persistent memory, and function calling capabilities.

Project description

Linden

Linden Logo

A Python framework for building AI agents with multi-provider LLM support, persistent memory, and function calling capabilities.

Python License Version CI Coverage

Table of Contents

Overview

Linden is a comprehensive AI agent framework that provides a unified interface for interacting with multiple Large Language Model (LLM) providers including OpenAI, Anthropic, Groq, and Ollama. It features persistent conversation memory, automatic tool/function calling, and robust error handling for building production-ready AI applications.

Features

  • Multi-Provider LLM Support: Seamless integration with OpenAI, Anthropic, Groq, and Ollama
  • Persistent Memory: Long-term conversation memory using FAISS vector storage and embeddings
  • Function Calling: Automatic parsing and execution of tools with Google-style docstring support
  • Streaming Support: Real-time response streaming for interactive applications
  • Thread-Safe Memory: Concurrent agent support with isolated memory per agent
  • Configuration Management: Flexible TOML-based configuration with environment variable support
  • Type Safety: Full Pydantic model support for structured outputs and agent configuration
  • Error Handling: Comprehensive error handling with retry mechanisms
  • Validated Configuration: Strict parameter validation with Pydantic's AgentConfiguration model

Installation

pip install linden

Requirements

  • Python >= 3.9
  • Dependencies automatically installed:
    • openai - OpenAI API client
    • anthropic - Anthropic API client
    • groq - Groq API client
    • ollama - Ollama local LLM client
    • pydantic - Data validation and serialization
    • mem0 - Memory management
    • docstring_parser - Function documentation parsing

Agent Configuration

Linden uses a Pydantic model called AgentConfiguration to define and validate all agent parameters. This provides:

  • Strong typing and validation for all agent parameters
  • Rejection of invalid or unsupported parameters
  • Default values for optional parameters
  • Clear documentation of configuration options

Example of using AgentConfiguration:

from linden.core import AgentConfiguration, Provider

config = AgentConfiguration(
    user_id="user123",
    name="assistant",
    model="gpt-4",
    temperature=0.7,
    system_prompt="You are a helpful AI assistant.",
    tools=[get_weather],  # Optional list of callable functions
    output_type=PersonInfo,  # Optional Pydantic model for structured output
    client=Provider.OPENAI,  # AI provider enum
    retries=3  # Retry attempts for failed requests
)

# Create agent with configuration
agent = AgentRunner(config=config)

Quick Start

Basic Agent Setup

from linden.core import AgentRunner, AgentConfiguration, Provider

# Create an agent configuration
config = AgentConfiguration(
    user_id="user123",
    name="assistant",
    model="gpt-4",
    temperature=0.7,
    system_prompt="You are a helpful AI assistant.",
    client=Provider.OPENAI
)

# Initialize the agent with configuration
agent = AgentRunner(config=config)

# Ask a question
response = agent.run("What is the capital of France?")
print(response)

Agent with Function Calling

def get_weather(location: str, units: str = "celsius") -> str:
    """Get current weather for a location.
    
    Args:
        location (str): The city name or location
        units (str, optional): Temperature units (celsius/fahrenheit). Defaults to celsius.
        
    Returns:
        str: Weather information
    """
    return f"The weather in {location} is 22°{units[0].upper()}"

# Create agent configuration with tools
config = AgentConfiguration(
    user_id="user123",
    name="weather_bot",
    model="gpt-4",
    temperature=0.7,
    system_prompt="You are a weather assistant.",
    tools=[get_weather],
    client=Provider.OPENAI
)

# Initialize the agent
agent = AgentRunner(config=config)

response = agent.run("What's the weather in Paris?")
print(response)

Streaming Responses

# Stream responses for real-time interaction
for chunk in agent.run("Tell me a story", stream=True):
    print(chunk, end="", flush=True)

Structured Output with Pydantic

from pydantic import BaseModel
from linden.core import AgentRunner, AgentConfiguration, Provider

class PersonInfo(BaseModel):
    name: str
    age: int
    occupation: str

# Create agent configuration with output_type for structured outputs
config = AgentConfiguration(
    user_id="user123",
    name="extractor",
    model="gpt-4",
    temperature=0.1,
    system_prompt="Extract person information from text.",
    output_type=PersonInfo,
    client=Provider.OPENAI
)

# Initialize the agent
agent = AgentRunner(config=config)

result = agent.run("John Smith is a 30-year-old software engineer.")
print(f"Name: {result.name}, Age: {result.age}")

Configuration

Create a config.toml file in your project root:

[models]
dec = "gpt-4"
tool = "gpt-4"
extractor = "gpt-3.5-turbo"
speaker = "gpt-4"

[openai]
api_key = "your-openai-api-key"
timeout = 30

[anthropic]
api_key = "your-anthropic-api-key"
timeout = 30
max_tokens = 1024 #example

[groq]
base_url = "https://api.groq.com/openai/v1"
api_key = "your-groq-api-key" 
timeout = 30

[ollama]
timeout = 60

[memory]
path = "./memory_db"
collection_name = "agent_memories"

Environment Variables

Set your API keys as environment variables:

export OPENAI_API_KEY="your-openai-api-key"
export ANTHROPIC_API_KEY="your-anthropic-api-key"
export GROQ_API_KEY="your-groq-api-key"

Architecture

Core Components

AgentRunner

The main agent orchestrator that handles:

  • LLM interaction and response processing
  • Tool calling and execution
  • Memory management
  • Error handling and retries
  • Streaming and non-streaming responses

Memory System

  • AgentMemory: Per-agent conversation history and semantic search
  • MemoryManager: Thread-safe singleton for shared vector storage
  • Persistent Storage: FAISS-based vector database for long-term memory

AI Clients

Abstract interface with concrete implementations:

  • OpenAiClient: OpenAI GPT models
  • AnthropicClient: Anthropic Claude models
  • GroqClient: Groq inference API
  • Ollama: Local LLM execution

Function Calling

  • Automatic parsing of Google-style docstrings
  • JSON Schema generation for tool descriptions
  • Type-safe argument parsing and validation
  • Error handling for tool execution

Memory Architecture

The memory system uses a shared FAISS vector store with agent isolation:

# Each agent has isolated memory
agent1 = AgentRunner(name="agent1", ...)
agent2 = AgentRunner(name="agent2", ...)

# Memories are automatically isolated by agent_id
agent1.run("Remember I like coffee")
agent2.run("Remember I like tea")

# Each agent only retrieves its own memories

Function Tool Definition

Functions must use Google-style docstrings for automatic parsing:

def search_database(query: str, limit: int = 10, filters: dict = None) -> list:
    """Search the knowledge database.
    
    Args:
        query (str): The search query string
        limit (int, optional): Maximum results to return. Defaults to 10.
        filters (dict, optional): Additional search filters:
            category (str): Filter by category
            date_range (str): Date range in ISO format
            
    Returns:
        list: List of search results with metadata
    """
    # Implementation here
    pass

Advanced Usage

Multi-Turn Conversations

from linden.core import AgentRunner, AgentConfiguration

# Create agent configuration
config = AgentConfiguration(
    user_id="user123", 
    name="chat_bot", 
    model="gpt-4", 
    temperature=0.7,
    system_prompt="You are a helpful assistant."
)
agent = AgentRunner(config=config)

# Conversation maintains context automatically
agent.run("My name is Alice")
agent.run("What's my name?")  # Will remember "Alice"
agent.run("Tell me about my previous question")  # Has full context

Error Handling and Retries

from linden.core import AgentRunner, AgentConfiguration
from linden.core.model import ToolError, ToolNotFound

# Configure agent with retries
config = AgentConfiguration(
    user_id="user123",
    name="robust_agent",
    model="gpt-4", 
    temperature=0.7,
    system_prompt="You are a helpful assistant.",
    retries=3  # Retry failed calls up to 3 times
)
agent = AgentRunner(config=config)

try:
    response = agent.run("Complex query that might fail")
except ToolError as e:
    print(f"Tool execution failed: {e.message}")
except ToolNotFound as e:
    print(f"Tool not found: {e.message}")

Memory Management

# Reset agent memory
agent.reset()

# Add context without user interaction
agent.add_to_context("Important context information", persist=True)

# Get conversation history
history = agent.memory.get_conversation("Current query")

Provider-Specific Features

from linden.core import AgentRunner, AgentConfiguration, Provider

# Use Anthropic Claude models
claude_config = AgentConfiguration(
    user_id="user123",
    name="claude_agent",
    model="claude-3-opus-20240229",
    system_prompt="You are a helpful assistant.",
    temperature=0.7,
    client=Provider.ANTHROPIC
)
claude_agent = AgentRunner(config=claude_config)

# Use local Ollama models
ollama_config = AgentConfiguration(
    user_id="user123",
    name="local_agent",
    model="llama2",
    system_prompt="You are a helpful assistant.",
    temperature=0.7,
    client=Provider.OLLAMA
)
local_agent = AgentRunner(config=ollama_config)

# Use Groq for fast inference
groq_config = AgentConfiguration(
    user_id="user123",
    name="fast_agent", 
    model="mixtral-8x7b-32768",
    system_prompt="You are a helpful assistant.",
    temperature=0.7,
    client=Provider.GROQ
)
fast_agent = AgentRunner(config=groq_config)

API Reference

AgentConfiguration

Parameters

  • user_id (str): Unique identifier for the user
  • name (str): Unique agent identifier (defaults to UUID4)
  • model (str): LLM model name
  • temperature (float): Response randomness (0-1)
  • system_prompt (str): System instruction
  • tools (list[Callable], optional): Available functions (defaults to empty list)
  • output_type (BaseModel, optional): Structured output schema (defaults to None)
  • client (Provider): LLM provider selection (defaults to Provider.OLLAMA)
  • retries (int): Maximum retry attempts (defaults to 3)

Features

  • Type validation for all parameters
  • Strict parameter validation (rejects unknown parameters)
  • Default values for optional parameters

AgentRunner

Constructor Parameters

  • config (AgentConfiguration): Configuration object for the agent with all the necessary settings

Methods

  • run(user_question: str, stream: bool = False): Execute agent query
  • reset(): Clear conversation history
  • add_to_context(content: str, persist: bool = False): Add contextual information

Memory Classes

AgentMemory

  • record(message: str, persist: bool = False): Store message
  • get_conversation(user_input: str): Retrieve relevant context
  • reset(): Clear agent memory

MemoryManager (Singleton)

  • get_memory(): Access shared memory instance
  • get_all_agent_memories(agent_id: str = None): Retrieve stored memories

Configuration

ConfigManager

  • initialize(config_path: str | Path): Load configuration file
  • get(config_path: Optional[str | Path] = None): Get configuration instance
  • reload(): Refresh configuration from file

Error Types

  • ToolNotFound: Requested function not available
  • ToolError: Function execution failed
  • ValidationError: Pydantic model validation failed
  • RequestException: HTTP/API communication error

Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/new-feature)
  3. Commit your changes (git commit -am 'Add new feature')
  4. Push to the branch (git push origin feature/new-feature)
  5. Create a Pull Request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Support

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

linden-0.5.0.tar.gz (27.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

linden-0.5.0-py3-none-any.whl (32.0 kB view details)

Uploaded Python 3

File details

Details for the file linden-0.5.0.tar.gz.

File metadata

  • Download URL: linden-0.5.0.tar.gz
  • Upload date:
  • Size: 27.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for linden-0.5.0.tar.gz
Algorithm Hash digest
SHA256 aea3031bfbe387a5b5f597355d93c04e30680349740dbbfad18e6f57d74aa341
MD5 0b155d19ad06dfcc26bf893dd9c6ed92
BLAKE2b-256 ab8817e0b8ae792fb1061243e39f5bf3beb185db5026e5919144322abc35d26e

See more details on using hashes here.

Provenance

The following attestation bundles were made for linden-0.5.0.tar.gz:

Publisher: python-publish.yml on matstech/linden

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file linden-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: linden-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 32.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for linden-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b68b0fee54d0a6410b5fddec42190c1ed363d11fea87fc5cc9c43ae0b5dc98ac
MD5 902219c57a65e969c0ec1c8520886500
BLAKE2b-256 3c6c8db5e3f267b111858157cd50dfc807726029c48af0cac4d0a3a6601819e6

See more details on using hashes here.

Provenance

The following attestation bundles were made for linden-0.5.0-py3-none-any.whl:

Publisher: python-publish.yml on matstech/linden

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page