Getting Started with Vel
Complete guide to installing and using Vel for the first time.
Installation
Prerequisites
- Python 3.10 or higher
- pip
Install from Source
# Clone the repository
git clone <repo-url>
cd vel
# Install in development mode
pip install -e .
# Optional: Install dev dependencies
pip install -e ".[dev]"
Configuration
Environment Variables
Create a .env
file in the project root:
# Copy the example
cp .env.example .env
# Edit with your API keys
vim .env
Required Variables
# For OpenAI
OPENAI_API_KEY=sk-...
# OR for Google Gemini
GOOGLE_API_KEY=...
# OR for Anthropic Claude
ANTHROPIC_API_KEY=sk-ant-...
Optional Variables
# Database (for persistent sessions)
POSTGRES_DSN=postgresql+psycopg://user:pass@localhost:5432/vel
# Redis (for caching)
REDIS_URL=redis://localhost:6379/0
# OpenAI Custom Endpoint
OPENAI_API_BASE=https://api.openai.com/v1
Note: If POSTGRES_DSN
and REDIS_URL
are not set, Vel will use in-memory storage (fine for development).
API Key Configuration Methods
Vel supports two ways to provide API keys, making it suitable for both applications and libraries:
Method 1: Environment Variables (recommended for applications)
Set environment variables as shown above. Agents will automatically use them:
agent = Agent(
id='my-agent',
model={'provider': 'openai', 'model': 'gpt-4o'}
)
# Uses OPENAI_API_KEY from environment
Pros:
- ✓ Secure (not in code)
- ✓ Easy for development
- ✓ Standard practice
Cons:
- ✗ Not suitable for libraries imported by others
- ✗ Can’t use different keys for different agents
Method 2: Explicit API Keys (recommended for libraries/production)
Pass API keys directly in the model config:
agent = Agent(
id='my-agent',
model={
'provider': 'openai',
'model': 'gpt-4o',
'api_key': 'sk-...' # Override environment variable
}
)
Pros:
- ✓ Works in any environment
- ✓ Suitable for installable packages
- ✓ Different agents can use different keys
- ✓ Perfect for multi-tenant applications
Cons:
- ✗ Must manage secrets carefully
Use Cases
Scenario | Method |
---|---|
Building an application | Environment variables |
Building a library/package | Explicit API keys |
Multi-tenant SaaS | Explicit API keys (per-tenant) |
Development/testing | Environment variables |
CI/CD | Environment variables |
Quick Start
Basic Usage
import asyncio
from dotenv import load_dotenv
from vel import Agent
load_dotenv()
async def main():
# Create an agent
agent = Agent(
id='my-agent',
model={'provider': 'openai', 'model': 'gpt-4o'}
)
# Non-streaming mode
answer = await agent.run({'message': 'Hello, how are you?'})
print(answer)
if __name__ == '__main__':
asyncio.run(main())
Streaming Mode
async def streaming_example():
agent = Agent(
id='my-agent',
model={'provider': 'openai', 'model': 'gpt-4o'}
)
# Stream events as they arrive
async for event in agent.run_stream({'message': 'Tell me a joke'}):
print(event)
With Sessions (Multi-turn)
async def session_example():
agent = Agent(
id='my-agent',
model={'provider': 'openai', 'model': 'gpt-4o'},
session_storage='memory' # or 'database'
)
session_id = 'user-123'
# First turn
answer1 = await agent.run(
{'message': 'My name is Alice'},
session_id=session_id
)
print(answer1)
# Second turn - remembers Alice
answer2 = await agent.run(
{'message': 'What is my name?'},
session_id=session_id
)
print(answer2) # "Your name is Alice"
With Tools
from vel import Agent, ToolSpec, register_tool
# Define a custom tool
def get_weather_handler(input: dict, ctx: dict) -> dict:
city = input['city']
return {'temp_f': 72, 'condition': 'sunny', 'city': city}
weather_tool = ToolSpec(
name='get_weather',
input_schema={
'type': 'object',
'properties': {'city': {'type': 'string'}},
'required': ['city']
},
output_schema={
'type': 'object',
'properties': {
'temp_f': {'type': 'number'},
'condition': {'type': 'string'},
'city': {'type': 'string'}
},
'required': ['temp_f', 'condition', 'city']
},
handler=get_weather_handler
)
register_tool(weather_tool)
# Use the agent with tools
async def tool_example():
agent = Agent(
id='my-agent',
model={'provider': 'openai', 'model': 'gpt-4o'},
tools=['get_weather']
)
answer = await agent.run({'message': 'What is the weather in New York?'})
print(answer) # Agent will call the tool and respond
Storing Messages for Database
Use MessageReducer to aggregate streaming events into structured messages compatible with the Vercel AI SDK format:
from vel import Agent, MessageReducer
async def database_storage_example():
"""Aggregate streaming events for database storage"""
# Create reducer
reducer = MessageReducer()
# Add user message
user_msg = reducer.add_user_message(
"What's the weather in San Francisco?",
metadata={"user_id": "user-123", "timestamp": "2024-01-15T10:00:00Z"}
)
# Stream agent response
agent = Agent(
id='weather-agent',
model={'provider': 'openai', 'model': 'gpt-4o'},
tools=['get_weather']
)
async for event in agent.run_stream({'message': "What's the weather in SF?"}):
reducer.process_event(event)
# Get messages in Vercel AI SDK format
messages = reducer.get_messages(
assistant_metadata={"model": "gpt-4o"}
)
# [
# {user message},
# {assistant message with parts: [tool-call, tool-result, text]}
# ]
# Store in database
for msg in messages:
await db.insert_message(msg)
Key Features:
- ✓ Compatible with Vercel AI SDK
useChat
hook - ✓ Aggregates text, tool calls, and tool results into parts array
- ✓ Includes provider metadata (OpenAI message/call IDs)
- ✓ Supports custom message IDs and metadata
- ✓ Ready for database storage
See Stream Protocol - Message Aggregation for complete details.
With Generation Configuration
Control model behavior with fine-grained parameters:
from vel import Agent
async def generation_config_example():
# Agent with default config
agent = Agent(
id='my-agent',
model={'provider': 'openai', 'model': 'gpt-4o'},
generation_config={
'temperature': 0.7, # Creativity
'max_tokens': 500 # Output limit
}
)
# Use default config
creative = await agent.run({'message': 'Write a poem'})
print(creative)
# Override for specific run
factual = await agent.run(
{'message': 'What is 2+2?'},
generation_config={'temperature': 0} # Deterministic for this run
)
print(factual)
Common Parameters:
temperature
- Creativity (0-2)max_tokens
- Output length limittop_p
- Nucleus sampling (0-1)seed
- Reproducible outputs (OpenAI, Anthropic)
See Providers for all parameters.
REST API
Start the Service
# Start with uvicorn
uvicorn agents_service.main:app --reload
# Or with custom host/port
uvicorn agents_service.main:app --host 0.0.0.0 --port 8000
Streaming Endpoint
curl -X POST http://localhost:8000/runs \
-H "Content-Type: application/json" \
-d '{
"agent_id": "chat-general:v1",
"provider": "openai",
"model": "gpt-4o",
"input": {"message": "hello"}
}'
Non-Streaming Endpoint
curl -X POST http://localhost:8000/runs/sync \
-H "Content-Type: application/json" \
-d '{
"agent_id": "chat-general:v1",
"provider": "google",
"model": "gemini-1.5-pro",
"input": {"message": "hello"}
}'
Architecture Overview
Vel uses a two-layer architecture based on the Single Responsibility Principle:
Layer 1: Translators (Protocol Adapters)
Responsibility: Convert provider-specific events → standard stream protocol
from vel.providers.translators import OpenAIAPITranslator
translator = OpenAIAPITranslator()
# Converts OpenAI chunks → Vel protocol events
- Job: Protocol translation only
- Scope: Single LLM response stream
- Stateful: Only tracks current response (text blocks, tool calls)
- Reusable: Works with any orchestrator (Vel Agent, Mesh, LangGraph)
Layer 2: Agent (Orchestrator)
Responsibility: Multi-step execution, tool calling, context management
from vel import Agent
agent = Agent(id='my-agent', model={...}, tools=[...])
# Handles orchestration, tool execution, sessions
- Job: Full agentic workflow
- Scope: Multi-step execution with tools
- Stateful: Sessions, context, run history
- Opinionated: Implements specific orchestration pattern
Why Two Layers?
This separation enables composability:
- Use Agent for turnkey agentic workflows (most common)
- Use Translator when integrating with external frameworks or building custom orchestrators
The translator layer can be reused across different orchestration strategies without modification.
Learn more: Event Translators - Complete architecture details and usage guide
Next Steps
- Session Management - Learn about multi-turn conversations
- Providers - Configure OpenAI, Gemini, and Claude
- Tools - Create custom tools
- Stream Protocol - Understand streaming events and custom data-* events for RAG, progress tracking, and analytics
- Event Translators - Protocol adapter architecture and custom orchestration
- API Reference - Complete API documentation
Troubleshooting
“Illegal header value b’Bearer ‘”
Your OPENAI_API_KEY
, GOOGLE_API_KEY
, or ANTHROPIC_API_KEY
is not set. Check your .env
file.
“Connection refused” (Postgres/Redis)
If you see connection errors for Postgres or Redis, comment out POSTGRES_DSN
and REDIS_URL
in your .env
file to use in-memory storage.
Import Errors
Make sure you installed the package:
pip install -e .
And that you’re loading environment variables:
from dotenv import load_dotenv
load_dotenv()