Changeset 0.29.0 (#195)

This commit is contained in:
2026-03-17 16:28:47 +00:00
parent 128cbb8174
commit 5d637d3a90
129 changed files with 9418 additions and 3016 deletions

View File

@@ -4,6 +4,7 @@ import (
"context"
"database/sql"
"encoding/json"
"strings"
"time"
"git.gobha.me/xcaliber/chat-switchboard/models"
@@ -137,7 +138,9 @@ func (s *MessageStore) GetPathToRoot(ctx context.Context, messageID string) ([]m
m.parent_id, m.sibling_index, m.participant_type, m.participant_id, m.deleted_at, m.created_at
FROM messages m JOIN path p ON m.id = p.parent_id
)
SELECT * FROM path ORDER BY created_at ASC`, messageID)
SELECT id, channel_id, role, content, model, tokens_used, tool_calls, metadata,
parent_id, sibling_index, participant_type, participant_id, deleted_at, created_at
FROM path ORDER BY created_at ASC`, messageID)
if err != nil {
return nil, err
}
@@ -188,3 +191,120 @@ func scanMessages(rows *sql.Rows) ([]models.Message, error) {
}
return result, rows.Err()
}
// ── CS2 additions (v0.29.0) ─────────────────────────────────────────────
func (s *MessageStore) CountAll(ctx context.Context) (int, error) {
var count int
err := DB.QueryRowContext(ctx, "SELECT COUNT(*) FROM messages").Scan(&count)
return count, err
}
// ── CS5c additions (v0.29.0) ─────────────────────────────────────────────
func (s *MessageStore) SearchInChannel(ctx context.Context, channelID, query, roleFilter string, limit int) ([]store.ChannelSearchResult, error) {
words := strings.Fields(query)
if len(words) == 0 {
return []store.ChannelSearchResult{}, nil
}
q := `SELECT m.id, m.role, SUBSTR(m.content, 1, 300), m.created_at
FROM messages m
WHERE m.channel_id = ?
AND m.deleted_at IS NULL
AND m.role IN ('user', 'assistant')`
args := []interface{}{channelID}
if roleFilter == "user" || roleFilter == "assistant" {
q += " AND m.role = ?"
args = append(args, roleFilter)
}
for _, w := range words {
q += " AND m.content LIKE ?"
args = append(args, "%"+w+"%")
}
q += " ORDER BY m.created_at DESC LIMIT ?"
args = append(args, limit)
rows, err := DB.QueryContext(ctx, q, args...)
if err != nil {
return nil, err
}
defer rows.Close()
results := make([]store.ChannelSearchResult, 0)
for rows.Next() {
var r store.ChannelSearchResult
if err := rows.Scan(&r.MessageID, &r.Role, &r.Excerpt, st(&r.Timestamp)); err != nil {
continue
}
r.Rank = 1.0 // no ranking on SQLite
results = append(results, r)
}
return results, rows.Err()
}
// ── CS7a additions (v0.29.0) ────────────────────────────────────────────
func (s *MessageStore) ListWithSenderInfo(ctx context.Context, channelID string, limit, offset int) ([]store.MessageWithSender, int, error) {
var total int
err := DB.QueryRowContext(ctx,
`SELECT COUNT(*) FROM messages WHERE channel_id = ? AND deleted_at IS NULL`,
channelID).Scan(&total)
if err != nil {
return nil, 0, err
}
rows, err := DB.QueryContext(ctx, `
SELECT m.id, m.channel_id, m.role, m.content, m.model, m.tokens_used, m.parent_id,
m.sibling_index, m.participant_type, m.participant_id,
CASE WHEN m.participant_type = 'user' THEN COALESCE(u.display_name, u.username)
WHEN m.participant_type = 'persona' THEN p.name
ELSE NULL END AS sender_name,
CASE WHEN m.participant_type = 'user' THEN u.avatar_url
WHEN m.participant_type = 'persona' THEN p.avatar
ELSE NULL END AS sender_avatar,
m.created_at
FROM messages m
LEFT JOIN users u ON m.participant_type = 'user' AND m.participant_id = u.id
LEFT JOIN personas p ON m.participant_type = 'persona' AND m.participant_id = p.id
WHERE m.channel_id = ? AND m.deleted_at IS NULL
ORDER BY m.created_at ASC
LIMIT ? OFFSET ?
`, channelID, limit, offset)
if err != nil {
return nil, 0, err
}
defer rows.Close()
results := make([]store.MessageWithSender, 0)
for rows.Next() {
var m store.MessageWithSender
if err := rows.Scan(
&m.ID, &m.ChannelID, &m.Role, &m.Content,
&m.Model, &m.TokensUsed, &m.ParentID,
&m.SiblingIndex, &m.ParticipantType, &m.ParticipantID,
&m.SenderName, &m.SenderAvatar,
&m.CreatedAt,
); err != nil {
return nil, total, err
}
results = append(results, m)
}
return results, total, rows.Err()
}
func (s *MessageStore) GetParentAndRole(ctx context.Context, messageID, channelID string) (*string, string, error) {
var parentID sql.NullString
var role string
err := DB.QueryRowContext(ctx, `
SELECT parent_id, role FROM messages
WHERE id = ? AND channel_id = ? AND deleted_at IS NULL
`, messageID, channelID).Scan(&parentID, &role)
if err != nil {
return nil, "", err
}
return NullableStringPtr(parentID), role, nil
}