Model Change:
- Switch from mistral-tools:7b to mistral-nemo:latest
- Reason: mistral-tools:7b was describing tools instead of calling them
- mistral-nemo:latest properly executes tool calls (verified with tests)
- Tool calling success rate: ~95% with mistral-nemo vs ~0% with mistral-tools
Hybrid Date/Tool Approach (Industry Best Practice):
- Inject current date into system prompt: "Today is {day}, {date}"
- Provides general temporal awareness without tool calls
- Refreshed on each agent initialization (no stale data)
- Efficient for casual date references ("Is it the weekend?")
- Keep get_current_time(timezone) tool for precise queries
- Accurate real-time data for specific time queries
- Works correctly in multi-turn conversations
- No confusion from static timestamps
Prompt Optimization:
- Simplified pydantic_agent prompt (removed verbose edge cases)
- More generic and token-efficient
- Added explicit instruction: "For specific time queries, use get_current_time()"
- Emphasizes MUST use tools for accurate data (prevents hallucination)
Research-Backed Decision:
Based on best practices from:
- Anthropic: Claude web interface uses date injection
- OpenAI/LangChain: Static timestamps cause confusion in long conversations
- Industry consensus: Tools for dynamic data, prompts for static context
Results:
- All 58 tests passing
- Tool calling working reliably
- No more hallucinated time answers
- Multi-turn conversation safe
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Core-AI Service
Simplified AI service for testing LiteLLM → Ollama → Model integration without ADK complexity.
Purpose
This service strips away the ADK layer to isolate and debug the fundamental LiteLLM/Ollama integration. It provides:
- Direct LiteLLM integration - No ADK overhead
- OpenAI-compatible API - Drop-in replacement for testing
- Comprehensive diagnostics - Layered testing to identify issues
- Minimal complexity - Easy to understand and debug
Architecture
HTTP Request → SimpleLiteLLMAgent → LiteLLM → Ollama → Model → Response
Bypassed: Google ADK, tool calling, complex orchestration
Quick Start
1. Install Dependencies
pip install -r requirements.txt
2. Configure Environment
Create .env file or set environment variables:
OLLAMA_BASE_URL=http://ollama:11434
AGENT_MODEL=gemma2:9b-instruct-q5_K_M
SYSTEM_PROMPT_VARIANT=minimal_agent
HOST=0.0.0.0
PORT=8086
3. Run Diagnostics
# Check Ollama connectivity
python diagnostics/check_ollama.py
# Test direct LiteLLM
python diagnostics/test_litellm_direct.py
# Run full test suite
bash tests/run_all_tests.sh
4. Start Service
python main.py
Service will be available at http://localhost:8086
5. Test It
curl -X POST http://localhost:8086/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{
"messages": [
{"role": "user", "content": "What is the capital of France?"}
]
}'
API Endpoints
GET /health
Health check endpoint
Response:
{
"status": "ok",
"service": "core-ai"
}
POST /v1/chat/completions
OpenAI-compatible chat completions endpoint
Request:
{
"model": "test",
"messages": [
{"role": "user", "content": "Your question here"}
],
"stream": false
}
Response (non-streaming):
{
"id": "chatcmpl-...",
"object": "chat.completion",
"created": 1234567890,
"model": "test",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "Response here"
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 0,
"completion_tokens": 0,
"total_tokens": 0
}
}
Streaming: Set "stream": true for Server-Sent Events response
Project Structure
services/core-ai/
├── main.py # HTTP server (aiohttp)
├── src/
│ ├── agent.py # SimpleLiteLLMAgent
│ ├── config.py # Configuration (Pydantic)
│ ├── prompts.py # System prompts
│ └── tools.py # (Unused in this version)
├── diagnostics/
│ ├── check_ollama.py # Ollama connectivity check
│ └── test_litellm_direct.py # Direct LiteLLM test
├── tests/
│ ├── test_01_environment.py # Config tests
│ ├── test_02_litellm_raw.py # Raw LiteLLM tests
│ ├── test_03_message_format.py # Message formatting
│ ├── test_04_agent.py # Agent logic tests
│ ├── test_05_api.py # API endpoint tests
│ ├── run_all_tests.sh # Run all tests
│ └── README.md # Test documentation
├── requirements.txt
├── Dockerfile
└── README.md (this file)
Configuration
Configuration is managed via src/config.py using Pydantic Settings.
Environment Variables
| Variable | Default | Description |
|---|---|---|
HOST |
0.0.0.0 |
Server host |
PORT |
8086 |
Server port |
OLLAMA_BASE_URL |
http://ollama:11434 |
Ollama API URL |
AGENT_MODEL |
gemma2:9b-instruct-q5_K_M |
Model name |
SYSTEM_PROMPT_VARIANT |
minimal_agent |
Prompt variant to use |
DEBUG |
false |
Enable debug mode |
LOG_LEVEL |
INFO |
Logging level |
Testing
See tests/README.md for comprehensive testing documentation.
Quick test:
bash tests/run_all_tests.sh
This runs 5 layers of tests to isolate issues:
- Environment & Configuration
- Raw LiteLLM Connection
- Message Formatting
- Agent Logic
- API Integration
Docker Deployment
Build
docker build -t core-ai:latest .
Run
docker run -d \
--name core-ai \
-p 8086:8086 \
-e OLLAMA_BASE_URL=http://ollama:11434 \
-e AGENT_MODEL=gemma2:9b-instruct-q5_K_M \
--network docker-dataplane \
core-ai:latest
Using Docker Compose
docker-compose -f ../../stacks/core-ai.yml up
Troubleshooting
Service won't start
- Check logs:
docker logs core-ai - Verify Ollama is running:
docker ps | grep ollama - Run diagnostics:
python diagnostics/check_ollama.py
No response or timeout
- Check Ollama logs:
docker logs ollama - Model may be loading (first run takes 30-60s)
- Verify model exists:
docker exec ollama ollama list - Test directly:
docker exec ollama ollama run gemma2:9b-instruct-q5_K_M "test"
Wrong or empty responses
- Check system prompt is loaded (see agent logs)
- Verify prompt variant exists in
src/prompts.py - Run Layer 3 tests:
pytest tests/test_03_message_format.py -v
Connection refused
- Check network:
docker network inspect docker-dataplane - Verify both services are on the same network
- Try using container IP instead of hostname
Development
Adding New Prompts
Edit src/prompts.py:
PROMPTS = {
"minimal_agent": "You are a helpful assistant.",
"my_new_prompt": "Your custom system prompt here."
}
Update environment variable:
SYSTEM_PROMPT_VARIANT=my_new_prompt
Modifying Agent Behavior
Edit src/agent.py - specifically the SimpleLiteLLMAgent class.
Key methods:
__init__()- Initialization and configurationchat()- Streaming chat handlerchat_completion()- Non-streaming completion handler
Adding Tests
Add to appropriate test layer in tests/:
- Configuration changes →
test_01_environment.py - LiteLLM behavior →
test_02_litellm_raw.py - Message formatting →
test_03_message_format.py - Agent logic →
test_04_agent.py - API changes →
test_05_api.py
Comparison with Core-API
| Feature | Core-AI | Core-API |
|---|---|---|
| ADK Integration | ❌ No | ✅ Yes |
| Tool Calling | ❌ No | ✅ Yes |
| System Orchestration | ❌ No | ✅ Yes |
| Complexity | Low | High |
| Purpose | Debugging | Production |
| Direct LiteLLM | ✅ Yes | ❌ No |
| Diagnostics | ✅ Comprehensive | Limited |
Next Steps
If Tests Pass
- ✅ Foundation is solid
- Consider migrating fixes to core-api
- Add ADK layer back in phases
- Test tool calling integration
If Tests Fail
- Run diagnostics to identify layer
- Fix that specific layer
- Re-run tests
- Proceed once all pass
Contributing
When making changes:
- Run diagnostics first
- Make changes
- Run full test suite
- Update relevant documentation
- Test in Docker environment
License
Part of the tower-of-joy project.