@struktoai/mirage-core
Version:
A Unified Virtual Filesystem For AI Agents
134 lines • 5.97 kB
JavaScript
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
import { mountedPath, mountPrefixOf } from "../../utils/key_prefix.js";
import { scoreFromDistance } from "../../utils/score.js";
import { rstripSlash, stripSlash } from "../../utils/slash.js";
import { compareCodePoints } from "../../utils/sort.js";
import { floatOption, intOption, textOption, validateOptions } from "../../vfs/search.js";
import { scalarString } from "../slug_tree/rows.js";
import { searchScope, targetEntries, validateQuery } from "../slug_tree/search.js";
import { PAGE_CHUNK_BATCH_SIZE } from "./client.js";
import { CHROMA_TREE } from "./tree.js";
export async function searchSegments(accessor, query, paths, topK = 10, mountPrefix = '', threshold = 0) {
validateQuery(query, topK);
if (mountPrefix === '' && paths.length > 0) {
mountPrefix =
(paths[0] === undefined ? undefined : mountPrefixOf(paths[0].virtual, paths[0].vfsPath)) ?? '';
}
let scopedSlugs = null;
let where;
if (paths.length > 0) {
scopedSlugs = new Set((await targetEntries(CHROMA_TREE, accessor, paths)).keys());
if (scopedSlugs.size === 0)
return [];
where = {
[accessor.config.slugField]: { $in: [...scopedSlugs].sort(compareCodePoints) },
};
}
const collection = await accessor.getCollection();
const response = await collection.query({
queryTexts: [query],
nResults: topK,
include: ['documents', 'metadatas', 'distances'],
...(where !== undefined ? { where } : {}),
});
return renderRecords(response, accessor.config.slugField, mountPrefix, scopedSlugs, threshold);
}
function renderRecords(response, slugField, mountPrefix, scopedSlugs = null, threshold = 0) {
const documents = firstResultList(response.documents);
const metadatas = firstResultList(response.metadatas);
const distances = firstResultList(response.distances);
const contents = [];
for (let i = 0; i < documents.length; i++) {
const document = documents[i];
const metadata = metadatas[i];
if (metadata === null || typeof metadata !== 'object' || Array.isArray(metadata))
continue;
const slug = scalarString(metadata[slugField]);
if (slug === null)
continue;
const slugValue = stripSlash(slug);
if (scopedSlugs !== null && !scopedSlugs.has(slugValue))
continue;
const score = scoreFromDistance(distances[i]);
if (threshold > 0 && Number(score) < threshold)
continue;
let path = '/' + slugValue;
const prefix = rstripSlash(mountPrefix);
if (prefix !== '')
path = prefix + path;
const content = typeof document === 'string' ? document : '';
contents.push([path, `${path}:${score}\n${content}`]);
}
return contents;
}
function firstResultList(value) {
if (!Array.isArray(value))
return [];
if (value.length > 0 && Array.isArray(value[0]))
return value[0];
return value;
}
export async function searchMany(accessor, paths, query) {
validateOptions(query, ['top_k', 'method', 'threshold']);
const topK = intOption(query, 'top_k', 10);
const threshold = floatOption(query, 'threshold', 0);
if (textOption(query, 'method', 'semantic') !== 'semantic') {
throw new Error("only the 'semantic' method is supported");
}
validateQuery(query.query, topK);
const [targets, prefix] = await searchScope(CHROMA_TREE, accessor, paths);
return searchSegments(accessor, query.query, targets, topK, prefix, threshold);
}
/**
* The pages with a chunk holding `text`.
*
* A page is its chunks joined by line breaks, so a line, and so any match
* grep makes, lies within one chunk, and Chroma's `$contains` over the chunks
* answers exactly. It matches case, so -i reads every page. Below the mount's
* root only the walked pages are asked. Mirrors Python's `files_containing`.
*/
export async function filesContaining(accessor, text, under, ignoreCase) {
const [first] = under;
if (ignoreCase || first === undefined)
return null;
const field = accessor.config.slugField;
let where;
if (under.every((scope) => stripSlash(scope.mountPath) !== '')) {
const walked = await targetEntries(CHROMA_TREE, accessor, under);
if (walked.size === 0)
return [];
where = { [field]: { $in: [...walked.keys()].sort(compareCodePoints) } };
}
const collection = await accessor.getCollection();
const slugs = new Set();
for (let offset = 0;; offset += PAGE_CHUNK_BATCH_SIZE) {
const result = await collection.get({
...(where !== undefined ? { where } : {}),
whereDocument: { $contains: text },
include: ['metadatas'],
limit: PAGE_CHUNK_BATCH_SIZE,
offset,
});
for (const metadata of result.metadatas) {
const slug = scalarString(metadata?.[field]);
if (slug !== null)
slugs.add(stripSlash(slug));
}
if (result.ids.length < PAGE_CHUNK_BATCH_SIZE)
break;
}
return [...slugs].sort(compareCodePoints).map((slug) => mountedPath(first, `/${slug}`));
}
//# sourceMappingURL=search.js.map