# Ax (ax-llm/ax): DSPy-style signatures and optimizers for TypeScript

> Ax is a TypeScript-first framework for typed LLM generation, agents and flows, with the same program model compiled into Python, Java, C++, Go and Rust. It suits teams that want DSPy's ideas without leaving the Node ecosystem.

**ax-llm/ax** — The pretty much "official" DSPy framework for Typescript

- Repository: https://github.com/ax-llm/ax
- Website: http://axllm.dev
- Stars: 2,956 · Forks: 194
- Language: TypeScript
- License: Apache-2.0
- Published: 2026-09-09 · Updated: 2026-09-09 · Language: en
- Canonical page: https://hysenlabs.com/projects/ax-llm-ax

## The problem Ax solves for TypeScript teams

Most JavaScript LLM code is prompt strings plus a JSON.parse and a try/catch. The prompt, the expected shape and the validation live in three places, and nothing connects them. Ax's answer is the signature: a declaration of the input and output fields with their types, written as a string DSL, built with the fluent f() builder, or expressed as a Standard Schema v1 validator such as Zod, Valibot or ArkType. The README's opening example is a one-line signature, 'review:string -> sentiment:class "positive, negative, neutral"', and the returned sentiment is typed as the literal union. That is the whole pitch. The framework is for engineers who already write TypeScript and want the DSPy programming model, signatures plus optimizers, rather than a prompt template library. It is also for teams that expect the same program to be reimplemented in another language later: the repository compiles one semantic core into TypeScript, Python, Java, C++, Go and Rust packages.

## Signatures, deployment profiles and the thin hot path

A signature is rendered, sent to a provider, parsed and returned as a typed value. The README describes this loop as intentionally thin, and says streaming is the default so Ax can parse fields as they arrive, run streaming assertions, fail early and cancel an in-flight stream instead of paying for an output already known to be invalid. forward() still returns a single final object; streamingForward() exposes the stream. Provider selection happens through a named deployment profile rather than a model string. ai({ name: "openai", apiKey: ... }) creates the client, and the README states that switching to "anthropic", "google-gemini", "meta", "together", "fireworks", "deepseek" or "grok" keeps the same signature and code. The distinction matters: the deployment name selects wire behavior, and the model ID is resolved only inside it, so a DeepSeek model hosted by Together uses Together's endpoint and reasoning rules. Around that core sit AxAgent, with context budgets, checkpoints, action-log replay, memory, skills and delegation, and AxFlow, described as typed program graphs with branches, loops, feedback, cache behavior, parallel execution and .returns(...) projection. GEPA and few-shot bootstrapping sit in the optimizer layer, with portable optimizer artifacts and evaluation/apply flows.

## Installing @ax-llm/ax and running a first signature

The TypeScript package is the source implementation and the published package, so install it from npm. The README's 30 seconds example uses an environment variable for the key, which keeps credentials out of the source file.

```bash
npm install @ax-llm/ax
export OPENAI_APIKEY=sk-...
```

A first program declares the signature, creates the client and calls forward(). The README gives this exact shape, with the review string as input and sentiment as the typed output.

```typescript
import { ai, ax } from "@ax-llm/ax";

const llm = ai({ name: "openai", apiKey: process.env.OPENAI_APIKEY });

const classify = ax(
  'review:string -> sentiment:class "positive, negative, neutral"',
);

const { sentiment } = await classify.forward(llm, {
  review: "This product is amazing!",
});
```

What you should see is a destructured sentiment whose value is one of the three literals in the signature, not an arbitrary string. If you want the other language bindings, the repository runner executes the committed examples without you assembling compiler commands, for instance npm run example -- python src/examples/python/generation/axgen-openai.py. The README points to src/examples/README.md for the full runnable list.

## The cross-language compiler is the unusual part, and the riskiest

AxIR is a language-agnostic intermediate representation, and the generated Python, Java, C++, Go and Rust libraries are checked in under packages/<language> so the supported APIs can be inspected. When AxIR changes, the README says to run npm run axir:generate-packages to refresh those packages. The repository's test script reflects how much machinery this implies: test:axir, test:axir:tools, test:axir:packages, test:axir:conformance-sync and test:axir:perturb-check all run as part of npm test. That is a serious amount of generated code to keep in step. The practical consequence is that the TypeScript package is where the design lives, and the other languages follow it. If you are a Python team looking for the most idiomatic option, a generated library that mirrors a TypeScript-first API is a different proposition from a library written for Python first, and the README does not claim otherwise. The Go entry is listed as installable with go get plus an opt-in runtime/goja actor runtime, C++ is a source build through CMake FetchContent, and Rust is described as a protocol-first code runtime.

