Expand description
The model-executor seam: the general injection point a host supplies so the server can perform a model call on a client-driven run’s behalf.
§Why it is a seam, not baked in
This mirrors the AgentFactory decision exactly. The
server owns the mechanism of a durable model step (append the write-ahead
intent, perform the call, append the completion, answer retries from the
log), but it must not own the policy of which provider runs, where its
credential comes from, or how the request is shaped. Salvor is for anyone
building on it, browser or backend; aarg is only the first consumer. So the
executor is a trait the embedding binary implements and injects, never a
hard-wired provider.
salvor serve wires a default LlmModelExecutor from its own
client-construction path, so the feature works out of the box; another host
(a future aarg serve) injects its own executor resolving a keychain
credential, and nothing consumer-specific leaks into salvor-server.
§The two methods
ModelExecutor::executeperforms a single call and returns the assembledMessageResponse. This backs the non-streaming model step.ModelExecutor::open_streamopens the provider stream and returns aModelStreamthe server pumps: each event feeds a live ticker frame and a [MessageAccumulator], and the assembled completion is recorded once at the end. This backs the server-sent-events model step.
The request crosses the seam as a raw Value (the caller’s canonical
request JSON), not a typed MessageRequest, because the server hashes and
records exactly those bytes: the hash it recorded is the hash it sent.
An executor error is a plain String, the same human-message convention the
agent factory uses. A provider failure maps to the server error envelope
without a completion being recorded, so the intent is left dangling (legal,
the crash story) and the run stays drivable.
§Naming the key on a 401, the way the CLI already does
A raw 401 from salvor_llm::Error::Api says the request was rejected; it
says nothing about where to fix it. The CLI’s contextualize_auth_error
(salvor-cli/src/commands.rs) solves this for the agent-driven run/resume
path because it still has the agent’s own [llm] api_key_env in scope. This
executor has no such per-agent config to read: a client-driven run’s model
step is served by the one LlmModelExecutor salvor serve wires for the
whole process, built by Config::from_env, which only ever reads
ANTHROPIC_API_KEY (see salvor-llm/src/config.rs). So contextualize_401
names that fixed variable directly, and points at the machine running the
server rather than the client’s own environment: for a client-driven run the
key lives with the server, not the caller driving it over HTTP.
Structs§
- LlmModel
Executor - The default
ModelExecutor, wrapping a generalsalvor_llm::Client.
Traits§
- Model
Executor - Performs a model call on behalf of a client-driven run. Injected by the
embedding binary, exactly like
AgentFactory. - Model
Stream - A stream of provider events opened by
ModelExecutor::open_stream.