aideml / aide /backend /utils.py
Dixing (Dex) Xu
:sparkles: Add anthropic tool use (#39)
f92d1a2 unverified
from dataclasses import dataclass
import jsonschema
from dataclasses_json import DataClassJsonMixin
import backoff
import logging
from typing import Callable
PromptType = str | dict | list
FunctionCallType = dict
OutputType = str | FunctionCallType
logger = logging.getLogger("aide")
@backoff.on_predicate(
wait_gen=backoff.expo,
max_value=60,
factor=1.5,
)
def backoff_create(
create_fn: Callable, retry_exceptions: list[Exception], *args, **kwargs
):
try:
return create_fn(*args, **kwargs)
except retry_exceptions as e:
logger.info(f"Backoff exception: {e}")
return False
def opt_messages_to_list(
system_message: str | None, user_message: str | None
) -> list[dict[str, str]]:
messages = []
if system_message:
messages.append({"role": "system", "content": system_message})
if user_message:
messages.append({"role": "user", "content": user_message})
return messages
def compile_prompt_to_md(prompt: PromptType, _header_depth: int = 1) -> str:
if isinstance(prompt, str):
return prompt.strip() + "\n"
elif isinstance(prompt, list):
return "\n".join([f"- {s.strip()}" for s in prompt] + ["\n"])
out = []
header_prefix = "#" * _header_depth
for k, v in prompt.items():
out.append(f"{header_prefix} {k}\n")
out.append(compile_prompt_to_md(v, _header_depth=_header_depth + 1))
return "\n".join(out)
@dataclass
class FunctionSpec(DataClassJsonMixin):
name: str
json_schema: dict # JSON schema
description: str
def __post_init__(self):
# validate the schema
jsonschema.Draft7Validator.check_schema(self.json_schema)
@property
def as_openai_tool_dict(self):
"""Convert to OpenAI's function format."""
return {
"type": "function",
"function": {
"name": self.name,
"description": self.description,
"parameters": self.json_schema,
},
}
@property
def openai_tool_choice_dict(self):
return {
"type": "function",
"function": {"name": self.name},
}
@property
def as_anthropic_tool_dict(self):
"""Convert to Anthropic's tool format."""
return {
"name": self.name,
"description": self.description,
"input_schema": self.json_schema, # Anthropic uses input_schema instead of parameters
}
@property
def anthropic_tool_choice_dict(self):
"""Convert to Anthropic's tool choice format."""
return {
"type": "tool", # Anthropic uses "tool" instead of "function"
"name": self.name,
}