- Wire local embedding provider as the default on startup when no profile is configured - Refactor embedding settings into dedicated service, DTOs, mappers and models - Rebuild settings page with profile management UI and live test feedback - Expose index summary (indexed versions + embedding count) on repo endpoints - Harden indexing pipeline and context search with additional test coverage Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
104 lines
3.3 KiB
TypeScript
104 lines
3.3 KiB
TypeScript
/**
|
|
* GET /api/v1/settings/embedding/test
|
|
*
|
|
* Tests the active default embedding profile by creating a provider instance
|
|
* and checking availability. Returns success with profile metadata or error.
|
|
*/
|
|
|
|
import { json } from '@sveltejs/kit';
|
|
import type { RequestHandler } from './$types';
|
|
import { LocalEmbeddingProvider } from '$lib/server/embeddings/local.provider';
|
|
import { createProviderFromProfile } from '$lib/server/embeddings/registry';
|
|
import { EmbeddingProfileEntity } from '$lib/server/models/embedding-profile';
|
|
import { EmbeddingProfileMapper } from '$lib/server/mappers/embedding-profile.mapper';
|
|
import { handleServiceError } from '$lib/server/utils/validation';
|
|
|
|
export const GET: RequestHandler = async () => {
|
|
try {
|
|
const provider = new LocalEmbeddingProvider();
|
|
const available = await provider.isAvailable();
|
|
|
|
return json({
|
|
available,
|
|
profile: {
|
|
id: 'local-default',
|
|
providerKind: 'local-transformers',
|
|
model: provider.model,
|
|
dimensions: provider.dimensions
|
|
}
|
|
});
|
|
} catch (err) {
|
|
return handleServiceError(err);
|
|
}
|
|
};
|
|
|
|
export const POST: RequestHandler = async ({ request }) => {
|
|
try {
|
|
const body = await request.json();
|
|
if (typeof body !== 'object' || body === null) {
|
|
throw new Error('Request body must be a JSON object');
|
|
}
|
|
|
|
const candidate = body as Record<string, unknown>;
|
|
if (candidate.providerKind !== 'openai-compatible') {
|
|
throw new Error('Only openai-compatible providers can be tested via this endpoint');
|
|
}
|
|
if (typeof candidate.model !== 'string' || typeof candidate.dimensions !== 'number') {
|
|
throw new Error('model and dimensions are required');
|
|
}
|
|
|
|
const provider = createProviderFromProfile(
|
|
EmbeddingProfileMapper.fromEntity(
|
|
new EmbeddingProfileEntity({
|
|
id: typeof candidate.id === 'string' ? candidate.id : 'test-openai-profile',
|
|
provider_kind: 'openai-compatible',
|
|
title: typeof candidate.title === 'string' ? candidate.title : 'Test Provider',
|
|
enabled: true,
|
|
is_default: false,
|
|
model: candidate.model,
|
|
dimensions: candidate.dimensions,
|
|
config:
|
|
typeof candidate.config === 'object' && candidate.config !== null
|
|
? (candidate.config as Record<string, unknown>)
|
|
: {},
|
|
created_at: Date.now(),
|
|
updated_at: Date.now()
|
|
})
|
|
)
|
|
);
|
|
|
|
const available = await provider.isAvailable();
|
|
if (!available) {
|
|
return new Response(
|
|
JSON.stringify({
|
|
error: 'Provider is not available. Check your configuration.'
|
|
}),
|
|
{ status: 400, headers: { 'Content-Type': 'application/json' } }
|
|
);
|
|
}
|
|
|
|
// Perform a real embedding call to catch auth / model-name errors.
|
|
let dimensions: number;
|
|
try {
|
|
const results = await provider.embed(['test']);
|
|
if (results.length === 0) {
|
|
return new Response(
|
|
JSON.stringify({ error: 'Provider returned no embeddings for the test input.' }),
|
|
{ status: 400, headers: { 'Content-Type': 'application/json' } }
|
|
);
|
|
}
|
|
dimensions = results[0].dimensions;
|
|
} catch (embedErr) {
|
|
const message = embedErr instanceof Error ? embedErr.message : 'Embedding call failed';
|
|
return new Response(JSON.stringify({ error: message }), {
|
|
status: 400,
|
|
headers: { 'Content-Type': 'application/json' }
|
|
});
|
|
}
|
|
|
|
return json({ ok: true, dimensions });
|
|
} catch (err) {
|
|
return handleServiceError(err);
|
|
}
|
|
};
|