585 lines
22 KiB
Go
585 lines
22 KiB
Go
package mcp
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/maximhq/bifrost/core/schemas"
|
|
)
|
|
|
|
// agentAPIAdapter defines the interface for API-specific operations in agent mode.
|
|
// This adapter pattern allows the agent execution logic to work with both Chat Completions
|
|
// and Responses APIs without requiring API-specific code in the agent loop.
|
|
//
|
|
// The adapter handles format conversions at the boundaries:
|
|
// - Responses API requests/responses are converted to/from Chat API format
|
|
// - Tool calls are extracted in Chat format for uniform processing
|
|
// - Results are converted back to the original API format for the response
|
|
//
|
|
// This design ensures that:
|
|
// 1. Tool execution logic is format-agnostic
|
|
// 2. Both APIs have feature parity
|
|
// 3. Conversions are localized to adapters
|
|
// 4. The agent loop remains API-neutral
|
|
type agentAPIAdapter interface {
|
|
// Extract conversation history from the original request
|
|
getConversationHistory() []interface{}
|
|
|
|
// Get original request
|
|
getOriginalRequest() interface{}
|
|
|
|
// Get initial response
|
|
getInitialResponse() interface{}
|
|
|
|
// Check if response has tool calls
|
|
hasToolCalls(response interface{}) bool
|
|
|
|
// Extract tool calls from response.
|
|
// For Chat API: Returns tool calls directly from the response.
|
|
// For Responses API: Converts ResponsesMessage tool calls to ChatAssistantMessageToolCall for processing.
|
|
extractToolCalls(response interface{}) []schemas.ChatAssistantMessageToolCall
|
|
|
|
// Add assistant message with tool calls to conversation
|
|
addAssistantMessage(conversation []interface{}, response interface{}) []interface{}
|
|
|
|
// Add tool results to conversation.
|
|
// For Chat API: Adds ChatMessage results directly.
|
|
// For Responses API: Converts ChatMessage results to ResponsesMessage via ToResponsesToolMessage().
|
|
addToolResults(conversation []interface{}, toolResults []*schemas.ChatMessage) []interface{}
|
|
|
|
// Create new request with updated conversation
|
|
createNewRequest(conversation []interface{}) interface{}
|
|
|
|
// Make LLM call
|
|
makeLLMCall(ctx *schemas.BifrostContext, request interface{}) (interface{}, *schemas.BifrostError)
|
|
|
|
// Create response with executed tools and non-auto-executable calls
|
|
createResponseWithExecutedTools(
|
|
response interface{},
|
|
executedToolResults []*schemas.ChatMessage,
|
|
executedToolCalls []schemas.ChatAssistantMessageToolCall,
|
|
nonAutoExecutableToolCalls []schemas.ChatAssistantMessageToolCall,
|
|
) interface{}
|
|
|
|
// extractUsage returns the token usage from a response as BifrostLLMUsage.
|
|
extractUsage(response interface{}) *schemas.BifrostLLMUsage
|
|
|
|
// applyUsage sets accumulated usage on the response in place.
|
|
applyUsage(response interface{}, usage *schemas.BifrostLLMUsage)
|
|
}
|
|
|
|
// chatAPIAdapter implements agentAPIAdapter for Chat API
|
|
type chatAPIAdapter struct {
|
|
originalReq *schemas.BifrostChatRequest
|
|
initialResponse *schemas.BifrostChatResponse
|
|
makeReq func(ctx *schemas.BifrostContext, req *schemas.BifrostChatRequest) (*schemas.BifrostChatResponse, *schemas.BifrostError)
|
|
}
|
|
|
|
// responsesAPIAdapter implements agentAPIAdapter for Responses API.
|
|
// It enables the agent mode execution loop to work with Responses API requests and responses
|
|
// by handling format conversions transparently.
|
|
//
|
|
// Key conversions performed:
|
|
// - extractToolCalls(): Converts ResponsesMessage tool calls to ChatAssistantMessageToolCall
|
|
// via BifrostResponsesResponse.ToBifrostChatResponse() and existing extraction logic
|
|
// - addToolResults(): Converts ChatMessage tool results back to ResponsesMessage
|
|
// via ChatMessage.ToResponsesMessages() and ToResponsesToolMessage()
|
|
// - createNewRequest(): Builds a new BifrostResponsesRequest from converted conversation
|
|
// - createResponseWithExecutedTools(): Creates a Responses response with results and pending tools
|
|
//
|
|
// This adapter enables full feature parity between Chat Completions and Responses APIs
|
|
// for tool execution in agent mode.
|
|
type responsesAPIAdapter struct {
|
|
originalReq *schemas.BifrostResponsesRequest
|
|
initialResponse *schemas.BifrostResponsesResponse
|
|
makeReq func(ctx *schemas.BifrostContext, req *schemas.BifrostResponsesRequest) (*schemas.BifrostResponsesResponse, *schemas.BifrostError)
|
|
}
|
|
|
|
// Chat API adapter implementations
|
|
func (c *chatAPIAdapter) getConversationHistory() []interface{} {
|
|
history := make([]interface{}, 0)
|
|
if c.originalReq.Input != nil {
|
|
for _, msg := range c.originalReq.Input {
|
|
history = append(history, msg)
|
|
}
|
|
}
|
|
return history
|
|
}
|
|
|
|
func (c *chatAPIAdapter) getOriginalRequest() interface{} {
|
|
return c.originalReq
|
|
}
|
|
|
|
func (c *chatAPIAdapter) getInitialResponse() interface{} {
|
|
return c.initialResponse
|
|
}
|
|
|
|
func (c *chatAPIAdapter) hasToolCalls(response interface{}) bool {
|
|
chatResponse := response.(*schemas.BifrostChatResponse)
|
|
return hasToolCallsForChatResponse(chatResponse)
|
|
}
|
|
|
|
func (c *chatAPIAdapter) extractToolCalls(response interface{}) []schemas.ChatAssistantMessageToolCall {
|
|
chatResponse := response.(*schemas.BifrostChatResponse)
|
|
return extractToolCalls(chatResponse)
|
|
}
|
|
|
|
func (c *chatAPIAdapter) addAssistantMessage(conversation []interface{}, response interface{}) []interface{} {
|
|
chatResponse := response.(*schemas.BifrostChatResponse)
|
|
for _, choice := range chatResponse.Choices {
|
|
if choice.ChatNonStreamResponseChoice != nil && choice.ChatNonStreamResponseChoice.Message != nil {
|
|
conversation = append(conversation, *choice.ChatNonStreamResponseChoice.Message)
|
|
}
|
|
}
|
|
return conversation
|
|
}
|
|
|
|
func (c *chatAPIAdapter) addToolResults(conversation []interface{}, toolResults []*schemas.ChatMessage) []interface{} {
|
|
for _, toolResult := range toolResults {
|
|
conversation = append(conversation, *toolResult)
|
|
}
|
|
return conversation
|
|
}
|
|
|
|
func (c *chatAPIAdapter) createNewRequest(conversation []interface{}) interface{} {
|
|
// Convert conversation back to ChatMessage slice
|
|
chatMessages := make([]schemas.ChatMessage, 0, len(conversation))
|
|
for _, msg := range conversation {
|
|
if msg == nil {
|
|
continue
|
|
}
|
|
if chatMessage, ok := msg.(schemas.ChatMessage); ok {
|
|
chatMessages = append(chatMessages, chatMessage)
|
|
}
|
|
}
|
|
|
|
return &schemas.BifrostChatRequest{
|
|
Provider: c.originalReq.Provider,
|
|
Model: c.originalReq.Model,
|
|
Fallbacks: c.originalReq.Fallbacks,
|
|
Params: c.originalReq.Params,
|
|
Input: chatMessages,
|
|
}
|
|
}
|
|
|
|
func (c *chatAPIAdapter) makeLLMCall(ctx *schemas.BifrostContext, request interface{}) (interface{}, *schemas.BifrostError) {
|
|
chatRequest := request.(*schemas.BifrostChatRequest)
|
|
return c.makeReq(ctx, chatRequest)
|
|
}
|
|
|
|
func (c *chatAPIAdapter) createResponseWithExecutedTools(
|
|
response interface{},
|
|
executedToolResults []*schemas.ChatMessage,
|
|
executedToolCalls []schemas.ChatAssistantMessageToolCall,
|
|
nonAutoExecutableToolCalls []schemas.ChatAssistantMessageToolCall,
|
|
) interface{} {
|
|
chatResponse := response.(*schemas.BifrostChatResponse)
|
|
return createChatResponseWithExecutedToolsAndNonAutoExecutableCalls(
|
|
chatResponse,
|
|
executedToolResults,
|
|
executedToolCalls,
|
|
nonAutoExecutableToolCalls,
|
|
)
|
|
}
|
|
|
|
func (c *chatAPIAdapter) extractUsage(response interface{}) *schemas.BifrostLLMUsage {
|
|
return response.(*schemas.BifrostChatResponse).Usage
|
|
}
|
|
|
|
func (c *chatAPIAdapter) applyUsage(response interface{}, usage *schemas.BifrostLLMUsage) {
|
|
response.(*schemas.BifrostChatResponse).Usage = usage
|
|
}
|
|
|
|
// createChatResponseWithExecutedToolsAndNonAutoExecutableCalls creates a chat response
|
|
// that includes executed tool results and non-auto-executable tool calls. The response
|
|
// contains a formatted text summary of executed tool results and includes the non-auto-executable
|
|
// tool calls for the caller to handle. The finish reason is set to "stop" to prevent
|
|
// further agent loop iterations.
|
|
//
|
|
// Parameters:
|
|
// - originalResponse: The original chat response to copy metadata from
|
|
// - executedToolResults: List of tool execution results from auto-executable tools
|
|
// - executedToolCalls: List of tool calls that were executed
|
|
// - nonAutoExecutableToolCalls: List of tool calls that require manual execution
|
|
//
|
|
// Returns:
|
|
// - *schemas.BifrostChatResponse: A new chat response with executed results and pending tool calls
|
|
func createChatResponseWithExecutedToolsAndNonAutoExecutableCalls(
|
|
originalResponse *schemas.BifrostChatResponse,
|
|
executedToolResults []*schemas.ChatMessage,
|
|
executedToolCalls []schemas.ChatAssistantMessageToolCall,
|
|
nonAutoExecutableToolCalls []schemas.ChatAssistantMessageToolCall,
|
|
) *schemas.BifrostChatResponse {
|
|
// Start with a copy of the original response metadata
|
|
response := &schemas.BifrostChatResponse{
|
|
ID: originalResponse.ID,
|
|
Object: originalResponse.Object,
|
|
Created: originalResponse.Created,
|
|
Model: originalResponse.Model,
|
|
Choices: make([]schemas.BifrostResponseChoice, 0),
|
|
ServiceTier: originalResponse.ServiceTier,
|
|
SystemFingerprint: originalResponse.SystemFingerprint,
|
|
Usage: originalResponse.Usage,
|
|
ExtraFields: originalResponse.ExtraFields,
|
|
SearchResults: originalResponse.SearchResults,
|
|
Videos: originalResponse.Videos,
|
|
Citations: originalResponse.Citations,
|
|
}
|
|
|
|
// Build a map from tool call ID to tool name for easy lookup
|
|
toolCallIDToName := make(map[string]string)
|
|
for _, toolCall := range executedToolCalls {
|
|
if toolCall.ID != nil && toolCall.Function.Name != nil {
|
|
toolCallIDToName[*toolCall.ID] = *toolCall.Function.Name
|
|
}
|
|
}
|
|
|
|
// Build content text showing executed tool results
|
|
var contentText string
|
|
if len(executedToolResults) > 0 {
|
|
// Format tool results as JSON-like structure
|
|
toolResultsMap := make(map[string]interface{})
|
|
for _, toolResult := range executedToolResults {
|
|
// Get tool name from tool call ID mapping
|
|
var toolName string
|
|
if toolResult.ChatToolMessage != nil && toolResult.ChatToolMessage.ToolCallID != nil {
|
|
toolCallID := *toolResult.ChatToolMessage.ToolCallID
|
|
if name, ok := toolCallIDToName[toolCallID]; ok {
|
|
toolName = name
|
|
} else {
|
|
toolName = toolCallID // Fallback to tool call ID if name not found
|
|
}
|
|
} else {
|
|
toolName = "unknown_tool"
|
|
}
|
|
|
|
// Extract output from tool result
|
|
var output interface{}
|
|
if toolResult.Content != nil {
|
|
if toolResult.Content.ContentStr != nil {
|
|
output = *toolResult.Content.ContentStr
|
|
} else if toolResult.Content.ContentBlocks != nil {
|
|
// Convert content blocks to a readable format
|
|
blocks := make([]map[string]interface{}, 0)
|
|
for _, block := range toolResult.Content.ContentBlocks {
|
|
blockMap := make(map[string]interface{})
|
|
blockMap["type"] = string(block.Type)
|
|
if block.Text != nil {
|
|
blockMap["text"] = *block.Text
|
|
}
|
|
blocks = append(blocks, blockMap)
|
|
}
|
|
output = blocks
|
|
}
|
|
}
|
|
toolResultsMap[toolName] = output
|
|
}
|
|
|
|
// Convert to JSON string for display
|
|
jsonBytes, err := schemas.MarshalSorted(toolResultsMap)
|
|
if err != nil {
|
|
// Fallback to simple string representation
|
|
contentText = fmt.Sprintf("The Output from allowed tools calls is - %v\n\nNow I shall call these tools next...", toolResultsMap)
|
|
} else {
|
|
contentText = fmt.Sprintf("The Output from allowed tools calls is - %s\n\nNow I shall call these tools next...", string(jsonBytes))
|
|
}
|
|
} else {
|
|
contentText = "Now I shall call these tools next..."
|
|
}
|
|
|
|
// Create content with the formatted text
|
|
content := &schemas.ChatMessageContent{
|
|
ContentStr: &contentText,
|
|
}
|
|
|
|
// Determine finish reason
|
|
// Note: We set finish_reason to "stop" (not "tool_calls") for non-auto-executable tools
|
|
// to prevent the agent loop from retrying. The tool calls are still included in the response
|
|
// for the caller to handle, but setting finish_reason to "stop" ensures hasToolCalls returns false
|
|
// and the agent loop exits properly.
|
|
finishReason := "stop"
|
|
|
|
// Create a single choice with the formatted content and non-auto-executable tool calls
|
|
response.Choices = append(response.Choices, schemas.BifrostResponseChoice{
|
|
Index: 0,
|
|
FinishReason: &finishReason,
|
|
ChatNonStreamResponseChoice: &schemas.ChatNonStreamResponseChoice{
|
|
Message: &schemas.ChatMessage{
|
|
Role: schemas.ChatMessageRoleAssistant,
|
|
Content: content,
|
|
ChatAssistantMessage: &schemas.ChatAssistantMessage{
|
|
ToolCalls: nonAutoExecutableToolCalls,
|
|
},
|
|
},
|
|
},
|
|
})
|
|
|
|
return response
|
|
}
|
|
|
|
// Responses API adapter implementations
|
|
func (r *responsesAPIAdapter) getConversationHistory() []interface{} {
|
|
history := make([]interface{}, 0)
|
|
if r.originalReq.Input != nil {
|
|
for _, msg := range r.originalReq.Input {
|
|
history = append(history, msg)
|
|
}
|
|
}
|
|
return history
|
|
}
|
|
|
|
func (r *responsesAPIAdapter) getOriginalRequest() interface{} {
|
|
return r.originalReq
|
|
}
|
|
|
|
func (r *responsesAPIAdapter) getInitialResponse() interface{} {
|
|
return r.initialResponse
|
|
}
|
|
|
|
func (r *responsesAPIAdapter) hasToolCalls(response interface{}) bool {
|
|
responsesResponse := response.(*schemas.BifrostResponsesResponse)
|
|
return hasToolCallsForResponsesResponse(responsesResponse)
|
|
}
|
|
|
|
func (r *responsesAPIAdapter) extractToolCalls(response interface{}) []schemas.ChatAssistantMessageToolCall {
|
|
responsesResponse := response.(*schemas.BifrostResponsesResponse)
|
|
// Convert to Chat format and extract tool calls using existing logic
|
|
chatResponse := responsesResponse.ToBifrostChatResponse()
|
|
return extractToolCalls(chatResponse)
|
|
}
|
|
|
|
func (r *responsesAPIAdapter) addAssistantMessage(conversation []interface{}, response interface{}) []interface{} {
|
|
responsesResponse := response.(*schemas.BifrostResponsesResponse)
|
|
for _, output := range responsesResponse.Output {
|
|
conversation = append(conversation, output)
|
|
}
|
|
return conversation
|
|
}
|
|
|
|
func (r *responsesAPIAdapter) addToolResults(conversation []interface{}, toolResults []*schemas.ChatMessage) []interface{} {
|
|
for _, toolResult := range toolResults {
|
|
// Convert using existing converter
|
|
responsesMessages := toolResult.ToResponsesMessages()
|
|
for _, respMsg := range responsesMessages {
|
|
conversation = append(conversation, respMsg)
|
|
}
|
|
}
|
|
return conversation
|
|
}
|
|
|
|
func (r *responsesAPIAdapter) createNewRequest(conversation []interface{}) interface{} {
|
|
// Convert conversation back to ResponsesMessage slice
|
|
responsesMessages := make([]schemas.ResponsesMessage, 0, len(conversation))
|
|
for _, msg := range conversation {
|
|
responsesMessages = append(responsesMessages, msg.(schemas.ResponsesMessage))
|
|
}
|
|
|
|
return &schemas.BifrostResponsesRequest{
|
|
Provider: r.originalReq.Provider,
|
|
Model: r.originalReq.Model,
|
|
Fallbacks: r.originalReq.Fallbacks,
|
|
Params: r.originalReq.Params,
|
|
Input: responsesMessages,
|
|
}
|
|
}
|
|
|
|
func (r *responsesAPIAdapter) makeLLMCall(ctx *schemas.BifrostContext, request interface{}) (interface{}, *schemas.BifrostError) {
|
|
responsesRequest := request.(*schemas.BifrostResponsesRequest)
|
|
return r.makeReq(ctx, responsesRequest)
|
|
}
|
|
|
|
func (r *responsesAPIAdapter) createResponseWithExecutedTools(
|
|
response interface{},
|
|
executedToolResults []*schemas.ChatMessage,
|
|
executedToolCalls []schemas.ChatAssistantMessageToolCall,
|
|
nonAutoExecutableToolCalls []schemas.ChatAssistantMessageToolCall,
|
|
) interface{} {
|
|
responsesResponse := response.(*schemas.BifrostResponsesResponse)
|
|
|
|
// Create response with executed tools directly on Responses schema
|
|
return createResponsesResponseWithExecutedToolsAndNonAutoExecutableCalls(
|
|
responsesResponse,
|
|
executedToolResults,
|
|
executedToolCalls,
|
|
nonAutoExecutableToolCalls,
|
|
)
|
|
}
|
|
|
|
func (r *responsesAPIAdapter) extractUsage(response interface{}) *schemas.BifrostLLMUsage {
|
|
return response.(*schemas.BifrostResponsesResponse).Usage.ToBifrostLLMUsage()
|
|
}
|
|
|
|
func (r *responsesAPIAdapter) applyUsage(response interface{}, usage *schemas.BifrostLLMUsage) {
|
|
response.(*schemas.BifrostResponsesResponse).Usage = usage.ToResponsesResponseUsage()
|
|
}
|
|
|
|
// createResponsesResponseWithExecutedToolsAndNonAutoExecutableCalls creates a responses response
|
|
// that includes executed tool results and non-auto-executable tool calls. The response
|
|
// contains a formatted text summary of executed tool results and includes the non-auto-executable
|
|
// tool calls for the caller to handle. All Response-specific fields are preserved.
|
|
//
|
|
// Parameters:
|
|
// - originalResponse: The original responses response to copy metadata from
|
|
// - executedToolResults: List of tool execution results from auto-executable tools
|
|
// - executedToolCalls: List of tool calls that were executed
|
|
// - nonAutoExecutableToolCalls: List of tool calls that require manual execution
|
|
//
|
|
// Returns:
|
|
// - *schemas.BifrostResponsesResponse: A new responses response with executed results and pending tool calls
|
|
func createResponsesResponseWithExecutedToolsAndNonAutoExecutableCalls(
|
|
originalResponse *schemas.BifrostResponsesResponse,
|
|
executedToolResults []*schemas.ChatMessage,
|
|
executedToolCalls []schemas.ChatAssistantMessageToolCall,
|
|
nonAutoExecutableToolCalls []schemas.ChatAssistantMessageToolCall,
|
|
) *schemas.BifrostResponsesResponse {
|
|
// Start with a copy of the original response, preserving all Response-specific fields
|
|
response := &schemas.BifrostResponsesResponse{
|
|
ID: originalResponse.ID,
|
|
Background: originalResponse.Background,
|
|
Conversation: originalResponse.Conversation,
|
|
CreatedAt: originalResponse.CreatedAt,
|
|
Error: originalResponse.Error,
|
|
Include: originalResponse.Include,
|
|
IncompleteDetails: originalResponse.IncompleteDetails,
|
|
Instructions: originalResponse.Instructions,
|
|
MaxOutputTokens: originalResponse.MaxOutputTokens,
|
|
MaxToolCalls: originalResponse.MaxToolCalls,
|
|
Metadata: originalResponse.Metadata,
|
|
ParallelToolCalls: originalResponse.ParallelToolCalls,
|
|
PreviousResponseID: originalResponse.PreviousResponseID,
|
|
Prompt: originalResponse.Prompt,
|
|
PromptCacheKey: originalResponse.PromptCacheKey,
|
|
Reasoning: originalResponse.Reasoning,
|
|
SafetyIdentifier: originalResponse.SafetyIdentifier,
|
|
ServiceTier: originalResponse.ServiceTier,
|
|
StreamOptions: originalResponse.StreamOptions,
|
|
Store: originalResponse.Store,
|
|
Temperature: originalResponse.Temperature,
|
|
Text: originalResponse.Text,
|
|
TopLogProbs: originalResponse.TopLogProbs,
|
|
TopP: originalResponse.TopP,
|
|
ToolChoice: originalResponse.ToolChoice,
|
|
Tools: originalResponse.Tools,
|
|
Truncation: originalResponse.Truncation,
|
|
Usage: originalResponse.Usage,
|
|
ExtraFields: originalResponse.ExtraFields,
|
|
// Perplexity-specific fields
|
|
SearchResults: originalResponse.SearchResults,
|
|
Videos: originalResponse.Videos,
|
|
Citations: originalResponse.Citations,
|
|
Output: make([]schemas.ResponsesMessage, 0),
|
|
}
|
|
|
|
// Build a map from tool call ID to tool name for easy lookup
|
|
toolCallIDToName := make(map[string]string)
|
|
for _, toolCall := range executedToolCalls {
|
|
if toolCall.ID != nil && toolCall.Function.Name != nil {
|
|
toolCallIDToName[*toolCall.ID] = *toolCall.Function.Name
|
|
}
|
|
}
|
|
|
|
// Build content text showing executed tool results
|
|
var contentText string
|
|
if len(executedToolResults) > 0 {
|
|
// Format tool results as JSON-like structure
|
|
toolResultsMap := make(map[string]interface{})
|
|
for _, toolResult := range executedToolResults {
|
|
// Get tool name from tool call ID mapping
|
|
var toolName string
|
|
if toolResult.ChatToolMessage != nil && toolResult.ChatToolMessage.ToolCallID != nil {
|
|
toolCallID := *toolResult.ChatToolMessage.ToolCallID
|
|
if name, ok := toolCallIDToName[toolCallID]; ok {
|
|
toolName = name
|
|
} else {
|
|
toolName = toolCallID // Fallback to tool call ID if name not found
|
|
}
|
|
} else {
|
|
toolName = "unknown_tool"
|
|
}
|
|
|
|
// Extract output from tool result
|
|
var output interface{}
|
|
if toolResult.Content != nil {
|
|
if toolResult.Content.ContentStr != nil {
|
|
output = *toolResult.Content.ContentStr
|
|
} else if toolResult.Content.ContentBlocks != nil {
|
|
// Convert content blocks to a readable format
|
|
blocks := make([]map[string]interface{}, 0)
|
|
for _, block := range toolResult.Content.ContentBlocks {
|
|
blockMap := make(map[string]interface{})
|
|
blockMap["type"] = string(block.Type)
|
|
if block.Text != nil {
|
|
blockMap["text"] = *block.Text
|
|
}
|
|
blocks = append(blocks, blockMap)
|
|
}
|
|
output = blocks
|
|
}
|
|
}
|
|
toolResultsMap[toolName] = output
|
|
}
|
|
|
|
// Convert to JSON string for display
|
|
jsonBytes, err := schemas.MarshalSorted(toolResultsMap)
|
|
if err != nil {
|
|
// Fallback to simple string representation
|
|
contentText = fmt.Sprintf("The Output from allowed tools calls is - %v\n\nNow I shall call these tools next...", toolResultsMap)
|
|
} else {
|
|
contentText = fmt.Sprintf("The Output from allowed tools calls is - %s\n\nNow I shall call these tools next...", string(jsonBytes))
|
|
}
|
|
} else {
|
|
contentText = "Now I shall call these tools next..."
|
|
}
|
|
|
|
// Create assistant message with the formatted text content
|
|
messageType := schemas.ResponsesMessageTypeMessage
|
|
role := schemas.ResponsesInputMessageRoleAssistant
|
|
assistantMessage := schemas.ResponsesMessage{
|
|
Type: &messageType,
|
|
Role: &role,
|
|
Content: &schemas.ResponsesMessageContent{
|
|
ContentBlocks: []schemas.ResponsesMessageContentBlock{
|
|
{
|
|
Type: schemas.ResponsesOutputMessageContentTypeText,
|
|
Text: &contentText,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
response.Output = append(response.Output, assistantMessage)
|
|
|
|
// Add non-auto-executable tool calls as separate function_call messages
|
|
for _, toolCall := range nonAutoExecutableToolCalls {
|
|
functionCallType := schemas.ResponsesMessageTypeFunctionCall
|
|
assistantRole := schemas.ResponsesInputMessageRoleAssistant
|
|
|
|
var callID *string
|
|
if toolCall.ID != nil && *toolCall.ID != "" {
|
|
callID = toolCall.ID
|
|
}
|
|
|
|
var namePtr *string
|
|
if toolCall.Function.Name != nil && *toolCall.Function.Name != "" {
|
|
namePtr = toolCall.Function.Name
|
|
}
|
|
|
|
var argumentsPtr *string
|
|
if toolCall.Function.Arguments != "" {
|
|
argumentsPtr = &toolCall.Function.Arguments
|
|
}
|
|
|
|
toolCallMessage := schemas.ResponsesMessage{
|
|
Type: &functionCallType,
|
|
Role: &assistantRole,
|
|
ResponsesToolMessage: &schemas.ResponsesToolMessage{
|
|
CallID: callID,
|
|
Name: namePtr,
|
|
Arguments: argumentsPtr,
|
|
},
|
|
}
|
|
|
|
response.Output = append(response.Output, toolCallMessage)
|
|
}
|
|
|
|
return response
|
|
}
|