Compare commits
2 Commits
38075b2943
...
e0945f5414
| Author | SHA1 | Date |
|---|---|---|
|
|
e0945f5414 | |
|
|
2364c07247 |
|
|
@ -7,3 +7,4 @@ DEEPSEEK_API_KEY=
|
|||
DASHSCOPE_API_KEY=
|
||||
ZHIPU_API_KEY=
|
||||
OPENROUTER_API_KEY=
|
||||
MINIMAX_API_KEY=
|
||||
|
|
|
|||
|
|
@ -239,6 +239,7 @@ def select_llm_provider() -> tuple[str, str | None]:
|
|||
("DeepSeek", "deepseek", "https://api.deepseek.com"),
|
||||
("Qwen", "qwen", "https://dashscope.aliyuncs.com/compatible-mode/v1"),
|
||||
("GLM", "glm", "https://open.bigmodel.cn/api/paas/v4/"),
|
||||
("MiniMax", "minimax", "https://api.minimaxi.chat/v1"),
|
||||
("OpenRouter", "openrouter", "https://openrouter.ai/api/v1"),
|
||||
("Azure OpenAI", "azure", None),
|
||||
("Ollama", "ollama", "http://localhost:11434/v1"),
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ from .azure_client import AzureOpenAIClient
|
|||
|
||||
# Providers that use the OpenAI-compatible chat completions API
|
||||
_OPENAI_COMPATIBLE = (
|
||||
"openai", "xai", "deepseek", "qwen", "glm", "ollama", "openrouter",
|
||||
"openai", "xai", "deepseek", "qwen", "glm", "ollama", "openrouter", "minimax",
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -99,6 +99,20 @@ MODEL_OPTIONS: ProviderModeOptions = {
|
|||
("Custom model ID", "custom"),
|
||||
],
|
||||
},
|
||||
"minimax": {
|
||||
"quick": [
|
||||
("MiniMax-M2.7", "MiniMax-M2.7"),
|
||||
("MiniMax-M2.7-highspeed", "MiniMax-M2.7-highspeed"),
|
||||
("MiniMax-M2.5", "MiniMax-M2.5"),
|
||||
("MiniMax-M2.5-highspeed", "MiniMax-M2.5-highspeed"),
|
||||
],
|
||||
"deep": [
|
||||
("MiniMax-M2.7", "MiniMax-M2.7"),
|
||||
("MiniMax-M2.7-highspeed", "MiniMax-M2.7-highspeed"),
|
||||
("MiniMax-M2.5", "MiniMax-M2.5"),
|
||||
("MiniMax-M2.5-highspeed", "MiniMax-M2.5-highspeed"),
|
||||
],
|
||||
},
|
||||
# OpenRouter: fetched dynamically. Azure: any deployed model name.
|
||||
"ollama": {
|
||||
"quick": [
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import os
|
||||
import time
|
||||
import logging
|
||||
from typing import Any, Optional
|
||||
|
||||
from langchain_openai import ChatOpenAI
|
||||
|
|
@ -6,6 +8,11 @@ from langchain_openai import ChatOpenAI
|
|||
from .base_client import BaseLLMClient, normalize_content
|
||||
from .validators import validate_model
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_NULL_CHOICES_RETRIES = 3
|
||||
_NULL_CHOICES_DELAY = 2 # seconds
|
||||
|
||||
|
||||
class NormalizedChatOpenAI(ChatOpenAI):
|
||||
"""ChatOpenAI with normalized content output.
|
||||
|
|
@ -16,7 +23,28 @@ class NormalizedChatOpenAI(ChatOpenAI):
|
|||
"""
|
||||
|
||||
def invoke(self, input, config=None, **kwargs):
|
||||
return normalize_content(super().invoke(input, config, **kwargs))
|
||||
for attempt in range(1, _NULL_CHOICES_RETRIES + 1):
|
||||
try:
|
||||
return normalize_content(super().invoke(input, config, **kwargs))
|
||||
except TypeError as e:
|
||||
if "null value for 'choices'" in str(e):
|
||||
if attempt < _NULL_CHOICES_RETRIES:
|
||||
logger.warning(
|
||||
"Received null choices from API (content filter or transient error). "
|
||||
"Retrying in %ds (attempt %d/%d)...",
|
||||
_NULL_CHOICES_DELAY,
|
||||
attempt,
|
||||
_NULL_CHOICES_RETRIES,
|
||||
)
|
||||
time.sleep(_NULL_CHOICES_DELAY)
|
||||
else:
|
||||
raise RuntimeError(
|
||||
"API returned null choices after retries. "
|
||||
"The request may have been blocked by content moderation. "
|
||||
"Try rephrasing the prompt or check the provider's content policy."
|
||||
) from e
|
||||
else:
|
||||
raise
|
||||
|
||||
# Kwargs forwarded from user config to ChatOpenAI
|
||||
_PASSTHROUGH_KWARGS = (
|
||||
|
|
@ -32,6 +60,7 @@ _PROVIDER_CONFIG = {
|
|||
"glm": ("https://api.z.ai/api/paas/v4/", "ZHIPU_API_KEY"),
|
||||
"openrouter": ("https://openrouter.ai/api/v1", "OPENROUTER_API_KEY"),
|
||||
"ollama": ("http://localhost:11434/v1", None),
|
||||
"minimax": ("https://api.minimaxi.chat/v1", "MINIMAX_API_KEY"),
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue