SIGN IN SIGN UP

feat(llm): add Novita provider via a Responses-API translation adapter (#55)

* feat(llm): add Novita provider via a Responses-API translation adapter

Novita's LLM API is OpenAI-compatible only at the Chat Completions layer,
not the Responses API cumora's agent turn/convene/triage/etc. call sites
are written against — a base-URL swap alone would 404. server/src/novita.ts
translates Responses-shaped requests (instructions/input/tools/streaming)
into Chat Completions calls and translates the result (including streamed
ChatCompletionChunks) back into the exact ResponseStreamEvent sequence
turn-stream.ts already consumes, so no existing call site changes.

getLlmClient() routes per-call based on a `novita/<model>` prefix on the
model id (server/src/llm.ts's withNovitaRouting), so an agent opts in by
setting that as its model — AgentEditor's model field hint documents the
convention for Cloud agents.

* fix(novita): drop undefined timeout/maxRetries keys before SDK call

The OpenAI SDK's buildRequest() guards timeout validation with
'timeout' in options rather than a nullish check, so always including
the key (even as undefined) threw OpenAIError: timeout must be an
integer before any request was sent — surfacing upstream in turn.ts
as a misleading connection/timeout failure. Only set signal/maxRetries/
timeout on the per-call options object when a real value is present.

* fix(llm): don't route novita/ models to Novita when NOVITA_API_KEY is unset

env.ts promised an unset key falls back to the tenant's normal client;
the router actually sent the call to api.novita.ai with an empty bearer
and the agent died on an unexplained 401. Make the documented fallback
real, with a one-time warn so operators can see why the model isn't
resolving to Novita.

---------

Co-authored-by: jax-novita <jax-novita@users.noreply.github.com>
Co-authored-by: yetone <yetoneful@gmail.com>
J
jax-novita committed
bd8dba8e45c91f685ea3c319aae173d44d26cbd6
Parent: 13222e3
Committed by GitHub <noreply@github.com> on 8/22/2026, 12:01:08 PM