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
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ body:
description: A minimal sequence someone else can run. Include the exact commands.
placeholder: |
1. 10xgraph init
2. 10xgraph api
2. 10xgraph dev
3. curl -X POST localhost:8000/v1/graph/invoke ...
validations:
required: true
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ jobs:
fail-fast: false
matrix:
# Must cover every version advertised in pyproject.toml classifiers.
python-version: ["3.12", "3.13"]
python-version: ["3.12", "3.13", "3.14", "3.15"]

services:
redis:
Expand Down
58 changes: 58 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,64 @@ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Breaking

- **`10xgraph api` and `10xgraph play` are removed; `10xgraph dev` is the only server command.**
All three ran the same local Uvicorn dev server. Migration: `10xgraph api` ->
`10xgraph dev --no-open`, `10xgraph play` -> `10xgraph dev`. Options are unchanged
(`--config`, `--host`, `--port`, `--reload/--no-reload`). `10xgraph demo --style` no longer
accepts the `play` / `api` aliases. Production is unaffected: the image from `10xgraph build`
runs Gunicorn directly.

### Added

- **Python 3.14 and 3.15 are officially supported.** The test suite runs on 3.12, 3.13, 3.14
and 3.15 in CI, and the package classifiers list all four.
- **`websocket.enabled` in `10xgraph.json`** turns the WebSocket routes on or off. `false` leaves
out `/v1/graph/ws` and `/v1/graph/live`, so the server needs no WebSocket library. `true`
without one refuses to boot with the install hint. Unset keeps them mounted, as before, but
now logs a warning when no library is installed (previously a non-Google project silently
answered 404 on both routes, because only `google-genai` brought in `websockets`).
- **`10xgraph-api[websocket]` extra** (`websockets`) for those routes.
- **`10xgraph init` asks whether to enable WebSocket streaming and realtime audio**
(`--websocket/--no-websocket`, default off). Yes adds the `websocket` extra to a production
`pyproject.toml` and writes `"websocket": {"enabled": true}`; no writes `false`.
- **`10xgraph init` asks for the AI provider** (Google Gemini, OpenAI or Anthropic Claude;
`--provider` for non-interactive runs, default `google`). The scaffold's model, provider,
`.env.example` API key, test conftest and eval simulator model follow the answer.
- **`10xgraph-api[images]` extra.** Pillow is optional in core `10xgraph`; this extra installs it
for image resize, EXIF orientation and format conversion.

### Changed

- Replaced `asyncio.iscoroutinefunction`, which is removed in Python 3.16, with
`inspect.iscoroutinefunction`. No behaviour change.
- **Production scaffolds list only the dependencies the answers need.** `pyproject.toml` now
depends on `10xgraph[<provider>]` and `10xgraph-api[jwt,redis]` as chosen (no extras when
neither is picked), and drops the hardcoded `google-genai`. The generated Dockerfile installs
this list, so JWT and Redis work in the image without a separate `requirements.txt`.
- **The generated Dockerfile removes pip and the CLI's own libraries from the image.** typer,
rich, questionary and what only they pull in (pygments, markdown-it-py, mdurl, prompt-toolkit,
wcwidth, shellingham) are uninstalled in the builder stage by `tenxgraph_api/_slim_image.py`,
unless another installed package still needs them. With pip, about 38 MB smaller.
- Dropped unused dev dependencies (`mkdocs-gen-files`, `mkdocstrings`, `mypy-extensions`,
`lib`, `markdown-it-py`).

### Fixed

- **`10xgraph init` generated a `pyproject.toml` claiming Python 3.10.** The template said
`requires-python = ">=3.10"`, but `10xgraph` needs 3.12, so installing a generated project
on 3.10 or 3.11 failed. It now says `>=3.12`.
- **Redis rate limiting from a URL crashed at startup** with `NameError: Redis`: the class was
imported only for type checking but used at runtime to bind the client.
- **A production scaffold with the Redis rate limiter did not start:** `10xgraph.json` had no
Redis URL. It now reads `"redis": {"url": "${REDIS_URL}"}` from `.env`.
- **A production scaffold did not start at all:** `10xgraph.json` points at
`graph.agent:container`, which the template never defined. `graph/agent.py` now exports it.
- **The production scaffold's `tests/test_graph_nodes.py` failed on collection** (it imported
`graph.nodes` and `graph.utils`, which the template does not create). It now tests the agent
in `graph/agent.py`.

