add: more commands

This commit is contained in:
2026-02-17 09:01:41 -08:00
parent 424abfbb9e
commit ade140bb87
7 changed files with 332 additions and 44 deletions

View File

@@ -0,0 +1,101 @@
package give
import (
"context"
"fmt"
"strings"
"github.com/ollama/ollama/api"
"tipsy.codes/charles/mc-god/v2/internal/pkg/rcon"
)
type Give struct{}
func Get() *Give {
return &Give{}
}
func (g *Give) Do(ctx context.Context, toolCall api.ToolCall, client *rcon.Client) error {
// Extract the arguments from the tool call
args := toolCall.Function.Arguments
player, found := args.Get("player")
if !found {
return fmt.Errorf("missing player argument")
}
playerString, ok := player.(string)
if !ok {
return fmt.Errorf("incorrect data type %v; want string", player)
}
item, found := args.Get("item")
if !found {
return fmt.Errorf("missing item argument")
}
itemString, ok := item.(string)
if !ok {
return fmt.Errorf("incorrect data type %T; want int", item)
}
// Handle count (optional, default to 1)
count := 1
countVal, found := args.Get("count")
if found {
if countFloat, ok := countVal.(float64); ok {
count = int(countFloat)
} else {
return fmt.Errorf("incorrect data type %T; want number", countVal)
}
}
// Validate that we have valid player and item names (basic validation)
if strings.TrimSpace(playerString) == "" {
return fmt.Errorf("player and item names cannot be empty")
}
// Send the give command to the Minecraft server
command := "/give " + playerString + " " + itemString + " " + fmt.Sprintf("%d", count)
_, err := client.Execute(command)
if err != nil {
return fmt.Errorf("failed to execute give command: %w", err)
}
return nil
}
func (g *Give) Desc() api.Tool {
toolPropertiesMap := api.NewToolPropertiesMap()
toolPropertiesMap.Set("player", api.ToolProperty{
Type: api.PropertyType{"string"},
Description: "The player to give the item to",
})
toolPropertiesMap.Set("item", api.ToolProperty{
Type: api.PropertyType{"string"},
Description: `The item to give. Items can include:
- dirt
- carrot
- reeds
`,
})
toolPropertiesMap.Set("count", api.ToolProperty{
Type: api.PropertyType{"integer"},
Description: "The number of items to give (default is 1)",
})
return api.Tool{
Type: "function",
Function: api.ToolFunction{
Name: g.Name(),
Description: "Give items to a player in Minecraft",
Parameters: api.ToolFunctionParameters{
Type: "object",
Properties: toolPropertiesMap,
Required: []string{"player", "item"},
},
},
}
}
func (g *Give) Name() string {
return "give"
}

View File

@@ -0,0 +1,75 @@
package say
import (
"context"
"fmt"
"github.com/ollama/ollama/api"
"tipsy.codes/charles/mc-god/v2/internal/pkg/rcon"
)
type Say struct{}
func Get() *Say {
return &Say{}
}
func (s *Say) Do(ctx context.Context, toolCall api.ToolCall, client *rcon.Client) error {
// Extract the message from the tool call
message, found := toolCall.Function.Arguments.Get("message")
if !found {
return fmt.Errorf("missing message argument")
}
messageString, ok := message.(string)
if !ok {
return fmt.Errorf("incorrect data type %v; want string", message)
}
player, found := toolCall.Function.Arguments.Get("player")
if !found {
return fmt.Errorf("missing player argument")
}
playerString, ok := player.(string)
if !ok {
return fmt.Errorf("incorrect data type %v; want string", player)
}
// Send the say command to the Minecraft server
command := fmt.Sprintf("/execute as %q run say %s", playerString, messageString)
_, err := client.Execute(command)
if err != nil {
return fmt.Errorf("failed to execute say command: %w", err)
}
return nil
}
func (s *Say) Desc() api.Tool {
toolPropertiesMap := api.NewToolPropertiesMap()
toolPropertiesMap.Set("message", api.ToolProperty{
Type: api.PropertyType{"string"},
Description: "The message to send to the server chat",
})
toolPropertiesMap.Set("player", api.ToolProperty{
Type: api.PropertyType{"string"},
Description: "The player to speak as",
})
return api.Tool{
Type: "function",
Function: api.ToolFunction{
Name: s.Name(),
Description: "Speak as a player, sending a message to the server chat as that player",
Parameters: api.ToolFunctionParameters{
Type: "object",
Properties: toolPropertiesMap,
Required: []string{"player", "message"},
},
},
}
}
func (s *Say) Name() string {
return "say"
}

