style: introduce ruff as linter and formatter (#1356)

* style: remove necessary imports

* style: fix F841

* style: fix F401

* style: fix F811

* style: fix E402

* style: fix E721

* style: fix E722

* style: fix E722

* style: fix F541

* style: ruff format

* style: all passed

* style: add ruff in deps

* style: more ignores in ruff.toml

* style: add pre-commit
This commit is contained in:
Junyan Qin (Chin)
2025-04-29 17:24:07 +08:00
committed by GitHub
parent 09e70d70e9
commit 209f16af76
240 changed files with 5307 additions and 4689 deletions

View File

@@ -2,16 +2,12 @@ from __future__ import annotations
import typing
import json
import traceback
import base64
import anthropic
import httpx
from ....core import app
from .. import entities, errors, requester
from .. import errors, requester
from .. import entities, errors
from ....core import entities as core_entities
from ... import entities as llm_entities
from ...tools import entities as tools_entities
@@ -29,7 +25,6 @@ class AnthropicMessages(requester.LLMAPIRequester):
}
async def initialize(self):
httpx_client = anthropic._base_client.AsyncHttpxClientWrapper(
base_url=self.requester_cfg['base_url'],
# cast to a valid type because mypy doesn't understand our type narrowing
@@ -40,7 +35,7 @@ class AnthropicMessages(requester.LLMAPIRequester):
)
self.client = anthropic.AsyncAnthropic(
api_key="",
api_key='',
http_client=httpx_client,
)
@@ -55,7 +50,7 @@ class AnthropicMessages(requester.LLMAPIRequester):
self.client.api_key = model.token_mgr.get_token()
args = extra_args.copy()
args["model"] = model.model_entity.name
args['model'] = model.model_entity.name
# 处理消息
@@ -63,14 +58,15 @@ class AnthropicMessages(requester.LLMAPIRequester):
system_role_message = None
for i, m in enumerate(messages):
if m.role == "system":
if m.role == 'system':
system_role_message = m
messages.pop(i)
break
if isinstance(system_role_message, llm_entities.Message) \
and isinstance(system_role_message.content, str):
if isinstance(system_role_message, llm_entities.Message) and isinstance(
system_role_message.content, str
):
args['system'] = system_role_message.content
req_messages = []
@@ -79,67 +75,64 @@ class AnthropicMessages(requester.LLMAPIRequester):
if m.role == 'tool':
tool_call_id = m.tool_call_id
req_messages.append({
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": tool_call_id,
"content": m.content
}
]
})
req_messages.append(
{
'role': 'user',
'content': [
{
'type': 'tool_result',
'tool_use_id': tool_call_id,
'content': m.content,
}
],
}
)
continue
msg_dict = m.dict(exclude_none=True)
if isinstance(m.content, str) and m.content.strip() != "":
msg_dict["content"] = [
{
"type": "text",
"text": m.content
}
]
if isinstance(m.content, str) and m.content.strip() != '':
msg_dict['content'] = [{'type': 'text', 'text': m.content}]
elif isinstance(m.content, list):
for i, ce in enumerate(m.content):
if ce.type == "image_base64":
image_b64, image_format = await image.extract_b64_and_format(ce.image_base64)
if ce.type == 'image_base64':
image_b64, image_format = await image.extract_b64_and_format(
ce.image_base64
)
alter_image_ele = {
"type": "image",
"source": {
"type": "base64",
"media_type": f"image/{image_format}",
"data": image_b64
}
'type': 'image',
'source': {
'type': 'base64',
'media_type': f'image/{image_format}',
'data': image_b64,
},
}
msg_dict["content"][i] = alter_image_ele
msg_dict['content'][i] = alter_image_ele
if m.tool_calls:
for tool_call in m.tool_calls:
msg_dict["content"].append({
"type": "tool_use",
"id": tool_call.id,
"name": tool_call.function.name,
"input": json.loads(tool_call.function.arguments)
})
msg_dict['content'].append(
{
'type': 'tool_use',
'id': tool_call.id,
'name': tool_call.function.name,
'input': json.loads(tool_call.function.arguments),
}
)
del msg_dict["tool_calls"]
del msg_dict['tool_calls']
req_messages.append(msg_dict)
args["messages"] = req_messages
args['messages'] = req_messages
if funcs:
tools = await self.ap.tool_mgr.generate_tools_for_anthropic(funcs)
if tools:
args["tools"] = tools
args['tools'] = tools
try:
# print(json.dumps(args, indent=4, ensure_ascii=False))
@@ -149,23 +142,24 @@ class AnthropicMessages(requester.LLMAPIRequester):
'content': '',
'role': resp.role,
}
assert type(resp) is anthropic.types.message.Message
for block in resp.content:
if block.type == 'thinking':
args['content'] = '<think>' + block.thinking + '</think>\n' + args['content']
args['content'] = (
'<think>' + block.thinking + '</think>\n' + args['content']
)
elif block.type == 'text':
args['content'] += block.text
elif block.type == 'tool_use':
assert type(block) is anthropic.types.tool_use_block.ToolUseBlock
tool_call = llm_entities.ToolCall(
id=block.id,
type="function",
type='function',
function=llm_entities.FunctionCall(
name=block.name,
arguments=json.dumps(block.input)
)
name=block.name, arguments=json.dumps(block.input)
),
)
if 'tool_calls' not in args:
args['tool_calls'] = []

