openai: test features of templates at runtime, to make sure no bits of intel are lost

This commit is contained in:
ochafik 2024-03-30 01:00:07 +00:00
parent 61f35e07a5
commit d8a53eadf2
4 changed files with 688 additions and 418 deletions

View file

@ -1,199 +0,0 @@
#
#
# python -m examples.agent.test_chat_handlers | tee examples/agent/test_chat_handlers.md
import json
from pathlib import Path
import typer
from typing import Annotated
from examples.openai.api import ChatCompletionRequest, ChatCompletionResponse, Message, Tool, ToolFunction
from examples.openai.gguf_kvs import GGUFKeyValues, Keys
from examples.openai.prompting import ChatHandlerArgs, ChatTemplate, ToolsPromptStyle, get_chat_handler
TEST_MESSAGES = [
Message(**{
"role": "user",
"name": None,
"tool_call_id": None,
"content": "What is the sum of 2535 squared and 32222000403 then multiplied by one and a half. What's a third of the result?",
"tool_calls": None
}),
Message(**{
"role": "assistant",
"name": None,
"tool_call_id": None,
"content": "?",
"tool_calls": [
{
"id": "call_531873",
"type": "function",
"function": {
"name": "add",
"arguments": {
"a": 2535,
"b": 32222000403
}
}
}
]
}),
Message(**{
"role": "tool",
"name": "add",
"tool_call_id": "call_531873",
"content": "32222002938",
"tool_calls": None
})
]
TEST_TOOLS = [
Tool(
type="function",
function=ToolFunction(
name="add",
description="Adds two numbers",
parameters={
"properties": {
"a": {"type": "integer"},
"b": {"type": "integer"},
},
"required": ["a", "b"]
}
)
),
Tool(
type="function",
function=ToolFunction(
name="say",
description="Says something out loud (TTS)",
parameters={
"properties": {
"text": {
"description": "The text to say out loud",
"type": "string"
},
},
"required": ["text"]
}
)
)
]
TEST_OUTPUT_SCHEMA = {"type": "integer"}
if __name__ == "__main__":
# chat_templates = {
# 'mistral_instruct_v0_1': ChatTemplate.from_huggingface("mistralai/Mixtral-8x7B-Instruct-v0.1"),
# 'functionary_v2_2': ChatTemplate.from_huggingface("meetkai/functionary-small-v2.2"),
# 'hermes_2_pro_mistral': ChatTemplate.from_huggingface("NousResearch/Hermes-2-Pro-Mistral-7B"),
# 'llama2': ChatTemplate.from_huggingface("meta-llama/Llama-2-7b-chat-hf"),
# }
# print(json.dumps({k: v.model_dump() for k, v in chat_templates.items()}, indent=2))
# exit(0)
chat_templates = {
"mistral_instruct_v0_1": {
"template": "{{ bos_token }}{% for message in messages %}{% if (message['role'] == 'user') != (loop.index0 % 2 == 0) %}{{ raise_exception('Conversation roles must alternate user/assistant/user/assistant/...') }}{% endif %}{% if message['role'] == 'user' %}{{ '[INST] ' + message['content'] + ' [/INST]' }}{% elif message['role'] == 'assistant' %}{{ message['content'] + eos_token}}{% else %}{{ raise_exception('Only user and assistant roles are supported!') }}{% endif %}{% endfor %}",
"eos_token": "</s>",
"bos_token": "<s>"
},
"functionary_v2_2": {
"template": "{#v2.2#}\n{% for message in messages %}\n{% if message['role'] == 'user' or message['role'] == 'system' %}\n{{ '<|from|>' + message['role'] + '\n<|recipient|>all\n<|content|>' + message['content'] + '\n' }}{% elif message['role'] == 'tool' %}\n{{ '<|from|>' + message['name'] + '\n<|recipient|>all\n<|content|>' + message['content'] + '\n' }}{% else %}\n{% set contain_content='no'%}\n{% if message['content'] is not none %}\n{{ '<|from|>assistant\n<|recipient|>all\n<|content|>' + message['content'] }}{% set contain_content='yes'%}\n{% endif %}\n{% if 'tool_calls' in message and message['tool_calls'] is not none %}\n{% for tool_call in message['tool_calls'] %}\n{% set prompt='<|from|>assistant\n<|recipient|>' + tool_call['function']['name'] + '\n<|content|>' + tool_call['function']['arguments'] %}\n{% if loop.index == 1 and contain_content == \"no\" %}\n{{ prompt }}{% else %}\n{{ '\n' + prompt}}{% endif %}\n{% endfor %}\n{% endif %}\n{{ '<|stop|>\n' }}{% endif %}\n{% endfor %}\n{% if add_generation_prompt %}{{ '<|from|>assistant\n<|recipient|>' }}{% endif %}",
"eos_token": "</s>",
"bos_token": "<s>"
},
"hermes_2_pro_mistral": {
"template": "{% for message in messages %}{{'<|im_start|>' + message['role'] + '\n' + message['content'] + '<|im_end|>' + '\n'}}{% endfor %}{% if add_generation_prompt %}{{ '<|im_start|>assistant\n' }}{% endif %}",
"eos_token": "<|im_end|>",
"bos_token": "<s>"
},
"llama2": {
"template": "{% if messages[0]['role'] == 'system' %}{% set loop_messages = messages[1:] %}{% set system_message = messages[0]['content'] %}{% else %}{% set loop_messages = messages %}{% set system_message = false %}{% endif %}{% for message in loop_messages %}{% if (message['role'] == 'user') != (loop.index0 % 2 == 0) %}{{ raise_exception('Conversation roles must alternate user/assistant/user/assistant/...') }}{% endif %}{% if loop.index0 == 0 and system_message != false %}{% set content = '<<SYS>>\\n' + system_message + '\\n<</SYS>>\\n\\n' + message['content'] %}{% else %}{% set content = message['content'] %}{% endif %}{% if message['role'] == 'user' %}{{ bos_token + '[INST] ' + content.strip() + ' [/INST]' }}{% elif message['role'] == 'assistant' %}{{ ' ' + content.strip() + ' ' + eos_token }}{% endif %}{% endfor %}",
"eos_token": "</s>",
"bos_token": "<s>"
},
}
chat_templates = {k: ChatTemplate(**v) for k, v in chat_templates.items()}
print(f'\nMessages:\n\n```js\n{json.dumps([m.model_dump() for m in TEST_MESSAGES], indent=2)}\n```\n')
for model_name, chat_template in chat_templates.items():
print(f"\n# {model_name}\n")
print(f'\nTemplate:\n\n```js\n{chat_template.template}\n```\n')
print(f'\nPrompt:\n\n```js\n{chat_template.render(TEST_MESSAGES, add_generation_prompt=True)}\n```\n')
argss = {
"with tools": ChatHandlerArgs(
chat_template=chat_template, #ChatTemplate.from_gguf(GGUFKeyValues(model)),
response_schema=TEST_OUTPUT_SCHEMA,
tools=TEST_TOOLS,
),
"without tools": ChatHandlerArgs(
chat_template=chat_template, #ChatTemplate.from_gguf(GGUFKeyValues(model)),
response_schema=TEST_OUTPUT_SCHEMA,
tools=[],
),
}
for style in ToolsPromptStyle:
if (style == ToolsPromptStyle.TYPESCRIPT_FUNCTIONARY_V2) != (model_name.startswith("functionary")):
continue
if style == ToolsPromptStyle.TOOLS_MIXTRAL and model_name != "mistral_instruct_v0_1":
continue
if model_name == "mistral_instruct_v0_1" and style not in (ToolsPromptStyle.TOOLS_THOUGHTFUL_STEPS, ToolsPromptStyle.TOOLS_MIXTRAL):
continue
print(f'\n## {style}\n')
for tn, args in argss.items():
ch = get_chat_handler(args, parallel_calls=True, tool_style=style)
print(f'\n### {tn}\n')
print(f'\nPrompt:\n\n```json\n{ch.output_format_prompt.content}\n```\n')
print(f'\nGrammar:\n\n```js\n{ch.grammar}\n```\n')
# test_templates([
# Message(**{
# "role": "user",
# "name": None,
# "tool_call_id": None,
# "content": "What is the sum of 2535 squared and 32222000403 then multiplied by one and a half. What's a third of the result?",
# "tool_calls": None
# }),
# Message(**{
# "role": "assistant",
# # "name": None,
# "tool_call_id": None,
# "content": "?",
# "tool_calls": [
# {
# # "id": "call_531873",
# "type": "function",
# "function": {
# "name": "add",
# "arguments": {
# "a": 2535,
# "b": 32222000403
# }
# }
# }
# ]
# }),
# Message(**{
# "role": "tool",
# "name": "add",
# "tool_call_id": "call_531873",
# "content": "32222002938",
# "tool_calls": None
# })
# ])

View file

@ -6,8 +6,8 @@ from pathlib import Path
import random
import re
import sys
from typing import Optional
from pydantic import BaseModel
from typing import Annotated, Optional
from pydantic import BaseModel, Field
from examples.json_schema_to_grammar import SchemaConverter
from examples.openai.api import Tool, Message, FunctionCall, ToolCall
@ -52,16 +52,31 @@ def raise_exception(msg: str):
class ChatTemplate(BaseModel):
template: str
inferred_tool_style: Optional['ToolsPromptStyle'] = None
eos_token: str
bos_token: str
inferred_tool_style: Annotated[Optional['ToolsPromptStyle'], Field(exclude=True)] = None
expects_stringified_function_arguments: Annotated[Optional[bool], Field(exclude=True)] = None
expects_strict_user_assistant_alternance: Annotated[Optional[bool], Field(exclude=True)] = None
formats_tool_call: Annotated[Optional[bool], Field(exclude=True)] = None
formats_tool_call_content: Annotated[Optional[bool], Field(exclude=True)] = None
formats_tool_result: Optional[bool] = None
formats_tool_name: Optional[bool] = None
@property
def potentially_supports_parallel_calls(self) -> bool:
return self.formats_tool_result and self.formats_tool_name
def __init__(self, template: str, eos_token: str, bos_token: str):
super().__init__(template=template, eos_token=eos_token, bos_token=bos_token)
env = jinja2.Environment(loader=jinja2.BaseLoader(), trim_blocks=True, lstrip_blocks=True)
self._template = env.from_string(template)
print(template)
self._strict_user_assistant_alternation = "{% if (message['role'] == 'user') != (loop.index0 % 2 == 0) %}{{ raise_exception" in template
# self.expects_strict_user_assistant_alternance = "{% if (message['role'] == 'user') != (loop.index0 % 2 == 0) %}{{ raise_exception" in template
self.probe_template_capabilities()
self.extract_prefix_suffix_from_template()
if "<|recipient|>' + tool_call['function']['name']" in template:
self.inferred_tool_style = ToolsPromptStyle.TYPESCRIPT_FUNCTIONARY_V2
@ -71,7 +86,50 @@ class ChatTemplate(BaseModel):
# self.inferred_tool_style = ToolsPromptStyle.TOOLS_HERMES_2_PRO
# self.inferred_tool_style = ToolsPromptStyle.TOOLS_MIXTRAL
# TODO: Test whether the template supports formatting tool_calls
def probe_template_capabilities(self):
def test(messages: list[Message]):
return self._template.render(messages=messages, eos_token=self.eos_token, bos_token=self.bos_token, raise_exception=raise_exception, add_generation_prompt=True)
def succeeds(messages: list[Message], strings_to_find = ()):
try:
result = test(messages)
print(result)
for s in strings_to_find:
if s not in result:
return False
return True
except Exception as e:
print(e)
return False
# if self.inferred_tool_style == ToolsPromptStyle.TYPESCRIPT_FUNCTIONARY_V2:
user_msg = Message(role="user", content="Hey")
assistant_msg = Message(role="assistant", content="I, Robot")
self.expects_strict_user_assistant_alternance = not succeeds([assistant_msg, user_msg]) and succeeds([user_msg, assistant_msg])
thought = "Precious thought"
fn_name = "callMeMaybe"
toolcall = ToolCall(id="call_531873", type="function", function=FunctionCall(name=fn_name, arguments={"lol": 123}))
toolcall_msg = Message(role="assistant", content=None, tool_calls=[toolcall])
tool_result = "Tool result"
tool_name = "additioner"
tool_msg = Message(role="tool", name=tool_name, content=tool_result)
stringified_toolcall_msg = Message(role="assistant", content=None, tool_calls=[ToolCall(function=FunctionCall(name=fn_name, arguments=json.dumps({"lol": 123})))])
toolcall_content_msg = Message(role="assistant", content=thought, tool_calls=toolcall_msg.tool_calls)
self.formats_tool_call = succeeds([user_msg, toolcall_msg], (fn_name,))
if self.formats_tool_call:
self.formats_tool_call_content = succeeds([user_msg, toolcall_content_msg], (thought,))
self.expects_stringified_function_arguments = \
not succeeds([user_msg, toolcall_content_msg]) and succeeds([user_msg, stringified_toolcall_msg], (fn_name,))
self.formats_tool_result = succeeds([user_msg, assistant_msg, tool_msg], (tool_result,))
self.formats_tool_name = succeeds([user_msg, assistant_msg, tool_msg], (tool_name,))
# assert self.formats_tools or self.expects_strict_user_assistant_alternance
def extract_prefix_suffix_from_template(self):
delimiter = '<%$[SAMPLE]$%>'
user_msg = Message(role="user", content="Hey")
@ -85,6 +143,7 @@ class ChatTemplate(BaseModel):
self._prefix = prefix
self._suffix = suffix
def strip_suffix(self, s: str) -> str:
if s.endswith(self._suffix):
return s[:-len(self._suffix)]
@ -123,54 +182,88 @@ class ChatTemplate(BaseModel):
eos_token = tokenizer.eos_token)
def render(self, messages: list[Message], add_generation_prompt: bool, omit_bos: bool = False):
if self._strict_user_assistant_alternation and any(m.role not in ('user', 'assistant') for m in messages):
def normalize(m: Message):
if m.tool_calls:
if not self.formats_tool_call or not self.formats_tool_call_content:
return Message(
role=m.role,
content='\n'.join([
*([m.content] if m.content else ()),
*([
f'<tool_call>{json.dumps(tc.model_dump())}</tool_call>'
for tc in m.tool_calls
])
])
)
elif self.expects_stringified_function_arguments:
return Message(
role=m.role,
content=m.content,
name=m.name,
tool_call_id=m.tool_call_id,
tool_calls=[
ToolCall(
id=tc.id,
type=tc.type,
function=FunctionCall(
name=tc.function.name,
arguments=json.dumps(tc.function.arguments)
)
)
for tc in m.tool_calls
],
)
else:
return m
elif self.expects_strict_user_assistant_alternance and m.role not in ('user', 'assistant'):
if m.role == "system":
return Message(role="user", content=f'[SYS]{m.content}[/SYS]')
elif m.role == "tool":
return Message(role="user", content=f'[TOOL(name={m.name}, id={m.tool_call_id})]{m.content}[/TOOL]')
else:
sys.stderr.write(f'Unexpected message role: {message.role}\n')
return Message(role="user", content=f'[{m.role.upper()}]{m.content}[/{m.role.upper()}]')
else:
return m
messages=[normalize(m) for m in messages]
if self.expects_strict_user_assistant_alternance:
new_messages=[]
i = 0
n = len(messages)
current_role = 'user'
current_content = []
def flush():
nonlocal current_content
nonlocal current_role
new_messages.append(Message(
role=current_role,
content='\n'.join(current_content)
))
current_content = []
if self.expects_strict_user_assistant_alternance or current_content:
new_messages.append(Message(
role=current_role,
content='\n'.join(current_content)
))
current_content = []
for i, message in enumerate(messages):
assert message.role in ('user', 'assistant')
if message.role == current_role:
current_content.append(message.content)
elif message.role in ('user', 'assistant'):
if message.content:
current_content.append(message.content)
else:
flush()
current_role = 'assistant' if current_role == 'user' else 'user'
current_content.append(message.content)
else:
if current_role == 'assistant':
flush()
current_role = 'user'
if message.role == 'system':
current_content.append(f'[SYS]{messages[i].content}[/SYS]')
elif message.role == 'tool':
current_content.append(f'[TOOL RESULT(name={messages[i].name}, id={messages[i].tool_call_id}]{messages[i].content}[/TOOL RESULT]')
else:
sys.stderr.write(f'Unexpected message role: {message.role}\n')
current_content.append(f'[ROLE={messages[i].role}]{messages[i].content}[/ROLE]')
current_content.extend(
f'<tool_call>{json.dumps(tc.model_dump())}</tool_call>'
for tc in (message.tool_calls or [])
)
if message.content:
current_content.append(message.content)
if current_content:
flush()
messages = new_messages
# JSON!
messages = [m.model_dump() for m in messages]
if self.inferred_tool_style == ToolsPromptStyle.TYPESCRIPT_FUNCTIONARY_V2:
# if self.inferred_tool_style == ToolsPromptStyle.TYPESCRIPT_FUNCTIONARY_V2:
if self.expects_stringified_function_arguments:
messages = [
{
**m,
@ -556,6 +649,10 @@ _LONG_TEMPLATE='\n'.join([
def get_chat_handler(args: ChatHandlerArgs, parallel_calls: bool, tool_style: Optional[ToolsPromptStyle] = None, verbose=False) -> ChatHandler:
tool_style = tool_style if tool_style is not None else args.chat_template.inferred_tool_style
if parallel_calls and not args.chat_template.potentially_supports_parallel_calls:
sys.stderr.write(f"# WARNING: Disabled parallel_calls as model does not seem to support it (will fall back to sequential calls)\n")
parallel_calls = False
if verbose:
sys.stderr.write(f"# Using tool style: {tool_style}\n")

View file

@ -1,3 +1,111 @@
{{ bos_token }}{% for message in messages %}{% if (message['role'] == 'user') != (loop.index0 % 2 == 0) %}{{ raise_exception('Conversation roles must alternate user/assistant/user/assistant/...') }}{% endif %}{% if message['role'] == 'user' %}{{ '[INST] ' + message['content'] + ' [/INST]' }}{% elif message['role'] == 'assistant' %}{{ message['content'] + eos_token}}{% else %}{{ raise_exception('Only user and assistant roles are supported!') }}{% endif %}{% endfor %}
Conversation roles must alternate user/assistant/user/assistant/...
<s>[INST] Hey [/INST]I, Robot</s>
unsupported operand type(s) for +: 'NoneType' and 'str'
Conversation roles must alternate user/assistant/user/assistant/...
Conversation roles must alternate user/assistant/user/assistant/...
{#v2.2#}
{% for message in messages %}
{% if message['role'] == 'user' or message['role'] == 'system' %}
{{ '<|from|>' + message['role'] + '
<|recipient|>all
<|content|>' + message['content'] + '
' }}{% elif message['role'] == 'tool' %}
{{ '<|from|>' + message['name'] + '
<|recipient|>all
<|content|>' + message['content'] + '
' }}{% else %}
{% set contain_content='no'%}
{% if message['content'] is not none %}
{{ '<|from|>assistant
<|recipient|>all
<|content|>' + message['content'] }}{% set contain_content='yes'%}
{% endif %}
{% if 'tool_calls' in message and message['tool_calls'] is not none %}
{% for tool_call in message['tool_calls'] %}
{% set prompt='<|from|>assistant
<|recipient|>' + tool_call['function']['name'] + '
<|content|>' + tool_call['function']['arguments'] %}
{% if loop.index == 1 and contain_content == "no" %}
{{ prompt }}{% else %}
{{ '
' + prompt}}{% endif %}
{% endfor %}
{% endif %}
{{ '<|stop|>
' }}{% endif %}
{% endfor %}
{% if add_generation_prompt %}{{ '<|from|>assistant
<|recipient|>' }}{% endif %}
<|from|>assistant
<|recipient|>all
<|content|>I, Robot<|stop|>
<|from|>user
<|recipient|>all
<|content|>Hey
<|from|>assistant
<|recipient|>
<|from|>user
<|recipient|>all
<|content|>Hey
<|stop|>
<|from|>assistant
<|recipient|>
<|from|>user
<|recipient|>all
<|content|>Hey
<|from|>assistant
<|recipient|>all
<|content|>I, Robot<|stop|>
<|from|>additioner
<|recipient|>all
<|content|>Tool result
<|from|>assistant
<|recipient|>
<|from|>user
<|recipient|>all
<|content|>Hey
<|from|>assistant
<|recipient|>all
<|content|>I, Robot<|stop|>
<|from|>additioner
<|recipient|>all
<|content|>Tool result
<|from|>assistant
<|recipient|>
{% for message in messages %}{{'<|im_start|>' + message['role'] + '
' + message['content'] + '<|im_end|>' + '
'}}{% endfor %}{% if add_generation_prompt %}{{ '<|im_start|>assistant
' }}{% endif %}
<|im_start|>assistant
I, Robot<|im_end|>
<|im_start|>user
Hey<|im_end|>
<|im_start|>assistant
can only concatenate str (not "NoneType") to str
<|im_start|>user
Hey<|im_end|>
<|im_start|>assistant
I, Robot<|im_end|>
<|im_start|>tool
Tool result<|im_end|>
<|im_start|>assistant
<|im_start|>user
Hey<|im_end|>
<|im_start|>assistant
I, Robot<|im_end|>
<|im_start|>tool
Tool result<|im_end|>
<|im_start|>assistant
{% if messages[0]['role'] == 'system' %}{% set loop_messages = messages[1:] %}{% set system_message = messages[0]['content'] %}{% else %}{% set loop_messages = messages %}{% set system_message = false %}{% endif %}{% for message in loop_messages %}{% if (message['role'] == 'user') != (loop.index0 % 2 == 0) %}{{ raise_exception('Conversation roles must alternate user/assistant/user/assistant/...') }}{% endif %}{% if loop.index0 == 0 and system_message != false %}{% set content = '<<SYS>>\n' + system_message + '\n<</SYS>>\n\n' + message['content'] %}{% else %}{% set content = message['content'] %}{% endif %}{% if message['role'] == 'user' %}{{ bos_token + '[INST] ' + content.strip() + ' [/INST]' }}{% elif message['role'] == 'assistant' %}{{ ' ' + content.strip() + ' ' + eos_token }}{% endif %}{% endfor %}
Conversation roles must alternate user/assistant/user/assistant/...
<s>[INST] Hey [/INST] I, Robot </s>
'None' has no attribute 'strip'
Conversation roles must alternate user/assistant/user/assistant/...
Conversation roles must alternate user/assistant/user/assistant/...
Messages:
@ -7,20 +115,20 @@ Messages:
"role": "user",
"name": null,
"tool_call_id": null,
"content": "What is the sum of 2535 squared and 32222000403 then multiplied by one and a half. What's a third of the result?",
"content": "Add two numbers for the purpose of this test.",
"tool_calls": null
},
{
"role": "assistant",
"name": null,
"tool_call_id": null,
"content": "?",
"content": null,
"tool_calls": [
{
"id": "call_531873",
"type": "function",
"function": {
"name": "add",
"name": "superSecretTool",
"arguments": {
"a": 2535,
"b": 32222000403
@ -31,10 +139,17 @@ Messages:
},
{
"role": "tool",
"name": "add",
"name": "superSecretTool",
"tool_call_id": "call_531873",
"content": "32222002938",
"tool_calls": null
},
{
"role": "assistant",
"name": null,
"tool_call_id": null,
"content": "The sum of 2535 and 32222000403 is 42.",
"tool_calls": null
}
]
```
@ -53,8 +168,7 @@ Template:
Prompt:
```js
<s>[INST] What is the sum of 2535 squared and 32222000403 then multiplied by one and a half. What's a third of the result? [/INST]?
<tool_call>{"id": "call_531873", "type": "function", "function": {"name": "add", "arguments": {"a": 2535, "b": 32222000403}}}</tool_call></s>[INST] [TOOL RESULT(name=add, id=call_531873]32222002938[/TOOL RESULT] [/INST]
<s>[INST] Add two numbers for the purpose of this test. [/INST]<tool_call>{"id": "call_531873", "type": "function", "function": {"name": "superSecretTool", "arguments": {"a": 2535, "b": 32222000403}}}</tool_call></s>[INST] [TOOL(name=superSecretTool, id=call_531873)]32222002938[/TOOL] [/INST]The sum of 2535 and 32222000403 is 42.</s>
```
@ -72,7 +186,7 @@ Here are the tools available:
{
"type": "function",
"function": {
"name": "add",
"name": "superSecretTool",
"description": "Adds two numbers",
"parameters": {
"properties": {
@ -121,22 +235,24 @@ Please respond in JSON format with the following schema: {
{
"properties": {
"tool_calls": {
"prefixItems": {
"properties": {
"name": {
"title": "Name of the tool to call",
"type": "string"
"prefixItems": [
{
"properties": {
"name": {
"title": "Name of the tool to call",
"type": "string"
},
"arguments": {
"title": "Arguments to pass to the tool",
"type": "object"
}
},
"arguments": {
"title": "Arguments to pass to the tool",
"type": "object"
}
},
"required": [
"name",
"arguments"
]
}
"required": [
"name",
"arguments"
]
}
]
}
},
"required": [
@ -174,22 +290,22 @@ integer ::= ("-"? integral-part) space
integral-part ::= [0-9] | [1-9] [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]?
next-step ::= next-step-0 | next-step-1
next-step-0 ::= "{" space next-step-0-tool-calls-kv "}" space
next-step-0-tool-calls ::= "[" space ( next-step-0-tool-calls-item ( "," space next-step-0-tool-calls-item )* )? "]" space
next-step-0-tool-calls-item ::= next-step-0-tool-calls-item-0 | next-step-0-tool-calls-item-1
next-step-0-tool-calls-item-0 ::= "{" space next-step-0-tool-calls-item-0-name-kv "," space next-step-0-tool-calls-item-0-arguments-kv "}" space
next-step-0-tool-calls-item-0-arguments ::= "{" space next-step-0-tool-calls-item-0-arguments-a-kv "," space next-step-0-tool-calls-item-0-arguments-b-kv "}" space
next-step-0-tool-calls-item-0-arguments-a-kv ::= "\"a\"" space ":" space integer
next-step-0-tool-calls-item-0-arguments-b-kv ::= "\"b\"" space ":" space integer
next-step-0-tool-calls-item-0-arguments-kv ::= "\"arguments\"" space ":" space next-step-0-tool-calls-item-0-arguments
next-step-0-tool-calls-item-0-name ::= "\"add\""
next-step-0-tool-calls-item-0-name-kv ::= "\"name\"" space ":" space next-step-0-tool-calls-item-0-name
next-step-0-tool-calls-item-1 ::= "{" space next-step-0-tool-calls-item-1-name-kv "," space next-step-0-tool-calls-item-1-arguments-kv "}" space
next-step-0-tool-calls-item-1-arguments ::= "{" space next-step-0-tool-calls-item-1-arguments-text-kv "}" space
next-step-0-tool-calls-item-1-arguments-kv ::= "\"arguments\"" space ":" space next-step-0-tool-calls-item-1-arguments
next-step-0-tool-calls-item-1-arguments-text-kv ::= "\"text\"" space ":" space string
next-step-0-tool-calls-item-1-name ::= "\"say\""
next-step-0-tool-calls-item-1-name-kv ::= "\"name\"" space ":" space next-step-0-tool-calls-item-1-name
next-step-0-tool-calls ::= "[" space next-step-0-tool-calls-tuple-0 "]" space
next-step-0-tool-calls-kv ::= "\"tool_calls\"" space ":" space next-step-0-tool-calls
next-step-0-tool-calls-tuple-0 ::= next-step-0-tool-calls-tuple-0-0 | next-step-0-tool-calls-tuple-0-1
next-step-0-tool-calls-tuple-0-0 ::= "{" space next-step-0-tool-calls-tuple-0-0-name-kv "," space next-step-0-tool-calls-tuple-0-0-arguments-kv "}" space
next-step-0-tool-calls-tuple-0-0-arguments ::= "{" space next-step-0-tool-calls-tuple-0-0-arguments-a-kv "," space next-step-0-tool-calls-tuple-0-0-arguments-b-kv "}" space
next-step-0-tool-calls-tuple-0-0-arguments-a-kv ::= "\"a\"" space ":" space integer
next-step-0-tool-calls-tuple-0-0-arguments-b-kv ::= "\"b\"" space ":" space integer
next-step-0-tool-calls-tuple-0-0-arguments-kv ::= "\"arguments\"" space ":" space next-step-0-tool-calls-tuple-0-0-arguments
next-step-0-tool-calls-tuple-0-0-name ::= "\"superSecretTool\""
next-step-0-tool-calls-tuple-0-0-name-kv ::= "\"name\"" space ":" space next-step-0-tool-calls-tuple-0-0-name
next-step-0-tool-calls-tuple-0-1 ::= "{" space next-step-0-tool-calls-tuple-0-1-name-kv "," space next-step-0-tool-calls-tuple-0-1-arguments-kv "}" space
next-step-0-tool-calls-tuple-0-1-arguments ::= "{" space next-step-0-tool-calls-tuple-0-1-arguments-text-kv "}" space
next-step-0-tool-calls-tuple-0-1-arguments-kv ::= "\"arguments\"" space ":" space next-step-0-tool-calls-tuple-0-1-arguments
next-step-0-tool-calls-tuple-0-1-arguments-text-kv ::= "\"text\"" space ":" space string
next-step-0-tool-calls-tuple-0-1-name ::= "\"say\""
next-step-0-tool-calls-tuple-0-1-name-kv ::= "\"name\"" space ":" space next-step-0-tool-calls-tuple-0-1-name
next-step-1 ::= "{" space next-step-1-result-kv "}" space
next-step-1-result-kv ::= "\"result\"" space ":" space integer
next-step-kv ::= "\"next_step\"" space ":" space next-step
@ -239,7 +355,7 @@ Call one or more functions to assist with the user query, every time this is pos
{
"type": "function",
"function": {
"name": "add",
"name": "superSecretTool",
"description": "Adds two numbers",
"parameters": {
"properties": {
@ -287,18 +403,11 @@ To call each function, give its name and arguments within <tool_call></tool_call
Grammar:
```js
add-tool-call ::= "{" space add-tool-call-name-kv "," space add-tool-call-arguments-kv "}" space
add-tool-call-arguments ::= "{" space add-tool-call-arguments-a-kv "," space add-tool-call-arguments-b-kv "}" space
add-tool-call-arguments-a-kv ::= "\"a\"" space ":" space integer
add-tool-call-arguments-b-kv ::= "\"b\"" space ":" space integer
add-tool-call-arguments-kv ::= "\"arguments\"" space ":" space add-tool-call-arguments
add-tool-call-name ::= "\"" "add" "\"" space
add-tool-call-name-kv ::= "\"name\"" space ":" space add-tool-call-name
content ::= [^<] | "<" [^t<] | "<t" [^o<]
decimal-part ::= [0-9] [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]?
integer ::= ("-"? integral-part) space
integral-part ::= [0-9] | [1-9] [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]?
root ::= content* (tool-call+ content*)?
root ::= content* tool-call?
say-tool-call ::= "{" space say-tool-call-name-kv "," space say-tool-call-arguments-kv "}" space
say-tool-call-arguments ::= "{" space say-tool-call-arguments-text-kv "}" space
say-tool-call-arguments-kv ::= "\"arguments\"" space ":" space say-tool-call-arguments
@ -310,7 +419,14 @@ string ::= "\"" (
[^"\\] |
"\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F])
)* "\"" space
tool-call ::= "<tool" "\\"? "_" "call>" space (add-tool-call | say-tool-call) space "</tool" "\\"? "_" "call>"
superSecretTool-tool-call ::= "{" space superSecretTool-tool-call-name-kv "," space superSecretTool-tool-call-arguments-kv "}" space
superSecretTool-tool-call-arguments ::= "{" space superSecretTool-tool-call-arguments-a-kv "," space superSecretTool-tool-call-arguments-b-kv "}" space
superSecretTool-tool-call-arguments-a-kv ::= "\"a\"" space ":" space integer
superSecretTool-tool-call-arguments-b-kv ::= "\"b\"" space ":" space integer
superSecretTool-tool-call-arguments-kv ::= "\"arguments\"" space ":" space superSecretTool-tool-call-arguments
superSecretTool-tool-call-name ::= "\"" "superSecretTool" "\"" space
superSecretTool-tool-call-name-kv ::= "\"name\"" space ":" space superSecretTool-tool-call-name
tool-call ::= "<tool" "\\"? "_" "call>" space (superSecretTool-tool-call | say-tool-call) space "</tool" "\\"? "_" "call>"
```
@ -383,17 +499,17 @@ Prompt:
```js
<|from|>user
<|recipient|>all
<|content|>What is the sum of 2535 squared and 32222000403 then multiplied by one and a half. What's a third of the result?
<|content|>Add two numbers for the purpose of this test.
<|from|>assistant
<|recipient|>all
<|content|>?
<|from|>assistant
<|recipient|>add
<|content|>{"a": 2535, "b": 32222000403}<|stop|>
<|from|>add
<|content|><tool_call>{"id": "call_531873", "type": "function", "function": {"name": "superSecretTool", "arguments": {"a": 2535, "b": 32222000403}}}</tool_call><|stop|>
<|from|>superSecretTool
<|recipient|>all
<|content|>32222002938
<|from|>assistant
<|recipient|>all
<|content|>The sum of 2535 and 32222000403 is 42.<|stop|>
<|from|>assistant
<|recipient|>
```
@ -410,7 +526,7 @@ Prompt:
// Supported function definitions that should be called when necessary.
namespace functions {
// Adds two numbers
type add = (_: {
type superSecretTool = (_: {
a: number,
b: number
}) => any;
@ -427,10 +543,6 @@ text: string
Grammar:
```js
add-args ::= "{" space add-args-a-kv "," space add-args-b-kv "}" space
add-args-a-kv ::= "\"a\"" space ":" space integer
add-args-b-kv ::= "\"b\"" space ":" space integer
add-call ::= "add" "\n<|content|>\n" add-args "\n"
content ::= start content-without-start
content-without-start ::= "all\n<|content|>" not-from*
decimal-part ::= [0-9] [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]?
@ -447,8 +559,12 @@ string ::= "\"" (
[^"\\] |
"\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F])
)* "\"" space
superSecretTool-args ::= "{" space superSecretTool-args-a-kv "," space superSecretTool-args-b-kv "}" space
superSecretTool-args-a-kv ::= "\"a\"" space ":" space integer
superSecretTool-args-b-kv ::= "\"b\"" space ":" space integer
superSecretTool-call ::= "superSecretTool" "\n<|content|>\n" superSecretTool-args "\n"
tool-call ::= start tool-call-without-start
tool-call-without-start ::= add-call | say-call
tool-call-without-start ::= superSecretTool-call | say-call
```
@ -491,12 +607,14 @@ Prompt:
```js
<|im_start|>user
What is the sum of 2535 squared and 32222000403 then multiplied by one and a half. What's a third of the result?<|im_end|>
Add two numbers for the purpose of this test.<|im_end|>
<|im_start|>assistant
?<|im_end|>
<tool_call>{"id": "call_531873", "type": "function", "function": {"name": "superSecretTool", "arguments": {"a": 2535, "b": 32222000403}}}</tool_call><|im_end|>
<|im_start|>tool
32222002938<|im_end|>
<|im_start|>assistant
The sum of 2535 and 32222000403 is 42.<|im_end|>
<|im_start|>assistant
```
@ -515,7 +633,7 @@ Here are the tools available:
{
"type": "function",
"function": {
"name": "add",
"name": "superSecretTool",
"description": "Adds two numbers",
"parameters": {
"properties": {
@ -558,18 +676,11 @@ Here are the tools available:
Grammar:
```js
add-tool-call ::= "{" space add-tool-call-name-kv "," space add-tool-call-arguments-kv "}" space
add-tool-call-arguments ::= "{" space add-tool-call-arguments-a-kv "," space add-tool-call-arguments-b-kv "}" space
add-tool-call-arguments-a-kv ::= "\"a\"" space ":" space integer
add-tool-call-arguments-b-kv ::= "\"b\"" space ":" space integer
add-tool-call-arguments-kv ::= "\"arguments\"" space ":" space add-tool-call-arguments
add-tool-call-name ::= "\"add\""
add-tool-call-name-kv ::= "\"name\"" space ":" space add-tool-call-name
content ::= [^<] | "<" [^t<] | "<t" [^o<]
decimal-part ::= [0-9] [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]?
integer ::= ("-"? integral-part) space
integral-part ::= [0-9] | [1-9] [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]?
root ::= content* (tool-call+ content*)?
root ::= content* tool-call?
say-tool-call ::= "{" space say-tool-call-name-kv "," space say-tool-call-arguments-kv "}" space
say-tool-call-arguments ::= "{" space say-tool-call-arguments-text-kv "}" space
say-tool-call-arguments-kv ::= "\"arguments\"" space ":" space say-tool-call-arguments
@ -581,7 +692,14 @@ string ::= "\"" (
[^"\\] |
"\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F])
)* "\"" space
tool-call ::= "<tool_call>" space (add-tool-call | say-tool-call) space "</tool_call>"
superSecretTool-tool-call ::= "{" space superSecretTool-tool-call-name-kv "," space superSecretTool-tool-call-arguments-kv "}" space
superSecretTool-tool-call-arguments ::= "{" space superSecretTool-tool-call-arguments-a-kv "," space superSecretTool-tool-call-arguments-b-kv "}" space
superSecretTool-tool-call-arguments-a-kv ::= "\"a\"" space ":" space integer
superSecretTool-tool-call-arguments-b-kv ::= "\"b\"" space ":" space integer
superSecretTool-tool-call-arguments-kv ::= "\"arguments\"" space ":" space superSecretTool-tool-call-arguments
superSecretTool-tool-call-name ::= "\"superSecretTool\""
superSecretTool-tool-call-name-kv ::= "\"name\"" space ":" space superSecretTool-tool-call-name
tool-call ::= "<tool_call>" space (superSecretTool-tool-call | say-tool-call) space "</tool_call>"
```
@ -621,7 +739,7 @@ Call one or more functions to assist with the user query, every time this is pos
{
"type": "function",
"function": {
"name": "add",
"name": "superSecretTool",
"description": "Adds two numbers",
"parameters": {
"properties": {
@ -669,18 +787,11 @@ To call each function, give its name and arguments within <tool_call></tool_call
Grammar:
```js
add-tool-call ::= "{" space add-tool-call-name-kv "," space add-tool-call-arguments-kv "}" space
add-tool-call-arguments ::= "{" space add-tool-call-arguments-a-kv "," space add-tool-call-arguments-b-kv "}" space
add-tool-call-arguments-a-kv ::= "\"a\"" space ":" space integer
add-tool-call-arguments-b-kv ::= "\"b\"" space ":" space integer
add-tool-call-arguments-kv ::= "\"arguments\"" space ":" space add-tool-call-arguments
add-tool-call-name ::= "\"add\""
add-tool-call-name-kv ::= "\"name\"" space ":" space add-tool-call-name
content ::= [^<] | "<" [^t<] | "<t" [^o<]
decimal-part ::= [0-9] [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]?
integer ::= ("-"? integral-part) space
integral-part ::= [0-9] | [1-9] [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]?
root ::= content* (tool-call+ content*)?
root ::= content* tool-call?
say-tool-call ::= "{" space say-tool-call-name-kv "," space say-tool-call-arguments-kv "}" space
say-tool-call-arguments ::= "{" space say-tool-call-arguments-text-kv "}" space
say-tool-call-arguments-kv ::= "\"arguments\"" space ":" space say-tool-call-arguments
@ -692,7 +803,14 @@ string ::= "\"" (
[^"\\] |
"\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F])
)* "\"" space
tool-call ::= "<tool_call>" space (add-tool-call | say-tool-call) space "</tool_call>"
superSecretTool-tool-call ::= "{" space superSecretTool-tool-call-name-kv "," space superSecretTool-tool-call-arguments-kv "}" space
superSecretTool-tool-call-arguments ::= "{" space superSecretTool-tool-call-arguments-a-kv "," space superSecretTool-tool-call-arguments-b-kv "}" space
superSecretTool-tool-call-arguments-a-kv ::= "\"a\"" space ":" space integer
superSecretTool-tool-call-arguments-b-kv ::= "\"b\"" space ":" space integer
superSecretTool-tool-call-arguments-kv ::= "\"arguments\"" space ":" space superSecretTool-tool-call-arguments
superSecretTool-tool-call-name ::= "\"superSecretTool\""
superSecretTool-tool-call-name-kv ::= "\"name\"" space ":" space superSecretTool-tool-call-name
tool-call ::= "<tool_call>" space (superSecretTool-tool-call | say-tool-call) space "</tool_call>"
```
@ -732,7 +850,7 @@ Here are the tools available:
{
"type": "function",
"function": {
"name": "add",
"name": "superSecretTool",
"description": "Adds two numbers",
"parameters": {
"properties": {
@ -781,22 +899,24 @@ Please respond in JSON format with the following schema: {
{
"properties": {
"tool_calls": {
"prefixItems": {
"properties": {
"name": {
"title": "Name of the tool to call",
"type": "string"
"prefixItems": [
{
"properties": {
"name": {
"title": "Name of the tool to call",
"type": "string"
},
"arguments": {
"title": "Arguments to pass to the tool",
"type": "object"
}
},
"arguments": {
"title": "Arguments to pass to the tool",
"type": "object"
}
},
"required": [
"name",
"arguments"
]
}
"required": [
"name",
"arguments"
]
}
]
}
},
"required": [
@ -834,22 +954,22 @@ integer ::= ("-"? integral-part) space
integral-part ::= [0-9] | [1-9] [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]?
next-step ::= next-step-0 | next-step-1
next-step-0 ::= "{" space next-step-0-tool-calls-kv "}" space
next-step-0-tool-calls ::= "[" space ( next-step-0-tool-calls-item ( "," space next-step-0-tool-calls-item )* )? "]" space
next-step-0-tool-calls-item ::= next-step-0-tool-calls-item-0 | next-step-0-tool-calls-item-1
next-step-0-tool-calls-item-0 ::= "{" space next-step-0-tool-calls-item-0-name-kv "," space next-step-0-tool-calls-item-0-arguments-kv "}" space
next-step-0-tool-calls-item-0-arguments ::= "{" space next-step-0-tool-calls-item-0-arguments-a-kv "," space next-step-0-tool-calls-item-0-arguments-b-kv "}" space
next-step-0-tool-calls-item-0-arguments-a-kv ::= "\"a\"" space ":" space integer
next-step-0-tool-calls-item-0-arguments-b-kv ::= "\"b\"" space ":" space integer
next-step-0-tool-calls-item-0-arguments-kv ::= "\"arguments\"" space ":" space next-step-0-tool-calls-item-0-arguments
next-step-0-tool-calls-item-0-name ::= "\"add\""
next-step-0-tool-calls-item-0-name-kv ::= "\"name\"" space ":" space next-step-0-tool-calls-item-0-name
next-step-0-tool-calls-item-1 ::= "{" space next-step-0-tool-calls-item-1-name-kv "," space next-step-0-tool-calls-item-1-arguments-kv "}" space
next-step-0-tool-calls-item-1-arguments ::= "{" space next-step-0-tool-calls-item-1-arguments-text-kv "}" space
next-step-0-tool-calls-item-1-arguments-kv ::= "\"arguments\"" space ":" space next-step-0-tool-calls-item-1-arguments
next-step-0-tool-calls-item-1-arguments-text-kv ::= "\"text\"" space ":" space string
next-step-0-tool-calls-item-1-name ::= "\"say\""
next-step-0-tool-calls-item-1-name-kv ::= "\"name\"" space ":" space next-step-0-tool-calls-item-1-name
next-step-0-tool-calls ::= "[" space next-step-0-tool-calls-tuple-0 "]" space
next-step-0-tool-calls-kv ::= "\"tool_calls\"" space ":" space next-step-0-tool-calls
next-step-0-tool-calls-tuple-0 ::= next-step-0-tool-calls-tuple-0-0 | next-step-0-tool-calls-tuple-0-1
next-step-0-tool-calls-tuple-0-0 ::= "{" space next-step-0-tool-calls-tuple-0-0-name-kv "," space next-step-0-tool-calls-tuple-0-0-arguments-kv "}" space
next-step-0-tool-calls-tuple-0-0-arguments ::= "{" space next-step-0-tool-calls-tuple-0-0-arguments-a-kv "," space next-step-0-tool-calls-tuple-0-0-arguments-b-kv "}" space
next-step-0-tool-calls-tuple-0-0-arguments-a-kv ::= "\"a\"" space ":" space integer
next-step-0-tool-calls-tuple-0-0-arguments-b-kv ::= "\"b\"" space ":" space integer
next-step-0-tool-calls-tuple-0-0-arguments-kv ::= "\"arguments\"" space ":" space next-step-0-tool-calls-tuple-0-0-arguments
next-step-0-tool-calls-tuple-0-0-name ::= "\"superSecretTool\""
next-step-0-tool-calls-tuple-0-0-name-kv ::= "\"name\"" space ":" space next-step-0-tool-calls-tuple-0-0-name
next-step-0-tool-calls-tuple-0-1 ::= "{" space next-step-0-tool-calls-tuple-0-1-name-kv "," space next-step-0-tool-calls-tuple-0-1-arguments-kv "}" space
next-step-0-tool-calls-tuple-0-1-arguments ::= "{" space next-step-0-tool-calls-tuple-0-1-arguments-text-kv "}" space
next-step-0-tool-calls-tuple-0-1-arguments-kv ::= "\"arguments\"" space ":" space next-step-0-tool-calls-tuple-0-1-arguments
next-step-0-tool-calls-tuple-0-1-arguments-text-kv ::= "\"text\"" space ":" space string
next-step-0-tool-calls-tuple-0-1-name ::= "\"say\""
next-step-0-tool-calls-tuple-0-1-name-kv ::= "\"name\"" space ":" space next-step-0-tool-calls-tuple-0-1-name
next-step-1 ::= "{" space next-step-1-result-kv "}" space
next-step-1-result-kv ::= "\"result\"" space ":" space integer
next-step-kv ::= "\"next_step\"" space ":" space next-step
@ -894,7 +1014,7 @@ space ::= " "?
Prompt:
```json
You are a function calling AI agent with self-recursion. You can call only one function at a time and analyse data you get from function response. You are provided with function signatures within <tools></tools> XML tags. The current date is: 2024-03-29. You may use agentic frameworks for reasoning and planning to help with user query. Please call a function and wait for function results to be provided to you in the next iteration. Don't make assumptions about what values to plug into function arguments. Once you have called a function, results will be fed back to you within <tool_response></tool_response> XML tags. Don't make assumptions about tool results if <tool_response> XML tags are not present since function hasn't been executed yet. Analyze the data once you get the results and call another function. At each iteration please continue adding the your analysis to previous summary. Your final response should directly answer the user query with an anlysis or summary of the results of function calls. Here are the available tools: <tools> ['{"type":"function","function":{"name":"add","description":"Adds two numbers","parameters":{"properties":{"a":{"type":"integer"},"b":{"type":"integer"}},"required":["a","b"]}}}', '{"type":"function","function":{"name":"say","description":"Says something out loud (TTS)","parameters":{"properties":{"text":{"description":"The text to say out loud","type":"string"}},"required":["text"]}}}'] </tools> If the provided function signatures doesn't have the function you must call, you may write executable python code in markdown syntax and call code_interpreter() function as follows: <tool_call> {"arguments": {"code_markdown": <python-code>, "name": "code_interpreter"}} </tool_call> Make sure that the json object above with code markdown block is parseable with json.loads() and the XML block with XML ElementTree. Use the following pydantic model json schema for each tool call you will make: {'properties': {'arguments': {'title': 'Arguments', 'type': 'object'}, 'name': {'title': 'Name', 'type': 'string'}}, 'required': ['arguments', 'name'], 'title': 'FunctionCall', 'type': 'object'} At the very first turn you don't have <tool_results> so you shouldn't not make up the results.
You are a function calling AI agent with self-recursion. You can call only one function at a time and analyse data you get from function response. You are provided with function signatures within <tools></tools> XML tags. The current date is: 2024-03-30. You may use agentic frameworks for reasoning and planning to help with user query. Please call a function and wait for function results to be provided to you in the next iteration. Don't make assumptions about what values to plug into function arguments. Once you have called a function, results will be fed back to you within <tool_response></tool_response> XML tags. Don't make assumptions about tool results if <tool_response> XML tags are not present since function hasn't been executed yet. Analyze the data once you get the results and call another function. At each iteration please continue adding the your analysis to previous summary. Your final response should directly answer the user query with an anlysis or summary of the results of function calls. Here are the available tools: <tools> ['{"type":"function","function":{"name":"superSecretTool","description":"Adds two numbers","parameters":{"properties":{"a":{"type":"integer"},"b":{"type":"integer"}},"required":["a","b"]}}}', '{"type":"function","function":{"name":"say","description":"Says something out loud (TTS)","parameters":{"properties":{"text":{"description":"The text to say out loud","type":"string"}},"required":["text"]}}}'] </tools> If the provided function signatures doesn't have the function you must call, you may write executable python code in markdown syntax and call code_interpreter() function as follows: <tool_call> {"arguments": {"code_markdown": <python-code>, "name": "code_interpreter"}} </tool_call> Make sure that the json object above with code markdown block is parseable with json.loads() and the XML block with XML ElementTree. Use the following pydantic model json schema for each tool call you will make: {'properties': {'arguments': {'title': 'Arguments', 'type': 'object'}, 'name': {'title': 'Name', 'type': 'string'}}, 'required': ['arguments', 'name'], 'title': 'FunctionCall', 'type': 'object'} At the very first turn you don't have <tool_results> so you shouldn't not make up the results.
Please keep a running summary with analysis of previous function results and summaries from previous iterations.
Do not stop calling functions until the task has been accomplished or you've reached max iteration of 10.
Calling multiple functions at once can overload the system and increase cost so call one function at a time please.
@ -910,18 +1030,11 @@ For each function call return a valid json object (using doulbe quotes) with fun
Grammar:
```js
add-tool-call ::= "{" space add-tool-call-name-kv "," space add-tool-call-arguments-kv "}" space
add-tool-call-arguments ::= "{" space add-tool-call-arguments-a-kv "," space add-tool-call-arguments-b-kv "}" space
add-tool-call-arguments-a-kv ::= "\"a\"" space ":" space integer
add-tool-call-arguments-b-kv ::= "\"b\"" space ":" space integer
add-tool-call-arguments-kv ::= "\"arguments\"" space ":" space add-tool-call-arguments
add-tool-call-name ::= "\"add\""
add-tool-call-name-kv ::= "\"name\"" space ":" space add-tool-call-name
content ::= [^<] | "<" [^t<] | "<t" [^o<]
decimal-part ::= [0-9] [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]?
integer ::= ("-"? integral-part) space
integral-part ::= [0-9] | [1-9] [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]?
root ::= content* (tool-call+ content*)?
root ::= content* tool-call?
say-tool-call ::= "{" space say-tool-call-name-kv "," space say-tool-call-arguments-kv "}" space
say-tool-call-arguments ::= "{" space say-tool-call-arguments-text-kv "}" space
say-tool-call-arguments-kv ::= "\"arguments\"" space ":" space say-tool-call-arguments
@ -933,7 +1046,14 @@ string ::= "\"" (
[^"\\] |
"\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F])
)* "\"" space
tool-call ::= "<tool_call>" space (add-tool-call | say-tool-call) space "</tool_call>"
superSecretTool-tool-call ::= "{" space superSecretTool-tool-call-name-kv "," space superSecretTool-tool-call-arguments-kv "}" space
superSecretTool-tool-call-arguments ::= "{" space superSecretTool-tool-call-arguments-a-kv "," space superSecretTool-tool-call-arguments-b-kv "}" space
superSecretTool-tool-call-arguments-a-kv ::= "\"a\"" space ":" space integer
superSecretTool-tool-call-arguments-b-kv ::= "\"b\"" space ":" space integer
superSecretTool-tool-call-arguments-kv ::= "\"arguments\"" space ":" space superSecretTool-tool-call-arguments
superSecretTool-tool-call-name ::= "\"superSecretTool\""
superSecretTool-tool-call-name-kv ::= "\"name\"" space ":" space superSecretTool-tool-call-name
tool-call ::= "<tool_call>" space (superSecretTool-tool-call | say-tool-call) space "</tool_call>"
```
@ -972,8 +1092,7 @@ Template:
Prompt:
```js
<s>[INST] What is the sum of 2535 squared and 32222000403 then multiplied by one and a half. What's a third of the result? [/INST] ?
<tool_call>{"id": "call_531873", "type": "function", "function": {"name": "add", "arguments": {"a": 2535, "b": 32222000403}}}</tool_call> </s><s>[INST] [TOOL RESULT(name=add, id=call_531873]32222002938[/TOOL RESULT] [/INST]
<s>[INST] Add two numbers for the purpose of this test. [/INST] <tool_call>{"id": "call_531873", "type": "function", "function": {"name": "superSecretTool", "arguments": {"a": 2535, "b": 32222000403}}}</tool_call> </s><s>[INST] [TOOL(name=superSecretTool, id=call_531873)]32222002938[/TOOL] [/INST] The sum of 2535 and 32222000403 is 42. </s>
```
@ -991,7 +1110,7 @@ Here are the tools available:
{
"type": "function",
"function": {
"name": "add",
"name": "superSecretTool",
"description": "Adds two numbers",
"parameters": {
"properties": {
@ -1034,18 +1153,11 @@ Here are the tools available:
Grammar:
```js
add-tool-call ::= "{" space add-tool-call-name-kv "," space add-tool-call-arguments-kv "}" space
add-tool-call-arguments ::= "{" space add-tool-call-arguments-a-kv "," space add-tool-call-arguments-b-kv "}" space
add-tool-call-arguments-a-kv ::= "\"a\"" space ":" space integer
add-tool-call-arguments-b-kv ::= "\"b\"" space ":" space integer
add-tool-call-arguments-kv ::= "\"arguments\"" space ":" space add-tool-call-arguments
add-tool-call-name ::= "\"add\""
add-tool-call-name-kv ::= "\"name\"" space ":" space add-tool-call-name
content ::= [^<] | "<" [^t<] | "<t" [^o<]
decimal-part ::= [0-9] [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]?
integer ::= ("-"? integral-part) space
integral-part ::= [0-9] | [1-9] [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]?
root ::= content* (tool-call+ content*)?
root ::= content* tool-call?
say-tool-call ::= "{" space say-tool-call-name-kv "," space say-tool-call-arguments-kv "}" space
say-tool-call-arguments ::= "{" space say-tool-call-arguments-text-kv "}" space
say-tool-call-arguments-kv ::= "\"arguments\"" space ":" space say-tool-call-arguments
@ -1057,7 +1169,14 @@ string ::= "\"" (
[^"\\] |
"\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F])
)* "\"" space
tool-call ::= "<tool_call>" space (add-tool-call | say-tool-call) space "</tool_call>"
superSecretTool-tool-call ::= "{" space superSecretTool-tool-call-name-kv "," space superSecretTool-tool-call-arguments-kv "}" space
superSecretTool-tool-call-arguments ::= "{" space superSecretTool-tool-call-arguments-a-kv "," space superSecretTool-tool-call-arguments-b-kv "}" space
superSecretTool-tool-call-arguments-a-kv ::= "\"a\"" space ":" space integer
superSecretTool-tool-call-arguments-b-kv ::= "\"b\"" space ":" space integer
superSecretTool-tool-call-arguments-kv ::= "\"arguments\"" space ":" space superSecretTool-tool-call-arguments
superSecretTool-tool-call-name ::= "\"superSecretTool\""
superSecretTool-tool-call-name-kv ::= "\"name\"" space ":" space superSecretTool-tool-call-name
tool-call ::= "<tool_call>" space (superSecretTool-tool-call | say-tool-call) space "</tool_call>"
```
@ -1097,7 +1216,7 @@ Call one or more functions to assist with the user query, every time this is pos
{
"type": "function",
"function": {
"name": "add",
"name": "superSecretTool",
"description": "Adds two numbers",
"parameters": {
"properties": {
@ -1145,18 +1264,11 @@ To call each function, give its name and arguments within <tool_call></tool_call
Grammar:
```js
add-tool-call ::= "{" space add-tool-call-name-kv "," space add-tool-call-arguments-kv "}" space
add-tool-call-arguments ::= "{" space add-tool-call-arguments-a-kv "," space add-tool-call-arguments-b-kv "}" space
add-tool-call-arguments-a-kv ::= "\"a\"" space ":" space integer
add-tool-call-arguments-b-kv ::= "\"b\"" space ":" space integer
add-tool-call-arguments-kv ::= "\"arguments\"" space ":" space add-tool-call-arguments
add-tool-call-name ::= "\"add\""
add-tool-call-name-kv ::= "\"name\"" space ":" space add-tool-call-name
content ::= [^<] | "<" [^t<] | "<t" [^o<]
decimal-part ::= [0-9] [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]?
integer ::= ("-"? integral-part) space
integral-part ::= [0-9] | [1-9] [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]?
root ::= content* (tool-call+ content*)?
root ::= content* tool-call?
say-tool-call ::= "{" space say-tool-call-name-kv "," space say-tool-call-arguments-kv "}" space
say-tool-call-arguments ::= "{" space say-tool-call-arguments-text-kv "}" space
say-tool-call-arguments-kv ::= "\"arguments\"" space ":" space say-tool-call-arguments
@ -1168,7 +1280,14 @@ string ::= "\"" (
[^"\\] |
"\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F])
)* "\"" space
tool-call ::= "<tool_call>" space (add-tool-call | say-tool-call) space "</tool_call>"
superSecretTool-tool-call ::= "{" space superSecretTool-tool-call-name-kv "," space superSecretTool-tool-call-arguments-kv "}" space
superSecretTool-tool-call-arguments ::= "{" space superSecretTool-tool-call-arguments-a-kv "," space superSecretTool-tool-call-arguments-b-kv "}" space
superSecretTool-tool-call-arguments-a-kv ::= "\"a\"" space ":" space integer
superSecretTool-tool-call-arguments-b-kv ::= "\"b\"" space ":" space integer
superSecretTool-tool-call-arguments-kv ::= "\"arguments\"" space ":" space superSecretTool-tool-call-arguments
superSecretTool-tool-call-name ::= "\"superSecretTool\""
superSecretTool-tool-call-name-kv ::= "\"name\"" space ":" space superSecretTool-tool-call-name
tool-call ::= "<tool_call>" space (superSecretTool-tool-call | say-tool-call) space "</tool_call>"
```
@ -1208,7 +1327,7 @@ Here are the tools available:
{
"type": "function",
"function": {
"name": "add",
"name": "superSecretTool",
"description": "Adds two numbers",
"parameters": {
"properties": {
@ -1257,22 +1376,24 @@ Please respond in JSON format with the following schema: {
{
"properties": {
"tool_calls": {
"prefixItems": {
"properties": {
"name": {
"title": "Name of the tool to call",
"type": "string"
"prefixItems": [
{
"properties": {
"name": {
"title": "Name of the tool to call",
"type": "string"
},
"arguments": {
"title": "Arguments to pass to the tool",
"type": "object"
}
},
"arguments": {
"title": "Arguments to pass to the tool",
"type": "object"
}
},
"required": [
"name",
"arguments"
]
}
"required": [
"name",
"arguments"
]
}
]
}
},
"required": [
@ -1310,22 +1431,22 @@ integer ::= ("-"? integral-part) space
integral-part ::= [0-9] | [1-9] [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]?
next-step ::= next-step-0 | next-step-1
next-step-0 ::= "{" space next-step-0-tool-calls-kv "}" space
next-step-0-tool-calls ::= "[" space ( next-step-0-tool-calls-item ( "," space next-step-0-tool-calls-item )* )? "]" space
next-step-0-tool-calls-item ::= next-step-0-tool-calls-item-0 | next-step-0-tool-calls-item-1
next-step-0-tool-calls-item-0 ::= "{" space next-step-0-tool-calls-item-0-name-kv "," space next-step-0-tool-calls-item-0-arguments-kv "}" space
next-step-0-tool-calls-item-0-arguments ::= "{" space next-step-0-tool-calls-item-0-arguments-a-kv "," space next-step-0-tool-calls-item-0-arguments-b-kv "}" space
next-step-0-tool-calls-item-0-arguments-a-kv ::= "\"a\"" space ":" space integer
next-step-0-tool-calls-item-0-arguments-b-kv ::= "\"b\"" space ":" space integer
next-step-0-tool-calls-item-0-arguments-kv ::= "\"arguments\"" space ":" space next-step-0-tool-calls-item-0-arguments
next-step-0-tool-calls-item-0-name ::= "\"add\""
next-step-0-tool-calls-item-0-name-kv ::= "\"name\"" space ":" space next-step-0-tool-calls-item-0-name
next-step-0-tool-calls-item-1 ::= "{" space next-step-0-tool-calls-item-1-name-kv "," space next-step-0-tool-calls-item-1-arguments-kv "}" space
next-step-0-tool-calls-item-1-arguments ::= "{" space next-step-0-tool-calls-item-1-arguments-text-kv "}" space
next-step-0-tool-calls-item-1-arguments-kv ::= "\"arguments\"" space ":" space next-step-0-tool-calls-item-1-arguments
next-step-0-tool-calls-item-1-arguments-text-kv ::= "\"text\"" space ":" space string
next-step-0-tool-calls-item-1-name ::= "\"say\""
next-step-0-tool-calls-item-1-name-kv ::= "\"name\"" space ":" space next-step-0-tool-calls-item-1-name
next-step-0-tool-calls ::= "[" space next-step-0-tool-calls-tuple-0 "]" space
next-step-0-tool-calls-kv ::= "\"tool_calls\"" space ":" space next-step-0-tool-calls
next-step-0-tool-calls-tuple-0 ::= next-step-0-tool-calls-tuple-0-0 | next-step-0-tool-calls-tuple-0-1
next-step-0-tool-calls-tuple-0-0 ::= "{" space next-step-0-tool-calls-tuple-0-0-name-kv "," space next-step-0-tool-calls-tuple-0-0-arguments-kv "}" space
next-step-0-tool-calls-tuple-0-0-arguments ::= "{" space next-step-0-tool-calls-tuple-0-0-arguments-a-kv "," space next-step-0-tool-calls-tuple-0-0-arguments-b-kv "}" space
next-step-0-tool-calls-tuple-0-0-arguments-a-kv ::= "\"a\"" space ":" space integer
next-step-0-tool-calls-tuple-0-0-arguments-b-kv ::= "\"b\"" space ":" space integer
next-step-0-tool-calls-tuple-0-0-arguments-kv ::= "\"arguments\"" space ":" space next-step-0-tool-calls-tuple-0-0-arguments
next-step-0-tool-calls-tuple-0-0-name ::= "\"superSecretTool\""
next-step-0-tool-calls-tuple-0-0-name-kv ::= "\"name\"" space ":" space next-step-0-tool-calls-tuple-0-0-name
next-step-0-tool-calls-tuple-0-1 ::= "{" space next-step-0-tool-calls-tuple-0-1-name-kv "," space next-step-0-tool-calls-tuple-0-1-arguments-kv "}" space
next-step-0-tool-calls-tuple-0-1-arguments ::= "{" space next-step-0-tool-calls-tuple-0-1-arguments-text-kv "}" space
next-step-0-tool-calls-tuple-0-1-arguments-kv ::= "\"arguments\"" space ":" space next-step-0-tool-calls-tuple-0-1-arguments
next-step-0-tool-calls-tuple-0-1-arguments-text-kv ::= "\"text\"" space ":" space string
next-step-0-tool-calls-tuple-0-1-name ::= "\"say\""
next-step-0-tool-calls-tuple-0-1-name-kv ::= "\"name\"" space ":" space next-step-0-tool-calls-tuple-0-1-name
next-step-1 ::= "{" space next-step-1-result-kv "}" space
next-step-1-result-kv ::= "\"result\"" space ":" space integer
next-step-kv ::= "\"next_step\"" space ":" space next-step
@ -1370,7 +1491,7 @@ space ::= " "?
Prompt:
```json
You are a function calling AI agent with self-recursion. You can call only one function at a time and analyse data you get from function response. You are provided with function signatures within <tools></tools> XML tags. The current date is: 2024-03-29. You may use agentic frameworks for reasoning and planning to help with user query. Please call a function and wait for function results to be provided to you in the next iteration. Don't make assumptions about what values to plug into function arguments. Once you have called a function, results will be fed back to you within <tool_response></tool_response> XML tags. Don't make assumptions about tool results if <tool_response> XML tags are not present since function hasn't been executed yet. Analyze the data once you get the results and call another function. At each iteration please continue adding the your analysis to previous summary. Your final response should directly answer the user query with an anlysis or summary of the results of function calls. Here are the available tools: <tools> ['{"type":"function","function":{"name":"add","description":"Adds two numbers","parameters":{"properties":{"a":{"type":"integer"},"b":{"type":"integer"}},"required":["a","b"]}}}', '{"type":"function","function":{"name":"say","description":"Says something out loud (TTS)","parameters":{"properties":{"text":{"description":"The text to say out loud","type":"string"}},"required":["text"]}}}'] </tools> If the provided function signatures doesn't have the function you must call, you may write executable python code in markdown syntax and call code_interpreter() function as follows: <tool_call> {"arguments": {"code_markdown": <python-code>, "name": "code_interpreter"}} </tool_call> Make sure that the json object above with code markdown block is parseable with json.loads() and the XML block with XML ElementTree. Use the following pydantic model json schema for each tool call you will make: {'properties': {'arguments': {'title': 'Arguments', 'type': 'object'}, 'name': {'title': 'Name', 'type': 'string'}}, 'required': ['arguments', 'name'], 'title': 'FunctionCall', 'type': 'object'} At the very first turn you don't have <tool_results> so you shouldn't not make up the results.
You are a function calling AI agent with self-recursion. You can call only one function at a time and analyse data you get from function response. You are provided with function signatures within <tools></tools> XML tags. The current date is: 2024-03-30. You may use agentic frameworks for reasoning and planning to help with user query. Please call a function and wait for function results to be provided to you in the next iteration. Don't make assumptions about what values to plug into function arguments. Once you have called a function, results will be fed back to you within <tool_response></tool_response> XML tags. Don't make assumptions about tool results if <tool_response> XML tags are not present since function hasn't been executed yet. Analyze the data once you get the results and call another function. At each iteration please continue adding the your analysis to previous summary. Your final response should directly answer the user query with an anlysis or summary of the results of function calls. Here are the available tools: <tools> ['{"type":"function","function":{"name":"superSecretTool","description":"Adds two numbers","parameters":{"properties":{"a":{"type":"integer"},"b":{"type":"integer"}},"required":["a","b"]}}}', '{"type":"function","function":{"name":"say","description":"Says something out loud (TTS)","parameters":{"properties":{"text":{"description":"The text to say out loud","type":"string"}},"required":["text"]}}}'] </tools> If the provided function signatures doesn't have the function you must call, you may write executable python code in markdown syntax and call code_interpreter() function as follows: <tool_call> {"arguments": {"code_markdown": <python-code>, "name": "code_interpreter"}} </tool_call> Make sure that the json object above with code markdown block is parseable with json.loads() and the XML block with XML ElementTree. Use the following pydantic model json schema for each tool call you will make: {'properties': {'arguments': {'title': 'Arguments', 'type': 'object'}, 'name': {'title': 'Name', 'type': 'string'}}, 'required': ['arguments', 'name'], 'title': 'FunctionCall', 'type': 'object'} At the very first turn you don't have <tool_results> so you shouldn't not make up the results.
Please keep a running summary with analysis of previous function results and summaries from previous iterations.
Do not stop calling functions until the task has been accomplished or you've reached max iteration of 10.
Calling multiple functions at once can overload the system and increase cost so call one function at a time please.
@ -1386,18 +1507,11 @@ For each function call return a valid json object (using doulbe quotes) with fun
Grammar:
```js
add-tool-call ::= "{" space add-tool-call-name-kv "," space add-tool-call-arguments-kv "}" space
add-tool-call-arguments ::= "{" space add-tool-call-arguments-a-kv "," space add-tool-call-arguments-b-kv "}" space
add-tool-call-arguments-a-kv ::= "\"a\"" space ":" space integer
add-tool-call-arguments-b-kv ::= "\"b\"" space ":" space integer
add-tool-call-arguments-kv ::= "\"arguments\"" space ":" space add-tool-call-arguments
add-tool-call-name ::= "\"add\""
add-tool-call-name-kv ::= "\"name\"" space ":" space add-tool-call-name
content ::= [^<] | "<" [^t<] | "<t" [^o<]
decimal-part ::= [0-9] [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]?
integer ::= ("-"? integral-part) space
integral-part ::= [0-9] | [1-9] [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]? [0-9]?
root ::= content* (tool-call+ content*)?
root ::= content* tool-call?
say-tool-call ::= "{" space say-tool-call-name-kv "," space say-tool-call-arguments-kv "}" space
say-tool-call-arguments ::= "{" space say-tool-call-arguments-text-kv "}" space
say-tool-call-arguments-kv ::= "\"arguments\"" space ":" space say-tool-call-arguments
@ -1409,7 +1523,14 @@ string ::= "\"" (
[^"\\] |
"\\" (["\\/bfnrt] | "u" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F])
)* "\"" space
tool-call ::= "<tool_call>" space (add-tool-call | say-tool-call) space "</tool_call>"
superSecretTool-tool-call ::= "{" space superSecretTool-tool-call-name-kv "," space superSecretTool-tool-call-arguments-kv "}" space
superSecretTool-tool-call-arguments ::= "{" space superSecretTool-tool-call-arguments-a-kv "," space superSecretTool-tool-call-arguments-b-kv "}" space
superSecretTool-tool-call-arguments-a-kv ::= "\"a\"" space ":" space integer
superSecretTool-tool-call-arguments-b-kv ::= "\"b\"" space ":" space integer
superSecretTool-tool-call-arguments-kv ::= "\"arguments\"" space ":" space superSecretTool-tool-call-arguments
superSecretTool-tool-call-name ::= "\"superSecretTool\""
superSecretTool-tool-call-name-kv ::= "\"name\"" space ":" space superSecretTool-tool-call-name
tool-call ::= "<tool_call>" space (superSecretTool-tool-call | say-tool-call) space "</tool_call>"
```

View file

@ -0,0 +1,251 @@
#
#
# python -m examples.openai.test_chat_handlers | tee examples/openai/test_chat_handlers.md
import json
from examples.openai.api import FunctionCall, Message, Tool, ToolCall, ToolFunction
from examples.openai.prompting import ChatHandlerArgs, ChatTemplate, ToolsPromptStyle, get_chat_handler
TEST_ARG_A = 2535
TEST_ARG_B = 32222000403
TEST_SUM = 32222002938
QUESTION = "Add two numbers for the purpose of this test."
ANSWER = "The sum of 2535 and 32222000403 is 42."
PROMPT_MESSAGE = Message(
role="user",
content=QUESTION,
)
ASSIST_MESSAGE = Message(
role="assistant",
content=ANSWER,
)
TOOL_NAME = "superSecretTool"
TOOL_CALL = ToolCall(
id="call_531873",
type="function",
function=FunctionCall(
name=TOOL_NAME,
arguments={
"a": TEST_ARG_A,
"b": TEST_ARG_B
}
)
)
TOOL_CALL_MESSAGE = Message(
role="assistant",
content=None,
tool_calls=[TOOL_CALL],
)
TEST_THOUGHT = "I've thought a lot about this."
THOUGHTFUL_TOOL_CALL_MESSAGE = Message(
role="assistant",
content=TEST_THOUGHT,
tool_calls=[TOOL_CALL],
)
# UNDERSCORE_ESCAPED_TOOL_CALL_MESSAGE = Message(**{
# **TOOL_CALL_MESSAGE.model_dump(),
# "tool_calls": [
# json.loads(tc.model_dump_json().replace("_", "\\_"))
# for tc in TOOL_CALL_MESSAGE.tool_calls
# ],
# })
TOOL_MESSAGE = Message(
role="tool",
name=TOOL_NAME,
tool_call_id="call_531873",
content=f'{TEST_SUM}',
tool_calls=None
)
TEST_MESSAGES = [
PROMPT_MESSAGE,
TOOL_CALL_MESSAGE,
TOOL_MESSAGE,
ASSIST_MESSAGE,
]
TEST_MESSAGES_THOUGHT = [
PROMPT_MESSAGE,
THOUGHTFUL_TOOL_CALL_MESSAGE,
TOOL_MESSAGE,
ASSIST_MESSAGE,
]
TEST_TOOLS = [
Tool(
type="function",
function=ToolFunction(
name=TOOL_NAME,
description="Adds two numbers",
parameters={
"properties": {
"a": {"type": "integer"},
"b": {"type": "integer"},
},
"required": ["a", "b"]
}
)
),
Tool(
type="function",
function=ToolFunction(
name="say",
description="Says something out loud (TTS)",
parameters={
"properties": {
"text": {
"description": "The text to say out loud",
"type": "string"
},
},
"required": ["text"]
}
)
)
]
TEST_OUTPUT_SCHEMA = {"type": "integer"}
# Generate the JSON for TEST_TEMPLATES below by uncommenting this block:
#
# TEST_TEMPLATES = {
# 'mistral_instruct_v0_1': ChatTemplate.from_huggingface("mistralai/Mixtral-8x7B-Instruct-v0.1"),
# 'functionary_v2_2': ChatTemplate.from_huggingface("meetkai/functionary-small-v2.2"),
# 'hermes_2_pro_mistral': ChatTemplate.from_huggingface("NousResearch/Hermes-2-Pro-Mistral-7B"),
# 'llama2': ChatTemplate.from_huggingface("meta-llama/Llama-2-7b-chat-hf"),
# }
# print(json.dumps({k: v.model_dump() for k, v in TEST_TEMPLATES.items()}, indent=2))
# exit(0)
TEST_TEMPLATES = {
"mistral_instruct_v0_1": {
"template": "{{ bos_token }}{% for message in messages %}{% if (message['role'] == 'user') != (loop.index0 % 2 == 0) %}{{ raise_exception('Conversation roles must alternate user/assistant/user/assistant/...') }}{% endif %}{% if message['role'] == 'user' %}{{ '[INST] ' + message['content'] + ' [/INST]' }}{% elif message['role'] == 'assistant' %}{{ message['content'] + eos_token}}{% else %}{{ raise_exception('Only user and assistant roles are supported!') }}{% endif %}{% endfor %}",
"eos_token": "</s>",
"bos_token": "<s>"
},
"functionary_v2_2": {
"template": "{#v2.2#}\n{% for message in messages %}\n{% if message['role'] == 'user' or message['role'] == 'system' %}\n{{ '<|from|>' + message['role'] + '\n<|recipient|>all\n<|content|>' + message['content'] + '\n' }}{% elif message['role'] == 'tool' %}\n{{ '<|from|>' + message['name'] + '\n<|recipient|>all\n<|content|>' + message['content'] + '\n' }}{% else %}\n{% set contain_content='no'%}\n{% if message['content'] is not none %}\n{{ '<|from|>assistant\n<|recipient|>all\n<|content|>' + message['content'] }}{% set contain_content='yes'%}\n{% endif %}\n{% if 'tool_calls' in message and message['tool_calls'] is not none %}\n{% for tool_call in message['tool_calls'] %}\n{% set prompt='<|from|>assistant\n<|recipient|>' + tool_call['function']['name'] + '\n<|content|>' + tool_call['function']['arguments'] %}\n{% if loop.index == 1 and contain_content == \"no\" %}\n{{ prompt }}{% else %}\n{{ '\n' + prompt}}{% endif %}\n{% endfor %}\n{% endif %}\n{{ '<|stop|>\n' }}{% endif %}\n{% endfor %}\n{% if add_generation_prompt %}{{ '<|from|>assistant\n<|recipient|>' }}{% endif %}",
"eos_token": "</s>",
"bos_token": "<s>"
},
"hermes_2_pro_mistral": {
"template": "{% for message in messages %}{{'<|im_start|>' + message['role'] + '\n' + message['content'] + '<|im_end|>' + '\n'}}{% endfor %}{% if add_generation_prompt %}{{ '<|im_start|>assistant\n' }}{% endif %}",
"eos_token": "<|im_end|>",
"bos_token": "<s>"
},
"llama2": {
"template": "{% if messages[0]['role'] == 'system' %}{% set loop_messages = messages[1:] %}{% set system_message = messages[0]['content'] %}{% else %}{% set loop_messages = messages %}{% set system_message = false %}{% endif %}{% for message in loop_messages %}{% if (message['role'] == 'user') != (loop.index0 % 2 == 0) %}{{ raise_exception('Conversation roles must alternate user/assistant/user/assistant/...') }}{% endif %}{% if loop.index0 == 0 and system_message != false %}{% set content = '<<SYS>>\\n' + system_message + '\\n<</SYS>>\\n\\n' + message['content'] %}{% else %}{% set content = message['content'] %}{% endif %}{% if message['role'] == 'user' %}{{ bos_token + '[INST] ' + content.strip() + ' [/INST]' }}{% elif message['role'] == 'assistant' %}{{ ' ' + content.strip() + ' ' + eos_token }}{% endif %}{% endfor %}",
"eos_token": "</s>",
"bos_token": "<s>"
},
}
TEST_TEMPLATES = {k: ChatTemplate(**v) for k, v in TEST_TEMPLATES.items()}
if __name__ == "__main__":
failures = []
print(f'\nMessages:\n\n```js\n{json.dumps([m.model_dump() for m in TEST_MESSAGES], indent=2)}\n```\n')
for model_name, chat_template in TEST_TEMPLATES.items():
# if model_name == 'hermes_2_pro_mistral':
# print("Skipping hermes_2_pro_mistral")
# continue
def check_finds(msgs, strings_to_find):
prompt = chat_template.render(msgs, add_generation_prompt=True)
for s in strings_to_find:
if str(s) not in prompt:
failures.append(f"Missing {s} in prompt for {model_name}:\n{prompt}")
check_finds([PROMPT_MESSAGE], (QUESTION,))
check_finds([ASSIST_MESSAGE], (ANSWER,))
check_finds([TOOL_CALL_MESSAGE], (TEST_ARG_A, TEST_ARG_B, TOOL_NAME))
check_finds([THOUGHTFUL_TOOL_CALL_MESSAGE], (TEST_THOUGHT, TEST_ARG_A, TEST_ARG_B, TOOL_NAME,))
check_finds([TOOL_MESSAGE], (TEST_SUM,))
if chat_template.potentially_supports_parallel_calls:
check_finds([TOOL_MESSAGE], (TOOL_NAME,))
print(f"\n# {model_name}\n")
print(f'\nTemplate:\n\n```js\n{chat_template.template}\n```\n')
print(f'\nPrompt:\n\n```js\n{chat_template.render(TEST_MESSAGES, add_generation_prompt=True)}\n```\n')
argss = {
"with tools": ChatHandlerArgs(
chat_template=chat_template, #ChatTemplate.from_gguf(GGUFKeyValues(model)),
response_schema=TEST_OUTPUT_SCHEMA,
tools=TEST_TOOLS,
),
"without tools": ChatHandlerArgs(
chat_template=chat_template, #ChatTemplate.from_gguf(GGUFKeyValues(model)),
response_schema=TEST_OUTPUT_SCHEMA,
tools=[],
),
}
for style in ToolsPromptStyle:
if (style == ToolsPromptStyle.TYPESCRIPT_FUNCTIONARY_V2) != (model_name.startswith("functionary")):
continue
if style == ToolsPromptStyle.TOOLS_MIXTRAL and model_name != "mistral_instruct_v0_1":
continue
if model_name == "mistral_instruct_v0_1" and style not in (ToolsPromptStyle.TOOLS_THOUGHTFUL_STEPS, ToolsPromptStyle.TOOLS_MIXTRAL):
continue
print(f'\n## {style}\n')
for tn, args in argss.items():
ch = get_chat_handler(args, parallel_calls=True, tool_style=style)
print(f'\n### {tn}\n')
print(f'\nPrompt:\n\n```json\n{ch.output_format_prompt.content}\n```\n')
print(f'\nGrammar:\n\n```js\n{ch.grammar}\n```\n')
if failures:
for f in failures:
print(f'{f}\n\n')
assert not failures
# test_templates([
# Message(**{
# "role": "user",
# "name": None,
# "tool_call_id": None,
# "content": "What is the sum of 2535 squared and 32222000403 then multiplied by one and a half. What's a third of the result?",
# "tool_calls": None
# }),
# Message(**{
# "role": "assistant",
# # "name": None,
# "tool_call_id": None,
# "content": "?",
# "tool_calls": [
# {
# # "id": "call_531873",
# "type": "function",
# "function": {
# "name": TOOL_NAME,
# "arguments": {
# "a": 2535,
# "b": 32222000403
# }
# }
# }
# ]
# }),
# Message(**{
# "role": "tool",
# "name": TOOL_NAME,
# "tool_call_id": "call_531873",
# "content": "32222002938",
# "tool_calls": None
# })
# ])