d3ro-voice/server/supabase/functions/search-knowledge/index.ts

46 lines
2 KiB
TypeScript

// server/supabase/functions/search-knowledge/index.ts
// Composition root: wires the real adapters into the search-knowledge use case.
import { createClient } from '@supabase/supabase-js'
import { requireUser } from '../_shared/auth.ts'
import { createServiceRoleClient } from '../_shared/quota.ts'
import { readProviderKey } from '../_shared/provider-key.ts'
import { createOpenAIEmbeddingProvider } from '../_shared/openai-embeddings.ts'
import { createSupabaseEmbeddingUsageStore } from '../_shared/embedding-quota.ts'
import { type ChunkSearcher, createSearchKnowledgeHandler, SIMILARITY_THRESHOLD } from './handler.ts'
let serviceClient: ReturnType<typeof createServiceRoleClient> | null = null
function service(): ReturnType<typeof createServiceRoleClient> {
serviceClient ??= createServiceRoleClient()
return serviceClient
}
/** match_knowledge_chunks runs as the caller so RLS limits results to their own chunks. */
const searchChunks: ChunkSearcher = async (req, embedding, count) => {
const authHeader = req.headers.get('Authorization') ?? ''
const supabaseUrl = Deno.env.get('SUPABASE_URL') ?? ''
const anonKey = Deno.env.get('SUPABASE_ANON_KEY') ?? ''
if (!supabaseUrl || !anonKey) return { ok: false, reason: 'unavailable' }
const userClient = createClient(supabaseUrl, anonKey, {
global: { headers: { Authorization: authHeader } },
auth: { persistSession: false, autoRefreshToken: false },
})
const result = await userClient.rpc('match_knowledge_chunks', {
query_embedding: embedding,
match_count: count,
similarity_threshold: SIMILARITY_THRESHOLD,
})
if (result.error) return { ok: false, reason: 'failed' }
return { ok: true, results: Array.isArray(result.data) ? result.data : [] }
}
Deno.serve(createSearchKnowledgeHandler({
authenticate: requireUser,
embeddingProvider: () => {
const key = readProviderKey('OPENAI_API_KEY')
return key ? createOpenAIEmbeddingProvider(key) : null
},
usageStore: () => createSupabaseEmbeddingUsageStore(service()),
searchChunks,
}))