- AI tagging through any OpenAI-compatible API: DeepSeek by default, or OpenRouter, OpenAI, or a local server (Ollama, LM Studio). Settings → AI with presets, prices and "Save & test connection"; the key never leaves the server except to the provider. - Runs over untagged models, all models, or one model from its page: live progress, tokens and cost, stop/resume (retrying failures), an optional spending limit, and paused rather than resumed after a restart. Adapts to models without JSON mode or image input; stops on a rejected key or no credit. Tags are only added; descriptions filled only where empty unless asked. - Search by meaning: local bge-small-en-v1.5 embeddings (transformers.js), indexed in the background whenever a model's text changes; Words/Meaning switch on the search box. Stored as REAL[] in Postgres, no pgvector needed. - Automatic tags: strict whole-word rules (part types, mechanisms, materials, techniques) over names, file names and archive contents, plus your own word -> tag rules; on/off (optionally removing its tags) and re-apply. - Tags remember where they came from (user / rule / ai); edits keep that, rule and AI passes never touch your own tags, and the model page marks them. - Docker: ships the embedding model and only Linux onnxruntime binaries. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
33 lines
1.7 KiB
JavaScript
33 lines
1.7 KiB
JavaScript
// Automatic tagging rules (lib/autotag.js).
|
|
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const { tokenize, matchTags } = require('../lib/autotag');
|
|
|
|
test('names split into words at punctuation, camelCase and numbers', () => {
|
|
assert.deepEqual(tokenize('PETGWallHook_v2.stl'), ['petg', 'wall', 'hook', 'v', '2', 'stl']);
|
|
assert.deepEqual(tokenize('Gridfinity-6x4 baseplate'), ['gridfinity', '6', 'x', '4', 'baseplate']);
|
|
});
|
|
|
|
test('rules match whole words only, plurals included', () => {
|
|
assert.deepEqual(matchTags(['Wall Hooks.stl']), ['hook']);
|
|
assert.deepEqual(matchTags(['facebook_logo.stl', 'blocked-path.3mf', 'planter.stl', 'absolute.obj']), []);
|
|
assert.deepEqual(matchTags(['gears_and_threads.3mf']).sort(), ['gear', 'threaded']);
|
|
});
|
|
|
|
test('phrases match across separators but not across different names', () => {
|
|
assert.deepEqual(matchTags(['Print-In-Place_Dragon.3mf']), ['print-in-place']);
|
|
assert.deepEqual(matchTags(['Dragon Print', 'in place']), []);
|
|
assert.deepEqual(matchTags(['Spiral Vase Mode lamp']), ['vase mode']);
|
|
});
|
|
|
|
test('materials and techniques are picked up', () => {
|
|
assert.deepEqual(matchTags(['benchy_PLA.3mf', 'TPU tyre']).sort(), ['pla', 'tpu']);
|
|
assert.deepEqual(matchTags(['MultiColour Keyring']).sort(), ['keychain', 'multicolor']);
|
|
});
|
|
|
|
test('your own rules add tags the same strict way', () => {
|
|
const rules = [{ phrase: 'benchy', tag: 'Calibration' }, { phrase: 'cable tie', tag: 'cable management' }];
|
|
assert.deepEqual(matchTags(['3DBenchy.stl'], rules), ['calibration']);
|
|
assert.deepEqual(matchTags(['cable_ties_holder.stl'], rules).sort(), ['cable management', 'holder']);
|
|
assert.deepEqual(matchTags(['benchymark.stl'], rules), []);
|
|
});
|