AI Gateway

Use Astro-provided LLM access without managing your own provider keys

View as Markdown

Set one line in your spec and your agent gets a managed API key for calling supported models — no provider account, no API keys to store, no key rotation to manage. The gateway is OpenAI-API-compatible, so it works with the SDKs and frameworks you already use.

Quick start

Scaffold a new agent wired to the gateway in one step:

$ast create my-agent --model gateway

This generates astro_ai_gateway: true in the spec and agent code that reads the injected env vars — no provider key to configure. To enable the gateway on an existing agent, add the flag to astropods.yml by hand:

1agent:
2 image: my-agent:latest
3 astro_ai_gateway: true

On ast deploy (and ast dev), your agent container receives two environment variables:

Env varPurpose
ASTRO_GATEWAY_URLGateway host. Append /v1 for the OpenAI-compatible API (see below).
ASTRO_GATEWAY_API_KEYBearer credential. Treat as a secret.

Calling the gateway

The OpenAI-compatible API lives under the /v1 path, so set your client’s base URL to ${ASTRO_GATEWAY_URL}/v1. Everything else is a standard OpenAI-style request — pass one of the supported models as model and authenticate with ASTRO_GATEWAY_API_KEY as a bearer token.

Append /v1 to ASTRO_GATEWAY_URL. The env var is the host only (e.g. https://aig.example); the OpenAI-compatible endpoints are served under /v1 (/v1/chat/completions, /v1/embeddings). Pointing a client at the bare host returns 404.

1from openai import OpenAI
2import os
3
4client = OpenAI(
5 api_key=os.environ["ASTRO_GATEWAY_API_KEY"],
6 base_url=f"{os.environ['ASTRO_GATEWAY_URL']}/v1",
7)
8
9response = client.chat.completions.create(
10 model="claude-sonnet-4-6",
11 messages=[{"role": "user", "content": "Hello"}],
12)
13print(response.choices[0].message.content)

For Mastra: the AI-SDK provider returned by createOpenAI is what Agent.model: accepts, so the rest of your Agent / Workflow / Tool definitions stay the same.

Mastra needs an AI SDK v5+ model. Add @ai-sdk/openai at v2 or later to your own package.json. Version 1.x produces an AI SDK v4 model (specificationVersion: "v1") that Mastra’s stream() rejects with AGENT_STREAM_V1_MODEL_NOT_SUPPORTED. This can be newer than the @ai-sdk/openai version Mastra pins internally, so install it explicitly rather than relying on the transitive one.

Supported models

Pass any of these strings as model on the SDK call:

ModelUse case
claude-opus-4-8Most capable; longer reasoning, agentic workflows.
claude-sonnet-4-6Workhorse; balanced cost / quality. Good default.
claude-haiku-4-5Fast and cheap; tool-use loops, classification.
nova-proAmazon Nova; balanced general use with low latency.
nova-liteAmazon Nova; cheaper and faster for high-volume calls.
nova-microAmazon Nova; lowest-cost text tier.
mistral-large-3Mistral flagship; strong general reasoning and coding.
pixtral-largeMistral vision-language; accepts image + text input.
titan-embed-text-v2Text embeddings (RAG, vector search).

All chat models are called the same way — swap the model string. titan-embed-text-v2 is an embeddings model (use the embeddings endpoint, below).

Streaming

Set stream: true to receive tokens incrementally as they’re generated.

1stream = client.chat.completions.create(
2 model="claude-sonnet-4-6",
3 messages=[{"role": "user", "content": "Write a haiku about the sea."}],
4 stream=True,
5)
6for chunk in stream:
7 delta = chunk.choices[0].delta.content
8 if delta:
9 print(delta, end="", flush=True)

Embeddings

Use titan-embed-text-v2 on the embeddings endpoint for RAG and vector search. It returns a 1024-dimension vector per input.

1resp = client.embeddings.create(
2 model="titan-embed-text-v2",
3 input="Astro deploys agents as containers.",
4)
5vector = resp.data[0].embedding # list[float], length 1024

Images (vision)

pixtral-large accepts image input alongside text. Pass the image as a base64 data URI in an image_url content block — the gateway forwards the image bytes to the model, so remote URLs are not fetched server-side.

Python
1import base64
2
3with open("chart.png", "rb") as f:
4 data_uri = "data:image/png;base64," + base64.b64encode(f.read()).decode()
5
6response = client.chat.completions.create(
7 model="pixtral-large",
8 messages=[{
9 "role": "user",
10 "content": [
11 {"type": "text", "text": "What does this chart show?"},
12 {"type": "image_url", "image_url": {"url": data_uri}},
13 ],
14 }],
15)
16print(response.choices[0].message.content)

Structured output

Prompting for a JSON object and parsing it (with a fallback for the occasional non-JSON reply) is a portable way to get structured output that works regardless of which structured-output features a given model exposes through the gateway.

Python
1import json
2
3response = client.chat.completions.create(
4 model="claude-sonnet-4-6",
5 messages=[
6 {"role": "system", "content": "Reply with only a JSON object: {\"sentiment\": \"positive|negative|neutral\"}."},
7 {"role": "user", "content": "I love this product."},
8 ],
9)
10raw = response.choices[0].message.content
11try:
12 result = json.loads(raw)
13except json.JSONDecodeError:
14 result = {"sentiment": "unknown"}

Local development

ast dev handles the gateway automatically. Run:

$ast login
$ast dev project start

Your local agent container receives the same ASTRO_GATEWAY_* env vars it would have in production. Code written against those env vars works identically in dev and prod.

Run ast login first if you haven’t — the gateway is account-scoped, so the CLI needs to know who you are.

Mixing with your own keys

astro_ai_gateway: true is independent of any models you declare. You can use the gateway for some calls and your own provider key for others:

1agent:
2 image: my-agent:latest
3 astro_ai_gateway: true # gateway-managed models
4models:
5 custom-fine-tune:
6 provider: openai # your own OpenAI key for a specific use case

Your agent code reads ASTRO_GATEWAY_API_KEY for gateway calls and OPENAI_API_KEY for the BYOK provider.

Errors and limits

The gateway returns standard OpenAI-style error responses. Common cases:

StatusMeaningWhat to do
401Missing/invalid ASTRO_GATEWAY_API_KEY.Redeploy so the key is re-injected; don’t hardcode it.
404Wrong path.Ensure your base URL ends in /v1.
429Rate or usage limit reached.Back off and retry; the OpenAI SDKs retry 429/5xx automatically.

What it doesn’t cover

  • Provider-native SDKs and prefixes. The gateway serves models from several providers (Anthropic Claude, Amazon Nova/Titan, Mistral), but you call them all through the OpenAI-compatible API by their gateway id (for example claude-sonnet-4-6), not with a provider prefix such as anthropic/.
  • Image generation. No image-generation models are offered. Image input (vision) is supported by pixtral-large.
  • Bring-your-own-model. Adding new models or fine-tunes to the gateway isn’t self-serve; reach out to support.