From 0e212f23c1b5f191d9f872e6f3936682d55ba80b Mon Sep 17 00:00:00 2001 From: jax-novita Date: Wed, 5 Aug 2026 19:03:57 +0800 Subject: [PATCH 1/3] Add NovitaLLM provider Novita AI exposes an OpenAI-compatible chat completions API. This adds a NovitaLLM adapter following the same pattern as SiliconFlowLLM: it subclasses OpenAILLM, overrides client construction to point at Novita's base URL, and records token usage without a dollar cost since LiteLLM has no pricing data for Novita-hosted models. --- docs/modules/llm.md | 29 +++++++++++++++ docs/zh/modules/llm.md | 29 +++++++++++++++ evoagentx/models/__init__.py | 1 + evoagentx/models/model_configs.py | 32 ++++++++++++++++ evoagentx/models/novita_model.py | 61 +++++++++++++++++++++++++++++++ 5 files changed, 152 insertions(+) create mode 100644 evoagentx/models/novita_model.py diff --git a/docs/modules/llm.md b/docs/modules/llm.md index 72947b28..5d564d36 100644 --- a/docs/modules/llm.md +++ b/docs/modules/llm.md @@ -94,6 +94,35 @@ response = llm.generate( ) ``` +### NovitaLLM + +NovitaLLM is an adapter for models hosted on the [Novita AI platform](https://novita.ai/), which offers access to open-source and proprietary LLMs via an OpenAI-compatible API. + +Thanks to Novita's OpenAI-compatible interface, the `NovitaLLM` model class in EvoAgentX allows seamless switching between models hosted on Novita using the same API format. + +**Basic Usage:** + +```python +from evoagentx.models import NovitaConfig, NovitaLLM + +# Configure the model +config = NovitaConfig( + model="deepseek/deepseek-v3", + novita_key="your-novita-api-key", + temperature=0.7, + max_tokens=1000 +) + +# Initialize the model +llm = NovitaLLM(config=config) + +# Generate text +response = llm.generate( + prompt="Write a poem about artificial intelligence.", + system_message="You are a creative poet." +) +``` + ### OpenRouterLLM OpenRouterLLM is an adapter for the [OpenRouter platform](https://openrouter.ai/), which provides access to a wide range of language models from various providers through a unified API. It supports models from providers like Anthropic, Google, Meta, Mistral AI, and more, all accessible through a single interface. diff --git a/docs/zh/modules/llm.md b/docs/zh/modules/llm.md index d7e94b85..85c763fc 100644 --- a/docs/zh/modules/llm.md +++ b/docs/zh/modules/llm.md @@ -93,6 +93,35 @@ response = llm.generate( ) ``` +### NovitaLLM + +NovitaLLM 是 [Novita AI 平台](https://novita.ai/) 上托管模型的适配器,该平台通过 OpenAI 兼容的 API 提供对开源和专有模型的访问。 + +得益于 Novita 兼容 OpenAI 的接口,EvoAgentX 中的 `NovitaLLM` 模型类允许使用相同的 API 格式在 Novita 上托管的不同模型之间无缝切换。 + +**基本用法:** + +```python +from evoagentx.models import NovitaConfig, NovitaLLM + +# Configure the model +config = NovitaConfig( + model="deepseek/deepseek-v3", + novita_key="your-novita-api-key", + temperature=0.7, + max_tokens=1000 +) + +# Initialize the model +llm = NovitaLLM(config=config) + +# Generate text +response = llm.generate( + prompt="Write a poem about artificial intelligence.", + system_message="You are a creative poet." +) +``` + ### OpenRouterLLM OpenRouterLLM 是 [OpenRouter 平台](https://openrouter.ai/) 的适配器,该平台通过统一的 API 提供对各种提供商的语言模型的访问。它支持来自 Anthropic、Google、Meta、Mistral AI 等提供商的模型,所有这些都可以通过单一接口访问。 diff --git a/evoagentx/models/__init__.py b/evoagentx/models/__init__.py index 232d9cb8..1e37a69f 100644 --- a/evoagentx/models/__init__.py +++ b/evoagentx/models/__init__.py @@ -7,3 +7,4 @@ from .siliconflow_model import * from .openrouter_model import * from .aliyun_model import * +from .novita_model import * diff --git a/evoagentx/models/model_configs.py b/evoagentx/models/model_configs.py index 60b2ca4b..ed8a545c 100644 --- a/evoagentx/models/model_configs.py +++ b/evoagentx/models/model_configs.py @@ -140,6 +140,38 @@ def __str__(self): return self.model +class NovitaConfig(LLMConfig): + + # LLM keys + llm_type: str = "NovitaLLM" + novita_key: Optional[str] = Field(default=None, description="the API key used to authenticate Novita AI requests") + + # generation parameters + temperature: Optional[float] = Field(default=None, description="the temperature used to scaling logits") + max_tokens : Optional[int] = Field(default=None, description="maximum number of generated tokens") + max_completion_tokens: Optional[int] = Field(default=None, description="An upper bound for the number of tokens that can be generated for a completion, including visible output tokens and reasoning tokens. Commonly used in OpenAI's o1 series models.") + top_p: Optional[float] = Field(default=None, description="Only sample from tokens with cumulative probability greater than top_p when generating text.") + n: Optional[int] = Field(default=None, description="How many chat completion choices to generate for each input message.") + stream: Optional[bool] = Field(default=None, description=" If set to true, it sends partial message deltas. Tokens will be sent as they become available, with the stream terminated by a [DONE] message.") + stream_options: Optional[dict] = Field(default=None, description="Options for streaming response. Only set this when you set stream: true") + timeout: Optional[Union[float, int]] = Field(default=None, description="Timeout in seconds for completion requests (Defaults to 600 seconds)") + + # tools + tools: Optional[List] = Field(default=None, description="A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of functions the model may generate JSON inputs for.") + tool_choice: Optional[Union[str, dict]] = Field(default=None, description="Controls which (if any) function is called by the model. none means the model will not call a function and instead generates a message. auto means the model can pick between generating a message or calling a function. required forces the model to call a tool. Specifying a particular function via {\"type\": \"function\", \"function\": {\"name\": \"my_function\"}} forces the model to call that function.") + parallel_tool_calls: Optional[bool] = Field(default=None, description="Whether to enable parallel function calling during tool use. OpenAI default is true.") + + # token probabilities + logprobs: Optional[bool] = Field(default=None, description="Whether to return log probabilities of the output tokens or not. If true, returns the log probabilities of each output token returned in the content of message.") + top_logprobs: Optional[int] = Field(default=None, description="An integer between 0 and 5 specifying the number of most likely tokens to return at each token position, each with an associated log probability. logprobs must be set to true if this parameter is used.") + + # output format + response_format: Optional[Union[BaseModel, dict]] = Field(default=None, description=" An object specifying the format that the model must output.") + + def __str__(self): + return self.model + + # def get_default_device(): # return "cuda" if torch.cuda.is_available() else "cpu" diff --git a/evoagentx/models/novita_model.py b/evoagentx/models/novita_model.py new file mode 100644 index 00000000..aecb0208 --- /dev/null +++ b/evoagentx/models/novita_model.py @@ -0,0 +1,61 @@ +from typing import Union + +from openai import AsyncOpenAI, OpenAI +from openai.types.chat import ChatCompletion, ChatCompletionChunk + +from .openai_model import OpenAILLM +from .model_configs import NovitaConfig +from ..core.logging import logger +from ..core.registry import register_model +from .model_utils import Cost, cost_manager + +# Novita AI exposes an OpenAI-compatible API at this base URL. +NOVITA_BASE_URL = "https://api.novita.ai/openai" + + +@register_model(config_cls=NovitaConfig, alias=["novita"]) +class NovitaLLM(OpenAILLM): + """Novita AI LLM client. + + Novita speaks the OpenAI chat-completions protocol, so this reuses all of + ``OpenAILLM``'s generation/streaming/tool-call logic and only overrides client + construction and cost handling. + + LiteLLM has no pricing data for Novita-hosted models, so the dollar cost + cannot be computed or approximated. Token counts are still tracked; the + per-model cost is recorded as 0. A warning is emitted once at init time. + """ + + def init_model(self): + self._client = None + self._async_client = None + # parameters in NovitaConfig that are not Novita models' input parameters + self._default_ignore_fields = ["llm_type", "novita_key", "output_response"] + logger.warning( + "[NovitaLLM] LiteLLM has no pricing data for Novita models, so dollar " + "cost cannot be computed. Token usage will be tracked, but cost will " + "be recorded as 0." + ) + + def _init_client(self, config: NovitaConfig): + return OpenAI(api_key=config.novita_key, base_url=NOVITA_BASE_URL) + + def _init_async_client(self, config: NovitaConfig): + return AsyncOpenAI(api_key=config.novita_key, base_url=NOVITA_BASE_URL) + + def _update_cost(self, response: Union[ChatCompletion, ChatCompletionChunk]): + # Override OpenAILLM's LiteLLM-based cost computation: only record token + # counts and leave cost at 0 (see class docstring). + usage = getattr(response, "usage", None) + if usage is None: + logger.warning( + f"[NovitaLLM] usage is None in response (id={getattr(response, 'id', '?')}); " + "tokens will not be recorded." + ) + return + cost = Cost( + input_tokens=usage.prompt_tokens, + output_tokens=usage.completion_tokens, + cost=0.0, + ) + cost_manager.update_cost(cost=cost, model=self.config.model) From 56a6f7022b245000994cf47e5a87091bee9200e3 Mon Sep 17 00:00:00 2001 From: jax-novita Date: Wed, 5 Aug 2026 21:08:08 +0800 Subject: [PATCH 2/3] Recommend Novita's current flagship models The models listed for Novita were older ids that no longer reflect what the platform leads with. Point the recommendations at the three current flagships instead, each verified against api.novita.ai: moonshotai/kimi-k3 1M context, native vision zai-org/glm-5.2 1M context, long-horizon agentic work deepseek/deepseek-v4-flash-0731 1M context, cheapest of the three Context windows, output limits, input modalities and pricing were taken from the live /openai/v1/models response rather than carried over. --- docs/modules/llm.md | 2 +- docs/zh/modules/llm.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/modules/llm.md b/docs/modules/llm.md index 5d564d36..1c935947 100644 --- a/docs/modules/llm.md +++ b/docs/modules/llm.md @@ -107,7 +107,7 @@ from evoagentx.models import NovitaConfig, NovitaLLM # Configure the model config = NovitaConfig( - model="deepseek/deepseek-v3", + model="moonshotai/kimi-k3", novita_key="your-novita-api-key", temperature=0.7, max_tokens=1000 diff --git a/docs/zh/modules/llm.md b/docs/zh/modules/llm.md index 85c763fc..9ac2003d 100644 --- a/docs/zh/modules/llm.md +++ b/docs/zh/modules/llm.md @@ -106,7 +106,7 @@ from evoagentx.models import NovitaConfig, NovitaLLM # Configure the model config = NovitaConfig( - model="deepseek/deepseek-v3", + model="moonshotai/kimi-k3", novita_key="your-novita-api-key", temperature=0.7, max_tokens=1000 From 3239d1cd08e9882218cf4063bbdde9dcd96a3be2 Mon Sep 17 00:00:00 2001 From: jax-novita Date: Tue, 11 Aug 2026 08:51:31 +0800 Subject: [PATCH 3/3] Update Novita example model to current flagship deepseek-v4-pro --- docs/modules/llm.md | 2 +- docs/zh/modules/llm.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/modules/llm.md b/docs/modules/llm.md index 1c935947..0a4c0c49 100644 --- a/docs/modules/llm.md +++ b/docs/modules/llm.md @@ -107,7 +107,7 @@ from evoagentx.models import NovitaConfig, NovitaLLM # Configure the model config = NovitaConfig( - model="moonshotai/kimi-k3", + model="deepseek/deepseek-v4-pro", novita_key="your-novita-api-key", temperature=0.7, max_tokens=1000 diff --git a/docs/zh/modules/llm.md b/docs/zh/modules/llm.md index 9ac2003d..be1adb2f 100644 --- a/docs/zh/modules/llm.md +++ b/docs/zh/modules/llm.md @@ -106,7 +106,7 @@ from evoagentx.models import NovitaConfig, NovitaLLM # Configure the model config = NovitaConfig( - model="moonshotai/kimi-k3", + model="deepseek/deepseek-v4-pro", novita_key="your-novita-api-key", temperature=0.7, max_tokens=1000