npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

better-future-llm-kit

v0.6.0

Published

Voorstel om de chat-+-LLM-functionaliteit uit **Organisatie-Anatomie** los te trekken tot een set herbruikbare bouwstenen, zodat elke app er een AI-assistent in kan hangen door alleen **een handvol dingen** te configureren in plaats van het hele apparaat

Readme

LLM-integratie — herbruikbaar chat-+-LLM-fundament

Voorstel om de chat-+-LLM-functionaliteit uit Organisatie-Anatomie los te trekken tot een set herbruikbare bouwstenen, zodat elke app er een AI-assistent in kan hangen door alleen een handvol dingen te configureren in plaats van het hele apparaat opnieuw te bouwen.

Status: geïmplementeerd, behalve de payment-gate. De vier pakketten leven in packages/* (contract, gateway, chat-core, chat-vue) met een draaiend voorbeeld in examples/demo-app/. De org-builder-migratie staat als config in examples/org-builder/ (US-038) en een tweede consument valideert de naden in examples/second-app/ (US-040). Fan-out + reducers (US-033) en de provider-conformance-suite (US-032) zijn af. Nog niet gebouwd: het policy-checkpoint / de paywall (US-015, US-019, US-022–US-026, US-031, US-035) — bewust uitgesteld tot er een echt betaalmodel is. De originele werkende implementatie leeft nog in organisatie-anatomie/ (zie referenties hieronder).


1. Waarom

De org-builder is feitelijk een generiek apparaat met vier app-specifieke knoppen. Het transport, de UI, de conversatie-orchestratie en een paar terugkerende patronen (gestructureerde output via tools, valideer-en-herprobeer, context-injectie) zijn domein-agnostisch. Wat per app verandert is klein en scherp af te bakenen.

Door die scheiding expliciet te maken krijg je:

  • Eén chat-webcomponent die je overal inhangt.
  • Eén serverless gateway die de API-sleutel verbergt, provider-/modelkeuze centraliseert én het policy-checkpoint (entitlement/payment + metering) huisvest.
  • Vijf configpunten per app: systeemprompt, context-builder, output-schema, apply-callback, en (optioneel) entitlement/payment.

2. De naden (wat is generiek, wat verandert per app)

Huidige verdeling in Organisatie-Anatomie en het hergebruik-oordeel:

| # | Laag | Nu in de code | Herbruikbaar? | Wat per app verandert | |---|------|---------------|---------------|------------------------| | 1 | Chat-UI (webcomponent) | components/OrgBuilderChat.vue | ✅ generiek | titel, intro, theme-tokens | | 2 | Conversatie-orchestratie | composables/useOrgBuilder.ts | ✅ generiek | — | | 3 | LLM-proxy / gateway | netlify/functions/org-builder.ts | ✅ generiek | model-id, max_tokens, provider | | 4 | Systeem-instructie | SYSTEM_PROMPT (hardcoded) | ❌ per app | de hele prompt | | 5 | Context-injectie (input) | roleLibrary inline in prompt | ⚠️ patroon generiek | wélke state je meestuurt | | 6 | Output-contract (tool/schema) | SUBMIT_TOOL + SNAPSHOT_SCHEMA | ⚠️ patroon generiek | het schema | | 7 | Validatie + retry | client-side validateImport | ✅ patroon generiek | het schema | | 8 | Apply-to-state adapter | store.importAsNewDataset() + view='graph' | ❌ per app | het hele koppelstuk | | 9 | Feature-gating (cosmetisch) | ?ai-enabled=true | ✅ generiek | — (mag UI tonen/verbergen, géén poort) | | 10 | Entitlement / payment gate (enforcement) | — (nog niet aanwezig) | ✅ patroon generiek | wie + welk plan/quota | | 11 | Metering / observability | alleen prompt-caching, geen meting | ✅ generiek | — (voedt billing + quota) | | 12 | Paywall-state (UI) | — | ✅ generiek | upgrade-CTA / tekst |

Kernobservatie: de drie naden die jij benoemde (UI, LLM-interface, in/output-contract) zijn correct, maar het "in/output-contract" valt in de praktijk uiteen in drie aparte naden (5, 6, 7) en er is een vierde die makkelijk over het hoofd wordt gezien: de apply-to-state adapter (8) — het stukje dat de LLM-output op de app-state toepast. Dat is het meest app-specifieke deel en hoort als callback naar buiten getrokken, niet ingebakken.

Over de payment gate (10–12): een betaalpoort is géén peer-laag naast UI en LLM — het is een policy-checkpoint ín de gateway, want enforcement kan alleen server-side, achter de sleutel. Client-gating (rij 9, ?ai-enabled=true) is triviaal te omzeilen en mag daarom uitsluitend cosmetisch zijn — de echte poort staat op de enige plek waar elke call langskomt en die de gebruiker niet kan vervalsen. Het splitst in twee helften die op de metering-naad (11) aansluiten: pre-call authorize (mag dit, quota over?) en post-call meter (registreer tokens/kosten → billing + volgende quota-check). Een geweigerde call levert een nieuw antwoordtype payment_required op, zodat de chat (12) een paywall toont i.p.v. een foutbanner.

De scheidslijn in één zin

Generiek = transport + UI + orchestratie + de patronen + het policy-checkpoint. Per app = vijf configs: systemPrompt, buildContext, outputSchema, onResult, entitlement.


3. Pakketindeling

Vier pakketten, van server naar UI. Elk is los bruikbaar; samen vormen ze de volledige stack.

@llm-kit/gateway     server   provider-agnostische serverless proxy + tool-loop + retry + policy-checkpoint
@llm-kit/chat-core   client   framework-agnostische conversatie-orchestratie (geen Vue)
@llm-kit/chat-vue    client   de Vue 3 chat-webcomponent (UI + animaties + paywall-slot)
@llm-kit/contract    shared   schema-typen + validatiehelpers (door gateway én client gedeeld)
@llm-kit/billing     server   OPTIONEEL — kant-en-klare adapters (Stripe, usage-meter) voor het policy-checkpoint

De gateway krijgt een middleware-keten rond de LLM-call: identify → authorize → [LLM] → meter. De hooks zijn generiek; de app prikt er zijn billing-systeem in (zelf geschreven, of via een @llm-kit/billing-adapter). @llm-kit/billing is optioneel — een app zonder betaalmuur laat de hooks gewoon weg.

Afhankelijkheden (pijl = "hangt af van"):

chat-vue ──► chat-core ──► (fetch naar) gateway ──► provider-SDK
                  │                          │  │
                  └────────► contract ◄──────┘  └──► billing-adapter (optioneel)

Waarom vier en niet één: de gateway draait server-side (heeft de SDK + sleutel), chat-core is puur client-mechaniek zonder Vue zodat het ook in React/vanilla kan, en chat-vue is de presentatielaag. contract is bewust apart zodat hetzelfde JSON-Schema zowel de tool-definitie (server) als de client-validatie voedt — één bron.


4. De vier configpunten

Alles wat een app moet leveren om een werkende AI-assistent te krijgen:

| Config | Type | Org-builder-invulling | |--------|------|------------------------| | systemPrompt | string \| (ctx) => string | de interviewregels uit SYSTEM_PROMPT | | buildContext | () => string \| object | JSON.stringify(roleLibrary) + huidige org-state | | outputSchema | { name, description, schema } | submit_snapshot + SNAPSHOT_SCHEMA | | onResult | (output) => void \| Promise | store.importAsNewDataset(...) + view='graph' | | entitlement | { identify, authorize, meter } | server-side; koppelt aan Stripe/intern plan (nog te bouwen) |

entitlement is optioneel: laat je het weg, dan is er geen poort (huidig gedrag). Plus optionele presentatie-config (titel, intro-bubble, theme) en transport-config (model-id, endpoint). Zie interfaces.ts voor de volledige typen.

Het policy-checkpoint (de payment gate) in detail

De gateway draait een middleware-keten rond de LLM-call. Alleen de gateway is een betrouwbaar handhavingspunt — server-side, achter de sleutel, op de route waar elke call hoe dan ook langskomt.

request ─► identify(req)        wie is dit? (JWT/cookie/header → Principal)
        ─► authorize(principal) mag dit, quota over?  ── nee ─► { type: 'payment_required', … }
        ─► [ LLM-call + tool-loop + retry ]
        ─► meter(principal, usage)   registreer tokens/kosten → billing + quota-teller
        ─► response

De app levert drie kleine functies (net als onResult app-specifiek):

  • identify(req) — haal de gebruiker/tenant uit het verzoek. Geen identiteit en de route is betaald? → meteen payment_required.
  • authorize(principal, ctx) — check abonnement/quota tegen je billing-systeem. Retourneert toestaan / weigeren-met-reden.
  • meter(principal, usage) — post-call: leg verbruik vast. Dit is dezelfde data als de observability-naad (11), maar nu load-bearing voor usage-based billing.

Aan de clientkant is payment_required een terminale state naast done en error: chat-core zet 'm, chat-vue rendert een paywall-slot (upgrade-CTA) i.p.v. de rode foutbanner. De bestaande ?ai-enabled=true-gating (rij 9) blijft puur cosmetisch — hij bepaalt of de knop zichtbaar is, niet of de call mag.

Per-seat én usage-based — dezelfde hooks

De interface legt geen betaalmodel vast; het zit in wat authorize/meter lezen en schrijven. Beide modellen — en de combinatie — vallen uit dezelfde drie functies:

| Model | authorize checkt | meter doet | Scope (identify) | |-------|--------------------|--------------|--------------------| | Per-seat abonnement | "plan actief / seat geldig?" | (alleen loggen voor inzicht) | meestal tenantId | | Usage-based / credits | "saldo / quota > 0?" | saldo aftrekken o.b.v. tokens | userId of tenantId | | Hybride | "plan actief én maand-bundel niet op?" | bundel-teller bijwerken, daarna pay-as-you-go | tenantId + userId |

Een app kan zelfs per route/feature verschillen (gratis chat, betaalde bouwer) door gewoon een andere entitlement aan die gateway-handler te hangen. De Principal draagt zowel userId als tenantId, dus per-gebruiker én per-organisatie gaten zijn mogelijk zonder interface-wijziging. Conclusie: we hoeven nu niet te kiezen — het ontwerp houdt beide open; de keuze zit puur in de billing-backend achter de hooks.


5. Hoe org-builder inkrimpt

Na extractie is org-builder geen eigen apparaat meer maar een dunne configuratie van het fundament. Zie examples/org-builder.config.md voor de volledige before/after. Kort:

  • OrgBuilderChat.vue (≈360 regels) → <LlmChat :config="orgBuilderConfig" /> (≈10 regels)
  • useOrgBuilder.ts (≈150 regels) → verdwijnt; orchestratie zit in @llm-kit/chat-core
  • netlify/functions/org-builder.ts (≈280 regels) → createGatewayHandler(orgBuilderGatewayConfig) (≈15 regels) + de prompt/het schema als data

De prompt, het schema, de context-builder en de apply-callback blijven; al het mechaniek eromheen wordt geïmporteerd.


6. Patronen die we meteen beter doen dan nu

De extractie is ook het moment om drie zwakke plekken in de huidige implementatie op te lossen — ze zijn generiek, dus ze horen in het fundament:

  1. Validate-and-retry loop. Nu krijgt het model één kans; een ongeldig snapshot wordt client-side gevangen en als rode banner getoond (useOrgBuilder.ts:121-127). Het fundament stuurt de validatiefout terug naar het model voor een herkansing (N pogingen) vóór het opgeeft. Dit is puur generiek.
  2. Streaming. De huidige call is blocking (anthropic.messages.create). De gateway krijgt een streaming-variant zodat de chat token-voor-token kan tonen; de paragraaf-animatie in de UI wordt dan echte streaming i.p.v. gesimuleerd.
  3. Observability + metering. Tokens/kosten/latency loggen en provider kunnen wisselen via één env-var. Nu zit alleen prompt-caching erin (org-builder.ts:248-253), geen meting. Dezelfde meet-hook is de meter-helft van de payment gate — bouw 'm één keer, gebruik 'm voor zowel inzicht als billing.

7. Migratiepad

Incrementeel, elke fase levert werkende code op:

  1. @llm-kit/contract — trek SNAPSHOT_SCHEMA-stijl typen + validatiehelper los. Geen gedragsverandering.
  2. @llm-kit/gatewaycreateGatewayHandler(config) factory; org-builder.ts wordt de eerste consument. Voeg de retry-loop toe.
  3. @llm-kit/chat-core — generaliseer useOrgBuilder.ts naar een config-gedreven conversatie-engine met onResult-callback.
  4. @llm-kit/chat-vue — generaliseer OrgBuilderChat.vue tot <LlmChat>; org-builder wordt een config-object. Voeg de payment_required-state + paywall-slot toe.
  5. Policy-checkpoint — voeg de identify → authorize → meter-middleware aan de gateway toe (de meter-hook bestaat dan al uit fase 2's observability). Pas wanneer er écht een betaalmodel is; org-builder draait tot dan met entitlement weggelaten.
  6. Tweede app — pas het fundament toe op een nieuwe usecase om de naden te valideren (de echte test of de abstractie klopt).

Vuistregel: pas abstraheren als de tweede consument er is. Fase 1–2 zijn veilig (puur losmaken), fase 3–5 wachten idealiter tot er een concrete tweede usecase of een echt betaalmodel is, zodat we niet de verkeerde naden vastleggen.


8. Open vragen

  • Webcomponent of Vue-component? ✅ Beide bestaan nu. De Vue-component (/vue) blijft voor Vue/Nuxt-apps; daarnaast is er een framework-vrije custom element <llm-chat> (/element, vanilla + Shadow DOM, volledig getokeniseerd) als portable variant — zie FRONTEND.md.
  • Waar leeft de systeemprompt? Org-builder houdt SKILL.md als bron van waarheid en synct SYSTEM_PROMPT handmatig (org-builder.ts:18-19). Wil je dat patroon (skill = bron, prompt = afgeleide) onderdeel van het fundament maken, of per app vrijlaten?
  • Monorepo of losse repo's? Vijf pakketten met onderlinge afhankelijkheden schreeuwen om een workspace. Past dit in een bestaande monorepo onder for-the-money/?
  • Stripe of intern saldo-systeem? De authorize/meter-implementaties praten met je billing-backend; die keuze staat los van de interface maar moet wel gemaakt worden.

Referenties (huidige implementatie)

| Onderdeel | Pad | |-----------|-----| | Chat-UI | organisatie-anatomie/components/OrgBuilderChat.vue | | Orchestratie | organisatie-anatomie/composables/useOrgBuilder.ts | | Gateway | organisatie-anatomie/netlify/functions/org-builder.ts | | Output-schema | org-builder.ts:37-138 (SNAPSHOT_SCHEMA, SUBMIT_TOOL) | | Systeemprompt | org-builder.ts:145-201 | | Apply-to-state | useOrgBuilder.ts:104-127 | | Feature-gating | organisatie-anatomie/components/TopHeader.vue:52-68 |