## [0.8.0] - 2026-10-16

Security and scalability pass over the API, following an external review. Every item below
Expand Down
25 changes: 11 additions & 14 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ output schema id, `AF-*` CLI error codes.

It turns a 10xGraph `CompiledGraph` into a production FastAPI service, plus a Typer CLI to
scaffold, run, build, test, and evaluate that service. You write a graph, point `10xgraph.json`
at it, and `10xgraph api` serves it over REST + WebSocket with auth, rate limiting, media
at it, and `10xgraph dev` serves it over REST + WebSocket with auth, rate limiting, media
handling, checkpointer/thread management, and a memory store API.

## Package layout
Expand All @@ -51,17 +51,15 @@ Public exports from the package root (`from tenxgraph_api import ...`): `BaseAut

| Command | Purpose | Notable options |
|---|---|---|
| `10xgraph api` | Start the API server | `--config/-c` (default `10xgraph.json`), `--host/-H`, `--port/-p` (8000), `--reload/--no-reload`, `-v/-q` |
| `10xgraph play` | Start the server and open the hosted playground | same as `api` |
| `10xgraph dev` | Goal-oriented local dev server: same runner as `api`, opens the playground by default. `api`/`play` stay for compatibility | same as `api`, plus `--open/--no-open` |
| `10xgraph init` | Interactively scaffold a project (guided prompts pick dev vs production, auth, rate limit) | `--path/-p`, `--force/-f`, `--name`, `--template` (quick-start\|production), `--auth` (none\|jwt\|custom), `--rate-limit` (none\|memory\|redis), `--yes/-y`, `--non-interactive`, `--dry-run`. There is **no `--prod` flag** |
| `10xgraph build` | Generate a `Dockerfile` (and optionally `docker-compose.yml` / `k8s.yaml`) | `--output/-o`, `--force/-f`, `--python-version` (3.13), `--port`, `--docker-compose/--no-docker-compose`, `--k8s/--no-k8s`, `--service-name` |
| `10xgraph dev` | The only server command: local Uvicorn dev server (reload on) via `commands/api.py::APICommand`, opens the hosted playground when ready. `api` and `play` were removed (use `dev --no-open` / `dev`); production runs Gunicorn in the `10xgraph build` image | `--config/-c` (default `10xgraph.json`), `--host/-H`, `--port/-p` (8000), `--reload/--no-reload`, `--open/--no-open`, `-v/-q` |
| `10xgraph init` | Interactively scaffold a project (guided prompts pick dev vs production, auth, rate limit) | `--path/-p`, `--force/-f`, `--name`, `--template` (quick-start\|production), `--auth` (none\|jwt\|custom), `--rate-limit` (none\|memory\|redis), `--provider` (google\|openai\|anthropic), `--websocket/--no-websocket` (default off; writes `websocket.enabled`). Prod `pyproject.toml` depends on `10xgraph[<provider>]` and `10xgraph-api[jwt,redis,websocket]` as chosen, `--yes/-y`, `--non-interactive`, `--dry-run`. There is **no `--prod` flag** |
| `10xgraph build` | Generate a `Dockerfile` (and optionally `docker-compose.yml` / `k8s.yaml`). The builder stage strips CLI assets, runs `tenxgraph_api/_slim_image.py` (uninstalls typer/rich/questionary and what only they need, unless another package needs them) and removes pip, so the image carries no CLI code paths | `--output/-o`, `--force/-f`, `--python-version` (3.13), `--port`, `--docker-compose/--no-docker-compose`, `--k8s/--no-k8s`, `--service-name` |
| `10xgraph eval` | Run agent evaluations; discovers `*_eval.py`/`eval_*.py`, runs cases (optionally `--parallel`), writes HTML+JSON to `eval_reports/` | `--output/-o`, `--no-report`, `--threshold/-t`, `--open`, `--parallel/-p`, `--max-concurrency/-c` |
| `10xgraph test` | Run project tests via pytest (args after `--` forwarded verbatim) | `--coverage/-C`, `--html`, `-k`, path arg |
| `10xgraph skills` | Install the bundled 10xGraph skill (Agent Skills spec) for Codex/Claude/GitHub, or validate skills | `--agent/-a`, `--path/-p`, `--force/-f`, `--all`, `--list/-l`, `--validate PATH` |
| `10xgraph version` | Show CLI + core framework version | both resolve from installed distribution metadata |
| `10xgraph audit` | Read-only audit of the interpreter, installed CLI/core packages, evaluation-API compatibility, `10xgraph.json`, and the default port. Exits `1` on failure, `0` on warnings only, so it works as a CI gate | `-v/--verbose`, `-q/--quiet` |
| `10xgraph demo` | Preview the animation/timeline/progress themes with no side effects (`Diagnostics` help panel) | `--style` (all\|typing\|network\|init\|build\|eval; `play`/`api` alias to typing/network) |
| `10xgraph demo` | Preview the animation/timeline/progress themes with no side effects (`Diagnostics` help panel) | `--style` (all\|typing\|network\|init\|build\|eval) |
| `10xgraph config` | Browser editor for `10xgraph.json` (`Manage` panel). Loopback-only stdlib HTTP server in `cli/config_editor/` (`schema.py` lists every key, `validation.py` reuses the `graph_config` parsers, `store.py` does conflict-checked atomic writes with a `.bak`). The page is a Preact + Tailwind app whose source lives in `config-editor-ui/`; `npm run build` there writes the committed `static/app.js` and `static/app.css`, so rebuild after editing `config-editor-ui/src` | `--config/-c`, `--port/-p` (0 = any free port), `--open/--no-open` |

Defaults (from `cli/constants.py`): `DEFAULT_HOST="127.0.0.1"`, `DEFAULT_PORT=8000`,
Expand Down Expand Up @@ -97,7 +95,7 @@ Parsed by `tenxgraph_api/src/app/core/config/graph_config.py`. Supported keys:
| `redis` | Redis URL string |
| `rate_limit` | Object (see below) |
| `ag_ui` | `{"enabled": bool}`, default off. Mounts `POST /v1/ag-ui` (AG-UI protocol, for CopilotKit and other AG-UI clients). Needs the `ag-ui` extra |
| `websocket` | `max_connections` (1000), `max_connections_per_user` (10), `realtime_models` (models a `/v1/graph/live` client may pick), `realtime_client_overrides` (init options a client may set; default `voice`, `modalities`, `vad`; add `system_prompt` only for trusted callers) |
| `websocket` | `enabled` (`false` leaves out `/v1/graph/ws` and `/v1/graph/live`; `true` without `websockets`/`wsproto` refuses to boot; unset keeps them and warns if no library; `routers/setup_router.py::_graph_router`), `max_connections` (1000), `max_connections_per_user` (10), `realtime_models` (models a `/v1/graph/live` client may pick), `realtime_client_overrides` (init options a client may set; default `voice`, `modalities`, `vad`; add `system_prompt` only for trusted callers) |
| `runs` | `RunsConfig`: `thread_ids` (`client` default, `server` = only existing ids may be named, enforced by the ownership/rbac backends), `config_keys` / `state_keys` (allow-lists for request config and `initial_state`/PUT state; 422 on other keys; `thread_id`, `run_id` always allowed), `max_concurrent_per_user` (HTTP invoke/stream/AG-UI runs in flight, default 10, 429 over it; `routers/graph/run_limits.py`), `timeout` (seconds; invoke 504, streams end with an error chunk) |
| `token_revocation` | `"redis"` / `"memory"` / `"module:attr"` -> `core/auth/revocation.py::TokenRevocationBackend`, checked by `RequirePermission` (401) and before every WS run; backend errors fail closed (503) |
| `graph_introspection` | `"open"` or `"scoped"` (default scoped in production): `GET /v1/graph`, `/v1/graph/tools`, `/v1/graph:StateSchema` need an explicit `graph:read` scope (`RequirePermission(..., introspection=True)`) |
Expand Down Expand Up @@ -199,16 +197,16 @@ Production error responses hide only 5xx details; raise `request_config.ClientIn
## Optional extras (`pyproject.toml`)

