diff --git a/benchmark/locomo/openclaw/import_to_ov.py b/benchmark/locomo/openclaw/import_to_ov.py index 14a51cb21..02d9d6578 100644 --- a/benchmark/locomo/openclaw/import_to_ov.py +++ b/benchmark/locomo/openclaw/import_to_ov.py @@ -303,7 +303,7 @@ async def viking_ingest( task_id = result.get("task_id") if task_id: # 轮询任务状态直到完成 - max_attempts = 1200 # 最多等待20分钟 + max_attempts = 3600 # 最多等待1小时 for attempt in range(max_attempts): task = await client.get_task(task_id) status = task.get("status") if task else "unknown" diff --git a/bot/vikingbot/agent/context.py b/bot/vikingbot/agent/context.py index 01f3e36a7..a687d2a19 100644 --- a/bot/vikingbot/agent/context.py +++ b/bot/vikingbot/agent/context.py @@ -68,7 +68,7 @@ def _ensure_templates_once(self): self._templates_ensured = True async def build_system_prompt( - self, session_key: SessionKey, current_message: str, history: list[dict[str, Any]] + self, session_key: SessionKey, current_message: str, history: list[dict[str, Any]], ov_tools_enable: bool = True ) -> str: """ Build the system prompt from bootstrap files, memory, and skills. @@ -123,22 +123,23 @@ async def build_system_prompt( {skills_summary}""") - # Viking user profile - start = _time.time() - profile = await self.memory.get_viking_user_profile( - workspace_id=workspace_id, user_id=self._sender_id - ) - cost = round(_time.time() - start, 2) - logger.info( - f"[READ_USER_PROFILE]: cost {cost}s, profile={profile[:50] if profile else 'None'}" - ) - if profile: - parts.append(f"## Current user's information\n{profile}") + # Viking user profile (only if ov tools are enabled) + if ov_tools_enable: + start = _time.time() + profile = await self.memory.get_viking_user_profile( + workspace_id=workspace_id, user_id=self._sender_id + ) + cost = round(_time.time() - start, 2) + logger.info( + f"[READ_USER_PROFILE]: cost {cost}s, profile={profile[:50] if profile else 'None'}" + ) + if profile: + parts.append(f"## Current user's information\n{profile}") return "\n\n---\n\n".join(parts) async def _build_user_memory( - self, session_key: SessionKey, current_message: str, sender_id: str + self, session_key: SessionKey, current_message: str, sender_id: str, ov_tools_enable: bool = True ) -> str: """ Build the system prompt from bootstrap files, memory, and skills. @@ -168,21 +169,22 @@ async def _build_user_memory( workspace_id = self.sandbox_manager.to_workspace_id(session_key) - # Viking agent memory - start = _time.time() - viking_memory = await self.memory.get_viking_memory_context( - current_message=current_message, workspace_id=workspace_id, sender_id=sender_id - ) - logger.info(f'viking_memory={viking_memory}') - cost = round(_time.time() - start, 2) - logger.info( - f"[READ_USER_MEMORY]: cost {cost}s, memory={viking_memory[:50] if viking_memory else 'None'}" - ) - if viking_memory: - parts.append( - f"## openviking_search(query=[user_query])\n" - f"{viking_memory}" + # Viking agent memory (only if ov tools are enabled) + if ov_tools_enable: + start = _time.time() + viking_memory = await self.memory.get_viking_memory_context( + current_message=current_message, workspace_id=workspace_id, sender_id=sender_id ) + logger.info(f'viking_memory={viking_memory}') + cost = round(_time.time() - start, 2) + logger.info( + f"[READ_USER_MEMORY]: cost {cost}s, memory={viking_memory[:50] if viking_memory else 'None'}" + ) + if viking_memory: + parts.append( + f"## openviking_search(query=[user_query])\n" + f"{viking_memory}" + ) parts.append("Reply in the same language as the user's query, ignoring the language of the reference materials. User's query:") @@ -249,6 +251,7 @@ async def build_messages( current_message: str, media: list[str] | None = None, session_key: SessionKey | None = None, + ov_tools_enable: bool = True, ) -> list[dict[str, Any]]: """ Build the complete message list for an LLM call. @@ -258,6 +261,7 @@ async def build_messages( current_message: The new user message. media: Optional list of local file paths for images/media. session_key: Optional session key. + ov_tools_enable: Whether to enable OpenViking tools and memory. Returns: List of messages including system prompt. @@ -265,7 +269,7 @@ async def build_messages( messages = [] # System prompt - system_prompt = await self.build_system_prompt(session_key, current_message, history) + system_prompt = await self.build_system_prompt(session_key, current_message, history, ov_tools_enable=ov_tools_enable) messages.append({"role": "system", "content": system_prompt}) # logger.debug(f"system_prompt: {system_prompt}") @@ -274,7 +278,7 @@ async def build_messages( messages.extend(history) # User - user_info = await self._build_user_memory(session_key, current_message, self._sender_id) + user_info = await self._build_user_memory(session_key, current_message, self._sender_id, ov_tools_enable=ov_tools_enable) messages.append({"role": "user", "content": user_info}) # Current message (with optional image attachments) diff --git a/bot/vikingbot/agent/loop.py b/bot/vikingbot/agent/loop.py index d11a8df6a..df0d31478 100644 --- a/bot/vikingbot/agent/loop.py +++ b/bot/vikingbot/agent/loop.py @@ -217,6 +217,7 @@ async def _run_agent_loop( session_key: SessionKey, publish_events: bool = True, sender_id: str | None = None, + ov_tools_enable: bool = True, ) -> tuple[str | None, list[dict], dict[str, int], int]: """ Run the core agent loop: call LLM, execute tools, repeat until done. @@ -225,6 +226,7 @@ async def _run_agent_loop( messages: Initial message list session_key: Session key for tool execution context publish_events: Whether to publish ITERATION/REASONING/TOOL_CALL events to the bus + ov_tools_enable: Whether to enable OpenViking tools for this session Returns: tuple of (final_content, tools_used) @@ -252,7 +254,7 @@ async def _run_agent_loop( response = await self.provider.chat( messages=messages, - tools=self.tools.get_definitions(), + tools=self.tools.get_definitions(ov_tools_enable=ov_tools_enable), model=self.model, session_id=session_key.safe_name(), ) @@ -526,14 +528,16 @@ async def check_long_running(): eval=self._eval, ) + ov_tools_enable = self._get_ov_tools_enable(session_key) # Build initial messages (use get_history for LLM-formatted messages) messages = await message_context.build_messages( history=session.get_history(), current_message=msg.content, media=msg.media if msg.media else None, session_key=msg.session_key, + ov_tools_enable=ov_tools_enable, ) - # logger.info(f"New messages: {messages}") + logger.info(f"New messages: {messages}") # Run agent loop final_content, tools_used, token_usage, iteration = await self._run_agent_loop( @@ -541,6 +545,7 @@ async def check_long_running(): session_key=session_key, publish_events=True, sender_id=msg.sender_id, + ov_tools_enable=ov_tools_enable, ) # Log response preview @@ -577,6 +582,29 @@ async def check_long_running(): except asyncio.CancelledError: pass + def _get_channel_config(self, session_key: SessionKey): + """Get channel config for a session key. + + Args: + session_key: Session key to get channel config for + + Returns: + Channel config object if found, None otherwise + """ + return self.config.channels_config.get_channel_by_key(session_key.channel_key()) + + def _get_ov_tools_enable(self, session_key: SessionKey) -> bool: + """Get ov_tools_enable setting from channel config. + + Args: + session_key: Session key to get channel config for + + Returns: + True if ov tools should be enabled, False otherwise + """ + channel_config = self._get_channel_config(session_key) + return getattr(channel_config, "ov_tools_enable", True) if channel_config else True + async def _process_system_message(self, msg: InboundMessage) -> OutboundMessage | None: """ Process a system message (e.g., subagent announce). @@ -589,15 +617,23 @@ async def _process_system_message(self, msg: InboundMessage) -> OutboundMessage session = self.sessions.get_or_create(msg.session_key) # Build messages with the announce content + ov_tools_enable = self._get_ov_tools_enable(msg.session_key) messages = await self.context.build_messages( - history=session.get_history(), current_message=msg.content, session_key=msg.session_key + history=session.get_history(), + current_message=msg.content, + session_key=msg.session_key, + ov_tools_enable=ov_tools_enable, ) + # Check channel config for ov_tools_enable setting + ov_tools_enable = self._get_ov_tools_enable(msg.session_key) + # Run agent loop (no events published) final_content, tools_used, token_usage, iteration = await self._run_agent_loop( messages=messages, session_key=msg.session_key, publish_events=False, + ov_tools_enable=ov_tools_enable, ) if final_content is None or ( @@ -753,12 +789,11 @@ def _check_cmd_auth(self, msg: InboundMessage) -> bool: allow_from = [] if self.config.ov_server and self.config.ov_server.admin_user_id: allow_from.append(self.config.ov_server.admin_user_id) - for channel in self.config.channels_config.get_all_channels(): - if channel.channel_key() == msg.session_key.channel_key(): - allow_cmd = getattr(channel, 'allow_cmd_from', []) - if allow_cmd: - allow_from.extend(allow_cmd) - break + channel_config = self._get_channel_config(msg.session_key) + if channel_config: + allow_cmd = getattr(channel_config, 'allow_cmd_from', []) + if allow_cmd: + allow_from.extend(allow_cmd) # If channel not found or sender not in allow_from list, ignore message if msg.sender_id not in allow_from: diff --git a/bot/vikingbot/agent/tools/registry.py b/bot/vikingbot/agent/tools/registry.py index 628e2bd44..1797ebce9 100644 --- a/bot/vikingbot/agent/tools/registry.py +++ b/bot/vikingbot/agent/tools/registry.py @@ -100,13 +100,17 @@ def has(self, name: str) -> bool: """ return name in self._tools - def get_definitions(self) -> list[dict[str, Any]]: + def get_definitions(self, ov_tools_enable: bool = True) -> list[dict[str, Any]]: """ Get all tool definitions in OpenAI format. Converts all registered tools to the OpenAI function schema format, suitable for use with OpenAI's function calling API. + Args: + ov_tools_enable: Whether to include OpenViking tools. If False, + tools with names starting with "openviking_" will be excluded. + Returns: List of tool schemas in OpenAI format, where each schema contains the tool's type, name, description, and parameters. @@ -116,7 +120,10 @@ def get_definitions(self) -> list[dict[str, Any]]: >>> for defn in definitions: ... print(f"Tool: {defn['function']['name']}") """ - return [tool.to_schema() for tool in self._tools.values()] + tools = self._tools.values() + if not ov_tools_enable: + tools = [tool for tool in tools if not tool.name.startswith("openviking_")] + return [tool.to_schema() for tool in tools] async def execute( self, diff --git a/bot/vikingbot/config/schema.py b/bot/vikingbot/config/schema.py index 0ae4bfff3..fcefdcee3 100644 --- a/bot/vikingbot/config/schema.py +++ b/bot/vikingbot/config/schema.py @@ -60,6 +60,7 @@ class BaseChannelConfig(BaseModel): type: Any = ChannelType.TELEGRAM # Default for backwards compatibility enabled: bool = True + ov_tools_enable: bool = True def channel_id(self) -> str: return "default" @@ -403,6 +404,20 @@ def get_all_channels(self) -> list[BaseChannelConfig]: result.append(item) return result + def get_channel_by_key(self, channel_key: str) -> BaseChannelConfig | None: + """Get channel config by channel key. + + Args: + channel_key: Channel key in format "type__channel_id" + + Returns: + Channel config if found, None otherwise + """ + for channel_config in self.get_all_channels(): + if channel_config.channel_key() == channel_key: + return channel_config + return None + class AgentsConfig(BaseModel): """Agent configuration."""