| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import { supabase } from './supabase' |
| import { invokeEdgeFunction } from './apiAdapter' |
|
|
| export interface ReseedProgress { |
| methodologyCode: string |
| status: 'updated' | 'failed' | 'skipped' |
| similaritySample?: number |
| error?: string |
| } |
|
|
| export interface ReseedOptions { |
| |
| onProgress?: (current: number, total: number, last: ReseedProgress) => void |
| |
| throttleMs?: number |
| |
| dryRun?: boolean |
| } |
|
|
| export interface ReseedResult { |
| total: number |
| updated: number |
| failed: number |
| skipped: number |
| log: ReseedProgress[] |
| } |
|
|
| |
| |
| |
| async function fetchEmbedding(text: string): Promise<{ embedding: number[]; source: string }> { |
| const payload = await invokeEdgeFunction<{ text: string }, { embedding: number[]; source: string }>('embed-text', { text }) |
| if (!Array.isArray(payload.embedding) || payload.embedding.length !== 1536) { |
| throw new Error(`์๋ชป๋ embedding ์ฐจ์: ${payload.embedding?.length}`) |
| } |
| return payload |
| } |
|
|
| |
| |
| |
| |
| async function reseedOne( |
| row: { |
| id: string |
| methodology_code: string |
| name_ko: string |
| applicability: string |
| }, |
| dryRun: boolean, |
| ): Promise<ReseedProgress> { |
| try { |
| const seedText = `${row.name_ko} ${row.applicability}`.trim() |
| const { embedding, source } = await fetchEmbedding(seedText) |
|
|
| if (dryRun) { |
| return { |
| methodologyCode: row.methodology_code, |
| status: 'skipped', |
| similaritySample: source === 'openai' ? 1 : 0, |
| } |
| } |
|
|
| |
| const { error } = await supabase |
| .from('methodology_vectors') |
| .update({ embedding: embedding, updated_at: new Date().toISOString() }) |
| .eq('id', row.id) |
| if (error) throw error |
|
|
| return { |
| methodologyCode: row.methodology_code, |
| status: 'updated', |
| } |
| } catch (e) { |
| return { |
| methodologyCode: row.methodology_code, |
| status: 'failed', |
| error: e instanceof Error ? e.message : String(e), |
| } |
| } |
| } |
|
|
| |
| |
| |
| |
| export async function reseedAllMethodologyVectors( |
| opts: ReseedOptions = {}, |
| ): Promise<ReseedResult> { |
| const { onProgress, throttleMs = 250, dryRun = false } = opts |
|
|
| const { data: rows, error } = await supabase |
| .from('methodology_vectors') |
| .select('id, methodology_code, name_ko, applicability') |
| .order('methodology_code', { ascending: true }) |
|
|
| if (error) throw error |
| const total = rows?.length ?? 0 |
| const log: ReseedProgress[] = [] |
| let updated = 0 |
| let failed = 0 |
| let skipped = 0 |
|
|
| for (let i = 0; i < total; i++) { |
| const row = rows![i] as { |
| id: string |
| methodology_code: string |
| name_ko: string |
| applicability: string |
| } |
| const progress = await reseedOne(row, dryRun) |
| log.push(progress) |
| if (progress.status === 'updated') updated += 1 |
| else if (progress.status === 'failed') failed += 1 |
| else skipped += 1 |
|
|
| onProgress?.(i + 1, total, progress) |
| if (throttleMs > 0 && i < total - 1) { |
| await new Promise((r) => setTimeout(r, throttleMs)) |
| } |
| } |
|
|
| return { total, updated, failed, skipped, log } |
| } |
|
|
| |
| |
| |
| |
| |
| export function exposeReseedToWindow() { |
| if (typeof window === 'undefined') return |
| const w = window as unknown as Record<string, unknown> |
| if (!w.carboany) w.carboany = {} |
| const api = w.carboany as Record<string, unknown> |
| api.reseedMethodologies = async (dryRun = false) => { |
| const result = await reseedAllMethodologyVectors({ |
| dryRun, |
| onProgress: (i, n, p) => { |
| console.log(`[reseed] ${i}/${n} ${p.methodologyCode} โ ${p.status}`, p.error ?? '') |
| }, |
| }) |
| console.table(result.log) |
| console.log(`total=${result.total} updated=${result.updated} failed=${result.failed} skipped=${result.skipped}`) |
| return result |
| } |
| } |
|
|