67 lines
1.8 KiB
Go
67 lines
1.8 KiB
Go
// Package session enthält das Baum-Modell der Konversation (E-03) und dessen
|
|
// Persistenz als Append-only JSONL (Abschnitt 4.5).
|
|
package session
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/oklog/ulid/v2"
|
|
|
|
"nub/internal/llm"
|
|
)
|
|
|
|
type NodeKind string
|
|
|
|
const (
|
|
NodeMessage NodeKind = "message"
|
|
NodeSummary NodeKind = "summary"
|
|
)
|
|
|
|
type Node struct {
|
|
ID string `json:"id"`
|
|
ParentID string `json:"parent_id"` // "" = Wurzel
|
|
Kind NodeKind `json:"kind"`
|
|
Message llm.Message `json:"message"`
|
|
Replaces []string `json:"replaces,omitempty"` // nur bei NodeSummary
|
|
Meta NodeMeta `json:"meta"`
|
|
}
|
|
|
|
type NodeMeta struct {
|
|
Model string `json:"model,omitempty"`
|
|
Usage llm.Usage `json:"usage"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
Label string `json:"label,omitempty"`
|
|
Bookmark bool `json:"bookmark,omitempty"`
|
|
}
|
|
|
|
// NewNode erzeugt einen neuen Message-Node mit frischer ULID. parentID ist
|
|
// typischerweise der aktuelle Head.
|
|
func NewNode(parentID string, msg llm.Message, meta NodeMeta) *Node {
|
|
if meta.CreatedAt.IsZero() {
|
|
meta.CreatedAt = time.Now()
|
|
}
|
|
return &Node{
|
|
ID: ulid.Make().String(),
|
|
ParentID: parentID,
|
|
Kind: NodeMessage,
|
|
Message: msg,
|
|
Meta: meta,
|
|
}
|
|
}
|
|
|
|
// NewSummaryNode erzeugt einen Summary-Node, der die Nodes in replaces aus
|
|
// dem aktiven Kontext ersetzt (E-08). Die ersetzten Nodes bleiben im Baum
|
|
// erhalten — kein destruktives Löschen, Rewind auf sie funktioniert weiter.
|
|
func NewSummaryNode(parentID, text string, replaces []string, meta NodeMeta) *Node {
|
|
if meta.CreatedAt.IsZero() {
|
|
meta.CreatedAt = time.Now()
|
|
}
|
|
return &Node{
|
|
ID: ulid.Make().String(),
|
|
ParentID: parentID,
|
|
Kind: NodeSummary,
|
|
Message: llm.Message{Role: llm.RoleUser, Content: []llm.Block{{Kind: llm.KindText, Text: text}}},
|
|
Replaces: replaces,
|
|
Meta: meta,
|
|
}
|
|
}
|