Skip to content

Commit 284b15d

Browse files
committed
docs(kiira): type-check third-party libs via externalPackages (kiira 0.5.0)
Upgrade kiira to 0.5.0 and declare the third-party packages the docs import (Vercel AI SDK, openai, arktype/valibot, redis/pino/@opentelemetry/api/express/ hono, @modelcontextprotocol/sdk, and the community adapters) under externalPackages, so kiira installs them into an isolated cache and type-checks against the real types instead of ignoring those fences. Un-ignored ~67 fences across migration/comparison, community-adapters, advanced (redis/pino/otel), mcp, structured-outputs, and server quick-starts -- catching real API drift in the examples along the way. Remaining ignores are genuine: removed/old TanStack APIs in before-code, framework route boilerplate, MCP/cencori subpath exports kiira's external resolver can't map yet, and a couple of pseudo-code blocks. Kiira: 663 snippets pass, 0 fail, 147 ignored (was 596 / 214).
1 parent 62616d9 commit 284b15d

19 files changed

Lines changed: 340 additions & 160 deletions

.agent/self-learning/lessons/2026-06-23-authoring-kiira-doc-snippets.md

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,10 +74,20 @@ is a means (keep examples honest), not the end (a green check).
7474
sets `noImplicitReturns: false` for docs, so a hook that returns on some
7575
branches and falls through on others is fine un-annotated.
7676

77-
7. **`ignore` is the last resort,** only for genuinely un-checkable fences:
78-
competitor SDKs not installed (`ai`, `@ai-sdk/*`), uninstalled community
79-
adapters, framework route-registration boilerplate (`createFileRoute`). Never
80-
`ignore` to mask a fixable error.
77+
7. **Third-party libraries are NOT a reason to `ignore` anymore.** kiira 0.5.0+
78+
has `externalPackages` (in kiira.config.ts) — declare the npm package + range
79+
and kiira installs it into an isolated cache and type-checks against the real
80+
types. We use it for the Vercel AI SDK (`ai`, `@ai-sdk/*`), `openai`,
81+
`arktype`/`valibot`, `redis`/`pino`/`@opentelemetry/api`/`express`/`hono`,
82+
`@modelcontextprotocol/sdk`, and every community adapter. So a snippet that
83+
imports a real published package should declare it in `externalPackages` and
84+
type-check — not be ignored. See [[kiira-ci-setup]].
85+
86+
8. **`ignore` is the last resort,** only for genuinely un-checkable fences:
87+
imports of packages NOT declared in `externalPackages` (e.g. `react-native` /
88+
Expo, intentionally not installed), framework route-registration boilerplate
89+
(`createFileRoute`, SvelteKit `./$types`), or deliberate non-compiling
90+
pseudo-code. Never `ignore` to mask a fixable error.
8191

8292
**Heuristic:** before committing a snippet fix, ask "did I make the example more
8393
correct, or just make the error go away?" If a reader copying this snippet would

.agent/self-learning/lessons/2026-06-23-kiira-ci-setup.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,22 @@ its resolution model and always read errors with `--verbose` before acting.
1919
("zod mismatch") and a CI-only failure (`@tanstack/ai-angular`). Both are
2020
explained by how kiira resolves and reports.
2121

22+
**Third-party deps — use `externalPackages` (kiira 0.5.0+), don't `ignore`.**
23+
Declare any npm package the docs import but the workspace doesn't depend on under
24+
`externalPackages: { "<pkg>": "<range>" }` (also per-glob via `overrides`). kiira
25+
installs them into an isolated cache (`node_modules/.kiira`, gitignored) and
26+
type-checks against the real types. We declare the Vercel AI SDK (`ai`,
27+
`@ai-sdk/*`), `openai`, `arktype`/`valibot`, `redis`/`pino`/`@opentelemetry/api`/
28+
`express`/`hono`/`@modelcontextprotocol/sdk`, and every community adapter — so
29+
those snippets validate for real instead of being ignored. Notes:
30+
- The isolated install prints non-fatal `npm warn`/`npm error ... matches` noise
31+
(a messy transitive dep tree in one community adapter) but still succeeds via
32+
the pnpm fallback — exit code is 0; only the kiira "found N errors" line
33+
matters. Verified on a cold cache (`rm -rf node_modules/.kiira`).
34+
- CI runs this install on each kiira run (network + time). Acceptable trade for
35+
real type-checking; if a package's dep tree ever breaks the install, drop that
36+
one package back to an `ignore`.
37+
2238
**How it's wired (the working setup):**
2339

