Skip to content

Commit 57ed6c9

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

File tree

19 files changed

+2181
-134
lines changed

19 files changed

+2181
-134
lines changed

components/model/agenticopenai/content_block_extra.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ 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"
32+
toolCallNamespaceKey = "openai-tool-call-namespace"
3133
)
3234

3335
func setItemID(block *schema.ContentBlock, itemID string) {
@@ -43,6 +45,14 @@ func getItemID(block *schema.ContentBlock) (string, bool) {
4345
return itemIDStr, ok
4446
}
4547

48+
func setNamespace(block *schema.ContentBlock, namespace string) {
49+
setBlockExtraValue(block, toolCallNamespaceKey, namespace)
50+
}
51+
52+
func getNamespace(block *schema.ContentBlock) (string, bool) {
53+
return getBlockExtraValue[string](block, toolCallNamespaceKey)
54+
}
55+
4656
func setItemStatus(block *schema.ContentBlock, status string) {
4757
setBlockExtraValue(block, itemStatusKey, blockExtraItemStatus(status))
4858
}
@@ -56,6 +66,15 @@ func GetItemStatus(block *schema.ContentBlock) (string, bool) {
5666
return itemStatusStr, ok
5767
}
5868

69+
func setToolSearchToolCall(block *schema.ContentBlock) {
70+
setBlockExtraValue(block, isToolSearchToolCall, true)
71+
}
72+
73+
func GetToolSearchToolCall(block *schema.ContentBlock) bool {
74+
ok, success := getBlockExtraValue[bool](block, isToolSearchToolCall)
75+
return success && ok
76+
}
77+
5978
func setBlockExtraValue[T any](block *schema.ContentBlock, key string, value T) {
6079
if block == nil {
6180
return

components/model/agenticopenai/convertor.go

Lines changed: 165 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{
@@ -788,6 +815,21 @@ func functionToolCallToInputItem(block *schema.ContentBlock) (item responses.Res
788815

789816
id, _ := getItemID(block)
790817
status, _ := GetItemStatus(block)
818+
namespace, _ := getNamespace(block)
819+
820+
if GetToolSearchToolCall(block) {
821+
// client tool search call
822+
item = responses.ResponseInputItemUnionParam{
823+
OfToolSearchCall: &responses.ResponseInputItemToolSearchCallParam{
824+
Arguments: json.RawMessage(content.Arguments),
825+
ID: newOpenaiStrOpt(id),
826+
CallID: param.NewOpt(content.CallID),
827+
Status: status,
828+
Execution: "client",
829+
},
830+
}
831+
return item, nil
832+
}
791833

792834
item = responses.ResponseInputItemUnionParam{
793835
OfFunctionCall: &responses.ResponseFunctionToolCallParam{
@@ -796,6 +838,7 @@ func functionToolCallToInputItem(block *schema.ContentBlock) (item responses.Res
796838
CallID: content.CallID,
797839
Name: content.Name,
798840
Arguments: content.Arguments,
841+
Namespace: newOpenaiStrOpt(namespace),
799842
},
800843
}
801844

@@ -847,6 +890,20 @@ func serverToolCallToInputItem(block *schema.ContentBlock) (item responses.Respo
847890
return imageGenerationToolCallToInputItem(arguments.ImageGeneration, block)
848891
case arguments.Shell != nil:
849892
return shellToolCallToInputItem(arguments.Shell, block)
893+
case arguments.ToolSearch != nil:
894+
id, _ := getItemID(block)
895+
status, _ := GetItemStatus(block)
896+
var action *responses.ResponseInputItemToolSearchCallParam
897+
action, err = getToolSearchCallActionParam(arguments.ToolSearch)
898+
if err != nil {
899+
return item, err
900+
}
901+
action.CallID = newOpenaiStrOpt(content.CallID)
902+
action.ID = param.NewOpt(id)
903+
action.Status = status
904+
return responses.ResponseInputItemUnionParam{
905+
OfToolSearchCall: action,
906+
}, nil
850907
default:
851908
return item, fmt.Errorf("server tool call arguments are nil")
852909
}
@@ -1000,6 +1057,13 @@ func shellToolCallToInputItem(args *ShellArguments, block *schema.ContentBlock)
10001057
return item, nil
10011058
}
10021059

1060+
func getToolSearchCallActionParam(ts *ToolSearchCall) (*responses.ResponseInputItemToolSearchCallParam, error) {
1061+
return &responses.ResponseInputItemToolSearchCallParam{
1062+
Arguments: ts.Arguments,
1063+
Execution: "server",
1064+
}, nil
1065+
}
1066+
10031067
func serverToolResultToInputItem(block *schema.ContentBlock) (item responses.ResponseInputItemUnionParam, err error) {
10041068
content := block.ServerToolResult
10051069
if content == nil {
@@ -1022,6 +1086,21 @@ func serverToolResultToInputItem(block *schema.ContentBlock) (item responses.Res
10221086
return imageGenerationToolResultToInputItem(result.ImageGeneration, block)
10231087
case result.Shell != nil:
10241088
return shellToolResultToInputItem(result.Shell, block)
1089+
case result.ToolSearch != nil:
1090+
id, _ := getItemID(block)
1091+
status, _ := GetItemStatus(block)
1092+
var action *responses.ResponseToolSearchOutputItemParam
1093+
action, err = getToolSearchResultActionParam(result.ToolSearch)
1094+
if err != nil {
1095+
return item, err
1096+
}
1097+
action.CallID = newOpenaiStrOpt(content.CallID)
1098+
action.ID = param.NewOpt(id)
1099+
action.Status = responses.ResponseToolSearchOutputItemParamStatus(status)
1100+
1101+
return responses.ResponseInputItemUnionParam{
1102+
OfToolSearchOutput: action,
1103+
}, nil
10251104
default:
10261105
return item, fmt.Errorf("server tool result is nil")
10271106
}
@@ -1209,6 +1288,17 @@ func shellToolResultToInputItem(result *ShellResult, block *schema.ContentBlock)
12091288
return item, nil
12101289
}
12111290

1291+
func getToolSearchResultActionParam(ts *ToolSearchResult) (*responses.ResponseToolSearchOutputItemParam, error) {
1292+
tools, err := toDeferredFunctionTools(ts.Tools)
1293+
if err != nil {
1294+
return nil, err
1295+
}
1296+
return &responses.ResponseToolSearchOutputItemParam{
1297+
Tools: tools,
1298+
Execution: "server",
1299+
}, nil
1300+
}
1301+
12121302
func mcpToolApprovalRequestToInputItem(block *schema.ContentBlock) (item responses.ResponseInputItemUnionParam, err error) {
12131303
content := block.MCPToolApprovalRequest
12141304
if content == nil {
@@ -1323,7 +1413,7 @@ func mcpToolResultToInputItem(block *schema.ContentBlock) (item responses.Respon
13231413
return item, nil
13241414
}
13251415

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

13291419
for _, item := range resp.Output {
@@ -1411,6 +1501,19 @@ func toOutputMessage(resp *responses.Response) (msg *schema.AgenticMessage, err
14111501
return nil, fmt.Errorf("failed to convert function shell output to content block: %w", err)
14121502
}
14131503
tmpBlocks = []*schema.ContentBlock{block}
1504+
case responses.ResponseToolSearchCall:
1505+
block, err := toolSearchToolCallToContentBlock(variant, options)
1506+
if err != nil {
1507+
return nil, fmt.Errorf("failed to convert tool search call to content block: %w", err)
1508+
}
1509+
tmpBlocks = append(tmpBlocks, block)
1510+
1511+
case responses.ResponseToolSearchOutputItem:
1512+
block, err := toolSearchToolResultToContentBlock(variant)
1513+
if err != nil {
1514+
return nil, fmt.Errorf("failed to convert tool search output item to content block: %w", err)
1515+
}
1516+
tmpBlocks = append(tmpBlocks, block)
14141517

14151518
default:
14161519
return nil, fmt.Errorf("unknown output item type: %T", variant)
@@ -1547,6 +1650,9 @@ func functionToolCallToContentBlock(item responses.ResponseFunctionToolCall) (bl
15471650
if s := string(item.Status); s != "" {
15481651
setItemStatus(block, s)
15491652
}
1653+
if len(item.Namespace) > 0 {
1654+
setNamespace(block, item.Namespace)
1655+
}
15501656

15511657
return block, nil
15521658
}
@@ -1631,6 +1737,64 @@ func webSearchToContentBlocks(item responses.ResponseFunctionWebSearch) (blocks
16311737
return blocks, nil
16321738
}
16331739

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

0 commit comments

Comments
 (0)