View File

@@ -0,0 +1,82 @@
package teleport
import (
"context"
"fmt"
"strings"
"github.com/ollama/ollama/api"
"tipsy.codes/charles/mc-god/v2/internal/pkg/rcon"
)
type Teleport struct{}
func Get() *Teleport {
return &Teleport{}
}
func (t *Teleport) Do(ctx context.Context, toolCall api.ToolCall, client *rcon.Client) error {
// Extract the arguments from the tool call
args := toolCall.Function.Arguments
source, found := args.Get("source")
if !found {
return fmt.Errorf("missing source argument")
}
sourceString, ok := source.(string)
if !ok {
return fmt.Errorf("incorrect data type %v; want string", source)
}
target, found := args.Get("target")
if !found {
return fmt.Errorf("missing target argument")
}
targetString, ok := target.(string)
if !ok {
return fmt.Errorf("incorrect data type %v; want string", target)
}
// Validate that we have valid player names (basic validation)
if strings.TrimSpace(sourceString) == "" || strings.TrimSpace(targetString) == "" {
return fmt.Errorf("source and target player names cannot be empty")
}
// Send the teleport command to the Minecraft server
command := "/tp " + sourceString + " " + targetString
_, err := client.Execute(command)
if err != nil {
return fmt.Errorf("failed to execute teleport command: %w", err)
}
return nil
}
func (t *Teleport) Desc() api.Tool {
toolPropertiesMap := api.NewToolPropertiesMap()
toolPropertiesMap.Set("source", api.ToolProperty{
Type: api.PropertyType{"string"},
Description: "The player who will be teleported",
})
toolPropertiesMap.Set("target", api.ToolProperty{
Type: api.PropertyType{"string"},
Description: "The player to teleport to",
})
return api.Tool{
Type: "function",
Function: api.ToolFunction{
Name: t.Name(),
Description: "Teleport one player to another player in Minecraft",
Parameters: api.ToolFunctionParameters{
Type: "object",
Properties: toolPropertiesMap,
Required: []string{"source", "target"},
},
},
}
}
func (t *Teleport) Name() string {
return "teleport"
}

View File

@@ -4,9 +4,16 @@ package tools
import (
"context"
"fmt"
"log/slog"
"github.com/ollama/ollama/api"
"tipsy.codes/charles/mc-god/v2/internal/pkg/rcon"
"tipsy.codes/charles/mc-god/v2/internal/pkg/tools/give"
"tipsy.codes/charles/mc-god/v2/internal/pkg/tools/say"
"tipsy.codes/charles/mc-god/v2/internal/pkg/tools/teleport"
"tipsy.codes/charles/mc-god/v2/internal/pkg/tools/time"
"tipsy.codes/charles/mc-god/v2/internal/pkg/tools/weather"
"tipsy.codes/charles/mc-god/v2/internal/pkg/tools/zombie"
)
type Tool interface {
@@ -25,6 +32,18 @@ func New(tools ...Tool) Tools {
return ret
}
// DefaultTools returns the default set of tools for mc-god
func DefaultTools() Tools {
return New(
say.Get(),
teleport.Get(),
give.Get(),
weather.Get(),
time.Get(),
zombie.Get(),
)
}
func (t Tools) AsAPI() api.Tools {
var ret api.Tools
for _, tool := range t {
@@ -38,5 +57,10 @@ func (t Tools) Do(ctx context.Context, toolCall api.ToolCall, client *rcon.Clien
if !found {
return fmt.Errorf("unknown tool %q", toolCall.Function.Name)
}
args, err := toolCall.Function.Arguments.MarshalJSON()
if err != nil {
return fmt.Errorf("failed to marshal args: %v", err)
}
slog.Info("calling function", "name", toolCall.Function.Name, "args", string(args))
return tool.Do(ctx, toolCall, client)
}

View File

@@ -25,7 +25,7 @@ func (t *Tool) Desc() api.Tool {
toolPropertiesMap := api.NewToolPropertiesMap()
toolPropertiesMap.Set("weather", api.ToolProperty{
Type: api.PropertyType{"string"},
Description: "What to set the weather too",
Description: "What to set the weather too. NOTE: case with the value provided is important. Keep it lower case.",
Enum: []any{"clear", "rain", "thunder"},
})
return api.Tool{