@kody/ai
README.md
174 lines · 6.3 KB · Markdown@kody/ai
Generic geometric mark (not a vendor logo). This package is not affiliated with OpenAI, Anthropic, Groq, or Cloudflare.
Intent
Give every Kody account reusable, provider-agnostic AI helpers: chat completions, structured JSON generation, and a thin host-side tool loop. Callers pass the provider, model, and their own saved API key. Success means a forked copy can dry-run a request, then generate text or JSON, without anyone baking in personal keys, account ids, or a single vendor.
Live @kody/ai storage is the platform package bucket, not the caller's.
Fork first (or packages.invoke the user's copy) before saving defaults.
Auth
Secret-backed HTTP. There is no AI OAuth integration and no baked-in key.
Do not open /connect/oauth for this package.
| Secret | Provider | Hosts to approve |
|---|---|---|
openaiApiKey | OpenAI | api.openai.com |
anthropicApiKey | Anthropic | api.anthropic.com |
groqApiKey | Groq | api.groq.com |
cloudflareApiToken | Cloudflare Workers AI | api.cloudflare.com, gateway.ai.cloudflare.com |
openaiCompatibleApiKey | Custom OpenAI-compatible base URL | the host you pass as baseUrl |
Readable defaults (not secrets) live in this package's packageStorage()
after you fork: provider, model, baseUrl, cloudflareAccountId,
cloudflareAiGatewayId. Writes default to dry-run and need confirm: true.
Required setup
- Create an API key in the provider console. Never paste it into chat.
- Save the matching secret (pick one provider to start):
- Approve the host(s) listed above for that secret.
- Cloudflare also needs
cloudflareAccountIdin./settingson your fork (optionalcloudflareAiGatewayIdif you already have a gateway — this package never creates one). - Run the smoke test.
Additional accounts
Pass account: "work" to use openaiApiKey-work (or the matching provider
secret), or pass apiKeySecret explicitly.
Exports
kody:@kody/ai— overview, orgenerateText/generateObjectwhen you passmessages/schemakody:@kody/ai/complete— one chat completion (generateText)kody:@kody/ai/generate-object— structured JSON from a JSON Schemakody:@kody/ai/model-step— model-only step plus default Kody search/execute tool schemaskody:@kody/ai/turn— optional tool-using agent turn (runskody.search/kody.executein this package)kody:@kody/ai/settings— fork-local provider/model defaults; writes needconfirm: truekody:@kody/ai/guide— console steps and prefilled secret URLskody:@kody/ai/smoke-test— dry-run self-check; optional models-list when a secret existskody:@kody/ai/types— input/output type metadata
Prefer runModelStep when a host package should execute tools in its own
package context (correct secret allowlists). runAgentTurn still runs tools
as package ai.
No jobs or schedules ship in this package.
Smoke test
./smoke-test is safe before anyone saves credentials: it returns
{ ok: true, live: false } and the setup URLs. After a secret and host
approval, invoke the package so placeholders resolve. The live path lists
models only — it does not spend completion tokens.
import ai from 'kody:@kody/ai'
export default async function main() {
return await ai()
}import { packages } from 'kody:runtime'
export default async function main() {
return await packages.invoke({
kodyId: 'ai',
exportName: './smoke-test',
})
}dryRun
./settings writes default to dry-run and need confirm: true.
Completions are live when you pass messages (that is the helper). Pass
dryRun: true to preview the provider, model, URL, and body without calling
the vendor.
import { generateText } from 'kody:@kody/ai'
export default async function main() {
return await generateText({
provider: 'openai',
messages: [{ role: 'user', content: 'Say hello in one word.' }],
dryRun: true,
})
}Live completion after the secret exists:
import { generateText } from 'kody:@kody/ai'
export default async function main() {
return await generateText({
provider: 'openai',
messages: [{ role: 'user', content: 'Say hello in one word.' }],
})
}Structured JSON:
import { generateObject } from 'kody:@kody/ai'
export default async function main() {
return await generateObject({
provider: 'openai',
messages: [{ role: 'user', content: 'Label this: I love this package' }],
schema: {
type: 'object',
properties: {
label: { type: 'string', enum: ['positive', 'neutral', 'negative'] },
},
required: ['label'],
additionalProperties: false,
},
dryRun: true,
})
}Errors
| Symptom | What to do |
|---|---|
| Missing API key / 401 | Save the provider secret at the URL above and approve its host. |
| Cloudflare missing account | Fork, then ./settings with cloudflareAccountId and confirm: true. |
| openai-compatible missing baseUrl | Pass baseUrl (https) or store it in ./settings on your fork. |
| Unknown provider | Use openai, anthropic, groq, cloudflare, or openai-compatible. |