Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.venv
__pycache__
dist
build
.git
.gitignore
.env
*.egg-info
htmlcov
.dockerignore
8 changes: 8 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
root = true

[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
indent_style = space
indent_size = 4
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Copy to .env and set values
OPENAI_API_KEY=sk-...
# Optional: telemetry configuration
OTEL_EXPORTER_OTLP_ENDPOINT=
NEW_RELIC_LICENSE_KEY=
35 changes: 35 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: CI

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup uv
uses: astral-sh/setup-uv@v6

- name: Install Python (from pyproject or .python-version)
run: uv python install

- name: Lock & sync (dev)
run: |
uv lock
uv sync --dev

- name: Lint (Ruff)
run: |
uv run ruff format --check .
uv run ruff check .

- name: Type-check (Pyright)
run: uv run pyright

- name: Tests
run: uv run pytest -q
19 changes: 19 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Environments
.venv/
.env
__pycache__/
*.pyc

# Builds
dist/
build/
*.egg-info/

# Coverage
.coverage*
htmlcov/

# OS / IDE
.DS_Store
.vscode/
.idea/
7 changes: 7 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.5.7
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
67 changes: 67 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# AGENTS.md — project guide for coding agents

This file is the **single source of truth for agents in this repo**. It tells AI coding agents (and humans!) how this project is structured, how to run it, and how to safely extend it. Inspired by OpenAI’s AGENTS.md format and best practices for the Agents SDK.

## 1) Agent roster
- **starter_agent** — a minimal, general‑purpose assistant with a `now_iso` tool.

Add new agents under `src/agent_template/agents/`, each with:
- `name`: descriptive, unique
- `instructions`: clear, outcome‑oriented; avoid vague “be helpful” phrasing
- `tools`: pick from `src/agent_template/tools` or add new
- Optional: `handoff_description`, `guardrails` and per‑agent config

## 2) Models & budgets
- Default model: configured via env (see `Settings`).
- Start with your best model to set a quality baseline, then down‑shift to faster/cheaper models where acceptable.
- Track token+latency in CI runs to guard regressions.

## 3) Tools registry
Add tools in `src/agent_template/tools/`:
- Prefer **typed** Python functions decorated with `@function_tool`.
- Ensure **idempotence** where possible and add **docstrings**—these render as tool descriptions for the model.
- Write **unit tests** and add examples in docstrings.

## 4) Guardrails & safety
- Implement lightweight **validators** (e.g., Pydantic) and **allow‑lists** for risky actions.
- Add **tripwires** for irreversible ops and route to a **human‑in‑the‑loop** when thresholds are exceeded.
- Keep prompts **explicit** about boundaries; see `src/agent_template/agents/prompts.py`.

## 5) Orchestration
- Prefer a **single agent with tools** first; split to multi‑agent only when prompts or tool selection get unwieldy.
- Use runs with exit conditions (final output tool, no tool calls, max turns).

## 6) Observability
- Log structured events via `structlog`.
- (Optional) Add OpenTelemetry exporters and Phoenix / New Relic integration in `src/agent_template/telemetry/`.

## 7) Evaluation
- Keep a small **golden set** under `eval/` (inputs + expected traits).
- Add **pytest** checks for tool correctness and policy adherence.
- Gate merges on eval pass + lint + type‑check.

## 8) Local commands
```bash
# Sync dev env
uv sync --dev

# Run starter agent
uv run agent-starter

# Lint / format
uv run ruff format . && uv run ruff check .

# Type-check
uv run pyright

# Tests
uv run pytest -q
```

## 9) PR conventions for agent changes
- Include **before/after behaviors** and **risk notes**.
- Update **AGENTS.md** and **eval/** if tool contracts or prompts change.
- CI must pass: ruff, pyright, pytest, minimal evals.

## 10) Environment
Copy `.env.example` to `.env` and set required keys. The OpenAI Python SDK will read `OPENAI_API_KEY`.
6 changes: 6 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Contributing

- Run `uv sync` before development.
- Use `uv run ruff format . && uv run ruff check .` before commits.
- Add tests for new features; keep coverage ≥ 80%.
- Update `AGENTS.md` when changing agents, tools, or guardrails.
58 changes: 58 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Production-ready Dockerfile for uv-based Python project (no dev deps in final image)
# - Locks deps inside the build (deterministic)
# - Installs only runtime deps in final image
# - Runs as non-root
# - Small final image (python:3.12-slim)

# ---------- Base image (common) ----------
FROM python:3.12-slim AS base
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1 \
PYTHONOPTIMIZE=2
# Install system deps if needed (kept minimal)
RUN apt-get update -qq && apt-get install -y --no-install-recommends \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*

# ---------- Builder: uv + lock + install (no dev) ----------
FROM base AS builder
# Bring in uv binary
COPY --from=ghcr.io/astral-sh/uv:0.8.13 /uv /usr/local/bin/uv
WORKDIR /app

# Copy project metadata first for layer caching
COPY pyproject.toml /app/

# Create or refresh lock deterministically for Python 3.12
# (Respects [tool.uv] python = "3.12")
RUN --mount=type=cache,target=/root/.cache/uv \
uv lock

# Install ONLY runtime deps into a local venv (no project yet, and no dev deps)
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --frozen --no-install-project --no-dev

# Now copy the source and install the project into the same venv
COPY . /app
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --frozen --no-dev

# ---------- Final runtime image ----------
FROM base AS runtime
WORKDIR /app

# Create a non-root user
RUN useradd -m -u 10001 appuser

# Copy the entire app (including .venv from builder)
COPY --from=builder /app /app

# Put venv on PATH
ENV PATH="/app/.venv/bin:${PATH}"

USER appuser
EXPOSE 8000

# Default command: run the CLI entry point
CMD ["uv", "run", "agent-starter"]
35 changes: 34 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,34 @@
# python-agent-template
# python-agent-template-uv

A production‑ready Python template for building LLM **agents** using **uv** for packaging & dev, **Ruff** for lint/format, **Pyright** for type checking, and **pytest** for tests. It also includes an **AGENTS.md** that documents your agents, tools, guardrails, and run conventions.

## Quickstart

```bash
# 1) Install uv (see https://docs.astral.sh/uv/)
# 2) Create & sync env (installs deps & dev-deps)
uv sync --dev

# 3) Set your API key (OpenAI by default; add others as needed)
export OPENAI_API_KEY=sk-...

# 4) Run the starter agent
uv run agent-starter

# 5) Lint / type-check / test
uv run ruff format .
uv run ruff check .
uv run pyright
uv run pytest -q
```

## What’s inside
- **uv** project + lockfile, modern `pyproject.toml`
- **AGENTS.md** with roster, tools, guardrails, eval/obs and PR conventions
- **Ruff** lint + formatter; **Pyright** type checking
- **pytest** + coverage
- **OpenAI Agents SDK** starter agent + sample tool
- GitHub Actions CI (setup-uv) and a multi‑stage Dockerfile
- Pre‑commit hooks (Ruff)

See `AGENTS.md` for how to add/modify agents and tools.
Empty file added eval/.keep
Empty file.
66 changes: 66 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
[build-system]
requires = ["hatchling>=1.25.0"]
build-backend = "hatchling.build"

[project]
name = "python-agent-template-uv"
version = "0.1.0"
description = "Modern Python agent template using uv + OpenAI Agents SDK"
readme = "README.md"
requires-python = ">=3.12,<3.13"
keywords = ["agents", "llm", "openai", "uv"]
classifiers = [
"Programming Language :: Python :: 3.12",
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
]

# Runtime deps
dependencies = [
"openai>=1.52.0",
"openai-agents>=0.2.9", # Agents SDK
"pydantic>=2.8.0",
"pydantic-settings>=2.3.0",
"httpx>=0.27.0",
"structlog>=24.1.0",
"tenacity>=8.3.0",
"rich>=13.7.0",
]

# Dev / tooling

[project.scripts]
agent-starter = "agent_template.cli:main"

[tool.ruff]
line-length = 100
target-version = "py312"
lint.select = ["E","F","I","UP","B","SIM","C4","PIE","RUF"]
lint.ignore = ["E203","E501"] # leave formatting to ruff formatter, cap lines via formatter
exclude = ["dist", "build", ".venv"]

[tool.ruff.format]
quote-style = "double"
indent-style = "space"
line-ending = "auto"
skip-magic-trailing-comma = false

[tool.pytest.ini_options]
addopts = "-q"
testpaths = ["tests"]

[tool.hatch.build.targets.wheel]
packages = ["src/agent_template"]
include = ["src/agent_template/**"]

[tool.uv]
default-groups = ["dev"]

[dependency-groups]
dev = [
"pytest>=8.3.0",
"pytest-cov>=5.0.0",
"ruff>=0.5.7",
"pyright>=1.1.379",
"pre-commit>=3.7.1"
]
14 changes: 14 additions & 0 deletions pyrightconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"venvPath": ".",
"venv": ".venv",
"include": [
"src"
],
"exclude": [
"**/__pycache__",
"dist",
"build"
],
"typeCheckingMode": "standard",
"reportMissingTypeStubs": false
}
2 changes: 2 additions & 0 deletions src/agent_template/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
__all__ = ["__version__"]
__version__ = "0.1.0"
Empty file.
14 changes: 14 additions & 0 deletions src/agent_template/agents/starter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from __future__ import annotations

from agents import Agent

from ..prompts import BASE_INSTRUCTIONS
from ..tools.time_tools import now_iso


def starter_agent() -> Agent:
return Agent(
name="StarterAgent",
instructions=BASE_INSTRUCTIONS,
tools=[now_iso],
)
20 changes: 20 additions & 0 deletions src/agent_template/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from __future__ import annotations

from agents import Runner
from rich.console import Console

from .agents.starter import starter_agent
from .config import Settings

console = Console()


def main() -> None:
# Validate env and show a friendly banner
Settings.require()
agent = starter_agent()
console.rule("[bold]python-agent-template-uv[/]")
console.print("Running StarterAgent...\n")
result = Runner().run(agent, "Say hello and tell me the current time using the tool.")
console.print("\n[b]Agent output[/]:")
console.print(result) # The Agents SDK pretty-prints responses
Loading
Loading