View File

@@ -4,8 +4,6 @@ import typing
import openai
from . import chatcmpl
from .. import requester
from ....core import app
class BailianChatCompletions(chatcmpl.OpenAIChatCompletions):

View File

@@ -2,22 +2,15 @@ from __future__ import annotations
import asyncio
import typing
import json
import base64
from typing import AsyncGenerator
import openai
import openai.types.chat.chat_completion as chat_completion
import openai.types.chat.chat_completion_message_tool_call as chat_completion_message_tool_call
import httpx
import aiohttp
import async_lru
from .. import entities, errors, requester
from ....core import entities as core_entities, app
from .. import errors, requester
from ....core import entities as core_entities
from ... import entities as llm_entities
from ...tools import entities as tools_entities
from ....utils import image
class OpenAIChatCompletions(requester.LLMAPIRequester):
@@ -26,18 +19,17 @@ class OpenAIChatCompletions(requester.LLMAPIRequester):
client: openai.AsyncClient
default_config: dict[str, typing.Any] = {
"base_url": "https://api.openai.com/v1",
"timeout": 120,
'base_url': 'https://api.openai.com/v1',
'timeout': 120,
}
async def initialize(self):
self.client = openai.AsyncClient(
api_key="",
base_url=self.requester_cfg["base_url"],
timeout=self.requester_cfg["timeout"],
api_key='',
base_url=self.requester_cfg['base_url'],
timeout=self.requester_cfg['timeout'],
http_client=httpx.AsyncClient(
trust_env=True, timeout=self.requester_cfg["timeout"]
trust_env=True, timeout=self.requester_cfg['timeout']
),
)
@@ -54,8 +46,8 @@ class OpenAIChatCompletions(requester.LLMAPIRequester):
chatcmpl_message = chat_completion.choices[0].message.model_dump()
# 确保 role 字段存在且不为 None
if "role" not in chatcmpl_message or chatcmpl_message["role"] is None:
chatcmpl_message["role"] = "assistant"
if 'role' not in chatcmpl_message or chatcmpl_message['role'] is None:
chatcmpl_message['role'] = 'assistant'
message = llm_entities.Message(**chatcmpl_message)
@@ -72,27 +64,27 @@ class OpenAIChatCompletions(requester.LLMAPIRequester):
self.client.api_key = use_model.token_mgr.get_token()
args = extra_args.copy()
args["model"] = use_model.model_entity.name
args['model'] = use_model.model_entity.name
if use_funcs:
tools = await self.ap.tool_mgr.generate_tools_for_openai(use_funcs)
if tools:
args["tools"] = tools
args['tools'] = tools
# 设置此次请求中的messages
messages = req_messages.copy()
# 检查vision
for msg in messages:
if "content" in msg and isinstance(msg["content"], list):
for me in msg["content"]:
if me["type"] == "image_base64":
me["image_url"] = {"url": me["image_base64"]}
me["type"] = "image_url"
del me["image_base64"]
if 'content' in msg and isinstance(msg['content'], list):
for me in msg['content']:
if me['type'] == 'image_base64':
me['image_url'] = {'url': me['image_base64']}
me['type'] = 'image_url'
del me['image_base64']
args["messages"] = messages
args['messages'] = messages
# 发送请求
resp = await self._req(args)
@@ -113,15 +105,15 @@ class OpenAIChatCompletions(requester.LLMAPIRequester):
req_messages = [] # req_messages 仅用于类内,外部同步由 query.messages 进行
for m in messages:
msg_dict = m.dict(exclude_none=True)
content = msg_dict.get("content")
content = msg_dict.get('content')
if isinstance(content, list):
# 检查 content 列表中是否每个部分都是文本
if all(
isinstance(part, dict) and part.get("type") == "text"
isinstance(part, dict) and part.get('type') == 'text'
for part in content
):
# 将所有文本部分合并为一个字符串
msg_dict["content"] = "\n".join(part["text"] for part in content)
msg_dict['content'] = '\n'.join(part['text'] for part in content)
req_messages.append(msg_dict)
try:
@@ -133,17 +125,17 @@ class OpenAIChatCompletions(requester.LLMAPIRequester):
extra_args=extra_args,
)
except asyncio.TimeoutError:
raise errors.RequesterError("请求超时")
raise errors.RequesterError('请求超时')
except openai.BadRequestError as e:
if "context_length_exceeded" in e.message:
raise errors.RequesterError(f"上文过长,请重置会话: {e.message}")
if 'context_length_exceeded' in e.message:
raise errors.RequesterError(f'上文过长,请重置会话: {e.message}')
else:
raise errors.RequesterError(f"请求参数错误: {e.message}")
raise errors.RequesterError(f'请求参数错误: {e.message}')
except openai.AuthenticationError as e:
raise errors.RequesterError(f"无效的 api-key: {e.message}")
raise errors.RequesterError(f'无效的 api-key: {e.message}')
except openai.NotFoundError as e:
raise errors.RequesterError(f"请求路径错误: {e.message}")
raise errors.RequesterError(f'请求路径错误: {e.message}')
except openai.RateLimitError as e:
raise errors.RequesterError(f"请求过于频繁或余额不足: {e.message}")
raise errors.RequesterError(f'请求过于频繁或余额不足: {e.message}')
except openai.APIError as e:
raise errors.RequesterError(f"请求错误: {e.message}")
raise errors.RequesterError(f'请求错误: {e.message}')

View File

@@ -3,8 +3,8 @@ from __future__ import annotations
import typing
from . import chatcmpl
from .. import entities, errors, requester
from ....core import entities as core_entities, app
from .. import errors, requester
from ....core import entities as core_entities
from ... import entities as llm_entities
from ...tools import entities as tools_entities
@@ -28,23 +28,23 @@ class DeepseekChatCompletions(chatcmpl.OpenAIChatCompletions):
self.client.api_key = use_model.token_mgr.get_token()
args = extra_args.copy()
args["model"] = use_model.model_entity.name
args['model'] = use_model.model_entity.name
if use_funcs:
tools = await self.ap.tool_mgr.generate_tools_for_openai(use_funcs)
if tools:
args["tools"] = tools
args['tools'] = tools
# 设置此次请求中的messages
messages = req_messages
# deepseek 不支持多模态把content都转换成纯文字
for m in messages:
if 'content' in m and isinstance(m["content"], list):
m["content"] = " ".join([c["text"] for c in m["content"]])
if 'content' in m and isinstance(m['content'], list):
m['content'] = ' '.join([c['text'] for c in m['content']])
args["messages"] = messages
args['messages'] = messages
# 发送请求
resp = await self._req(args)
@@ -55,4 +55,4 @@ class DeepseekChatCompletions(chatcmpl.OpenAIChatCompletions):
# 处理请求结果
message = await self._make_msg(resp)
return message
return message

View File

