jpmschweitzerandClaude Opus 4.5 b5ee1f3e44 fix: use run() instead of run_stream() for scoped tools to avoid Ollama 400 bug
PydanticAI + Ollama streaming with tool calls has known issues:
- Issue #1292: Streaming stops after tool call due to empty TextPart
- Issue #2256: Empty text part causes run to end prematurely

This change uses run() for the actual tool execution while still
yielding the response in chunks to maintain the streaming UX.
The orchestration loop can emit <think> updates between await calls.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-12 10:59:58 +01:00
2025-12-07 15:44:27 +01:00

Tatlock - Your Homelab Butler

📖 For the complete system vision and architectural philosophy, see PHILOSOPHY.md

A privacy-first, offline-capable personal assistant system that coordinates specialized AI agents to help with research, development, home automation, and daily organization.

Current Status

  • Production-ready testing API with OpenAI Responses API format
  • Open WebUI integration with reasoning bubbles (<think> tags)
  • Conversation history with auto-generated IDs and context management
  • Tatlock PydanticAI Agent - Real LLM integration with Ollama + permanent tools
  • Permanent Tools - Calculator, date/time toolkit, web search (SearXNG)
  • Comprehensive testing - 131 tests, 81.78% coverage

Features

API Endpoints

  • Responses API (/v1/responses) - OpenAI Responses API format with structured output

    • Reasoning items for displaying thinking process
    • Function call items for tool execution
    • Message items for assistant responses
    • Streaming and non-streaming support
  • Chat Completions (/v1/chat/completions) - OpenAI Chat Completions compatibility

    • Automatic reasoning conversion to <think> tags for Open WebUI
    • Full OpenAI API compatibility
    • Streaming support
  • Models (/v1/models) - List available models

Advanced Capabilities

  • Conversation History: Auto-generated IDs, configurable max turns (default: 20)
  • Context Management: Token counting, automatic trimming, usage statistics
  • Parameter Validation: Temperature (0.0-2.0), reasoning effort levels, max tokens, stop sequences
  • Real-time Enforcement: Stop sequence detection and max token limits during streaming

Available Models

  • lorem-tester: Full-featured mock agent with realistic behavior

    • Configurable reasoning effort levels
    • Random tool/function calls
    • Error triggers for testing (rate_limit, context_overflow)
  • Tatlock: Real PydanticAI agent with butler personality

    • LLM Backend: Ollama (mistral-nemo:latest)
    • Personality: Witty British butler, research-oriented
    • Permanent Tools:
      • Calculator: Safe mathematical expression evaluation (arithmetic, algebra, trigonometry, logarithms)
      • Date/Time Toolkit: Current time, relative dates ("1 week ago"), time differences
      • Web Search: Privacy-preserving search via SearXNG
    • Capabilities: Streaming, reasoning, tool calling
    • Phase: Phase 1 - Basic Integration (full household coordination coming in future phases)

Requirements

  • Python 3.12+ (Python 3.12.11 recommended)
  • Ollama (for Tatlock agent): Running locally or network-accessible
  • SearXNG (for web search tool): Optional but recommended
    • Docker: docker run -d -p 8087:8080 searxng/searxng
    • Or use public instance (less private)

Quick Start

Installation

# Clone the repository
git clone https://git.schweitz.net/jpmschweitzer/tatlock.git
cd tatlock

# Create virtual environment
python -m venv .venv
source .venv/bin/activate  # Windows: .venv\Scripts\activate

# Install dependencies
pip install -r requirements.txt

Run the Server

uvicorn src.main:app --reload

API available at http://localhost:8000

Usage Examples

Responses API

Generate a response with reasoning:

curl http://localhost:8000/v1/responses \
  -H "Content-Type: application/json" \
  -d '{
    "model": "lorem-tester",
    "input": [
      {"role": "user", "content": "Explain quantum computing"}
    ],
    "reasoning": {
      "effort": "medium",
      "summary": "auto"
    },
    "max_output_tokens": 500,
    "stream": false
  }'

Response Structure:

{
  "id": "resp_abc123",
  "object": "response",
  "created_at": 1733529600,
  "model": "lorem-tester",
  "status": "completed",
  "output": [
    {
      "type": "reasoning",
      "summary": ["Analyzing the request...", "Considering quantum mechanics..."]
    },
    {
      "type": "message",
      "role": "assistant",
      "content": [{"type": "output_text", "text": "Quantum computing uses..."}]
    }
  ],
  "usage": {
    "input_tokens": 10,
    "output_tokens": 50,
    "reasoning_tokens": 20,
    "total_tokens": 80
  }
}

Chat Completions (OpenAI-compatible)

curl http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "lorem-tester",
    "messages": [
      {"role": "user", "content": "Hello!"}
    ],
    "temperature": 0.7,
    "stream": true
  }'

List Models

curl http://localhost:8000/v1/models

Conversation History

Optionally track conversations using metadata:

curl http://localhost:8000/v1/responses \
  -H "Content-Type: application/json" \
  -d '{
    "model": "lorem-tester",
    "input": [{"role": "user", "content": "Hello"}],
    "metadata": {"conversation_id": "conv_abc123"}
  }'

Note: Client must send full conversation history in input array (OpenAI compatible). Server optionally tracks via metadata.conversation_id for future features.

Using Tatlock with Tools

Tatlock automatically uses his permanent tools when appropriate:

# Mathematical calculation
curl http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "Tatlock",
    "messages": [{"role": "user", "content": "What is sqrt(144) + 25?"}]
  }'

