Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- **Lazy-loaded embedding providers no longer mislabel model/runtime failures as "not installed" ([#118](https://github.com/jdutton/vibe-agent-toolkit/issues/118)).** `loadPipeline` in `transformers-embedding-provider.ts` wrapped both the dynamic `import('@xenova/transformers')` and the model download/inference in a single `catch` that always rethrew a fixed `@xenova/transformers is not installed` message, swallowing the real error (not even as `cause`) — so a model-download or `onnxruntime-node` native-backend failure on an installed package was reported as a missing dependency. The two failure modes are now separated: an import failure keeps the actionable install hint (now with the original error attached as `cause`), while a model/inference failure throws `Failed to load transformers model '<model>'` preserving `cause`. The sibling `onnx-embedding-provider.ts` was audited: its install-hint `catch` was already correctly scoped to the import alone, but its model download (`ensureModelFiles`) and session creation (`InferenceSession.create`) previously bubbled raw errors with no provider/model context, so they now throw `Failed to download ONNX model '<model>'` / `Failed to load ONNX model '<model>'` with `cause` preserved.
- **Transformers.js integration tests now skip on Windows CI instead of flaking.** `transformers-embedding-provider.integration.test.ts` and the Transformers.js block of `comparison.integration.test.ts` skip on Windows (in addition to skipping when the optional `@xenova/transformers` dependency is absent), matching the existing `onnx-embedding-provider` test. These tests download a model over the network and load the `onnxruntime-node` native backend — both flaky in Windows CI. Such a failure was previously mislabeled `@xenova/transformers is not installed` by an over-broad `catch` in the provider's `loadPipeline` (the package was installed; the model download/inference is what failed), which is also why an availability-only guard did not prevent it.
- **Config-first skill discovery now honors `..` in `skills.include` patterns.** `vat build`, `vat verify`, and `vat skills validate` all funnel through `discoverSkillsFromConfig`, which previously passed every include pattern to a single downward-only crawl rooted at `projectRoot` — so an include like `"../../docs/skills/*/SKILL.md"` (common in monorepos where SKILL.md sources live alongside, not inside, the package) silently matched zero skills. `vat audit` accepted the same config only because it has a separate filesystem-first walker. Each include pattern is now split into a literal base + glob remainder via `picomatch.scan`, patterns are grouped by their resolved absolute base, and the crawler runs once per base — making config-first discovery agree with audit. User-supplied excludes stay anchored to `projectRoot` so patterns like `docs/private/**` keep their original meaning, and a pattern resolving to a nonexistent base now silently produces zero matches.

Expand Down
24 changes: 18 additions & 6 deletions packages/rag/src/embedding-providers/onnx-embedding-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,19 +202,31 @@ export class OnnxEmbeddingProvider implements EmbeddingProvider {
modelPath = safePath.join(this.configModelPath, 'model.onnx');
vocabPath = safePath.join(this.configModelPath, 'vocab.txt');
} else {
const files = await ensureModelFiles(this.model, this.cacheDir);
modelPath = files.modelPath;
vocabPath = files.vocabPath;
try {
const files = await ensureModelFiles(this.model, this.cacheDir);
modelPath = files.modelPath;
vocabPath = files.vocabPath;
} catch (cause) {
throw new Error(`Failed to download ONNX model '${this.model}': ${String(cause)}`, {
cause,
});
}
}

const sessionOptions = this.executionProviders
? { executionProviders: this.executionProviders }
: undefined;

const session = await ort.InferenceSession.create(modelPath, sessionOptions);
const tokenizer = await BertTokenizer.fromVocabFile(vocabPath);
try {
const session = await ort.InferenceSession.create(modelPath, sessionOptions);
const tokenizer = await BertTokenizer.fromVocabFile(vocabPath);

return { session, tokenizer };
return { session, tokenizer };
} catch (cause) {
throw new Error(`Failed to load ONNX model '${this.model}': ${String(cause)}`, {
cause,
});
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
* Requires optional dependency: npm install @xenova/transformers
*/

import type { pipeline as PipelineFactory } from '@xenova/transformers';

import type { EmbeddingProvider } from '../interfaces/embedding.js';

/**
Expand All @@ -31,16 +33,25 @@ type PipelineFunction = (
async function loadPipeline(
model: string,
): Promise<PipelineFunction> {
let pipeline: typeof PipelineFactory;
try {
const { pipeline } = await import('@xenova/transformers');
return (await pipeline('feature-extraction', model, {
quantized: true,
})) as PipelineFunction;
} catch {
({ pipeline } = await import('@xenova/transformers'));
} catch (cause) {
throw new Error(
'@xenova/transformers is not installed. Install with: npm install @xenova/transformers',
{ cause },
);
}

try {
return (await pipeline('feature-extraction', model, {
quantized: true,
})) as PipelineFunction;
} catch (cause) {
throw new Error(`Failed to load transformers model '${model}': ${String(cause)}`, {
cause,
});
}
}

/**
Expand Down
Loading