diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml
new file mode 100644
index 00000000..a03f51ef
--- /dev/null
+++ b/.github/workflows/publish-to-pypi.yml
@@ -0,0 +1,46 @@
+name: Build and Publish to PyPI
+
+on:
+ workflow_dispatch:
+ release:
+ types: [published]
+
+jobs:
+ build-and-publish:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ id-token: write # Required for trusted publishing to PyPI
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ persist-credentials: false
+
+ - name: Set up Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '22'
+
+ - name: Build frontend
+ run: |
+ cd web
+ npm install -g pnpm
+ pnpm install
+ pnpm build
+ mkdir -p ../src/langbot/web/out
+ cp -r out ../src/langbot/web/
+
+ - name: Install the latest version of uv
+ uses: astral-sh/setup-uv@v6
+ with:
+ version: "latest"
+
+ - name: Build package
+ run: |
+ uv build
+
+ - name: Publish to PyPI
+ run: |
+ uv publish --token ${{ secrets.PYPI_TOKEN }}
diff --git a/.gitignore b/.gitignore
index ecfb7207..2a0b6245 100644
--- a/.gitignore
+++ b/.gitignore
@@ -47,3 +47,9 @@ uv.lock
plugins.bak
coverage.xml
.coverage
+src/langbot/web/
+
+# Build artifacts
+/dist
+/build
+*.egg-info
diff --git a/README.md b/README.md
index 754f6258..94cc3870 100644
--- a/README.md
+++ b/README.md
@@ -31,6 +31,25 @@ LangBot 是一个开源的大语言模型原生即时通信机器人开发平台
## 📦 开始使用
+#### 快速体验(推荐)
+
+使用 `uvx` 一键启动(无需安装):
+
+```bash
+uvx langbot
+```
+
+或使用 `pip` 安装后运行:
+
+```bash
+pip install langbot
+langbot
+```
+
+访问 http://localhost:5300 即可开始使用。
+
+详细文档[PyPI 安装](docs/PYPI_INSTALLATION.md)。
+
#### Docker Compose 部署
```bash
diff --git a/README_EN.md b/README_EN.md
index e020beba..79117d44 100644
--- a/README_EN.md
+++ b/README_EN.md
@@ -25,6 +25,25 @@ LangBot is an open-source LLM native instant messaging robot development platfor
## 📦 Getting Started
+#### Quick Start (Recommended)
+
+Use `uvx` to start with one command (no installation required):
+
+```bash
+uvx langbot
+```
+
+Or install with `pip` and run:
+
+```bash
+pip install langbot
+langbot
+```
+
+Visit http://localhost:5300 to start using it.
+
+Detailed documentation [PyPI Installation](docs/PYPI_INSTALLATION.md).
+
#### Docker Compose Deployment
```bash
diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml
index f9bb6ffa..6d2ec1fc 100644
--- a/docker/docker-compose.yaml
+++ b/docker/docker-compose.yaml
@@ -7,6 +7,7 @@ services:
langbot_plugin_runtime:
image: rockchin/langbot:latest
container_name: langbot_plugin_runtime
+ platform: linux/amd64 # For Apple Silicon compatibility
volumes:
- ./data/plugins:/app/data/plugins
ports:
@@ -21,6 +22,7 @@ services:
langbot:
image: rockchin/langbot:latest
container_name: langbot
+ platform: linux/amd64 # For Apple Silicon compatibility
volumes:
- ./data:/app/data
- ./plugins:/app/plugins
diff --git a/docs/PYPI_INSTALLATION.md b/docs/PYPI_INSTALLATION.md
new file mode 100644
index 00000000..1144d5cb
--- /dev/null
+++ b/docs/PYPI_INSTALLATION.md
@@ -0,0 +1,117 @@
+# LangBot PyPI Package Installation
+
+## Quick Start with uvx
+
+The easiest way to run LangBot is using `uvx` (recommended for quick testing):
+
+```bash
+uvx langbot
+```
+
+This will automatically download and run the latest version of LangBot.
+
+## Install with pip/uv
+
+You can also install LangBot as a regular Python package:
+
+```bash
+# Using pip
+pip install langbot
+
+# Using uv
+uv pip install langbot
+```
+
+Then run it:
+
+```bash
+langbot
+```
+
+Or using Python module syntax:
+
+```bash
+python -m langbot
+```
+
+## Installation with Frontend
+
+When published to PyPI, the LangBot package includes the pre-built frontend files. You don't need to build the frontend separately.
+
+## Data Directory
+
+When running LangBot as a package, it will create a `data/` directory in your current working directory to store configuration, logs, and other runtime data. You can run LangBot from any directory, and it will set up its data directory there.
+
+## Command Line Options
+
+LangBot supports the following command line options:
+
+- `--standalone-runtime`: Use standalone plugin runtime
+- `--debug`: Enable debug mode
+
+Example:
+
+```bash
+langbot --debug
+```
+
+## Comparison with Other Installation Methods
+
+### PyPI Package (uvx/pip)
+- **Pros**: Easy to install and update, no need to clone repository or build frontend
+- **Cons**: Less flexible for development/customization
+
+### Docker
+- **Pros**: Isolated environment, easy deployment
+- **Cons**: Requires Docker
+
+### Manual Source Installation
+- **Pros**: Full control, easy to customize and develop
+- **Cons**: Requires building frontend, managing dependencies manually
+
+## Development
+
+If you want to contribute or customize LangBot, you should still use the manual installation method by cloning the repository:
+
+```bash
+git clone https://github.com/langbot-app/LangBot
+cd LangBot
+uv sync
+cd web
+npm install
+npm run build
+cd ..
+uv run main.py
+```
+
+## Updating
+
+To update to the latest version:
+
+```bash
+# With pip
+pip install --upgrade langbot
+
+# With uv
+uv pip install --upgrade langbot
+
+# With uvx (automatically uses latest)
+uvx langbot
+```
+
+## System Requirements
+
+- Python 3.10.1 or higher
+- Operating System: Linux, macOS, or Windows
+
+## Differences from Source Installation
+
+When running LangBot from the PyPI package (via uvx or pip), there are a few behavioral differences compared to running from source:
+
+1. **Version Check**: The package version does not prompt for user input when the Python version is incompatible. It simply prints an error message and exits. This makes it compatible with non-interactive environments like containers and CI/CD.
+
+2. **Working Directory**: The package version does not require being run from the LangBot project root. You can run `langbot` from any directory, and it will create a `data/` directory in your current working directory.
+
+3. **Frontend Files**: The frontend is pre-built and included in the package, so you don't need to run `npm build` separately.
+
+These differences are intentional to make the package more user-friendly and suitable for various deployment scenarios.
diff --git a/libs/dify_service_api/test.py b/libs/dify_service_api/test.py
deleted file mode 100644
index 7da04f6b..00000000
--- a/libs/dify_service_api/test.py
+++ /dev/null
@@ -1,45 +0,0 @@
-from v1 import client # type: ignore
-
-import asyncio
-
-import os
-import json
-
-
-class TestDifyClient:
- async def test_chat_messages(self):
- cln = client.AsyncDifyServiceClient(api_key=os.getenv('DIFY_API_KEY'), base_url=os.getenv('DIFY_BASE_URL'))
-
- async for chunk in cln.chat_messages(inputs={}, query='调用工具查看现在几点?', user='test'):
- print(json.dumps(chunk, ensure_ascii=False, indent=4))
-
- async def test_upload_file(self):
- cln = client.AsyncDifyServiceClient(api_key=os.getenv('DIFY_API_KEY'), base_url=os.getenv('DIFY_BASE_URL'))
-
- file_bytes = open('img.png', 'rb').read()
-
- print(type(file_bytes))
-
- file = ('img2.png', file_bytes, 'image/png')
-
- resp = await cln.upload_file(file=file, user='test')
- print(json.dumps(resp, ensure_ascii=False, indent=4))
-
- async def test_workflow_run(self):
- cln = client.AsyncDifyServiceClient(api_key=os.getenv('DIFY_API_KEY'), base_url=os.getenv('DIFY_BASE_URL'))
-
- # resp = await cln.workflow_run(inputs={}, user="test")
- # # print(json.dumps(resp, ensure_ascii=False, indent=4))
- # print(resp)
- chunks = []
-
- ignored_events = ['text_chunk']
- async for chunk in cln.workflow_run(inputs={}, user='test'):
- if chunk['event'] in ignored_events:
- continue
- chunks.append(chunk)
- print(json.dumps(chunks, ensure_ascii=False, indent=4))
-
-
-if __name__ == '__main__':
- asyncio.run(TestDifyClient().test_chat_messages())
diff --git a/main.py b/main.py
index 708a01cd..9e1f5c31 100644
--- a/main.py
+++ b/main.py
@@ -1,117 +1,3 @@
-import asyncio
-import argparse
-# LangBot 终端启动入口
-# 在此层级解决依赖项检查。
-# LangBot/main.py
+import langbot.__main__
-asciiart = r"""
- _ ___ _
-| | __ _ _ _ __ _| _ ) ___| |_
-| |__/ _` | ' \/ _` | _ \/ _ \ _|
-|____\__,_|_||_\__, |___/\___/\__|
- |___/
-
-⭐️ Open Source 开源地址: https://github.com/langbot-app/LangBot
-📖 Documentation 文档地址: https://docs.langbot.app
-"""
-
-
-async def main_entry(loop: asyncio.AbstractEventLoop):
- parser = argparse.ArgumentParser(description='LangBot')
- parser.add_argument(
- '--standalone-runtime',
- action='store_true',
- help='Use standalone plugin runtime / 使用独立插件运行时',
- default=False,
- )
- parser.add_argument('--debug', action='store_true', help='Debug mode / 调试模式', default=False)
- args = parser.parse_args()
-
- if args.standalone_runtime:
- from pkg.utils import platform
-
- platform.standalone_runtime = True
-
- if args.debug:
- from pkg.utils import constants
-
- constants.debug_mode = True
-
- print(asciiart)
-
- import sys
-
- # 检查依赖
-
- from pkg.core.bootutils import deps
-
- missing_deps = await deps.check_deps()
-
- if missing_deps:
- print('以下依赖包未安装,将自动安装,请完成后重启程序:')
- print(
- 'These dependencies are missing, they will be installed automatically, please restart the program after completion:'
- )
- for dep in missing_deps:
- print('-', dep)
- await deps.install_deps(missing_deps)
- print('已自动安装缺失的依赖包,请重启程序。')
- print('The missing dependencies have been installed automatically, please restart the program.')
- sys.exit(0)
-
- # # 检查pydantic版本,如果没有 pydantic.v1,则把 pydantic 映射为 v1
- # import pydantic.version
-
- # if pydantic.version.VERSION < '2.0':
- # import pydantic
-
- # sys.modules['pydantic.v1'] = pydantic
-
- # 检查配置文件
-
- from pkg.core.bootutils import files
-
- generated_files = await files.generate_files()
-
- if generated_files:
- print('以下文件不存在,已自动生成:')
- print('Following files do not exist and have been automatically generated:')
- for file in generated_files:
- print('-', file)
-
- from pkg.core import boot
-
- await boot.main(loop)
-
-
-if __name__ == '__main__':
- import os
- import sys
-
- # 必须大于 3.10.1
- if sys.version_info < (3, 10, 1):
- print('需要 Python 3.10.1 及以上版本,当前 Python 版本为:', sys.version)
- input('按任意键退出...')
- print('Your Python version is not supported. Please exit the program by pressing any key.')
- exit(1)
-
- # Check if the current directory is the LangBot project root directory
- invalid_pwd = False
-
- if not os.path.exists('main.py'):
- invalid_pwd = True
- else:
- with open('main.py', 'r', encoding='utf-8') as f:
- content = f.read()
- if 'LangBot/main.py' not in content:
- invalid_pwd = True
- if invalid_pwd:
- print('请在 LangBot 项目根目录下以命令形式运行此程序。')
- input('按任意键退出...')
- print('Please run this program in the LangBot project root directory in command form.')
- print('Press any key to exit...')
- exit(1)
-
- loop = asyncio.new_event_loop()
-
- loop.run_until_complete(main_entry(loop))
+langbot.__main__.main()
diff --git a/pkg/utils/announce.py b/pkg/utils/announce.py
deleted file mode 100644
index 8778a04f..00000000
--- a/pkg/utils/announce.py
+++ /dev/null
@@ -1,115 +0,0 @@
-from __future__ import annotations
-
-import json
-import typing
-import os
-import base64
-import logging
-
-import pydantic
-import requests
-
-from ..core import app
-
-
-class Announcement(pydantic.BaseModel):
- """公告"""
-
- id: int
-
- time: str
-
- timestamp: int
-
- content: str
-
- enabled: typing.Optional[bool] = True
-
- def to_dict(self) -> dict:
- return {
- 'id': self.id,
- 'time': self.time,
- 'timestamp': self.timestamp,
- 'content': self.content,
- 'enabled': self.enabled,
- }
-
-
-class AnnouncementManager:
- """公告管理器"""
-
- ap: app.Application = None
-
- def __init__(self, ap: app.Application):
- self.ap = ap
-
- async def fetch_all(self) -> list[Announcement]:
- """获取所有公告"""
- try:
- resp = requests.get(
- url='https://api.github.com/repos/langbot-app/LangBot/contents/res/announcement.json',
- proxies=self.ap.proxy_mgr.get_forward_proxies(),
- timeout=5,
- )
- resp.raise_for_status() # 检查请求是否成功
- obj_json = resp.json()
- b64_content = obj_json['content']
- # 解码
- content = base64.b64decode(b64_content).decode('utf-8')
-
- return [Announcement(**item) for item in json.loads(content)]
- except (requests.RequestException, json.JSONDecodeError, KeyError) as e:
- self.ap.logger.warning(f'获取公告失败: {e}')
- pass
- return [] # 请求失败时返回空列表
-
- async def fetch_saved(self) -> list[Announcement]:
- 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('data/labels/announcement_saved.json', 'r', encoding='utf-8') as f:
- content = f.read()
-
- if not content:
- content = '[]'
-
- return [Announcement(**item) for item in json.loads(content)]
-
- async def write_saved(self, content: list[Announcement]):
- 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))
-
- async def fetch_new(self) -> list[Announcement]:
- """获取新公告"""
- all = await self.fetch_all()
- saved = await self.fetch_saved()
-
- to_show: list[Announcement] = []
-
- for item in all:
- # 遍历saved检查是否有相同id的公告
- for saved_item in saved:
- if saved_item.id == item.id:
- break
- else:
- if item.enabled:
- # 没有相同id的公告
- to_show.append(item)
-
- await self.write_saved(all)
- return to_show
-
- async def show_announcements(self) -> typing.Tuple[str, int]:
- """显示公告"""
- try:
- announcements = await self.fetch_new()
- ann_text = ''
- for ann in announcements:
- ann_text += f'[公告] {ann.time}: {ann.content}\n'
-
- # TODO statistics
-
- return ann_text, logging.INFO
- except Exception as e:
- return f'获取公告时出错: {e}', logging.WARNING
diff --git a/pyproject.toml b/pyproject.toml
index 0dbe4fef..a5549e5c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,8 +1,9 @@
[project]
name = "langbot"
-version = "4.5.0"
+version = "4.6.0-beta.2"
description = "Easy-to-use global IM bot platform designed for LLM era"
readme = "README.md"
+license-files = ["LICENSE"]
requires-python = ">=3.10.1,<4.0"
dependencies = [
"aiocqhttp>=1.4.4",
@@ -45,7 +46,6 @@ dependencies = [
"urllib3>=2.4.0",
"websockets>=15.0.1",
"python-socks>=2.7.1", # dingtalk missing dependency
- "taskgroup==0.0.0a4", # graingert/taskgroup#20
"pip>=25.1.1",
"ruff>=0.11.9",
"pre-commit>=4.2.0",
@@ -85,11 +85,10 @@ keywords = [
"onebot",
]
classifiers = [
- "Development Status :: 3 - Alpha",
+ "Development Status :: 5 - Production/Stable",
"Framework :: AsyncIO",
"Framework :: Robot Framework",
"Framework :: Robot Framework :: Library",
- "License :: OSI Approved :: AGPL-3 License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Topic :: Communications :: Chat",
@@ -100,6 +99,16 @@ Homepage = "https://langbot.app"
Documentation = "https://docs.langbot.app"
Repository = "https://github.com/langbot-app/LangBot"
+[project.scripts]
+langbot = "langbot.__main__:main"
+
+[build-system]
+requires = ["setuptools>=61.0", "wheel"]
+build-backend = "setuptools.build_meta"
+
+[tool.setuptools]
+package-data = { "langbot" = ["templates/**", "pkg/provider/modelmgr/requesters/*", "pkg/platform/sources/*", "web/out/**"] }
+
[dependency-groups]
dev = [
"pre-commit>=4.2.0",
diff --git a/pytest.ini b/pytest.ini
index 80cda02e..ef5b705d 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -26,7 +26,7 @@ markers =
# Coverage options (when using pytest-cov)
[coverage:run]
-source = pkg
+source = langbot.pkg
omit =
*/tests/*
*/test_*.py
diff --git a/src/langbot/__init__.py b/src/langbot/__init__.py
new file mode 100644
index 00000000..fdaf8272
--- /dev/null
+++ b/src/langbot/__init__.py
@@ -0,0 +1,3 @@
+"""LangBot - Easy-to-use global IM bot platform designed for LLM era"""
+
+__version__ = '4.6.0-beta.2'
diff --git a/src/langbot/__main__.py b/src/langbot/__main__.py
new file mode 100644
index 00000000..b94500e7
--- /dev/null
+++ b/src/langbot/__main__.py
@@ -0,0 +1,104 @@
+"""LangBot entry point for package execution"""
+
+import asyncio
+import argparse
+import sys
+import os
+
+# ASCII art banner
+asciiart = r"""
+ _ ___ _
+| | __ _ _ _ __ _| _ ) ___| |_
+| |__/ _` | ' \/ _` | _ \/ _ \ _|
+|____\__,_|_||_\__, |___/\___/\__|
+ |___/
+
+⭐️ Open Source 开源地址: https://github.com/langbot-app/LangBot
+📖 Documentation 文档地址: https://docs.langbot.app
+"""
+
+
+async def main_entry(loop: asyncio.AbstractEventLoop):
+ """Main entry point for LangBot"""
+ parser = argparse.ArgumentParser(description='LangBot')
+ parser.add_argument(
+ '--standalone-runtime',
+ action='store_true',
+ help='Use standalone plugin runtime / 使用独立插件运行时',
+ default=False,
+ )
+ parser.add_argument('--debug', action='store_true', help='Debug mode / 调试模式', default=False)
+ args = parser.parse_args()
+
+ if args.standalone_runtime:
+ from langbot.pkg.utils import platform
+
+ platform.standalone_runtime = True
+
+ if args.debug:
+ from langbot.pkg.utils import constants
+
+ constants.debug_mode = True
+
+ print(asciiart)
+
+ # Check dependencies
+ from langbot.pkg.core.bootutils import deps
+
+ missing_deps = await deps.check_deps()
+
+ if missing_deps:
+ print('以下依赖包未安装,将自动安装,请完成后重启程序:')
+ print(
+ 'These dependencies are missing, they will be installed automatically, please restart the program after completion:'
+ )
+ for dep in missing_deps:
+ print('-', dep)
+ await deps.install_deps(missing_deps)
+ print('已自动安装缺失的依赖包,请重启程序。')
+ print('The missing dependencies have been installed automatically, please restart the program.')
+ sys.exit(0)
+
+ # Check configuration files
+ from langbot.pkg.core.bootutils import files
+
+ generated_files = await files.generate_files()
+
+ if generated_files:
+ print('以下文件不存在,已自动生成:')
+ print('Following files do not exist and have been automatically generated:')
+ for file in generated_files:
+ print('-', file)
+
+ from langbot.pkg.core import boot
+
+ await boot.main(loop)
+
+
+def main():
+ """Main function to be called by console script entry point"""
+ # Check Python version
+ if sys.version_info < (3, 10, 1):
+ print('需要 Python 3.10.1 及以上版本,当前 Python 版本为:', sys.version)
+ print('Your Python version is not supported.')
+ print('Python 3.10.1 or higher is required. Current version:', sys.version)
+ sys.exit(1)
+
+ # Set up the working directory
+ # When installed as a package, we need to handle the working directory differently
+ # We'll create data directory in current working directory if not exists
+ os.makedirs('data', exist_ok=True)
+
+ loop = asyncio.new_event_loop()
+
+ try:
+ loop.run_until_complete(main_entry(loop))
+ except KeyboardInterrupt:
+ print('\n正在退出...')
+ print('Exiting...')
+ finally:
+ loop.close()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/libs/LICENSE b/src/langbot/libs/LICENSE
similarity index 100%
rename from libs/LICENSE
rename to src/langbot/libs/LICENSE
diff --git a/libs/README.md b/src/langbot/libs/README.md
similarity index 100%
rename from libs/README.md
rename to src/langbot/libs/README.md
diff --git a/libs/coze_server_api/__init__.py b/src/langbot/libs/coze_server_api/__init__.py
similarity index 100%
rename from libs/coze_server_api/__init__.py
rename to src/langbot/libs/coze_server_api/__init__.py
diff --git a/libs/coze_server_api/client.py b/src/langbot/libs/coze_server_api/client.py
similarity index 60%
rename from libs/coze_server_api/client.py
rename to src/langbot/libs/coze_server_api/client.py
index 8d35a102..54fb4749 100644
--- a/libs/coze_server_api/client.py
+++ b/src/langbot/libs/coze_server_api/client.py
@@ -7,10 +7,8 @@ import os
from pathlib import Path
-
-
class AsyncCozeAPIClient:
- def __init__(self, api_key: str, api_base: str = "https://api.coze.cn"):
+ def __init__(self, api_key: str, api_base: str = 'https://api.coze.cn'):
self.api_key = api_key
self.api_base = api_base
self.session = None
@@ -24,13 +22,11 @@ class AsyncCozeAPIClient:
"""退出时自动关闭会话"""
await self.close()
-
-
async def coze_session(self):
"""确保HTTP session存在"""
if self.session is None:
connector = aiohttp.TCPConnector(
- ssl=False if self.api_base.startswith("http://") else True,
+ ssl=False if self.api_base.startswith('http://') else True,
limit=100,
limit_per_host=30,
keepalive_timeout=30,
@@ -42,12 +38,10 @@ class AsyncCozeAPIClient:
sock_read=120,
)
headers = {
- "Authorization": f"Bearer {self.api_key}",
- "Accept": "text/event-stream",
+ 'Authorization': f'Bearer {self.api_key}',
+ 'Accept': 'text/event-stream',
}
- self.session = aiohttp.ClientSession(
- headers=headers, timeout=timeout, connector=connector
- )
+ self.session = aiohttp.ClientSession(headers=headers, timeout=timeout, connector=connector)
return self.session
async def close(self):
@@ -63,15 +57,15 @@ class AsyncCozeAPIClient:
# 处理 Path 对象
if isinstance(file, Path):
if not file.exists():
- raise ValueError(f"File not found: {file}")
- with open(file, "rb") as f:
+ raise ValueError(f'File not found: {file}')
+ with open(file, 'rb') as f:
file = f.read()
# 处理文件路径字符串
elif isinstance(file, str):
if not os.path.isfile(file):
- raise ValueError(f"File not found: {file}")
- with open(file, "rb") as f:
+ raise ValueError(f'File not found: {file}')
+ with open(file, 'rb') as f:
file = f.read()
# 处理文件对象
@@ -79,43 +73,39 @@ class AsyncCozeAPIClient:
file = file.read()
session = await self.coze_session()
- url = f"{self.api_base}/v1/files/upload"
+ url = f'{self.api_base}/v1/files/upload'
try:
file_io = io.BytesIO(file)
async with session.post(
url,
data={
- "file": file_io,
+ 'file': file_io,
},
timeout=aiohttp.ClientTimeout(total=60),
) as response:
if response.status == 401:
- raise Exception("Coze API 认证失败,请检查 API Key 是否正确")
+ raise Exception('Coze API 认证失败,请检查 API Key 是否正确')
response_text = await response.text()
-
if response.status != 200:
- raise Exception(
- f"文件上传失败,状态码: {response.status}, 响应: {response_text}"
- )
+ raise Exception(f'文件上传失败,状态码: {response.status}, 响应: {response_text}')
try:
result = await response.json()
except json.JSONDecodeError:
- raise Exception(f"文件上传响应解析失败: {response_text}")
+ raise Exception(f'文件上传响应解析失败: {response_text}')
- if result.get("code") != 0:
- raise Exception(f"文件上传失败: {result.get('msg', '未知错误')}")
+ if result.get('code') != 0:
+ raise Exception(f'文件上传失败: {result.get("msg", "未知错误")}')
- file_id = result["data"]["id"]
+ file_id = result['data']['id']
return file_id
except asyncio.TimeoutError:
- raise Exception("文件上传超时")
+ raise Exception('文件上传超时')
except Exception as e:
- raise Exception(f"文件上传失败: {str(e)}")
-
+ raise Exception(f'文件上传失败: {str(e)}')
async def chat_messages(
self,
@@ -139,22 +129,21 @@ class AsyncCozeAPIClient:
timeout: 超时时间
"""
session = await self.coze_session()
- url = f"{self.api_base}/v3/chat"
+ url = f'{self.api_base}/v3/chat'
payload = {
- "bot_id": bot_id,
- "user_id": user_id,
- "stream": stream,
- "auto_save_history": auto_save_history,
+ 'bot_id': bot_id,
+ 'user_id': user_id,
+ 'stream': stream,
+ 'auto_save_history': auto_save_history,
}
if additional_messages:
- payload["additional_messages"] = additional_messages
+ payload['additional_messages'] = additional_messages
params = {}
if conversation_id:
- params["conversation_id"] = conversation_id
-
+ params['conversation_id'] = conversation_id
try:
async with session.post(
@@ -164,29 +153,25 @@ class AsyncCozeAPIClient:
timeout=aiohttp.ClientTimeout(total=timeout),
) as response:
if response.status == 401:
- raise Exception("Coze API 认证失败,请检查 API Key 是否正确")
+ raise Exception('Coze API 认证失败,请检查 API Key 是否正确')
if response.status != 200:
- raise Exception(f"Coze API 流式请求失败,状态码: {response.status}")
-
+ raise Exception(f'Coze API 流式请求失败,状态码: {response.status}')
async for chunk in response.content:
- chunk = chunk.decode("utf-8")
+ chunk = chunk.decode('utf-8')
if chunk != '\n':
- if chunk.startswith("event:"):
- chunk_type = chunk.replace("event:", "", 1).strip()
- elif chunk.startswith("data:"):
- chunk_data = chunk.replace("data:", "", 1).strip()
+ if chunk.startswith('event:'):
+ chunk_type = chunk.replace('event:', '', 1).strip()
+ elif chunk.startswith('data:'):
+ chunk_data = chunk.replace('data:', '', 1).strip()
else:
- yield {"event": chunk_type, "data": json.loads(chunk_data) if chunk_data else {}} # 处理本地部署时,接口返回的data为空值
+ yield {
+ 'event': chunk_type,
+ 'data': json.loads(chunk_data) if chunk_data else {},
+ } # 处理本地部署时,接口返回的data为空值
except asyncio.TimeoutError:
- raise Exception(f"Coze API 流式请求超时 ({timeout}秒)")
+ raise Exception(f'Coze API 流式请求超时 ({timeout}秒)')
except Exception as e:
- raise Exception(f"Coze API 流式请求失败: {str(e)}")
-
-
-
-
-
-
+ raise Exception(f'Coze API 流式请求失败: {str(e)}')
diff --git a/libs/dify_service_api/README.md b/src/langbot/libs/dify_service_api/README.md
similarity index 100%
rename from libs/dify_service_api/README.md
rename to src/langbot/libs/dify_service_api/README.md
diff --git a/libs/dify_service_api/__init__.py b/src/langbot/libs/dify_service_api/__init__.py
similarity index 100%
rename from libs/dify_service_api/__init__.py
rename to src/langbot/libs/dify_service_api/__init__.py
diff --git a/libs/dify_service_api/v1/__init__.py b/src/langbot/libs/dify_service_api/v1/__init__.py
similarity index 100%
rename from libs/dify_service_api/v1/__init__.py
rename to src/langbot/libs/dify_service_api/v1/__init__.py
diff --git a/libs/dify_service_api/v1/client.py b/src/langbot/libs/dify_service_api/v1/client.py
similarity index 100%
rename from libs/dify_service_api/v1/client.py
rename to src/langbot/libs/dify_service_api/v1/client.py
diff --git a/libs/dify_service_api/v1/client_test.py b/src/langbot/libs/dify_service_api/v1/client_test.py
similarity index 100%
rename from libs/dify_service_api/v1/client_test.py
rename to src/langbot/libs/dify_service_api/v1/client_test.py
diff --git a/libs/dify_service_api/v1/errors.py b/src/langbot/libs/dify_service_api/v1/errors.py
similarity index 100%
rename from libs/dify_service_api/v1/errors.py
rename to src/langbot/libs/dify_service_api/v1/errors.py
diff --git a/libs/dingtalk_api/EchoHandler.py b/src/langbot/libs/dingtalk_api/EchoHandler.py
similarity index 100%
rename from libs/dingtalk_api/EchoHandler.py
rename to src/langbot/libs/dingtalk_api/EchoHandler.py
diff --git a/libs/dingtalk_api/__init__.py b/src/langbot/libs/dingtalk_api/__init__.py
similarity index 100%
rename from libs/dingtalk_api/__init__.py
rename to src/langbot/libs/dingtalk_api/__init__.py
diff --git a/libs/dingtalk_api/api.py b/src/langbot/libs/dingtalk_api/api.py
similarity index 96%
rename from libs/dingtalk_api/api.py
rename to src/langbot/libs/dingtalk_api/api.py
index 03caabfd..abd68a40 100644
--- a/libs/dingtalk_api/api.py
+++ b/src/langbot/libs/dingtalk_api/api.py
@@ -194,28 +194,23 @@ class DingTalkClient:
'Type': 'richText',
'Elements': [], # 按顺序存储所有元素
'SimpleContent': '', # 兼容字段:纯文本内容
- 'SimplePicture': '' # 兼容字段:第一张图片
+ 'SimplePicture': '', # 兼容字段:第一张图片
}
# 先收集所有文本和图片占位符
text_elements = []
- image_placeholders = []
# 解析富文本内容,保持原始顺序
for item in data['richText']:
-
# 处理文本内容
- if 'text' in item and item['text'] != "\n":
- element = {
- 'Type': 'text',
- 'Content': item['text']
- }
+ if 'text' in item and item['text'] != '\n':
+ element = {'Type': 'text', 'Content': item['text']}
rich_content['Elements'].append(element)
text_elements.append(item['text'])
# 检查是否是图片元素 - 根据钉钉API的实际结构调整
# 钉钉富文本中的图片通常有特定标识,可能需要根据实际返回调整
- elif item.get("type") == "picture":
+ elif item.get('type') == 'picture':
# 创建图片占位符
element = {
'Type': 'image_placeholder',
@@ -232,10 +227,7 @@ class DingTalkClient:
if element['Type'] == 'image_placeholder':
if image_index < len(image_list) and image_list[image_index]:
image_url = await self.download_image(image_list[image_index])
- new_elements.append({
- 'Type': 'image',
- 'Picture': image_url
- })
+ new_elements.append({'Type': 'image', 'Picture': image_url})
image_index += 1
else:
# 如果没有对应的图片,保留占位符或跳过
@@ -245,7 +237,6 @@ class DingTalkClient:
rich_content['Elements'] = new_elements
-
# 设置兼容字段
all_texts = [elem['Content'] for elem in rich_content['Elements'] if elem.get('Type') == 'text']
rich_content['SimpleContent'] = '\n'.join(all_texts) if all_texts else ''
@@ -261,8 +252,6 @@ class DingTalkClient:
if all_images:
message_data['Picture'] = all_images[0]
-
-
elif incoming_message.message_type == 'text':
message_data['Content'] = incoming_message.get_text_list()[0]
diff --git a/libs/dingtalk_api/dingtalkevent.py b/src/langbot/libs/dingtalk_api/dingtalkevent.py
similarity index 99%
rename from libs/dingtalk_api/dingtalkevent.py
rename to src/langbot/libs/dingtalk_api/dingtalkevent.py
index f45264c6..29322bcb 100644
--- a/libs/dingtalk_api/dingtalkevent.py
+++ b/src/langbot/libs/dingtalk_api/dingtalkevent.py
@@ -43,7 +43,6 @@ class DingTalkEvent(dict):
def name(self):
return self.get('Name', '')
-
@property
def conversation(self):
return self.get('conversation_type', '')
diff --git a/libs/official_account_api/__init__.py b/src/langbot/libs/official_account_api/__init__.py
similarity index 100%
rename from libs/official_account_api/__init__.py
rename to src/langbot/libs/official_account_api/__init__.py
diff --git a/libs/official_account_api/api.py b/src/langbot/libs/official_account_api/api.py
similarity index 99%
rename from libs/official_account_api/api.py
rename to src/langbot/libs/official_account_api/api.py
index 569196f8..57d41c49 100644
--- a/libs/official_account_api/api.py
+++ b/src/langbot/libs/official_account_api/api.py
@@ -1,12 +1,12 @@
# 微信公众号的加解密算法与企业微信一样,所以直接使用企业微信的加解密算法文件
import time
import traceback
-from libs.wecom_api.WXBizMsgCrypt3 import WXBizMsgCrypt
+from langbot.libs.wecom_api.WXBizMsgCrypt3 import WXBizMsgCrypt
import xml.etree.ElementTree as ET
from quart import Quart, request
import hashlib
from typing import Callable
-from .oaevent import OAEvent
+from langbot.libs.official_account_api.oaevent import OAEvent
import asyncio
diff --git a/libs/official_account_api/oaevent.py b/src/langbot/libs/official_account_api/oaevent.py
similarity index 100%
rename from libs/official_account_api/oaevent.py
rename to src/langbot/libs/official_account_api/oaevent.py
diff --git a/libs/qq_official_api/__init__.py b/src/langbot/libs/qq_official_api/__init__.py
similarity index 100%
rename from libs/qq_official_api/__init__.py
rename to src/langbot/libs/qq_official_api/__init__.py
diff --git a/libs/qq_official_api/api.py b/src/langbot/libs/qq_official_api/api.py
similarity index 100%
rename from libs/qq_official_api/api.py
rename to src/langbot/libs/qq_official_api/api.py
diff --git a/libs/qq_official_api/qqofficialevent.py b/src/langbot/libs/qq_official_api/qqofficialevent.py
similarity index 100%
rename from libs/qq_official_api/qqofficialevent.py
rename to src/langbot/libs/qq_official_api/qqofficialevent.py
diff --git a/libs/slack_api/__init__.py b/src/langbot/libs/slack_api/__init__.py
similarity index 100%
rename from libs/slack_api/__init__.py
rename to src/langbot/libs/slack_api/__init__.py
diff --git a/libs/slack_api/api.py b/src/langbot/libs/slack_api/api.py
similarity index 100%
rename from libs/slack_api/api.py
rename to src/langbot/libs/slack_api/api.py
diff --git a/libs/slack_api/slackevent.py b/src/langbot/libs/slack_api/slackevent.py
similarity index 100%
rename from libs/slack_api/slackevent.py
rename to src/langbot/libs/slack_api/slackevent.py
diff --git a/libs/wechatpad_api/LICENSE b/src/langbot/libs/wechatpad_api/LICENSE
similarity index 100%
rename from libs/wechatpad_api/LICENSE
rename to src/langbot/libs/wechatpad_api/LICENSE
diff --git a/libs/wechatpad_api/README.md b/src/langbot/libs/wechatpad_api/README.md
similarity index 100%
rename from libs/wechatpad_api/README.md
rename to src/langbot/libs/wechatpad_api/README.md
diff --git a/libs/wechatpad_api/__init__.py b/src/langbot/libs/wechatpad_api/__init__.py
similarity index 100%
rename from libs/wechatpad_api/__init__.py
rename to src/langbot/libs/wechatpad_api/__init__.py
diff --git a/libs/wechatpad_api/api/__init__.py b/src/langbot/libs/wechatpad_api/api/__init__.py
similarity index 100%
rename from libs/wechatpad_api/api/__init__.py
rename to src/langbot/libs/wechatpad_api/api/__init__.py
diff --git a/libs/wechatpad_api/api/chatroom.py b/src/langbot/libs/wechatpad_api/api/chatroom.py
similarity index 84%
rename from libs/wechatpad_api/api/chatroom.py
rename to src/langbot/libs/wechatpad_api/api/chatroom.py
index 2d9281a2..63360a23 100644
--- a/libs/wechatpad_api/api/chatroom.py
+++ b/src/langbot/libs/wechatpad_api/api/chatroom.py
@@ -1,4 +1,4 @@
-from libs.wechatpad_api.util.http_util import post_json
+from langbot.libs.wechatpad_api.util.http_util import post_json
class ChatRoomApi:
diff --git a/libs/wechatpad_api/api/downloadpai.py b/src/langbot/libs/wechatpad_api/api/downloadpai.py
similarity index 94%
rename from libs/wechatpad_api/api/downloadpai.py
rename to src/langbot/libs/wechatpad_api/api/downloadpai.py
index 2d45fac6..3fbdb624 100644
--- a/libs/wechatpad_api/api/downloadpai.py
+++ b/src/langbot/libs/wechatpad_api/api/downloadpai.py
@@ -1,4 +1,4 @@
-from libs.wechatpad_api.util.http_util import post_json
+from langbot.libs.wechatpad_api.util.http_util import post_json
import httpx
import base64
diff --git a/libs/wechatpad_api/api/friend.py b/src/langbot/libs/wechatpad_api/api/friend.py
similarity index 100%
rename from libs/wechatpad_api/api/friend.py
rename to src/langbot/libs/wechatpad_api/api/friend.py
diff --git a/libs/wechatpad_api/api/login.py b/src/langbot/libs/wechatpad_api/api/login.py
similarity index 96%
rename from libs/wechatpad_api/api/login.py
rename to src/langbot/libs/wechatpad_api/api/login.py
index 4aa4ae8d..23e89b88 100644
--- a/libs/wechatpad_api/api/login.py
+++ b/src/langbot/libs/wechatpad_api/api/login.py
@@ -1,4 +1,4 @@
-from libs.wechatpad_api.util.http_util import post_json, get_json
+from langbot.libs.wechatpad_api.util.http_util import post_json, get_json
class LoginApi:
diff --git a/libs/wechatpad_api/api/message.py b/src/langbot/libs/wechatpad_api/api/message.py
similarity index 97%
rename from libs/wechatpad_api/api/message.py
rename to src/langbot/libs/wechatpad_api/api/message.py
index cca76313..52c7bdb5 100644
--- a/libs/wechatpad_api/api/message.py
+++ b/src/langbot/libs/wechatpad_api/api/message.py
@@ -1,4 +1,4 @@
-from libs.wechatpad_api.util.http_util import post_json
+from langbot.libs.wechatpad_api.util.http_util import post_json
class MessageApi:
diff --git a/libs/wechatpad_api/api/user.py b/src/langbot/libs/wechatpad_api/api/user.py
similarity index 91%
rename from libs/wechatpad_api/api/user.py
rename to src/langbot/libs/wechatpad_api/api/user.py
index d2187c7c..0725b9d3 100644
--- a/libs/wechatpad_api/api/user.py
+++ b/src/langbot/libs/wechatpad_api/api/user.py
@@ -1,4 +1,4 @@
-from libs.wechatpad_api.util.http_util import post_json, async_request, get_json
+from langbot.libs.wechatpad_api.util.http_util import post_json, async_request, get_json
class UserApi:
diff --git a/libs/wechatpad_api/client.py b/src/langbot/libs/wechatpad_api/client.py
similarity index 89%
rename from libs/wechatpad_api/client.py
rename to src/langbot/libs/wechatpad_api/client.py
index 5e699d03..bb9b2f54 100644
--- a/libs/wechatpad_api/client.py
+++ b/src/langbot/libs/wechatpad_api/client.py
@@ -1,9 +1,9 @@
-from libs.wechatpad_api.api.login import LoginApi
-from libs.wechatpad_api.api.friend import FriendApi
-from libs.wechatpad_api.api.message import MessageApi
-from libs.wechatpad_api.api.user import UserApi
-from libs.wechatpad_api.api.downloadpai import DownloadApi
-from libs.wechatpad_api.api.chatroom import ChatRoomApi
+from langbot.libs.wechatpad_api.api.login import LoginApi
+from langbot.libs.wechatpad_api.api.friend import FriendApi
+from langbot.libs.wechatpad_api.api.message import MessageApi
+from langbot.libs.wechatpad_api.api.user import UserApi
+from langbot.libs.wechatpad_api.api.downloadpai import DownloadApi
+from langbot.libs.wechatpad_api.api.chatroom import ChatRoomApi
class WeChatPadClient:
diff --git a/libs/wechatpad_api/util/__init__.py b/src/langbot/libs/wechatpad_api/util/__init__.py
similarity index 100%
rename from libs/wechatpad_api/util/__init__.py
rename to src/langbot/libs/wechatpad_api/util/__init__.py
diff --git a/libs/wechatpad_api/util/http_util.py b/src/langbot/libs/wechatpad_api/util/http_util.py
similarity index 100%
rename from libs/wechatpad_api/util/http_util.py
rename to src/langbot/libs/wechatpad_api/util/http_util.py
diff --git a/libs/wechatpad_api/util/terminal_printer.py b/src/langbot/libs/wechatpad_api/util/terminal_printer.py
similarity index 100%
rename from libs/wechatpad_api/util/terminal_printer.py
rename to src/langbot/libs/wechatpad_api/util/terminal_printer.py
diff --git a/libs/wecom_ai_bot_api/WXBizMsgCrypt3.py b/src/langbot/libs/wecom_ai_bot_api/WXBizMsgCrypt3.py
similarity index 99%
rename from libs/wecom_ai_bot_api/WXBizMsgCrypt3.py
rename to src/langbot/libs/wecom_ai_bot_api/WXBizMsgCrypt3.py
index 96e9367f..bd8250da 100644
--- a/libs/wecom_ai_bot_api/WXBizMsgCrypt3.py
+++ b/src/langbot/libs/wecom_ai_bot_api/WXBizMsgCrypt3.py
@@ -16,7 +16,7 @@ import struct
from Crypto.Cipher import AES
import xml.etree.cElementTree as ET
import socket
-from libs.wecom_ai_bot_api import ierror
+from langbot.libs.wecom_ai_bot_api import ierror
"""
diff --git a/libs/wecom_ai_bot_api/api.py b/src/langbot/libs/wecom_ai_bot_api/api.py
similarity index 93%
rename from libs/wecom_ai_bot_api/api.py
rename to src/langbot/libs/wecom_ai_bot_api/api.py
index b20c6ed3..0620791b 100644
--- a/libs/wecom_ai_bot_api/api.py
+++ b/src/langbot/libs/wecom_ai_bot_api/api.py
@@ -13,9 +13,9 @@ import httpx
from Crypto.Cipher import AES
from quart import Quart, request, Response, jsonify
-from libs.wecom_ai_bot_api import wecombotevent
-from libs.wecom_ai_bot_api.WXBizMsgCrypt3 import WXBizMsgCrypt
-from pkg.platform.logger import EventLogger
+from langbot.libs.wecom_ai_bot_api import wecombotevent
+from langbot.libs.wecom_ai_bot_api.WXBizMsgCrypt3 import WXBizMsgCrypt
+from langbot.pkg.platform.logger import EventLogger
@dataclass
@@ -219,10 +219,7 @@ class WecomBotClient:
self.ReceiveId = ''
self.app = Quart(__name__)
self.app.add_url_rule(
- '/callback/command',
- 'handle_callback',
- self.handle_callback_request,
- methods=['POST', 'GET']
+ '/callback/command', 'handle_callback', self.handle_callback_request, methods=['POST', 'GET']
)
self._message_handlers = {
'example': [],
@@ -420,7 +417,7 @@ class WecomBotClient:
await self.logger.error("请求体中缺少 'encrypt' 字段")
return Response('Bad Request', status=400)
- xml_post_data = f""
+ xml_post_data = f''
ret, decrypted_xml = self.wxcpt.DecryptMsg(xml_post_data, msg_signature, timestamp, nonce)
if ret != 0:
await self.logger.error('解密失败')
@@ -458,7 +455,7 @@ class WecomBotClient:
picurl = item.get('image', {}).get('url')
if texts:
- message_data['content'] = "".join(texts) # 拼接所有 text
+ message_data['content'] = ''.join(texts) # 拼接所有 text
if picurl:
base64 = await self.download_url_to_base64(picurl, self.EnCodingAESKey)
message_data['picurl'] = base64 # 只保留第一个 image
@@ -466,7 +463,9 @@ class WecomBotClient:
# Extract user information
from_info = msg_json.get('from', {})
message_data['userid'] = from_info.get('userid', '')
- message_data['username'] = from_info.get('alias', '') or from_info.get('name', '') or from_info.get('userid', '')
+ message_data['username'] = (
+ from_info.get('alias', '') or from_info.get('name', '') or from_info.get('userid', '')
+ )
# Extract chat/group information
if msg_json.get('chattype', '') == 'group':
@@ -555,7 +554,7 @@ class WecomBotClient:
encrypted_bytes = response.content
- aes_key = base64.b64decode(encoding_aes_key + "=") # base64 补齐
+ aes_key = base64.b64decode(encoding_aes_key + '=') # base64 补齐
iv = aes_key[:16]
cipher = AES.new(aes_key, AES.MODE_CBC, iv)
@@ -564,22 +563,22 @@ class WecomBotClient:
pad_len = decrypted[-1]
decrypted = decrypted[:-pad_len]
- if decrypted.startswith(b"\xff\xd8"): # JPEG
- mime_type = "image/jpeg"
- elif decrypted.startswith(b"\x89PNG"): # PNG
- mime_type = "image/png"
- elif decrypted.startswith((b"GIF87a", b"GIF89a")): # GIF
- mime_type = "image/gif"
- elif decrypted.startswith(b"BM"): # BMP
- mime_type = "image/bmp"
- elif decrypted.startswith(b"II*\x00") or decrypted.startswith(b"MM\x00*"): # TIFF
- mime_type = "image/tiff"
+ if decrypted.startswith(b'\xff\xd8'): # JPEG
+ mime_type = 'image/jpeg'
+ elif decrypted.startswith(b'\x89PNG'): # PNG
+ mime_type = 'image/png'
+ elif decrypted.startswith((b'GIF87a', b'GIF89a')): # GIF
+ mime_type = 'image/gif'
+ elif decrypted.startswith(b'BM'): # BMP
+ mime_type = 'image/bmp'
+ elif decrypted.startswith(b'II*\x00') or decrypted.startswith(b'MM\x00*'): # TIFF
+ mime_type = 'image/tiff'
else:
- mime_type = "application/octet-stream"
+ mime_type = 'application/octet-stream'
# 转 base64
- base64_str = base64.b64encode(decrypted).decode("utf-8")
- return f"data:{mime_type};base64,{base64_str}"
+ base64_str = base64.b64encode(decrypted).decode('utf-8')
+ return f'data:{mime_type};base64,{base64_str}'
async def run_task(self, host: str, port: int, *args, **kwargs):
"""
diff --git a/libs/wecom_ai_bot_api/ierror.py b/src/langbot/libs/wecom_ai_bot_api/ierror.py
similarity index 100%
rename from libs/wecom_ai_bot_api/ierror.py
rename to src/langbot/libs/wecom_ai_bot_api/ierror.py
diff --git a/libs/wecom_ai_bot_api/wecombotevent.py b/src/langbot/libs/wecom_ai_bot_api/wecombotevent.py
similarity index 88%
rename from libs/wecom_ai_bot_api/wecombotevent.py
rename to src/langbot/libs/wecom_ai_bot_api/wecombotevent.py
index 099c58bc..b2f02831 100644
--- a/libs/wecom_ai_bot_api/wecombotevent.py
+++ b/src/langbot/libs/wecom_ai_bot_api/wecombotevent.py
@@ -29,7 +29,12 @@ class WecomBotEvent(dict):
"""
用户名称
"""
- return self.get('username', '') or self.get('from', {}).get('alias', '') or self.get('from', {}).get('name', '') or self.userid
+ return (
+ self.get('username', '')
+ or self.get('from', {}).get('alias', '')
+ or self.get('from', {}).get('name', '')
+ or self.userid
+ )
@property
def chatname(self) -> str:
@@ -65,7 +70,7 @@ class WecomBotEvent(dict):
消息id
"""
return self.get('msgid', '')
-
+
@property
def ai_bot_id(self) -> str:
"""
diff --git a/libs/wecom_api/WXBizMsgCrypt3.py b/src/langbot/libs/wecom_api/WXBizMsgCrypt3.py
similarity index 100%
rename from libs/wecom_api/WXBizMsgCrypt3.py
rename to src/langbot/libs/wecom_api/WXBizMsgCrypt3.py
diff --git a/libs/wecom_api/__init__.py b/src/langbot/libs/wecom_api/__init__.py
similarity index 100%
rename from libs/wecom_api/__init__.py
rename to src/langbot/libs/wecom_api/__init__.py
diff --git a/libs/wecom_api/api.py b/src/langbot/libs/wecom_api/api.py
similarity index 99%
rename from libs/wecom_api/api.py
rename to src/langbot/libs/wecom_api/api.py
index d9cb7726..352a550c 100644
--- a/libs/wecom_api/api.py
+++ b/src/langbot/libs/wecom_api/api.py
@@ -340,4 +340,3 @@ class WecomClient:
async def get_media_id(self, image: platform_message.Image):
media_id = await self.upload_to_work(image=image)
return media_id
-
diff --git a/libs/wecom_api/ierror.py b/src/langbot/libs/wecom_api/ierror.py
similarity index 100%
rename from libs/wecom_api/ierror.py
rename to src/langbot/libs/wecom_api/ierror.py
diff --git a/libs/wecom_api/wecomevent.py b/src/langbot/libs/wecom_api/wecomevent.py
similarity index 100%
rename from libs/wecom_api/wecomevent.py
rename to src/langbot/libs/wecom_api/wecomevent.py
diff --git a/libs/wecom_customer_service_api/__init__.py b/src/langbot/libs/wecom_customer_service_api/__init__.py
similarity index 100%
rename from libs/wecom_customer_service_api/__init__.py
rename to src/langbot/libs/wecom_customer_service_api/__init__.py
diff --git a/libs/wecom_customer_service_api/api.py b/src/langbot/libs/wecom_customer_service_api/api.py
similarity index 100%
rename from libs/wecom_customer_service_api/api.py
rename to src/langbot/libs/wecom_customer_service_api/api.py
diff --git a/libs/wecom_customer_service_api/wecomcsevent.py b/src/langbot/libs/wecom_customer_service_api/wecomcsevent.py
similarity index 100%
rename from libs/wecom_customer_service_api/wecomcsevent.py
rename to src/langbot/libs/wecom_customer_service_api/wecomcsevent.py
diff --git a/pkg/__init__.py b/src/langbot/pkg/__init__.py
similarity index 100%
rename from pkg/__init__.py
rename to src/langbot/pkg/__init__.py
diff --git a/pkg/api/__init__.py b/src/langbot/pkg/api/__init__.py
similarity index 100%
rename from pkg/api/__init__.py
rename to src/langbot/pkg/api/__init__.py
diff --git a/pkg/api/http/__init__.py b/src/langbot/pkg/api/http/__init__.py
similarity index 100%
rename from pkg/api/http/__init__.py
rename to src/langbot/pkg/api/http/__init__.py
diff --git a/pkg/api/http/controller/__init__.py b/src/langbot/pkg/api/http/controller/__init__.py
similarity index 100%
rename from pkg/api/http/controller/__init__.py
rename to src/langbot/pkg/api/http/controller/__init__.py
diff --git a/pkg/api/http/controller/group.py b/src/langbot/pkg/api/http/controller/group.py
similarity index 97%
rename from pkg/api/http/controller/group.py
rename to src/langbot/pkg/api/http/controller/group.py
index 8a61cefc..2ed55187 100644
--- a/pkg/api/http/controller/group.py
+++ b/src/langbot/pkg/api/http/controller/group.py
@@ -110,7 +110,7 @@ class RouterGroup(abc.ABC):
elif auth_type == AuthType.USER_TOKEN_OR_API_KEY:
# Try API key first (check X-API-Key header)
api_key = quart.request.headers.get('X-API-Key', '')
-
+
if api_key:
# API key authentication
try:
@@ -124,7 +124,9 @@ class RouterGroup(abc.ABC):
token = quart.request.headers.get('Authorization', '').replace('Bearer ', '')
if not token:
- return self.http_status(401, -1, 'No valid authentication provided (user token or API key required)')
+ return self.http_status(
+ 401, -1, 'No valid authentication provided (user token or API key required)'
+ )
try:
user_email = await self.ap.user_service.verify_jwt_token(token)
diff --git a/pkg/api/http/controller/groups/__init__.py b/src/langbot/pkg/api/http/controller/groups/__init__.py
similarity index 100%
rename from pkg/api/http/controller/groups/__init__.py
rename to src/langbot/pkg/api/http/controller/groups/__init__.py
diff --git a/pkg/api/http/controller/groups/apikeys.py b/src/langbot/pkg/api/http/controller/groups/apikeys.py
similarity index 100%
rename from pkg/api/http/controller/groups/apikeys.py
rename to src/langbot/pkg/api/http/controller/groups/apikeys.py
diff --git a/pkg/api/http/controller/groups/files.py b/src/langbot/pkg/api/http/controller/groups/files.py
similarity index 100%
rename from pkg/api/http/controller/groups/files.py
rename to src/langbot/pkg/api/http/controller/groups/files.py
diff --git a/pkg/api/http/controller/groups/knowledge/__init__.py b/src/langbot/pkg/api/http/controller/groups/knowledge/__init__.py
similarity index 100%
rename from pkg/api/http/controller/groups/knowledge/__init__.py
rename to src/langbot/pkg/api/http/controller/groups/knowledge/__init__.py
diff --git a/pkg/api/http/controller/groups/knowledge/base.py b/src/langbot/pkg/api/http/controller/groups/knowledge/base.py
similarity index 100%
rename from pkg/api/http/controller/groups/knowledge/base.py
rename to src/langbot/pkg/api/http/controller/groups/knowledge/base.py
diff --git a/pkg/api/http/controller/groups/logs.py b/src/langbot/pkg/api/http/controller/groups/logs.py
similarity index 100%
rename from pkg/api/http/controller/groups/logs.py
rename to src/langbot/pkg/api/http/controller/groups/logs.py
diff --git a/pkg/api/http/controller/groups/pipelines/__init__.py b/src/langbot/pkg/api/http/controller/groups/pipelines/__init__.py
similarity index 100%
rename from pkg/api/http/controller/groups/pipelines/__init__.py
rename to src/langbot/pkg/api/http/controller/groups/pipelines/__init__.py
diff --git a/pkg/api/http/controller/groups/pipelines/pipelines.py b/src/langbot/pkg/api/http/controller/groups/pipelines/pipelines.py
similarity index 92%
rename from pkg/api/http/controller/groups/pipelines/pipelines.py
rename to src/langbot/pkg/api/http/controller/groups/pipelines/pipelines.py
index 8285fa85..924b68d4 100644
--- a/pkg/api/http/controller/groups/pipelines/pipelines.py
+++ b/src/langbot/pkg/api/http/controller/groups/pipelines/pipelines.py
@@ -27,7 +27,9 @@ class PipelinesRouterGroup(group.RouterGroup):
async def _() -> str:
return self.success(data={'configs': await self.ap.pipeline_service.get_pipeline_metadata()})
- @self.route('/', methods=['GET', 'PUT', 'DELETE'], auth_type=group.AuthType.USER_TOKEN_OR_API_KEY)
+ @self.route(
+ '/', methods=['GET', 'PUT', 'DELETE'], auth_type=group.AuthType.USER_TOKEN_OR_API_KEY
+ )
async def _(pipeline_uuid: str) -> str:
if quart.request.method == 'GET':
pipeline = await self.ap.pipeline_service.get_pipeline(pipeline_uuid)
@@ -47,7 +49,9 @@ class PipelinesRouterGroup(group.RouterGroup):
return self.success()
- @self.route('//extensions', methods=['GET', 'PUT'], auth_type=group.AuthType.USER_TOKEN_OR_API_KEY)
+ @self.route(
+ '//extensions', methods=['GET', 'PUT'], auth_type=group.AuthType.USER_TOKEN_OR_API_KEY
+ )
async def _(pipeline_uuid: str) -> str:
if quart.request.method == 'GET':
# Get current extensions and available plugins
diff --git a/pkg/api/http/controller/groups/pipelines/webchat.py b/src/langbot/pkg/api/http/controller/groups/pipelines/webchat.py
similarity index 100%
rename from pkg/api/http/controller/groups/pipelines/webchat.py
rename to src/langbot/pkg/api/http/controller/groups/pipelines/webchat.py
diff --git a/pkg/api/http/controller/groups/platform/__init__.py b/src/langbot/pkg/api/http/controller/groups/platform/__init__.py
similarity index 100%
rename from pkg/api/http/controller/groups/platform/__init__.py
rename to src/langbot/pkg/api/http/controller/groups/platform/__init__.py
diff --git a/pkg/api/http/controller/groups/platform/adapters.py b/src/langbot/pkg/api/http/controller/groups/platform/adapters.py
similarity index 85%
rename from pkg/api/http/controller/groups/platform/adapters.py
rename to src/langbot/pkg/api/http/controller/groups/platform/adapters.py
index 4136791c..b46e5263 100644
--- a/pkg/api/http/controller/groups/platform/adapters.py
+++ b/src/langbot/pkg/api/http/controller/groups/platform/adapters.py
@@ -1,6 +1,7 @@
import quart
-
+import mimetypes
from ... import group
+from langbot.pkg.utils import importutil
@group.group_class('adapters', '/api/v1/platform/adapters')
@@ -31,4 +32,6 @@ class AdaptersRouterGroup(group.RouterGroup):
if icon_path is None:
return self.http_status(404, -1, 'icon not found')
- return await quart.send_file(icon_path)
+ return quart.Response(
+ importutil.read_resource_file_bytes(icon_path), mimetype=mimetypes.guess_type(icon_path)[0]
+ )
diff --git a/pkg/api/http/controller/groups/platform/bots.py b/src/langbot/pkg/api/http/controller/groups/platform/bots.py
similarity index 100%
rename from pkg/api/http/controller/groups/platform/bots.py
rename to src/langbot/pkg/api/http/controller/groups/platform/bots.py
diff --git a/pkg/api/http/controller/groups/plugins.py b/src/langbot/pkg/api/http/controller/groups/plugins.py
similarity index 100%
rename from pkg/api/http/controller/groups/plugins.py
rename to src/langbot/pkg/api/http/controller/groups/plugins.py
diff --git a/pkg/api/http/controller/groups/provider/__init__.py b/src/langbot/pkg/api/http/controller/groups/provider/__init__.py
similarity index 100%
rename from pkg/api/http/controller/groups/provider/__init__.py
rename to src/langbot/pkg/api/http/controller/groups/provider/__init__.py
diff --git a/pkg/api/http/controller/groups/provider/models.py b/src/langbot/pkg/api/http/controller/groups/provider/models.py
similarity index 100%
rename from pkg/api/http/controller/groups/provider/models.py
rename to src/langbot/pkg/api/http/controller/groups/provider/models.py
diff --git a/pkg/api/http/controller/groups/provider/requesters.py b/src/langbot/pkg/api/http/controller/groups/provider/requesters.py
similarity index 86%
rename from pkg/api/http/controller/groups/provider/requesters.py
rename to src/langbot/pkg/api/http/controller/groups/provider/requesters.py
index af9e1540..268ed11d 100644
--- a/pkg/api/http/controller/groups/provider/requesters.py
+++ b/src/langbot/pkg/api/http/controller/groups/provider/requesters.py
@@ -1,6 +1,8 @@
import quart
+import mimetypes
from ... import group
+from langbot.pkg.utils import importutil
@group.group_class('provider/requesters', '/api/v1/provider/requesters')
@@ -32,4 +34,6 @@ class RequestersRouterGroup(group.RouterGroup):
if icon_path is None:
return self.http_status(404, -1, 'icon not found')
- return await quart.send_file(icon_path)
+ return quart.Response(
+ importutil.read_resource_file_bytes(icon_path), mimetype=mimetypes.guess_type(icon_path)[0]
+ )
diff --git a/pkg/api/http/controller/groups/resources/__init__.py b/src/langbot/pkg/api/http/controller/groups/resources/__init__.py
similarity index 100%
rename from pkg/api/http/controller/groups/resources/__init__.py
rename to src/langbot/pkg/api/http/controller/groups/resources/__init__.py
diff --git a/pkg/api/http/controller/groups/resources/mcp.py b/src/langbot/pkg/api/http/controller/groups/resources/mcp.py
similarity index 100%
rename from pkg/api/http/controller/groups/resources/mcp.py
rename to src/langbot/pkg/api/http/controller/groups/resources/mcp.py
diff --git a/pkg/api/http/controller/groups/stats.py b/src/langbot/pkg/api/http/controller/groups/stats.py
similarity index 100%
rename from pkg/api/http/controller/groups/stats.py
rename to src/langbot/pkg/api/http/controller/groups/stats.py
diff --git a/pkg/api/http/controller/groups/system.py b/src/langbot/pkg/api/http/controller/groups/system.py
similarity index 100%
rename from pkg/api/http/controller/groups/system.py
rename to src/langbot/pkg/api/http/controller/groups/system.py
diff --git a/pkg/api/http/controller/groups/user.py b/src/langbot/pkg/api/http/controller/groups/user.py
similarity index 100%
rename from pkg/api/http/controller/groups/user.py
rename to src/langbot/pkg/api/http/controller/groups/user.py
diff --git a/pkg/api/http/controller/groups/webhooks.py b/src/langbot/pkg/api/http/controller/groups/webhooks.py
similarity index 100%
rename from pkg/api/http/controller/groups/webhooks.py
rename to src/langbot/pkg/api/http/controller/groups/webhooks.py
diff --git a/pkg/api/http/controller/main.py b/src/langbot/pkg/api/http/controller/main.py
similarity index 98%
rename from pkg/api/http/controller/main.py
rename to src/langbot/pkg/api/http/controller/main.py
index ca12f4bb..1541a25e 100644
--- a/pkg/api/http/controller/main.py
+++ b/src/langbot/pkg/api/http/controller/main.py
@@ -86,7 +86,9 @@ class HTTPController:
ginst = g(self.ap, self.quart_app)
await ginst.initialize()
- frontend_path = 'web/out'
+ from ....utils import paths
+
+ frontend_path = paths.get_frontend_path()
@self.quart_app.route('/')
async def index():
diff --git a/pkg/api/http/service/__init__.py b/src/langbot/pkg/api/http/service/__init__.py
similarity index 100%
rename from pkg/api/http/service/__init__.py
rename to src/langbot/pkg/api/http/service/__init__.py
diff --git a/pkg/api/http/service/apikey.py b/src/langbot/pkg/api/http/service/apikey.py
similarity index 94%
rename from pkg/api/http/service/apikey.py
rename to src/langbot/pkg/api/http/service/apikey.py
index 1a8b2892..c46b5608 100644
--- a/pkg/api/http/service/apikey.py
+++ b/src/langbot/pkg/api/http/service/apikey.py
@@ -61,9 +61,7 @@ class ApiKeyService:
async def delete_api_key(self, key_id: int) -> None:
"""Delete an API key"""
- await self.ap.persistence_mgr.execute_async(
- sqlalchemy.delete(apikey.ApiKey).where(apikey.ApiKey.id == key_id)
- )
+ await self.ap.persistence_mgr.execute_async(sqlalchemy.delete(apikey.ApiKey).where(apikey.ApiKey.id == key_id))
async def update_api_key(self, key_id: int, name: str = None, description: str = None) -> None:
"""Update an API key's metadata (name, description)"""
diff --git a/pkg/api/http/service/bot.py b/src/langbot/pkg/api/http/service/bot.py
similarity index 100%
rename from pkg/api/http/service/bot.py
rename to src/langbot/pkg/api/http/service/bot.py
diff --git a/pkg/api/http/service/knowledge.py b/src/langbot/pkg/api/http/service/knowledge.py
similarity index 100%
rename from pkg/api/http/service/knowledge.py
rename to src/langbot/pkg/api/http/service/knowledge.py
diff --git a/pkg/api/http/service/mcp.py b/src/langbot/pkg/api/http/service/mcp.py
similarity index 98%
rename from pkg/api/http/service/mcp.py
rename to src/langbot/pkg/api/http/service/mcp.py
index 328b9c20..a1b034d0 100644
--- a/pkg/api/http/service/mcp.py
+++ b/src/langbot/pkg/api/http/service/mcp.py
@@ -84,13 +84,11 @@ class MCPService:
new_enable = server_data.get('enable', False)
need_remove = old_server_name and old_server_name in self.ap.tool_mgr.mcp_tool_loader.sessions
- need_start = new_enable
-
if old_enable and not new_enable:
if need_remove:
await self.ap.tool_mgr.mcp_tool_loader.remove_mcp_server(old_server_name)
-
+
elif not old_enable and new_enable:
result = await self.ap.persistence_mgr.execute_async(
sqlalchemy.select(persistence_mcp.MCPServer).where(persistence_mcp.MCPServer.uuid == server_uuid)
@@ -100,7 +98,7 @@ class MCPService:
server_config = self.ap.persistence_mgr.serialize_model(persistence_mcp.MCPServer, updated_server)
task = asyncio.create_task(self.ap.tool_mgr.mcp_tool_loader.host_mcp_server(server_config))
self.ap.tool_mgr.mcp_tool_loader._hosted_mcp_tasks.append(task)
-
+
elif old_enable and new_enable:
if need_remove:
await self.ap.tool_mgr.mcp_tool_loader.remove_mcp_server(old_server_name)
@@ -112,7 +110,6 @@ class MCPService:
server_config = self.ap.persistence_mgr.serialize_model(persistence_mcp.MCPServer, updated_server)
task = asyncio.create_task(self.ap.tool_mgr.mcp_tool_loader.host_mcp_server(server_config))
self.ap.tool_mgr.mcp_tool_loader._hosted_mcp_tasks.append(task)
-
async def delete_mcp_server(self, server_uuid: str) -> None:
result = await self.ap.persistence_mgr.execute_async(
diff --git a/pkg/api/http/service/model.py b/src/langbot/pkg/api/http/service/model.py
similarity index 100%
rename from pkg/api/http/service/model.py
rename to src/langbot/pkg/api/http/service/model.py
diff --git a/pkg/api/http/service/pipeline.py b/src/langbot/pkg/api/http/service/pipeline.py
similarity index 90%
rename from pkg/api/http/service/pipeline.py
rename to src/langbot/pkg/api/http/service/pipeline.py
index 2e00a74d..62e0879f 100644
--- a/pkg/api/http/service/pipeline.py
+++ b/src/langbot/pkg/api/http/service/pipeline.py
@@ -30,12 +30,12 @@ class PipelineService:
def __init__(self, ap: app.Application) -> None:
self.ap = ap
- async def get_pipeline_metadata(self) -> dict:
+ async def get_pipeline_metadata(self) -> list[dict]:
return [
- self.ap.pipeline_config_meta_trigger.data,
- self.ap.pipeline_config_meta_safety.data,
- self.ap.pipeline_config_meta_ai.data,
- self.ap.pipeline_config_meta_output.data,
+ self.ap.pipeline_config_meta_trigger,
+ self.ap.pipeline_config_meta_safety,
+ self.ap.pipeline_config_meta_ai,
+ self.ap.pipeline_config_meta_output,
]
async def get_pipelines(self, sort_by: str = 'created_at', sort_order: str = 'DESC') -> list[dict]:
@@ -74,11 +74,16 @@ class PipelineService:
return self.ap.persistence_mgr.serialize_model(persistence_pipeline.LegacyPipeline, pipeline)
async def create_pipeline(self, pipeline_data: dict, default: bool = False) -> str:
+ from ....utils import paths as path_utils
+
pipeline_data['uuid'] = str(uuid.uuid4())
pipeline_data['for_version'] = self.ap.ver_mgr.get_current_version()
pipeline_data['stages'] = default_stage_order.copy()
pipeline_data['is_default'] = default
- pipeline_data['config'] = json.load(open('templates/default-pipeline-config.json', 'r', encoding='utf-8'))
+
+ template_path = path_utils.get_resource_path('templates/default-pipeline-config.json')
+ with open(template_path, 'r', encoding='utf-8') as f:
+ pipeline_data['config'] = json.load(f)
await self.ap.persistence_mgr.execute_async(
sqlalchemy.insert(persistence_pipeline.LegacyPipeline).values(**pipeline_data)
@@ -137,7 +142,9 @@ class PipelineService:
)
await self.ap.pipeline_mgr.remove_pipeline(pipeline_uuid)
- async def update_pipeline_extensions(self, pipeline_uuid: str, bound_plugins: list[dict], bound_mcp_servers: list[str] = None) -> None:
+ async def update_pipeline_extensions(
+ self, pipeline_uuid: str, bound_plugins: list[dict], bound_mcp_servers: list[str] = None
+ ) -> None:
"""Update the bound plugins and MCP servers for a pipeline"""
# Get current pipeline
result = await self.ap.persistence_mgr.execute_async(
@@ -145,23 +152,23 @@ class PipelineService:
persistence_pipeline.LegacyPipeline.uuid == pipeline_uuid
)
)
-
+
pipeline = result.first()
if pipeline is None:
raise ValueError(f'Pipeline {pipeline_uuid} not found')
-
+
# Update extensions_preferences
extensions_preferences = pipeline.extensions_preferences or {}
extensions_preferences['plugins'] = bound_plugins
if bound_mcp_servers is not None:
extensions_preferences['mcp_servers'] = bound_mcp_servers
-
+
await self.ap.persistence_mgr.execute_async(
sqlalchemy.update(persistence_pipeline.LegacyPipeline)
.where(persistence_pipeline.LegacyPipeline.uuid == pipeline_uuid)
.values(extensions_preferences=extensions_preferences)
)
-
+
# Reload pipeline to apply changes
await self.ap.pipeline_mgr.remove_pipeline(pipeline_uuid)
pipeline = await self.get_pipeline(pipeline_uuid)
diff --git a/pkg/api/http/service/user.py b/src/langbot/pkg/api/http/service/user.py
similarity index 100%
rename from pkg/api/http/service/user.py
rename to src/langbot/pkg/api/http/service/user.py
diff --git a/pkg/api/http/service/webhook.py b/src/langbot/pkg/api/http/service/webhook.py
similarity index 100%
rename from pkg/api/http/service/webhook.py
rename to src/langbot/pkg/api/http/service/webhook.py
diff --git a/pkg/command/__init__.py b/src/langbot/pkg/command/__init__.py
similarity index 100%
rename from pkg/command/__init__.py
rename to src/langbot/pkg/command/__init__.py
diff --git a/pkg/command/cmdmgr.py b/src/langbot/pkg/command/cmdmgr.py
similarity index 100%
rename from pkg/command/cmdmgr.py
rename to src/langbot/pkg/command/cmdmgr.py
diff --git a/pkg/command/operator.py b/src/langbot/pkg/command/operator.py
similarity index 100%
rename from pkg/command/operator.py
rename to src/langbot/pkg/command/operator.py
diff --git a/pkg/command/operators/__init__.py b/src/langbot/pkg/command/operators/__init__.py
similarity index 100%
rename from pkg/command/operators/__init__.py
rename to src/langbot/pkg/command/operators/__init__.py
diff --git a/pkg/command/operators/delc.py b/src/langbot/pkg/command/operators/delc.py
similarity index 100%
rename from pkg/command/operators/delc.py
rename to src/langbot/pkg/command/operators/delc.py
diff --git a/pkg/command/operators/last.py b/src/langbot/pkg/command/operators/last.py
similarity index 100%
rename from pkg/command/operators/last.py
rename to src/langbot/pkg/command/operators/last.py
diff --git a/pkg/command/operators/list.py b/src/langbot/pkg/command/operators/list.py
similarity index 100%
rename from pkg/command/operators/list.py
rename to src/langbot/pkg/command/operators/list.py
diff --git a/pkg/command/operators/next.py b/src/langbot/pkg/command/operators/next.py
similarity index 100%
rename from pkg/command/operators/next.py
rename to src/langbot/pkg/command/operators/next.py
diff --git a/pkg/command/operators/prompt.py b/src/langbot/pkg/command/operators/prompt.py
similarity index 100%
rename from pkg/command/operators/prompt.py
rename to src/langbot/pkg/command/operators/prompt.py
diff --git a/pkg/command/operators/resend.py b/src/langbot/pkg/command/operators/resend.py
similarity index 100%
rename from pkg/command/operators/resend.py
rename to src/langbot/pkg/command/operators/resend.py
diff --git a/pkg/config/__init__.py b/src/langbot/pkg/config/__init__.py
similarity index 100%
rename from pkg/config/__init__.py
rename to src/langbot/pkg/config/__init__.py
diff --git a/pkg/config/impls/__init__.py b/src/langbot/pkg/config/impls/__init__.py
similarity index 100%
rename from pkg/config/impls/__init__.py
rename to src/langbot/pkg/config/impls/__init__.py
diff --git a/pkg/config/impls/json.py b/src/langbot/pkg/config/impls/json.py
similarity index 65%
rename from pkg/config/impls/json.py
rename to src/langbot/pkg/config/impls/json.py
index 44b4843c..00683415 100644
--- a/pkg/config/impls/json.py
+++ b/src/langbot/pkg/config/impls/json.py
@@ -1,8 +1,8 @@
import os
-import shutil
import json
+import importlib.resources as resources
-from .. import model as file_model
+from langbot.pkg.config import model as file_model
class JSONConfigFile(file_model.ConfigFile):
@@ -11,19 +11,29 @@ class JSONConfigFile(file_model.ConfigFile):
def __init__(
self,
config_file_name: str,
- template_file_name: str = None,
+ template_resource_name: str = None,
template_data: dict = None,
) -> None:
self.config_file_name = config_file_name
- self.template_file_name = template_file_name
+ self.template_resource_name = template_resource_name
self.template_data = template_data
def exists(self) -> bool:
return os.path.exists(self.config_file_name)
+ async def get_template_file_str(self) -> str:
+ if self.template_resource_name is None:
+ return None
+
+ with (
+ resources.files('langbot.templates').joinpath(self.template_resource_name).open('r', encoding='utf-8') as f
+ ):
+ return f.read()
+
async def create(self):
- if self.template_file_name is not None:
- shutil.copyfile(self.template_file_name, self.config_file_name)
+ if await self.get_template_file_str() is not None:
+ with open(self.config_file_name, 'w', encoding='utf-8') as f:
+ f.write(await self.get_template_file_str())
elif self.template_data is not None:
with open(self.config_file_name, 'w', encoding='utf-8') as f:
json.dump(self.template_data, f, indent=4, ensure_ascii=False)
@@ -34,9 +44,10 @@ class JSONConfigFile(file_model.ConfigFile):
if not self.exists():
await self.create()
- if self.template_file_name is not None:
- with open(self.template_file_name, 'r', encoding='utf-8') as f:
- self.template_data = json.load(f)
+ template_file_str = await self.get_template_file_str()
+
+ if template_file_str is not None:
+ self.template_data = json.loads(template_file_str)
with open(self.config_file_name, 'r', encoding='utf-8') as f:
try:
diff --git a/pkg/config/impls/pymodule.py b/src/langbot/pkg/config/impls/pymodule.py
similarity index 100%
rename from pkg/config/impls/pymodule.py
rename to src/langbot/pkg/config/impls/pymodule.py
diff --git a/pkg/config/impls/yaml.py b/src/langbot/pkg/config/impls/yaml.py
similarity index 64%
rename from pkg/config/impls/yaml.py
rename to src/langbot/pkg/config/impls/yaml.py
index 0d69ef9e..d9dc4bc2 100644
--- a/pkg/config/impls/yaml.py
+++ b/src/langbot/pkg/config/impls/yaml.py
@@ -1,8 +1,8 @@
import os
-import shutil
import yaml
+import importlib.resources as resources
-from .. import model as file_model
+from langbot.pkg.config import model as file_model
class YAMLConfigFile(file_model.ConfigFile):
@@ -11,19 +11,29 @@ class YAMLConfigFile(file_model.ConfigFile):
def __init__(
self,
config_file_name: str,
- template_file_name: str = None,
+ template_resource_name: str = None,
template_data: dict = None,
) -> None:
self.config_file_name = config_file_name
- self.template_file_name = template_file_name
+ self.template_resource_name = template_resource_name
self.template_data = template_data
def exists(self) -> bool:
return os.path.exists(self.config_file_name)
+ async def get_template_file_str(self) -> str:
+ if self.template_resource_name is None:
+ return None
+
+ with (
+ resources.files('langbot.templates').joinpath(self.template_resource_name).open('r', encoding='utf-8') as f
+ ):
+ return f.read()
+
async def create(self):
- if self.template_file_name is not None:
- shutil.copyfile(self.template_file_name, self.config_file_name)
+ if await self.get_template_file_str() is not None:
+ with open(self.config_file_name, 'w', encoding='utf-8') as f:
+ f.write(await self.get_template_file_str())
elif self.template_data is not None:
with open(self.config_file_name, 'w', encoding='utf-8') as f:
yaml.dump(self.template_data, f, indent=4, allow_unicode=True)
@@ -34,9 +44,10 @@ class YAMLConfigFile(file_model.ConfigFile):
if not self.exists():
await self.create()
- if self.template_file_name is not None:
- with open(self.template_file_name, 'r', encoding='utf-8') as f:
- self.template_data = yaml.load(f, Loader=yaml.FullLoader)
+ template_file_str = await self.get_template_file_str()
+
+ if template_file_str is not None:
+ self.template_data = yaml.load(template_file_str, Loader=yaml.FullLoader)
with open(self.config_file_name, 'r', encoding='utf-8') as f:
try:
diff --git a/pkg/config/manager.py b/src/langbot/pkg/config/manager.py
similarity index 86%
rename from pkg/config/manager.py
rename to src/langbot/pkg/config/manager.py
index d552b038..d22591b0 100644
--- a/pkg/config/manager.py
+++ b/src/langbot/pkg/config/manager.py
@@ -62,7 +62,7 @@ async def load_python_module_config(config_name: str, template_name: str, comple
async def load_json_config(
config_name: str,
- template_name: str = None,
+ template_resource_name: str = None,
template_data: dict = None,
completion: bool = True,
) -> ConfigManager:
@@ -70,11 +70,11 @@ async def load_json_config(
Args:
config_name (str): Config file name
- template_name (str): Template file name
+ template_resource_name (str): Template resource name
template_data (dict): Template data
completion (bool): Whether to automatically complete the config file in memory
"""
- cfg_inst = json_file.JSONConfigFile(config_name, template_name, template_data)
+ cfg_inst = json_file.JSONConfigFile(config_name, template_resource_name, template_data)
cfg_mgr = ConfigManager(cfg_inst)
await cfg_mgr.load_config(completion=completion)
@@ -84,7 +84,7 @@ async def load_json_config(
async def load_yaml_config(
config_name: str,
- template_name: str = None,
+ template_resource_name: str = None,
template_data: dict = None,
completion: bool = True,
) -> ConfigManager:
@@ -92,14 +92,14 @@ async def load_yaml_config(
Args:
config_name (str): Config file name
- template_name (str): Template file name
+ template_resource_name (str): Template resource name
template_data (dict): Template data
completion (bool): Whether to automatically complete the config file in memory
Returns:
ConfigManager: Config file manager
"""
- cfg_inst = yaml_file.YAMLConfigFile(config_name, template_name, template_data)
+ cfg_inst = yaml_file.YAMLConfigFile(config_name, template_resource_name, template_data)
cfg_mgr = ConfigManager(cfg_inst)
await cfg_mgr.load_config(completion=completion)
diff --git a/pkg/config/model.py b/src/langbot/pkg/config/model.py
similarity index 100%
rename from pkg/config/model.py
rename to src/langbot/pkg/config/model.py
diff --git a/pkg/core/__init__.py b/src/langbot/pkg/core/__init__.py
similarity index 100%
rename from pkg/core/__init__.py
rename to src/langbot/pkg/core/__init__.py
diff --git a/pkg/core/app.py b/src/langbot/pkg/core/app.py
similarity index 96%
rename from pkg/core/app.py
rename to src/langbot/pkg/core/app.py
index 88cd0d50..a3ad68e8 100644
--- a/pkg/core/app.py
+++ b/src/langbot/pkg/core/app.py
@@ -9,13 +9,13 @@ from ..platform import botmgr as im_mgr
from ..platform.webhook_pusher import WebhookPusher
from ..provider.session import sessionmgr as llm_session_mgr
from ..provider.modelmgr import modelmgr as llm_model_mgr
-from ..provider.tools import toolmgr as llm_tool_mgr
+from langbot.pkg.provider.tools import toolmgr as llm_tool_mgr
from ..config import manager as config_mgr
from ..command import cmdmgr
from ..plugin import connector as plugin_connector
from ..pipeline import pool
from ..pipeline import controller, pipelinemgr
-from ..utils import version as version_mgr, proxy as proxy_mgr, announce as announce_mgr
+from ..utils import version as version_mgr, proxy as proxy_mgr
from ..persistence import mgr as persistencemgr
from ..api.http.controller import main as http_controller
from ..api.http.service import user as user_service
@@ -95,8 +95,6 @@ class Application:
ver_mgr: version_mgr.VersionManager = None
- ann_mgr: announce_mgr.AnnouncementManager = None
-
proxy_mgr: proxy_mgr.ProxyManager = None
logger: logging.Logger = None
@@ -186,7 +184,11 @@ class Application:
async def print_web_access_info(self):
"""Print access webui tips"""
- if not os.path.exists(os.path.join('.', 'web/out')):
+ from ..utils import paths
+
+ frontend_path = paths.get_frontend_path()
+
+ if not os.path.exists(frontend_path):
self.logger.warning('WebUI 文件缺失,请根据文档部署:https://docs.langbot.app/zh')
self.logger.warning(
'WebUI files are missing, please deploy according to the documentation: https://docs.langbot.app/en'
diff --git a/pkg/core/boot.py b/src/langbot/pkg/core/boot.py
similarity index 100%
rename from pkg/core/boot.py
rename to src/langbot/pkg/core/boot.py
diff --git a/pkg/core/bootutils/__init__.py b/src/langbot/pkg/core/bootutils/__init__.py
similarity index 100%
rename from pkg/core/bootutils/__init__.py
rename to src/langbot/pkg/core/bootutils/__init__.py
diff --git a/pkg/core/bootutils/config.py b/src/langbot/pkg/core/bootutils/config.py
similarity index 100%
rename from pkg/core/bootutils/config.py
rename to src/langbot/pkg/core/bootutils/config.py
diff --git a/pkg/core/bootutils/deps.py b/src/langbot/pkg/core/bootutils/deps.py
similarity index 100%
rename from pkg/core/bootutils/deps.py
rename to src/langbot/pkg/core/bootutils/deps.py
diff --git a/pkg/core/bootutils/files.py b/src/langbot/pkg/core/bootutils/files.py
similarity index 77%
rename from pkg/core/bootutils/files.py
rename to src/langbot/pkg/core/bootutils/files.py
index 07bb7779..067b60c1 100644
--- a/pkg/core/bootutils/files.py
+++ b/src/langbot/pkg/core/bootutils/files.py
@@ -20,6 +20,8 @@ required_paths = [
async def generate_files() -> list[str]:
global required_files, required_paths
+ from ...utils import paths as path_utils
+
for required_paths in required_paths:
if not os.path.exists(required_paths):
os.mkdir(required_paths)
@@ -27,7 +29,8 @@ async def generate_files() -> list[str]:
generated_files = []
for file in required_files:
if not os.path.exists(file):
- shutil.copyfile(required_files[file], file)
+ template_path = path_utils.get_resource_path(required_files[file])
+ shutil.copyfile(template_path, file)
generated_files.append(file)
return generated_files
diff --git a/pkg/core/bootutils/log.py b/src/langbot/pkg/core/bootutils/log.py
similarity index 100%
rename from pkg/core/bootutils/log.py
rename to src/langbot/pkg/core/bootutils/log.py
diff --git a/pkg/core/entities.py b/src/langbot/pkg/core/entities.py
similarity index 100%
rename from pkg/core/entities.py
rename to src/langbot/pkg/core/entities.py
diff --git a/pkg/core/migration.py b/src/langbot/pkg/core/migration.py
similarity index 100%
rename from pkg/core/migration.py
rename to src/langbot/pkg/core/migration.py
diff --git a/pkg/core/migrations/__init__.py b/src/langbot/pkg/core/migrations/__init__.py
similarity index 100%
rename from pkg/core/migrations/__init__.py
rename to src/langbot/pkg/core/migrations/__init__.py
diff --git a/pkg/core/migrations/m001_sensitive_word_migration.py b/src/langbot/pkg/core/migrations/m001_sensitive_word_migration.py
similarity index 100%
rename from pkg/core/migrations/m001_sensitive_word_migration.py
rename to src/langbot/pkg/core/migrations/m001_sensitive_word_migration.py
diff --git a/pkg/core/migrations/m002_openai_config_migration.py b/src/langbot/pkg/core/migrations/m002_openai_config_migration.py
similarity index 100%
rename from pkg/core/migrations/m002_openai_config_migration.py
rename to src/langbot/pkg/core/migrations/m002_openai_config_migration.py
diff --git a/pkg/core/migrations/m003_anthropic_requester_cfg_completion.py b/src/langbot/pkg/core/migrations/m003_anthropic_requester_cfg_completion.py
similarity index 100%
rename from pkg/core/migrations/m003_anthropic_requester_cfg_completion.py
rename to src/langbot/pkg/core/migrations/m003_anthropic_requester_cfg_completion.py
diff --git a/pkg/core/migrations/m004_moonshot_cfg_completion.py b/src/langbot/pkg/core/migrations/m004_moonshot_cfg_completion.py
similarity index 100%
rename from pkg/core/migrations/m004_moonshot_cfg_completion.py
rename to src/langbot/pkg/core/migrations/m004_moonshot_cfg_completion.py
diff --git a/pkg/core/migrations/m005_deepseek_cfg_completion.py b/src/langbot/pkg/core/migrations/m005_deepseek_cfg_completion.py
similarity index 100%
rename from pkg/core/migrations/m005_deepseek_cfg_completion.py
rename to src/langbot/pkg/core/migrations/m005_deepseek_cfg_completion.py
diff --git a/pkg/core/migrations/m006_vision_config.py b/src/langbot/pkg/core/migrations/m006_vision_config.py
similarity index 100%
rename from pkg/core/migrations/m006_vision_config.py
rename to src/langbot/pkg/core/migrations/m006_vision_config.py
diff --git a/pkg/core/migrations/m007_qcg_center_url.py b/src/langbot/pkg/core/migrations/m007_qcg_center_url.py
similarity index 100%
rename from pkg/core/migrations/m007_qcg_center_url.py
rename to src/langbot/pkg/core/migrations/m007_qcg_center_url.py
diff --git a/pkg/core/migrations/m008_ad_fixwin_config_migrate.py b/src/langbot/pkg/core/migrations/m008_ad_fixwin_config_migrate.py
similarity index 100%
rename from pkg/core/migrations/m008_ad_fixwin_config_migrate.py
rename to src/langbot/pkg/core/migrations/m008_ad_fixwin_config_migrate.py
diff --git a/pkg/core/migrations/m009_msg_truncator_cfg.py b/src/langbot/pkg/core/migrations/m009_msg_truncator_cfg.py
similarity index 100%
rename from pkg/core/migrations/m009_msg_truncator_cfg.py
rename to src/langbot/pkg/core/migrations/m009_msg_truncator_cfg.py
diff --git a/pkg/core/migrations/m010_ollama_requester_config.py b/src/langbot/pkg/core/migrations/m010_ollama_requester_config.py
similarity index 100%
rename from pkg/core/migrations/m010_ollama_requester_config.py
rename to src/langbot/pkg/core/migrations/m010_ollama_requester_config.py
diff --git a/pkg/core/migrations/m011_command_prefix_config.py b/src/langbot/pkg/core/migrations/m011_command_prefix_config.py
similarity index 100%
rename from pkg/core/migrations/m011_command_prefix_config.py
rename to src/langbot/pkg/core/migrations/m011_command_prefix_config.py
diff --git a/pkg/core/migrations/m012_runner_config.py b/src/langbot/pkg/core/migrations/m012_runner_config.py
similarity index 100%
rename from pkg/core/migrations/m012_runner_config.py
rename to src/langbot/pkg/core/migrations/m012_runner_config.py
diff --git a/pkg/core/migrations/m013_http_api_config.py b/src/langbot/pkg/core/migrations/m013_http_api_config.py
similarity index 100%
rename from pkg/core/migrations/m013_http_api_config.py
rename to src/langbot/pkg/core/migrations/m013_http_api_config.py
diff --git a/pkg/core/migrations/m014_force_delay_config.py b/src/langbot/pkg/core/migrations/m014_force_delay_config.py
similarity index 100%
rename from pkg/core/migrations/m014_force_delay_config.py
rename to src/langbot/pkg/core/migrations/m014_force_delay_config.py
diff --git a/pkg/core/migrations/m015_gitee_ai_config.py b/src/langbot/pkg/core/migrations/m015_gitee_ai_config.py
similarity index 100%
rename from pkg/core/migrations/m015_gitee_ai_config.py
rename to src/langbot/pkg/core/migrations/m015_gitee_ai_config.py
diff --git a/pkg/core/migrations/m016_dify_service_api.py b/src/langbot/pkg/core/migrations/m016_dify_service_api.py
similarity index 100%
rename from pkg/core/migrations/m016_dify_service_api.py
rename to src/langbot/pkg/core/migrations/m016_dify_service_api.py
diff --git a/pkg/core/migrations/m017_dify_api_timeout_params.py b/src/langbot/pkg/core/migrations/m017_dify_api_timeout_params.py
similarity index 100%
rename from pkg/core/migrations/m017_dify_api_timeout_params.py
rename to src/langbot/pkg/core/migrations/m017_dify_api_timeout_params.py
diff --git a/pkg/core/migrations/m018_xai_config.py b/src/langbot/pkg/core/migrations/m018_xai_config.py
similarity index 100%
rename from pkg/core/migrations/m018_xai_config.py
rename to src/langbot/pkg/core/migrations/m018_xai_config.py
diff --git a/pkg/core/migrations/m019_zhipuai_config.py b/src/langbot/pkg/core/migrations/m019_zhipuai_config.py
similarity index 100%
rename from pkg/core/migrations/m019_zhipuai_config.py
rename to src/langbot/pkg/core/migrations/m019_zhipuai_config.py
diff --git a/pkg/core/migrations/m020_wecom_config.py b/src/langbot/pkg/core/migrations/m020_wecom_config.py
similarity index 100%
rename from pkg/core/migrations/m020_wecom_config.py
rename to src/langbot/pkg/core/migrations/m020_wecom_config.py
diff --git a/pkg/core/migrations/m021_lark_config.py b/src/langbot/pkg/core/migrations/m021_lark_config.py
similarity index 100%
rename from pkg/core/migrations/m021_lark_config.py
rename to src/langbot/pkg/core/migrations/m021_lark_config.py
diff --git a/pkg/core/migrations/m022_lmstudio_config.py b/src/langbot/pkg/core/migrations/m022_lmstudio_config.py
similarity index 100%
rename from pkg/core/migrations/m022_lmstudio_config.py
rename to src/langbot/pkg/core/migrations/m022_lmstudio_config.py
diff --git a/pkg/core/migrations/m023_siliconflow_config.py b/src/langbot/pkg/core/migrations/m023_siliconflow_config.py
similarity index 100%
rename from pkg/core/migrations/m023_siliconflow_config.py
rename to src/langbot/pkg/core/migrations/m023_siliconflow_config.py
diff --git a/pkg/core/migrations/m024_discord_config.py b/src/langbot/pkg/core/migrations/m024_discord_config.py
similarity index 100%
rename from pkg/core/migrations/m024_discord_config.py
rename to src/langbot/pkg/core/migrations/m024_discord_config.py
diff --git a/pkg/core/migrations/m025_gewechat_config.py b/src/langbot/pkg/core/migrations/m025_gewechat_config.py
similarity index 100%
rename from pkg/core/migrations/m025_gewechat_config.py
rename to src/langbot/pkg/core/migrations/m025_gewechat_config.py
diff --git a/pkg/core/migrations/m026_qqofficial_config.py b/src/langbot/pkg/core/migrations/m026_qqofficial_config.py
similarity index 100%
rename from pkg/core/migrations/m026_qqofficial_config.py
rename to src/langbot/pkg/core/migrations/m026_qqofficial_config.py
diff --git a/pkg/core/migrations/m027_wx_official_account_config.py b/src/langbot/pkg/core/migrations/m027_wx_official_account_config.py
similarity index 100%
rename from pkg/core/migrations/m027_wx_official_account_config.py
rename to src/langbot/pkg/core/migrations/m027_wx_official_account_config.py
diff --git a/pkg/core/migrations/m028_aliyun_requester_config.py b/src/langbot/pkg/core/migrations/m028_aliyun_requester_config.py
similarity index 100%
rename from pkg/core/migrations/m028_aliyun_requester_config.py
rename to src/langbot/pkg/core/migrations/m028_aliyun_requester_config.py
diff --git a/pkg/core/migrations/m029_dashscope_app_api_config.py b/src/langbot/pkg/core/migrations/m029_dashscope_app_api_config.py
similarity index 100%
rename from pkg/core/migrations/m029_dashscope_app_api_config.py
rename to src/langbot/pkg/core/migrations/m029_dashscope_app_api_config.py
diff --git a/pkg/core/migrations/m030_lark_config_cmpl.py b/src/langbot/pkg/core/migrations/m030_lark_config_cmpl.py
similarity index 100%
rename from pkg/core/migrations/m030_lark_config_cmpl.py
rename to src/langbot/pkg/core/migrations/m030_lark_config_cmpl.py
diff --git a/pkg/core/migrations/m031_dingtalk_config.py b/src/langbot/pkg/core/migrations/m031_dingtalk_config.py
similarity index 100%
rename from pkg/core/migrations/m031_dingtalk_config.py
rename to src/langbot/pkg/core/migrations/m031_dingtalk_config.py
diff --git a/pkg/core/migrations/m032_volcark_config.py b/src/langbot/pkg/core/migrations/m032_volcark_config.py
similarity index 100%
rename from pkg/core/migrations/m032_volcark_config.py
rename to src/langbot/pkg/core/migrations/m032_volcark_config.py
diff --git a/pkg/core/migrations/m033_dify_thinking_config.py b/src/langbot/pkg/core/migrations/m033_dify_thinking_config.py
similarity index 100%
rename from pkg/core/migrations/m033_dify_thinking_config.py
rename to src/langbot/pkg/core/migrations/m033_dify_thinking_config.py
diff --git a/pkg/core/migrations/m034_gewechat_file_url_config.py b/src/langbot/pkg/core/migrations/m034_gewechat_file_url_config.py
similarity index 100%
rename from pkg/core/migrations/m034_gewechat_file_url_config.py
rename to src/langbot/pkg/core/migrations/m034_gewechat_file_url_config.py
diff --git a/pkg/core/migrations/m035_wxoa_mode.py b/src/langbot/pkg/core/migrations/m035_wxoa_mode.py
similarity index 100%
rename from pkg/core/migrations/m035_wxoa_mode.py
rename to src/langbot/pkg/core/migrations/m035_wxoa_mode.py
diff --git a/pkg/core/migrations/m036_wxoa_loading_message.py b/src/langbot/pkg/core/migrations/m036_wxoa_loading_message.py
similarity index 100%
rename from pkg/core/migrations/m036_wxoa_loading_message.py
rename to src/langbot/pkg/core/migrations/m036_wxoa_loading_message.py
diff --git a/pkg/core/migrations/m037_mcp_config.py b/src/langbot/pkg/core/migrations/m037_mcp_config.py
similarity index 100%
rename from pkg/core/migrations/m037_mcp_config.py
rename to src/langbot/pkg/core/migrations/m037_mcp_config.py
diff --git a/pkg/core/migrations/m038_tg_dingtalk_markdown.py b/src/langbot/pkg/core/migrations/m038_tg_dingtalk_markdown.py
similarity index 100%
rename from pkg/core/migrations/m038_tg_dingtalk_markdown.py
rename to src/langbot/pkg/core/migrations/m038_tg_dingtalk_markdown.py
diff --git a/pkg/core/migrations/m039_modelscope_cfg_completion.py b/src/langbot/pkg/core/migrations/m039_modelscope_cfg_completion.py
similarity index 100%
rename from pkg/core/migrations/m039_modelscope_cfg_completion.py
rename to src/langbot/pkg/core/migrations/m039_modelscope_cfg_completion.py
diff --git a/pkg/core/migrations/m040_ppio_config.py b/src/langbot/pkg/core/migrations/m040_ppio_config.py
similarity index 100%
rename from pkg/core/migrations/m040_ppio_config.py
rename to src/langbot/pkg/core/migrations/m040_ppio_config.py
diff --git a/pkg/core/note.py b/src/langbot/pkg/core/note.py
similarity index 100%
rename from pkg/core/note.py
rename to src/langbot/pkg/core/note.py
diff --git a/pkg/core/notes/__init__.py b/src/langbot/pkg/core/notes/__init__.py
similarity index 100%
rename from pkg/core/notes/__init__.py
rename to src/langbot/pkg/core/notes/__init__.py
diff --git a/pkg/core/notes/n001_classic_msgs.py b/src/langbot/pkg/core/notes/n001_classic_msgs.py
similarity index 87%
rename from pkg/core/notes/n001_classic_msgs.py
rename to src/langbot/pkg/core/notes/n001_classic_msgs.py
index 265ddbe9..190958c6 100644
--- a/pkg/core/notes/n001_classic_msgs.py
+++ b/src/langbot/pkg/core/notes/n001_classic_msgs.py
@@ -13,6 +13,4 @@ class ClassicNotes(note.LaunchNote):
return True
async def yield_note(self) -> typing.AsyncGenerator[typing.Tuple[str, int], None]:
- yield await self.ap.ann_mgr.show_announcements()
-
yield await self.ap.ver_mgr.show_version_update()
diff --git a/pkg/core/notes/n002_selection_mode_on_windows.py b/src/langbot/pkg/core/notes/n002_selection_mode_on_windows.py
similarity index 100%
rename from pkg/core/notes/n002_selection_mode_on_windows.py
rename to src/langbot/pkg/core/notes/n002_selection_mode_on_windows.py
diff --git a/pkg/core/notes/n003_print_version.py b/src/langbot/pkg/core/notes/n003_print_version.py
similarity index 100%
rename from pkg/core/notes/n003_print_version.py
rename to src/langbot/pkg/core/notes/n003_print_version.py
diff --git a/pkg/core/stage.py b/src/langbot/pkg/core/stage.py
similarity index 100%
rename from pkg/core/stage.py
rename to src/langbot/pkg/core/stage.py
diff --git a/pkg/core/stages/__init__.py b/src/langbot/pkg/core/stages/__init__.py
similarity index 100%
rename from pkg/core/stages/__init__.py
rename to src/langbot/pkg/core/stages/__init__.py
diff --git a/pkg/core/stages/build_app.py b/src/langbot/pkg/core/stages/build_app.py
similarity index 96%
rename from pkg/core/stages/build_app.py
rename to src/langbot/pkg/core/stages/build_app.py
index 407b929d..51fd9a9f 100644
--- a/pkg/core/stages/build_app.py
+++ b/src/langbot/pkg/core/stages/build_app.py
@@ -3,7 +3,7 @@ from __future__ import annotations
import asyncio
from .. import stage, app
-from ...utils import version, proxy, announce
+from ...utils import version, proxy
from ...pipeline import pool, controller, pipelinemgr
from ...plugin import connector as plugin_connector
from ...command import cmdmgr
@@ -39,7 +39,7 @@ class BuildAppStage(stage.BootingStage):
ap.task_mgr = taskmgr.AsyncTaskManager(ap)
discover = discover_engine.ComponentDiscoveryEngine(ap)
- discover.discover_blueprint('components.yaml')
+ discover.discover_blueprint('templates/components.yaml')
ap.discover = discover
proxy_mgr = proxy.ProxyManager(ap)
@@ -50,10 +50,6 @@ class BuildAppStage(stage.BootingStage):
await ver_mgr.initialize()
ap.ver_mgr = ver_mgr
- # Send announcement
- ann_mgr = announce.AnnouncementManager(ap)
- ap.ann_mgr = ann_mgr
-
ap.query_pool = pool.QueryPool()
log_cache = logcache.LogCache()
diff --git a/pkg/core/stages/genkeys.py b/src/langbot/pkg/core/stages/genkeys.py
similarity index 100%
rename from pkg/core/stages/genkeys.py
rename to src/langbot/pkg/core/stages/genkeys.py
diff --git a/pkg/core/stages/load_config.py b/src/langbot/pkg/core/stages/load_config.py
similarity index 60%
rename from pkg/core/stages/load_config.py
rename to src/langbot/pkg/core/stages/load_config.py
index 2ef5623e..b2b5abba 100644
--- a/pkg/core/stages/load_config.py
+++ b/src/langbot/pkg/core/stages/load_config.py
@@ -2,6 +2,8 @@ from __future__ import annotations
import os
from typing import Any
+import yaml
+import importlib.resources as resources
from .. import stage, app
from ..bootutils import config
@@ -95,47 +97,45 @@ class LoadConfigStage(stage.BootingStage):
async def run(self, ap: app.Application):
"""Load config file"""
- # ======= deprecated =======
- if os.path.exists('data/config/command.json'):
- ap.command_cfg = await config.load_json_config(
- 'data/config/command.json',
- 'templates/legacy/command.json',
- completion=False,
- )
+ # # ======= deprecated =======
+ # if os.path.exists('data/config/command.json'):
+ # ap.command_cfg = await config.load_json_config(
+ # 'data/config/command.json',
+ # 'templates/legacy/command.json',
+ # completion=False,
+ # )
- if os.path.exists('data/config/pipeline.json'):
- ap.pipeline_cfg = await config.load_json_config(
- 'data/config/pipeline.json',
- 'templates/legacy/pipeline.json',
- completion=False,
- )
+ # if os.path.exists('data/config/pipeline.json'):
+ # ap.pipeline_cfg = await config.load_json_config(
+ # 'data/config/pipeline.json',
+ # 'templates/legacy/pipeline.json',
+ # completion=False,
+ # )
- if os.path.exists('data/config/platform.json'):
- ap.platform_cfg = await config.load_json_config(
- 'data/config/platform.json',
- 'templates/legacy/platform.json',
- completion=False,
- )
+ # if os.path.exists('data/config/platform.json'):
+ # ap.platform_cfg = await config.load_json_config(
+ # 'data/config/platform.json',
+ # 'templates/legacy/platform.json',
+ # completion=False,
+ # )
- if os.path.exists('data/config/provider.json'):
- ap.provider_cfg = await config.load_json_config(
- 'data/config/provider.json',
- 'templates/legacy/provider.json',
- completion=False,
- )
+ # if os.path.exists('data/config/provider.json'):
+ # ap.provider_cfg = await config.load_json_config(
+ # 'data/config/provider.json',
+ # 'templates/legacy/provider.json',
+ # completion=False,
+ # )
- if os.path.exists('data/config/system.json'):
- ap.system_cfg = await config.load_json_config(
- 'data/config/system.json',
- 'templates/legacy/system.json',
- completion=False,
- )
+ # if os.path.exists('data/config/system.json'):
+ # ap.system_cfg = await config.load_json_config(
+ # 'data/config/system.json',
+ # 'templates/legacy/system.json',
+ # completion=False,
+ # )
- # ======= deprecated =======
+ # # ======= deprecated =======
- ap.instance_config = await config.load_yaml_config(
- 'data/config.yaml', 'templates/config.yaml', completion=False
- )
+ ap.instance_config = await config.load_yaml_config('data/config.yaml', 'config.yaml', completion=False)
# Apply environment variable overrides to data/config.yaml
ap.instance_config.data = _apply_env_overrides_to_config(ap.instance_config.data)
@@ -144,22 +144,15 @@ class LoadConfigStage(stage.BootingStage):
ap.sensitive_meta = await config.load_json_config(
'data/metadata/sensitive-words.json',
- 'templates/metadata/sensitive-words.json',
+ 'metadata/sensitive-words.json',
)
await ap.sensitive_meta.dump_config()
- ap.pipeline_config_meta_trigger = await config.load_yaml_config(
- 'templates/metadata/pipeline/trigger.yaml',
- 'templates/metadata/pipeline/trigger.yaml',
- )
- ap.pipeline_config_meta_safety = await config.load_yaml_config(
- 'templates/metadata/pipeline/safety.yaml',
- 'templates/metadata/pipeline/safety.yaml',
- )
- ap.pipeline_config_meta_ai = await config.load_yaml_config(
- 'templates/metadata/pipeline/ai.yaml', 'templates/metadata/pipeline/ai.yaml'
- )
- ap.pipeline_config_meta_output = await config.load_yaml_config(
- 'templates/metadata/pipeline/output.yaml',
- 'templates/metadata/pipeline/output.yaml',
- )
+ async def load_resource_yaml_template_data(resource_name: str) -> dict:
+ with resources.files('langbot.templates').joinpath(resource_name).open('r', encoding='utf-8') as f:
+ return yaml.load(f, Loader=yaml.FullLoader)
+
+ ap.pipeline_config_meta_trigger = await load_resource_yaml_template_data('metadata/pipeline/trigger.yaml')
+ ap.pipeline_config_meta_safety = await load_resource_yaml_template_data('metadata/pipeline/safety.yaml')
+ ap.pipeline_config_meta_ai = await load_resource_yaml_template_data('metadata/pipeline/ai.yaml')
+ ap.pipeline_config_meta_output = await load_resource_yaml_template_data('metadata/pipeline/output.yaml')
diff --git a/pkg/core/stages/migrate.py b/src/langbot/pkg/core/stages/migrate.py
similarity index 100%
rename from pkg/core/stages/migrate.py
rename to src/langbot/pkg/core/stages/migrate.py
diff --git a/pkg/core/stages/setup_logger.py b/src/langbot/pkg/core/stages/setup_logger.py
similarity index 100%
rename from pkg/core/stages/setup_logger.py
rename to src/langbot/pkg/core/stages/setup_logger.py
diff --git a/pkg/core/stages/show_notes.py b/src/langbot/pkg/core/stages/show_notes.py
similarity index 100%
rename from pkg/core/stages/show_notes.py
rename to src/langbot/pkg/core/stages/show_notes.py
diff --git a/pkg/core/taskmgr.py b/src/langbot/pkg/core/taskmgr.py
similarity index 100%
rename from pkg/core/taskmgr.py
rename to src/langbot/pkg/core/taskmgr.py
diff --git a/pkg/discover/__init__.py b/src/langbot/pkg/discover/__init__.py
similarity index 100%
rename from pkg/discover/__init__.py
rename to src/langbot/pkg/discover/__init__.py
diff --git a/pkg/discover/engine.py b/src/langbot/pkg/discover/engine.py
similarity index 91%
rename from pkg/discover/engine.py
rename to src/langbot/pkg/discover/engine.py
index 335862c0..ca7f2588 100644
--- a/pkg/discover/engine.py
+++ b/src/langbot/pkg/discover/engine.py
@@ -6,7 +6,8 @@ import os
import yaml
import pydantic
-from ..core import app
+from langbot.pkg.core import app
+from langbot.pkg.utils import importutil
class I18nString(pydantic.BaseModel):
@@ -165,7 +166,7 @@ class Component(pydantic.BaseModel):
if module_path.endswith('.py'):
module_path = module_path[:-3]
module_path = module_path.replace('/', '.').replace('\\', '.')
- module = importlib.import_module(module_path)
+ module = importlib.import_module(f'langbot.{module_path}')
return getattr(module, self.execution.python.attr)
def to_plain_dict(self) -> dict:
@@ -193,16 +194,17 @@ class ComponentDiscoveryEngine:
def load_component_manifest(self, path: str, owner: str = 'builtin', no_save: bool = False) -> Component | None:
"""加载组件清单"""
- with open(path, 'r', encoding='utf-8') as f:
- manifest = yaml.safe_load(f)
- if not Component.is_component_manifest(manifest):
- return None
- comp = Component(owner=owner, manifest=manifest, rel_path=path)
- if not no_save:
- if comp.kind not in self.components:
- self.components[comp.kind] = []
- self.components[comp.kind].append(comp)
- return comp
+ # with open(path, 'r', encoding='utf-8') as f:
+ # manifest = yaml.safe_load(f)
+ manifest = yaml.safe_load(importutil.read_resource_file(path))
+ if not Component.is_component_manifest(manifest):
+ return None
+ comp = Component(owner=owner, manifest=manifest, rel_path=path)
+ if not no_save:
+ if comp.kind not in self.components:
+ self.components[comp.kind] = []
+ self.components[comp.kind].append(comp)
+ return comp
def load_component_manifests_in_dir(
self,
@@ -217,7 +219,8 @@ class ComponentDiscoveryEngine:
def recursive_load_component_manifests_in_dir(path: str, depth: int = 1):
if depth > max_depth:
return
- for file in os.listdir(path):
+
+ for file in importutil.list_resource_files(path):
if (not os.path.isdir(os.path.join(path, file))) and (file.endswith('.yaml') or file.endswith('.yml')):
comp = self.load_component_manifest(os.path.join(path, file), owner, no_save)
if comp is not None:
diff --git a/pkg/entity/__init__.py b/src/langbot/pkg/entity/__init__.py
similarity index 100%
rename from pkg/entity/__init__.py
rename to src/langbot/pkg/entity/__init__.py
diff --git a/pkg/entity/errors/__init__.py b/src/langbot/pkg/entity/errors/__init__.py
similarity index 100%
rename from pkg/entity/errors/__init__.py
rename to src/langbot/pkg/entity/errors/__init__.py
diff --git a/pkg/entity/errors/platform.py b/src/langbot/pkg/entity/errors/platform.py
similarity index 100%
rename from pkg/entity/errors/platform.py
rename to src/langbot/pkg/entity/errors/platform.py
diff --git a/pkg/entity/errors/provider.py b/src/langbot/pkg/entity/errors/provider.py
similarity index 100%
rename from pkg/entity/errors/provider.py
rename to src/langbot/pkg/entity/errors/provider.py
diff --git a/pkg/entity/persistence/__init__.py b/src/langbot/pkg/entity/persistence/__init__.py
similarity index 100%
rename from pkg/entity/persistence/__init__.py
rename to src/langbot/pkg/entity/persistence/__init__.py
diff --git a/pkg/entity/persistence/apikey.py b/src/langbot/pkg/entity/persistence/apikey.py
similarity index 100%
rename from pkg/entity/persistence/apikey.py
rename to src/langbot/pkg/entity/persistence/apikey.py
diff --git a/pkg/entity/persistence/base.py b/src/langbot/pkg/entity/persistence/base.py
similarity index 100%
rename from pkg/entity/persistence/base.py
rename to src/langbot/pkg/entity/persistence/base.py
diff --git a/pkg/entity/persistence/bot.py b/src/langbot/pkg/entity/persistence/bot.py
similarity index 100%
rename from pkg/entity/persistence/bot.py
rename to src/langbot/pkg/entity/persistence/bot.py
diff --git a/pkg/entity/persistence/bstorage.py b/src/langbot/pkg/entity/persistence/bstorage.py
similarity index 100%
rename from pkg/entity/persistence/bstorage.py
rename to src/langbot/pkg/entity/persistence/bstorage.py
diff --git a/pkg/entity/persistence/mcp.py b/src/langbot/pkg/entity/persistence/mcp.py
similarity index 100%
rename from pkg/entity/persistence/mcp.py
rename to src/langbot/pkg/entity/persistence/mcp.py
diff --git a/pkg/entity/persistence/metadata.py b/src/langbot/pkg/entity/persistence/metadata.py
similarity index 100%
rename from pkg/entity/persistence/metadata.py
rename to src/langbot/pkg/entity/persistence/metadata.py
diff --git a/pkg/entity/persistence/model.py b/src/langbot/pkg/entity/persistence/model.py
similarity index 100%
rename from pkg/entity/persistence/model.py
rename to src/langbot/pkg/entity/persistence/model.py
diff --git a/pkg/entity/persistence/pipeline.py b/src/langbot/pkg/entity/persistence/pipeline.py
similarity index 100%
rename from pkg/entity/persistence/pipeline.py
rename to src/langbot/pkg/entity/persistence/pipeline.py
diff --git a/pkg/entity/persistence/plugin.py b/src/langbot/pkg/entity/persistence/plugin.py
similarity index 100%
rename from pkg/entity/persistence/plugin.py
rename to src/langbot/pkg/entity/persistence/plugin.py
diff --git a/pkg/entity/persistence/rag.py b/src/langbot/pkg/entity/persistence/rag.py
similarity index 100%
rename from pkg/entity/persistence/rag.py
rename to src/langbot/pkg/entity/persistence/rag.py
diff --git a/pkg/entity/persistence/user.py b/src/langbot/pkg/entity/persistence/user.py
similarity index 100%
rename from pkg/entity/persistence/user.py
rename to src/langbot/pkg/entity/persistence/user.py
diff --git a/pkg/entity/persistence/vector.py b/src/langbot/pkg/entity/persistence/vector.py
similarity index 100%
rename from pkg/entity/persistence/vector.py
rename to src/langbot/pkg/entity/persistence/vector.py
diff --git a/pkg/entity/persistence/webhook.py b/src/langbot/pkg/entity/persistence/webhook.py
similarity index 100%
rename from pkg/entity/persistence/webhook.py
rename to src/langbot/pkg/entity/persistence/webhook.py
diff --git a/pkg/entity/rag/__init__.py b/src/langbot/pkg/entity/rag/__init__.py
similarity index 100%
rename from pkg/entity/rag/__init__.py
rename to src/langbot/pkg/entity/rag/__init__.py
diff --git a/pkg/entity/rag/retriever.py b/src/langbot/pkg/entity/rag/retriever.py
similarity index 100%
rename from pkg/entity/rag/retriever.py
rename to src/langbot/pkg/entity/rag/retriever.py
diff --git a/pkg/persistence/__init__.py b/src/langbot/pkg/persistence/__init__.py
similarity index 100%
rename from pkg/persistence/__init__.py
rename to src/langbot/pkg/persistence/__init__.py
diff --git a/pkg/persistence/database.py b/src/langbot/pkg/persistence/database.py
similarity index 100%
rename from pkg/persistence/database.py
rename to src/langbot/pkg/persistence/database.py
diff --git a/pkg/persistence/databases/__init__.py b/src/langbot/pkg/persistence/databases/__init__.py
similarity index 100%
rename from pkg/persistence/databases/__init__.py
rename to src/langbot/pkg/persistence/databases/__init__.py
diff --git a/pkg/persistence/databases/postgresql.py b/src/langbot/pkg/persistence/databases/postgresql.py
similarity index 100%
rename from pkg/persistence/databases/postgresql.py
rename to src/langbot/pkg/persistence/databases/postgresql.py
diff --git a/pkg/persistence/databases/sqlite.py b/src/langbot/pkg/persistence/databases/sqlite.py
similarity index 100%
rename from pkg/persistence/databases/sqlite.py
rename to src/langbot/pkg/persistence/databases/sqlite.py
diff --git a/pkg/persistence/mgr.py b/src/langbot/pkg/persistence/mgr.py
similarity index 97%
rename from pkg/persistence/mgr.py
rename to src/langbot/pkg/persistence/mgr.py
index 8390a506..53b20011 100644
--- a/pkg/persistence/mgr.py
+++ b/src/langbot/pkg/persistence/mgr.py
@@ -107,7 +107,7 @@ class PersistenceManager:
if result.first() is None:
self.ap.logger.info('Creating default pipeline...')
- pipeline_config = json.load(open('templates/default-pipeline-config.json', 'r', encoding='utf-8'))
+ pipeline_config = json.loads(importutil.read_resource_file('templates/default-pipeline-config.json'))
default_pipeline_uuid = str(uuid.uuid4())
pipeline_data = {
diff --git a/pkg/persistence/migration.py b/src/langbot/pkg/persistence/migration.py
similarity index 100%
rename from pkg/persistence/migration.py
rename to src/langbot/pkg/persistence/migration.py
diff --git a/pkg/persistence/migrations/__init__.py b/src/langbot/pkg/persistence/migrations/__init__.py
similarity index 100%
rename from pkg/persistence/migrations/__init__.py
rename to src/langbot/pkg/persistence/migrations/__init__.py
diff --git a/pkg/persistence/migrations/dbm001_migrate_v3_config.py b/src/langbot/pkg/persistence/migrations/dbm001_migrate_v3_config.py
similarity index 99%
rename from pkg/persistence/migrations/dbm001_migrate_v3_config.py
rename to src/langbot/pkg/persistence/migrations/dbm001_migrate_v3_config.py
index 1f2d9770..55e63fff 100644
--- a/pkg/persistence/migrations/dbm001_migrate_v3_config.py
+++ b/src/langbot/pkg/persistence/migrations/dbm001_migrate_v3_config.py
@@ -212,7 +212,9 @@ class DBMigrateV3Config(migration.DBMigration):
self.ap.instance_config.data['api']['port'] = self.ap.system_cfg.data['http-api']['port']
self.ap.instance_config.data['command'] = {
'prefix': self.ap.command_cfg.data['command-prefix'],
- 'enable': self.ap.command_cfg.data['command-enable'] if 'command-enable' in self.ap.command_cfg.data else True,
+ 'enable': self.ap.command_cfg.data['command-enable']
+ if 'command-enable' in self.ap.command_cfg.data
+ else True,
'privilege': self.ap.command_cfg.data['privilege'],
}
self.ap.instance_config.data['concurrency']['pipeline'] = self.ap.system_cfg.data['pipeline-concurrency']
diff --git a/pkg/persistence/migrations/dbm002_combine_quote_msg_config.py b/src/langbot/pkg/persistence/migrations/dbm002_combine_quote_msg_config.py
similarity index 100%
rename from pkg/persistence/migrations/dbm002_combine_quote_msg_config.py
rename to src/langbot/pkg/persistence/migrations/dbm002_combine_quote_msg_config.py
diff --git a/pkg/persistence/migrations/dbm003_n8n_config.py b/src/langbot/pkg/persistence/migrations/dbm003_n8n_config.py
similarity index 100%
rename from pkg/persistence/migrations/dbm003_n8n_config.py
rename to src/langbot/pkg/persistence/migrations/dbm003_n8n_config.py
diff --git a/pkg/persistence/migrations/dbm004_rag_kb_uuid.py b/src/langbot/pkg/persistence/migrations/dbm004_rag_kb_uuid.py
similarity index 100%
rename from pkg/persistence/migrations/dbm004_rag_kb_uuid.py
rename to src/langbot/pkg/persistence/migrations/dbm004_rag_kb_uuid.py
diff --git a/pkg/persistence/migrations/dbm005_pipeline_remove_cot_config.py b/src/langbot/pkg/persistence/migrations/dbm005_pipeline_remove_cot_config.py
similarity index 100%
rename from pkg/persistence/migrations/dbm005_pipeline_remove_cot_config.py
rename to src/langbot/pkg/persistence/migrations/dbm005_pipeline_remove_cot_config.py
diff --git a/pkg/persistence/migrations/dbm006_langflow_api_config.py b/src/langbot/pkg/persistence/migrations/dbm006_langflow_api_config.py
similarity index 100%
rename from pkg/persistence/migrations/dbm006_langflow_api_config.py
rename to src/langbot/pkg/persistence/migrations/dbm006_langflow_api_config.py
diff --git a/pkg/persistence/migrations/dbm007_plugin_install_source.py b/src/langbot/pkg/persistence/migrations/dbm007_plugin_install_source.py
similarity index 100%
rename from pkg/persistence/migrations/dbm007_plugin_install_source.py
rename to src/langbot/pkg/persistence/migrations/dbm007_plugin_install_source.py
diff --git a/pkg/persistence/migrations/dbm008_plugin_config.py b/src/langbot/pkg/persistence/migrations/dbm008_plugin_config.py
similarity index 100%
rename from pkg/persistence/migrations/dbm008_plugin_config.py
rename to src/langbot/pkg/persistence/migrations/dbm008_plugin_config.py
diff --git a/pkg/persistence/migrations/dbm009_pipeline_extension_preferences.py b/src/langbot/pkg/persistence/migrations/dbm009_pipeline_extension_preferences.py
similarity index 100%
rename from pkg/persistence/migrations/dbm009_pipeline_extension_preferences.py
rename to src/langbot/pkg/persistence/migrations/dbm009_pipeline_extension_preferences.py
diff --git a/pkg/persistence/migrations/dbm010_pipeline_multi_knowledge_base.py b/src/langbot/pkg/persistence/migrations/dbm010_pipeline_multi_knowledge_base.py
similarity index 97%
rename from pkg/persistence/migrations/dbm010_pipeline_multi_knowledge_base.py
rename to src/langbot/pkg/persistence/migrations/dbm010_pipeline_multi_knowledge_base.py
index c28b64ed..cb95c6a5 100644
--- a/pkg/persistence/migrations/dbm010_pipeline_multi_knowledge_base.py
+++ b/src/langbot/pkg/persistence/migrations/dbm010_pipeline_multi_knowledge_base.py
@@ -22,17 +22,17 @@ class DBMigratePipelineMultiKnowledgeBase(migration.DBMigration):
# Convert knowledge-base from string to array
if 'local-agent' in config['ai']:
current_kb = config['ai']['local-agent'].get('knowledge-base', '')
-
+
# If it's already a list, skip
if isinstance(current_kb, list):
continue
-
+
# Convert string to list
if current_kb and current_kb != '__none__':
config['ai']['local-agent']['knowledge-bases'] = [current_kb]
else:
config['ai']['local-agent']['knowledge-bases'] = []
-
+
# Remove old field
if 'knowledge-base' in config['ai']['local-agent']:
del config['ai']['local-agent']['knowledge-base']
@@ -61,17 +61,17 @@ class DBMigratePipelineMultiKnowledgeBase(migration.DBMigration):
# Convert knowledge-bases from array back to string
if 'local-agent' in config['ai']:
current_kbs = config['ai']['local-agent'].get('knowledge-bases', [])
-
+
# If it's already a string, skip
if isinstance(current_kbs, str):
continue
-
+
# Convert list to string (take first one or empty)
if current_kbs and len(current_kbs) > 0:
config['ai']['local-agent']['knowledge-base'] = current_kbs[0]
else:
config['ai']['local-agent']['knowledge-base'] = ''
-
+
# Remove new field
if 'knowledge-bases' in config['ai']['local-agent']:
del config['ai']['local-agent']['knowledge-bases']
diff --git a/pkg/persistence/migrations/dbm011_dify_base_prompt_config.py b/src/langbot/pkg/persistence/migrations/dbm011_dify_base_prompt_config.py
similarity index 100%
rename from pkg/persistence/migrations/dbm011_dify_base_prompt_config.py
rename to src/langbot/pkg/persistence/migrations/dbm011_dify_base_prompt_config.py
diff --git a/pkg/pipeline/__init__.py b/src/langbot/pkg/pipeline/__init__.py
similarity index 100%
rename from pkg/pipeline/__init__.py
rename to src/langbot/pkg/pipeline/__init__.py
diff --git a/pkg/pipeline/bansess/__init__.py b/src/langbot/pkg/pipeline/bansess/__init__.py
similarity index 100%
rename from pkg/pipeline/bansess/__init__.py
rename to src/langbot/pkg/pipeline/bansess/__init__.py
diff --git a/pkg/pipeline/bansess/bansess.py b/src/langbot/pkg/pipeline/bansess/bansess.py
similarity index 100%
rename from pkg/pipeline/bansess/bansess.py
rename to src/langbot/pkg/pipeline/bansess/bansess.py
diff --git a/pkg/pipeline/cntfilter/__init__.py b/src/langbot/pkg/pipeline/cntfilter/__init__.py
similarity index 100%
rename from pkg/pipeline/cntfilter/__init__.py
rename to src/langbot/pkg/pipeline/cntfilter/__init__.py
diff --git a/pkg/pipeline/cntfilter/cntfilter.py b/src/langbot/pkg/pipeline/cntfilter/cntfilter.py
similarity index 100%
rename from pkg/pipeline/cntfilter/cntfilter.py
rename to src/langbot/pkg/pipeline/cntfilter/cntfilter.py
diff --git a/pkg/pipeline/cntfilter/entities.py b/src/langbot/pkg/pipeline/cntfilter/entities.py
similarity index 100%
rename from pkg/pipeline/cntfilter/entities.py
rename to src/langbot/pkg/pipeline/cntfilter/entities.py
diff --git a/pkg/pipeline/cntfilter/filter.py b/src/langbot/pkg/pipeline/cntfilter/filter.py
similarity index 100%
rename from pkg/pipeline/cntfilter/filter.py
rename to src/langbot/pkg/pipeline/cntfilter/filter.py
diff --git a/pkg/pipeline/cntfilter/filters/__init__.py b/src/langbot/pkg/pipeline/cntfilter/filters/__init__.py
similarity index 100%
rename from pkg/pipeline/cntfilter/filters/__init__.py
rename to src/langbot/pkg/pipeline/cntfilter/filters/__init__.py
diff --git a/pkg/pipeline/cntfilter/filters/baiduexamine.py b/src/langbot/pkg/pipeline/cntfilter/filters/baiduexamine.py
similarity index 100%
rename from pkg/pipeline/cntfilter/filters/baiduexamine.py
rename to src/langbot/pkg/pipeline/cntfilter/filters/baiduexamine.py
diff --git a/pkg/pipeline/cntfilter/filters/banwords.py b/src/langbot/pkg/pipeline/cntfilter/filters/banwords.py
similarity index 100%
rename from pkg/pipeline/cntfilter/filters/banwords.py
rename to src/langbot/pkg/pipeline/cntfilter/filters/banwords.py
diff --git a/pkg/pipeline/cntfilter/filters/cntignore.py b/src/langbot/pkg/pipeline/cntfilter/filters/cntignore.py
similarity index 100%
rename from pkg/pipeline/cntfilter/filters/cntignore.py
rename to src/langbot/pkg/pipeline/cntfilter/filters/cntignore.py
diff --git a/pkg/pipeline/controller.py b/src/langbot/pkg/pipeline/controller.py
similarity index 100%
rename from pkg/pipeline/controller.py
rename to src/langbot/pkg/pipeline/controller.py
diff --git a/pkg/pipeline/entities.py b/src/langbot/pkg/pipeline/entities.py
similarity index 100%
rename from pkg/pipeline/entities.py
rename to src/langbot/pkg/pipeline/entities.py
diff --git a/pkg/pipeline/longtext/__init__.py b/src/langbot/pkg/pipeline/longtext/__init__.py
similarity index 100%
rename from pkg/pipeline/longtext/__init__.py
rename to src/langbot/pkg/pipeline/longtext/__init__.py
diff --git a/pkg/pipeline/longtext/longtext.py b/src/langbot/pkg/pipeline/longtext/longtext.py
similarity index 100%
rename from pkg/pipeline/longtext/longtext.py
rename to src/langbot/pkg/pipeline/longtext/longtext.py
diff --git a/pkg/pipeline/longtext/strategies/__init__.py b/src/langbot/pkg/pipeline/longtext/strategies/__init__.py
similarity index 100%
rename from pkg/pipeline/longtext/strategies/__init__.py
rename to src/langbot/pkg/pipeline/longtext/strategies/__init__.py
diff --git a/pkg/pipeline/longtext/strategies/forward.py b/src/langbot/pkg/pipeline/longtext/strategies/forward.py
similarity index 100%
rename from pkg/pipeline/longtext/strategies/forward.py
rename to src/langbot/pkg/pipeline/longtext/strategies/forward.py
diff --git a/pkg/pipeline/longtext/strategies/image.py b/src/langbot/pkg/pipeline/longtext/strategies/image.py
similarity index 100%
rename from pkg/pipeline/longtext/strategies/image.py
rename to src/langbot/pkg/pipeline/longtext/strategies/image.py
diff --git a/pkg/pipeline/longtext/strategy.py b/src/langbot/pkg/pipeline/longtext/strategy.py
similarity index 100%
rename from pkg/pipeline/longtext/strategy.py
rename to src/langbot/pkg/pipeline/longtext/strategy.py
diff --git a/pkg/pipeline/msgtrun/__init__.py b/src/langbot/pkg/pipeline/msgtrun/__init__.py
similarity index 100%
rename from pkg/pipeline/msgtrun/__init__.py
rename to src/langbot/pkg/pipeline/msgtrun/__init__.py
diff --git a/pkg/pipeline/msgtrun/msgtrun.py b/src/langbot/pkg/pipeline/msgtrun/msgtrun.py
similarity index 100%
rename from pkg/pipeline/msgtrun/msgtrun.py
rename to src/langbot/pkg/pipeline/msgtrun/msgtrun.py
diff --git a/pkg/pipeline/msgtrun/truncator.py b/src/langbot/pkg/pipeline/msgtrun/truncator.py
similarity index 100%
rename from pkg/pipeline/msgtrun/truncator.py
rename to src/langbot/pkg/pipeline/msgtrun/truncator.py
diff --git a/pkg/pipeline/msgtrun/truncators/__init__.py b/src/langbot/pkg/pipeline/msgtrun/truncators/__init__.py
similarity index 100%
rename from pkg/pipeline/msgtrun/truncators/__init__.py
rename to src/langbot/pkg/pipeline/msgtrun/truncators/__init__.py
diff --git a/pkg/pipeline/msgtrun/truncators/round.py b/src/langbot/pkg/pipeline/msgtrun/truncators/round.py
similarity index 100%
rename from pkg/pipeline/msgtrun/truncators/round.py
rename to src/langbot/pkg/pipeline/msgtrun/truncators/round.py
diff --git a/pkg/pipeline/pipelinemgr.py b/src/langbot/pkg/pipeline/pipelinemgr.py
similarity index 99%
rename from pkg/pipeline/pipelinemgr.py
rename to src/langbot/pkg/pipeline/pipelinemgr.py
index c4206c0d..9470eb23 100644
--- a/pkg/pipeline/pipelinemgr.py
+++ b/src/langbot/pkg/pipeline/pipelinemgr.py
@@ -68,10 +68,10 @@ class RuntimePipeline:
stage_containers: list[StageInstContainer]
"""阶段实例容器"""
-
+
bound_plugins: list[str]
"""绑定到此流水线的插件列表(格式:author/plugin_name)"""
-
+
bound_mcp_servers: list[str]
"""绑定到此流水线的MCP服务器列表(格式:uuid)"""
@@ -84,12 +84,12 @@ class RuntimePipeline:
self.ap = ap
self.pipeline_entity = pipeline_entity
self.stage_containers = stage_containers
-
+
# Extract bound plugins and MCP servers from extensions_preferences
extensions_prefs = pipeline_entity.extensions_preferences or {}
plugin_list = extensions_prefs.get('plugins', [])
- self.bound_plugins = [f"{p['author']}/{p['name']}" for p in plugin_list] if plugin_list else []
-
+ self.bound_plugins = [f'{p["author"]}/{p["name"]}' for p in plugin_list] if plugin_list else []
+
mcp_server_list = extensions_prefs.get('mcp_servers', [])
self.bound_mcp_servers = mcp_server_list if mcp_server_list else []
@@ -207,7 +207,7 @@ class RuntimePipeline:
try:
# Get bound plugins for this pipeline
bound_plugins = query.variables.get('_pipeline_bound_plugins', None)
-
+
# ======== 触发 MessageReceived 事件 ========
event_type = (
events.PersonMessageReceived
diff --git a/pkg/pipeline/pool.py b/src/langbot/pkg/pipeline/pool.py
similarity index 100%
rename from pkg/pipeline/pool.py
rename to src/langbot/pkg/pipeline/pool.py
diff --git a/pkg/pipeline/preproc/__init__.py b/src/langbot/pkg/pipeline/preproc/__init__.py
similarity index 100%
rename from pkg/pipeline/preproc/__init__.py
rename to src/langbot/pkg/pipeline/preproc/__init__.py
diff --git a/pkg/pipeline/preproc/preproc.py b/src/langbot/pkg/pipeline/preproc/preproc.py
similarity index 100%
rename from pkg/pipeline/preproc/preproc.py
rename to src/langbot/pkg/pipeline/preproc/preproc.py
diff --git a/pkg/pipeline/process/__init__.py b/src/langbot/pkg/pipeline/process/__init__.py
similarity index 100%
rename from pkg/pipeline/process/__init__.py
rename to src/langbot/pkg/pipeline/process/__init__.py
diff --git a/pkg/pipeline/process/handler.py b/src/langbot/pkg/pipeline/process/handler.py
similarity index 100%
rename from pkg/pipeline/process/handler.py
rename to src/langbot/pkg/pipeline/process/handler.py
diff --git a/pkg/pipeline/process/handlers/__init__.py b/src/langbot/pkg/pipeline/process/handlers/__init__.py
similarity index 100%
rename from pkg/pipeline/process/handlers/__init__.py
rename to src/langbot/pkg/pipeline/process/handlers/__init__.py
diff --git a/pkg/pipeline/process/handlers/chat.py b/src/langbot/pkg/pipeline/process/handlers/chat.py
similarity index 100%
rename from pkg/pipeline/process/handlers/chat.py
rename to src/langbot/pkg/pipeline/process/handlers/chat.py
diff --git a/pkg/pipeline/process/handlers/command.py b/src/langbot/pkg/pipeline/process/handlers/command.py
similarity index 100%
rename from pkg/pipeline/process/handlers/command.py
rename to src/langbot/pkg/pipeline/process/handlers/command.py
diff --git a/pkg/pipeline/process/process.py b/src/langbot/pkg/pipeline/process/process.py
similarity index 100%
rename from pkg/pipeline/process/process.py
rename to src/langbot/pkg/pipeline/process/process.py
diff --git a/pkg/pipeline/ratelimit/__init__.py b/src/langbot/pkg/pipeline/ratelimit/__init__.py
similarity index 100%
rename from pkg/pipeline/ratelimit/__init__.py
rename to src/langbot/pkg/pipeline/ratelimit/__init__.py
diff --git a/pkg/pipeline/ratelimit/algo.py b/src/langbot/pkg/pipeline/ratelimit/algo.py
similarity index 100%
rename from pkg/pipeline/ratelimit/algo.py
rename to src/langbot/pkg/pipeline/ratelimit/algo.py
diff --git a/pkg/pipeline/ratelimit/algos/__init__.py b/src/langbot/pkg/pipeline/ratelimit/algos/__init__.py
similarity index 100%
rename from pkg/pipeline/ratelimit/algos/__init__.py
rename to src/langbot/pkg/pipeline/ratelimit/algos/__init__.py
diff --git a/pkg/pipeline/ratelimit/algos/fixedwin.py b/src/langbot/pkg/pipeline/ratelimit/algos/fixedwin.py
similarity index 100%
rename from pkg/pipeline/ratelimit/algos/fixedwin.py
rename to src/langbot/pkg/pipeline/ratelimit/algos/fixedwin.py
diff --git a/pkg/pipeline/ratelimit/ratelimit.py b/src/langbot/pkg/pipeline/ratelimit/ratelimit.py
similarity index 100%
rename from pkg/pipeline/ratelimit/ratelimit.py
rename to src/langbot/pkg/pipeline/ratelimit/ratelimit.py
diff --git a/pkg/pipeline/respback/__init__.py b/src/langbot/pkg/pipeline/respback/__init__.py
similarity index 100%
rename from pkg/pipeline/respback/__init__.py
rename to src/langbot/pkg/pipeline/respback/__init__.py
diff --git a/pkg/pipeline/respback/respback.py b/src/langbot/pkg/pipeline/respback/respback.py
similarity index 100%
rename from pkg/pipeline/respback/respback.py
rename to src/langbot/pkg/pipeline/respback/respback.py
diff --git a/pkg/pipeline/resprule/__init__.py b/src/langbot/pkg/pipeline/resprule/__init__.py
similarity index 100%
rename from pkg/pipeline/resprule/__init__.py
rename to src/langbot/pkg/pipeline/resprule/__init__.py
diff --git a/pkg/pipeline/resprule/entities.py b/src/langbot/pkg/pipeline/resprule/entities.py
similarity index 100%
rename from pkg/pipeline/resprule/entities.py
rename to src/langbot/pkg/pipeline/resprule/entities.py
diff --git a/pkg/pipeline/resprule/resprule.py b/src/langbot/pkg/pipeline/resprule/resprule.py
similarity index 100%
rename from pkg/pipeline/resprule/resprule.py
rename to src/langbot/pkg/pipeline/resprule/resprule.py
diff --git a/pkg/pipeline/resprule/rule.py b/src/langbot/pkg/pipeline/resprule/rule.py
similarity index 100%
rename from pkg/pipeline/resprule/rule.py
rename to src/langbot/pkg/pipeline/resprule/rule.py
diff --git a/pkg/pipeline/resprule/rules/__init__.py b/src/langbot/pkg/pipeline/resprule/rules/__init__.py
similarity index 100%
rename from pkg/pipeline/resprule/rules/__init__.py
rename to src/langbot/pkg/pipeline/resprule/rules/__init__.py
diff --git a/pkg/pipeline/resprule/rules/atbot.py b/src/langbot/pkg/pipeline/resprule/rules/atbot.py
similarity index 91%
rename from pkg/pipeline/resprule/rules/atbot.py
rename to src/langbot/pkg/pipeline/resprule/rules/atbot.py
index 68c3ace9..9d549d10 100644
--- a/pkg/pipeline/resprule/rules/atbot.py
+++ b/src/langbot/pkg/pipeline/resprule/rules/atbot.py
@@ -21,7 +21,9 @@ class AtBotRule(rule_model.GroupRespondRule):
def remove_at(message_chain: platform_message.MessageChain):
nonlocal found
for component in message_chain.root:
- if isinstance(component, platform_message.At) and str(component.target) == str(query.adapter.bot_account_id):
+ if isinstance(component, platform_message.At) and str(component.target) == str(
+ query.adapter.bot_account_id
+ ):
message_chain.remove(component)
found = True
break
diff --git a/pkg/pipeline/resprule/rules/prefix.py b/src/langbot/pkg/pipeline/resprule/rules/prefix.py
similarity index 100%
rename from pkg/pipeline/resprule/rules/prefix.py
rename to src/langbot/pkg/pipeline/resprule/rules/prefix.py
diff --git a/pkg/pipeline/resprule/rules/random.py b/src/langbot/pkg/pipeline/resprule/rules/random.py
similarity index 100%
rename from pkg/pipeline/resprule/rules/random.py
rename to src/langbot/pkg/pipeline/resprule/rules/random.py
diff --git a/pkg/pipeline/resprule/rules/regexp.py b/src/langbot/pkg/pipeline/resprule/rules/regexp.py
similarity index 100%
rename from pkg/pipeline/resprule/rules/regexp.py
rename to src/langbot/pkg/pipeline/resprule/rules/regexp.py
diff --git a/pkg/pipeline/stage.py b/src/langbot/pkg/pipeline/stage.py
similarity index 100%
rename from pkg/pipeline/stage.py
rename to src/langbot/pkg/pipeline/stage.py
diff --git a/pkg/pipeline/wrapper/__init__.py b/src/langbot/pkg/pipeline/wrapper/__init__.py
similarity index 100%
rename from pkg/pipeline/wrapper/__init__.py
rename to src/langbot/pkg/pipeline/wrapper/__init__.py
diff --git a/pkg/pipeline/wrapper/wrapper.py b/src/langbot/pkg/pipeline/wrapper/wrapper.py
similarity index 100%
rename from pkg/pipeline/wrapper/wrapper.py
rename to src/langbot/pkg/pipeline/wrapper/wrapper.py
diff --git a/pkg/platform/__init__.py b/src/langbot/pkg/platform/__init__.py
similarity index 100%
rename from pkg/platform/__init__.py
rename to src/langbot/pkg/platform/__init__.py
diff --git a/pkg/platform/botmgr.py b/src/langbot/pkg/platform/botmgr.py
similarity index 96%
rename from pkg/platform/botmgr.py
rename to src/langbot/pkg/platform/botmgr.py
index dca24f96..73c59da4 100644
--- a/pkg/platform/botmgr.py
+++ b/src/langbot/pkg/platform/botmgr.py
@@ -13,7 +13,6 @@ from ..entity.persistence import bot as persistence_bot
from ..entity.errors import platform as platform_errors
from .logger import EventLogger
-from .webhook_pusher import WebhookPusher
import langbot_plugin.api.entities.builtin.provider.session as provider_session
import langbot_plugin.api.entities.builtin.platform.events as platform_events
@@ -70,9 +69,7 @@ class RuntimeBot:
# Push to webhooks
if hasattr(self.ap, 'webhook_pusher') and self.ap.webhook_pusher:
asyncio.create_task(
- self.ap.webhook_pusher.push_person_message(
- event, self.bot_entity.uuid, adapter.__class__.__name__
- )
+ self.ap.webhook_pusher.push_person_message(event, self.bot_entity.uuid, adapter.__class__.__name__)
)
await self.ap.query_pool.add_query(
@@ -103,9 +100,7 @@ class RuntimeBot:
# Push to webhooks
if hasattr(self.ap, 'webhook_pusher') and self.ap.webhook_pusher:
asyncio.create_task(
- self.ap.webhook_pusher.push_group_message(
- event, self.bot_entity.uuid, adapter.__class__.__name__
- )
+ self.ap.webhook_pusher.push_group_message(event, self.bot_entity.uuid, adapter.__class__.__name__)
)
await self.ap.query_pool.add_query(
diff --git a/pkg/platform/logger.py b/src/langbot/pkg/platform/logger.py
similarity index 100%
rename from pkg/platform/logger.py
rename to src/langbot/pkg/platform/logger.py
diff --git a/pkg/platform/sources/__init__.py b/src/langbot/pkg/platform/sources/__init__.py
similarity index 100%
rename from pkg/platform/sources/__init__.py
rename to src/langbot/pkg/platform/sources/__init__.py
diff --git a/pkg/platform/sources/aiocqhttp.py b/src/langbot/pkg/platform/sources/aiocqhttp.py
similarity index 100%
rename from pkg/platform/sources/aiocqhttp.py
rename to src/langbot/pkg/platform/sources/aiocqhttp.py
diff --git a/pkg/platform/sources/aiocqhttp.yaml b/src/langbot/pkg/platform/sources/aiocqhttp.yaml
similarity index 100%
rename from pkg/platform/sources/aiocqhttp.yaml
rename to src/langbot/pkg/platform/sources/aiocqhttp.yaml
diff --git a/pkg/platform/sources/dingtalk.py b/src/langbot/pkg/platform/sources/dingtalk.py
similarity index 95%
rename from pkg/platform/sources/dingtalk.py
rename to src/langbot/pkg/platform/sources/dingtalk.py
index ed468e35..c072a567 100644
--- a/pkg/platform/sources/dingtalk.py
+++ b/src/langbot/pkg/platform/sources/dingtalk.py
@@ -1,14 +1,13 @@
-
import traceback
import typing
-from libs.dingtalk_api.dingtalkevent import DingTalkEvent
+from langbot.libs.dingtalk_api.dingtalkevent import DingTalkEvent
import langbot_plugin.api.entities.builtin.platform.message as platform_message
import langbot_plugin.api.definition.abstract.platform.adapter as abstract_platform_adapter
import langbot_plugin.api.entities.builtin.platform.events as platform_events
import langbot_plugin.api.entities.builtin.platform.entities as platform_entities
-from libs.dingtalk_api.api import DingTalkClient
+from langbot.libs.dingtalk_api.api import DingTalkClient
import datetime
-from ..logger import EventLogger
+from langbot.pkg.platform.logger import EventLogger
class DingTalkMessageConverter(abstract_platform_adapter.AbstractMessageConverter):
@@ -38,7 +37,7 @@ class DingTalkMessageConverter(abstract_platform_adapter.AbstractMessageConverte
yiri_msg_list.append(platform_message.At(target=bot_name))
if event.rich_content:
- elements = event.rich_content.get("Elements")
+ elements = event.rich_content.get('Elements')
for element in elements:
if element.get('Type') == 'text':
text = element.get('Content', '').replace('@' + bot_name, '')
@@ -60,8 +59,6 @@ class DingTalkMessageConverter(abstract_platform_adapter.AbstractMessageConverte
if event.audio:
yiri_msg_list.append(platform_message.Voice(base64=event.audio))
-
-
chain = platform_message.MessageChain(yiri_msg_list)
return chain
@@ -122,7 +119,6 @@ class DingTalkAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
)
def __init__(self, config: dict, logger: EventLogger):
-
required_keys = [
'client_id',
'client_secret',
@@ -133,13 +129,13 @@ class DingTalkAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
if missing_keys:
raise Exception('钉钉缺少相关配置项,请查看文档或联系管理员')
bot = DingTalkClient(
- client_id=config['client_id'],
- client_secret=config['client_secret'],
- robot_name=config['robot_name'],
- robot_code=config['robot_code'],
- markdown_card=config['markdown_card'],
- logger=logger,
- )
+ client_id=config['client_id'],
+ client_secret=config['client_secret'],
+ robot_name=config['robot_name'],
+ robot_code=config['robot_code'],
+ markdown_card=config['markdown_card'],
+ logger=logger,
+ )
bot_account_id = config['robot_name']
super().__init__(
config=config,
@@ -148,7 +144,6 @@ class DingTalkAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
bot_account_id=bot_account_id,
bot=bot,
listeners={},
-
)
async def reply_message(
diff --git a/pkg/platform/sources/dingtalk.svg b/src/langbot/pkg/platform/sources/dingtalk.svg
similarity index 100%
rename from pkg/platform/sources/dingtalk.svg
rename to src/langbot/pkg/platform/sources/dingtalk.svg
diff --git a/pkg/platform/sources/dingtalk.yaml b/src/langbot/pkg/platform/sources/dingtalk.yaml
similarity index 100%
rename from pkg/platform/sources/dingtalk.yaml
rename to src/langbot/pkg/platform/sources/dingtalk.yaml
diff --git a/pkg/platform/sources/discord.py b/src/langbot/pkg/platform/sources/discord.py
similarity index 99%
rename from pkg/platform/sources/discord.py
rename to src/langbot/pkg/platform/sources/discord.py
index 98791260..933961de 100644
--- a/pkg/platform/sources/discord.py
+++ b/src/langbot/pkg/platform/sources/discord.py
@@ -22,7 +22,6 @@ import langbot_plugin.api.definition.abstract.platform.event_logger as abstract_
from ..logger import EventLogger
-
# 语音功能相关异常定义
class VoiceConnectionError(Exception):
"""语音连接基础异常"""
diff --git a/pkg/platform/sources/discord.svg b/src/langbot/pkg/platform/sources/discord.svg
similarity index 100%
rename from pkg/platform/sources/discord.svg
rename to src/langbot/pkg/platform/sources/discord.svg
diff --git a/pkg/platform/sources/discord.yaml b/src/langbot/pkg/platform/sources/discord.yaml
similarity index 100%
rename from pkg/platform/sources/discord.yaml
rename to src/langbot/pkg/platform/sources/discord.yaml
diff --git a/pkg/platform/sources/lark.py b/src/langbot/pkg/platform/sources/lark.py
similarity index 99%
rename from pkg/platform/sources/lark.py
rename to src/langbot/pkg/platform/sources/lark.py
index 23257e6f..684091a2 100644
--- a/pkg/platform/sources/lark.py
+++ b/src/langbot/pkg/platform/sources/lark.py
@@ -627,6 +627,7 @@ class LarkAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
except Exception as e:
raise e
+
async def create_message_card(self, message_id, event) -> str:
"""
创建卡片消息。
diff --git a/pkg/platform/sources/lark.svg b/src/langbot/pkg/platform/sources/lark.svg
similarity index 100%
rename from pkg/platform/sources/lark.svg
rename to src/langbot/pkg/platform/sources/lark.svg
diff --git a/pkg/platform/sources/lark.yaml b/src/langbot/pkg/platform/sources/lark.yaml
similarity index 100%
rename from pkg/platform/sources/lark.yaml
rename to src/langbot/pkg/platform/sources/lark.yaml
diff --git a/pkg/platform/sources/legacy/gewechat.png b/src/langbot/pkg/platform/sources/legacy/gewechat.png
similarity index 100%
rename from pkg/platform/sources/legacy/gewechat.png
rename to src/langbot/pkg/platform/sources/legacy/gewechat.png
diff --git a/pkg/platform/sources/legacy/gewechat.py b/src/langbot/pkg/platform/sources/legacy/gewechat.py
similarity index 100%
rename from pkg/platform/sources/legacy/gewechat.py
rename to src/langbot/pkg/platform/sources/legacy/gewechat.py
diff --git a/pkg/platform/sources/legacy/gewechat.yaml b/src/langbot/pkg/platform/sources/legacy/gewechat.yaml
similarity index 100%
rename from pkg/platform/sources/legacy/gewechat.yaml
rename to src/langbot/pkg/platform/sources/legacy/gewechat.yaml
diff --git a/pkg/platform/sources/legacy/nakuru.png b/src/langbot/pkg/platform/sources/legacy/nakuru.png
similarity index 100%
rename from pkg/platform/sources/legacy/nakuru.png
rename to src/langbot/pkg/platform/sources/legacy/nakuru.png
diff --git a/pkg/platform/sources/legacy/nakuru.py b/src/langbot/pkg/platform/sources/legacy/nakuru.py
similarity index 100%
rename from pkg/platform/sources/legacy/nakuru.py
rename to src/langbot/pkg/platform/sources/legacy/nakuru.py
diff --git a/pkg/platform/sources/legacy/nakuru.yaml b/src/langbot/pkg/platform/sources/legacy/nakuru.yaml
similarity index 100%
rename from pkg/platform/sources/legacy/nakuru.yaml
rename to src/langbot/pkg/platform/sources/legacy/nakuru.yaml
diff --git a/pkg/platform/sources/legacy/qqbotpy.py b/src/langbot/pkg/platform/sources/legacy/qqbotpy.py
similarity index 100%
rename from pkg/platform/sources/legacy/qqbotpy.py
rename to src/langbot/pkg/platform/sources/legacy/qqbotpy.py
diff --git a/pkg/platform/sources/legacy/qqbotpy.svg b/src/langbot/pkg/platform/sources/legacy/qqbotpy.svg
similarity index 100%
rename from pkg/platform/sources/legacy/qqbotpy.svg
rename to src/langbot/pkg/platform/sources/legacy/qqbotpy.svg
diff --git a/pkg/platform/sources/legacy/qqbotpy.yaml b/src/langbot/pkg/platform/sources/legacy/qqbotpy.yaml
similarity index 100%
rename from pkg/platform/sources/legacy/qqbotpy.yaml
rename to src/langbot/pkg/platform/sources/legacy/qqbotpy.yaml
diff --git a/pkg/platform/sources/line.png b/src/langbot/pkg/platform/sources/line.png
similarity index 100%
rename from pkg/platform/sources/line.png
rename to src/langbot/pkg/platform/sources/line.png
diff --git a/pkg/platform/sources/line.py b/src/langbot/pkg/platform/sources/line.py
similarity index 80%
rename from pkg/platform/sources/line.py
rename to src/langbot/pkg/platform/sources/line.py
index 1cbf9850..29ab361e 100644
--- a/pkg/platform/sources/line.py
+++ b/src/langbot/pkg/platform/sources/line.py
@@ -3,18 +3,11 @@ import quart
import traceback
-import typing
import asyncio
-import re
import base64
-import uuid
-import json
import datetime
-import hashlib
-from Crypto.Cipher import AES
-from ...core import app
import langbot_plugin.api.definition.abstract.platform.adapter as abstract_platform_adapter
import langbot_plugin.api.entities.builtin.platform.message as platform_message
import langbot_plugin.api.entities.builtin.platform.events as platform_events
@@ -22,30 +15,15 @@ import langbot_plugin.api.entities.builtin.platform.entities as platform_entitie
from ..logger import EventLogger
-
-from linebot.v3 import (
- WebhookHandler
-)
-from linebot.v3.exceptions import (
- InvalidSignatureError
-)
-from linebot.v3.messaging import (
- Configuration,
- ApiClient,
- MessagingApi,
- ReplyMessageRequest,
- TextMessage,
- ImageMessage
-)
+from linebot.v3 import WebhookHandler
+from linebot.v3.exceptions import InvalidSignatureError
+from linebot.v3.messaging import Configuration, ApiClient, MessagingApi, ReplyMessageRequest, TextMessage, ImageMessage
from linebot.v3.webhooks import (
MessageEvent,
TextMessageContent,
ImageMessageContent,
VideoMessageContent,
AudioMessageContent,
- FileMessageContent,
- LocationMessageContent,
- StickerMessageContent
)
# from linebot import WebhookParser
@@ -53,12 +31,9 @@ from linebot.v3.webhook import WebhookParser
from linebot.v3.messaging import MessagingApiBlob
-
class LINEMessageConverter(abstract_platform_adapter.AbstractMessageConverter):
@staticmethod
- async def yiri2target(
- message_chain: platform_message.MessageChain, api_client: ApiClient
- ) -> typing.Tuple[list]:
+ async def yiri2target(message_chain: platform_message.MessageChain, api_client: ApiClient) -> typing.Tuple[list]:
content_list = []
for component in message_chain:
if isinstance(component, platform_message.At):
@@ -72,15 +47,11 @@ class LINEMessageConverter(abstract_platform_adapter.AbstractMessageConverter):
elif isinstance(component, platform_message.Voice):
content_list.append({'type': 'voice', 'url': component.url, 'length': component.length})
-
return content_list
@staticmethod
- async def target2yiri(
- message,
- bot_client
- ) -> platform_message.MessageChain:
+ async def target2yiri(message, bot_client) -> platform_message.MessageChain:
lb_msg_list = []
msg_create_time = datetime.datetime.fromtimestamp(int(message.timestamp) / 1000)
@@ -99,8 +70,8 @@ class LINEMessageConverter(abstract_platform_adapter.AbstractMessageConverter):
# 如果需要Data URI格式(用于直接嵌入HTML等)
# 首先需要知道图片类型,LINE图片通常是JPEG
- data_uri = f"data:image/jpeg;base64,{base64_string}"
- lb_msg_list.append(platform_message.Image(base64 = data_uri))
+ data_uri = f'data:image/jpeg;base64,{base64_string}'
+ lb_msg_list.append(platform_message.Image(base64=data_uri))
return platform_message.MessageChain(lb_msg_list)
@@ -112,13 +83,10 @@ class LINEEventConverter(abstract_platform_adapter.AbstractEventConverter):
pass
@staticmethod
- async def target2yiri(
- event,
- bot_client
- ) -> platform_events.Event:
+ async def target2yiri(event, bot_client) -> platform_events.Event:
message_chain = await LINEMessageConverter.target2yiri(event, bot_client)
- if event.source.type== 'user':
+ if event.source.type == 'user':
return platform_events.FriendMessage(
sender=platform_entities.Friend(
id=event.message.id,
@@ -150,6 +118,7 @@ class LINEEventConverter(abstract_platform_adapter.AbstractEventConverter):
source_platform_object=event,
)
+
class LINEAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
bot: MessagingApi
api_client: ApiClient
@@ -166,7 +135,6 @@ class LINEAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
config: dict
quart_app: quart.Quart
-
card_id_dict: dict[str, str] # 消息id到卡片id的映射,便于创建卡片后的发送消息到指定卡片
seq: int # 用于在发送卡片消息中识别消息顺序,直接以seq作为标识
@@ -179,22 +147,21 @@ class LINEAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
bot_account_id = config.get('bot_account_id', 'langbot')
-
super().__init__(
- config = config,
- logger = logger,
- quart_app = quart.Quart(__name__),
- listeners = {},
- card_id_dict = {},
- seq = 1,
- event_converter = LINEEventConverter(),
- message_converter = LINEMessageConverter(),
- line_webhook = line_webhook,
- parser = parser,
+ config=config,
+ logger=logger,
+ quart_app=quart.Quart(__name__),
+ listeners={},
+ card_id_dict={},
+ seq=1,
+ event_converter=LINEEventConverter(),
+ message_converter=LINEMessageConverter(),
+ line_webhook=line_webhook,
+ parser=parser,
configuration=configuration,
- api_client = api_client,
- bot = MessagingApi(api_client),
- bot_account_id = bot_account_id,
+ api_client=api_client,
+ bot=MessagingApi(api_client),
+ bot_account_id=bot_account_id,
)
@self.quart_app.route('/line/callback', methods=['POST'])
@@ -205,28 +172,22 @@ class LINEAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
events = parser.parse(body, signature) # 解密解析消息
try:
-
# print(events)
lb_event = await self.event_converter.target2yiri(events[0], self.api_client)
if lb_event.__class__ in self.listeners:
await self.listeners[lb_event.__class__](lb_event, self)
except InvalidSignatureError:
- self.logger.info(f"Invalid signature. Please check your channel access token/channel secret.{traceback.format_exc()}")
+ self.logger.info(
+ f'Invalid signature. Please check your channel access token/channel secret.{traceback.format_exc()}'
+ )
return quart.Response('Invalid signature', status=400)
-
return {'code': 200, 'message': 'ok'}
except Exception:
await self.logger.error(f'Error in LINE callback: {traceback.format_exc()}')
return {'code': 500, 'message': 'error'}
-
-
-
-
-
async def send_message(self, target_type: str, target_id: str, message: platform_message.MessageChain):
-
pass
async def reply_message(
@@ -242,14 +203,14 @@ class LINEAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
self.bot.reply_message_with_http_info(
ReplyMessageRequest(
reply_token=message_source.source_platform_object.reply_token,
- messages=[TextMessage(text=content['content'])]
+ messages=[TextMessage(text=content['content'])],
)
)
elif content['type'] == 'image':
- self.bot.reply_message_with_http_info(
+ self.bot.reply_message_with_http_info(
ReplyMessageRequest(
reply_token=message_source.source_platform_object.reply_token,
- messages=[ImageMessage(text=content['content'])]
+ messages=[ImageMessage(text=content['content'])],
)
)
@@ -259,14 +220,18 @@ class LINEAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
def register_listener(
self,
event_type: typing.Type[platform_events.Event],
- callback: typing.Callable[[platform_events.Event, abstract_platform_adapter.AbstractMessagePlatformAdapter], None],
+ callback: typing.Callable[
+ [platform_events.Event, abstract_platform_adapter.AbstractMessagePlatformAdapter], None
+ ],
):
self.listeners[event_type] = callback
def unregister_listener(
self,
event_type: typing.Type[platform_events.Event],
- callback: typing.Callable[[platform_events.Event, abstract_platform_adapter.AbstractMessagePlatformAdapter], None],
+ callback: typing.Callable[
+ [platform_events.Event, abstract_platform_adapter.AbstractMessagePlatformAdapter], None
+ ],
):
self.listeners.pop(event_type)
@@ -276,6 +241,7 @@ class LINEAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
async def shutdown_trigger_placeholder():
while True:
await asyncio.sleep(1)
+
await self.quart_app.run_task(
host='0.0.0.0',
port=port,
diff --git a/pkg/platform/sources/line.yaml b/src/langbot/pkg/platform/sources/line.yaml
similarity index 100%
rename from pkg/platform/sources/line.yaml
rename to src/langbot/pkg/platform/sources/line.yaml
diff --git a/pkg/platform/sources/officialaccount.png b/src/langbot/pkg/platform/sources/officialaccount.png
similarity index 100%
rename from pkg/platform/sources/officialaccount.png
rename to src/langbot/pkg/platform/sources/officialaccount.png
diff --git a/pkg/platform/sources/officialaccount.py b/src/langbot/pkg/platform/sources/officialaccount.py
similarity index 93%
rename from pkg/platform/sources/officialaccount.py
rename to src/langbot/pkg/platform/sources/officialaccount.py
index 1f70f3eb..7b7f38d9 100644
--- a/pkg/platform/sources/officialaccount.py
+++ b/src/langbot/pkg/platform/sources/officialaccount.py
@@ -5,14 +5,13 @@ import traceback
import pydantic
import datetime
import langbot_plugin.api.definition.abstract.platform.adapter as abstract_platform_adapter
-from libs.official_account_api.oaevent import OAEvent
-from libs.official_account_api.api import OAClient
-from libs.official_account_api.api import OAClientForLongerResponse
+from langbot.libs.official_account_api.oaevent import OAEvent
+from langbot.libs.official_account_api.api import OAClient
+from langbot.libs.official_account_api.api import OAClientForLongerResponse
import langbot_plugin.api.entities.builtin.platform.entities as platform_entities
import langbot_plugin.api.entities.builtin.platform.message as platform_message
import langbot_plugin.api.entities.builtin.platform.events as platform_events
-from langbot_plugin.api.entities.builtin.command import errors as command_errors
-from ..logger import EventLogger
+from langbot.pkg.platform.logger import EventLogger
class OAMessageConverter(abstract_platform_adapter.AbstractMessageConverter):
@@ -66,7 +65,6 @@ class OfficialAccountAdapter(abstract_platform_adapter.AbstractMessagePlatformAd
if missing_keys:
raise Exception(f'OfficialAccount 缺少配置项: {missing_keys}')
-
if config['Mode'] == 'drop':
bot = OAClient(
token=config['token'],
@@ -89,7 +87,6 @@ class OfficialAccountAdapter(abstract_platform_adapter.AbstractMessagePlatformAd
bot_account_id = config.get('AppID', '')
-
super().__init__(
bot=bot,
bot_account_id=bot_account_id,
@@ -97,10 +94,6 @@ class OfficialAccountAdapter(abstract_platform_adapter.AbstractMessagePlatformAd
logger=logger,
)
-
-
-
-
async def reply_message(
self,
message_source: platform_events.FriendMessage,
@@ -159,5 +152,8 @@ class OfficialAccountAdapter(abstract_platform_adapter.AbstractMessagePlatformAd
):
return super().unregister_listener(event_type, callback)
- async def is_muted(self, group_id: str, ) -> bool:
+ async def is_muted(
+ self,
+ group_id: str,
+ ) -> bool:
pass
diff --git a/pkg/platform/sources/officialaccount.yaml b/src/langbot/pkg/platform/sources/officialaccount.yaml
similarity index 100%
rename from pkg/platform/sources/officialaccount.yaml
rename to src/langbot/pkg/platform/sources/officialaccount.yaml
diff --git a/pkg/platform/sources/onebot.png b/src/langbot/pkg/platform/sources/onebot.png
similarity index 100%
rename from pkg/platform/sources/onebot.png
rename to src/langbot/pkg/platform/sources/onebot.png
diff --git a/pkg/platform/sources/qqofficial.py b/src/langbot/pkg/platform/sources/qqofficial.py
similarity index 95%
rename from pkg/platform/sources/qqofficial.py
rename to src/langbot/pkg/platform/sources/qqofficial.py
index 240b46d0..c0375e16 100644
--- a/pkg/platform/sources/qqofficial.py
+++ b/src/langbot/pkg/platform/sources/qqofficial.py
@@ -9,11 +9,10 @@ import langbot_plugin.api.definition.abstract.platform.adapter as abstract_platf
import langbot_plugin.api.entities.builtin.platform.message as platform_message
import langbot_plugin.api.entities.builtin.platform.events as platform_events
import langbot_plugin.api.entities.builtin.platform.entities as platform_entities
-from langbot_plugin.api.entities.builtin.command import errors as command_errors
-from libs.qq_official_api.api import QQOfficialClient
-from libs.qq_official_api.qqofficialevent import QQOfficialEvent
-from ...utils import image
-from ..logger import EventLogger
+from langbot.libs.qq_official_api.api import QQOfficialClient
+from langbot.libs.qq_official_api.qqofficialevent import QQOfficialEvent
+from langbot.pkg.utils import image
+from langbot.pkg.platform.logger import EventLogger
class QQOfficialMessageConverter(abstract_platform_adapter.AbstractMessageConverter):
@@ -139,9 +138,7 @@ class QQOfficialAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter
event_converter: QQOfficialEventConverter = QQOfficialEventConverter()
def __init__(self, config: dict, logger: EventLogger):
- bot = QQOfficialClient(
- app_id=config['appid'], secret=config['secret'], token=config['token'], logger=logger
- )
+ bot = QQOfficialClient(app_id=config['appid'], secret=config['secret'], token=config['token'], logger=logger)
super().__init__(
config=config,
diff --git a/pkg/platform/sources/qqofficial.svg b/src/langbot/pkg/platform/sources/qqofficial.svg
similarity index 100%
rename from pkg/platform/sources/qqofficial.svg
rename to src/langbot/pkg/platform/sources/qqofficial.svg
diff --git a/pkg/platform/sources/qqofficial.yaml b/src/langbot/pkg/platform/sources/qqofficial.yaml
similarity index 100%
rename from pkg/platform/sources/qqofficial.yaml
rename to src/langbot/pkg/platform/sources/qqofficial.yaml
diff --git a/pkg/platform/sources/slack.png b/src/langbot/pkg/platform/sources/slack.png
similarity index 100%
rename from pkg/platform/sources/slack.png
rename to src/langbot/pkg/platform/sources/slack.png
diff --git a/pkg/platform/sources/slack.py b/src/langbot/pkg/platform/sources/slack.py
similarity index 97%
rename from pkg/platform/sources/slack.py
rename to src/langbot/pkg/platform/sources/slack.py
index e08cc8c0..dd0ed655 100644
--- a/pkg/platform/sources/slack.py
+++ b/src/langbot/pkg/platform/sources/slack.py
@@ -5,15 +5,15 @@ import traceback
import datetime
-from libs.slack_api.api import SlackClient
+from langbot.libs.slack_api.api import SlackClient
import langbot_plugin.api.definition.abstract.platform.adapter as abstract_platform_adapter
-from libs.slack_api.slackevent import SlackEvent
+from langbot.libs.slack_api.slackevent import SlackEvent
import langbot_plugin.api.entities.builtin.platform.events as platform_events
import langbot_plugin.api.entities.builtin.platform.message as platform_message
import langbot_plugin.api.entities.builtin.platform.entities as platform_entities
from langbot_plugin.api.entities.builtin.command import errors as command_errors
-from ...utils import image
-from ..logger import EventLogger
+from langbot.pkg.utils import image
+from langbot.pkg.platform.logger import EventLogger
class SlackMessageConverter(abstract_platform_adapter.AbstractMessageConverter):
diff --git a/pkg/platform/sources/slack.yaml b/src/langbot/pkg/platform/sources/slack.yaml
similarity index 100%
rename from pkg/platform/sources/slack.yaml
rename to src/langbot/pkg/platform/sources/slack.yaml
diff --git a/pkg/platform/sources/telegram.py b/src/langbot/pkg/platform/sources/telegram.py
similarity index 100%
rename from pkg/platform/sources/telegram.py
rename to src/langbot/pkg/platform/sources/telegram.py
diff --git a/pkg/platform/sources/telegram.svg b/src/langbot/pkg/platform/sources/telegram.svg
similarity index 100%
rename from pkg/platform/sources/telegram.svg
rename to src/langbot/pkg/platform/sources/telegram.svg
diff --git a/pkg/platform/sources/telegram.yaml b/src/langbot/pkg/platform/sources/telegram.yaml
similarity index 100%
rename from pkg/platform/sources/telegram.yaml
rename to src/langbot/pkg/platform/sources/telegram.yaml
diff --git a/pkg/platform/sources/webchat.py b/src/langbot/pkg/platform/sources/webchat.py
similarity index 100%
rename from pkg/platform/sources/webchat.py
rename to src/langbot/pkg/platform/sources/webchat.py
diff --git a/pkg/platform/sources/webchat.yaml b/src/langbot/pkg/platform/sources/webchat.yaml
similarity index 100%
rename from pkg/platform/sources/webchat.yaml
rename to src/langbot/pkg/platform/sources/webchat.yaml
diff --git a/pkg/platform/sources/wechatpad.png b/src/langbot/pkg/platform/sources/wechatpad.png
similarity index 100%
rename from pkg/platform/sources/wechatpad.png
rename to src/langbot/pkg/platform/sources/wechatpad.png
diff --git a/pkg/platform/sources/wechatpad.py b/src/langbot/pkg/platform/sources/wechatpad.py
similarity index 99%
rename from pkg/platform/sources/wechatpad.py
rename to src/langbot/pkg/platform/sources/wechatpad.py
index 26d735ae..72609cfc 100644
--- a/pkg/platform/sources/wechatpad.py
+++ b/src/langbot/pkg/platform/sources/wechatpad.py
@@ -4,7 +4,7 @@ import json
import time
import httpx
-from libs.wechatpad_api.client import WeChatPadClient
+from langbot.libs.wechatpad_api.client import WeChatPadClient
import typing
import asyncio
@@ -16,7 +16,7 @@ import threading
import quart
-from ..logger import EventLogger
+from langbot.pkg.platform.logger import EventLogger
import xml.etree.ElementTree as ET
from typing import Optional, Tuple
from functools import partial
diff --git a/pkg/platform/sources/wechatpad.yaml b/src/langbot/pkg/platform/sources/wechatpad.yaml
similarity index 100%
rename from pkg/platform/sources/wechatpad.yaml
rename to src/langbot/pkg/platform/sources/wechatpad.yaml
diff --git a/pkg/platform/sources/wecom.png b/src/langbot/pkg/platform/sources/wecom.png
similarity index 100%
rename from pkg/platform/sources/wecom.png
rename to src/langbot/pkg/platform/sources/wecom.png
diff --git a/pkg/platform/sources/wecom.py b/src/langbot/pkg/platform/sources/wecom.py
similarity index 96%
rename from pkg/platform/sources/wecom.py
rename to src/langbot/pkg/platform/sources/wecom.py
index 3f5c0676..a009fbd8 100644
--- a/pkg/platform/sources/wecom.py
+++ b/src/langbot/pkg/platform/sources/wecom.py
@@ -5,12 +5,11 @@ import traceback
import datetime
-from libs.wecom_api.api import WecomClient
+from langbot.libs.wecom_api.api import WecomClient
import langbot_plugin.api.definition.abstract.platform.adapter as abstract_platform_adapter
-from libs.wecom_api.wecomevent import WecomEvent
-from langbot_plugin.api.entities.builtin.command import errors as command_errors
-from ...utils import image
-from ..logger import EventLogger
+from langbot.libs.wecom_api.wecomevent import WecomEvent
+from langbot.pkg.utils import image
+from langbot.pkg.platform.logger import EventLogger
import langbot_plugin.api.entities.builtin.platform.message as platform_message
import langbot_plugin.api.entities.builtin.platform.events as platform_events
import langbot_plugin.api.entities.builtin.platform.entities as platform_entities
@@ -156,15 +155,13 @@ class WecomAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
logger=logger,
)
-
super().__init__(
config=config,
logger=logger,
bot=bot,
- bot_account_id="",
+ bot_account_id='',
)
-
async def reply_message(
self,
message_source: platform_events.MessageEvent,
@@ -239,6 +236,6 @@ class WecomAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
],
):
return super().unregister_listener(event_type, callback)
-
+
async def is_muted(self, group_id: int) -> bool:
pass
diff --git a/pkg/platform/sources/wecom.yaml b/src/langbot/pkg/platform/sources/wecom.yaml
similarity index 100%
rename from pkg/platform/sources/wecom.yaml
rename to src/langbot/pkg/platform/sources/wecom.yaml
diff --git a/pkg/platform/sources/wecombot.png b/src/langbot/pkg/platform/sources/wecombot.png
similarity index 100%
rename from pkg/platform/sources/wecombot.png
rename to src/langbot/pkg/platform/sources/wecombot.png
diff --git a/pkg/platform/sources/wecombot.py b/src/langbot/pkg/platform/sources/wecombot.py
similarity index 90%
rename from pkg/platform/sources/wecombot.py
rename to src/langbot/pkg/platform/sources/wecombot.py
index 13dd8e92..dca8f2c3 100644
--- a/pkg/platform/sources/wecombot.py
+++ b/src/langbot/pkg/platform/sources/wecombot.py
@@ -8,11 +8,10 @@ import langbot_plugin.api.definition.abstract.platform.adapter as abstract_platf
import langbot_plugin.api.entities.builtin.platform.message as platform_message
import langbot_plugin.api.entities.builtin.platform.events as platform_events
import langbot_plugin.api.entities.builtin.platform.entities as platform_entities
-import pydantic
-from ..logger import EventLogger
-from libs.wecom_ai_bot_api.wecombotevent import WecomBotEvent
-from libs.wecom_ai_bot_api.api import WecomBotClient
-from ...core import app
+from langbot.pkg.platform.logger import EventLogger
+from langbot.libs.wecom_ai_bot_api.wecombotevent import WecomBotEvent
+from langbot.libs.wecom_ai_bot_api.api import WecomBotClient
+
class WecomBotMessageConverter(abstract_platform_adapter.AbstractMessageConverter):
@staticmethod
@@ -36,14 +35,14 @@ class WecomBotMessageConverter(abstract_platform_adapter.AbstractMessageConverte
return chain
+
class WecomBotEventConverter(abstract_platform_adapter.AbstractEventConverter):
+ @staticmethod
+ async def yiri2target(event: platform_events.MessageEvent):
+ return event.source_platform_object
@staticmethod
- async def yiri2target(event:platform_events.MessageEvent):
- return event.source_platform_object
-
- @staticmethod
- async def target2yiri(event:WecomBotEvent):
+ async def target2yiri(event: WecomBotEvent):
message_chain = await WecomBotMessageConverter.target2yiri(event)
if event.type == 'single':
return platform_events.FriendMessage(
@@ -82,6 +81,7 @@ class WecomBotEventConverter(abstract_platform_adapter.AbstractEventConverter):
except Exception:
print(traceback.format_exc())
+
class WecomBotAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
bot: WecomBotClient
bot_account_id: str
@@ -111,9 +111,12 @@ class WecomBotAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
bot_account_id=bot_account_id,
)
-
- async def reply_message(self, message_source:platform_events.MessageEvent, message:platform_message.MessageChain,quote_origin: bool = False):
-
+ async def reply_message(
+ self,
+ message_source: platform_events.MessageEvent,
+ message: platform_message.MessageChain,
+ quote_origin: bool = False,
+ ):
content = await self.message_converter.yiri2target(message)
await self.bot.set_message(message_source.source_platform_object.message_id, content)
@@ -167,7 +170,9 @@ class WecomBotAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
def register_listener(
self,
event_type: typing.Type[platform_events.Event],
- callback: typing.Callable[[platform_events.Event, abstract_platform_adapter.AbstractMessagePlatformAdapter], None],
+ callback: typing.Callable[
+ [platform_events.Event, abstract_platform_adapter.AbstractMessagePlatformAdapter], None
+ ],
):
async def on_message(event: WecomBotEvent):
try:
@@ -175,6 +180,7 @@ class WecomBotAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
except Exception:
await self.logger.error(f'Error in wecombot callback: {traceback.format_exc()}')
print(traceback.format_exc())
+
try:
if event_type == platform_events.FriendMessage:
self.bot.on_message('single')(on_message)
@@ -182,7 +188,6 @@ class WecomBotAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
self.bot.on_message('group')(on_message)
except Exception:
print(traceback.format_exc())
-
async def run_async(self):
async def shutdown_trigger_placeholder():
@@ -201,11 +206,11 @@ class WecomBotAdapter(abstract_platform_adapter.AbstractMessagePlatformAdapter):
async def unregister_listener(
self,
event_type: type,
- callback: typing.Callable[[platform_events.Event, abstract_platform_adapter.AbstractMessagePlatformAdapter], None],
+ callback: typing.Callable[
+ [platform_events.Event, abstract_platform_adapter.AbstractMessagePlatformAdapter], None
+ ],
):
return super().unregister_listener(event_type, callback)
-
+
async def is_muted(self, group_id: int) -> bool:
pass
-
-
diff --git a/pkg/platform/sources/wecombot.yaml b/src/langbot/pkg/platform/sources/wecombot.yaml
similarity index 100%
rename from pkg/platform/sources/wecombot.yaml
rename to src/langbot/pkg/platform/sources/wecombot.yaml
diff --git a/pkg/platform/sources/wecomcs.py b/src/langbot/pkg/platform/sources/wecomcs.py
similarity index 98%
rename from pkg/platform/sources/wecomcs.py
rename to src/langbot/pkg/platform/sources/wecomcs.py
index 7ce3a064..756daec0 100644
--- a/pkg/platform/sources/wecomcs.py
+++ b/src/langbot/pkg/platform/sources/wecomcs.py
@@ -6,9 +6,9 @@ import traceback
import datetime
import pydantic
-from libs.wecom_customer_service_api.api import WecomCSClient
+from langbot.libs.wecom_customer_service_api.api import WecomCSClient
import langbot_plugin.api.definition.abstract.platform.adapter as abstract_platform_adapter
-from libs.wecom_customer_service_api.wecomcsevent import WecomCSEvent
+from langbot.libs.wecom_customer_service_api.wecomcsevent import WecomCSEvent
import langbot_plugin.api.entities.builtin.platform.entities as platform_entities
import langbot_plugin.api.entities.builtin.platform.message as platform_message
import langbot_plugin.api.entities.builtin.platform.events as platform_events
diff --git a/pkg/platform/sources/wecomcs.yaml b/src/langbot/pkg/platform/sources/wecomcs.yaml
similarity index 100%
rename from pkg/platform/sources/wecomcs.yaml
rename to src/langbot/pkg/platform/sources/wecomcs.yaml
diff --git a/pkg/platform/webhook_pusher.py b/src/langbot/pkg/platform/webhook_pusher.py
similarity index 100%
rename from pkg/platform/webhook_pusher.py
rename to src/langbot/pkg/platform/webhook_pusher.py
diff --git a/pkg/plugin/__init__.py b/src/langbot/pkg/plugin/__init__.py
similarity index 100%
rename from pkg/plugin/__init__.py
rename to src/langbot/pkg/plugin/__init__.py
diff --git a/pkg/plugin/connector.py b/src/langbot/pkg/plugin/connector.py
similarity index 98%
rename from pkg/plugin/connector.py
rename to src/langbot/pkg/plugin/connector.py
index dea374f6..25223528 100644
--- a/pkg/plugin/connector.py
+++ b/src/langbot/pkg/plugin/connector.py
@@ -128,13 +128,15 @@ class PluginRuntimeConnector:
# See also: https://docs.python.org/zh-cn/3.13/library/asyncio-platforms.html
# We have to launch runtime via cmd but communicate via ws.
self.ap.logger.info('(windows) use cmd to launch plugin runtime and communicate via ws')
-
+
if self.runtime_subprocess_on_windows is None: # only launch once
python_path = sys.executable
env = os.environ.copy()
self.runtime_subprocess_on_windows = await asyncio.create_subprocess_exec(
python_path,
- '-m', 'langbot_plugin.cli.__init__', 'rt',
+ '-m',
+ 'langbot_plugin.cli.__init__',
+ 'rt',
env=env,
)
@@ -150,9 +152,11 @@ class PluginRuntimeConnector:
if exc is not None:
self.ap.logger.error(f'(windows) Failed to connect to plugin runtime({ws_url}): {exc}')
else:
- self.ap.logger.error(f'(windows) Failed to connect to plugin runtime({ws_url}), trying to reconnect...')
+ self.ap.logger.error(
+ f'(windows) Failed to connect to plugin runtime({ws_url}), trying to reconnect...'
+ )
await self.runtime_disconnect_callback(self)
-
+
self.ctrl = ws_client_controller.WebSocketClientController(
ws_url=ws_url,
make_connection_failed_callback=make_connection_failed_callback,
diff --git a/pkg/plugin/handler.py b/src/langbot/pkg/plugin/handler.py
similarity index 100%
rename from pkg/plugin/handler.py
rename to src/langbot/pkg/plugin/handler.py
diff --git a/pkg/provider/__init__.py b/src/langbot/pkg/provider/__init__.py
similarity index 100%
rename from pkg/provider/__init__.py
rename to src/langbot/pkg/provider/__init__.py
diff --git a/pkg/provider/modelmgr/__init__.py b/src/langbot/pkg/provider/modelmgr/__init__.py
similarity index 100%
rename from pkg/provider/modelmgr/__init__.py
rename to src/langbot/pkg/provider/modelmgr/__init__.py
diff --git a/pkg/provider/modelmgr/entities.py b/src/langbot/pkg/provider/modelmgr/entities.py
similarity index 100%
rename from pkg/provider/modelmgr/entities.py
rename to src/langbot/pkg/provider/modelmgr/entities.py
diff --git a/pkg/provider/modelmgr/errors.py b/src/langbot/pkg/provider/modelmgr/errors.py
similarity index 100%
rename from pkg/provider/modelmgr/errors.py
rename to src/langbot/pkg/provider/modelmgr/errors.py
diff --git a/pkg/provider/modelmgr/modelmgr.py b/src/langbot/pkg/provider/modelmgr/modelmgr.py
similarity index 100%
rename from pkg/provider/modelmgr/modelmgr.py
rename to src/langbot/pkg/provider/modelmgr/modelmgr.py
diff --git a/pkg/provider/modelmgr/requester.py b/src/langbot/pkg/provider/modelmgr/requester.py
similarity index 100%
rename from pkg/provider/modelmgr/requester.py
rename to src/langbot/pkg/provider/modelmgr/requester.py
diff --git a/pkg/provider/modelmgr/requester.yaml b/src/langbot/pkg/provider/modelmgr/requester.yaml
similarity index 100%
rename from pkg/provider/modelmgr/requester.yaml
rename to src/langbot/pkg/provider/modelmgr/requester.yaml
diff --git a/pkg/provider/modelmgr/requesters/302ai.png b/src/langbot/pkg/provider/modelmgr/requesters/302ai.png
similarity index 100%
rename from pkg/provider/modelmgr/requesters/302ai.png
rename to src/langbot/pkg/provider/modelmgr/requesters/302ai.png
diff --git a/pkg/provider/modelmgr/requesters/302aichatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/302aichatcmpl.py
similarity index 100%
rename from pkg/provider/modelmgr/requesters/302aichatcmpl.py
rename to src/langbot/pkg/provider/modelmgr/requesters/302aichatcmpl.py
diff --git a/pkg/provider/modelmgr/requesters/302aichatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/302aichatcmpl.yaml
similarity index 100%
rename from pkg/provider/modelmgr/requesters/302aichatcmpl.yaml
rename to src/langbot/pkg/provider/modelmgr/requesters/302aichatcmpl.yaml
diff --git a/pkg/provider/modelmgr/requesters/__init__.py b/src/langbot/pkg/provider/modelmgr/requesters/__init__.py
similarity index 100%
rename from pkg/provider/modelmgr/requesters/__init__.py
rename to src/langbot/pkg/provider/modelmgr/requesters/__init__.py
diff --git a/pkg/provider/modelmgr/requesters/anthropic.svg b/src/langbot/pkg/provider/modelmgr/requesters/anthropic.svg
similarity index 100%
rename from pkg/provider/modelmgr/requesters/anthropic.svg
rename to src/langbot/pkg/provider/modelmgr/requesters/anthropic.svg
diff --git a/pkg/provider/modelmgr/requesters/anthropicmsgs.py b/src/langbot/pkg/provider/modelmgr/requesters/anthropicmsgs.py
similarity index 100%
rename from pkg/provider/modelmgr/requesters/anthropicmsgs.py
rename to src/langbot/pkg/provider/modelmgr/requesters/anthropicmsgs.py
diff --git a/pkg/provider/modelmgr/requesters/anthropicmsgs.yaml b/src/langbot/pkg/provider/modelmgr/requesters/anthropicmsgs.yaml
similarity index 100%
rename from pkg/provider/modelmgr/requesters/anthropicmsgs.yaml
rename to src/langbot/pkg/provider/modelmgr/requesters/anthropicmsgs.yaml
diff --git a/pkg/provider/modelmgr/requesters/bailian.png b/src/langbot/pkg/provider/modelmgr/requesters/bailian.png
similarity index 100%
rename from pkg/provider/modelmgr/requesters/bailian.png
rename to src/langbot/pkg/provider/modelmgr/requesters/bailian.png
diff --git a/pkg/provider/modelmgr/requesters/bailianchatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/bailianchatcmpl.py
similarity index 88%
rename from pkg/provider/modelmgr/requesters/bailianchatcmpl.py
rename to src/langbot/pkg/provider/modelmgr/requesters/bailianchatcmpl.py
index adeaf17f..c60165bb 100644
--- a/pkg/provider/modelmgr/requesters/bailianchatcmpl.py
+++ b/src/langbot/pkg/provider/modelmgr/requesters/bailianchatcmpl.py
@@ -44,10 +44,10 @@ class BailianChatCompletions(modelscopechatcmpl.ModelScopeChatCompletions):
# 设置此次请求中的messages
messages = req_messages.copy()
- is_use_dashscope_call = False # 是否使用阿里原生库调用
+ is_use_dashscope_call = False # 是否使用阿里原生库调用
is_enable_multi_model = True # 是否支持多轮对话
- use_time_num = 0 # 模型已调用次数,防止存在多文件时重复调用
- use_time_ids = [] # 已调用的ID列表
+ use_time_num = 0 # 模型已调用次数,防止存在多文件时重复调用
+ use_time_ids = [] # 已调用的ID列表
message_id = 0 # 记录消息序号
for msg in messages:
@@ -67,20 +67,32 @@ class BailianChatCompletions(modelscopechatcmpl.ModelScopeChatCompletions):
me['video_url'] = {'url': me['file_url']}
del me['file_url']
del me['file_name']
- use_time_num +=1
+ use_time_num += 1
use_time_ids.append(message_id)
is_enable_multi_model = False
# 2. 语音文件识别, 无法通过openai的audio字段传递,暂时不支持
# https://bailian.console.aliyun.com/?tab=doc#/doc/?type=model&url=2979031
- elif file_type in ['aac', 'amr', 'aiff', 'flac', 'm4a',
- 'mp3', 'mpeg', 'ogg', 'opus', 'wav', 'webm', 'wma']:
+ elif file_type in [
+ 'aac',
+ 'amr',
+ 'aiff',
+ 'flac',
+ 'm4a',
+ 'mp3',
+ 'mpeg',
+ 'ogg',
+ 'opus',
+ 'wav',
+ 'webm',
+ 'wma',
+ ]:
me['audio'] = me['file_url']
me['type'] = 'audio'
del me['file_url']
del me['type']
del me['file_name']
is_use_dashscope_call = True
- use_time_num +=1
+ use_time_num += 1
use_time_ids.append(message_id)
is_enable_multi_model = False
message_id += 1
@@ -108,26 +120,26 @@ class BailianChatCompletions(modelscopechatcmpl.ModelScopeChatCompletions):
api_key=use_model.token_mgr.get_token(),
model=use_model.model_entity.name,
messages=messages,
- result_format="message",
+ result_format='message',
asr_options={
# "language": "zh", # 可选,若已知音频的语种,可通过该参数指定待识别语种,以提升识别准确率
- "enable_lid": True,
- "enable_itn": False
+ 'enable_lid': True,
+ 'enable_itn': False,
},
- stream=True
+ stream=True,
)
content_length_list = []
previous_length = 0 # 记录上一次的内容长度
for res in response:
- chunk = res["output"]
+ chunk = res['output']
# 解析 chunk 数据
if hasattr(chunk, 'choices') and chunk.choices:
choice = chunk.choices[0]
- delta_content = choice["message"].content[0]["text"]
- finish_reason = choice["finish_reason"]
+ delta_content = choice['message'].content[0]['text']
+ finish_reason = choice['finish_reason']
content_length_list.append(len(delta_content))
else:
- delta_content = ""
+ delta_content = ''
finish_reason = None
# 跳过空的第一个 chunk(只有 role 没有内容)
@@ -137,7 +149,7 @@ class BailianChatCompletions(modelscopechatcmpl.ModelScopeChatCompletions):
# 检查 content_length_list 是否有足够的数据
if len(content_length_list) >= 2:
- now_content = delta_content[previous_length: content_length_list[-1]]
+ now_content = delta_content[previous_length : content_length_list[-1]]
previous_length = content_length_list[-1] # 更新上一次的长度
else:
now_content = delta_content # 第一次循环时直接使用 delta_content
@@ -147,7 +159,7 @@ class BailianChatCompletions(modelscopechatcmpl.ModelScopeChatCompletions):
chunk_data = {
'role': role,
'content': now_content if now_content else None,
- 'is_final': bool(finish_reason) and finish_reason != "null",
+ 'is_final': bool(finish_reason) and finish_reason != 'null',
}
# 移除 None 值
diff --git a/pkg/provider/modelmgr/requesters/bailianchatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/bailianchatcmpl.yaml
similarity index 100%
rename from pkg/provider/modelmgr/requesters/bailianchatcmpl.yaml
rename to src/langbot/pkg/provider/modelmgr/requesters/bailianchatcmpl.yaml
diff --git a/pkg/provider/modelmgr/requesters/chatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/chatcmpl.py
similarity index 100%
rename from pkg/provider/modelmgr/requesters/chatcmpl.py
rename to src/langbot/pkg/provider/modelmgr/requesters/chatcmpl.py
diff --git a/pkg/provider/modelmgr/requesters/chatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/chatcmpl.yaml
similarity index 100%
rename from pkg/provider/modelmgr/requesters/chatcmpl.yaml
rename to src/langbot/pkg/provider/modelmgr/requesters/chatcmpl.yaml
diff --git a/pkg/provider/modelmgr/requesters/compshare.png b/src/langbot/pkg/provider/modelmgr/requesters/compshare.png
similarity index 100%
rename from pkg/provider/modelmgr/requesters/compshare.png
rename to src/langbot/pkg/provider/modelmgr/requesters/compshare.png
diff --git a/pkg/provider/modelmgr/requesters/compsharechatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/compsharechatcmpl.py
similarity index 100%
rename from pkg/provider/modelmgr/requesters/compsharechatcmpl.py
rename to src/langbot/pkg/provider/modelmgr/requesters/compsharechatcmpl.py
diff --git a/pkg/provider/modelmgr/requesters/compsharechatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/compsharechatcmpl.yaml
similarity index 100%
rename from pkg/provider/modelmgr/requesters/compsharechatcmpl.yaml
rename to src/langbot/pkg/provider/modelmgr/requesters/compsharechatcmpl.yaml
diff --git a/pkg/provider/modelmgr/requesters/deepseek.svg b/src/langbot/pkg/provider/modelmgr/requesters/deepseek.svg
similarity index 100%
rename from pkg/provider/modelmgr/requesters/deepseek.svg
rename to src/langbot/pkg/provider/modelmgr/requesters/deepseek.svg
diff --git a/pkg/provider/modelmgr/requesters/deepseekchatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/deepseekchatcmpl.py
similarity index 100%
rename from pkg/provider/modelmgr/requesters/deepseekchatcmpl.py
rename to src/langbot/pkg/provider/modelmgr/requesters/deepseekchatcmpl.py
diff --git a/pkg/provider/modelmgr/requesters/deepseekchatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/deepseekchatcmpl.yaml
similarity index 100%
rename from pkg/provider/modelmgr/requesters/deepseekchatcmpl.yaml
rename to src/langbot/pkg/provider/modelmgr/requesters/deepseekchatcmpl.yaml
diff --git a/pkg/provider/modelmgr/requesters/gemini.svg b/src/langbot/pkg/provider/modelmgr/requesters/gemini.svg
similarity index 100%
rename from pkg/provider/modelmgr/requesters/gemini.svg
rename to src/langbot/pkg/provider/modelmgr/requesters/gemini.svg
diff --git a/pkg/provider/modelmgr/requesters/geminichatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/geminichatcmpl.py
similarity index 100%
rename from pkg/provider/modelmgr/requesters/geminichatcmpl.py
rename to src/langbot/pkg/provider/modelmgr/requesters/geminichatcmpl.py
diff --git a/pkg/provider/modelmgr/requesters/geminichatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/geminichatcmpl.yaml
similarity index 100%
rename from pkg/provider/modelmgr/requesters/geminichatcmpl.yaml
rename to src/langbot/pkg/provider/modelmgr/requesters/geminichatcmpl.yaml
diff --git a/pkg/provider/modelmgr/requesters/giteeai.svg b/src/langbot/pkg/provider/modelmgr/requesters/giteeai.svg
similarity index 100%
rename from pkg/provider/modelmgr/requesters/giteeai.svg
rename to src/langbot/pkg/provider/modelmgr/requesters/giteeai.svg
diff --git a/pkg/provider/modelmgr/requesters/giteeaichatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/giteeaichatcmpl.py
similarity index 100%
rename from pkg/provider/modelmgr/requesters/giteeaichatcmpl.py
rename to src/langbot/pkg/provider/modelmgr/requesters/giteeaichatcmpl.py
diff --git a/pkg/provider/modelmgr/requesters/giteeaichatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/giteeaichatcmpl.yaml
similarity index 100%
rename from pkg/provider/modelmgr/requesters/giteeaichatcmpl.yaml
rename to src/langbot/pkg/provider/modelmgr/requesters/giteeaichatcmpl.yaml
diff --git a/pkg/provider/modelmgr/requesters/jiekouai.png b/src/langbot/pkg/provider/modelmgr/requesters/jiekouai.png
similarity index 100%
rename from pkg/provider/modelmgr/requesters/jiekouai.png
rename to src/langbot/pkg/provider/modelmgr/requesters/jiekouai.png
diff --git a/pkg/provider/modelmgr/requesters/jiekouaichatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/jiekouaichatcmpl.py
similarity index 100%
rename from pkg/provider/modelmgr/requesters/jiekouaichatcmpl.py
rename to src/langbot/pkg/provider/modelmgr/requesters/jiekouaichatcmpl.py
diff --git a/pkg/provider/modelmgr/requesters/jiekouaichatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/jiekouaichatcmpl.yaml
similarity index 100%
rename from pkg/provider/modelmgr/requesters/jiekouaichatcmpl.yaml
rename to src/langbot/pkg/provider/modelmgr/requesters/jiekouaichatcmpl.yaml
diff --git a/pkg/provider/modelmgr/requesters/lmstudio.webp b/src/langbot/pkg/provider/modelmgr/requesters/lmstudio.webp
similarity index 100%
rename from pkg/provider/modelmgr/requesters/lmstudio.webp
rename to src/langbot/pkg/provider/modelmgr/requesters/lmstudio.webp
diff --git a/pkg/provider/modelmgr/requesters/lmstudiochatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/lmstudiochatcmpl.py
similarity index 100%
rename from pkg/provider/modelmgr/requesters/lmstudiochatcmpl.py
rename to src/langbot/pkg/provider/modelmgr/requesters/lmstudiochatcmpl.py
diff --git a/pkg/provider/modelmgr/requesters/lmstudiochatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/lmstudiochatcmpl.yaml
similarity index 100%
rename from pkg/provider/modelmgr/requesters/lmstudiochatcmpl.yaml
rename to src/langbot/pkg/provider/modelmgr/requesters/lmstudiochatcmpl.yaml
diff --git a/pkg/provider/modelmgr/requesters/modelscope.svg b/src/langbot/pkg/provider/modelmgr/requesters/modelscope.svg
similarity index 100%
rename from pkg/provider/modelmgr/requesters/modelscope.svg
rename to src/langbot/pkg/provider/modelmgr/requesters/modelscope.svg
diff --git a/pkg/provider/modelmgr/requesters/modelscopechatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/modelscopechatcmpl.py
similarity index 100%
rename from pkg/provider/modelmgr/requesters/modelscopechatcmpl.py
rename to src/langbot/pkg/provider/modelmgr/requesters/modelscopechatcmpl.py
diff --git a/pkg/provider/modelmgr/requesters/modelscopechatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/modelscopechatcmpl.yaml
similarity index 100%
rename from pkg/provider/modelmgr/requesters/modelscopechatcmpl.yaml
rename to src/langbot/pkg/provider/modelmgr/requesters/modelscopechatcmpl.yaml
diff --git a/pkg/provider/modelmgr/requesters/moonshot.png b/src/langbot/pkg/provider/modelmgr/requesters/moonshot.png
similarity index 100%
rename from pkg/provider/modelmgr/requesters/moonshot.png
rename to src/langbot/pkg/provider/modelmgr/requesters/moonshot.png
diff --git a/pkg/provider/modelmgr/requesters/moonshotchatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/moonshotchatcmpl.py
similarity index 100%
rename from pkg/provider/modelmgr/requesters/moonshotchatcmpl.py
rename to src/langbot/pkg/provider/modelmgr/requesters/moonshotchatcmpl.py
diff --git a/pkg/provider/modelmgr/requesters/moonshotchatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/moonshotchatcmpl.yaml
similarity index 100%
rename from pkg/provider/modelmgr/requesters/moonshotchatcmpl.yaml
rename to src/langbot/pkg/provider/modelmgr/requesters/moonshotchatcmpl.yaml
diff --git a/pkg/provider/modelmgr/requesters/newapi.png b/src/langbot/pkg/provider/modelmgr/requesters/newapi.png
similarity index 100%
rename from pkg/provider/modelmgr/requesters/newapi.png
rename to src/langbot/pkg/provider/modelmgr/requesters/newapi.png
diff --git a/pkg/provider/modelmgr/requesters/newapichatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/newapichatcmpl.py
similarity index 100%
rename from pkg/provider/modelmgr/requesters/newapichatcmpl.py
rename to src/langbot/pkg/provider/modelmgr/requesters/newapichatcmpl.py
diff --git a/pkg/provider/modelmgr/requesters/newapichatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/newapichatcmpl.yaml
similarity index 100%
rename from pkg/provider/modelmgr/requesters/newapichatcmpl.yaml
rename to src/langbot/pkg/provider/modelmgr/requesters/newapichatcmpl.yaml
diff --git a/pkg/provider/modelmgr/requesters/ollama.svg b/src/langbot/pkg/provider/modelmgr/requesters/ollama.svg
similarity index 100%
rename from pkg/provider/modelmgr/requesters/ollama.svg
rename to src/langbot/pkg/provider/modelmgr/requesters/ollama.svg
diff --git a/pkg/provider/modelmgr/requesters/ollamachat.py b/src/langbot/pkg/provider/modelmgr/requesters/ollamachat.py
similarity index 100%
rename from pkg/provider/modelmgr/requesters/ollamachat.py
rename to src/langbot/pkg/provider/modelmgr/requesters/ollamachat.py
diff --git a/pkg/provider/modelmgr/requesters/ollamachat.yaml b/src/langbot/pkg/provider/modelmgr/requesters/ollamachat.yaml
similarity index 100%
rename from pkg/provider/modelmgr/requesters/ollamachat.yaml
rename to src/langbot/pkg/provider/modelmgr/requesters/ollamachat.yaml
diff --git a/pkg/provider/modelmgr/requesters/openai.svg b/src/langbot/pkg/provider/modelmgr/requesters/openai.svg
similarity index 100%
rename from pkg/provider/modelmgr/requesters/openai.svg
rename to src/langbot/pkg/provider/modelmgr/requesters/openai.svg
diff --git a/pkg/provider/modelmgr/requesters/openrouter.svg b/src/langbot/pkg/provider/modelmgr/requesters/openrouter.svg
similarity index 100%
rename from pkg/provider/modelmgr/requesters/openrouter.svg
rename to src/langbot/pkg/provider/modelmgr/requesters/openrouter.svg
diff --git a/pkg/provider/modelmgr/requesters/openrouterchatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/openrouterchatcmpl.py
similarity index 100%
rename from pkg/provider/modelmgr/requesters/openrouterchatcmpl.py
rename to src/langbot/pkg/provider/modelmgr/requesters/openrouterchatcmpl.py
diff --git a/pkg/provider/modelmgr/requesters/openrouterchatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/openrouterchatcmpl.yaml
similarity index 100%
rename from pkg/provider/modelmgr/requesters/openrouterchatcmpl.yaml
rename to src/langbot/pkg/provider/modelmgr/requesters/openrouterchatcmpl.yaml
diff --git a/pkg/provider/modelmgr/requesters/ppio.svg b/src/langbot/pkg/provider/modelmgr/requesters/ppio.svg
similarity index 100%
rename from pkg/provider/modelmgr/requesters/ppio.svg
rename to src/langbot/pkg/provider/modelmgr/requesters/ppio.svg
diff --git a/pkg/provider/modelmgr/requesters/ppiochatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/ppiochatcmpl.py
similarity index 100%
rename from pkg/provider/modelmgr/requesters/ppiochatcmpl.py
rename to src/langbot/pkg/provider/modelmgr/requesters/ppiochatcmpl.py
diff --git a/pkg/provider/modelmgr/requesters/ppiochatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/ppiochatcmpl.yaml
similarity index 100%
rename from pkg/provider/modelmgr/requesters/ppiochatcmpl.yaml
rename to src/langbot/pkg/provider/modelmgr/requesters/ppiochatcmpl.yaml
diff --git a/pkg/provider/modelmgr/requesters/qhaigc.png b/src/langbot/pkg/provider/modelmgr/requesters/qhaigc.png
similarity index 100%
rename from pkg/provider/modelmgr/requesters/qhaigc.png
rename to src/langbot/pkg/provider/modelmgr/requesters/qhaigc.png
diff --git a/pkg/provider/modelmgr/requesters/qhaigcchatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/qhaigcchatcmpl.py
similarity index 100%
rename from pkg/provider/modelmgr/requesters/qhaigcchatcmpl.py
rename to src/langbot/pkg/provider/modelmgr/requesters/qhaigcchatcmpl.py
diff --git a/pkg/provider/modelmgr/requesters/qhaigcchatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/qhaigcchatcmpl.yaml
similarity index 100%
rename from pkg/provider/modelmgr/requesters/qhaigcchatcmpl.yaml
rename to src/langbot/pkg/provider/modelmgr/requesters/qhaigcchatcmpl.yaml
diff --git a/pkg/provider/modelmgr/requesters/shengsuanyun.py b/src/langbot/pkg/provider/modelmgr/requesters/shengsuanyun.py
similarity index 100%
rename from pkg/provider/modelmgr/requesters/shengsuanyun.py
rename to src/langbot/pkg/provider/modelmgr/requesters/shengsuanyun.py
diff --git a/pkg/provider/modelmgr/requesters/shengsuanyun.svg b/src/langbot/pkg/provider/modelmgr/requesters/shengsuanyun.svg
similarity index 100%
rename from pkg/provider/modelmgr/requesters/shengsuanyun.svg
rename to src/langbot/pkg/provider/modelmgr/requesters/shengsuanyun.svg
diff --git a/pkg/provider/modelmgr/requesters/shengsuanyun.yaml b/src/langbot/pkg/provider/modelmgr/requesters/shengsuanyun.yaml
similarity index 100%
rename from pkg/provider/modelmgr/requesters/shengsuanyun.yaml
rename to src/langbot/pkg/provider/modelmgr/requesters/shengsuanyun.yaml
diff --git a/pkg/provider/modelmgr/requesters/siliconflow.svg b/src/langbot/pkg/provider/modelmgr/requesters/siliconflow.svg
similarity index 100%
rename from pkg/provider/modelmgr/requesters/siliconflow.svg
rename to src/langbot/pkg/provider/modelmgr/requesters/siliconflow.svg
diff --git a/pkg/provider/modelmgr/requesters/siliconflowchatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/siliconflowchatcmpl.py
similarity index 100%
rename from pkg/provider/modelmgr/requesters/siliconflowchatcmpl.py
rename to src/langbot/pkg/provider/modelmgr/requesters/siliconflowchatcmpl.py
diff --git a/pkg/provider/modelmgr/requesters/siliconflowchatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/siliconflowchatcmpl.yaml
similarity index 100%
rename from pkg/provider/modelmgr/requesters/siliconflowchatcmpl.yaml
rename to src/langbot/pkg/provider/modelmgr/requesters/siliconflowchatcmpl.yaml
diff --git a/pkg/provider/modelmgr/requesters/tokenpony.svg b/src/langbot/pkg/provider/modelmgr/requesters/tokenpony.svg
similarity index 100%
rename from pkg/provider/modelmgr/requesters/tokenpony.svg
rename to src/langbot/pkg/provider/modelmgr/requesters/tokenpony.svg
diff --git a/pkg/provider/modelmgr/requesters/tokenpony.yaml b/src/langbot/pkg/provider/modelmgr/requesters/tokenpony.yaml
similarity index 100%
rename from pkg/provider/modelmgr/requesters/tokenpony.yaml
rename to src/langbot/pkg/provider/modelmgr/requesters/tokenpony.yaml
diff --git a/pkg/provider/modelmgr/requesters/tokenponychatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/tokenponychatcmpl.py
similarity index 100%
rename from pkg/provider/modelmgr/requesters/tokenponychatcmpl.py
rename to src/langbot/pkg/provider/modelmgr/requesters/tokenponychatcmpl.py
diff --git a/pkg/provider/modelmgr/requesters/volcark.svg b/src/langbot/pkg/provider/modelmgr/requesters/volcark.svg
similarity index 100%
rename from pkg/provider/modelmgr/requesters/volcark.svg
rename to src/langbot/pkg/provider/modelmgr/requesters/volcark.svg
diff --git a/pkg/provider/modelmgr/requesters/volcarkchatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/volcarkchatcmpl.py
similarity index 100%
rename from pkg/provider/modelmgr/requesters/volcarkchatcmpl.py
rename to src/langbot/pkg/provider/modelmgr/requesters/volcarkchatcmpl.py
diff --git a/pkg/provider/modelmgr/requesters/volcarkchatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/volcarkchatcmpl.yaml
similarity index 100%
rename from pkg/provider/modelmgr/requesters/volcarkchatcmpl.yaml
rename to src/langbot/pkg/provider/modelmgr/requesters/volcarkchatcmpl.yaml
diff --git a/pkg/provider/modelmgr/requesters/xai.svg b/src/langbot/pkg/provider/modelmgr/requesters/xai.svg
similarity index 100%
rename from pkg/provider/modelmgr/requesters/xai.svg
rename to src/langbot/pkg/provider/modelmgr/requesters/xai.svg
diff --git a/pkg/provider/modelmgr/requesters/xaichatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/xaichatcmpl.py
similarity index 100%
rename from pkg/provider/modelmgr/requesters/xaichatcmpl.py
rename to src/langbot/pkg/provider/modelmgr/requesters/xaichatcmpl.py
diff --git a/pkg/provider/modelmgr/requesters/xaichatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/xaichatcmpl.yaml
similarity index 100%
rename from pkg/provider/modelmgr/requesters/xaichatcmpl.yaml
rename to src/langbot/pkg/provider/modelmgr/requesters/xaichatcmpl.yaml
diff --git a/pkg/provider/modelmgr/requesters/zhipuai.svg b/src/langbot/pkg/provider/modelmgr/requesters/zhipuai.svg
similarity index 100%
rename from pkg/provider/modelmgr/requesters/zhipuai.svg
rename to src/langbot/pkg/provider/modelmgr/requesters/zhipuai.svg
diff --git a/pkg/provider/modelmgr/requesters/zhipuaichatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/zhipuaichatcmpl.py
similarity index 100%
rename from pkg/provider/modelmgr/requesters/zhipuaichatcmpl.py
rename to src/langbot/pkg/provider/modelmgr/requesters/zhipuaichatcmpl.py
diff --git a/pkg/provider/modelmgr/requesters/zhipuaichatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/zhipuaichatcmpl.yaml
similarity index 100%
rename from pkg/provider/modelmgr/requesters/zhipuaichatcmpl.yaml
rename to src/langbot/pkg/provider/modelmgr/requesters/zhipuaichatcmpl.yaml
diff --git a/pkg/provider/modelmgr/token.py b/src/langbot/pkg/provider/modelmgr/token.py
similarity index 100%
rename from pkg/provider/modelmgr/token.py
rename to src/langbot/pkg/provider/modelmgr/token.py
diff --git a/pkg/provider/runner.py b/src/langbot/pkg/provider/runner.py
similarity index 70%
rename from pkg/provider/runner.py
rename to src/langbot/pkg/provider/runner.py
index 83acfe13..f89c079d 100644
--- a/pkg/provider/runner.py
+++ b/src/langbot/pkg/provider/runner.py
@@ -4,8 +4,6 @@ import abc
import typing
from ..core import app
-import langbot_plugin.api.entities.builtin.provider.message as provider_message
-import langbot_plugin.api.entities.builtin.pipeline.query as pipeline_query
preregistered_runners: list[typing.Type[RequestRunner]] = []
@@ -36,6 +34,8 @@ class RequestRunner(abc.ABC):
self.pipeline_config = pipeline_config
@abc.abstractmethod
- async def run(self, query: core_entities.Query) -> typing.AsyncGenerator[llm_entities.Message | llm_entities.MessageChunk, None]:
+ async def run(
+ self, query: core_entities.Query
+ ) -> typing.AsyncGenerator[llm_entities.Message | llm_entities.MessageChunk, None]:
"""运行请求"""
- pass
\ No newline at end of file
+ pass
diff --git a/pkg/provider/runners/__init__.py b/src/langbot/pkg/provider/runners/__init__.py
similarity index 100%
rename from pkg/provider/runners/__init__.py
rename to src/langbot/pkg/provider/runners/__init__.py
diff --git a/pkg/provider/runners/cozeapi.py b/src/langbot/pkg/provider/runners/cozeapi.py
similarity index 77%
rename from pkg/provider/runners/cozeapi.py
rename to src/langbot/pkg/provider/runners/cozeapi.py
index 0fdb6f9b..26980f81 100644
--- a/pkg/provider/runners/cozeapi.py
+++ b/src/langbot/pkg/provider/runners/cozeapi.py
@@ -2,15 +2,15 @@ from __future__ import annotations
import typing
import json
-import uuid
import base64
-from .. import runner
-from ...core import app
+from langbot.pkg.provider import runner
+from langbot.pkg.core import app
import langbot_plugin.api.entities.builtin.provider.message as provider_message
-from ...utils import image
+from langbot.pkg.utils import image
import langbot_plugin.api.entities.builtin.pipeline.query as pipeline_query
-from libs.coze_server_api.client import AsyncCozeAPIClient
+from langbot.libs.coze_server_api.client import AsyncCozeAPIClient
+
@runner.runner_class('coze-api')
class CozeAPIRunner(runner.RequestRunner):
@@ -19,17 +19,14 @@ class CozeAPIRunner(runner.RequestRunner):
def __init__(self, ap: app.Application, pipeline_config: dict):
self.pipeline_config = pipeline_config
self.ap = ap
- self.agent_token = pipeline_config["ai"]['coze-api']['api-key']
- self.bot_id = pipeline_config["ai"]['coze-api'].get('bot-id')
- self.chat_timeout = pipeline_config["ai"]['coze-api'].get('timeout')
- self.auto_save_history = pipeline_config["ai"]['coze-api'].get('auto_save_history')
- self.api_base = pipeline_config["ai"]['coze-api'].get('api-base')
+ self.agent_token = pipeline_config['ai']['coze-api']['api-key']
+ self.bot_id = pipeline_config['ai']['coze-api'].get('bot-id')
+ self.chat_timeout = pipeline_config['ai']['coze-api'].get('timeout')
+ self.auto_save_history = pipeline_config['ai']['coze-api'].get('auto_save_history')
+ self.api_base = pipeline_config['ai']['coze-api'].get('api-base')
+
+ self.coze = AsyncCozeAPIClient(self.agent_token, self.api_base)
- self.coze = AsyncCozeAPIClient(
- self.agent_token,
- self.api_base
- )
-
def _process_thinking_content(
self,
content: str,
@@ -62,7 +59,7 @@ class CozeAPIRunner(runner.RequestRunner):
if thinking_content:
content = f'\n{thinking_content}\n\n{content}'.strip()
return content, thinking_content
-
+
async def _preprocess_user_message(self, query: pipeline_query.Query) -> list[dict]:
"""预处理用户消息,转换为Coze消息格式
@@ -70,44 +67,43 @@ class CozeAPIRunner(runner.RequestRunner):
list[dict]: Coze消息列表
"""
messages = []
-
+
if isinstance(query.user_message.content, list):
# 多模态消息处理
content_parts = []
-
+
for ce in query.user_message.content:
if ce.type == 'text':
- content_parts.append({"type": "text", "text": ce.text})
+ content_parts.append({'type': 'text', 'text': ce.text})
elif ce.type == 'image_base64':
image_b64, image_format = await image.extract_b64_and_format(ce.image_base64)
file_bytes = base64.b64decode(image_b64)
file_id = await self._get_file_id(file_bytes)
- content_parts.append({"type": "image", "file_id": file_id})
+ content_parts.append({'type': 'image', 'file_id': file_id})
elif ce.type == 'file':
# 处理文件,上传到Coze
file_id = await self._get_file_id(ce.file)
- content_parts.append({"type": "file", "file_id": file_id})
-
+ content_parts.append({'type': 'file', 'file_id': file_id})
+
# 创建多模态消息
if content_parts:
- messages.append({
- "role": "user",
- "content": json.dumps(content_parts),
- "content_type": "object_string",
- "meta_data": None
- })
-
+ messages.append(
+ {
+ 'role': 'user',
+ 'content': json.dumps(content_parts),
+ 'content_type': 'object_string',
+ 'meta_data': None,
+ }
+ )
+
elif isinstance(query.user_message.content, str):
# 纯文本消息
- messages.append({
- "role": "user",
- "content": query.user_message.content,
- "content_type": "text",
- "meta_data": None
- })
-
+ messages.append(
+ {'role': 'user', 'content': query.user_message.content, 'content_type': 'text', 'meta_data': None}
+ )
+
return messages
-
+
async def _get_file_id(self, file) -> str:
"""上传文件到Coze服务
Args:
@@ -122,21 +118,21 @@ class CozeAPIRunner(runner.RequestRunner):
self, query: pipeline_query.Query
) -> typing.AsyncGenerator[provider_message.Message, None]:
"""调用聊天助手(非流式)
-
+
注意:由于cozepy没有提供非流式API,这里使用流式API并在结束后一次性返回完整内容
"""
user_id = f'{query.launcher_type.value}_{query.launcher_id}'
# 预处理用户消息
additional_messages = await self._preprocess_user_message(query)
-
+
# 获取会话ID
conversation_id = None
-
+
# 收集完整内容
full_content = ''
full_reasoning = ''
-
+
try:
# 调用Coze API流式接口
async for chunk in self.coze.chat_messages(
@@ -146,54 +142,54 @@ class CozeAPIRunner(runner.RequestRunner):
conversation_id=conversation_id,
timeout=self.chat_timeout,
auto_save_history=self.auto_save_history,
- stream=True
+ stream=True,
):
self.ap.logger.debug(f'coze-chat-stream: {chunk}')
-
+
event_type = chunk.get('event')
data = chunk.get('data', {})
# Removed debug print statement to avoid cluttering logs in production
-
+
if event_type == 'conversation.message.delta':
# 收集内容
if 'content' in data:
full_content += data.get('content', '')
-
+
# 收集推理内容(如果有)
if 'reasoning_content' in data:
full_reasoning += data.get('reasoning_content', '')
-
- elif event_type.split(".")[-1] == 'done' : # 本地部署coze时,结束event不为done
+
+ elif event_type.split('.')[-1] == 'done': # 本地部署coze时,结束event不为done
# 保存会话ID
if 'conversation_id' in data:
conversation_id = data.get('conversation_id')
-
+
elif event_type == 'error':
# 处理错误
- error_msg = f"Coze API错误: {data.get('message', '未知错误')}"
+ error_msg = f'Coze API错误: {data.get("message", "未知错误")}'
yield provider_message.Message(
role='assistant',
content=error_msg,
)
return
-
+
# 处理思维链内容
content, thinking_content = self._process_thinking_content(full_content)
if full_reasoning:
remove_think = self.pipeline_config.get('output', {}).get('misc', {}).get('remove-think', False)
if not remove_think:
content = f'\n{full_reasoning}\n\n{content}'.strip()
-
+
# 一次性返回完整内容
yield provider_message.Message(
role='assistant',
content=content,
)
-
+
# 保存会话ID
if conversation_id and query.session.using_conversation:
query.session.using_conversation.uuid = conversation_id
-
+
except Exception as e:
self.ap.logger.error(f'Coze API错误: {str(e)}')
yield provider_message.Message(
@@ -201,7 +197,6 @@ class CozeAPIRunner(runner.RequestRunner):
content=f'Coze API调用失败: {str(e)}',
)
-
async def _chat_messages_chunk(
self, query: pipeline_query.Query
) -> typing.AsyncGenerator[provider_message.MessageChunk, None]:
@@ -221,8 +216,6 @@ class CozeAPIRunner(runner.RequestRunner):
full_content = ''
remove_think = self.pipeline_config.get('output', {}).get('misc', {}).get('remove-think', False)
-
-
try:
# 调用Coze API流式接口
async for chunk in self.coze.chat_messages(
@@ -232,22 +225,21 @@ class CozeAPIRunner(runner.RequestRunner):
conversation_id=conversation_id,
timeout=self.chat_timeout,
auto_save_history=self.auto_save_history,
- stream=True
+ stream=True,
):
self.ap.logger.debug(f'coze-chat-stream-chunk: {chunk}')
event_type = chunk.get('event')
data = chunk.get('data', {})
- content = ""
-
+ content = ''
+
if event_type == 'conversation.message.delta':
message_idx += 1
# 处理内容增量
- if "reasoning_content" in data and not remove_think:
-
+ if 'reasoning_content' in data and not remove_think:
reasoning_content = data.get('reasoning_content', '')
if reasoning_content and not start_reasoning:
- content = f"\n"
+ content = '\n'
start_reasoning = True
content += reasoning_content
@@ -255,11 +247,10 @@ class CozeAPIRunner(runner.RequestRunner):
if data.get('content', ''):
content += data.get('content', '')
if not stop_reasoning and start_reasoning:
- content = f"\n{content}"
+ content = f'\n{content}'
stop_reasoning = True
-
- elif event_type.split(".")[-1] == 'done' : # 本地部署coze时,结束event不为done
+ elif event_type.split('.')[-1] == 'done': # 本地部署coze时,结束event不为done
# 保存会话ID
if 'conversation_id' in data:
conversation_id = data.get('conversation_id')
@@ -267,34 +258,22 @@ class CozeAPIRunner(runner.RequestRunner):
query.session.using_conversation.uuid = conversation_id
is_final = True
-
elif event_type == 'error':
# 处理错误
- error_msg = f"Coze API错误: {data.get('message', '未知错误')}"
- yield provider_message.MessageChunk(
- role='assistant',
- content=error_msg,
- finish_reason='error'
- )
+ error_msg = f'Coze API错误: {data.get("message", "未知错误")}'
+ yield provider_message.MessageChunk(role='assistant', content=error_msg, finish_reason='error')
return
full_content += content
if message_idx % 8 == 0 or is_final:
if full_content:
- yield provider_message.MessageChunk(
- role='assistant',
- content=full_content,
- is_final=is_final
- )
-
+ yield provider_message.MessageChunk(role='assistant', content=full_content, is_final=is_final)
+
except Exception as e:
self.ap.logger.error(f'Coze API流式调用错误: {str(e)}')
yield provider_message.MessageChunk(
- role='assistant',
- content=f'Coze API流式调用失败: {str(e)}',
- finish_reason='error'
+ role='assistant', content=f'Coze API流式调用失败: {str(e)}', finish_reason='error'
)
-
async def run(self, query: pipeline_query.Query) -> typing.AsyncGenerator[provider_message.Message, None]:
"""运行"""
msg_seq = 0
@@ -307,7 +286,3 @@ class CozeAPIRunner(runner.RequestRunner):
else:
async for msg in self._chat_messages(query):
yield msg
-
-
-
-
diff --git a/pkg/provider/runners/dashscopeapi.py b/src/langbot/pkg/provider/runners/dashscopeapi.py
similarity index 100%
rename from pkg/provider/runners/dashscopeapi.py
rename to src/langbot/pkg/provider/runners/dashscopeapi.py
diff --git a/pkg/provider/runners/difysvapi.py b/src/langbot/pkg/provider/runners/difysvapi.py
similarity index 99%
rename from pkg/provider/runners/difysvapi.py
rename to src/langbot/pkg/provider/runners/difysvapi.py
index eefc7524..21fb471e 100644
--- a/pkg/provider/runners/difysvapi.py
+++ b/src/langbot/pkg/provider/runners/difysvapi.py
@@ -6,13 +6,12 @@ import uuid
import base64
-from .. import runner
-from ...core import app
+from langbot.pkg.provider import runner
+from langbot.pkg.core import app
import langbot_plugin.api.entities.builtin.provider.message as provider_message
-from ...utils import image
+from langbot.pkg.utils import image
import langbot_plugin.api.entities.builtin.pipeline.query as pipeline_query
-from libs.dify_service_api.v1 import client, errors
-
+from langbot.libs.dify_service_api.v1 import client, errors
@runner.runner_class('dify-service-api')
diff --git a/pkg/provider/runners/langflowapi.py b/src/langbot/pkg/provider/runners/langflowapi.py
similarity index 100%
rename from pkg/provider/runners/langflowapi.py
rename to src/langbot/pkg/provider/runners/langflowapi.py
diff --git a/pkg/provider/runners/localagent.py b/src/langbot/pkg/provider/runners/localagent.py
similarity index 99%
rename from pkg/provider/runners/localagent.py
rename to src/langbot/pkg/provider/runners/localagent.py
index f89a4e1c..6375ca31 100644
--- a/pkg/provider/runners/localagent.py
+++ b/src/langbot/pkg/provider/runners/localagent.py
@@ -42,7 +42,7 @@ class LocalAgentRunner(runner.RequestRunner):
# Get knowledge bases list (new field)
kb_uuids = query.pipeline_config['ai']['local-agent'].get('knowledge-bases', [])
-
+
# Fallback to old field for backward compatibility
if not kb_uuids:
old_kb_uuid = query.pipeline_config['ai']['local-agent'].get('knowledge-base', '')
@@ -64,7 +64,7 @@ class LocalAgentRunner(runner.RequestRunner):
if kb_uuids and user_message_text:
# only support text for now
all_results = []
-
+
# Retrieve from each knowledge base
for kb_uuid in kb_uuids:
kb = await self.ap.rag_mgr.get_knowledge_base_by_uuid(kb_uuid)
@@ -74,7 +74,7 @@ class LocalAgentRunner(runner.RequestRunner):
continue
result = await kb.retrieve(user_message_text, kb.knowledge_base_entity.top_k)
-
+
if result:
all_results.extend(result)
diff --git a/pkg/provider/runners/n8nsvapi.py b/src/langbot/pkg/provider/runners/n8nsvapi.py
similarity index 100%
rename from pkg/provider/runners/n8nsvapi.py
rename to src/langbot/pkg/provider/runners/n8nsvapi.py
diff --git a/pkg/provider/runners/tboxapi.py b/src/langbot/pkg/provider/runners/tboxapi.py
similarity index 93%
rename from pkg/provider/runners/tboxapi.py
rename to src/langbot/pkg/provider/runners/tboxapi.py
index f0b1bd6a..0fb22a64 100644
--- a/pkg/provider/runners/tboxapi.py
+++ b/src/langbot/pkg/provider/runners/tboxapi.py
@@ -65,10 +65,8 @@ class TboxAPIRunner(runner.RequestRunner):
with tempfile.NamedTemporaryFile(suffix=f'.{image_format}', delete=False) as tmp_file:
tmp_file.write(file_bytes)
tmp_file_path = tmp_file.name
- file_upload_resp = self.tbox_client.upload_file(
- tmp_file_path
- )
- image_id = file_upload_resp.get("data", "")
+ file_upload_resp = self.tbox_client.upload_file(tmp_file_path)
+ image_id = file_upload_resp.get('data', '')
image_ids.append(image_id)
finally:
# 清理临时文件
@@ -97,15 +95,12 @@ class TboxAPIRunner(runner.RequestRunner):
files = None
if image_ids:
- files = [
- File(file_id=image_id, type=FileType.IMAGE)
- for image_id in image_ids
- ]
+ files = [File(file_id=image_id, type=FileType.IMAGE) for image_id in image_ids]
# 发送对话请求
response = self.tbox_client.chat(
app_id=self.app_id, # Tbox中智能体应用的ID
- user_id=query.bot_uuid, # 用户ID
+ user_id=query.bot_uuid, # 用户ID
query=plain_text, # 用户输入的文本信息
stream=is_stream, # 是否流式输出
conversation_id=conversation_id, # 会话ID,为None时Tbox会自动创建一个新会话
@@ -124,13 +119,13 @@ class TboxAPIRunner(runner.RequestRunner):
)
def _process_non_stream_message(self, response: typing.Dict, query: pipeline_query.Query, remove_think: bool):
- if response.get('errorCode') != "0":
+ if response.get('errorCode') != '0':
raise TboxAPIError(f'Tbox API 请求失败: {response.get("errorMsg", "")}')
payload = response.get('data', {})
conversation_id = payload.get('conversationId', '')
query.session.using_conversation.uuid = conversation_id
thinking_content = payload.get('reasoningContent', [])
- result = ""
+ result = ''
if thinking_content and not remove_think:
result += f'\n{thinking_content[0].get("text", "")}\n\n'
content = payload.get('result', [])
@@ -138,7 +133,9 @@ class TboxAPIRunner(runner.RequestRunner):
result += content[0].get('chunk', '')
return result
- def _process_stream_message(self, response: typing.Generator[dict], query: pipeline_query.Query, remove_think: bool):
+ def _process_stream_message(
+ self, response: typing.Generator[dict], query: pipeline_query.Query, remove_think: bool
+ ):
idx_msg = 0
pending_content = ''
conversation_id = None
@@ -170,7 +167,7 @@ class TboxAPIRunner(runner.RequestRunner):
payload = json.loads(chunk.get('payload', '{}'))
if payload.get('ext_data', {}).get('text'):
idx_msg += 1
- content = payload.get('ext_data', {}).get('text')
+ content = payload.get('ext_data', {}).get('text')
if not think_start:
think_start = True
pending_content += f'\n{content}'
diff --git a/pkg/provider/session/__init__.py b/src/langbot/pkg/provider/session/__init__.py
similarity index 100%
rename from pkg/provider/session/__init__.py
rename to src/langbot/pkg/provider/session/__init__.py
diff --git a/pkg/provider/session/sessionmgr.py b/src/langbot/pkg/provider/session/sessionmgr.py
similarity index 100%
rename from pkg/provider/session/sessionmgr.py
rename to src/langbot/pkg/provider/session/sessionmgr.py
diff --git a/pkg/provider/tools/__init__.py b/src/langbot/pkg/provider/tools/__init__.py
similarity index 100%
rename from pkg/provider/tools/__init__.py
rename to src/langbot/pkg/provider/tools/__init__.py
diff --git a/pkg/provider/tools/loader.py b/src/langbot/pkg/provider/tools/loader.py
similarity index 100%
rename from pkg/provider/tools/loader.py
rename to src/langbot/pkg/provider/tools/loader.py
diff --git a/pkg/provider/tools/loaders/__init__.py b/src/langbot/pkg/provider/tools/loaders/__init__.py
similarity index 100%
rename from pkg/provider/tools/loaders/__init__.py
rename to src/langbot/pkg/provider/tools/loaders/__init__.py
diff --git a/pkg/provider/tools/loaders/mcp.py b/src/langbot/pkg/provider/tools/loaders/mcp.py
similarity index 100%
rename from pkg/provider/tools/loaders/mcp.py
rename to src/langbot/pkg/provider/tools/loaders/mcp.py
diff --git a/pkg/provider/tools/loaders/plugin.py b/src/langbot/pkg/provider/tools/loaders/plugin.py
similarity index 100%
rename from pkg/provider/tools/loaders/plugin.py
rename to src/langbot/pkg/provider/tools/loaders/plugin.py
diff --git a/pkg/provider/tools/toolmgr.py b/src/langbot/pkg/provider/tools/toolmgr.py
similarity index 90%
rename from pkg/provider/tools/toolmgr.py
rename to src/langbot/pkg/provider/tools/toolmgr.py
index a9379e80..f6b18a89 100644
--- a/pkg/provider/tools/toolmgr.py
+++ b/src/langbot/pkg/provider/tools/toolmgr.py
@@ -3,9 +3,9 @@ from __future__ import annotations
import typing
from ...core import app
-from ...utils import importutil
-from . import loaders
-from .loaders import mcp as mcp_loader, plugin as plugin_loader
+from langbot.pkg.utils import importutil
+from langbot.pkg.provider.tools import loaders
+from langbot.pkg.provider.tools.loaders import mcp as mcp_loader, plugin as plugin_loader
import langbot_plugin.api.entities.builtin.resource.tool as resource_tool
importutil.import_modules_in_pkg(loaders)
@@ -28,7 +28,9 @@ class ToolManager:
self.mcp_tool_loader = mcp_loader.MCPLoader(self.ap)
await self.mcp_tool_loader.initialize()
- async def get_all_tools(self, bound_plugins: list[str] | None = None, bound_mcp_servers: list[str] | None = None) -> list[resource_tool.LLMTool]:
+ async def get_all_tools(
+ self, bound_plugins: list[str] | None = None, bound_mcp_servers: list[str] | None = None
+ ) -> list[resource_tool.LLMTool]:
"""获取所有函数"""
all_functions: list[resource_tool.LLMTool] = []
diff --git a/pkg/rag/knowledge/kbmgr.py b/src/langbot/pkg/rag/knowledge/kbmgr.py
similarity index 96%
rename from pkg/rag/knowledge/kbmgr.py
rename to src/langbot/pkg/rag/knowledge/kbmgr.py
index ed242696..17e2af32 100644
--- a/pkg/rag/knowledge/kbmgr.py
+++ b/src/langbot/pkg/rag/knowledge/kbmgr.py
@@ -4,13 +4,13 @@ import uuid
import zipfile
import io
from .services import parser, chunker
-from pkg.core import app
-from pkg.rag.knowledge.services.embedder import Embedder
-from pkg.rag.knowledge.services.retriever import Retriever
+from langbot.pkg.core import app
+from langbot.pkg.rag.knowledge.services.embedder import Embedder
+from langbot.pkg.rag.knowledge.services.retriever import Retriever
import sqlalchemy
-from ...entity.persistence import rag as persistence_rag
-from pkg.core import taskmgr
-from ...entity.rag import retriever as retriever_entities
+from langbot.pkg.entity.persistence import rag as persistence_rag
+from langbot.pkg.core import taskmgr
+from langbot.pkg.entity.rag import retriever as retriever_entities
class RuntimeKnowledgeBase:
diff --git a/pkg/rag/knowledge/services/__init__.py b/src/langbot/pkg/rag/knowledge/services/__init__.py
similarity index 100%
rename from pkg/rag/knowledge/services/__init__.py
rename to src/langbot/pkg/rag/knowledge/services/__init__.py
diff --git a/pkg/rag/knowledge/services/base_service.py b/src/langbot/pkg/rag/knowledge/services/base_service.py
similarity index 100%
rename from pkg/rag/knowledge/services/base_service.py
rename to src/langbot/pkg/rag/knowledge/services/base_service.py
diff --git a/pkg/rag/knowledge/services/chunker.py b/src/langbot/pkg/rag/knowledge/services/chunker.py
similarity index 94%
rename from pkg/rag/knowledge/services/chunker.py
rename to src/langbot/pkg/rag/knowledge/services/chunker.py
index 19b1f296..0cb16816 100644
--- a/pkg/rag/knowledge/services/chunker.py
+++ b/src/langbot/pkg/rag/knowledge/services/chunker.py
@@ -2,8 +2,8 @@ from __future__ import annotations
import json
from typing import List
-from pkg.rag.knowledge.services import base_service
-from pkg.core import app
+from langbot.pkg.rag.knowledge.services import base_service
+from langbot.pkg.core import app
from langchain_text_splitters import RecursiveCharacterTextSplitter
diff --git a/pkg/rag/knowledge/services/embedder.py b/src/langbot/pkg/rag/knowledge/services/embedder.py
similarity index 86%
rename from pkg/rag/knowledge/services/embedder.py
rename to src/langbot/pkg/rag/knowledge/services/embedder.py
index a0ae3d49..c8a1c3d3 100644
--- a/pkg/rag/knowledge/services/embedder.py
+++ b/src/langbot/pkg/rag/knowledge/services/embedder.py
@@ -1,10 +1,10 @@
from __future__ import annotations
import uuid
from typing import List
-from pkg.rag.knowledge.services.base_service import BaseService
-from ....entity.persistence import rag as persistence_rag
-from ....core import app
-from ....provider.modelmgr.requester import RuntimeEmbeddingModel
+from langbot.pkg.rag.knowledge.services.base_service import BaseService
+from langbot.pkg.entity.persistence import rag as persistence_rag
+from langbot.pkg.core import app
+from langbot.pkg.provider.modelmgr.requester import RuntimeEmbeddingModel
import sqlalchemy
diff --git a/pkg/rag/knowledge/services/parser.py b/src/langbot/pkg/rag/knowledge/services/parser.py
similarity index 99%
rename from pkg/rag/knowledge/services/parser.py
rename to src/langbot/pkg/rag/knowledge/services/parser.py
index 004dbdaa..50410738 100644
--- a/pkg/rag/knowledge/services/parser.py
+++ b/src/langbot/pkg/rag/knowledge/services/parser.py
@@ -9,7 +9,7 @@ import markdown
from bs4 import BeautifulSoup
import re
import asyncio # Import asyncio for async operations
-from pkg.core import app
+from langbot.pkg.core import app
class FileParser:
diff --git a/pkg/rag/knowledge/services/retriever.py b/src/langbot/pkg/rag/knowledge/services/retriever.py
similarity index 100%
rename from pkg/rag/knowledge/services/retriever.py
rename to src/langbot/pkg/rag/knowledge/services/retriever.py
diff --git a/pkg/storage/__init__.py b/src/langbot/pkg/storage/__init__.py
similarity index 100%
rename from pkg/storage/__init__.py
rename to src/langbot/pkg/storage/__init__.py
diff --git a/pkg/storage/mgr.py b/src/langbot/pkg/storage/mgr.py
similarity index 100%
rename from pkg/storage/mgr.py
rename to src/langbot/pkg/storage/mgr.py
diff --git a/pkg/storage/provider.py b/src/langbot/pkg/storage/provider.py
similarity index 100%
rename from pkg/storage/provider.py
rename to src/langbot/pkg/storage/provider.py
diff --git a/pkg/storage/providers/__init__.py b/src/langbot/pkg/storage/providers/__init__.py
similarity index 100%
rename from pkg/storage/providers/__init__.py
rename to src/langbot/pkg/storage/providers/__init__.py
diff --git a/pkg/storage/providers/localstorage.py b/src/langbot/pkg/storage/providers/localstorage.py
similarity index 100%
rename from pkg/storage/providers/localstorage.py
rename to src/langbot/pkg/storage/providers/localstorage.py
diff --git a/pkg/storage/providers/s3storage.py b/src/langbot/pkg/storage/providers/s3storage.py
similarity index 100%
rename from pkg/storage/providers/s3storage.py
rename to src/langbot/pkg/storage/providers/s3storage.py
diff --git a/pkg/utils/__init__.py b/src/langbot/pkg/utils/__init__.py
similarity index 100%
rename from pkg/utils/__init__.py
rename to src/langbot/pkg/utils/__init__.py
diff --git a/pkg/utils/constants.py b/src/langbot/pkg/utils/constants.py
similarity index 83%
rename from pkg/utils/constants.py
rename to src/langbot/pkg/utils/constants.py
index c225ed82..503974af 100644
--- a/pkg/utils/constants.py
+++ b/src/langbot/pkg/utils/constants.py
@@ -1,4 +1,4 @@
-semantic_version = 'v4.5.0'
+semantic_version = 'v4.6.0-beta.2'
required_database_version = 11
"""Tag the version of the database schema, used to check if the database needs to be migrated"""
diff --git a/pkg/utils/funcschema.py b/src/langbot/pkg/utils/funcschema.py
similarity index 100%
rename from pkg/utils/funcschema.py
rename to src/langbot/pkg/utils/funcschema.py
diff --git a/pkg/utils/image.py b/src/langbot/pkg/utils/image.py
similarity index 100%
rename from pkg/utils/image.py
rename to src/langbot/pkg/utils/image.py
diff --git a/pkg/utils/importutil.py b/src/langbot/pkg/utils/importutil.py
similarity index 58%
rename from pkg/utils/importutil.py
rename to src/langbot/pkg/utils/importutil.py
index 1933d611..a35052a6 100644
--- a/pkg/utils/importutil.py
+++ b/src/langbot/pkg/utils/importutil.py
@@ -1,5 +1,5 @@
import importlib
-import importlib.util
+import importlib.resources
import os
import typing
@@ -25,7 +25,7 @@ def import_dot_style_dir(dot_sep_path: str):
return import_dir(os.path.join(*sec))
-def import_dir(path: str):
+def import_dir(path: str, path_prefix: str = 'langbot.'):
for file in os.listdir(path):
if file.endswith('.py') and file != '__init__.py':
full_path = os.path.join(path, file)
@@ -33,10 +33,17 @@ def import_dir(path: str):
rel_path = rel_path[1:]
rel_path = rel_path.replace('/', '.')[:-3]
rel_path = rel_path.replace('\\', '.')
- importlib.import_module(rel_path)
+ importlib.import_module(f'{path_prefix}{rel_path}')
-if __name__ == '__main__':
- from pkg.platform import types
+def read_resource_file(resource_path: str) -> str:
+ with importlib.resources.files('langbot').joinpath(resource_path).open('r', encoding='utf-8') as f:
+ return f.read()
- import_modules_in_pkg(types)
+
+def read_resource_file_bytes(resource_path: str) -> bytes:
+ return importlib.resources.files('langbot').joinpath(resource_path).read_bytes()
+
+
+def list_resource_files(resource_path: str) -> list[str]:
+ return [f.name for f in importlib.resources.files('langbot').joinpath(resource_path).iterdir()]
diff --git a/pkg/utils/logcache.py b/src/langbot/pkg/utils/logcache.py
similarity index 100%
rename from pkg/utils/logcache.py
rename to src/langbot/pkg/utils/logcache.py
diff --git a/src/langbot/pkg/utils/paths.py b/src/langbot/pkg/utils/paths.py
new file mode 100644
index 00000000..5553c154
--- /dev/null
+++ b/src/langbot/pkg/utils/paths.py
@@ -0,0 +1,92 @@
+"""Utility functions for finding package resources"""
+
+import os
+from pathlib import Path
+
+
+_is_source_install = None
+
+
+def _check_if_source_install() -> bool:
+ """
+ Check if we're running from source directory or an installed package.
+ Cached to avoid repeated file I/O.
+ """
+ global _is_source_install
+
+ if _is_source_install is not None:
+ return _is_source_install
+
+ # Check if main.py exists in current directory with LangBot marker
+ if os.path.exists('main.py'):
+ try:
+ with open('main.py', 'r', encoding='utf-8') as f:
+ # Only read first 500 chars to check for marker
+ content = f.read(500)
+ if 'LangBot/main.py' in content:
+ _is_source_install = True
+ return True
+ except (IOError, OSError, UnicodeDecodeError):
+ # If we can't read the file, assume not a source install
+ pass
+
+ _is_source_install = False
+ return False
+
+
+def get_frontend_path() -> str:
+ """
+ Get the path to the frontend build files.
+
+ Returns the path to web/out directory, handling both:
+ - Development mode: running from source directory
+ - Package mode: installed via pip/uvx
+ """
+ # First, check if we're running from source directory
+ if _check_if_source_install() and os.path.exists('web/out'):
+ return 'web/out'
+
+ # Second, check current directory for web/out (in case user is in source dir)
+ if os.path.exists('web/out'):
+ return 'web/out'
+
+ # Third, find it relative to the package installation
+ # Get the directory where this file is located
+ # paths.py is in pkg/utils/, so parent.parent goes up to pkg/, then parent again goes up to the package root
+ pkg_dir = Path(__file__).parent.parent.parent
+ frontend_path = pkg_dir / 'web' / 'out'
+ if frontend_path.exists():
+ return str(frontend_path)
+
+ # Return the default path (will be checked by caller)
+ return 'web/out'
+
+
+def get_resource_path(resource: str) -> str:
+ """
+ Get the path to a resource file.
+
+ Args:
+ resource: Relative path to resource (e.g., 'templates/config.yaml')
+
+ Returns:
+ Absolute path to the resource
+ """
+ # First, check if resource exists in current directory (source install)
+ if _check_if_source_install() and os.path.exists(resource):
+ return resource
+
+ # Second, check current directory anyway
+ if os.path.exists(resource):
+ return resource
+
+ # Third, find it relative to package directory
+ # Get the directory where this file is located
+ # paths.py is in pkg/utils/, so parent.parent goes up to pkg/, then parent again goes up to the package root
+ pkg_dir = Path(__file__).parent.parent.parent
+ resource_path = pkg_dir / resource
+ if resource_path.exists():
+ return str(resource_path)
+
+ # Return the original path
+ return resource
diff --git a/pkg/utils/pkgmgr.py b/src/langbot/pkg/utils/pkgmgr.py
similarity index 100%
rename from pkg/utils/pkgmgr.py
rename to src/langbot/pkg/utils/pkgmgr.py
diff --git a/pkg/utils/platform.py b/src/langbot/pkg/utils/platform.py
similarity index 100%
rename from pkg/utils/platform.py
rename to src/langbot/pkg/utils/platform.py
diff --git a/pkg/utils/proxy.py b/src/langbot/pkg/utils/proxy.py
similarity index 100%
rename from pkg/utils/proxy.py
rename to src/langbot/pkg/utils/proxy.py
diff --git a/pkg/utils/version.py b/src/langbot/pkg/utils/version.py
similarity index 98%
rename from pkg/utils/version.py
rename to src/langbot/pkg/utils/version.py
index 3a2748fc..60df3f32 100644
--- a/pkg/utils/version.py
+++ b/src/langbot/pkg/utils/version.py
@@ -38,7 +38,7 @@ class VersionManager:
rls_list = rls_list_resp.json()
return rls_list
except Exception as e:
- self.ap.logger.warning(f"获取发行列表失败: {e}")
+ self.ap.logger.warning(f'获取发行列表失败: {e}')
pass
return []
diff --git a/pkg/vector/__init__.py b/src/langbot/pkg/vector/__init__.py
similarity index 100%
rename from pkg/vector/__init__.py
rename to src/langbot/pkg/vector/__init__.py
diff --git a/pkg/vector/mgr.py b/src/langbot/pkg/vector/mgr.py
similarity index 100%
rename from pkg/vector/mgr.py
rename to src/langbot/pkg/vector/mgr.py
diff --git a/pkg/vector/vdb.py b/src/langbot/pkg/vector/vdb.py
similarity index 100%
rename from pkg/vector/vdb.py
rename to src/langbot/pkg/vector/vdb.py
diff --git a/pkg/vector/vdbs/__init__.py b/src/langbot/pkg/vector/vdbs/__init__.py
similarity index 100%
rename from pkg/vector/vdbs/__init__.py
rename to src/langbot/pkg/vector/vdbs/__init__.py
diff --git a/pkg/vector/vdbs/chroma.py b/src/langbot/pkg/vector/vdbs/chroma.py
similarity index 96%
rename from pkg/vector/vdbs/chroma.py
rename to src/langbot/pkg/vector/vdbs/chroma.py
index 41ab7d36..94227c75 100644
--- a/pkg/vector/vdbs/chroma.py
+++ b/src/langbot/pkg/vector/vdbs/chroma.py
@@ -2,8 +2,8 @@ from __future__ import annotations
import asyncio
from typing import Any
from chromadb import PersistentClient
-from pkg.vector.vdb import VectorDatabase
-from pkg.core import app
+from langbot.pkg.vector.vdb import VectorDatabase
+from langbot.pkg.core import app
import chromadb
import chromadb.errors
diff --git a/pkg/vector/vdbs/qdrant.py b/src/langbot/pkg/vector/vdbs/qdrant.py
similarity index 98%
rename from pkg/vector/vdbs/qdrant.py
rename to src/langbot/pkg/vector/vdbs/qdrant.py
index 85a1ad81..a6fbd4ab 100644
--- a/pkg/vector/vdbs/qdrant.py
+++ b/src/langbot/pkg/vector/vdbs/qdrant.py
@@ -3,8 +3,8 @@ from __future__ import annotations
from typing import Any, Dict, List
from qdrant_client import AsyncQdrantClient, models
-from pkg.core import app
-from pkg.vector.vdb import VectorDatabase
+from langbot.pkg.core import app
+from langbot.pkg.vector.vdb import VectorDatabase
class QdrantVectorDatabase(VectorDatabase):
diff --git a/templates/__init__.py b/src/langbot/templates/__init__.py
similarity index 100%
rename from templates/__init__.py
rename to src/langbot/templates/__init__.py
diff --git a/components.yaml b/src/langbot/templates/components.yaml
similarity index 78%
rename from components.yaml
rename to src/langbot/templates/components.yaml
index 03b2d04e..a95235aa 100644
--- a/components.yaml
+++ b/src/langbot/templates/components.yaml
@@ -7,9 +7,6 @@ metadata:
zh_Hans: 内置组件
spec:
components:
- ComponentTemplate:
- fromFiles:
- - pkg/provider/modelmgr/requester.yaml
MessagePlatformAdapter:
fromDirs:
- path: pkg/platform/sources/
diff --git a/templates/config.yaml b/src/langbot/templates/config.yaml
similarity index 100%
rename from templates/config.yaml
rename to src/langbot/templates/config.yaml
diff --git a/templates/default-pipeline-config.json b/src/langbot/templates/default-pipeline-config.json
similarity index 100%
rename from templates/default-pipeline-config.json
rename to src/langbot/templates/default-pipeline-config.json
diff --git a/templates/legacy/command.json b/src/langbot/templates/legacy/command.json
similarity index 100%
rename from templates/legacy/command.json
rename to src/langbot/templates/legacy/command.json
diff --git a/templates/legacy/pipeline.json b/src/langbot/templates/legacy/pipeline.json
similarity index 100%
rename from templates/legacy/pipeline.json
rename to src/langbot/templates/legacy/pipeline.json
diff --git a/templates/legacy/platform.json b/src/langbot/templates/legacy/platform.json
similarity index 100%
rename from templates/legacy/platform.json
rename to src/langbot/templates/legacy/platform.json
diff --git a/templates/legacy/provider.json b/src/langbot/templates/legacy/provider.json
similarity index 100%
rename from templates/legacy/provider.json
rename to src/langbot/templates/legacy/provider.json
diff --git a/templates/legacy/system.json b/src/langbot/templates/legacy/system.json
similarity index 100%
rename from templates/legacy/system.json
rename to src/langbot/templates/legacy/system.json
diff --git a/templates/metadata/pipeline/ai.yaml b/src/langbot/templates/metadata/pipeline/ai.yaml
similarity index 100%
rename from templates/metadata/pipeline/ai.yaml
rename to src/langbot/templates/metadata/pipeline/ai.yaml
diff --git a/templates/metadata/pipeline/output.yaml b/src/langbot/templates/metadata/pipeline/output.yaml
similarity index 100%
rename from templates/metadata/pipeline/output.yaml
rename to src/langbot/templates/metadata/pipeline/output.yaml
diff --git a/templates/metadata/pipeline/safety.yaml b/src/langbot/templates/metadata/pipeline/safety.yaml
similarity index 100%
rename from templates/metadata/pipeline/safety.yaml
rename to src/langbot/templates/metadata/pipeline/safety.yaml
diff --git a/templates/metadata/pipeline/trigger.yaml b/src/langbot/templates/metadata/pipeline/trigger.yaml
similarity index 100%
rename from templates/metadata/pipeline/trigger.yaml
rename to src/langbot/templates/metadata/pipeline/trigger.yaml
diff --git a/templates/metadata/sensitive-words.json b/src/langbot/templates/metadata/sensitive-words.json
similarity index 100%
rename from templates/metadata/sensitive-words.json
rename to src/langbot/templates/metadata/sensitive-words.json
diff --git a/tests/unit_tests/pipeline/conftest.py b/tests/unit_tests/pipeline/conftest.py
index f6935395..40b2e930 100644
--- a/tests/unit_tests/pipeline/conftest.py
+++ b/tests/unit_tests/pipeline/conftest.py
@@ -10,16 +10,13 @@ This file provides infrastructure for all pipeline tests, including:
from __future__ import annotations
import pytest
-from unittest.mock import AsyncMock, MagicMock, Mock
-from typing import Any
+from unittest.mock import AsyncMock, Mock
import langbot_plugin.api.entities.builtin.pipeline.query as pipeline_query
import langbot_plugin.api.entities.builtin.platform.message as platform_message
-import langbot_plugin.api.entities.builtin.platform.events as platform_events
import langbot_plugin.api.entities.builtin.provider.session as provider_session
-import langbot_plugin.api.entities.builtin.provider.message as provider_message
-from pkg.pipeline import entities as pipeline_entities
+from langbot.pkg.pipeline import entities as pipeline_entities
class MockApplication:
@@ -203,7 +200,7 @@ def sample_query(sample_message_chain, sample_message_event, mock_adapter):
variables={},
resp_messages=[],
resp_message_chain=None,
- current_stage_name=None
+ current_stage_name=None,
)
return query
diff --git a/tests/unit_tests/pipeline/test_bansess.py b/tests/unit_tests/pipeline/test_bansess.py
index 2483d484..394fb8bc 100644
--- a/tests/unit_tests/pipeline/test_bansess.py
+++ b/tests/unit_tests/pipeline/test_bansess.py
@@ -5,7 +5,6 @@ Tests the actual BanSessionCheckStage implementation from pkg.pipeline.bansess
"""
import pytest
-from unittest.mock import Mock
from importlib import import_module
import langbot_plugin.api.entities.builtin.provider.session as provider_session
@@ -13,9 +12,8 @@ import langbot_plugin.api.entities.builtin.provider.session as provider_session
def get_modules():
"""Lazy import to ensure proper initialization order"""
# Import pipelinemgr first to trigger proper stage registration
- pipelinemgr = import_module('pkg.pipeline.pipelinemgr')
- bansess = import_module('pkg.pipeline.bansess.bansess')
- entities = import_module('pkg.pipeline.entities')
+ bansess = import_module('langbot.pkg.pipeline.bansess.bansess')
+ entities = import_module('langbot.pkg.pipeline.entities')
return bansess, entities
@@ -26,14 +24,7 @@ async def test_whitelist_allow(mock_app, sample_query):
sample_query.launcher_type = provider_session.LauncherTypes.PERSON
sample_query.launcher_id = '12345'
- sample_query.pipeline_config = {
- 'trigger': {
- 'access-control': {
- 'mode': 'whitelist',
- 'whitelist': ['person_12345']
- }
- }
- }
+ sample_query.pipeline_config = {'trigger': {'access-control': {'mode': 'whitelist', 'whitelist': ['person_12345']}}}
stage = bansess.BanSessionCheckStage(mock_app)
await stage.initialize(sample_query.pipeline_config)
@@ -51,14 +42,7 @@ async def test_whitelist_deny(mock_app, sample_query):
sample_query.launcher_type = provider_session.LauncherTypes.PERSON
sample_query.launcher_id = '99999'
- sample_query.pipeline_config = {
- 'trigger': {
- 'access-control': {
- 'mode': 'whitelist',
- 'whitelist': ['person_12345']
- }
- }
- }
+ sample_query.pipeline_config = {'trigger': {'access-control': {'mode': 'whitelist', 'whitelist': ['person_12345']}}}
stage = bansess.BanSessionCheckStage(mock_app)
await stage.initialize(sample_query.pipeline_config)
@@ -75,14 +59,7 @@ async def test_blacklist_allow(mock_app, sample_query):
sample_query.launcher_type = provider_session.LauncherTypes.PERSON
sample_query.launcher_id = '12345'
- sample_query.pipeline_config = {
- 'trigger': {
- 'access-control': {
- 'mode': 'blacklist',
- 'blacklist': ['person_99999']
- }
- }
- }
+ sample_query.pipeline_config = {'trigger': {'access-control': {'mode': 'blacklist', 'blacklist': ['person_99999']}}}
stage = bansess.BanSessionCheckStage(mock_app)
await stage.initialize(sample_query.pipeline_config)
@@ -99,14 +76,7 @@ async def test_blacklist_deny(mock_app, sample_query):
sample_query.launcher_type = provider_session.LauncherTypes.PERSON
sample_query.launcher_id = '12345'
- sample_query.pipeline_config = {
- 'trigger': {
- 'access-control': {
- 'mode': 'blacklist',
- 'blacklist': ['person_12345']
- }
- }
- }
+ sample_query.pipeline_config = {'trigger': {'access-control': {'mode': 'blacklist', 'blacklist': ['person_12345']}}}
stage = bansess.BanSessionCheckStage(mock_app)
await stage.initialize(sample_query.pipeline_config)
@@ -123,14 +93,7 @@ async def test_wildcard_group(mock_app, sample_query):
sample_query.launcher_type = provider_session.LauncherTypes.GROUP
sample_query.launcher_id = '12345'
- sample_query.pipeline_config = {
- 'trigger': {
- 'access-control': {
- 'mode': 'whitelist',
- 'whitelist': ['group_*']
- }
- }
- }
+ sample_query.pipeline_config = {'trigger': {'access-control': {'mode': 'whitelist', 'whitelist': ['group_*']}}}
stage = bansess.BanSessionCheckStage(mock_app)
await stage.initialize(sample_query.pipeline_config)
@@ -147,14 +110,7 @@ async def test_wildcard_person(mock_app, sample_query):
sample_query.launcher_type = provider_session.LauncherTypes.PERSON
sample_query.launcher_id = '12345'
- sample_query.pipeline_config = {
- 'trigger': {
- 'access-control': {
- 'mode': 'whitelist',
- 'whitelist': ['person_*']
- }
- }
- }
+ sample_query.pipeline_config = {'trigger': {'access-control': {'mode': 'whitelist', 'whitelist': ['person_*']}}}
stage = bansess.BanSessionCheckStage(mock_app)
await stage.initialize(sample_query.pipeline_config)
@@ -172,14 +128,7 @@ async def test_user_id_wildcard(mock_app, sample_query):
sample_query.launcher_type = provider_session.LauncherTypes.PERSON
sample_query.launcher_id = '12345'
sample_query.sender_id = '67890'
- sample_query.pipeline_config = {
- 'trigger': {
- 'access-control': {
- 'mode': 'whitelist',
- 'whitelist': ['*_67890']
- }
- }
- }
+ sample_query.pipeline_config = {'trigger': {'access-control': {'mode': 'whitelist', 'whitelist': ['*_67890']}}}
stage = bansess.BanSessionCheckStage(mock_app)
await stage.initialize(sample_query.pipeline_config)
diff --git a/tests/unit_tests/pipeline/test_pipelinemgr.py b/tests/unit_tests/pipeline/test_pipelinemgr.py
index c500c1c1..95c6d968 100644
--- a/tests/unit_tests/pipeline/test_pipelinemgr.py
+++ b/tests/unit_tests/pipeline/test_pipelinemgr.py
@@ -8,19 +8,19 @@ from importlib import import_module
def get_pipelinemgr_module():
- return import_module('pkg.pipeline.pipelinemgr')
+ return import_module('langbot.pkg.pipeline.pipelinemgr')
def get_stage_module():
- return import_module('pkg.pipeline.stage')
+ return import_module('langbot.pkg.pipeline.stage')
def get_entities_module():
- return import_module('pkg.pipeline.entities')
+ return import_module('langbot.pkg.pipeline.entities')
def get_persistence_pipeline_module():
- return import_module('pkg.entity.persistence.pipeline')
+ return import_module('langbot.pkg.entity.persistence.pipeline')
@pytest.mark.asyncio
diff --git a/tests/unit_tests/pipeline/test_ratelimit.py b/tests/unit_tests/pipeline/test_ratelimit.py
index 18e399fe..77649f70 100644
--- a/tests/unit_tests/pipeline/test_ratelimit.py
+++ b/tests/unit_tests/pipeline/test_ratelimit.py
@@ -13,10 +13,9 @@ import langbot_plugin.api.entities.builtin.provider.session as provider_session
def get_modules():
"""Lazy import to ensure proper initialization order"""
# Import pipelinemgr first to trigger proper stage registration
- pipelinemgr = import_module('pkg.pipeline.pipelinemgr')
- ratelimit = import_module('pkg.pipeline.ratelimit.ratelimit')
- entities = import_module('pkg.pipeline.entities')
- algo_module = import_module('pkg.pipeline.ratelimit.algo')
+ ratelimit = import_module('langbot.pkg.pipeline.ratelimit.ratelimit')
+ entities = import_module('langbot.pkg.pipeline.entities')
+ algo_module = import_module('langbot.pkg.pipeline.ratelimit.algo')
return ratelimit, entities, algo_module
@@ -44,11 +43,7 @@ async def test_require_access_allowed(mock_app, sample_query):
assert result.result_type == entities.ResultType.CONTINUE
assert result.new_query == sample_query
- mock_algo.require_access.assert_called_once_with(
- sample_query,
- 'person',
- '12345'
- )
+ mock_algo.require_access.assert_called_once_with(sample_query, 'person', '12345')
@pytest.mark.asyncio
@@ -102,8 +97,4 @@ async def test_release_access(mock_app, sample_query):
assert result.result_type == entities.ResultType.CONTINUE
assert result.new_query == sample_query
- mock_algo.release_access.assert_called_once_with(
- sample_query,
- 'person',
- '12345'
- )
+ mock_algo.release_access.assert_called_once_with(sample_query, 'person', '12345')
diff --git a/tests/unit_tests/pipeline/test_resprule.py b/tests/unit_tests/pipeline/test_resprule.py
index 69df165b..63dfbfd0 100644
--- a/tests/unit_tests/pipeline/test_resprule.py
+++ b/tests/unit_tests/pipeline/test_resprule.py
@@ -14,11 +14,11 @@ import langbot_plugin.api.entities.builtin.platform.message as platform_message
def get_modules():
"""Lazy import to ensure proper initialization order"""
# Import pipelinemgr first to trigger proper stage registration
- pipelinemgr = import_module('pkg.pipeline.pipelinemgr')
- resprule = import_module('pkg.pipeline.resprule.resprule')
- entities = import_module('pkg.pipeline.entities')
- rule = import_module('pkg.pipeline.resprule.rule')
- rule_entities = import_module('pkg.pipeline.resprule.entities')
+ # pipelinemgr = import_module('langbot.pkg.pipeline.pipelinemgr')
+ resprule = import_module('langbot.pkg.pipeline.resprule.resprule')
+ entities = import_module('langbot.pkg.pipeline.entities')
+ rule = import_module('langbot.pkg.pipeline.resprule.rule')
+ rule_entities = import_module('langbot.pkg.pipeline.resprule.entities')
return resprule, entities, rule, rule_entities
@@ -28,11 +28,7 @@ async def test_person_message_skip(mock_app, sample_query):
resprule, entities, rule, rule_entities = get_modules()
sample_query.launcher_type = provider_session.LauncherTypes.PERSON
- sample_query.pipeline_config = {
- 'trigger': {
- 'group-respond-rules': {}
- }
- }
+ sample_query.pipeline_config = {'trigger': {'group-respond-rules': {}}}
stage = resprule.GroupRespondRuleCheckStage(mock_app)
await stage.initialize(sample_query.pipeline_config)
@@ -50,18 +46,13 @@ async def test_group_message_no_match(mock_app, sample_query):
sample_query.launcher_type = provider_session.LauncherTypes.GROUP
sample_query.launcher_id = '12345'
- sample_query.pipeline_config = {
- 'trigger': {
- 'group-respond-rules': {}
- }
- }
+ sample_query.pipeline_config = {'trigger': {'group-respond-rules': {}}}
# Create mock rule matcher that doesn't match
mock_rule = Mock(spec=rule.GroupRespondRule)
- mock_rule.match = AsyncMock(return_value=rule_entities.RuleJudgeResult(
- matching=False,
- replacement=sample_query.message_chain
- ))
+ mock_rule.match = AsyncMock(
+ return_value=rule_entities.RuleJudgeResult(matching=False, replacement=sample_query.message_chain)
+ )
stage = resprule.GroupRespondRuleCheckStage(mock_app)
await stage.initialize(sample_query.pipeline_config)
@@ -81,23 +72,14 @@ async def test_group_message_match(mock_app, sample_query):
sample_query.launcher_type = provider_session.LauncherTypes.GROUP
sample_query.launcher_id = '12345'
- sample_query.pipeline_config = {
- 'trigger': {
- 'group-respond-rules': {}
- }
- }
+ sample_query.pipeline_config = {'trigger': {'group-respond-rules': {}}}
# Create new message chain after rule processing
- new_chain = platform_message.MessageChain([
- platform_message.Plain(text='Processed message')
- ])
+ new_chain = platform_message.MessageChain([platform_message.Plain(text='Processed message')])
# Create mock rule matcher that matches
mock_rule = Mock(spec=rule.GroupRespondRule)
- mock_rule.match = AsyncMock(return_value=rule_entities.RuleJudgeResult(
- matching=True,
- replacement=new_chain
- ))
+ mock_rule.match = AsyncMock(return_value=rule_entities.RuleJudgeResult(matching=True, replacement=new_chain))
stage = resprule.GroupRespondRuleCheckStage(mock_app)
await stage.initialize(sample_query.pipeline_config)
@@ -115,27 +97,21 @@ async def test_group_message_match(mock_app, sample_query):
async def test_atbot_rule_match(mock_app, sample_query):
"""Test AtBotRule removes At component"""
resprule, entities, rule, rule_entities = get_modules()
- atbot_module = import_module('pkg.pipeline.resprule.rules.atbot')
+ atbot_module = import_module('langbot.pkg.pipeline.resprule.rules.atbot')
sample_query.launcher_type = provider_session.LauncherTypes.GROUP
sample_query.adapter.bot_account_id = '999'
# Create message chain with At component
- message_chain = platform_message.MessageChain([
- platform_message.At(target='999'),
- platform_message.Plain(text='Hello bot')
- ])
+ message_chain = platform_message.MessageChain(
+ [platform_message.At(target='999'), platform_message.Plain(text='Hello bot')]
+ )
sample_query.message_chain = message_chain
atbot_rule = atbot_module.AtBotRule(mock_app)
await atbot_rule.initialize()
- result = await atbot_rule.match(
- str(message_chain),
- message_chain,
- {},
- sample_query
- )
+ result = await atbot_rule.match(str(message_chain), message_chain, {}, sample_query)
assert result.matching is True
# At component should be removed
@@ -147,25 +123,18 @@ async def test_atbot_rule_match(mock_app, sample_query):
async def test_atbot_rule_no_match(mock_app, sample_query):
"""Test AtBotRule when no At component present"""
resprule, entities, rule, rule_entities = get_modules()
- atbot_module = import_module('pkg.pipeline.resprule.rules.atbot')
+ atbot_module = import_module('langbot.pkg.pipeline.resprule.rules.atbot')
sample_query.launcher_type = provider_session.LauncherTypes.GROUP
sample_query.adapter.bot_account_id = '999'
# Create message chain without At component
- message_chain = platform_message.MessageChain([
- platform_message.Plain(text='Hello')
- ])
+ message_chain = platform_message.MessageChain([platform_message.Plain(text='Hello')])
sample_query.message_chain = message_chain
atbot_rule = atbot_module.AtBotRule(mock_app)
await atbot_rule.initialize()
- result = await atbot_rule.match(
- str(message_chain),
- message_chain,
- {},
- sample_query
- )
+ result = await atbot_rule.match(str(message_chain), message_chain, {}, sample_query)
assert result.matching is False
diff --git a/tests/unit_tests/storage/test_storage_provider_selection.py b/tests/unit_tests/storage/test_storage_provider_selection.py
index 9f87f10a..c5811e3c 100644
--- a/tests/unit_tests/storage/test_storage_provider_selection.py
+++ b/tests/unit_tests/storage/test_storage_provider_selection.py
@@ -4,9 +4,9 @@ Tests for storage manager and provider selection
import pytest
from unittest.mock import Mock, AsyncMock, patch
-from pkg.storage.mgr import StorageMgr
-from pkg.storage.providers.localstorage import LocalStorageProvider
-from pkg.storage.providers.s3storage import S3StorageProvider
+from langbot.pkg.storage.mgr import StorageMgr
+from langbot.pkg.storage.providers.localstorage import LocalStorageProvider
+from langbot.pkg.storage.providers.s3storage import S3StorageProvider
class TestStorageProviderSelection:
@@ -34,11 +34,7 @@ class TestStorageProviderSelection:
# Mock application
mock_app = Mock()
mock_app.instance_config = Mock()
- mock_app.instance_config.data = {
- 'storage': {
- 'use': 'local'
- }
- }
+ mock_app.instance_config.data = {'storage': {'use': 'local'}}
mock_app.logger = Mock()
storage_mgr = StorageMgr(mock_app)
@@ -62,8 +58,8 @@ class TestStorageProviderSelection:
'access_key_id': 'test_key',
'secret_access_key': 'test_secret',
'region': 'us-east-1',
- 'bucket': 'test-bucket'
- }
+ 'bucket': 'test-bucket',
+ },
}
}
mock_app.logger = Mock()
@@ -81,11 +77,7 @@ class TestStorageProviderSelection:
# Mock application
mock_app = Mock()
mock_app.instance_config = Mock()
- mock_app.instance_config.data = {
- 'storage': {
- 'use': 'invalid_type'
- }
- }
+ mock_app.instance_config.data = {'storage': {'use': 'invalid_type'}}
mock_app.logger = Mock()
storage_mgr = StorageMgr(mock_app)