-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathagentflow.py
More file actions
172 lines (146 loc) · 5.16 KB
/
Copy pathagentflow.py
File metadata and controls
172 lines (146 loc) · 5.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
import traceback
import tomllib
import json
import os
from metaflow import (
FlowSpec,
step,
Config,
Parameter,
current,
retry,
card,
secrets,
pypi,
pypi_base,
)
from session_service import with_session_service
MAX_AGENT_FAILURES = 3
def parse_csv(entries):
def _parse():
for entry in entries.splitlines():
name, born, synopsis = entry.split(",", 2)
yield {"ARTIST": name, "BORN": born, "SYNOPSIS": synopsis}
return {"artists": list(_parse())}
def read_html(html):
return {"html": html}
@pypi_base(python="3.12")
@with_session_service(name="musicbrainz")
class ResearchAgentFlow(FlowSpec):
timeline_html = Config(
"timeline_html", default="timelinecard.html", parser=read_html
)
input_list = Config("inputs", default="artists.csv", parser=parse_csv)
prompts = Config("prompts", default="prompts.toml", parser=tomllib.loads)
max_artists = Parameter("max-artists", type=int, default=-1)
checkpoint_interval = Parameter("checkpoint-interval", default=120)
@step
def start(self):
maxx = None if self.max_artists == -1 else self.max_artists
self.artists = list(map(dict, self.input_list.artists[:maxx]))
self.next(self.research, foreach="artists")
@pypi(packages={"openai-agents": "0.2.9", "pydantic": "2.11.7"})
@secrets(sources=["outerbounds.music-research-agent"])
@retry
@step
def research(self):
from research_agent import ResearchAgent
self.artist = self.input
self.artist_prompts = dict(self.prompts)
self.artist_prompts["main"] = self.prompts["main"].format(**self.artist)
self.agent_id = f"{self.input['ARTIST']}"
print(f"Researching {self.input['ARTIST']}")
print(f"Agent ID {self.agent_id}")
mcp_url = f"http://{self.session_service.host}:{self.session_service.info['mcp_port']}/mcp"
print(f"Using MCP session service at {mcp_url}")
self.events = None
self.agent_state = getattr(self, "agent_state", None)
if self.agent_state:
print("Resuming past agent state")
agent = ResearchAgent.load(self.agent_state)
else:
print("Starting a fresh agent")
agent = ResearchAgent(
agent_id=self.agent_id,
mcp_url=mcp_url,
prompt_config=self.artist_prompts,
)
try:
result = agent.run(
self.checkpoint_interval,
group_id=f"{self.agent_id}/{current.run_id}",
trace_metadata={
"artist": self.agent_id,
"pathspec": current.pathspec,
"run-id": current.run_id,
},
)
except:
traceback.print_exc()
self.agent_failed = getattr(self, "agent_failed", 0) + 1
print(f"The agent has failed {self.agent_failed} times now")
if self.agent_failed > MAX_AGENT_FAILURES:
print("..Quit trying")
self.decision = "failed"
else:
print("..Trying again")
self.agent_state = agent.save()
self.decision = "continue"
else:
if result["result"] == "timeout":
print("Timeout - checkpointing agent state")
self.agent_state = agent.save()
self.decision = "continue"
elif result["result"] == "unknown_artist":
print(f"Can't find an artist, {self.input['ARTIST']}")
self.decision = "unknown"
elif result["result"] == "finished":
self.events = result["events"]
self.decision = "done"
else:
print(f"Invalid result: {result}")
self.decision = "failed"
self.next(
{
"continue": self.research,
"done": self.done,
"unknown": self.unknown,
"failed": self.failed,
},
condition="decision",
)
@card(type="html")
@step
def done(self):
print(f"{self.artist['ARTIST']} researched successfully!")
values = {
"TIMELINE_EVENTS": json.dumps(self.events),
"ARTIST": self.artist["ARTIST"],
"SYNOPSIS": self.artist["SYNOPSIS"],
}
self.html = self.timeline_html["html"]
for tag, repl in values.items():
self.html = self.html.replace(f"[[[{tag}]]]", repl)
print("Events timeline:\n" + "\n".join(map(str, self.events)))
self.next(self.agent_complete)
@step
def unknown(self):
print(f"Unknown artist: {self.artist}")
self.next(self.agent_complete)
@step
def failed(self):
print(f"Research failed for {self.artist}")
self.next(self.agent_complete)
@step
def agent_complete(self):
self.next(self.join)
@step
def join(self, inputs):
print("Terminate session service")
self.session_service.terminate()
self.next(self.end)
@step
def end(self):
pass
if __name__ == "__main__":
ResearchAgentFlow()