{"slug": "introducing-experimental-workflows-and-orchestrators-in-tanstack-ai", "title": "Introducing Experimental Workflows and Orchestrators in TanStack AI", "summary": "TanStack released experimental AI Workflows and Orchestrators on May 28, 2026, enabling developers to compose multi-step LLM and agent processes as TypeScript async generators with streaming, human-in-the-loop approval, and SSE-based state updates. The pre-release packages, available via pkg.pr.new for testing and feedback, aim to replace hand-rolled workflow engines with typed, composable orchestration mechanisms that pause for human input and resume through a single streaming flow.", "body_md": "*by Alem Tuzlak on May 28, 2026.*\n\nMost AI apps start with one chat() call.\n\nBut as soon as you need something more complex, this all breaks apart. You either fall back to using sub-agents as tools, or you have to write your own glue and abstractions on top to make a semi-decent workflow or orchestration mechanism to power your app. This just detracts from your time to work on the features you really care about.\n\nThe model needs to draft, critique, revise, ask for approval, call another model, update state, and show the user what is happening while the run is still in progress. At that point, a one-shot chat endpoint turns into a hand-rolled workflow engine made of fetch calls, temporary state, custom SSE events, and a lot of code nobody wanted to own.\n\nToday, TanStack introduces an experimental answer: **TanStack AI Workflows & Orchestrators**.\n\nBefore we go further, a fair warning!\n\nThis is not merged to main. It is not shipped, stable, or available in normal npm versions. It is a PR build you can try today through pkg.pr.new while the API is still being shaped. The goal is to get it in front of real use cases, demos, and feedback before we commit to the public API shape.\n\nThis is where you come in. We need your help. We need people to test out our workflows and our orchestration mechanisms, give us their thoughts and opinions, and help us shape the final APIs.\n\nThe 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.\n\nInstall the PR packages directly from pkg.pr.new:\n\n```\nnpm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-orchestration@542\nnpm i https://pkg.pr.new/TanStack/ai/@tanstack/ai@542\nnpm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-react@542\nnpm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-client@542\nnpm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-openai@542\nnpm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-orchestration@542\nnpm i https://pkg.pr.new/TanStack/ai/@tanstack/ai@542\nnpm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-react@542\nnpm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-client@542\nnpm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-openai@542\n```\n\nUse this only for evaluation, demos, and feedback. The public API can still change before stabilization.\n\nFull documentation for this PR lives on the GitHub branch, not the released TanStack docs site:\n\nAgents are typed wrappers around a chat() call or any async function. defineAgent gives each step an input schema, output schema, and implementation.\n\n``` js\nimport { chat } from '@tanstack/ai'\nimport { defineAgent } from '@tanstack/ai-orchestration'\nimport { openaiText } from '@tanstack/ai-openai'\nimport { z } from 'zod'\n\nexport const ArticleSchema = z.object({\n  title: z.string(),\n  body: z.string(),\n})\n\nexport const writer = defineAgent({\n  name: 'writer',\n  input: z.object({\n    topic: z.string(),\n  }),\n  output: ArticleSchema,\n  run: ({ input }) =>\n    chat({\n      adapter: openaiText('gpt-4o'),\n      outputSchema: ArticleSchema,\n      stream: true,\n      systemPrompts: [\n        'Write a concise developer article with a clear title and practical body.',\n      ],\n      messages: [{ role: 'user', content: input.topic }],\n    }),\n})\n\nexport const editor = defineAgent({\n  name: 'editor',\n  input: z.object({\n    article: ArticleSchema,\n    feedback: z.string().optional(),\n  }),\n  output: z.object({\n    approved: z.boolean(),\n    article: ArticleSchema,\n    notes: z.string(),\n  }),\n  run: ({ input }) =>\n    chat({\n      adapter: openaiText('gpt-4o'),\n      outputSchema: z.object({\n        approved: z.boolean(),\n        article: ArticleSchema,\n        notes: z.string(),\n      }),\n      stream: true,\n      systemPrompts: [\n        'Edit the article for accuracy, clarity, and practical developer tone. Apply any optional reviewer feedback.',\n      ],\n      messages: [{ role: 'user', content: JSON.stringify(input) }],\n    }),\n})\njs\nimport { chat } from '@tanstack/ai'\nimport { defineAgent } from '@tanstack/ai-orchestration'\nimport { openaiText } from '@tanstack/ai-openai'\nimport { z } from 'zod'\n\nexport const ArticleSchema = z.object({\n  title: z.string(),\n  body: z.string(),\n})\n\nexport const writer = defineAgent({\n  name: 'writer',\n  input: z.object({\n    topic: z.string(),\n  }),\n  output: ArticleSchema,\n  run: ({ input }) =>\n    chat({\n      adapter: openaiText('gpt-4o'),\n      outputSchema: ArticleSchema,\n      stream: true,\n      systemPrompts: [\n        'Write a concise developer article with a clear title and practical body.',\n      ],\n      messages: [{ role: 'user', content: input.topic }],\n    }),\n})\n\nexport const editor = defineAgent({\n  name: 'editor',\n  input: z.object({\n    article: ArticleSchema,\n    feedback: z.string().optional(),\n  }),\n  output: z.object({\n    approved: z.boolean(),\n    article: ArticleSchema,\n    notes: z.string(),\n  }),\n  run: ({ input }) =>\n    chat({\n      adapter: openaiText('gpt-4o'),\n      outputSchema: z.object({\n        approved: z.boolean(),\n        article: ArticleSchema,\n        notes: z.string(),\n      }),\n      stream: true,\n      systemPrompts: [\n        'Edit the article for accuracy, clarity, and practical developer tone. Apply any optional reviewer feedback.',\n      ],\n      messages: [{ role: 'user', content: JSON.stringify(input) }],\n    }),\n})\n```\n\ndefineAgent wraps either a chat() call or a normal async function with input and output schemas. The workflow runtime uses those schemas to validate what enters and leaves the step, and TypeScript uses them to infer the callable shape inside a workflow. From the workflow's perspective, writer and editor are normal typed async steps.\n\nOnce agents exist, defineWorkflow composes them with yield* inside an async function*.\n\n``` js\nimport {\n  approve,\n  defineWorkflow,\n  fail,\n  succeed,\n} from '@tanstack/ai-orchestration'\nimport { z } from 'zod'\nimport { ArticleSchema, editor, writer } from './agents'\n\nconst ArticleInputSchema = z.object({\n  topic: z.string(),\n})\n\nconst ArticleWorkflowOutputSchema = z.discriminatedUnion('ok', [\n  z.object({\n    ok: z.literal(true),\n    article: ArticleSchema,\n  }),\n  z.object({\n    ok: z.literal(false),\n    reason: z.string(),\n  }),\n])\n\nexport const articleWorkflow = defineWorkflow({\n  name: 'article-workflow',\n  input: ArticleInputSchema,\n  output: ArticleWorkflowOutputSchema,\n  agents: {\n    writer,\n    editor,\n  },\n  run: async function* ({ input, agents }) {\n    const draft = yield* agents.writer({ topic: input.topic })\n    const edited = yield* agents.editor({ article: draft })\n\n    if (!edited.approved) {\n      return fail(edited.notes)\n    }\n\n    const decision = yield* approve({\n      title: `Publish \"${edited.article.title}\"?`,\n      description: edited.notes,\n    })\n\n    if (!decision.approved) {\n      return fail(decision.feedback ?? 'Publication declined')\n    }\n\n    return succeed({ article: edited.article })\n  },\n})\njs\nimport {\n  approve,\n  defineWorkflow,\n  fail,\n  succeed,\n} from '@tanstack/ai-orchestration'\nimport { z } from 'zod'\nimport { ArticleSchema, editor, writer } from './agents'\n\nconst ArticleInputSchema = z.object({\n  topic: z.string(),\n})\n\nconst ArticleWorkflowOutputSchema = z.discriminatedUnion('ok', [\n  z.object({\n    ok: z.literal(true),\n    article: ArticleSchema,\n  }),\n  z.object({\n    ok: z.literal(false),\n    reason: z.string(),\n  }),\n])\n\nexport const articleWorkflow = defineWorkflow({\n  name: 'article-workflow',\n  input: ArticleInputSchema,\n  output: ArticleWorkflowOutputSchema,\n  agents: {\n    writer,\n    editor,\n  },\n  run: async function* ({ input, agents }) {\n    const draft = yield* agents.writer({ topic: input.topic })\n    const edited = yield* agents.editor({ article: draft })\n\n    if (!edited.approved) {\n      return fail(edited.notes)\n    }\n\n    const decision = yield* approve({\n      title: `Publish \"${edited.article.title}\"?`,\n      description: edited.notes,\n    })\n\n    if (!decision.approved) {\n      return fail(decision.feedback ?? 'Publication declined')\n    }\n\n    return succeed({ article: edited.article })\n  },\n})\n```\n\nThe 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.\n\nWhy 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.\n\nThe 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*.\n\nEach 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.\n\nWorkflows run on the server. The browser consumes an event stream.\n\nThe PR adds parseWorkflowRequest, runWorkflow, and inMemoryRunStore from @tanstack/ai-orchestration. You can pipe the returned stream through the existing toServerSentEventsResponse helper from @tanstack/ai.\n\n``` js\nimport { toServerSentEventsResponse } from '@tanstack/ai'\nimport {\n  inMemoryRunStore,\n  parseWorkflowRequest,\n  runWorkflow,\n} from '@tanstack/ai-orchestration'\nimport { articleWorkflow } from './article-workflow'\n\nconst runStore = inMemoryRunStore({ ttl: 60 * 60 * 1000 })\n\nexport async function POST(request: Request) {\n  const params = await parseWorkflowRequest(request)\n  const stream = runWorkflow({\n    workflow: articleWorkflow,\n    runStore,\n    ...params,\n  })\n\n  return toServerSentEventsResponse(stream)\n}\njs\nimport { toServerSentEventsResponse } from '@tanstack/ai'\nimport {\n  inMemoryRunStore,\n  parseWorkflowRequest,\n  runWorkflow,\n} from '@tanstack/ai-orchestration'\nimport { articleWorkflow } from './article-workflow'\n\nconst runStore = inMemoryRunStore({ ttl: 60 * 60 * 1000 })\n\nexport async function POST(request: Request) {\n  const params = await parseWorkflowRequest(request)\n  const stream = runWorkflow({\n    workflow: articleWorkflow,\n    runStore,\n    ...params,\n  })\n\n  return toServerSentEventsResponse(stream)\n}\n```\n\nrunWorkflow emits AG-UI-style lifecycle events for the run. That includes run and step events, state snapshots, JSON Patch state deltas, output, and errors. The UI does not need to invent its own event protocol for \"writer started\", \"editor streamed text\", \"approval requested\", or \"run finished\".\n\nThe 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.\n\nOn the client, WorkflowClient, useWorkflow, and useOrchestration consume the streamed events and keep local run state updated.\n\n``` js\nimport { fetchWorkflowEvents, useWorkflow } from '@tanstack/ai-react'\nimport { z } from 'zod'\n\nconst ArticleSchema = z.object({\n  title: z.string(),\n  body: z.string(),\n})\n\nconst ArticleInputSchema = z.object({\n  topic: z.string(),\n})\n\nconst ArticleWorkflowOutputSchema = z.discriminatedUnion('ok', [\n  z.object({\n    ok: z.literal(true),\n    article: ArticleSchema,\n  }),\n  z.object({\n    ok: z.literal(false),\n    reason: z.string(),\n  }),\n])\n\nexport function ArticleWorkflowDemo() {\n  const workflow = useWorkflow({\n    input: ArticleInputSchema,\n    output: ArticleWorkflowOutputSchema,\n    connection: fetchWorkflowEvents('/api/article-workflow'),\n  })\n\n  return (\n    <section>\n      <button\n        type=\"button\"\n        disabled={workflow.status === 'running'}\n        onClick={() =>\n          workflow.start({\n            topic: 'How typed agent workflows improve AI product UX',\n          })\n        }\n      >\n        Start workflow\n      </button>\n\n      {workflow.steps.map((step) => (\n        <div key={step.stepId}>\n          <strong>{step.stepName}</strong>\n          <span>{step.status}</span>\n        </div>\n      ))}\n\n      {workflow.currentText ? <pre>{workflow.currentText}</pre> : null}\n\n      {workflow.pendingApproval ? (\n        <div>\n          <h2>{workflow.pendingApproval.title}</h2>\n          <p>{workflow.pendingApproval.description}</p>\n          <button type=\"button\" onClick={() => workflow.approve(true)}>\n            Approve\n          </button>\n          <button\n            type=\"button\"\n            onClick={() => workflow.approve(false, 'Tighten the conclusion.')}\n          >\n            Request changes\n          </button>\n        </div>\n      ) : null}\n\n      {workflow.output?.ok ? (\n        <article>\n          <h1>{workflow.output.article.title}</h1>\n          <p>{workflow.output.article.body}</p>\n        </article>\n      ) : null}\n\n      {workflow.output && !workflow.output.ok ? (\n        <p>{workflow.output.reason}</p>\n      ) : null}\n    </section>\n  )\n}\njs\nimport { fetchWorkflowEvents, useWorkflow } from '@tanstack/ai-react'\nimport { z } from 'zod'\n\nconst ArticleSchema = z.object({\n  title: z.string(),\n  body: z.string(),\n})\n\nconst ArticleInputSchema = z.object({\n  topic: z.string(),\n})\n\nconst ArticleWorkflowOutputSchema = z.discriminatedUnion('ok', [\n  z.object({\n    ok: z.literal(true),\n    article: ArticleSchema,\n  }),\n  z.object({\n    ok: z.literal(false),\n    reason: z.string(),\n  }),\n])\n\nexport function ArticleWorkflowDemo() {\n  const workflow = useWorkflow({\n    input: ArticleInputSchema,\n    output: ArticleWorkflowOutputSchema,\n    connection: fetchWorkflowEvents('/api/article-workflow'),\n  })\n\n  return (\n    <section>\n      <button\n        type=\"button\"\n        disabled={workflow.status === 'running'}\n        onClick={() =>\n          workflow.start({\n            topic: 'How typed agent workflows improve AI product UX',\n          })\n        }\n      >\n        Start workflow\n      </button>\n\n      {workflow.steps.map((step) => (\n        <div key={step.stepId}>\n          <strong>{step.stepName}</strong>\n          <span>{step.status}</span>\n        </div>\n      ))}\n\n      {workflow.currentText ? <pre>{workflow.currentText}</pre> : null}\n\n      {workflow.pendingApproval ? (\n        <div>\n          <h2>{workflow.pendingApproval.title}</h2>\n          <p>{workflow.pendingApproval.description}</p>\n          <button type=\"button\" onClick={() => workflow.approve(true)}>\n            Approve\n          </button>\n          <button\n            type=\"button\"\n            onClick={() => workflow.approve(false, 'Tighten the conclusion.')}\n          >\n            Request changes\n          </button>\n        </div>\n      ) : null}\n\n      {workflow.output?.ok ? (\n        <article>\n          <h1>{workflow.output.article.title}</h1>\n          <p>{workflow.output.article.body}</p>\n        </article>\n      ) : null}\n\n      {workflow.output && !workflow.output.ok ? (\n        <p>{workflow.output.reason}</p>\n      ) : null}\n    </section>\n  )\n}\n```\n\nThere is also useOrchestration for the same runtime with orchestration vocabulary. If your mental model is \"run a workflow\", use useWorkflow. If your mental model is \"let a router pick the next agent\", use useOrchestration.\n\n``` js\nimport { fetchWorkflowEvents, useOrchestration } from '@tanstack/ai-react'\n\nexport function ArticleOrchestrationDemo() {\n  const orchestration = useOrchestration({\n    input: ArticleInputSchema,\n    output: ArticleWorkflowOutputSchema,\n    connection: fetchWorkflowEvents('/api/article-orchestrator'),\n  })\n\n  return (\n    <button\n      type=\"button\"\n      disabled={orchestration.status === 'running'}\n      onClick={() =>\n        orchestration.start({\n          topic: 'How typed orchestration improves AI product UX',\n        })\n      }\n    >\n      Start orchestration\n    </button>\n  )\n}\njs\nimport { fetchWorkflowEvents, useOrchestration } from '@tanstack/ai-react'\n\nexport function ArticleOrchestrationDemo() {\n  const orchestration = useOrchestration({\n    input: ArticleInputSchema,\n    output: ArticleWorkflowOutputSchema,\n    connection: fetchWorkflowEvents('/api/article-orchestrator'),\n  })\n\n  return (\n    <button\n      type=\"button\"\n      disabled={orchestration.status === 'running'}\n      onClick={() =>\n        orchestration.start({\n          topic: 'How typed orchestration improves AI product UX',\n        })\n      }\n    >\n      Start orchestration\n    </button>\n  )\n}\n```\n\nHuman-in-the-loop control is not a side channel in this PR.\n\nWhen workflow code calls yield* approve(...), the runtime emits an approval-requested event, persists the paused run in the run store, and closes the SSE response. The HTTP request is done. The server does not keep a socket open while a user thinks, checks a diff, or gets approval from someone else.\n\nWhen the client calls approve(), it POSTs back to the same endpoint with the run id and approval result. runWorkflow resumes the generator and the next SSE response continues from the paused point.\n\nThat means approvals, revisions, and denial feedback can be modeled in the workflow itself:\n\n``` js\nconst decision =\n  yield *\n  approve({\n    title: 'Publish article?',\n    description: edited.notes,\n  })\n\nif (!decision.approved) {\n  const revised =\n    yield *\n    agents.editor({\n      article: edited.article,\n      feedback: decision.feedback ?? 'Revise before publishing.',\n    })\n\n  return succeed({ article: revised.article })\n}\njs\nconst decision =\n  yield *\n  approve({\n    title: 'Publish article?',\n    description: edited.notes,\n  })\n\nif (!decision.approved) {\n  const revised =\n    yield *\n    agents.editor({\n      article: edited.article,\n      feedback: decision.feedback ?? 'Revise before publishing.',\n    })\n\n  return succeed({ article: revised.article })\n}\n```\n\nThe same event stream also carries state snapshots and JSON Patch deltas, so a UI can show a live state inspector, a timeline, or a draft preview without waiting for the final result.\n\nUse a workflow when you know the pipeline.\n\nWriter, then editor, then approval is a workflow. Extract topics, draft outline, expand sections is a workflow. Run static checks, ask for approval, deploy to staging, ask again, deploy to production is a workflow.\n\nUse an orchestrator when the next step should be selected turn by turn.\n\ndefineOrchestrator is a thin wrapper over workflows, so the workflow behavior already described applies the same way to orchestrators. It uses the same runtime pieces: typed agents, streaming steps, state snapshots, approvals, SSE transport, and React hooks.\n\nThe body is a router. The router looks at input, mutable state, and the previous result, then returns the next agent to run. When the router is done, it returns the final output.\n\n``` js\nimport {\n  defineOrchestrator,\n  defineRouter,\n  succeed,\n} from '@tanstack/ai-orchestration'\nimport { z } from 'zod'\nimport { ArticleSchema, editor, writer } from './agents'\n\nconst ArticleStateSchema = z.object({\n  draft: ArticleSchema.optional(),\n  approved: z.boolean().default(false),\n})\n\nconst articleRouter = defineRouter(\n  {\n    name: 'article-router',\n    state: ArticleStateSchema,\n  },\n  async function* ({ input, state }) {\n    if (!state.draft) {\n      return {\n        agent: 'writer',\n        input: {\n          topic: input.topic,\n        },\n      }\n    }\n\n    if (!state.approved) {\n      return {\n        agent: 'editor',\n        input: {\n          article: state.draft,\n        },\n      }\n    }\n\n    return {\n      done: true,\n      output: succeed({\n        article: state.draft,\n      }),\n    }\n  },\n)\n\nexport const articleOrchestrator = defineOrchestrator({\n  name: 'article-orchestrator',\n  input: ArticleInputSchema,\n  output: ArticleWorkflowOutputSchema,\n  agents: {\n    writer,\n    editor,\n  },\n  router: articleRouter,\n})\njs\nimport {\n  defineOrchestrator,\n  defineRouter,\n  succeed,\n} from '@tanstack/ai-orchestration'\nimport { z } from 'zod'\nimport { ArticleSchema, editor, writer } from './agents'\n\nconst ArticleStateSchema = z.object({\n  draft: ArticleSchema.optional(),\n  approved: z.boolean().default(false),\n})\n\nconst articleRouter = defineRouter(\n  {\n    name: 'article-router',\n    state: ArticleStateSchema,\n  },\n  async function* ({ input, state }) {\n    if (!state.draft) {\n      return {\n        agent: 'writer',\n        input: {\n          topic: input.topic,\n        },\n      }\n    }\n\n    if (!state.approved) {\n      return {\n        agent: 'editor',\n        input: {\n          article: state.draft,\n        },\n      }\n    }\n\n    return {\n      done: true,\n      output: succeed({\n        article: state.draft,\n      }),\n    }\n  },\n)\n\nexport const articleOrchestrator = defineOrchestrator({\n  name: 'article-orchestrator',\n  input: ArticleInputSchema,\n  output: ArticleWorkflowOutputSchema,\n  agents: {\n    writer,\n    editor,\n  },\n  router: articleRouter,\n})\n```\n\nRouters may also yield if their decision needs async work, but their return value is the important part: either the next { agent, input } pair to run or { done, output } when orchestration is complete.\n\nThat 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.\n\nAlmost everything important enough to name.\n\nThe 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.\n\nExpect scrutiny around:\n\nThe current implementation is useful enough to try and early enough to change.\n\nIf you are building multi-step AI product flows, this is the moment to test the shape.\n\nTry 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).\n\nThen share demos, feedback, and rough edges on [PR 542](https://github.com/TanStack/ai/pull/542) before the API stabilizes.", "url": "https://wpnews.pro/news/introducing-experimental-workflows-and-orchestrators-in-tanstack-ai", "canonical_source": "https://tanstack.com/blog/tanstack-ai-orchestration", "published_at": "2026-05-28 12:00:00+00:00", "updated_at": "2026-05-28 19:46:32.129550+00:00", "lang": "en", "topics": ["ai-tools", "ai-products", "ai-agents", "ai-infrastructure", "generative-ai"], "entities": ["TanStack", "Alem Tuzlak", "TanStack AI", "TanStack AI Workflows & Orchestrators"], "alternates": {"html": "https://wpnews.pro/news/introducing-experimental-workflows-and-orchestrators-in-tanstack-ai", "markdown": "https://wpnews.pro/news/introducing-experimental-workflows-and-orchestrators-in-tanstack-ai.md", "text": "https://wpnews.pro/news/introducing-experimental-workflows-and-orchestrators-in-tanstack-ai.txt", "jsonld": "https://wpnews.pro/news/introducing-experimental-workflows-and-orchestrators-in-tanstack-ai.jsonld"}}