@realtimex/folio 0.1.16 → 0.1.17
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/api/src/middleware/auth.ts +77 -0
- package/api/src/routes/chat.ts +7 -1
- package/api/src/routes/index.ts +2 -0
- package/api/src/routes/ingestions.ts +45 -5
- package/api/src/routes/policies.ts +50 -7
- package/api/src/routes/stats.ts +9 -5
- package/api/src/routes/workspaces.ts +290 -0
- package/api/src/services/ChatService.ts +8 -2
- package/api/src/services/IngestionService.ts +38 -26
- package/api/src/services/PolicyEngine.ts +4 -1
- package/api/src/services/PolicyLearningService.ts +31 -6
- package/api/src/services/PolicyLoader.ts +44 -25
- package/api/src/services/RAGService.ts +52 -12
- package/dist/api/src/middleware/auth.js +59 -0
- package/dist/api/src/routes/chat.js +1 -1
- package/dist/api/src/routes/index.js +2 -0
- package/dist/api/src/routes/ingestions.js +45 -8
- package/dist/api/src/routes/policies.js +49 -7
- package/dist/api/src/routes/stats.js +9 -5
- package/dist/api/src/routes/workspaces.js +220 -0
- package/dist/api/src/services/ChatService.js +7 -2
- package/dist/api/src/services/IngestionService.js +35 -30
- package/dist/api/src/services/PolicyEngine.js +2 -1
- package/dist/api/src/services/PolicyLearningService.js +28 -6
- package/dist/api/src/services/PolicyLoader.js +29 -25
- package/dist/api/src/services/RAGService.js +43 -11
- package/dist/assets/index-CTn5FcC4.js +113 -0
- package/dist/assets/index-Dq9sxoZK.css +1 -0
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/supabase/functions/workspace-invite/index.ts +110 -0
- package/supabase/migrations/20260223000000_initial_foundation.sql +5 -0
- package/supabase/migrations/20260224000004_add_avatars_storage.sql +4 -0
- package/supabase/migrations/20260224000006_add_policies_table.sql +5 -0
- package/supabase/migrations/20260224000008_add_ingestions_table.sql +2 -0
- package/supabase/migrations/20260225000000_setup_compatible_mode.sql +17 -4
- package/supabase/migrations/20260225000003_add_baseline_configs.sql +4 -3
- package/supabase/migrations/20260226000000_add_processing_events.sql +1 -0
- package/supabase/migrations/20260226000002_add_dynamic_rag.sql +1 -0
- package/supabase/migrations/20260226000005_add_chat_tables.sql +3 -0
- package/supabase/migrations/20260228000001_add_policy_match_feedback.sql +4 -0
- package/supabase/migrations/20260302064608_add_ingestion_llm_settings_compat.sql +15 -0
- package/supabase/migrations/20260303000000_add_workspaces_phase1.sql +459 -0
- package/supabase/migrations/20260303010000_add_workspace_management_rpc.sql +310 -0
- package/supabase/migrations/20260303020000_workspace_scope_document_chunks.sql +139 -0
- package/dist/assets/index-DzN8-j-e.css +0 -1
- package/dist/assets/index-dnBz6SWG.js +0 -113
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
-- Workspace member management RPC helpers.
|
|
2
|
+
-- These SECURITY DEFINER functions provide controlled cross-member management
|
|
3
|
+
-- while keeping table-level RLS strict.
|
|
4
|
+
|
|
5
|
+
create or replace function public.workspace_list_members(
|
|
6
|
+
p_workspace_id uuid
|
|
7
|
+
)
|
|
8
|
+
returns table (
|
|
9
|
+
user_id uuid,
|
|
10
|
+
role text,
|
|
11
|
+
status text,
|
|
12
|
+
joined_at timestamptz,
|
|
13
|
+
first_name text,
|
|
14
|
+
last_name text,
|
|
15
|
+
email text,
|
|
16
|
+
avatar_url text,
|
|
17
|
+
is_current_user boolean
|
|
18
|
+
)
|
|
19
|
+
language plpgsql
|
|
20
|
+
security definer
|
|
21
|
+
set search_path = 'public', 'auth', 'pg_catalog'
|
|
22
|
+
as $$
|
|
23
|
+
declare
|
|
24
|
+
requester_id uuid := auth.uid();
|
|
25
|
+
requester_role text;
|
|
26
|
+
begin
|
|
27
|
+
if requester_id is null then
|
|
28
|
+
raise exception 'Authentication required' using errcode = '42501';
|
|
29
|
+
end if;
|
|
30
|
+
|
|
31
|
+
select wm.role
|
|
32
|
+
into requester_role
|
|
33
|
+
from public.workspace_members wm
|
|
34
|
+
where wm.workspace_id = p_workspace_id
|
|
35
|
+
and wm.user_id = requester_id
|
|
36
|
+
and wm.status = 'active'
|
|
37
|
+
limit 1;
|
|
38
|
+
|
|
39
|
+
if requester_role is null then
|
|
40
|
+
raise exception 'Workspace membership required' using errcode = '42501';
|
|
41
|
+
end if;
|
|
42
|
+
|
|
43
|
+
return query
|
|
44
|
+
select
|
|
45
|
+
wm.user_id,
|
|
46
|
+
wm.role,
|
|
47
|
+
wm.status,
|
|
48
|
+
wm.created_at as joined_at,
|
|
49
|
+
p.first_name,
|
|
50
|
+
p.last_name,
|
|
51
|
+
coalesce(p.email, u.email) as email,
|
|
52
|
+
p.avatar_url,
|
|
53
|
+
(wm.user_id = requester_id) as is_current_user
|
|
54
|
+
from public.workspace_members wm
|
|
55
|
+
left join public.profiles p
|
|
56
|
+
on p.id = wm.user_id
|
|
57
|
+
left join auth.users u
|
|
58
|
+
on u.id = wm.user_id
|
|
59
|
+
where wm.workspace_id = p_workspace_id
|
|
60
|
+
order by
|
|
61
|
+
case wm.role
|
|
62
|
+
when 'owner' then 0
|
|
63
|
+
when 'admin' then 1
|
|
64
|
+
else 2
|
|
65
|
+
end,
|
|
66
|
+
wm.created_at,
|
|
67
|
+
wm.user_id;
|
|
68
|
+
end;
|
|
69
|
+
$$;
|
|
70
|
+
|
|
71
|
+
grant execute on function public.workspace_list_members(uuid) to authenticated;
|
|
72
|
+
|
|
73
|
+
create or replace function public.workspace_invite_member(
|
|
74
|
+
p_workspace_id uuid,
|
|
75
|
+
p_email text,
|
|
76
|
+
p_role text default 'member'
|
|
77
|
+
)
|
|
78
|
+
returns table (
|
|
79
|
+
user_id uuid,
|
|
80
|
+
role text,
|
|
81
|
+
status text
|
|
82
|
+
)
|
|
83
|
+
language plpgsql
|
|
84
|
+
security definer
|
|
85
|
+
set search_path = 'public', 'auth', 'pg_catalog'
|
|
86
|
+
as $$
|
|
87
|
+
declare
|
|
88
|
+
requester_id uuid := auth.uid();
|
|
89
|
+
requester_role text;
|
|
90
|
+
target_email text := lower(trim(coalesce(p_email, '')));
|
|
91
|
+
target_user_id uuid;
|
|
92
|
+
existing_role text;
|
|
93
|
+
begin
|
|
94
|
+
if requester_id is null then
|
|
95
|
+
raise exception 'Authentication required' using errcode = '42501';
|
|
96
|
+
end if;
|
|
97
|
+
|
|
98
|
+
if p_role not in ('admin', 'member') then
|
|
99
|
+
raise exception 'Invalid role. Expected admin or member.' using errcode = '22023';
|
|
100
|
+
end if;
|
|
101
|
+
|
|
102
|
+
if target_email = '' then
|
|
103
|
+
raise exception 'Email is required' using errcode = '22023';
|
|
104
|
+
end if;
|
|
105
|
+
|
|
106
|
+
select wm.role
|
|
107
|
+
into requester_role
|
|
108
|
+
from public.workspace_members wm
|
|
109
|
+
where wm.workspace_id = p_workspace_id
|
|
110
|
+
and wm.user_id = requester_id
|
|
111
|
+
and wm.status = 'active'
|
|
112
|
+
limit 1;
|
|
113
|
+
|
|
114
|
+
if requester_role not in ('owner', 'admin') then
|
|
115
|
+
raise exception 'Only workspace admins can invite members' using errcode = '42501';
|
|
116
|
+
end if;
|
|
117
|
+
|
|
118
|
+
if p_role = 'admin' and requester_role <> 'owner' then
|
|
119
|
+
raise exception 'Only workspace owners can invite admins' using errcode = '42501';
|
|
120
|
+
end if;
|
|
121
|
+
|
|
122
|
+
select u.id
|
|
123
|
+
into target_user_id
|
|
124
|
+
from auth.users u
|
|
125
|
+
where lower(coalesce(u.email, '')) = target_email
|
|
126
|
+
limit 1;
|
|
127
|
+
|
|
128
|
+
if target_user_id is null then
|
|
129
|
+
raise exception 'No user found with that email. They must sign up first.' using errcode = '22023';
|
|
130
|
+
end if;
|
|
131
|
+
|
|
132
|
+
select wm.role
|
|
133
|
+
into existing_role
|
|
134
|
+
from public.workspace_members wm
|
|
135
|
+
where wm.workspace_id = p_workspace_id
|
|
136
|
+
and wm.user_id = target_user_id
|
|
137
|
+
limit 1;
|
|
138
|
+
|
|
139
|
+
if existing_role = 'owner' then
|
|
140
|
+
raise exception 'Owner membership cannot be modified' using errcode = '42501';
|
|
141
|
+
end if;
|
|
142
|
+
|
|
143
|
+
if requester_role <> 'owner' and existing_role = 'admin' then
|
|
144
|
+
raise exception 'Only workspace owners can modify admin memberships' using errcode = '42501';
|
|
145
|
+
end if;
|
|
146
|
+
|
|
147
|
+
if existing_role is null then
|
|
148
|
+
insert into public.workspace_members (workspace_id, user_id, role, status)
|
|
149
|
+
values (p_workspace_id, target_user_id, p_role, 'active');
|
|
150
|
+
else
|
|
151
|
+
update public.workspace_members
|
|
152
|
+
set
|
|
153
|
+
role = p_role,
|
|
154
|
+
status = 'active',
|
|
155
|
+
updated_at = now()
|
|
156
|
+
where workspace_id = p_workspace_id
|
|
157
|
+
and user_id = target_user_id;
|
|
158
|
+
end if;
|
|
159
|
+
|
|
160
|
+
return query
|
|
161
|
+
select wm.user_id, wm.role, wm.status
|
|
162
|
+
from public.workspace_members wm
|
|
163
|
+
where wm.workspace_id = p_workspace_id
|
|
164
|
+
and wm.user_id = target_user_id
|
|
165
|
+
limit 1;
|
|
166
|
+
end;
|
|
167
|
+
$$;
|
|
168
|
+
|
|
169
|
+
grant execute on function public.workspace_invite_member(uuid, text, text) to authenticated;
|
|
170
|
+
|
|
171
|
+
create or replace function public.workspace_update_member_role(
|
|
172
|
+
p_workspace_id uuid,
|
|
173
|
+
p_target_user_id uuid,
|
|
174
|
+
p_role text
|
|
175
|
+
)
|
|
176
|
+
returns table (
|
|
177
|
+
user_id uuid,
|
|
178
|
+
role text,
|
|
179
|
+
status text
|
|
180
|
+
)
|
|
181
|
+
language plpgsql
|
|
182
|
+
security definer
|
|
183
|
+
set search_path = 'public', 'auth', 'pg_catalog'
|
|
184
|
+
as $$
|
|
185
|
+
declare
|
|
186
|
+
requester_id uuid := auth.uid();
|
|
187
|
+
requester_role text;
|
|
188
|
+
target_role text;
|
|
189
|
+
begin
|
|
190
|
+
if requester_id is null then
|
|
191
|
+
raise exception 'Authentication required' using errcode = '42501';
|
|
192
|
+
end if;
|
|
193
|
+
|
|
194
|
+
if p_role not in ('admin', 'member') then
|
|
195
|
+
raise exception 'Invalid role. Expected admin or member.' using errcode = '22023';
|
|
196
|
+
end if;
|
|
197
|
+
|
|
198
|
+
select wm.role
|
|
199
|
+
into requester_role
|
|
200
|
+
from public.workspace_members wm
|
|
201
|
+
where wm.workspace_id = p_workspace_id
|
|
202
|
+
and wm.user_id = requester_id
|
|
203
|
+
and wm.status = 'active'
|
|
204
|
+
limit 1;
|
|
205
|
+
|
|
206
|
+
if requester_role not in ('owner', 'admin') then
|
|
207
|
+
raise exception 'Only workspace admins can manage roles' using errcode = '42501';
|
|
208
|
+
end if;
|
|
209
|
+
|
|
210
|
+
if p_role = 'admin' and requester_role <> 'owner' then
|
|
211
|
+
raise exception 'Only workspace owners can promote admins' using errcode = '42501';
|
|
212
|
+
end if;
|
|
213
|
+
|
|
214
|
+
select wm.role
|
|
215
|
+
into target_role
|
|
216
|
+
from public.workspace_members wm
|
|
217
|
+
where wm.workspace_id = p_workspace_id
|
|
218
|
+
and wm.user_id = p_target_user_id
|
|
219
|
+
and wm.status = 'active'
|
|
220
|
+
limit 1;
|
|
221
|
+
|
|
222
|
+
if target_role is null then
|
|
223
|
+
raise exception 'Workspace member not found' using errcode = '22023';
|
|
224
|
+
end if;
|
|
225
|
+
|
|
226
|
+
if target_role = 'owner' then
|
|
227
|
+
raise exception 'Owner membership cannot be modified' using errcode = '42501';
|
|
228
|
+
end if;
|
|
229
|
+
|
|
230
|
+
if requester_role <> 'owner' and target_role = 'admin' then
|
|
231
|
+
raise exception 'Only workspace owners can modify admin roles' using errcode = '42501';
|
|
232
|
+
end if;
|
|
233
|
+
|
|
234
|
+
update public.workspace_members
|
|
235
|
+
set
|
|
236
|
+
role = p_role,
|
|
237
|
+
updated_at = now()
|
|
238
|
+
where workspace_id = p_workspace_id
|
|
239
|
+
and user_id = p_target_user_id;
|
|
240
|
+
|
|
241
|
+
return query
|
|
242
|
+
select wm.user_id, wm.role, wm.status
|
|
243
|
+
from public.workspace_members wm
|
|
244
|
+
where wm.workspace_id = p_workspace_id
|
|
245
|
+
and wm.user_id = p_target_user_id
|
|
246
|
+
limit 1;
|
|
247
|
+
end;
|
|
248
|
+
$$;
|
|
249
|
+
|
|
250
|
+
grant execute on function public.workspace_update_member_role(uuid, uuid, text) to authenticated;
|
|
251
|
+
|
|
252
|
+
create or replace function public.workspace_remove_member(
|
|
253
|
+
p_workspace_id uuid,
|
|
254
|
+
p_target_user_id uuid
|
|
255
|
+
)
|
|
256
|
+
returns boolean
|
|
257
|
+
language plpgsql
|
|
258
|
+
security definer
|
|
259
|
+
set search_path = 'public', 'auth', 'pg_catalog'
|
|
260
|
+
as $$
|
|
261
|
+
declare
|
|
262
|
+
requester_id uuid := auth.uid();
|
|
263
|
+
requester_role text;
|
|
264
|
+
target_role text;
|
|
265
|
+
begin
|
|
266
|
+
if requester_id is null then
|
|
267
|
+
raise exception 'Authentication required' using errcode = '42501';
|
|
268
|
+
end if;
|
|
269
|
+
|
|
270
|
+
select wm.role
|
|
271
|
+
into requester_role
|
|
272
|
+
from public.workspace_members wm
|
|
273
|
+
where wm.workspace_id = p_workspace_id
|
|
274
|
+
and wm.user_id = requester_id
|
|
275
|
+
and wm.status = 'active'
|
|
276
|
+
limit 1;
|
|
277
|
+
|
|
278
|
+
if requester_role not in ('owner', 'admin') then
|
|
279
|
+
raise exception 'Only workspace admins can remove members' using errcode = '42501';
|
|
280
|
+
end if;
|
|
281
|
+
|
|
282
|
+
select wm.role
|
|
283
|
+
into target_role
|
|
284
|
+
from public.workspace_members wm
|
|
285
|
+
where wm.workspace_id = p_workspace_id
|
|
286
|
+
and wm.user_id = p_target_user_id
|
|
287
|
+
and wm.status = 'active'
|
|
288
|
+
limit 1;
|
|
289
|
+
|
|
290
|
+
if target_role is null then
|
|
291
|
+
return false;
|
|
292
|
+
end if;
|
|
293
|
+
|
|
294
|
+
if target_role = 'owner' then
|
|
295
|
+
raise exception 'Owner membership cannot be removed' using errcode = '42501';
|
|
296
|
+
end if;
|
|
297
|
+
|
|
298
|
+
if requester_role <> 'owner' and target_role = 'admin' then
|
|
299
|
+
raise exception 'Only workspace owners can remove admins' using errcode = '42501';
|
|
300
|
+
end if;
|
|
301
|
+
|
|
302
|
+
delete from public.workspace_members
|
|
303
|
+
where workspace_id = p_workspace_id
|
|
304
|
+
and user_id = p_target_user_id;
|
|
305
|
+
|
|
306
|
+
return found;
|
|
307
|
+
end;
|
|
308
|
+
$$;
|
|
309
|
+
|
|
310
|
+
grant execute on function public.workspace_remove_member(uuid, uuid) to authenticated;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
-- Workspace-scope RAG chunks and workspace-aware semantic search.
|
|
2
|
+
|
|
3
|
+
ALTER TABLE public.document_chunks
|
|
4
|
+
ADD COLUMN IF NOT EXISTS workspace_id UUID;
|
|
5
|
+
|
|
6
|
+
UPDATE public.document_chunks dc
|
|
7
|
+
SET workspace_id = i.workspace_id
|
|
8
|
+
FROM public.ingestions i
|
|
9
|
+
WHERE dc.workspace_id IS NULL
|
|
10
|
+
AND dc.ingestion_id = i.id;
|
|
11
|
+
|
|
12
|
+
ALTER TABLE public.document_chunks
|
|
13
|
+
ALTER COLUMN workspace_id SET NOT NULL;
|
|
14
|
+
|
|
15
|
+
DO $$
|
|
16
|
+
BEGIN
|
|
17
|
+
IF NOT EXISTS (
|
|
18
|
+
SELECT 1 FROM pg_constraint
|
|
19
|
+
WHERE conname = 'document_chunks_workspace_id_fkey'
|
|
20
|
+
) THEN
|
|
21
|
+
ALTER TABLE public.document_chunks
|
|
22
|
+
ADD CONSTRAINT document_chunks_workspace_id_fkey
|
|
23
|
+
FOREIGN KEY (workspace_id) REFERENCES public.workspaces(id) ON DELETE CASCADE;
|
|
24
|
+
END IF;
|
|
25
|
+
END $$;
|
|
26
|
+
|
|
27
|
+
CREATE INDEX IF NOT EXISTS document_chunks_workspace_id_idx
|
|
28
|
+
ON public.document_chunks(workspace_id);
|
|
29
|
+
|
|
30
|
+
CREATE INDEX IF NOT EXISTS document_chunks_workspace_model_scope_idx
|
|
31
|
+
ON public.document_chunks(workspace_id, embedding_provider, embedding_model, vector_dim);
|
|
32
|
+
|
|
33
|
+
DROP POLICY IF EXISTS "Users can manage their own document chunks" ON public.document_chunks;
|
|
34
|
+
DROP POLICY IF EXISTS "Workspace members can read document chunks" ON public.document_chunks;
|
|
35
|
+
DROP POLICY IF EXISTS "Workspace members can insert document chunks" ON public.document_chunks;
|
|
36
|
+
DROP POLICY IF EXISTS "Workspace members can update document chunks" ON public.document_chunks;
|
|
37
|
+
DROP POLICY IF EXISTS "Workspace members can delete document chunks" ON public.document_chunks;
|
|
38
|
+
|
|
39
|
+
CREATE POLICY "Workspace members can read document chunks"
|
|
40
|
+
ON public.document_chunks FOR SELECT
|
|
41
|
+
USING (public.is_workspace_member(workspace_id));
|
|
42
|
+
|
|
43
|
+
CREATE POLICY "Workspace members can insert document chunks"
|
|
44
|
+
ON public.document_chunks FOR INSERT
|
|
45
|
+
WITH CHECK (public.is_workspace_member(workspace_id) AND user_id = auth.uid());
|
|
46
|
+
|
|
47
|
+
CREATE POLICY "Workspace members can update document chunks"
|
|
48
|
+
ON public.document_chunks FOR UPDATE
|
|
49
|
+
USING (public.is_workspace_member(workspace_id))
|
|
50
|
+
WITH CHECK (public.is_workspace_member(workspace_id));
|
|
51
|
+
|
|
52
|
+
CREATE POLICY "Workspace members can delete document chunks"
|
|
53
|
+
ON public.document_chunks FOR DELETE
|
|
54
|
+
USING (public.is_workspace_member(workspace_id));
|
|
55
|
+
|
|
56
|
+
CREATE OR REPLACE FUNCTION public.search_workspace_documents(
|
|
57
|
+
p_workspace_id UUID,
|
|
58
|
+
p_embedding_provider TEXT,
|
|
59
|
+
p_embedding_model TEXT,
|
|
60
|
+
query_embedding vector,
|
|
61
|
+
match_threshold float DEFAULT 0.7,
|
|
62
|
+
match_count int DEFAULT 5,
|
|
63
|
+
query_dim int DEFAULT 1536
|
|
64
|
+
)
|
|
65
|
+
RETURNS TABLE (
|
|
66
|
+
id UUID,
|
|
67
|
+
ingestion_id UUID,
|
|
68
|
+
content TEXT,
|
|
69
|
+
similarity float
|
|
70
|
+
)
|
|
71
|
+
LANGUAGE plpgsql
|
|
72
|
+
AS $$
|
|
73
|
+
BEGIN
|
|
74
|
+
IF query_dim = 384 THEN
|
|
75
|
+
RETURN QUERY
|
|
76
|
+
SELECT
|
|
77
|
+
dc.id,
|
|
78
|
+
dc.ingestion_id,
|
|
79
|
+
dc.content,
|
|
80
|
+
1 - (dc.embedding::vector(384) <=> query_embedding::vector(384)) AS similarity
|
|
81
|
+
FROM public.document_chunks dc
|
|
82
|
+
WHERE dc.workspace_id = p_workspace_id
|
|
83
|
+
AND dc.embedding_provider = p_embedding_provider
|
|
84
|
+
AND dc.embedding_model = p_embedding_model
|
|
85
|
+
AND dc.vector_dim = 384
|
|
86
|
+
AND 1 - (dc.embedding::vector(384) <=> query_embedding::vector(384)) > match_threshold
|
|
87
|
+
ORDER BY dc.embedding::vector(384) <=> query_embedding::vector(384)
|
|
88
|
+
LIMIT match_count;
|
|
89
|
+
ELSIF query_dim = 768 THEN
|
|
90
|
+
RETURN QUERY
|
|
91
|
+
SELECT
|
|
92
|
+
dc.id,
|
|
93
|
+
dc.ingestion_id,
|
|
94
|
+
dc.content,
|
|
95
|
+
1 - (dc.embedding::vector(768) <=> query_embedding::vector(768)) AS similarity
|
|
96
|
+
FROM public.document_chunks dc
|
|
97
|
+
WHERE dc.workspace_id = p_workspace_id
|
|
98
|
+
AND dc.embedding_provider = p_embedding_provider
|
|
99
|
+
AND dc.embedding_model = p_embedding_model
|
|
100
|
+
AND dc.vector_dim = 768
|
|
101
|
+
AND 1 - (dc.embedding::vector(768) <=> query_embedding::vector(768)) > match_threshold
|
|
102
|
+
ORDER BY dc.embedding::vector(768) <=> query_embedding::vector(768)
|
|
103
|
+
LIMIT match_count;
|
|
104
|
+
ELSIF query_dim = 1536 THEN
|
|
105
|
+
RETURN QUERY
|
|
106
|
+
SELECT
|
|
107
|
+
dc.id,
|
|
108
|
+
dc.ingestion_id,
|
|
109
|
+
dc.content,
|
|
110
|
+
1 - (dc.embedding::vector(1536) <=> query_embedding::vector(1536)) AS similarity
|
|
111
|
+
FROM public.document_chunks dc
|
|
112
|
+
WHERE dc.workspace_id = p_workspace_id
|
|
113
|
+
AND dc.embedding_provider = p_embedding_provider
|
|
114
|
+
AND dc.embedding_model = p_embedding_model
|
|
115
|
+
AND dc.vector_dim = 1536
|
|
116
|
+
AND 1 - (dc.embedding::vector(1536) <=> query_embedding::vector(1536)) > match_threshold
|
|
117
|
+
ORDER BY dc.embedding::vector(1536) <=> query_embedding::vector(1536)
|
|
118
|
+
LIMIT match_count;
|
|
119
|
+
ELSE
|
|
120
|
+
RETURN QUERY
|
|
121
|
+
SELECT
|
|
122
|
+
dc.id,
|
|
123
|
+
dc.ingestion_id,
|
|
124
|
+
dc.content,
|
|
125
|
+
1 - (dc.embedding <=> query_embedding) AS similarity
|
|
126
|
+
FROM public.document_chunks dc
|
|
127
|
+
WHERE dc.workspace_id = p_workspace_id
|
|
128
|
+
AND dc.embedding_provider = p_embedding_provider
|
|
129
|
+
AND dc.embedding_model = p_embedding_model
|
|
130
|
+
AND dc.vector_dim = query_dim
|
|
131
|
+
AND 1 - (dc.embedding <=> query_embedding) > match_threshold
|
|
132
|
+
ORDER BY dc.embedding <=> query_embedding
|
|
133
|
+
LIMIT match_count;
|
|
134
|
+
END IF;
|
|
135
|
+
END;
|
|
136
|
+
$$;
|
|
137
|
+
|
|
138
|
+
COMMENT ON FUNCTION public.search_workspace_documents
|
|
139
|
+
IS 'Performs cosine similarity search against document chunks scoped to a workspace.';
|