yao/agent/assistant/llm.go
Max 31ce461162 Refactor Assistant Stream and Content Handling for Improved Context Management
- Updated the Stream method to include options for handling message history and context more effectively, ensuring original messages are preserved for autoSearch and delegation.
- Introduced a new buildContextMessage function to consolidate conversation context, filtering out system messages and limiting to the last five user messages for efficiency.
- Enhanced content processing by adding a convertToContentParts function to handle different content formats, improving compatibility with historical data.
- Improved logging and error handling in the executeLLMStream method to ensure clarity in LLM request tracing and response handling.
- Added new utility functions for extracting text content and building context messages, enhancing overall code clarity and maintainability.
2025-12-27 19:36:31 +08:00

135 lines
4.2 KiB
Go

package assistant
import (
"github.com/yaoapp/yao/agent/context"
"github.com/yaoapp/yao/agent/llm"
"github.com/yaoapp/yao/agent/output/message"
"github.com/yaoapp/yao/trace/types"
)
// executeLLMStream executes the LLM streaming call with pre-built request
// Returns completionResponse and error
func (ast *Assistant) executeLLMStream(
ctx *context.Context,
completionMessages []context.Message,
completionOptions *context.CompletionOptions,
agentNode types.Node,
streamHandler message.StreamFunc,
opts *context.Options,
) (*context.CompletionResponse, error) {
// Get connector object (capabilities were already set above, before stream_start)
conn, capabilities, err := ast.GetConnector(ctx, opts)
if err != nil {
ast.traceAgentFail(agentNode, err)
return nil, err
}
// Set capabilities in options if not already set
if completionOptions.Capabilities == nil && capabilities != nil {
completionOptions.Capabilities = capabilities
}
// Log the capabilities
ast.traceConnectorCapabilities(agentNode, capabilities)
// Build content - convert extended types (file, data, __yao.attachment://) to standard LLM types
// This is done here (right before LLM call) to ensure:
// 1. autoSearch receives original messages (not converted)
// 2. delegate receives original messages (not converted)
// 3. Only the actual LLM call sees converted messages
llmMessages, err := ast.BuildContent(ctx, completionMessages, completionOptions, opts)
if err != nil {
ast.traceAgentFail(agentNode, err)
return nil, err
}
// Trace Add LLM request (use converted messages for trace)
ast.traceLLMRequest(ctx, conn.ID(), llmMessages, completionOptions)
// Log LLM call start
ctx.Logger.LLMStart(conn.ID(), "", len(llmMessages))
// Create LLM instance with connector and options
llmInstance, err := llm.New(conn, completionOptions)
if err != nil {
// Mark LLM Request as failed in trace
ast.traceLLMFail(ctx, err)
return nil, err
}
// Call the LLM Completion Stream (streamHandler was set earlier)
// Use llmMessages (converted) instead of completionMessages (original)
completionResponse, err := llmInstance.Stream(ctx, llmMessages, completionOptions, streamHandler)
if err != nil {
// Mark LLM Request as failed in trace
ast.traceLLMFail(ctx, err)
return nil, err
}
// Mark LLM Request Complete
ast.traceLLMComplete(ctx, completionResponse)
return completionResponse, nil
}
// executeLLMForToolRetry executes LLM call for tool retry with streaming output
// This is used when retrying tool calls - we still want to show LLM's response to users
// Returns completionResponse and error
func (ast *Assistant) executeLLMForToolRetry(
ctx *context.Context,
completionMessages []context.Message,
completionOptions *context.CompletionOptions,
agentNode types.Node,
streamHandler message.StreamFunc,
opts *context.Options,
) (*context.CompletionResponse, error) {
// Get connector object
conn, capabilities, err := ast.GetConnector(ctx, opts)
if err != nil {
ast.traceAgentFail(agentNode, err)
return nil, err
}
// Set capabilities in options if not already set
if completionOptions.Capabilities == nil && capabilities != nil {
completionOptions.Capabilities = capabilities
}
// Build content - convert extended types for LLM call
llmMessages, err := ast.BuildContent(ctx, completionMessages, completionOptions, opts)
if err != nil {
ast.traceAgentFail(agentNode, err)
return nil, err
}
// Trace Add LLM retry request
ast.traceLLMRetryRequest(ctx, conn.ID(), llmMessages, completionOptions)
// Log LLM call start (retry)
ctx.Logger.LLMStart(conn.ID(), "", len(llmMessages))
// Create LLM instance with connector and options
llmInstance, err := llm.New(conn, completionOptions)
if err != nil {
// Mark LLM Retry Request as failed in trace
ast.traceLLMFail(ctx, err)
return nil, err
}
// Call the LLM Completion Stream (still streaming for tool retry)
// Use llmMessages (converted) instead of completionMessages (original)
completionResponse, err := llmInstance.Stream(ctx, llmMessages, completionOptions, streamHandler)
if err != nil {
// Mark LLM Retry Request as failed in trace
ast.traceLLMFail(ctx, err)
return nil, err
}
// Mark LLM Request Complete
ast.traceLLMComplete(ctx, completionResponse)
return completionResponse, nil
}