{"_id":"@animakit/complexity-scorer","_rev":"6-8476c6904690202e40514cda0d58b545","name":"@animakit/complexity-scorer","dist-tags":{"latest":"0.1.4"},"versions":{"0.0.1":{"name":"@animakit/complexity-scorer","version":"0.0.1","keywords":["llm","agents","routing","complexity","model-routing","cost-optimization"],"author":{"name":"Justine Serna Aza"},"license":"MIT","_id":"@animakit/complexity-scorer@0.0.1","maintainers":[{"name":"andrrewcorp","email":"andrew.corpdesing@gmail.com"}],"homepage":"https://github.com/animakit-ai/anima#readme","bugs":{"url":"https://github.com/animakit-ai/anima/issues"},"dist":{"shasum":"e62ec6c68b93248ea12c9c90f52a7a19ed7b1870","tarball":"https://registry.npmjs.org/@animakit/complexity-scorer/-/complexity-scorer-0.0.1.tgz","fileCount":4,"integrity":"sha512-r4UHKhCZjD9bkVxx1w7UWQosyQQBtAIgkMjnJuXpi1/XuWkesZAHjVi8p8A5wE2DwdrUnd9hyHWgDyex0rfaiA==","signatures":[{"sig":"MEQCIFRr0jQtv7gXW6dencGMY88Ejm6n7VDKIGBf80lVRG3cAiB6rx3zLFF/LAb127Z4upUoSgSWIj5/dXuhDRAA63U/tQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":4393},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"22f5a989c3b022ab347599490e034e7507bbd2d9","scripts":{"test":"vitest run --coverage","bench":"node --import tsx benchmarks/latency.ts","build":"tsup src/index.ts --format esm --dts --clean","typecheck":"tsc --noEmit"},"_npmUser":{"name":"andrrewcorp","email":"andrew.corpdesing@gmail.com"},"repository":{"url":"git+https://github.com/animakit-ai/anima.git","type":"git","directory":"packages/complexity-scorer"},"_npmVersion":"11.11.1","description":"Classify LLM tasks in <1ms with zero tokens - 5 weighted signals to complexity tier, before you ever call an LLM.","directories":{},"sideEffects":false,"_nodeVersion":"24.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/complexity-scorer_0.0.1_1781095938972_0.8264871295192002","host":"s3://npm-registry-packages-npm-production"}},"0.1.0":{"name":"@animakit/complexity-scorer","version":"0.1.0","keywords":["llm","agents","routing","complexity","model-routing","cost-optimization"],"author":{"name":"Justine Serna Aza"},"license":"MIT","_id":"@animakit/complexity-scorer@0.1.0","maintainers":[{"name":"andrrewcorp","email":"andrew.corpdesing@gmail.com"}],"homepage":"https://github.com/animakit-ai/anima#readme","bugs":{"url":"https://github.com/animakit-ai/anima/issues"},"dist":{"shasum":"1f0b3e000f88d13e4ecab4af154594176aa56de4","tarball":"https://registry.npmjs.org/@animakit/complexity-scorer/-/complexity-scorer-0.1.0.tgz","fileCount":4,"integrity":"sha512-UCFfVsP2blCJcUxPHX3J9UKM4yNZ4hQWa/z4LsMZ2Ab+zlgi3FVfpwsvNdZb9K9kjCyV1z0hCnx0HFi4WJxzrA==","signatures":[{"sig":"MEUCIQC0a84XGZW32WL0XYQ4cd2KFg/Y1ZZPDIKebezWCi9b/gIgd5JYSxYBVl7ua+M2vYRv0TfBv3fdPcw/l517DAE3Qw4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":25691},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"88a389d209cfc045c7a7a10947e9d27e528878c3","scripts":{"test":"vitest run --coverage","bench":"node --import tsx benchmarks/latency.ts","build":"tsup src/index.ts --format esm --dts --clean","typecheck":"tsc --noEmit"},"_npmUser":{"name":"andrrewcorp","email":"andrew.corpdesing@gmail.com"},"repository":{"url":"git+https://github.com/animakit-ai/anima.git","type":"git","directory":"packages/complexity-scorer"},"_npmVersion":"11.11.1","description":"Classify LLM tasks in <1ms with zero tokens - 5 weighted signals to complexity tier, before you ever call an LLM.","directories":{},"sideEffects":false,"_nodeVersion":"24.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/complexity-scorer_0.1.0_1781150134859_0.41758010788486466","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@animakit/complexity-scorer","version":"0.1.1","keywords":["llm","agents","routing","complexity","model-routing","cost-optimization"],"author":{"name":"Justine Serna Aza"},"license":"MIT","_id":"@animakit/complexity-scorer@0.1.1","maintainers":[{"name":"andrrewcorp","email":"andrew.corpdesing@gmail.com"}],"homepage":"https://github.com/animakit-ai/anima#readme","bugs":{"url":"https://github.com/animakit-ai/anima/issues"},"dist":{"shasum":"428c6eb1fbb6deddd2422631aedaaf3ae195973f","tarball":"https://registry.npmjs.org/@animakit/complexity-scorer/-/complexity-scorer-0.1.1.tgz","fileCount":6,"integrity":"sha512-vUusXvyifhidc7TibJJzgRXnFXJfEBeN51dJBOmgKBoOvsn0mHRPVxmTTHDaZ21SEfW/b5M2h47eSf/a+rkLIg==","signatures":[{"sig":"MEYCIQCNxfWtsL6K3IrcqRvZMArPP4DFgf/5nNNFy3Cpvo4uPgIhAOA3iQ/+lDAxZRNm8/b+SeIUG/+OwzXVwM5H/lw94gF5","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":45755},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"9ecb9f112464c2fc5e3d50a5c97f9a2124b9e92e","scripts":{"test":"vitest run --coverage","bench":"node --import tsx benchmarks/latency.ts","build":"tsup src/index.ts --format esm,cjs --dts --clean","typecheck":"tsc --noEmit"},"_npmUser":{"name":"andrrewcorp","email":"andrew.corpdesing@gmail.com"},"repository":{"url":"git+https://github.com/animakit-ai/anima.git","type":"git","directory":"packages/complexity-scorer"},"_npmVersion":"11.11.1","description":"Classify LLM tasks in <1ms with zero tokens - 5 weighted signals to complexity tier, before you ever call an LLM.","directories":{},"sideEffects":false,"_nodeVersion":"24.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/complexity-scorer_0.1.1_1781152582413_0.13661213954389195","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@animakit/complexity-scorer","version":"0.1.2","keywords":["llm","agents","routing","complexity","model-routing","cost-optimization"],"author":{"name":"Justine Serna Aza"},"license":"MIT","_id":"@animakit/complexity-scorer@0.1.2","maintainers":[{"name":"andrrewcorp","email":"andrew.corpdesing@gmail.com"}],"homepage":"https://github.com/animakit-ai/anima#readme","bugs":{"url":"https://github.com/animakit-ai/anima/issues"},"dist":{"shasum":"b077c3257abc0b9e4d7b8096d5e6f48506c6a7bb","tarball":"https://registry.npmjs.org/@animakit/complexity-scorer/-/complexity-scorer-0.1.2.tgz","fileCount":6,"integrity":"sha512-r77KMbMCUM0l5yoPkXEOPXdypeF/cYh68mj4hA7AEF3uOY0F57yAMIK9yVw+6UuYj/MK6jphliwzT9fY+e/KcA==","signatures":[{"sig":"MEUCIFR60+tFm8+qHt8yCSQ/a976rCVT59MRwOhz4Eg4pyXmAiEAk/HYzN0pFyXdih1kmWwagVkyg1hss8EwpaNZiE8hRK0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":45854},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"gitHead":"9ecb9f112464c2fc5e3d50a5c97f9a2124b9e92e","scripts":{"test":"vitest run --coverage","bench":"node --import tsx benchmarks/latency.ts","build":"tsup src/index.ts --format esm,cjs --dts --clean","typecheck":"tsc --noEmit"},"_npmUser":{"name":"andrrewcorp","email":"andrew.corpdesing@gmail.com"},"repository":{"url":"git+https://github.com/animakit-ai/anima.git","type":"git","directory":"packages/complexity-scorer"},"_npmVersion":"11.11.1","description":"Classify LLM tasks in <1ms with zero tokens - 5 weighted signals to complexity tier, before you ever call an LLM.","directories":{},"sideEffects":false,"_nodeVersion":"24.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/complexity-scorer_0.1.2_1781152695820_0.8458284691208575","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@animakit/complexity-scorer","version":"0.1.3","keywords":["llm","agents","routing","complexity","model-routing","cost-optimization"],"author":{"name":"Justine Serna Aza"},"license":"MIT","_id":"@animakit/complexity-scorer@0.1.3","maintainers":[{"name":"andrrewcorp","email":"andrew.corpdesing@gmail.com"}],"homepage":"https://github.com/animakit-ai/anima#readme","bugs":{"url":"https://github.com/animakit-ai/anima/issues"},"dist":{"shasum":"cb285f58502599b5a99282fc83ba4393fafede9d","tarball":"https://registry.npmjs.org/@animakit/complexity-scorer/-/complexity-scorer-0.1.3.tgz","fileCount":6,"integrity":"sha512-BxzcFM1WQcE6kCERaX52nDm7UjjgE+/AlOmScdwk2mDwAPaaHhTDePgSR94CQBpZ9wpKK6qn4qoI1LbxQewiXA==","signatures":[{"sig":"MEUCIB+MP61kv7SBu3IJjhnWCa8cAs/qOORg5cJtv8RqcNI7AiEA6dKx5Orc5ZdqLqtdaXBvi0gH+SgkbzycDc+EWdeAMoY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":46229},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20"},"exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"gitHead":"5a2853f20544f7518f50988fedf163148d58a60d","scripts":{"test":"vitest run --coverage","bench":"node --import tsx benchmarks/latency.ts","build":"tsup src/index.ts --format esm,cjs --dts --clean","typecheck":"tsc --noEmit"},"_npmUser":{"name":"andrrewcorp","email":"andrew.corpdesing@gmail.com"},"repository":{"url":"git+https://github.com/animakit-ai/anima.git","type":"git","directory":"packages/complexity-scorer"},"_npmVersion":"11.11.1","description":"Classify LLM tasks in <1ms with zero tokens - 5 weighted signals to complexity tier, before you ever call an LLM.","directories":{},"sideEffects":false,"_nodeVersion":"24.14.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/complexity-scorer_0.1.3_1781176584042_0.3499988958877307","host":"s3://npm-registry-packages-npm-production"}},"0.1.4":{"name":"@animakit/complexity-scorer","version":"0.1.4","description":"Classify LLM tasks in <1ms with zero tokens - 5 weighted signals to complexity tier, before you ever call an LLM.","license":"MIT","author":{"name":"Justine Serna Aza"},"repository":{"type":"git","url":"git+https://github.com/animakit-ai/anima.git","directory":"packages/complexity-scorer"},"keywords":["llm","agents","routing","complexity","model-routing","cost-optimization"],"type":"module","main":"./dist/index.cjs","types":"./dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"sideEffects":false,"engines":{"node":">=20"},"scripts":{"build":"tsup src/index.ts --format esm,cjs --dts --clean","test":"vitest run --coverage","typecheck":"tsc --noEmit","bench":"node --import tsx benchmarks/latency.ts"},"publishConfig":{"access":"public"},"gitHead":"eb193a4d3d2ddd8f40b63996b59fc33e50dee999","_id":"@animakit/complexity-scorer@0.1.4","bugs":{"url":"https://github.com/animakit-ai/anima/issues"},"homepage":"https://github.com/animakit-ai/anima#readme","_nodeVersion":"24.14.0","_npmVersion":"11.11.1","dist":{"integrity":"sha512-7wWCJPXIp7dx3HEsQzQb4VgyeyslmzbPmcTltow8ezVcCSbhWVNTe34THbD05mAJg7BugppBKmI83iE/zNtv8A==","shasum":"38e7f11c519fc15277836e020e84db46c04e8e50","tarball":"https://registry.npmjs.org/@animakit/complexity-scorer/-/complexity-scorer-0.1.4.tgz","fileCount":6,"unpackedSize":47336,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIFUM2asAt6dnmY02ww/sKjVYKnbXSM49xBScO8JiWANpAiBSHwSSW7gabeiueC2/isorX59mwuuEBSIYUQAoIeTjnQ=="}]},"_npmUser":{"name":"andrrewcorp","email":"andrew.corpdesing@gmail.com"},"directories":{},"maintainers":[{"name":"andrrewcorp","email":"andrew.corpdesing@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/complexity-scorer_0.1.4_1781181821463_0.6396898413237271"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-10T12:52:18.756Z","modified":"2026-06-11T12:43:41.714Z","0.0.1":"2026-06-10T12:52:19.101Z","0.1.0":"2026-06-11T03:55:35.001Z","0.1.1":"2026-06-11T04:36:22.546Z","0.1.2":"2026-06-11T04:38:15.960Z","0.1.3":"2026-06-11T11:16:24.179Z","0.1.4":"2026-06-11T12:43:41.584Z"},"bugs":{"url":"https://github.com/animakit-ai/anima/issues"},"author":{"name":"Justine Serna Aza"},"license":"MIT","homepage":"https://github.com/animakit-ai/anima#readme","keywords":["llm","agents","routing","complexity","model-routing","cost-optimization"],"repository":{"type":"git","url":"git+https://github.com/animakit-ai/anima.git","directory":"packages/complexity-scorer"},"description":"Classify LLM tasks in <1ms with zero tokens - 5 weighted signals to complexity tier, before you ever call an LLM.","maintainers":[{"name":"andrrewcorp","email":"andrew.corpdesing@gmail.com"}],"readme":"# @animakit/complexity-scorer\n\n> **Classify LLM tasks in <1ms with zero tokens — before you ever call an LLM.**\n\nFive weighted lexical signals → a complexity tier (`micro | small | medium | large`), so your caller decides which model handles each message. Pure functions, zero dependencies, zero I/O. Extracted from the production router of [ANIMA](https://github.com/animakit-ai/anima) — an agent that has been running a real business since February 2026.\n\n```bash\nnpm install @animakit/complexity-scorer\n```\n\n```ts\nimport { scoreComplexity } from '@animakit/complexity-scorer';\n\nconst { tier, score, signals } = scoreComplexity(\n  'Analyze the impact of raising prices on churn and MRR under three scenarios',\n);\n// tier: 'medium' — route to a frontier model\n// signals: { length, domain, structure, reasoning, contextRequired } — fully explainable\n\nscoreComplexity('thanks, perfect!').tier; // 'micro' — local model, $0\n```\n\n## Why\n\nMost agent frameworks send every message to the same (expensive) model, or spend 200-500 tokens asking an LLM \"how hard is this?\". On our real production traffic, **85% of messages never needed a frontier model**:\n\n| tier | share of real production traffic (353 human messages) |\n|---|---|\n| micro | 84.7% |\n| small | 4.2% |\n| medium | 10.2% |\n| large | 0.8% |\n\nSimulated routing cost on that corpus vs sending everything to a frontier model (published June 2026 pricing, $10/$50 per M tokens): **31.6% cheaper** — and that number deserves an honest footnote. The *miscalibrated* original thresholds \"saved\" 99%+ by under-routing everything to free local models; the calibrated defaults route 11% of messages to frontier models because they genuinely need them, and those long messages carry most of the cost mass. **Routing correctly costs more than routing badly — 31.6% is the honest number.** Reproduce with `benchmarks/replay.ts` on your own traffic and prices.\n\n## Latency\n\n10k iterations over a mixed corpus (Node 24, consumer CPU):\n\n| path | p50 | p95 | p99 |\n|---|---|---|---|\n| `createScorer()` precompiled (hot path) | 9.4µs | 63.6µs | **86µs** |\n| `scoreComplexity()` default singleton | 10.0µs | 63.1µs | 100.1µs |\n\nRelease gate: p99 < 1ms — currently passing with 11x margin. A performance regression blocks release (`benchmarks/latency.ts` runs in CI).\n\n## Validated against human judgment — including what it gets wrong\n\nWe blind-labeled 50 production messages (the operator who ran the agent for 53 sprints labeled which model tier each message *actually needed*). Results with the default thresholds, leave-one-out cross-validated:\n\n- **52% exact tier agreement, 82% within one tier**\n- Errors are asymmetric by design: 38% under-routing vs 4% over-routing after calibration (the original production thresholds under-routed 54% — the calibration data ships in `benchmarks/`)\n\n**The honest part:** the residual failure mode is *short, context-dependent messages* — \"so what should the priority be?\" needs real reasoning but contains zero lexical complexity markers. **No lexical scorer can see those by definition.** If your traffic is heavy on terse high-stakes questions, pair this scorer with behavioral feedback (that's what we're building next — see the [conformal routing work](https://github.com/animakit-ai/anima/tree/main/experiments/conformal-routing)).\n\n## API\n\n```ts\n// One-off (default bilingual EN+ES config)\nscoreComplexity(message: string, config?: ComplexityScorerConfig): ComplexityResult;\n\n// Hot path — precompiles vocabulary once\nconst score = createScorer(config);\nscore(message); // ~10µs\n\ninterface ComplexityResult {\n  score: number;                       // 0-1 weighted composite\n  signals: ComplexitySignals;          // the 5 sub-signals — explain every decision\n  tier: 'micro' | 'small' | 'medium' | 'large';\n}\n```\n\nEverything is configurable: signal `weights` (validated to sum to 1), `tierThresholds`, saturation, length curve, and the vocabulary itself:\n\n```ts\nconst score = createScorer({\n  language: 'en', // 'es' | 'bilingual' (default)\n  vocabulary: {\n    domainKeywords: { extend: ['kubernetes-operator', 'sharding'] }, // add your jargon\n  },\n});\n```\n\n`extend` adds to the base vocabulary; `replace` substitutes it — discourse connectors and reasoning patterns are universal, your domain terms are not.\n\n### Matching modes\n\nDefault is `matching: 'word'`: whole-word, diacritic-insensitive (`\"retencion\"` without the accent still matches `'retención'`; `'rut'` does **not** fire inside `\"rutina\"`). The original production scorer used substring matching — preserved as `matching: 'substring'` for exact replication.\n\n### `presets.animaProduction`\n\nThe **exact** configuration that runs in ANIMA's production agent — Colombian tax/legal terminology, business/SaaS vocabulary, Spanish, legacy matching, original thresholds. Use it as a reference for building your own domain vocabulary:\n\n```ts\nimport { createScorer, presets } from '@animakit/complexity-scorer';\nconst score = createScorer(presets.animaProduction);\n```\n\n## Model-agnostic by design\n\nThe scorer never names a model — it returns a tier, and mapping tiers to models is entirely your call, with any provider:\n\n```ts\n// One example — swap in whatever you run:\nconst model = {\n  micro: 'ollama:gemma4-12b-qat', // or e4b, qwen, anything local\n  small: 'deepseek-chat',         // or gemini-3.1-flash, gpt-5.1-mini\n  medium: 'claude-fable-5',       // or gemini-3.1-pro, gpt-5.4, kimi-k2.6\n  large: 'claude-mythos-5',       // or gpt-5.5, glm-5.1, grok\n}[tier];\n```\n\nThis isn't theoretical: the production agent this was extracted from has run on **Gemma 4 (e4b and 12B), Gemini 3.1 (flash/thinking/pro), GPT-5.1/5.4/5.5, Kimi k2.6, GLM 5.1, Grok, DeepSeek and Claude** at different points — the scorer never changed, only the mapping did. The cost tables in this README use one specific mapping (local/$0 → DeepSeek → Fable 5) because those were the prices simulated; your savings depend on your fleet.\n\n## What this is NOT\n\n- Not a model selector — it returns a tier; the mapping above is yours.\n- Not an LLM router with learned weights — deterministic and static by design; same input, same output, every time, explainable via `signals`.\n- Not async, no I/O, no network — ever.\n\n## Benchmarks are reproducible\n\nEvery number above comes from a script in [`benchmarks/`](./benchmarks): `latency.ts`, `replay.ts` (run it on your own message log), `precision.ts` (validate against your own labels), `calibrate-thresholds.ts` (fit thresholds to your traffic with LOO CV). If a claim isn't reproducible, we don't ship it.\n\nOne transparency note: our calibration set contains private business messages, so the exact labeled corpus doesn't ship — the scripts let you reproduce the *methodology* on your own data, which is the point: your traffic is what your thresholds should fit.\n\n## Part of ANIMA\n\n**A**gentic **N**euro-**I**nspired **M**emory **A**rchitecture — battle-tested cognitive architecture for LLM agents, extracted from 53 sprints in production. This is package 1 of 14. Next: `@animakit/homeostasis`, `@animakit/git-guardrails`.\n\n## License\n\nMIT © Justine Serna Aza\n","readmeFilename":"README.md"}