feat: switch embeddings from Voyage AI to Google Gemini text-embedding-004

- embedText: call Gemini REST API (768-dim) instead of Voyage (1024-dim)
- Migration: drop+recreate incidents.embedding as vector(768), update
  match_incidents function, swap VOYAGE_API_KEY → GOOGLE_AI_API_KEY in app_settings
- Settings UI: relabel to "Google AI API Key (Embeddings)"
- All call sites updated (incidents POST, similar GET)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CPf5Rc8QPx6V8KLEEgfKEQ
This commit is contained in:
2026-07-13 06:50:35 +08:00
co-authored by Claude Sonnet 4.6
parent 614c792225
commit d10c690c12
7 changed files with 82 additions and 30 deletions
+16 -13
View File
@@ -1,15 +1,18 @@
export async function embedText(text: string, apiKey?: string): Promise<number[]> {
const key = apiKey ?? process.env.VOYAGE_API_KEY
if (!key) throw new Error('VOYAGE_API_KEY is not set')
const res = await fetch('https://api.voyageai.com/v1/embeddings', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${key}`,
},
body: JSON.stringify({ input: [text], model: 'voyage-3-lite' }),
})
if (!res.ok) throw new Error(`Voyage embed failed: ${res.status}`)
const json = await res.json() as { data: Array<{ embedding: number[] }> }
return json.data[0].embedding
const key = apiKey ?? process.env.GOOGLE_AI_API_KEY
if (!key) throw new Error('GOOGLE_AI_API_KEY is not set')
const res = await fetch(
`https://generativelanguage.googleapis.com/v1beta/models/text-embedding-004:embedContent?key=${key}`,
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
model: 'models/text-embedding-004',
content: { parts: [{ text }] },
}),
}
)
if (!res.ok) throw new Error(`Gemini embed failed: ${res.status}`)
const json = await res.json() as { embedding: { values: number[] } }
return json.embedding.values
}