|
1 | 1 | --- |
2 | | -title: "Code Mode: Let Your AI Write Programs, Not Just Call Tools" |
| 2 | +title: 'Code Mode: Let Your AI Write Programs, Not Just Call Tools' |
3 | 3 | published: 2026-04-08 |
4 | 4 | excerpt: One tool call at a time is the bottleneck. TanStack AI Code Mode lets the LLM write and execute TypeScript programs in secure sandboxes, composing your tools with loops, conditionals, and Promise.all in a single shot. |
5 | 5 | authors: |
@@ -51,21 +51,21 @@ Without Code Mode, the LLM calls `getTopProducts`, waits for the result, then ca |
51 | 51 | With Code Mode, the LLM writes this: |
52 | 52 |
|
53 | 53 | ```typescript |
54 | | -const top = await external_getTopProducts({ limit: 5 }); |
| 54 | +const top = await external_getTopProducts({ limit: 5 }) |
55 | 55 |
|
56 | 56 | const ratings = await Promise.all( |
57 | 57 | top.products.map((p) => external_getProductRatings({ productId: p.id })), |
58 | | -); |
| 58 | +) |
59 | 59 |
|
60 | 60 | return top.products.map((product, i) => { |
61 | | - const scores = ratings[i].ratings.map((r) => r.score); |
62 | | - const avg = scores.reduce((sum, s) => sum + s, 0) / scores.length; |
| 61 | + const scores = ratings[i].ratings.map((r) => r.score) |
| 62 | + const avg = scores.reduce((sum, s) => sum + s, 0) / scores.length |
63 | 63 | return { |
64 | 64 | name: product.name, |
65 | 65 | sales: product.totalSales, |
66 | 66 | averageRating: Math.round(avg * 100) / 100, |
67 | | - }; |
68 | | -}); |
| 67 | + } |
| 68 | +}) |
69 | 69 | ``` |
70 | 70 |
|
71 | 71 | One tool call. Five API fetches in parallel. Math computed in JavaScript, not in the model. The averages are correct to the penny. The context window savings compound fast: every round-trip you eliminate is hundreds of tokens you don't spend. |
@@ -100,43 +100,43 @@ pnpm add @tanstack/ai-isolate-cloudflare |
100 | 100 | Same `toolDefinition()` API you already use. Nothing changes here: |
101 | 101 |
|
102 | 102 | ```typescript |
103 | | -import { toolDefinition } from "@tanstack/ai"; |
104 | | -import { z } from "zod"; |
| 103 | +import { toolDefinition } from '@tanstack/ai' |
| 104 | +import { z } from 'zod' |
105 | 105 |
|
106 | 106 | const fetchWeather = toolDefinition({ |
107 | | - name: "fetchWeather", |
108 | | - description: "Get current weather for a city", |
| 107 | + name: 'fetchWeather', |
| 108 | + description: 'Get current weather for a city', |
109 | 109 | inputSchema: z.object({ location: z.string() }), |
110 | 110 | outputSchema: z.object({ |
111 | 111 | temperature: z.number(), |
112 | 112 | condition: z.string(), |
113 | 113 | }), |
114 | 114 | }).server(async ({ location }) => { |
115 | | - const res = await fetch(`https://api.weather.example/v1?city=${location}`); |
116 | | - return res.json(); |
117 | | -}); |
| 115 | + const res = await fetch(`https://api.weather.example/v1?city=${location}`) |
| 116 | + return res.json() |
| 117 | +}) |
118 | 118 | ``` |
119 | 119 |
|
120 | 120 | ### Create Code Mode and use it with `chat()` |
121 | 121 |
|
122 | 122 | ```typescript |
123 | | -import { chat } from "@tanstack/ai"; |
124 | | -import { openaiText } from "@tanstack/ai-openai"; |
125 | | -import { createCodeMode } from "@tanstack/ai-code-mode"; |
126 | | -import { createNodeIsolateDriver } from "@tanstack/ai-isolate-node"; |
| 123 | +import { chat } from '@tanstack/ai' |
| 124 | +import { openaiText } from '@tanstack/ai-openai' |
| 125 | +import { createCodeMode } from '@tanstack/ai-code-mode' |
| 126 | +import { createNodeIsolateDriver } from '@tanstack/ai-isolate-node' |
127 | 127 |
|
128 | 128 | const { tool, systemPrompt } = createCodeMode({ |
129 | 129 | driver: createNodeIsolateDriver(), |
130 | 130 | tools: [fetchWeather], |
131 | 131 | timeout: 30_000, |
132 | | -}); |
| 132 | +}) |
133 | 133 |
|
134 | 134 | const result = await chat({ |
135 | | - adapter: openaiText("gpt-4o"), |
136 | | - systemPrompts: ["You are a helpful assistant.", systemPrompt], |
| 135 | + adapter: openaiText('gpt-4o'), |
| 136 | + systemPrompts: ['You are a helpful assistant.', systemPrompt], |
137 | 137 | tools: [tool], |
138 | 138 | messages, |
139 | | -}); |
| 139 | +}) |
140 | 140 | ``` |
141 | 141 |
|
142 | 142 | `createCodeMode` returns two things: the `execute_typescript` tool and a system prompt containing typed function stubs for every tool you passed in. The model sees exact input/output types, so it generates correct calls without guessing parameter shapes. TypeScript annotations are stripped automatically before execution. |
@@ -176,30 +176,30 @@ Right now the model rewrites the same logic every time. If it figures out a good |
176 | 176 | **High-level**: `codeModeWithSkills()` handles everything. Skill selection via a cheap LLM call, tool registry assembly, system prompt generation. |
177 | 177 |
|
178 | 178 | ```typescript |
179 | | -import { codeModeWithSkills } from "@tanstack/ai-code-mode-skills"; |
180 | | -import { createFileSkillStorage } from "@tanstack/ai-code-mode-skills/storage"; |
181 | | -import { createNodeIsolateDriver } from "@tanstack/ai-isolate-node"; |
182 | | -import { openaiText } from "@tanstack/ai-openai"; |
| 179 | +import { codeModeWithSkills } from '@tanstack/ai-code-mode-skills' |
| 180 | +import { createFileSkillStorage } from '@tanstack/ai-code-mode-skills/storage' |
| 181 | +import { createNodeIsolateDriver } from '@tanstack/ai-isolate-node' |
| 182 | +import { openaiText } from '@tanstack/ai-openai' |
183 | 183 |
|
184 | | -const storage = createFileSkillStorage({ directory: "./.skills" }); |
| 184 | +const storage = createFileSkillStorage({ directory: './.skills' }) |
185 | 185 |
|
186 | 186 | const { toolsRegistry, systemPrompt } = await codeModeWithSkills({ |
187 | 187 | config: { |
188 | 188 | driver: createNodeIsolateDriver(), |
189 | 189 | tools: [myTool1, myTool2], |
190 | 190 | timeout: 60_000, |
191 | 191 | }, |
192 | | - adapter: openaiText("gpt-4o-mini"), // cheap model for skill selection |
| 192 | + adapter: openaiText('gpt-4o-mini'), // cheap model for skill selection |
193 | 193 | skills: { storage, maxSkillsInContext: 5 }, |
194 | 194 | messages, |
195 | | -}); |
| 195 | +}) |
196 | 196 |
|
197 | 197 | const stream = chat({ |
198 | | - adapter: openaiText("gpt-4o"), // strong model for reasoning |
| 198 | + adapter: openaiText('gpt-4o'), // strong model for reasoning |
199 | 199 | toolRegistry: toolsRegistry, |
200 | 200 | messages, |
201 | | - systemPrompts: ["You are a helpful assistant.", systemPrompt], |
202 | | -}); |
| 201 | + systemPrompts: ['You are a helpful assistant.', systemPrompt], |
| 202 | +}) |
203 | 203 | ``` |
204 | 204 |
|
205 | 205 | **Manual**: use `createCodeMode`, `skillsToTools`, and `createSkillManagementTools` individually when you want full control over which skills load and how they're assembled. |
|
0 commit comments