You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 27b6027
Browse filesBrowse the repository at this point in the historyBrowse files
Partner placement is split into three separate decisions:
4
+
5
+
-**Eligibility:** whether a partner can appear for a surface, category, library, feature, or user context.
6
+
-**Tier:** the partner's commercial/display tier, which controls visual treatment and relative access to surfaces.
7
+
-**Order:** the sequence used inside a surface once eligible partners are known.
8
+
9
+
The order policy should be explicit per surface. Avoid treating the partner array order or legacy score as the policy itself.
10
+
11
+
## Order Strategies
12
+
13
+
### `static-curated`
14
+
15
+
Use for surfaces where the order is editorially or commercially curated and should remain stable. These surfaces can preserve the existing order, use partner seniority, or use private commercial priority when seniority is unavailable.
16
+
17
+
Do not expose private commercial criteria in client-facing field names, comments, or API responses. If a private priority is required, keep the public/client value generic, such as `placementPriority` or an already-resolved ordered list.
18
+
19
+
### `tier-rotated`
20
+
21
+
Use for visible partner surfaces where partners in the same tier should cycle over time. Tier order remains fixed, but partners inside the same tier are selected with weighted-random ordering for each page view and surface.
22
+
23
+
The root loader creates the initial page-view seed, so SSR and hydration receive the same value through loader data. A React provider owns the seed after hydration and refreshes it on client-side page views. Each surface combines its stable surface id with the page-view seed; avoid runtime-generated component ids in the seed because they can diverge across server and client rendering. Partners default to equal probability within their tier; a future `placementWeight` can bias same-tier odds without changing the ordering API.
24
+
25
+
### `contextual-recommendation`
26
+
27
+
Use when a surface behaves like a product recommendation or builder suggestion. Product fit should be established before commercial weighting affects order. This strategy currently preserves the legacy tier/priority behavior while giving recommendation surfaces a separate policy label.
28
+
29
+
### `machine-readable`
30
+
31
+
Use for AI- or crawler-facing outputs such as `llms.txt` and JSON feeds. These should be deterministic and relevance-first. Do not rotate them merely for logo fairness unless the downstream behavior has been designed and disclosed as such.
32
+
33
+
## Reserved Rules
34
+
35
+
Reserved rules should be narrow. For example, Cloudflare is reserved as the first deployment/hosting partner in any list that contains Cloudflare and other deployment partners. That should not imply Cloudflare is globally first across every partner surface; non-deployment partners can still appear before Cloudflare when the surface is mixed-category.
36
+
37
+
Deployment action buttons are a deployment-only surface. Cloudflare remains first when present. Tier order is still preserved, and providers within the same tier can rotate by page view.
38
+
39
+
## Current Surfaces
40
+
41
+
- Partner directory: `tier-rotated` for active partners, `static-curated` for previous partners.
42
+
- Partner grids and embeds: `tier-rotated`.
43
+
- Docs and blog rails: `tier-rotated`.
44
+
- Mobile docs strip: `tier-rotated`.
45
+
- Builder feature picker and starter partner suggestions: `contextual-recommendation`.
46
+
- Deploy action buttons: `tier-rotated` with deployment provider tiers preserved.
47
+
-`llms.txt` and `/api/data/partners`: `machine-readable`.
48
+
49
+
## Analytics
50
+
51
+
Partner impression and click events should include:
52
+
53
+
-`partner_id`
54
+
-`placement`
55
+
-`slot_index`
56
+
-`partner_tier`
57
+
-`order_strategy`
58
+
-`rotation_seed` when the strategy rotates
59
+
60
+
This lets reporting distinguish partner performance from placement policy and makes under/over-exposure easier to debug.
61
+
62
+
## Contract Language
63
+
64
+
Suggested external framing:
65
+
66
+
> Partner tiers determine eligibility, visual treatment, reporting, and relative access to surfaces. Placement within the same tier may rotate or be curated depending on the surface. Some placements may include explicitly reserved rules for product, infrastructure, legal, or strategic reasons.
67
+
68
+
For AI-assisted selection:
69
+
70
+
> AI-assisted partner selection prioritizes user need and capability fit first. Partner tier may influence selection only among qualified options.
excerpt: 'TanStack Start now supports Rsbuild 2 alongside Vite, so teams can choose the build tool that best fits their stack, preferences, and infrastructure.'
7
+
library: start
8
+
---
9
+
10
+

