Skip to content

Commit f0d6271

Browse files
authored
Fix TRL formatter (#378)
* Fix TRL formatter * No need for seperate return * Improve safety
1 parent 08c3c39 commit f0d6271

3 files changed

Lines changed: 116 additions & 19 deletions

File tree

deepfabric/formatters/builtin/trl_sft_tools.py

Lines changed: 98 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
- https://www.stephendiehl.com/posts/fine_tuning_tools/
3838
"""
3939

40+
import json
4041
import logging
4142

4243
from typing import Any
@@ -94,15 +95,17 @@ def get_config_model(self) -> type[BaseModel] | None:
9495

9596
def validate(self, sample: dict) -> bool:
9697
"""Validate that sample has required fields for TRL format."""
97-
# Must have messages
98-
if "messages" not in sample or not isinstance(sample["messages"], list):
99-
return False
98+
# Accept either messages format OR agent_cot_tools format
10099

101-
# Must have at least one message
102-
# Return whether there is at least one message
103-
# Should have available_tools for tool calling
104-
# (though we'll still process samples without it)
105-
return len(sample["messages"]) != 0
100+
# Check for messages format
101+
if "messages" in sample and isinstance(sample["messages"], list):
102+
return len(sample["messages"]) > 0
103+
104+
# Check for agent_cot_tools format (question + tool_used + answer/final_answer)
105+
if "question" in sample and "tool_used" in sample:
106+
return "answer" in sample or "final_answer" in sample
107+
108+
return False
106109

107110
def _format_single_sample(self, sample: dict) -> dict | None:
108111
"""
@@ -124,6 +127,10 @@ def _format_single_sample(self, sample: dict) -> dict | None:
124127
else TRLSFTToolsConfig()
125128
)
126129

130+
# Convert agent_cot_tools format to messages format if needed
131+
if "messages" not in sample:
132+
sample = self._convert_agent_to_messages(sample, config)
133+
127134
# Start with a copy of the sample
128135
formatted_sample = sample.copy()
129136

@@ -172,6 +179,89 @@ def _format_single_sample(self, sample: dict) -> dict | None:
172179

173180
return formatted_sample
174181

182+
def _convert_agent_to_messages(
183+
self, sample: dict, config: TRLSFTToolsConfig
184+
) -> dict:
185+
"""
186+
Convert agent_cot_tools format to messages format.
187+
188+
Args:
189+
sample: Sample in agent_cot_tools format
190+
config: Formatter configuration
191+
192+
Returns:
193+
Sample with messages field
194+
"""
195+
messages = []
196+
197+
# Add system message if configured
198+
if config.include_system_prompt:
199+
system_content = config.system_prompt_override or (
200+
"You are a helpful AI assistant with access to various tools and functions. "
201+
"When a user asks a question that requires information or actions you cannot "
202+
"directly provide, use the available tools to help answer the question."
203+
)
204+
messages.append({"role": "system", "content": system_content})
205+
206+
# Add user question
207+
question = sample.get("question", "")
208+
messages.append({"role": "user", "content": question})
209+
210+
# Extract tool usage information
211+
tool_used = sample.get("tool_used", "")
212+
tool_input = sample.get("tool_input", "{}")
213+
tool_output = sample.get("tool_output", "")
214+
answer = sample.get("answer") or sample.get("final_answer", "")
215+
216+
# Parse tool input
217+
if isinstance(tool_input, str):
218+
try:
219+
# First try parsing as standard JSON
220+
tool_args = json.loads(tool_input)
221+
except json.JSONDecodeError:
222+
try:
223+
# Fallback: try replacing single quotes with double quotes
224+
tool_args = json.loads(tool_input.replace("'", '"'))
225+
except json.JSONDecodeError:
226+
# Final fallback: wrap in a simple structure
227+
tool_args = {"input": tool_input}
228+
else:
229+
tool_args = tool_input
230+
231+
# Add assistant message with tool call
232+
# Using OpenAI-compatible function calling format
233+
tool_call = {
234+
"id": "call_1", # Placeholder ID
235+
"type": "function",
236+
"function": {"name": tool_used, "arguments": json.dumps(tool_args)},
237+
}
238+
239+
messages.append(
240+
{
241+
"role": "assistant",
242+
"content": None,
243+
"tool_calls": [tool_call],
244+
}
245+
)
246+
247+
# Add tool response message
248+
messages.append(
249+
{
250+
"role": "tool",
251+
"content": str(tool_output),
252+
"tool_call_id": "call_1",
253+
}
254+
)
255+
256+
# Add final assistant answer
257+
messages.append({"role": "assistant", "content": answer})
258+
259+
# Return sample with messages and preserve available_tools
260+
return {
261+
"messages": messages,
262+
"available_tools": sample.get("available_tools", []),
263+
}
264+
175265
def _validate_tool_schemas(self, tools: list[dict]) -> None:
176266
"""
177267
Validate that tool schemas are properly formatted for TRL.

examples/agent_tool_calling.yaml

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@ topic_tree:
2424
topic_prompt: "Real-world scenarios requiring tool usage for task completion"
2525

2626
# Model configuration
27-
provider: "gemini"
28-
model: "gemini-2.5-flash-lite"
27+
provider: "openai"
28+
model: "gpt-4o"
2929
temperature: 0.7
3030

3131
# Tree structure parameters
32-
depth: 3
32+
depth: 4
3333
degree: 3
3434

3535
# Save the generated tree
@@ -52,13 +52,10 @@ data_engine:
5252
Make examples pedagogically valuable for training reasoning skills.
5353
5454
# Model for generating the actual training data
55-
provider: "gemini"
56-
model: "gemini-2.5-flash-lite"
55+
provider: "openai"
56+
model: "gpt-4o"
5757
temperature: 0.8
5858

59-
# Number of samples to generate
60-
num_samples: 50
61-
6259
# Use the agent_cot_hybrid schema for detailed step-by-step reasoning with tools
6360
conversation_type: "agent_cot_hybrid"
6461

@@ -70,7 +67,7 @@ data_engine:
7067
# tool_registry_path: "examples/custom_tools.yaml"
7168

7269
# Save the generated data
73-
save_as: "agent_tool_calling_raw.jsonl"
70+
save_as: "agent_tool_calling.jsonl"
7471

7572
# Dataset configuration
7673
dataset:
@@ -79,8 +76,8 @@ dataset:
7976

8077
creation:
8178
# Dataset creation parameters
82-
num_steps: 2
83-
batch_size: 1
79+
num_steps: 20
80+
batch_size: 4
8481
sys_msg: true
8582

8683
# Apply the tool-calling formatter to convert to embedded execution format

examples/trl_format_config.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
dataset:
2+
dataset:
3+
formatters:
4+
- name: "trl_sft_tools"
5+
template: "builtin://trl_sft_tools"
6+
output: "agent_tool_calling_final_trl_sft_tools.jsonl"
7+
config:
8+
include_system_prompt: true
9+
validate_tool_schemas: true
10+
remove_available_tools_field: true

0 commit comments

Comments
 (0)