Repository: google/adk-go
File: agent/base_agent.go (Go)

Code before:

// WithName sets the agent name.
func WithName(name string) Option {
	return optionFunc(func(b *AgentBase) { b.name = name })
}

// WithDescription sets the agent description.
func WithDescription(desc string) Option {
	return optionFunc(func(b *AgentBase) { b.description = desc })
}

// AgentBase is the base agent implementation that offers
// shared, default agent behavior. All agents should extend this
// base agent and configure the [AgentBase.Self] from the constructor
// [NewAgentBase].

// For example,
//
//		type MyCustomAgent struct {
//		   *AgentBase
//		   ....
//		}
//
//		func NewMyCustomAgent(name string, opts ...agent.Option) *MyCustomAgent {
//		   a := &MyCustomAgent{AgentBase: agent.NewAgentBase(name, opts...)}
//		   a.Self = a  // This allows methods implemented in AgentBase can access *MyCustomAgent.
//	        ...
//		   return a
//		}
type AgentBase struct {
	name        string
	description string
	parentAgent adk.Agent
	subAgents   []adk.Agent

	Self adk.Agent
}

func NewAgentBase(name string, opts ...Option) *AgentBase {
	b := &AgentBase{name: name}
	for _, opt := range opts {
		opt.apply2Base(b)
	}
	return b
}

var _ adk.Agent = (*AgentBase)(nil)

func (a *AgentBase) Name() string           { return a.name }
func (a *AgentBase) Description() string    { return a.description }
func (a *AgentBase) Parent() adk.Agent      { return a.parentAgent }

Review comment (bug):
I thought about it a bit longer than I'd expected, but still didn't find a better solution than this one (keeping this reference in the base agent). 😄 

At least we can improve one thing now -- we can also hide `Self` and make it unexported?

```
NewBaseAgent(name string, implementation adk.Agent, opts ...Option)
if implementation == nil { return err } // here we can even validate it.
```

It's slightly better IMO:
1. We can validate the implementation is set by the user. And with current `a.Self = a` user may forget to set it.
2. By moving it to the constructor we actually make the user code shorter (e.g. when they set up subAgents):
```
a := &MyCustomAgent{}
a.BaseAgent = NewBaseAgent(name, a,
      WithSubAgents(a1, a2))
```

vs current
```
a := &MyCustomAgent{AgentBase: agent.NewAgentBase(name, opts...)}
a.Self = a // this MUST be set before AddSubAgents call.
a.AgentBase.AddSubAgents(a1, a2)
```

3. hide implementation detail `Self` field. For example if it's public/exported, user code may alter it at runtime (due to bug for example), leading to nasty bugs.

Code after:
// WithName sets the agent name.
func WithName(name string) Option {
	return optionFunc(func(b *BaseAgent) error {
		b.name = name
		return nil
	})
}

// WithDescription sets the agent description.
func WithDescription(desc string) Option {
	return optionFunc(func(b *BaseAgent) error {
		b.description = desc
		return nil
	})
}

func WithSubAgents(agents ...adk.Agent) Option {
	return optionFunc(func(b *BaseAgent) error {
		return b.AddSubAgents(agents...)
	})
}

// BaseAgent is the base agent implementation that offers
// shared, default agent behavior. All agents should extend this
// base agent by embedding.

// For example,
/*
	type MyCustomAgent struct {
	   *BaseAgent
	   ....
	}

	func NewMyCustomAgent(name string, opts ...agent.Option) (*MyCustomAgent, error) {
	   agent := &MyCustomAgent{}
	   base, err := agent.NewBaseAgent(name, agent, opts...)}
	   if err != nil { return nil, err }
	   agent.BaseAgent = base
	    ...
	   return agent, nil
	}
*/
type BaseAgent struct {
	name        string
	description string
	parentAgent adk.Agent
	subAgents   []adk.Agent

	self adk.Agent
}