Stately
PackagesAgent

Models and providers

Reuse models and executors from other AI frameworks via AI SDK LanguageModel objects, raw ai functions, and OpenAI-compatible endpoints.

Alpha: @statelyai/agent 2.0 is in alpha. APIs can change between releases; pin an exact version. Feedback: github.com/statelyai/agent.

Reusing models from other frameworks

Where a host's executors come from is the only integration point. The shared type across frameworks is the AI SDK LanguageModel object: whatever framework hands you one, drop it into createAiSdkExecutors({ models }) for a full { generateText, streamText, decide } set:

import { createAiSdkExecutors } from "@statelyai/agent/ai-sdk";

const executors = createAiSdkExecutors({
  models: { quick: someLanguageModel, careful: anotherLanguageModel },
});

await runAgent(machine, { input, executors });

Three ways in, from most to least capable:

  • AI SDK adapter. Any LanguageModel (Mastra, Cloudflare Workers AI via workers-ai-provider, TanStack AI, OpenRouter's AI SDK provider, any @ai-sdk/* package). Full support, including decide.
  • OpenAI-compatible endpoints. Point createOpenAI({ baseURL }) from @ai-sdk/openai at any OpenAI-shaped endpoint (Groq, Ollama, vLLM, Together, LM Studio) and feed the result to the same adapter. Full support, including decide.
  • Raw ai functions. Pass ai's generateText/streamText as your executors set. Text only: decide needs an adapter, and structured output is best-effort.

Mastra models

Mastra is a TypeScript agent framework whose agents are configured with an AI SDK LanguageModel. Reuse that same model object as an executor, no re-config and no second provider setup:

import { openai } from "@ai-sdk/openai";
import { createAiSdkExecutors } from "@statelyai/agent/ai-sdk";

// The model you already pass to `new Agent({ model })` in Mastra.
// Model IDs here are illustrative; substitute your provider's current models.
const model = openai("gpt-5.4-mini");

await runAgent(machine, {
  input,
  executors: createAiSdkExecutors({ models: { quick: model } }),
});

Anything exposing a LanguageModel works the same way, so machine and Mastra share one model definition.

Cloudflare Workers AI

Workers AI runs models on Cloudflare's edge, reached through a binding on the Worker's env. The workers-ai-provider package turns that binding into an AI SDK provider, so its models are ordinary LanguageModel objects:

import { createWorkersAI } from "workers-ai-provider";
import { createAiSdkExecutors } from "@statelyai/agent/ai-sdk";

export default {
  async fetch(request, env) {
    const workersai = createWorkersAI({ binding: env.AI });
    const result = await runAgent(machine, {
      input: await request.json(),
      executors: createAiSdkExecutors({
        models: { quick: workersai("@cf/meta/llama-3.1-8b-instruct") },
      }),
    });
    return Response.json(result);
  },
};

Pass Cloudflare-specific per-call options through request metadata: the host owns it, the machine just carries it.

Ollama and OpenAI-compatible endpoints

Ollama runs models locally and serves them over an OpenAI-compatible HTTP API, so the AI SDK's OpenAI provider pointed at the local endpoint is enough. apiKey is optional: omit it for keyless local servers.

import { createOpenAI } from "@ai-sdk/openai";
import { createAiSdkExecutors } from "@statelyai/agent/ai-sdk";

const ollama = createOpenAI({ baseURL: "http://localhost:11434/v1" });

await runAgent(machine, {
  input,
  executors: createAiSdkExecutors({
    models: { quick: ollama("llama3.1") },
  }),
});

Swap baseURL (and add apiKey where the endpoint requires one) for Groq, vLLM, Together, OpenRouter, or LM Studio; nothing else changes.

If you would rather not depend on ai at all, write the three executors over raw fetch against the same Chat Completions endpoint: build the request body from the plain AgentTextRequest fields, and use buildEnvelopeSchema, getJsonSchema, and parseOutput from @statelyai/agent for structured output, plus renderDecisionAttempts for decision retries. See Hosts.

Raw AI SDK functions

The generateText/streamText executors accept the raw Vercel AI SDK functions directly, no adapter needed:

import { generateText, streamText } from "ai";

await runAgent(machine, { input, executors: { generateText, streamText } });

An AgentTextRequest is spread-compatible with the AI SDK's call options, and result shapes unwrap natively ({ text }; { textStream }, final text via await result.text). Two caveats:

  • Structured output is best-effort. A request with an outputSchema has its raw text JSON.parsed and validated; a parse failure throws. For reliable structured output, use createAiSdkExecutors.
  • decide needs an adapter. The tool-per-event mapping lives in the adapter; there is no raw AI SDK function for it.

Support by path

PathgenerateTextstreamTextdecideStructured output
createAiSdkExecutorsyesyesyesyes
Raw fetch executorsyesyesyesyes (you map it)
Raw ai functionsyesyesnobest-effort

The decide executor maps each machine event to a forced tool call, and that mapping lives in the adapter layer, so raw ai functions cannot back a decision. For reliable structured output, use createAiSdkExecutors or map the envelope yourself. See Text requests and Decisions.

Reference hosts by provider

Runnable hosts, one per provider stack:

ExampleBacking
ai-sdk-hostVercel AI SDK, through the shipped adapter
openai-sdk-hostraw openai (Chat Completions); structured via response_format, decisions via tool_choice: 'required'
anthropic-sdk-hostraw @anthropic-ai/sdk (Messages); structured via forced tool call, decisions via tool_choice: { type: 'any' }
cloudflare-agent-hostDurable Object
cloudflare-workers-ai-hostWorkers AI binding

Package entry points are @statelyai/agent (root), @statelyai/agent/ai-sdk, @statelyai/agent/machines, @statelyai/agent/otel, @statelyai/agent/sqlite, and @statelyai/agent/agent-workflow.json. Everything a hand-written host needs (buildEnvelopeSchema, getJsonSchema, parseOutput, parseStructuredEnvelope, getAgentOutputMode, resolveDecision, renderDecisionAttempts) comes from the root.

On this page