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
4 changes: 4 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ into the image/compose that drive the same `main.py`.
| `AGENTOMATIC_LOGS_HISTORY` | Persist per-agent invoke/chat/stream history (default off) |
| `AGENTOMATIC_ALLOW_LOGSLLM_ANALYSIS` | Enable LLM analysis over those logs (default off) |
| `AGENTOMATIC_INGESTION_ROOT` | Confine ingestion source/output paths to this dir (default: cwd) |
| `AGENTOMATIC_TASK_EVENT_LOG` | Task-event replay backend: `memory` (default), `none` to disable, or a registered provider |
| `AGENTOMATIC_TASK_EVENTS_PER_TASK` / `AGENTOMATIC_TASK_EVENT_TASKS` / `AGENTOMATIC_TASK_EVENT_MAX_MB` | Task-event replay retention (default 512 / 1024 / 32 MB) |
| `AGENTOMATIC_STREAM_REPLAY` | Retain `/invoke/stream` frames for replay (default on; `0` disables) |
| `AGENTOMATIC_STREAM_FRAMES` / `AGENTOMATIC_STREAM_COUNT` / `AGENTOMATIC_STREAM_MAX_MB` | Stream replay retention (default 512 / 256 / 64 MB) |
| `AGENTOMATIC_DEBUG_ERRORS` | Return raw exception text in API errors (default off — dev only) |
| `AGENTOMATIC_OTEL_CONSOLE` | Print OpenTelemetry spans to stdout (default off) |
| `AGENTOMATIC_RATE_LIMIT_TRUST_PROXY_HEADERS` | Honour X-Forwarded-For for rate-limit keys (default off) |
Expand Down
100 changes: 98 additions & 2 deletions docs/architecture/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,10 +213,13 @@ curl -X POST http://localhost:8000/api/v1/my_agent/invoke/stream \
**SSE Response:**

```
id: 1
data: {"response": "", "steps_taken": ["retrieve"]}

id: 2
data: {"response": "Quantum computing uses...", "steps_taken": ["retrieve", "generate"]}

id: 3
data: [DONE]
```

Expand All @@ -226,10 +229,48 @@ data: [DONE]
|---|---|
| `Content-Type` | `text/event-stream` |
| `X-Agent` | Agent name |
| `X-Stream-Id` | Identity to replay this stream with |
| `Cache-Control` | `no-cache` |

---

#### `GET /invoke/stream/{stream_id}`

Re-send the frames a previous `/invoke/stream` already produced, using the
`X-Stream-Id` that response carried. Accepts `?since=` or `Last-Event-ID` and
returns only the frames after that point; `404` once the stream is no longer
retained.

A retained stream is scoped to the agent that produced it **and** the
principal it was produced for. Replaying another agent's stream — or another
caller's — returns `404`, the same answer as an id that does not exist, so the
response never confirms that someone else's stream is there.

```bash
curl -N "http://localhost:8000/api/v1/my_agent/invoke/stream/stream_a1b2c3d4?since=2"
```

!!! warning "Replay is not resumption"

This returns what was already **produced**. It does not restart the run:
the agent execution is bound to the request that was cancelled, so a
client that disconnects mid-answer loses the *generation*, and replay
recovers only the part that had been emitted.

For a stream that keeps working across a dropped connection, submit the
work as a task and follow the task's events — that path is resumable end
to end:

```bash
TASK=$(curl -sX POST http://localhost:8000/api/v1/my_agent/invoke/async \
-H 'Content-Type: application/json' -d '{"query": "..."}' | jq -r .id)
curl -N "http://localhost:8000/api/v1/tasks/$TASK/events"
```

