Skip to content

Commit 318f784

Browse files
committed
fix: completion parsing
1 parent a1a26ae commit 318f784

File tree

5 files changed

+80
-22
lines changed

5 files changed

+80
-22
lines changed

examples/01_basic_agent.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,10 @@ async def main():
8888
return
8989

9090
# Example 2: Using Tokyo
91-
print("\nExample 2: Using Tokyo")
92-
print("-" * 50)
93-
run = await get_capital_info.run(CityInput(city="Tokyo"))
94-
print(run)
91+
# print("\nExample 2: Using Tokyo")
92+
# print("-" * 50)
93+
# run = await get_capital_info.run(CityInput(city="Tokyo"))
94+
# print(run)
9595

9696
# Fetch and display completions for the Tokyo example
9797
await display_completions(run)

tests/fixtures/completions2.json

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"completions": [
3+
{
4+
"messages": [
5+
{
6+
"role": "assistant",
7+
"content": [
8+
{
9+
"type": "text",
10+
"text": "<instructions>\\nFind the capital city of the country where the input city is located.\\n\\nGuidelines:\\n1. First identify the country where the input city is located\\n2. Then provide the capital city of that country\\n3. Include an interesting historical or cultural fact about the capital\\n4. Be accurate and precise with geographical information\\n5. If the input city is itself the capital, still provide the information\\n</instructions>\\n\\nInput will be provided in the user message using a JSON following the schema:\\n```json\\n{\\n \"description\": \"Input model for the city-to-capital agent.\",\\n \"properties\": {\\n \"city\": {\\n \"description\": \"The name of the city for which to find the country\\'s capital\",\\n \"examples\": [\\n \"Paris\",\\n \"New York\",\\n \"Tokyo\"\\n ],\\n \"type\": \"string\"\\n }\\n },\\n \"required\": [\\n \"city\"\\n ],\\n \"type\": \"object\"\\n}\\n```\\n\\nReturn a single JSON object enforcing the following schema:\\n```json\\n{\\n \"description\": \"Output model containing information about the capital city.\",\\n \"properties\": {\\n \"country\": {\\n \"description\": \"The country where the input city is located\",\\n \"examples\": [\\n \"France\",\\n \"United States\",\\n \"Japan\"\\n ],\\n \"type\": \"string\"\\n },\\n \"capital\": {\\n \"description\": \"The capital city of the country\",\\n \"examples\": [\\n \"Paris\",\\n \"Washington D.C.\",\\n \"Tokyo\"\\n ],\\n \"type\": \"string\"\\n },\\n \"fun_fact\": {\\n \"description\": \"An interesting fact about the capital city\",\\n \"examples\": [\\n \"Paris has been the capital of France since 508 CE\"\\n ],\\n \"type\": \"string\"\\n }\\n },\\n \"required\": [\\n \"capital\",\\n \"country\",\\n \"fun_fact\"\\n ],\\n \"type\": \"object\"\\n}\\n```"
11+
}
12+
]
13+
},
14+
{
15+
"role": "user",
16+
"content": [
17+
{
18+
"type": "text",
19+
"text": "Input is:\\n```json\\n{\\n \"city\": \"Paris\"\\n}\\n```"
20+
}
21+
]
22+
}
23+
],
24+
"response": "{\\n \"country\": \"France\",\\n \"capital\": \"Paris\",\\n \"fun_fact\": \"Paris became the capital of France in 508 CE when Clovis I, the first King of the Franks, made the city his seat of power. It\\'s one of the oldest capital cities in Europe and was originally called Lutetia during the Roman period.\"\\n}",
25+
"usage": {
26+
"completion_token_count": 88,
27+
"completion_cost_usd": 0.00132,
28+
"prompt_token_count": 516,
29+
"prompt_cost_usd": 0.0015480000000000001,
30+
"model_context_window_size": 200000
31+
},
32+
"duration_seconds": 4.13,
33+
"provider": "anthropic"
34+
}
35+
]
36+
}

workflowai/core/client/agent_test.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from workflowai.core.client.client import (
2121
WorkflowAI,
2222
)
23-
from workflowai.core.domain.completion import Completion, CompletionUsage, Message
23+
from workflowai.core.domain.completion import Completion, CompletionUsage, Message, TextContent
2424
from workflowai.core.domain.errors import MaxTurnsReachedError, WorkflowAIError
2525
from workflowai.core.domain.run import Run
2626
from workflowai.core.domain.tool_call import ToolCallRequest
@@ -1131,6 +1131,24 @@ async def test_fetch_completions(self, agent: Agent[HelloTaskInput, HelloTaskOut
11311131
),
11321132
]
11331133

