Skip to content

Kody is live

Watch the launch video — what Kody is, and why it exists.

← Public packages

@kentcdodds/jev

Call TypeSafe Jev evaluations via Cloudflare AI Gateway (same token/gateway as @kentcdodds/ai).

AGENTS.md

93 lines · 2.9 KB · Markdown

@kentcdodds/jev — agent notes

Human setup and intent live in README.md. This file is for agents: imports, smoke checks, snippets, and edge cases. Secrets by name only — never paste token values.

Secret / storage

  • Secret name: cloudflareApiToken (user scope) — placeholder {{secret:cloudflareApiToken}} in fetch headers only
  • Hosts: api.cloudflare.com (required for /ai/run); gateway.ai.cloudflare.com optional for shared @kentcdodds/ai chat path
  • Package storage (via ./settings, no republish): cloudflareAccountId, cloudflareAiGatewayId (optional; default kody), cloudflareJevModel (optional; default typesafe/jev)
  • When Jev storage is empty, resolve uses packages.invoke on @kentcdodds/ai/settings (target package runtime) for accountId + gatewayId, then caches into Jev storage. Static kody:@ stamp cannot see ai storage.

Import paths

ExportImport
overview / checklistkody:@kentcdodds/jev
evaluate (preferred)kody:@kentcdodds/jev/evaluate
raw /ai/runkody:@kentcdodds/jev/request
settings read/writekody:@kentcdodds/jev/settings
typeskody:@kentcdodds/jev/types
smoke dry/livekody:@kentcdodds/jev/smoke-test

Prefer static kody:@kentcdodds/jev/... imports from execute. Do not lead with packages.invoke.

Smoke / dry checks

Dry (no model spend):

import smokeTest from 'kody:@kentcdodds/jev/smoke-test'

export default async function main() {
  return await smokeTest()
}

Live tiny noul:

import smokeTest from 'kody:@kentcdodds/jev/smoke-test'

export default async function main() {
  return await smokeTest({ live: true })
  // => { ok, status, answers, model, usage } — no tokens
}

Main helper:

import evaluate from 'kody:@kentcdodds/jev/evaluate'

export default async function main() {
  return await evaluate({
    state: 'hello',
    questions: {
      greeting: { type: 'noul', instructions: 'Is this a greeting?' },
    },
  })
}

Gateway call shape

POST https://api.cloudflare.com/client/v4/accounts/{accountId}/ai/run

  • Header Authorization: Bearer {{secret:cloudflareApiToken}}
  • Header cf-aig-gateway-id: {gatewayId}
  • Body { model: "typesafe/jev", input: { state, questions } }

Edge cases

  • If accountId is missing, exports return setup.secretsNewUrl instead of throwing loudly.
  • Never log Authorization headers or secret placeholders into returned chat text.
  • Question types: noul (boolean probability), choice (criteria map), score (criteria array). Cloudflare docs use noul; Vercel AI SDK may label the boolean type differently — this package follows Cloudflare.
  • Prefer ./evaluate for parsed answers; ./request when you need the raw Cloudflare envelope.
  • HTTP 402 from /ai/run means AI Gateway Unified Billing needs credits (or configure TypeSafe BYOK) — not a missing secret.