// Package tools is executus's library of generic, host-agnostic agent tools. // // A host registers the tools it wants against a tool.Registry, then runs an // agent whose RunnableAgent.LowLevelTools name them. Tools split two ways: // // - Always-available, zero-configuration tools register via Register (think, // now, cite) — all nil-safe, so a light host (gadfly) calls Register and is // immediately useful. // - Backed tools take a nil-safe Deps describing their host backend and // register via grouped registrars (RegisterMeta, and RegisterWeb/Store/… // as they land). Each Deps ships sensible defaults so "some setup" is small. // // A host adds its own domain tools against the SAME registry. package tools import ( "context" "errors" "gitea.stevedudenhoeffer.com/steve/executus/llmmeta" "gitea.stevedudenhoeffer.com/steve/executus/tool" ) // Register adds the always-available, zero-configuration generic tools: // // - think — record a thought to the run trace (no external effect) // - now — current time (UTC unless a CurrentTimeProvider is wired) // - cite — record a source citation (inert unless a CitationStorage is wired) // // All are nil-safe. Returns the first registration error. func Register(reg tool.Registry) error { return registerAll(reg, NewThink(), NewNow(nil), NewCite(nil), ) } // MetaDeps wires the LLM-backed meta tools (classify, extract_entities, // summarize). Helper is required. Budget defaults to an in-memory per-run cap; // Files is optional (summarize's file_id input is inert without it); MaxPerRun // and MaxWords default when non-positive. type MetaDeps struct { Helper *llmmeta.Helper Budget SearchBudget Files FileStorage MaxPerRun int // per-run cap for each meta tool; default 10 MaxWords int // summarize length cap; default 200 } // RegisterMeta adds classify, extract_entities, and summarize. It requires a // configured llmmeta.Helper (the fast-tier meta-LLM caller); everything else // defaults. func RegisterMeta(reg tool.Registry, d MetaDeps) error { if d.Helper == nil { return errors.New("tools: MetaDeps.Helper is required for the meta tools") } if d.MaxPerRun <= 0 { d.MaxPerRun = 10 } if d.MaxWords <= 0 { d.MaxWords = 200 } if d.Budget == nil { // Build the default budget WITH the configured per-run cap so // MetaDeps.MaxPerRun is honored — an empty caps map would fall back to // the budget's hardcoded default and silently ignore MaxPerRun. d.Budget = NewInMemorySearchBudget(map[string]int{ "classify": d.MaxPerRun, "extract_entities": d.MaxPerRun, "summarize": d.MaxPerRun, }) } cfg := fixedMetaConfig{maxPerRun: d.MaxPerRun, maxWords: d.MaxWords} return registerAll(reg, NewClassify(d.Helper, cfg, d.Budget), NewExtractEntities(d.Helper, cfg, d.Budget), NewSummarize(d.Helper, cfg, d.Budget, d.Files), ) } func registerAll(reg tool.Registry, ts ...tool.Tool) error { for _, t := range ts { if err := reg.Register(t); err != nil { return err } } return nil } // fixedMetaConfig satisfies ClassifyConfig / ExtractEntitiesConfig / // SummarizeConfig with static caps read from MetaDeps. type fixedMetaConfig struct{ maxPerRun, maxWords int } func (c fixedMetaConfig) MaxPerRun(context.Context) int { return c.maxPerRun } func (c fixedMetaConfig) MaxWords(context.Context) int { return c.maxWords }