Skip to content
This repository was archived by the owner on Sep 18, 2025. It is now read-only.

Commit 8d874b8

Browse files
committed
add initial message handling
1 parent 08bd75b commit 8d874b8

File tree

14 files changed

+1124
-458
lines changed

14 files changed

+1124
-458
lines changed

internal/db/messages.sql.go

Lines changed: 23 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/db/migrations/000001_initial.up.sql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ CREATE TABLE IF NOT EXISTS messages (
2424
session_id TEXT NOT NULL,
2525
role TEXT NOT NULL,
2626
parts TEXT NOT NULL default '[]',
27+
model TEXT,
2728
created_at INTEGER NOT NULL, -- Unix timestamp in milliseconds
2829
updated_at INTEGER NOT NULL, -- Unix timestamp in milliseconds
30+
finished_at INTEGER, -- Unix timestamp in milliseconds
2931
FOREIGN KEY (session_id) REFERENCES sessions (id) ON DELETE CASCADE
3032
);
3133

internal/db/models.go

Lines changed: 8 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/db/sql/messages.sql

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,23 @@ INSERT INTO messages (
1515
session_id,
1616
role,
1717
parts,
18+
model,
1819
created_at,
1920
updated_at
2021
) VALUES (
21-
?, ?, ?, ?, strftime('%s', 'now'), strftime('%s', 'now')
22+
?, ?, ?, ?, ?, strftime('%s', 'now'), strftime('%s', 'now')
2223
)
2324
RETURNING *;
2425

2526
-- name: UpdateMessage :exec
2627
UPDATE messages
2728
SET
2829
parts = ?,
30+
finished_at = ?,
2931
updated_at = strftime('%s', 'now')
3032
WHERE id = ?;
3133

34+
3235
-- name: DeleteMessage :exec
3336
DELETE FROM messages
3437
WHERE id = ?;

internal/message/content.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package message
22

33
import (
44
"encoding/base64"
5+
"time"
56
)
67

78
type MessageRole string
@@ -64,6 +65,7 @@ type ToolCall struct {
6465
Name string `json:"name"`
6566
Input string `json:"input"`
6667
Type string `json:"type"`
68+
Metadata any `json:"metadata"`
6769
Finished bool `json:"finished"`
6870
}
6971

@@ -80,6 +82,7 @@ func (ToolResult) isPart() {}
8082

8183
type Finish struct {
8284
Reason string `json:"reason"`
85+
Time int64 `json:"time"`
8386
}
8487

8588
func (Finish) isPart() {}
@@ -161,6 +164,15 @@ func (m *Message) IsFinished() bool {
161164
return false
162165
}
163166

167+
func (m *Message) FinishPart() *Finish {
168+
for _, part := range m.Parts {
169+
if c, ok := part.(Finish); ok {
170+
return &c
171+
}
172+
}
173+
return nil
174+
}
175+
164176
func (m *Message) FinishReason() string {
165177
for _, part := range m.Parts {
166178
if c, ok := part.(Finish); ok {
@@ -232,7 +244,7 @@ func (m *Message) SetToolResults(tr []ToolResult) {
232244
}
233245

234246
func (m *Message) AddFinish(reason string) {
235-
m.Parts = append(m.Parts, Finish{Reason: reason})
247+
m.Parts = append(m.Parts, Finish{Reason: reason, Time: time.Now().Unix()})
236248
}
237249

238250
func (m *Message) AddImageURL(url, detail string) {

internal/message/message.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,20 @@ package message
22

33
import (
44
"context"
5+
"database/sql"
56
"encoding/json"
67
"fmt"
78

89
"github.com/google/uuid"
910
"github.com/kujtimiihoxha/termai/internal/db"
11+
"github.com/kujtimiihoxha/termai/internal/llm/models"
1012
"github.com/kujtimiihoxha/termai/internal/pubsub"
1113
)
1214

1315
type CreateMessageParams struct {
1416
Role MessageRole
1517
Parts []ContentPart
18+
Model models.ModelID
1619
}
1720

1821
type Service interface {
@@ -68,6 +71,7 @@ func (s *service) Create(sessionID string, params CreateMessageParams) (Message,
6871
SessionID: sessionID,
6972
Role: string(params.Role),
7073
Parts: string(partsJSON),
74+
Model: sql.NullString{String: string(params.Model), Valid: true},
7175
})
7276
if err != nil {
7377
return Message{}, err
@@ -101,9 +105,15 @@ func (s *service) Update(message Message) error {
101105
if err != nil {
102106
return err
103107
}
108+
finishedAt := sql.NullInt64{}
109+
if f := message.FinishPart(); f != nil {
110+
finishedAt.Int64 = f.Time
111+
finishedAt.Valid = true
112+
}
104113
err = s.q.UpdateMessage(s.ctx, db.UpdateMessageParams{
105-
ID: message.ID,
106-
Parts: string(parts),
114+
ID: message.ID,
115+
Parts: string(parts),
116+
FinishedAt: finishedAt,
107117
})
108118
if err != nil {
109119
return err
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package chat
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/charmbracelet/lipgloss"
7+
"github.com/charmbracelet/x/ansi"
8+
"github.com/kujtimiihoxha/termai/internal/config"
9+
"github.com/kujtimiihoxha/termai/internal/session"
10+
"github.com/kujtimiihoxha/termai/internal/tui/styles"
11+
"github.com/kujtimiihoxha/termai/internal/version"
12+
)
13+
14+
type SendMsg struct {
15+
Text string
16+
}
17+
18+
type SessionSelectedMsg = session.Session
19+
20+
type SessionClearedMsg struct{}
21+
22+
type AgentWorkingMsg bool
23+
24+
type EditorFocusMsg bool
25+
26+
func lspsConfigured(width int) string {
27+
cfg := config.Get()
28+
title := "LSP Configuration"
29+
title = ansi.Truncate(title, width, "…")
30+
31+
lsps := styles.BaseStyle.Width(width).Foreground(styles.PrimaryColor).Bold(true).Render(title)
32+
33+
var lspViews []string
34+
for name, lsp := range cfg.LSP {
35+
lspName := styles.BaseStyle.Foreground(styles.Forground).Render(
36+
fmt.Sprintf("• %s", name),
37+
)
38+
cmd := lsp.Command
39+
cmd = ansi.Truncate(cmd, width-lipgloss.Width(lspName)-3, "…")
40+
lspPath := styles.BaseStyle.Foreground(styles.ForgroundDim).Render(
41+
fmt.Sprintf(" (%s)", cmd),
42+
)
43+
lspViews = append(lspViews,
44+
styles.BaseStyle.
45+
Width(width).
46+
Render(
47+
lipgloss.JoinHorizontal(
48+
lipgloss.Left,
49+
lspName,
50+
lspPath,
51+
),
52+
),
53+
)
54+
55+
}
56+
return styles.BaseStyle.
57+
Width(width).
58+
Render(
59+
lipgloss.JoinVertical(
60+
lipgloss.Left,
61+
lsps,
62+
lipgloss.JoinVertical(
63+
lipgloss.Left,
64+
lspViews...,
65+
),
66+
),
67+
)
68+
}
69+
70+
func logo(width int) string {
71+
logo := fmt.Sprintf("%s %s", styles.OpenCodeIcon, "OpenCode")
72+
73+
version := styles.BaseStyle.Foreground(styles.ForgroundDim).Render(version.Version)
74+
75+
return styles.BaseStyle.
76+
Bold(true).
77+
Width(width).
78+
Render(
79+
lipgloss.JoinHorizontal(
80+
lipgloss.Left,
81+
logo,
82+
" ",
83+
version,
84+
),
85+
)
86+
}
87+
88+
func repo(width int) string {
89+
repo := "https://github.com/kujtimiihoxha/opencode"
90+
return styles.BaseStyle.
91+
Foreground(styles.ForgroundDim).
92+
Width(width).
93+
Render(repo)
94+
}
95+
96+
func cwd(width int) string {
97+
cwd := fmt.Sprintf("cwd: %s", config.WorkingDirectory())
98+
return styles.BaseStyle.
99+
Foreground(styles.ForgroundDim).
100+
Width(width).
101+
Render(cwd)
102+
}
103+
104+
func header(width int) string {
105+
header := lipgloss.JoinVertical(
106+
lipgloss.Top,
107+
logo(width),
108+
repo(width),
109+
"",
110+
cwd(width),
111+
)
112+
return header
113+
}

0 commit comments

Comments
 (0)