// Copyright (c) 2026 Probo Inc . // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. package vetting import ( "fmt" "go.probo.inc/probo/pkg/agent" "go.probo.inc/probo/pkg/llm" ) // subAgentSpec describes a vetting sub-agent. The generic builder // `newSubAgent[T]` reads it once and constructs the agent. This avoids // duplicating the same option boilerplate across 16 constructor functions. type subAgentSpec struct { name string outputName string prompt string maxTurns int thinkingBudget int // 0 disables extended thinking parallelTools bool // true enables parallel tool calls } // subAgentBuilder constructs a sub-agent from a client, model, tools, and // extra options. The structured output type is captured by the closure // returned from buildFor[T]. type subAgentBuilder func(client *llm.Client, model string, tools []agent.Tool, extraOpts ...agent.Option) (*agent.Agent, error) // buildFor returns a subAgentBuilder bound to a structured output type T // and a spec. This lets the orchestrator hold a slice of entries whose // build closures only differ in their type parameter. func buildFor[T any](spec subAgentSpec) subAgentBuilder { return func(client *llm.Client, model string, tools []agent.Tool, extraOpts ...agent.Option) (*agent.Agent, error) { return newSubAgent[T](client, model, spec, tools, extraOpts...) } } // newSubAgent builds a vetting sub-agent from its spec, the tools it // should use, and any caller-supplied extra options (logger, hooks). // The type parameter T is the structured output type the agent must // produce. func newSubAgent[T any]( client *llm.Client, model string, spec subAgentSpec, tools []agent.Tool, extraOpts ...agent.Option, ) (*agent.Agent, error) { outputType, err := newVettingOutputType[T](spec.outputName) if err != nil { return nil, fmt.Errorf("cannot create output type %q: %w", spec.outputName, err) } opts := []agent.Option{ agent.WithInstructions(spec.prompt), agent.WithModel(model), agent.WithTools(tools...), agent.WithMaxTurns(spec.maxTurns), agent.WithOutputType(outputType), } if spec.thinkingBudget > 0 { opts = append(opts, agent.WithThinking(spec.thinkingBudget)) } if spec.parallelTools { opts = append(opts, agent.WithParallelToolCalls(true)) } opts = append(opts, extraOpts...) return agent.New(spec.name, client, opts...), nil }