Skip to content

Commit 6039e97

Browse files
authored
Merge pull request #12 from mozilla-ai/add-cli
Add Comprehensive Management CLI with Rich Formatting
2 parents 04cdd2e + 0d87dfe commit 6039e97

File tree

18 files changed

+4636
-57
lines changed

18 files changed

+4636
-57
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,6 @@ wheels/
1313

1414
# Generated by setuptools_scm; do not commit
1515
src/any_llm_platform_client/_version.py
16+
17+
.coverage
18+
.ralph/

AGENTS.md

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
# AGENTS.md - Agent Development Guide
2+
3+
This document is the **table of contents** for AI coding agents working in this repository. It provides a map to deeper documentation rather than exhaustive instructions.
4+
5+
## Quick Start
6+
7+
```bash
8+
# Setup
9+
uv sync --dev && uv run pre-commit install
10+
11+
# Run tests
12+
uv run pytest -v
13+
14+
# Run single test
15+
uv run pytest tests/test_cli.py::test_cli_help -v
16+
17+
# Lint and format
18+
uv run ruff check . --fix && uv run ruff format .
19+
```
20+
21+
## Project Identity
22+
23+
- **Package**: `any-llm-platform-client`
24+
- **CLI**: `any-llm`
25+
- **Python**: 3.11+ (compatible with 3.11–3.14)
26+
- **Package Manager**: `uv` (or `pip`)
27+
- **Source**: `src/any_llm_platform_client/`
28+
- **Tests**: `tests/`
29+
30+
## Repository Knowledge Map
31+
32+
The repository follows a **structured documentation approach** inspired by agent-first development principles. Knowledge lives in versioned, discoverable artifacts—not external documents or chat threads.
33+
34+
### Core Documentation
35+
36+
| Document | Purpose |
37+
|----------|---------|
38+
| `docs/DEVELOPMENT.md` | Development workflow, commands, testing |
39+
| `docs/CODE_STYLE.md` | Style guide, formatting, naming, type hints |
40+
| `docs/CLI_USAGE.md` | CLI commands, authentication, examples |
41+
| `docs/architecture/CRYPTOGRAPHY.md` | Encryption design, security properties |
42+
| `docs/architecture/PROJECT_STRUCTURE.md` | Code organization, module responsibilities |
43+
44+
### Quick Reference
45+
46+
- **Build/Test/Lint**: See `docs/DEVELOPMENT.md`
47+
- **Code Style**: See `docs/CODE_STYLE.md`
48+
- **CLI Usage**: See `docs/CLI_USAGE.md`
49+
- **Architecture**: See `docs/architecture/`
50+
51+
## Core Principles
52+
53+
Following the OpenAI agent-first development model, this repository enforces:
54+
55+
1. **Agent Legibility**: Code structure optimized for agent reasoning
56+
2. **Repository as Truth**: All knowledge versioned in-repo (not in Slack/Docs)
57+
3. **Progressive Disclosure**: Start with this map, navigate to details as needed
58+
4. **Mechanical Enforcement**: Linters enforce architecture, not docs alone
59+
60+
## Code Style Highlights
61+
62+
```python
63+
# Type hints (required)
64+
def decrypt_data(encrypted_data: bytes, private_key: nacl.public.PrivateKey) -> bytes:
65+
"""Decrypt data using X25519 sealed box.
66+
67+
Args:
68+
encrypted_data: The encrypted data bytes
69+
private_key: The X25519 private key for decryption
70+
71+
Returns:
72+
Decrypted data as bytes
73+
74+
Raises:
75+
ValueError: If decryption fails
76+
"""
77+
...
78+
79+
# Modern syntax
80+
items: list[str] = [] # Not List[str]
81+
result: str | None = None # Not Optional[str]
82+
83+
# Imports (grouped: stdlib → third-party → local)
84+
import logging
85+
from typing import Any
86+
87+
import httpx
88+
from rich.console import Console
89+
90+
from .client import AnyLLMPlatformClient
91+
```
92+
93+
**Key Rules**:
94+
- 120 char line length
95+
- Double quotes for strings
96+
- Google docstring format
97+
- Type hints required (except tests, `__init__.py`)
98+
- No bare `except:`, use specific exception types
99+
100+
## Project Structure
101+
102+
```
103+
src/any_llm_platform_client/
104+
├── __init__.py # Public API exports
105+
├── cli.py # Click CLI commands
106+
├── client.py # Core decryption client
107+
├── client_management.py # Management API (CRUD)
108+
├── crypto.py # X25519 cryptography
109+
└── exceptions.py # Custom exceptions
110+
111+
tests/
112+
├── test_basic.py # Basic imports
113+
├── test_cli.py # CLI commands
114+
├── test_cli_integration.py # Integration tests
115+
└── test_client.py # Client library tests
116+
117+
docs/
118+
├── DEVELOPMENT.md # Build, test, lint
119+
├── CODE_STYLE.md # Style guide
120+
├── CLI_USAGE.md # CLI reference
121+
└── architecture/ # Design docs
122+
├── CRYPTOGRAPHY.md
123+
└── PROJECT_STRUCTURE.md
124+
```
125+
126+
## Environment Variables
127+
128+
- `ANY_LLM_USERNAME`: Username for management commands
129+
- `ANY_LLM_PASSWORD`: Password for management commands
130+
- `ANY_LLM_PLATFORM_URL`: API base URL (default: `http://localhost:8000/api/v1`)
131+
- `ANY_LLM_KEY`: Encryption key format: `ANY.v1.<kid>.<fingerprint>-<base64_key>`
132+
133+
## Common Pitfalls
134+
135+
1. **Don't** skip type annotations in production code
136+
2. **Don't** use mutable default arguments
137+
3. **Don't** commit secrets (pre-commit hooks check)
138+
4. **Don't** ignore ruff's bugbear rules (B)—they catch real bugs
139+
5. **Don't** create files without reading existing ones first
140+
141+
## CI/CD
142+
143+
- **Platforms**: Linux, macOS, Windows
144+
- **Python versions**: 3.11, 3.12, 3.13, 3.14
145+
- **Pre-commit**: Linting, formatting, secrets detection
146+
- **Coverage**: Codecov (Ubuntu + Python 3.11 only)
147+
148+
## Where to Look Next
149+
150+
- **First time here?** Start with `docs/DEVELOPMENT.md`
151+
- **Writing code?** Check `docs/CODE_STYLE.md`
152+
- **Using the CLI?** See `docs/CLI_USAGE.md`
153+
- **Understanding crypto?** Read `docs/architecture/CRYPTOGRAPHY.md`
154+
- **Need examples?** See `docs/CLI_USAGE.md` examples section
155+
156+
---
157+
158+
**Philosophy**: This file is a **map**, not a manual. For detailed instructions, follow the links above to specialized documentation.

