Skip to content

Commit b95d9e9

Browse files
feat: claude&agentic openai tool search
1 parent 0aff612 commit b95d9e9

File tree

19 files changed

+2211
-131
lines changed

19 files changed

+2211
-131
lines changed

components/model/agenticopenai/content_block_extra.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@ type blockExtraItemID string
2626
type blockExtraItemStatus string
2727

2828
const (
29-
itemIDKey = "openai-item-id"
30-
itemStatusKey = "openai-item-status"
29+
itemIDKey = "openai-item-id"
30+
itemStatusKey = "openai-item-status"
31+
isToolSearchToolCall = "openai-tool-search-tool-call"
3132
)
3233

3334
func setItemID(block *schema.ContentBlock, itemID string) {
@@ -56,6 +57,15 @@ func GetItemStatus(block *schema.ContentBlock) (string, bool) {
5657
return itemStatusStr, ok
5758
}
5859

60+
func setToolSearchToolCall(block *schema.ContentBlock) {
61+
setBlockExtraValue(block, isToolSearchToolCall, true)
62+
}
63+
64+
func GetToolSearchToolCall(block *schema.ContentBlock) bool {
65+
ok, success := getBlockExtraValue[bool](block, isToolSearchToolCall)
66+
return success && ok
67+
}
68+
5969
func setBlockExtraValue[T any](block *schema.ContentBlock, key string, value T) {
6070
if block == nil {
6171
return

components/model/agenticopenai/convertor.go

Lines changed: 157 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@
1717
package agenticopenai
1818

1919
import (
20+
"encoding/json"
2021
"fmt"
2122
"strings"
2223
"sync"
2324

2425
"github.com/bytedance/sonic"
26+
"github.com/cloudwego/eino/components/model"
2527
"github.com/cloudwego/eino/schema"
2628
"github.com/cloudwego/eino/schema/openai"
2729
"github.com/eino-contrib/jsonschema"
@@ -554,6 +556,12 @@ func toUserRoleInputItems(msg *schema.AgenticMessage) (items []responses.Respons
554556
return nil, fmt.Errorf("failed to convert function tool result to input item: %w", err)
555557
}
556558

559+
case schema.ContentBlockTypeToolSearchResult:
560+
item, err = toolSearchToolResultToInputItem(block.ToolSearchFunctionToolResult)
561+
if err != nil {
562+
return nil, fmt.Errorf("failed to convert tool search function tool result to input item: %w", err)
563+
}
564+
557565
case schema.ContentBlockTypeMCPToolApprovalResponse:
558566
item, err = mcpToolApprovalResponseToInputItem(block.MCPToolApprovalResponse)
559567
if err != nil {
@@ -664,6 +672,25 @@ func userInputFileToInputItem(role responses.EasyInputMessageRole, block *schema
664672
return item, nil
665673
}
666674

675+
func toolSearchToolResultToInputItem(block *schema.ToolSearchFunctionToolResult) (item responses.ResponseInputItemUnionParam, err error) {
676+
if block.Result == nil {
677+
return item, fmt.Errorf("tool search result should not be nil")
678+
}
679+
tools, err := toDeferredFunctionTools(block.Result.Tools)
680+
if err != nil {
681+
return item, err
682+
}
683+
item = responses.ResponseInputItemUnionParam{
684+
OfToolSearchOutput: &responses.ResponseToolSearchOutputItemParam{
685+
Tools: tools,
686+
CallID: param.NewOpt(block.CallID),
687+
Status: responses.ResponseToolSearchOutputItemParamStatusCompleted,
688+
Execution: responses.ResponseToolSearchOutputItemParamExecutionClient,
689+
},
690+
}
691+
return item, nil
692+
}
693+
667694
func functionToolResultToInputItem(block *schema.FunctionToolResult) (item responses.ResponseInputItemUnionParam, err error) {
668695
item = responses.ResponseInputItemUnionParam{
669696
OfFunctionCallOutput: &responses.ResponseInputItemFunctionCallOutputParam{
@@ -789,6 +816,20 @@ func functionToolCallToInputItem(block *schema.ContentBlock) (item responses.Res
789816
id, _ := getItemID(block)
790817
status, _ := GetItemStatus(block)
791818

819+
if GetToolSearchToolCall(block) {
820+
// client tool search call
821+
item = responses.ResponseInputItemUnionParam{
822+
OfToolSearchCall: &responses.ResponseInputItemToolSearchCallParam{
823+
Arguments: json.RawMessage(content.Arguments),
824+
ID: newOpenaiStrOpt(id),
825+
CallID: param.NewOpt(content.CallID),
826+
Status: status,
827+
Execution: "client",
828+
},
829+
}
830+
return item, nil
831+
}
832+
792833
item = responses.ResponseInputItemUnionParam{
793834
OfFunctionCall: &responses.ResponseFunctionToolCallParam{
794835
ID: newOpenaiStrOpt(id),
@@ -847,6 +888,19 @@ func serverToolCallToInputItem(block *schema.ContentBlock) (item responses.Respo
847888
return imageGenerationToolCallToInputItem(arguments.ImageGeneration, block)
848889
case arguments.Shell != nil:
849890
return shellToolCallToInputItem(arguments.Shell, block)
891+
case arguments.ToolSearch != nil:
892+
var action *responses.ResponseInputItemToolSearchCallParam
893+
action, err = getToolSearchCallActionParam(arguments.ToolSearch)
894+
if err != nil {
895+
return item, err
896+
}
897+
action.CallID = param.NewOpt(content.CallID)
898+
action.ID = param.NewOpt(id)
899+
action.Status = status
900+
901+
item = responses.ResponseInputItemUnionParam{
902+
OfToolSearchCall: action,
903+
}
850904
default:
851905
return item, fmt.Errorf("server tool call arguments are nil")
852906
}
@@ -1000,6 +1054,13 @@ func shellToolCallToInputItem(args *ShellArguments, block *schema.ContentBlock)
10001054
return item, nil
10011055
}
10021056

1057+
func getToolSearchCallActionParam(ts *ToolSearchCall) (*responses.ResponseInputItemToolSearchCallParam, error) {
1058+
return &responses.ResponseInputItemToolSearchCallParam{
1059+
Arguments: ts.Arguments,
1060+
Execution: "server",
1061+
}, nil
1062+
}
1063+
10031064
func serverToolResultToInputItem(block *schema.ContentBlock) (item responses.ResponseInputItemUnionParam, err error) {
10041065
content := block.ServerToolResult
10051066
if content == nil {
@@ -1022,6 +1083,19 @@ func serverToolResultToInputItem(block *schema.ContentBlock) (item responses.Res
10221083
return imageGenerationToolResultToInputItem(result.ImageGeneration, block)
10231084
case result.Shell != nil:
10241085
return shellToolResultToInputItem(result.Shell, block)
1086+
case result.ToolSearch != nil:
1087+
var action *responses.ResponseToolSearchOutputItemParam
1088+
action, err = getToolSearchResultActionParam(result.ToolSearch)
1089+
if err != nil {
1090+
return item, err
1091+
}
1092+
action.CallID = param.NewOpt(content.CallID)
1093+
action.ID = param.NewOpt(id)
1094+
action.Status = responses.ResponseToolSearchOutputItemParamStatus(status)
1095+
1096+
item = responses.ResponseInputItemUnionParam{
1097+
OfToolSearchOutput: action,
1098+
}
10251099
default:
10261100
return item, fmt.Errorf("server tool result is nil")
10271101
}
@@ -1209,6 +1283,17 @@ func shellToolResultToInputItem(result *ShellResult, block *schema.ContentBlock)
12091283
return item, nil
12101284
}
12111285

1286+
func getToolSearchResultActionParam(ts *ToolSearchResult) (*responses.ResponseToolSearchOutputItemParam, error) {
1287+
tools, err := toDeferredFunctionTools(ts.Tools)
1288+
if err != nil {
1289+
return nil, err
1290+
}
1291+
return &responses.ResponseToolSearchOutputItemParam{
1292+
Tools: tools,
1293+
Execution: "server",
1294+
}, nil
1295+
}
1296+
12121297
func mcpToolApprovalRequestToInputItem(block *schema.ContentBlock) (item responses.ResponseInputItemUnionParam, err error) {
12131298
content := block.MCPToolApprovalRequest
12141299
if content == nil {
@@ -1323,7 +1408,7 @@ func mcpToolResultToInputItem(block *schema.ContentBlock) (item responses.Respon
13231408
return item, nil
13241409
}
13251410

1326-
func toOutputMessage(resp *responses.Response) (msg *schema.AgenticMessage, err error) {
1411+
func toOutputMessage(resp *responses.Response, options *model.Options) (msg *schema.AgenticMessage, err error) {
13271412
blocks := make([]*schema.ContentBlock, 0, len(resp.Output))
13281413

13291414
for _, item := range resp.Output {
@@ -1411,6 +1496,19 @@ func toOutputMessage(resp *responses.Response) (msg *schema.AgenticMessage, err
14111496
return nil, fmt.Errorf("failed to convert function shell output to content block: %w", err)
14121497
}
14131498
tmpBlocks = []*schema.ContentBlock{block}
1499+
case responses.ResponseToolSearchCall:
1500+
block, err := toolSearchToolCallToContentBlock(variant, options)
1501+
if err != nil {
1502+
return nil, fmt.Errorf("failed to convert tool search call to content block: %w", err)
1503+
}
1504+
tmpBlocks = append(tmpBlocks, block)
1505+
1506+
case responses.ResponseToolSearchOutputItem:
1507+
block, err := toolSearchToolResultToContentBlock(variant)
1508+
if err != nil {
1509+
return nil, fmt.Errorf("failed to convert tool search output item to content block: %w", err)
1510+
}
1511+
tmpBlocks = append(tmpBlocks, block)
14141512

14151513
default:
14161514
return nil, fmt.Errorf("unknown output item type: %T", variant)
@@ -1631,6 +1729,64 @@ func webSearchToContentBlocks(item responses.ResponseFunctionWebSearch) (blocks
16311729
return blocks, nil
16321730
}
16331731

1732+
func toolSearchToolCallToContentBlock(item responses.ResponseToolSearchCall, options *model.Options) (*schema.ContentBlock, error) {
1733+
args, err := json.Marshal(item.Arguments)
1734+
if err != nil {
1735+
return nil, fmt.Errorf("failed to marshal tool search arguments: %w", err)
1736+
}
1737+
1738+
switch item.Execution {
1739+
case responses.ResponseToolSearchCallExecutionClient:
1740+
if options.ToolSearchTool == nil {
1741+
return nil, fmt.Errorf("haven't set client tool search tool, but get a client tool search tool call")
1742+
}
1743+
block := schema.NewContentBlock(&schema.FunctionToolCall{
1744+
CallID: item.CallID,
1745+
Name: options.ToolSearchTool.Name,
1746+
Arguments: string(args),
1747+
})
1748+
setItemID(block, item.ID)
1749+
setItemStatus(block, string(item.Status))
1750+
setToolSearchToolCall(block)
1751+
return block, nil
1752+
1753+
case responses.ResponseToolSearchCallExecutionServer:
1754+
// server tool call
1755+
block := schema.NewContentBlock(&schema.ServerToolCall{
1756+
CallID: item.CallID,
1757+
Arguments: &ServerToolCallArguments{
1758+
ToolSearch: &ToolSearchCall{
1759+
Arguments: args,
1760+
},
1761+
},
1762+
})
1763+
setItemID(block, item.ID)
1764+
setItemStatus(block, string(item.Status))
1765+
return block, nil
1766+
1767+
default:
1768+
return nil, fmt.Errorf("invalid tool search execution type: %s", item.Execution)
1769+
}
1770+
}
1771+
1772+
func toolSearchToolResultToContentBlock(item responses.ResponseToolSearchOutputItem) (blocks *schema.ContentBlock, err error) {
1773+
tools, err := fromFunctionTools(item.Tools)
1774+
if err != nil {
1775+
return nil, err
1776+
}
1777+
block := schema.NewContentBlock(&schema.ServerToolResult{
1778+
CallID: item.CallID,
1779+
Result: &ServerToolResult{
1780+
ToolSearch: &ToolSearchResult{
1781+
Tools: tools,
1782+
},
1783+
},
1784+
})
1785+
setItemID(block, item.ID)
1786+
setItemStatus(block, string(item.Status))
1787+
return block, nil
1788+
}
1789+
16341790
func fileSearchToContentBlocks(item responses.ResponseFileSearchToolCall) (blocks []*schema.ContentBlock, err error) {
16351791
args := &ServerToolCallArguments{
16361792
FileSearch: &FileSearchArguments{

0 commit comments

Comments
 (0)