Skip to content

Commit 26a4f58

Browse files
committed
Improve InputMessage to support list-type content
1 parent 36d02f6 commit 26a4f58

6 files changed

Lines changed: 126 additions & 2 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,7 @@ reporter = AgentSpec(
567567
- [patterns](examples/patterns)
568568
- [agents-as-tools](examples/agents-as-tools)
569569
- [react-mcp](examples/react-mcp)
570+
- [react-vision](examples/react-vision)
570571
- [a2a](examples/a2a)
571572
- [mcp](examples/mcp)
572573
- [mcp-new](examples/mcp-new)

coagent/agents/react_agent/__init__.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,15 @@
22
from .agent import ReActAgent
33
from .context import RunContext
44
from .mcp import MCPTools
5-
from .messages import InputMessage, InputHistory, OutputMessage
5+
from .messages import (
6+
InputMessage,
7+
InputHistory,
8+
OutputMessage,
9+
InputMessageTextParam,
10+
InputMessageImageParam,
11+
InputMessageFileParam,
12+
InputMessageAudioParam,
13+
)
614
from .types import (
715
MessageOutputItem,
816
ToolCallItem,

coagent/agents/react_agent/messages.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
11
from coagent.core import Message
22
from pydantic import Field
33
from typing import Literal
4+
from typing_extensions import TypeAlias
45

56
from .types import (
67
MessageOutputItem,
78
ToolCallItem,
89
ToolCallOutputItem,
910
ToolCallProgressItem,
11+
ResponseInputTextParam as InputMessageTextParam,
12+
ResponseInputImageParam as InputMessageImageParam,
13+
ResponseInputFileParam as InputMessageFileParam,
14+
ResponseInputAudioParam as InputMessageAudioParam,
1015
)
1116

17+
InputContentParam: TypeAlias = list[InputMessageTextParam | InputMessageImageParam | InputMessageFileParam | InputMessageAudioParam]
18+
1219

1320
class InputMessage(Message):
1421
role: Literal["user", "assistant"] = Field(
1522
..., description="The role of the message. (e.g. `user`, `assistant`)"
1623
)
17-
content: str = Field(
24+
content: str | InputContentParam = Field(
1825
...,
1926
description="The content of the message.",
2027
)

coagent/agents/react_agent/types.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@
1111
ResponseFunctionToolCall,
1212
ResponseFunctionToolCallOutputItem,
1313
)
14+
from openai.types.responses.response_input_message_content_list_param import (
15+
ResponseInputTextParam, # noqa: F401
16+
ResponseInputImageParam, # noqa: F401
17+
ResponseInputFileParam, # noqa: F401
18+
ResponseInputAudioParam # noqa: F401
19+
)
1420
from openai.types.responses.response_reasoning_item import ResponseReasoningItem
1521
from pydantic import BaseModel
1622

examples/react-vision/README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# ReAct Vision
2+
3+
This example demonstrates how to handle images using a vision model (e.g. gemini-2.5-flash) in a ReAct agent.
4+
5+
6+
## Prerequisites
7+
8+
- Install `coagent` (see [Installation](../../README.md#installation)).
9+
10+
11+
## Quick Start
12+
13+
Set the environment variables for your vision model:
14+
15+
```bash
16+
export MODEL_ID="your-model-id"
17+
export MODEL_BASE_URL="your-base-url"
18+
export MODEL_API_KEY="your-api-key"
19+
```
20+
21+
Run the agent:
22+
23+
```bash
24+
python examples/react-vision/agent.py
25+
```

examples/react-vision/agent.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import asyncio
2+
from coagent.agents.react_agent import (
3+
ReActAgent,
4+
InputMessage,
5+
InputMessageTextParam,
6+
InputMessageImageParam,
7+
InputHistory,
8+
OutputMessage,
9+
MessageOutputItem,
10+
ToolCallItem,
11+
ToolCallOutputItem,
12+
ToolCallProgressItem,
13+
)
14+
from coagent.core import AgentSpec, new, init_logger
15+
from coagent.runtimes import LocalRuntime
16+
17+
18+
analyzer = AgentSpec(
19+
"analyzer",
20+
new(
21+
ReActAgent,
22+
name="reporter",
23+
system="You are a helpful image analyzer",
24+
),
25+
)
26+
27+
28+
async def main():
29+
async with LocalRuntime() as runtime:
30+
await runtime.register(analyzer)
31+
32+
result = await analyzer.run(
33+
InputHistory(
34+
messages=[
35+
InputMessage(
36+
role="user",
37+
content=[
38+
InputMessageTextParam(
39+
type="input_text", text="What’s in this image?"
40+
),
41+
InputMessageImageParam(
42+
detail="auto",
43+
type="input_image",
44+
image_url="https://raw.githubusercontent.com/OpenCSGs/coagent/main/assets/coagent-overview.png",
45+
),
46+
],
47+
)
48+
]
49+
).encode(),
50+
stream=True,
51+
)
52+
async for chunk in result:
53+
msg = OutputMessage.decode(chunk)
54+
i = msg.item
55+
match i:
56+
case MessageOutputItem():
57+
print(i.raw_item.content[0].text, end="", flush=True)
58+
case ToolCallItem():
59+
print(
60+
f"\n[tool#{i.raw_item.call_id} call: {i.raw_item.name}]",
61+
flush=True,
62+
)
63+
case ToolCallProgressItem():
64+
print(
65+
f"\n[tool#{i.raw_item.call_id} progress: {i.raw_item.message}]",
66+
flush=True,
67+
)
68+
case ToolCallOutputItem():
69+
print(
70+
f"\n[tool#{i.raw_item.call_id} output: {i.raw_item.output}]",
71+
flush=True,
72+
)
73+
74+
75+
if __name__ == "__main__":
76+
init_logger()
77+
asyncio.run(main())

0 commit comments

Comments
 (0)