diff --git a/api/apps/llm_app.py b/api/apps/llm_app.py
index eb9ee1c94..2ec8180cd 100644
--- a/api/apps/llm_app.py
+++ b/api/apps/llm_app.py
@@ -57,6 +57,7 @@ def set_api_key():
# test if api key works
chat_passed, embd_passed, rerank_passed = False, False, False
factory = req["llm_factory"]
+ extra = {"provider": factory}
msg = ""
for llm in LLMService.query(fid=factory):
if not embd_passed and llm.model_type == LLMType.EMBEDDING.value:
@@ -73,7 +74,7 @@ def set_api_key():
elif not chat_passed and llm.model_type == LLMType.CHAT.value:
assert factory in ChatModel, f"Chat model from {factory} is not supported yet."
mdl = ChatModel[factory](
- req["api_key"], llm.llm_name, base_url=req.get("base_url"))
+ req["api_key"], llm.llm_name, base_url=req.get("base_url"), **extra)
try:
m, tc = mdl.chat(None, [{"role": "user", "content": "Hello! How are you doing!"}],
{"temperature": 0.9, 'max_tokens': 50})
@@ -204,6 +205,7 @@ def add_llm():
msg = ""
mdl_nm = llm["llm_name"].split("___")[0]
+ extra = {"provider": factory}
if llm["model_type"] == LLMType.EMBEDDING.value:
assert factory in EmbeddingModel, f"Embedding model from {factory} is not supported yet."
mdl = EmbeddingModel[factory](
@@ -221,7 +223,8 @@ def add_llm():
mdl = ChatModel[factory](
key=llm['api_key'],
model_name=mdl_nm,
- base_url=llm["api_base"]
+ base_url=llm["api_base"],
+ **extra,
)
try:
m, tc = mdl.chat(None, [{"role": "user", "content": "Hello! How are you doing!"}], {
@@ -312,12 +315,12 @@ def delete_factory():
def my_llms():
try:
include_details = request.args.get('include_details', 'false').lower() == 'true'
-
+
if include_details:
res = {}
objs = TenantLLMService.query(tenant_id=current_user.id)
factories = LLMFactoriesService.query(status=StatusEnum.VALID.value)
-
+
for o in objs:
o_dict = o.to_dict()
factory_tags = None
@@ -325,13 +328,13 @@ def my_llms():
if f.name == o_dict["llm_factory"]:
factory_tags = f.tags
break
-
+
if o_dict["llm_factory"] not in res:
res[o_dict["llm_factory"]] = {
"tags": factory_tags,
"llm": []
}
-
+
res[o_dict["llm_factory"]]["llm"].append({
"type": o_dict["model_type"],
"name": o_dict["llm_name"],
@@ -352,7 +355,7 @@ def my_llms():
"name": o["llm_name"],
"used_token": o["used_tokens"]
})
-
+
return get_json_result(data=res)
except Exception as e:
return server_error_response(e)
diff --git a/api/db/services/llm_service.py b/api/db/services/llm_service.py
index dac080b64..fbfa7d65e 100644
--- a/api/db/services/llm_service.py
+++ b/api/db/services/llm_service.py
@@ -141,6 +141,7 @@ class TenantLLMService(CommonService):
@DB.connection_context()
def model_instance(cls, tenant_id, llm_type, llm_name=None, lang="Chinese", **kwargs):
model_config = TenantLLMService.get_model_config(tenant_id, llm_type, llm_name)
+ kwargs.update({"provider": model_config["llm_factory"]})
if llm_type == LLMType.EMBEDDING.value:
if model_config["llm_factory"] not in EmbeddingModel:
return
diff --git a/pyproject.toml b/pyproject.toml
index 51f740ca3..e0202451a 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -24,7 +24,7 @@ dependencies = [
"chardet==5.2.0",
"cn2an==0.5.22",
"cohere==5.6.2",
- "Crawl4AI==0.3.8",
+ "Crawl4AI>=0.3.8",
"dashscope==1.20.11",
"deepl==1.18.0",
"demjson3==3.0.6",
@@ -43,7 +43,7 @@ dependencies = [
"groq==0.9.0",
"hanziconv==0.3.2",
"html-text==0.6.2",
- "httpx==0.27.0",
+ "httpx==0.27.2",
"huggingface-hub>=0.25.0,<0.26.0",
"infinity-sdk==0.6.0-dev4",
"infinity-emb>=0.0.66,<0.0.67",
@@ -58,7 +58,7 @@ dependencies = [
"ollama==0.2.1",
"onnxruntime==1.19.2; sys_platform == 'darwin' or platform_machine != 'x86_64'",
"onnxruntime-gpu==1.19.2; sys_platform != 'darwin' and platform_machine == 'x86_64'",
- "openai==1.45.0",
+ "openai>=1.45.0",
"opencv-python==4.10.0.84",
"opencv-python-headless==4.10.0.84",
"openpyxl>=3.1.0,<4.0.0",
@@ -128,6 +128,7 @@ dependencies = [
"opensearch-py==2.7.1",
"pluginlib==0.9.4",
"click>=8.1.8",
+ "litellm>=1.74.15.post1",
]
[project.optional-dependencies]
diff --git a/rag/llm/__init__.py b/rag/llm/__init__.py
index e9542bbe8..58c8379cb 100644
--- a/rag/llm/__init__.py
+++ b/rag/llm/__init__.py
@@ -19,6 +19,48 @@
import importlib
import inspect
+from strenum import StrEnum
+
+
+class SupportedLiteLLMProvider(StrEnum):
+ Tongyi_Qianwen = "Tongyi-Qianwen"
+ Dashscope = "Dashscope"
+ Bedrock = "Bedrock"
+ Moonshot = "Moonshot"
+ xAI = "xAI"
+ DeepInfra = "DeepInfra"
+ Groq = "Groq"
+ Cohere = "Cohere"
+ Gemini = "Gemini"
+ DeepSeek = "DeepSeek"
+ Nvidia = "NVIDIA"
+ TogetherAI = "TogetherAI"
+ Anthropic = "Anthropic"
+
+
+FACTORY_DEFAULT_BASE_URL = {
+ SupportedLiteLLMProvider.Tongyi_Qianwen: "https://dashscope.aliyuncs.com/compatible-mode/v1",
+ SupportedLiteLLMProvider.Dashscope: "https://dashscope.aliyuncs.com/compatible-mode/v1",
+ SupportedLiteLLMProvider.Moonshot: "https://api.moonshot.cn/v1",
+}
+
+
+LITELLM_PROVIDER_PREFIX = {
+ SupportedLiteLLMProvider.Tongyi_Qianwen: "dashscope/",
+ SupportedLiteLLMProvider.Dashscope: "dashscope/",
+ SupportedLiteLLMProvider.Bedrock: "bedrock/",
+ SupportedLiteLLMProvider.Moonshot: "moonshot/",
+ SupportedLiteLLMProvider.xAI: "xai/",
+ SupportedLiteLLMProvider.DeepInfra: "deepinfra/",
+ SupportedLiteLLMProvider.Groq: "groq/",
+ SupportedLiteLLMProvider.Cohere: "", # don't need a prefix
+ SupportedLiteLLMProvider.Gemini: "gemini/",
+ SupportedLiteLLMProvider.DeepSeek: "deepseek/",
+ SupportedLiteLLMProvider.Nvidia: "nvidia_nim/",
+ SupportedLiteLLMProvider.TogetherAI: "together_ai/",
+ SupportedLiteLLMProvider.Anthropic: "", # don't need a prefix
+}
+
ChatModel = globals().get("ChatModel", {})
CvModel = globals().get("CvModel", {})
EmbeddingModel = globals().get("EmbeddingModel", {})
@@ -26,6 +68,7 @@ RerankModel = globals().get("RerankModel", {})
Seq2txtModel = globals().get("Seq2txtModel", {})
TTSModel = globals().get("TTSModel", {})
+
MODULE_MAPPING = {
"chat_model": ChatModel,
"cv_model": CvModel,
@@ -42,20 +85,30 @@ for module_name, mapping_dict in MODULE_MAPPING.items():
module = importlib.import_module(full_module_name)
base_class = None
+ lite_llm_base_class = None
for name, obj in inspect.getmembers(module):
- if inspect.isclass(obj) and name == "Base":
- base_class = obj
- break
- if base_class is None:
- continue
+ if inspect.isclass(obj):
+ if name == "Base":
+ base_class = obj
+ elif name == "LiteLLMBase":
+ lite_llm_base_class = obj
+ assert hasattr(obj, "_FACTORY_NAME"), "LiteLLMbase should have _FACTORY_NAME field."
+ if hasattr(obj, "_FACTORY_NAME"):
+ if isinstance(obj._FACTORY_NAME, list):
+ for factory_name in obj._FACTORY_NAME:
+ mapping_dict[factory_name] = obj
+ else:
+ mapping_dict[obj._FACTORY_NAME] = obj
+
+ if base_class is not None:
+ for _, obj in inspect.getmembers(module):
+ if inspect.isclass(obj) and issubclass(obj, base_class) and obj is not base_class and hasattr(obj, "_FACTORY_NAME"):
+ if isinstance(obj._FACTORY_NAME, list):
+ for factory_name in obj._FACTORY_NAME:
+ mapping_dict[factory_name] = obj
+ else:
+ mapping_dict[obj._FACTORY_NAME] = obj
- for _, obj in inspect.getmembers(module):
- if inspect.isclass(obj) and issubclass(obj, base_class) and obj is not base_class and hasattr(obj, "_FACTORY_NAME"):
- if isinstance(obj._FACTORY_NAME, list):
- for factory_name in obj._FACTORY_NAME:
- mapping_dict[factory_name] = obj
- else:
- mapping_dict[obj._FACTORY_NAME] = obj
__all__ = [
"ChatModel",
diff --git a/rag/llm/chat_model.py b/rag/llm/chat_model.py
index 1770b5b9e..c96afa12f 100644
--- a/rag/llm/chat_model.py
+++ b/rag/llm/chat_model.py
@@ -26,18 +26,20 @@ from typing import Any, Protocol
from urllib.parse import urljoin
import json_repair
+import litellm
import openai
import requests
-from dashscope import Generation
from ollama import Client
from openai import OpenAI
from openai.lib.azure import AzureOpenAI
from strenum import StrEnum
from zhipuai import ZhipuAI
+from rag.llm import FACTORY_DEFAULT_BASE_URL, LITELLM_PROVIDER_PREFIX, SupportedLiteLLMProvider
from rag.nlp import is_chinese, is_english
from rag.utils import num_tokens_from_string
+
# Error message constants
class LLMErrorCode(StrEnum):
ERROR_RATE_LIMIT = "RATE_LIMIT_EXCEEDED"
@@ -58,6 +60,7 @@ class ReActMode(StrEnum):
FUNCTION_CALL = "function_call"
REACT = "react"
+
ERROR_PREFIX = "**ERROR**"
LENGTH_NOTIFICATION_CN = "······\n由于大模型的上下文窗口大小限制,回答已经被大模型截断。"
LENGTH_NOTIFICATION_EN = "...\nThe answer is truncated by your chosen LLM due to its limitation on context length."
@@ -113,7 +116,7 @@ class Base(ABC):
def _chat(self, history, gen_conf, **kwargs):
logging.info("[HISTORY]" + json.dumps(history, ensure_ascii=False, indent=2))
- if self.model_name.lower().find("qwen3") >=0:
+ if self.model_name.lower().find("qwen3") >= 0:
kwargs["extra_body"] = {"enable_thinking": False}
response = self.client.chat.completions.create(model=self.model_name, messages=history, **gen_conf, **kwargs)
@@ -167,7 +170,7 @@ class Base(ABC):
error_code = LLMErrorCode.ERROR_MAX_RETRIES
# Check if it's a rate limit error or server error and not the last attempt
- should_retry = (error_code == LLMErrorCode.ERROR_RATE_LIMIT or error_code == LLMErrorCode.ERROR_SERVER)
+ should_retry = error_code == LLMErrorCode.ERROR_RATE_LIMIT or error_code == LLMErrorCode.ERROR_SERVER
if not should_retry:
return f"{ERROR_PREFIX}: {error_code} - {str(e)}"
@@ -176,11 +179,7 @@ class Base(ABC):
time.sleep(delay)
def _verbose_tool_use(self, name, args, res):
- return "" + json.dumps({
- "name": name,
- "args": args,
- "result": res
- }, ensure_ascii=False, indent=2) + ""
+ return "" + json.dumps({"name": name, "args": args, "result": res}, ensure_ascii=False, indent=2) + ""
def _append_history(self, hist, tool_call, tool_res):
hist.append(
@@ -213,7 +212,7 @@ class Base(ABC):
self.toolcall_session = toolcall_session
self.tools = tools
- def chat_with_tools(self, system: str, history: list, gen_conf: dict={}):
+ def chat_with_tools(self, system: str, history: list, gen_conf: dict = {}):
gen_conf = self._clean_conf(gen_conf)
if system:
history.insert(0, {"role": "system", "content": system})
@@ -225,7 +224,7 @@ class Base(ABC):
for attempt in range(self.max_retries + 1):
history = hist
try:
- for _ in range(self.max_rounds+1):
+ for _ in range(self.max_rounds + 1):
logging.info(f"{self.tools=}")
response = self.client.chat.completions.create(model=self.model_name, messages=history, tools=self.tools, tool_choice="auto", **gen_conf)
tk_count += self.total_token_count(response)
@@ -255,7 +254,7 @@ class Base(ABC):
history.append({"role": "tool", "tool_call_id": tool_call.id, "content": f"Tool call error: \n{tool_call}\nException:\n" + str(e)})
ans += self._verbose_tool_use(name, {}, str(e))
- logging.warning( f"Exceed max rounds: {self.max_rounds}")
+ logging.warning(f"Exceed max rounds: {self.max_rounds}")
history.append({"role": "user", "content": f"Exceed max rounds: {self.max_rounds}"})
response, token_count = self._chat(history, gen_conf)
ans += response
@@ -297,7 +296,7 @@ class Base(ABC):
return final_tool_calls
- def chat_streamly_with_tools(self, system: str, history: list, gen_conf: dict={}):
+ def chat_streamly_with_tools(self, system: str, history: list, gen_conf: dict = {}):
gen_conf = self._clean_conf(gen_conf)
tools = self.tools
if system:
@@ -309,7 +308,7 @@ class Base(ABC):
for attempt in range(self.max_retries + 1):
history = hist
try:
- for _ in range(self.max_rounds+1):
+ for _ in range(self.max_rounds + 1):
reasoning_start = False
logging.info(f"{tools=}")
response = self.client.chat.completions.create(model=self.model_name, messages=history, stream=True, tools=tools, tool_choice="auto", **gen_conf)
@@ -373,7 +372,7 @@ class Base(ABC):
history.append({"role": "tool", "tool_call_id": tool_call.id, "content": f"Tool call error: \n{tool_call}\nException:\n" + str(e)})
yield self._verbose_tool_use(name, {}, str(e))
- logging.warning( f"Exceed max rounds: {self.max_rounds}")
+ logging.warning(f"Exceed max rounds: {self.max_rounds}")
history.append({"role": "user", "content": f"Exceed max rounds: {self.max_rounds}"})
response = self.client.chat.completions.create(model=self.model_name, messages=history, stream=True, **gen_conf)
for resp in response:
@@ -402,7 +401,7 @@ class Base(ABC):
assert False, "Shouldn't be here."
- def chat_streamly(self, system, history, gen_conf: dict={}, **kwargs):
+ def chat_streamly(self, system, history, gen_conf: dict = {}, **kwargs):
if system:
history.insert(0, {"role": "system", "content": system})
gen_conf = self._clean_conf(gen_conf)
@@ -474,15 +473,6 @@ class GptTurbo(Base):
super().__init__(key, model_name, base_url, **kwargs)
-class MoonshotChat(Base):
- _FACTORY_NAME = "Moonshot"
-
- def __init__(self, key, model_name="moonshot-v1-8k", base_url="https://api.moonshot.cn/v1", **kwargs):
- if not base_url:
- base_url = "https://api.moonshot.cn/v1"
- super().__init__(key, model_name, base_url)
-
-
class XinferenceChat(Base):
_FACTORY_NAME = "Xinference"
@@ -513,15 +503,6 @@ class ModelScopeChat(Base):
super().__init__(key, model_name.split("___")[0], base_url, **kwargs)
-class DeepSeekChat(Base):
- _FACTORY_NAME = "DeepSeek"
-
- def __init__(self, key, model_name="deepseek-chat", base_url="https://api.deepseek.com/v1", **kwargs):
- if not base_url:
- base_url = "https://api.deepseek.com/v1"
- super().__init__(key, model_name, base_url, **kwargs)
-
-
class AzureChat(Base):
_FACTORY_NAME = "Azure-OpenAI"
@@ -608,26 +589,6 @@ class BaiChuanChat(Base):
yield total_tokens
-class xAIChat(Base):
- _FACTORY_NAME = "xAI"
-
- def __init__(self, key, model_name="grok-3", base_url=None, **kwargs):
- if not base_url:
- base_url = "https://api.x.ai/v1"
- super().__init__(key, model_name, base_url=base_url, **kwargs)
- return
-
-
-class QWenChat(Base):
- _FACTORY_NAME = "Tongyi-Qianwen"
-
- def __init__(self, key, model_name=Generation.Models.qwen_turbo, base_url=None, **kwargs):
- if not base_url:
- base_url = "https://dashscope.aliyuncs.com/compatible-mode/v1"
- super().__init__(key, model_name, base_url=base_url, **kwargs)
- return
-
-
class ZhipuChat(Base):
_FACTORY_NAME = "ZHIPU-AI"
@@ -973,217 +934,6 @@ class MistralChat(Base):
yield total_tokens
-class BedrockChat(Base):
- _FACTORY_NAME = "Bedrock"
-
- def __init__(self, key, model_name, base_url=None, **kwargs):
- super().__init__(key, model_name, base_url=base_url, **kwargs)
-
- import boto3
-
- self.bedrock_ak = json.loads(key).get("bedrock_ak", "")
- self.bedrock_sk = json.loads(key).get("bedrock_sk", "")
- self.bedrock_region = json.loads(key).get("bedrock_region", "")
- self.model_name = model_name
-
- if self.bedrock_ak == "" or self.bedrock_sk == "" or self.bedrock_region == "":
- # Try to create a client using the default credentials (AWS_PROFILE, AWS_DEFAULT_REGION, etc.)
- self.client = boto3.client("bedrock-runtime")
- else:
- self.client = boto3.client(service_name="bedrock-runtime", region_name=self.bedrock_region, aws_access_key_id=self.bedrock_ak, aws_secret_access_key=self.bedrock_sk)
-
- def _clean_conf(self, gen_conf):
- for k in list(gen_conf.keys()):
- if k not in ["temperature"]:
- del gen_conf[k]
- return gen_conf
-
- def _chat(self, history, gen_conf={}, **kwargs):
- system = history[0]["content"] if history and history[0]["role"] == "system" else ""
- hist = []
- for item in history:
- if item["role"] == "system":
- continue
- hist.append(deepcopy(item))
- if not isinstance(hist[-1]["content"], list) and not isinstance(hist[-1]["content"], tuple):
- hist[-1]["content"] = [{"text": hist[-1]["content"]}]
- # Send the message to the model, using a basic inference configuration.
- response = self.client.converse(
- modelId=self.model_name,
- messages=hist,
- inferenceConfig=gen_conf,
- system=[{"text": (system if system else "Answer the user's message.")}],
- )
-
- # Extract and print the response text.
- ans = response["output"]["message"]["content"][0]["text"]
- return ans, num_tokens_from_string(ans)
-
- def chat_streamly(self, system, history, gen_conf={}, **kwargs):
- from botocore.exceptions import ClientError
-
- for k in list(gen_conf.keys()):
- if k not in ["temperature"]:
- del gen_conf[k]
- for item in history:
- if not isinstance(item["content"], list) and not isinstance(item["content"], tuple):
- item["content"] = [{"text": item["content"]}]
-
- if self.model_name.split(".")[0] == "ai21":
- try:
- response = self.client.converse(modelId=self.model_name, messages=history, inferenceConfig=gen_conf, system=[{"text": (system if system else "Answer the user's message.")}])
- ans = response["output"]["message"]["content"][0]["text"]
- return ans, num_tokens_from_string(ans)
-
- except (ClientError, Exception) as e:
- return f"ERROR: Can't invoke '{self.model_name}'. Reason: {e}", 0
-
- ans = ""
- try:
- # Send the message to the model, using a basic inference configuration.
- streaming_response = self.client.converse_stream(
- modelId=self.model_name, messages=history, inferenceConfig=gen_conf, system=[{"text": (system if system else "Answer the user's message.")}]
- )
-
- # Extract and print the streamed response text in real-time.
- for resp in streaming_response["stream"]:
- if "contentBlockDelta" in resp:
- ans = resp["contentBlockDelta"]["delta"]["text"]
- yield ans
-
- except (ClientError, Exception) as e:
- yield ans + f"ERROR: Can't invoke '{self.model_name}'. Reason: {e}"
-
- yield num_tokens_from_string(ans)
-
-
-class GeminiChat(Base):
- _FACTORY_NAME = "Gemini"
-
- def __init__(self, key, model_name, base_url=None, **kwargs):
- super().__init__(key, model_name, base_url=base_url, **kwargs)
-
- from google.generativeai import GenerativeModel, client
-
- client.configure(api_key=key)
- _client = client.get_default_generative_client()
- self.model_name = "models/" + model_name
- self.model = GenerativeModel(model_name=self.model_name)
- self.model._client = _client
-
- def _clean_conf(self, gen_conf):
- for k in list(gen_conf.keys()):
- if k not in ["temperature", "top_p", "max_tokens"]:
- del gen_conf[k]
- # if max_tokens exists, rename it to max_output_tokens to match Gemini's API
- if k == "max_tokens":
- gen_conf["max_output_tokens"] = gen_conf.pop("max_tokens")
- return gen_conf
-
- def _chat(self, history, gen_conf={}, **kwargs):
- from google.generativeai.types import content_types
-
- system = history[0]["content"] if history and history[0]["role"] == "system" else ""
- hist = []
- for item in history:
- if item["role"] == "system":
- continue
- hist.append(deepcopy(item))
- item = hist[-1]
- if "role" in item and item["role"] == "assistant":
- item["role"] = "model"
- if "role" in item and item["role"] == "system":
- item["role"] = "user"
- if "content" in item:
- item["parts"] = item.pop("content")
-
- if system:
- self.model._system_instruction = content_types.to_content(system)
- retry_count = 0
- max_retries = 3
- while retry_count < max_retries:
- try:
- response = self.model.generate_content(hist, generation_config=gen_conf)
- ans = response.text
- return ans, response.usage_metadata.total_token_count
- except Exception as e:
- retry_count += 1
- if retry_count >= max_retries:
- raise e
- else:
- import time
- time.sleep(50)
-
- def chat_streamly(self, system, history, gen_conf={}, **kwargs):
- from google.generativeai.types import content_types
-
- gen_conf = self._clean_conf(gen_conf)
- if system:
- self.model._system_instruction = content_types.to_content(system)
- for item in history:
- if "role" in item and item["role"] == "assistant":
- item["role"] = "model"
- if "content" in item:
- item["parts"] = item.pop("content")
- ans = ""
- try:
- response = self.model.generate_content(history, generation_config=gen_conf, stream=True)
- for resp in response:
- ans = resp.text
- yield ans
-
- yield response._chunks[-1].usage_metadata.total_token_count
- except Exception as e:
- yield ans + "\n**ERROR**: " + str(e)
-
- yield 0
-
-
-class GroqChat(Base):
- _FACTORY_NAME = "Groq"
-
- def __init__(self, key, model_name, base_url=None, **kwargs):
- super().__init__(key, model_name, base_url=base_url, **kwargs)
-
- from groq import Groq
-
- self.client = Groq(api_key=key)
- self.model_name = model_name
-
- def _clean_conf(self, gen_conf):
- for k in list(gen_conf.keys()):
- if k not in ["temperature", "top_p", "max_tokens"]:
- del gen_conf[k]
- return gen_conf
-
- def chat_streamly(self, system, history, gen_conf={}, **kwargs):
- if system:
- history.insert(0, {"role": "system", "content": system})
- for k in list(gen_conf.keys()):
- if k not in ["temperature", "top_p", "max_tokens"]:
- del gen_conf[k]
- ans = ""
- total_tokens = 0
- try:
- response = self.client.chat.completions.create(model=self.model_name, messages=history, stream=True, **gen_conf)
- for resp in response:
- if not resp.choices or not resp.choices[0].delta.content:
- continue
- ans = resp.choices[0].delta.content
- total_tokens += 1
- if resp.choices[0].finish_reason == "length":
- if is_chinese(ans):
- ans += LENGTH_NOTIFICATION_CN
- else:
- ans += LENGTH_NOTIFICATION_EN
- yield ans
-
- except Exception as e:
- yield ans + "\n**ERROR**: " + str(e)
-
- yield total_tokens
-
-
## openrouter
class OpenRouterChat(Base):
_FACTORY_NAME = "OpenRouter"
@@ -1203,15 +953,6 @@ class StepFunChat(Base):
super().__init__(key, model_name, base_url, **kwargs)
-class NvidiaChat(Base):
- _FACTORY_NAME = "NVIDIA"
-
- def __init__(self, key, model_name, base_url="https://integrate.api.nvidia.com/v1", **kwargs):
- if not base_url:
- base_url = "https://integrate.api.nvidia.com/v1"
- super().__init__(key, model_name, base_url, **kwargs)
-
-
class LmStudioChat(Base):
_FACTORY_NAME = "LM-Studio"
@@ -1243,83 +984,6 @@ class PPIOChat(Base):
super().__init__(key, model_name, base_url, **kwargs)
-class CoHereChat(Base):
- _FACTORY_NAME = "Cohere"
-
- def __init__(self, key, model_name, base_url=None, **kwargs):
- super().__init__(key, model_name, base_url=base_url, **kwargs)
-
- from cohere import Client
-
- self.client = Client(api_key=key)
- self.model_name = model_name
-
- def _clean_conf(self, gen_conf):
- if "max_tokens" in gen_conf:
- del gen_conf["max_tokens"]
- if "top_p" in gen_conf:
- gen_conf["p"] = gen_conf.pop("top_p")
- if "frequency_penalty" in gen_conf and "presence_penalty" in gen_conf:
- gen_conf.pop("presence_penalty")
- return gen_conf
-
- def _chat(self, history, gen_conf):
- hist = []
- for item in history:
- hist.append(deepcopy(item))
- item = hist[-1]
- if "role" in item and item["role"] == "user":
- item["role"] = "USER"
- if "role" in item and item["role"] == "assistant":
- item["role"] = "CHATBOT"
- if "content" in item:
- item["message"] = item.pop("content")
- mes = hist.pop()["message"]
- response = self.client.chat(model=self.model_name, chat_history=hist, message=mes, **gen_conf)
- ans = response.text
- if response.finish_reason == "MAX_TOKENS":
- ans += "...\nFor the content length reason, it stopped, continue?" if is_english([ans]) else "······\n由于长度的原因,回答被截断了,要继续吗?"
- return (
- ans,
- response.meta.tokens.input_tokens + response.meta.tokens.output_tokens,
- )
-
- def chat_streamly(self, system, history, gen_conf={}, **kwargs):
- if system:
- history.insert(0, {"role": "system", "content": system})
- if "max_tokens" in gen_conf:
- del gen_conf["max_tokens"]
- if "top_p" in gen_conf:
- gen_conf["p"] = gen_conf.pop("top_p")
- if "frequency_penalty" in gen_conf and "presence_penalty" in gen_conf:
- gen_conf.pop("presence_penalty")
- for item in history:
- if "role" in item and item["role"] == "user":
- item["role"] = "USER"
- if "role" in item and item["role"] == "assistant":
- item["role"] = "CHATBOT"
- if "content" in item:
- item["message"] = item.pop("content")
- mes = history.pop()["message"]
- ans = ""
- total_tokens = 0
- try:
- response = self.client.chat_stream(model=self.model_name, chat_history=history, message=mes, **gen_conf)
- for resp in response:
- if resp.event_type == "text-generation":
- ans = resp.text
- total_tokens += num_tokens_from_string(resp.text)
- elif resp.event_type == "stream-end":
- if resp.finish_reason == "MAX_TOKENS":
- ans += "...\nFor the content length reason, it stopped, continue?" if is_english([ans]) else "······\n由于长度的原因,回答被截断了,要继续吗?"
- yield ans
-
- except Exception as e:
- yield ans + "\n**ERROR**: " + str(e)
-
- yield total_tokens
-
-
class LeptonAIChat(Base):
_FACTORY_NAME = "LeptonAI"
@@ -1329,15 +993,6 @@ class LeptonAIChat(Base):
super().__init__(key, model_name, base_url, **kwargs)
-class TogetherAIChat(Base):
- _FACTORY_NAME = "TogetherAI"
-
- def __init__(self, key, model_name, base_url="https://api.together.xyz/v1", **kwargs):
- if not base_url:
- base_url = "https://api.together.xyz/v1"
- super().__init__(key, model_name, base_url, **kwargs)
-
-
class PerfXCloudChat(Base):
_FACTORY_NAME = "PerfXCloud"
@@ -1581,15 +1236,6 @@ class BaiduYiyanChat(Base):
yield total_tokens
-class AnthropicChat(Base):
- _FACTORY_NAME = "Anthropic"
-
- def __init__(self, key, model_name, base_url="https://api.anthropic.com/v1/", **kwargs):
- if not base_url:
- base_url = "https://api.anthropic.com/v1/"
- super().__init__(key, model_name, base_url=base_url, **kwargs)
-
-
class GoogleChat(Base):
_FACTORY_NAME = "Google Cloud"
@@ -1738,14 +1384,7 @@ class GPUStackChat(Base):
raise ValueError("Local llm url cannot be None")
base_url = urljoin(base_url, "v1")
super().__init__(key, model_name, base_url, **kwargs)
-class DeepInfraChat(Base):
- _FACTORY_NAME = "DeepInfra"
- def __init__(self, key, model_name, base_url="https://api.deepinfra.com/v1/openai", **kwargs):
- if not base_url:
- base_url = "https://api.deepinfra.com/v1/openai"
- super().__init__(key, model_name, base_url, **kwargs)
-
class Ai302Chat(Base):
_FACTORY_NAME = "302.AI"
@@ -1754,3 +1393,489 @@ class Ai302Chat(Base):
if not base_url:
base_url = "https://api.302.ai/v1"
super().__init__(key, model_name, base_url, **kwargs)
+
+
+class LiteLLMBase(ABC):
+ _FACTORY_NAME = ["Tongyi-Qianwen", "Bedrock", "Moonshot", "xAI", "DeepInfra", "Groq", "Cohere", "Gemini", "DeepSeek", "NVIDIA", "TogetherAI", "Anthropic"]
+
+ def __init__(self, key, model_name, base_url=None, **kwargs):
+ self.timeout = int(os.environ.get("LM_TIMEOUT_SECONDS", 600))
+ self.provider = kwargs.get("provider", "")
+ self.prefix = LITELLM_PROVIDER_PREFIX.get(self.provider, "")
+ self.model_name = f"{self.prefix}{model_name}"
+ self.api_key = key
+ self.base_url = base_url or FACTORY_DEFAULT_BASE_URL.get(self.provider, "")
+ # Configure retry parameters
+ self.max_retries = kwargs.get("max_retries", int(os.environ.get("LLM_MAX_RETRIES", 5)))
+ self.base_delay = kwargs.get("retry_interval", float(os.environ.get("LLM_BASE_DELAY", 2.0)))
+ self.max_rounds = kwargs.get("max_rounds", 5)
+ self.is_tools = False
+ self.tools = []
+ self.toolcall_sessions = {}
+
+ # Factory specific fields
+ if self.provider == SupportedLiteLLMProvider.Bedrock:
+ self.bedrock_ak = json.loads(key).get("bedrock_ak", "")
+ self.bedrock_sk = json.loads(key).get("bedrock_sk", "")
+ self.bedrock_region = json.loads(key).get("bedrock_region", "")
+
+ def _get_delay(self):
+ """Calculate retry delay time"""
+ return self.base_delay * random.uniform(10, 150)
+
+ def _classify_error(self, error):
+ """Classify error based on error message content"""
+ error_str = str(error).lower()
+
+ keywords_mapping = [
+ (["quota", "capacity", "credit", "billing", "balance", "欠费"], LLMErrorCode.ERROR_QUOTA),
+ (["rate limit", "429", "tpm limit", "too many requests", "requests per minute"], LLMErrorCode.ERROR_RATE_LIMIT),
+ (["auth", "key", "apikey", "401", "forbidden", "permission"], LLMErrorCode.ERROR_AUTHENTICATION),
+ (["invalid", "bad request", "400", "format", "malformed", "parameter"], LLMErrorCode.ERROR_INVALID_REQUEST),
+ (["server", "503", "502", "504", "500", "unavailable"], LLMErrorCode.ERROR_SERVER),
+ (["timeout", "timed out"], LLMErrorCode.ERROR_TIMEOUT),
+ (["connect", "network", "unreachable", "dns"], LLMErrorCode.ERROR_CONNECTION),
+ (["filter", "content", "policy", "blocked", "safety", "inappropriate"], LLMErrorCode.ERROR_CONTENT_FILTER),
+ (["model", "not found", "does not exist", "not available"], LLMErrorCode.ERROR_MODEL),
+ (["max rounds"], LLMErrorCode.ERROR_MODEL),
+ ]
+ for words, code in keywords_mapping:
+ if re.search("({})".format("|".join(words)), error_str):
+ return code
+
+ return LLMErrorCode.ERROR_GENERIC
+
+ def _clean_conf(self, gen_conf):
+ if "max_tokens" in gen_conf:
+ del gen_conf["max_tokens"]
+ return gen_conf
+
+ def _chat(self, history, gen_conf, **kwargs):
+ logging.info("[HISTORY]" + json.dumps(history, ensure_ascii=False, indent=2))
+ if self.model_name.lower().find("qwen3") >= 0:
+ kwargs["extra_body"] = {"enable_thinking": False}
+
+ completion_args = self._construct_completion_args(history=history, **gen_conf)
+ response = litellm.completion(
+ **completion_args,
+ drop_params=True,
+ timeout=self.timeout,
+ )
+ # response = self.client.chat.completions.create(model=self.model_name, messages=history, **gen_conf, **kwargs)
+
+ if any([not response.choices, not response.choices[0].message, not response.choices[0].message.content]):
+ return "", 0
+ ans = response.choices[0].message.content.strip()
+ if response.choices[0].finish_reason == "length":
+ ans = self._length_stop(ans)
+
+ return ans, self.total_token_count(response)
+
+ def _chat_streamly(self, history, gen_conf, **kwargs):
+ logging.info("[HISTORY STREAMLY]" + json.dumps(history, ensure_ascii=False, indent=4))
+ reasoning_start = False
+
+ completion_args = self._construct_completion_args(history=history, **gen_conf)
+ stop = kwargs.get("stop")
+ if stop:
+ completion_args["stop"] = stop
+ response = litellm.completion(
+ **completion_args,
+ drop_params=True,
+ timeout=self.timeout,
+ )
+
+ for resp in response:
+ if not hasattr(resp, "choices") or not resp.choices:
+ continue
+
+ delta = resp.choices[0].delta
+ if not hasattr(delta, "content") or delta.content is None:
+ delta.content = ""
+
+ if kwargs.get("with_reasoning", True) and hasattr(delta, "reasoning_content") and delta.reasoning_content:
+ ans = ""
+ if not reasoning_start:
+ reasoning_start = True
+ ans = ""
+ ans += delta.reasoning_content + ""
+ else:
+ reasoning_start = False
+ ans = delta.content
+
+ tol = self.total_token_count(resp)
+ if not tol:
+ tol = num_tokens_from_string(delta.content)
+
+ finish_reason = resp.choices[0].finish_reason if hasattr(resp.choices[0], "finish_reason") else ""
+ if finish_reason == "length":
+ if is_chinese(ans):
+ ans += LENGTH_NOTIFICATION_CN
+ else:
+ ans += LENGTH_NOTIFICATION_EN
+
+ yield ans, tol
+
+ def _length_stop(self, ans):
+ if is_chinese([ans]):
+ return ans + LENGTH_NOTIFICATION_CN
+ return ans + LENGTH_NOTIFICATION_EN
+
+ def _exceptions(self, e, attempt):
+ logging.exception("OpenAI chat_with_tools")
+ # Classify the error
+ error_code = self._classify_error(e)
+ if attempt == self.max_retries:
+ error_code = LLMErrorCode.ERROR_MAX_RETRIES
+
+ # Check if it's a rate limit error or server error and not the last attempt
+ should_retry = error_code == LLMErrorCode.ERROR_RATE_LIMIT or error_code == LLMErrorCode.ERROR_SERVER
+ if not should_retry:
+ return f"{ERROR_PREFIX}: {error_code} - {str(e)}"
+
+ delay = self._get_delay()
+ logging.warning(f"Error: {error_code}. Retrying in {delay:.2f} seconds... (Attempt {attempt + 1}/{self.max_retries})")
+ time.sleep(delay)
+
+ def _verbose_tool_use(self, name, args, res):
+ return "" + json.dumps({"name": name, "args": args, "result": res}, ensure_ascii=False, indent=2) + ""
+
+ def _append_history(self, hist, tool_call, tool_res):
+ hist.append(
+ {
+ "role": "assistant",
+ "tool_calls": [
+ {
+ "index": tool_call.index,
+ "id": tool_call.id,
+ "function": {
+ "name": tool_call.function.name,
+ "arguments": tool_call.function.arguments,
+ },
+ "type": "function",
+ },
+ ],
+ }
+ )
+ try:
+ if isinstance(tool_res, dict):
+ tool_res = json.dumps(tool_res, ensure_ascii=False)
+ finally:
+ hist.append({"role": "tool", "tool_call_id": tool_call.id, "content": str(tool_res)})
+ return hist
+
+ def bind_tools(self, toolcall_session, tools):
+ if not (toolcall_session and tools):
+ return
+ self.is_tools = True
+ self.toolcall_session = toolcall_session
+ self.tools = tools
+
+ def _construct_completion_args(self, history, **kwargs):
+ completion_args = {
+ "model": self.model_name,
+ "messages": history,
+ "stream": False,
+ "tools": self.tools,
+ "tool_choice": "auto",
+ "api_key": self.api_key,
+ **kwargs,
+ }
+ if self.provider in SupportedLiteLLMProvider:
+ completion_args.update({"api_base": self.base_url})
+ elif self.provider == SupportedLiteLLMProvider.Bedrock:
+ completion_args.pop("api_key", None)
+ completion_args.pop("api_base", None)
+ completion_args.update(
+ {
+ "aws_access_key_id": self.bedrock_ak,
+ "aws_secret_access_key": self.bedrock_sk,
+ "aws_region_name": self.bedrock_region,
+ }
+ )
+ return completion_args
+
+ def chat_with_tools(self, system: str, history: list, gen_conf: dict = {}):
+ gen_conf = self._clean_conf(gen_conf)
+ if system:
+ history.insert(0, {"role": "system", "content": system})
+
+ ans = ""
+ tk_count = 0
+ hist = deepcopy(history)
+
+ # Implement exponential backoff retry strategy
+ for attempt in range(self.max_retries + 1):
+ history = deepcopy(hist) # deepcopy is required here
+ try:
+ for _ in range(self.max_rounds + 1):
+ logging.info(f"{self.tools=}")
+
+ completion_args = self._construct_completion_args(history=history, **gen_conf)
+ response = litellm.completion(
+ **completion_args,
+ drop_params=True,
+ timeout=self.timeout,
+ )
+
+ tk_count += self.total_token_count(response)
+
+ if not hasattr(response, "choices") or not response.choices or not response.choices[0].message:
+ raise Exception(f"500 response structure error. Response: {response}")
+
+ message = response.choices[0].message
+
+ if not hasattr(message, "tool_calls") or not message.tool_calls:
+ if hasattr(message, "reasoning_content") and message.reasoning_content:
+ ans += f"{message.reasoning_content}"
+ ans += message.content or ""
+ if response.choices[0].finish_reason == "length":
+ ans = self._length_stop(ans)
+ return ans, tk_count
+
+ for tool_call in message.tool_calls:
+ logging.info(f"Response {tool_call=}")
+ name = tool_call.function.name
+ try:
+ args = json_repair.loads(tool_call.function.arguments)
+ tool_response = self.toolcall_session.tool_call(name, args)
+ history = self._append_history(history, tool_call, tool_response)
+ ans += self._verbose_tool_use(name, args, tool_response)
+ except Exception as e:
+ logging.exception(msg=f"Wrong JSON argument format in LLM tool call response: {tool_call}")
+ history.append({"role": "tool", "tool_call_id": tool_call.id, "content": f"Tool call error: \n{tool_call}\nException:\n" + str(e)})
+ ans += self._verbose_tool_use(name, {}, str(e))
+
+ logging.warning(f"Exceed max rounds: {self.max_rounds}")
+ history.append({"role": "user", "content": f"Exceed max rounds: {self.max_rounds}"})
+
+ response, token_count = self._chat(history, gen_conf)
+ ans += response
+ tk_count += token_count
+ return ans, tk_count
+
+ except Exception as e:
+ e = self._exceptions(e, attempt)
+ if e:
+ return e, tk_count
+
+ assert False, "Shouldn't be here."
+
+ def chat(self, system, history, gen_conf={}, **kwargs):
+ if system:
+ history.insert(0, {"role": "system", "content": system})
+ gen_conf = self._clean_conf(gen_conf)
+
+ # Implement exponential backoff retry strategy
+ for attempt in range(self.max_retries + 1):
+ try:
+ response = self._chat(history, gen_conf, **kwargs)
+ return response
+ except Exception as e:
+ e = self._exceptions(e, attempt)
+ if e:
+ return e, 0
+ assert False, "Shouldn't be here."
+
+ def _wrap_toolcall_message(self, stream):
+ final_tool_calls = {}
+
+ for chunk in stream:
+ for tool_call in chunk.choices[0].delta.tool_calls or []:
+ index = tool_call.index
+
+ if index not in final_tool_calls:
+ final_tool_calls[index] = tool_call
+
+ final_tool_calls[index].function.arguments += tool_call.function.arguments
+
+ return final_tool_calls
+
+ def chat_streamly_with_tools(self, system: str, history: list, gen_conf: dict = {}):
+ gen_conf = self._clean_conf(gen_conf)
+ tools = self.tools
+ if system:
+ history.insert(0, {"role": "system", "content": system})
+
+ total_tokens = 0
+ hist = deepcopy(history)
+
+ # Implement exponential backoff retry strategy
+ for attempt in range(self.max_retries + 1):
+ history = deepcopy(hist) # deepcopy is required here
+ try:
+ for _ in range(self.max_rounds + 1):
+ reasoning_start = False
+ logging.info(f"{tools=}")
+
+ completion_args = self._construct_completion_args(history=history, **gen_conf)
+ response = litellm.completion(
+ **completion_args,
+ drop_params=True,
+ timeout=self.timeout,
+ )
+
+ final_tool_calls = {}
+ answer = ""
+
+ for resp in response:
+ if not hasattr(resp, "choices") or not resp.choices:
+ continue
+
+ delta = resp.choices[0].delta
+
+ if hasattr(delta, "tool_calls") and delta.tool_calls:
+ for tool_call in delta.tool_calls:
+ index = tool_call.index
+ if index not in final_tool_calls:
+ if not tool_call.function.arguments:
+ tool_call.function.arguments = ""
+ final_tool_calls[index] = tool_call
+ else:
+ final_tool_calls[index].function.arguments += tool_call.function.arguments or ""
+ continue
+
+ if not hasattr(delta, "content") or delta.content is None:
+ delta.content = ""
+
+ if hasattr(delta, "reasoning_content") and delta.reasoning_content:
+ ans = ""
+ if not reasoning_start:
+ reasoning_start = True
+ ans = ""
+ ans += delta.reasoning_content + ""
+ yield ans
+ else:
+ reasoning_start = False
+ answer += delta.content
+ yield delta.content
+
+ tol = self.total_token_count(resp)
+ if not tol:
+ total_tokens += num_tokens_from_string(delta.content)
+ else:
+ total_tokens += tol
+
+ finish_reason = getattr(resp.choices[0], "finish_reason", "")
+ if finish_reason == "length":
+ yield self._length_stop("")
+
+ if answer:
+ yield total_tokens
+ return
+
+ for tool_call in final_tool_calls.values():
+ name = tool_call.function.name
+ try:
+ args = json_repair.loads(tool_call.function.arguments)
+ yield self._verbose_tool_use(name, args, "Begin to call...")
+ tool_response = self.toolcall_session.tool_call(name, args)
+ history = self._append_history(history, tool_call, tool_response)
+ yield self._verbose_tool_use(name, args, tool_response)
+ except Exception as e:
+ logging.exception(msg=f"Wrong JSON argument format in LLM tool call response: {tool_call}")
+ history.append(
+ {
+ "role": "tool",
+ "tool_call_id": tool_call.id,
+ "content": f"Tool call error: \n{tool_call}\nException:\n{str(e)}",
+ }
+ )
+ yield self._verbose_tool_use(name, {}, str(e))
+
+ logging.warning(f"Exceed max rounds: {self.max_rounds}")
+ history.append({"role": "user", "content": f"Exceed max rounds: {self.max_rounds}"})
+
+ completion_args = self._construct_completion_args(history=history, **gen_conf)
+ response = litellm.completion(
+ **completion_args,
+ drop_params=True,
+ timeout=self.timeout,
+ )
+
+ for resp in response:
+ if not hasattr(resp, "choices") or not resp.choices:
+ continue
+ delta = resp.choices[0].delta
+ if not hasattr(delta, "content") or delta.content is None:
+ continue
+ tol = self.total_token_count(resp)
+ if not tol:
+ total_tokens += num_tokens_from_string(delta.content)
+ else:
+ total_tokens += tol
+ yield delta.content
+
+ yield total_tokens
+ return
+
+ except Exception as e:
+ e = self._exceptions(e, attempt)
+ if e:
+ yield e
+ yield total_tokens
+ return
+
+ assert False, "Shouldn't be here."
+
+ def chat_streamly(self, system, history, gen_conf: dict = {}, **kwargs):
+ if system:
+ history.insert(0, {"role": "system", "content": system})
+ gen_conf = self._clean_conf(gen_conf)
+ ans = ""
+ total_tokens = 0
+ try:
+ for delta_ans, tol in self._chat_streamly(history, gen_conf, **kwargs):
+ yield delta_ans
+ total_tokens += tol
+ except openai.APIError as e:
+ yield ans + "\n**ERROR**: " + str(e)
+
+ yield total_tokens
+
+ def total_token_count(self, resp):
+ try:
+ return resp.usage.total_tokens
+ except Exception:
+ pass
+ try:
+ return resp["usage"]["total_tokens"]
+ except Exception:
+ pass
+ return 0
+
+ def _calculate_dynamic_ctx(self, history):
+ """Calculate dynamic context window size"""
+
+ def count_tokens(text):
+ """Calculate token count for text"""
+ # Simple calculation: 1 token per ASCII character
+ # 2 tokens for non-ASCII characters (Chinese, Japanese, Korean, etc.)
+ total = 0
+ for char in text:
+ if ord(char) < 128: # ASCII characters
+ total += 1
+ else: # Non-ASCII characters (Chinese, Japanese, Korean, etc.)
+ total += 2
+ return total
+
+ # Calculate total tokens for all messages
+ total_tokens = 0
+ for message in history:
+ content = message.get("content", "")
+ # Calculate content tokens
+ content_tokens = count_tokens(content)
+ # Add role marker token overhead
+ role_tokens = 4
+ total_tokens += content_tokens + role_tokens
+
+ # Apply 1.2x buffer ratio
+ total_tokens_with_buffer = int(total_tokens * 1.2)
+
+ if total_tokens_with_buffer <= 8192:
+ ctx_size = 8192
+ else:
+ ctx_multiplier = (total_tokens_with_buffer // 8192) + 1
+ ctx_size = ctx_multiplier * 8192
+
+ return ctx_size
diff --git a/uv.lock b/uv.lock
index 731e47c3f..0ea2819a9 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1,4 +1,5 @@
version = 1
+revision = 1
requires-python = ">=3.10, <3.13"
resolution-markers = [
"python_full_version >= '3.12' and sys_platform == 'darwin'",
@@ -30,6 +31,15 @@ wheels = [
{ url = "https://mirrors.aliyun.com/pypi/packages/9f/1c/a17fb513aeb684fb83bef5f395910f53103ab30308bbdd77fd66d6698c46/accelerate-1.9.0-py3-none-any.whl", hash = "sha256:c24739a97ade1d54af4549a65f8b6b046adc87e2b3e4d6c66516e32c53d5a8f1" },
]
+[[package]]
+name = "aiofiles"
+version = "24.1.0"
+source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
+sdist = { url = "https://mirrors.aliyun.com/pypi/packages/0b/03/a88171e277e8caa88a4c77808c20ebb04ba74cc4681bf1e9416c862de237/aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c" }
+wheels = [
+ { url = "https://mirrors.aliyun.com/pypi/packages/a5/45/30bb92d442636f570cb5651bc661f52b610e2eec3f891a5dc3a4c3667db0/aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5" },
+]
+
[[package]]
name = "aiohappyeyeballs"
version = "2.6.1"
@@ -1028,24 +1038,29 @@ wheels = [
[[package]]
name = "crawl4ai"
-version = "0.3.8"
+version = "0.3.745"
source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
dependencies = [
+ { name = "aiofiles" },
{ name = "aiosqlite" },
{ name = "beautifulsoup4" },
+ { name = "colorama" },
{ name = "html2text" },
{ name = "litellm" },
{ name = "lxml" },
{ name = "numpy" },
{ name = "pillow" },
{ name = "playwright" },
- { name = "playwright-stealth" },
{ name = "python-dotenv" },
+ { name = "rank-bm25" },
{ name = "requests" },
+ { name = "snowballstemmer" },
+ { name = "tf-playwright-stealth" },
+ { name = "xxhash" },
]
-sdist = { url = "https://mirrors.aliyun.com/pypi/packages/1c/31/327598a0c2cc3cd13dcb786ab41e9638c4c100db1940c9345b1e4d953f39/crawl4ai-0.3.8.tar.gz", hash = "sha256:bacc97509ddbfa5e328e299538a27a4c7fc2317e3fd5ad707b04677e4fc23fc6" }
+sdist = { url = "https://mirrors.aliyun.com/pypi/packages/02/5a/919e64ff2977d7aa1b2cda4d45f16ff8996cd2c2dc1f55936fb6cd214222/crawl4ai-0.3.745.tar.gz", hash = "sha256:990396d57e10ae7ccabf35c34a317dbd8c59a3ceca475eac75320a8808334438" }
wheels = [
- { url = "https://mirrors.aliyun.com/pypi/packages/af/03/4d69b8d64b39096a721808a349199ca5d7989acf2177e270d15e6f82c356/Crawl4AI-0.3.8-py3-none-any.whl", hash = "sha256:aa19165440c32b667b7325c166d68b00a99375b09e3a7db929d3873064d5ef4f" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/ed/7e/ebe351a457140330b20b6d8289b8f243b21de6e6bce505cd15b230a83bcb/Crawl4AI-0.3.745-py3-none-any.whl", hash = "sha256:763e6aba80959e60e1fe70cb9d954a4cf257eb230af30f51fcd99ff641a7a88d" },
]
[[package]]
@@ -1175,9 +1190,6 @@ name = "datrie"
version = "0.8.2"
source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
sdist = { url = "https://mirrors.aliyun.com/pypi/packages/9d/fe/db74bd405d515f06657f11ad529878fd389576dca4812bea6f98d9b31574/datrie-0.8.2.tar.gz", hash = "sha256:525b08f638d5cf6115df6ccd818e5a01298cd230b2dac91c8ff2e6499d18765d" }
-wheels = [
- { url = "https://mirrors.aliyun.com/pypi/packages/44/02/53f0cf0bf0cd629ba6c2cc13f2f9db24323459e9c19463783d890a540a96/datrie-0.8.2-pp273-pypy_73-win32.whl", hash = "sha256:b07bd5fdfc3399a6dab86d6e35c72b1dbd598e80c97509c7c7518ab8774d3fda" },
-]
[[package]]
name = "debugpy"
@@ -1423,6 +1435,14 @@ wheels = [
{ url = "https://mirrors.aliyun.com/pypi/packages/36/f4/c6e662dade71f56cd2f3735141b265c3c79293c109549c1e6933b0651ffc/exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10" },
]
+[[package]]
+name = "fake-http-header"
+version = "0.3.5"
+source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
+wheels = [
+ { url = "https://mirrors.aliyun.com/pypi/packages/e3/0b/2849c87d9f13766e29c0a2f4d31681aa72e035016b251ab19d99bde7b592/fake_http_header-0.3.5-py3-none-any.whl", hash = "sha256:cd05f4bebf1b7e38b5f5c03d7fb820c0c17e87d9614fbee0afa39c32c7a2ad3c" },
+]
+
[[package]]
name = "fake-useragent"
version = "1.5.1"
@@ -1486,17 +1506,17 @@ name = "fastembed-gpu"
version = "0.3.6"
source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
dependencies = [
- { name = "huggingface-hub" },
- { name = "loguru" },
- { name = "mmh3" },
- { name = "numpy" },
- { name = "onnxruntime-gpu" },
- { name = "pillow" },
- { name = "pystemmer" },
- { name = "requests" },
- { name = "snowballstemmer" },
- { name = "tokenizers" },
- { name = "tqdm" },
+ { name = "huggingface-hub", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
+ { name = "loguru", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
+ { name = "mmh3", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
+ { name = "numpy", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
+ { name = "onnxruntime-gpu", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
+ { name = "pillow", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
+ { name = "pystemmer", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
+ { name = "requests", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
+ { name = "snowballstemmer", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
+ { name = "tokenizers", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
+ { name = "tqdm", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
]
sdist = { url = "https://mirrors.aliyun.com/pypi/packages/da/07/7336c7f3d7ee47f33b407eeb50f5eeb152889de538a52a8f1cc637192816/fastembed_gpu-0.3.6.tar.gz", hash = "sha256:ee2de8918b142adbbf48caaffec0c492f864d73c073eea5a3dcd0e8c1041c50d" }
wheels = [
@@ -2142,37 +2162,37 @@ wheels = [
[[package]]
name = "greenlet"
-version = "3.0.3"
+version = "3.2.3"
source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
-sdist = { url = "https://mirrors.aliyun.com/pypi/packages/17/14/3bddb1298b9a6786539ac609ba4b7c9c0842e12aa73aaa4d8d73ec8f8185/greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491" }
+sdist = { url = "https://mirrors.aliyun.com/pypi/packages/c9/92/bb85bd6e80148a4d2e0c59f7c0c2891029f8fd510183afc7d8d2feeed9b6/greenlet-3.2.3.tar.gz", hash = "sha256:8b0dd8ae4c0d6f5e54ee55ba935eeb3d735a9b58a8a1e5b5cbab64e01a39f365" }
wheels = [
- { url = "https://mirrors.aliyun.com/pypi/packages/a6/64/bea53c592e3e45799f7c8039a8ee7d6883c518eafef1fcae60beb776070f/greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a" },
- { url = "https://mirrors.aliyun.com/pypi/packages/a6/d6/408ad9603339db28ce334021b1403dfcfbcb7501a435d49698408d928de7/greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881" },
- { url = "https://mirrors.aliyun.com/pypi/packages/6c/90/5b14670653f7363fb3e1665f8da6d64bd4c31d53a796d09ef69f48be7273/greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b" },
- { url = "https://mirrors.aliyun.com/pypi/packages/ef/17/e8e72cabfb5a906c0d976d7fbcc88310df292beea0f816efbefdaf694284/greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a" },
- { url = "https://mirrors.aliyun.com/pypi/packages/1c/2f/64628f6ae48e05f585e0eb3fb7399b52e240ef99f602107b445bf6be23ef/greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83" },
- { url = "https://mirrors.aliyun.com/pypi/packages/24/35/945d5b10648fec9b20bcc6df8952d20bb3bba76413cd71c1fdbee98f5616/greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405" },
- { url = "https://mirrors.aliyun.com/pypi/packages/74/00/27e2da76b926e9b5a2c97d3f4c0baf1b7d8181209d3026c0171f621ae6c0/greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f" },
- { url = "https://mirrors.aliyun.com/pypi/packages/e1/65/506e0a80931170b0dac1a03d36b7fc299f3fa3576235b916718602fff2c3/greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb" },
- { url = "https://mirrors.aliyun.com/pypi/packages/a6/76/e1ee9f290bb0d46b09704c2fb0e609cae329eb308ad404c0ee6fa1ecb8a5/greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9" },
- { url = "https://mirrors.aliyun.com/pypi/packages/6e/20/68a278a6f93fa36e21cfc3d7599399a8a831225644eb3b6b18755cd3d6fc/greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61" },
- { url = "https://mirrors.aliyun.com/pypi/packages/21/b4/90e06e07c78513ab03855768200bdb35c8e764e805b3f14fb488e56f82dc/greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559" },
- { url = "https://mirrors.aliyun.com/pypi/packages/f6/a2/0ed21078039072f9dc738bbf3af12b103a84106b1385ac4723841f846ce7/greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e" },
- { url = "https://mirrors.aliyun.com/pypi/packages/42/11/42ad6b1104c357826bbee7d7b9e4f24dbd9fde94899a03efb004aab62963/greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33" },
- { url = "https://mirrors.aliyun.com/pypi/packages/bb/6b/384dee7e0121cbd1757bdc1824a5ee28e43d8d4e3f99aa59521f629442fe/greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379" },
- { url = "https://mirrors.aliyun.com/pypi/packages/c6/1f/12d5a6cc26e8b483c2e7975f9c22e088ac735c0d8dcb8a8f72d31a4e5f04/greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22" },
- { url = "https://mirrors.aliyun.com/pypi/packages/c7/ec/85b647e59e0f137c7792a809156f413e38379cf7f3f2e1353c37f4be4026/greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3" },
- { url = "https://mirrors.aliyun.com/pypi/packages/94/ed/1e5f4bca691a81700e5a88e86d6f0e538acb10188cd2cc17140e523255ef/greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d" },
- { url = "https://mirrors.aliyun.com/pypi/packages/47/79/26d54d7d700ef65b689fc2665a40846d13e834da0486674a8d4f0f371a47/greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728" },
- { url = "https://mirrors.aliyun.com/pypi/packages/a2/2f/461615adc53ba81e99471303b15ac6b2a6daa8d2a0f7f77fd15605e16d5b/greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be" },
- { url = "https://mirrors.aliyun.com/pypi/packages/e9/55/2c3cfa3cdbb940cf7321fbcf544f0e9c74898eed43bf678abf416812d132/greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e" },
- { url = "https://mirrors.aliyun.com/pypi/packages/38/77/efb21ab402651896c74f24a172eb4d7479f9f53898bd5e56b9e20bb24ffd/greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676" },
- { url = "https://mirrors.aliyun.com/pypi/packages/74/3a/92f188ace0190f0066dca3636cf1b09481d0854c46e92ec5e29c7cefe5b1/greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc" },
- { url = "https://mirrors.aliyun.com/pypi/packages/63/0f/847ed02cdfce10f0e6e3425cd054296bddb11a17ef1b34681fa01a055187/greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230" },
- { url = "https://mirrors.aliyun.com/pypi/packages/bd/37/56b0da468a85e7704f3b2bc045015301bdf4be2184a44868c71f6dca6fe2/greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf" },
- { url = "https://mirrors.aliyun.com/pypi/packages/7c/68/b5f4084c0a252d7e9c0d95fc1cfc845d08622037adb74e05be3a49831186/greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305" },
- { url = "https://mirrors.aliyun.com/pypi/packages/a4/fa/31e22345518adcd69d1d6ab5087a12c178aa7f3c51103f6d5d702199d243/greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6" },
- { url = "https://mirrors.aliyun.com/pypi/packages/53/80/3d94d5999b4179d91bcc93745d1b0815b073d61be79dd546b840d17adb18/greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/92/db/b4c12cff13ebac2786f4f217f06588bccd8b53d260453404ef22b121fc3a/greenlet-3.2.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:1afd685acd5597349ee6d7a88a8bec83ce13c106ac78c196ee9dde7c04fe87be" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/52/61/75b4abd8147f13f70986df2801bf93735c1bd87ea780d70e3b3ecda8c165/greenlet-3.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:761917cac215c61e9dc7324b2606107b3b292a8349bdebb31503ab4de3f559ac" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/35/aa/6894ae299d059d26254779a5088632874b80ee8cf89a88bca00b0709d22f/greenlet-3.2.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:a433dbc54e4a37e4fff90ef34f25a8c00aed99b06856f0119dcf09fbafa16392" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/30/64/e01a8261d13c47f3c082519a5e9dbf9e143cc0498ed20c911d04e54d526c/greenlet-3.2.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:72e77ed69312bab0434d7292316d5afd6896192ac4327d44f3d613ecb85b037c" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/47/48/ff9ca8ba9772d083a4f5221f7b4f0ebe8978131a9ae0909cf202f94cd879/greenlet-3.2.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:68671180e3849b963649254a882cd544a3c75bfcd2c527346ad8bb53494444db" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/e9/45/626e974948713bc15775b696adb3eb0bd708bec267d6d2d5c47bb47a6119/greenlet-3.2.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:49c8cfb18fb419b3d08e011228ef8a25882397f3a859b9fe1436946140b6756b" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/b1/8e/8b6f42c67d5df7db35b8c55c9a850ea045219741bb14416255616808c690/greenlet-3.2.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:efc6dc8a792243c31f2f5674b670b3a95d46fa1c6a912b8e310d6f542e7b0712" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/05/46/ab58828217349500a7ebb81159d52ca357da747ff1797c29c6023d79d798/greenlet-3.2.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:731e154aba8e757aedd0781d4b240f1225b075b4409f1bb83b05ff410582cf00" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/68/7f/d1b537be5080721c0f0089a8447d4ef72839039cdb743bdd8ffd23046e9a/greenlet-3.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:96c20252c2f792defe9a115d3287e14811036d51e78b3aaddbee23b69b216302" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/fc/2e/d4fcb2978f826358b673f779f78fa8a32ee37df11920dc2bb5589cbeecef/greenlet-3.2.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:784ae58bba89fa1fa5733d170d42486580cab9decda3484779f4759345b29822" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/16/24/929f853e0202130e4fe163bc1d05a671ce8dcd604f790e14896adac43a52/greenlet-3.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0921ac4ea42a5315d3446120ad48f90c3a6b9bb93dd9b3cf4e4d84a66e42de83" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/d1/b2/0320715eb61ae70c25ceca2f1d5ae620477d246692d9cc284c13242ec31c/greenlet-3.2.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:d2971d93bb99e05f8c2c0c2f4aa9484a18d98c4c3bd3c62b65b7e6ae33dfcfaf" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/bd/49/445fd1a210f4747fedf77615d941444349c6a3a4a1135bba9701337cd966/greenlet-3.2.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c667c0bf9d406b77a15c924ef3285e1e05250948001220368e039b6aa5b5034b" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/7e/c8/ca19760cf6eae75fa8dc32b487e963d863b3ee04a7637da77b616703bc37/greenlet-3.2.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:592c12fb1165be74592f5de0d70f82bc5ba552ac44800d632214b76089945147" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/65/89/77acf9e3da38e9bcfca881e43b02ed467c1dedc387021fc4d9bd9928afb8/greenlet-3.2.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29e184536ba333003540790ba29829ac14bb645514fbd7e32af331e8202a62a5" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/97/c6/ae244d7c95b23b7130136e07a9cc5aadd60d59b5951180dc7dc7e8edaba7/greenlet-3.2.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:93c0bb79844a367782ec4f429d07589417052e621aa39a5ac1fb99c5aa308edc" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/89/5f/b16dec0cbfd3070658e0d744487919740c6d45eb90946f6787689a7efbce/greenlet-3.2.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:751261fc5ad7b6705f5f76726567375bb2104a059454e0226e1eef6c756748ba" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/66/77/d48fb441b5a71125bcac042fc5b1494c806ccb9a1432ecaa421e72157f77/greenlet-3.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:83a8761c75312361aa2b5b903b79da97f13f556164a7dd2d5448655425bd4c34" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/f3/94/ad0d435f7c48debe960c53b8f60fb41c2026b1d0fa4a99a1cb17c3461e09/greenlet-3.2.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:25ad29caed5783d4bd7a85c9251c651696164622494c00802a139c00d639242d" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/93/5d/7c27cf4d003d6e77749d299c7c8f5fd50b4f251647b5c2e97e1f20da0ab5/greenlet-3.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:88cd97bf37fe24a6710ec6a3a7799f3f81d9cd33317dcf565ff9950c83f55e0b" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/c6/7e/807e1e9be07a125bb4c169144937910bf59b9d2f6d931578e57f0bce0ae2/greenlet-3.2.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:baeedccca94880d2f5666b4fa16fc20ef50ba1ee353ee2d7092b383a243b0b0d" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/9d/ab/158c1a4ea1068bdbc78dba5a3de57e4c7aeb4e7fa034320ea94c688bfb61/greenlet-3.2.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:be52af4b6292baecfa0f397f3edb3c6092ce071b499dd6fe292c9ac9f2c8f264" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/cc/0d/93729068259b550d6a0288da4ff72b86ed05626eaf1eb7c0d3466a2571de/greenlet-3.2.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0cc73378150b8b78b0c9fe2ce56e166695e67478550769536a6742dca3651688" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/f6/f6/c82ac1851c60851302d8581680573245c8fc300253fc1ff741ae74a6c24d/greenlet-3.2.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:706d016a03e78df129f68c4c9b4c4f963f7d73534e48a24f5f5a7101ed13dbbb" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/98/82/d022cf25ca39cf1200650fc58c52af32c90f80479c25d1cbf57980ec3065/greenlet-3.2.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:419e60f80709510c343c57b4bb5a339d8767bf9aef9b8ce43f4f143240f88b7c" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/f5/e1/25297f70717abe8104c20ecf7af0a5b82d2f5a980eb1ac79f65654799f9f/greenlet-3.2.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:93d48533fade144203816783373f27a97e4193177ebaaf0fc396db19e5d61163" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/1f/8f/8f9e56c5e82eb2c26e8cde787962e66494312dc8cb261c460e1f3a9c88bc/greenlet-3.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:7454d37c740bb27bdeddfc3f358f26956a07d5220818ceb467a483197d84f849" },
]
[[package]]
@@ -2375,7 +2395,7 @@ wheels = [
[[package]]
name = "httpx"
-version = "0.27.0"
+version = "0.27.2"
source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
dependencies = [
{ name = "anyio" },
@@ -2384,9 +2404,9 @@ dependencies = [
{ name = "idna" },
{ name = "sniffio" },
]
-sdist = { url = "https://mirrors.aliyun.com/pypi/packages/5c/2d/3da5bdf4408b8b2800061c339f240c1802f2e82d55e50bd39c5a881f47f0/httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5" }
+sdist = { url = "https://mirrors.aliyun.com/pypi/packages/78/82/08f8c936781f67d9e6b9eeb8a0c8b4e406136ea4c3d1f89a5db71d42e0e6/httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2" }
wheels = [
- { url = "https://mirrors.aliyun.com/pypi/packages/41/7b/ddacf6dcebb42466abd03f368782142baa82e08fc0c1f8eaa05b4bae87d5/httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/56/95/9377bcb415797e44274b51d46e3249eba641711cf3348050f76ee7b15ffc/httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0" },
]
[[package]]
@@ -2857,24 +2877,24 @@ wheels = [
[[package]]
name = "litellm"
-version = "1.48.0"
+version = "1.75.0"
source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
dependencies = [
{ name = "aiohttp" },
{ name = "click" },
+ { name = "httpx" },
{ name = "importlib-metadata" },
{ name = "jinja2" },
{ name = "jsonschema" },
{ name = "openai" },
{ name = "pydantic" },
{ name = "python-dotenv" },
- { name = "requests" },
{ name = "tiktoken" },
{ name = "tokenizers" },
]
-sdist = { url = "https://mirrors.aliyun.com/pypi/packages/85/cf/ec69c348c6f16148a55657f3bd63215e965028441c0f322ae8edf9c1210a/litellm-1.48.0.tar.gz", hash = "sha256:31a9b8a25a9daf44c24ddc08bf74298da920f2c5cea44135e5061278d0aa6fc9" }
+sdist = { url = "https://mirrors.aliyun.com/pypi/packages/1b/28/50837cb0246c42a8caac45610572883de7f478543cf4d143e84f099c0234/litellm-1.75.0.tar.gz", hash = "sha256:ec7fbfe79e1b9cd4a2b36ca9e71e71959d8fc43305b222e5f257aced1a0d1d63" }
wheels = [
- { url = "https://mirrors.aliyun.com/pypi/packages/37/2b/6a42747557dc557e71d1e0664c4d5a814b08cda0589213921bb51c64c5e4/litellm-1.48.0-py3-none-any.whl", hash = "sha256:7765e8a92069778f5fc66aacfabd0e2f8ec8d74fb117f5e475567d89b0d376b9" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/db/43/e10905870d42e927de3b095a9248f2764156c7eb45ec172d72be35cd2bb4/litellm-1.75.0-py3-none-any.whl", hash = "sha256:1657472f37d291b366050dd2035e3640eebd96142d6fa0f935ceb290a0e1d5ad" },
]
[[package]]
@@ -3765,12 +3785,12 @@ name = "onnxruntime-gpu"
version = "1.19.2"
source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
dependencies = [
- { name = "coloredlogs" },
- { name = "flatbuffers" },
- { name = "numpy" },
- { name = "packaging" },
- { name = "protobuf" },
- { name = "sympy" },
+ { name = "coloredlogs", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
+ { name = "flatbuffers", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
+ { name = "numpy", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
+ { name = "packaging", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
+ { name = "protobuf", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
+ { name = "sympy", marker = "(platform_machine != 'aarch64' and sys_platform == 'linux') or (sys_platform != 'darwin' and sys_platform != 'linux')" },
]
wheels = [
{ url = "https://mirrors.aliyun.com/pypi/packages/d0/9c/3fa310e0730643051eb88e884f19813a6c8b67d0fbafcda610d960e589db/onnxruntime_gpu-1.19.2-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a49740e079e7c5215830d30cde3df792e903df007aa0b0fd7aa797937061b27a" },
@@ -3783,7 +3803,7 @@ wheels = [
[[package]]
name = "openai"
-version = "1.45.0"
+version = "1.99.1"
source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
dependencies = [
{ name = "anyio" },
@@ -3795,9 +3815,9 @@ dependencies = [
{ name = "tqdm" },
{ name = "typing-extensions" },
]
-sdist = { url = "https://mirrors.aliyun.com/pypi/packages/70/cd/5ec65b9a56999370c032af7933433143f78239d44a8c03a5ba34159af945/openai-1.45.0.tar.gz", hash = "sha256:731207d10637335413aa3c0955f8f8df30d7636a4a0f9c381f2209d32cf8de97" }
+sdist = { url = "https://mirrors.aliyun.com/pypi/packages/03/30/f0fb7907a77e733bb801c7bdcde903500b31215141cdb261f04421e6fbec/openai-1.99.1.tar.gz", hash = "sha256:2c9d8e498c298f51bb94bcac724257a3a6cac6139ccdfc1186c6708f7a93120f" }
wheels = [
- { url = "https://mirrors.aliyun.com/pypi/packages/d4/2a/97e80a4551346efc9cd937e11adb640207acc5045fdf4e06786eac55bfb1/openai-1.45.0-py3-none-any.whl", hash = "sha256:2f1f7b7cf90f038a9f1c24f0d26c0f1790c102ec5acd07ffd70a9b7feac1ff4e" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/54/15/9c85154ffd283abfc43309ff3aaa63c3fd02f7767ee684e73670f6c5ade2/openai-1.99.1-py3-none-any.whl", hash = "sha256:8eeccc69e0ece1357b51ca0d9fb21324afee09b20c3e5b547d02445ca18a4e03" },
]
[[package]]
@@ -4242,32 +4262,21 @@ wheels = [
[[package]]
name = "playwright"
-version = "1.47.0"
+version = "1.54.0"
source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
dependencies = [
{ name = "greenlet" },
{ name = "pyee" },
]
wheels = [
- { url = "https://mirrors.aliyun.com/pypi/packages/f8/70/01cad1d41861cd939fe66bff725771dd03f2de39b7c25b4479de2f583ce0/playwright-1.47.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:f205df24edb925db1a4ab62f1ab0da06f14bb69e382efecfb0deedc4c7f4b8cd" },
- { url = "https://mirrors.aliyun.com/pypi/packages/42/17/2300e578b434b56ebfc3d56a5e0fe6dc5e99d6ff43a88fa492b881f3b7e3/playwright-1.47.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7fc820faf6885f69a52ba4ec94124e575d3c4a4003bf29200029b4a4f2b2d0ab" },
- { url = "https://mirrors.aliyun.com/pypi/packages/5a/6a/3cff2abfa4b4c52e1fa34fa8b71bf09cc2a89b03b7417733e5138f1be61d/playwright-1.47.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:8e212dc472ff19c7d46ed7e900191c7a786ce697556ac3f1615986ec3aa00341" },
- { url = "https://mirrors.aliyun.com/pypi/packages/80/a6/c5152c817db664d75c439c2bd99d51f906a31c1df4a04e673ef51008b12f/playwright-1.47.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:a1935672531963e4b2a321de5aa59b982fb92463ee6e1032dd7326378e462955" },
- { url = "https://mirrors.aliyun.com/pypi/packages/d6/50/b573c13d3748a1ab94ed45f2faeb868c63263df0055f57028c4cc775419f/playwright-1.47.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0a1b61473d6f7f39c5d77d4800b3cbefecb03344c90b98f3fbcae63294ad249" },
- { url = "https://mirrors.aliyun.com/pypi/packages/7d/6c/34225ee5707db5e34bffa77f05d152c797c0e0b9bf3d3a5b426d99160f8f/playwright-1.47.0-py3-none-win32.whl", hash = "sha256:1b977ed81f6bba5582617684a21adab9bad5676d90a357ebf892db7bdf4a9974" },
- { url = "https://mirrors.aliyun.com/pypi/packages/cb/88/9a3c77025702e506fe04275e677676246ff0b2e6964de5d2527dfdab3416/playwright-1.47.0-py3-none-win_amd64.whl", hash = "sha256:0ec1056042d2e86088795a503347407570bffa32cbe20748e5d4c93dba085280" },
-]
-
-[[package]]
-name = "playwright-stealth"
-version = "1.0.6"
-source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
-dependencies = [
- { name = "playwright" },
-]
-sdist = { url = "https://mirrors.aliyun.com/pypi/packages/e5/dc/4e88b517e4c9cfb63f1b0b67d59adddcef2dc2fe0883b90e07119d15895a/playwright-stealth-1.0.6.tar.gz", hash = "sha256:b504d951d00fac755c7d13665a29611d415180510bd7d23f14ebc89439ba2043" }
-wheels = [
- { url = "https://mirrors.aliyun.com/pypi/packages/34/10/60981cb8d8e22487061b98a0803313c4fb519cc95ab1421516304a0cfcd0/playwright_stealth-1.0.6-py3-none-any.whl", hash = "sha256:b1b2bcf58eb6859aa53d42c49b91c4e27b74a6d13fc3d0c85eea513dd55efda3" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/f3/09/33d5bfe393a582d8dac72165a9e88b274143c9df411b65ece1cc13f42988/playwright-1.54.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:bf3b845af744370f1bd2286c2a9536f474cc8a88dc995b72ea9a5be714c9a77d" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/e1/7b/51882dc584f7aa59f446f2bb34e33c0e5f015de4e31949e5b7c2c10e54f0/playwright-1.54.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:780928b3ca2077aea90414b37e54edd0c4bbb57d1aafc42f7aa0b3fd2c2fac02" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/73/a1/7aa8ae175b240c0ec8849fcf000e078f3c693f9aa2ffd992da6550ea0dff/playwright-1.54.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:81d0b6f28843b27f288cfe438af0a12a4851de57998009a519ea84cee6fbbfb9" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/34/a9/45084fd23b6206f954198296ce39b0acf50debfdf3ec83a593e4d73c9c8a/playwright-1.54.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:09919f45cc74c64afb5432646d7fef0d19fff50990c862cb8d9b0577093f40cc" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/02/d4/6a692f4c6db223adc50a6e53af405b45308db39270957a6afebddaa80ea2/playwright-1.54.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13ae206c55737e8e3eae51fb385d61c0312eeef31535643bb6232741b41b6fdc" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/72/7a/4ee60a1c3714321db187bebbc40d52cea5b41a856925156325058b5fca5a/playwright-1.54.0-py3-none-win32.whl", hash = "sha256:0b108622ffb6906e28566f3f31721cd57dda637d7e41c430287804ac01911f56" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/aa/77/8f8fae05a242ef639de963d7ae70a69d0da61d6d72f1207b8bbf74ffd3e7/playwright-1.54.0-py3-none-win_amd64.whl", hash = "sha256:9e5aee9ae5ab1fdd44cd64153313a2045b136fcbcfb2541cc0a3d909132671a2" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/33/ff/99a6f4292a90504f2927d34032a4baf6adb498dc3f7cf0f3e0e22899e310/playwright-1.54.0-py3-none-win_arm64.whl", hash = "sha256:a975815971f7b8dca505c441a4c56de1aeb56a211290f8cc214eeef5524e8d75" },
]
[[package]]
@@ -4653,8 +4662,6 @@ wheels = [
{ url = "https://mirrors.aliyun.com/pypi/packages/59/fe/aae679b64363eb78326c7fdc9d06ec3de18bac68be4b612fc1fe8902693c/pycryptodome-3.23.0-cp37-abi3-win32.whl", hash = "sha256:507dbead45474b62b2bbe318eb1c4c8ee641077532067fec9c1aa82c31f84886" },
{ url = "https://mirrors.aliyun.com/pypi/packages/54/2f/e97a1b8294db0daaa87012c24a7bb714147c7ade7656973fd6c736b484ff/pycryptodome-3.23.0-cp37-abi3-win_amd64.whl", hash = "sha256:c75b52aacc6c0c260f204cbdd834f76edc9fb0d8e0da9fbf8352ef58202564e2" },
{ url = "https://mirrors.aliyun.com/pypi/packages/18/3d/f9441a0d798bf2b1e645adc3265e55706aead1255ccdad3856dbdcffec14/pycryptodome-3.23.0-cp37-abi3-win_arm64.whl", hash = "sha256:11eeeb6917903876f134b56ba11abe95c0b0fd5e3330def218083c7d98bbcb3c" },
- { url = "https://mirrors.aliyun.com/pypi/packages/9f/7c/f5b0556590e7b4e710509105e668adb55aa9470a9f0e4dea9c40a4a11ce1/pycryptodome-3.23.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:350ebc1eba1da729b35ab7627a833a1a355ee4e852d8ba0447fafe7b14504d56" },
- { url = "https://mirrors.aliyun.com/pypi/packages/33/38/dcc795578d610ea1aaffef4b148b8cafcfcf4d126b1e58231ddc4e475c70/pycryptodome-3.23.0-pp27-pypy_73-win32.whl", hash = "sha256:93837e379a3e5fd2bb00302a47aee9fdf7940d83595be3915752c74033d17ca7" },
{ url = "https://mirrors.aliyun.com/pypi/packages/d9/12/e33935a0709c07de084d7d58d330ec3f4daf7910a18e77937affdb728452/pycryptodome-3.23.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ddb95b49df036ddd264a0ad246d1be5b672000f12d6961ea2c267083a5e19379" },
{ url = "https://mirrors.aliyun.com/pypi/packages/22/0b/aa8f9419f25870889bebf0b26b223c6986652bdf071f000623df11212c90/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e95564beb8782abfd9e431c974e14563a794a4944c29d6d3b7b5ea042110b4" },
{ url = "https://mirrors.aliyun.com/pypi/packages/d4/5e/63f5cbde2342b7f70a39e591dbe75d9809d6338ce0b07c10406f1a140cdc/pycryptodome-3.23.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14e15c081e912c4b0d75632acd8382dfce45b258667aa3c67caf7a4d4c13f630" },
@@ -4678,8 +4685,6 @@ wheels = [
{ url = "https://mirrors.aliyun.com/pypi/packages/48/7d/0f2b09490b98cc6a902ac15dda8760c568b9c18cfe70e0ef7a16de64d53a/pycryptodomex-3.20.0-cp35-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:7a7a8f33a1f1fb762ede6cc9cbab8f2a9ba13b196bfaf7bc6f0b39d2ba315a43" },
{ url = "https://mirrors.aliyun.com/pypi/packages/b0/1c/375adb14b71ee1c8d8232904e928b3e7af5bbbca7c04e4bec94fe8e90c3d/pycryptodomex-3.20.0-cp35-abi3-win32.whl", hash = "sha256:c39778fd0548d78917b61f03c1fa8bfda6cfcf98c767decf360945fe6f97461e" },
{ url = "https://mirrors.aliyun.com/pypi/packages/b2/e8/1b92184ab7e5595bf38000587e6f8cf9556ebd1bf0a583619bee2057afbd/pycryptodomex-3.20.0-cp35-abi3-win_amd64.whl", hash = "sha256:2a47bcc478741b71273b917232f521fd5704ab4b25d301669879e7273d3586cc" },
- { url = "https://mirrors.aliyun.com/pypi/packages/e7/c5/9140bb867141d948c8e242013ec8a8011172233c898dfdba0a2417c3169a/pycryptodomex-3.20.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:1be97461c439a6af4fe1cf8bf6ca5936d3db252737d2f379cc6b2e394e12a458" },
- { url = "https://mirrors.aliyun.com/pypi/packages/5e/6a/04acb4978ce08ab16890c70611ebc6efd251681341617bbb9e53356dee70/pycryptodomex-3.20.0-pp27-pypy_73-win32.whl", hash = "sha256:19764605feea0df966445d46533729b645033f134baeb3ea26ad518c9fdf212c" },
{ url = "https://mirrors.aliyun.com/pypi/packages/eb/df/3f1ea084e43b91e6d2b6b3493cc948864c17ea5d93ff1261a03812fbfd1a/pycryptodomex-3.20.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f2e497413560e03421484189a6b65e33fe800d3bd75590e6d78d4dfdb7accf3b" },
{ url = "https://mirrors.aliyun.com/pypi/packages/c9/f3/83ffbdfa0c8f9154bcd8866895f6cae5a3ec749da8b0840603cf936c4412/pycryptodomex-3.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e48217c7901edd95f9f097feaa0388da215ed14ce2ece803d3f300b4e694abea" },
{ url = "https://mirrors.aliyun.com/pypi/packages/c9/9d/c113e640aaf02af5631ae2686b742aac5cd0e1402b9d6512b1c7ec5ef05d/pycryptodomex-3.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d00fe8596e1cc46b44bf3907354e9377aa030ec4cd04afbbf6e899fc1e2a7781" },
@@ -4792,14 +4797,14 @@ wheels = [
[[package]]
name = "pyee"
-version = "12.0.0"
+version = "13.0.0"
source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
dependencies = [
{ name = "typing-extensions" },
]
-sdist = { url = "https://mirrors.aliyun.com/pypi/packages/d2/a7/8faaa62a488a2a1e0d56969757f087cbd2729e9bcfa508c230299f366b4c/pyee-12.0.0.tar.gz", hash = "sha256:c480603f4aa2927d4766eb41fa82793fe60a82cbfdb8d688e0d08c55a534e145" }
+sdist = { url = "https://mirrors.aliyun.com/pypi/packages/95/03/1fd98d5841cd7964a27d729ccf2199602fe05eb7a405c1462eb7277945ed/pyee-13.0.0.tar.gz", hash = "sha256:b391e3c5a434d1f5118a25615001dbc8f669cf410ab67d04c4d4e07c55481c37" }
wheels = [
- { url = "https://mirrors.aliyun.com/pypi/packages/1d/0d/95993c08c721ec68892547f2117e8f9dfbcef2ca71e098533541b4a54d5f/pyee-12.0.0-py3-none-any.whl", hash = "sha256:7b14b74320600049ccc7d0e0b1becd3b4bd0a03c745758225e31a59f4095c990" },
+ { url = "https://mirrors.aliyun.com/pypi/packages/9b/4d/b9add7c84060d4c1906abe9a7e5359f2a60f7a9a4f67268b2766673427d8/pyee-13.0.0-py3-none-any.whl", hash = "sha256:48195a3cddb3b1515ce0695ed76036b5ccc2ef3a9f963ff9f77aec0139845498" },
]
[[package]]
@@ -5239,6 +5244,7 @@ dependencies = [
{ name = "itsdangerous" },
{ name = "json-repair" },
{ name = "langfuse" },
+ { name = "litellm" },
{ name = "markdown" },
{ name = "markdown-to-json" },
{ name = "mcp" },
@@ -5357,7 +5363,7 @@ requires-dist = [
{ name = "click", specifier = ">=8.1.8" },
{ name = "cn2an", specifier = "==0.5.22" },
{ name = "cohere", specifier = "==5.6.2" },
- { name = "crawl4ai", specifier = "==0.3.8" },
+ { name = "crawl4ai", specifier = ">=0.3.8" },
{ name = "dashscope", specifier = "==1.20.11" },
{ name = "datrie", specifier = "==0.8.2" },
{ name = "debugpy", specifier = ">=1.8.13" },
@@ -5384,13 +5390,14 @@ requires-dist = [
{ name = "groq", specifier = "==0.9.0" },
{ name = "hanziconv", specifier = "==0.3.2" },
{ name = "html-text", specifier = "==0.6.2" },
- { name = "httpx", specifier = "==0.27.0" },
+ { name = "httpx", specifier = "==0.27.2" },
{ name = "huggingface-hub", specifier = ">=0.25.0,<0.26.0" },
{ name = "infinity-emb", specifier = ">=0.0.66,<0.0.67" },
{ name = "infinity-sdk", specifier = "==0.6.0.dev4" },
{ name = "itsdangerous", specifier = "==2.1.2" },
{ name = "json-repair", specifier = "==0.35.0" },
{ name = "langfuse", specifier = ">=2.60.0" },
+ { name = "litellm", specifier = ">=1.74.15.post1" },
{ name = "markdown", specifier = "==3.6" },
{ name = "markdown-to-json", specifier = "==2.1.1" },
{ name = "mcp", specifier = ">=1.9.4" },
@@ -5402,7 +5409,7 @@ requires-dist = [
{ name = "ollama", specifier = "==0.2.1" },
{ name = "onnxruntime", marker = "platform_machine != 'x86_64' or sys_platform == 'darwin'", specifier = "==1.19.2" },
{ name = "onnxruntime-gpu", marker = "platform_machine == 'x86_64' and sys_platform != 'darwin'", specifier = "==1.19.2" },
- { name = "openai", specifier = "==1.45.0" },
+ { name = "openai", specifier = ">=1.45.0" },
{ name = "opencv-python", specifier = "==4.10.0.84" },
{ name = "opencv-python-headless", specifier = "==4.10.0.84" },
{ name = "opendal", specifier = ">=0.45.0,<0.46.0" },
@@ -5467,6 +5474,7 @@ requires-dist = [
{ name = "yfinance", specifier = "==0.2.65" },
{ name = "zhipuai", specifier = "==2.0.1" },
]
+provides-extras = ["full"]
[package.metadata.requires-dev]
test = [
@@ -5481,6 +5489,18 @@ test = [
{ name = "requests-toolbelt", specifier = ">=1.0.0" },
]
+[[package]]
+name = "rank-bm25"
+version = "0.2.2"
+source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
+dependencies = [
+ { name = "numpy" },
+]
+sdist = { url = "https://mirrors.aliyun.com/pypi/packages/fc/0a/f9579384aa017d8b4c15613f86954b92a95a93d641cc849182467cf0bb3b/rank_bm25-0.2.2.tar.gz", hash = "sha256:096ccef76f8188563419aaf384a02f0ea459503fdf77901378d4fd9d87e5e51d" }
+wheels = [
+ { url = "https://mirrors.aliyun.com/pypi/packages/2a/21/f691fb2613100a62b3fa91e9988c991e9ca5b89ea31c0d3152a3210344f9/rank_bm25-0.2.2-py3-none-any.whl", hash = "sha256:7bd4a95571adadfc271746fa146a4bcfd89c0cf731e49c3d1ad863290adbe8ae" },
+]
+
[[package]]
name = "ranx"
version = "0.3.20"
@@ -6423,6 +6443,19 @@ wheels = [
{ url = "https://mirrors.aliyun.com/pypi/packages/55/08/98090d1a139e8995053ed22e099b43aa4dea8cffe056f8f0bc5178aeecbd/tencentcloud_sdk_python-3.0.1215-py2.py3-none-any.whl", hash = "sha256:899ced749baf74846f1eabf452f74aa0e48d1965f0ca7828a8b73b446f76f5f2" },
]
+[[package]]
+name = "tf-playwright-stealth"
+version = "1.2.0"
+source = { registry = "https://mirrors.aliyun.com/pypi/simple" }
+dependencies = [
+ { name = "fake-http-header" },
+ { name = "playwright" },
+]
+sdist = { url = "https://mirrors.aliyun.com/pypi/packages/d6/6b/32bb58c65991f91aeaaf7473b650175d9d4af5dd383983d177d49ccba08d/tf_playwright_stealth-1.2.0.tar.gz", hash = "sha256:7bb8d32d3e60324fbf6b9eeae540b8cd9f3b9e07baeb33b025dbc98ad47658ba" }
+wheels = [
+ { url = "https://mirrors.aliyun.com/pypi/packages/11/3d/2653f4cf49660bb44eeac8270617cc4c0287d61716f249f55053f0af0724/tf_playwright_stealth-1.2.0-py3-none-any.whl", hash = "sha256:26ee47ee89fa0f43c606fe37c188ea3ccd36f96ea90c01d167b768df457e7886" },
+]
+
[[package]]
name = "threadpoolctl"
version = "3.6.0"
diff --git a/web/src/locales/config.ts b/web/src/locales/config.ts
index cd8d928f5..65d85d718 100644
--- a/web/src/locales/config.ts
+++ b/web/src/locales/config.ts
@@ -10,9 +10,9 @@ import translation_fr from './fr';
import translation_id from './id';
import translation_ja from './ja';
import translation_pt_br from './pt-br';
+import translation_ru from './ru';
import { createTranslationTable, flattenObject } from './until';
import translation_vi from './vi';
-import translation_ru from './ru';
import translation_zh from './zh';
import translation_zh_traditional from './zh-traditional';
diff --git a/web/src/locales/ru.ts b/web/src/locales/ru.ts
index 21d798dea..4cf23cd62 100644
--- a/web/src/locales/ru.ts
+++ b/web/src/locales/ru.ts
@@ -246,8 +246,7 @@ export default {
methodExamples: 'Примеры',
methodExamplesDescription: 'Скриншоты для пояснения:',
dialogueExamplesTitle: 'просмотр',
- methodEmpty:
- 'Здесь будет визуальное объяснение категорий баз знаний',
+ methodEmpty: 'Здесь будет визуальное объяснение категорий баз знаний',
book: `
Поддерживаемые форматы: DOCX, PDF, TXT.
Для PDF укажите диапазон страниц.
`,
laws: `Поддерживаемые форматы: DOCX, PDF, TXT.
@@ -316,21 +315,19 @@ export default {
В столбце тегов используйте запятую для разделения тегов.
`,
useRaptor: 'Использовать RAPTOR',
- useRaptorTip:
- 'Включите RAPTOR для многошаговых вопросно-ответных задач.',
+ useRaptorTip: 'Включите RAPTOR для многошаговых вопросно-ответных задач.',
prompt: 'Промпт',
- promptTip:
- 'Опишите задачу для LLM, укажите формат ответа и требования.',
+ promptTip: 'Опишите задачу для LLM, укажите формат ответа и требования.',
promptMessage: 'Требуется промпт',
promptText: `Пожалуйста, обобщите следующие абзацы. Будьте внимательны с числами, не выдумывайте. Абзацы:
{cluster_content}
Выше представлен контент для обобщения.`,
maxToken: 'Макс. токенов',
- maxTokenTip: 'Максимальное количество токенов на суммаризирующий фрагмент.',
+ maxTokenTip:
+ 'Максимальное количество токенов на суммаризирующий фрагмент.',
maxTokenMessage: 'Требуется макс. токенов',
threshold: 'Порог',
- thresholdTip:
- 'Минимальное сходство для группировки фрагментов в RAPTOR.',
+ thresholdTip: 'Минимальное сходство для группировки фрагментов в RAPTOR.',
thresholdMessage: 'Требуется порог',
maxCluster: 'Макс. кластеров',
maxClusterTip: 'Максимальное количество кластеров.',
@@ -463,8 +460,7 @@ export default {
'Устанавливает порог для выбора наиболее вероятных слов (ядерная выборка).',
presencePenalty: 'Штраф за присутствие',
presencePenaltyMessage: 'Требуется штраф за присутствие',
- presencePenaltyTip:
- 'Штрафует слова, уже появившиеся в диалоге.',
+ presencePenaltyTip: 'Штрафует слова, уже появившиеся в диалоге.',
frequencyPenalty: 'Штраф за частоту',
frequencyPenaltyMessage: 'Требуется штраф за частоту',
frequencyPenaltyTip:
@@ -553,8 +549,7 @@ export default {
maxTokensInvalidMessage: 'Введите корректное число для Макс. токенов.',
maxTokensMinMessage: 'Макс. токенов не может быть меньше 0.',
password: 'Пароль',
- passwordDescription:
- 'Введите текущий пароль для изменения пароля.',
+ passwordDescription: 'Введите текущий пароль для изменения пароля.',
model: 'Провайдеры моделей',
modelDescription: 'Настройте параметры моделей и API KEY.',
team: 'Команда',
@@ -584,17 +579,14 @@ export default {
'Ваш новый пароль должен быть длиннее 8 символов.',
confirmPassword: 'Подтвердите новый пароль',
confirmPasswordMessage: 'Подтвердите пароль!',
- confirmPasswordNonMatchMessage:
- 'Новые пароли не совпадают!',
+ confirmPasswordNonMatchMessage: 'Новые пароли не совпадают!',
cancel: 'Отмена',
addedModels: 'Добавленные модели',
modelsToBeAdded: 'Модели для добавления',
addTheModel: 'Добавить модель',
apiKey: 'API-Ключ',
- apiKeyMessage:
- 'Введите API ключ (для локальных моделей игнорируйте).',
- apiKeyTip:
- 'API ключ можно получить у поставщика LLM.',
+ apiKeyMessage: 'Введите API ключ (для локальных моделей игнорируйте).',
+ apiKeyTip: 'API ключ можно получить у поставщика LLM.',
showMoreModels: 'Показать модели',
hideModels: 'Скрыть модели',
baseUrl: 'Базовый URL',
@@ -603,22 +595,18 @@ export default {
modify: 'Изменить',
systemModelSettings: 'Установить модели по умолчанию',
chatModel: 'Модель чата',
- chatModelTip:
- 'Модель чата по умолчанию для новых баз знаний.',
+ chatModelTip: 'Модель чата по умолчанию для новых баз знаний.',
embeddingModel: 'Модель эмбеддинга',
- embeddingModelTip:
- 'Модель эмбеддинга по умолчанию для новых баз знаний.',
+ embeddingModelTip: 'Модель эмбеддинга по умолчанию для новых баз знаний.',
img2txtModel: 'Модель Img2txt',
- img2txtModelTip:
- 'Модель описания изображений/видео по умолчанию.',
+ img2txtModelTip: 'Модель описания изображений/видео по умолчанию.',
sequence2txtModel: 'Модель Speech2txt',
sequence2txtModelTip:
'Модель ASR по умолчанию для преобразования речи в текст.',
rerankModel: 'Модель реранкинга',
rerankModelTip: `Модель реранкинга фрагментов по умолчанию.`,
ttsModel: 'Модель TTS',
- ttsModelTip:
- 'Модель преобразования текста в речь по умолчанию.',
+ ttsModelTip: 'Модель преобразования текста в речь по умолчанию.',
workspace: 'Рабочее пространство',
upgrade: 'Обновить',
addLlmTitle: 'Добавить LLM',
@@ -677,8 +665,7 @@ export default {
yiyanAKMessage: 'Введите ваш API KEY',
addyiyanSK: 'yiyan Secret KEY',
yiyanSKMessage: 'Введите ваш Secret KEY',
- FishAudioModelNameMessage:
- 'Дайте имя вашей модели синтеза речи',
+ FishAudioModelNameMessage: 'Дайте имя вашей модели синтеза речи',
addFishAudioAK: 'Fish Audio API KEY',
addFishAudioAKMessage: 'Введите ваш API KEY',
addFishAudioRefID: 'FishAudio Reference ID',
@@ -715,7 +702,7 @@ export default {
configuration: 'Конфигурация',
langfuseDescription:
'Трассировка, оценка, управление промптами и метрики для отладки и улучшения вашего LLM-приложения.',
- viewLangfuseSDocumentation: "Документация Langfuse",
+ viewLangfuseSDocumentation: 'Документация Langfuse',
view: 'Просмотр',
modelsToBeAddedTooltip:
'Если ваш провайдер не указан, но заявляет о "совместимости с OpenAI API", выберите соответствующую карточку.',
@@ -776,8 +763,7 @@ export default {
s3: 'S3 загрузки',
preview: 'Просмотр',
fileError: 'Ошибка файла',
- uploadLimit:
- 'Каждый файл ≤10MB, всего файлов ≤128.',
+ uploadLimit: 'Каждый файл ≤10MB, всего файлов ≤128.',
destinationFolder: 'Целевая папка',
},
flow: {
@@ -844,8 +830,7 @@ export default {
baidu: 'Baidu',
baiduDescription: `Ищет на baidu.com.`,
duckDuckGo: 'DuckDuckGo',
- duckDuckGoDescription:
- 'Ищет на duckduckgo.com.',
+ duckDuckGoDescription: 'Ищет на duckduckgo.com.',
channel: 'Канал',
channelTip: `Текстовый или новостной поиск`,
text: 'Текст',
@@ -855,14 +840,11 @@ export default {
'Количество сообщений истории, видимых LLM. Учитывайте ограничение токенов модели.',
wikipedia: 'Wikipedia',
pubMed: 'PubMed',
- pubMedDescription:
- 'Ищет на https://pubmed.ncbi.nlm.nih.gov/.',
+ pubMedDescription: 'Ищет на https://pubmed.ncbi.nlm.nih.gov/.',
email: 'Email',
- emailTip:
- 'Email обязателен.',
+ emailTip: 'Email обязателен.',
arXiv: 'ArXiv',
- arXivDescription:
- 'Ищет на https://arxiv.org/.',
+ arXivDescription: 'Ищет на https://arxiv.org/.',
sortBy: 'Сортировать по',
submittedDate: 'Дата отправки',
lastUpdatedDate: 'Дата обновления',
@@ -877,24 +859,20 @@ export default {
country: 'Страна и регион',
language: 'Язык',
googleScholar: 'Google Scholar',
- googleScholarDescription:
- 'Ищет на https://scholar.google.com/.',
+ googleScholarDescription: 'Ищет на https://scholar.google.com/.',
yearLow: 'Год от',
yearHigh: 'Год до',
patents: 'Патенты',
data: 'Данные',
deepL: 'DeepL',
- deepLDescription:
- 'Перевод с помощью https://www.deepl.com/.',
+ deepLDescription: 'Перевод с помощью https://www.deepl.com/.',
authKey: 'Ключ авторизации',
sourceLang: 'Исходный язык',
targetLang: 'Целевой язык',
gitHub: 'GitHub',
- gitHubDescription:
- 'Ищет репозитории на https://github.com/.',
+ gitHubDescription: 'Ищет репозитории на https://github.com/.',
baiduFanyi: 'BaiduFanyi',
- baiduFanyiDescription:
- 'Перевод с помощью https://fanyi.baidu.com/.',
+ baiduFanyiDescription: 'Перевод с помощью https://fanyi.baidu.com/.',
appid: 'App ID',
secretKey: 'Секретный ключ',
domain: 'Домен',
@@ -1062,8 +1040,7 @@ export default {
yahooFinanceDescription:
'Запрашивает информацию о публичной компании по тикеру.',
crawler: 'Веб-краулер',
- crawlerDescription:
- 'Скачивает HTML-код с указанного URL.',
+ crawlerDescription: 'Скачивает HTML-код с указанного URL.',
proxy: 'Прокси',
crawlerResultOptions: {
html: 'Html',
@@ -1077,8 +1054,7 @@ export default {
balanceSheet: 'Баланс',
cashFlowStatement: 'Отчет о движении денежных средств',
jin10: 'Jin10',
- jin10Description:
- 'Получает финансовую информацию с Jin10 Open Platform.',
+ jin10Description: 'Получает финансовую информацию с Jin10 Open Platform.',
flashType: 'Тип новости',
filter: 'Фильтр',
contain: 'Содержит',
@@ -1265,13 +1241,13 @@ export default {
'Выберите базы знаний для ассистента или переменные с ID баз знаний.',
knowledgeBaseVars: 'Переменные базы знаний',
code: 'Код',
- codeDescription: 'Позволяет разработчикам писать пользовательскую логику на Python.',
+ codeDescription:
+ 'Позволяет разработчикам писать пользовательскую логику на Python.',
inputVariables: 'Входные переменные',
runningHintText: 'выполняется...🕞',
openingSwitch: 'Приветствие',
openingCopy: 'Приветственное сообщение',
- openingSwitchTip:
- 'Пользователи увидят это приветствие в начале.',
+ openingSwitchTip: 'Пользователи увидят это приветствие в начале.',
modeTip: 'Режим определяет, как запускается рабочий процесс.',
beginInputTip:
'Определите входные параметры для доступа в последующих процессах.',