See [Tasks & Execution Modes](../guide/tasks.md#resuming-a-dropped-stream).

---

#### `POST /chat`

Session-aware chat with automatic conversation memory. When a thread store is configured, this endpoint automatically loads history, invokes the agent, and persists messages.
Expand Down Expand Up @@ -360,18 +401,40 @@ curl http://localhost:8000/api/v1/my_agent/card
"streaming": true,
"chat": true,
"invoke": true,
"a2a": true
"a2a": true,
"stateTransitionHistory": true,
"pushNotifications": true,
"resumableStreams": true
},
"endpoints": {
"invoke": "/api/v1/my_agent/invoke",
"chat": "/api/v1/my_agent/chat",
"stream": "/api/v1/my_agent/invoke/stream",
"health": "/api/v1/my_agent/health"
"stream_replay": "/api/v1/my_agent/invoke/stream/{stream_id}",
"health": "/api/v1/my_agent/health",
"card": "/api/v1/my_agent/card",
"a2a_tasks": "/api/v1/my_agent/a2a/tasks",
"a2a_events": "/api/v1/my_agent/a2a/tasks/{task_id}/events"
},
"metadata": {}
}
```

`stateTransitionHistory`, `pushNotifications` and `resumableStreams` all
depend on the task subsystem. When it is disabled they report `false` and the
`a2a_*` endpoints are omitted — the card describes what this deployment
actually serves, so a client is never sent down a path that answers `501`.

!!! note "Discovery returns the same card"

`GET /.well-known/agent.json` — the canonical A2A discovery document —
renders each agent through the same builder, so its cards are identical to
this one, wrapped in a platform envelope:

```json
{"platform": "My Platform", "version": "1.0.0", "agents": {"my_agent": { ... }}}
```

---

#### `POST /a2a/tasks`
Expand Down Expand Up @@ -429,6 +492,39 @@ curl http://localhost:8000/api/v1/my_agent/a2a/tasks/task_a1b2c3d4e5f6

---

#### `GET /a2a/tasks/{task_id}/events`

Subscribe — or **re-subscribe** — to an A2A task's progress over
Server-Sent Events. Frames are A2A task objects (`submitted`, `working`,
`completed`, `failed`, `canceled`), and the stream ends with `data: [DONE]`.
Returns `501` without the task subsystem, `404` for an unknown task.

Every frame carries an SSE `id:` holding the event's monotonic `sequence`, so
a client that loses the connection resumes precisely instead of restarting:

```bash
# Subscribe
curl -N http://localhost:8000/api/v1/my_agent/a2a/tasks/task_a1b2c3d4e5f6/events

# Re-subscribe after event 4 — replays 5 onwards, then goes live
curl -N -H "Last-Event-ID: 4" \
http://localhost:8000/api/v1/my_agent/a2a/tasks/task_a1b2c3d4e5f6/events
curl -N "http://localhost:8000/api/v1/my_agent/a2a/tasks/task_a1b2c3d4e5f6/events?since=4"
```

```json
{"task_id": "task_a1b2c3d4e5f6", "sequence": 5, "status": "working",
"progress": {"percent": 60.0, "message": "step 3"}}
```

A reconnect whose resume point has already been evicted receives a
`{"event": "truncated"}` frame followed by the current task object, so a
client is never handed a partial history as if it were complete. See
[Tasks & Execution Modes](../guide/tasks.md#resuming-a-dropped-stream) for
retention limits and the multi-worker caveat.

---

#### `POST /a2a/tasks/{task_id}/cancel`

Request cooperative cancellation of an in-flight A2A task. This endpoint is
Expand Down
39 changes: 36 additions & 3 deletions docs/cli/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,18 +65,48 @@ Commands:
Scaffold a new agent directory from one of the pre-built templates.

```text
Usage: agentomatic init [OPTIONS] NAME
Usage: agentomatic init [OPTIONS] [NAME]

Arguments:
NAME Agent name (snake_case) [required]
NAME Agent name (snake_case) [prompted for if omitted]

