4242from conductor .ai .agents import Agent , AgentRuntime , tool , wait_for_message_tool
4343from settings import settings
4444
45- # Shared directory for IPC between main process and worker processes.
46- # Workers run as separate OS processes (different PIDs, same filesystem).
47- _ipc_dir = Path (tempfile .mkdtemp (prefix = "message_bus_" ))
45+ _IPC_DIR_ENV = "MESSAGE_BUS_IPC_DIR"
46+ if _IPC_DIR_ENV in os .environ :
47+ _ipc_dir = Path (os .environ [_IPC_DIR_ENV ])
48+ else :
49+ _ipc_dir = Path (tempfile .mkdtemp (prefix = "message_bus_" ))
50+ os .environ [_IPC_DIR_ENV ] = str (_ipc_dir )
4851_FORWARDED_DIR = _ipc_dir / "forwarded" # one file per forwarded topic
49- _FORWARDED_DIR .mkdir ()
52+ _FORWARDED_DIR .mkdir (exist_ok = True )
5053
5154TOPICS = [
5255 "the impact of edge computing on cloud infrastructure" ,
5356 "why Rust is gaining adoption in systems programming" ,
5457 "how vector databases work" ,
5558]
5659
60+ _WRITER_EXECUTION_ID_ENV = "MESSAGE_BUS_WRITER_EXECUTION_ID"
5761
58- def build_researcher (runtime : AgentRuntime , writer_execution_id : str ) -> Agent :
62+
63+ @tool
64+ def forward_to_writer (topic : str , notes : str ) -> str :
65+ """Forward research notes to the Writer and signal the main process."""
66+ print (f" [researcher → writer] forwarding notes on { topic !r} " )
67+ writer_execution_id = os .environ [_WRITER_EXECUTION_ID_ENV ]
68+ with AgentRuntime () as rt :
69+ rt .send_message (writer_execution_id , {"topic" : topic , "notes" : notes })
70+ (_FORWARDED_DIR / f"{ time .time_ns ()} .done" ).touch ()
71+ return "forwarded"
72+
73+
74+ def build_researcher () -> Agent :
5975 """Build the Researcher agent with a forward tool wired to the Writer's queue."""
6076
6177 receive_topic = wait_for_message_tool (
6278 name = "wait_for_topic" ,
6379 description = "Wait for the next research topic." ,
6480 )
6581
66- @tool
67- def forward_to_writer (topic : str , notes : str ) -> str :
68- """Forward research notes to the Writer and signal the main process."""
69- print (f" [researcher → writer] forwarding notes on { topic !r} " )
70- runtime .send_message (writer_execution_id , {"topic" : topic , "notes" : notes })
71- (_FORWARDED_DIR / f"{ time .time_ns ()} .done" ).touch ()
72- return "forwarded"
73-
7482 return Agent (
7583 name = "researcher" ,
7684 model = settings .llm_model ,
@@ -88,6 +96,14 @@ def forward_to_writer(topic: str, notes: str) -> str:
8896 )
8997
9098
99+ @tool
100+ def publish (topic : str , paragraph : str ) -> str :
101+ """Publish the finished paragraph."""
102+ print (f"\n [writer] ── { topic } ──" )
103+ print (f" { paragraph } \n " )
104+ return "published"
105+
106+
91107def build_writer () -> Agent :
92108 """Build the Writer agent that polishes research notes into paragraphs."""
93109
@@ -99,13 +115,6 @@ def build_writer() -> Agent:
99115 ),
100116 )
101117
102- @tool
103- def publish (topic : str , paragraph : str ) -> str :
104- """Publish the finished paragraph."""
105- print (f"\n [writer] ── { topic } ──" )
106- print (f" { paragraph } \n " )
107- return "published"
108-
109118 return Agent (
110119 name = "writer" ,
111120 model = settings .llm_model ,
@@ -122,34 +131,41 @@ def publish(topic: str, paragraph: str) -> str:
122131 )
123132
124133
125- try :
126- with AgentRuntime () as runtime :
127- # Start the Writer first so its execution_id is available to the Researcher
128- writer_handle = runtime .start (build_writer (), "Begin. Wait for research notes." )
129- writer_id = writer_handle .execution_id
130- print (f"Writer started: { writer_id } " )
131-
132- researcher = build_researcher (runtime , writer_id )
133- researcher_handle = runtime .start (researcher , "Begin. Wait for your first topic." )
134- researcher_id = researcher_handle .execution_id
135- print (f"Researcher started: { researcher_id } \n " )
136-
137- time .sleep (4 )
138- print ("Sending topics to Researcher...\n " )
139- for topic in TOPICS :
140- print (f" → { topic !r} " )
141- runtime .send_message (researcher_id , {"topic" : topic })
142-
143- # Wait until all topics have been forwarded to the Writer
144- while len (list (_FORWARDED_DIR .iterdir ())) < len (TOPICS ):
145- time .sleep (0.1 )
146-
147- # Deterministic stop — no stop-handling instructions needed.
148- researcher_handle .stop ()
149- writer_handle .stop ()
150- researcher_handle .join (timeout = 30 )
151- writer_handle .join (timeout = 30 )
152-
153- print ("Done." )
154- finally :
155- shutil .rmtree (_ipc_dir , ignore_errors = True )
134+ def main () -> None :
135+ try :
136+ with AgentRuntime () as runtime :
137+ # Start the Writer first so its execution_id is available to the Researcher
138+ writer_handle = runtime .start (build_writer (), "Begin. Wait for research notes." )
139+ writer_id = writer_handle .execution_id
140+ print (f"Writer started: { writer_id } " )
141+
142+ os .environ [_WRITER_EXECUTION_ID_ENV ] = writer_id
143+
144+ researcher = build_researcher ()
145+ researcher_handle = runtime .start (researcher , "Begin. Wait for your first topic." )
146+ researcher_id = researcher_handle .execution_id
147+ print (f"Researcher started: { researcher_id } \n " )
148+
149+ time .sleep (4 )
150+ print ("Sending topics to Researcher...\n " )
151+ for topic in TOPICS :
152+ print (f" → { topic !r} " )
153+ runtime .send_message (researcher_id , {"topic" : topic })
154+
155+ # Wait until all topics have been forwarded to the Writer
156+ while len (list (_FORWARDED_DIR .iterdir ())) < len (TOPICS ):
157+ time .sleep (0.1 )
158+
159+ # Deterministic stop — no stop-handling instructions needed.
160+ researcher_handle .stop ()
161+ writer_handle .stop ()
162+ researcher_handle .join (timeout = 30 )
163+ writer_handle .join (timeout = 30 )
164+
165+ print ("Done." )
166+ finally :
167+ shutil .rmtree (_ipc_dir , ignore_errors = True )
168+
169+
170+ if __name__ == "__main__" :
171+ main ()
0 commit comments