Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
0584f28
feat: adk implementation
luohq-bytedance Jun 3, 2025
b1c83cb
fix: Runner run with new runctx
luohq-bytedance Jun 19, 2025
78b8546
feat: agent as tool (#293)
meguminnnnnnnnn Jun 20, 2025
a82fd16
feat: the input will not be passed when calling subagent because the …
meguminnnnnnnnn Jun 26, 2025
d41f1e5
feat: add Agent CallOption for adk (#312)
hi-pender Jul 2, 2025
a8d7cfd
feat: prebuilt/supervisor (#329)
luohq-bytedance Jul 11, 2025
8c8d7c7
fix: disallow to parrent for sub agents (#336)
hi-pender Jul 14, 2025
f890d8b
St/adk1 (#339)
shentongmartin Jul 17, 2025
dd77822
revert(adk): check tool_call until stream end (#346)
hi-pender Jul 18, 2025
61006e7
fix: only set automatic close on events added to session and emitted …
shentongmartin Jul 21, 2025
1e68920
Feat/wdz/adk interrupt (#311)
meguminnnnnnnnn Jul 22, 2025
b812e69
fix(adk): reserve stream event (#378)
meguminnnnnnnnn Aug 4, 2025
460a3a1
feat(adk): optimize pass event (#379)
meguminnnnnnnnn Aug 5, 2025
e93b9e3
fix(adk): use uuid as mock transfer tool call id (#388)
meguminnnnnnnnn Aug 7, 2025
08186c5
feat: adk -- implement plan_execute_replan (#386)
luohq-bytedance Aug 8, 2025
b9d01ea
feat: support skip transfer messages (#399)
meguminnnnnnnnn Aug 15, 2025
f05729f
feat(adk): history entry support user input (#404)
meguminnnnnnnnn Aug 19, 2025
d8e87d7
fix: resume chatmodel agent panic (#414)
meguminnnnnnnnn Aug 22, 2025
6db8425
fix: encode agent event wrapper & input message in history rewrite (#…
meguminnnnnnnnn Aug 26, 2025
d92a8f6
fix: history rewrite user input correctly (#419)
meguminnnnnnnnn Aug 27, 2025
19a3221
fix: gob will discord tool call index(*int), so concat message stream…
meguminnnnnnnnn Aug 27, 2025
469121d
fix: plan_execute: improve plan instruction (#427)
luohq-bytedance Aug 28, 2025
6b30855
refactor: change run path from string to RunStep (#423)
shentongmartin Aug 28, 2025
d0c3988
feat(adk): support WithSessionValues agent run option (#428)
meguminnnnnnnnn Aug 28, 2025
b9643dd
feat: enable history rewriter in root agent & report run ctx nil in r…
meguminnnnnnnnn Aug 28, 2025
1e8fd57
fix(adk_deterministic_transfer): support deterministic transfer inter…
meguminnnnnnnnn Sep 1, 2025
bebf0ff
feat(adk): add GenInputFn for planner/executor/replanner (#420)
hi-pender Sep 1, 2025
b830cae
Feat/workflow inherit (#439)
shentongmartin Sep 4, 2025
70f76d7
refactor: add sub directory for prebuilt agent and add comments (#443)
hi-pender Sep 9, 2025
525d4e3
feat: use MaxIterations instead of MaxSteps for ChatModelAgent (#447)
hi-pender Sep 10, 2025
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
1 change: 1 addition & 0 deletions _typos.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ invokable = "invokable"
InvokableLambda = "InvokableLambda"
InvokableRun = "InvokableRun"
typ = "typ"
byted = "byted"

[files]
extend-exclude = ["go.mod", "go.sum", "check_branch_name.sh"]
249 changes: 249 additions & 0 deletions adk/agent_tool.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
/*
* Copyright 2025 CloudWeGo Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package adk

import (
"context"
"errors"
"fmt"

"github.com/bytedance/sonic"

"github.com/cloudwego/eino/components/tool"
"github.com/cloudwego/eino/compose"
"github.com/cloudwego/eino/schema"
)

var (
defaultAgentToolParam = schema.NewParamsOneOfByParams(map[string]*schema.ParameterInfo{
"request": {
Desc: "request to be processed",
Required: true,
Type: schema.String,
},
})
)

type agentToolOptions struct {
agentName string
opts []AgentRunOption
}

func withAgentToolOptions(agentName string, opts []AgentRunOption) tool.Option {
return tool.WrapImplSpecificOptFn(func(opt *agentToolOptions) {
opt.agentName = agentName
opt.opts = opts
})
}

func getOptionsByAgentName(agentName string, opts []tool.Option) []AgentRunOption {
var ret []AgentRunOption
for _, opt := range opts {
o := tool.GetImplSpecificOptions[agentToolOptions](nil, opt)
if o != nil && o.agentName == agentName {
ret = append(ret, o.opts...)
}
}
return ret
}

type agentTool struct {
agent Agent

fullChatHistoryAsInput bool
}

func (at *agentTool) Info(ctx context.Context) (*schema.ToolInfo, error) {
var param *schema.ParamsOneOf
if !at.fullChatHistoryAsInput {
param = defaultAgentToolParam
}

return &schema.ToolInfo{
Name: at.agent.Name(ctx),
Desc: at.agent.Description(ctx),
ParamsOneOf: param,
}, nil
}

func (at *agentTool) InvokableRun(ctx context.Context, argumentsInJSON string, opts ...tool.Option) (string, error) {
var intData *agentToolInterruptInfo
var bResume bool
err := compose.ProcessState(ctx, func(ctx context.Context, s *State) error {
toolCallID := compose.GetToolCallID(ctx)
intData, bResume = s.AgentToolInterruptData[toolCallID]
if bResume {
delete(s.AgentToolInterruptData, toolCallID)
}
return nil
})
if err != nil {
// cannot resume
bResume = false
}

var ms *mockStore
var iter *AsyncIterator[*AgentEvent]
if bResume {
ms = newResumeStore(intData.Data)

iter, err = newInvokableAgentToolRunner(at.agent, ms).Resume(ctx, mockCheckPointID, getOptionsByAgentName(at.agent.Name(ctx), opts)...)
if err != nil {
return "", err
}
} else {
ms = newEmptyStore()
var input []Message
if at.fullChatHistoryAsInput {
history, err := getReactChatHistory(ctx, at.agent.Name(ctx))
if err != nil {
return "", err
}

input = history
} else {
type request struct {
Request string `json:"request"`
}

req := &request{}
err := sonic.UnmarshalString(argumentsInJSON, req)
if err != nil {
return "", err
}
input = []Message{
schema.UserMessage(req.Request),
}
}

iter = newInvokableAgentToolRunner(at.agent, ms).Run(ctx, input, append(getOptionsByAgentName(at.agent.Name(ctx), opts), WithCheckPointID(mockCheckPointID))...)
}

var lastEvent *AgentEvent
for {
event, ok := iter.Next()
if !ok {
break
}

if event.Err != nil {
return "", event.Err
}

lastEvent = event
}

if lastEvent != nil && lastEvent.Action != nil && lastEvent.Action.Interrupted != nil {
data, existed, err_ := ms.Get(ctx, mockCheckPointID)
if err_ != nil {
return "", fmt.Errorf("failed to get interrupt info: %w", err_)
}
if !existed {
return "", fmt.Errorf("interrupt has happened, but cannot find interrupt info")
}
err = compose.ProcessState(ctx, func(ctx context.Context, st *State) error {
st.AgentToolInterruptData[compose.GetToolCallID(ctx)] = &agentToolInterruptInfo{
LastEvent: lastEvent,
Data: data,
}
return nil
})
if err != nil {
return "", fmt.Errorf("failed to save agent tool checkpoint to state: %w", err)
}
return "", compose.InterruptAndRerun
}

if lastEvent == nil {
return "", errors.New("no event returned")
}

var ret string
if lastEvent.Output != nil {
if output := lastEvent.Output.MessageOutput; output != nil {
if !output.IsStreaming {
ret = output.Message.Content
} else {
msg, err := schema.ConcatMessageStream(output.MessageStream)
if err != nil {
return "", err
}
ret = msg.Content
}
}
}

return ret, nil
}

type AgentToolOptions struct {
fullChatHistoryAsInput bool
}

type AgentToolOption func(*AgentToolOptions)

func WithFullChatHistoryAsInput() AgentToolOption {
return func(options *AgentToolOptions) {
options.fullChatHistoryAsInput = true
}
}

func getReactChatHistory(ctx context.Context, destAgentName string) ([]Message, error) {
var messages []Message
var agentName string
err := compose.ProcessState(ctx, func(ctx context.Context, st *State) error {
messages = st.Messages
agentName = st.AgentName
return nil
})

messages = messages[:len(messages)-1] // remove the last assistant message, which is the tool call message
history := make([]Message, 0, len(messages))
history = append(history, messages...)
a, t := GenTransferMessages(ctx, destAgentName)
history = append(history, a, t)
for _, msg := range messages {
if msg.Role == schema.System {
continue
}

if msg.Role == schema.Assistant || msg.Role == schema.Tool {
msg = rewriteMessage(msg, agentName)
}

history = append(history, msg)
}

return history, err
}

func NewAgentTool(_ context.Context, agent Agent, options ...AgentToolOption) tool.BaseTool {
opts := &AgentToolOptions{}
for _, opt := range options {
opt(opts)
}

return &agentTool{agent: agent, fullChatHistoryAsInput: opts.fullChatHistoryAsInput}
}

func newInvokableAgentToolRunner(agent Agent, store compose.CheckPointStore) *Runner {
return &Runner{
a: agent,
enableStreaming: false,
store: store,
}
}
Loading
Loading