Skip to main content

Module openai_translator

Module openai_translator 

Source
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-000000000002

Empty / 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 moves prompt into SpawnConfig::input so the guest can pull it.
  • TranslatedRequest::args is left empty, deliberately, and for a load-bearing reason: the executor’s call_export_with_args uses wasmtime’s dynamic Func::call_async path 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 single i32 would make wasmtime reject the call (ExecError::Wasmtime) and break every off-the-shelf guest. The empty-args path takes the typed func.typed::<(), ()>() fast path that those guests rely on — identical to how routes.rs’s /invoke runs an argument-less body. The prompt travels through the wasi:tensor/host input channel instead of the numeric argv, so lowering it into args is 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§

TranslatedRequest
Translation output: the resolved Uuid function id, the fully-assembled prompt text, and the synthetic WasmArg vector to pass into call_export_with_args.

Constants§

ENV_OPENAI_MODEL_MAP
Environment variable carrying the comma-separated model:function_uuid map. 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: content line; a final assistant: line signals the guest that it should generate the next turn.
model_map_from_env
Read TENSOR_WASM_API_OPENAI_MODEL_MAP from the ambient process env and return an Arc-wrapped HashMap for installation into crate::AppState. Empty / unset env var produces an empty map.
parse_model_map_env
Parse the TENSOR_WASM_API_OPENAI_MODEL_MAP env-var value into a HashMap<String, Uuid>.
translate_chat_completions_request
Translate a ChatCompletionsRequest into a TranslatedRequest.
translate_completions_request
Translate a CompletionsRequest into a TranslatedRequest.

Type Aliases§

ModelMap
Resolved model → FunctionId map, shared across handler invocations via Arc.