Options:
-t, --template [basic|class|full|coordinator|pipeline|rag|chatbot|deepagent|custom|legacy_dict|plugin|endpoint|connection|ingestion|extraction|langchain]
Template to use (interactive picker if omitted)
-d, --dir TEXT Agents parent directory [default: agents]
-d, --dir TEXT Parent directory for the package (default: the
enclosing project's agents/, or the template's own
directory — plugins/, endpoints/, ingestion/,
pipelines/)
--here Scaffold into the current directory, skipping
project-root detection
-f, --force Overwrite existing agent directory
```

#### Where the code lands

`init` writes into the **enclosing project**, not blindly into the current
directory. It walks up from where you are (stopping at a repository root or
your home directory) looking for a project marker — `.agentomatic-stack`, or
an `agents/` directory alongside `stacks/` or a platform `main.py`.

```bash
cd my_platform/notebooks
agentomatic init helper --template basic
# 📦 Project root: /path/to/my_platform (detected from notebooks/)
# 📍 Location: /path/to/my_platform/agents/helper
```

If you run it in the directory you *just created a project in* — the
`agentomatic new my_platform` then forgot-to-`cd` case — the lone project in a
subdirectory is used, and the choice is printed. Pass `--here` to scaffold into
the current directory instead, or `--dir` to choose the location outright.

When no project is found, the current directory is bootstrapped into one:
`stacks/local.yaml`, `stacks/remote.yaml`, `.agentomatic-stack`,
`agents/__init__.py` and a platform `main.py` (an existing `main.py` is never
overwritten). Nothing else is created — no empty `plugins/`, `endpoints/`,
`ingestion/` or `pipelines/`; use `agentomatic new` for the full layout.

#### Templates

| Template | Description |
Expand All @@ -101,6 +131,9 @@ Options:
#### Examples

```bash
# Fully interactive — prompts for the name, then the template
agentomatic init

# Interactive template selection (requires questionary)
agentomatic init support_agent

Expand Down
24 changes: 16 additions & 8 deletions docs/getting-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,19 +84,27 @@ If you prefer a lightweight install, you can select only the modules and depende

!!! warning "What `all` does *not* include"

`all` covers `langgraph`, `ollama`, `metrics`, `db`, `cli`, `studio`,
`optimize`, `telemetry`, `dotenv`, `security`, `swarm` and `vector`.

It deliberately leaves out the vendor LLM drivers — `openai`, `azure`,
`vertex` — which follow the provider-agnostic principle: you install the
SDK for the backend you actually use. It also leaves out `db-postgres`
(an alternative to `db`) and `ui` (Chainlit), which is a heavy dependency.
`all` covers `langgraph`, `langchain`, `openai`, `ollama`, `metrics`,
`db`, `cli`, `studio`, `optimize`, `telemetry`, `dotenv`, `security`,
`swarm` and `vector`.

`openai` is in that list for the **local-first** path, not for OpenAI:
the `omlx` provider in a freshly scaffolded `stacks/local.yaml` talks to
your own OpenAI-*compatible* server (oMLX, llama.cpp, vLLM, LM Studio)
through `langchain-openai`. Without it, `agentomatic[all]` scaffolds a
project whose every LLM call fails with *"client library is not
installed"*.

It still leaves out the hosted vendor drivers `azure` and `vertex`, which
follow the provider-agnostic principle: you install the SDK for the
backend you actually use. It also leaves out `db-postgres` (an
alternative to `db`) and `ui` (Chainlit), which is a heavy dependency.

So `agentomatic ui` needs `pip install "agentomatic[ui]"` even after an
`all` install. Add what you need alongside it:

```bash
pip install "agentomatic[all,openai,ui]"
pip install "agentomatic[all,ui]"
```

