@@ -30,6 +30,8 @@ type stepExecutionResult struct {
3030// StopCondition defines a function that determines when an agent should stop executing.
3131type StopCondition = func (steps []StepResult ) bool
3232
33+ type responseGenerator = func (ctx context.Context , model LanguageModel , call Call ) (* Response , error )
34+
3335// StepCountIs returns a stop condition that stops after the specified number of steps.
3436func StepCountIs (stepCount int ) StopCondition {
3537 return func (steps []StepResult ) bool {
@@ -304,6 +306,7 @@ type AgentResult struct {
304306// Agent represents an AI agent that can generate responses and stream responses.
305307type Agent interface {
306308 Generate (context.Context , AgentCall ) (* AgentResult , error )
309+ GenerateObject (context.Context , schema.Schema , AgentCall ) (* AgentResult , error )
307310 Stream (context.Context , AgentStreamCall ) (* AgentResult , error )
308311}
309312
@@ -367,13 +370,12 @@ func (a *agent) prepareCall(call AgentCall) AgentCall {
367370 return call
368371}
369372
370- // Generate implements Agent.
371- func (a * agent ) Generate (ctx context.Context , opts AgentCall ) (* AgentResult , error ) {
372- opts = a .prepareCall (opts )
373- initialPrompt , err := a .createPrompt (a .settings .systemPrompt , opts .Prompt , opts .Messages , opts .Files ... )
374- if err != nil {
375- return nil , err
376- }
373+ func (a * agent ) executeLoop (
374+ ctx context.Context ,
375+ initialPrompt Prompt ,
376+ gen responseGenerator ,
377+ opts AgentCall ,
378+ ) ([]StepResult , error ) {
377379 var responseMessages []Message
378380 var steps []StepResult
379381
@@ -446,7 +448,7 @@ func (a *agent) Generate(ctx context.Context, opts AgentCall) (*AgentResult, err
446448 retryOptions .OnRetry = opts .OnRetry
447449 retry := RetryWithExponentialBackoffRespectingRetryHeaders [* Response ](retryOptions )
448450 result , err := retry (ctx , func () (* Response , error ) {
449- return stepModel . Generate (ctx , Call {
451+ return gen (ctx , stepModel , Call {
450452 Prompt : stepInputMessages ,
451453 MaxOutputTokens : opts .MaxOutputTokens ,
452454 Temperature : opts .Temperature ,
@@ -485,7 +487,7 @@ func (a *agent) Generate(ctx context.Context, opts AgentCall) (*AgentResult, err
485487
486488 toolResults , err := a .executeTools (ctx , stepTools , stepExecProviderTools , stepToolCalls , nil )
487489
488- // Build step content with validated tool calls and tool results. // Provider-executed tool calls are kept as-is.
490+ // Build step content with validated tool calls and tool results. Provider-executed tool calls are kept as-is.
489491 stepContent := []Content {}
490492 toolCallIndex := 0
491493 for _ , content := range result .Content {
@@ -528,8 +530,12 @@ func (a *agent) Generate(ctx context.Context, opts AgentCall) (*AgentResult, err
528530 }
529531 }
530532
531- totalUsage := Usage {}
533+ //nolint:nilerr // tool execution failure breaks the loop but does not prevent an answer from being returned
534+ return steps , nil
535+ }
532536
537+ func toAgentResult (steps []StepResult ) * AgentResult {
538+ totalUsage := Usage {}
533539 for _ , step := range steps {
534540 usage := step .Usage
535541 totalUsage .InputTokens += usage .InputTokens
@@ -540,12 +546,89 @@ func (a *agent) Generate(ctx context.Context, opts AgentCall) (*AgentResult, err
540546 totalUsage .TotalTokens += usage .TotalTokens
541547 }
542548
543- agentResult := & AgentResult {
549+ return & AgentResult {
544550 Steps : steps ,
545551 Response : steps [len (steps )- 1 ].Response ,
546552 TotalUsage : totalUsage ,
547553 }
548- return agentResult , nil
554+ }
555+
556+ // Generate implements Agent.
557+ func (a * agent ) Generate (ctx context.Context , opts AgentCall ) (* AgentResult , error ) {
558+ opts = a .prepareCall (opts )
559+ initialPrompt , err := a .createPrompt (a .settings .systemPrompt , opts .Prompt , opts .Messages , opts .Files ... )
560+ if err != nil {
561+ return nil , err
562+ }
563+ steps , err := a .executeLoop (
564+ ctx ,
565+ initialPrompt ,
566+ func (ctx context.Context , stepModel LanguageModel , call Call ) (* Response , error ) {
567+ return stepModel .Generate (ctx , call )
568+ },
569+ opts ,
570+ )
571+ if err != nil {
572+ return nil , err
573+ }
574+
575+ return toAgentResult (steps ), nil
576+ }
577+
578+ func (a * agent ) GenerateObject (ctx context.Context , s schema.Schema , opts AgentCall ) (* AgentResult , error ) {
579+ opts = a .prepareCall (opts )
580+ initialPrompt , err := a .createPrompt (a .settings .systemPrompt , opts .Prompt , opts .Messages , opts .Files ... )
581+ if err != nil {
582+ return nil , err
583+ }
584+
585+ steps , err := a .executeLoop (
586+ ctx ,
587+ initialPrompt ,
588+ func (ctx context.Context , model LanguageModel , call Call ) (* Response , error ) {
589+ res , err := model .GenerateObject (ctx , ObjectCall {
590+ Prompt : call .Prompt ,
591+ Schema : s ,
592+ MaxOutputTokens : call .MaxOutputTokens ,
593+ Temperature : call .Temperature ,
594+ TopP : call .TopP ,
595+ TopK : call .TopK ,
596+ PresencePenalty : call .PresencePenalty ,
597+ FrequencyPenalty : call .FrequencyPenalty ,
598+ UserAgent : call .UserAgent ,
599+ ProviderOptions : call .ProviderOptions ,
600+ RepairText : nil ,
601+ Tools : call .Tools ,
602+ ToolChoice : call .ToolChoice ,
603+ })
604+ if err != nil {
605+ return nil , err
606+ }
607+
608+ var content ResponseContent
609+ for _ , toolCall := range res .ToolCalls {
610+ content = append (content , toolCall )
611+ }
612+
613+ if res .RawText != "" {
614+ content = append (content , TextContent {Text : res .RawText })
615+ }
616+
617+ return & Response {
618+ Content : content ,
619+ FinishReason : res .FinishReason ,
620+ Usage : res .Usage ,
621+ Warnings : res .Warnings ,
622+ ProviderMetadata : res .ProviderMetadata ,
623+ }, nil
624+ },
625+ opts ,
626+ )
627+ if err != nil {
628+ return nil , err
629+ }
630+
631+ return toAgentResult (steps ), nil
549632}
550633
551634func isStopConditionMet (conditions []StopCondition , steps []StepResult ) bool {
0 commit comments