Skip to content

Improve summary to keep context #159

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 15, 2025
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
2 changes: 1 addition & 1 deletion internal/db/db.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion internal/db/files.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion internal/db/messages.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-- +goose Up
-- +goose StatementBegin
ALTER TABLE sessions ADD COLUMN summary_message_id TEXT;
-- +goose StatementEnd

-- +goose Down
-- +goose StatementBegin
ALTER TABLE sessions DROP COLUMN summary_message_id;
-- +goose StatementEnd
3 changes: 2 additions & 1 deletion internal/db/models.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion internal/db/querier.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 19 additions & 10 deletions internal/db/sessions.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions internal/db/sql/sessions.sql
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ INSERT INTO sessions (
prompt_tokens,
completion_tokens,
cost,
summary_message_id,
updated_at,
created_at
) VALUES (
Expand All @@ -17,6 +18,7 @@ INSERT INTO sessions (
?,
?,
?,
null,
strftime('%s', 'now'),
strftime('%s', 'now')
) RETURNING *;
Expand All @@ -38,6 +40,7 @@ SET
title = ?,
prompt_tokens = ?,
completion_tokens = ?,
summary_message_id = ?,
cost = ?
WHERE id = ?
RETURNING *;
Expand Down
60 changes: 46 additions & 14 deletions internal/llm/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"strings"
"sync"
"time"

"github.com/opencode-ai/opencode/internal/config"
"github.com/opencode-ai/opencode/internal/llm/models"
Expand Down Expand Up @@ -245,6 +246,23 @@ func (a *agent) processGeneration(ctx context.Context, sessionID, content string
}
}()
}
session, err := a.sessions.Get(ctx, sessionID)
if err != nil {
return a.err(fmt.Errorf("failed to get session: %w", err))
}
if session.SummaryMessageID != "" {
summaryMsgInex := -1
for i, msg := range msgs {
if msg.ID == session.SummaryMessageID {
summaryMsgInex = i
break
}
}
if summaryMsgInex != -1 {
msgs = msgs[summaryMsgInex:]
msgs[0].Role = message.User
}
}