`sentry`, `firebase`, `snowflakekit`, `redis`, `jwt`, `ag-ui` (AG-UI endpoint), `media` (document text extraction via
`textxtract`), `gcloud` (Cloud Logging), `otel` (includes FastAPI instrumentation + OTLP exporter).
`textxtract`), `images` (Pillow via `10xgraph[images]`, for image resize / EXIF / format conversion), `websocket` (`websockets`, needed to serve `/v1/graph/ws` and `/v1/graph/live`), `gcloud` (Cloud Logging), `otel` (includes FastAPI instrumentation + OTLP exporter).

## Development workflow

```bash
# from this folder (10xgraph-api/); a .venv is present
.venv/bin/python -m pytest # tests in tests/
10xgraph init # scaffold (interactive)
10xgraph api --reload # dev server on 127.0.0.1:8000
10xgraph play # server + hosted playground
10xgraph dev # dev server on 127.0.0.1:8000 + hosted playground
10xgraph dev --no-open # dev server only
10xgraph build --docker-compose # Dockerfile + compose
ruff check . && ruff format .
```
Expand All @@ -232,6 +230,5 @@ ruff check . && ruff format .
files from git history if that surface is actually built.
- The local git remote is still `Iamsdt/pyagenity-api.git`; GitHub redirects it to
`10xGraph/10xgraph-api`, so pushes work. Repoint it when convenient.
- The workspace-root `CLAUDE.md` lists only `init/api/play/build` and an older `10xgraph.json`
shape; the real CLI has `eval/test/skills/version` too and the config supports `rate_limit`,
`thread_name_generator`, and `authorization`.
- The docs site (`agentflow-docs/`) still uses `10xgraph api` / `10xgraph play` in many pages;
both commands were removed in favour of `10xgraph dev` (`dev --no-open` for server only).
31 changes: 22 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,17 @@ Optional extras; install only what you configure:
pip install "10xgraph-api[redis]" # Redis rate-limit / cache backend
pip install "10xgraph-api[jwt]" # JWT authentication
pip install "10xgraph-api[media]" # Document text extraction (multimodal)
pip install "10xgraph-api[images]" # Image resize / EXIF / format conversion (Pillow)
pip install "10xgraph-api[ag-ui]" # AG-UI endpoint
pip install "10xgraph-api[websocket]" # WebSocket streaming + realtime audio
pip install "10xgraph-api[otel]" # OpenTelemetry tracing
pip install "10xgraph-api[snowflakekit]" # Snowflake ID generation
pip install "10xgraph-api[sentry]" # Sentry error reporting
pip install "10xgraph-api[firebase]" # Firebase Admin SDK
pip install "10xgraph-api[gcloud]" # Google Cloud Logging
```

Requires Python 3.12 or newer. Installs the core `10xgraph` framework as a dependency.
Requires Python 3.12 or newer. Tested on 3.12, 3.13, 3.14 and 3.15. Installs the core `10xgraph` framework as a dependency.

---

Expand All @@ -113,9 +119,6 @@ Requires Python 3.12 or newer. Installs the core `10xgraph` framework as a depen
10xgraph build --docker-compose
```

