chore: pydantic-settings config, logging, CI workflow

Config (src/config.py):
- pydantic-settings Settings class: OLLAMA_URL, OLLAMA_MODEL, DEBUG
- Reads from .env (gitignored) with sane defaults
- settings singleton imported by health.py and agent.py

Removes two hardcodes:
- health.py: OLLAMA_URL="http://localhost:11434" → settings.ollama_url
- agent.py:  Ollama(id="llama3.2")              → settings.ollama_model

app.py:
- logging.basicConfig at INFO — requests/errors now visible in terminal
- docs_url/redoc_url gated on settings.debug (off by default)

pyproject.toml:
- pydantic-settings>=2.0.0 added to main dependencies
- hatch wheel config updated to include src/config.py

.env.example: documents all three env vars with inline comments
.gitignore: add !.env.example negation so the template gets committed

.github/workflows/tests.yml: runs pytest --cov on every push/PR
(ubuntu-latest, Python 3.11, pip cache)

All 27 tests pass.

https://claude.ai/code/session_01M4L3R98N5fgXFZRvV8X9b6
This commit is contained in:
Claude
2026-02-19 19:31:48 +00:00
parent 46b848a2d7
commit c1d47eb883
8 changed files with 85 additions and 7 deletions

13
.env.example Normal file
View File

@@ -0,0 +1,13 @@
# Timmy Time — Mission Control
# Copy this file to .env and uncomment lines you want to override.
# .env is gitignored and never committed.
# Ollama host (default: http://localhost:11434)
# Override if Ollama is running on another machine or port.
# OLLAMA_URL=http://localhost:11434
# LLM model to use via Ollama (default: llama3.2)
# OLLAMA_MODEL=llama3.2
# Enable FastAPI interactive docs at /docs and /redoc (default: false)
# DEBUG=true

25
.github/workflows/tests.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: Tests
on:
push:
branches: ["**"]
pull_request:
branches: ["**"]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: "pip"
- name: Install dependencies
run: pip install -e ".[dev]"
- name: Run tests
run: pytest --cov=src --cov-report=term-missing

3
.gitignore vendored
View File

@@ -13,9 +13,10 @@ dist/
venv/
env/
# Secrets / local config
# Secrets / local config — commit only .env.example (the template)
.env
.env.*
!.env.example
# SQLite memory — never commit agent memory
*.db

View File

@@ -19,6 +19,7 @@ dependencies = [
"aiofiles>=24.0.0",
"typer>=0.12.0",
"rich>=13.0.0",
"pydantic-settings>=2.0.0",
]
[project.optional-dependencies]
@@ -32,7 +33,8 @@ dev = [
timmy = "timmy.cli:main"
[tool.hatch.build.targets.wheel]
packages = ["src/timmy", "src/dashboard"]
sources = {"src" = ""}
include = ["src/timmy", "src/dashboard", "src/config.py"]
[tool.pytest.ini_options]
testpaths = ["tests"]

21
src/config.py Normal file
View File

@@ -0,0 +1,21 @@
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
# Ollama host — override with OLLAMA_URL env var or .env file
ollama_url: str = "http://localhost:11434"
# LLM model passed to Agno/Ollama — override with OLLAMA_MODEL
ollama_model: str = "llama3.2"
# Set DEBUG=true to enable /docs and /redoc (disabled by default)
debug: bool = False
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
extra="ignore",
)
settings = Settings()

View File

@@ -1,3 +1,4 @@
import logging
from pathlib import Path
from fastapi import FastAPI, Request
@@ -5,13 +6,27 @@ from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from config import settings
from dashboard.routes.agents import router as agents_router
from dashboard.routes.health import router as health_router
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)-8s %(name)s%(message)s",
datefmt="%H:%M:%S",
)
logger = logging.getLogger(__name__)
BASE_DIR = Path(__file__).parent
PROJECT_ROOT = BASE_DIR.parent.parent
app = FastAPI(title="Timmy Time — Mission Control", version="1.0.0")
app = FastAPI(
title="Timmy Time — Mission Control",
version="1.0.0",
# Docs disabled unless DEBUG=true in env / .env
docs_url="/docs" if settings.debug else None,
redoc_url="/redoc" if settings.debug else None,
)
templates = Jinja2Templates(directory=str(BASE_DIR / "templates"))
app.mount("/static", StaticFiles(directory=str(PROJECT_ROOT / "static")), name="static")

View File

@@ -4,17 +4,17 @@ from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from pathlib import Path
from config import settings
router = APIRouter(tags=["health"])
templates = Jinja2Templates(directory=str(Path(__file__).parent.parent / "templates"))
OLLAMA_URL = "http://localhost:11434"
async def check_ollama() -> bool:
"""Ping Ollama to verify it's running."""
try:
async with httpx.AsyncClient(timeout=2.0) as client:
r = await client.get(OLLAMA_URL)
r = await client.get(settings.ollama_url)
return r.status_code == 200
except Exception:
return False

View File

@@ -3,13 +3,14 @@ from agno.models.ollama import Ollama
from agno.db.sqlite import SqliteDb
from timmy.prompts import TIMMY_SYSTEM_PROMPT
from config import settings
def create_timmy(db_file: str = "timmy.db") -> Agent:
"""Instantiate Timmy with Agno + Ollama + SQLite memory."""
return Agent(
name="Timmy",
model=Ollama(id="llama3.2"),
model=Ollama(id=settings.ollama_model),
db=SqliteDb(db_file=db_file),
description=TIMMY_SYSTEM_PROMPT,
add_history_to_context=True,