Add llm client max iterations

This commit is contained in:
dwrz
2026-06-02 12:02:49 +00:00
parent f19177bd84
commit b75ad9067a
2 changed files with 21 additions and 7 deletions

View File

@@ -120,9 +120,15 @@ func (c *Client) DefaultModel() string {
return c.model return c.model
} }
// ErrMaxIterations is returned when the agent loop exceeds the configured
// maximum number of iterations.
var ErrMaxIterations = fmt.Errorf("max iterations exceeded")
// Query sends messages to the LLM using the specified model. // Query sends messages to the LLM using the specified model.
// If model is empty, uses the default configured model. // If model is empty, uses the default configured model.
// If systemMessage is non-empty, it overrides the configured system message. // If systemMessage is non-empty, it overrides the configured system message.
// maxIterations caps the number of agent loop iterations; a value of 0
// means unlimited (no cap).
// Returns all messages generated during the query, including tool calls // Returns all messages generated during the query, including tool calls
// and tool results. The final message is the last element in the slice. // and tool results. The final message is the last element in the slice.
func (c *Client) Query( func (c *Client) Query(
@@ -130,6 +136,7 @@ func (c *Client) Query(
messages []openai.ChatCompletionMessage, messages []openai.ChatCompletionMessage,
model string, model string,
systemMessage string, systemMessage string,
maxIterations int,
) ([]openai.ChatCompletionMessage, error) { ) ([]openai.ChatCompletionMessage, error) {
ctx, cancel := context.WithTimeout(ctx, c.timeout) ctx, cancel := context.WithTimeout(ctx, c.timeout)
defer cancel() defer cancel()
@@ -160,8 +167,8 @@ func (c *Client) Query(
// Track messages generated during this query. // Track messages generated during this query.
var generated []openai.ChatCompletionMessage var generated []openai.ChatCompletionMessage
// Loop for tool calls. // Loop for tool calls. maxIterations == 0 means unlimited.
for { for i := 0; maxIterations == 0 || i < maxIterations; i++ {
req := openai.ChatCompletionRequest{ req := openai.ChatCompletionRequest{
Model: model, Model: model,
Messages: messages, Messages: messages,
@@ -236,6 +243,8 @@ func (c *Client) Query(
} }
// Loop to get LLM's response after tool execution. // Loop to get LLM's response after tool execution.
} }
return generated, ErrMaxIterations
} }
// StreamEvent wraps a ChatCompletionMessage produced during streaming. // StreamEvent wraps a ChatCompletionMessage produced during streaming.
@@ -248,12 +257,15 @@ type StreamEvent struct {
// tool result) is sent to the events channel as it becomes available. // tool result) is sent to the events channel as it becomes available.
// The channel is closed before returning. // The channel is closed before returning.
// If systemMessage is non-empty, it overrides the configured system message. // If systemMessage is non-empty, it overrides the configured system message.
// Returns all messages generated during the query. // maxIterations caps the number of agent loop iterations; a value of 0
// means unlimited (no cap).
// Returns ErrMaxIterations if the iteration limit is exceeded.
func (c *Client) QueryStream( func (c *Client) QueryStream(
ctx context.Context, ctx context.Context,
messages []openai.ChatCompletionMessage, messages []openai.ChatCompletionMessage,
model string, model string,
systemMessage string, systemMessage string,
maxIterations int,
events chan<- StreamEvent, events chan<- StreamEvent,
) error { ) error {
defer close(events) defer close(events)
@@ -284,8 +296,8 @@ func (c *Client) QueryStream(
) )
} }
// Loop for tool calls. // Loop for tool calls. maxIterations == 0 means unlimited.
for { for i := 0; maxIterations == 0 || i < maxIterations; i++ {
req := openai.ChatCompletionRequest{ req := openai.ChatCompletionRequest{
Model: model, Model: model,
Messages: messages, Messages: messages,
@@ -423,4 +435,6 @@ func (c *Client) QueryStream(
} }
// Loop to get LLM's response after tool execution. // Loop to get LLM's response after tool execution.
} }
return ErrMaxIterations
} }

View File

@@ -453,7 +453,7 @@ func (svc *Service) voice(w http.ResponseWriter, r *http.Request) {
if model == "" { if model == "" {
model = svc.llm.DefaultModel() model = svc.llm.DefaultModel()
} }
msgs, err := svc.llm.Query(ctx, messages, model, req.SystemMessage) msgs, err := svc.llm.Query(ctx, messages, model, req.SystemMessage, 0)
if err != nil { if err != nil {
log.ErrorContext( log.ErrorContext(
ctx, ctx,
@@ -718,7 +718,7 @@ func (svc *Service) voiceStream(w http.ResponseWriter, r *http.Request) {
llmErr error llmErr error
) )
go func() { go func() {
llmErr = svc.llm.QueryStream(ctx, messages, model, req.SystemMessage, events) llmErr = svc.llm.QueryStream(ctx, messages, model, req.SystemMessage, 0, events)
}() }()
// Consume events and send as SSE. // Consume events and send as SSE.