Skip to content

feat(api): understand unfamiliar failures and moderate supporter text with AI - #254

Open
abarghoud wants to merge 3 commits into
testingfrom
feat/typesafe-jev-error-classification
Open

abarghoud wants to merge 3 commits into
testingfrom
feat/typesafe-jev-error-classification

Conversation

@abarghoud

@abarghoud abarghoud commented Sep 18, 2026 •

Copy link
Copy Markdown
Owner

What this does

SentryGuard needs to know why a notification or a Tesla request failed, so it can react correctly: stop writing to someone who blocked the bot, retry what is worth retrying, and leave the rest alone.

Until now that decision came from a fixed list of keywords, so any wording it had not seen before was simply treated as "unknown". This adds an AI judgment (TypeSafe / Jev) as a second opinion, consulted only for wording the existing keyword list does not recognise.

The same mechanism moderates the names and messages shown on the supporters wall.

What changes in practice

  • Failures phrased in an unfamiliar way are now understood instead of ignored
  • The supporters wall filters profanity, spam and political content, including deliberately disguised spellings, while leaving genuine names alone
  • Nothing is required to adopt it: with no API key configured, the app behaves exactly as it does today

Safety

  • The AI is never on the critical path of an alert. Every judgment is time-boxed, and one that runs out of time falls back to the previous behaviour. It can cost classification accuracy, never a delivered alert.
  • Supporter moderation runs when a donation arrives, not when the page is viewed, so the public page stays as fast as before.
  • Every judgment is logged with its outcome, confidence, duration and cost, so the results can be reviewed once deployed. Donor names and messages are never written to the logs.

Notes

TYPESAFE_API_KEY must be set for this to be active in production; it is optional everywhere else.

Results on sample failures

Each pair below describes the same situation. The keyword list only recognises the first wording of each pair.

Failure wording Before After
Forbidden: bot was blocked by the user bot blocked bot blocked
The user has blocked the bot and messages cannot be delivered unknown bot blocked
Bad Request: chat not found chat gone chat gone
Failed to fetch conversation: the chat is no longer available unknown chat gone
socket hang up while sending update unknown temporary, retry
The user has revoked access for this application unknown token revoked
Tesla gateway unreachable, free wording unknown vehicle unreachable

Supporters wall

Input Result
Alexandre, Marine Dupont, Jean Dupon(d) kept
D1ckens family fund kept
Conn@rd, C0nnard, FUCK YOU, Hitler anonymised
Marine Le Pen, Trump 2028!, Votez pour nous aux européennes removed
Buy my crypto course, guaranteed returns removed
contact me at alice@example.com removed

These come from replaying hand-picked wordings and real production error samples, not from a production measurement. The judgment logs will give the real distribution once deployed.

@abarghoud
abarghoud force-pushed the feat/typesafe-jev-error-classification branch 4 times, most recently from 9152467 to d4ea573 Compare September 18, 2026 21:11
Add two Jev judgments behind deterministic guards, so the API keeps its
previous behaviour when TYPESAFE_API_KEY is unset.

- ErrorMeaningClassifierService: choice() judgments for Telegram send
  failures and Tesla API failures. The keyword matcher runs first and Jev is
  consulted only for wording it does not recognise, so known errors stay
  instant on the notification path. Verdicts are memoised per error object,
  so the two predicates TelegramService applies to a failure cost at most one
  judgment.
- Supporter moderation: noul() probabilities for profanity, spam and politics
  with block and review thresholds. It runs at webhook ingestion only. The
  blocklist judge is the default everywhere, so a caller that forgets to pass
  one cannot reach the network, and the public uncached GET /supporters never
  issues a judgment.

Judgments are bounded: 2s and no retry on the alert path, 10s and one retry
at ingestion. The classifier runs inside the catch block of the Telegram
send, outside the withTimeout that caps the send, so the SDK defaults would
otherwise let one judgment hold a notification worker for ~30s. Five workers
feed a queue that drops alerts once full. A judgment is an optimisation over
the keyword matcher, so expiring it costs accuracy, never delivery.

The client is provided through a DI token rather than a constructor default,
which Nest cannot resolve; a test compiles the container to keep it that way.

Every judgment emits [TYPESAFE_JUDGMENT] with its outcome, confidence,
duration, model and token counts, and failures emit
[TYPESAFE_JUDGMENT_FAILED], both reaching the OCI logging pipeline. Supporter
moderation judges personal data, so its log carries the decision and the
probabilities but never the text.
@abarghoud
abarghoud force-pushed the feat/typesafe-jev-error-classification branch from d4ea573 to 2d5c3b5 Compare September 18, 2026 21:19
@abarghoud
abarghoud requested a review from JordiMa September 18, 2026 21:21
@vercel
vercel Bot temporarily deployed to Preview – testing-webapp September 18, 2026 22:04 Inactive
JordiMa
JordiMa previously approved these changes Sep 26, 2026

@JordiMa JordiMa left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, let's send it out for testing! And see how it goes

…error-classification

# Conflicts:
#	package.json

This branch was successfully deployed

1 active deployment
Preview – testing-webapp — 4de5359e Deployed Oct 11, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants