okami.mom/albedo
git clone https://git.nkp.pet/okami.mom/albedo
61c43ca
main
proxy
The proxy extension serves an OpenAI-compatible Chat Completions endpoint for every saved provider profile, so other tools can use albedo's providers (Codex, Antigravity, OpenAI-compatible endpoints) through one base url. It is installed and disabled; enable it globally in /extensions or with {"enabled": {"proxy": true}} in $ALBEDO_HOME/extensions.json. The change applies to the next request.
endpoint
The proxy is a service mounted at http://127.0.0.1:<port>/extensions/proxy/v1. Set ALBEDO_PORT in the daemon's environment to keep the port stable; otherwise it changes each time the daemon starts (see $ALBEDO_HOME/daemon.json).
GET /extensions/proxy/v1/modelslists<profile>/<model>for every usable profile inconfig.json. A profile with its ownbaseUrl(an API-key profile) lists only its saved model, because models.dev can only guess what an arbitrary endpoint serves. Other profiles, such ascodexandantigravity, list their provider's catalog, or their saved model while no catalog is cached. Profiles that cannot be used, such as a malformed entry, are named under a nonstandarderrorsfield ([{"profile", "message"}]) instead of failing the list.- The list only helps clients choose. Any
<profile>/<model>is requested as given, listed or not, and a broken or signed-out profile fails only its own requests. POST /extensions/proxy/v1/chat/completionsacceptsmodelas<profile>/<model>, or a bare<profile>for its saved model. Streaming (stream,stream_options.include_usage) and non-streaming replies are supported.
Use the daemon bearer token as the client's API key. The operator can set
ALBEDO_PROXY_ALLOW_ANONYMOUS=1 when starting the daemon to allow anonymous
loopback requests to these two proxy routes. Core and other extension routes
still require their own authentication. Browser requests follow the daemon's
configured origin policy.
The request body limit is 32,000,000 bytes. Fixed-length and chunked uploads
are supported; ambiguous framing returns 400, and oversized bodies return
413. A request without body framing has an empty body.
translation
Each request is translated into albedo's request types and projected onto the profile's upstream, as a session's history is:
- Leading
systemanddevelopermessages become the instructions. Later ones become user notes in place. usertext and base64data:images become user input. Image urls are not fetched.assistantmessages withtool_callsbecome replayable tool calls.toolmessages become their results.tools,max_tokens/max_completion_tokens,temperature,top_p,stop,tool_choice,parallel_tool_calls,reasoning_effort(orreasoning.effort), andresponse_format(json_objectorjson_schema) become albedo's generation options. Each provider applies what its wire can express:- OpenAI Chat Completions and Responses take all of them, except that Responses has no stop sequences.
- Codex keeps its fixed sampling. Effort, tool choice, parallel calls, and format override its defaults (medium effort, automatic parallel tools).
- Antigravity maps effort onto each model's thinking budget or level, and sampling, stop sequences, and format into
generationConfig. Claude routes always run toolsVALIDATED. On Gemini routes a forced tool choice is also restated as a final instruction, because Cloud Code Assist drops the tool mode there.
Text and reasoning stream as delta.content and delta.reasoning_content. Each tool call is sent whole, with its id, name, and arguments, when the turn ends. Errors from the provider arrive as an error chunk while streaming, or as a JSON error otherwise.
state
The proxy stores nothing. Each request carries its conversation, and provider identity that should be stable across it, such as a Codex account or an Antigravity trajectory, is derived from the profile and the first user message.
Providers also return opaque state that they require back within a tool-calling turn: Codex's encrypted reasoning items, Gemini's thought signatures on function calls, and Claude's signed thinking. Clients echo only plain Chat Completions, but they always echo a tool call's id verbatim. So when a turn ends in tool calls, the proxy compresses that turn's native provider output, tags it with the profile that produced it, and appends it to the first call's id as <id>__albedo__<state>, using only [A-Za-z0-9_-]. When the history comes back, those turns become albedo transcript entries again and pass through the same projection a session uses. A request to the same profile replays them verbatim; a request to another profile gets the portable text and calls. Tool results are matched to the provider's own call ids. State larger than 256 KiB is not carried, and damaged state falls back to the portable history. Turns without tool calls carry no state, which the providers do not need after the next user message.
1# proxy 2 3The `proxy` extension serves an OpenAI-compatible Chat Completions endpoint for every saved provider profile, so other tools can use albedo's providers (Codex, Antigravity, OpenAI-compatible endpoints) through one base url. It is installed and disabled; enable it globally in `/extensions` or with `{"enabled": {"proxy": true}}` in `$ALBEDO_HOME/extensions.json`. The change applies to the next request. 4 5## endpoint 6 7The proxy is a [service](extensions.md) mounted at `http://127.0.0.1:<port>/extensions/proxy/v1`. Set `ALBEDO_PORT` in the daemon's environment to keep the port stable; otherwise it changes each time the daemon starts (see `$ALBEDO_HOME/daemon.json`). 8 9- `GET /extensions/proxy/v1/models` lists `<profile>/<model>` for every usable profile in `config.json`. A profile with its own `baseUrl` (an API-key profile) lists only its saved model, because models.dev can only guess what an arbitrary endpoint serves. Other profiles, such as `codex` and `antigravity`, list their provider's catalog, or their saved model while no catalog is cached. Profiles that cannot be used, such as a malformed entry, are named under a nonstandard `errors` field (`[{"profile", "message"}]`) instead of failing the list. 10- The list only helps clients choose. Any `<profile>/<model>` is requested as given, listed or not, and a broken or signed-out profile fails only its own requests. 11- `POST /extensions/proxy/v1/chat/completions` accepts `model` as `<profile>/<model>`, or a bare `<profile>` for its saved model. Streaming (`stream`, `stream_options.include_usage`) and non-streaming replies are supported. 12 13Use the daemon bearer token as the client's API key. The operator can set 14`ALBEDO_PROXY_ALLOW_ANONYMOUS=1` when starting the daemon to allow anonymous 15loopback requests to these two proxy routes. Core and other extension routes 16still require their own authentication. Browser requests follow the daemon's 17configured origin policy. 18 19The request body limit is 32,000,000 bytes. Fixed-length and chunked uploads 20are supported; ambiguous framing returns `400`, and oversized bodies return 21`413`. A request without body framing has an empty body. 22 23## translation 24 25Each request is translated into albedo's request types and projected onto the profile's upstream, as a session's history is: 26 27- Leading `system` and `developer` messages become the instructions. Later ones become user notes in place. 28- `user` text and base64 `data:` images become user input. Image urls are not fetched. 29- `assistant` messages with `tool_calls` become replayable tool calls. `tool` messages become their results. 30- `tools`, `max_tokens`/`max_completion_tokens`, `temperature`, `top_p`, `stop`, `tool_choice`, `parallel_tool_calls`, `reasoning_effort` (or `reasoning.effort`), and `response_format` (`json_object` or `json_schema`) become albedo's generation options. Each provider applies what its wire can express: 31- OpenAI Chat Completions and Responses take all of them, except that Responses has no stop sequences. 32- Codex keeps its fixed sampling. Effort, tool choice, parallel calls, and format override its defaults (medium effort, automatic parallel tools). 33- Antigravity maps effort onto each model's thinking budget or level, and sampling, stop sequences, and format into `generationConfig`. Claude routes always run tools `VALIDATED`. On Gemini routes a forced tool choice is also restated as a final instruction, because Cloud Code Assist drops the tool mode there. 34 35Text and reasoning stream as `delta.content` and `delta.reasoning_content`. Each tool call is sent whole, with its id, name, and arguments, when the turn ends. Errors from the provider arrive as an `error` chunk while streaming, or as a JSON error otherwise. 36 37## state 38 39The proxy stores nothing. Each request carries its conversation, and provider identity that should be stable across it, such as a Codex account or an Antigravity trajectory, is derived from the profile and the first user message. 40 41Providers also return opaque state that they require back within a tool-calling turn: Codex's encrypted reasoning items, Gemini's thought signatures on function calls, and Claude's signed thinking. Clients echo only plain Chat Completions, but they always echo a tool call's id verbatim. So when a turn ends in tool calls, the proxy compresses that turn's native provider output, tags it with the profile that produced it, and appends it to the first call's id as `<id>__albedo__<state>`, using only `[A-Za-z0-9_-]`. When the history comes back, those turns become albedo transcript entries again and pass through the same projection a session uses. A request to the same profile replays them verbatim; a request to another profile gets the portable text and calls. Tool results are matched to the provider's own call ids. State larger than 256 KiB is not carried, and damaged state falls back to the portable history. Turns without tool calls carry no state, which the providers do not need after the next user message.