Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
67142f5
Make admin analytics dense and searchable on one page
mrmps Sep 20, 2026
33ca638
Fix payment reconciliation during overdue flags, refunds, and provide…
mrmps Sep 20, 2026
ae39576
Add TypeSafe SDK-compatible API (#102)
mrmps Sep 21, 2026
8218b28
Reduce classification latency with west-region Laya and fewer account…
mrmps Sep 21, 2026
6ff9a1a
Apply workspace keys to TypeSafe SDK requests (#104)
mrmps Sep 21, 2026
51b2806
Serve Laya on Beam and add Kev, retiring the Modal deployment (#105)
mrmps Sep 21, 2026
a68b524
Describe the Beam transport in the repository instructions (#106)
mrmps Sep 21, 2026
bed15c9
Bound free inference spending and isolate internal endpoints (#107)
mrmps Sep 22, 2026
7cba8af
Keep anonymous live smart checks within the request allowance (#108)
mrmps Sep 22, 2026
4c1a0c8
Simplify billing to input tokens plus Smart escalations (#109)
mrmps Sep 22, 2026
2f84aed
Fix agent-reported batch recovery and spending error reporting (#110)
mrmps Sep 22, 2026
08fe256
Let paid requests settle into a bounded negative balance (#111)
mrmps Sep 22, 2026
b52c5f0
Add agent testimonials to feedback (#112)
mrmps Sep 22, 2026
d9e89a1
Route documents over 32,000 characters to chunklaya
myxamediyar Sep 23, 2026
f1ba0fa
Merge main into chunklaya-routing
myxamediyar Sep 23, 2026
4b40984
Merge pull request #113 from mrmps/chunklaya-routing
myxamediyar Sep 23, 2026
29961c6
Price the chunklaya route as chunklaya under a spending permit
myxamediyar Sep 23, 2026
5f69538
Merge pull request #114 from mrmps/chunklaya-spending-provider
myxamediyar Sep 23, 2026
4ee8d5d
Let the deploy's anonymous probe pass through the reputation gate
myxamediyar Sep 23, 2026
9bee908
Merge pull request #115 from mrmps/deploy-smoke-check
myxamediyar Sep 23, 2026
3a15930
Give operator inference a separate bounded daily allowance
mrmps Sep 23, 2026
fbae8db
Limit anonymous traffic by label set (#116)
mrmps Sep 23, 2026
a8c08f2
Add paginated admin view of all retained label sets (#117)
mrmps Sep 23, 2026
0c1ea80
Exclude obsolete raw keys from the label registry (#118)
mrmps Sep 23, 2026
8f2bb2b
Index collected label names for the admin catalog (#119)
mrmps Sep 23, 2026
dc6a460
Expose classification token usage and customer pricing (#121)
mrmps Sep 23, 2026
6a078a2
Classify long documents with Jev screening and fixed context pricing …
mrmps Sep 23, 2026
946a56c
Give long-context Jev calls bounded longer deadlines (#123)
mrmps Sep 23, 2026
ad89193
Open POST /v1/systemone to images through the dgemma service
myxamediyar Sep 23, 2026
d697830
Enable paid 10M-token long-context classification jobs (#125)
mrmps Sep 23, 2026
c7553d2
Tighten the dgemma door after review
myxamediyar Sep 23, 2026
da9c3df
Let the System One response schema carry a skipped dgemma answer
myxamediyar Sep 23, 2026
e7f8181
Merge pull request #124 from mrmps/dgemma-systemone
myxamediyar Sep 23, 2026
8c60a1b
Keep the dgemma request's redirects manual
myxamediyar Sep 23, 2026
7282a85
Merge pull request #126 from mrmps/dgemma-redirect-manual
myxamediyar Sep 23, 2026
64e83f0
Support time-limited complimentary Pro grants
mrmps Sep 23, 2026
07cc792
Start complimentary Pro year on verified signup
mrmps Sep 23, 2026
d16139f
Accept whole 10M-token documents in one upload (#127)
mrmps Sep 23, 2026
57ba714
Add URL classification with metered Context.dev scraping (#128)
mrmps Sep 24, 2026
74ed3d9
Restore public chat with classification tools (#130)
mrmps Sep 24, 2026
76741c6
Bring compact admin analytics onto current main
mrmps Sep 24, 2026
1c7a409
Measure chat usage in the unified analytics dashboard
mrmps Sep 24, 2026
aff0600
Align chart units and verify dashboard navigation and export
mrmps Sep 24, 2026
363d8c4
Count every chat classification tool variant
mrmps Sep 24, 2026
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
4 changes: 2 additions & 2 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@
},
"metadata": {
"description": "Plugins from classifier.dev: zero-shot text classification with a calibrated confidence per answer, no API key.",
"version": "1.0.0"
"version": "1.0.1"
},
"plugins": [
{
"name": "classifier",
"source": "./plugins/classifier",
"description": "Sort up to 1,000 texts into your own labels in one call, with a calibrated confidence per answer. No API key.",
"version": "1.0.0",
"version": "1.0.1",
"author": { "name": "Michael Ryaboy", "email": "contact@classifier.dev", "url": "https://classifier.dev" },
"homepage": "https://classifier.dev/mcp-setup",
"repository": "https://github.com/mrmps/classifier-dev",
Expand Down
68 changes: 68 additions & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ permissions:
jobs:
check:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:17
env:
POSTGRES_PASSWORD: spending-test
ports: ["5432:5432"]
options: --health-cmd pg_isready --health-interval 5s --health-timeout 5s --health-retries 10
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
Expand All @@ -23,6 +30,14 @@ jobs:
NEON_MIGRATION_TEST_URL: ${{ secrets.NEON_MIGRATION_TEST_URL }}
- run: node --test
working-directory: cli
- name: Verify paid reservations with concurrent PostgreSQL connections
run: bun test tests/spending.e2e.test.ts
env:
POSTGRES_TEST_URL: postgres://postgres:spending-test@localhost:5432/postgres
- name: Verify document admission under concurrent PostgreSQL requests
run: bun e2e/document-admission.ts
env:
POSTGRES_TEST_URL: postgres://postgres:spending-test@localhost:5432/postgres
- name: Render secret-free build configuration
env:
CLOUDFLARE_ACCOUNT_ID: "00000000000000000000000000000000"
Expand All @@ -31,3 +46,56 @@ jobs:
run: node .github/render-wrangler.mjs
- name: Build application
run: npm run build
- name: Verify chat usage and authenticated analytics
run: npm run test:e2e:chat-analytics
- name: Save chat analytics evidence
uses: actions/upload-artifact@v4
with:
name: chat-analytics-e2e
path: captures/chat-analytics.json
- name: Verify public chat and classification tools
run: npm run test:e2e:chat
- name: Save chat verification evidence
uses: actions/upload-artifact@v4
with:
name: chat-e2e
path: captures/chat-e2e.json
- name: Verify spending through the built Worker and real Durable Objects
run: npm run test:e2e:spending
- name: Verify classification usage responses
run: npm run test:e2e:usage
- name: Verify long-context screening and judgment
run: npm run test:e2e:long-context
- name: Verify URL scraping and billing
run: npm run test:e2e:url
- name: Save URL classification evidence
uses: actions/upload-artifact@v4
with:
name: url-classification-e2e
path: captures/url-classification.json
- name: Verify long-context workspace billing
run: npm run test:e2e:long-context-billing
- name: Verify ten-million-token job and settlement
run: npm run test:e2e:long-context-job
- name: Verify one whole ten-million-token document upload
run: npm run test:e2e:whole-document
- name: Save long-context verification evidence
uses: actions/upload-artifact@v4
with:
name: long-context-e2e
path: |
captures/long-context-e2e.json
captures/long-context-billing.json
captures/long-context-job.json
captures/whole-document.json
captures/document-admission.json
- name: Save classification usage evidence
uses: actions/upload-artifact@v4
with:
name: usage-e2e
path: captures/usage-e2e.json
- name: Save spending verification evidence
uses: actions/upload-artifact@v4
with:
name: spending-e2e
path: captures/spending-e2e.json
74 changes: 70 additions & 4 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,51 @@ jobs:
# maintainer deploys with locally.
- name: Build application
run: npm run build
- name: Verify spending through the built Worker and real Durable Objects
run: npm run test:e2e:spending
- name: Verify classification usage responses
run: npm run test:e2e:usage
- name: Verify long-context screening and judgment
run: npm run test:e2e:long-context
- name: Verify URL scraping and billing
run: npm run test:e2e:url
- name: Save URL classification evidence
uses: actions/upload-artifact@v4
with:
name: url-classification-e2e
path: captures/url-classification.json
- name: Verify long-context workspace billing
run: npm run test:e2e:long-context-billing
- name: Verify complimentary Pro lifecycle
run: bun e2e/complimentary-pro.ts
- name: Verify ten-million-token job and settlement
run: npm run test:e2e:long-context-job
- name: Verify one whole ten-million-token document upload
run: npm run test:e2e:whole-document
- name: Save long-context verification evidence
uses: actions/upload-artifact@v4
with:
name: long-context-e2e
path: |
captures/long-context-e2e.json
captures/long-context-billing.json
captures/long-context-job.json
captures/whole-document.json
- name: Save complimentary Pro verification evidence
uses: actions/upload-artifact@v4
with:
name: complimentary-pro-e2e
path: captures/complimentary-pro.json
- name: Save classification usage evidence
uses: actions/upload-artifact@v4
with:
name: usage-e2e
path: captures/usage-e2e.json
- name: Save spending verification evidence
uses: actions/upload-artifact@v4
with:
name: spending-e2e
path: captures/spending-e2e.json

- name: Migrate account database
env:
Expand All @@ -111,15 +156,25 @@ jobs:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
run: bun scripts/check-newsletter-cutover.ts --activate

# The chunklaya pair is optional and travels together: with both set as
# repository secrets explicit chunklaya requests route there; with either
# missing they are left out of the file, so an existing value survives
# and unconfigured explicit requests answer chunklaya_unavailable. The
# dgemma pair (the image-capable System One service) works the same way.
- name: Deploy
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
CHUNKLAYA_URL: ${{ secrets.CHUNKLAYA_URL }}
CHUNKLAYA_TOKEN: ${{ secrets.CHUNKLAYA_TOKEN }}
DGEMMA_URL: ${{ secrets.DGEMMA_URL }}
DGEMMA_TOKEN: ${{ secrets.DGEMMA_TOKEN }}
run: |
node --input-type=module -e '
import { writeFileSync } from "node:fs";
const { DATABASE_URL, CHUNKLAYA_URL, CHUNKLAYA_TOKEN, DGEMMA_URL, DGEMMA_TOKEN } = process.env;
writeFileSync(process.env.RUNNER_TEMP + "/classifier-secrets.json",
JSON.stringify({ DATABASE_URL: process.env.DATABASE_URL }), { mode: 0o600 });
JSON.stringify({ DATABASE_URL, ...(CHUNKLAYA_URL && CHUNKLAYA_TOKEN ? { CHUNKLAYA_URL, CHUNKLAYA_TOKEN } : {}), ...(DGEMMA_URL && DGEMMA_TOKEN ? { DGEMMA_URL, DGEMMA_TOKEN } : {}) }), { mode: 0o600 });
'
npx wrangler deploy --secrets-file "$RUNNER_TEMP/classifier-secrets.json"

Expand All @@ -132,9 +187,20 @@ jobs:
--retry-all-errors https://classifier.dev/v1/health)
echo "$health"
echo "$health" | grep -q '"ok": true'
label=$(curl --fail --silent --show-error https://classifier.dev/spam,not+spam/Win+a+free+iPhone)
echo "classify -> $label"
test "$label" = "spam"
# The anonymous probe runs from a hosted runner, whose IP the free-traffic
# reputation gate may refuse (403 proxy_requires_payment). That is the
# Worker answering with its own gate, not a broken deploy, so it passes
# with a notice; every other non-200 fails. The body is always printed,
# so a failure here says what was answered rather than only that it was.
code=$(curl --silent --show-error -o classify.json -w '%{http_code}' -H 'accept: application/json' \
https://classifier.dev/spam,not+spam/Win+a+free+iPhone)
echo "classify -> HTTP $code"; cat classify.json; echo
case "$code" in
200) grep -Eq '"label": *"spam"' classify.json ;;
403) grep -Eq '"code": *"proxy_requires_payment"' classify.json &&
echo "::notice::the anonymous probe was refused by the reputation gate for this runner's IP; the Worker is live" ;;
*) echo "::error::anonymous classification answered HTTP $code"; exit 1 ;;
esac
- name: Check multidimensional classification end to end
env:
CLASSIFIER_BASE_URL: https://classifier.dev
Expand Down
51 changes: 49 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# AGENTS.md — working in this repository

classifier.dev is one Cloudflare Worker (`src/index.ts`) with no runtime
dependencies, a one-file CLI (`cli/classify.js`) and a Python eval harness
classifier.dev is one Cloudflare Worker (`src/index.ts`),
a one-file CLI (`cli/classify.js`) and a Python eval harness
(`eval/`). Everything the site says is generated from constants in `src/`, so
the plain text (`curl classifier.dev`), the HTML and the Markdown never drift.

Expand All @@ -24,6 +24,7 @@ the plain text (`curl classifier.dev`), the HTML and the Markdown never drift.
- Docs are plain text with UPPERCASE headings (`src/docs.ts`, `src/pages.ts`); `renderDoc` turns them into HTML and `toMarkdown` into Markdown.
- Discovery files (`/.well-known/*`, sitemap, robots, auth.md) are generated in `src/wellknown.ts` from `SITE` and the MCP tool table — edit the source, never a served file.
- The MCP servers (`src/mcp.ts`) are stateless Streamable HTTP; tools call the API through `worker.fetch` so limits and logging are shared.
- Whole-document uploads use `POST /v1/classify`: `src/document-upload.ts` stream-parses JSON or UTF-8 text up to 10M tokens/100 MB, preserving exact cl100k counts across internal fragment boundaries. `src/long-context-job.ts` stores source privately while queued, deletes it as SQLite Durable Object alarms screen it, then judges and settles automatically. Reserve the actual document price after upload. Delete remaining source/evidence on failure, cancellation or 24-hour expiry. The old manual-part API remains supported. Keep generated docs and `e2e/whole-document.mjs` in sync; test through the built Worker, real Durable Objects and PostgreSQL ledger.
- Never commit secrets; `.secrets.env`, `.dev.vars` are ignored. `eval/data/` is ignored except the summary copied to `src/vs-jev.json`.
- Measured numbers on the site come from `eval/`; do not type numbers in by hand.
- Jev is asked through Vercel's AI Gateway first when `AI_GATEWAY_API_KEY`
Expand All @@ -32,6 +33,52 @@ the plain text (`curl classifier.dev`), the HTML and the Markdown never drift.
when the gateway refuses; both transports and the translation between them
live in `src/jev.ts`. Nothing downstream should know which door answered
beyond the `model` label.
- Laya (`jev/laya`) and Kev (`jev/kev`) are hosted by Beam, which speaks the
same System One protocol, so they are a third transport in `src/jev.ts`
rather than a second client: one packer, one retry policy, one validator,
one meter, selected by a `Backend` descriptor. `src/laya.ts` owns only the
product contract — lanes, caller limits, quota cost. `BEAM_API_KEY` is the
single credential; there is no deployment of ours and nothing on Modal.
Beam refuses more than 32 named questions per request and rejects an
oversized context rather than truncating, so a context refusal is
translated to `max_tokens_exceeded` and the batch halves and retries.
Unlike Jev, a Beam request is never retried: a lane quota counts attempts.
- Default/explicit Jev inputs over 32,000 characters use long-context Jev:
Chonkie RecursiveChunker with 600 cl100k_base tokens, parallel relevance/
uncertainty screening that preserves opposing evidence and exceptions,
then whole eligible chunks in source order for final Jev within 20,000
cl100k_base tokens and a safe provider estimate. Eligible evidence can be
omitted when the budget fills; disclose selection in usage.long_context.
Limits: 250,000 original context tokens summed once across inputs, 20
documents, 32 decisions (documents × dimensions or multi-label categories),
1 MB request body. Fast only. Require a workspace
with paid balance or active paid subscription; signup credit is insufficient.
Retail is original context tokens × 2 × $0.042/M, independent of dimensions
and actual screening/final usage. No evidence returns 422
long_context_no_evidence without charge. Never claim universal accuracy or
that the final call reads the full original document.
Preserve existing trusted dedicated enterprise/operator access. Aggregate
long-context counts go to classifier_long_context_events and appended
account analytics fields; never record document text or caller identifiers.
- Explicit chunklaya (`chunklaya/multilingual`) is our legacy opt-in service: Laya
behind a chunk-and-index harness, github.com/myxamediyar/chunklaya under
`serve/`, on a RunPod pod. It speaks System One too, so it is a fourth
transport in `src/jev.ts`, reached through `CHUNKLAYA_URL` and
`CHUNKLAYA_TOKEN` (Worker secrets). It requires explicit model: "chunklaya",
`CHUNKLAYA_ENABLED` set to `"true"` and both secrets; it is never selected
automatically for long text. One document is one
request; the service refuses rather than truncates, its 4xx become
`chunklaya_input`, and there is no fallback to Jev or the LLM chain. It is
billed by the hour, so no per-token provider cost is metered.
- dgemma (`dgemma`) is DiffusionGemma 26B-A4B in vLLM's structured-read mode
(vllm-project/vllm#57250), on a RunPod pod, behind the PR's `/v1/systemone`
interposer, which speaks System One and reads an `images` array. It is the
image door of POST /v1/systemone: `src/dgemma.ts` sends a body that names
model "dgemma" or carries images there, with `DGEMMA_URL` and `DGEMMA_TOKEN`
(Worker secrets) when `DGEMMA_ENABLED` is `"true"`; every other body stays
TypeSafe's. Nothing answers for the other: a refused body is `dgemma_input`,
a busy service `dgemma_busy`, a down or unconfigured one `dgemma_unavailable`,
and images under another model `images_unsupported`. Billed by the hour.
- The updates roadmap is one constant, `ROADMAP` in `src/newsletter.ts`; the plain
text, the signup form and the Markdown all render from it. Addresses go to the
`subscriber` table in the shared application Neon database. Preserve consent,
Expand Down
Loading
Loading