§ unswayed-backend · API contract & docs
AI model seam — per-call model selection (ADR-0042)
What it is
A small but load-bearing upgrade to AiCompletionPort — the single seam every LLM call
in the backend goes through (Lexi, the resume AI, and the three Lenux LLM calls). It used
to send one hard-coded model + sampling for everyone; now each call site picks the
right model, temperature, and response format for its task, and the whole thing can be
pointed at a different OpenAI-compatible provider with one env var.
The shape
complete(messages: ChatMessage[], options?: AiCompletionOptions): Promise<string>
interface AiCompletionOptions {
model?: string;
maxTokens?: number;
temperature?: number;
topP?: number;
frequencyPenalty?: number;
presencePenalty?: number;
responseFormat?: 'text' | 'json'; // 'json' → response_format:{type:'json_object'}
}
Omitting options reproduces the exact previous behaviour (Lexi's tuned conversational
params), so nothing that didn't opt in changed.
Config — openaiConfig
Two additions:
baseURL(OPENAI_BASE_URL) — point the OpenAI SDK at any OpenAI-API-compatible endpoint (e.g. a zero-retention DeepSeek/Kimi host). Unset ⇒ OpenAI itself.models— a per-purpose map, each env-overridable:default(OPENAI_MODEL, gpt-4o) — Lexiresume(OPENAI_RESUME_MODEL, defaults to the base model) — resume extract/enhancelenux(OPENAI_LENUX_MODEL, gpt-4o-mini) — interview questions + JD optimizationintent(OPENAI_INTENT_MODEL, gpt-4o-mini) — Lenux chat intent classification
How each call is routed
| Call | Model | Sampling | JSON mode |
|---|---|---|---|
| Lexi assistant | models.default (gpt-4o) |
tuned defaults | no (unchanged) |
| Resume extract/enhance | models.resume (gpt-4o) |
enhance temp/tokens | extract: yes |
| Interview questions, JD bias/skills/SEO | models.lenux (gpt-4o-mini) |
temp 0.2 | yes |
| Chat intent classification | models.intent (gpt-4o-mini) |
temp 0, max 400 | yes |
Why these defaults
The Lenux tasks are short structured generation / classification, not reasoning — so a cheap fast small tier (gpt-4o-mini, ~16–40× cheaper output than gpt-4o) is the right tool; a "more powerful" reasoning model would be slower, pricier, and worse at JSON/tool reliability. Chat-intent is on the request path, so it gets the lowest temperature and a small token cap for latency. Lexi and the resume rewrite stay on gpt-4o because they're user-facing quality surfaces — they're not silently downgraded. JSON mode is the reliability win; the existing retry-once parsers stay as defence (json_object can still occasionally return empty content).
A newer/cheaper SKU (or a different provider via baseURL) is now a config flip, not a
code change. Compliance caveat (ADR-0041): candidate data must not go to a China-jurisdiction
first-party API — if DeepSeek/Kimi is ever used it must be the open weights on a Western
SOC2/zero-retention host. Default is OpenAI.
See ADR-0042.