mirror of
https://github.com/HugeFrog24/go-telegram-bot.git
synced 2026-06-29 22:07:12 +00:00
Switch to Anthropic SDK because we need MCP servers
This commit is contained in:
+175
@@ -0,0 +1,175 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/anthropics/anthropic-sdk-go"
|
||||
"github.com/anthropics/anthropic-sdk-go/packages/param"
|
||||
)
|
||||
|
||||
// ErrModelNotFound is returned when the configured Anthropic model is no longer available
|
||||
// (deprecated or removed). Callers can use errors.Is to detect this and surface an
|
||||
// actionable message to admins/owners while keeping the response vague for regular users.
|
||||
var ErrModelNotFound = errors.New("model not found or deprecated")
|
||||
|
||||
func (b *Bot) getAnthropicResponse(ctx context.Context, messages []anthropic.BetaMessageParam, isNewChat, isOwner, isEmojiOnly bool, username string, firstName string, lastName string, isPremium bool, languageCode string, messageTime int) (string, error) {
|
||||
// Use prompts from config
|
||||
var systemMessage string
|
||||
if isNewChat {
|
||||
systemMessage = b.config.SystemPrompts["new_chat"]
|
||||
} else {
|
||||
systemMessage = b.config.SystemPrompts["continue_conversation"]
|
||||
}
|
||||
|
||||
// Combine default prompt with custom instructions
|
||||
systemMessage = b.config.SystemPrompts["default"] + " " + b.config.SystemPrompts["custom_instructions"] + " " + systemMessage
|
||||
|
||||
// Handle username placeholder
|
||||
usernameValue := username
|
||||
if username == "" {
|
||||
usernameValue = "unknown" // Use "unknown" when username is not available
|
||||
}
|
||||
systemMessage = strings.ReplaceAll(systemMessage, "{username}", usernameValue)
|
||||
|
||||
// Handle firstname placeholder
|
||||
firstnameValue := firstName
|
||||
if firstName == "" {
|
||||
firstnameValue = "unknown" // Use "unknown" when first name is not available
|
||||
}
|
||||
systemMessage = strings.ReplaceAll(systemMessage, "{firstname}", firstnameValue)
|
||||
|
||||
// Handle lastname placeholder
|
||||
lastnameValue := lastName
|
||||
if lastName == "" {
|
||||
lastnameValue = "" // Empty string when last name is not available
|
||||
}
|
||||
systemMessage = strings.ReplaceAll(systemMessage, "{lastname}", lastnameValue)
|
||||
|
||||
// Handle language code placeholder
|
||||
langValue := languageCode
|
||||
if languageCode == "" {
|
||||
langValue = "en" // Default to English when language code is not available
|
||||
}
|
||||
systemMessage = strings.ReplaceAll(systemMessage, "{language}", langValue)
|
||||
|
||||
// Handle premium status
|
||||
premiumStatus := "regular user"
|
||||
if isPremium {
|
||||
premiumStatus = "premium user"
|
||||
}
|
||||
systemMessage = strings.ReplaceAll(systemMessage, "{premium_status}", premiumStatus)
|
||||
|
||||
// Handle time awareness
|
||||
timeObj := time.Unix(int64(messageTime), 0)
|
||||
hour := timeObj.Hour()
|
||||
var timeContext string
|
||||
if hour >= 5 && hour < 12 {
|
||||
timeContext = "morning"
|
||||
} else if hour >= 12 && hour < 18 {
|
||||
timeContext = "afternoon"
|
||||
} else if hour >= 18 && hour < 22 {
|
||||
timeContext = "evening"
|
||||
} else {
|
||||
timeContext = "night"
|
||||
}
|
||||
systemMessage = strings.ReplaceAll(systemMessage, "{time_context}", timeContext)
|
||||
|
||||
if !isOwner {
|
||||
systemMessage += " " + b.config.SystemPrompts["avoid_sensitive"]
|
||||
}
|
||||
|
||||
if isEmojiOnly {
|
||||
systemMessage += " " + b.config.SystemPrompts["respond_with_emojis"]
|
||||
}
|
||||
|
||||
// Debug logging
|
||||
InfoLogger.Printf("Sending %d messages to Anthropic", len(messages))
|
||||
|
||||
params := anthropic.BetaMessageNewParams{
|
||||
Model: b.config.Model,
|
||||
MaxTokens: 1000,
|
||||
Messages: messages,
|
||||
System: []anthropic.BetaTextBlockParam{{Text: systemMessage}},
|
||||
}
|
||||
|
||||
// Apply temperature if set in config
|
||||
if b.config.Temperature != nil {
|
||||
params.Temperature = param.NewOpt(float64(*b.config.Temperature))
|
||||
}
|
||||
|
||||
// MCP servers + matching toolset entries. The mcp-client-2025-11-20 beta
|
||||
// requires per-tool filtering on the toolset (Configs + DefaultConfig),
|
||||
// NOT the deprecated per-server tool_configuration block.
|
||||
if len(b.config.MCPServers) > 0 {
|
||||
mcpServers := make([]anthropic.BetaRequestMCPServerURLDefinitionParam, 0, len(b.config.MCPServers))
|
||||
tools := make([]anthropic.BetaToolUnionParam, 0, len(b.config.MCPServers))
|
||||
for _, s := range b.config.MCPServers {
|
||||
srv := anthropic.BetaRequestMCPServerURLDefinitionParam{
|
||||
Name: s.Name,
|
||||
URL: s.URL,
|
||||
}
|
||||
if s.AuthorizationToken != "" {
|
||||
srv.AuthorizationToken = param.NewOpt(s.AuthorizationToken)
|
||||
}
|
||||
mcpServers = append(mcpServers, srv)
|
||||
|
||||
toolset := &anthropic.BetaMCPToolsetParam{
|
||||
MCPServerName: s.Name,
|
||||
}
|
||||
if len(s.AllowedTools) > 0 {
|
||||
toolset.DefaultConfig = anthropic.BetaMCPToolDefaultConfigParam{
|
||||
Enabled: param.NewOpt(false),
|
||||
}
|
||||
toolset.Configs = make(map[string]anthropic.BetaMCPToolConfigParam, len(s.AllowedTools))
|
||||
for _, tool := range s.AllowedTools {
|
||||
toolset.Configs[tool] = anthropic.BetaMCPToolConfigParam{
|
||||
Enabled: param.NewOpt(true),
|
||||
}
|
||||
}
|
||||
}
|
||||
tools = append(tools, anthropic.BetaToolUnionParam{OfMCPToolset: toolset})
|
||||
}
|
||||
params.MCPServers = mcpServers
|
||||
params.Tools = tools
|
||||
params.Betas = []anthropic.AnthropicBeta{anthropic.AnthropicBetaMCPClient2025_11_20}
|
||||
}
|
||||
|
||||
resp, err := b.anthropicClient.Beta.Messages.New(ctx, params)
|
||||
if err != nil {
|
||||
var apiErr *anthropic.Error
|
||||
if errors.As(err, &apiErr) && apiErr.StatusCode == http.StatusNotFound {
|
||||
return "", fmt.Errorf("%w: %s", ErrModelNotFound, b.config.Model)
|
||||
}
|
||||
return "", fmt.Errorf("error creating Anthropic message: %w", err)
|
||||
}
|
||||
|
||||
var sb strings.Builder
|
||||
for _, block := range resp.Content {
|
||||
switch block.Type {
|
||||
case "text":
|
||||
sb.WriteString(block.Text)
|
||||
case "mcp_tool_use":
|
||||
InfoLogger.Printf("[mcp] tool_use server=%q name=%q id=%q input=%s",
|
||||
block.ServerName, block.Name, block.ID, string(block.Input))
|
||||
case "mcp_tool_result":
|
||||
preview := block.JSON.Content.Raw()
|
||||
if len(preview) > 500 {
|
||||
preview = preview[:500] + "...(truncated)"
|
||||
}
|
||||
InfoLogger.Printf("[mcp] tool_result tool_use_id=%q server=%q is_error=%v content=%s",
|
||||
block.ToolUseID, block.ServerName, block.IsError, preview)
|
||||
default:
|
||||
InfoLogger.Printf("[mcp] block type=%q (unhandled)", block.Type)
|
||||
}
|
||||
}
|
||||
out := sb.String()
|
||||
if out == "" {
|
||||
return "", fmt.Errorf("unexpected response format from Anthropic")
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
Reference in New Issue
Block a user