# Date/time queries
curl http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "Tatlock",
    "messages": [{"role": "user", "content": "What was the date 2 weeks ago?"}]
  }'

# Web search for current information
curl http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{
    "model": "Tatlock",
    "messages": [{"role": "user", "content": "Search for recent Python 3.12 features"}]
  }'

Tatlock's Tool Usage Philosophy:

  • Uses calculator for ALL mathematics (even simple arithmetic)
  • Uses date/time tools instead of guessing dates
  • Searches for current/volatile information to verify facts
  • Maintains a researcher's mindset with tool-assisted verification

Open WebUI Integration

Connection

If running Open WebUI in Docker and API on host:

# Use Docker bridge gateway IP
http://172.17.0.1:8000/v1/chat/completions

Reasoning Display

The Chat Completions endpoint automatically:

  1. Enables reasoning generation
  2. Converts reasoning to <think> tags
  3. Streams thinking before the response

Open WebUI displays this as thought bubbles separate from the main response.

Testing Error Handling

Use special triggers in user messages:

  • "trigger_rate_limit" - Simulates rate limit error
  • "trigger_context_overflow" - Simulates context length error

API Documentation

Interactive documentation available at:

  • Swagger UI: http://localhost:8000/docs
  • ReDoc: http://localhost:8000/redoc

Testing

# Run all tests
pytest

# Run with coverage
pytest --cov=src --cov-report=term-missing

# Current: 131 tests, 81.78% coverage

Test Categories:

  • Unit tests: Agent tools, streaming, schemas
  • Integration tests: Full API stack with real Ollama calls
  • End-to-end tests: Chat completions, responses API

Deployment

Production Server

# Multiple workers for production
uvicorn src.main:app --host 0.0.0.0 --port 8000 --workers 4

Recommendations

  • Use reverse proxy (nginx/caddy) for HTTPS
  • Enable rate limiting
  • Set up monitoring and logging
  • Configure resource limits
  • Use process manager (systemd/supervisor)

Configuration

Create a .env file for custom configuration:

# API Configuration
API_HOST=0.0.0.0
API_PORT=8000

# Ollama Configuration
OLLAMA_HOST=http://localhost:11434
OLLAMA_DEFAULT_MODEL=mistral-nemo:latest
OLLAMA_TIMEOUT=120

# SearXNG Configuration (for web search tool)
SEARXNG_HOST=http://localhost:8087
SEARXNG_TIMEOUT=30

# Logging
LOG_LEVEL=INFO

# CORS (default: allow all)
CORS_ORIGINS=["*"]

See .env.example for full configuration options.

Troubleshooting

Streaming not working

  • Verify SSE-Starlette is installed
  • Check client supports Server-Sent Events
  • Test with: pytest tests/responses/ -k streaming

Open WebUI can't connect

  • Use Docker bridge gateway IP: 172.17.0.1:8000
  • Check firewall settings
  • Verify server is running on 0.0.0.0

Reasoning not showing

  • Ensure using Chat Completions endpoint (auto-enables reasoning)
  • Or manually enable in Responses API: "reasoning": {"effort": "medium", "summary": "auto"}
  • Check Open WebUI version supports <think> tags

Tatlock agent errors

  • Verify Ollama is running: curl http://localhost:11434/api/tags
  • Check model is downloaded: ollama list
  • Review environment variables: OLLAMA_HOST, OLLAMA_DEFAULT_MODEL
  • Check logs: tail -f logs/server.log

Web search not working

  • Verify SearXNG is running: curl http://localhost:8087/
  • Check SEARXNG_HOST environment variable
  • SearXNG is optional - Tatlock will note if search is unavailable

Project Structure

tatlock/
├── src/
│   ├── agents/          # Agent interface and implementations
│   │   ├── base.py      # AgentInterface abstract class
│   │   ├── lorem_tester.py  # Mock agent for testing
│   │   ├── tatlock.py   # Real PydanticAI butler agent
│   │   ├── tools.py     # Permanent tools (calculator, date/time, search)
│   │   └── registry.py  # Model registry
│   ├── responses/       # Responses API (primary endpoint)
│   ├── chat/            # Chat Completions wrapper
│   ├── models/          # Models listing
│   ├── core/            # Shared utilities and config
│   └── main.py          # Application entry point
├── tests/               # Comprehensive test suite (131 tests)
├── AGENTS.md            # LLM agent development guidelines
├── PHILOSOPHY.md        # System vision and architecture
├── IMPLEMENTATION_ROADMAP.md  # Development phases
├── CHANGELOG.md         # Version history
└── README.md            # This file

Development

For LLM agent development guidelines and architectural decisions, see AGENTS.md.

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make changes with tests
  4. Ensure tests pass: pytest
  5. Submit pull request

Documentation

  • System Philosophy: PHILOSOPHY.md - Vision, goals, and architectural patterns
  • User Guide: This file - Installation, usage, and examples
  • Developer Guidelines: AGENTS.md - LLM agent development patterns
  • Version History: CHANGELOG.md - Changes and releases

External References

License

[Add your license here]

Version

Current version: 0.2.5 - Phase 2: The Steward (Two-Tier Architecture)


Note: This is a production-ready testing API with mock responses. The architecture is designed for easy integration with real LLM backends (PydanticAI, Ollama, OpenAI, etc.).

S
Description
No description provided
Readme
1.3 MiB
2026-08-08 18:30:28 +02:00
Languages
Python 95.3%
HTML 3.7%
Shell 0.6%
Makefile 0.4%