- Added `inactive_plugins` field to `RunnerReadyPayload` and `ReloadPluginResultPayload` to track plugins that are not activated due to being disabled or unmet dependencies. - Introduced `InspectPluginConfigPayload` and `InspectPluginConfigResultPayload` for inspecting plugin configuration metadata. - Implemented `PluginActivationStatus` enum to better represent plugin activation states. - Updated `_activate_plugin` method to return activation status and handle inactive plugins accordingly. - Added hooks for send service to allow modification of messages before and after sending. - Created new runtime routes for listing hook specifications in the WebUI. - Refactored plugin configuration handling to utilize runtime inspection for better accuracy and flexibility. - Enhanced error handling and logging for plugin configuration operations.
179 lines
5.6 KiB
Python
179 lines
5.6 KiB
Python
"""运行时 Hook 载荷序列化辅助。"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, Dict, List, Sequence
|
|
|
|
from src.chat.message_receive.message import SessionMessage
|
|
from src.common.data_models.llm_service_data_models import PromptMessage
|
|
from src.llm_models.payload_content.message import Message
|
|
from src.llm_models.payload_content.tool_option import ToolCall, ToolDefinitionInput, normalize_tool_options
|
|
from src.plugin_runtime.host.message_utils import PluginMessageUtils
|
|
|
|
|
|
def serialize_session_message(message: SessionMessage) -> Dict[str, Any]:
|
|
"""将会话消息序列化为 Hook 可传输载荷。
|
|
|
|
Args:
|
|
message: 待序列化的会话消息。
|
|
|
|
Returns:
|
|
Dict[str, Any]: 可通过插件运行时传输的消息字典。
|
|
"""
|
|
|
|
return dict(PluginMessageUtils._session_message_to_dict(message))
|
|
|
|
|
|
def deserialize_session_message(raw_message: Any) -> SessionMessage:
|
|
"""从 Hook 载荷恢复会话消息。
|
|
|
|
Args:
|
|
raw_message: Hook 返回的消息字典。
|
|
|
|
Returns:
|
|
SessionMessage: 恢复后的会话消息对象。
|
|
|
|
Raises:
|
|
ValueError: 消息结构不合法时抛出。
|
|
"""
|
|
|
|
if not isinstance(raw_message, dict):
|
|
raise ValueError("Hook 返回的 `message` 必须是字典")
|
|
return PluginMessageUtils._build_session_message_from_dict(raw_message)
|
|
|
|
|
|
def serialize_tool_calls(tool_calls: Sequence[ToolCall] | None) -> List[Dict[str, Any]]:
|
|
"""将工具调用列表序列化为 Hook 可传输载荷。
|
|
|
|
Args:
|
|
tool_calls: 原始工具调用列表。
|
|
|
|
Returns:
|
|
List[Dict[str, Any]]: 序列化后的工具调用列表。
|
|
"""
|
|
|
|
if not tool_calls:
|
|
return []
|
|
|
|
return [
|
|
{
|
|
"id": tool_call.call_id,
|
|
"function": {
|
|
"name": tool_call.func_name,
|
|
"arguments": dict(tool_call.args or {}),
|
|
},
|
|
}
|
|
for tool_call in tool_calls
|
|
]
|
|
|
|
|
|
def deserialize_tool_calls(raw_tool_calls: Any) -> List[ToolCall]:
|
|
"""从 Hook 载荷恢复工具调用列表。
|
|
|
|
Args:
|
|
raw_tool_calls: Hook 返回的工具调用列表。
|
|
|
|
Returns:
|
|
List[ToolCall]: 恢复后的工具调用列表。
|
|
|
|
Raises:
|
|
ValueError: 结构不合法时抛出。
|
|
"""
|
|
|
|
if raw_tool_calls in (None, []):
|
|
return []
|
|
if not isinstance(raw_tool_calls, list):
|
|
raise ValueError("Hook 返回的 `tool_calls` 必须是列表")
|
|
|
|
normalized_tool_calls: List[ToolCall] = []
|
|
for raw_tool_call in raw_tool_calls:
|
|
if not isinstance(raw_tool_call, dict):
|
|
raise ValueError("Hook 返回的工具调用项必须是字典")
|
|
|
|
function_info = raw_tool_call.get("function", {})
|
|
if isinstance(function_info, dict):
|
|
function_name = function_info.get("name")
|
|
function_arguments = function_info.get("arguments")
|
|
else:
|
|
function_name = raw_tool_call.get("name")
|
|
function_arguments = raw_tool_call.get("arguments")
|
|
|
|
call_id = raw_tool_call.get("id") or raw_tool_call.get("call_id")
|
|
if not isinstance(call_id, str) or not isinstance(function_name, str):
|
|
raise ValueError("Hook 返回的工具调用缺少 `id` 或函数名称")
|
|
|
|
normalized_tool_calls.append(
|
|
ToolCall(
|
|
call_id=call_id,
|
|
func_name=function_name,
|
|
args=function_arguments if isinstance(function_arguments, dict) else {},
|
|
)
|
|
)
|
|
return normalized_tool_calls
|
|
|
|
|
|
def serialize_prompt_messages(messages: Sequence[Message]) -> List[PromptMessage]:
|
|
"""将 LLM 消息列表序列化为 Hook 可传输载荷。
|
|
|
|
Args:
|
|
messages: 原始 LLM 消息列表。
|
|
|
|
Returns:
|
|
List[PromptMessage]: 序列化后的消息字典列表。
|
|
"""
|
|
|
|
serialized_messages: List[PromptMessage] = []
|
|
for message in messages:
|
|
serialized_message: PromptMessage = {
|
|
"role": message.role.value,
|
|
"content": message.content,
|
|
}
|
|
if message.tool_call_id:
|
|
serialized_message["tool_call_id"] = message.tool_call_id
|
|
if message.tool_calls:
|
|
serialized_message["tool_calls"] = serialize_tool_calls(message.tool_calls)
|
|
serialized_messages.append(serialized_message)
|
|
return serialized_messages
|
|
|
|
|
|
def deserialize_prompt_messages(raw_messages: Any) -> List[Message]:
|
|
"""从 Hook 载荷恢复 LLM 消息列表。
|
|
|
|
Args:
|
|
raw_messages: Hook 返回的消息列表。
|
|
|
|
Returns:
|
|
List[Message]: 恢复后的 LLM 消息列表。
|
|
|
|
Raises:
|
|
ValueError: 结构不合法时抛出。
|
|
"""
|
|
|
|
if not isinstance(raw_messages, list):
|
|
raise ValueError("Hook 返回的 `messages` 必须是列表")
|
|
|
|
from src.services.llm_service import _build_message_from_dict
|
|
|
|
normalized_messages: List[Message] = []
|
|
for raw_message in raw_messages:
|
|
if not isinstance(raw_message, dict):
|
|
raise ValueError("Hook 返回的消息项必须是字典")
|
|
normalized_messages.append(_build_message_from_dict(raw_message))
|
|
return normalized_messages
|
|
|
|
|
|
def serialize_tool_definitions(tool_definitions: Sequence[ToolDefinitionInput]) -> List[Dict[str, Any]]:
|
|
"""将工具定义列表序列化为 Hook 可传输载荷。
|
|
|
|
Args:
|
|
tool_definitions: 原始工具定义列表。
|
|
|
|
Returns:
|
|
List[Dict[str, Any]]: 序列化后的工具定义列表。
|
|
"""
|
|
|
|
normalized_tool_options = normalize_tool_options(list(tool_definitions))
|
|
if not normalized_tool_options:
|
|
return []
|
|
return [tool_option.to_openai_function_schema() for tool_option in normalized_tool_options]
|