feat: auto router (#15)
This commit is contained in:
+18
-9
@@ -26,6 +26,9 @@ uv run ty check .
|
||||
make check
|
||||
```
|
||||
|
||||
Takopi runs in **auto-router** mode by default. `default_engine` in `takopi.toml` selects
|
||||
the engine for new threads; engine subcommands override that default for the process.
|
||||
|
||||
## Module Responsibilities
|
||||
|
||||
### `bridge.py` - Telegram bridge loop
|
||||
@@ -44,9 +47,11 @@ The orchestrator module containing:
|
||||
**Key patterns:**
|
||||
- Bridge schedules runs FIFO per thread to avoid concurrent progress messages; runner locks enforce per-thread serialization
|
||||
- `/cancel` routes by reply-to progress message id (accepts extra text)
|
||||
- Progress edits are throttled to ~1s intervals and only run when new events arrive
|
||||
- `/{engine}` on the first line selects the engine for new threads
|
||||
- Progress edits are throttled to 2s intervals and only run when new events arrive
|
||||
- Resume tokens are runner-formatted command lines (e.g., `` `codex resume <token>` ``)
|
||||
- Resume parsing is delegated to the active runner (no cross-engine fallback)
|
||||
- Resume parsing polls all runners via `AutoRouter.resolve_resume()` and routes to the first match
|
||||
- Bot command menu is synced on startup (`cancel` + engine commands)
|
||||
|
||||
### `cli.py` - CLI entry point
|
||||
|
||||
@@ -164,12 +169,16 @@ poll_updates() drains backlog, long-polls, filters chat_id == from_id == cfg.cha
|
||||
↓
|
||||
run_main_loop() spawns tasks in TaskGroup
|
||||
↓
|
||||
handle_message() spawned as task
|
||||
router.resolve_resume(text, reply_text) → ResumeToken | None
|
||||
↓
|
||||
router.runner_for(resume_token) → selects runner (default engine if None)
|
||||
↓
|
||||
handle_message() spawned as task with selected runner
|
||||
↓
|
||||
Send initial progress message (silent)
|
||||
↓
|
||||
CodexRunner.run()
|
||||
├── Spawns: codex exec --json ... -
|
||||
runner.run(prompt, resume_token)
|
||||
├── Spawns engine subprocess (e.g., codex exec --json)
|
||||
├── Streams JSONL from stdout
|
||||
├── Normalizes JSONL -> takopi events
|
||||
├── Yields Takopi events (async iterator)
|
||||
@@ -186,10 +195,10 @@ Send/edit final message
|
||||
|
||||
### Resume Flow
|
||||
|
||||
Same as above, but:
|
||||
- Runners parse resume lines (e.g. `` `codex resume <token>` ``)
|
||||
- Command becomes: `codex exec --json resume <token> -`
|
||||
- Per-token lock serializes concurrent resumes
|
||||
Same as above; auto-router polls all runners to extract resume tokens:
|
||||
- Router returns first matching token (e.g. `` `claude --resume <id>` `` routes to Claude)
|
||||
- Selected runner spawns with resume (e.g. `codex exec --json resume <token> -`)
|
||||
- Per-token lock serializes concurrent resumes on the same thread
|
||||
|
||||
## Error Handling
|
||||
|
||||
|
||||
+51
-13
@@ -1,10 +1,10 @@
|
||||
# Takopi Specification v0.2.0 (minimal) [2025-12-31]
|
||||
# Takopi Specification v0.4.0 [2026-01-01]
|
||||
|
||||
This document is **normative**. The words **MUST**, **SHOULD**, and **MAY** express requirements.
|
||||
|
||||
## 1. Scope
|
||||
|
||||
Takopi v0.2.0 specifies:
|
||||
Takopi v0.4.0 specifies:
|
||||
|
||||
- A **Telegram** bot bridge that runs an agent **Runner** and posts:
|
||||
- a throttled, edited **progress message**
|
||||
@@ -12,12 +12,12 @@ Takopi v0.2.0 specifies:
|
||||
- **Thread continuation** via a **resume command** embedded in chat messages
|
||||
- **Parallel runs across different threads**
|
||||
- **Serialization within a thread** (no concurrent runs on the same thread)
|
||||
- **Automatic runner selection** among multiple engines based on ResumeLine (with a configurable default for new threads)
|
||||
- A Takopi-owned **normalized event model** produced by runners and consumed by renderers/bridge
|
||||
|
||||
Out of scope for v0.2.0:
|
||||
Out of scope for v0.4.0:
|
||||
|
||||
- Non-Telegram clients (Slack/Discord/etc.)
|
||||
- Auto-selecting among multiple runners
|
||||
- Token-by-token streaming of the assistant’s final answer
|
||||
- Engines/runners that cannot provide **stable action IDs** within a run
|
||||
|
||||
@@ -71,11 +71,13 @@ Constraints:
|
||||
|
||||
### 3.4 Bridge resume resolution (MUST)
|
||||
|
||||
Given `text` (user message) and optional `reply_text` (the message being replied to):
|
||||
Given `text` (user message), optional `reply_text` (the message being replied to), and an ordered list of available runners `runners`:
|
||||
|
||||
1. The bridge MUST attempt `runner.extract_resume(text)`.
|
||||
2. If not found, it MUST attempt `runner.extract_resume(reply_text)` if present.
|
||||
3. If still not found, the run MUST start with `resume=None` (new thread).
|
||||
1. The bridge MUST attempt to extract a resume token by polling all runners in order:
|
||||
1. for each `r` in `runners`, attempt `r.extract_resume(text)`
|
||||
2. choose the **first** runner that returns a non-`None` token and stop
|
||||
2. If not found, it MUST repeat step (1) for `reply_text` if present.
|
||||
3. If still not found, the run MUST start with `resume=None` (new thread) on the default runner (per §8, including chat-level overrides).
|
||||
|
||||
## 4. Normalized event model
|
||||
|
||||
@@ -335,12 +337,30 @@ Action update collapsing:
|
||||
|
||||
## 8. Configuration and engine selection
|
||||
|
||||
Decision (v0.2.0):
|
||||
Decision (v0.4.0):
|
||||
|
||||
* Exactly one runner is selected at startup via a CLI subcommand (no default).
|
||||
* If no engine subcommand is provided, Takopi prints an engine chooser panel and exits.
|
||||
* Resume extraction uses only the selected runner.
|
||||
* If a user provides a resume line for a different engine, extraction fails and the bridge treats the message as a new thread (`resume=None`).
|
||||
* Takopi MUST support configuring a **default engine** used to start new threads (`resume=None`).
|
||||
* If not configured, the default engine is implementation-defined (non-normative: the reference implementation defaults to `codex`).
|
||||
* If no engine subcommand is provided, Takopi MUST run in **auto-router** mode:
|
||||
* new threads use the configured default engine
|
||||
* resumed threads are routed based on ResumeLine extraction (per §3.4)
|
||||
* If an engine subcommand is provided, Takopi MUST still use the auto-router, but it overrides the configured default engine for new threads.
|
||||
* Resume extraction MUST poll **all** available runners (per §3.4) and route to the first matching runner.
|
||||
* New thread engine override (chat-level):
|
||||
* Users MAY prefix the first non-empty line with `/{engine}` (e.g. `/claude` or `/codex`) to select the engine for a **new** thread.
|
||||
* The bridge MUST strip that directive from the prompt before invoking the runner.
|
||||
* If a ResumeToken is resolved from the message or reply, it MUST take precedence and the `/{engine}` directive MUST be ignored.
|
||||
|
||||
### 8.1 Command menu (Telegram)
|
||||
|
||||
Takopi SHOULD keep the bot’s slash-command menu in sync at startup by calling
|
||||
`setMyCommands` with the canonical list of supported commands.
|
||||
|
||||
* The command list MUST include:
|
||||
* `cancel` — cancel the current run
|
||||
* one entry per configured engine
|
||||
* The command list MUST NOT include commands the bot does not support.
|
||||
* Command descriptions SHOULD be terse and lowercase.
|
||||
|
||||
## 9. Testing requirements (MUST)
|
||||
|
||||
@@ -373,5 +393,23 @@ Tests MUST cover:
|
||||
|
||||
* completed-only actions render correctly
|
||||
* repeated events for same Action.id collapse as intended
|
||||
7. **Auto-router engine selection**
|
||||
|
||||
* resume lines for non-default engines are detected and routed correctly (poll all runners)
|
||||
* new threads use the configured default engine, with CLI subcommand overriding it
|
||||
|
||||
Test tooling SHOULD include event factories, deterministic/fake time, and a script/mock runner.
|
||||
|
||||
## 10. Changelog
|
||||
|
||||
### v0.4.0 (2026-01-01)
|
||||
|
||||
- Add auto-router engine selection by polling all runners to decode resume lines; add configurable default engine for new threads (subcommand overrides default).
|
||||
|
||||
### v0.3.0 (2026-01-01)
|
||||
|
||||
- Require runners to implement explicit resume formatting/extraction/detection and treat runners as authoritative for resume tokens/lines.
|
||||
|
||||
### v0.2.0 (2025-12-31)
|
||||
|
||||
- Initial minimal Takopi specification (Telegram bridge + runner protocol + normalized events + resume support).
|
||||
|
||||
Reference in New Issue
Block a user