Source code for kerb.parsing.functions
"""Function calling and tool use formatting.
This module provides utilities for formatting function definitions for LLM function
calling, parsing function calls from LLM outputs, and formatting results.
"""
import json
import re
from typing import Any, Dict, List, Optional
from .json import extract_json
from .types import ParseMode, ParseResult
[docs]
def format_function_call(
name: str,
description: str,
parameters: Dict[str, Any],
required: Optional[List[str]] = None,
) -> Dict[str, Any]:
"""Format a function definition for LLM function calling.
Args:
name (str): Function name
description (str): Function description
parameters (Dict): Parameter schema (JSON Schema format)
required (List[str], optional): List of required parameter names
Returns:
Dict: Formatted function definition
Examples:
>>> format_function_call(
... name="get_weather",
... description="Get weather for a location",
... parameters={"location": {"type": "string"}},
... required=["location"]
... )
"""
function_def = {
"name": name,
"description": description,
"parameters": {
"type": "object",
"properties": parameters,
},
}
if required:
function_def["parameters"]["required"] = required
return function_def
[docs]
def format_tool_call(
name: str,
description: str,
parameters: Dict[str, Any],
required: Optional[List[str]] = None,
) -> Dict[str, Any]:
"""Format a tool definition for LLM tool use (OpenAI format).
Args:
name (str): Tool name
description (str): Tool description
parameters (Dict): Parameter schema (JSON Schema format)
required (List[str], optional): List of required parameter names
Returns:
Dict: Formatted tool definition
"""
return {
"type": "function",
"function": format_function_call(name, description, parameters, required),
}
[docs]
def parse_function_call(text: str, mode: ParseMode = ParseMode.LENIENT) -> ParseResult:
"""Parse a function call from LLM output.
Extracts function name and arguments from various formats:
- JSON format: {"name": "func", "arguments": {...}}
- Plain format: func(arg1=val1, arg2=val2)
- Markdown format with code blocks
Args:
text (str): Text containing function call
mode (ParseMode): Parsing mode
Returns:
ParseResult: Parsed function call with name and arguments
"""
original = text
# Try JSON format first
json_result = extract_json(text, mode)
if json_result.success:
data = json_result.data
if isinstance(data, dict) and "name" in data:
# OpenAI function call format
result_data = {"name": data["name"], "arguments": data.get("arguments", {})}
# If arguments is a string, try to parse it
if isinstance(result_data["arguments"], str):
try:
result_data["arguments"] = json.loads(result_data["arguments"])
except json.JSONDecodeError:
pass
return ParseResult(
success=True,
data=result_data,
fixed=json_result.fixed,
original=original,
warnings=json_result.warnings,
)
# Try plain function call format: func_name(arg1=val1, arg2=val2)
pattern = r"(\w+)\((.*?)\)"
match = re.search(pattern, text)
if match:
func_name = match.group(1)
args_str = match.group(2)
# Parse arguments
arguments = {}
if args_str.strip():
# Simple key=value parsing
arg_pairs = re.findall(r"(\w+)\s*=\s*([^,]+)", args_str)
for key, value in arg_pairs:
value = value.strip()
# Try to parse value as JSON
try:
arguments[key] = json.loads(value)
except json.JSONDecodeError:
# Remove quotes if present
if value.startswith('"') and value.endswith('"'):
arguments[key] = value[1:-1]
elif value.startswith("'") and value.endswith("'"):
arguments[key] = value[1:-1]
else:
arguments[key] = value
return ParseResult(
success=True,
data={"name": func_name, "arguments": arguments},
fixed=True,
original=original,
warnings=["Parsed plain function call format"],
)
return ParseResult(
success=False,
error="Could not parse function call from text",
original=original,
)
[docs]
def format_function_result(result: Any, name: Optional[str] = None) -> Dict[str, Any]:
"""Format a function result for returning to the LLM.
Args:
result: Function execution result
name (str, optional): Function name
Returns:
Dict: Formatted function result
"""
formatted = {
"role": "function",
"content": json.dumps(result) if not isinstance(result, str) else result,
}
if name:
formatted["name"] = name
return formatted