userMsg, err := a.createUserMessage(ctx, sessionID, content, attachmentParts)
if err != nil {
Expand Down Expand Up @@ -614,37 +632,51 @@ func (a *agent) Summarize(ctx context.Context, sessionID string) error {
a.Publish(pubsub.CreatedEvent, event)
return
}
// Create a new session with the summary
newSession, err := a.sessions.Create(summarizeCtx, oldSession.Title+" - Continuation")
// Create a message in the new session with the summary
msg, err := a.messages.Create(summarizeCtx, oldSession.ID, message.CreateMessageParams{
Role: message.Assistant,
Parts: []message.ContentPart{
message.TextContent{Text: summary},
message.Finish{
Reason: message.FinishReasonEndTurn,
Time: time.Now().Unix(),
},
},
Model: a.summarizeProvider.Model().ID,
})
if err != nil {
event = AgentEvent{
Type: AgentEventTypeError,
Error: fmt.Errorf("failed to create new session: %w", err),
Error: fmt.Errorf("failed to create summary message: %w", err),
Done: true,
}

a.Publish(pubsub.CreatedEvent, event)
return
}

// Create a message in the new session with the summary
_, err = a.messages.Create(summarizeCtx, newSession.ID, message.CreateMessageParams{
Role: message.Assistant,
Parts: []message.ContentPart{message.TextContent{Text: summary}},
Model: a.summarizeProvider.Model().ID,
})
oldSession.SummaryMessageID = msg.ID
oldSession.CompletionTokens = response.Usage.OutputTokens
oldSession.PromptTokens = 0
model := a.summarizeProvider.Model()
usage := response.Usage
cost := model.CostPer1MInCached/1e6*float64(usage.CacheCreationTokens) +
model.CostPer1MOutCached/1e6*float64(usage.CacheReadTokens) +
model.CostPer1MIn/1e6*float64(usage.InputTokens) +
model.CostPer1MOut/1e6*float64(usage.OutputTokens)
oldSession.Cost += cost
_, err = a.sessions.Save(summarizeCtx, oldSession)
if err != nil {
event = AgentEvent{
Type: AgentEventTypeError,
Error: fmt.Errorf("failed to create summary message: %w", err),
Error: fmt.Errorf("failed to save session: %w", err),
Done: true,
}

a.Publish(pubsub.CreatedEvent, event)
return
}

event = AgentEvent{
Type: AgentEventTypeSummarize,
SessionID: newSession.ID,
SessionID: oldSession.ID,
Progress: "Summary complete",
Done: true,
}
Expand Down
8 changes: 7 additions & 1 deletion internal/session/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type Session struct {
MessageCount int64
PromptTokens int64
CompletionTokens int64
SummaryMessageID string
Cost float64
CreatedAt int64
UpdatedAt int64
Expand Down Expand Up @@ -105,7 +106,11 @@ func (s *service) Save(ctx context.Context, session Session) (Session, error) {
Title: session.Title,
PromptTokens: session.PromptTokens,
CompletionTokens: session.CompletionTokens,
Cost: session.Cost,
SummaryMessageID: sql.NullString{
String: session.SummaryMessageID,
Valid: session.SummaryMessageID != "",
},
Cost: session.Cost,
})
if err != nil {
return Session{}, err
Expand Down Expand Up @@ -135,6 +140,7 @@ func (s service) fromDBItem(item db.Session) Session {
MessageCount: item.MessageCount,
PromptTokens: item.PromptTokens,
CompletionTokens: item.CompletionTokens,
SummaryMessageID: item.SummaryMessageID.String,
Cost: item.Cost,
CreatedAt: item.CreatedAt,
UpdatedAt: item.UpdatedAt,
Expand Down
11 changes: 11 additions & 0 deletions internal/tui/components/chat/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,14 @@ func (m *messagesCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case renderFinishedMsg:
m.rendering = false
m.viewport.GotoBottom()
case pubsub.Event[session.Session]:
if msg.Type == pubsub.UpdatedEvent && msg.Payload.ID == m.session.ID {
m.session = msg.Payload
if m.session.SummaryMessageID == m.currentMsgID {
delete(m.cachedContent, m.currentMsgID)
m.renderView()
}
}
case pubsub.Event[message.Message]:
needsRerender := false
if msg.Type == pubsub.CreatedEvent {
Expand Down Expand Up @@ -208,12 +216,15 @@ func (m *messagesCmp) renderView() {
m.uiMessages = append(m.uiMessages, cache.content...)
continue
}
isSummary := m.session.SummaryMessageID == msg.ID

assistantMessages := renderAssistantMessage(
msg,
inx,
m.messages,
m.app.Messages,
m.currentMsgID,
isSummary,
m.width,
pos,
)
Expand Down
4 changes: 4 additions & 0 deletions internal/tui/components/chat/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ func renderAssistantMessage(
allMessages []message.Message, // we need this to get tool results and the user message
messagesService message.Service, // We need this to get the task tool messages
focusedUIMessageId string,
isSummary bool,
width int,
position int,
) []uiMessage {
Expand Down Expand Up @@ -168,6 +169,9 @@ func renderAssistantMessage(
if content == "" {
content = "*Finished without output*"
}
if isSummary {
info = append(info, baseStyle.Width(width-1).Foreground(t.TextMuted()).Render(" (summary)"))
}

content = renderMessage(content, false, true, width, info...)
messages = append(messages, uiMessage{
Expand Down
24 changes: 0 additions & 24 deletions internal/tui/tui.go
Original file line number Diff line number Diff line change
Expand Up @@ -331,30 +331,6 @@ func (a appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {

if payload.Done && payload.Type == agent.AgentEventTypeSummarize {
a.isCompacting = false

if payload.SessionID != "" {
// Switch to the new session
return a, func() tea.Msg {
sessions, err := a.app.Sessions.List(context.Background())
if err != nil {
return util.InfoMsg{
Type: util.InfoTypeError,
Msg: "Failed to list sessions: " + err.Error(),
}
}

for _, s := range sessions {
if s.ID == payload.SessionID {
return dialog.SessionSelectedMsg{Session: s}
}
}

return util.InfoMsg{
Type: util.InfoTypeError,
Msg: "Failed to find new session",
}
}
}
return a, util.ReportInfo("Session summarization complete")
} else if payload.Done && payload.Type == agent.AgentEventTypeResponse && a.selectedSession.ID != "" {
model := a.app.CoderAgent.Model()
Expand Down