@@ -8,11 +8,12 @@ import (
88
99 "github.com/xraph/cortex"
1010 "github.com/xraph/cortex/agent"
11+ "github.com/xraph/cortex/engine"
1112 "github.com/xraph/cortex/id"
1213)
1314
1415func (a * API ) registerAgentRoutes (router forge.Router ) error {
15- g := router .Group ("/cortex " , forge .WithGroupTags ("agents" ))
16+ g := router .Group ("" , forge .WithGroupTags ("agents" ))
1617
1718 if err := g .POST ("/agents" , a .createAgent ,
1819 forge .WithSummary ("Create agent" ),
@@ -87,6 +88,16 @@ func (a *API) registerAgentRoutes(router forge.Router) error {
8788 return fmt .Errorf ("register agent routes: %w" , err )
8889 }
8990
91+ if err := g .POST ("/agents/:name/preview-prompt" , a .previewPrompt ,
92+ forge .WithSummary ("Preview prompt" ),
93+ forge .WithDescription ("Returns the computed system prompt for the agent without executing." ),
94+ forge .WithOperationID ("previewPrompt" ),
95+ forge .WithResponseSchema (http .StatusOK , "Computed prompt" , & PreviewPromptResponse {}),
96+ forge .WithErrorResponses (),
97+ ); err != nil {
98+ return fmt .Errorf ("register agent routes: %w" , err )
99+ }
100+
90101 return nil
91102}
92103
@@ -132,7 +143,7 @@ func (a *API) getAgent(ctx forge.Context, _ *GetAgentRequest) (*agent.Config, er
132143 return cfg , ctx .JSON (http .StatusOK , cfg )
133144}
134145
135- func (a * API ) listAgents (ctx forge.Context , req * ListAgentsRequest ) ([] * agent. Config , error ) {
146+ func (a * API ) listAgents (ctx forge.Context , req * ListAgentsRequest ) (* ListAgentsResponse , error ) {
136147 agents , err := a .eng .ListAgents (ctx .Context (), & agent.ListFilter {
137148 AppID : cortex .AppFromContext (ctx .Context ()),
138149 Limit : defaultLimit (req .Limit ),
@@ -141,7 +152,8 @@ func (a *API) listAgents(ctx forge.Context, req *ListAgentsRequest) ([]*agent.Co
141152 if err != nil {
142153 return nil , fmt .Errorf ("list agents: %w" , err )
143154 }
144- return agents , ctx .JSON (http .StatusOK , agents )
155+ resp := & ListAgentsResponse {Items : agents }
156+ return resp , ctx .JSON (http .StatusOK , resp )
145157}
146158
147159func (a * API ) updateAgent (ctx forge.Context , req * UpdateAgentRequest ) (* agent.Config , error ) {
@@ -210,12 +222,91 @@ func (a *API) deleteAgent(ctx forge.Context, _ *DeleteAgentRequest) (*struct{},
210222 return nil , ctx .NoContent (http .StatusNoContent )
211223}
212224
213- func (a * API ) runAgent (_ forge.Context , _ * RunAgentRequest ) (* struct {}, error ) {
214- // TODO: implement agent execution in phase 2
215- return nil , forge .NotFound ("agent execution not yet implemented" )
225+ func (a * API ) runAgent (ctx forge.Context , req * RunAgentRequest ) (* RunAgentResponse , error ) {
226+ if req .Input == "" {
227+ return nil , forge .BadRequest ("input is required" )
228+ }
229+
230+ appID := cortex .AppFromContext (ctx .Context ())
231+ r , err := a .eng .RunAgent (ctx .Context (), appID , req .Name , req .Input , mapOverrides (req .Overrides ))
232+ if err != nil {
233+ return nil , mapStoreError (err )
234+ }
235+
236+ var durationMs int64
237+ if r .StartedAt != nil && r .CompletedAt != nil {
238+ durationMs = r .CompletedAt .Sub (* r .StartedAt ).Milliseconds ()
239+ }
240+
241+ resp := & RunAgentResponse {
242+ RunID : r .ID .String (),
243+ Output : r .Output ,
244+ State : string (r .State ),
245+ StepCount : r .StepCount ,
246+ TokensUsed : r .TokensUsed ,
247+ DurationMs : durationMs ,
248+ }
249+ return resp , ctx .JSON (http .StatusOK , resp )
216250}
217251
218- func (a * API ) streamAgent (_ forge.Context , _ * StreamAgentRequest ) (* struct {}, error ) {
219- // TODO: implement streaming agent execution in phase 2
220- return nil , forge .NotFound ("agent streaming not yet implemented" )
252+ func (a * API ) streamAgent (ctx forge.Context , req * StreamAgentRequest ) (* struct {}, error ) {
253+ if req .Input == "" {
254+ return nil , forge .BadRequest ("input is required" )
255+ }
256+
257+ ctx .SetHeader ("Content-Type" , "text/event-stream" )
258+ ctx .SetHeader ("Cache-Control" , "no-cache" )
259+ ctx .SetHeader ("Connection" , "keep-alive" )
260+ ctx .SetHeader ("X-Accel-Buffering" , "no" )
261+
262+ appID := cortex .AppFromContext (ctx .Context ())
263+ events := make (chan engine.StreamEvent , 64 )
264+
265+ if err := a .eng .StreamAgent (ctx .Context (), appID , req .Name , req .Input , mapOverrides (req .Overrides ), events ); err != nil {
266+ return nil , mapStoreError (err )
267+ }
268+
269+ for evt := range events {
270+ if err := ctx .WriteSSE (string (evt .Type ), evt .Data ); err != nil {
271+ break
272+ }
273+ }
274+
275+ return nil , nil
276+ }
277+
278+ func (a * API ) previewPrompt (ctx forge.Context , _ * PreviewPromptRequest ) (* PreviewPromptResponse , error ) {
279+ appID := cortex .AppFromContext (ctx .Context ())
280+ ag , err := a .eng .GetAgentByName (ctx .Context (), appID , ctx .Param ("name" ))
281+ if err != nil {
282+ return nil , mapStoreError (err )
283+ }
284+
285+ // Use the engine's prompt builder for a consistent preview.
286+ prompt := a .eng .BuildSystemPrompt (ctx .Context (), ag , nil )
287+
288+ resp := & PreviewPromptResponse {
289+ Prompt : prompt ,
290+ }
291+ return resp , ctx .JSON (http .StatusOK , resp )
292+ }
293+
294+ // mapOverrides converts API-layer overrides to engine-layer overrides.
295+ func mapOverrides (o * AgentOverrides ) * engine.RunOverrides {
296+ if o == nil {
297+ return nil
298+ }
299+ return & engine.RunOverrides {
300+ Model : o .Model ,
301+ Temperature : o .Temperature ,
302+ MaxSteps : o .MaxSteps ,
303+ MaxTokens : o .MaxTokens ,
304+ ReasoningLoop : o .ReasoningLoop ,
305+ SystemPrompt : o .SystemPrompt ,
306+ PersonaRef : o .PersonaRef ,
307+ InlineSkills : o .InlineSkills ,
308+ InlineTraits : o .InlineTraits ,
309+ InlineBehaviors : o .InlineBehaviors ,
310+ Tools : o .Tools ,
311+ }
221312}
0 commit comments