2440
- `kiira` devDep + `kiira.config.ts` (no `tsconfig.docs.json`). Config:

docs/advanced/built-in-middleware.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,12 @@ All methods may return a `Promise` for async backends. The middleware handles TT
107107

108108
**Redis example:**
109109

110-
```typescript ignore
110+
```typescript
111+
import { chat } from "@tanstack/ai";
111112
import { createClient } from "redis";
112113
import { toolCacheMiddleware, type ToolCacheStorage } from "@tanstack/ai/middlewares";
114+
import { adapter, messages } from "./server";
115+
import { weatherTool } from "./tools";
113116

114117
const redis = createClient();
115118

@@ -213,10 +216,12 @@ const stream = chat({
213216

214217
Emits vendor-neutral OpenTelemetry traces and metrics for every `chat()` call — a root span per call, a child span per agent-loop iteration, and a grandchild span per tool execution, all tagged with [GenAI semantic-convention attributes](https://opentelemetry.io/docs/specs/semconv/gen-ai/).
215218

216-
```typescript ignore
219+
```typescript
217220
import { chat } from "@tanstack/ai";
221+
import { openaiText } from "@tanstack/ai-openai";
218222
import { otelMiddleware } from "@tanstack/ai/middlewares/otel";
219223
import { trace, metrics } from "@opentelemetry/api";
224+
import { messages } from "./server";
220225

221226
const otel = otelMiddleware({
222227
tracer: trace.getTracer("my-app"),

docs/advanced/debug-logging.md

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { chat } from "@tanstack/ai";
2727
import { openaiText } from "@tanstack/ai-openai";
2828

2929
const stream = chat({
30-
adapter: openaiText("gpt-4o"),
30+
adapter: openaiText("gpt-5.5"),
3131
messages: [{ role: "user", content: "Hello" }],
3232
debug: true,
3333
});
@@ -36,7 +36,7 @@ const stream = chat({
3636
Every internal event now prints to the console with a `[tanstack-ai:<category>]` prefix:
3737

3838
```
39-
[tanstack-ai:request] activity=chat provider=openai model=gpt-4o messages=1 tools=0 stream=true
39+
[tanstack-ai:request] activity=chat provider=openai model=gpt-5.5 messages=1 tools=0 stream=true
4040
[tanstack-ai:agentLoop] run started
4141
[tanstack-ai:provider] provider=openai type=response.output_text.delta
4242
[tanstack-ai:output] type=TEXT_MESSAGE_CONTENT
@@ -84,9 +84,11 @@ chat({
8484

8585
Pass a `Logger` implementation and all debug output flows through it instead of `console`:
8686

87-
```typescript ignore
88-
import type { Logger } from "@tanstack/ai";
87+
```typescript
88+
import { chat, type Logger } from "@tanstack/ai";
89+
import { openaiText } from "@tanstack/ai-openai";
8990
import pino from "pino";
91+
import { messages } from "./server";
9092

9193
const pinoLogger = pino();
9294
const logger: Logger = {
@@ -97,7 +99,7 @@ const logger: Logger = {
9799
};
98100

99101
chat({
100-
adapter: openaiText("gpt-4o"),
102+
adapter: openaiText("gpt-5.5"),
101103
messages,
102104
debug: { logger }, // all categories on, piped to pino
103105
});
@@ -115,7 +117,11 @@ If your `Logger` implementation throws — a cyclic-meta `JSON.stringify`, a tra
115117

116118
If you need to know when your own logger is failing, guard inside your implementation:
117119

118-
```typescript ignore
120+
```typescript
121+
import { type Logger } from "@tanstack/ai";
122+
import pino from "pino";
123+
124+
const pinoLogger = pino();
119125
const logger: Logger = {
120126
debug: (msg, meta) => {
121127
try {
@@ -125,7 +131,9 @@ const logger: Logger = {
125131
process.stderr.write(`logger failed: ${String(err)}\n`);
126132
}
127133
},
128-
// ... info, warn, error
134+
info: (msg, meta) => pinoLogger.info(meta, msg),
135+
warn: (msg, meta) => pinoLogger.warn(meta, msg),
136+
error: (msg, meta) => pinoLogger.error(meta, msg),
129137
};
130138
```
131139

docs/advanced/otel.md

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ const otel = otelMiddleware({
3838
})
3939

4040
const result = await chat({
41-
adapter: openaiText('gpt-4o'),
41+
adapter: openaiText('gpt-5.5'),
4242
messages: [{ role: 'user', content: 'hi' }],
4343
middleware: [otel],
4444
stream: false,
@@ -50,11 +50,11 @@ const result = await chat({
5050
### Spans
5151

5252
```text
53-
chat gpt-4o (root, kind: INTERNAL)
54-
├── chat gpt-4o #0 (iteration, kind: CLIENT)
53+
chat gpt-5.5 (root, kind: INTERNAL)
54+
├── chat gpt-5.5 #0 (iteration, kind: CLIENT)
5555
│ ├── execute_tool get_weather
5656
│ └── execute_tool get_time
57-
└── chat gpt-4o #1 (iteration, kind: CLIENT)
57+
└── chat gpt-5.5 #1 (iteration, kind: CLIENT)
5858
```
5959

6060
Iteration spans are numbered (`#0`, `#1`, ...) so distinct iterations of the same chat are easy to pick apart in trace viewers.
@@ -109,7 +109,12 @@ By default, only metadata lands on spans. To record prompt and completion conten
109109

110110
Pass a `redact` function to strip PII before anything is recorded:
111111

112-
```ts ignore
112+
```ts
113+
import { otelMiddleware } from '@tanstack/ai/middlewares/otel'
114+
import { trace } from '@opentelemetry/api'
115+
116+
const tracer = trace.getTracer('my-app')
117+
113118
otelMiddleware({
114119
tracer,
115120
captureContent: true,
@@ -133,7 +138,12 @@ All four extensions are optional. Each wraps user code in try/catch — a thrown
133138

134139
Override default span names. `info.kind` is `'chat' | 'iteration' | 'tool'`.
135140

136-
```ts ignore
141+
```ts
142+
import { otelMiddleware } from '@tanstack/ai/middlewares/otel'
143+
import { trace } from '@opentelemetry/api'
144+
145+
const tracer = trace.getTracer('my-app')
146+
137147
otelMiddleware({
138148
tracer,
139149
spanNameFormatter: (info) =>
@@ -145,7 +155,13 @@ otelMiddleware({
145155

146156
Add custom attributes to every span. Fires once per span.
147157

148-
```ts ignore
158+
```ts
159+
import { otelMiddleware } from '@tanstack/ai/middlewares/otel'
160+
import { trace } from '@opentelemetry/api'
161+
import { getCurrentTenant } from './context'
162+
163+
const tracer = trace.getTracer('my-app')
164+
149165
otelMiddleware({
150166
tracer,
151167
attributeEnricher: () => ({
@@ -162,7 +178,13 @@ Mutate `SpanOptions` immediately before `tracer.startSpan(...)`. Useful for addi
162178

163179
Fires just before every `span.end()`. Common uses: record custom events, emit per-tool metrics via your own `Meter`.
164180

165-
```ts ignore
181+
```ts
182+
import { otelMiddleware } from '@tanstack/ai/middlewares/otel'
183+
import { trace, metrics } from '@opentelemetry/api'
184+
185+
const tracer = trace.getTracer('my-app')
186+
const meter = metrics.getMeter('my-app')
187+
166188
const toolDuration = meter.createHistogram('tool.duration')
167189
otelMiddleware({
168190
tracer,

docs/community-adapters/cencori.md

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,13 @@ npm install @cencori/ai-sdk
2424
## Basic Usage
2525

2626
```typescript ignore
27+
// ignore: @cencori/ai-sdk/tanstack is a subpath export; kiira's paths["*"] wildcard maps it
28+
// to a flat directory lookup and does not consult the package.json exports field,
29+
// so the subpath cannot be resolved until kiira.config.ts adds an explicit path entry.
2730
import { chat } from "@tanstack/ai";
2831
import { cencori } from "@cencori/ai-sdk/tanstack";
2932

30-
const adapter = cencori("gpt-4o");
33+
const adapter = cencori("o1");
3134

3235
for await (const chunk of chat({
3336
adapter,
@@ -42,19 +45,21 @@ for await (const chunk of chat({
4245
## Configuration
4346

4447
```typescript ignore
48+
// ignore: @cencori/ai-sdk/tanstack subpath not resolvable via kiira's paths["*"] wildcard.
4549
import { createCencori } from "@cencori/ai-sdk/tanstack";
4650

47-
const cencori = createCencori({
51+
const myCencori = createCencori({
4852
apiKey: process.env.CENCORI_API_KEY!,
4953
baseUrl: "https://cencori.com", // Optional
5054
});
5155

52-
const adapter = cencori("gpt-4o");
56+
const adapter = myCencori("o1");
5357
```
5458

5559
## Streaming
5660

5761
```typescript ignore
62+
// ignore: @cencori/ai-sdk/tanstack subpath not resolvable via kiira's paths["*"] wildcard.
5863
import { chat } from "@tanstack/ai";
5964
import { cencori } from "@cencori/ai-sdk/tanstack";
6065

@@ -67,7 +72,7 @@ for await (const chunk of chat({
6772
if (chunk.type === "TEXT_MESSAGE_CONTENT") {
6873
process.stdout.write(chunk.delta);
6974
} else if (chunk.type === "RUN_FINISHED") {
70-
console.log("\nDone:", chunk.finishReason);
75+
console.log("\nDone");
7176
}
7277
}
7378
```
@@ -76,11 +81,12 @@ for await (const chunk of chat({
7681
## Tool Calling
7782

7883
```typescript ignore
84+
// ignore: @cencori/ai-sdk/tanstack subpath not resolvable via kiira's paths["*"] wildcard.
7985
import { chat, toolDefinition } from "@tanstack/ai";
8086
import { cencori } from "@cencori/ai-sdk/tanstack";
8187
import { z } from "zod";
8288

83-
const adapter = cencori("gpt-4o");
89+
const adapter = cencori("o1");
8490

8591
const getWeatherDef = toolDefinition({
8692
name: "getWeather",
@@ -99,9 +105,9 @@ for await (const chunk of chat({
99105
tools: [getWeather],
100106
})) {
101107
if (chunk.type === "TOOL_CALL_START") {
102-
console.log("Tool call:", chunk.toolName);
108+
console.log("Tool call:", chunk.toolCallName);
103109
} else if (chunk.type === "TOOL_CALL_END") {
104-
console.log("Tool result:", chunk.result);
110+
console.log("Tool call finished:", chunk.toolCallId);
105111
}
106112
}
107113
```
@@ -112,10 +118,11 @@ for await (const chunk of chat({
112118
Switch between providers with a single parameter:
113119

114120
```typescript ignore
121+
// ignore: @cencori/ai-sdk/tanstack subpath not resolvable via kiira's paths["*"] wildcard.
115122
import { cencori } from "@cencori/ai-sdk/tanstack";
116123

117-
// OpenAI
118-
const openai = cencori("gpt-4o");
124+
// OpenAI-compatible
125+
const openaiCompat = cencori("o1");
119126

120127
// Anthropic
121128
const anthropic = cencori("claude-3-5-sonnet");

0 commit comments

Comments
 (0)