A Python framework for building AI agents with multi-provider LLM support, persistent memory, and function calling capabilities.
Project description
Linden
A Python framework for building AI agents with multi-provider LLM support, persistent memory, and function calling capabilities.
Table of Contents
- Overview
- Features
- Installation
- Requirements
- Quick Start
- Configuration
- Architecture
- Advanced Usage
- API Reference
- Error Types
- Contributing
- License
- Support
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, Google (Gemini), 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, Google (Gemini), 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 clientanthropic- Anthropic API clientgroq- Groq API clientgoogle-genai- Google Gemini clientollama- Ollama local LLM clientpydantic- Data validation and serializationmem0- Memory managementdocstring_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
Linden is configured through a config.toml file and/or environment variables.
Priority
Configuration values are loaded with the following priority:
- Environment Variables (e.g.,
OPENAI_API_KEY) config.tomlfile
This allows you to securely provide API keys in your deployment environment, overriding any values in your local configuration file.
config.toml
Create a config.toml file in your project root. All sections for providers you do not use can be omitted.
[models]
dec = "gpt-4"
tool = "gpt-4"
extractor = "gpt-3.5-turbo"
speaker = "gpt-4"
[openai]
api_key = "your-openai-api-key" # Overridden by env var if set
timeout = 30
[anthropic]
api_key = "your-anthropic-api-key" # Overridden by env var if set
timeout = 30
max_tokens = 1024 #example
[groq]
base_url = "https://api.groq.com/openai/v1"
api_key = "your-groq-api-key" # Overridden by env var if set
timeout = 30
[ollama]
timeout = 60
[google]
api_key = "your-google-api-key" # Overridden by env var if set
timeout = 60
[memory]
# This entire section is optional if you disable long-term memory on your agents.
path = "./memory_db"
collection_name = "agent_memories"
Environment Variables
You can set your API keys as environment variables. These will take precedence over any keys defined in config.toml.
export OPENAI_API_KEY="your-openai-api-key"
export ANTHROPIC_API_KEY="your-anthropic-api-key"
export GROQ_API_KEY="your-groq-api-key"
export GOOGLE_API_KEY="your-google-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
- GoogleClient: Google Gemini models
- 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)
Agents without Long-Term Memory
By default, all agents use both short-term (session) and long-term (persistent) memory. You can disable the long-term memory for agents that only need conversational context for a single session. This also removes the need to have the [memory] section in your config.toml.
from linden.core import AgentRunner, AgentConfiguration
# Set enable_memory to False in the configuration
config = AgentConfiguration(
user_id="user456",
name="stateless_chat_bot",
model="gpt-4",
system_prompt="You are a helpful assistant with no long-term memory.",
enable_memory=False # This disables long-term persistence
)
agent = AgentRunner(config=config)
# This conversation will have short-term context
agent.run("My name is Bob.")
agent.run("What is my name?") # Will correctly answer "Bob"
# The agent will not remember this conversation in a new session.
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)
# Use Google Gemini
# Assicurati di avere GOOGLE_API_KEY configurata o nel config.toml
# tools devono essere nel formato supportato da Gemini (function_declarations)
gemini_config = AgentConfiguration(
user_id="user123",
name="gemini_agent",
model="gemini-1.5-flash",
system_prompt="You are a helpful assistant.",
temperature=0.7,
client=Provider.GOOGLE,
)
gemini_agent = AgentRunner(config=gemini_config)
API Reference
AgentConfiguration
Parameters
user_id(str): Unique identifier for the username(str): Unique agent identifier (defaults to UUID4)model(str): LLM model nametemperature(float): Response randomness (0-1)system_prompt(str): System instructiontools(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)enable_memory(bool): Enables long-term memory (defaults to True)
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 queryreset(): Clear conversation historyadd_to_context(content: str, persist: bool = False): Add contextual information
Memory Classes
AgentMemory
record(message: str, persist: bool = False): Store messageget_conversation(user_input: str): Retrieve relevant contextreset(): Clear agent memory
MemoryManager (Singleton)
get_memory(): Access shared memory instanceget_all_agent_memories(agent_id: str = None): Retrieve stored memories
Configuration
ConfigManager
initialize(config_path: str | Path): Load configuration fileget(config_path: Optional[str | Path] = None): Get configuration instancereload(): Refresh configuration from file
Error Types
ToolNotFound: Requested function not availableToolError: Function execution failedValidationError: Pydantic model validation failedRequestException: HTTP/API communication error
Contributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/new-feature) - Commit your changes (
git commit -am 'Add new feature') - Push to the branch (
git push origin feature/new-feature) - Create a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Support
- GitHub Issues: https://github.com/matstech/linden/issues
- Documentation: https://github.com/matstech/linden
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file linden-0.6.0.tar.gz.
File metadata
- Download URL: linden-0.6.0.tar.gz
- Upload date:
- Size: 32.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fdfe9aa6731c21710c068794e2bb6ca81498311d54671f0209ea942cf2166e5d
|
|
| MD5 |
8ace6538bafb6d78d287fc90f7560088
|
|
| BLAKE2b-256 |
e943e27b7d924ac1ef7c7122f4c9828f1fee0c8af08eaae3cf9d0ab9de2964bd
|
Provenance
The following attestation bundles were made for linden-0.6.0.tar.gz:
Publisher:
python-publish.yml on matstech/linden
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
linden-0.6.0.tar.gz -
Subject digest:
fdfe9aa6731c21710c068794e2bb6ca81498311d54671f0209ea942cf2166e5d - Sigstore transparency entry: 1018273815
- Sigstore integration time:
-
Permalink:
matstech/linden@23be9b2dab685fac504e8a773451d72df2560473 -
Branch / Tag:
refs/tags/0.6.0 - Owner: https://github.com/matstech
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@23be9b2dab685fac504e8a773451d72df2560473 -
Trigger Event:
release
-
Statement type:
File details
Details for the file linden-0.6.0-py3-none-any.whl.
File metadata
- Download URL: linden-0.6.0-py3-none-any.whl
- Upload date:
- Size: 36.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7afc5b15535607b60e0a06c0649868466986d3a8d784da126937510b9b9239f9
|
|
| MD5 |
e306dfe63ae8c9322ebc2d6fc07d18a6
|
|
| BLAKE2b-256 |
052323fad5402e024054a4eb96b6d0a867626d3c5b19aadda2138f6f1a3c177a
|
Provenance
The following attestation bundles were made for linden-0.6.0-py3-none-any.whl:
Publisher:
python-publish.yml on matstech/linden
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
linden-0.6.0-py3-none-any.whl -
Subject digest:
7afc5b15535607b60e0a06c0649868466986d3a8d784da126937510b9b9239f9 - Sigstore transparency entry: 1018273821
- Sigstore integration time:
-
Permalink:
matstech/linden@23be9b2dab685fac504e8a773451d72df2560473 -
Branch / Tag:
refs/tags/0.6.0 - Owner: https://github.com/matstech
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@23be9b2dab685fac504e8a773451d72df2560473 -
Trigger Event:
release
-
Statement type: