| """ |
| Tool/Function Calling Support for DarkSyntrix LLM Spaces. |
| Converts OpenAI tool definitions into system prompts and parses tool call responses. |
| """ |
|
|
| import json |
| import re |
| from typing import Optional |
|
|
|
|
| TOOL_CALL_TEMPLATE = """You have access to the following tools. When you need to use a tool, respond with a JSON object containing "tool_calls" array. |
| |
| Available tools: |
| {tools_json} |
| |
| To call a tool, respond with: |
| {{"tool_calls": [{{"id": "call_xxx", "type": "function", "function": {{"name": "tool_name", "arguments": {{...}}}}}}]}} |
| |
| If you don't need a tool, respond normally with text. |
| """ |
|
|
|
|
| def build_tool_system_prompt(tools: list[dict]) -> str: |
| """Build system prompt from OpenAI-format tools array.""" |
| if not tools: |
| return "" |
| simplified = [] |
| for tool in tools: |
| fn = tool.get("function", tool) |
| simplified.append({ |
| "name": fn.get("name", "unknown"), |
| "description": fn.get("description", ""), |
| "parameters": fn.get("parameters", {}) |
| }) |
| return TOOL_CALL_TEMPLATE.format(tools_json=json.dumps(simplified, indent=2)) |
|
|
|
|
| TOOL_CALL_PATTERNS = [ |
| re.compile(r'(\{[\s\S]*?"tool_calls"[\s\S]*?\})', re.DOTALL), |
| re.compile(r'<tool_call>([\s\S]*?)</tool_call>', re.DOTALL), |
| re.compile(r'\[TOOL_CALL\]([\s\S]*?)\[\/TOOL_CALL\]', re.DOTALL), |
| re.compile(r'Function call:\s*(\{[\s\S]*?["\']\})', re.DOTALL), |
| ] |
|
|
|
|
| def parse_tool_calls(text: str) -> Optional[list[dict]]: |
| """Parse tool calls from model output. Returns None if no tool calls found.""" |
| for pattern in TOOL_CALL_PATTERNS: |
| matches = pattern.findall(text) |
| for match in matches: |
| try: |
| parsed = json.loads(match.strip()) |
| tc = parsed.get("tool_calls", parsed.get("tool_call", [])) |
| if isinstance(tc, dict): |
| tc = [tc] |
| if tc and isinstance(tc, list): |
| return tc |
| except json.JSONDecodeError: |
| continue |
|
|
| try: |
| parsed = json.loads(text.strip()) |
| tc = parsed.get("tool_calls", parsed.get("tool_call", [])) |
| if isinstance(tc, dict): |
| tc = [tc] |
| if tc and isinstance(tc, list): |
| return tc |
| except json.JSONDecodeError: |
| pass |
|
|
| return None |
|
|
|
|
| def strip_tool_calls(text: str) -> str: |
| """Remove tool call JSON from text, keeping only natural language.""" |
| for pattern in TOOL_CALL_PATTERNS: |
| text = pattern.sub("", text) |
| text = re.sub(r'\s*<tool_call>.*?</tool_call>\s*', '', text, flags=re.DOTALL) |
| text = re.sub(r'\s*\[TOOL_CALL\].*?\[\/TOOL_CALL\]\s*', '', text, flags=re.DOTALL) |
| return text.strip() |
|
|
|
|
| def format_tool_call_response(tool_calls: list[dict]) -> str: |
| """Format tool calls as OpenAI-compatible response delta.""" |
| return json.dumps({ |
| "choices": [{ |
| "index": 0, |
| "delta": { |
| "role": "assistant", |
| "content": None, |
| "tool_calls": tool_calls |
| }, |
| "finish_reason": "tool_calls" |
| }] |
| }) |
|
|