11
+
12
+
TanStack Start now supports Rsbuild 2 alongside Vite, giving teams two supported build-tool paths for Start.
13
+
14
+
Some teams prefer Vite. Some prefer Rsbuild because it is built on Rspack, feels familiar to teams with Webpack experience, or matches how the rest of their frontend infrastructure is organized. The new adapter makes that choice explicit: Start fits into either build tool through its plugin system.
15
+
16
+
We built the adapter together with the Rsbuild and Rspack team, and some of that work fed back into Rsbuild and Rspack along the way.
React and Solid are supported today, and because the Rsbuild work lives at Start's shared build layer, future framework adapters have the same path available to them.
31
+
32
+
Rsbuild owns the build, while the framework plugin handles React or Solid and `tanstackStart()` wires Start into both the client and server builds. If you have used Start with Vite, the pattern is intentionally familiar because the Start build-tool plugins are designed to fit naturally into the toolchain you choose.
33
+
34
+
## A Start path for teams using Rsbuild
35
+
36
+
Some teams want to use Start with Rsbuild because they want Rspack under the hood, a Webpack-familiar model, or a build tool that matches the rest of their stack.
37
+
38
+
That also matters for existing apps. A team moving to Start can keep more of its build setup in place instead of changing framework and build tool at once. One large company is already using the adapter while moving several apps from a Webpack setup to Start on Rsbuild.
39
+
40
+
That path includes the full Start feature set, including React and Solid apps, Server Functions, SSR and streaming SSR, HMR, import protection, and React Server Components for React apps.
41
+
42
+
## The two-adapter rule
43
+
44
+
There is a pattern we keep running into at TanStack: the first adapter proves something can work, and the second adapter shows which parts actually belong in the core.
45
+
46
+
TanStack Router and TanStack Start already went through this once at the UI framework layer, where adding Solid made the React assumptions easier to see and improved the shared core. Adding Rsbuild brought the same clarity to the build layer.
47
+
48
+
Because the first Start build adapter was Vite, some build logic naturally grew around Vite's plugin lifecycle, and adding Rsbuild made the boundary sharper: shared Start build behavior in one place, build-tool-specific behavior in the Vite and Rsbuild adapters.
49
+
50
+
## Your build tool, your choice
51
+
52
+
Choosing Start should let a team keep its preferred build tool. With Vite and Rsbuild both supported, teams can use the toolchain that fits their stack while building the same kind of Start app.
53
+
54
+
Teams that prefer Rsbuild, or already work in the Webpack and Rspack world, now get a native Start integration through Rsbuild's plugin system.
55
+
56
+
The Rsbuild adapter is available now. Try it in a new Rsbuild project or in an existing codebase moving to Start.
Copy file name to clipboardExpand all lines: src/blog/tanstack-ai-orchestration.md
+79-76Lines changed: 79 additions & 76 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -29,29 +29,6 @@ This is where you come in. We need your help. We need people to test out our wor
29
29
30
30
The goal is simple: compose multiple typed LLM and agent steps as normal TypeScript async generators, stream each step to the UI, pause for human approval, and resume through the same SSE flow.
31
31
32
-
## Try the PR build
33
-
34
-
Install the PR packages directly from pkg.pr.new:
35
-
36
-
```bash
37
-
npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-orchestration@542
38
-
npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai@542
39
-
npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-react@542
40
-
npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-client@542
41
-
npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-openai@542
42
-
```
43
-
44
-
Use this only for evaluation, demos, and feedback. The public API can still change before stabilization.
45
-
46
-
Full documentation for this PR lives on the GitHub branch, not the released TanStack docs site:
Agents are typed wrappers around a `chat()` call or any async function. `defineAgent` gives each step an input schema, output schema, and implementation.
The interesting part is the lack of framework ceremony. TypeScript knows the input expected by `agents.writer`, and it knows the shape returned after the `yield*`. The runtime can emit lifecycle events around each yielded step, stream text while it runs, validate the result, snapshot state, and resume the generator with the typed output.
152
+
The interesting part is the lack of framework ceremony. TypeScript knows the input expected by `agents.writer`, and it knows the shape returned after the `yield*`.
178
153
179
-
Why async generator workflows? There are a lot of ways to model agent workflows. You can build a graph DSL. You can define nodes in JSON. You can describe a DAG and ask the runtime to interpret it. Well, the reason we decided to go with generator workflows is because whenever you yield the agent's step, it's streamed straight down to the client. The user sees everything in real time, and then, by the end of it, you just get the final output back. The user saw everything that went on: tool calls, reasoning, whatever.
154
+
Each `yield* agents.someAgent(...)` becomes a typed step. The runtime can emit lifecycle events around it, stream text while it runs, validate the result, snapshot state, and resume the generator with the typed output.
180
155
181
156
The workflow body is just TypeScript. Use `if`, `for`, `while`, `try`, `await`, helper functions, and whatever domain code you already have. The orchestration runtime only cares about the things you `yield*`.
182
157
183
-
Each `yield* agents.someAgent(...)` becomes a typed step. The runtime can emit lifecycle events around it, stream text while it runs, validate the result, snapshot state, and resume the generator with the typed output.
158
+
Why async generator workflows? There are a lot of ways to model agent workflows. You can build a graph DSL. You can define nodes in JSON. You can describe a DAG and ask the runtime to interpret it. The reason we went with generator workflows is that whenever you yield the agent's step, it's streamed straight down to the client. The user sees everything in real time — tool calls, reasoning, whatever happens along the way — and by the end you just get the final output back.
184
159
185
160
## Expose it over SSE
186
161
@@ -215,33 +190,57 @@ export async function POST(request: Request) {
215
190
216
191
The current built-in persistence is `inMemoryRunStore`. That is useful for local demos and single-process evaluation. Production durability is still future-facing and experimental run-store-interface territory, especially for long pauses, deploys, restarts, and multi-node environments. But the API is there to implement your own durable run store and swap it in when you're ready.
217
192
193
+
## Run it without a UI
194
+
195
+
A UI is optional. The SSE endpoint above exists so a browser can watch a run unfold, but the workflow itself is just a server-side async generator. If nothing is watching — a cron job, a queue worker, a batch script, or a plain JSON endpoint that only returns the final answer — you can run the workflow headless and never stream a single event to a client.
196
+
197
+
`runWorkflow` returns an async iterable, and iterating it is what drives the workflow forward. So for a headless run you drain the stream to completion, then read the final result back from the run store:
// No client is listening, so just drive the run to completion.
216
+
forawait (const _event ofstream) {
217
+
// Optionally log progress or persist events here.
218
+
}
219
+
220
+
const run =awaitrunStore.getRunState(runId)
221
+
222
+
if (run?.status==='finished') {
223
+
returnrun.output
224
+
}
225
+
226
+
thrownewError(run?.error?.message??`Workflow ended as "${run?.status}"`)
227
+
}
228
+
```
229
+
230
+
The same typed agents, schemas, state snapshots, and validation apply. You just skip `toServerSentEventsResponse`, the React hooks, and the browser entirely.
231
+
232
+
One caveat: a headless run has nobody to answer a `yield* approve(...)`. If the workflow hits an approval step, it pauses and `getRunState` reports `status: 'paused'` instead of `'finished'`. Server-only workflows shine when the pipeline runs end to end on its own. If you need a human in the loop, either resume the paused run programmatically with an `approval` result or keep a UI connected.
233
+
218
234
## Consume it from React
219
235
220
236
On the client, `WorkflowClient`, `useWorkflow`, and `useOrchestration` consume the streamed events and keep local run state updated.
@@ -439,27 +442,27 @@ Routers may also yield if their decision needs async work, but their return valu
439
442
440
443
That gives you the same behavior with a different control-flow style. A workflow puts the sequence directly in the generator body. An orchestrator puts the next-step decision in a router.
441
444
442
-
## What is still experimental?
443
-
444
-
Almost everything important enough to name.
445
-
446
-
The package name is `@tanstack/ai-orchestration`, the public feature name is TanStack AI Workflows & Orchestrators, and the core shape is visible in PR 542. But this is still a PR build.
447
-
448
-
Expect scrutiny around:
445
+
## Try it and send feedback
449
446
450
-
- API names and return shapes
451
-
- Durable persistence beyond `inMemoryRunStore`
452
-
- Resume behavior across deploys and multiple server nodes
453
-
- Event naming and AG-UI compatibility details
454
-
- How much of the run-store interface should be public now
455
-
- How examples should guide production usage without overstating guarantees
447
+
If you are building multi-step AI product flows, this is the moment to test the shape. This is a PR build meant for evaluation, demos, and feedback — the public API can still change before stabilization.
456
448
457
-
The current implementation is useful enough to try and early enough to change.
449
+
Install the PR packages directly from pkg.pr.new:
458
450
459
-
## Try it and send feedback
451
+
```bash
452
+
npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-orchestration@542
453
+
npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai@542
454
+
npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-react@542
455
+
npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-client@542
456
+
npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-openai@542
457
+
```
460
458
461
-
If you are building multi-step AI product flows, this is the moment to test the shape.
459
+
Full documentation for this PR lives on the GitHub branch, not the released TanStack docs site:
462
460
463
-
Try the [PR build packages](https://github.com/TanStack/ai/pull/542#issuecomment-4416347869). Read the branch docs for [workflows](https://github.com/TanStack/ai/blob/worktree-cryptic-singing-wadler/docs/orchestration/workflows.md), [orchestrators](https://github.com/TanStack/ai/blob/worktree-cryptic-singing-wadler/docs/orchestration/orchestrators.md), [approvals](https://github.com/TanStack/ai/blob/worktree-cryptic-singing-wadler/docs/orchestration/approvals.md), [run persistence](https://github.com/TanStack/ai/blob/worktree-cryptic-singing-wadler/docs/orchestration/run-persistence.md), and the [API reference](https://github.com/TanStack/ai/blob/worktree-cryptic-singing-wadler/docs/api/ai-orchestration.md).
0 commit comments