@@ -1,17 +1,13 @@
from __future__ import annotations
import json
import asyncio
import aiohttp
import typing
from . import chatcmpl
from .. import entities, errors, requester
from ....core import app, entities as core_entities
from .. import requester
from ....core import entities as core_entities
from ... import entities as llm_entities
from ...tools import entities as tools_entities
from .. import entities as modelmgr_entities
class GiteeAIChatCompletions(chatcmpl.OpenAIChatCompletions):
@@ -33,20 +29,20 @@ class GiteeAIChatCompletions(chatcmpl.OpenAIChatCompletions):
self.client.api_key = use_model.token_mgr.get_token()
args = extra_args.copy()
args["model"] = use_model.model_entity.name
args['model'] = use_model.model_entity.name
if use_funcs:
tools = await self.ap.tool_mgr.generate_tools_for_openai(use_funcs)
if tools:
args["tools"] = tools
args['tools'] = tools
# gitee 不支持多模态把content都转换成纯文字
for m in req_messages:
if 'content' in m and isinstance(m["content"], list):
m["content"] = " ".join([c["text"] for c in m["content"]])
if 'content' in m and isinstance(m['content'], list):
m['content'] = ' '.join([c['text'] for c in m['content']])
args["messages"] = req_messages
args['messages'] = req_messages
resp = await self._req(args)

View File

@@ -4,8 +4,6 @@ import typing
import openai
from . import chatcmpl
from .. import requester
from ....core import app
class LmStudioChatCompletions(chatcmpl.OpenAIChatCompletions):

View File

@@ -2,11 +2,10 @@ from __future__ import annotations
import typing
from ....core import app
from . import chatcmpl
from .. import entities, errors, requester
from ....core import entities as core_entities, app
from .. import requester
from ....core import entities as core_entities
from ... import entities as llm_entities
from ...tools import entities as tools_entities
@@ -30,26 +29,26 @@ class MoonshotChatCompletions(chatcmpl.OpenAIChatCompletions):
self.client.api_key = use_model.token_mgr.get_token()
args = extra_args.copy()
args["model"] = use_model.model_entity.name
args['model'] = use_model.model_entity.name
if use_funcs:
tools = await self.ap.tool_mgr.generate_tools_for_openai(use_funcs)
if tools:
args["tools"] = tools
args['tools'] = tools
# 设置此次请求中的messages
messages = req_messages
# deepseek 不支持多模态把content都转换成纯文字
for m in messages:
if 'content' in m and isinstance(m["content"], list):
m["content"] = " ".join([c["text"] for c in m["content"]])
if 'content' in m and isinstance(m['content'], list):
m['content'] = ' '.join([c['text'] for c in m['content']])
# 删除空的
messages = [m for m in messages if m["content"].strip() != ""]
messages = [m for m in messages if m['content'].strip() != '']
args["messages"] = messages
args['messages'] = messages
# 发送请求
resp = await self._req(args)
@@ -57,4 +56,4 @@ class MoonshotChatCompletions(chatcmpl.OpenAIChatCompletions):
# 处理请求结果
message = await self._make_msg(resp)
return message
return message

View File

