diff --git a/pkg/api/http/service/bot.py b/pkg/api/http/service/bot.py index fcd81fcb..557b63f1 100644 --- a/pkg/api/http/service/bot.py +++ b/pkg/api/http/service/bot.py @@ -6,6 +6,7 @@ import sqlalchemy from ....core import app from ....entity.persistence import bot as persistence_bot +from ....entity.persistence import pipeline as persistence_pipeline class BotService: @@ -46,6 +47,16 @@ class BotService: """创建机器人""" # TODO: 检查配置信息格式 bot_data['uuid'] = str(uuid.uuid4()) + + # checkout the default pipeline + result = await self.ap.persistence_mgr.execute_async( + sqlalchemy.select(persistence_pipeline.LegacyPipeline).where(persistence_pipeline.LegacyPipeline.is_default == True) + ) + pipeline = result.first() + if pipeline is not None: + bot_data['use_pipeline_uuid'] = pipeline.uuid + bot_data['use_pipeline_name'] = pipeline.name + await self.ap.persistence_mgr.execute_async( sqlalchemy.insert(persistence_bot.Bot).values(bot_data) ) @@ -60,6 +71,18 @@ class BotService: """更新机器人""" if 'uuid' in bot_data: del bot_data['uuid'] + + # set use_pipeline_name + if 'use_pipeline_uuid' in bot_data: + result = await self.ap.persistence_mgr.execute_async( + sqlalchemy.select(persistence_pipeline.LegacyPipeline).where(persistence_pipeline.LegacyPipeline.uuid == bot_data['use_pipeline_uuid']) + ) + pipeline = result.first() + if pipeline is not None: + bot_data['use_pipeline_name'] = pipeline.name + else: + raise Exception("Pipeline not found") + await self.ap.persistence_mgr.execute_async( sqlalchemy.update(persistence_bot.Bot).values(bot_data).where(persistence_bot.Bot.uuid == bot_uuid) ) diff --git a/pkg/api/http/service/pipeline.py b/pkg/api/http/service/pipeline.py index 72b7daf7..c0f1c08a 100644 --- a/pkg/api/http/service/pipeline.py +++ b/pkg/api/http/service/pipeline.py @@ -61,10 +61,11 @@ class PipelineService: return self.ap.persistence_mgr.serialize_model(persistence_pipeline.LegacyPipeline, pipeline) - async def create_pipeline(self, pipeline_data: dict) -> str: + async def create_pipeline(self, pipeline_data: dict, default: bool = False) -> str: 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 # TODO: 检查pipeline config是否完整 diff --git a/pkg/core/bootutils/log.py b/pkg/core/bootutils/log.py index 62359dec..7cbb7412 100644 --- a/pkg/core/bootutils/log.py +++ b/pkg/core/bootutils/log.py @@ -31,7 +31,7 @@ async def init_logging(extra_handlers: list[logging.Handler] = None) -> logging. "%Y-%m-%d", time.localtime() ) - qcg_logger = logging.getLogger("qcg") + qcg_logger = logging.getLogger("langbot") qcg_logger.setLevel(level) diff --git a/pkg/entity/persistence/bot.py b/pkg/entity/persistence/bot.py index 77e13994..0cd7bce7 100644 --- a/pkg/entity/persistence/bot.py +++ b/pkg/entity/persistence/bot.py @@ -13,6 +13,7 @@ class Bot(Base): adapter = sqlalchemy.Column(sqlalchemy.String(255), nullable=False) adapter_config = sqlalchemy.Column(sqlalchemy.JSON, nullable=False) enable = sqlalchemy.Column(sqlalchemy.Boolean, nullable=False, default=False) + use_pipeline_name = sqlalchemy.Column(sqlalchemy.String(255), nullable=True) use_pipeline_uuid = sqlalchemy.Column(sqlalchemy.String(255), nullable=True) created_at = sqlalchemy.Column(sqlalchemy.DateTime, nullable=False, server_default=sqlalchemy.func.now()) updated_at = sqlalchemy.Column(sqlalchemy.DateTime, nullable=False, server_default=sqlalchemy.func.now(), onupdate=sqlalchemy.func.now()) diff --git a/pkg/entity/persistence/pipeline.py b/pkg/entity/persistence/pipeline.py index 369cd57a..aaa393a7 100644 --- a/pkg/entity/persistence/pipeline.py +++ b/pkg/entity/persistence/pipeline.py @@ -13,6 +13,7 @@ class LegacyPipeline(Base): created_at = sqlalchemy.Column(sqlalchemy.DateTime, nullable=False, server_default=sqlalchemy.func.now()) updated_at = sqlalchemy.Column(sqlalchemy.DateTime, nullable=False, server_default=sqlalchemy.func.now(), onupdate=sqlalchemy.func.now()) for_version = sqlalchemy.Column(sqlalchemy.String(255), nullable=False) + is_default = sqlalchemy.Column(sqlalchemy.Boolean, nullable=False, default=False) stages = sqlalchemy.Column(sqlalchemy.JSON, nullable=False) config = sqlalchemy.Column(sqlalchemy.JSON, nullable=False) diff --git a/pkg/persistence/mgr.py b/pkg/persistence/mgr.py index dcf19f5a..39f4f432 100644 --- a/pkg/persistence/mgr.py +++ b/pkg/persistence/mgr.py @@ -3,6 +3,8 @@ from __future__ import annotations import asyncio import datetime import typing +import json +import uuid import sqlalchemy.ext.asyncio as sqlalchemy_asyncio import sqlalchemy @@ -13,6 +15,7 @@ from ..core import app from .databases import sqlite from ..utils import constants from .migrations import dbm001_migrate_v3_config +from ..api.http.service import pipeline as pipeline_service class PersistenceManager: @@ -47,7 +50,10 @@ class PersistenceManager: await conn.commit() + # ======= write initial data ======= + # write initial metadata + self.ap.logger.info("Creating initial metadata...") for item in metadata.initial_metadata: # check if the item exists result = await self.execute_async( @@ -58,6 +64,30 @@ class PersistenceManager: await self.execute_async( sqlalchemy.insert(metadata.Metadata).values(item) ) + + # write default pipeline + result = await self.execute_async( + sqlalchemy.select(pipeline.LegacyPipeline) + ) + if result.first() is None: + self.ap.logger.info("Creating default pipeline...") + + pipeline_config = json.load(open('templates/default-pipeline-config.json')) + + pipeline_data = { + 'uuid': str(uuid.uuid4()), + 'for_version': self.ap.ver_mgr.get_current_version(), + 'stages': pipeline_service.default_stage_order, + 'is_default': True, + 'name': 'Chat Pipeline', + 'description': '默认对话配置流水线', + 'config': pipeline_config, + } + + await self.execute_async( + sqlalchemy.insert(pipeline.LegacyPipeline).values(pipeline_data) + ) + # ================================= # run migrations database_version = await self.execute_async( diff --git a/pkg/plugin/context.py b/pkg/plugin/context.py index 1672b1ae..fc06b9ec 100644 --- a/pkg/plugin/context.py +++ b/pkg/plugin/context.py @@ -366,27 +366,6 @@ class RuntimeContainer(pydantic.BaseModel): class Config: arbitrary_types_allowed = True - def to_setting_dict(self): - return { - 'name': self.plugin_name, - 'description': self.plugin_description.to_dict(), - 'version': self.plugin_version, - 'author': self.plugin_author, - 'source': self.plugin_repository, - 'main_file': self.main_file, - 'pkg_path': self.pkg_path, - 'priority': self.priority, - 'enabled': self.enabled, - } - - def set_from_setting_dict( - self, - setting: dict - ): - self.plugin_repository = setting['source'] - self.priority = setting['priority'] - self.enabled = setting['enabled'] - def model_dump(self, *args, **kwargs): return { 'name': self.plugin_name, diff --git a/templates/default-pipeline-config.json b/templates/default-pipeline-config.json new file mode 100644 index 00000000..ead811be --- /dev/null +++ b/templates/default-pipeline-config.json @@ -0,0 +1,77 @@ +{ + "trigger": { + "group-respond-rules": { + "at": true, + "prefix": [ + "ai" + ], + "regexp": [], + "random": 0.0 + }, + "access-control": { + "mode": "blacklist", + "blacklist": [], + "whitelist": [] + }, + "ignore-rules": { + "prefix": [], + "regexp": [] + } + }, + "safety": { + "content-filter": { + "scope": "all", + "check-sensitive-words": true + }, + "rate-limit": { + "window-length": 60, + "limitation": 60, + "strategy": "drop" + } + }, + "ai": { + "runner": { + "runner": "local-agent" + }, + "local-agent": { + "model": "", + "max-round": 10, + "prompt": [ + { + "role": "system", + "content": "You are a helpful assistant." + } + ] + }, + "dify-service-api": { + "base-url": "https://api.dify.ai/v1", + "app-type": "chat", + "api-key": "your-api-key", + "thinking-convert": "plain", + "timeout": 30 + }, + "dashscope-app-api": { + "app-type": "agent", + "api-key": "your-api-key", + "app-id": "your-app-id", + "references-quote": "参考资料来自:" + } + }, + "output": { + "long-text-processing": { + "threshold": 1000, + "strategy": "forward", + "font-path": "" + }, + "force-delay": { + "min": 0, + "max": 0 + }, + "misc": { + "hide-exception": true, + "at-sender": true, + "quote-origin": true, + "track-function-calls": false + } + } +} \ No newline at end of file