🛟 Updated.
5e919b3 parent: 53ad5d2 modified
.tickets/epics.yaml +3 -0 | @@ -45,3 +45,6 @@ epics: | ||
| 45 | 45 | - title: Loop |
| 46 | 46 | state: open |
| 47 | 47 | color: "#1d76db" |
| 48 | + - title: Mini Me as a service | |
| 49 | + state: open | |
| 50 | + color: "#006b75" | |
| @@ -45,3 +45,6 @@ epics: | |||
| 45 | - title: Loop | 45 | - title: Loop |
| 46 | state: open | 46 | state: open |
| 47 | color: "#1d76db" | 47 | color: "#1d76db" |
| 48 | + - title: Mini Me as a service | ||
| 49 | + state: open | ||
| 50 | + color: "#006b75" | ||
added
.tickets/issues/0113-mini-me-as-a-service-ideas.yaml +175 -0 | new file mode 100644 | ||
| @@ -0,0 +1,175 @@ | ||
| 1 | +# Managed by IssueSpec. Hand edits are welcome; keep the schema valid. | |
| 2 | +id: 113 | |
| 3 | +title: "Mini Me as a service: turn mm into a dedicated app, or expose it as services — ideas" | |
| 4 | +state: open | |
| 5 | +author: | |
| 6 | + name: k33g | |
| 7 | + email: ph.charriere@gmail.com | |
| 8 | +createdAt: 2026-10-21T13:00:00.000Z | |
| 9 | +updatedAt: 2026-10-21T13:00:00.000Z | |
| 10 | +labels: [] | |
| 11 | +milestone: v1.0.0 | |
| 12 | +epic: Mini Me as a service | |
| 13 | +related: | |
| 14 | + - 71 | |
| 15 | + - 105 | |
| 16 | + - 46 | |
| 17 | + - 89 | |
| 18 | + - 66 | |
| 19 | +body: | | |
| 20 | + Starting point for the epic "Mini Me as a service". Ideas only — nothing | |
| 21 | + decided, nothing implemented. | |
| 22 | + | |
| 23 | + ## The wish (user's words, 2026-10-21) | |
| 24 | + Make it possible — through plugins, scripts, a UI… — to turn mini-me into | |
| 25 | + an application dedicated to one particular task, OR to have mini-me expose | |
| 26 | + services that other applications call. The ideas are still vague: this | |
| 27 | + ticket collects directions to choose from. | |
| 28 | + | |
| 29 | + ## What mm already has that points this way (checked in the repo) | |
| 30 | + - A dedicated app is already "just YAML" for many demos: demos/translator, | |
| 31 | + little-coder, guards, docs-search, memory-search, plans… — a system | |
| 32 | + prompt, a model, skills, declared tools (`tools:`), hooks, AGENTS.md. | |
| 33 | + What is missing is packaging and a UI of their own. | |
| 34 | + - mm is already callable from a program, three ways: `mm -p "…"` (one | |
| 35 | + answer on stdout; with `structuredOutput`, JSON shaped by Go types), | |
| 36 | + `mm --ask <agent>` (context on stdin), and ACP — a JSON-RPC protocol on | |
| 37 | + stdio with sessions, tool calls, permissions and forms. The `sidekick` | |
| 38 | + web client already drives mm over ACP. | |
| 39 | + - Agents already call agents (`mmConfigs`, `/ask-<name>`, `ask_agent`): | |
| 40 | + a first "service" model, process to process. | |
| 41 | + - Extension points: hooks (any language, JSON on stdin/stdout), declared | |
| 42 | + tools, MCP servers, skills; WASM plugins studied in ticket 46. | |
| 43 | + - Packaging: the Docker Sandbox kits (sbx), release binaries. | |
| 44 | + - Ticket 71 (SDK) already proposes the Go-library road: remove the global | |
| 45 | + state (config.Cfg, the active sink, the plan store), a public package, | |
| 46 | + and a headless `mm -p --output-format stream-json`. Ticket 105 asks for a | |
| 47 | + package that exposes the methods. | |
| 48 | + | |
| 49 | + ## Direction A — mini-me as a dedicated application | |
| 50 | + 1. An "app bundle": one folder = agent.yaml + skills + tools + hooks + | |
| 51 | + prompts + an optional UI description; `mm run ./translator` (or | |
| 52 | + `mm app add <url>`). Versioned, shareable, installable. | |
| 53 | + 2. Commands defined in the YAML: `/translate-pro <text>` = a prompt | |
| 54 | + template + chosen tools, shown in /help and the editor menu — a task app | |
| 55 | + without writing Go. | |
| 56 | + 3. A start-up form: the app asks what it needs (the ask_user forms already | |
| 57 | + exist) before the first question — a wizard (demos/project-wizard does | |
| 58 | + it through the model today). | |
| 59 | + 4. A window layout per app: header text, which panes, which keys, a result | |
| 60 | + pane (e.g. "translation" beside "original"). | |
| 61 | + 5. Output contracts per app: structuredOutput, files written with | |
| 62 | + save_answer, a "result" event a script can wait for. | |
| 63 | + 6. A small web UI served by mm itself for one app (a form + the answer), | |
| 64 | + on top of the service below. | |
| 65 | + | |
| 66 | + ## Direction B — mini-me exposes services | |
| 67 | + 1. **mm as an MCP server** (`mm -mcp`): each configured agent becomes one | |
| 68 | + MCP tool (`ask_translator`, `ask_expert`). Any MCP client — Claude | |
| 69 | + Desktop, Zed, other agents, mm itself — can call a mini-me agent with | |
| 70 | + its model, skills and tools. Reuses the ask machinery. Likely the | |
| 71 | + cheapest first step. | |
| 72 | + 2. **An OpenAI-compatible HTTP façade** (`mm -serve :8080`, POST | |
| 73 | + /v1/chat/completions): any OpenAI client (Open WebUI, scripts, IDE | |
| 74 | + plugins) talks to an mm AGENT — tools, skills, hooks run server-side — | |
| 75 | + instead of a bare model. | |
| 76 | + 3. **A small REST API of mm's own**: POST /agents/{name}/ask, sessions | |
| 77 | + (GET /sessions, resume), /docs and /memory searches as endpoints, | |
| 78 | + streaming as SSE. | |
| 79 | + 4. **ACP over a network transport** (WebSocket) instead of stdio, so a web | |
| 80 | + page or a remote editor drives mm like sidekick does locally. | |
| 81 | + 5. **A worker mode**: mm reads jobs from a folder, a queue or a webhook and | |
| 82 | + writes results — no human in the loop (see ticket 89). | |
| 83 | + 6. **Headless stream-json** (from ticket 71): one JSON event per line, the | |
| 84 | + simplest contract for Python/Node/shell callers. | |
| 85 | + | |
| 86 | + ## Direction C — plugins | |
| 87 | + 1. A plugin = a package of tools + hooks + skills (+ maybe a UI piece), | |
| 88 | + installed with `mm plugin add`, declared in agent.yaml. | |
| 89 | + 2. WASM for sandboxed plugins (ticket 46); plain processes for the rest. | |
| 90 | + 3. A plugin can also be a "front end": the window, ACP and the REPL are | |
| 91 | + already three clients of one loop; a fourth could be a plugin. | |
| 92 | + | |
| 93 | + ## What every direction needs (prerequisites to weigh) | |
| 94 | + - No more process-wide state (ticket 71 phase 1): one process serving many | |
| 95 | + callers needs per-session config, sink, plans (ticket 108 did plans for | |
| 96 | + ACP; the active-sink global and config.Cfg remain). | |
| 97 | + - Concurrency: ACP turns are serialised today; a service needs parallel | |
| 98 | + sessions — and local models usually serve one request at a time. | |
| 99 | + - Permissions without a human: yolo on/off is per agent; a service needs a | |
| 100 | + policy (allow-lists, a separate approval channel, ticket 89). | |
| 101 | + - Authentication and limits for anything on the network. | |
| 102 | + - A versioned contract: once other apps call mm, its API cannot break | |
| 103 | + freely. | |
| 104 | + - Observability: trace (exists), per-request logs, errors as data. | |
| 105 | + | |
| 106 | + ## Suggested first experiments (small, measurable) | |
| 107 | + 1. `mm -mcp`: expose the agents of `mmConfigs` as MCP tools; call them | |
| 108 | + from Zed or Claude Desktop. | |
| 109 | + 2. An app-bundle convention on one existing demo (translator): `mm run | |
| 110 | + demos/translator`, with YAML-defined commands. | |
| 111 | + 3. The OpenAI-compatible façade on one agent, driven by Open WebUI. | |
| 112 | + | |
| 113 | + ## Open questions | |
| 114 | + - Who is the first "application" you have in mind (a team tool, a | |
| 115 | + personal assistant, a CI step, a web page)? It decides A vs B. | |
| 116 | + - Local only (one machine, one user) or networked (several users)? | |
| 117 | + - Go library (ticket 71) first, or a protocol (MCP / HTTP) first? | |
| 118 | +tasks: | |
| 119 | + - id: 1 | |
| 120 | + title: "Pick the first target application and the direction (A dedicated app, B services, C plugins)" | |
| 121 | + state: open | |
| 122 | + priority: none | |
| 123 | + author: | |
| 124 | + name: k33g | |
| 125 | + email: ph.charriere@gmail.com | |
| 126 | + createdAt: 2026-10-21T13:00:00.000Z | |
| 127 | + updatedAt: 2026-10-21T13:00:00.000Z | |
| 128 | + - id: 2 | |
| 129 | + title: "Experiment: mm as an MCP server exposing the mmConfigs agents as tools" | |
| 130 | + state: open | |
| 131 | + priority: none | |
| 132 | + author: | |
| 133 | + name: k33g | |
| 134 | + email: ph.charriere@gmail.com | |
| 135 | + createdAt: 2026-10-21T13:00:00.000Z | |
| 136 | + updatedAt: 2026-10-21T13:00:00.000Z | |
| 137 | + - id: 3 | |
| 138 | + title: "Experiment: an app-bundle convention (one folder, `mm run <dir>`, YAML-defined commands) on demos/translator" | |
| 139 | + state: open | |
| 140 | + priority: none | |
| 141 | + author: | |
| 142 | + name: k33g | |
| 143 | + email: ph.charriere@gmail.com | |
| 144 | + createdAt: 2026-10-21T13:00:00.000Z | |
| 145 | + updatedAt: 2026-10-21T13:00:00.000Z | |
| 146 | + - id: 4 | |
| 147 | + title: "Experiment: an OpenAI-compatible HTTP façade in front of one agent" | |
| 148 | + state: open | |
| 149 | + priority: none | |
| 150 | + author: | |
| 151 | + name: k33g | |
| 152 | + email: ph.charriere@gmail.com | |
| 153 | + createdAt: 2026-10-21T13:00:00.000Z | |
| 154 | + updatedAt: 2026-10-21T13:00:00.000Z | |
| 155 | + - id: 5 | |
| 156 | + title: "List the prerequisites to lift (global state, concurrency, permissions without a human, auth) and link them to tickets 71, 89, 108" | |
| 157 | + state: open | |
| 158 | + priority: none | |
| 159 | + author: | |
| 160 | + name: k33g | |
| 161 | + email: ph.charriere@gmail.com | |
| 162 | + createdAt: 2026-10-21T13:00:00.000Z | |
| 163 | + updatedAt: 2026-10-21T13:00:00.000Z | |
| 164 | +comments: | |
| 165 | + - id: 1 | |
| 166 | + author: | |
| 167 | + name: k33g | |
| 168 | + email: ph.charriere@gmail.com | |
| 169 | + createdAt: 2026-10-21T14:00:00.000Z | |
| 170 | + body: | | |
| 171 | + User's direction (2026-10-21): local use; each mm instance exposes a | |
| 172 | + web service on its own port; the service is the equivalent of the | |
| 173 | + user's prompt (like mm -p); structured output; streaming and | |
| 174 | + non-streaming; a cancel API. The OpenAI-compatible HTTP façade | |
| 175 | + (direction B.2) is the candidate: specified in ticket 114. | |
| new file mode 100644 | |||
| @@ -0,0 +1,175 @@ | |||
| 1 | +# Managed by IssueSpec. Hand edits are welcome; keep the schema valid. | ||
| 2 | +id: 113 | ||
| 3 | +title: "Mini Me as a service: turn mm into a dedicated app, or expose it as services — ideas" | ||
| 4 | +state: open | ||
| 5 | +author: | ||
| 6 | + name: k33g | ||
| 7 | + email: ph.charriere@gmail.com | ||
| 8 | +createdAt: 2026-10-21T13:00:00.000Z | ||
| 9 | +updatedAt: 2026-10-21T13:00:00.000Z | ||
| 10 | +labels: [] | ||
| 11 | +milestone: v1.0.0 | ||
| 12 | +epic: Mini Me as a service | ||
| 13 | +related: | ||
| 14 | + - 71 | ||
| 15 | + - 105 | ||
| 16 | + - 46 | ||
| 17 | + - 89 | ||
| 18 | + - 66 | ||
| 19 | +body: | | ||
| 20 | + Starting point for the epic "Mini Me as a service". Ideas only — nothing | ||
| 21 | + decided, nothing implemented. | ||
| 22 | + | ||
| 23 | + ## The wish (user's words, 2026-10-21) | ||
| 24 | + Make it possible — through plugins, scripts, a UI… — to turn mini-me into | ||
| 25 | + an application dedicated to one particular task, OR to have mini-me expose | ||
| 26 | + services that other applications call. The ideas are still vague: this | ||
| 27 | + ticket collects directions to choose from. | ||
| 28 | + | ||
| 29 | + ## What mm already has that points this way (checked in the repo) | ||
| 30 | + - A dedicated app is already "just YAML" for many demos: demos/translator, | ||
| 31 | + little-coder, guards, docs-search, memory-search, plans… — a system | ||
| 32 | + prompt, a model, skills, declared tools (`tools:`), hooks, AGENTS.md. | ||
| 33 | + What is missing is packaging and a UI of their own. | ||
| 34 | + - mm is already callable from a program, three ways: `mm -p "…"` (one | ||
| 35 | + answer on stdout; with `structuredOutput`, JSON shaped by Go types), | ||
| 36 | + `mm --ask <agent>` (context on stdin), and ACP — a JSON-RPC protocol on | ||
| 37 | + stdio with sessions, tool calls, permissions and forms. The `sidekick` | ||
| 38 | + web client already drives mm over ACP. | ||
| 39 | + - Agents already call agents (`mmConfigs`, `/ask-<name>`, `ask_agent`): | ||
| 40 | + a first "service" model, process to process. | ||
| 41 | + - Extension points: hooks (any language, JSON on stdin/stdout), declared | ||
| 42 | + tools, MCP servers, skills; WASM plugins studied in ticket 46. | ||
| 43 | + - Packaging: the Docker Sandbox kits (sbx), release binaries. | ||
| 44 | + - Ticket 71 (SDK) already proposes the Go-library road: remove the global | ||
| 45 | + state (config.Cfg, the active sink, the plan store), a public package, | ||
| 46 | + and a headless `mm -p --output-format stream-json`. Ticket 105 asks for a | ||
| 47 | + package that exposes the methods. | ||
| 48 | + | ||
| 49 | + ## Direction A — mini-me as a dedicated application | ||
| 50 | + 1. An "app bundle": one folder = agent.yaml + skills + tools + hooks + | ||
| 51 | + prompts + an optional UI description; `mm run ./translator` (or | ||
| 52 | + `mm app add <url>`). Versioned, shareable, installable. | ||
| 53 | + 2. Commands defined in the YAML: `/translate-pro <text>` = a prompt | ||
| 54 | + template + chosen tools, shown in /help and the editor menu — a task app | ||
| 55 | + without writing Go. | ||
| 56 | + 3. A start-up form: the app asks what it needs (the ask_user forms already | ||
| 57 | + exist) before the first question — a wizard (demos/project-wizard does | ||
| 58 | + it through the model today). | ||
| 59 | + 4. A window layout per app: header text, which panes, which keys, a result | ||
| 60 | + pane (e.g. "translation" beside "original"). | ||
| 61 | + 5. Output contracts per app: structuredOutput, files written with | ||
| 62 | + save_answer, a "result" event a script can wait for. | ||
| 63 | + 6. A small web UI served by mm itself for one app (a form + the answer), | ||
| 64 | + on top of the service below. | ||
| 65 | + | ||
| 66 | + ## Direction B — mini-me exposes services | ||
| 67 | + 1. **mm as an MCP server** (`mm -mcp`): each configured agent becomes one | ||
| 68 | + MCP tool (`ask_translator`, `ask_expert`). Any MCP client — Claude | ||
| 69 | + Desktop, Zed, other agents, mm itself — can call a mini-me agent with | ||
| 70 | + its model, skills and tools. Reuses the ask machinery. Likely the | ||
| 71 | + cheapest first step. | ||
| 72 | + 2. **An OpenAI-compatible HTTP façade** (`mm -serve :8080`, POST | ||
| 73 | + /v1/chat/completions): any OpenAI client (Open WebUI, scripts, IDE | ||
| 74 | + plugins) talks to an mm AGENT — tools, skills, hooks run server-side — | ||
| 75 | + instead of a bare model. | ||
| 76 | + 3. **A small REST API of mm's own**: POST /agents/{name}/ask, sessions | ||
| 77 | + (GET /sessions, resume), /docs and /memory searches as endpoints, | ||
| 78 | + streaming as SSE. | ||
| 79 | + 4. **ACP over a network transport** (WebSocket) instead of stdio, so a web | ||
| 80 | + page or a remote editor drives mm like sidekick does locally. | ||
| 81 | + 5. **A worker mode**: mm reads jobs from a folder, a queue or a webhook and | ||
| 82 | + writes results — no human in the loop (see ticket 89). | ||
| 83 | + 6. **Headless stream-json** (from ticket 71): one JSON event per line, the | ||
| 84 | + simplest contract for Python/Node/shell callers. | ||
| 85 | + | ||
| 86 | + ## Direction C — plugins | ||
| 87 | + 1. A plugin = a package of tools + hooks + skills (+ maybe a UI piece), | ||
| 88 | + installed with `mm plugin add`, declared in agent.yaml. | ||
| 89 | + 2. WASM for sandboxed plugins (ticket 46); plain processes for the rest. | ||
| 90 | + 3. A plugin can also be a "front end": the window, ACP and the REPL are | ||
| 91 | + already three clients of one loop; a fourth could be a plugin. | ||
| 92 | + | ||
| 93 | + ## What every direction needs (prerequisites to weigh) | ||
| 94 | + - No more process-wide state (ticket 71 phase 1): one process serving many | ||
| 95 | + callers needs per-session config, sink, plans (ticket 108 did plans for | ||
| 96 | + ACP; the active-sink global and config.Cfg remain). | ||
| 97 | + - Concurrency: ACP turns are serialised today; a service needs parallel | ||
| 98 | + sessions — and local models usually serve one request at a time. | ||
| 99 | + - Permissions without a human: yolo on/off is per agent; a service needs a | ||
| 100 | + policy (allow-lists, a separate approval channel, ticket 89). | ||
| 101 | + - Authentication and limits for anything on the network. | ||
| 102 | + - A versioned contract: once other apps call mm, its API cannot break | ||
| 103 | + freely. | ||
| 104 | + - Observability: trace (exists), per-request logs, errors as data. | ||
| 105 | + | ||
| 106 | + ## Suggested first experiments (small, measurable) | ||
| 107 | + 1. `mm -mcp`: expose the agents of `mmConfigs` as MCP tools; call them | ||
| 108 | + from Zed or Claude Desktop. | ||
| 109 | + 2. An app-bundle convention on one existing demo (translator): `mm run | ||
| 110 | + demos/translator`, with YAML-defined commands. | ||
| 111 | + 3. The OpenAI-compatible façade on one agent, driven by Open WebUI. | ||
| 112 | + | ||
| 113 | + ## Open questions | ||
| 114 | + - Who is the first "application" you have in mind (a team tool, a | ||
| 115 | + personal assistant, a CI step, a web page)? It decides A vs B. | ||
| 116 | + - Local only (one machine, one user) or networked (several users)? | ||
| 117 | + - Go library (ticket 71) first, or a protocol (MCP / HTTP) first? | ||
| 118 | +tasks: | ||
| 119 | + - id: 1 | ||
| 120 | + title: "Pick the first target application and the direction (A dedicated app, B services, C plugins)" | ||
| 121 | + state: open | ||
| 122 | + priority: none | ||
| 123 | + author: | ||
| 124 | + name: k33g | ||
| 125 | + email: ph.charriere@gmail.com | ||
| 126 | + createdAt: 2026-10-21T13:00:00.000Z | ||
| 127 | + updatedAt: 2026-10-21T13:00:00.000Z | ||
| 128 | + - id: 2 | ||
| 129 | + title: "Experiment: mm as an MCP server exposing the mmConfigs agents as tools" | ||
| 130 | + state: open | ||
| 131 | + priority: none | ||
| 132 | + author: | ||
| 133 | + name: k33g | ||
| 134 | + email: ph.charriere@gmail.com | ||
| 135 | + createdAt: 2026-10-21T13:00:00.000Z | ||
| 136 | + updatedAt: 2026-10-21T13:00:00.000Z | ||
| 137 | + - id: 3 | ||
| 138 | + title: "Experiment: an app-bundle convention (one folder, `mm run <dir>`, YAML-defined commands) on demos/translator" | ||
| 139 | + state: open | ||
| 140 | + priority: none | ||
| 141 | + author: | ||
| 142 | + name: k33g | ||
| 143 | + email: ph.charriere@gmail.com | ||
| 144 | + createdAt: 2026-10-21T13:00:00.000Z | ||
| 145 | + updatedAt: 2026-10-21T13:00:00.000Z | ||
| 146 | + - id: 4 | ||
| 147 | + title: "Experiment: an OpenAI-compatible HTTP façade in front of one agent" | ||
| 148 | + state: open | ||
| 149 | + priority: none | ||
| 150 | + author: | ||
| 151 | + name: k33g | ||
| 152 | + email: ph.charriere@gmail.com | ||
| 153 | + createdAt: 2026-10-21T13:00:00.000Z | ||
| 154 | + updatedAt: 2026-10-21T13:00:00.000Z | ||
| 155 | + - id: 5 | ||
| 156 | + title: "List the prerequisites to lift (global state, concurrency, permissions without a human, auth) and link them to tickets 71, 89, 108" | ||
| 157 | + state: open | ||
| 158 | + priority: none | ||
| 159 | + author: | ||
| 160 | + name: k33g | ||
| 161 | + email: ph.charriere@gmail.com | ||
| 162 | + createdAt: 2026-10-21T13:00:00.000Z | ||
| 163 | + updatedAt: 2026-10-21T13:00:00.000Z | ||
| 164 | +comments: | ||
| 165 | + - id: 1 | ||
| 166 | + author: | ||
| 167 | + name: k33g | ||
| 168 | + email: ph.charriere@gmail.com | ||
| 169 | + createdAt: 2026-10-21T14:00:00.000Z | ||
| 170 | + body: | | ||
| 171 | + User's direction (2026-10-21): local use; each mm instance exposes a | ||
| 172 | + web service on its own port; the service is the equivalent of the | ||
| 173 | + user's prompt (like mm -p); structured output; streaming and | ||
| 174 | + non-streaming; a cancel API. The OpenAI-compatible HTTP façade | ||
| 175 | + (direction B.2) is the candidate: specified in ticket 114. | ||
added
.tickets/issues/0114-openai-compatible-http-service.yaml +204 -0 | new file mode 100644 | ||
| @@ -0,0 +1,204 @@ | ||
| 1 | +# Managed by IssueSpec. Hand edits are welcome; keep the schema valid. | |
| 2 | +id: 114 | |
| 3 | +title: "OpenAI-compatible HTTP service: each mm instance answers prompts on its own port (stream, non-stream, structured output, cancel)" | |
| 4 | +state: open | |
| 5 | +author: | |
| 6 | + name: k33g | |
| 7 | + email: ph.charriere@gmail.com | |
| 8 | +createdAt: 2026-10-21T14:00:00.000Z | |
| 9 | +updatedAt: 2026-10-21T14:00:00.000Z | |
| 10 | +labels: | |
| 11 | + - kind::feature | |
| 12 | +milestone: v1.0.0 | |
| 13 | +epic: Mini Me as a service | |
| 14 | +related: | |
| 15 | + - 113 | |
| 16 | + - 71 | |
| 17 | + - 89 | |
| 18 | +body: | | |
| 19 | + An OpenAI-compatible HTTP service in front of ONE mini-me agent: what | |
| 20 | + `mm -p` does — one prompt in, the agent's turn (its tools, skills, hooks | |
| 21 | + run inside mm), the answer out — but over HTTP, so any program, or any | |
| 22 | + OpenAI client, can use the agent. Epic "Mini Me as a service"; the first | |
| 23 | + concrete step of ticket 113. | |
| 24 | + | |
| 25 | + ## The user's requirements (2026-10-21) | |
| 26 | + - local use; | |
| 27 | + - each mini-me instance exposes a web service on its own port; | |
| 28 | + - the service is the equivalent of the user's prompt — like `mm -p`; | |
| 29 | + - structured output must be possible; | |
| 30 | + - answers in streaming AND non-streaming mode; | |
| 31 | + - a cancel API. | |
| 32 | + | |
| 33 | + ## Proposal (to confirm) | |
| 34 | + | |
| 35 | + ### Start | |
| 36 | + - `mm -serve 8080 agent.yaml` (flag) or a `serve:` block in the YAML | |
| 37 | + (`port`, `host`). Default host 127.0.0.1: local only; another host must | |
| 38 | + be asked for explicitly. One agent per instance, one port per instance — | |
| 39 | + several agents = several `mm -serve` on several ports. | |
| 40 | + - Optional `serve.apiKey`: when set, requests must carry | |
| 41 | + `Authorization: Bearer <key>` (OpenAI clients already send one); 401 | |
| 42 | + otherwise. Even on 127.0.0.1, other local processes can reach the port. | |
| 43 | + | |
| 44 | + ### Endpoints | |
| 45 | + - `POST /v1/chat/completions` — the prompt. From the request: | |
| 46 | + - `messages`: the LAST user message is the prompt; earlier user/assistant | |
| 47 | + messages are the conversation so far (stateless, as OpenAI clients | |
| 48 | + expect: they resend the history each time). The agent's own system | |
| 49 | + prompt stays first; a client `system` message is appended after it | |
| 50 | + (or refused — to decide). | |
| 51 | + - `stream`: false → one `chat.completion` JSON; true → Server-Sent | |
| 52 | + Events, `chat.completion.chunk` deltas, then `data: [DONE]`. | |
| 53 | + `stream_options.include_usage` adds the usage chunk. | |
| 54 | + - `model`: accepted if it names this agent (see /v1/models), refused | |
| 55 | + otherwise — or ignored (to decide). | |
| 56 | + - `response_format`: see "Structured output" below. | |
| 57 | + - `tools` / `tool_choice` from the client: refused (400) — the agent's | |
| 58 | + tools are its own and run inside mm; the client never sees a tool call. | |
| 59 | + - `temperature`, `max_tokens`…: passed over the agent's sampling, or | |
| 60 | + ignored (to decide). | |
| 61 | + - `GET /v1/models` — one model: this agent (id = `acp.name`, e.g. `bob`), | |
| 62 | + so clients that list models (Open WebUI) can select it. | |
| 63 | + - **Cancel**: | |
| 64 | + - closing the connection (the client stops reading a stream, or gives up | |
| 65 | + on a request) cancels the turn — the standard behaviour of OpenAI | |
| 66 | + clients; | |
| 67 | + - `POST /v1/mm/cancel/{id}` (mm's own extension) cancels a request by id | |
| 68 | + from elsewhere — another process, a "Stop" button. The id is the | |
| 69 | + response's `id` (`chatcmpl-…`), also sent at once as an | |
| 70 | + `X-MM-Request-Id` header, so a non-streaming caller knows it before the | |
| 71 | + answer; | |
| 72 | + - a cancelled request ends with `finish_reason: "cancelled"` (stream) or | |
| 73 | + HTTP 499 / an error body (non-stream) — to decide. | |
| 74 | + - `GET /v1/mm/health` — up, the agent's name, the model, busy or idle. | |
| 75 | + | |
| 76 | + ### Structured output | |
| 77 | + Today `structuredOutput` (Go types in the YAML) turns an agent into a JSON | |
| 78 | + answerer and FORBIDS tools (config.StructuredOutputConflict). Two levels: | |
| 79 | + 1. v1: an agent with `structuredOutput` serves JSON answers — the | |
| 80 | + `content` is the JSON, `response_format` may be omitted; a request | |
| 81 | + asking `{"type":"json_object"}` or `json_schema` from an agent without | |
| 82 | + structuredOutput is refused (400) with the reason. | |
| 83 | + 2. v2: `response_format: {"type":"json_schema", …}` per request, on an | |
| 84 | + agent WITH tools: the turn runs with its tools, then a last formatting | |
| 85 | + call turns the answer into the schema. To design. | |
| 86 | + | |
| 87 | + ### What streams | |
| 88 | + - The answer's text, as deltas. | |
| 89 | + - Thinking models: `reasoning_content` deltas (as llama.cpp and DMR send | |
| 90 | + them), shown only if the client asks — or never (to decide). | |
| 91 | + - What the agent does meanwhile (tool calls, hooks) is NOT part of the | |
| 92 | + OpenAI format. Options: nothing (default); mm extension events | |
| 93 | + (`event: mm.tool` SSE events that OpenAI clients ignore); or short | |
| 94 | + progress lines in the text — to decide. | |
| 95 | + | |
| 96 | + ### One turn at a time | |
| 97 | + mm runs one turn at a time (process-wide active sink, engine not designed | |
| 98 | + for concurrent turns — the same reason ACP serialises with turnMu), and a | |
| 99 | + local model usually serves one request at a time anyway. A second request | |
| 100 | + waits in a short queue, or gets 429 + Retry-After when the queue is full | |
| 101 | + (size to decide, e.g. 4). | |
| 102 | + | |
| 103 | + ### No human in the loop | |
| 104 | + Same rules as `mm -p`: tools that would ask for permission run only with | |
| 105 | + `yolo: true`, otherwise they are refused (the model reads the refusal); | |
| 106 | + `ask_user` is not available. Hooks run as usual. | |
| 107 | + | |
| 108 | + ### Sessions | |
| 109 | + Stateless by default (the client sends the history). With | |
| 110 | + `sessions.enabled: true`, each request could be saved as a session file — | |
| 111 | + or an `X-MM-Session` header could continue a stored session on the server | |
| 112 | + side (to decide; not needed by OpenAI clients). | |
| 113 | + | |
| 114 | + ### Errors | |
| 115 | + OpenAI's error shape: `{"error": {"message", "type", "code"}}` with 400 | |
| 116 | + (bad request), 401, 404, 429, 500, 503 (model server down — engine.Explain's | |
| 117 | + line as the message). | |
| 118 | + | |
| 119 | + ## How to check it | |
| 120 | + curl (streaming and not), the official `openai` Python client | |
| 121 | + (`base_url="http://127.0.0.1:8080/v1"`), Open WebUI pointed at it, a cancel | |
| 122 | + from a second terminal; a structured agent answered by `jq`. A demo in | |
| 123 | + demos/ with a small model, measured. | |
| 124 | + | |
| 125 | + ## Open questions for the user | |
| 126 | + - A client `system` message: append after the agent's prompt, or refuse? | |
| 127 | + - Client sampling (`temperature`, `max_tokens`): honour or ignore? | |
| 128 | + - Show tool activity while streaming (mm extension events), or nothing? | |
| 129 | + - Cancelled non-streaming request: which HTTP answer? | |
| 130 | + - Sessions: stateless only, or save / continue sessions server-side? | |
| 131 | +tasks: | |
| 132 | + - id: 1 | |
| 133 | + title: "Agree on the open questions (system message, sampling, tool activity in the stream, cancelled status, sessions)" | |
| 134 | + state: open | |
| 135 | + priority: none | |
| 136 | + author: | |
| 137 | + name: k33g | |
| 138 | + email: ph.charriere@gmail.com | |
| 139 | + createdAt: 2026-10-21T14:00:00.000Z | |
| 140 | + updatedAt: 2026-10-21T14:00:00.000Z | |
| 141 | + - id: 2 | |
| 142 | + title: "mm -serve / serve: block (port, host 127.0.0.1, optional apiKey); GET /v1/models and /v1/mm/health — with tests" | |
| 143 | + state: open | |
| 144 | + priority: none | |
| 145 | + author: | |
| 146 | + name: k33g | |
| 147 | + email: ph.charriere@gmail.com | |
| 148 | + createdAt: 2026-10-21T14:00:00.000Z | |
| 149 | + updatedAt: 2026-10-21T14:00:00.000Z | |
| 150 | + - id: 3 | |
| 151 | + title: "POST /v1/chat/completions non-streaming: last user message = prompt, history from messages, the agent's turn as in mm -p, OpenAI response and errors — with tests" | |
| 152 | + state: open | |
| 153 | + priority: none | |
| 154 | + author: | |
| 155 | + name: k33g | |
| 156 | + email: ph.charriere@gmail.com | |
| 157 | + createdAt: 2026-10-21T14:00:00.000Z | |
| 158 | + updatedAt: 2026-10-21T14:00:00.000Z | |
| 159 | + - id: 4 | |
| 160 | + title: "Streaming: SSE chat.completion.chunk deltas, [DONE], include_usage — a ui.Sink writing the stream — with tests" | |
| 161 | + state: open | |
| 162 | + priority: none | |
| 163 | + author: | |
| 164 | + name: k33g | |
| 165 | + email: ph.charriere@gmail.com | |
| 166 | + createdAt: 2026-10-21T14:00:00.000Z | |
| 167 | + updatedAt: 2026-10-21T14:00:00.000Z | |
| 168 | + - id: 5 | |
| 169 | + title: "Cancel: client disconnect cancels the turn; POST /v1/mm/cancel/{id} + X-MM-Request-Id; one turn at a time with a short queue / 429 — with tests" | |
| 170 | + state: open | |
| 171 | + priority: none | |
| 172 | + author: | |
| 173 | + name: k33g | |
| 174 | + email: ph.charriere@gmail.com | |
| 175 | + createdAt: 2026-10-21T14:00:00.000Z | |
| 176 | + updatedAt: 2026-10-21T14:00:00.000Z | |
| 177 | + - id: 6 | |
| 178 | + title: "Structured output v1: an agent with structuredOutput answers JSON; json response_format refused on an agent without it — with tests" | |
| 179 | + state: open | |
| 180 | + priority: none | |
| 181 | + author: | |
| 182 | + name: k33g | |
| 183 | + email: ph.charriere@gmail.com | |
| 184 | + createdAt: 2026-10-21T14:00:00.000Z | |
| 185 | + updatedAt: 2026-10-21T14:00:00.000Z | |
| 186 | + - id: 7 | |
| 187 | + title: "Check with curl, the openai Python client, Open WebUI; a measured demo; docs EN+FR (how-to, reference, explanation)" | |
| 188 | + state: open | |
| 189 | + priority: none | |
| 190 | + author: | |
| 191 | + name: k33g | |
| 192 | + email: ph.charriere@gmail.com | |
| 193 | + createdAt: 2026-10-21T14:00:00.000Z | |
| 194 | + updatedAt: 2026-10-21T14:00:00.000Z | |
| 195 | + - id: 8 | |
| 196 | + title: "Later (v2): per-request response_format json_schema on an agent with tools (a formatting call after the turn)" | |
| 197 | + state: open | |
| 198 | + priority: none | |
| 199 | + author: | |
| 200 | + name: k33g | |
| 201 | + email: ph.charriere@gmail.com | |
| 202 | + createdAt: 2026-10-21T14:00:00.000Z | |
| 203 | + updatedAt: 2026-10-21T14:00:00.000Z | |
| 204 | +comments: [] | |
| new file mode 100644 | |||
| @@ -0,0 +1,204 @@ | |||
| 1 | +# Managed by IssueSpec. Hand edits are welcome; keep the schema valid. | ||
| 2 | +id: 114 | ||
| 3 | +title: "OpenAI-compatible HTTP service: each mm instance answers prompts on its own port (stream, non-stream, structured output, cancel)" | ||
| 4 | +state: open | ||
| 5 | +author: | ||
| 6 | + name: k33g | ||
| 7 | + email: ph.charriere@gmail.com | ||
| 8 | +createdAt: 2026-10-21T14:00:00.000Z | ||
| 9 | +updatedAt: 2026-10-21T14:00:00.000Z | ||
| 10 | +labels: | ||
| 11 | + - kind::feature | ||
| 12 | +milestone: v1.0.0 | ||
| 13 | +epic: Mini Me as a service | ||
| 14 | +related: | ||
| 15 | + - 113 | ||
| 16 | + - 71 | ||
| 17 | + - 89 | ||
| 18 | +body: | | ||
| 19 | + An OpenAI-compatible HTTP service in front of ONE mini-me agent: what | ||
| 20 | + `mm -p` does — one prompt in, the agent's turn (its tools, skills, hooks | ||
| 21 | + run inside mm), the answer out — but over HTTP, so any program, or any | ||
| 22 | + OpenAI client, can use the agent. Epic "Mini Me as a service"; the first | ||
| 23 | + concrete step of ticket 113. | ||
| 24 | + | ||
| 25 | + ## The user's requirements (2026-10-21) | ||
| 26 | + - local use; | ||
| 27 | + - each mini-me instance exposes a web service on its own port; | ||
| 28 | + - the service is the equivalent of the user's prompt — like `mm -p`; | ||
| 29 | + - structured output must be possible; | ||
| 30 | + - answers in streaming AND non-streaming mode; | ||
| 31 | + - a cancel API. | ||
| 32 | + | ||
| 33 | + ## Proposal (to confirm) | ||
| 34 | + | ||
| 35 | + ### Start | ||
| 36 | + - `mm -serve 8080 agent.yaml` (flag) or a `serve:` block in the YAML | ||
| 37 | + (`port`, `host`). Default host 127.0.0.1: local only; another host must | ||
| 38 | + be asked for explicitly. One agent per instance, one port per instance — | ||
| 39 | + several agents = several `mm -serve` on several ports. | ||
| 40 | + - Optional `serve.apiKey`: when set, requests must carry | ||
| 41 | + `Authorization: Bearer <key>` (OpenAI clients already send one); 401 | ||
| 42 | + otherwise. Even on 127.0.0.1, other local processes can reach the port. | ||
| 43 | + | ||
| 44 | + ### Endpoints | ||
| 45 | + - `POST /v1/chat/completions` — the prompt. From the request: | ||
| 46 | + - `messages`: the LAST user message is the prompt; earlier user/assistant | ||
| 47 | + messages are the conversation so far (stateless, as OpenAI clients | ||
| 48 | + expect: they resend the history each time). The agent's own system | ||
| 49 | + prompt stays first; a client `system` message is appended after it | ||
| 50 | + (or refused — to decide). | ||
| 51 | + - `stream`: false → one `chat.completion` JSON; true → Server-Sent | ||
| 52 | + Events, `chat.completion.chunk` deltas, then `data: [DONE]`. | ||
| 53 | + `stream_options.include_usage` adds the usage chunk. | ||
| 54 | + - `model`: accepted if it names this agent (see /v1/models), refused | ||
| 55 | + otherwise — or ignored (to decide). | ||
| 56 | + - `response_format`: see "Structured output" below. | ||
| 57 | + - `tools` / `tool_choice` from the client: refused (400) — the agent's | ||
| 58 | + tools are its own and run inside mm; the client never sees a tool call. | ||
| 59 | + - `temperature`, `max_tokens`…: passed over the agent's sampling, or | ||
| 60 | + ignored (to decide). | ||
| 61 | + - `GET /v1/models` — one model: this agent (id = `acp.name`, e.g. `bob`), | ||
| 62 | + so clients that list models (Open WebUI) can select it. | ||
| 63 | + - **Cancel**: | ||
| 64 | + - closing the connection (the client stops reading a stream, or gives up | ||
| 65 | + on a request) cancels the turn — the standard behaviour of OpenAI | ||
| 66 | + clients; | ||
| 67 | + - `POST /v1/mm/cancel/{id}` (mm's own extension) cancels a request by id | ||
| 68 | + from elsewhere — another process, a "Stop" button. The id is the | ||
| 69 | + response's `id` (`chatcmpl-…`), also sent at once as an | ||
| 70 | + `X-MM-Request-Id` header, so a non-streaming caller knows it before the | ||
| 71 | + answer; | ||
| 72 | + - a cancelled request ends with `finish_reason: "cancelled"` (stream) or | ||
| 73 | + HTTP 499 / an error body (non-stream) — to decide. | ||
| 74 | + - `GET /v1/mm/health` — up, the agent's name, the model, busy or idle. | ||
| 75 | + | ||
| 76 | + ### Structured output | ||
| 77 | + Today `structuredOutput` (Go types in the YAML) turns an agent into a JSON | ||
| 78 | + answerer and FORBIDS tools (config.StructuredOutputConflict). Two levels: | ||
| 79 | + 1. v1: an agent with `structuredOutput` serves JSON answers — the | ||
| 80 | + `content` is the JSON, `response_format` may be omitted; a request | ||
| 81 | + asking `{"type":"json_object"}` or `json_schema` from an agent without | ||
| 82 | + structuredOutput is refused (400) with the reason. | ||
| 83 | + 2. v2: `response_format: {"type":"json_schema", …}` per request, on an | ||
| 84 | + agent WITH tools: the turn runs with its tools, then a last formatting | ||
| 85 | + call turns the answer into the schema. To design. | ||
| 86 | + | ||
| 87 | + ### What streams | ||
| 88 | + - The answer's text, as deltas. | ||
| 89 | + - Thinking models: `reasoning_content` deltas (as llama.cpp and DMR send | ||
| 90 | + them), shown only if the client asks — or never (to decide). | ||
| 91 | + - What the agent does meanwhile (tool calls, hooks) is NOT part of the | ||
| 92 | + OpenAI format. Options: nothing (default); mm extension events | ||
| 93 | + (`event: mm.tool` SSE events that OpenAI clients ignore); or short | ||
| 94 | + progress lines in the text — to decide. | ||
| 95 | + | ||
| 96 | + ### One turn at a time | ||
| 97 | + mm runs one turn at a time (process-wide active sink, engine not designed | ||
| 98 | + for concurrent turns — the same reason ACP serialises with turnMu), and a | ||
| 99 | + local model usually serves one request at a time anyway. A second request | ||
| 100 | + waits in a short queue, or gets 429 + Retry-After when the queue is full | ||
| 101 | + (size to decide, e.g. 4). | ||
| 102 | + | ||
| 103 | + ### No human in the loop | ||
| 104 | + Same rules as `mm -p`: tools that would ask for permission run only with | ||
| 105 | + `yolo: true`, otherwise they are refused (the model reads the refusal); | ||
| 106 | + `ask_user` is not available. Hooks run as usual. | ||
| 107 | + | ||
| 108 | + ### Sessions | ||
| 109 | + Stateless by default (the client sends the history). With | ||
| 110 | + `sessions.enabled: true`, each request could be saved as a session file — | ||
| 111 | + or an `X-MM-Session` header could continue a stored session on the server | ||
| 112 | + side (to decide; not needed by OpenAI clients). | ||
| 113 | + | ||
| 114 | + ### Errors | ||
| 115 | + OpenAI's error shape: `{"error": {"message", "type", "code"}}` with 400 | ||
| 116 | + (bad request), 401, 404, 429, 500, 503 (model server down — engine.Explain's | ||
| 117 | + line as the message). | ||
| 118 | + | ||
| 119 | + ## How to check it | ||
| 120 | + curl (streaming and not), the official `openai` Python client | ||
| 121 | + (`base_url="http://127.0.0.1:8080/v1"`), Open WebUI pointed at it, a cancel | ||
| 122 | + from a second terminal; a structured agent answered by `jq`. A demo in | ||
| 123 | + demos/ with a small model, measured. | ||
| 124 | + | ||
| 125 | + ## Open questions for the user | ||
| 126 | + - A client `system` message: append after the agent's prompt, or refuse? | ||
| 127 | + - Client sampling (`temperature`, `max_tokens`): honour or ignore? | ||
| 128 | + - Show tool activity while streaming (mm extension events), or nothing? | ||
| 129 | + - Cancelled non-streaming request: which HTTP answer? | ||
| 130 | + - Sessions: stateless only, or save / continue sessions server-side? | ||
| 131 | +tasks: | ||
| 132 | + - id: 1 | ||
| 133 | + title: "Agree on the open questions (system message, sampling, tool activity in the stream, cancelled status, sessions)" | ||
| 134 | + state: open | ||
| 135 | + priority: none | ||
| 136 | + author: | ||
| 137 | + name: k33g | ||
| 138 | + email: ph.charriere@gmail.com | ||
| 139 | + createdAt: 2026-10-21T14:00:00.000Z | ||
| 140 | + updatedAt: 2026-10-21T14:00:00.000Z | ||
| 141 | + - id: 2 | ||
| 142 | + title: "mm -serve / serve: block (port, host 127.0.0.1, optional apiKey); GET /v1/models and /v1/mm/health — with tests" | ||
| 143 | + state: open | ||
| 144 | + priority: none | ||
| 145 | + author: | ||
| 146 | + name: k33g | ||
| 147 | + email: ph.charriere@gmail.com | ||
| 148 | + createdAt: 2026-10-21T14:00:00.000Z | ||
| 149 | + updatedAt: 2026-10-21T14:00:00.000Z | ||
| 150 | + - id: 3 | ||
| 151 | + title: "POST /v1/chat/completions non-streaming: last user message = prompt, history from messages, the agent's turn as in mm -p, OpenAI response and errors — with tests" | ||
| 152 | + state: open | ||
| 153 | + priority: none | ||
| 154 | + author: | ||
| 155 | + name: k33g | ||
| 156 | + email: ph.charriere@gmail.com | ||
| 157 | + createdAt: 2026-10-21T14:00:00.000Z | ||
| 158 | + updatedAt: 2026-10-21T14:00:00.000Z | ||
| 159 | + - id: 4 | ||
| 160 | + title: "Streaming: SSE chat.completion.chunk deltas, [DONE], include_usage — a ui.Sink writing the stream — with tests" | ||
| 161 | + state: open | ||
| 162 | + priority: none | ||
| 163 | + author: | ||
| 164 | + name: k33g | ||
| 165 | + email: ph.charriere@gmail.com | ||
| 166 | + createdAt: 2026-10-21T14:00:00.000Z | ||
| 167 | + updatedAt: 2026-10-21T14:00:00.000Z | ||
| 168 | + - id: 5 | ||
| 169 | + title: "Cancel: client disconnect cancels the turn; POST /v1/mm/cancel/{id} + X-MM-Request-Id; one turn at a time with a short queue / 429 — with tests" | ||
| 170 | + state: open | ||
| 171 | + priority: none | ||
| 172 | + author: | ||
| 173 | + name: k33g | ||
| 174 | + email: ph.charriere@gmail.com | ||
| 175 | + createdAt: 2026-10-21T14:00:00.000Z | ||
| 176 | + updatedAt: 2026-10-21T14:00:00.000Z | ||
| 177 | + - id: 6 | ||
| 178 | + title: "Structured output v1: an agent with structuredOutput answers JSON; json response_format refused on an agent without it — with tests" | ||
| 179 | + state: open | ||
| 180 | + priority: none | ||
| 181 | + author: | ||
| 182 | + name: k33g | ||
| 183 | + email: ph.charriere@gmail.com | ||
| 184 | + createdAt: 2026-10-21T14:00:00.000Z | ||
| 185 | + updatedAt: 2026-10-21T14:00:00.000Z | ||
| 186 | + - id: 7 | ||
| 187 | + title: "Check with curl, the openai Python client, Open WebUI; a measured demo; docs EN+FR (how-to, reference, explanation)" | ||
| 188 | + state: open | ||
| 189 | + priority: none | ||
| 190 | + author: | ||
| 191 | + name: k33g | ||
| 192 | + email: ph.charriere@gmail.com | ||
| 193 | + createdAt: 2026-10-21T14:00:00.000Z | ||
| 194 | + updatedAt: 2026-10-21T14:00:00.000Z | ||
| 195 | + - id: 8 | ||
| 196 | + title: "Later (v2): per-request response_format json_schema on an agent with tools (a formatting call after the turn)" | ||
| 197 | + state: open | ||
| 198 | + priority: none | ||
| 199 | + author: | ||
| 200 | + name: k33g | ||
| 201 | + email: ph.charriere@gmail.com | ||
| 202 | + createdAt: 2026-10-21T14:00:00.000Z | ||
| 203 | + updatedAt: 2026-10-21T14:00:00.000Z | ||
| 204 | +comments: [] | ||