Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 19 additions & 13 deletions internal/flow/processor/functioncall.go
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,15 @@ func (p *FunctionCallResponseProcessor) buildMergedParallelEvent(
return mergedEvent
}

func toolJsonContent(ctx context.Context, toolName string, toolDeclaration *tool.Declaration, jsonArgs []byte, id string, result any) (tool.Message, error) {
body, err := json.Marshal(result)
return model.Message{
Role: model.RoleTool,
Content: string(body),
ToolID: id,
}, err
}

// executeToolCall executes a single tool call and returns the choice.
// Parameters:
// - ctx: context for cancellation and tracing
Expand Down Expand Up @@ -602,27 +611,24 @@ func (p *FunctionCallResponseProcessor) executeToolCall(
}
}

// Marshal the result to JSON.
resultBytes, err := json.Marshal(result)
format := toolJsonContent
if p.toolCallbacks.ToolFormatMessage != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p.toolCallbacks 可能是 nil,得判断一下

format = p.toolCallbacks.ToolFormatMessage
}

msg, err := format(ctx, toolCall.Function.Name, tl.Declaration(), toolCall.Function.Arguments, toolCall.ID, result)
if err != nil {
// Marshal failures (for example, NaN in floats) do not
// affect the overall flow. Downgrade to warning to avoid
// noisy alerts while still surfacing the issue.
log.Warnf("Failed to marshal tool result for %s: %v",
log.Warnf("Failed to format tool result for %s: %v",
toolCall.Function.Name, err)
return nil, modifiedArgs, true,
fmt.Errorf("%s: %w", ErrorMarshalResult, err)
}

log.Debugf("CallableTool %s executed successfully, result: %s", toolCall.Function.Name, string(resultBytes))
log.Debugf("CallableTool %s executed successfully, result: %s %v", toolCall.Function.Name, msg)

return &model.Choice{
Index: index,
Message: model.Message{
Role: model.RoleTool,
Content: string(resultBytes),
ToolID: toolCall.ID,
},
Index: index,
Message: msg.(model.Message),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

如果业务实现 callback 不是规范的 model.Message,这里可能会 panic。

}, modifiedArgs, true, nil
}

Expand Down
7 changes: 7 additions & 0 deletions tool/callbacks.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,19 @@
// - error: if not nil, this error will be returned.
type AfterToolCallback func(ctx context.Context, toolName string, toolDeclaration *Declaration, jsonArgs []byte, result any, runErr error) (any, error)

type Message any // should be model.Message, avoid cycle import

Check warning on line 29 in tool/callbacks.go

View workflow job for this annotation

GitHub Actions / lint

exported: exported type Message should have comment or be unexported (revive)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个导出字段需要加一下注释 // Message ...


// ToolFormatMessage is called to format the tool result.
type ToolFormatMessage func(ctx context.Context, toolName string, toolDeclaration *Declaration, jsonArgs []byte, id string, result any) (Message, error)

// Callbacks holds callbacks for tool operations.
type Callbacks struct {
// BeforeTool is a list of callbacks that are called before the tool is executed.
BeforeTool []BeforeToolCallback
// AfterTool is a list of callbacks that are called after the tool is executed.
AfterTool []AfterToolCallback

ToolFormatMessage ToolFormatMessage
}

// NewCallbacks creates a new Callbacks instance for tool.
Expand Down
Loading