mirror of
https://github.com/sst/opencode.git
synced 2025-08-22 14:04:07 +00:00
chore: refactor db
This commit is contained in:
parent
d8f3b60625
commit
01b6bf5bb7
19 changed files with 193 additions and 156 deletions
|
@ -15,13 +15,11 @@ INSERT INTO files (
|
|||
session_id,
|
||||
path,
|
||||
content,
|
||||
version,
|
||||
created_at,
|
||||
updated_at
|
||||
version
|
||||
) VALUES (
|
||||
?, ?, ?, ?, ?, strftime('%s', 'now'), strftime('%s', 'now')
|
||||
?, ?, ?, ?, ?
|
||||
)
|
||||
RETURNING id, session_id, path, content, version, created_at, updated_at
|
||||
RETURNING id, session_id, path, content, version, is_new, created_at, updated_at
|
||||
`
|
||||
|
||||
type CreateFileParams struct {
|
||||
|
@ -47,6 +45,7 @@ func (q *Queries) CreateFile(ctx context.Context, arg CreateFileParams) (File, e
|
|||
&i.Path,
|
||||
&i.Content,
|
||||
&i.Version,
|
||||
&i.IsNew,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
|
@ -74,7 +73,7 @@ func (q *Queries) DeleteSessionFiles(ctx context.Context, sessionID string) erro
|
|||
}
|
||||
|
||||
const getFile = `-- name: GetFile :one
|
||||
SELECT id, session_id, path, content, version, created_at, updated_at
|
||||
SELECT id, session_id, path, content, version, is_new, created_at, updated_at
|
||||
FROM files
|
||||
WHERE id = ? LIMIT 1
|
||||
`
|
||||
|
@ -88,6 +87,7 @@ func (q *Queries) GetFile(ctx context.Context, id string) (File, error) {
|
|||
&i.Path,
|
||||
&i.Content,
|
||||
&i.Version,
|
||||
&i.IsNew,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
|
@ -95,7 +95,7 @@ func (q *Queries) GetFile(ctx context.Context, id string) (File, error) {
|
|||
}
|
||||
|
||||
const getFileByPathAndSession = `-- name: GetFileByPathAndSession :one
|
||||
SELECT id, session_id, path, content, version, created_at, updated_at
|
||||
SELECT id, session_id, path, content, version, is_new, created_at, updated_at
|
||||
FROM files
|
||||
WHERE path = ? AND session_id = ?
|
||||
ORDER BY created_at DESC
|
||||
|
@ -116,6 +116,7 @@ func (q *Queries) GetFileByPathAndSession(ctx context.Context, arg GetFileByPath
|
|||
&i.Path,
|
||||
&i.Content,
|
||||
&i.Version,
|
||||
&i.IsNew,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
|
@ -123,7 +124,7 @@ func (q *Queries) GetFileByPathAndSession(ctx context.Context, arg GetFileByPath
|
|||
}
|
||||
|
||||
const listFilesByPath = `-- name: ListFilesByPath :many
|
||||
SELECT id, session_id, path, content, version, created_at, updated_at
|
||||
SELECT id, session_id, path, content, version, is_new, created_at, updated_at
|
||||
FROM files
|
||||
WHERE path = ?
|
||||
ORDER BY created_at DESC
|
||||
|
@ -144,6 +145,7 @@ func (q *Queries) ListFilesByPath(ctx context.Context, path string) ([]File, err
|
|||
&i.Path,
|
||||
&i.Content,
|
||||
&i.Version,
|
||||
&i.IsNew,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
|
@ -161,7 +163,7 @@ func (q *Queries) ListFilesByPath(ctx context.Context, path string) ([]File, err
|
|||
}
|
||||
|
||||
const listFilesBySession = `-- name: ListFilesBySession :many
|
||||
SELECT id, session_id, path, content, version, created_at, updated_at
|
||||
SELECT id, session_id, path, content, version, is_new, created_at, updated_at
|
||||
FROM files
|
||||
WHERE session_id = ?
|
||||
ORDER BY created_at ASC
|
||||
|
@ -182,6 +184,7 @@ func (q *Queries) ListFilesBySession(ctx context.Context, sessionID string) ([]F
|
|||
&i.Path,
|
||||
&i.Content,
|
||||
&i.Version,
|
||||
&i.IsNew,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
|
@ -199,7 +202,7 @@ func (q *Queries) ListFilesBySession(ctx context.Context, sessionID string) ([]F
|
|||
}
|
||||
|
||||
const listLatestSessionFiles = `-- name: ListLatestSessionFiles :many
|
||||
SELECT f.id, f.session_id, f.path, f.content, f.version, f.created_at, f.updated_at
|
||||
SELECT f.id, f.session_id, f.path, f.content, f.version, f.is_new, f.created_at, f.updated_at
|
||||
FROM files f
|
||||
INNER JOIN (
|
||||
SELECT path, MAX(created_at) as max_created_at
|
||||
|
@ -225,6 +228,7 @@ func (q *Queries) ListLatestSessionFiles(ctx context.Context, sessionID string)
|
|||
&i.Path,
|
||||
&i.Content,
|
||||
&i.Version,
|
||||
&i.IsNew,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
|
@ -242,7 +246,7 @@ func (q *Queries) ListLatestSessionFiles(ctx context.Context, sessionID string)
|
|||
}
|
||||
|
||||
const listNewFiles = `-- name: ListNewFiles :many
|
||||
SELECT id, session_id, path, content, version, created_at, updated_at
|
||||
SELECT id, session_id, path, content, version, is_new, created_at, updated_at
|
||||
FROM files
|
||||
WHERE is_new = 1
|
||||
ORDER BY created_at DESC
|
||||
|
@ -263,6 +267,7 @@ func (q *Queries) ListNewFiles(ctx context.Context) ([]File, error) {
|
|||
&i.Path,
|
||||
&i.Content,
|
||||
&i.Version,
|
||||
&i.IsNew,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
|
@ -284,9 +289,9 @@ UPDATE files
|
|||
SET
|
||||
content = ?,
|
||||
version = ?,
|
||||
updated_at = strftime('%s', 'now')
|
||||
updated_at = strftime('%Y-%m-%dT%H:%M:%f000Z', 'now')
|
||||
WHERE id = ?
|
||||
RETURNING id, session_id, path, content, version, created_at, updated_at
|
||||
RETURNING id, session_id, path, content, version, is_new, created_at, updated_at
|
||||
`
|
||||
|
||||
type UpdateFileParams struct {
|
||||
|
@ -304,6 +309,7 @@ func (q *Queries) UpdateFile(ctx context.Context, arg UpdateFileParams) (File, e
|
|||
&i.Path,
|
||||
&i.Content,
|
||||
&i.Version,
|
||||
&i.IsNew,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
|
|
|
@ -17,27 +17,24 @@ INSERT INTO logs (
|
|||
timestamp,
|
||||
level,
|
||||
message,
|
||||
attributes,
|
||||
created_at
|
||||
attributes
|
||||
) VALUES (
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?
|
||||
) RETURNING id, session_id, timestamp, level, message, attributes, created_at
|
||||
) RETURNING id, session_id, timestamp, level, message, attributes, created_at, updated_at
|
||||
`
|
||||
|
||||
type CreateLogParams struct {
|
||||
ID string `json:"id"`
|
||||
SessionID sql.NullString `json:"session_id"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
Level string `json:"level"`
|
||||
Message string `json:"message"`
|
||||
Attributes sql.NullString `json:"attributes"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateLog(ctx context.Context, arg CreateLogParams) (Log, error) {
|
||||
|
@ -48,7 +45,6 @@ func (q *Queries) CreateLog(ctx context.Context, arg CreateLogParams) (Log, erro
|
|||
arg.Level,
|
||||
arg.Message,
|
||||
arg.Attributes,
|
||||
arg.CreatedAt,
|
||||
)
|
||||
var i Log
|
||||
err := row.Scan(
|
||||
|
@ -59,12 +55,13 @@ func (q *Queries) CreateLog(ctx context.Context, arg CreateLogParams) (Log, erro
|
|||
&i.Message,
|
||||
&i.Attributes,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const listAllLogs = `-- name: ListAllLogs :many
|
||||
SELECT id, session_id, timestamp, level, message, attributes, created_at FROM logs
|
||||
SELECT id, session_id, timestamp, level, message, attributes, created_at, updated_at FROM logs
|
||||
ORDER BY timestamp DESC
|
||||
LIMIT ?
|
||||
`
|
||||
|
@ -86,6 +83,7 @@ func (q *Queries) ListAllLogs(ctx context.Context, limit int64) ([]Log, error) {
|
|||
&i.Message,
|
||||
&i.Attributes,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -101,7 +99,7 @@ func (q *Queries) ListAllLogs(ctx context.Context, limit int64) ([]Log, error) {
|
|||
}
|
||||
|
||||
const listLogsBySession = `-- name: ListLogsBySession :many
|
||||
SELECT id, session_id, timestamp, level, message, attributes, created_at FROM logs
|
||||
SELECT id, session_id, timestamp, level, message, attributes, created_at, updated_at FROM logs
|
||||
WHERE session_id = ?
|
||||
ORDER BY timestamp ASC
|
||||
`
|
||||
|
@ -123,6 +121,7 @@ func (q *Queries) ListLogsBySession(ctx context.Context, sessionID sql.NullStrin
|
|||
&i.Message,
|
||||
&i.Attributes,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -16,11 +16,9 @@ INSERT INTO messages (
|
|||
session_id,
|
||||
role,
|
||||
parts,
|
||||
model,
|
||||
created_at,
|
||||
updated_at
|
||||
model
|
||||
) VALUES (
|
||||
?, ?, ?, ?, ?, strftime('%s', 'now'), strftime('%s', 'now')
|
||||
?, ?, ?, ?, ?
|
||||
)
|
||||
RETURNING id, session_id, role, parts, model, created_at, updated_at, finished_at
|
||||
`
|
||||
|
@ -145,7 +143,7 @@ ORDER BY created_at ASC
|
|||
|
||||
type ListMessagesBySessionAfterParams struct {
|
||||
SessionID string `json:"session_id"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
}
|
||||
|
||||
func (q *Queries) ListMessagesBySessionAfter(ctx context.Context, arg ListMessagesBySessionAfterParams) ([]Message, error) {
|
||||
|
@ -185,14 +183,14 @@ UPDATE messages
|
|||
SET
|
||||
parts = ?,
|
||||
finished_at = ?,
|
||||
updated_at = strftime('%s', 'now')
|
||||
updated_at = strftime('%Y-%m-%dT%H:%M:%f000Z', 'now')
|
||||
WHERE id = ?
|
||||
`
|
||||
|
||||
type UpdateMessageParams struct {
|
||||
Parts string `json:"parts"`
|
||||
FinishedAt sql.NullInt64 `json:"finished_at"`
|
||||
ID string `json:"id"`
|
||||
Parts string `json:"parts"`
|
||||
FinishedAt sql.NullString `json:"finished_at"`
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateMessage(ctx context.Context, arg UpdateMessageParams) error {
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
ALTER TABLE sessions ADD COLUMN summary TEXT;
|
||||
ALTER TABLE sessions ADD COLUMN summarized_at INTEGER;
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
-- +goose StatementBegin
|
||||
ALTER TABLE sessions DROP COLUMN summarized_at;
|
||||
ALTER TABLE sessions DROP COLUMN summary;
|
||||
-- +goose StatementEnd
|
|
@ -1,16 +0,0 @@
|
|||
-- +goose Up
|
||||
CREATE TABLE logs (
|
||||
id TEXT PRIMARY KEY,
|
||||
session_id TEXT REFERENCES sessions(id) ON DELETE CASCADE,
|
||||
timestamp INTEGER NOT NULL,
|
||||
level TEXT NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
attributes TEXT,
|
||||
created_at INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE INDEX logs_session_id_idx ON logs(session_id);
|
||||
CREATE INDEX logs_timestamp_idx ON logs(timestamp);
|
||||
|
||||
-- +goose Down
|
||||
DROP TABLE logs;
|
|
@ -6,17 +6,19 @@ CREATE TABLE IF NOT EXISTS sessions (
|
|||
parent_session_id TEXT,
|
||||
title TEXT NOT NULL,
|
||||
message_count INTEGER NOT NULL DEFAULT 0 CHECK (message_count >= 0),
|
||||
prompt_tokens INTEGER NOT NULL DEFAULT 0 CHECK (prompt_tokens >= 0),
|
||||
completion_tokens INTEGER NOT NULL DEFAULT 0 CHECK (completion_tokens>= 0),
|
||||
prompt_tokens INTEGER NOT NULL DEFAULT 0 CHECK (prompt_tokens >= 0),
|
||||
completion_tokens INTEGER NOT NULL DEFAULT 0 CHECK (completion_tokens >= 0),
|
||||
cost REAL NOT NULL DEFAULT 0.0 CHECK (cost >= 0.0),
|
||||
updated_at INTEGER NOT NULL, -- Unix timestamp in milliseconds
|
||||
created_at INTEGER NOT NULL -- Unix timestamp in milliseconds
|
||||
summary TEXT,
|
||||
summarized_at TEXT,
|
||||
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f000Z', 'now')),
|
||||
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f000Z', 'now'))
|
||||
);
|
||||
|
||||
CREATE TRIGGER IF NOT EXISTS update_sessions_updated_at
|
||||
AFTER UPDATE ON sessions
|
||||
BEGIN
|
||||
UPDATE sessions SET updated_at = strftime('%s', 'now')
|
||||
UPDATE sessions SET updated_at = strftime('%Y-%m-%dT%H:%M:%f000Z', 'now')
|
||||
WHERE id = new.id;
|
||||
END;
|
||||
|
||||
|
@ -27,8 +29,9 @@ CREATE TABLE IF NOT EXISTS files (
|
|||
path TEXT NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
version TEXT NOT NULL,
|
||||
created_at INTEGER NOT NULL, -- Unix timestamp in milliseconds
|
||||
updated_at INTEGER NOT NULL, -- Unix timestamp in milliseconds
|
||||
is_new INTEGER DEFAULT 0,
|
||||
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f000Z', 'now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f000Z', 'now')),
|
||||
FOREIGN KEY (session_id) REFERENCES sessions (id) ON DELETE CASCADE,
|
||||
UNIQUE(path, session_id, version)
|
||||
);
|
||||
|
@ -39,7 +42,7 @@ CREATE INDEX IF NOT EXISTS idx_files_path ON files (path);
|
|||
CREATE TRIGGER IF NOT EXISTS update_files_updated_at
|
||||
AFTER UPDATE ON files
|
||||
BEGIN
|
||||
UPDATE files SET updated_at = strftime('%s', 'now')
|
||||
UPDATE files SET updated_at = strftime('%Y-%m-%dT%H:%M:%f000Z', 'now')
|
||||
WHERE id = new.id;
|
||||
END;
|
||||
|
||||
|
@ -50,9 +53,9 @@ CREATE TABLE IF NOT EXISTS messages (
|
|||
role TEXT NOT NULL,
|
||||
parts TEXT NOT NULL default '[]',
|
||||
model TEXT,
|
||||
created_at INTEGER NOT NULL, -- Unix timestamp in milliseconds
|
||||
updated_at INTEGER NOT NULL, -- Unix timestamp in milliseconds
|
||||
finished_at INTEGER, -- Unix timestamp in milliseconds
|
||||
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f000Z', 'now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f000Z', 'now')),
|
||||
finished_at TEXT,
|
||||
FOREIGN KEY (session_id) REFERENCES sessions (id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
|
@ -61,7 +64,7 @@ CREATE INDEX IF NOT EXISTS idx_messages_session_id ON messages (session_id);
|
|||
CREATE TRIGGER IF NOT EXISTS update_messages_updated_at
|
||||
AFTER UPDATE ON messages
|
||||
BEGIN
|
||||
UPDATE messages SET updated_at = strftime('%s', 'now')
|
||||
UPDATE messages SET updated_at = strftime('%Y-%m-%dT%H:%M:%f000Z', 'now')
|
||||
WHERE id = new.id;
|
||||
END;
|
||||
|
||||
|
@ -81,6 +84,28 @@ UPDATE sessions SET
|
|||
WHERE id = old.session_id;
|
||||
END;
|
||||
|
||||
-- Logs
|
||||
CREATE TABLE IF NOT EXISTS logs (
|
||||
id TEXT PRIMARY KEY,
|
||||
session_id TEXT REFERENCES sessions(id) ON DELETE CASCADE,
|
||||
timestamp TEXT NOT NULL,
|
||||
level TEXT NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
attributes TEXT,
|
||||
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f000Z', 'now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%f000Z', 'now'))
|
||||
);
|
||||
|
||||
CREATE INDEX logs_session_id_idx ON logs(session_id);
|
||||
CREATE INDEX logs_timestamp_idx ON logs(timestamp);
|
||||
|
||||
CREATE TRIGGER IF NOT EXISTS update_logs_updated_at
|
||||
AFTER UPDATE ON logs
|
||||
BEGIN
|
||||
UPDATE logs SET updated_at = strftime('%Y-%m-%dT%H:%M:%f000Z', 'now')
|
||||
WHERE id = new.id;
|
||||
END;
|
||||
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
|
@ -88,11 +113,13 @@ END;
|
|||
DROP TRIGGER IF EXISTS update_sessions_updated_at;
|
||||
DROP TRIGGER IF EXISTS update_messages_updated_at;
|
||||
DROP TRIGGER IF EXISTS update_files_updated_at;
|
||||
DROP TRIGGER IF EXISTS update_logs_updated_at;
|
||||
|
||||
DROP TRIGGER IF EXISTS update_session_message_count_on_delete;
|
||||
DROP TRIGGER IF EXISTS update_session_message_count_on_insert;
|
||||
|
||||
DROP TABLE IF EXISTS sessions;
|
||||
DROP TABLE IF EXISTS logs;
|
||||
DROP TABLE IF EXISTS messages;
|
||||
DROP TABLE IF EXISTS files;
|
||||
DROP TABLE IF EXISTS sessions;
|
||||
-- +goose StatementEnd
|
|
@ -9,23 +9,25 @@ import (
|
|||
)
|
||||
|
||||
type File struct {
|
||||
ID string `json:"id"`
|
||||
SessionID string `json:"session_id"`
|
||||
Path string `json:"path"`
|
||||
Content string `json:"content"`
|
||||
Version string `json:"version"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
ID string `json:"id"`
|
||||
SessionID string `json:"session_id"`
|
||||
Path string `json:"path"`
|
||||
Content string `json:"content"`
|
||||
Version string `json:"version"`
|
||||
IsNew sql.NullInt64 `json:"is_new"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
|
||||
type Log struct {
|
||||
ID string `json:"id"`
|
||||
SessionID sql.NullString `json:"session_id"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
Level string `json:"level"`
|
||||
Message string `json:"message"`
|
||||
Attributes sql.NullString `json:"attributes"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
|
@ -34,9 +36,9 @@ type Message struct {
|
|||
Role string `json:"role"`
|
||||
Parts string `json:"parts"`
|
||||
Model sql.NullString `json:"model"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
FinishedAt sql.NullInt64 `json:"finished_at"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
FinishedAt sql.NullString `json:"finished_at"`
|
||||
}
|
||||
|
||||
type Session struct {
|
||||
|
@ -47,8 +49,8 @@ type Session struct {
|
|||
PromptTokens int64 `json:"prompt_tokens"`
|
||||
CompletionTokens int64 `json:"completion_tokens"`
|
||||
Cost float64 `json:"cost"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
Summary sql.NullString `json:"summary"`
|
||||
SummarizedAt sql.NullInt64 `json:"summarized_at"`
|
||||
SummarizedAt sql.NullString `json:"summarized_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
}
|
||||
|
|
|
@ -20,9 +20,7 @@ INSERT INTO sessions (
|
|||
completion_tokens,
|
||||
cost,
|
||||
summary,
|
||||
summarized_at,
|
||||
updated_at,
|
||||
created_at
|
||||
summarized_at
|
||||
) VALUES (
|
||||
?,
|
||||
?,
|
||||
|
@ -32,10 +30,8 @@ INSERT INTO sessions (
|
|||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
strftime('%s', 'now'),
|
||||
strftime('%s', 'now')
|
||||
) RETURNING id, parent_session_id, title, message_count, prompt_tokens, completion_tokens, cost, updated_at, created_at, summary, summarized_at
|
||||
?
|
||||
) RETURNING id, parent_session_id, title, message_count, prompt_tokens, completion_tokens, cost, summary, summarized_at, updated_at, created_at
|
||||
`
|
||||
|
||||
type CreateSessionParams struct {
|
||||
|
@ -47,7 +43,7 @@ type CreateSessionParams struct {
|
|||
CompletionTokens int64 `json:"completion_tokens"`
|
||||
Cost float64 `json:"cost"`
|
||||
Summary sql.NullString `json:"summary"`
|
||||
SummarizedAt sql.NullInt64 `json:"summarized_at"`
|
||||
SummarizedAt sql.NullString `json:"summarized_at"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateSession(ctx context.Context, arg CreateSessionParams) (Session, error) {
|
||||
|
@ -71,10 +67,10 @@ func (q *Queries) CreateSession(ctx context.Context, arg CreateSessionParams) (S
|
|||
&i.PromptTokens,
|
||||
&i.CompletionTokens,
|
||||
&i.Cost,
|
||||
&i.UpdatedAt,
|
||||
&i.CreatedAt,
|
||||
&i.Summary,
|
||||
&i.SummarizedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
@ -90,7 +86,7 @@ func (q *Queries) DeleteSession(ctx context.Context, id string) error {
|
|||
}
|
||||
|
||||
const getSessionByID = `-- name: GetSessionByID :one
|
||||
SELECT id, parent_session_id, title, message_count, prompt_tokens, completion_tokens, cost, updated_at, created_at, summary, summarized_at
|
||||
SELECT id, parent_session_id, title, message_count, prompt_tokens, completion_tokens, cost, summary, summarized_at, updated_at, created_at
|
||||
FROM sessions
|
||||
WHERE id = ? LIMIT 1
|
||||
`
|
||||
|
@ -106,16 +102,16 @@ func (q *Queries) GetSessionByID(ctx context.Context, id string) (Session, error
|
|||
&i.PromptTokens,
|
||||
&i.CompletionTokens,
|
||||
&i.Cost,
|
||||
&i.UpdatedAt,
|
||||
&i.CreatedAt,
|
||||
&i.Summary,
|
||||
&i.SummarizedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const listSessions = `-- name: ListSessions :many
|
||||
SELECT id, parent_session_id, title, message_count, prompt_tokens, completion_tokens, cost, updated_at, created_at, summary, summarized_at
|
||||
SELECT id, parent_session_id, title, message_count, prompt_tokens, completion_tokens, cost, summary, summarized_at, updated_at, created_at
|
||||
FROM sessions
|
||||
WHERE parent_session_id is NULL
|
||||
ORDER BY created_at DESC
|
||||
|
@ -138,10 +134,10 @@ func (q *Queries) ListSessions(ctx context.Context) ([]Session, error) {
|
|||
&i.PromptTokens,
|
||||
&i.CompletionTokens,
|
||||
&i.Cost,
|
||||
&i.UpdatedAt,
|
||||
&i.CreatedAt,
|
||||
&i.Summary,
|
||||
&i.SummarizedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.CreatedAt,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -166,7 +162,7 @@ SET
|
|||
summary = ?,
|
||||
summarized_at = ?
|
||||
WHERE id = ?
|
||||
RETURNING id, parent_session_id, title, message_count, prompt_tokens, completion_tokens, cost, updated_at, created_at, summary, summarized_at
|
||||
RETURNING id, parent_session_id, title, message_count, prompt_tokens, completion_tokens, cost, summary, summarized_at, updated_at, created_at
|
||||
`
|
||||
|
||||
type UpdateSessionParams struct {
|
||||
|
@ -175,7 +171,7 @@ type UpdateSessionParams struct {
|
|||
CompletionTokens int64 `json:"completion_tokens"`
|
||||
Cost float64 `json:"cost"`
|
||||
Summary sql.NullString `json:"summary"`
|
||||
SummarizedAt sql.NullInt64 `json:"summarized_at"`
|
||||
SummarizedAt sql.NullString `json:"summarized_at"`
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
|
@ -198,10 +194,10 @@ func (q *Queries) UpdateSession(ctx context.Context, arg UpdateSessionParams) (S
|
|||
&i.PromptTokens,
|
||||
&i.CompletionTokens,
|
||||
&i.Cost,
|
||||
&i.UpdatedAt,
|
||||
&i.CreatedAt,
|
||||
&i.Summary,
|
||||
&i.SummarizedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.CreatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
|
|
@ -28,11 +28,9 @@ INSERT INTO files (
|
|||
session_id,
|
||||
path,
|
||||
content,
|
||||
version,
|
||||
created_at,
|
||||
updated_at
|
||||
version
|
||||
) VALUES (
|
||||
?, ?, ?, ?, ?, strftime('%s', 'now'), strftime('%s', 'now')
|
||||
?, ?, ?, ?, ?
|
||||
)
|
||||
RETURNING *;
|
||||
|
||||
|
@ -41,7 +39,7 @@ UPDATE files
|
|||
SET
|
||||
content = ?,
|
||||
version = ?,
|
||||
updated_at = strftime('%s', 'now')
|
||||
updated_at = strftime('%Y-%m-%dT%H:%M:%f000Z', 'now')
|
||||
WHERE id = ?
|
||||
RETURNING *;
|
||||
|
||||
|
|
|
@ -5,15 +5,13 @@ INSERT INTO logs (
|
|||
timestamp,
|
||||
level,
|
||||
message,
|
||||
attributes,
|
||||
created_at
|
||||
attributes
|
||||
) VALUES (
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?
|
||||
) RETURNING *;
|
||||
|
||||
|
|
|
@ -21,11 +21,9 @@ INSERT INTO messages (
|
|||
session_id,
|
||||
role,
|
||||
parts,
|
||||
model,
|
||||
created_at,
|
||||
updated_at
|
||||
model
|
||||
) VALUES (
|
||||
?, ?, ?, ?, ?, strftime('%s', 'now'), strftime('%s', 'now')
|
||||
?, ?, ?, ?, ?
|
||||
)
|
||||
RETURNING *;
|
||||
|
||||
|
@ -34,7 +32,7 @@ UPDATE messages
|
|||
SET
|
||||
parts = ?,
|
||||
finished_at = ?,
|
||||
updated_at = strftime('%s', 'now')
|
||||
updated_at = strftime('%Y-%m-%dT%H:%M:%f000Z', 'now')
|
||||
WHERE id = ?;
|
||||
|
||||
|
||||
|
|
|
@ -8,9 +8,7 @@ INSERT INTO sessions (
|
|||
completion_tokens,
|
||||
cost,
|
||||
summary,
|
||||
summarized_at,
|
||||
updated_at,
|
||||
created_at
|
||||
summarized_at
|
||||
) VALUES (
|
||||
?,
|
||||
?,
|
||||
|
@ -20,9 +18,7 @@ INSERT INTO sessions (
|
|||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
strftime('%s', 'now'),
|
||||
strftime('%s', 'now')
|
||||
?
|
||||
) RETURNING *;
|
||||
|
||||
-- name: GetSessionByID :one
|
||||
|
|
|
@ -113,7 +113,13 @@ func (s *service) CreateVersion(ctx context.Context, sessionID, path, content st
|
|||
if b.Version == InitialVersion && a.Version != InitialVersion {
|
||||
return -1
|
||||
}
|
||||
return int(b.CreatedAt - a.CreatedAt) // Fallback to timestamp
|
||||
// Compare timestamps as strings (ISO format sorts correctly)
|
||||
if b.CreatedAt > a.CreatedAt {
|
||||
return 1
|
||||
} else if a.CreatedAt > b.CreatedAt {
|
||||
return -1
|
||||
}
|
||||
return 0 // Equal timestamps
|
||||
})
|
||||
|
||||
latestFile := files[0]
|
||||
|
@ -362,14 +368,27 @@ func (s *service) Subscribe(ctx context.Context) <-chan pubsub.Event[File] {
|
|||
}
|
||||
|
||||
func (s *service) fromDBItem(item db.File) File {
|
||||
// Parse timestamps from ISO strings
|
||||
createdAt, err := time.Parse(time.RFC3339Nano, item.CreatedAt)
|
||||
if err != nil {
|
||||
slog.Error("Failed to parse created_at", "value", item.CreatedAt, "error", err)
|
||||
createdAt = time.Now() // Fallback
|
||||
}
|
||||
|
||||
updatedAt, err := time.Parse(time.RFC3339Nano, item.UpdatedAt)
|
||||
if err != nil {
|
||||
slog.Error("Failed to parse created_at", "value", item.CreatedAt, "error", err)
|
||||
updatedAt = time.Now() // Fallback
|
||||
}
|
||||
|
||||
return File{
|
||||
ID: item.ID,
|
||||
SessionID: item.SessionID,
|
||||
Path: item.Path,
|
||||
Content: item.Content,
|
||||
Version: item.Version,
|
||||
CreatedAt: time.UnixMilli(item.CreatedAt * 1000),
|
||||
UpdatedAt: time.UnixMilli(item.UpdatedAt * 1000),
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -209,7 +209,7 @@ func (a *agent) prepareMessageHistory(ctx context.Context, sessionID string) (se
|
|||
var sessionMessages []message.Message
|
||||
if currentSession.Summary != "" && !currentSession.SummarizedAt.IsZero() {
|
||||
// If summary exists, only fetch messages after the summarization timestamp
|
||||
sessionMessages, err = a.messages.ListAfter(ctx, sessionID, currentSession.SummarizedAt.UnixMilli())
|
||||
sessionMessages, err = a.messages.ListAfter(ctx, sessionID, currentSession.SummarizedAt)
|
||||
if err != nil {
|
||||
return currentSession, nil, fmt.Errorf("failed to list messages after summary: %w", err)
|
||||
}
|
||||
|
|
|
@ -86,11 +86,10 @@ func (s *service) Create(ctx context.Context, timestamp time.Time, level, messag
|
|||
dbLog, err := s.db.CreateLog(ctx, db.CreateLogParams{
|
||||
ID: uuid.New().String(),
|
||||
SessionID: sql.NullString{String: sessionID, Valid: sessionID != ""},
|
||||
Timestamp: timestamp.UnixMilli(),
|
||||
Timestamp: timestamp.UTC().Format(time.RFC3339Nano),
|
||||
Level: level,
|
||||
Message: message,
|
||||
Attributes: attributesJSON,
|
||||
CreatedAt: time.Now().UnixMilli(),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
|
@ -135,10 +134,24 @@ func (s *service) fromDBItem(item db.Log) Log {
|
|||
log := Log{
|
||||
ID: item.ID,
|
||||
SessionID: item.SessionID.String,
|
||||
Timestamp: time.UnixMilli(item.Timestamp),
|
||||
Level: item.Level,
|
||||
Message: item.Message,
|
||||
CreatedAt: time.UnixMilli(item.CreatedAt),
|
||||
}
|
||||
|
||||
// Parse timestamp from ISO string
|
||||
timestamp, err := time.Parse(time.RFC3339Nano, item.Timestamp)
|
||||
if err == nil {
|
||||
log.Timestamp = timestamp
|
||||
} else {
|
||||
log.Timestamp = time.Now() // Fallback
|
||||
}
|
||||
|
||||
// Parse created_at from ISO string
|
||||
createdAt, err := time.Parse(time.RFC3339Nano, item.CreatedAt)
|
||||
if err == nil {
|
||||
log.CreatedAt = createdAt
|
||||
} else {
|
||||
log.CreatedAt = time.Now() // Fallback
|
||||
}
|
||||
|
||||
if item.Attributes.Valid && item.Attributes.String != "" {
|
||||
|
@ -195,7 +208,7 @@ func (sw *slogWriter) Write(p []byte) (n int, err error) {
|
|||
parsedTime, timeErr = time.Parse(time.RFC3339, value)
|
||||
if timeErr != nil {
|
||||
slog.Error("Failed to parse time in slog writer", "value", value, "error", timeErr)
|
||||
timestamp = time.Now()
|
||||
timestamp = time.Now().UTC()
|
||||
hasTimestamp = true
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ type Service interface {
|
|||
Update(ctx context.Context, message Message) (Message, error)
|
||||
Get(ctx context.Context, id string) (Message, error)
|
||||
List(ctx context.Context, sessionID string) ([]Message, error)
|
||||
ListAfter(ctx context.Context, sessionID string, timestampMillis int64) ([]Message, error)
|
||||
ListAfter(ctx context.Context, sessionID string, timestamp time.Time) ([]Message, error)
|
||||
Delete(ctx context.Context, id string) error
|
||||
DeleteSessionMessages(ctx context.Context, sessionID string) error
|
||||
}
|
||||
|
@ -134,12 +134,12 @@ func (s *service) Update(ctx context.Context, message Message) (Message, error)
|
|||
return Message{}, fmt.Errorf("failed to marshal message parts for update: %w", err)
|
||||
}
|
||||
|
||||
var dbFinishedAt sql.NullInt64
|
||||
var dbFinishedAt sql.NullString
|
||||
finishPart := message.FinishPart()
|
||||
if finishPart != nil && !finishPart.Time.IsZero() {
|
||||
dbFinishedAt = sql.NullInt64{
|
||||
Int64: finishPart.Time.UnixMilli(),
|
||||
Valid: true,
|
||||
dbFinishedAt = sql.NullString{
|
||||
String: finishPart.Time.UTC().Format(time.RFC3339Nano),
|
||||
Valid: true,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -199,13 +199,13 @@ func (s *service) List(ctx context.Context, sessionID string) ([]Message, error)
|
|||
return messages, nil
|
||||
}
|
||||
|
||||
func (s *service) ListAfter(ctx context.Context, sessionID string, timestampMillis int64) ([]Message, error) {
|
||||
func (s *service) ListAfter(ctx context.Context, sessionID string, timestamp time.Time) ([]Message, error) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
dbMessages, err := s.db.ListMessagesBySessionAfter(ctx, db.ListMessagesBySessionAfterParams{
|
||||
SessionID: sessionID,
|
||||
CreatedAt: timestampMillis,
|
||||
CreatedAt: timestamp.Format(time.RFC3339Nano),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("db.ListMessagesBySessionAfter: %w", err)
|
||||
|
@ -294,14 +294,27 @@ func (s *service) fromDBItem(item db.Message) (Message, error) {
|
|||
return Message{}, fmt.Errorf("unmarshallParts for message ID %s: %w. Raw parts: %s", item.ID, err, item.Parts)
|
||||
}
|
||||
|
||||
// Parse timestamps from ISO strings
|
||||
createdAt, err := time.Parse(time.RFC3339Nano, item.CreatedAt)
|
||||
if err != nil {
|
||||
slog.Error("Failed to parse created_at", "value", item.CreatedAt, "error", err)
|
||||
createdAt = time.Now() // Fallback
|
||||
}
|
||||
|
||||
updatedAt, err := time.Parse(time.RFC3339Nano, item.UpdatedAt)
|
||||
if err != nil {
|
||||
slog.Error("Failed to parse created_at", "value", item.CreatedAt, "error", err)
|
||||
updatedAt = time.Now() // Fallback
|
||||
}
|
||||
|
||||
msg := Message{
|
||||
ID: item.ID,
|
||||
SessionID: item.SessionID,
|
||||
Role: MessageRole(item.Role),
|
||||
Parts: parts,
|
||||
Model: models.ModelID(item.Model.String),
|
||||
CreatedAt: time.UnixMilli(item.CreatedAt),
|
||||
UpdatedAt: time.UnixMilli(item.UpdatedAt),
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
}
|
||||
|
||||
return msg, nil
|
||||
|
@ -323,8 +336,8 @@ func List(ctx context.Context, sessionID string) ([]Message, error) {
|
|||
return GetService().List(ctx, sessionID)
|
||||
}
|
||||
|
||||
func ListAfter(ctx context.Context, sessionID string, timestampMillis int64) ([]Message, error) {
|
||||
return GetService().ListAfter(ctx, sessionID, timestampMillis)
|
||||
func ListAfter(ctx context.Context, sessionID string, timestamp time.Time) ([]Message, error) {
|
||||
return GetService().ListAfter(ctx, sessionID, timestamp)
|
||||
}
|
||||
|
||||
func Delete(ctx context.Context, id string) error {
|
||||
|
|
|
@ -153,10 +153,6 @@ func (s *service) Update(ctx context.Context, session Session) (Session, error)
|
|||
if session.ID == "" {
|
||||
return Session{}, fmt.Errorf("cannot update session with empty ID")
|
||||
}
|
||||
var summarizedAt sql.NullInt64
|
||||
if !session.SummarizedAt.IsZero() {
|
||||
summarizedAt = sql.NullInt64{Int64: session.SummarizedAt.UnixMilli(), Valid: true}
|
||||
}
|
||||
|
||||
params := db.UpdateSessionParams{
|
||||
ID: session.ID,
|
||||
|
@ -165,7 +161,7 @@ func (s *service) Update(ctx context.Context, session Session) (Session, error)
|
|||
CompletionTokens: session.CompletionTokens,
|
||||
Cost: session.Cost,
|
||||
Summary: sql.NullString{String: session.Summary, Valid: session.Summary != ""},
|
||||
SummarizedAt: summarizedAt,
|
||||
SummarizedAt: sql.NullString{String: session.SummarizedAt.UTC().Format(time.RFC3339Nano), Valid: !session.SummarizedAt.IsZero()},
|
||||
}
|
||||
dbSession, err := s.db.UpdateSession(ctx, params)
|
||||
if err != nil {
|
||||
|
@ -206,9 +202,15 @@ func (s *service) Subscribe(ctx context.Context) <-chan pubsub.Event[Session] {
|
|||
func (s *service) fromDBItem(item db.Session) Session {
|
||||
var summarizedAt time.Time
|
||||
if item.SummarizedAt.Valid {
|
||||
summarizedAt = time.UnixMilli(item.SummarizedAt.Int64)
|
||||
parsedTime, err := time.Parse(time.RFC3339Nano, item.SummarizedAt.String)
|
||||
if err == nil {
|
||||
summarizedAt = parsedTime
|
||||
}
|
||||
}
|
||||
|
||||
createdAt, _ := time.Parse(time.RFC3339Nano, item.CreatedAt)
|
||||
updatedAt, _ := time.Parse(time.RFC3339Nano, item.UpdatedAt)
|
||||
|
||||
return Session{
|
||||
ID: item.ID,
|
||||
ParentSessionID: item.ParentSessionID.String,
|
||||
|
@ -219,8 +221,8 @@ func (s *service) fromDBItem(item db.Session) Session {
|
|||
Cost: item.Cost,
|
||||
Summary: item.Summary.String,
|
||||
SummarizedAt: summarizedAt,
|
||||
CreatedAt: time.UnixMilli(item.CreatedAt),
|
||||
UpdatedAt: time.UnixMilli(item.UpdatedAt),
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -605,7 +605,7 @@ func renderToolMessage(
|
|||
return toolMsg
|
||||
}
|
||||
|
||||
params := renderToolParams(width-2-lipgloss.Width(toolNameText), toolCall)
|
||||
params := renderToolParams(width-1-lipgloss.Width(toolNameText), toolCall)
|
||||
responseContent := ""
|
||||
if response != nil {
|
||||
responseContent = renderToolResponse(toolCall, *response, width-2)
|
||||
|
|
|
@ -159,8 +159,7 @@ func (i *tableCmp) updateRows() {
|
|||
rows := make([]table.Row, 0, len(i.logs))
|
||||
|
||||
for _, log := range i.logs {
|
||||
// Format timestamp as time
|
||||
timeStr := log.Timestamp.Format("15:04:05")
|
||||
timeStr := log.Timestamp.Local().Format("15:04:05")
|
||||
|
||||
// Include ID as hidden first column for selection
|
||||
row := table.Row{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue