Skip to content

toolcompose

Composition layer for building filtered tool collections and declarative skills.

Packages

Package Purpose
set Toolset composition, filtering, and exposure
skill Declarative skill planning and execution

Installation

go get github.com/jonwraymond/toolcompose@latest

Quick Start: Toolset

import (
  "fmt"
  "log"

  "github.com/jonwraymond/toolcompose/set"
  "github.com/jonwraymond/toolfoundation/adapter"
)

tools := []*adapter.CanonicalTool{
  {
    Namespace: "github",
    Name:      "create_issue",
    Tags:      []string{"issues"},
    InputSchema: &adapter.JSONSchema{Type: "object"},
  },
  {
    Namespace: "github",
    Name:      "list_issues",
    Tags:      []string{"issues"},
    InputSchema: &adapter.JSONSchema{Type: "object"},
  },
}

ts, err := set.NewBuilder("github-issues").
  FromTools(tools).
  WithNamespace("github").
  WithTags([]string{"issues"}).
  WithPolicy(set.DenyTags("danger")).
  Build()
if err != nil {
  log.Fatal(err)
}

fmt.Println(ts.IDs())

Quick Start: Skill

import (
  "context"
  "log"

  "github.com/jonwraymond/toolcompose/skill"
  "github.com/jonwraymond/toolexec/run"
)

// Define a skill
sk := skill.Skill{
  Name: "triage-issue",
  Steps: []skill.Step{
    {ID: "create", ToolID: "github:create_issue", Inputs: map[string]any{"title": "Bug"}},
    {ID: "label", ToolID: "github:add_labels", Inputs: map[string]any{"labels": []string{"bug"}}},
  },
}

plan, err := skill.NewPlanner().Plan(sk)
if err != nil {
  log.Fatal(err)
}

runner := run.NewRunner()

// Adapt toolexec runner to skill.Runner
type runAdapter struct{ exec run.Runner }
func (r runAdapter) Run(ctx context.Context, step skill.Step) (any, error) {
  res, err := r.exec.Run(ctx, step.ToolID, step.Inputs)
  if err != nil {
    return nil, err
  }
  return res.Output, nil
}

ctx := context.Background()
results, err := skill.Execute(ctx, plan, runAdapter{exec: runner})
if err != nil {
  log.Fatal(err)
}
_ = results