ci / verify (push) Canceled after 0s
Add customer and operator login, BLE provisioning, device fleet controls, call memory, memoir generation, and refreshed family and maintenance portals. Include API contracts, migrations, firmware sources, tests, assets, and deployment documentation.
88 lines
3.9 KiB
Go
88 lines
3.9 KiB
Go
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) {
|
|
return selectMemory(ctx, tx, tenantID, subjectID, queryVector, limit, false)
|
|
}
|
|
|
|
// RESPOND_ONLY and closed items can answer an explicit question, but cannot
|
|
// seed a proactive greeting or follow-up prompt.
|
|
func SelectMemoryForUserQueryVector(ctx context.Context, tx pgx.Tx, tenantID, subjectID, queryVector string, limit int) ([]Memory, error) {
|
|
return selectMemory(ctx, tx, tenantID, subjectID, queryVector, limit, true)
|
|
}
|
|
func selectMemory(ctx context.Context, tx pgx.Tx, tenantID, subjectID, queryVector string, limit int, responding bool) ([]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.proactive_policy<>'NEVER' AND ($5 OR cm.proactive_policy='ALLOWED')
|
|
AND ($5 OR cm.memory_type<>'OPEN_ITEM' OR cm.item_status='OPEN')
|
|
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, responding)
|
|
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
|
|
}
|