chore: establish local platform baseline
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
package retrieval
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"data-terminal-cloud/internal/developmentembedding"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
)
|
||||
|
||||
type Memory struct {
|
||||
MemoryID string `json:"memory_id"`
|
||||
MemoryVersionID string `json:"memory_version_id"`
|
||||
VersionNo int64 `json:"version_no"`
|
||||
Content string `json:"-"`
|
||||
SourceRefs json.RawMessage `json:"source_refs"`
|
||||
Sensitivity string `json:"sensitivity"`
|
||||
Relevance float64 `json:"relevance"`
|
||||
}
|
||||
|
||||
// SelectMemoryForContext fixes the tenant and subject boundary in the SQL
|
||||
// itself. Callers cannot supply a free-form filter. The first production
|
||||
// baseline admits only normal-sensitivity memories into a voice context.
|
||||
func SelectMemoryForContext(ctx context.Context, tx pgx.Tx, tenantID, subjectID, query string, limit int) ([]Memory, error) {
|
||||
queryVector := developmentembedding.Literal(developmentembedding.Vector(query))
|
||||
return SelectMemoryForContextVector(ctx, tx, tenantID, subjectID, queryVector, limit)
|
||||
}
|
||||
|
||||
// SelectMemoryForContextVector uses a vector generated before the caller's
|
||||
// transaction. This keeps production embedding network calls out of the
|
||||
// conversation creation transaction while retaining the SQL tenant, subject,
|
||||
// consent and lifecycle boundaries below.
|
||||
func SelectMemoryForContextVector(ctx context.Context, tx pgx.Tx, tenantID, subjectID, queryVector string, limit int) ([]Memory, error) {
|
||||
if limit < 1 || limit > 10 {
|
||||
return nil, fmt.Errorf("memory retrieval limit must be between 1 and 10")
|
||||
}
|
||||
if queryVector == "" {
|
||||
return nil, fmt.Errorf("memory retrieval vector is required")
|
||||
}
|
||||
rows, err := tx.Query(ctx, `
|
||||
SELECT cm.public_id::text,mv.public_id::text,mv.version_no,mv.content,mv.source_refs,
|
||||
cm.sensitivity,1-(mi.embedding <=> $3::vector) AS relevance
|
||||
FROM memory_index_entry mi
|
||||
JOIN curated_memory cm ON cm.id=mi.memory_id AND cm.tenant_id=mi.tenant_id AND cm.subject_id=mi.subject_id
|
||||
JOIN memory_version mv ON mv.id=mi.memory_version_id AND mv.memory_id=cm.id
|
||||
WHERE mi.tenant_id=$1::uuid AND mi.subject_id=$2::uuid
|
||||
AND mi.index_type='pgvector' AND mi.status='ACTIVE' AND mi.embedding IS NOT NULL
|
||||
AND cm.status='ACTIVE' AND cm.current_version_id=mv.id
|
||||
AND cm.sensitivity='normal'
|
||||
AND (cm.valid_from IS NULL OR cm.valid_from<=now())
|
||||
AND (cm.valid_to IS NULL OR cm.valid_to>now())
|
||||
AND EXISTS(SELECT 1 FROM consent_record cr
|
||||
WHERE cr.tenant_id=cm.tenant_id AND cr.subject_id=cm.subject_id
|
||||
AND cr.consent_type='personalized_memory' AND cr.withdrawn_at IS NULL
|
||||
AND (cr.expires_at IS NULL OR cr.expires_at>now()))
|
||||
ORDER BY mi.embedding <=> $3::vector,mv.confirmed_at DESC,mv.id
|
||||
LIMIT $4`, tenantID, subjectID, queryVector, limit)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("query eligible memory: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
items := make([]Memory, 0, limit)
|
||||
for rows.Next() {
|
||||
var item Memory
|
||||
if err := rows.Scan(&item.MemoryID, &item.MemoryVersionID, &item.VersionNo, &item.Content, &item.SourceRefs, &item.Sensitivity, &item.Relevance); err != nil {
|
||||
return nil, fmt.Errorf("scan eligible memory: %w", err)
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, fmt.Errorf("iterate eligible memory: %w", err)
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
Reference in New Issue
Block a user