mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-29 19:01:39 +00:00
fix: harden Voyage schema templating
This commit is contained in:
@@ -25,6 +25,22 @@ import { validateSlug, contentHash, rowToPage, rowToChunk, rowToSearchResult, pa
|
||||
import { resolveBoostMap, resolveHardExcludes } from './search/source-boost.ts';
|
||||
import { buildSourceFactorCase, buildHardExcludeClause, buildVisibilityClause } from './search/sql-ranking.ts';
|
||||
|
||||
function escapeSqlStringLiteral(value: string): string {
|
||||
return value.replace(/'/g, "''");
|
||||
}
|
||||
|
||||
export function getPostgresSchema(dims: number = 1536, model: string = 'text-embedding-3-large'): string {
|
||||
const parsedDims = Number(dims);
|
||||
if (!Number.isInteger(parsedDims) || parsedDims <= 0) {
|
||||
throw new Error(`Invalid embedding dimensions: ${dims}`);
|
||||
}
|
||||
const sanitizedModel = escapeSqlStringLiteral(String(model));
|
||||
return applyChunkEmbeddingIndexPolicy(SCHEMA_SQL, parsedDims)
|
||||
.replace(/vector\(1536\)/g, `vector(${parsedDims})`)
|
||||
.replace(/'text-embedding-3-large'/g, `'${sanitizedModel}'`)
|
||||
.replace(/\('embedding_dimensions', '1536'\)/g, `('embedding_dimensions', '${parsedDims}')`);
|
||||
}
|
||||
|
||||
// CONNECTION_ERROR_PATTERNS / isConnectionError were used by the per-call
|
||||
// executeRaw retry that #406 originally shipped. Eng-review D3 dropped that
|
||||
// retry as unsound (regex idempotence-boundary doesn't hold for writable
|
||||
@@ -111,9 +127,7 @@ export class PostgresEngine implements BrainEngine {
|
||||
model = gw.getEmbeddingModel().split(':').slice(1).join(':') || model;
|
||||
} catch { /* gateway not yet configured — use defaults */ }
|
||||
|
||||
const sql = applyChunkEmbeddingIndexPolicy(SCHEMA_SQL, dims)
|
||||
.replace(/vector\(1536\)/g, `vector(${dims})`)
|
||||
.replace(/'text-embedding-3-large'/g, `'${model}'`);
|
||||
const sql = getPostgresSchema(dims, model);
|
||||
|
||||
// Advisory lock prevents concurrent initSchema() calls from deadlocking
|
||||
// on DDL statements (DROP TRIGGER + CREATE TRIGGER acquire AccessExclusiveLock).
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { describe, test, expect } from 'bun:test';
|
||||
import { getPGLiteSchema, PGLITE_SCHEMA_SQL } from '../../src/core/pglite-schema.ts';
|
||||
import { getPostgresSchema } from '../../src/core/postgres-engine.ts';
|
||||
|
||||
describe('getPGLiteSchema', () => {
|
||||
test('default produces v0.13-compatible schema (1536d + text-embedding-3-large)', () => {
|
||||
@@ -41,3 +42,19 @@ describe('getPGLiteSchema', () => {
|
||||
expect(PGLITE_SCHEMA_SQL).toBe(getPGLiteSchema());
|
||||
});
|
||||
});
|
||||
|
||||
describe('getPostgresSchema', () => {
|
||||
test('Voyage 2048d updates vector column and seeded config but skips HNSW', () => {
|
||||
const sql = getPostgresSchema(2048, 'voyage-4-large');
|
||||
expect(sql).toMatch(/vector\(2048\)/);
|
||||
expect(sql).toMatch(/\('embedding_model', 'voyage-4-large'\)/);
|
||||
expect(sql).toMatch(/\('embedding_dimensions', '2048'\)/);
|
||||
expect(sql).not.toContain('idx_chunks_embedding ON content_chunks USING hnsw');
|
||||
});
|
||||
|
||||
test('escapes configured model before inserting into schema SQL literals', () => {
|
||||
const sql = getPostgresSchema(1024, "voyage-weird'quoted");
|
||||
expect(sql).toContain("'voyage-weird''quoted'");
|
||||
expect(sql).not.toContain("'voyage-weird'quoted'");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user