Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 24 additions & 15 deletions cmd/client-telegram/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,30 +188,24 @@ func (l *Logic) Handler(ctx context.Context, b *bot.Bot, update *models.Update)
fmt.Println("photo file ID:", highResImg.FileID)
fmt.Printf("photo info: W: %d, H: %d, Size: %d\n", highResImg.Width, highResImg.Height, highResImg.FileSize)
fmt.Println("caption:", update.Message.Caption)
f, err := b.GetFile(ctx, &bot.GetFileParams{
FileID: highResImg.FileID,
})
data, err := downloadFile(ctx, b, highResImg.FileID)
if err != nil {
fmt.Println("error getting file:", err)
fmt.Println("error downloading photo:", err)
return
}

// Download the file
dlURL := b.FileDownloadLink(f)
resp, err := http.Get(dlURL)
if err != nil {
fmt.Println("error downloading file:", err)
return
payloads = append(payloads, data)
if update.Message.Caption != "" {
msg = update.Message.Caption
}
defer func() { _ = resp.Body.Close() }()
}

data, err := io.ReadAll(resp.Body)
if update.Message.Document != nil {
data, err := downloadFile(ctx, b, update.Message.Document.FileID)
if err != nil {
fmt.Println("error reading file:", err)
fmt.Println("error downloading document:", err)
return
}
payloads = append(payloads, data)

if update.Message.Caption != "" {
msg = update.Message.Caption
}
Expand Down Expand Up @@ -282,6 +276,21 @@ func (l *Logic) Handler(ctx context.Context, b *bot.Bot, update *models.Update)

}

func downloadFile(ctx context.Context, b *bot.Bot, fileID string) ([]byte, error) {
f, err := b.GetFile(ctx, &bot.GetFileParams{FileID: fileID})
if err != nil {
return nil, err
}

resp, err := http.Get(b.FileDownloadLink(f))
if err != nil {
return nil, err
}
defer func() { _ = resp.Body.Close() }()

return io.ReadAll(resp.Body)
}

func biggestImage(photos []models.PhotoSize) models.PhotoSize {
if len(photos) == 0 {
return models.PhotoSize{}
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ require (
github.com/go-telegram/bot v1.17.0
github.com/google/uuid v1.6.0
github.com/mark3labs/mcp-go v0.29.1-0.20250521213157-f99e5472f312
github.com/openai/openai-go v1.8.2
github.com/philippgille/chromem-go v0.7.0
golang.org/x/oauth2 v0.30.0
google.golang.org/api v0.236.0
Expand Down Expand Up @@ -41,7 +42,6 @@ require (
github.com/mattn/go-colorable v0.1.2 // indirect
github.com/mattn/go-isatty v0.0.8 // indirect
github.com/mbleigh/raymond v0.0.0-20250414171441-6b3a58ab9e0a // indirect
github.com/openai/openai-go v1.8.2 // indirect
github.com/spf13/cast v1.7.1 // indirect
github.com/tidwall/gjson v1.18.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
Expand Down
9 changes: 6 additions & 3 deletions internal/ai/agent/logic.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package agent

import (
"context"
"encoding/base64"
"errors"
"fmt"
"log/slog"
Expand Down Expand Up @@ -207,11 +208,13 @@ func (l *Logic) HandleMessage(ctx context.Context, sessionID string, req domain.
userPromptParts := make([]*ai.Part, 0, len(req.InlineData)+1)
for _, inlineData := range req.InlineData {
// If we have some inline data convert them to prompt parts
userPromptParts = append(userPromptParts, ai.NewMediaPart(inlineData.MimeType, string(inlineData.Data)))
userPromptParts = append(userPromptParts, ai.NewMediaPart(inlineData.MimeType, "data:"+inlineData.MimeType+";base64,"+base64.StdEncoding.EncodeToString(inlineData.Data)))
}

// Add the user's request at the end too
userPromptParts = append(userPromptParts, ai.NewTextPart(req.Message))
// Add the user's request at the end too (skip if empty, e.g. file sent without caption)
if req.Message != "" {
userPromptParts = append(userPromptParts, ai.NewTextPart(req.Message))
}
hist = append(hist, ai.NewUserMessage(userPromptParts...))

// Inject static and dynamic information into the system prompt
Expand Down
20 changes: 20 additions & 0 deletions internal/history/history.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ func (l *Logic) Save(ctx context.Context, sessionID string, history []*ai.Messag
return errors.New("invalid sessionID")
}

history = stripMedia(history)

var b []byte
var err error
if l.config.HistorySummary > 0 && len(history) >= l.config.HistorySummary {
Expand Down Expand Up @@ -121,6 +123,24 @@ func (l *Logic) summarize(ctx context.Context, history []*ai.Message) (*ai.Messa
return ai.NewModelTextMessage(fmt.Sprintf("Summarized history:\n\n%s", summary)), nil
}

// stripMedia replaces PartMedia parts with a text placeholder so binary file
// data is not re-sent on every subsequent turn.
func stripMedia(msgs []*ai.Message) []*ai.Message {
out := make([]*ai.Message, len(msgs))
for i, msg := range msgs {
stripped := make([]*ai.Part, 0, len(msg.Content))
for _, p := range msg.Content {
if p.Kind == ai.PartMedia {
stripped = append(stripped, ai.NewTextPart("[attached: "+p.ContentType+"]"))
} else {
stripped = append(stripped, p)
}
}
out[i] = &ai.Message{Role: msg.Role, Content: stripped, Metadata: msg.Metadata}
}
return out
}

func contentToString(history []*ai.Message) string {
var sb strings.Builder
for _, h := range history {
Expand Down
Loading