diff --git a/.github/workflows/build-dev-image.yaml b/.github/workflows/build-dev-image.yaml
index 2784e40d..062a72d0 100644
--- a/.github/workflows/build-dev-image.yaml
+++ b/.github/workflows/build-dev-image.yaml
@@ -7,6 +7,8 @@ on:
jobs:
build-dev-image:
runs-on: ubuntu-latest
+ # 如果是tag则跳过
+ if: ${{ !startsWith(github.ref, 'refs/tags/') }}
steps:
- name: Checkout
uses: actions/checkout@v2
diff --git a/.github/workflows/build-release-artifacts.yaml b/.github/workflows/build-release-artifacts.yaml
index 2a1d2fc6..02ba8542 100644
--- a/.github/workflows/build-release-artifacts.yaml
+++ b/.github/workflows/build-release-artifacts.yaml
@@ -52,3 +52,11 @@ jobs:
with:
name: langbot-${{ steps.check_version.outputs.version }}-all
path: .
+
+ - name: Upload To Release
+ env:
+ GH_TOKEN: ${{ secrets.RELEASE_UPLOAD_GITHUB_TOKEN }}
+ run: |
+ # 本目录下所有文件打包成zip
+ zip -r langbot-${{ steps.check_version.outputs.version }}-all.zip .
+ gh release upload ${{ github.event.release.tag_name }} langbot-${{ steps.check_version.outputs.version }}-all.zip
diff --git a/.github/workflows/test-pr.yml b/.github/workflows/test-pr.yml
deleted file mode 100644
index 3e824322..00000000
--- a/.github/workflows/test-pr.yml
+++ /dev/null
@@ -1,80 +0,0 @@
-name: Test Pull Request
-
-on:
- pull_request:
- types: [ready_for_review]
- paths:
- # 任何py文件改动都会触发
- - '**.py'
- pull_request_review:
- types: [submitted]
- issue_comment:
- types: [created]
- # 允许手动触发
- workflow_dispatch:
-
-jobs:
- perform-test:
- runs-on: ubuntu-latest
- # 如果事件为pull_request_review且review状态为approved,则执行
- if: >
- github.event_name == 'pull_request' ||
- (github.event_name == 'pull_request_review' && github.event.review.state == 'APPROVED') ||
- github.event_name == 'workflow_dispatch' ||
- (github.event_name == 'issue_comment' && github.event.issue.pull_request != '' && contains(github.event.comment.body, '/test') && github.event.comment.user.login == 'RockChinQ')
- steps:
- # 签出测试工程仓库代码
- - name: Checkout
- uses: actions/checkout@v2
- with:
- # 仓库地址
- repository: RockChinQ/qcg-tester
- # 仓库路径
- path: qcg-tester
- - name: Setup Python
- uses: actions/setup-python@v2
- with:
- python-version: '3.10'
-
- - name: Install dependencies
- run: |
- cd qcg-tester
- python -m pip install --upgrade pip
- pip install -r requirements.txt
-
- - name: Get PR details
- id: get-pr
- if: github.event_name == 'issue_comment'
- uses: octokit/request-action@v2.x
- with:
- route: GET /repos/${{ github.repository }}/pulls/${{ github.event.issue.number }}
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Set PR source branch as env variable
- if: github.event_name == 'issue_comment'
- run: |
- PR_SOURCE_BRANCH=$(echo '${{ steps.get-pr.outputs.data }}' | jq -r '.head.ref')
- echo "BRANCH=$PR_SOURCE_BRANCH" >> $GITHUB_ENV
-
- - name: Set PR Branch as bash env
- if: github.event_name != 'issue_comment'
- run: |
- echo "BRANCH=${{ github.head_ref }}" >> $GITHUB_ENV
- - name: Set OpenAI API Key from Secrets
- run: |
- echo "OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }}" >> $GITHUB_ENV
- - name: Set OpenAI Reverse Proxy URL from Secrets
- run: |
- echo "OPENAI_REVERSE_PROXY=${{ secrets.OPENAI_REVERSE_PROXY }}" >> $GITHUB_ENV
- - name: Run test
- run: |
- cd qcg-tester
- python main.py
-
- - name: Upload coverage reports to Codecov
- run: |
- cd qcg-tester/resource/QChatGPT
- curl -Os https://uploader.codecov.io/latest/linux/codecov
- chmod +x codecov
- ./codecov -t ${{ secrets.CODECOV_TOKEN }}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 00859808..4ac91687 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,7 +2,6 @@
.idea/
__pycache__/
database.db
-qchatgpt.log
langbot.log
/banlist.py
/plugins/
@@ -17,8 +16,7 @@ scenario/
!scenario/default-template.json
override.json
cookies.json
-res/announcement_saved
-res/announcement_saved.json
+data/labels/announcement_saved.json
cmdpriv.json
tips.py
.venv
@@ -32,7 +30,7 @@ claude.json
bard.json
/*yaml
!/docker-compose.yaml
-res/instance_id.json
+data/labels/instance_id.json
.DS_Store
/data
botpy.log*
diff --git a/README.md b/README.md
index f8ecad8c..13e5371c 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,8 @@
-> [!IMPORTANT]
-> 我们被人在 X.com 和 pump.fun 上冒充了,以下两个账号利用本项目和作者信息在 X.com 上发布数字货币营销信息,请勿相信!我们已向 X 官方举报!我们从未以 LangBot 名义创建任何社交媒体账号或者数字货币。
-> We have been impersonated on X.com and pump.fun . The following two accounts are using this project and author information to post digital currency marketing information on X.com. Please do not believe that! We have reported to X official! We have never created any social media account or digital currency under the name LangBot.
-> 1. https://x.com/RockChinQ
-> 2. https://x.com/LangBotAI
+
+
@@ -82,3 +79,30 @@
- WebUI Demo: https://demo.langbot.dev/
- 登录信息:邮箱:`demo@langbot.app` 密码:`langbot123456`
- 注意:仅展示webui效果,公开环境,请不要在其中填入您的任何敏感信息。
+
+## 🔌 组件兼容性
+
+### 消息平台
+
+| 平台 | 状态 | 备注 |
+| --- | --- | --- |
+| QQ 个人号 | ✅ | QQ 个人号私聊、群聊 |
+| QQ 官方机器人 | ✅ | QQ 频道机器人,支持频道、私聊、群聊 |
+| 企业微信 | 🚧 | |
+| 钉钉 | 🚧 | |
+
+🚧: 正在开发中
+
+### 大模型
+
+| 模型 | 状态 | 备注 |
+| --- | --- | --- |
+| [OpenAI](https://platform.openai.com/) | ✅ | 可接入任何 OpenAI 接口格式模型 |
+| [DeepSeek](https://www.deepseek.com/) | ✅ | |
+| [Moonshot](https://www.moonshot.cn/) | ✅ | |
+| [Anthropic](https://www.anthropic.com/) | ✅ | |
+| [xAI](https://x.ai/) | ✅ | |
+| [智谱AI](https://open.bigmodel.cn/) | ✅ | |
+| [Dify](https://dify.ai) | ✅ | LLMOps 平台 |
+| [Ollama](https://ollama.com/) | ✅ | 本地大模型管理平台 |
+| [GiteeAI](https://ai.gitee.com/) | ✅ | 大模型接口聚合平台 |
diff --git a/main.py b/main.py
index 31441cfe..c297c2a4 100644
--- a/main.py
+++ b/main.py
@@ -49,12 +49,10 @@ async def main_entry(loop: asyncio.AbstractEventLoop):
generated_files = await files.generate_files()
if generated_files:
- print("以下文件不存在,已自动生成,请按需修改配置文件后重启:")
+ print("以下文件不存在,已自动生成:")
for file in generated_files:
print("-", file)
- sys.exit(0)
-
from pkg.core import boot
await boot.main(loop)
diff --git a/pkg/api/http/controller/groups/logs.py b/pkg/api/http/controller/groups/logs.py
index 36aa0d75..4244d889 100644
--- a/pkg/api/http/controller/groups/logs.py
+++ b/pkg/api/http/controller/groups/logs.py
@@ -12,7 +12,7 @@ from .. import group
class LogsRouterGroup(group.RouterGroup):
async def initialize(self) -> None:
- @self.route('', methods=['GET'])
+ @self.route('', methods=['GET'], auth_type=group.AuthType.USER_TOKEN)
async def _() -> str:
start_page_number = int(quart.request.args.get('start_page_number', 0))
diff --git a/pkg/api/http/controller/groups/plugins.py b/pkg/api/http/controller/groups/plugins.py
index 86ac7ec6..00951550 100644
--- a/pkg/api/http/controller/groups/plugins.py
+++ b/pkg/api/http/controller/groups/plugins.py
@@ -13,7 +13,7 @@ from .. import group
class PluginsRouterGroup(group.RouterGroup):
async def initialize(self) -> None:
- @self.route('', methods=['GET'])
+ @self.route('', methods=['GET'], auth_type=group.AuthType.USER_TOKEN)
async def _() -> str:
plugins = self.ap.plugin_mgr.plugins()
@@ -23,14 +23,14 @@ class PluginsRouterGroup(group.RouterGroup):
'plugins': plugins_data
})
- @self.route('/
//toggle', methods=['PUT'])
+ @self.route('///toggle', methods=['PUT'], auth_type=group.AuthType.USER_TOKEN)
async def _(author: str, plugin_name: str) -> str:
data = await quart.request.json
target_enabled = data.get('target_enabled')
await self.ap.plugin_mgr.update_plugin_switch(plugin_name, target_enabled)
return self.success()
- @self.route('///update', methods=['POST'])
+ @self.route('///update', methods=['POST'], auth_type=group.AuthType.USER_TOKEN)
async def _(author: str, plugin_name: str) -> str:
ctx = taskmgr.TaskContext.new()
wrapper = self.ap.task_mgr.create_user_task(
@@ -44,7 +44,7 @@ class PluginsRouterGroup(group.RouterGroup):
'task_id': wrapper.id
})
- @self.route('//', methods=['DELETE'])
+ @self.route('//', methods=['DELETE'], auth_type=group.AuthType.USER_TOKEN)
async def _(author: str, plugin_name: str) -> str:
ctx = taskmgr.TaskContext.new()
wrapper = self.ap.task_mgr.create_user_task(
@@ -59,13 +59,13 @@ class PluginsRouterGroup(group.RouterGroup):
'task_id': wrapper.id
})
- @self.route('/reorder', methods=['PUT'])
+ @self.route('/reorder', methods=['PUT'], auth_type=group.AuthType.USER_TOKEN)
async def _() -> str:
data = await quart.request.json
await self.ap.plugin_mgr.reorder_plugins(data.get('plugins'))
return self.success()
- @self.route('/install/github', methods=['POST'])
+ @self.route('/install/github', methods=['POST'], auth_type=group.AuthType.USER_TOKEN)
async def _() -> str:
data = await quart.request.json
diff --git a/pkg/api/http/controller/groups/settings.py b/pkg/api/http/controller/groups/settings.py
index 00693239..835d86ad 100644
--- a/pkg/api/http/controller/groups/settings.py
+++ b/pkg/api/http/controller/groups/settings.py
@@ -9,7 +9,7 @@ class SettingsRouterGroup(group.RouterGroup):
async def initialize(self) -> None:
- @self.route('', methods=['GET'])
+ @self.route('', methods=['GET'], auth_type=group.AuthType.USER_TOKEN)
async def _() -> str:
return self.success(
data={
@@ -23,7 +23,7 @@ class SettingsRouterGroup(group.RouterGroup):
}
)
- @self.route('/', methods=['GET'])
+ @self.route('/', methods=['GET'], auth_type=group.AuthType.USER_TOKEN)
async def _(manager_name: str) -> str:
manager = self.ap.settings_mgr.get_manager(manager_name)
@@ -44,7 +44,7 @@ class SettingsRouterGroup(group.RouterGroup):
}
)
- @self.route('//data', methods=['PUT'])
+ @self.route('//data', methods=['PUT'], auth_type=group.AuthType.USER_TOKEN)
async def _(manager_name: str) -> str:
data = await quart.request.json
manager = self.ap.settings_mgr.get_manager(manager_name)
diff --git a/pkg/api/http/controller/groups/stats.py b/pkg/api/http/controller/groups/stats.py
index 45326035..43d56f27 100644
--- a/pkg/api/http/controller/groups/stats.py
+++ b/pkg/api/http/controller/groups/stats.py
@@ -9,7 +9,7 @@ from .. import group
class StatsRouterGroup(group.RouterGroup):
async def initialize(self) -> None:
- @self.route('/basic', methods=['GET'])
+ @self.route('/basic', methods=['GET'], auth_type=group.AuthType.USER_TOKEN)
async def _() -> str:
conv_count = 0
diff --git a/pkg/api/http/controller/groups/system.py b/pkg/api/http/controller/groups/system.py
index f074531a..71d0d8df 100644
--- a/pkg/api/http/controller/groups/system.py
+++ b/pkg/api/http/controller/groups/system.py
@@ -20,7 +20,7 @@ class SystemRouterGroup(group.RouterGroup):
}
)
- @self.route('/tasks', methods=['GET'])
+ @self.route('/tasks', methods=['GET'], auth_type=group.AuthType.USER_TOKEN)
async def _() -> str:
task_type = quart.request.args.get("type")
@@ -31,7 +31,7 @@ class SystemRouterGroup(group.RouterGroup):
data=self.ap.task_mgr.get_tasks_dict(task_type)
)
- @self.route('/tasks/', methods=['GET'])
+ @self.route('/tasks/', methods=['GET'], auth_type=group.AuthType.USER_TOKEN)
async def _(task_id: str) -> str:
task = self.ap.task_mgr.get_task_by_id(int(task_id))
@@ -40,7 +40,7 @@ class SystemRouterGroup(group.RouterGroup):
return self.success(data=task.to_dict())
- @self.route('/reload', methods=['POST'])
+ @self.route('/reload', methods=['POST'], auth_type=group.AuthType.USER_TOKEN)
async def _() -> str:
json_data = await quart.request.json
@@ -51,7 +51,7 @@ class SystemRouterGroup(group.RouterGroup):
)
return self.success()
- @self.route('/_debug/exec', methods=['POST'])
+ @self.route('/_debug/exec', methods=['POST'], auth_type=group.AuthType.USER_TOKEN)
async def _() -> str:
if not constants.debug_mode:
return self.http_status(403, 403, "Forbidden")
diff --git a/pkg/audit/identifier.py b/pkg/audit/identifier.py
index 7c197c14..3e2ec57d 100644
--- a/pkg/audit/identifier.py
+++ b/pkg/audit/identifier.py
@@ -13,14 +13,14 @@ identifier = {
'instance_create_ts': 0,
}
-HOST_ID_FILE = os.path.expanduser('~/.qchatgpt/host_id.json')
-INSTANCE_ID_FILE = 'res/instance_id.json'
+HOST_ID_FILE = os.path.expanduser('~/.langbot/host_id.json')
+INSTANCE_ID_FILE = 'data/labels/instance_id.json'
def init():
global identifier
- if not os.path.exists(os.path.expanduser('~/.qchatgpt')):
- os.mkdir(os.path.expanduser('~/.qchatgpt'))
+ if not os.path.exists(os.path.expanduser('~/.langbot')):
+ os.mkdir(os.path.expanduser('~/.langbot'))
if not os.path.exists(HOST_ID_FILE):
new_host_id = 'host_'+str(uuid.uuid4())
diff --git a/pkg/core/app.py b/pkg/core/app.py
index b5d7c809..60cd1d03 100644
--- a/pkg/core/app.py
+++ b/pkg/core/app.py
@@ -197,5 +197,27 @@ class Application:
await self.plugin_mgr.load_plugins()
await self.plugin_mgr.initialize_plugins()
+ case core_entities.LifecycleControlScope.PROVIDER.value:
+ self.logger.info("执行热重载 scope="+scope)
+
+ llm_model_mgr_inst = llm_model_mgr.ModelManager(self)
+ await llm_model_mgr_inst.initialize()
+ self.model_mgr = llm_model_mgr_inst
+
+ llm_session_mgr_inst = llm_session_mgr.SessionManager(self)
+ await llm_session_mgr_inst.initialize()
+ self.sess_mgr = llm_session_mgr_inst
+
+ llm_prompt_mgr_inst = llm_prompt_mgr.PromptManager(self)
+ await llm_prompt_mgr_inst.initialize()
+ self.prompt_mgr = llm_prompt_mgr_inst
+
+ llm_tool_mgr_inst = llm_tool_mgr.ToolManager(self)
+ await llm_tool_mgr_inst.initialize()
+ self.tool_mgr = llm_tool_mgr_inst
+
+ runner_mgr_inst = runnermgr.RunnerManager(self)
+ await runner_mgr_inst.initialize()
+ self.runner_mgr = runner_mgr_inst
case _:
- pass
+ pass
\ No newline at end of file
diff --git a/pkg/core/bootutils/files.py b/pkg/core/bootutils/files.py
index 9bab3826..f52f7b09 100644
--- a/pkg/core/bootutils/files.py
+++ b/pkg/core/bootutils/files.py
@@ -24,6 +24,7 @@ required_paths = [
"data/scenario",
"data/logs",
"data/config",
+ "data/labels",
"plugins"
]
diff --git a/pkg/core/entities.py b/pkg/core/entities.py
index 35e49c15..a57fbbfd 100644
--- a/pkg/core/entities.py
+++ b/pkg/core/entities.py
@@ -23,6 +23,7 @@ class LifecycleControlScope(enum.Enum):
APPLICATION = "application"
PLATFORM = "platform"
PLUGIN = "plugin"
+ PROVIDER = "provider"
class LauncherTypes(enum.Enum):
diff --git a/pkg/core/migrations/m018_xai_config.py b/pkg/core/migrations/m018_xai_config.py
new file mode 100644
index 00000000..bf422451
--- /dev/null
+++ b/pkg/core/migrations/m018_xai_config.py
@@ -0,0 +1,25 @@
+from __future__ import annotations
+
+from .. import migration
+
+
+@migration.migration_class("xai-config", 18)
+class XaiConfigMigration(migration.Migration):
+ """迁移"""
+
+ async def need_migrate(self) -> bool:
+ """判断当前环境是否需要运行此迁移"""
+ return 'xai-chat-completions' not in self.ap.provider_cfg.data['requester']
+
+ async def run(self):
+ """执行迁移"""
+ self.ap.provider_cfg.data['requester']['xai-chat-completions'] = {
+ "base-url": "https://api.x.ai/v1",
+ "args": {},
+ "timeout": 120
+ }
+ self.ap.provider_cfg.data['keys']['xai'] = [
+ "xai-1234567890"
+ ]
+
+ await self.ap.provider_cfg.dump_config()
diff --git a/pkg/core/migrations/m019_zhipuai_config.py b/pkg/core/migrations/m019_zhipuai_config.py
new file mode 100644
index 00000000..67f33340
--- /dev/null
+++ b/pkg/core/migrations/m019_zhipuai_config.py
@@ -0,0 +1,25 @@
+from __future__ import annotations
+
+from .. import migration
+
+
+@migration.migration_class("zhipuai-config", 19)
+class ZhipuaiConfigMigration(migration.Migration):
+ """迁移"""
+
+ async def need_migrate(self) -> bool:
+ """判断当前环境是否需要运行此迁移"""
+ return 'zhipuai-chat-completions' not in self.ap.provider_cfg.data['requester']
+
+ async def run(self):
+ """执行迁移"""
+ self.ap.provider_cfg.data['requester']['zhipuai-chat-completions'] = {
+ "base-url": "https://open.bigmodel.cn/api/paas/v4",
+ "args": {},
+ "timeout": 120
+ }
+ self.ap.provider_cfg.data['keys']['zhipuai'] = [
+ "xxxxxxx"
+ ]
+
+ await self.ap.provider_cfg.dump_config()
diff --git a/pkg/core/stages/migrate.py b/pkg/core/stages/migrate.py
index 82ad4646..5d96d6da 100644
--- a/pkg/core/stages/migrate.py
+++ b/pkg/core/stages/migrate.py
@@ -7,7 +7,7 @@ from .. import migration
from ..migrations import m001_sensitive_word_migration, m002_openai_config_migration, m003_anthropic_requester_cfg_completion, m004_moonshot_cfg_completion
from ..migrations import m005_deepseek_cfg_completion, m006_vision_config, m007_qcg_center_url, m008_ad_fixwin_config_migrate, m009_msg_truncator_cfg
from ..migrations import m010_ollama_requester_config, m011_command_prefix_config, m012_runner_config, m013_http_api_config, m014_force_delay_config
-from ..migrations import m015_gitee_ai_config, m016_dify_service_api, m017_dify_api_timeout_params
+from ..migrations import m015_gitee_ai_config, m016_dify_service_api, m017_dify_api_timeout_params, m018_xai_config, m019_zhipuai_config
from ..migrations import m020_wecom_config
diff --git a/pkg/pipeline/process/handlers/chat.py b/pkg/pipeline/process/handlers/chat.py
index 975b18cc..83bb3335 100644
--- a/pkg/pipeline/process/handlers/chat.py
+++ b/pkg/pipeline/process/handlers/chat.py
@@ -105,7 +105,7 @@ class ChatMessageHandler(handler.MessageHandler):
await self.ap.ctr_mgr.usage.post_query_record(
session_type=query.session.launcher_type.value,
session_id=str(query.session.launcher_id),
- query_ability_provider="QChatGPT.Chat",
+ query_ability_provider="LangBot.Chat",
usage=text_length,
model_name=query.use_model.name,
response_seconds=int(time.time() - start_time),
diff --git a/pkg/platform/manager.py b/pkg/platform/manager.py
index 46171208..f8809750 100644
--- a/pkg/platform/manager.py
+++ b/pkg/platform/manager.py
@@ -50,17 +50,6 @@ class PlatformManager:
adapter=adapter
)
- async def on_stranger_message(event: platform_events.StrangerMessage, adapter: msadapter.MessageSourceAdapter):
-
- await self.ap.query_pool.add_query(
- launcher_type=core_entities.LauncherTypes.PERSON,
- launcher_id=event.sender.id,
- sender_id=event.sender.id,
- message_event=event,
- message_chain=event.message_chain,
- adapter=adapter
- )
-
async def on_group_message(event: platform_events.GroupMessage, adapter: msadapter.MessageSourceAdapter):
await self.ap.query_pool.add_query(
@@ -96,12 +85,6 @@ class PlatformManager:
)
self.adapters.append(adapter_inst)
- if adapter_name == 'yiri-mirai':
- adapter_inst.register_listener(
- platform_events.StrangerMessage,
- on_stranger_message
- )
-
adapter_inst.register_listener(
platform_events.FriendMessage,
on_friend_message
diff --git a/pkg/plugin/context.py b/pkg/plugin/context.py
index 65abc862..2131a59a 100644
--- a/pkg/plugin/context.py
+++ b/pkg/plugin/context.py
@@ -9,6 +9,7 @@ from . import events
from ..provider.tools import entities as tools_entities
from ..core import app
from ..platform.types import message as platform_message
+from ..platform import adapter as platform_adapter
def register(
@@ -113,6 +114,37 @@ class APIHost:
async def initialize(self):
pass
+ # ========== 插件可调用的 API(主程序API) ==========
+
+ def get_platform_adapters(self) -> list[platform_adapter.MessageSourceAdapter]:
+ """获取已启用的消息平台适配器列表
+
+ Returns:
+ list[platform.adapter.MessageSourceAdapter]: 已启用的消息平台适配器列表
+ """
+ return self.ap.platform_mgr.adapters
+
+ async def send_active_message(
+ self,
+ adapter: platform_adapter.MessageSourceAdapter,
+ target_type: str,
+ target_id: str,
+ message: platform_message.MessageChain,
+ ):
+ """发送主动消息
+
+ Args:
+ adapter (platform.adapter.MessageSourceAdapter): 消息平台适配器对象,调用 host.get_platform_adapters() 获取并取用其中某个
+ target_type (str): 目标类型,`person`或`group`
+ target_id (str): 目标ID
+ message (platform.types.MessageChain): 消息链
+ """
+ await adapter.send_message(
+ target_type=target_type,
+ target_id=target_id,
+ message=message,
+ )
+
def require_ver(
self,
ge: str,
@@ -127,16 +159,16 @@ class APIHost:
Returns:
bool: 是否满足要求, False时为无法获取版本号,True时为满足要求,报错为不满足要求
"""
- qchatgpt_version = ""
+ langbot_version = ""
try:
- qchatgpt_version = self.ap.ver_mgr.get_current_version() # 从updater模块获取版本号
+ langbot_version = self.ap.ver_mgr.get_current_version() # 从updater模块获取版本号
except:
return False
- if self.ap.ver_mgr.compare_version_str(qchatgpt_version, ge) < 0 or \
- (self.ap.ver_mgr.compare_version_str(qchatgpt_version, le) > 0):
- raise Exception("LangBot 版本不满足要求,某些功能(可能是由插件提供的)无法正常使用。(要求版本:{}-{},但当前版本:{})".format(ge, le, qchatgpt_version))
+ if self.ap.ver_mgr.compare_version_str(langbot_version, ge) < 0 or \
+ (self.ap.ver_mgr.compare_version_str(langbot_version, le) > 0):
+ raise Exception("LangBot 版本不满足要求,某些功能(可能是由插件提供的)无法正常使用。(要求版本:{}-{},但当前版本:{})".format(ge, le, langbot_version))
return True
diff --git a/pkg/provider/modelmgr/modelmgr.py b/pkg/provider/modelmgr/modelmgr.py
index fb6c703b..fcf5f4b6 100644
--- a/pkg/provider/modelmgr/modelmgr.py
+++ b/pkg/provider/modelmgr/modelmgr.py
@@ -6,7 +6,7 @@ from . import entities, requester
from ...core import app
from . import token
-from .requesters import chatcmpl, anthropicmsgs, moonshotchatcmpl, deepseekchatcmpl, ollamachat, giteeaichatcmpl
+from .requesters import chatcmpl, anthropicmsgs, moonshotchatcmpl, deepseekchatcmpl, ollamachat, giteeaichatcmpl, xaichatcmpl, zhipuaichatcmpl
FETCH_MODEL_LIST_URL = "https://api.qchatgpt.rockchin.top/api/v2/fetch/model_list"
diff --git a/pkg/provider/modelmgr/requesters/xaichatcmpl.py b/pkg/provider/modelmgr/requesters/xaichatcmpl.py
new file mode 100644
index 00000000..62f48bc9
--- /dev/null
+++ b/pkg/provider/modelmgr/requesters/xaichatcmpl.py
@@ -0,0 +1,21 @@
+from __future__ import annotations
+
+import openai
+
+from . import chatcmpl
+from .. import requester
+from ....core import app
+
+
+@requester.requester_class("xai-chat-completions")
+class XaiChatCompletions(chatcmpl.OpenAIChatCompletions):
+ """xAI ChatCompletion API 请求器"""
+
+ client: openai.AsyncClient
+
+ requester_cfg: dict
+
+ def __init__(self, ap: app.Application):
+ self.ap = ap
+
+ self.requester_cfg = self.ap.provider_cfg.data['requester']['xai-chat-completions']
diff --git a/pkg/provider/modelmgr/requesters/zhipuaichatcmpl.py b/pkg/provider/modelmgr/requesters/zhipuaichatcmpl.py
new file mode 100644
index 00000000..b0011001
--- /dev/null
+++ b/pkg/provider/modelmgr/requesters/zhipuaichatcmpl.py
@@ -0,0 +1,21 @@
+from __future__ import annotations
+
+import openai
+
+from ....core import app
+from . import chatcmpl
+from .. import requester
+
+
+@requester.requester_class("zhipuai-chat-completions")
+class ZhipuAIChatCompletions(chatcmpl.OpenAIChatCompletions):
+ """智谱AI ChatCompletion API 请求器"""
+
+ client: openai.AsyncClient
+
+ requester_cfg: dict
+
+ def __init__(self, ap: app.Application):
+ self.ap = ap
+
+ self.requester_cfg = self.ap.provider_cfg.data['requester']['zhipuai-chat-completions']
diff --git a/pkg/provider/runners/difysvapi.py b/pkg/provider/runners/difysvapi.py
index 733d6344..5c3f19bd 100644
--- a/pkg/provider/runners/difysvapi.py
+++ b/pkg/provider/runners/difysvapi.py
@@ -5,6 +5,8 @@ import json
import uuid
import base64
+import aiohttp
+
from .. import runner
from ...core import entities as core_entities
from .. import entities as llm_entities
@@ -97,7 +99,7 @@ class DifyServiceAPIRunner(runner.RequestRunner):
files=files,
timeout=self.ap.provider_cfg.data["dify-service-api"]["chat"]["timeout"],
):
- self.ap.logger.debug("dify-chat-chunk: ", chunk)
+ self.ap.logger.debug("dify-chat-chunk: " + str(chunk))
if chunk['event'] == 'workflow_started':
mode = "workflow"
@@ -149,7 +151,8 @@ class DifyServiceAPIRunner(runner.RequestRunner):
files=files,
timeout=self.ap.provider_cfg.data["dify-service-api"]["chat"]["timeout"],
):
- self.ap.logger.debug("dify-agent-chunk: ", chunk)
+ self.ap.logger.debug("dify-agent-chunk: " + str(chunk))
+
if chunk["event"] in ignored_events:
continue
if chunk["event"] == "agent_thought":
@@ -179,6 +182,21 @@ class DifyServiceAPIRunner(runner.RequestRunner):
],
)
yield msg
+ if chunk['event'] == 'message_file':
+
+ if chunk['type'] == 'image' and chunk['belongs_to'] == 'assistant':
+
+ base_url = self.dify_client.base_url
+
+ if base_url.endswith('/v1'):
+ base_url = base_url[:-3]
+
+ image_url = base_url + chunk['url']
+
+ yield llm_entities.Message(
+ role="assistant",
+ content=[llm_entities.ContentElement.from_image_url(image_url)],
+ )
query.session.using_conversation.uuid = chunk["conversation_id"]
@@ -215,7 +233,7 @@ class DifyServiceAPIRunner(runner.RequestRunner):
files=files,
timeout=self.ap.provider_cfg.data["dify-service-api"]["workflow"]["timeout"],
):
- self.ap.logger.debug("dify-workflow-chunk: ", chunk)
+ self.ap.logger.debug("dify-workflow-chunk: " + str(chunk))
if chunk["event"] in ignored_events:
continue
diff --git a/pkg/utils/announce.py b/pkg/utils/announce.py
index d3297c29..1fb6e166 100644
--- a/pkg/utils/announce.py
+++ b/pkg/utils/announce.py
@@ -62,11 +62,11 @@ class AnnouncementManager:
async def fetch_saved(
self
) -> list[Announcement]:
- if not os.path.exists("res/announcement_saved.json"):
- with open("res/announcement_saved.json", "w", encoding="utf-8") as f:
+ if not os.path.exists("data/labels/announcement_saved.json"):
+ with open("data/labels/announcement_saved.json", "w", encoding="utf-8") as f:
f.write("[]")
- with open("res/announcement_saved.json", "r", encoding="utf-8") as f:
+ with open("data/labels/announcement_saved.json", "r", encoding="utf-8") as f:
content = f.read()
if not content:
@@ -79,7 +79,7 @@ class AnnouncementManager:
content: list[Announcement]
):
- with open("res/announcement_saved.json", "w", encoding="utf-8") as f:
+ with open("data/labels/announcement_saved.json", "w", encoding="utf-8") as f:
f.write(json.dumps([
item.to_dict() for item in content
], indent=4, ensure_ascii=False))
diff --git a/pkg/utils/constants.py b/pkg/utils/constants.py
index 4d0e1896..035fb6b9 100644
--- a/pkg/utils/constants.py
+++ b/pkg/utils/constants.py
@@ -1,4 +1,4 @@
-semantic_version = "v3.4.1.5"
+semantic_version = "v3.4.2.1"
debug_mode = False
diff --git a/templates/metadata/llm-models.json b/templates/metadata/llm-models.json
index 7810aacc..7be765bc 100644
--- a/templates/metadata/llm-models.json
+++ b/templates/metadata/llm-models.json
@@ -115,6 +115,97 @@
"name": "deepseek-coder",
"requester": "deepseek-chat-completions",
"token_mgr": "deepseek"
+ },
+ {
+ "name": "grok-2-latest",
+ "requester": "xai-chat-completions",
+ "token_mgr": "xai"
+ },
+ {
+ "name": "grok-2",
+ "requester": "xai-chat-completions",
+ "token_mgr": "xai"
+ },
+ {
+ "name": "grok-2-vision-1212",
+ "requester": "xai-chat-completions",
+ "token_mgr": "xai",
+ "vision_supported": true
+ },
+ {
+ "name": "grok-2-1212",
+ "requester": "xai-chat-completions",
+ "token_mgr": "xai"
+ },
+ {
+ "name": "grok-vision-beta",
+ "requester": "xai-chat-completions",
+ "token_mgr": "xai",
+ "vision_supported": true
+ },
+ {
+ "name": "grok-beta",
+ "requester": "xai-chat-completions",
+ "token_mgr": "xai"
+ },
+ {
+ "name": "glm-4-plus",
+ "requester": "zhipuai-chat-completions",
+ "token_mgr": "zhipuai"
+ },
+ {
+ "name": "glm-4-0520",
+ "requester": "zhipuai-chat-completions",
+ "token_mgr": "zhipuai"
+ },
+ {
+ "name": "glm-4-air",
+ "requester": "zhipuai-chat-completions",
+ "token_mgr": "zhipuai"
+ },
+ {
+ "name": "glm-4-airx",
+ "requester": "zhipuai-chat-completions",
+ "token_mgr": "zhipuai"
+ },
+ {
+ "name": "glm-4-long",
+ "requester": "zhipuai-chat-completions",
+ "token_mgr": "zhipuai"
+ },
+ {
+ "name": "glm-4-flashx",
+ "requester": "zhipuai-chat-completions",
+ "token_mgr": "zhipuai"
+ },
+ {
+ "name": "glm-4-flash",
+ "requester": "zhipuai-chat-completions",
+ "token_mgr": "zhipuai"
+ },
+ {
+ "name": "glm-4v-plus",
+ "requester": "zhipuai-chat-completions",
+ "token_mgr": "zhipuai",
+ "vision_supported": true
+ },
+ {
+ "name": "glm-4v",
+ "requester": "zhipuai-chat-completions",
+ "token_mgr": "zhipuai",
+ "vision_supported": true
+ },
+ {
+ "name": "glm-4v-flash",
+ "requester": "zhipuai-chat-completions",
+ "token_mgr": "zhipuai",
+ "vision_supported": true
+ },
+ {
+ "name": "glm-zero-preview",
+ "requester": "zhipuai-chat-completions",
+ "token_mgr": "zhipuai",
+ "vision_supported": true
}
]
}
\ No newline at end of file
diff --git a/templates/provider.json b/templates/provider.json
index 30656f8c..1aadbf88 100644
--- a/templates/provider.json
+++ b/templates/provider.json
@@ -16,6 +16,12 @@
],
"gitee-ai": [
"XXXXX"
+ ],
+ "xai": [
+ "xai-1234567890"
+ ],
+ "zhipuai": [
+ "xxxxxxx"
]
},
"requester": {
@@ -50,6 +56,16 @@
"base-url": "https://ai.gitee.com/v1",
"args": {},
"timeout": 120
+ },
+ "xai-chat-completions": {
+ "base-url": "https://api.x.ai/v1",
+ "args": {},
+ "timeout": 120
+ },
+ "zhipuai-chat-completions": {
+ "base-url": "https://open.bigmodel.cn/api/paas/v4",
+ "args": {},
+ "timeout": 120
}
},
"model": "gpt-4o",
diff --git a/templates/schema/provider.json b/templates/schema/provider.json
index d4e453a4..b28597ae 100644
--- a/templates/schema/provider.json
+++ b/templates/schema/provider.json
@@ -22,7 +22,6 @@
"openai": {
"type": "array",
"title": "OpenAI API 密钥",
- "description": "OpenAI API 密钥",
"items": {
"type": "string"
},
@@ -31,7 +30,6 @@
"anthropic": {
"type": "array",
"title": "Anthropic API 密钥",
- "description": "Anthropic API 密钥",
"items": {
"type": "string"
},
@@ -40,7 +38,6 @@
"moonshot": {
"type": "array",
"title": "Moonshot API 密钥",
- "description": "Moonshot API 密钥",
"items": {
"type": "string"
},
@@ -49,7 +46,6 @@
"deepseek": {
"type": "array",
"title": "DeepSeek API 密钥",
- "description": "DeepSeek API 密钥",
"items": {
"type": "string"
},
@@ -57,8 +53,23 @@
},
"gitee": {
"type": "array",
- "title": "Gitee API 密钥",
- "description": "Gitee API 密钥",
+ "title": "Gitee AI API 密钥",
+ "items": {
+ "type": "string"
+ },
+ "default": []
+ },
+ "xai": {
+ "type": "array",
+ "title": "xAI API 密钥",
+ "items": {
+ "type": "string"
+ },
+ "default": []
+ },
+ "zhipuai": {
+ "type": "array",
+ "title": "智谱AI API 密钥",
"items": {
"type": "string"
},
@@ -188,6 +199,42 @@
"default": 120
}
}
+ },
+ "xai-chat-completions": {
+ "type": "object",
+ "title": "xAI API 请求配置",
+ "description": "仅可编辑 URL 和 超时时间,额外请求参数不支持可视化编辑,请到编辑器编辑",
+ "properties": {
+ "base-url": {
+ "type": "string",
+ "title": "API URL"
+ },
+ "args": {
+ "type": "object"
+ },
+ "timeout": {
+ "type": "number",
+ "title": "API 请求超时时间",
+ "default": 120
+ }
+ }
+ },
+ "zhipuai-chat-completions": {
+ "type": "object",
+ "title": "智谱AI API 请求配置",
+ "description": "仅可编辑 URL 和 超时时间,额外请求参数不支持可视化编辑,请到编辑器编辑",
+ "properties": {
+ "base-url": {
+ "type": "string",
+ "title": "API URL"
+ },
+ "args": {
+ "type": "object"
+ },
+ "timeout": {
+ "type": "number"
+ }
+ }
}
}
},
diff --git a/web/src/App.vue b/web/src/App.vue
index e1fb3b16..926ad3a7 100644
--- a/web/src/App.vue
+++ b/web/src/App.vue
@@ -79,6 +79,12 @@
重载插件
+
+
+
+ 重载 LLM 管理器
+
+
@@ -169,7 +175,8 @@ function openDocs() {
const reloadScopeLabel = {
'platform': "消息平台",
- 'plugin': "插件"
+ 'plugin': "插件",
+ 'provider': "LLM 管理器"
}
function reload(scope) {