refactor(tool): adopt gollem type-safe NewTool API and bump gollem/tools deps#226
Conversation
There was a problem hiding this comment.
Code Review
This pull request refactors the tool and agent implementations across BigQuery, Falcon, Slack, Warren, and Knowledge packages to utilize a new type-safe toolset utility (pkg/utils/toolset). This change replaces manual map parsing and hand-written tool specifications with structured input types parsed by gollem. The review feedback highlights several opportunities for defensive programming, recommending nil checks on configurations, captured context variables, and toolset references to prevent potential runtime panics.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| func newInternalTool(config *Config, projectID, impersonateServiceAccount string) *internalTool { | ||
| t := &internalTool{ | ||
| config: config, | ||
| projectID: projectID, | ||
| impersonateServiceAccount: impersonateServiceAccount, | ||
| } |
There was a problem hiding this comment.
Defensive programming: config is a pointer and is dereferenced later in the function (e.g., config.Tables and config.Runbooks). If config is nil, this will cause a runtime panic. Consider adding a nil check at the beginning of newInternalTool to prevent this.
func newInternalTool(config *Config, projectID, impersonateServiceAccount string) *internalTool {
if config == nil {
panic("config is required")
}
t := &internalTool{
config: config,
projectID: projectID,
impersonateServiceAccount: impersonateServiceAccount,
}| func(ctx context.Context, in slackPostMessageInput) (map[string]any, error) { | ||
| if in.Message == "" { | ||
| return map[string]any{"error": "message parameter is required and must be a non-empty string"}, nil | ||
| } |
There was a problem hiding this comment.
Defensive programming: The tool handler closure captures uc and t from the outer scope. If either uc or t is nil, dereferencing them (e.g., uc.slackService or t.SlackThread) will cause a runtime panic. Consider adding a nil check at the beginning of the handler function.
| func(ctx context.Context, in slackPostMessageInput) (map[string]any, error) { | |
| if in.Message == "" { | |
| return map[string]any{"error": "message parameter is required and must be a non-empty string"}, nil | |
| } | |
| func(ctx context.Context, in slackPostMessageInput) (map[string]any, error) { | |
| if uc == nil || t == nil { | |
| return nil, goerr.New("invalid usecase or ticket reference") | |
| } | |
| if in.Message == "" { | |
| return map[string]any{"error": "message parameter is required and must be a non-empty string"}, nil | |
| } |
| for _, t := range tools { | ||
| name := t.Spec().Name |
There was a problem hiding this comment.
Defensive programming: If any of the tools passed to New is nil, calling t.Spec() will cause a nil pointer dereference panic. Consider adding a nil check to handle this programming error gracefully.
| for _, t := range tools { | |
| name := t.Spec().Name | |
| for _, t := range tools { | |
| if t == nil { | |
| panic(goerr.New("nil tool provided to toolset", goerr.T(errutil.TagInvalidState))) | |
| } | |
| name := t.Spec().Name |
| func (s *Set) Specs(_ context.Context) ([]gollem.ToolSpec, error) { | ||
| specs := make([]gollem.ToolSpec, 0, len(s.order)) |
There was a problem hiding this comment.
Defensive programming: If the Set pointer s or its underlying tools map is nil, calling Specs will cause a runtime panic. Consider adding a nil check at the beginning of Specs.
| func (s *Set) Specs(_ context.Context) ([]gollem.ToolSpec, error) { | |
| specs := make([]gollem.ToolSpec, 0, len(s.order)) | |
| func (s *Set) Specs(_ context.Context) ([]gollem.ToolSpec, error) { | |
| if s == nil || s.tools == nil { | |
| return nil, nil | |
| } | |
| specs := make([]gollem.ToolSpec, 0, len(s.order)) |
| func (s *Set) Run(ctx context.Context, name string, args map[string]any) (map[string]any, error) { | ||
| t, ok := s.tools[name] |
There was a problem hiding this comment.
Defensive programming: If the Set pointer s or its underlying tools map is nil, calling Run will cause a runtime panic. Consider adding a nil check at the beginning of Run.
| func (s *Set) Run(ctx context.Context, name string, args map[string]any) (map[string]any, error) { | |
| t, ok := s.tools[name] | |
| func (s *Set) Run(ctx context.Context, name string, args map[string]any) (map[string]any, error) { | |
| if s == nil || s.tools == nil { | |
| return nil, goerr.New("toolset is not initialized", goerr.T(errutil.TagInvalidState)) | |
| } | |
| t, ok := s.tools[name] |
Summary
Adopts the type-safe tool-construction API that
github.com/gollem-dev/gollem(main) andgithub.com/gollem-dev/tools/*(v0.2.0 / v0.3.0) now provide, and applies it across warren's own hand-written tools.Dependency bump
gollem→ main HEAD (purely additive:typed_tool.go+ErrInvalidToolType, no breaking changes)tools12 modules →v0.3.0for github / slack / webfetch,v0.2.0for the restpkg/tool/<name>/action.goneed no code change — every constructor /Option/With*signature is unchanged; the bumps are internal-only.Type-safe migration of warren's own tools (7 files / ~23 tools)
pkg/utils/toolsetconverts[]gollem.Tool(built viagollem.NewTool[In,Out]) into thegollem.ToolSet(Specs/Run) contract thatinterfaces.ToolSetrequires.refine(1),tool/base(6),agents/falcon(10),agents/slack(3),agents/bigquery(2–3, runbook conditional),tool/bigqueryconfig generator (2),tool/knowledge(up to 11, mode-dependent).getArg/args[...].(T)/buildQueryParamsboilerplate and manualfloat64→int64special-casing; schemas are now inferred from typed input structs.webfetch SSRF
pkg/tool/webfetch/README.mdupdated to reflect this.Design notes
interfaces.ToolSet(Specs/Run + ID/Description/Prompt) shape is preserved because the planner groups tools by ToolSet; only each set's internals are made type-safe.float64so the inferred JSON schema remainsnumber, matching the prior wire-level type.generate_config's(map, ErrExitConversation)path toNewTooldoes not change observable behavior.Test
go test ./...— all packages pass (github/slack wrapper spec-count assertions updated for the new toolsgithub_get_issue/github_get_pull_request/slack_get_messages).go vet ./...,gofmt -l,golangci-lint run(0 issues),gosec— all clean.pkg/utils/toolset(ordering, dispatch, unknown-tool error, duplicate-name panic, int64 decode).