diff --git a/src/chat/emoji_system/maisaka_tool.py b/src/chat/emoji_system/maisaka_tool.py index 982a9473..44508d96 100644 --- a/src/chat/emoji_system/maisaka_tool.py +++ b/src/chat/emoji_system/maisaka_tool.py @@ -1,5 +1,6 @@ """Maisaka 表情工具内置能力。""" +from collections.abc import Awaitable, Callable from dataclasses import dataclass, field from typing import Any, Optional, Sequence @@ -17,6 +18,11 @@ from .emoji_manager import _serialize_emoji_for_hook, emoji_manager, emoji_manag logger = get_logger("emoji_maisaka_tool") +EmojiSelector = Callable[ + [str, str, Sequence[str] | None, int], + Awaitable[tuple[MaiEmoji | None, str]], +] + @dataclass(slots=True) class MaisakaEmojiSendResult: @@ -198,13 +204,14 @@ async def send_emoji_for_maisaka( requested_emotion: str = "", reasoning: str = "", context_texts: Sequence[str] | None = None, + emoji_selector: EmojiSelector | None = None, ) -> MaisakaEmojiSendResult: """为 Maisaka 选择并发送一个表情。""" normalized_requested_emotion = requested_emotion.strip() normalized_reasoning = reasoning.strip() normalized_context_texts = _normalize_context_texts(context_texts) - sample_size = 30 + sample_size = 20 before_select_result = await _get_runtime_manager().invoke_hook( "emoji.maisaka.before_select", @@ -232,12 +239,20 @@ async def send_emoji_for_maisaka( normalized_context_texts = _normalize_context_texts(before_select_kwargs.get("context_texts")) sample_size = _coerce_positive_int(before_select_kwargs.get("sample_size"), sample_size) - selected_emoji, matched_emotion = await select_emoji_for_maisaka( - requested_emotion=normalized_requested_emotion, - reasoning=normalized_reasoning, - context_texts=normalized_context_texts, - sample_size=sample_size, - ) + if emoji_selector is None: + selected_emoji, matched_emotion = await select_emoji_for_maisaka( + requested_emotion=normalized_requested_emotion, + reasoning=normalized_reasoning, + context_texts=normalized_context_texts, + sample_size=sample_size, + ) + else: + selected_emoji, matched_emotion = await emoji_selector( + normalized_requested_emotion, + normalized_reasoning, + normalized_context_texts, + sample_size, + ) after_select_result = await _get_runtime_manager().invoke_hook( "emoji.maisaka.after_select", stream_id=stream_id, diff --git a/src/chat/replyer/maisaka_generator.py b/src/chat/replyer/maisaka_generator.py index 7c5a5670..c96b8848 100644 --- a/src/chat/replyer/maisaka_generator.py +++ b/src/chat/replyer/maisaka_generator.py @@ -151,7 +151,7 @@ class MaisakaReplyGenerator: content = self._normalize_content(content_body) if not content: continue - visible_speaker = speaker_name or global_config.maisaka.user_name.strip() or "User" + visible_speaker = speaker_name or global_config.maisaka.cli_user_name.strip() or "User" parts.append(f"{timestamp} {visible_speaker}: {content}") continue diff --git a/src/chat/replyer/maisaka_generator_multi.py b/src/chat/replyer/maisaka_generator_multi.py index dd705cd7..44f95ee8 100644 --- a/src/chat/replyer/maisaka_generator_multi.py +++ b/src/chat/replyer/maisaka_generator_multi.py @@ -162,7 +162,7 @@ class MaisakaReplyGenerator: def _build_history_messages(self, chat_history: List[LLMContextMessage]) -> List[Message]: """将 replyer 上下文拆成多条 LLM 消息。""" bot_nickname = global_config.bot.nickname.strip() or "Bot" - default_user_name = global_config.maisaka.user_name.strip() or "User" + default_user_name = global_config.maisaka.cli_user_name.strip() or "User" messages: List[Message] = [] for message in chat_history: diff --git a/src/cli/maisaka_cli.py b/src/cli/maisaka_cli.py index d2e33323..6206bfec 100644 --- a/src/cli/maisaka_cli.py +++ b/src/cli/maisaka_cli.py @@ -67,7 +67,7 @@ class BufferCLI: timestamp=timestamp, platform=BufferCLI._CLI_PLATFORM, ) - user_name = global_config.maisaka.user_name.strip() or "用户" + user_name = global_config.maisaka.cli_user_name.strip() or "用户" message.message_info = MessageInfo( user_info=UserInfo( user_id=BufferCLI._CLI_USER_ID, diff --git a/src/config/official_configs.py b/src/config/official_configs.py index 59f00cff..278406e1 100644 --- a/src/config/official_configs.py +++ b/src/config/official_configs.py @@ -282,7 +282,24 @@ class ChatConfig(ConfigBase): "x-icon": "list", }, ) - """_wrap_为指定聊天添加额外的 prompt 配置列表""" + + direct_image_input: bool = Field( + default=True, + json_schema_extra={ + "x-widget": "switch", + "x-icon": "image", + }, + ) + """是否直接输入图片""" + + replyer_generator_type: Literal["legacy", "multi"] = Field( + default="legacy", + json_schema_extra={ + "x-widget": "select", + "x-icon": "git-branch", + }, + ) + """Maisaka replyer 生成器类型:legacy(旧版单 prompt)/ multi(多消息版)""" enable_talk_value_rules: bool = Field( default=True, @@ -964,6 +981,14 @@ class DebugConfig(ConfigBase): "x-icon": "brain", }, ) + + show_maisaka_thinking: bool = Field( + default=True, + json_schema_extra={ + "x-widget": "switch", + "x-icon": "brain", + }, + ) """是否显示回复器推理""" show_jargon_prompt: bool = Field( @@ -1427,16 +1452,7 @@ class MaiSakaConfig(ConfigBase): }, ) """启用知识库模块""" - show_thinking: bool = Field( - default=True, - json_schema_extra={ - "x-widget": "switch", - "x-icon": "brain", - }, - ) - """是否显示MaiSaka思考过程""" - - user_name: str = Field( + cli_user_name: str = Field( default="用户", json_schema_extra={ "x-widget": "input", @@ -1445,33 +1461,6 @@ class MaiSakaConfig(ConfigBase): ) """MaiSaka 使用的用户名称""" - direct_image_input: bool = Field( - default=True, - json_schema_extra={ - "x-widget": "switch", - "x-icon": "image", - }, - ) - """是否直接输入图片""" - - merge_user_messages: bool = Field( - default=True, - json_schema_extra={ - "x-widget": "switch", - "x-icon": "merge", - }, - ) - """是否将新接收的用户发言合并为单个用户消息""" - - replyer_generator_type: Literal["legacy", "multi"] = Field( - default="legacy", - json_schema_extra={ - "x-widget": "select", - "x-icon": "git-branch", - }, - ) - """Maisaka replyer 生成器类型:legacy(旧版单 prompt)/ multi(多消息版)""" - max_internal_rounds: int = Field( default=6, ge=1, @@ -1511,14 +1500,14 @@ class MaiSakaConfig(ConfigBase): ) """工具筛选阶段最多保留的非内置工具数量""" - terminal_image_display_mode: Literal["legacy", "path_link"] = Field( - default="legacy", + show_image_path: bool = Field( + default=True, json_schema_extra={ - "x-widget": "select", + "x-widget": "switch", "x-icon": "image", }, ) - """图片展示模式:legacy(仅显示元信息)/ path_link(可点击本地路径)""" + """是否显示图片本地路径""" class MCPAuthorizationConfig(ConfigBase): diff --git a/src/maisaka/builtin_tool/send_emoji.py b/src/maisaka/builtin_tool/send_emoji.py index 66fb32d2..bbd9563f 100644 --- a/src/maisaka/builtin_tool/send_emoji.py +++ b/src/maisaka/builtin_tool/send_emoji.py @@ -1,16 +1,40 @@ """send_emoji 内置工具。""" +from datetime import datetime +from random import sample +from secrets import token_hex from typing import Any, Dict, Optional +import asyncio + +from pydantic import BaseModel, Field as PydanticField + +from src.chat.emoji_system.emoji_manager import emoji_manager from src.chat.emoji_system.maisaka_tool import send_emoji_for_maisaka +from src.common.data_models.message_component_data_model import ImageComponent, MessageSequence, TextComponent +from src.common.data_models.image_data_model import MaiEmoji from src.common.logger import get_logger from src.core.tooling import ToolExecutionContext, ToolExecutionResult, ToolInvocation, ToolSpec -from src.maisaka.context_messages import LLMContextMessage +from src.llm_models.payload_content.resp_format import RespFormat, RespFormatType +from src.maisaka.context_messages import LLMContextMessage, ReferenceMessage, ReferenceMessageType, SessionBackedMessage from .context import BuiltinToolRuntimeContext logger = get_logger("maisaka_builtin_send_emoji") +_EMOJI_SUB_AGENT_CONTEXT_LIMIT = 12 +_EMOJI_SUB_AGENT_MAX_TOKENS = 240 +_EMOJI_SUB_AGENT_SAMPLE_SIZE = 20 +_EMOJI_SUCCESS_MESSAGE = "???????" + + +class EmojiSelectionResult(BaseModel): + """表情包子代理的结构化选择结果。""" + + emoji_id: str = PydanticField(default="", description="选中的候选表情包 ID。") + matched_emotion: str = PydanticField(default="", description="本次命中的情绪标签,可为空。") + reason: str = PydanticField(default="", description="简短选择理由。") + def get_tool_spec() -> ToolSpec: """获取 send_emoji 工具声明。""" @@ -33,6 +57,105 @@ def get_tool_spec() -> ToolSpec: ) +async def _build_emoji_candidate_message(emoji: MaiEmoji, candidate_id: str) -> SessionBackedMessage: + """构建供子代理挑选的图片候选消息。""" + + image_bytes = await asyncio.to_thread(emoji.full_path.read_bytes) + raw_message = MessageSequence( + [ + TextComponent(f"ID: {candidate_id}"), + ImageComponent(binary_hash=str(emoji.file_hash or ""), binary_data=image_bytes), + ] + ) + return SessionBackedMessage( + raw_message=raw_message, + visible_text=f"ID: {candidate_id}", + timestamp=datetime.now(), + source_kind="emoji_candidate", + ) + + +async def _select_emoji_with_sub_agent( + tool_ctx: BuiltinToolRuntimeContext, + requested_emotion: str, + reasoning: str, + context_texts: list[str], + sample_size: int, +) -> tuple[MaiEmoji | None, str]: + """通过临时子代理从候选表情包中选出一个结果。""" + + available_emojis = list(emoji_manager.emojis) + if not available_emojis: + return None, "" + + effective_sample_size = min(max(sample_size, 1), _EMOJI_SUB_AGENT_SAMPLE_SIZE, len(available_emojis)) + sampled_emojis = sample(available_emojis, effective_sample_size) + + candidate_map: dict[str, MaiEmoji] = {} + candidate_messages: list[LLMContextMessage] = [] + for emoji in sampled_emojis: + candidate_id = token_hex(4) + while candidate_id in candidate_map: + candidate_id = token_hex(4) + candidate_map[candidate_id] = emoji + candidate_messages.append(await _build_emoji_candidate_message(emoji, candidate_id)) + + context_text = "\n".join(context_texts[-5:]) if context_texts else "(暂无额外上下文)" + system_prompt = ( + "你是 Maisaka 的临时表情包选择子代理。\n" + "你会收到一段群聊上下文,以及若干条候选表情包消息。每条候选消息里都有一个临时 ID。\n" + "你的任务是根据上下文、当前语气和发送意图,从候选里选出最合适的一个表情包。\n" + "必须只从候选消息中选择,不能编造新的 ID。\n" + "如果提供了 requested_emotion,请优先考虑与其接近的候选;如果没有完全匹配,则选择最符合上下文语气的候选。\n" + "你必须返回一个 JSON 对象(json object),不要输出任何 JSON 之外的内容。\n" + '返回格式固定为:{"emoji_id":"候选ID","matched_emotion":"情绪标签","reason":"简短理由"}' + ) + prompt_message = ReferenceMessage( + content=( + f"[选择任务]\n" + f"requested_emotion: {requested_emotion or '未指定'}\n" + f"reasoning: {reasoning or '辅助表达当前语气和情绪'}\n" + f"recent_context:\n{context_text}\n" + '请只输出 JSON。' + ), + timestamp=datetime.now(), + reference_type=ReferenceMessageType.TOOL_HINT, + remaining_uses_value=1, + display_prefix="[表情包选择任务]", + ) + + response = await tool_ctx.runtime.run_sub_agent( + context_message_limit=_EMOJI_SUB_AGENT_CONTEXT_LIMIT, + system_prompt=system_prompt, + extra_messages=[prompt_message, *candidate_messages], + max_tokens=_EMOJI_SUB_AGENT_MAX_TOKENS, + response_format=RespFormat( + format_type=RespFormatType.JSON_SCHEMA, + schema=EmojiSelectionResult, + ), + ) + + try: + selection = EmojiSelectionResult.model_validate_json(response.content or "") + except Exception as exc: + logger.warning(f"{tool_ctx.runtime.log_prefix} 表情包子代理结果解析失败,将回退到候选首项: {exc}") + fallback_emoji = sampled_emojis[0] if sampled_emojis else None + return fallback_emoji, requested_emotion + + selected_emoji = candidate_map.get(selection.emoji_id.strip()) + if selected_emoji is None: + logger.warning( + f"{tool_ctx.runtime.log_prefix} 表情包子代理返回了无效 ID: {selection.emoji_id!r},将回退到候选首项" + ) + fallback_emoji = sampled_emojis[0] if sampled_emojis else None + return fallback_emoji, requested_emotion + + matched_emotion = selection.matched_emotion.strip() + if not matched_emotion: + matched_emotion = requested_emotion.strip() + return selected_emoji, matched_emotion + + async def handle_tool( tool_ctx: BuiltinToolRuntimeContext, invocation: ToolInvocation, @@ -64,6 +187,13 @@ async def handle_tool( requested_emotion=emotion, reasoning=tool_ctx.engine.last_reasoning_content, context_texts=context_texts, + emoji_selector=lambda requested_emotion, reasoning, context_texts, sample_size: _select_emoji_with_sub_agent( + tool_ctx, + requested_emotion, + reasoning, + list(context_texts or []), + sample_size, + ), ) except Exception as exc: logger.exception(f"{tool_ctx.runtime.log_prefix} 发送表情包时发生异常: {exc}") @@ -74,28 +204,29 @@ async def handle_tool( structured_content=structured_result, ) - structured_result["description"] = send_result.description - structured_result["emotion"] = list(send_result.emotions) - structured_result["matched_emotion"] = send_result.matched_emotion - structured_result["message"] = send_result.message - if send_result.success: + structured_result["message"] = _EMOJI_SUCCESS_MESSAGE logger.info( - f"{tool_ctx.runtime.log_prefix} 表情包发送成功 " - f"描述={send_result.description!r} 情绪标签={send_result.emotions} " - f"请求情绪={emotion!r} 命中情绪={send_result.matched_emotion!r}" + f"{tool_ctx.runtime.log_prefix} ??????? " + f"??={send_result.description!r} ????={send_result.emotions} " + f"????={emotion!r} ????={send_result.matched_emotion!r}" ) tool_ctx.append_sent_emoji_to_chat_history( emoji_base64=send_result.emoji_base64, - success_message=send_result.message, + success_message=_EMOJI_SUCCESS_MESSAGE, ) structured_result["success"] = True return tool_ctx.build_success_result( invocation.tool_name, - send_result.message, + _EMOJI_SUCCESS_MESSAGE, structured_content=structured_result, ) + structured_result["description"] = send_result.description + structured_result["emotion"] = list(send_result.emotions) + structured_result["matched_emotion"] = send_result.matched_emotion + structured_result["message"] = send_result.message + logger.warning( f"{tool_ctx.runtime.log_prefix} 表情包发送失败 " f"请求情绪={emotion!r} 错误信息={send_result.message}" diff --git a/src/maisaka/chat_loop_service.py b/src/maisaka/chat_loop_service.py index 93dba8b5..9e5aba95 100644 --- a/src/maisaka/chat_loop_service.py +++ b/src/maisaka/chat_loop_service.py @@ -210,7 +210,7 @@ class MaisakaChatLoopService: self._extra_tools: List[ToolOption] = [] self._interrupt_flag: asyncio.Event | None = None self._tool_registry: ToolRegistry | None = None - self._prompts_loaded = False + self._prompts_loaded = chat_system_prompt is not None self._prompt_load_lock = asyncio.Lock() self._personality_prompt = self._build_personality_prompt() if chat_system_prompt is None: @@ -392,7 +392,12 @@ class MaisakaChatLoopService: """设置当前 planner 请求使用的中断标记。""" self._interrupt_flag = interrupt_flag - def _build_request_messages(self, selected_history: List[LLMContextMessage]) -> List[Message]: + def _build_request_messages( + self, + selected_history: List[LLMContextMessage], + *, + system_prompt: Optional[str] = None, + ) -> List[Message]: """构造发给大模型的消息列表。 Args: @@ -404,7 +409,7 @@ class MaisakaChatLoopService: messages: List[Message] = [] system_msg = MessageBuilder().set_role(RoleType.System) - system_msg.add_text_content(self._chat_system_prompt) + system_msg.add_text_content(system_prompt if system_prompt is not None else self._chat_system_prompt) messages.append(system_msg.build()) for msg in selected_history: @@ -691,7 +696,13 @@ class MaisakaChatLoopService: return extract_category_ids_from_result(generation_result.response or "") - async def chat_loop_step(self, chat_history: List[LLMContextMessage]) -> ChatResponse: + async def chat_loop_step( + self, + chat_history: List[LLMContextMessage], + *, + response_format: RespFormat | None = None, + tool_definitions: Sequence[ToolDefinitionInput] | None = None, + ) -> ChatResponse: """执行一轮 Maisaka 规划器请求。 Args: @@ -701,8 +712,9 @@ class MaisakaChatLoopService: ChatResponse: 本轮规划器返回结果。 """ - await self.ensure_chat_prompt_loaded() - selected_history, selection_reason = self._select_llm_context_messages(chat_history) + if not self._prompts_loaded: + await self.ensure_chat_prompt_loaded() + selected_history, selection_reason = self.select_llm_context_messages(chat_history) built_messages = self._build_request_messages(selected_history) def message_factory(_client: BaseClient) -> List[Message]: @@ -719,7 +731,9 @@ class MaisakaChatLoopService: return built_messages all_tools: List[ToolDefinitionInput] - if self._tool_registry is not None: + if tool_definitions is not None: + all_tools = list(tool_definitions) + elif self._tool_registry is not None: tool_specs = await self._tool_registry.list_tools() filtered_tool_specs = await self._filter_tool_specs_for_planner(selected_history, tool_specs) all_tools = [tool_spec.to_llm_definition() for tool_spec in filtered_tool_specs] @@ -748,10 +762,10 @@ class MaisakaChatLoopService: ordered_panels = PromptCLIVisualizer.build_prompt_panels( built_messages, - image_display_mode=global_config.maisaka.terminal_image_display_mode, + image_display_mode="path_link" if global_config.maisaka.show_image_path else "legacy", ) - if global_config.maisaka.show_thinking and ordered_panels: + if global_config.debug.show_maisaka_thinking and ordered_panels: console.print( Panel( Group(*ordered_panels), @@ -776,6 +790,7 @@ class MaisakaChatLoopService: tool_options=all_tools if all_tools else None, temperature=self._temperature, max_tokens=self._max_tokens, + response_format=response_format, interrupt_flag=self._interrupt_flag, ), ) @@ -837,6 +852,40 @@ class MaisakaChatLoopService: total_tokens=total_tokens, ) + @staticmethod + def select_llm_context_messages( + chat_history: List[LLMContextMessage], + *, + max_context_size: Optional[int] = None, + ) -> tuple[List[LLMContextMessage], str]: + """??????? LLM ???????""" + + effective_context_size = max(1, int(max_context_size or global_config.chat.max_context_size)) + selected_indices: List[int] = [] + counted_message_count = 0 + + for index in range(len(chat_history) - 1, -1, -1): + message = chat_history[index] + if message.to_llm_message() is None: + continue + + selected_indices.append(index) + if message.count_in_context: + counted_message_count += 1 + if counted_message_count >= effective_context_size: + break + + if not selected_indices: + return [], f"???????? {effective_context_size} ? user/assistant??? 0 ??" + + selected_indices.reverse() + selected_history = [chat_history[index] for index in selected_indices] + selected_history = MaisakaChatLoopService._drop_leading_orphan_tool_results(selected_history) + return ( + selected_history, + f"???????? {effective_context_size} ? user/assistant??????????? {len(selected_history)} ?", + ) + @staticmethod def _select_llm_context_messages(chat_history: List[LLMContextMessage]) -> tuple[List[LLMContextMessage], str]: """选择真正发送给 LLM 的上下文消息。 @@ -905,4 +954,4 @@ class MaisakaChatLoopService: if first_valid_index == 0: return selected_history - return selected_history[first_valid_index:] \ No newline at end of file + return selected_history[first_valid_index:] diff --git a/src/maisaka/reasoning_engine.py b/src/maisaka/reasoning_engine.py index 362e3e87..26436455 100644 --- a/src/maisaka/reasoning_engine.py +++ b/src/maisaka/reasoning_engine.py @@ -266,7 +266,7 @@ class MaisakaReasoningEngine: source_sequence = message.raw_message planner_components = clone_message_sequence(source_sequence).components - if global_config.maisaka.direct_image_input: + if global_config.chat.direct_image_input: await self._hydrate_visual_components(planner_components) if planner_components and isinstance(planner_components[0], TextComponent): planner_components[0].text = planner_prefix + planner_components[0].text @@ -610,16 +610,8 @@ class MaisakaReasoningEngine: return f"你尝试回复消息 {target_message_id or 'unknown'},但失败了:{error_text}" if invocation.tool_name == "send_emoji": - description = str(structured_content.get("description") or "").strip() - emotion_list = structured_content.get("emotion") - if isinstance(emotion_list, list): - emotion_text = "、".join(str(item).strip() for item in emotion_list if str(item).strip()) - else: - emotion_text = "" - if result.success and description: - if emotion_text: - return f"你发送了表情包:{description}(情绪:{emotion_text})" - return f"你发送了表情包:{description}" + if result.success: + return "你发送了表情包。" return f"你尝试发送表情包,但失败了:{self._truncate_tool_record_text(result.error_message or history_content, 120)}" if invocation.tool_name == "wait": diff --git a/src/maisaka/runtime.py b/src/maisaka/runtime.py index 1617dbd2..84d28499 100644 --- a/src/maisaka/runtime.py +++ b/src/maisaka/runtime.py @@ -1,6 +1,6 @@ """Maisaka 非 CLI 运行时。""" -from typing import Any, Literal, Optional +from typing import Any, Literal, Optional, Sequence import asyncio import time @@ -20,12 +20,14 @@ from src.core.tooling import ToolRegistry from src.know_u.knowledge import KnowledgeLearner from src.learners.expression_learner import ExpressionLearner from src.learners.jargon_miner import JargonMiner +from src.llm_models.payload_content.resp_format import RespFormat +from src.llm_models.payload_content.tool_option import ToolDefinitionInput from src.mcp_module import MCPManager from src.mcp_module.host_llm_bridge import MCPHostLLMBridge from src.mcp_module.provider import MCPToolProvider from src.plugin_runtime.tool_provider import PluginToolProvider -from .chat_loop_service import MaisakaChatLoopService +from .chat_loop_service import ChatResponse, MaisakaChatLoopService from .context_messages import LLMContextMessage from .reasoning_engine import MaisakaReasoningEngine from .tool_provider import MaisakaBuiltinToolProvider @@ -197,6 +199,40 @@ class MaisakaHeartFlowChatting: self._tool_registry.register_provider(PluginToolProvider()) self._chat_loop_service.set_tool_registry(self._tool_registry) + async def run_sub_agent( + self, + *, + context_message_limit: int, + system_prompt: str, + extra_messages: Optional[Sequence[LLMContextMessage]] = None, + max_tokens: int = 512, + response_format: RespFormat | None = None, + temperature: float = 0.2, + tool_definitions: Optional[Sequence[ToolDefinitionInput]] = None, + ) -> ChatResponse: + """运行一个复制上下文的临时子代理,并在完成后立即销毁。""" + + selected_history, _ = MaisakaChatLoopService.select_llm_context_messages( + self._chat_history, + max_context_size=context_message_limit, + ) + sub_agent_history = list(selected_history) + if extra_messages: + sub_agent_history.extend(list(extra_messages)) + + sub_agent = MaisakaChatLoopService( + chat_system_prompt=system_prompt, + session_id=self.session_id, + is_group_chat=self.chat_stream.is_group_session, + temperature=temperature, + max_tokens=max_tokens, + ) + return await sub_agent.chat_loop_step( + sub_agent_history, + response_format=response_format, + tool_definitions=[] if tool_definitions is None else tool_definitions, + ) + async def _main_loop(self) -> None: try: while self._running: @@ -421,7 +457,7 @@ class MaisakaHeartFlowChatting: if self.chat_stream.user_id: return UserInfo( user_id=self.chat_stream.user_id, - user_nickname=global_config.maisaka.user_name.strip() or "用户", + user_nickname=global_config.maisaka.cli_user_name.strip() or "用户", user_cardname=None, ) return UserInfo(user_id="maisaka_user", user_nickname="用户", user_cardname=None) @@ -455,7 +491,7 @@ class MaisakaHeartFlowChatting: tool_results: Optional[list[str]] = None, ) -> None: """在终端展示当前聊天流的上下文占用、规划结果与工具摘要。""" - if not global_config.maisaka.show_thinking: + if not global_config.debug.show_maisaka_thinking: return session_name = chat_manager.get_session_name(self.session_id) or self.session_id