@netlify/agent-runner-cli 1.68.0 → 1.69.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,510 @@
1
+ ---
2
+ name: netlify-inference
3
+ description: Use Netlify AI Gateway for AI inference in Netlify Functions and server-side code. Use when adding AI/LLM features with OpenAI, Anthropic, or Google Gemini without managing API keys.
4
+ ---
5
+
6
+ # Netlify AI Gateway
7
+
8
+ Zero-config AI inference for Netlify projects. Netlify automatically injects environment variables so official
9
+ provider SDKs work without API keys or configuration.
10
+
11
+ ## How It Works
12
+
13
+ Netlify sets provider-specific environment variables in all compute contexts (Functions, Edge Functions, server-side
14
+ framework code). Official SDKs auto-detect these variables, so a default constructor like `new OpenAI()` works
15
+ out of the box. Requests are proxied through AI Gateway and billed to your Netlify account credits.
16
+
17
+ Netlify **never overrides** environment variables you have already set. The check is per-provider: if you set your own
18
+ `OPENAI_API_KEY`, Netlify will not set `OPENAI_API_KEY` or `OPENAI_BASE_URL`, but will still inject Anthropic and
19
+ Gemini variables independently.
20
+
21
+ AI Gateway requires a **credit-based plan** (Free, Personal, or Pro). It is not available on legacy pricing plans.
22
+
23
+ ### Environment Variables
24
+
25
+ | Provider | Variables |
26
+ |----------|-----------|
27
+ | **Anthropic** | `ANTHROPIC_API_KEY`, `ANTHROPIC_BASE_URL` |
28
+ | **OpenAI** | `OPENAI_API_KEY`, `OPENAI_BASE_URL` |
29
+ | **Google Gemini** | `GEMINI_API_KEY`, `GOOGLE_GEMINI_BASE_URL` |
30
+ | **Gateway (always set)** | `NETLIFY_AI_GATEWAY_KEY`, `NETLIFY_AI_GATEWAY_BASE_URL` |
31
+
32
+ ## Quick Start
33
+
34
+ Install the SDK for your provider and use a zero-config constructor. No API key or base URL needed.
35
+
36
+ ### Anthropic
37
+
38
+ ```bash
39
+ npm install @anthropic-ai/sdk
40
+ ```
41
+
42
+ ```typescript
43
+ import Anthropic from '@anthropic-ai/sdk'
44
+
45
+ const anthropic = new Anthropic()
46
+
47
+ const message = await anthropic.messages.create({
48
+ model: 'claude-opus-4-6',
49
+ max_tokens: 1024,
50
+ messages: [{ role: 'user', content: 'Hello!' }],
51
+ })
52
+
53
+ console.log(message.content[0].text)
54
+ ```
55
+
56
+ ### OpenAI
57
+
58
+ ```bash
59
+ npm install openai
60
+ ```
61
+
62
+ ```typescript
63
+ import OpenAI from 'openai'
64
+
65
+ const openai = new OpenAI()
66
+
67
+ const completion = await openai.chat.completions.create({
68
+ model: 'gpt-5.2',
69
+ messages: [{ role: 'user', content: 'Hello!' }],
70
+ })
71
+
72
+ console.log(completion.choices[0].message.content)
73
+ ```
74
+
75
+ OpenAI also supports the newer Responses API:
76
+
77
+ ```typescript
78
+ import OpenAI from 'openai'
79
+
80
+ const openai = new OpenAI()
81
+
82
+ const response = await openai.responses.create({
83
+ model: 'gpt-5.2',
84
+ input: [{ role: 'user', content: 'Hello!' }],
85
+ })
86
+
87
+ console.log(response.output_text)
88
+ ```
89
+
90
+ ### Google Gemini
91
+
92
+ ```bash
93
+ npm install @google/genai
94
+ ```
95
+
96
+ ```typescript
97
+ import { GoogleGenAI } from '@google/genai'
98
+
99
+ const ai = new GoogleGenAI({})
100
+
101
+ const response = await ai.models.generateContent({
102
+ model: 'gemini-3-flash-preview',
103
+ contents: 'Hello!',
104
+ })
105
+
106
+ console.log(response.text)
107
+ ```
108
+
109
+ ## Streaming
110
+
111
+ ### Anthropic Streaming
112
+
113
+ ```typescript
114
+ import Anthropic from '@anthropic-ai/sdk'
115
+
116
+ const anthropic = new Anthropic()
117
+
118
+ const stream = await anthropic.messages.create({
119
+ model: 'claude-opus-4-6',
120
+ max_tokens: 1024,
121
+ messages: [{ role: 'user', content: 'Tell me a story.' }],
122
+ stream: true,
123
+ })
124
+
125
+ for await (const event of stream) {
126
+ if (event.type === 'content_block_delta' && event.delta.type === 'text_delta') {
127
+ process.stdout.write(event.delta.text)
128
+ }
129
+ }
130
+ ```
131
+
132
+ ### OpenAI Streaming
133
+
134
+ ```typescript
135
+ import OpenAI from 'openai'
136
+
137
+ const openai = new OpenAI()
138
+
139
+ const stream = await openai.chat.completions.create({
140
+ model: 'gpt-5.2',
141
+ messages: [{ role: 'user', content: 'Tell me a story.' }],
142
+ stream: true,
143
+ })
144
+
145
+ for await (const chunk of stream) {
146
+ if (chunk.choices[0]?.delta?.content) {
147
+ process.stdout.write(chunk.choices[0].delta.content)
148
+ }
149
+ }
150
+ ```
151
+
152
+ ### Gemini Streaming
153
+
154
+ ```typescript
155
+ import { GoogleGenAI } from '@google/genai'
156
+
157
+ const ai = new GoogleGenAI({})
158
+
159
+ const stream = await ai.models.generateContentStream({
160
+ model: 'gemini-3-flash-preview',
161
+ contents: 'Tell me a story.',
162
+ })
163
+
164
+ for await (const chunk of stream) {
165
+ if (chunk.text) {
166
+ process.stdout.write(chunk.text)
167
+ }
168
+ }
169
+ ```
170
+
171
+ ## Image Generation
172
+
173
+ ### OpenAI (gpt-image-1)
174
+
175
+ ```typescript
176
+ import OpenAI from 'openai'
177
+
178
+ const openai = new OpenAI()
179
+
180
+ const result = await openai.images.generate({
181
+ model: 'gpt-image-1',
182
+ prompt: 'A cute otter in a river',
183
+ })
184
+
185
+ const imageBase64 = result.data[0].b64_json
186
+ ```
187
+
188
+ Image generation is also available via the Responses API with the `image_generation` tool:
189
+
190
+ ```typescript
191
+ const response = await openai.responses.create({
192
+ model: 'gpt-4o',
193
+ input: 'Create a simple logo',
194
+ tools: [{ type: 'image_generation' }],
195
+ })
196
+
197
+ for (const item of response.output) {
198
+ if (item.type === 'image_generation_call') {
199
+ const imageBase64 = item.result.data
200
+ }
201
+ }
202
+ ```
203
+
204
+ ### Google Gemini (Imagen)
205
+
206
+ ```typescript
207
+ import { GoogleGenAI } from '@google/genai'
208
+
209
+ const ai = new GoogleGenAI({})
210
+
211
+ const response = await ai.models.generateImages({
212
+ model: 'imagen-4.0-generate-001',
213
+ prompt: 'A cute otter in a river',
214
+ config: { numberOfImages: 1 },
215
+ })
216
+
217
+ for (const image of response.generatedImages) {
218
+ const imageBytes = image.image.imageBytes
219
+ }
220
+ ```
221
+
222
+ ## Netlify Functions
223
+
224
+ Full example of a Netlify Function using AI Gateway:
225
+
226
+ ```typescript
227
+ // netlify/functions/chat.mts
228
+ import type { Context } from '@netlify/functions'
229
+ import Anthropic from '@anthropic-ai/sdk'
230
+
231
+ const anthropic = new Anthropic()
232
+
233
+ export default async (req: Request, context: Context) => {
234
+ const { prompt } = await req.json()
235
+
236
+ const message = await anthropic.messages.create({
237
+ model: 'claude-opus-4-6',
238
+ max_tokens: 1024,
239
+ messages: [{ role: 'user', content: prompt }],
240
+ })
241
+
242
+ return Response.json({ response: message.content[0].text })
243
+ }
244
+
245
+ export const config = {
246
+ path: '/api/chat',
247
+ }
248
+ ```
249
+
250
+ For streaming responses from a function:
251
+
252
+ ```typescript
253
+ // netlify/functions/stream.mts
254
+ import type { Context } from '@netlify/functions'
255
+ import OpenAI from 'openai'
256
+
257
+ const openai = new OpenAI()
258
+
259
+ export default async (req: Request, context: Context) => {
260
+ const { prompt } = await req.json()
261
+
262
+ const stream = await openai.chat.completions.create({
263
+ model: 'gpt-5.2',
264
+ messages: [{ role: 'user', content: prompt }],
265
+ stream: true,
266
+ })
267
+
268
+ return new Response(
269
+ new ReadableStream({
270
+ async start(controller) {
271
+ for await (const chunk of stream) {
272
+ const text = chunk.choices[0]?.delta?.content
273
+ if (text) {
274
+ controller.enqueue(new TextEncoder().encode(text))
275
+ }
276
+ }
277
+ controller.close()
278
+ },
279
+ }),
280
+ { headers: { 'Content-Type': 'text/plain; charset=utf-8' } },
281
+ )
282
+ }
283
+
284
+ export const config = {
285
+ path: '/api/stream',
286
+ }
287
+ ```
288
+
289
+ ## Framework Server-Side Code
290
+
291
+ AI Gateway env vars are available in any server-side context, not just Netlify Functions.
292
+
293
+ ### Next.js Route Handler
294
+
295
+ ```typescript
296
+ // app/api/chat/route.ts
297
+ import OpenAI from 'openai'
298
+
299
+ const openai = new OpenAI()
300
+
301
+ export async function POST(request: Request) {
302
+ const { prompt } = await request.json()
303
+
304
+ const completion = await openai.chat.completions.create({
305
+ model: 'gpt-5.2',
306
+ messages: [{ role: 'user', content: prompt }],
307
+ })
308
+
309
+ return Response.json({ response: completion.choices[0].message.content })
310
+ }
311
+ ```
312
+
313
+ This also works in server components, API routes in Astro, Remix loaders/actions, and SvelteKit server routes.
314
+
315
+ ## REST API / Direct Fetch
316
+
317
+ For HTTP-level control, use the gateway variables directly:
318
+
319
+ ```typescript
320
+ const response = await fetch(`${process.env.NETLIFY_AI_GATEWAY_BASE_URL}/anthropic/v1/messages`, {
321
+ method: 'POST',
322
+ headers: {
323
+ 'Content-Type': 'application/json',
324
+ 'Authorization': `Bearer ${process.env.NETLIFY_AI_GATEWAY_KEY}`,
325
+ 'anthropic-version': '2023-06-01',
326
+ },
327
+ body: JSON.stringify({
328
+ model: 'claude-opus-4-6',
329
+ max_tokens: 1024,
330
+ messages: [{ role: 'user', content: 'Hello!' }],
331
+ }),
332
+ })
333
+
334
+ const data = await response.json()
335
+ ```
336
+
337
+ Or using the provider-specific variables with `fetch`:
338
+
339
+ ```typescript
340
+ const response = await fetch(`${process.env.ANTHROPIC_BASE_URL}/v1/messages`, {
341
+ method: 'POST',
342
+ headers: {
343
+ 'Content-Type': 'application/json',
344
+ 'x-api-key': process.env.ANTHROPIC_API_KEY,
345
+ 'anthropic-version': '2023-06-01',
346
+ },
347
+ body: JSON.stringify({
348
+ model: 'claude-opus-4-6',
349
+ max_tokens: 1024,
350
+ messages: [{ role: 'user', content: 'Hello!' }],
351
+ }),
352
+ })
353
+ ```
354
+
355
+ ## Local Development
356
+
357
+ ### Netlify CLI
358
+
359
+ Run `netlify dev` to start a local development server with AI Gateway env vars injected automatically.
360
+
361
+ ```bash
362
+ netlify dev
363
+ ```
364
+
365
+ ### Vite Plugin
366
+
367
+ Alternatively, use the `@netlify/vite-plugin` for Vite-based projects:
368
+
369
+ ```bash
370
+ npm install @netlify/vite-plugin
371
+ ```
372
+
373
+ ```javascript
374
+ // vite.config.js
375
+ import { defineConfig } from 'vite'
376
+ import netlify from '@netlify/vite-plugin'
377
+
378
+ export default defineConfig({
379
+ plugins: [netlify()],
380
+ })
381
+ ```
382
+
383
+ Then run your normal dev command (`npm run dev`).
384
+
385
+ **Requirement:** The site must have at least one production deploy before AI Gateway env vars become available locally.
386
+
387
+ ## Available Models
388
+
389
+ Key models per provider. For the latest list, see https://docs.netlify.com/build/ai-gateway/overview/.
390
+
391
+ ### Anthropic
392
+
393
+ - `claude-opus-4-6`
394
+ - `claude-opus-4-5-20251101`
395
+ - `claude-opus-4-1-20250805`
396
+ - `claude-sonnet-4-5-20250929`
397
+ - `claude-sonnet-4-20250514`
398
+ - `claude-haiku-4-5-20251001`
399
+ - `claude-3-7-sonnet-20250219`
400
+ - `claude-3-5-haiku-20241022`
401
+
402
+ ### OpenAI
403
+
404
+ - `gpt-5.2`
405
+ - `gpt-5.1`
406
+ - `gpt-5.1-codex`
407
+ - `gpt-5`
408
+ - `gpt-5-mini`
409
+ - `gpt-5-nano`
410
+ - `gpt-4.1`
411
+ - `gpt-4.1-mini`
412
+ - `gpt-4.1-nano`
413
+ - `gpt-4o`
414
+ - `gpt-4o-mini`
415
+ - `o3`
416
+ - `o3-mini`
417
+ - `o4-mini`
418
+ - `gpt-image-1` (image generation)
419
+ - `codex-mini-latest`
420
+
421
+ ### Google Gemini
422
+
423
+ - `gemini-3-pro-preview`
424
+ - `gemini-3-flash-preview`
425
+ - `gemini-2.5-pro`
426
+ - `gemini-2.5-flash`
427
+ - `gemini-2.5-flash-lite`
428
+ - `gemini-2.0-flash`
429
+ - `gemini-2.0-flash-lite`
430
+ - `imagen-4.0-generate-001` (image generation)
431
+ - `veo-3.0-generate-preview` (video generation)
432
+
433
+ ## Rate Limits
434
+
435
+ Tokens per minute (TPM) are scoped per **account** across all projects. Both input and output tokens count.
436
+ For Anthropic, cached tokens are excluded; for other providers, cached tokens are included.
437
+ For the latest limits, see https://docs.netlify.com/build/ai-gateway/overview/.
438
+
439
+ | Model | Free | Personal | Pro |
440
+ |-------|------|----------|-----|
441
+ | claude-sonnet-4-5-20250929 | 18,000 | 90,000 | 180,000 |
442
+ | gpt-5 | 18,000 | 90,000 | 180,000 |
443
+ | gpt-4o-mini | 250,000 | 500,000 | 750,000 |
444
+ | gemini-2.5-pro | 24,000 | 120,000 | 240,000 |
445
+
446
+ Set up rate limiting rules on your functions to prevent abuse from client-side callers.
447
+
448
+ ## Limitations
449
+
450
+ - **Context window:** Max 200k tokens per request
451
+ - **Batch inference:** Not supported
452
+ - **Custom headers:** Cannot send custom request headers to providers
453
+ - **Prompt caching:** Anthropic supports 5-minute ephemeral caching only. Gemini caching is not supported. OpenAI sets `prompt_cache_key` per-account automatically.
454
+ - **Priority processing (OpenAI):** Not supported
455
+ - **Production deploy required:** At least one production deploy must exist before AI Gateway activates
456
+
457
+ ## Disabling AI Gateway
458
+
459
+ To prevent Netlify from injecting any AI-related environment variables, disable AI features in the Netlify UI under
460
+ **Project configuration > Build & deploy > Build with AI > Manage AI features**.
461
+
462
+ ## Monitoring Usage
463
+
464
+ View AI Gateway token usage and costs in the Netlify UI under **Team settings > Billing > Usage**. Each request's
465
+ token consumption is tracked per model and converted to Netlify credits.
466
+
467
+ ## Common Errors & Solutions
468
+
469
+ ### "API key not found" or env var is undefined
470
+
471
+ **Cause:** AI Gateway env vars are not injected.
472
+
473
+ **Fix:**
474
+
475
+ 1. Ensure the site has at least one production deploy
476
+ 2. Use `netlify dev` (not a bare `npm run dev`) for local development
477
+ 3. Check that you are reading the env var in server-side code, not client-side browser code
478
+
479
+ ### Rate limit exceeded (429)
480
+
481
+ **Cause:** Account hit the tokens-per-minute limit for that model.
482
+
483
+ **Fix:**
484
+
485
+ 1. Wait briefly and retry — TPM limits reset each minute
486
+ 2. Switch to a smaller/cheaper model (e.g. `gpt-4.1-mini` instead of `gpt-4.1`)
487
+ 3. Reduce prompt length or `max_tokens`
488
+ 4. Upgrade plan tier for higher limits
489
+
490
+ ### Model not available
491
+
492
+ **Cause:** The requested model is not supported through AI Gateway.
493
+
494
+ **Fix:**
495
+
496
+ 1. Check the available models list above
497
+ 2. Use the exact model ID string (e.g. `claude-sonnet-4-5-20250929`, not `claude-sonnet`)
498
+
499
+ ## Packages
500
+
501
+ ```bash
502
+ # Anthropic
503
+ npm install @anthropic-ai/sdk
504
+
505
+ # OpenAI
506
+ npm install openai
507
+
508
+ # Google Gemini
509
+ npm install @google/genai
510
+ ```
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@netlify/agent-runner-cli",
3
3
  "type": "module",
4
- "version": "1.68.0",
4
+ "version": "1.69.0",
5
5
  "description": "CLI tool for running Netlify agents",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",