Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 11 additions & 17 deletions dspy/adapters/chat_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,31 +167,25 @@ def format_assistant_message_content(
return assistant_message_content

def parse(self, signature: type[Signature], completion: str) -> dict[str, Any]:
sections = [(None, [])]

for line in completion.splitlines():
match = field_header_pattern.match(line.strip())
if match:
# If the header pattern is found, split the rest of the line as content
header = match.group(1)
remaining_content = line[match.end() :].strip()
sections.append((header, [remaining_content] if remaining_content else []))
else:
sections[-1][1].append(line)

sections = [(k, "\n".join(v).strip()) for k, v in sections]
# Split the entire completion by field markers, keeping the headers
parts = re.split(field_header_pattern, completion)

fields = {}
for k, v in sections:
if (k not in fields) and (k in signature.output_fields):
# Iterate over captured headers and their bodies
for i in range(1, len(parts), 2):
header = parts[i]
body = parts[i + 1] if i + 1 < len(parts) else ""
if (header not in fields) and (header in signature.output_fields):
try:
fields[k] = parse_value(v, signature.output_fields[k].annotation)
fields[header] = parse_value(body.strip(), signature.output_fields[header].annotation)
except Exception as e:
raise AdapterParseError(
adapter_name="ChatAdapter",
signature=signature,
lm_response=completion,
message=f"Failed to parse field {k} with value {v} from the LM response. Error message: {e}",
message=(
f"Failed to parse field {header} with value {body.strip()} from the LM response. Error message: {e}"
),
)
if fields.keys() != signature.output_fields.keys():
raise AdapterParseError(
Expand Down
27 changes: 27 additions & 0 deletions tests/adapters/test_chat_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -591,3 +591,30 @@ def get_weather(city: str) -> str:
assert result[0]["tool_calls"] == dspy.ToolCalls(
tool_calls=[dspy.ToolCalls.ToolCall(name="get_weather", args={"city": "Paris"})]
)


def test_chat_adapter_same_line_markers():
MOCK_ANSWER = """
[[ ## reasoning ## ]]
The user asked a question about the weather. The topic is the current weather.
[[ ## topic ## ]]
Current Weather[[ ## answer ## ]]
The current weather is sunny.[[ ## completed ## ]]
"""

class MySignature(dspy.Signature):
question: str = dspy.InputField()
reasoning: str = dspy.OutputField()
topic: str = dspy.OutputField()
answer: str = dspy.OutputField()

adapter = dspy.ChatAdapter()
with mock.patch("litellm.completion") as mock_completion:
mock_completion.return_value = ModelResponse(
choices=[Choices(message=Message(content=MOCK_ANSWER))],
model="openai/gpt-4o-mini",
)
result = adapter(dspy.LM(model="openai/gpt-4o-mini", cache=False), {}, MySignature, [], {"question": "What is the weather?"})
assert result[0]["reasoning"] == "The user asked a question about the weather. The topic is the current weather."
assert result[0]["topic"] == "Current Weather"
assert result[0]["answer"] == "The current weather is sunny."
Loading