README.md

Lines changed: 163 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,169 @@ any-llm <provider>
3939

4040
### Command Line Interface
4141

42-
Interactive mode (prompts for provider):
42+
The CLI provides a unified interface for managing your any-llm platform:
43+
44+
```bash
45+
# Get help
46+
any-llm --help
47+
48+
# View available commands
49+
any-llm project --help
50+
any-llm key --help
51+
any-llm budget --help
52+
any-llm client --help
53+
```
54+
55+
#### Authentication
56+
57+
Set credentials for management commands:
58+
59+
```bash
60+
export ANY_LLM_USERNAME="your-email@example.com"
61+
export ANY_LLM_PASSWORD="your-password" # pragma: allowlist secret
62+
export ANY_LLM_PLATFORM_URL="http://localhost:8000/api/v1" # optional
63+
```
64+
65+
#### Managing Projects
66+
4367
```bash
68+
# List all projects
69+
any-llm project list
70+
71+
# Create a new project
72+
any-llm project create "My Project" --description "My project description"
73+
74+
# Show project details
75+
any-llm project show <project-id>
76+
77+
# Update a project
78+
any-llm project update <project-id> --name "Updated Name"
79+
80+
# Delete a project
81+
any-llm project delete <project-id>
82+
```
83+
84+
#### Managing Provider Keys
85+
86+
```bash
87+
# List provider keys for a project
88+
any-llm key list <project-id>
89+
90+
# Create a provider key
91+
any-llm key create <project-id> openai <encrypted-key>
92+
93+
# Update a provider key
94+
any-llm key update <provider-key-id> <encrypted-key>
95+
96+
# Archive a provider key (soft delete)
97+
any-llm key delete <provider-key-id>
98+
99+
# Permanently delete a provider key
100+
any-llm key delete <provider-key-id> --permanent
101+
102+
# Restore an archived key
103+
any-llm key unarchive <provider-key-id>
104+
```
105+
106+
#### Generating New Encryption Keys
107+
108+
Generate a new encryption key and automatically migrate provider keys:
109+
110+
```bash
111+
# Generate new key and migrate from old key
112+
any-llm key generate <project-id> --old-key "ANY.v1.<old-key>"
113+
114+
# Generate new key without migration (archives all provider keys)
115+
any-llm key generate <project-id>
116+
117+
# Skip confirmation prompts (for automation)
118+
any-llm key generate <project-id> --old-key "ANY.v1.<old-key>" --yes
119+
```
120+
121+
This command will:
122+
1. Generate a new X25519 keypair
123+
2. Update the project's encryption key
124+
3. Migrate all provider keys from the old key to the new key (if old key provided)
125+
4. Display the new ANY_LLM_KEY (save it securely!)
126+
127+
**Important:** Save the generated `ANY_LLM_KEY` in a secure location. It cannot be recovered if lost!
128+
129+
**Migration Behavior:**
130+
- **With old key:** Successfully decrypted provider keys are re-encrypted with the new key. Keys that fail to decrypt are archived.
131+
- **Without old key:** All encrypted provider keys are archived. You'll need to re-enter them in the web interface.
132+
- **Local providers** (e.g., Ollama with empty keys) are skipped during migration.
133+
134+
#### Decrypting Provider Keys
135+
136+
Decrypt a provider API key using your ANY_LLM_KEY:
137+
138+
```bash
139+
# Set your ANY_LLM_KEY
44140
export ANY_LLM_KEY='ANY.v1.<kid>.<fingerprint>-<base64_key>'
45-
any-llm
141+
142+
# Decrypt a provider key
143+
any-llm key decrypt openai
144+
any-llm key decrypt anthropic
145+
146+
# Or provide the key inline
147+
any-llm --any-llm-key 'ANY.v1...' key decrypt openai
148+
```
149+
150+
#### Managing Budgets
151+
152+
```bash
153+
# List budgets for a project
154+
any-llm budget list <project-id>
155+
156+
# Create a project budget
157+
any-llm budget create <project-id> 100.00 --period monthly
158+
159+
# Show a specific budget
160+
any-llm budget show <project-id> monthly
161+
162+
# Update a budget
163+
any-llm budget update <project-id> monthly 200.00
164+
165+
# Delete a budget
166+
any-llm budget delete <project-id> monthly
46167
```
47168

48-
Direct mode (specify provider as argument):
169+
Budget periods: `daily`, `weekly`, `monthly`
170+
171+
#### Managing Clients
172+
49173
```bash
50-
any-llm openai
174+
# List clients for a project
175+
any-llm client list <project-id>
176+
177+
# Create a new client
178+
any-llm client create <project-id> "My Client" --default
179+
180+
# Show client details
181+
any-llm client show <project-id> <client-id>
182+
183+
# Update a client
184+
any-llm client update <project-id> <client-id> --name "Updated Client"
185+
186+
# Set as default client
187+
any-llm client set-default <project-id> <client-id>
188+
189+
# Delete a client
190+
any-llm client delete <project-id> <client-id>
191+
```
192+
193+
#### Output Formats
194+
195+
All commands support two output formats:
196+
- `table` (default): Human-readable formatted output
197+
- `json`: Machine-readable JSON output for scripting
198+
199+
```bash
200+
# Get JSON output for scripting
201+
any-llm --format json project list
202+
203+
# Example: Extract project ID
204+
PROJECT_ID=$(any-llm --format json project create "New Project" | jq -r '.id')
51205
```
52206

53207
### Configuring the API Base URL
@@ -64,13 +218,12 @@ client = AnyLLMPlatformClient(any_llm_platform_url="https://api.example.com/v1")
64218
challenge_data = client.create_challenge(public_key)
65219
```
66220

67-
Or set the environment variable before running the CLI. The CLI will use the
68-
first defined of `--api-base-url` or `ANY_LLM_PLATFORM_URL`.
221+
Or set the `ANY_LLM_PLATFORM_URL` environment variable before running the CLI:
69222

70223
```bash
71224
# Example: temporarily point CLI to a staging backend
72225
export ANY_LLM_PLATFORM_URL="https://staging-api.example.com/v1"
73-
any-llm openai
226+
any-llm key decrypt openai
74227
```
75228

76229
### As a Python Library
@@ -166,7 +319,9 @@ asyncio.run(main())
166319
ANY.v1.<kid>.<fingerprint>-<base64_32byte_private_key>
167320
```
168321

169-
Generate your ANY_LLM_KEY from the project page in the web UI.
322+
You can generate a new ANY_LLM_KEY using:
323+
- The CLI: `any-llm key generate <project-id>`
324+
- The project page in the web UI
170325

171326
## Security Notes
172327

0 commit comments

Comments
 (0)