chore: move examples

Kujtim Hoxha created

Change summary

internal/ai/examples/agent/main.go                  |  46 +--
internal/ai/examples/simple/main.go                 |   0 
internal/ai/examples/stream/main.go                 |   0 
internal/ai/examples/streaming-agent-simple/main.go |  48 +--
internal/ai/examples/streaming-agent/main.go        | 169 +++++++-------
5 files changed, 114 insertions(+), 149 deletions(-)

Detailed changes

internal/ai/providers/examples/agent/main.go → internal/ai/examples/agent/main.go 🔗

@@ -7,49 +7,31 @@ import (
 
 	"github.com/charmbracelet/crush/internal/ai"
 	"github.com/charmbracelet/crush/internal/ai/providers"
-	"github.com/charmbracelet/crush/internal/llm/tools"
 )
 
-type weatherTool struct{}
-
-// Info implements tools.BaseTool.
-func (w *weatherTool) Info() tools.ToolInfo {
-	return tools.ToolInfo{
-		Name: "weather",
-		Parameters: map[string]any{
-			"location": map[string]string{
-				"type":        "string",
-				"description": "the city",
-			},
-		},
-		Required: []string{"location"},
-	}
-}
-
-// Name implements tools.BaseTool.
-func (w *weatherTool) Name() string {
-	return "weather"
-}
-
-// Run implements tools.BaseTool.
-func (w *weatherTool) Run(ctx context.Context, params tools.ToolCall) (tools.ToolResponse, error) {
-	return tools.NewTextResponse("40 C"), nil
-}
-
-func newWeatherTool() tools.BaseTool {
-	return &weatherTool{}
-}
-
 func main() {
 	provider := providers.NewOpenAIProvider(
 		providers.WithOpenAIApiKey(os.Getenv("OPENAI_API_KEY")),
 	)
 	model := provider.LanguageModel("gpt-4o")
 
+	// Create weather tool using the new type-safe API
+	type WeatherInput struct {
+		Location string `json:"location" description:"the city"`
+	}
+
+	weatherTool := ai.NewTypedToolFunc(
+		"weather",
+		"Get weather information for a location",
+		func(ctx context.Context, input WeatherInput, _ ai.ToolCall) (ai.ToolResponse, error) {
+			return ai.NewTextResponse("40 C"), nil
+		},
+	)
+
 	agent := ai.NewAgent(
 		model,
 		ai.WithSystemPrompt("You are a helpful assistant"),
-		ai.WithTools(newWeatherTool()),
+		ai.WithTools(weatherTool),
 	)
 
 	result, _ := agent.Generate(context.Background(), ai.AgentCall{

internal/ai/providers/examples/streaming-agent-simple/main.go → internal/ai/examples/streaming-agent-simple/main.go 🔗

@@ -7,30 +7,8 @@ import (
 
 	"github.com/charmbracelet/crush/internal/ai"
 	"github.com/charmbracelet/crush/internal/ai/providers"
-	"github.com/charmbracelet/crush/internal/llm/tools"
 )
 
-// Simple echo tool for demonstration
-type EchoTool struct{}
-
-func (e *EchoTool) Info() tools.ToolInfo {
-	return tools.ToolInfo{
-		Name:        "echo",
-		Description: "Echo back the provided message",
-		Parameters: map[string]any{
-			"message": map[string]any{
-				"type":        "string",
-				"description": "The message to echo back",
-			},
-		},
-		Required: []string{"message"},
-	}
-}
-
-func (e *EchoTool) Run(ctx context.Context, params tools.ToolCall) (tools.ToolResponse, error) {
-	return tools.NewTextResponse("Echo: " + params.Input), nil
-}
-
 func main() {
 	// Check for API key
 	apiKey := os.Getenv("OPENAI_API_KEY")
@@ -45,11 +23,24 @@ func main() {
 	)
 	model := provider.LanguageModel("gpt-4o-mini")
 
+	// Create echo tool using the new type-safe API
+	type EchoInput struct {
+		Message string `json:"message" description:"The message to echo back"`
+	}
+
+	echoTool := ai.NewTypedToolFunc(
+		"echo",
+		"Echo back the provided message",
+		func(ctx context.Context, input EchoInput, _ ai.ToolCall) (ai.ToolResponse, error) {
+			return ai.NewTextResponse("Echo: " + input.Message), nil
+		},
+	)
+
 	// Create streaming agent
 	agent := ai.NewAgent(
 		model,
 		ai.WithSystemPrompt("You are a helpful assistant."),
-		ai.WithTools(&EchoTool{}),
+		ai.WithTools(echoTool),
 	)
 
 	ctx := context.Background()
@@ -61,22 +52,22 @@ func main() {
 	// Basic streaming with key callbacks
 	streamCall := ai.AgentStreamCall{
 		Prompt: "Please echo back 'Hello, streaming world!'",
-		
+
 		// Show real-time text as it streams
 		OnTextDelta: func(id, text string) {
 			fmt.Print(text)
 		},
-		
+
 		// Show when tools are called
 		OnToolCall: func(toolCall ai.ToolCallContent) {
 			fmt.Printf("\n[Tool: %s called]\n", toolCall.ToolName)
 		},
-		
+
 		// Show tool results
 		OnToolResult: func(result ai.ToolResultContent) {
 			fmt.Printf("[Tool result received]\n")
 		},
-		
+
 		// Show when each step completes
 		OnStepFinish: func(step ai.StepResult) {
 			fmt.Printf("\n[Step completed: %s]\n", step.FinishReason)
@@ -92,4 +83,5 @@ func main() {
 
 	fmt.Printf("\n\nFinal result: %s\n", result.Response.Content.Text())
 	fmt.Printf("Steps: %d, Total tokens: %d\n", len(result.Steps), result.TotalUsage.TotalTokens)
-}
+}
+

internal/ai/providers/examples/streaming-agent/main.go → internal/ai/examples/streaming-agent/main.go 🔗

@@ -8,87 +8,8 @@ import (
 
 	"github.com/charmbracelet/crush/internal/ai"
 	"github.com/charmbracelet/crush/internal/ai/providers"
-	"github.com/charmbracelet/crush/internal/llm/tools"
 )
 
-// WeatherTool is a simple tool that simulates weather lookup
-type WeatherTool struct{}
-
-func (w *WeatherTool) Info() tools.ToolInfo {
-	return tools.ToolInfo{
-		Name:        "get_weather",
-		Description: "Get the current weather for a specific location",
-		Parameters: map[string]any{
-			"location": map[string]any{
-				"type":        "string",
-				"description": "The city and country, e.g. 'London, UK'",
-			},
-			"unit": map[string]any{
-				"type":        "string",
-				"description": "Temperature unit (celsius or fahrenheit)",
-				"enum":        []string{"celsius", "fahrenheit"},
-				"default":     "celsius",
-			},
-		},
-		Required: []string{"location"},
-	}
-}
-
-func (w *WeatherTool) Run(ctx context.Context, params tools.ToolCall) (tools.ToolResponse, error) {
-	// Simulate weather lookup with some fake data
-	location := "Unknown"
-	if strings.Contains(params.Input, "pristina") || strings.Contains(params.Input, "Pristina") {
-		location = "Pristina, Kosovo"
-	} else if strings.Contains(params.Input, "london") || strings.Contains(params.Input, "London") {
-		location = "London, UK"
-	} else if strings.Contains(params.Input, "new york") || strings.Contains(params.Input, "New York") {
-		location = "New York, USA"
-	}
-
-	unit := "celsius"
-	if strings.Contains(params.Input, "fahrenheit") {
-		unit = "fahrenheit"
-	}
-
-	var temp string
-	if unit == "fahrenheit" {
-		temp = "72°F"
-	} else {
-		temp = "22°C"
-	}
-
-	weather := fmt.Sprintf("The current weather in %s is %s with partly cloudy skies and light winds.", location, temp)
-	return tools.NewTextResponse(weather), nil
-}
-
-// CalculatorTool demonstrates a second tool for multi-tool scenarios
-type CalculatorTool struct{}
-
-func (c *CalculatorTool) Info() tools.ToolInfo {
-	return tools.ToolInfo{
-		Name:        "calculate",
-		Description: "Perform basic mathematical calculations",
-		Parameters: map[string]any{
-			"expression": map[string]any{
-				"type":        "string",
-				"description": "Mathematical expression to evaluate (e.g., '2 + 2', '10 * 5')",
-			},
-		},
-		Required: []string{"expression"},
-	}
-}
-
-func (c *CalculatorTool) Run(ctx context.Context, params tools.ToolCall) (tools.ToolResponse, error) {
-	// Simple calculator simulation
-	expr := strings.ReplaceAll(params.Input, "\"", "")
-	if strings.Contains(expr, "2 + 2") || strings.Contains(expr, "2+2") {
-		return tools.NewTextResponse("2 + 2 = 4"), nil
-	} else if strings.Contains(expr, "10 * 5") || strings.Contains(expr, "10*5") {
-		return tools.NewTextResponse("10 * 5 = 50"), nil
-	}
-	return tools.NewTextResponse("I can calculate simple expressions like '2 + 2' or '10 * 5'"), nil
-}
-
 func main() {
 	// Check for API key
 	apiKey := os.Getenv("OPENAI_API_KEY")
@@ -108,11 +29,80 @@ func main() {
 	)
 	model := provider.LanguageModel("gpt-4o-mini") // Using mini for faster/cheaper responses
 
+	// Define input types for type-safe tools
+	type WeatherInput struct {
+		Location string `json:"location" description:"The city and country, e.g. 'London, UK'"`
+		Unit     string `json:"unit,omitempty" enum:"celsius,fahrenheit" description:"Temperature unit (celsius or fahrenheit)"`
+	}
+
+	type CalculatorInput struct {
+		Expression string `json:"expression" description:"Mathematical expression to evaluate (e.g., '2 + 2', '10 * 5')"`
+	}
+
+	// Create weather tool using the new type-safe API
+	weatherTool := ai.NewTypedToolFunc(
+		"get_weather",
+		"Get the current weather for a specific location",
+		func(ctx context.Context, input WeatherInput, _ ai.ToolCall) (ai.ToolResponse, error) {
+			// Simulate weather lookup with some fake data
+			location := input.Location
+			if location == "" {
+				location = "Unknown"
+			}
+
+			// Default to celsius if not specified
+			unit := input.Unit
+			if unit == "" {
+				unit = "celsius"
+			}
+
+			// Simulate different temperatures for different cities
+			var temp string
+			if strings.Contains(strings.ToLower(location), "pristina") {
+				temp = "15°C"
+				if unit == "fahrenheit" {
+					temp = "59°F"
+				}
+			} else if strings.Contains(strings.ToLower(location), "london") {
+				temp = "12°C"
+				if unit == "fahrenheit" {
+					temp = "54°F"
+				}
+			} else {
+				temp = "22°C"
+				if unit == "fahrenheit" {
+					temp = "72°F"
+				}
+			}
+
+			weather := fmt.Sprintf("The current weather in %s is %s with partly cloudy skies and light winds.", location, temp)
+			return ai.NewTextResponse(weather), nil
+		},
+	)
+
+	// Create calculator tool using the new type-safe API
+	calculatorTool := ai.NewTypedToolFunc(
+		"calculate",
+		"Perform basic mathematical calculations",
+		func(ctx context.Context, input CalculatorInput, _ ai.ToolCall) (ai.ToolResponse, error) {
+			// Simple calculator simulation
+			expr := strings.TrimSpace(input.Expression)
+			if strings.Contains(expr, "2 + 2") || strings.Contains(expr, "2+2") {
+				return ai.NewTextResponse("2 + 2 = 4"), nil
+			} else if strings.Contains(expr, "10 * 5") || strings.Contains(expr, "10*5") {
+				return ai.NewTextResponse("10 * 5 = 50"), nil
+			} else if strings.Contains(expr, "15 + 27") || strings.Contains(expr, "15+27") {
+				return ai.NewTextResponse("15 + 27 = 42"), nil
+			}
+			return ai.NewTextResponse("I can calculate simple expressions like '2 + 2', '10 * 5', or '15 + 27'"), nil
+		},
+	)
+
 	// Create agent with tools
 	agent := ai.NewAgent(
 		model,
 		ai.WithSystemPrompt("You are a helpful assistant that can check weather and do calculations. Be concise and friendly."),
-		ai.WithTools(&WeatherTool{}, &CalculatorTool{}),
+		ai.WithTools(weatherTool, calculatorTool),
 	)
 
 	ctx := context.Background()
@@ -224,11 +214,11 @@ func main() {
 	fmt.Println("📋 Final Summary")
 	fmt.Println("================")
 	fmt.Printf("Steps executed: %d\n", len(result.Steps))
-	fmt.Printf("Total tokens used: %d (input: %d, output: %d)\n", 
-		result.TotalUsage.TotalTokens, 
-		result.TotalUsage.InputTokens, 
+	fmt.Printf("Total tokens used: %d (input: %d, output: %d)\n",
+		result.TotalUsage.TotalTokens,
+		result.TotalUsage.InputTokens,
 		result.TotalUsage.OutputTokens)
-	
+
 	if result.TotalUsage.ReasoningTokens > 0 {
 		fmt.Printf("Reasoning tokens: %d\n", result.TotalUsage.ReasoningTokens)
 	}
@@ -243,13 +233,13 @@ func main() {
 		fmt.Printf("Step %d:\n", i+1)
 		fmt.Printf("  Finish reason: %s\n", step.FinishReason)
 		fmt.Printf("  Content types: ")
-		
+
 		var contentTypes []string
 		for _, content := range step.Content {
 			contentTypes = append(contentTypes, string(content.GetType()))
 		}
 		fmt.Printf("%s\n", strings.Join(contentTypes, ", "))
-		
+
 		// Show tool calls and results
 		toolCalls := step.Content.ToolCalls()
 		if len(toolCalls) > 0 {
@@ -260,15 +250,16 @@ func main() {
 			}
 			fmt.Printf("%s\n", strings.Join(toolNames, ", "))
 		}
-		
+
 		toolResults := step.Content.ToolResults()
 		if len(toolResults) > 0 {
 			fmt.Printf("  Tool results: %d\n", len(toolResults))
 		}
-		
+
 		fmt.Printf("  Tokens: %d\n", step.Usage.TotalTokens)
 		fmt.Println()
 	}
 
 	fmt.Println("✨ Example completed successfully!")
-}
+}
+