1414use Cortex \Memory \ChatMemory ;
1515use UnexpectedValueException ;
1616use Cortex \Contracts \Pipeable ;
17+ use Cortex \Events \PipelineEnd ;
18+ use Cortex \Events \AgentStepEnd ;
1719use Cortex \LLM \Data \ChatResult ;
20+ use Cortex \Events \PipelineError ;
1821use Cortex \LLM \Enums \ToolChoice ;
22+ use Cortex \Events \AgentStepError ;
23+ use Cortex \Events \AgentStepStart ;
1924use Cortex \LLM \Contracts \Message ;
2025use Cortex \Memory \Contracts \Store ;
2126use Cortex \Pipeline \RuntimeConfig ;
2227use Cortex \Support \Traits \CanPipe ;
2328use Cortex \Agents \Stages \AppendUsage ;
2429use Cortex \LLM \Data \ChatStreamResult ;
30+ use Cortex \Events \Contracts \AgentEvent ;
2531use Cortex \Exceptions \GenericException ;
2632use Cortex \Memory \Stores \InMemoryStore ;
2733use Cortex \Agents \Stages \HandleToolCalls ;
2834use Cortex \JsonSchema \Types \ObjectSchema ;
2935use Cortex \LLM \Enums \StructuredOutputMode ;
3036use Cortex \JsonSchema \Contracts \JsonSchema ;
3137use Cortex \LLM \Data \Messages \SystemMessage ;
38+ use Cortex \Support \Traits \DispatchesEvents ;
3239use Illuminate \Contracts \Support \Arrayable ;
3340use Cortex \Agents \Stages \AddMessageToMemory ;
3441use Cortex \LLM \Contracts \LLM as LLMContract ;
4148class Agent implements Pipeable
4249{
4350 use CanPipe;
51+ use DispatchesEvents;
4452
4553 protected LLMContract $ llm ;
4654
@@ -95,19 +103,17 @@ public function __construct(
95103 public function pipeline (bool $ shouldParseOutput = true ): Pipeline
96104 {
97105 $ tools = Utils::toToolCollection ($ this ->getTools ());
98-
99- return $ this ->executionPipeline ($ shouldParseOutput )
100- ->when (
101- $ tools ->isNotEmpty (),
102- fn (Pipeline $ pipeline ): Pipeline => $ pipeline ->pipe (
103- new HandleToolCalls (
104- $ tools ,
105- $ this ->memory ,
106- $ this ->executionPipeline ($ shouldParseOutput ),
107- $ this ->maxSteps ,
108- ),
109- ),
110- );
106+ $ executionPipeline = $ this ->executionPipeline ($ shouldParseOutput );
107+
108+ return $ executionPipeline ->when (
109+ $ tools ->isNotEmpty (),
110+ fn (Pipeline $ pipeline ): Pipeline => $ pipeline ->pipe (new HandleToolCalls (
111+ $ tools ,
112+ $ this ->memory ,
113+ $ executionPipeline ,
114+ $ this ->maxSteps ,
115+ )),
116+ );
111117 }
112118
113119 /**
@@ -116,13 +122,28 @@ public function pipeline(bool $shouldParseOutput = true): Pipeline
116122 public function executionPipeline (bool $ shouldParseOutput = true ): Pipeline
117123 {
118124 return $ this ->prompt
125+ ->pipe (function ($ payload , RuntimeConfig $ config , $ next ) {
126+ $ this ->dispatchEvent (new AgentStepStart ($ this , 1 ));
127+
128+ return $ next ($ payload , $ config );
129+ })
119130 ->pipe ($ this ->llm ->shouldParseOutput ($ shouldParseOutput ))
120131 ->pipe (new AddMessageToMemory ($ this ->memory ))
121132 ->pipe (new AppendUsage ($ this ->usage ))
122133 ->pipe (function ($ payload , RuntimeConfig $ config , $ next ) {
123134 $ config ->context ->set ('execution_step ' , $ config ->context ->get ('execution_step ' , 0 ) + 1 );
135+ $ this ->dispatchEvent (new AgentStepEnd ($ this , $ config ->context ->get ('execution_step ' )));
124136
125137 return $ next ($ payload , $ config );
138+ })
139+ ->onError (function (PipelineError $ event ): void {
140+ $ this ->dispatchEvent (
141+ new AgentStepError (
142+ $ this ,
143+ $ event ->config ->context ->get ('execution_step ' ),
144+ $ event ->exception ,
145+ ),
146+ );
126147 });
127148 }
128149
@@ -218,6 +239,35 @@ public function getUsage(): Usage
218239 return $ this ->usage ;
219240 }
220241
242+ public function getRuntimeConfig (): ?RuntimeConfig
243+ {
244+ return $ this ->runtimeConfig ;
245+ }
246+
247+ /**
248+ * Register a listener for the start of an agent step.
249+ */
250+ public function onStepStart (Closure $ listener ): self
251+ {
252+ return $ this ->on (AgentStepStart::class, $ listener );
253+ }
254+
255+ /**
256+ * Register a listener for the end of an agent step.
257+ */
258+ public function onStepEnd (Closure $ listener ): self
259+ {
260+ return $ this ->on (AgentStepEnd::class, $ listener );
261+ }
262+
263+ /**
264+ * Register a listener for the error of an agent step.
265+ */
266+ public function onStepError (Closure $ listener ): self
267+ {
268+ return $ this ->on (AgentStepError::class, $ listener );
269+ }
270+
221271 /**
222272 * @param array<int, \Cortex\LLM\Contracts\Message> $messages
223273 * @param array<string, mixed> $input
@@ -230,26 +280,24 @@ protected function invokePipeline(
230280 ?RuntimeConfig $ config = null ,
231281 bool $ streaming = false ,
232282 ): ChatResult |ChatStreamResult {
233- $ this ->memory ->setVariables ([
234- ...$ this ->initialPromptVariables ,
235- ...$ input ,
236- ]);
237-
238- $ messages = $ this ->memory ->getMessages ()->merge ($ messages );
239- $ this ->memory ->setMessages ($ messages );
240-
241- $ pipeline = $ streaming
242- ? $ this ->pipeline ->enableStreaming ()
243- : $ this ->pipeline ;
244-
245- return $ pipeline ->pipe (function ($ payload , RuntimeConfig $ config , $ next ) {
246- $ this ->runtimeConfig = $ config ;
247-
248- return $ next ($ payload , $ config );
249- })->invoke ([
283+ $ this ->memory
284+ ->setMessages ($ this ->memory ->getMessages ()->merge ($ messages ))
285+ ->setVariables ([
286+ ...$ this ->initialPromptVariables ,
287+ ...$ input ,
288+ ]);
289+
290+ $ payload = [
250291 ...$ input ,
251292 'messages ' => $ this ->memory ->getMessages (),
252- ], $ config );
293+ ];
294+
295+ return $ this ->pipeline
296+ ->enableStreaming ($ streaming )
297+ ->onEnd (function (PipelineEnd $ event ): void {
298+ $ this ->runtimeConfig = $ event ->config ;
299+ })
300+ ->invoke ($ payload , $ config );
253301 }
254302
255303 /**
@@ -349,4 +397,9 @@ protected static function buildOutput(ObjectSchema|array|string|null $output): O
349397
350398 return $ output ;
351399 }
400+
401+ protected function eventBelongsToThisInstance (object $ event ): bool
402+ {
403+ return $ event instanceof AgentEvent && $ event ->agent === $ this ;
404+ }
352405}
0 commit comments