Add new tools to DuplexPipeline: calculator, code_interpreter, turn_on_camera, turn_off_camera, increase_volume, and decrease_volume. Implement fallback schema for unknown string tools and assign default client executors for specific tools. Update tests to validate new functionality and ensure correct tool handling in the pipeline.

This commit is contained in:
Xin Wang
2026-02-27 13:59:37 +08:00
parent 8d453e10da
commit d942c85eff
3 changed files with 214 additions and 27 deletions

View File

@@ -84,7 +84,75 @@ class DuplexPipeline:
"required": [],
},
},
"calculator": {
"name": "calculator",
"description": "Execute a math expression",
"parameters": {
"type": "object",
"properties": {
"expression": {"type": "string", "description": "Math expression, e.g. 2 + 3 * 4"},
},
"required": ["expression"],
},
},
"code_interpreter": {
"name": "code_interpreter",
"description": "Safely evaluate a Python expression",
"parameters": {
"type": "object",
"properties": {
"code": {"type": "string", "description": "Python expression to evaluate"},
},
"required": ["code"],
},
},
"turn_on_camera": {
"name": "turn_on_camera",
"description": "Turn on client camera",
"parameters": {
"type": "object",
"properties": {},
"required": [],
},
},
"turn_off_camera": {
"name": "turn_off_camera",
"description": "Turn off client camera",
"parameters": {
"type": "object",
"properties": {},
"required": [],
},
},
"increase_volume": {
"name": "increase_volume",
"description": "Increase client volume",
"parameters": {
"type": "object",
"properties": {
"step": {"type": "integer", "description": "Volume increase step, default 1"},
},
"required": [],
},
},
"decrease_volume": {
"name": "decrease_volume",
"description": "Decrease client volume",
"parameters": {
"type": "object",
"properties": {
"step": {"type": "integer", "description": "Volume decrease step, default 1"},
},
"required": [],
},
},
}
_DEFAULT_CLIENT_EXECUTORS = frozenset({
"turn_on_camera",
"turn_off_camera",
"increase_volume",
"decrease_volume",
})
def __init__(
self,
@@ -1313,9 +1381,14 @@ class DuplexPipeline:
def _resolved_tool_schemas(self) -> List[Dict[str, Any]]:
schemas: List[Dict[str, Any]] = []
seen: set[str] = set()
for item in self._runtime_tools:
if isinstance(item, str):
base = self._DEFAULT_TOOL_SCHEMAS.get(item)
tool_name = item.strip()
if not tool_name or tool_name in seen:
continue
seen.add(tool_name)
base = self._DEFAULT_TOOL_SCHEMAS.get(tool_name)
if base:
schemas.append(
{
@@ -1327,6 +1400,17 @@ class DuplexPipeline:
},
}
)
else:
schemas.append(
{
"type": "function",
"function": {
"name": tool_name,
"description": f"Execute tool '{tool_name}'",
"parameters": {"type": "object", "properties": {}},
},
}
)
continue
if not isinstance(item, dict):
@@ -1334,12 +1418,15 @@ class DuplexPipeline:
fn = item.get("function")
if isinstance(fn, dict) and fn.get("name"):
fn_name = str(fn.get("name"))
fn_name = str(fn.get("name")).strip()
if not fn_name or fn_name in seen:
continue
seen.add(fn_name)
schemas.append(
{
"type": "function",
"function": {
"name": str(fn.get("name")),
"name": fn_name,
"description": str(fn.get("description") or item.get("description") or ""),
"parameters": fn.get("parameters") or {"type": "object", "properties": {}},
},
@@ -1348,11 +1435,15 @@ class DuplexPipeline:
continue
if item.get("name"):
item_name = str(item.get("name")).strip()
if not item_name or item_name in seen:
continue
seen.add(item_name)
schemas.append(
{
"type": "function",
"function": {
"name": str(item.get("name")),
"name": item_name,
"description": str(item.get("description") or ""),
"parameters": item.get("parameters") or {"type": "object", "properties": {}},
},
@@ -1363,6 +1454,11 @@ class DuplexPipeline:
def _resolved_tool_executor_map(self) -> Dict[str, str]:
result: Dict[str, str] = {}
for item in self._runtime_tools:
if isinstance(item, str):
name = item.strip()
if name in self._DEFAULT_CLIENT_EXECUTORS:
result[name] = "client"
continue
if not isinstance(item, dict):
continue
fn = item.get("function")