`10xgraph api` (server only) and `10xgraph play` (server + playground) remain
available; `dev` is the goal-oriented wrapper around them.

---

## CLI commands
Expand All @@ -133,9 +136,17 @@ Initialize a new project with configuration and a sample graph.
10xgraph init --path ./my-app --name MyAgent --template quick-start \
--non-interactive # reproducible CI/agent workflow
10xgraph init --path ./my-app --template production --auth jwt \
--rate-limit redis --yes --dry-run
--rate-limit redis --provider openai --yes --dry-run
```

`init` asks which AI provider to use (`--provider google|openai|anthropic`, default `google`)
and writes the matching model and API key variable. It also asks whether to enable WebSocket
streaming and realtime audio (`--websocket`, default off); off leaves `/v1/graph/ws` and
`/v1/graph/live` unmounted and installs no WebSocket library. A production project's `pyproject.toml`
lists only what your answers need, for example `10xgraph[openai]` and
`10xgraph-api[jwt,redis]`, and `10xgraph build` installs exactly that list, so no separate
`requirements.txt` is needed.

### `10xgraph dev`

Start the development API server and open the hosted playground when it is ready.
Expand All @@ -147,16 +158,18 @@ Start the development API server and open the hosted playground when it is ready
10xgraph dev --no-open --no-reload # API only, without auto-reload
```