@@ -6,18 +6,15 @@ import typing
from typing import Union, Mapping, Any, AsyncIterator
import uuid
import json
import base64
import async_lru
import ollama
from .. import entities, errors, requester
from .. import errors, requester
from ... import entities as llm_entities
from ...tools import entities as tools_entities
from ....core import app, entities as core_entities
from ....utils import image
from ....core import entities as core_entities
REQUESTER_NAME: str = "ollama-chat"
REQUESTER_NAME: str = 'ollama-chat'
class OllamaChatCompletions(requester.LLMAPIRequester):
@@ -26,13 +23,13 @@ class OllamaChatCompletions(requester.LLMAPIRequester):
client: ollama.AsyncClient
default_config: dict[str, typing.Any] = {
"base_url": "http://127.0.0.1:11434",
"timeout": 120,
'base_url': 'http://127.0.0.1:11434',
'timeout': 120,
}
async def initialize(self):
os.environ["OLLAMA_HOST"] = self.requester_cfg["base_url"]
self.client = ollama.AsyncClient(timeout=self.requester_cfg["timeout"])
os.environ['OLLAMA_HOST'] = self.requester_cfg['base_url']
self.client = ollama.AsyncClient(timeout=self.requester_cfg['timeout'])
async def _req(
self,
@@ -49,35 +46,35 @@ class OllamaChatCompletions(requester.LLMAPIRequester):
extra_args: dict[str, typing.Any] = {},
) -> llm_entities.Message:
args = extra_args.copy()
args["model"] = use_model.model_entity.name
args['model'] = use_model.model_entity.name
messages: list[dict] = req_messages.copy()
for msg in messages:
if "content" in msg and isinstance(msg["content"], list):
if 'content' in msg and isinstance(msg['content'], list):
text_content: list = []
image_urls: list = []
for me in msg["content"]:
if me["type"] == "text":
text_content.append(me["text"])
elif me["type"] == "image_base64":
image_urls.append(me["image_base64"])
for me in msg['content']:
if me['type'] == 'text':
text_content.append(me['text'])
elif me['type'] == 'image_base64':
image_urls.append(me['image_base64'])
msg["content"] = "\n".join(text_content)
msg["images"] = [url.split(",")[1] for url in image_urls]
msg['content'] = '\n'.join(text_content)
msg['images'] = [url.split(',')[1] for url in image_urls]
if (
"tool_calls" in msg
'tool_calls' in msg
): # LangBot 内部以 str 存储 tool_calls 的参数,这里需要转换为 dict
for tool_call in msg["tool_calls"]:
tool_call["function"]["arguments"] = json.loads(
tool_call["function"]["arguments"]
for tool_call in msg['tool_calls']:
tool_call['function']['arguments'] = json.loads(
tool_call['function']['arguments']
)
args["messages"] = messages
args['messages'] = messages
args["tools"] = []
args['tools'] = []
if user_funcs:
tools = await self.ap.tool_mgr.generate_tools_for_openai(user_funcs)
if tools:
args["tools"] = tools
args['tools'] = tools
resp = await self._req(args)
message: llm_entities.Message = await self._make_msg(resp)
@@ -93,7 +90,7 @@ class OllamaChatCompletions(requester.LLMAPIRequester):
ret_msg: llm_entities.Message = None
if message.content is not None:
ret_msg = llm_entities.Message(role="assistant", content=message.content)
ret_msg = llm_entities.Message(role='assistant', content=message.content)
if message.tool_calls is not None and len(message.tool_calls) > 0:
tool_calls: list[llm_entities.ToolCall] = []
@@ -101,7 +98,7 @@ class OllamaChatCompletions(requester.LLMAPIRequester):
tool_calls.append(
llm_entities.ToolCall(
id=uuid.uuid4().hex,
type="function",
type='function',
function=llm_entities.FunctionCall(
name=tool_call.function.name,
arguments=json.dumps(tool_call.function.arguments),
@@ -123,13 +120,13 @@ class OllamaChatCompletions(requester.LLMAPIRequester):
req_messages: list = []
for m in messages:
msg_dict: dict = m.dict(exclude_none=True)
content: Any = msg_dict.get("content")
content: Any = msg_dict.get('content')
if isinstance(content, list):
if all(
isinstance(part, dict) and part.get("type") == "text"
isinstance(part, dict) and part.get('type') == 'text'
for part in content
):
msg_dict["content"] = "\n".join(part["text"] for part in content)
msg_dict['content'] = '\n'.join(part['text'] for part in content)
req_messages.append(msg_dict)
try:
return await self._closure(
@@ -140,4 +137,4 @@ class OllamaChatCompletions(requester.LLMAPIRequester):
extra_args=extra_args,
)
except asyncio.TimeoutError:
raise errors.RequesterError("请求超时")
raise errors.RequesterError('请求超时')

View File

@@ -4,8 +4,6 @@ import typing
import openai
from . import chatcmpl
from .. import requester
from ....core import app
class SiliconFlowChatCompletions(chatcmpl.OpenAIChatCompletions):

View File

@@ -4,8 +4,6 @@ import typing
import openai
from . import chatcmpl
from .. import requester
from ....core import app
class VolcArkChatCompletions(chatcmpl.OpenAIChatCompletions):

View File

@@ -4,8 +4,6 @@ import typing
import openai
from . import chatcmpl
from .. import requester
from ....core import app
class XaiChatCompletions(chatcmpl.OpenAIChatCompletions):

View File

@@ -3,9 +3,7 @@ from __future__ import annotations
import typing
import openai
from ....core import app
from . import chatcmpl
from .. import requester
class ZhipuAIChatCompletions(chatcmpl.OpenAIChatCompletions):