No description
https://grundlag.mortenolsen.pro
- TypeScript 97.5%
- JavaScript 2.3%
- Dockerfile 0.2%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
All checks were successful
ci/woodpecker/push/compliance Pipeline was successful
ci/woodpecker/push/quality Pipeline was successful
ci/woodpecker/push/draft-notes Pipeline was successful
ci/woodpecker/tag/deploy-docs Pipeline was successful
ci/woodpecker/tag/release Pipeline was successful
ci/woodpecker/tag/release-image Pipeline was successful
Adds `execute`, taking back the `intent` from a `classify` match, over
`NlpEngine`, `NlpService`, and `POST /api/nlp/execute`.
The response is now an envelope. `score` and `text` describe how the match was
arrived at; `intent` is the executable part and goes back untouched. Deciding
whether a match is strong enough therefore happens exactly once, at the classify
boundary, and `execute` neither wants nor accepts a score to second-guess it with.
The score was already plumbed end to end but carried no information, which is the
more important fix here. nlp.js's own threshold was set to 0.8, and it replaces a
below-threshold intent with `None` *and* rewrites the score to 1 — so a caller
only ever saw scores above the floor, and the engine had already made the decision
for them. Its thresholding is now disabled and the floor applied in
`classifyWith`, with `minScore` overridable per call.
Measuring that turned up something worth recording: the score is honest but not
calibrated, and a threshold does not transfer between hosts. Unrelated input
scores ~0.504 against two slot-bearing intents, ~0.902 once a short slot-free
intent joins them, and ~0.960 when that intent gains phrasings — converging on
what a genuine match scores. Documented in the README with the numbers, and the
guide now tells integrators to probe their own corpus rather than trust the 0.8
default.
An intent raising `ToolInterrupt` answers `status: 'interrupted'` with a 200
rather than a 500, since needing a human is an outcome. It cannot be resumed;
an intent has no resumption channel.
Also reframes the guide's opening. It claimed a provider's actions are mostly
reached by a model composing arguments, which reads as though Grundlag were an
agent framework. Actions are called by whatever is built on top — scripts,
automations, apps, agents — and phrase matching is one more way in, cheap to add
precisely because the catalogue already exists for everything else.
BREAKING CHANGE: `classify` returns `{ intent, score, text }` rather than a flat
match, so `match.id` is now `match.intent.id` and `match.entities` is an
`NlpEntityRegistry` rather than a plain record.
|
||
| .woodpecker | ||
| docs | ||
| examples/client | ||
| instance | ||
| packages | ||
| providers | ||
| scripts | ||
| .dockerignore | ||
| .gitignore | ||
| .npmrc | ||
| .prettierignore | ||
| .prettierrc | ||
| AGENTS.md | ||
| CLAUDE.md | ||
| CONTEXT.md | ||
| CONTRIBUTING.md | ||
| docker-compose.yaml | ||
| Dockerfile | ||
| eslint.config.mjs | ||
| LICENSE | ||
| mise.toml | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| Taskfile.yml | ||
| turbo.json | ||
| vitest.config.ts | ||