agentleFS
OpentronsOpentrons/opentrons/.cursor/skills/ai-server/SKILL.md

ai-server

Skill525 starsChanged 2 months agoApache-2.0View on GitHub

Conventions for the opentrons-ai-server FastAPI service — project structure, uv dependency management, settings, testing, Docker, and deployment. Use when working with files in opentrons-ai-server/ or discussing the AI server API.

  • Reads credentials

We split this skill into the rules it gives an agent

12 rules. About 0 are common, 1 is distinctive, and 11 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 11

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    All endpoints require a Bearer token in Authorization.

    line 25
  2. Very specific:

    After changing deps, commit both pyproject.toml and uv.lock.

    line 49
  3. Very specific:

    All runtime configuration lives in api/settings.py via pydantic-settings:

    line 79
  4. Very specific:

    Every new env var or secret must be added as a field on the Settings class

    line 83
  5. Very specific:

    Always import settings via the get_settings() singleton (not Settings() directly) to avoid re-parsing .env on every import

    line 85
  6. Very specific:

    All config is in pyproject.toml — no separate config files:

    line 97
  7. Very specific:

    All targets run from opentrons-ai-server/.

    line 109
  8. Very specific:

    Test helpers in tests/helpers/ handle Auth0 token caching and HTTP client setup

    line 171
  9. Very specific:

    Import sorting handled by ruff's I rule (isort-compatible)

    line 183
  10. Very specific:

    Add the field to the Settings class in api/settings.py (use SecretStr for secrets)

    line 187
  11. Very specific:

    Add the value to your local .env file

    line 188
  12. Distinctive:

    Before deploying: add the value in AWS Secrets Manager under the environment's secret name

    line 189

≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

AI Server Instructions

Overview

opentrons-ai-server is a standalone FastAPI service for Opentrons AI — protocol generation, chat completions, and related AI features. It is not part of the monorepo build system; it has its own dependency management, CI workflows, and deployment pipeline.

Deployed environments: staging (staging.opentrons.ai) and prod (ai.opentrons.com), running on AWS ECS Fargate behind CloudFront.

API Endpoints

The server exposes four chat endpoints that return JSON responses:

EndpointPurpose
POST /api/chat/completionGeneral chat (no file attachments)
POST /api/chat/completion-multipartChat with file attachments (multipart form)
POST /api/chat/create-protocolGenerate a new protocol
POST /api/chat/update-protocolUpdate an existing protocol

All endpoints require a Bearer token in Authorization. Setting "fake": true in the request body bypasses the LLM and returns a canned response from api/domain/fake_responses.py — useful for local development without Anthropic API calls.

Package Manager — uv

This project uses uv for Python dependency management (not pipenv, pip-tools, or poetry).

FileRoleCommitted?
pyproject.tomlSingle source of truth for dependencies AND all tool configYes
uv.lockLocked dependency graphYes
requirements.txtUnused. Docker installs from uv.lock directlyNo (gitignored)
.venv/Local virtual environment created by uv syncNo (gitignored)

Key Commands

make setup                    # Install all deps (uv sync --frozen)
uv add <package>              # Add production dep
uv add --dev <package>        # Add dev-only dep
uv remove <package>           # Remove dep
uv lock                       # Re-resolve after manual pyproject.toml edits
uv run <command>              # Run inside the managed venv

After changing deps, commit both pyproject.toml and uv.lock.

Project Structure

opentrons-ai-server/
├── api/ # Application source code
│ ├── handler/ # FastAPI app, routes, middleware (fast.py entrypoint)
│ ├── domain/ # Business logic — LLM prediction (Anthropic, OpenAI)
│ ├── models/ # Pydantic request/response models
│ ├── services/ # File processing and other services
│ ├── integration/ # External integrations (Auth0, Google Sheets, AWS)
│ ├── constants/ # Shared constants
│ ├── data/ # Static data files
│ ├── storage/ # Stored API docs, indexes
│ ├── utils/ # API docs sync, curation, metadata helpers
│ └── settings.py # Pydantic Settings — all env vars and secrets
├── tests/
│ ├── conftest.py # Pytest fixtures and --env option
│ ├── helpers/ # Client, token helpers for live testing
│ └── test\_\*.py # Unit and live tests
├── deploy.py # ECS Fargate deployment script
├── Dockerfile
├── Makefile
├── pyproject.toml
└── uv.lock

Configuration & Settings

