mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-08 14:56:03 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0623f4009a | ||
|
|
06adeb72c4 | ||
|
|
ef044f4fc7 | ||
|
|
7cd4e904ca | ||
|
|
c724494ee7 |
26
pkg/core/migrations/m033_dify_thinking_config.py
Normal file
26
pkg/core/migrations/m033_dify_thinking_config.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from .. import migration
|
||||||
|
|
||||||
|
|
||||||
|
@migration.migration_class("dify-thinking-config", 33)
|
||||||
|
class DifyThinkingConfigMigration(migration.Migration):
|
||||||
|
"""迁移"""
|
||||||
|
|
||||||
|
async def need_migrate(self) -> bool:
|
||||||
|
"""判断当前环境是否需要运行此迁移"""
|
||||||
|
|
||||||
|
if 'options' not in self.ap.provider_cfg.data["dify-service-api"]:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if 'convert-thinking-tips' not in self.ap.provider_cfg.data["dify-service-api"]["options"]:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
async def run(self):
|
||||||
|
"""执行迁移"""
|
||||||
|
self.ap.provider_cfg.data["dify-service-api"]["options"] = {
|
||||||
|
"convert-thinking-tips": "plain"
|
||||||
|
}
|
||||||
|
await self.ap.provider_cfg.dump_config()
|
||||||
@@ -11,7 +11,7 @@ from ..migrations import m015_gitee_ai_config, m016_dify_service_api, m017_dify_
|
|||||||
from ..migrations import m020_wecom_config, m021_lark_config, m022_lmstudio_config, m023_siliconflow_config, m024_discord_config, m025_gewechat_config
|
from ..migrations import m020_wecom_config, m021_lark_config, m022_lmstudio_config, m023_siliconflow_config, m024_discord_config, m025_gewechat_config
|
||||||
from ..migrations import m026_qqofficial_config, m027_wx_official_account_config, m028_aliyun_requester_config
|
from ..migrations import m026_qqofficial_config, m027_wx_official_account_config, m028_aliyun_requester_config
|
||||||
from ..migrations import m029_dashscope_app_api_config, m030_lark_config_cmpl, m031_dingtalk_config, m032_volcark_config
|
from ..migrations import m029_dashscope_app_api_config, m030_lark_config_cmpl, m031_dingtalk_config, m032_volcark_config
|
||||||
|
from ..migrations import m033_dify_thinking_config
|
||||||
|
|
||||||
@stage.stage_class("MigrationStage")
|
@stage.stage_class("MigrationStage")
|
||||||
class MigrationStage(stage.BootingStage):
|
class MigrationStage(stage.BootingStage):
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ class ComponentDiscoveryEngine:
|
|||||||
|
|
||||||
def load_component_manifest(self, path: str, owner: str = 'builtin', no_save: bool = False) -> Component:
|
def load_component_manifest(self, path: str, owner: str = 'builtin', no_save: bool = False) -> Component:
|
||||||
"""加载组件清单"""
|
"""加载组件清单"""
|
||||||
with open(path, 'r') as f:
|
with open(path, 'r', encoding='utf-8') as f:
|
||||||
manifest = yaml.safe_load(f)
|
manifest = yaml.safe_load(f)
|
||||||
comp = Component(
|
comp = Component(
|
||||||
owner=owner,
|
owner=owner,
|
||||||
|
|||||||
@@ -49,70 +49,7 @@ class OpenAIChatCompletions(requester.LLMAPIRequester):
|
|||||||
self,
|
self,
|
||||||
args: dict,
|
args: dict,
|
||||||
) -> chat_completion.ChatCompletion:
|
) -> chat_completion.ChatCompletion:
|
||||||
args["stream"] = True
|
return await self.client.chat.completions.create(**args)
|
||||||
|
|
||||||
chunk = None
|
|
||||||
|
|
||||||
pending_content = ""
|
|
||||||
|
|
||||||
tool_calls = []
|
|
||||||
|
|
||||||
resp_gen: openai.AsyncStream = await self.client.chat.completions.create(**args)
|
|
||||||
|
|
||||||
async for chunk in resp_gen:
|
|
||||||
# print(chunk)
|
|
||||||
if not chunk or not chunk.id or not chunk.choices or not chunk.choices[0] or not chunk.choices[0].delta:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if chunk.choices[0].delta.content is not None:
|
|
||||||
pending_content += chunk.choices[0].delta.content
|
|
||||||
|
|
||||||
if chunk.choices[0].delta.tool_calls is not None:
|
|
||||||
for tool_call in chunk.choices[0].delta.tool_calls:
|
|
||||||
for tc in tool_calls:
|
|
||||||
if tc.index == tool_call.index:
|
|
||||||
tc.function.arguments += tool_call.function.arguments
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
tool_calls.append(tool_call)
|
|
||||||
|
|
||||||
if chunk.choices[0].finish_reason is not None:
|
|
||||||
break
|
|
||||||
|
|
||||||
real_tool_calls = []
|
|
||||||
|
|
||||||
for tc in tool_calls:
|
|
||||||
function = chat_completion_message_tool_call.Function(
|
|
||||||
name=tc.function.name,
|
|
||||||
arguments=tc.function.arguments
|
|
||||||
)
|
|
||||||
real_tool_calls.append(chat_completion_message_tool_call.ChatCompletionMessageToolCall(
|
|
||||||
id=tc.id,
|
|
||||||
function=function,
|
|
||||||
type="function"
|
|
||||||
))
|
|
||||||
|
|
||||||
return chat_completion.ChatCompletion(
|
|
||||||
id=chunk.id,
|
|
||||||
object="chat.completion",
|
|
||||||
created=chunk.created,
|
|
||||||
choices=[
|
|
||||||
chat_completion.Choice(
|
|
||||||
index=0,
|
|
||||||
message=chat_completion.ChatCompletionMessage(
|
|
||||||
role="assistant",
|
|
||||||
content=pending_content,
|
|
||||||
tool_calls=real_tool_calls if len(real_tool_calls) > 0 else None
|
|
||||||
),
|
|
||||||
finish_reason=chunk.choices[0].finish_reason if hasattr(chunk.choices[0], 'finish_reason') and chunk.choices[0].finish_reason is not None else 'stop',
|
|
||||||
logprobs=chunk.choices[0].logprobs,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
model=chunk.model,
|
|
||||||
service_tier=chunk.service_tier if hasattr(chunk, 'service_tier') else None,
|
|
||||||
system_fingerprint=chunk.system_fingerprint if hasattr(chunk, 'system_fingerprint') else None,
|
|
||||||
usage=chunk.usage if hasattr(chunk, 'usage') else None
|
|
||||||
) if chunk else None
|
|
||||||
|
|
||||||
async def _make_msg(
|
async def _make_msg(
|
||||||
self,
|
self,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|||||||
import typing
|
import typing
|
||||||
import json
|
import json
|
||||||
import uuid
|
import uuid
|
||||||
|
import re
|
||||||
import base64
|
import base64
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
@@ -41,6 +42,23 @@ class DifyServiceAPIRunner(runner.RequestRunner):
|
|||||||
base_url=self.ap.provider_cfg.data["dify-service-api"]["base-url"],
|
base_url=self.ap.provider_cfg.data["dify-service-api"]["base-url"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _try_convert_thinking(self, resp_text: str) -> str:
|
||||||
|
"""尝试转换 Dify 的思考提示"""
|
||||||
|
if not resp_text.startswith("<details style=\"color:gray;background-color: #f8f8f8;padding: 8px;border-radius: 4px;\" open> <summary> Thinking... </summary>"):
|
||||||
|
return resp_text
|
||||||
|
|
||||||
|
if self.ap.provider_cfg.data["dify-service-api"]["options"]["convert-thinking-tips"] == "original":
|
||||||
|
return resp_text
|
||||||
|
|
||||||
|
if self.ap.provider_cfg.data["dify-service-api"]["options"]["convert-thinking-tips"] == "remove":
|
||||||
|
return re.sub(r'<details style="color:gray;background-color: #f8f8f8;padding: 8px;border-radius: 4px;" open> <summary> Thinking... </summary>.*?</details>', '', resp_text, flags=re.DOTALL)
|
||||||
|
|
||||||
|
if self.ap.provider_cfg.data["dify-service-api"]["options"]["convert-thinking-tips"] == "plain":
|
||||||
|
pattern = r'<details style="color:gray;background-color: #f8f8f8;padding: 8px;border-radius: 4px;" open> <summary> Thinking... </summary>(.*?)</details>'
|
||||||
|
thinking_text = re.search(pattern, resp_text, flags=re.DOTALL)
|
||||||
|
content_text = re.sub(pattern, '', resp_text, flags=re.DOTALL)
|
||||||
|
return f"<think>{thinking_text.group(1)}</think>\n{content_text}"
|
||||||
|
|
||||||
async def _preprocess_user_message(
|
async def _preprocess_user_message(
|
||||||
self, query: core_entities.Query
|
self, query: core_entities.Query
|
||||||
) -> tuple[str, list[str]]:
|
) -> tuple[str, list[str]]:
|
||||||
@@ -109,7 +127,7 @@ class DifyServiceAPIRunner(runner.RequestRunner):
|
|||||||
if chunk['data']['node_type'] == 'answer':
|
if chunk['data']['node_type'] == 'answer':
|
||||||
yield llm_entities.Message(
|
yield llm_entities.Message(
|
||||||
role="assistant",
|
role="assistant",
|
||||||
content=chunk['data']['outputs']['answer'],
|
content=self._try_convert_thinking(chunk['data']['outputs']['answer']),
|
||||||
)
|
)
|
||||||
elif mode == "basic":
|
elif mode == "basic":
|
||||||
if chunk['event'] == 'message':
|
if chunk['event'] == 'message':
|
||||||
@@ -117,7 +135,7 @@ class DifyServiceAPIRunner(runner.RequestRunner):
|
|||||||
elif chunk['event'] == 'message_end':
|
elif chunk['event'] == 'message_end':
|
||||||
yield llm_entities.Message(
|
yield llm_entities.Message(
|
||||||
role="assistant",
|
role="assistant",
|
||||||
content=basic_mode_pending_chunk,
|
content=self._try_convert_thinking(basic_mode_pending_chunk),
|
||||||
)
|
)
|
||||||
basic_mode_pending_chunk = ''
|
basic_mode_pending_chunk = ''
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
semantic_version = "v3.4.9"
|
semantic_version = "v3.4.9.2"
|
||||||
|
|
||||||
debug_mode = False
|
debug_mode = False
|
||||||
|
|
||||||
|
|||||||
@@ -106,6 +106,9 @@
|
|||||||
"dify-service-api": {
|
"dify-service-api": {
|
||||||
"base-url": "https://api.dify.ai/v1",
|
"base-url": "https://api.dify.ai/v1",
|
||||||
"app-type": "chat",
|
"app-type": "chat",
|
||||||
|
"options": {
|
||||||
|
"convert-thinking-tips": "plain"
|
||||||
|
},
|
||||||
"chat": {
|
"chat": {
|
||||||
"api-key": "app-1234567890",
|
"api-key": "app-1234567890",
|
||||||
"timeout": 120
|
"timeout": 120
|
||||||
|
|||||||
@@ -404,6 +404,20 @@
|
|||||||
"enum": ["chat", "workflow", "agent"],
|
"enum": ["chat", "workflow", "agent"],
|
||||||
"default": "chat"
|
"default": "chat"
|
||||||
},
|
},
|
||||||
|
"options": {
|
||||||
|
"type": "object",
|
||||||
|
"title": "Dify Service API 配置选项",
|
||||||
|
"properties": {
|
||||||
|
"convert-thinking-tips": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "转换思考提示",
|
||||||
|
"description": "设置转换思考提示。值为 original 时,不转换思考提示;值为 plain 时,将思考提示转换为类似 DeepSeek 官方的<think>...</think>格式;值为 remove 时,删除思考提示",
|
||||||
|
"enum": ["original", "plain", "remove"],
|
||||||
|
"default": "plain"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
"chat": {
|
"chat": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"title": "聊天助手 API 参数",
|
"title": "聊天助手 API 参数",
|
||||||
|
|||||||
Reference in New Issue
Block a user