db

package
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 7, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package db es la capa de persistencia de nem: modelos GORM sobre SQLite (glebarez/sqlite, Go puro, sin cgo) más una capa FTS5 en SQL crudo para búsqueda full-text con ranking BM25.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Chat

type Chat struct {
	ID          string `gorm:"primaryKey"`
	Title       string
	Source      string `gorm:"index"` // "codex" | "claude" | "manual"
	CreatedAt   int64  // unix seconds, autocompletado por GORM
	SessionPath string

	Messages []Message `gorm:"foreignKey:ChatID;constraint:OnDelete:CASCADE"`
}

Chat representa una conversación ingestada desde un agente (codex, claude) o creada manualmente. Es el contenedor de mensajes y commits.

type Commit

type Commit struct {
	Hash      string `gorm:"primaryKey"`
	ChatID    string `gorm:"index"`
	Branch    string `gorm:"default:main"`
	Message   string // mensaje del commit, escrito por el agente o el humano
	MsgFrom   string // id del primer mensaje del rango
	MsgTo     string // id del último mensaje del rango
	Snapshot  string // JSON con el texto copiado de los mensajes (inmutable)
	CreatedAt int64
}

Commit es un snapshot INMUTABLE de un rango de mensajes. Copia el texto en Snapshot (JSON) al momento de commitear, de modo que reingestar o editar mensajes nunca altera lo que un commit ya capturó: "tu agente olvida, nem no".

type Config

type Config struct {
	// contains filtered or unexported fields
}

Config contiene la configuración para abrir un Store.

type Embedding added in v0.2.0

type Embedding struct {
	NodeID string `gorm:"primaryKey"`
	Dim    int
	Vec    []byte // float32 little-endian (ver internal/embed.Encode)
}

Embedding es el vector (opcional) de un nodo del índice, guardado como BLOB float32. Capa semántica apagada por default; se llena con `nem index` cuando hay un backend de embeddings configurado.

type Memory

type Memory struct {
	ID         string `gorm:"primaryKey"`
	ChatID     string `gorm:"index"`
	Content    string
	CommitHash string // commit que respalda este recuerdo (evidencia)
	CreatedAt  int64
	UpdatedAt  int64
}

Memory es la capa MUTABLE sobre el registro inmutable: un hecho/decisión destilado que el agente lee al empezar una sesión. Puede actualizarse, y referencia el commit que lo respalda como evidencia.

type Message

type Message struct {
	ID         string `gorm:"primaryKey"`
	ChatID     string `gorm:"index"`
	Role       string // "user" | "assistant" | "tool" | "system"
	Content    string
	Timestamp  int64
	TokenCount int
	Seq        int64 `gorm:"index"` // orden dentro del chat
}

Message es un turno individual dentro de un chat. El campo Seq da orden determinístico dentro del chat (los timestamps pueden colisionar).

type Node added in v0.2.0

type Node struct {
	ID           string `gorm:"primaryKey"`
	ParentID     string `gorm:"index"` // padre en el árbol ("" para root)
	Kind         string `gorm:"index"` // root|project|chat|commit|segment
	ChatID       string `gorm:"index"` // chat dueño (chat/commit/segment)
	Title        string //
	Summary      string // lo que el agente lee para navegar
	MsgFromSeq   int64  // rango cubierto (drill-down)
	MsgToSeq     int64  //
	CommitHash   string // si Kind==commit
	CreatedAt    int64  `gorm:"index"` // tiempo de la fuente (orden temporal); se setea explícito
	Tokens       int    // tamaño aprox. del contenido (para presupuestar)
	Superseded   bool   // la decisión fue reemplazada por una posterior
	SupersededBy string // id del nodo/commit que la reemplaza
	Pinned       bool   `gorm:"index"` // resumen escrito por el agente/humano; `index` nunca lo auto-pisa (capa mutable sobre los commits inmutables)

	// Duraciones (capa temporal): tiempo activo real vs span de calendario, para
	// que el agente calibre estimaciones. Se computan en `index` desde los
	// timestamps de los mensajes (ver internal/timing).
	ActiveSecs int64 // tiempo activo (segundos), modelo role-aware
	WallSecs   int64 // span de calendario (último - primer mensaje), segundos
	Sessions   int   // nº de "sesiones" (sentadas separadas por huecos largos)
	LastActive int64 // unix del último mensaje (para recencia)
}