1134+
async def test_fetch_completions_content_list(
1135+
self,
1136+
agent: Agent[HelloTaskInput, HelloTaskOutput],
1137+
httpx_mock: HTTPXMock,
1138+
):
1139+
"""Test that fetch_completions correctly fetches and returns completions
1140+
when the message content is a list of objects"""
1141+
# Mock the HTTP response instead of the API client method
1142+
httpx_mock.add_response(
1143+
url="http://localhost:8000/v1/_/agents/123/runs/1/completions",
1144+
json=fixtures_json("completions2.json"),
1145+
)
1146+
1147+
completions = await agent.fetch_completions("1")
1148+
assert len(completions) == 1
1149+
assert isinstance(completions[0].messages[0].content, list)
1150+
assert isinstance(completions[0].messages[0].content[0], TextContent)
1151+
11341152

11351153
class TestStream:
11361154
async def test_stream(self, httpx_mock: HTTPXMock, agent: Agent[HelloTaskInput, HelloTaskOutput]):

workflowai/core/domain/completion.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ class Message(BaseModel):
7373
"""A message in a completion."""
7474

7575
role: str = ""
76-
content: Union[str, MessageContent] = Field(default="")
76+
content: Union[str, list[MessageContent]] = Field(default="")
7777

7878

7979
class Completion(BaseModel):

workflowai/core/domain/completion_test.py

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,70 +17,74 @@ def test_with_text_content(self):
1717
json_str = """
1818
{
1919
"role": "assistant",
20-
"content": {
20+
"content": [{
2121
"type": "text",
2222
"text": "This is a test message"
23-
}
23+
}]
2424
}
2525
"""
2626
message = Message.model_validate_json(json_str)
2727
assert message.role == "assistant"
28-
assert isinstance(message.content, TextContent)
29-
assert message.content.text == "This is a test message"
28+
assert isinstance(message.content, list)
29+
assert isinstance(message.content[0], TextContent)
30+
assert message.content[0].text == "This is a test message"
3031

3132
def test_with_document_content(self):
3233
# Test message with DocumentContent
3334
json_str = """
3435
{
3536
"role": "user",
36-
"content": {
37+
"content": [{
3738
"type": "document_url",
3839
"source": {
3940
"url": "https://example.com/doc.pdf"
4041
}
41-
}
42+
}]
4243
}
4344
"""
4445
message = Message.model_validate_json(json_str)
4546
assert message.role == "user"
46-
assert isinstance(message.content, DocumentContent)
47-
assert message.content.source.url == "https://example.com/doc.pdf"
47+
assert isinstance(message.content, list)
48+
assert isinstance(message.content[0], DocumentContent)
49+
assert message.content[0].source.url == "https://example.com/doc.pdf"
4850

4951
def test_with_image_content(self):
5052
# Test message with ImageContent
5153
json_str = """
5254
{
5355
"role": "user",
54-
"content": {
56+
"content": [{
5557
"type": "image_url",
5658
"image_url": {
5759
"url": "https://example.com/image.jpg"
5860
}
59-
}
61+
}]
6062
}
6163
"""
6264
message = Message.model_validate_json(json_str)
6365
assert message.role == "user"
64-
assert isinstance(message.content, ImageContent)
65-
assert message.content.image_url.url == "https://example.com/image.jpg"
66+
assert isinstance(message.content, list)
67+
assert isinstance(message.content[0], ImageContent)
68+
assert message.content[0].image_url.url == "https://example.com/image.jpg"
6669

6770
def test_with_audio_content(self):
6871
# Test message with AudioContent
6972
json_str = """
7073
{
7174
"role": "user",
72-
"content": {
75+
"content": [{
7376
"type": "audio_url",
7477
"audio_url": {
7578
"url": "https://example.com/audio.mp3"
7679
}
77-
}
80+
}]
7881
}
7982
"""
8083
message = Message.model_validate_json(json_str)
8184
assert message.role == "user"
82-
assert isinstance(message.content, AudioContent)
83-
assert message.content.audio_url.url == "https://example.com/audio.mp3"
85+
assert isinstance(message.content, list)
86+
assert isinstance(message.content[0], AudioContent)
87+
assert message.content[0].audio_url.url == "https://example.com/audio.mp3"
8488

8589
def test_empty_role(self):
8690
# Test message with empty role

0 commit comments

Comments
 (0)