!!! tip "Quote the extras in zsh/bash"
Expand Down
8 changes: 6 additions & 2 deletions docs/guide/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,10 @@ directory, host, port, and stack values passed to `agentomatic run`.
| `AGENTOMATIC_PLUGIN_AUTORELOAD` | `plugin_autoreload` | `false` | Watch the promoted artifact version and reload plugins safely |
| `AGENTOMATIC_PLUGIN_AUTORELOAD_INTERVAL` | `plugin_autoreload_interval` | `5` | Seconds between artifact-pointer checks |
| `AGENTOMATIC_RUNS_ROOT` | `runs_root` | `.local/runs` | Scratch directory for pipeline/task outputs |
| `AGENTOMATIC_TASK_EVENT_LOG` | task-event replay backend | `memory` | `none` disables replay; any registered provider name also works |
| `AGENTOMATIC_TASK_EVENTS_PER_TASK` / `AGENTOMATIC_TASK_EVENT_TASKS` / `AGENTOMATIC_TASK_EVENT_MAX_MB` | task-event retention | `512` / `1024` / `32` | Caps how much history `/tasks/{id}/events` can replay. The MB ceiling is what protects a container's memory limit |
| `AGENTOMATIC_STREAM_REPLAY` | `/invoke/stream` frame retention | `true` | `0` streams without retaining anything |
| `AGENTOMATIC_STREAM_FRAMES` / `AGENTOMATIC_STREAM_COUNT` / `AGENTOMATIC_STREAM_MAX_MB` | stream retention | `512` / `256` / `64` | Caps what `GET /invoke/stream/{id}` can replay |
| `AGENTOMATIC_AUDIT_LOG` | `audit_log` | `""` (disabled) | JSONL op-audit sink path (non-PII metadata only) |
| `AGENTOMATIC_AUDIT_HASH_KEY` | — | API key or process-local key | HMAC key for audit correlation references |
| `AGENTOMATIC_CHUNK_SIZE_TOKENS` | `chunk_size_tokens` | `1200` | Default ingestion chunk size |
Expand Down Expand Up @@ -487,7 +491,7 @@ platform.run(

??? question "My environment variables aren't being loaded"
Check these common causes:

1. **`.env` file location**: Must be in the project root (same directory as `main.py`)
2. **Variable shape**: Generated-platform variables start with `AGENTOMATIC_`
(for example, `AGENTOMATIC_LOG_LEVEL`); `PlatformSettings` variables use
Expand All @@ -497,7 +501,7 @@ platform.run(

??? question "CORS errors from my frontend"
Pass your frontend's origin to `cors_origins`:

```python
platform = AgentPlatform.from_folder(
"agents/",
Expand Down
19 changes: 18 additions & 1 deletion docs/guide/status.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,30 @@ starting the platform. It polls the JSON endpoint every few seconds and renders:
"by_status": { "queued": 0, "running": 1, "succeeded": 10, "failed": 1, "cancelled": 0 },
"running": 1,
"max_concurrency": 8,
"supported_targets": ["agent", "endpoint", "ingestion", "pipeline", "plugin"]
"supported_targets": ["agent", "endpoint", "ingestion", "pipeline", "plugin"],
"event_log": { "tasks": 4, "events": 61, "bytes": 31232 }
},
"streams": { "streams": 2, "frames": 9, "bytes": 12408 },
"storage": { "status": "healthy" },
"generated_at": 1718900000.0
}
```

### Replay occupancy

`tasks.event_log` and `streams` report what the replay buffers currently
hold — the history that makes
[dropped streams resumable](tasks.md#resuming-a-dropped-stream). They hold
memory, so `bytes` is the number to watch when sizing a container: if it sits
at the configured ceiling, raise it or lower retention with
`AGENTOMATIC_TASK_EVENT_MAX_MB` / `AGENTOMATIC_STREAM_MAX_MB`, or turn replay
off entirely (`AGENTOMATIC_TASK_EVENT_LOG=none`,
`AGENTOMATIC_STREAM_REPLAY=0`). See
[Configuration](configuration.md).

`event_log` is absent when a registered custom backend does not report
occupancy — `stats` is not part of the `TaskEventLog` contract.

The top-level `status` is `degraded` if any resource (including a configured
connection) is unhealthy or storage is unhealthy, otherwise `healthy` — handy
for a single uptime check.
Expand Down
Loading
Loading