tools.go

 1package tools
 2
 3import (
 4	"context"
 5	"os"
 6	"strconv"
 7	"strings"
 8	"testing"
 9
10	"charm.land/fantasy"
11)
12
13type (
14	sessionIDContextKey string
15	messageIDContextKey string
16	supportsImagesKey   string
17	modelNameKey        string
18)
19
20const (
21	// SessionIDContextKey is the key for the session ID in the context.
22	SessionIDContextKey sessionIDContextKey = "session_id"
23	// MessageIDContextKey is the key for the message ID in the context.
24	MessageIDContextKey messageIDContextKey = "message_id"
25	// SupportsImagesContextKey is the key for the model's image support capability.
26	SupportsImagesContextKey supportsImagesKey = "supports_images"
27	// ModelNameContextKey is the key for the model name in the context.
28	ModelNameContextKey modelNameKey = "model_name"
29)
30
31// getContextValue is a generic helper that retrieves a typed value from context.
32// If the value is not found or has the wrong type, it returns the default value.
33func getContextValue[T any](ctx context.Context, key any, defaultValue T) T {
34	value := ctx.Value(key)
35	if value == nil {
36		return defaultValue
37	}
38	if typedValue, ok := value.(T); ok {
39		return typedValue
40	}
41	return defaultValue
42}
43
44// GetSessionFromContext retrieves the session ID from the context.
45func GetSessionFromContext(ctx context.Context) string {
46	return getContextValue(ctx, SessionIDContextKey, "")
47}
48
49// GetMessageFromContext retrieves the message ID from the context.
50func GetMessageFromContext(ctx context.Context) string {
51	return getContextValue(ctx, MessageIDContextKey, "")
52}
53
54// GetSupportsImagesFromContext retrieves whether the model supports images from the context.
55func GetSupportsImagesFromContext(ctx context.Context) bool {
56	return getContextValue(ctx, SupportsImagesContextKey, false)
57}
58
59// GetModelNameFromContext retrieves the model name from the context.
60func GetModelNameFromContext(ctx context.Context) string {
61	return getContextValue(ctx, ModelNameContextKey, "")
62}
63
64// NewPermissionDeniedResponse returns a tool response indicating the user
65// denied permission, with StopTurn set so the agent loop does not retry.
66func NewPermissionDeniedResponse() fantasy.ToolResponse {
67	resp := fantasy.NewTextErrorResponse("User denied permission")
68	resp.StopTurn = true
69	return resp
70}
71
72// FirstLineDescription returns just the first non-empty line from the embedded
73// markdown description. The full description can be used by setting
74// CRUSH_SHORT_TOOL_DESCRIPTIONS=0.
75func FirstLineDescription(content []byte) string {
76	if !testing.Testing() {
77		if v, err := strconv.ParseBool(os.Getenv("CRUSH_SHORT_TOOL_DESCRIPTIONS")); err == nil && !v {
78			return strings.TrimSpace(string(content))
79		}
80	}
81	for line := range strings.SplitSeq(string(content), "\n") {
82		line = strings.TrimSpace(line)
83		if line != "" {
84			return line
85		}
86	}
87	return ""
88}