Node es un nodo del árbol de índice (estilo PageIndex) que el agente navega: root → project → chat → commit (→ segment). El Summary es lo que el agente lee para decidir en qué rama bajar; el rango [MsgFromSeq, MsgToSeq] permite el drill-down al contenido real. Es el equivalente a una "tabla de contenidos" de todo el historial, sin embeddings.

type NodeHit added in v0.2.0

type NodeHit struct {
	Node
	Score float64
}

NodeHit es un resultado de búsqueda sobre el árbol: el nodo más su score BM25.

type Option

type Option func(*Config) error

Option configura la apertura de un Store.

func WithPath

func WithPath(path string) Option

WithPath fija la ruta del archivo SQLite. Usar ":memory:" para tests.

func WithVerbose

func WithVerbose(verbose bool) Option

WithVerbose habilita el logging de queries de GORM (debug).

type SearchHit

type SearchHit struct {
	Message
	ChatTitle  string
	ChatSource string
	Score      float64
}

SearchHit es un resultado de búsqueda: el mensaje más metadata del chat y el score BM25 (menor = más relevante).

type Staging

type Staging struct {
	ID        string `gorm:"primaryKey"`
	ChatID    string `gorm:"index;uniqueIndex:idx_staging_chat_msg"`
	MsgID     string `gorm:"uniqueIndex:idx_staging_chat_msg"`
	Seq       int64  // copia del Seq del mensaje para ordenar el rango
	CreatedAt int64
}

Staging es el index git-like: los mensajes marcados con `nem add` que esperan ser commiteados. Una fila por mensaje staged del chat activo.

type Stamp added in v0.3.0

type Stamp struct {
	Role      string
	Timestamp int64
}

Stamp es el par (rol, timestamp) de un mensaje, para medir duraciones sin cargar el contenido completo.

type Store

type Store interface {
	// Migrate aplica el esquema (modelos + FTS5). Es idempotente.
	Migrate() error
	// Close libera la conexión subyacente.
	Close() error

	// UpsertChat inserta el chat o actualiza su metadata si ya existe.
	UpsertChat(chat *Chat) error
	// InsertMessages inserta mensajes ignorando los que ya existen (por id).
	// Devuelve cuántos se insertaron realmente (idempotente).
	InsertMessages(msgs []Message) (int64, error)
	// CountMessages cuenta los mensajes de un chat.
	CountMessages(chatID string) (int64, error)
	// LastMessages devuelve los últimos n mensajes del chat por orden Seq,
	// filtrando por roles (vacío/nil = todos). El filtro se aplica ANTES del
	// límite: "los últimos n de los roles elegidos".
	LastMessages(chatID string, n int, roles []string) ([]Message, error)
	// GetChat devuelve un chat por id (nil, nil si no existe).
	GetChat(id string) (*Chat, error)
	// ListChats devuelve todos los chats (para resolver scopes).
	ListChats() ([]Chat, error)
	// MessageByID devuelve un mensaje por id dentro de un chat.
	MessageByID(chatID, msgID string) (*Message, error)
	// MessagesBySeqRange devuelve los mensajes del chat con Seq en [from,to],
	// filtrando por roles (vacío/nil = todos).
	MessagesBySeqRange(chatID string, fromSeq, toSeq int64, roles []string) ([]Message, error)
	// MessageStamps devuelve los (rol, timestamp) válidos del chat en orden de Seq
	// (para medir duración activa sin cargar el contenido).
	MessageStamps(chatID string) ([]Stamp, error)
	// SearchMessages busca full-text (FTS5/BM25) y devuelve los mejores hits.
	// roles filtra por rol de mensaje; chatIDs limita a esos chats (scope).
	// Ambos vacíos/nil = sin filtro.
	SearchMessages(query string, limit int, roles, chatIDs []string) ([]SearchHit, error)

	// StageMessages agrega mensajes al staging del chat (idempotente por msg).
	// Devuelve cuántos quedaron staged nuevos.
	StageMessages(chatID string, msgs []Message) (int64, error)
	// StagedMessages devuelve los mensajes staged del chat, ordenados por Seq.
	StagedMessages(chatID string) ([]Message, error)
	// CountStaged cuenta los mensajes staged del chat.
	CountStaged(chatID string) (int64, error)
	// ClearStaging vacía el staging del chat.
	ClearStaging(chatID string) error

	// CreateCommit persiste un commit inmutable.
	CreateCommit(c *Commit) error
	// HeadCommit devuelve el commit más reciente del chat (nil si no hay).
	HeadCommit(chatID string) (*Commit, error)
	// GetCommit devuelve un commit por hash (acepta prefijo único).
	GetCommit(hash string) (*Commit, error)
	// ListCommits lista los commits del chat, del más nuevo al más viejo.
	ListCommits(chatID string) ([]Commit, error)
	// ListAllCommits lista todos los commits (para exportar en sync).
	ListAllCommits() ([]Commit, error)

	// --- árbol de índice (nodes) ---
	// ClearNodes borra todo el árbol (para un rebuild completo de `nem index`).
	ClearNodes() error
	// UpsertNodes inserta o actualiza nodos por id. Devuelve cuántos escribió.
	UpsertNodes(nodes []Node) (int64, error)
	// GetNode devuelve un nodo por id (nil, nil si no existe).
	GetNode(id string) (*Node, error)
	// SetNodeSummary reescribe el resumen de un nodo y lo marca Pinned (escrito a
	// mano por el agente/humano), de modo que `nem index` no lo regenere.
	SetNodeSummary(id, summary string) error
	// PinnedNodes devuelve los nodos con resumen Pinned (para respetarlos en Build).
	PinnedNodes() ([]Node, error)
	// ChildNodes devuelve los hijos directos de un nodo (orden por CreatedAt).
	ChildNodes(parentID string) ([]Node, error)
	// RootNodes devuelve los nodos raíz (ParentID == "").
	RootNodes() ([]Node, error)
	// CountNodes cuenta los nodos del árbol.
	CountNodes() (int64, error)
	// SearchNodes busca full-text (FTS5/BM25) sobre title+summary de los nodos.
	// chatIDs limita a esos chats (scope); vacío/nil = sin filtro.
	SearchNodes(query string, limit int, chatIDs []string) ([]NodeHit, error)
	// CommitNodes devuelve los nodos de commit de los chats dados, ordenados por
	// CreatedAt ascendente (para la vista temporal `nem timeline`). chatIDs
	// vacío/nil = todos.
	CommitNodes(chatIDs []string) ([]Node, error)

	// --- embeddings (capa opcional) ---
	// ClearEmbeddings borra todos los vectores.
	ClearEmbeddings() error
	// UpsertEmbeddings inserta/actualiza vectores por NodeID.
	UpsertEmbeddings(embs []Embedding) (int64, error)
	// AllEmbeddings devuelve todos los vectores guardados.
	AllEmbeddings() ([]Embedding, error)
}

Store es la interface de acceso a datos de nem. Toda la app depende de esta abstracción, no de GORM directamente, para que los comandos sean testeables con un mock.

func New

func New(options ...Option) (Store, error)

New abre (o crea) el Store en la ruta configurada y aplica la migración. Devuelve error si falta la ruta o si la conexión/migración fallan.

Ejemplo:

s, err := db.New(db.WithPath("/home/me/.nem/nem.db"))
if err != nil {
    log.Fatal(err)
}
defer s.Close()

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL