Skip to content
Merged
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
18 changes: 16 additions & 2 deletions docs/logs/2026-02-15.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ author: bob <unknown@example.com>
date: 2026-02-15
tz: Europe/Berlin
created: 2026-02-15T00:08:59+01:00
last_section: 2026-02-15T21:03:09+01:00
updated: 2026-02-15T21:03:09+01:00
last_section: 2026-02-15T21:38:03+01:00
updated: 2026-02-15T21:38:03+01:00
---

## 00:08
Expand Down Expand Up @@ -609,3 +609,17 @@ Opened a fresh PR branch from updated main to avoid previously merged/overlappin
## 21:03

Opened new README refresh PR using GitHub App credentials from ~/.config/github-app helper (in-memory token only). PR URL: https://github.com/janitrai/janitr/pull/23.

## 21:19

Changed extension inference to fail hard on transformer errors (no automatic fastText fallback) and clear transformer runtime cache state on failure so stale rejected model promises do not cause persistent fallback loops. Also removed silent HF-run->builtin source fallback in offscreen and normalized legacy 'auto' backend values to transformer; removed 'auto' from popup/options backend selectors to avoid fallback expectations.

Opened PR #24 from fix/transformer-no-fasttext-fallback to main after branch-protection rejection on direct main push. This PR hard-disables transformer->fastText fallback and surfaces transformer errors to users for direct diagnosis.

## 21:29

Reduced extension UI paragraph spacing in options/popup styles and increased content-script debug text preview truncation from 200 to 500 characters for inference logs.

## 21:38

Fixed transformer runtime fetch path: model-repo no longer configures ort wasm paths to Hugging Face URLs for builtin/HF runs; runtime now uses bundled extension ORT assets to avoid 'Failed to fetch' during inference. Added transformer smoke test harness (test page + TS runner + Playwright spec) that downloads/activates a transformer run via extension runtime messages and asserts an obvious scam sample is classified as scam with engine=transformer. Updated extension build entrypoints to emit transformer-smoke test script.
2 changes: 1 addition & 1 deletion extension/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const normalizeEngine = (value) => {
.toLowerCase();
if (candidate === ENGINE_FASTTEXT) return ENGINE_FASTTEXT;
if (candidate === ENGINE_TRANSFORMER) return ENGINE_TRANSFORMER;
if (candidate === ENGINE_AUTO) return ENGINE_AUTO;
if (candidate === ENGINE_AUTO) return ENGINE_TRANSFORMER;
return DEFAULT_ENGINE;
};
const getStorageArea = () => {
Expand Down
2 changes: 1 addition & 1 deletion extension/content-script.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const normalizeText = (text) =>
String(text || "")
.replace(/\s+/g, " ")
.trim();
const previewText = (text, limit = 200) => {
const previewText = (text, limit = 500) => {
const cleaned = normalizeText(text);
if (cleaned.length <= limit) return cleaned;
return `${cleaned.slice(0, limit)}...`;
Expand Down
66 changes: 12 additions & 54 deletions extension/offscreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {
import {
getActiveTransformerSource,
resolveTransformerAssetsForSource,
setActiveTransformerSource,
transformerSourceKey,
} from "./transformer/model-repo.js";
const ENGINE_FASTTEXT = "fasttext";
Expand All @@ -26,8 +25,8 @@ const normalizeEngine = (value) => {
.toLowerCase();
if (candidate === ENGINE_FASTTEXT) return ENGINE_FASTTEXT;
if (candidate === ENGINE_TRANSFORMER) return ENGINE_TRANSFORMER;
if (candidate === ENGINE_AUTO) return ENGINE_AUTO;
return ENGINE_FASTTEXT;
if (candidate === ENGINE_AUTO) return ENGINE_TRANSFORMER;
return ENGINE_TRANSFORMER;
};
let queue = Promise.resolve();
let activeTransformerSourceKey = null;
Expand Down Expand Up @@ -55,24 +54,7 @@ const classifyTextsTransformer = async (texts) => {
const source = await getActiveTransformerSource();
const sourceKey = transformerSourceKey(source);
if (!activeTransformerAssets || activeTransformerSourceKey !== sourceKey) {
try {
activeTransformerAssets = await resolveTransformerAssetsForSource(source);
} catch (error) {
if (source.type === "hf_run") {
if (typeof console !== "undefined" && console.warn) {
console.warn(
"Unable to load selected transformer run; switching back to bundled transformer.",
error,
);
}
await setActiveTransformerSource({ type: "builtin" });
activeTransformerAssets = await resolveTransformerAssetsForSource({
type: "builtin",
});
} else {
throw error;
}
}
activeTransformerAssets = await resolveTransformerAssetsForSource(source);
activeTransformerSourceKey = activeTransformerAssets.sourceKey;
resetTransformerModel();
}
Expand All @@ -86,44 +68,22 @@ const classifyTextsTransformer = async (texts) => {
});
return { results, engine: ENGINE_TRANSFORMER };
};
const resetTransformerRuntimeState = () => {
resetTransformerModel();
activeTransformerSourceKey = null;
activeTransformerAssets = null;
};
const classifyTexts = async (texts, engine) => {
const requested = normalizeEngine(engine);
if (requested === ENGINE_FASTTEXT) {
return classifyTextsFasttext(texts);
}
if (requested === ENGINE_TRANSFORMER) {
try {
return await classifyTextsTransformer(texts);
} catch (err) {
if (typeof console !== "undefined" && console.warn) {
console.warn(
"Transformer inference failed, falling back to fastText.",
err,
);
}
const fallback = await classifyTextsFasttext(texts);
return {
...fallback,
fallbackFrom: ENGINE_TRANSFORMER,
fallbackReason: String(err && err.message ? err.message : err),
};
}
}
try {
return await classifyTextsTransformer(texts);
} catch (err) {
if (typeof console !== "undefined" && console.warn) {
console.warn(
"Transformer inference failed in auto mode, falling back to fastText.",
err,
);
}
const fallback = await classifyTextsFasttext(texts);
return {
...fallback,
fallbackFrom: ENGINE_TRANSFORMER,
fallbackReason: String(err && err.message ? err.message : err),
};
resetTransformerRuntimeState();
const reason = String(err && err.message ? err.message : err);
throw new Error(`Transformer inference failed: ${reason}`);
}
};
chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
Expand All @@ -143,9 +103,7 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
})
.catch((err) => {
resetClassifierModel();
resetTransformerModel();
activeTransformerSourceKey = null;
activeTransformerAssets = null;
resetTransformerRuntimeState();
sendResponse({
ok: false,
error: String(err && err.stack ? err.stack : err),
Expand Down
5 changes: 5 additions & 0 deletions extension/options.css
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ body {
background: radial-gradient(circle at top, #eaf2ff 0%, var(--bg) 45%);
}

p {
margin: 0;
line-height: 1.3;
}

.app {
margin: 0 auto;
padding: 24px;
Expand Down
1 change: 0 additions & 1 deletion extension/options.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ <h2>Backend</h2>
<select id="backend-select">
<option value="transformer">transformer</option>
<option value="fasttext">fasttext</option>
<option value="auto">auto (transformer fallback)</option>
</select>
</section>

Expand Down
7 changes: 6 additions & 1 deletion extension/popup.css
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ body {
color: var(--text);
}

p {
margin: 0;
line-height: 1.25;
}

.app {
padding: 12px;
display: grid;
Expand All @@ -33,7 +38,7 @@ header h1 {
}

header p {
margin: 2px 0 0;
margin: 1px 0 0;
font-size: 0.78rem;
color: var(--muted);
}
Expand Down
1 change: 0 additions & 1 deletion extension/popup.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ <h1>Janitr</h1>
<select id="backend-select">
<option value="transformer">transformer</option>
<option value="fasttext">fasttext</option>
<option value="auto">auto</option>
</select>

<div class="meta-row">
Expand Down
1 change: 1 addition & 0 deletions extension/scripts/build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const ENTRY_POINTS = [
"extension/src/transformer/classifier-transformer.ts",
"extension/src/transformer/model-repo.ts",
"extension/src/tests/wasm-smoke.ts",
"extension/src/tests/transformer-smoke.ts",
];

await build({
Expand Down
2 changes: 1 addition & 1 deletion extension/src/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const normalizeEngine = (value: unknown): Engine => {
.toLowerCase();
if (candidate === ENGINE_FASTTEXT) return ENGINE_FASTTEXT;
if (candidate === ENGINE_TRANSFORMER) return ENGINE_TRANSFORMER;
if (candidate === ENGINE_AUTO) return ENGINE_AUTO;
if (candidate === ENGINE_AUTO) return ENGINE_TRANSFORMER;
return DEFAULT_ENGINE;
};

Expand Down
2 changes: 1 addition & 1 deletion extension/src/content-script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ const normalizeText = (text: unknown): string =>
.replace(/\s+/g, " ")
.trim();

const previewText = (text: unknown, limit = 200): string => {
const previewText = (text: unknown, limit = 500): string => {
const cleaned = normalizeText(text);
if (cleaned.length <= limit) return cleaned;
return `${cleaned.slice(0, limit)}...`;
Expand Down
70 changes: 13 additions & 57 deletions extension/src/offscreen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
import {
getActiveTransformerSource,
resolveTransformerAssetsForSource,
setActiveTransformerSource,
transformerSourceKey,
} from "./transformer/model-repo.js";
import type { ClassifierResult, Engine } from "./types.js";
Expand All @@ -28,8 +27,8 @@ const normalizeEngine = (value: unknown): Engine => {
.toLowerCase();
if (candidate === ENGINE_FASTTEXT) return ENGINE_FASTTEXT;
if (candidate === ENGINE_TRANSFORMER) return ENGINE_TRANSFORMER;
if (candidate === ENGINE_AUTO) return ENGINE_AUTO;
return ENGINE_FASTTEXT;
if (candidate === ENGINE_AUTO) return ENGINE_TRANSFORMER;
return ENGINE_TRANSFORMER;
};

let queue: Promise<void> = Promise.resolve();
Expand Down Expand Up @@ -68,24 +67,7 @@ const classifyTextsTransformer = async (
const sourceKey = transformerSourceKey(source);

if (!activeTransformerAssets || activeTransformerSourceKey !== sourceKey) {
try {
activeTransformerAssets = await resolveTransformerAssetsForSource(source);
} catch (error) {
if (source.type === "hf_run") {
if (typeof console !== "undefined" && console.warn) {
console.warn(
"Unable to load selected transformer run; switching back to bundled transformer.",
error,
);
}
await setActiveTransformerSource({ type: "builtin" });
activeTransformerAssets = await resolveTransformerAssetsForSource({
type: "builtin",
});
} else {
throw error;
}
}
activeTransformerAssets = await resolveTransformerAssetsForSource(source);
activeTransformerSourceKey = activeTransformerAssets.sourceKey;
resetTransformerModel();
}
Expand All @@ -104,8 +86,12 @@ const classifyTextsTransformer = async (
type ClassifiedResponse = {
results: ClassifierResult[];
engine: Engine;
fallbackFrom?: Engine;
fallbackReason?: string;
};

const resetTransformerRuntimeState = (): void => {
resetTransformerModel();
activeTransformerSourceKey = null;
activeTransformerAssets = null;
};

const classifyTexts = async (
Expand All @@ -116,40 +102,12 @@ const classifyTexts = async (
if (requested === ENGINE_FASTTEXT) {
return classifyTextsFasttext(texts);
}
if (requested === ENGINE_TRANSFORMER) {
try {
return await classifyTextsTransformer(texts);
} catch (err: any) {
if (typeof console !== "undefined" && console.warn) {
console.warn(
"Transformer inference failed, falling back to fastText.",
err,
);
}
const fallback = await classifyTextsFasttext(texts);
return {
...fallback,
fallbackFrom: ENGINE_TRANSFORMER,
fallbackReason: String(err && err.message ? err.message : err),
};
}
}

try {
return await classifyTextsTransformer(texts);
} catch (err: any) {
if (typeof console !== "undefined" && console.warn) {
console.warn(
"Transformer inference failed in auto mode, falling back to fastText.",
err,
);
}
const fallback = await classifyTextsFasttext(texts);
return {
...fallback,
fallbackFrom: ENGINE_TRANSFORMER,
fallbackReason: String(err && err.message ? err.message : err),
};
resetTransformerRuntimeState();
const reason = String(err && err.message ? err.message : err);
throw new Error(`Transformer inference failed: ${reason}`);
}
};

Expand All @@ -172,9 +130,7 @@ chrome.runtime.onMessage.addListener(
})
.catch((err: any) => {
resetClassifierModel();
resetTransformerModel();
activeTransformerSourceKey = null;
activeTransformerAssets = null;
resetTransformerRuntimeState();
sendResponse({
ok: false,
error: String(err && err.stack ? err.stack : err),
Expand Down
Loading