socraticode
Version:
SocratiCode — MCP server for local codebase indexing, semantic search, and code dependency graphs. All private, all local via Docker.
288 lines • 13 kB
JavaScript
// SPDX-License-Identifier: AGPL-3.0-only
// Copyright (C) 2026 Giancarlo Erra - Altaire Limited
import { EXTENSION_LANGUAGE_MAP, MAX_CHUNK_CHARS, MAX_FILE_BYTES, } from "../constants.js";
import { documentIncludesPath, documentPrefix, getEmbeddingConfig, loadEmbeddingConfig, queryPrefix, withEmbeddingConfig, } from "./embedding-config.js";
import { getEmbeddingProvider } from "./embedding-provider.js";
export const INDEX_PROFILE_SCHEMA_VERSION = 1;
/**
* Bumped when a build changes the chunks it produces from unchanged input, so
* that an index built by an earlier build is reported as differing from what
* this build would produce.
*
* 2: the per-chunk character cap splits instead of truncating. Indexing skips
* files whose content hash is unchanged, so without this an index built by an
* earlier version keeps its truncated chunks indefinitely — only edited files
* would be re-chunked, leaving the collection quietly mixed.
*/
export const CURRENT_INDEX_FORMAT_VERSION = 2;
const LEGACY_QUERY_PREFIX = "search_query: ";
const LEGACY_DOCUMENT_PREFIX = "search_document: ";
const LEGACY_MAX_CHUNK_CHARS = 2000;
const READINESS_TTL_MS = 60_000;
const embeddingReadyAt = new Map();
const embeddingReadinessInFlight = new Map();
const EMBEDDING_UNVERIFIED_FIELDS = [
"embedding.provider",
"embedding.model",
"embedding.dimensions",
"embedding.contextLength",
"embedding.litellmSendDimensions",
];
function effectiveContextLength(config) {
if (config.embeddingContextLength > 0)
return config.embeddingContextLength;
return config.embeddingProvider === "openai" ? 8191 : 2048;
}
function requestedEmbeddingIdentity() {
const config = loadEmbeddingConfig();
return {
provider: config.embeddingProvider,
model: config.embeddingModel,
dimensions: config.embeddingDimensions,
contextLength: effectiveContextLength(config),
litellmSendDimensions: config.litellmSendDimensions,
};
}
function extensionLanguageMapRecord() {
return Object.fromEntries([...EXTENSION_LANGUAGE_MAP.entries()].sort(([a], [b]) => a.localeCompare(b)));
}
export function requestedIndexProfile(kind) {
return {
schemaVersion: INDEX_PROFILE_SCHEMA_VERSION,
indexFormatVersion: CURRENT_INDEX_FORMAT_VERSION,
kind,
source: "fresh",
queryPrefix: queryPrefix(),
documentPrefix: documentPrefix(),
documentIncludesPath: documentIncludesPath(),
maxChunkChars: MAX_CHUNK_CHARS,
embedding: requestedEmbeddingIdentity(),
...(kind === "code"
? {
extensionLanguageMap: extensionLanguageMapRecord(),
maxFileBytes: MAX_FILE_BYTES,
}
: {}),
legacyUnverifiedFields: [],
};
}
export function legacyIndexProfile(kind, verifiedEmbeddingDimensions) {
const requested = requestedIndexProfile(kind);
const dimensionsAreVerified = Number.isInteger(verifiedEmbeddingDimensions) &&
verifiedEmbeddingDimensions > 0;
return {
...requested,
indexFormatVersion: 0,
source: "legacy-adopted",
queryPrefix: LEGACY_QUERY_PREFIX,
documentPrefix: LEGACY_DOCUMENT_PREFIX,
documentIncludesPath: true,
maxChunkChars: LEGACY_MAX_CHUNK_CHARS,
embedding: {
...requested.embedding,
...(dimensionsAreVerified
? { dimensions: verifiedEmbeddingDimensions }
: {}),
},
legacyUnverifiedFields: [
...EMBEDDING_UNVERIFIED_FIELDS.filter((field) => field !== "embedding.dimensions" || !dimensionsAreVerified),
...(kind === "code" ? ["extensionLanguageMap", "maxFileBytes"] : []),
],
};
}
export function resolveEffectiveIndexProfile(kind, stored, collectionHasData, legacyEmbeddingDimensions) {
if (stored) {
if (stored.kind !== kind) {
throw new Error(`Stored ${stored.kind} index profile cannot be used for a ${kind} collection.`);
}
return stored;
}
return collectionHasData
? legacyIndexProfile(kind, legacyEmbeddingDimensions)
: requestedIndexProfile(kind);
}
export function profileExtensionLanguageMap(profile) {
return new Map(Object.entries(profile.extensionLanguageMap ?? {}));
}
export function withEffectiveEmbedding(profile, operation) {
return withEmbeddingConfig({
embeddingProvider: profile.embedding.provider,
embeddingModel: profile.embedding.model,
embeddingDimensions: profile.embedding.dimensions,
embeddingContextLength: profile.embedding.contextLength,
litellmSendDimensions: profile.embedding.litellmSendDimensions,
}, operation);
}
/** Ensure the provider selected by a collection profile is ready for indexing. */
export function ensureEffectiveEmbeddingReady(profile, onProgress) {
return withEffectiveEmbedding(profile, async () => {
const config = getEmbeddingConfig();
const key = JSON.stringify({
embedding: profile.embedding,
ollamaMode: config.ollamaMode,
ollamaUrl: config.ollamaUrl,
ollamaMaxConnections: config.ollamaMaxConnections,
lmstudioUrl: config.lmstudioUrl,
litellmUrl: config.litellmUrl,
allowMissingModelListing: config.allowMissingModelListing,
});
const readyAt = embeddingReadyAt.get(key);
if (readyAt !== undefined && Date.now() - readyAt < READINESS_TTL_MS) {
return { modelPulled: false, containerStarted: false, imagePulled: false };
}
const existing = embeddingReadinessInFlight.get(key);
if (existing)
return existing;
const attempt = (async () => {
const provider = await getEmbeddingProvider(onProgress);
const result = await provider.ensureReady();
embeddingReadyAt.set(key, Date.now());
return result;
})();
embeddingReadinessInFlight.set(key, attempt);
try {
return await attempt;
}
finally {
if (embeddingReadinessInFlight.get(key) === attempt) {
embeddingReadinessInFlight.delete(key);
}
}
});
}
/** Reset effective-provider readiness state for tests. */
export function resetEffectiveEmbeddingReadiness() {
embeddingReadyAt.clear();
embeddingReadinessInFlight.clear();
}
export function documentTextProfile(profile) {
return {
documentPrefix: profile.documentPrefix,
documentIncludesPath: profile.documentIncludesPath,
};
}
export function queryProfileKey(profile, collectionName) {
const embeddingIsUnverified = profile.legacyUnverifiedFields.some((field) => field.startsWith("embedding."));
if (embeddingIsUnverified)
return `unverified:${collectionName}`;
return JSON.stringify({
queryPrefix: profile.queryPrefix,
embedding: profile.embedding,
});
}
export function indexProfileDifferences(effective, requested) {
const differences = [];
const compare = (field, a, b) => {
if (JSON.stringify(a) !== JSON.stringify(b))
differences.push(field);
};
compare("indexFormatVersion", effective.indexFormatVersion, requested.indexFormatVersion);
compare("EMBEDDING_QUERY_PREFIX", effective.queryPrefix, requested.queryPrefix);
compare("EMBEDDING_DOCUMENT_PREFIX", effective.documentPrefix, requested.documentPrefix);
compare("EMBEDDING_DOCUMENT_INCLUDE_PATH", effective.documentIncludesPath, requested.documentIncludesPath);
compare("MAX_CHUNK_CHARS", effective.maxChunkChars, requested.maxChunkChars);
compare("EMBEDDING_PROVIDER", effective.embedding.provider, requested.embedding.provider);
compare("EMBEDDING_MODEL", effective.embedding.model, requested.embedding.model);
compare("EMBEDDING_DIMENSIONS", effective.embedding.dimensions, requested.embedding.dimensions);
compare("EMBEDDING_CONTEXT_LENGTH", effective.embedding.contextLength, requested.embedding.contextLength);
compare("LITELLM_SEND_DIMENSIONS", effective.embedding.litellmSendDimensions, requested.embedding.litellmSendDimensions);
if (effective.kind === "code") {
compare("EXTENSION_LANGUAGE_MAP", effective.extensionLanguageMap, requested.extensionLanguageMap);
compare("MAX_FILE_SIZE_MB", effective.maxFileBytes, requested.maxFileBytes);
}
return differences;
}
function assertRecord(value, label) {
if (typeof value !== "object" || value === null || Array.isArray(value)) {
throw new Error(`${label} must be an object.`);
}
return value;
}
function assertString(value, label) {
if (typeof value !== "string")
throw new Error(`${label} must be a string.`);
return value;
}
function assertPositiveInteger(value, label) {
if (!Number.isInteger(value) || value < 1) {
throw new Error(`${label} must be a positive integer.`);
}
return value;
}
function assertInteger(value, label) {
if (!Number.isInteger(value)) {
throw new Error(`${label} must be an integer.`);
}
return value;
}
export function parseEffectiveIndexProfile(value, expectedKind) {
const raw = assertRecord(value, "Effective index profile");
if (raw.schemaVersion !== INDEX_PROFILE_SCHEMA_VERSION) {
throw new Error(`Unsupported effective index profile schema version: ${String(raw.schemaVersion)}.`);
}
if (!Number.isInteger(raw.indexFormatVersion) || raw.indexFormatVersion < 0) {
throw new Error("Effective index profile format version must be a non-negative integer.");
}
if (raw.indexFormatVersion > CURRENT_INDEX_FORMAT_VERSION) {
throw new Error(`Index format version ${String(raw.indexFormatVersion)} is newer than this SocratiCode build supports (${CURRENT_INDEX_FORMAT_VERSION}).`);
}
if (raw.kind !== "code" && raw.kind !== "context") {
throw new Error('Effective index profile kind must be "code" or "context".');
}
if (expectedKind && raw.kind !== expectedKind) {
throw new Error(`Effective index profile kind is ${raw.kind}; expected ${expectedKind}.`);
}
if (raw.source !== "fresh" && raw.source !== "legacy-adopted") {
throw new Error('Effective index profile source must be "fresh" or "legacy-adopted".');
}
if (typeof raw.documentIncludesPath !== "boolean") {
throw new Error("Effective index profile documentIncludesPath must be a boolean.");
}
const embedding = assertRecord(raw.embedding, "Effective embedding identity");
const provider = embedding.provider;
if (provider !== "ollama" &&
provider !== "openai" &&
provider !== "google" &&
provider !== "lmstudio" &&
provider !== "litellm") {
throw new Error("Effective embedding provider is invalid.");
}
if (typeof embedding.litellmSendDimensions !== "boolean") {
throw new Error("Effective embedding litellmSendDimensions must be a boolean.");
}
const legacyUnverifiedFields = raw.legacyUnverifiedFields;
if (!Array.isArray(legacyUnverifiedFields) ||
legacyUnverifiedFields.some((field) => typeof field !== "string")) {
throw new Error("Effective index profile legacyUnverifiedFields must be a string array.");
}
let extensionLanguageMap;
let maxFileBytes;
if (raw.kind === "code") {
const extensionMapRaw = assertRecord(raw.extensionLanguageMap, "Effective extension language map");
extensionLanguageMap = {};
for (const [extension, canonicalExtension] of Object.entries(extensionMapRaw)) {
extensionLanguageMap[extension] = assertString(canonicalExtension, `Effective extension language map value for ${extension}`);
}
maxFileBytes = assertInteger(raw.maxFileBytes, "Effective maxFileBytes");
}
return {
schemaVersion: INDEX_PROFILE_SCHEMA_VERSION,
indexFormatVersion: raw.indexFormatVersion,
kind: raw.kind,
source: raw.source,
queryPrefix: assertString(raw.queryPrefix, "Effective queryPrefix"),
documentPrefix: assertString(raw.documentPrefix, "Effective documentPrefix"),
documentIncludesPath: raw.documentIncludesPath,
maxChunkChars: assertPositiveInteger(raw.maxChunkChars, "Effective maxChunkChars"),
embedding: {
provider,
model: assertString(embedding.model, "Effective embedding model"),
dimensions: assertPositiveInteger(embedding.dimensions, "Effective embedding dimensions"),
contextLength: assertPositiveInteger(embedding.contextLength, "Effective embedding context length"),
litellmSendDimensions: embedding.litellmSendDimensions,
},
...(raw.kind === "code" ? { extensionLanguageMap, maxFileBytes } : {}),
legacyUnverifiedFields: [...legacyUnverifiedFields],
};
}
//# sourceMappingURL=index-profile.js.map