## Optimizers cost tokens, and the README does not price them

GEPA, few-shot bootstrapping and the evaluation/apply flow are the features that separate Ax from a typed client wrapper. They are also the features with the least operational detail in the README. An optimizer improves a program by running it against examples and a metric, which means repeated provider calls; the README does not document a cost model, a stopping rule, or how to bound a run. Treat the optimizer as a training job with a bill attached, and decide your evaluation set and metric before you start. The other gap is provider coverage. The README names openai, anthropic, google-gemini, meta, together, fireworks, deepseek and grok, and the topics list adds ollama and cohere, but nothing in the repository states that every feature, streaming assertions or tool calling included, behaves identically across all of them. The deployment profile abstraction exists precisely because wire behavior differs. Verify your provider against docs/AI_PROFILES.md before you build a flow on top of it.

## Ax next to DSPy, and when a plain SDK is the better call

The project describes itself as the pretty much official DSPy framework for TypeScript, and the comparison people search for is Ax against DSPy. The difference is the host language and the runtime. DSPy is a Python framework, so its signatures, modules and optimizers are Python objects; Ax is TypeScript-first and ships as @ax-llm/ax, with the same ideas compiled into Python and four other languages through AxIR. If your application, build pipeline and type system are TypeScript, Ax removes the need to run a Python service beside your Node process just to get signatures and GEPA. If your team is already deep in Python and wants the reference implementation with the ecosystem around it, generated bindings are a weaker reason to switch than a native Python library. And if all you need is one call to one model with a JSON schema, a provider SDK plus a validator does the job with fewer moving parts; Ax earns its place when you want the same typed program to be optimized, streamed, wrapped in an agent, or reimplemented in another language.

## Licence, release cadence and upgrade cost

Ax is Apache-2.0, which permits commercial use and modification and includes an explicit patent grant; the usual obligations around attribution and notices apply, and if you redistribute a modified version there are change-notice expectations. That is a summary of the identifier, not legal advice, and the LICENSE file in the repository is the authority. Upgrade cost is the more practical question. The project is on the 24.x line, with 24.0.18 published on 2026-09-09, and the last push to the main branch was on 2026-09-09. Releases in the 24.0.x series arrive days apart, so pinning a version and reading CHANGELOG.md before moving is cheaper than tracking main. The repository also carries AGENTS.md, a contribution policy test and a release-workflow test in npm test, which tells you the project enforces its own process rather than accepting patches ad hoc. For a framework that generates five language bindings from one IR, that discipline is the thing to rely on when you decide how often to upgrade.

## Conclusion

Adopt Ax if you are building in TypeScript and want typed structured output, agents and flows under one signature model, or if you need the same program shape in Python, Java, C++, Go or Rust. Do not adopt it if you want a thin SDK wrapper over one provider, or if you cannot accept an optimizer loop that runs your program repeatedly against a metric. Before committing, read docs/AI_PROFILES.md to confirm your provider is covered, and run one signature through forward() against your own model to see the parse and retry behavior on your data.

## FAQ

### What is ax in AI?

Ax is a framework for building with large language models, described in its README as DSPy for TypeScript, Python, Java, C++, Go and Rust. It is TypeScript-first and ships as the npm package @ax-llm/ax.

### How do I install Ax for TypeScript?

Install the published package with npm install @ax-llm/ax, then import ai and ax from the package. The README's first example creates a client with ai({ name: "openai", apiKey: process.env.OPENAI_APIKEY }) and defines a signature with ax(...).

### Is Ax the same as DSPy?

No. Ax describes itself as the pretty much official DSPy framework for TypeScript, so it carries the same ideas of signatures and optimizers, but the source implementation and published package are TypeScript. The Python, Java, C++, Go and Rust libraries are generated from the shared AxIR core.

### Which LLM providers does Ax support?

The README names openai, anthropic, google-gemini, meta, together, fireworks, deepseek and grok as deployment profiles you can switch between with the same signature, and the repository topics list ollama and cohere. Provider behaviour is selected by the deployment name, so check docs/AI_PROFILES.md for the one you plan to use.

## Sources

- [ax-llm/ax on GitHub](https://github.com/ax-llm/ax)
- [License: Apache-2.0](https://github.com/ax-llm/ax/blob/main/LICENSE)
- [Project website](http://axllm.dev)
- [README](https://github.com/ax-llm/ax/blob/main/README.md)
- [Releases](https://github.com/ax-llm/ax/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/ax-llm-ax
