mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-08-06 10:21:11 +00:00
Phase 6 — Reasoning token stripping: - ReasoningDecision.StripThinking auto-flags Kimi + DeepSeek-Reasoner - Guard clauses in Anthropic/OpenAI/Codex stream handlers - Usage.ThinkingTokens + RawAssistantContent preserved (billing + tool passback safe) Phase 8 — Per-agent dreaming config: - MemoryConfig.Dreaming JSONB (no migration), resolver callback pattern - Enabled/DebounceMs/Threshold/VerboseLog fields with partial-override merge - ConsolidationDeps gains optional AgentStore Phase 10 — Dreaming weighted scoring: - Migration 000045 adds recall_count/recall_score/last_recalled_at on episodic_summaries - ComputeRecallScore 4-component formula (freq/rel/recency/freshness, 14d half-life) - memory_search fire-and-forget RecordRecall; ListUnpromotedScored in DreamingWorker - Bootstrap-friendly filter: unrecalled entries bypass thresholds - Debounce stamped on filter-empty skip to prevent starvation loop Phase 5 follow-up — last_compaction_at in sessions.metadata JSONB: - v3 PruneStage.CompactMessages and v2 maybeSummarize both stamp timestamp - Zero migration; exported const SessionMetaKeyLastCompactionAt RequiredSchemaVersion: 44 → 45 (PG), SchemaVersion: 12 → 13 (SQLite). 27 new tests; builds pass under PG and sqliteonly tags.
165 lines
4.8 KiB
Go
165 lines
4.8 KiB
Go
package pg
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/lib/pq"
|
|
"github.com/nextlevelbuilder/goclaw/internal/store"
|
|
)
|
|
|
|
// documentInfoRow is an sqlx scan struct for memory_documents SELECT queries.
|
|
// Handles TIMESTAMPTZ→int64 (UnixMilli) and nullable user_id conversion.
|
|
type documentInfoRow struct {
|
|
AgentID string `db:"agent_id"`
|
|
Path string `db:"path"`
|
|
Hash string `db:"hash"`
|
|
UserID *string `db:"user_id"`
|
|
UpdatedAt time.Time `db:"updated_at"`
|
|
}
|
|
|
|
func (r *documentInfoRow) toDocumentInfo() store.DocumentInfo {
|
|
info := store.DocumentInfo{
|
|
AgentID: r.AgentID,
|
|
Path: r.Path,
|
|
Hash: r.Hash,
|
|
UpdatedAt: r.UpdatedAt.UnixMilli(),
|
|
}
|
|
if r.UserID != nil {
|
|
info.UserID = *r.UserID
|
|
}
|
|
return info
|
|
}
|
|
|
|
// documentDetailRow is an sqlx scan struct for the GetDocumentDetail query.
|
|
type documentDetailRow struct {
|
|
Path string `db:"path"`
|
|
Content string `db:"content"`
|
|
Hash string `db:"hash"`
|
|
UserID *string `db:"user_id"`
|
|
CreatedAt time.Time `db:"created_at"`
|
|
UpdatedAt time.Time `db:"updated_at"`
|
|
ChunkCount int `db:"chunk_count"`
|
|
EmbeddedCount int `db:"embedded_count"`
|
|
}
|
|
|
|
func (r *documentDetailRow) toDocumentDetail() store.DocumentDetail {
|
|
d := store.DocumentDetail{
|
|
Path: r.Path,
|
|
Content: r.Content,
|
|
Hash: r.Hash,
|
|
ChunkCount: r.ChunkCount,
|
|
EmbeddedCount: r.EmbeddedCount,
|
|
CreatedAt: r.CreatedAt.UnixMilli(),
|
|
UpdatedAt: r.UpdatedAt.UnixMilli(),
|
|
}
|
|
if r.UserID != nil {
|
|
d.UserID = *r.UserID
|
|
}
|
|
return d
|
|
}
|
|
|
|
// chunkInfoRow is an sqlx scan struct for memory_chunks SELECT queries.
|
|
// All fields are directly compatible with store.ChunkInfo.
|
|
type chunkInfoRow struct {
|
|
ID string `db:"id"`
|
|
StartLine int `db:"start_line"`
|
|
EndLine int `db:"end_line"`
|
|
TextPreview string `db:"text_preview"`
|
|
HasEmbedding bool `db:"has_embedding"`
|
|
}
|
|
|
|
func (r *chunkInfoRow) toChunkInfo() store.ChunkInfo {
|
|
return store.ChunkInfo{
|
|
ID: r.ID,
|
|
StartLine: r.StartLine,
|
|
EndLine: r.EndLine,
|
|
TextPreview: r.TextPreview,
|
|
HasEmbedding: r.HasEmbedding,
|
|
}
|
|
}
|
|
|
|
// scoredChunkRow is an sqlx scan struct for ftsSearch/vectorSearch queries in memory_search.go.
|
|
type scoredChunkRow struct {
|
|
Path string `db:"path"`
|
|
StartLine int `db:"start_line"`
|
|
EndLine int `db:"end_line"`
|
|
Text string `db:"text"`
|
|
UserID *string `db:"user_id"`
|
|
Score float64 `db:"score"`
|
|
}
|
|
|
|
func (r *scoredChunkRow) toScoredChunk() scoredChunk {
|
|
return scoredChunk{
|
|
Path: r.Path,
|
|
StartLine: r.StartLine,
|
|
EndLine: r.EndLine,
|
|
Text: r.Text,
|
|
Score: r.Score,
|
|
UserID: r.UserID,
|
|
}
|
|
}
|
|
|
|
// episodicSummaryRow is an sqlx scan struct for episodic_summaries SELECT queries.
|
|
// Handles TEXT[] key_topics via pq.StringArray.
|
|
type episodicSummaryRow struct {
|
|
ID string `db:"id"`
|
|
TenantID string `db:"tenant_id"`
|
|
AgentID string `db:"agent_id"`
|
|
UserID string `db:"user_id"`
|
|
SessionKey string `db:"session_key"`
|
|
Summary string `db:"summary"`
|
|
KeyTopics pq.StringArray `db:"key_topics"`
|
|
TurnCount int `db:"turn_count"`
|
|
TokenCount int `db:"token_count"`
|
|
L0Abstract string `db:"l0_abstract"`
|
|
SourceID string `db:"source_id"`
|
|
SourceType string `db:"source_type"`
|
|
CreatedAt time.Time `db:"created_at"`
|
|
ExpiresAt *time.Time `db:"expires_at"`
|
|
RecallCount int `db:"recall_count"`
|
|
RecallScore float64 `db:"recall_score"`
|
|
LastRecalledAt *time.Time `db:"last_recalled_at"`
|
|
}
|
|
|
|
func (r *episodicSummaryRow) toEpisodicSummary() store.EpisodicSummary {
|
|
ep := store.EpisodicSummary{
|
|
UserID: r.UserID,
|
|
SessionKey: r.SessionKey,
|
|
Summary: r.Summary,
|
|
TurnCount: r.TurnCount,
|
|
TokenCount: r.TokenCount,
|
|
L0Abstract: r.L0Abstract,
|
|
SourceID: r.SourceID,
|
|
SourceType: r.SourceType,
|
|
CreatedAt: r.CreatedAt,
|
|
ExpiresAt: r.ExpiresAt,
|
|
RecallCount: r.RecallCount,
|
|
RecallScore: r.RecallScore,
|
|
LastRecalledAt: r.LastRecalledAt,
|
|
}
|
|
_ = ep.ID.Scan(r.ID)
|
|
_ = ep.TenantID.Scan(r.TenantID)
|
|
_ = ep.AgentID.Scan(r.AgentID)
|
|
ep.KeyTopics = []string(r.KeyTopics)
|
|
return ep
|
|
}
|
|
|
|
// episodicScoredRow is an sqlx scan struct for ftsSearch/vectorSearch in episodic_search.go.
|
|
type episodicScoredRow struct {
|
|
ID string `db:"id"`
|
|
SessionKey string `db:"session_key"`
|
|
L0Abstract string `db:"l0_abstract"`
|
|
Score float64 `db:"score"`
|
|
CreatedAt time.Time `db:"created_at"`
|
|
}
|
|
|
|
func (r *episodicScoredRow) toEpisodicScored() episodicScored {
|
|
return episodicScored{
|
|
id: r.ID,
|
|
sessionKey: r.SessionKey,
|
|
l0: r.L0Abstract,
|
|
score: r.Score,
|
|
createdAt: r.CreatedAt,
|
|
}
|
|
}
|