micro--go-micro
c4b4cbef25
Move tool discovery/execution fully into the ai package as ai.Tools (formerly ai.ToolSet), and simplify the usage model: - NewTools(reg, ai.ToolClient(c)) takes the execution client as an option instead of threading it through Handler(c) per call - New ai.WithTools(tools) option wires the tool handler into a model in one call, replacing ai.WithToolHandler(set.Handler(c)) - ai.DiscoverTools(reg) for one-shot discovery Before: set := ai.NewToolSet(reg) list, _ := set.Discover() m := ai.New(p, ai.WithToolHandler(set.Handler(client))) After: tools := ai.NewTools(reg, ai.ToolClient(client)) list, _ := tools.Discover() m := ai.New(p, ai.WithTools(tools)) Update ai/flow, micro chat, README, ai integration doc, Atlas Cloud guide, and blog posts 3/8/9/10. Co-authored-by: Claude <noreply@anthropic.com>
116 行
2.6 KiB
Go
116 行
2.6 KiB
Go
package ai
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"go-micro.dev/v5/registry"
|
|
)
|
|
|
|
func TestToolJSONType(t *testing.T) {
|
|
cases := map[string]string{
|
|
"string": "string",
|
|
"int": "integer",
|
|
"int64": "integer",
|
|
"float64": "number",
|
|
"bool": "boolean",
|
|
"User": "object",
|
|
"": "object",
|
|
}
|
|
for in, want := range cases {
|
|
if got := toolJSONType(in); got != want {
|
|
t.Errorf("toolJSONType(%q) = %q, want %q", in, got, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDiscoverTools_Empty(t *testing.T) {
|
|
reg := registry.NewMemoryRegistry()
|
|
tools, err := DiscoverTools(reg)
|
|
if err != nil {
|
|
t.Fatalf("DiscoverTools: %v", err)
|
|
}
|
|
if len(tools) != 0 {
|
|
t.Errorf("expected 0 tools, got %d", len(tools))
|
|
}
|
|
}
|
|
|
|
func TestDiscoverTools_DiscoversEndpoints(t *testing.T) {
|
|
reg := registry.NewMemoryRegistry()
|
|
svc := ®istry.Service{
|
|
Name: "users",
|
|
Version: "1.0.0",
|
|
Nodes: []*registry.Node{
|
|
{Id: "users-1", Address: "127.0.0.1:9000"},
|
|
},
|
|
Endpoints: []*registry.Endpoint{
|
|
{
|
|
Name: "Users.Get",
|
|
Metadata: map[string]string{
|
|
"description": "Fetch a user by ID",
|
|
},
|
|
Request: ®istry.Value{
|
|
Name: "GetRequest",
|
|
Type: "GetRequest",
|
|
Values: []*registry.Value{
|
|
{Name: "id", Type: "string"},
|
|
{Name: "expand", Type: "bool"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
if err := reg.Register(svc); err != nil {
|
|
t.Fatalf("Register: %v", err)
|
|
}
|
|
|
|
tools, err := DiscoverTools(reg)
|
|
if err != nil {
|
|
t.Fatalf("DiscoverTools: %v", err)
|
|
}
|
|
if len(tools) != 1 {
|
|
t.Fatalf("expected 1 tool, got %d", len(tools))
|
|
}
|
|
|
|
tool := tools[0]
|
|
if tool.Name != "users_Users_Get" {
|
|
t.Errorf("safe name = %q", tool.Name)
|
|
}
|
|
if tool.OriginalName != "users.Users.Get" {
|
|
t.Errorf("original = %q", tool.OriginalName)
|
|
}
|
|
if tool.Description != "Fetch a user by ID" {
|
|
t.Errorf("description = %q", tool.Description)
|
|
}
|
|
}
|
|
|
|
func TestTools_HandlerResolvesSafeName(t *testing.T) {
|
|
tools := NewTools(registry.NewMemoryRegistry())
|
|
tools.names.put("users_Users_Get", "users.Users.Get")
|
|
|
|
resolved, ok := tools.names.get("users_Users_Get")
|
|
if !ok || resolved != "users.Users.Get" {
|
|
t.Errorf("name map lookup = (%q, %v)", resolved, ok)
|
|
}
|
|
}
|
|
|
|
func TestTools_HandlerInvalidName(t *testing.T) {
|
|
tools := NewTools(registry.NewMemoryRegistry())
|
|
h := tools.Handler()
|
|
|
|
result, content := h("foo", map[string]any{})
|
|
if result == nil {
|
|
t.Fatal("expected error result")
|
|
}
|
|
if content == "" {
|
|
t.Error("expected non-empty content")
|
|
}
|
|
}
|
|
|
|
func TestWithTools(t *testing.T) {
|
|
tools := NewTools(registry.NewMemoryRegistry())
|
|
opts := NewOptions(WithTools(tools))
|
|
if opts.ToolHandler == nil {
|
|
t.Error("WithTools did not set a ToolHandler")
|
|
}
|
|
}
|