mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-08 14:56:03 +00:00
Compare commits
3 Commits
v4.3.0.bet
...
v4.0.8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
60b50a35f1 | ||
|
|
abd02f04af | ||
|
|
14411a8af6 |
@@ -119,6 +119,7 @@ docker compose up -d
|
|||||||
| [Anthropic](https://www.anthropic.com/) | ✅ | |
|
| [Anthropic](https://www.anthropic.com/) | ✅ | |
|
||||||
| [xAI](https://x.ai/) | ✅ | |
|
| [xAI](https://x.ai/) | ✅ | |
|
||||||
| [智谱AI](https://open.bigmodel.cn/) | ✅ | |
|
| [智谱AI](https://open.bigmodel.cn/) | ✅ | |
|
||||||
|
| [优云智算](https://www.compshare.cn/) | ✅ | 大模型和 GPU 资源平台 |
|
||||||
| [PPIO](https://ppinfra.com/user/register?invited_by=QJKFYD&utm_source=github_langbot) | ✅ | 大模型和 GPU 资源平台 |
|
| [PPIO](https://ppinfra.com/user/register?invited_by=QJKFYD&utm_source=github_langbot) | ✅ | 大模型和 GPU 资源平台 |
|
||||||
| [302 AI](https://share.302.ai/SuTG99) | ✅ | 大模型聚合平台 |
|
| [302 AI](https://share.302.ai/SuTG99) | ✅ | 大模型聚合平台 |
|
||||||
| [Google Gemini](https://aistudio.google.com/prompts/new_chat) | ✅ | |
|
| [Google Gemini](https://aistudio.google.com/prompts/new_chat) | ✅ | |
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ Directly use the released version to run, see the [Manual Deployment](https://do
|
|||||||
| [Anthropic](https://www.anthropic.com/) | ✅ | |
|
| [Anthropic](https://www.anthropic.com/) | ✅ | |
|
||||||
| [xAI](https://x.ai/) | ✅ | |
|
| [xAI](https://x.ai/) | ✅ | |
|
||||||
| [Zhipu AI](https://open.bigmodel.cn/) | ✅ | |
|
| [Zhipu AI](https://open.bigmodel.cn/) | ✅ | |
|
||||||
|
| [CompShare](https://www.compshare.cn/) | ✅ | LLM and GPU resource platform |
|
||||||
| [Dify](https://dify.ai) | ✅ | LLMOps platform |
|
| [Dify](https://dify.ai) | ✅ | LLMOps platform |
|
||||||
| [PPIO](https://ppinfra.com/user/register?invited_by=QJKFYD&utm_source=github_langbot) | ✅ | LLM and GPU resource platform |
|
| [PPIO](https://ppinfra.com/user/register?invited_by=QJKFYD&utm_source=github_langbot) | ✅ | LLM and GPU resource platform |
|
||||||
| [302 AI](https://share.302.ai/SuTG99) | ✅ | LLM gateway(MaaS) |
|
| [302 AI](https://share.302.ai/SuTG99) | ✅ | LLM gateway(MaaS) |
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ LangBotはBTPanelにリストされています。BTPanelをインストール
|
|||||||
| [Anthropic](https://www.anthropic.com/) | ✅ | |
|
| [Anthropic](https://www.anthropic.com/) | ✅ | |
|
||||||
| [xAI](https://x.ai/) | ✅ | |
|
| [xAI](https://x.ai/) | ✅ | |
|
||||||
| [Zhipu AI](https://open.bigmodel.cn/) | ✅ | |
|
| [Zhipu AI](https://open.bigmodel.cn/) | ✅ | |
|
||||||
|
| [CompShare](https://www.compshare.cn/) | ✅ | 大模型とGPUリソースプラットフォーム |
|
||||||
| [PPIO](https://ppinfra.com/user/register?invited_by=QJKFYD&utm_source=github_langbot) | ✅ | 大模型とGPUリソースプラットフォーム |
|
| [PPIO](https://ppinfra.com/user/register?invited_by=QJKFYD&utm_source=github_langbot) | ✅ | 大模型とGPUリソースプラットフォーム |
|
||||||
| [302 AI](https://share.302.ai/SuTG99) | ✅ | LLMゲートウェイ(MaaS) |
|
| [302 AI](https://share.302.ai/SuTG99) | ✅ | LLMゲートウェイ(MaaS) |
|
||||||
| [Google Gemini](https://aistudio.google.com/prompts/new_chat) | ✅ | |
|
| [Google Gemini](https://aistudio.google.com/prompts/new_chat) | ✅ | |
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import base64
|
|||||||
import uuid
|
import uuid
|
||||||
import os
|
import os
|
||||||
import datetime
|
import datetime
|
||||||
|
import io
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
|
||||||
@@ -35,28 +36,88 @@ class DiscordMessageConverter(adapter.MessageConverter):
|
|||||||
for ele in message_chain:
|
for ele in message_chain:
|
||||||
if isinstance(ele, platform_message.Image):
|
if isinstance(ele, platform_message.Image):
|
||||||
image_bytes = None
|
image_bytes = None
|
||||||
|
filename = f'{uuid.uuid4()}.png' # 默认文件名
|
||||||
|
|
||||||
if ele.base64:
|
if ele.base64:
|
||||||
image_bytes = base64.b64decode(ele.base64)
|
# 处理base64编码的图片
|
||||||
|
if ele.base64.startswith('data:'):
|
||||||
|
# 从data URL中提取文件类型
|
||||||
|
data_header = ele.base64.split(',')[0]
|
||||||
|
if 'jpeg' in data_header or 'jpg' in data_header:
|
||||||
|
filename = f'{uuid.uuid4()}.jpg'
|
||||||
|
elif 'gif' in data_header:
|
||||||
|
filename = f'{uuid.uuid4()}.gif'
|
||||||
|
elif 'webp' in data_header:
|
||||||
|
filename = f'{uuid.uuid4()}.webp'
|
||||||
|
# 去掉data:image/xxx;base64,前缀
|
||||||
|
base64_data = ele.base64.split(',')[1]
|
||||||
|
else:
|
||||||
|
base64_data = ele.base64
|
||||||
|
image_bytes = base64.b64decode(base64_data)
|
||||||
elif ele.url:
|
elif ele.url:
|
||||||
|
# 从URL下载图片
|
||||||
async with aiohttp.ClientSession() as session:
|
async with aiohttp.ClientSession() as session:
|
||||||
async with session.get(ele.url) as response:
|
async with session.get(ele.url) as response:
|
||||||
image_bytes = await response.read()
|
image_bytes = await response.read()
|
||||||
|
# 从URL或Content-Type推断文件类型
|
||||||
|
content_type = response.headers.get('Content-Type', '')
|
||||||
|
if 'jpeg' in content_type or 'jpg' in content_type:
|
||||||
|
filename = f'{uuid.uuid4()}.jpg'
|
||||||
|
elif 'gif' in content_type:
|
||||||
|
filename = f'{uuid.uuid4()}.gif'
|
||||||
|
elif 'webp' in content_type:
|
||||||
|
filename = f'{uuid.uuid4()}.webp'
|
||||||
|
elif ele.url.lower().endswith(('.jpg', '.jpeg')):
|
||||||
|
filename = f'{uuid.uuid4()}.jpg'
|
||||||
|
elif ele.url.lower().endswith('.gif'):
|
||||||
|
filename = f'{uuid.uuid4()}.gif'
|
||||||
|
elif ele.url.lower().endswith('.webp'):
|
||||||
|
filename = f'{uuid.uuid4()}.webp'
|
||||||
elif ele.path:
|
elif ele.path:
|
||||||
with open(ele.path, 'rb') as f:
|
# 从文件路径读取图片
|
||||||
image_bytes = f.read()
|
# 确保路径没有空字节
|
||||||
|
clean_path = ele.path.replace('\x00', '')
|
||||||
|
clean_path = os.path.abspath(clean_path)
|
||||||
|
|
||||||
|
if not os.path.exists(clean_path):
|
||||||
|
continue # 跳过不存在的文件
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(clean_path, 'rb') as f:
|
||||||
|
image_bytes = f.read()
|
||||||
|
# 从文件路径获取文件名,保持原始扩展名
|
||||||
|
original_filename = os.path.basename(clean_path)
|
||||||
|
if original_filename and '.' in original_filename:
|
||||||
|
# 保持原始文件名的扩展名
|
||||||
|
ext = original_filename.split('.')[-1].lower()
|
||||||
|
filename = f'{uuid.uuid4()}.{ext}'
|
||||||
|
else:
|
||||||
|
# 如果没有扩展名,尝试从文件内容检测
|
||||||
|
if image_bytes.startswith(b'\xff\xd8\xff'):
|
||||||
|
filename = f'{uuid.uuid4()}.jpg'
|
||||||
|
elif image_bytes.startswith(b'GIF'):
|
||||||
|
filename = f'{uuid.uuid4()}.gif'
|
||||||
|
elif image_bytes.startswith(b'RIFF') and b'WEBP' in image_bytes[:20]:
|
||||||
|
filename = f'{uuid.uuid4()}.webp'
|
||||||
|
# 默认保持PNG
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error reading image file {clean_path}: {e}")
|
||||||
|
continue # 跳过读取失败的文件
|
||||||
|
|
||||||
image_files.append(discord.File(fp=image_bytes, filename=f'{uuid.uuid4()}.png'))
|
if image_bytes:
|
||||||
|
# 使用BytesIO创建文件对象,避免路径问题
|
||||||
|
import io
|
||||||
|
image_files.append(discord.File(fp=io.BytesIO(image_bytes), filename=filename))
|
||||||
elif isinstance(ele, platform_message.Plain):
|
elif isinstance(ele, platform_message.Plain):
|
||||||
text_string += ele.text
|
text_string += ele.text
|
||||||
elif isinstance(ele, platform_message.Forward):
|
elif isinstance(ele, platform_message.Forward):
|
||||||
for node in ele.node_list:
|
for node in ele.node_list:
|
||||||
(
|
(
|
||||||
text_string,
|
node_text,
|
||||||
image_files,
|
node_images,
|
||||||
) = await DiscordMessageConverter.yiri2target(node.message_chain)
|
) = await DiscordMessageConverter.yiri2target(node.message_chain)
|
||||||
text_string += text_string
|
text_string += node_text
|
||||||
image_files.extend(image_files)
|
image_files.extend(node_images)
|
||||||
|
|
||||||
return text_string, image_files
|
return text_string, image_files
|
||||||
|
|
||||||
@@ -199,7 +260,27 @@ class DiscordAdapter(adapter.MessagePlatformAdapter):
|
|||||||
self.bot = MyClient(intents=intents, **args)
|
self.bot = MyClient(intents=intents, **args)
|
||||||
|
|
||||||
async def send_message(self, target_type: str, target_id: str, message: platform_message.MessageChain):
|
async def send_message(self, target_type: str, target_id: str, message: platform_message.MessageChain):
|
||||||
pass
|
msg_to_send, image_files = await self.message_converter.yiri2target(message)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 获取频道对象
|
||||||
|
channel = self.bot.get_channel(int(target_id))
|
||||||
|
if channel is None:
|
||||||
|
# 如果本地缓存中没有,尝试从API获取
|
||||||
|
channel = await self.bot.fetch_channel(int(target_id))
|
||||||
|
|
||||||
|
args = {
|
||||||
|
'content': msg_to_send,
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(image_files) > 0:
|
||||||
|
args['files'] = image_files
|
||||||
|
|
||||||
|
await channel.send(**args)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
await self.logger.error(f"Discord send_message failed: {e}")
|
||||||
|
raise e
|
||||||
|
|
||||||
async def reply_message(
|
async def reply_message(
|
||||||
self,
|
self,
|
||||||
|
|||||||
BIN
pkg/provider/modelmgr/requesters/compshare.png
Normal file
BIN
pkg/provider/modelmgr/requesters/compshare.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 59 KiB |
17
pkg/provider/modelmgr/requesters/compsharechatcmpl.py
Normal file
17
pkg/provider/modelmgr/requesters/compsharechatcmpl.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import typing
|
||||||
|
import openai
|
||||||
|
|
||||||
|
from . import chatcmpl
|
||||||
|
|
||||||
|
|
||||||
|
class CompShareChatCompletions(chatcmpl.OpenAIChatCompletions):
|
||||||
|
"""CompShare ChatCompletion API 请求器"""
|
||||||
|
|
||||||
|
client: openai.AsyncClient
|
||||||
|
|
||||||
|
default_config: dict[str, typing.Any] = {
|
||||||
|
'base_url': 'https://api.modelverse.cn/v1',
|
||||||
|
'timeout': 120,
|
||||||
|
}
|
||||||
28
pkg/provider/modelmgr/requesters/compsharechatcmpl.yaml
Normal file
28
pkg/provider/modelmgr/requesters/compsharechatcmpl.yaml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
apiVersion: v1
|
||||||
|
kind: LLMAPIRequester
|
||||||
|
metadata:
|
||||||
|
name: compshare-chat-completions
|
||||||
|
label:
|
||||||
|
en_US: CompShare
|
||||||
|
zh_Hans: 优云智算
|
||||||
|
icon: compshare.png
|
||||||
|
spec:
|
||||||
|
config:
|
||||||
|
- name: base_url
|
||||||
|
label:
|
||||||
|
en_US: Base URL
|
||||||
|
zh_Hans: 基础 URL
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
default: "https://api.modelverse.cn/v1"
|
||||||
|
- name: timeout
|
||||||
|
label:
|
||||||
|
en_US: Timeout
|
||||||
|
zh_Hans: 超时时间
|
||||||
|
type: integer
|
||||||
|
required: true
|
||||||
|
default: 120
|
||||||
|
execution:
|
||||||
|
python:
|
||||||
|
path: ./compsharechatcmpl.py
|
||||||
|
attr: CompShareChatCompletions
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
semantic_version = 'v4.0.7'
|
semantic_version = 'v4.0.8'
|
||||||
|
|
||||||
required_database_version = 3
|
required_database_version = 3
|
||||||
"""标记本版本所需要的数据库结构版本,用于判断数据库迁移"""
|
"""标记本版本所需要的数据库结构版本,用于判断数据库迁移"""
|
||||||
|
|||||||
Reference in New Issue
Block a user