spine-framework 0.3.108 → 0.3.109
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.framework/functions/_shared/agent-runner.ts +139 -70
- package/.framework/functions/_shared/pipeline-runner.ts +31 -1
- package/.framework/functions/ai-interactions.ts +240 -0
- package/.framework/functions/embeddings.ts +426 -10
- package/.framework/functions/eval-harness.ts +353 -0
- package/.framework/functions/observability.ts +146 -1
- package/.framework/migrations/027_embedding_lifecycle.sql +176 -0
- package/package.json +2 -1
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
-- Migration 027: Embedding lifecycle — approved_at, revoked_at, content_hash
|
|
2
|
+
--
|
|
3
|
+
-- Adds three columns to the embeddings table so that:
|
|
4
|
+
-- 1. Only approved embeddings are returned by match_embeddings (hard filter, not prompt-based).
|
|
5
|
+
-- 2. Embeddings are invalidated automatically when their source item is unpublished or deleted.
|
|
6
|
+
-- 3. Re-embedding is skipped when content has not changed (content_hash deduplication).
|
|
7
|
+
--
|
|
8
|
+
-- Adds filter_item_types parameter to match_embeddings RPC so agents can restrict
|
|
9
|
+
-- retrieval to specific content types (kb_article, code_chunk, db_schema, etc.) at
|
|
10
|
+
-- the DB layer rather than relying on post-processing.
|
|
11
|
+
--
|
|
12
|
+
-- Adds helper functions revoke_embeddings_for_item and approve_embeddings_for_item
|
|
13
|
+
-- called by the items_embedding_sync trigger.
|
|
14
|
+
--
|
|
15
|
+
-- IMPORTANT: approved_at defaults to now() so all existing embeddings are treated
|
|
16
|
+
-- as approved. revoked_at defaults to NULL (not revoked).
|
|
17
|
+
|
|
18
|
+
-- ============================================
|
|
19
|
+
-- COLUMNS
|
|
20
|
+
-- ============================================
|
|
21
|
+
|
|
22
|
+
ALTER TABLE public.embeddings
|
|
23
|
+
ADD COLUMN IF NOT EXISTS approved_at timestamptz DEFAULT now(),
|
|
24
|
+
ADD COLUMN IF NOT EXISTS revoked_at timestamptz DEFAULT NULL,
|
|
25
|
+
ADD COLUMN IF NOT EXISTS content_hash text DEFAULT NULL;
|
|
26
|
+
|
|
27
|
+
-- Partial index for fast active-embedding lookups (approved and not revoked)
|
|
28
|
+
CREATE INDEX IF NOT EXISTS embeddings_active_idx
|
|
29
|
+
ON public.embeddings (account_id, document_id)
|
|
30
|
+
WHERE approved_at IS NOT NULL AND revoked_at IS NULL;
|
|
31
|
+
|
|
32
|
+
-- ============================================
|
|
33
|
+
-- HELPER FUNCTIONS
|
|
34
|
+
-- ============================================
|
|
35
|
+
|
|
36
|
+
-- Revoke all embeddings for a given item (called when item leaves published state)
|
|
37
|
+
CREATE OR REPLACE FUNCTION public.revoke_embeddings_for_item(p_item_id uuid)
|
|
38
|
+
RETURNS integer
|
|
39
|
+
LANGUAGE plpgsql
|
|
40
|
+
SECURITY DEFINER
|
|
41
|
+
SET search_path = public
|
|
42
|
+
AS $$
|
|
43
|
+
DECLARE
|
|
44
|
+
v_count integer;
|
|
45
|
+
BEGIN
|
|
46
|
+
UPDATE public.embeddings
|
|
47
|
+
SET revoked_at = now()
|
|
48
|
+
WHERE document_id = p_item_id::text
|
|
49
|
+
AND revoked_at IS NULL;
|
|
50
|
+
|
|
51
|
+
GET DIAGNOSTICS v_count = ROW_COUNT;
|
|
52
|
+
RETURN v_count;
|
|
53
|
+
END;
|
|
54
|
+
$$;
|
|
55
|
+
|
|
56
|
+
-- Approve all embeddings for a given item (called when item re-enters published state)
|
|
57
|
+
CREATE OR REPLACE FUNCTION public.approve_embeddings_for_item(p_item_id uuid)
|
|
58
|
+
RETURNS integer
|
|
59
|
+
LANGUAGE plpgsql
|
|
60
|
+
SECURITY DEFINER
|
|
61
|
+
SET search_path = public
|
|
62
|
+
AS $$
|
|
63
|
+
DECLARE
|
|
64
|
+
v_count integer;
|
|
65
|
+
BEGIN
|
|
66
|
+
UPDATE public.embeddings
|
|
67
|
+
SET approved_at = now(),
|
|
68
|
+
revoked_at = NULL
|
|
69
|
+
WHERE document_id = p_item_id::text;
|
|
70
|
+
|
|
71
|
+
GET DIAGNOSTICS v_count = ROW_COUNT;
|
|
72
|
+
RETURN v_count;
|
|
73
|
+
END;
|
|
74
|
+
$$;
|
|
75
|
+
|
|
76
|
+
GRANT EXECUTE ON FUNCTION public.revoke_embeddings_for_item(uuid) TO service_role;
|
|
77
|
+
GRANT EXECUTE ON FUNCTION public.approve_embeddings_for_item(uuid) TO service_role;
|
|
78
|
+
|
|
79
|
+
-- ============================================
|
|
80
|
+
-- ITEMS → EMBEDDINGS SYNC TRIGGER
|
|
81
|
+
-- ============================================
|
|
82
|
+
-- When an item's status changes, keep its embeddings in sync automatically.
|
|
83
|
+
-- published → approve (clear revoked_at, set approved_at)
|
|
84
|
+
-- anything → revoke (set revoked_at, leave approved_at intact for audit)
|
|
85
|
+
|
|
86
|
+
CREATE OR REPLACE FUNCTION public.sync_item_embeddings()
|
|
87
|
+
RETURNS TRIGGER
|
|
88
|
+
LANGUAGE plpgsql
|
|
89
|
+
SECURITY DEFINER
|
|
90
|
+
SET search_path = public
|
|
91
|
+
AS $$
|
|
92
|
+
BEGIN
|
|
93
|
+
-- Only act on status changes
|
|
94
|
+
IF OLD.status IS NOT DISTINCT FROM NEW.status THEN
|
|
95
|
+
RETURN NEW;
|
|
96
|
+
END IF;
|
|
97
|
+
|
|
98
|
+
IF NEW.status = 'published' THEN
|
|
99
|
+
PERFORM public.approve_embeddings_for_item(NEW.id);
|
|
100
|
+
ELSE
|
|
101
|
+
PERFORM public.revoke_embeddings_for_item(NEW.id);
|
|
102
|
+
END IF;
|
|
103
|
+
|
|
104
|
+
RETURN NEW;
|
|
105
|
+
END;
|
|
106
|
+
$$;
|
|
107
|
+
|
|
108
|
+
DROP TRIGGER IF EXISTS items_embedding_sync ON public.items;
|
|
109
|
+
CREATE TRIGGER items_embedding_sync
|
|
110
|
+
AFTER UPDATE OF status ON public.items
|
|
111
|
+
FOR EACH ROW
|
|
112
|
+
EXECUTE FUNCTION public.sync_item_embeddings();
|
|
113
|
+
|
|
114
|
+
-- ============================================
|
|
115
|
+
-- UPDATED match_embeddings RPC
|
|
116
|
+
-- ============================================
|
|
117
|
+
-- Adds filter_item_types parameter and hard approved_at / revoked_at filter.
|
|
118
|
+
-- Backward compatible: existing callers that omit filter_item_types get the same
|
|
119
|
+
-- behaviour as before, except that revoked / unapproved embeddings are now excluded.
|
|
120
|
+
|
|
121
|
+
CREATE OR REPLACE FUNCTION public.match_embeddings(
|
|
122
|
+
query_embedding vector(1536),
|
|
123
|
+
match_count int DEFAULT 10,
|
|
124
|
+
filter_account_ids uuid[] DEFAULT NULL,
|
|
125
|
+
filter_vector_type text DEFAULT NULL,
|
|
126
|
+
similarity_threshold float DEFAULT 0.15,
|
|
127
|
+
filter_item_types text[] DEFAULT NULL
|
|
128
|
+
)
|
|
129
|
+
RETURNS TABLE (
|
|
130
|
+
id uuid,
|
|
131
|
+
document_id text,
|
|
132
|
+
content text,
|
|
133
|
+
metadata jsonb,
|
|
134
|
+
account_id uuid,
|
|
135
|
+
similarity float
|
|
136
|
+
)
|
|
137
|
+
LANGUAGE plpgsql
|
|
138
|
+
SECURITY DEFINER
|
|
139
|
+
SET search_path = public
|
|
140
|
+
AS $$
|
|
141
|
+
BEGIN
|
|
142
|
+
RETURN QUERY
|
|
143
|
+
SELECT
|
|
144
|
+
e.id,
|
|
145
|
+
e.document_id,
|
|
146
|
+
e.content,
|
|
147
|
+
e.metadata,
|
|
148
|
+
e.account_id,
|
|
149
|
+
1 - (e.embedding <=> query_embedding) AS similarity
|
|
150
|
+
FROM public.embeddings e
|
|
151
|
+
WHERE
|
|
152
|
+
-- ── Hard lifecycle filter — never negotiable ──────────────────────────
|
|
153
|
+
e.approved_at IS NOT NULL
|
|
154
|
+
AND e.revoked_at IS NULL
|
|
155
|
+
|
|
156
|
+
-- ── Account scope (NULL = no account filter — platform-wide search) ───
|
|
157
|
+
AND (filter_account_ids IS NULL OR e.account_id = ANY(filter_account_ids))
|
|
158
|
+
|
|
159
|
+
-- ── Vector type (NULL = all types) ────────────────────────────────────
|
|
160
|
+
AND (filter_vector_type IS NULL OR e.metadata->>'vector_type' = filter_vector_type)
|
|
161
|
+
|
|
162
|
+
-- ── Item type (NULL = all item types) ─────────────────────────────────
|
|
163
|
+
AND (filter_item_types IS NULL OR e.metadata->>'item_type' = ANY(filter_item_types))
|
|
164
|
+
|
|
165
|
+
-- ── Similarity gate ───────────────────────────────────────────────────
|
|
166
|
+
AND (1 - (e.embedding <=> query_embedding)) >= similarity_threshold
|
|
167
|
+
|
|
168
|
+
ORDER BY e.embedding <=> query_embedding
|
|
169
|
+
LIMIT match_count;
|
|
170
|
+
END;
|
|
171
|
+
$$;
|
|
172
|
+
|
|
173
|
+
-- Re-grant execute to all roles (replace = drop+create resets grants)
|
|
174
|
+
GRANT EXECUTE ON FUNCTION public.match_embeddings(vector, int, uuid[], text, float, text[]) TO authenticated;
|
|
175
|
+
GRANT EXECUTE ON FUNCTION public.match_embeddings(vector, int, uuid[], text, float, text[]) TO service_role;
|
|
176
|
+
GRANT EXECUTE ON FUNCTION public.match_embeddings(vector, int, uuid[], text, float, text[]) TO anon;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spine-framework",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.109",
|
|
4
4
|
"description": "Multi-tenant, modular application platform for modern SaaS systems",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
".framework/migrations/024_messages_account_id.sql",
|
|
34
34
|
".framework/migrations/025_retype_threads_messages.sql",
|
|
35
35
|
".framework/migrations/026_fix_rls_policies.sql",
|
|
36
|
+
".framework/migrations/027_embedding_lifecycle.sql",
|
|
36
37
|
".framework/public/",
|
|
37
38
|
".framework/scripts/",
|
|
38
39
|
".framework/seeds/",
|