Skip to content

feat(providers): add Novita AI as OpenAI-compatible provider#451

Open
Alex-wuhu wants to merge 1 commit intomoltis-org:mainfrom
Alex-wuhu:novita-integration
Open

feat(providers): add Novita AI as OpenAI-compatible provider#451
Alex-wuhu wants to merge 1 commit intomoltis-org:mainfrom
Alex-wuhu:novita-integration

Conversation

@Alex-wuhu
Copy link
Copy Markdown

Summary

  • Adds Novita AI as an OpenAI-compatible LLM provider via https://api.novita.ai/openai
  • Registers three models: moonshotai/kimi-k2.5, deepseek/deepseek-v3.2, zai-org/glm-5
  • Configured via NOVITA_API_KEY environment variable (or [providers.novita] in config)
  • Follows the existing table-driven OpenAiCompatDef pattern; no changes to existing providers

Validation

Completed

  • cargo check -p moltis-providers -p moltis-provider-setup -p moltis-config passes
  • cargo test -p moltis-providers -p moltis-provider-setup — 278 tests pass
  • New tests: novita_provider_is_registered, novita_model_ids_are_chat_capable, novita_context_windows
  • Provider appears in known_providers() and all existing provider test lists updated

Manual QA

  1. Set NOVITA_API_KEY=<key> and start moltis — Novita models should appear in the model picker
  2. Or add [providers.novita] / api_key = "..." to moltis.toml
  3. Select novita::moonshotai/kimi-k2.5 and send a message — response should stream correctly

Add Novita AI (https://api.novita.ai/openai) as an OpenAI-compatible
LLM provider with three models: moonshotai/kimi-k2.5, deepseek/deepseek-v3.2,
and zai-org/glm-5. Configured via NOVITA_API_KEY environment variable.
@Alex-wuhu Alex-wuhu marked this pull request as ready for review March 19, 2026 10:24
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 19, 2026

Greptile Summary

This PR integrates Novita AI as an OpenAI-compatible provider following the existing OpenAiCompatDef table-driven pattern. It registers three models (moonshotai/kimi-k2.5, deepseek/deepseek-v3.2, zai-org/glm-5), wires up the NOVITA_API_KEY / NOVITA_BASE_URL environment variables, and updates the config template with a commented-out [providers.novita] stanza.

Key observations:

  • The implementation is clean and follows the established patterns precisely — no existing providers are modified.
  • capability_model_id correctly handles slash-delimited org-prefixed model IDs (e.g., moonshotai/kimi-k2.5kimi-k2.5), so context_window_for_model and is_chat_capable_model both work correctly for the new models.
  • Three new tests (novita_provider_is_registered, novita_model_ids_are_chat_capable, novita_context_windows) are included and well-structured.
  • The novita_context_windows test omits deepseek/deepseek-v3.2; this model's capability ID (deepseek-v3.2) has no matching rule in context_window_for_model, so it falls through to the 200 k default — which may differ from the actual context limit exposed by Novita's API for that model.
  • The KnownProvider entry in provider-setup/src/lib.rs is ordered between kimi-code and venice, but the OpenAiCompatDef entry in providers/src/lib.rs is appended at the end of the array (after gemini), creating a minor ordering inconsistency between the two lists.

Confidence Score: 4/5

  • Safe to merge — no changes to existing providers and the new code follows established patterns; minor test-coverage gap and a cosmetic ordering inconsistency should be addressed but are not blockers.
  • The implementation is structurally sound, all existing tests continue to pass, and the new tests cover the most critical properties. The only substantive concern is the undocumented/untested 200k context window fallback for deepseek/deepseek-v3.2, which could mislead users about available context. The ordering inconsistency between the two provider lists is cosmetic.
  • crates/providers/src/lib.rs — specifically the novita_context_windows test and the position of the OpenAiCompatDef entry in OPENAI_COMPAT_PROVIDERS.

Important Files Changed

Filename Overview
crates/providers/src/lib.rs Adds NOVITA_MODELS constant and OpenAiCompatDef entry for Novita; three new tests cover registration, chat-capability, and context windows for two of three models — deepseek/deepseek-v3.2 context window is untested and defaults to the 200k fallback.
crates/provider-setup/src/lib.rs Adds KnownProvider entry for Novita AI with correct NOVITA_API_KEY env var, base URL, and key_optional: false; all four relevant test lists updated consistently.
crates/config/src/template.rs Template updated with Novita in the provider list comment and a new commented-out [providers.novita] stanza; content is accurate and consistent with the implementation.

Sequence Diagram

sequenceDiagram
    participant User
    participant Moltis
    participant NovitaAPI as Novita AI (api.novita.ai/openai)

    User->>Moltis: Set NOVITA_API_KEY / [providers.novita]
    Moltis->>NovitaAPI: GET /models (model discovery)
    NovitaAPI-->>Moltis: model list (merged with NOVITA_MODELS static catalog)
    Moltis-->>User: novita::moonshotai/kimi-k2.5, novita::deepseek/deepseek-v3.2, novita::zai-org/glm-5 appear in picker

    User->>Moltis: Send message with novita::moonshotai/kimi-k2.5
    Moltis->>NovitaAPI: POST /chat/completions (model: moonshotai/kimi-k2.5)
    NovitaAPI-->>Moltis: stream SSE chunks
    Moltis-->>User: streamed response
Loading

Last reviewed commit: "feat(providers): add..."

Comment on lines +4092 to +4101
#[test]
fn novita_context_windows() {
// moonshotai/kimi-k2.5 — capability ID is "kimi-k2.5" → 128k
assert_eq!(
context_window_for_model("moonshotai/kimi-k2.5"),
128_000
);
// zai-org/glm-5 — capability ID is "glm-5" → 128k
assert_eq!(context_window_for_model("zai-org/glm-5"), 128_000);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Missing context-window assertion for deepseek/deepseek-v3.2

The novita_context_windows test deliberately covers two of the three Novita models (with explanatory comments about their capability IDs), but silently omits deepseek/deepseek-v3.2.

capability_model_id("deepseek/deepseek-v3.2") strips the org-prefix to "deepseek-v3.2", which does not match any of the rules in context_window_for_model — so the function falls through to the 200 k default. DeepSeek V3 is typically deployed with a 64 k context window on most platforms; reporting 200 k to the UI means users can construct prompts that will be rejected by the actual API.

Either add a deepseek- prefix rule to context_window_for_model (consistent with how kimi-, glm-, etc. are handled) or add a test assertion that explicitly documents the intended fallback value, so the behaviour is visible and deliberate:

Suggested change
#[test]
fn novita_context_windows() {
// moonshotai/kimi-k2.5 — capability ID is "kimi-k2.5" → 128k
assert_eq!(
context_window_for_model("moonshotai/kimi-k2.5"),
128_000
);
// zai-org/glm-5 — capability ID is "glm-5" → 128k
assert_eq!(context_window_for_model("zai-org/glm-5"), 128_000);
}
#[test]
fn novita_context_windows() {
// moonshotai/kimi-k2.5 — capability ID is "kimi-k2.5" → 128k
assert_eq!(
context_window_for_model("moonshotai/kimi-k2.5"),
128_000
);
// zai-org/glm-5 — capability ID is "glm-5" → 128k
assert_eq!(context_window_for_model("zai-org/glm-5"), 128_000);
// deepseek/deepseek-v3.2 — capability ID is "deepseek-v3.2" → falls
// back to 200k default; update if a tighter window is confirmed.
assert_eq!(context_window_for_model("deepseek/deepseek-v3.2"), 200_000);
}

Comment on lines +1041 to +1050
OpenAiCompatDef {
config_name: "novita",
env_key: "NOVITA_API_KEY",
env_base_url_key: "NOVITA_BASE_URL",
default_base_url: "https://api.novita.ai/openai",
models: NOVITA_MODELS,
supports_model_discovery: true,
requires_api_key: true,
local_only: false,
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Provider ordering inconsistent with known_providers()

In provider-setup/src/lib.rs, the new KnownProvider entry is inserted between kimi-code and venice (matching the alphabetical-ish ordering of the surrounding list). Here in OPENAI_COMPAT_PROVIDERS the same provider is appended at the very end, after gemini.

Keeping the two lists in the same relative order makes it easier to cross-reference them and reduces the risk of accidentally missing a provider when adding the next one. Consider moving this entry to follow zai / moonshot, where the other same-region providers live:

// after the existing `zai` entry and before `venice`
OpenAiCompatDef {
    config_name: "novita",
    ...
},

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant