From ee2d2e764007f0a745583477b8f707a8e9d9c21d Mon Sep 17 00:00:00 2001 From: Alex O'Connell Date: Sat, 20 Dec 2025 10:24:08 -0500 Subject: [PATCH] lets try functiongemma instead --- data/generate_data.py | 65 +++-- data/tools.py | 224 +++++++++--------- train/{ => chat_templates}/chatml_template.j2 | 0 .../{ => chat_templates}/gemma3_withtools.j2 | 15 +- train/{ => chat_templates}/zephyr_legacy.j2 | 0 .../zephyr_legacy_ollama.gotmpl | 0 train/functiongemma-270m.yml | 43 ++++ train/gemma3-270m.yml | 13 +- 8 files changed, 206 insertions(+), 154 deletions(-) rename train/{ => chat_templates}/chatml_template.j2 (100%) rename train/{ => chat_templates}/gemma3_withtools.j2 (75%) rename train/{ => chat_templates}/zephyr_legacy.j2 (100%) rename train/{ => chat_templates}/zephyr_legacy_ollama.gotmpl (100%) create mode 100644 train/functiongemma-270m.yml diff --git a/data/generate_data.py b/data/generate_data.py index 1b3b99a..68ec617 100644 --- a/data/generate_data.py +++ b/data/generate_data.py @@ -520,7 +520,7 @@ def generate_status_request(template: dict, persona: str, language: str, max_dev else: return result -def format_example_sharegpt(example, persona, language, use_system_role, use_service_names): +def format_example_sharegpt(example, persona, language, use_system_role, use_service_names, tool_response_format): piles = get_dataset_piles(language) sys_prompt = generate_system_prompt(example, persona, language, piles.pile_of_system_prompts) question = example["question"] @@ -567,21 +567,26 @@ def format_example_sharegpt(example, persona, language, use_system_role, use_ser ] if len(tool_calls) > 0: - conversation.extend([ - { - "role": "assistant", - "content": [{ "type": "text", "text": answer_starting }], - "tool_calls": tool_calls - }, - { + assistant_starting_block = { + "role": "assistant", + "content": [{ "type": "text", "text": answer_starting }], + "tool_calls": [ { "function": tc } for tc in tool_calls ] + } + if tool_response_format == "text": + tool_response_block = { "role": "tool", "content": [{ "type": "text", "text": json.dumps(result) } for result in tool_results] - }, - { - "role": "assistant", - "content": [{ "type": "text", "text": answers }], - }, - ]) + } + elif tool_response_format == "functiongemma": + tool_response_block = { + "role": "tool", + "content": [{ "name": result["tool_name"], "response": {"result": result["tool_result"]} } for result in tool_results] + } + assistant_confirmation_block = { + "role": "assistant", + "content": [{ "type": "text", "text": answers }], + } + conversation.extend([assistant_starting_block, tool_response_block, assistant_confirmation_block]) else: conversation.extend([ { @@ -595,7 +600,19 @@ def format_example_sharegpt(example, persona, language, use_system_role, use_ser "tools": SERVICE_TOOLS if use_service_names else HASS_TOOLS } -def generate_sft_file(filename: str, seed: int, format_func: Callable, use_system_role: bool, use_service_names: bool, personas: list[str], language: str, *, static_factor: float, template_factor: int, status_request_factor: int): +def generate_sft_file( + filename: str, + seed: int, + format_func: Callable, + use_system_role: bool, + use_service_names: bool, + personas: list[str], + language: str, + tool_response_format: str, + *, + static_factor: float, + template_factor: int, + status_request_factor: int): random.seed(seed) np.random.seed(seed) piles = get_dataset_piles(language) @@ -605,10 +622,10 @@ def generate_sft_file(filename: str, seed: int, format_func: Callable, use_syste def run_factor_times(func, examples, data, persona, factor, language): if factor >= 1: for i in range(factor): - examples.append(format_func(func(data, persona, language, use_service_names=use_service_names), persona, language, use_system_role, use_service_names)) + examples.append(format_func(func(data, persona, language, use_service_names=use_service_names), persona, language, use_system_role, use_service_names, tool_response_format)) else: if random.random() < factor: - examples.append(format_func(func(data, persona, language, use_service_names=use_service_names), persona, language, use_system_role, use_service_names)) + examples.append(format_func(func(data, persona, language, use_service_names=use_service_names), persona, language, use_system_role, use_service_names, tool_response_format)) generated_examples = [] @@ -680,6 +697,7 @@ def main(args=None): parser.add_argument("--train", action="store_true", help="Set this flag to enable generation of the train dataset.") parser.add_argument("--language", nargs="+", default=["english"], help="List of languages to generate: english, german, french, spanish, polish") parser.add_argument("--no-system-role", action="store_true", help="Set this flag to disable the system role. It will be combined with the user role") + parser.add_argument("--tool-response-format", default="text", choices=["text", "functiongemma"], help="Format to use for tool responses.") train_size_group = parser.add_mutually_exclusive_group() train_size_group.add_argument('--small', action='store_const', const='small', dest='size') @@ -704,6 +722,7 @@ def main(args=None): use_system_role = not args.no_system_role use_service_names = args.use_service_names + tool_response_format = args.tool_response_format for language in args.language: piles = get_dataset_piles(language) @@ -711,20 +730,20 @@ def main(args=None): suffix = f"_{language}" if len(args.language) > 1 else "" if args.sample: - generate_sft_file(f"sample{suffix}", 42, format_func, use_system_role, use_service_names, personas, language, static_factor=1, template_factor=1, status_request_factor=1) + generate_sft_file(f"sample{suffix}", 42, format_func, use_system_role, use_service_names, personas, language, tool_response_format, static_factor=1, template_factor=1, status_request_factor=1) if args.train: if args.size == "small": - generate_sft_file(f"home_assistant_train{suffix}", 42, format_func, use_system_role, use_service_names, personas, language, static_factor=1, template_factor=10, status_request_factor=8) + generate_sft_file(f"home_assistant_train{suffix}", 42, format_func, use_system_role, use_service_names, personas, language, tool_response_format, static_factor=1, template_factor=10, status_request_factor=8) elif args.size == "medium": - generate_sft_file(f"home_assistant_train{suffix}", 42, format_func, use_system_role, use_service_names, personas, language, static_factor=5, template_factor=15, status_request_factor=12) + generate_sft_file(f"home_assistant_train{suffix}", 42, format_func, use_system_role, use_service_names, personas, language, tool_response_format, static_factor=5, template_factor=15, status_request_factor=12) elif args.size == "large": - generate_sft_file(f"home_assistant_train{suffix}", 42, format_func, use_system_role, use_service_names, personas, language, static_factor=5, template_factor=20, status_request_factor=15) + generate_sft_file(f"home_assistant_train{suffix}", 42, format_func, use_system_role, use_service_names, personas, language, tool_response_format, static_factor=5, template_factor=20, status_request_factor=15) elif args.size == "xl": - generate_sft_file(f"home_assistant_train{suffix}", 42, format_func, use_system_role, use_service_names, personas, language, static_factor=7, template_factor=25, status_request_factor=18) + generate_sft_file(f"home_assistant_train{suffix}", 42, format_func, use_system_role, use_service_names, personas, language, tool_response_format, static_factor=7, template_factor=25, status_request_factor=18) else: raise Exception(f"Unrecognized dataset size: {args.size}") if args.test: - generate_sft_file(f"home_assistant_test{suffix}", 12345, format_func, use_system_role, use_service_names, personas, language, static_factor=0.25, template_factor=1, status_request_factor=2) + generate_sft_file(f"home_assistant_test{suffix}", 12345, format_func, use_system_role, use_service_names, personas, language, tool_response_format, static_factor=0.25, template_factor=1, status_request_factor=2) if len(args.language) > 1: if args.sample: diff --git a/data/tools.py b/data/tools.py index a847cd4..eb7ade8 100644 --- a/data/tools.py +++ b/data/tools.py @@ -53,7 +53,7 @@ SERVICE_TO_TOOL_MAP = { # Home Assistant Intent Tools Definition HASS_TOOLS = [ - { + {"function": { "name": TOOL_TURN_ON, "description": "Turns on/opens/unlocks a device or entity", "parameters": { @@ -67,8 +67,8 @@ HASS_TOOLS = [ }, "required": [] } - }, - { + }}, + {"function": { "name": TOOL_TURN_OFF, "description": "Turns off/closes/locks a device or entity", "parameters": { @@ -82,8 +82,8 @@ HASS_TOOLS = [ }, "required": [] } - }, - { + }}, + {"function": { "name": TOOL_TOGGLE, "description": "Toggles a device or entity", "parameters": { @@ -97,8 +97,8 @@ HASS_TOOLS = [ }, "required": [] } - }, - { + }}, + {"function": { "name": TOOL_SET_POSITION, "description": "Sets the position of a device or entity (e.g., blinds, covers)", "parameters": { @@ -111,8 +111,8 @@ HASS_TOOLS = [ }, "required": ["position"] } - }, - { + }}, + {"function": { "name": TOOL_LIGHT_SET, "description": "Sets the brightness or color of a light", "parameters": { @@ -127,8 +127,8 @@ HASS_TOOLS = [ }, "required": [] } - }, - { + }}, + {"function": { "name": TOOL_CLIMATE_SET_TEMPERATURE, "description": "Sets the target temperature of a climate device", "parameters": { @@ -141,8 +141,8 @@ HASS_TOOLS = [ }, "required": ["temperature"] } - }, - { + }}, + {"function": { "name": TOOL_SET_HUMIDITY, "description": "Sets the target humidity level of a humidifier device", "parameters": { @@ -153,8 +153,8 @@ HASS_TOOLS = [ }, "required": ["name", "humidity"] } - }, - { + }}, + {"function": { "name": TOOL_SET_HUMIDIFIER_MODE, "description": "Sets the mode of a humidifier device", "parameters": { @@ -165,8 +165,8 @@ HASS_TOOLS = [ }, "required": ["name", "mode"] } - }, - { + }}, + {"function": { "name": TOOL_MEDIA_UNPAUSE, "description": "Resumes playback on a media player", "parameters": { @@ -178,8 +178,8 @@ HASS_TOOLS = [ }, "required": [] } - }, - { + }}, + {"function": { "name": TOOL_MEDIA_PAUSE, "description": "Pauses playback on a media player", "parameters": { @@ -191,8 +191,8 @@ HASS_TOOLS = [ }, "required": [] } - }, - { + }}, + {"function": { "name": TOOL_MEDIA_NEXT, "description": "Skips to the next media item on a media player", "parameters": { @@ -204,8 +204,8 @@ HASS_TOOLS = [ }, "required": [] } - }, - { + }}, + {"function": { "name": TOOL_SET_VOLUME, "description": "Sets the volume of a media player", "parameters": { @@ -218,8 +218,8 @@ HASS_TOOLS = [ }, "required": ["volume_level"] } - }, - { + }}, + {"function": { "name": TOOL_VACUUM_START, "description": "Starts a vacuum", "parameters": { @@ -231,8 +231,8 @@ HASS_TOOLS = [ }, "required": [] } - }, - { + }}, + {"function": { "name": TOOL_VACUUM_RETURN_TO_BASE, "description": "Returns a vacuum to its base", "parameters": { @@ -244,8 +244,8 @@ HASS_TOOLS = [ }, "required": [] } - }, - { + }}, + {"function": { "name": TOOL_LIST_ADD_ITEM, "description": "Adds an item to a todo list", "parameters": { @@ -256,8 +256,8 @@ HASS_TOOLS = [ }, "required": ["item"] } - }, - { + }}, + {"function": { "name": TOOL_START_TIMER, "description": "Starts a timer", "parameters": { @@ -268,8 +268,8 @@ HASS_TOOLS = [ }, "required": [] } - }, - { + }}, + {"function": { "name": TOOL_CANCEL_TIMER, "description": "Cancels a timer", "parameters": { @@ -279,8 +279,8 @@ HASS_TOOLS = [ }, "required": [] } - }, - { + }}, + {"function": { "name": TOOL_PAUSE_TIMER, "description": "Pauses a timer", "parameters": { @@ -290,8 +290,8 @@ HASS_TOOLS = [ }, "required": [] } - }, - { + }}, + {"function": { "name": TOOL_UNPAUSE_TIMER, "description": "Resumes a paused timer", "parameters": { @@ -301,7 +301,7 @@ HASS_TOOLS = [ }, "required": [] } - } + }} ] SERVICE_TOOL_ALLOWED_SERVICES = ["turn_on", "turn_off", "toggle", "press", "increase_speed", "decrease_speed", "open_cover", "close_cover", "stop_cover", "lock", "unlock", @@ -309,7 +309,7 @@ SERVICE_TOOL_ALLOWED_SERVICES = ["turn_on", "turn_off", "toggle", "press", "incr SERVICE_TOOL_ALLOWED_DOMAINS = ["light", "switch", "button", "fan", "cover", "lock", "media_player", "climate", "vacuum", "todo", "timer", "script"] SERVICE_TOOLS = [ - { + {"function": { "name": "", "description": "", "parameters": { @@ -330,8 +330,8 @@ SERVICE_TOOLS = [ "target_device" ] } - }, - { + }}, + {"function": { "name": "light.turn_on", "description": "", "parameters": { @@ -345,8 +345,8 @@ SERVICE_TOOLS = [ "target_device" ] } - }, - { + }}, + {"function": { "name": "light.turn_off", "description": "", "parameters": { @@ -358,8 +358,8 @@ SERVICE_TOOLS = [ "target_device" ] } - }, - { + }}, + {"function": { "name": "light.toggle", "description": "", "parameters": { @@ -371,8 +371,8 @@ SERVICE_TOOLS = [ "target_device" ] } - }, - { + }}, + {"function": { "name": "switch.turn_on", "description": "", "parameters": { @@ -384,8 +384,8 @@ SERVICE_TOOLS = [ "target_device" ] } - }, - { + }}, + {"function": { "name": "switch.turn_off", "description": "", "parameters": { @@ -397,8 +397,8 @@ SERVICE_TOOLS = [ "target_device" ] } - }, - { + }}, + {"function": { "name": "switch.toggle", "description": "", "parameters": { @@ -410,8 +410,8 @@ SERVICE_TOOLS = [ "target_device" ] } - }, - { + }}, + {"function": { "name": "fan.turn_on", "description": "", "parameters": { @@ -423,8 +423,8 @@ SERVICE_TOOLS = [ "target_device" ] } - }, - { + }}, + {"function": { "name": "fan.turn_off", "description": "", "parameters": { @@ -436,8 +436,8 @@ SERVICE_TOOLS = [ "target_device" ] } - }, - { + }}, + {"function": { "name": "fan.toggle", "description": "", "parameters": { @@ -449,8 +449,8 @@ SERVICE_TOOLS = [ "target_device" ] } - }, - { + }}, + {"function": { "name": "fan.set_speed", "description": "", "parameters": { @@ -464,8 +464,8 @@ SERVICE_TOOLS = [ "fan_mode" ] } - }, - { + }}, + {"function": { "name": "fan.increase_speed", "description": "", "parameters": { @@ -477,8 +477,8 @@ SERVICE_TOOLS = [ "target_device" ] } - }, - { + }}, + {"function": { "name": "fan.decrease_speed", "description": "", "parameters": { @@ -490,8 +490,8 @@ SERVICE_TOOLS = [ "target_device" ] } - }, - { + }}, + {"function": { "name": "button.press", "description": "", "parameters": { @@ -503,8 +503,8 @@ SERVICE_TOOLS = [ "target_device" ] } - }, - { + }}, + {"function": { "name": "cover.open_cover", "description": "", "parameters": { @@ -516,8 +516,8 @@ SERVICE_TOOLS = [ "target_device" ] } - }, - { + }}, + {"function": { "name": "cover.close_cover", "description": "", "parameters": { @@ -529,8 +529,8 @@ SERVICE_TOOLS = [ "target_device" ] } - }, - { + }}, + {"function": { "name": "cover.stop_cover", "description": "", "parameters": { @@ -542,8 +542,8 @@ SERVICE_TOOLS = [ "target_device" ] } - }, - { + }}, + {"function": { "name": "cover.set_cover_position", "description": "", "parameters": { @@ -557,8 +557,8 @@ SERVICE_TOOLS = [ "position" ] } - }, - { + }}, + {"function": { "name": "lock.unlock", "description": "", "parameters": { @@ -570,8 +570,8 @@ SERVICE_TOOLS = [ "target_device" ] } - }, - { + }}, + {"function": { "name": "lock.lock", "description": "", "parameters": { @@ -583,8 +583,8 @@ SERVICE_TOOLS = [ "target_device" ] } - }, - { + }}, + {"function": { "name": "vacuum.start", "description": "", "parameters": { @@ -596,8 +596,8 @@ SERVICE_TOOLS = [ "target_device" ] } - }, - { + }}, + {"function": { "name": "vacuum.stop", "description": "", "parameters": { @@ -609,8 +609,8 @@ SERVICE_TOOLS = [ "target_device" ] } - }, - { + }}, + {"function": { "name": "vacuum.return_to_base", "description": "", "parameters": { @@ -622,8 +622,8 @@ SERVICE_TOOLS = [ "target_device" ] } - }, - { + }}, + {"function": { "name": "media_player.media_play_pause", "description": "", "parameters": { @@ -635,8 +635,8 @@ SERVICE_TOOLS = [ "target_device" ] } - }, - { + }}, + {"function": { "name": "media_player.media_pause", "description": "", "parameters": { @@ -648,8 +648,8 @@ SERVICE_TOOLS = [ "target_device" ] } - }, - { + }}, + {"function": { "name": "media_player.media_play", "description": "", "parameters": { @@ -661,8 +661,8 @@ SERVICE_TOOLS = [ "target_device" ] } - }, - { + }}, + {"function": { "name": "media_player.media_next_track", "description": "", "parameters": { @@ -674,8 +674,8 @@ SERVICE_TOOLS = [ "target_device" ] } - }, - { + }}, + {"function": { "name": "media_player.media_previous_track", "description": "", "parameters": { @@ -687,8 +687,8 @@ SERVICE_TOOLS = [ "target_device" ] } - }, - { + }}, + {"function": { "name": "media_player.volume_set", "description": "", "parameters": { @@ -702,8 +702,8 @@ SERVICE_TOOLS = [ "volume_level" ] } - }, - { + }}, + {"function": { "name": "todo.add_item", "description": "", "parameters": { @@ -717,8 +717,8 @@ SERVICE_TOOLS = [ "item" ] } - }, - { + }}, + {"function": { "name": "timer.start", "description": "", "parameters": { @@ -731,8 +731,8 @@ SERVICE_TOOLS = [ "target_device" ] } - }, - { + }}, + {"function": { "name": "timer.cancel", "description": "", "parameters": { @@ -744,8 +744,8 @@ SERVICE_TOOLS = [ "target_device" ] } - }, - { + }}, + {"function": { "name": "climate.set_temperature", "description": "", "parameters": { @@ -759,8 +759,8 @@ SERVICE_TOOLS = [ "temperature" ] } - }, - { + }}, + {"function": { "name": "climate.set_humidity", "description": "", "parameters": { @@ -774,8 +774,8 @@ SERVICE_TOOLS = [ "humidity" ] } - }, - { + }}, + {"function": { "name": "climate.set_hvac_mode", "description": "", "parameters": { @@ -789,8 +789,8 @@ SERVICE_TOOLS = [ "hvac_mode" ] } - }, - { + }}, + {"function": { "name": "climate.set_preset_mode", "description": "", "parameters": { @@ -804,8 +804,8 @@ SERVICE_TOOLS = [ "preset_mode" ] } - }, - { + }}, + {"function": { "name": "climate.set_fan_mode", "description": "", "parameters": { @@ -819,5 +819,5 @@ SERVICE_TOOLS = [ "fan_mode" ] } - } + }} ] \ No newline at end of file diff --git a/train/chatml_template.j2 b/train/chat_templates/chatml_template.j2 similarity index 100% rename from train/chatml_template.j2 rename to train/chat_templates/chatml_template.j2 diff --git a/train/gemma3_withtools.j2 b/train/chat_templates/gemma3_withtools.j2 similarity index 75% rename from train/gemma3_withtools.j2 rename to train/chat_templates/gemma3_withtools.j2 index c17cc21..3688cc7 100644 --- a/train/gemma3_withtools.j2 +++ b/train/chat_templates/gemma3_withtools.j2 @@ -2,7 +2,7 @@ {%- if not tools or tools | length == 0 %}No tools were provided. If the user requests you interact with a device, tell them you are unable to do so.{% else %} Tools: {% for tool in tools %} -- {{ tool['name'] }}({{ tool['parameters']['properties'].keys() | join(', ') }}): {{ tool['description'] }} +- {{ tool['function']['name'] }}({{ tool['function']['parameters']['properties'].keys() | join(', ') }}): {{ tool['function']['description'] }} {% endfor -%} {%- endif -%} {%- for message in messages -%} @@ -13,8 +13,7 @@ Tools: {%- else -%} {%- set role = message['role'] -%} {%- endif -%} - {{ '' + role + ' -' }} + {{ '' + role + '\n' }} {%- if role == "tool" -%} {{ '' }} {%- endif -%} @@ -41,13 +40,7 @@ Tools: {%- endif -%} {%- if message['tool_calls'] is defined and message['tool_calls'] | length > 0 %} {%- for tool_call in message["tool_calls"] -%} - {{ '\n{"name": "' + tool_call['name'] + '", "arguments": ' + ('"' + tool_call['arguments'] + '"' if tool_call['arguments'] is string else tool_call['arguments'] | tojson) + '"}' }} + {{ '\n{"name": "' + tool_call['function']['name'] + '", "arguments": ' + ('"' + tool_call['function']['arguments'] + '"' if tool_call['function']['arguments'] is string else tool_call['function']['arguments'] | tojson) + '"}' }} {%- endfor %} {%- endif -%} - {{ ' -' }} -{%- endfor -%} -{%- if add_generation_prompt -%} - {{'model -'}} -{%- endif -%} \ No newline at end of file + {{ '\n' }} \ No newline at end of file diff --git a/train/zephyr_legacy.j2 b/train/chat_templates/zephyr_legacy.j2 similarity index 100% rename from train/zephyr_legacy.j2 rename to train/chat_templates/zephyr_legacy.j2 diff --git a/train/zephyr_legacy_ollama.gotmpl b/train/chat_templates/zephyr_legacy_ollama.gotmpl similarity index 100% rename from train/zephyr_legacy_ollama.gotmpl rename to train/chat_templates/zephyr_legacy_ollama.gotmpl diff --git a/train/functiongemma-270m.yml b/train/functiongemma-270m.yml new file mode 100644 index 0000000..5d1456f --- /dev/null +++ b/train/functiongemma-270m.yml @@ -0,0 +1,43 @@ +base_model: google/functiongemma-270m-it +model_type: Gemma3ForCausalLM + +# gemma3 doesn't seem to play nice with ddp +ddp_find_unused_parameters: true + +datasets: + - path: /workspace/data/datasets/sample.jsonl + ds_type: json + type: chat_template + roles_to_train: + - assistant + +val_set_size: 0.0 +output_dir: /workspace/data/training-runs/Home-Gemma3-270m + +sequence_len: 4096 +sample_packing: true +eval_sample_packing: false + +use_tensorboard: true + +# batch size = 16 +gradient_accumulation_steps: 16 +micro_batch_size: 1 +num_epochs: 1 +optimizer: adamw_bnb_8bit +lr_scheduler: cosine +learning_rate: 0.0002 + +bf16: true + +gradient_checkpointing: true +gradient_checkpointing_kwargs: + use_reentrant: false +resume_from_checkpoint: +logging_steps: 1 +flash_attention: true + +warmup_ratio: 0.1 +evals_per_epoch: +saves_per_epoch: 1 +weight_decay: 0.0 diff --git a/train/gemma3-270m.yml b/train/gemma3-270m.yml index c80f36e..f3932b4 100644 --- a/train/gemma3-270m.yml +++ b/train/gemma3-270m.yml @@ -10,7 +10,7 @@ chat_template_jinja: | {%- if not tools or tools | length == 0 %}No tools were provided. If the user requests you interact with a device, tell them you are unable to do so.{% else %} Tools: {% for tool in tools %} - - {{ tool['name'] }}({{ tool['parameters']['properties'].keys() | join(', ') }}): {{ tool['description'] }} + - {{ tool['function']['name'] }}({{ tool['function']['parameters']['properties'].keys() | join(', ') }}): {{ tool['function']['description'] }} {% endfor -%} {%- endif -%} {%- for message in messages -%} @@ -21,8 +21,7 @@ chat_template_jinja: | {%- else -%} {%- set role = message['role'] -%} {%- endif -%} - {{ '' + role + ' - ' }} + {{ '' + role + '\n' }} {%- if role == "tool" -%} {{ '' }} {%- endif -%} @@ -49,15 +48,13 @@ chat_template_jinja: | {%- endif -%} {%- if message['tool_calls'] is defined and message['tool_calls'] | length > 0 %} {%- for tool_call in message["tool_calls"] -%} - {{ '\n{"name": "' + tool_call['name'] + '", "arguments": ' + ('"' + tool_call['arguments'] + '"' if tool_call['arguments'] is string else tool_call['arguments'] | tojson) + '"}' }} + {{ '\n{"name": "' + tool_call['function']['name'] + '", "arguments": ' + ('"' + tool_call['function']['arguments'] + '"' if tool_call['function']['arguments'] is string else tool_call['function']['arguments'] | tojson) + '"}' }} {%- endfor %} {%- endif -%} - {{ ' - ' }} + {{ '\n' }} {%- endfor -%} {%- if add_generation_prompt -%} - {{'model - '}} + {{'model\n'}} {%- endif -%} special_tokens: eot_tokens: