feat(TRUEREF-0022): complete iteration 0 — worker-thread indexing, parallel jobs, SSE progress
- Move IndexingPipeline.run() into Worker Threads via WorkerPool - Add dedicated embedding worker thread with single model instance - Add stage/stageDetail columns to indexing_jobs schema - Create ProgressBroadcaster for SSE channel management - Add SSE endpoints: GET /api/v1/jobs/:id/stream, GET /api/v1/jobs/stream - Replace UI polling with EventSource on repo detail and admin pages - Add concurrency settings UI and API endpoint - Build worker entries separately via esbuild
This commit is contained in:
93
src/lib/server/pipeline/embed-worker-entry.ts
Normal file
93
src/lib/server/pipeline/embed-worker-entry.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import { workerData, parentPort } from 'node:worker_threads';
|
||||
import Database from 'better-sqlite3';
|
||||
import { EmbeddingService } from '$lib/server/embeddings/embedding.service.js';
|
||||
import { createProviderFromProfile } from '$lib/server/embeddings/registry.js';
|
||||
import { EmbeddingProfileMapper } from '$lib/server/mappers/embedding-profile.mapper.js';
|
||||
import { EmbeddingProfileEntity } from '$lib/server/models/embedding-profile.js';
|
||||
import type { EmbedWorkerRequest, EmbedWorkerResponse, WorkerInitData } from './worker-types.js';
|
||||
|
||||
const { dbPath, embeddingProfileId } = workerData as WorkerInitData;
|
||||
|
||||
if (!embeddingProfileId) {
|
||||
parentPort!.postMessage({
|
||||
type: 'embed-failed',
|
||||
jobId: 'init',
|
||||
error: 'embeddingProfileId is required in workerData'
|
||||
} satisfies EmbedWorkerResponse);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const db = new Database(dbPath);
|
||||
db.pragma('journal_mode = WAL');
|
||||
db.pragma('foreign_keys = ON');
|
||||
db.pragma('busy_timeout = 5000');
|
||||
|
||||
// Load the embedding profile from DB
|
||||
const rawProfile = db.prepare('SELECT * FROM embedding_profiles WHERE id = ?').get(embeddingProfileId);
|
||||
|
||||
if (!rawProfile) {
|
||||
db.close();
|
||||
parentPort!.postMessage({
|
||||
type: 'embed-failed',
|
||||
jobId: 'init',
|
||||
error: `Embedding profile ${embeddingProfileId} not found`
|
||||
} satisfies EmbedWorkerResponse);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const profileEntity = new EmbeddingProfileEntity(rawProfile as any);
|
||||
const profile = EmbeddingProfileMapper.fromEntity(profileEntity);
|
||||
|
||||
// Create provider and embedding service
|
||||
const provider = createProviderFromProfile(profile);
|
||||
const embeddingService = new EmbeddingService(db, provider, embeddingProfileId);
|
||||
|
||||
// Signal ready after service initialization
|
||||
parentPort!.postMessage({
|
||||
type: 'ready'
|
||||
} satisfies EmbedWorkerResponse);
|
||||
|
||||
parentPort!.on('message', async (msg: EmbedWorkerRequest) => {
|
||||
if (msg.type === 'shutdown') {
|
||||
db.close();
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
if (msg.type === 'embed') {
|
||||
try {
|
||||
const snippetIds = embeddingService.findSnippetIdsMissingEmbeddings(
|
||||
msg.repositoryId,
|
||||
msg.versionId
|
||||
);
|
||||
|
||||
await embeddingService.embedSnippets(snippetIds, (done: number, total: number) => {
|
||||
parentPort!.postMessage({
|
||||
type: 'embed-progress',
|
||||
jobId: msg.jobId,
|
||||
done,
|
||||
total
|
||||
} satisfies EmbedWorkerResponse);
|
||||
});
|
||||
|
||||
parentPort!.postMessage({
|
||||
type: 'embed-done',
|
||||
jobId: msg.jobId
|
||||
} satisfies EmbedWorkerResponse);
|
||||
} catch (err) {
|
||||
parentPort!.postMessage({
|
||||
type: 'embed-failed',
|
||||
jobId: msg.jobId,
|
||||
error: err instanceof Error ? err.message : String(err)
|
||||
} satisfies EmbedWorkerResponse);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
process.on('uncaughtException', (err) => {
|
||||
parentPort!.postMessage({
|
||||
type: 'embed-failed',
|
||||
jobId: 'uncaught',
|
||||
error: err instanceof Error ? err.message : String(err)
|
||||
} satisfies EmbedWorkerResponse);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user