Expand description
OpenAI request → internal invoke translation (T41, v0.4 wire-up).
The v0.3.7 scaffold (crates/tensor-wasm-api/src/openai.rs) parsed
OpenAI request bodies and returned 501 openai_not_yet_wired. T41
lands the translation step that resolves the model field against
a deploy-time configured model → FunctionId map and prepares an
invocation against the existing tensor-wasm-exec executor.
§Configuration
The map is read from the env var
TENSOR_WASM_API_OPENAI_MODEL_MAP. Format:
model_id_1:function_uuid_1,model_id_2:function_uuid_2,...Example:
gpt-3.5-turbo:00000000-0000-4000-8000-000000000001,gpt-4:00000000-0000-4000-8000-000000000002Empty / unset env var produces an empty map. With an empty map every
request fails the model lookup and returns 404 model_not_found
(per the OpenAI contract). A YAML config file alternative is deferred
to v0.5.
§Prompt → guest input channel (pull model)
The translator returns a TranslatedRequest carrying the resolved
function id, the assembled prompt text, the prompt’s byte length
(prompt_len_hint), and an args vector.
How the prompt reaches the guest. The handler in openai.rs
stages the assembled prompt bytes on
SpawnConfig::input
at spawn time. The shared
tensor_wasm_exec::executor::TensorWasmExecutor installs those
bytes on the per-instance host state and wires the wasi:tensor/host
pull-model input channel — input-len() -> u32 and read-input(ptr, len) -> s32 — so the guest copies the prompt into its own linear
memory at the start of the invocation (size the buffer from
input-len(), then drain it with read-input). This is the input
counterpart to the output-only
StreamingContext
emit-chunk channel.
Note this is a bytes channel, distinct from the numeric
WasmArg call args:
- The assembled prompt and its byte length are preserved on
TranslatedRequest(prompt/prompt_len_hint); the handler movespromptintoSpawnConfig::inputso the guest can pull it. TranslatedRequest::argsis left empty, deliberately, and for a load-bearing reason: the executor’scall_export_with_argsuses wasmtime’s dynamicFunc::call_asyncpath for any non-empty arg slice, which validates the param arity against the export’s declared signature exactly. The standard WASI command export is_start () -> (); handing it even a singlei32would make wasmtime reject the call (ExecError::Wasmtime) and break every off-the-shelf guest. The empty-args path takes the typedfunc.typed::<(), ()>()fast path that those guests rely on — identical to howroutes.rs’s/invokeruns an argument-less body. The prompt travels through thewasi:tensor/hostinput channel instead of the numeric argv, so lowering it intoargsis neither needed nor safe.
A guest that does not import read-input still runs argument-less and
produces its response via the
wasi:tensor/host.emit-chunk host function; the handler drains the
receiver and surfaces the emitted bytes as the completion text.
Staging input is therefore non-breaking for such guests — the
staged bytes simply go unread.
§Unsupported sampling knobs
max_tokens, temperature, n, echo, and tools are parsed off
the wire but the executor exposes no knob for any of them. Rather
than silently ignore a caller’s explicit setting (which would make
the gateway lie about honouring it), the translator returns a clear
400 invalid_request_error (code: "unsupported_parameter",
param naming the field) whenever one of these is set to a value the
gateway cannot satisfy.
A value that coincides with a no-op default is accepted so common
SDK boilerplate still works: temperature: 1.0, n: 1,
echo: false, and an empty / null / absent tools all translate
cleanly. max_tokens has no honourable value — we cannot cap
generation — so any explicit max_tokens is rejected. This is
revisited once the executor grows the corresponding sampling
controls.
§Chat → prompt
For /v1/chat/completions the translator concatenates the
messages array into a single prompt string with role-tagged
lines:
system: You are a helpful assistant.
user: Hello!
assistant:Empty content fields are tolerated; the role line is still emitted
so a guest that splits on :\n sees a stable shape. The trailing
assistant: line signals “complete this turn” to the guest.
Structs§
- Translated
Request - Translation output: the resolved
Uuidfunction id, the fully-assembled prompt text, and the syntheticWasmArgvector to pass intocall_export_with_args.
Constants§
- ENV_
OPENAI_ MODEL_ MAP - Environment variable carrying the comma-separated
model:function_uuidmap. See module-level docs.
Functions§
- assemble_
chat_ prompt - Assemble the chat-messages array into a single prompt string with
role-tagged lines. Each entry becomes one
role: contentline; a finalassistant:line signals the guest that it should generate the next turn. - model_
map_ from_ env - Read
TENSOR_WASM_API_OPENAI_MODEL_MAPfrom the ambient process env and return anArc-wrappedHashMapfor installation intocrate::AppState. Empty / unset env var produces an empty map. - parse_
model_ map_ env - Parse the
TENSOR_WASM_API_OPENAI_MODEL_MAPenv-var value into aHashMap<String, Uuid>. - translate_
chat_ completions_ request - Translate a
ChatCompletionsRequestinto aTranslatedRequest. - translate_
completions_ request - Translate a
CompletionsRequestinto aTranslatedRequest.
Type Aliases§
- Model
Map - Resolved
model → FunctionIdmap, shared across handler invocations viaArc.