mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-12 20:15:42 +00:00
65 lines
2.2 KiB
Go
65 lines
2.2 KiB
Go
package agent
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/nextlevelbuilder/goclaw/internal/providers"
|
|
usagecaps "github.com/nextlevelbuilder/goclaw/internal/usage/caps"
|
|
)
|
|
|
|
const titleGenerateTimeout = 120 * time.Second
|
|
|
|
const titleSystemPrompt = `Generate a short title (max 15 words) for this conversation based on the user's message. Reply with only the title, no quotes or punctuation wrapping.`
|
|
|
|
// GenerateTitle uses a lightweight LLM call to create a short conversation title
|
|
// from the user's first message. Returns empty string on error.
|
|
func GenerateTitle(ctx context.Context, provider providers.Provider, model, userMessage string) string {
|
|
return GenerateTitleWithUsageCaps(ctx, nil, provider, model, userMessage)
|
|
}
|
|
|
|
func GenerateTitleWithUsageCaps(ctx context.Context, usageCaps *usagecaps.Service, provider providers.Provider, model, userMessage string) string {
|
|
ctx, cancel := context.WithTimeout(ctx, titleGenerateTimeout)
|
|
defer cancel()
|
|
|
|
req := providers.ChatRequest{
|
|
Messages: []providers.Message{
|
|
{Role: "system", Content: titleSystemPrompt},
|
|
{Role: "user", Content: userMessage},
|
|
},
|
|
Model: model,
|
|
Options: map[string]any{
|
|
// Larger budget: thinking-capable models (Gemini 2.5/3, GPT-5 reasoning)
|
|
// can consume output tokens on reasoning traces. 256 leaves room for a
|
|
// 15-word title even when the provider allocates some budget to thinking.
|
|
providers.OptMaxTokens: 256,
|
|
providers.OptTemperature: 0.3,
|
|
// Disable extended thinking for title generation — it's a trivial task
|
|
// that doesn't benefit from reasoning and defaults (esp. Gemini's "high")
|
|
// otherwise eat the entire max_tokens budget, truncating the title to 1 word.
|
|
providers.OptThinkingLevel: "off",
|
|
},
|
|
}
|
|
resp, err := usageCaps.Chat(ctx, provider, req, usagecaps.ChatOptions{
|
|
ModelID: model,
|
|
Purpose: "session-title",
|
|
MaxOutputTokens: 256,
|
|
})
|
|
if err != nil {
|
|
slog.Warn("title generation failed", "error", err)
|
|
return ""
|
|
}
|
|
|
|
title := strings.TrimSpace(resp.Content)
|
|
// Strip surrounding quotes if present.
|
|
title = strings.Trim(title, "\"'`")
|
|
title = strings.TrimSpace(title)
|
|
|
|
if runes := []rune(title); len(runes) > 100 {
|
|
title = string(runes[:100])
|
|
}
|
|
return title
|
|
}
|