All runtime configuration lives in api/settings.py via pydantic-settings:

  • Locally: values come from a .env file (gitignored)
  • Deployed: values come from AWS Secrets Manager, loaded into ECS by deploy.py
  • Every new env var or secret must be added as a field on the Settings class
  • Secrets use SecretStr type; non-secret vars are plain strings with defaults
  • Always import settings via the get_settings() singleton (not Settings() directly) to avoid re-parsing .env on every import

Notable settings:

  • allowed_origins — comma-separated CORS origins (must be explicit; wildcard * is invalid with allow_credentials=True)
  • request_timeout_seconds — request timeout in seconds (default "300"); production proxies must be configured to allow at least this duration
  • anthropic_max_tokens — stored as a string, cast to int when used

Generate a template .env from defaults: make gen-env

Tool Configuration

All config is in pyproject.toml — no separate config files:

ToolSectionPurpose
ruff[tool.ruff], [tool.ruff.lint], [tool.ruff.format]Linting AND formatting
mypy[tool.mypy], [[tool.mypy.overrides]]Strict type checking with pydantic plugin
pytest[tool.pytest.ini_options]Test runner config, markers: unit, live

Line length: 140. Target: Python 3.12. Mypy is in strict mode.

Makefile Targets

All targets run from opentrons-ai-server/.

Development

TargetDescription
make setupInstall all deps (uv sync --frozen --extra dev)
make teardownDelete .venv/
make formatAuto-fix lint + format with ruff, then prettier for .md/.json
make lintCheck lint (ruff) + type check (mypy) — no auto-fix
make prepformat then lint then unit-test
make unit-testRun unit tests (pytest tests -m unit)

Running Locally

TargetDescription
make local-runRun FastAPI with uvicorn (hot reload, no Docker)
make buildSync API docs, then build the Docker image
make runRun the Docker container (requires .env file)
make rebuildclean + build + run
make live-testRun live tests against a running server (ENV=local default)
make live-clientInteractive client for testing the API

Deployment

TargetDescription
make deploy ENV=stagingBuild, push to ECR, update ECS service
make dry-deploy ENV=stagingRetrieve AWS data but make no changes
make build-only ENV=stagingBuild Docker image only, no push/deploy

Docker Build

The image is a two-stage build. The dependency stage copies a digest-pinned uv 0.11.17 binary and runs uv sync --frozen --no-dev --no-install-project, so packages come from uv.lock with their recorded hashes. The runtime stage copies only that virtualenv and api/; it does not contain uv.

make build syncs the Python API docs, then builds. The Docker build context is the repo root (not opentrons-ai-server/). Docs come from the pinned DOCS_TAG Makefile variable and must be present under api/storage/api_docs/docs/v2.

Entrypoint: uvicorn api.handler.fast:app (3 workers, port 8000).

Python API docs curation

The helper model that selects relevant docs reads api/storage/api_docs/api_docs_struct.md. Rich <about> routing text is not taken from synced markdown alone; it comes from committed curation.

FileEdit?Purpose
api_docs_struct_about.mdYesSource of truth for curated <about> text
api_docs_struct.mdNoGenerated on make sync-api-docs
docs/v2/ (synced markdown)NoGitignored; fetched from DOCS_TAG
make sync-api-docs              # regenerate api_docs_struct.md from curated about file
make check-api-docs-curation    # fail if curated entries and synced docs diverge

Full details: opentrons-ai-server/docs/API_DOCS_CURATION.md

Testing

  • Unit tests (@pytest.mark.unit): run offline → make unit-test
  • Live tests (@pytest.mark.live): run against a real server → make live-test ENV=local
  • The --env pytest option selects the target environment (local/staging/prod)
  • Test helpers in tests/helpers/ handle Auth0 token caching and HTTP client setup

Authentication

Auth0 JWT verification via api/integration/auth.py. Config: auth0_domain, auth0_api_audience, auth0_issuer, auth0_algorithms in Settings.

Code Conventions

  • Formatting and linting: ruff only (no black). make format to auto-fix
  • Type annotations: required everywhere — mypy strict mode
  • Pydantic models for all request/response schemas (in api/models/)
  • Structured logging via structlog
  • Import sorting handled by ruff's I rule (isort-compatible)

Adding a New Env Var or Secret

  1. Add the field to the Settings class in api/settings.py (use SecretStr for secrets)
  2. Add the value to your local .env file
  3. Before deploying: add the value in AWS Secrets Manager under the environment's secret name
  4. Re-deploy — the deploy script maps Settings fields to ECS container env vars automatically

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

Reviews can't be read right now.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.