`10xgraph api` and `10xgraph play` remain available as compatibility commands.
`dev` is the only server command: the old `api` and `play` commands were removed
(use `10xgraph dev --no-open` and `10xgraph dev`). It runs Uvicorn with reload for local
work; production runs Gunicorn in the image from `10xgraph build`.

### Adaptive and structured output

```bash
10xgraph play # branded intro, then output in your scrollback
10xgraph dev # branded intro, then output in your scrollback
10xgraph demo # preview every animation theme safely
10xgraph demo --style build # one theme: typing, network, init, build, or eval
10xgraph --fullscreen play # opt-in pinned header/footer surface
10xgraph --no-animation play # accessible/static workflow
10xgraph --fullscreen dev # opt-in pinned header/footer surface
10xgraph --no-animation dev # accessible/static workflow
10xgraph --format plain --no-color audit
10xgraph --format jsonl eval --parallel
10xgraph --quiet build
Expand Down
2 changes: 1 addition & 1 deletion examples/security/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export JWT_ACCESS_TOKEN_EXPIRE_MINUTES=30

**Step 3:** Start the application
```bash
10xgraph api
10xgraph dev --no-open
```

### 2. Custom Authentication
Expand Down
17 changes: 12 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ classifiers = [
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: 3.15",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Internet :: WWW/HTTP :: HTTP Servers",
]
Expand Down Expand Up @@ -100,6 +102,11 @@ jwt = [
media = [
"textxtract[pdf,docx,html,xml,md]>=0.2.0",
]
# Pillow for image resize / EXIF / format conversion in core's media processor.
# Without it, uploaded images pass through unchanged.
images = [
"10xgraph[images]>=0.10.0,<2.0",
]
gcloud = [
"google-cloud-logging",
]
Expand All @@ -112,6 +119,11 @@ otel = [
ag-ui = [
"ag-ui-protocol>=1.0,<2",
]
# WebSocket streaming (/v1/graph/ws) and realtime audio (/v1/graph/live). Uvicorn needs a
# WebSocket library to serve them; set "websocket": {"enabled": false} to run without one.
websocket = [
"websockets>=13.0,<17",
]

[project.scripts]
10xgraph = "tenxgraph_api.cli.main:main"
Expand Down Expand Up @@ -371,12 +383,7 @@ dev = [
"pre-commit>=3.8.0",
"mypy>=1.11.0",
"ruff==0.5.2",
"mkdocs-gen-files==0.5.0",
"mkdocstrings==0.25.2",
"mypy-extensions==1.0.0",
"httpx==0.27.0",
"lib==4.0.0",
"markdown-it-py==3.0.0",
"requests==2.32.3",
"redis>=5.0.7",
"PyJWT",
Expand Down
Loading
Loading