mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-08-14 14:31:00 +00:00
fix(tools): harden agent runner tool runtimes
This commit is contained in:
@@ -6,6 +6,7 @@ import typing
|
||||
import langbot_plugin.api.entities.builtin.resource.tool as resource_tool
|
||||
|
||||
from .. import loader
|
||||
from .availability import is_box_backend_available
|
||||
|
||||
# Align with Claude Code's Skill tool design:
|
||||
# - activate: Activate a skill via Tool Call, returns SKILL.md content
|
||||
@@ -45,18 +46,7 @@ class SkillToolLoader(loader.ToolLoader):
|
||||
|
||||
async def _check_sandbox_available(self) -> bool:
|
||||
"""Check if the box backend is truly available (not just the runtime)."""
|
||||
box_service = getattr(self.ap, 'box_service', None)
|
||||
if box_service is None:
|
||||
return False
|
||||
if not getattr(box_service, 'available', False):
|
||||
return False
|
||||
# Check if backend is truly available via get_status
|
||||
try:
|
||||
status = await box_service.get_status()
|
||||
backend_info = status.get('backend', {})
|
||||
return backend_info.get('available', False)
|
||||
except Exception:
|
||||
return False
|
||||
return await is_box_backend_available(self.ap)
|
||||
|
||||
async def get_tools(self, bound_plugins: list[str] | None = None) -> list[resource_tool.LLMTool]:
|
||||
if not self._is_available():
|
||||
@@ -92,17 +82,17 @@ class SkillToolLoader(loader.ToolLoader):
|
||||
if not skill_name:
|
||||
raise ValueError('skill_name is required')
|
||||
|
||||
skill_mgr = self.ap.skill_mgr
|
||||
skill_data = skill_mgr.get_skill_by_name(skill_name)
|
||||
from . import skill as skill_loader
|
||||
|
||||
skill_data = skill_loader.get_visible_skill(self.ap, query, skill_name)
|
||||
if skill_data is None:
|
||||
visible_skills = getattr(skill_mgr, 'skills', {})
|
||||
visible_skills = skill_loader.get_visible_skills(self.ap, query)
|
||||
available_names = ', '.join(sorted(visible_skills.keys())) or 'none'
|
||||
raise ValueError(f'Skill "{skill_name}" not found. Available skills: {available_names}')
|
||||
|
||||
# Register activated skill for sandbox mount path resolution
|
||||
from . import skill as skill_loader
|
||||
|
||||
skill_loader.register_activated_skill(query, skill_data)
|
||||
await skill_loader.persist_activated_skill(self.ap, query, skill_name)
|
||||
|
||||
# Return SKILL.md content as Tool Result (injects into context)
|
||||
instructions = skill_data.get('instructions', '')
|
||||
@@ -201,13 +191,13 @@ class SkillToolLoader(loader.ToolLoader):
|
||||
return resource_tool.LLMTool(
|
||||
name=ACTIVATE_SKILL_TOOL_NAME,
|
||||
human_desc='Activate a skill',
|
||||
description=self._build_activate_tool_description(),
|
||||
description='Activate a pipeline-visible skill by name and return its instructions as a tool result.',
|
||||
parameters={
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'skill_name': {
|
||||
'type': 'string',
|
||||
'description': 'The skill name to activate (no arguments). E.g., "pdf" or "data-analysis"',
|
||||
'description': 'The skill name to activate.',
|
||||
},
|
||||
},
|
||||
'required': ['skill_name'],
|
||||
@@ -255,50 +245,3 @@ class SkillToolLoader(loader.ToolLoader):
|
||||
},
|
||||
func=lambda parameters: parameters,
|
||||
)
|
||||
|
||||
def _build_activate_tool_description(self) -> str:
|
||||
"""Build tool description with embedded available_skills list."""
|
||||
skill_mgr = getattr(self.ap, 'skill_mgr', None)
|
||||
if skill_mgr is None:
|
||||
return 'Activate a skill. No skills are currently available.'
|
||||
|
||||
skills = getattr(skill_mgr, 'skills', {})
|
||||
if not skills:
|
||||
return 'Activate a skill. No skills are currently available.'
|
||||
|
||||
# Build <available_skills> section
|
||||
available_skills_lines = ['<available_skills>']
|
||||
for skill_name, skill_data in sorted(skills.items()):
|
||||
description = skill_data.get('description', '')
|
||||
available_skills_lines.append('<skill>')
|
||||
available_skills_lines.append(f'<name>{skill_name}</name>')
|
||||
available_skills_lines.append(f'<description>{description}</description>')
|
||||
available_skills_lines.append('</skill>')
|
||||
available_skills_lines.append('</available_skills>')
|
||||
|
||||
available_skills_block = '\n'.join(available_skills_lines)
|
||||
|
||||
return f"""Activate a skill within the main conversation.
|
||||
|
||||
<skills_instructions>
|
||||
When users ask you to perform tasks, check if any of the available skills
|
||||
below can help complete the task more effectively. Skills provide specialized
|
||||
capabilities and domain knowledge.
|
||||
|
||||
How to use skills:
|
||||
- Invoke skills using this tool with the skill name only (no arguments)
|
||||
- When you invoke a skill, you will see <command-message>
|
||||
The skill is activated
|
||||
</command-message>
|
||||
- The skill's instructions will be provided in the tool result
|
||||
- Examples:
|
||||
- skill_name: "pdf" - invoke the pdf skill
|
||||
- skill_name: "data-analysis" - invoke the data-analysis skill
|
||||
|
||||
Important:
|
||||
- Only use skills listed in <available_skills> below
|
||||
- Do not invoke a skill that is already running
|
||||
- To create a new skill: prepare it in /workspace, then use register_skill tool
|
||||
</skills_instructions>
|
||||
|
||||
{available_skills_block}"""
|
||||
|
||||
Reference in New Issue
Block a user