Add go-llm v2: redesigned API for simpler LLM abstraction
v2 is a new Go module (v2/) with a dramatically simpler API: - Unified Message type (no more Input marker interface) - Define[T] for ergonomic tool creation with standard context.Context - Chat session with automatic tool-call loop (agent loop) - Streaming via pull-based StreamReader - MCP one-call connect (MCPStdioServer, MCPHTTPServer, MCPSSEServer) - Middleware support (logging, retry, timeout, usage tracking) - Decoupled JSON Schema (map[string]any, no provider coupling) - Sample tools: WebSearch, Browser, Exec, ReadFile, WriteFile, HTTP - Providers: OpenAI, Anthropic, Google (all with streaming) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
181
v2/internal/schema/schema_test.go
Normal file
181
v2/internal/schema/schema_test.go
Normal file
@@ -0,0 +1,181 @@
|
||||
package schema
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type SimpleParams struct {
|
||||
Name string `json:"name" description:"The name"`
|
||||
Age int `json:"age" description:"The age"`
|
||||
}
|
||||
|
||||
type OptionalParams struct {
|
||||
Required string `json:"required" description:"A required field"`
|
||||
Optional *string `json:"optional,omitempty" description:"An optional field"`
|
||||
}
|
||||
|
||||
type EnumParams struct {
|
||||
Color string `json:"color" description:"The color" enum:"red,green,blue"`
|
||||
}
|
||||
|
||||
type NestedParams struct {
|
||||
Inner SimpleParams `json:"inner" description:"Nested object"`
|
||||
}
|
||||
|
||||
type ArrayParams struct {
|
||||
Items []string `json:"items" description:"A list of items"`
|
||||
}
|
||||
|
||||
type EmbeddedBase struct {
|
||||
ID string `json:"id" description:"The ID"`
|
||||
}
|
||||
|
||||
type EmbeddedParams struct {
|
||||
EmbeddedBase
|
||||
Name string `json:"name" description:"The name"`
|
||||
}
|
||||
|
||||
func TestFromStruct_Simple(t *testing.T) {
|
||||
s := FromStruct(SimpleParams{})
|
||||
|
||||
if s["type"] != "object" {
|
||||
t.Errorf("expected type=object, got %v", s["type"])
|
||||
}
|
||||
|
||||
props, ok := s["properties"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatal("expected properties to be map[string]any")
|
||||
}
|
||||
|
||||
if len(props) != 2 {
|
||||
t.Errorf("expected 2 properties, got %d", len(props))
|
||||
}
|
||||
|
||||
nameSchema, ok := props["name"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatal("expected name property to be map[string]any")
|
||||
}
|
||||
if nameSchema["type"] != "string" {
|
||||
t.Errorf("expected name type=string, got %v", nameSchema["type"])
|
||||
}
|
||||
if nameSchema["description"] != "The name" {
|
||||
t.Errorf("expected name description='The name', got %v", nameSchema["description"])
|
||||
}
|
||||
|
||||
ageSchema, ok := props["age"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatal("expected age property to be map[string]any")
|
||||
}
|
||||
if ageSchema["type"] != "integer" {
|
||||
t.Errorf("expected age type=integer, got %v", ageSchema["type"])
|
||||
}
|
||||
|
||||
required, ok := s["required"].([]string)
|
||||
if !ok {
|
||||
t.Fatal("expected required to be []string")
|
||||
}
|
||||
if len(required) != 2 {
|
||||
t.Errorf("expected 2 required fields, got %d", len(required))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromStruct_Optional(t *testing.T) {
|
||||
s := FromStruct(OptionalParams{})
|
||||
|
||||
required, ok := s["required"].([]string)
|
||||
if !ok {
|
||||
t.Fatal("expected required to be []string")
|
||||
}
|
||||
|
||||
// Only "required" field should be required, not "optional"
|
||||
if len(required) != 1 {
|
||||
t.Errorf("expected 1 required field, got %d: %v", len(required), required)
|
||||
}
|
||||
if required[0] != "required" {
|
||||
t.Errorf("expected required field 'required', got %v", required[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromStruct_Enum(t *testing.T) {
|
||||
s := FromStruct(EnumParams{})
|
||||
|
||||
props := s["properties"].(map[string]any)
|
||||
colorSchema := props["color"].(map[string]any)
|
||||
|
||||
if colorSchema["type"] != "string" {
|
||||
t.Errorf("expected enum type=string, got %v", colorSchema["type"])
|
||||
}
|
||||
|
||||
enums, ok := colorSchema["enum"].([]string)
|
||||
if !ok {
|
||||
t.Fatal("expected enum to be []string")
|
||||
}
|
||||
if len(enums) != 3 {
|
||||
t.Errorf("expected 3 enum values, got %d", len(enums))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromStruct_Nested(t *testing.T) {
|
||||
s := FromStruct(NestedParams{})
|
||||
|
||||
props := s["properties"].(map[string]any)
|
||||
innerSchema := props["inner"].(map[string]any)
|
||||
|
||||
if innerSchema["type"] != "object" {
|
||||
t.Errorf("expected nested type=object, got %v", innerSchema["type"])
|
||||
}
|
||||
|
||||
innerProps := innerSchema["properties"].(map[string]any)
|
||||
if len(innerProps) != 2 {
|
||||
t.Errorf("expected 2 inner properties, got %d", len(innerProps))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromStruct_Array(t *testing.T) {
|
||||
s := FromStruct(ArrayParams{})
|
||||
|
||||
props := s["properties"].(map[string]any)
|
||||
itemsSchema := props["items"].(map[string]any)
|
||||
|
||||
if itemsSchema["type"] != "array" {
|
||||
t.Errorf("expected array type=array, got %v", itemsSchema["type"])
|
||||
}
|
||||
|
||||
items := itemsSchema["items"].(map[string]any)
|
||||
if items["type"] != "string" {
|
||||
t.Errorf("expected items type=string, got %v", items["type"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromStruct_Embedded(t *testing.T) {
|
||||
s := FromStruct(EmbeddedParams{})
|
||||
|
||||
props := s["properties"].(map[string]any)
|
||||
|
||||
// Should have both ID from embedded and Name
|
||||
if len(props) != 2 {
|
||||
t.Errorf("expected 2 properties (flattened), got %d", len(props))
|
||||
}
|
||||
|
||||
if _, ok := props["id"]; !ok {
|
||||
t.Error("expected 'id' property from embedded struct")
|
||||
}
|
||||
if _, ok := props["name"]; !ok {
|
||||
t.Error("expected 'name' property")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromStruct_ValidJSON(t *testing.T) {
|
||||
s := FromStruct(SimpleParams{})
|
||||
|
||||
data, err := json.Marshal(s)
|
||||
if err != nil {
|
||||
t.Fatalf("schema should be valid JSON: %v", err)
|
||||
}
|
||||
|
||||
var parsed map[string]any
|
||||
if err := json.Unmarshal(data, &parsed); err != nil {
|
||||
t.Fatalf("schema should round-trip through JSON: %v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user