""" 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'([\s\S]*?)', 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*.*?\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" }] })