Spaces:
Runtime error
Runtime error
| """ | |
| 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" | |
| }] | |
| }) | |