# AI (/docs/nodes/ai)

Run AI text or image steps with ChatGPT, Grok, OpenRouter, or Venice AI.



The AI node runs a model inside your workflow. It sits between the data you pull and the action you take: read a Jupiter quote and decide whether to swap, judge how close a Kamino position is to liquidation, score trending tokens before a Phoenix limit order. It can also do the plain work, classifying results, writing messages, turning messy text into JSON, or generating an image for another node.

There is no separate **OpenAI node** anymore. Every provider runs through this one **AI** node:

* Log in with **ChatGPT** to use your ChatGPT account.
* Log in with **Grok** to use your Grok account.
* Use **OpenRouter** and pick any model ID, such as `openai/gpt-5-mini`.
* Use **Venice AI** for private inference through Venice models.

For normal text output, downstream nodes read the reply as `{aiResponse.data}`. For image output, downstream nodes read `{aiResponse.imageUrl}`.

Providers [#providers]

| Provider       | Best for                                                                                   | Catalog                                                               | Notes                                                                                                                                      |
| -------------- | ------------------------------------------------------------------------------------------ | --------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| **OpenRouter** | Access to 300+ models in one place (GPT, Claude, Gemini, DeepSeek, Grok, Qwen, Kimi, etc.) | Aggregated; model IDs are `provider/model` (e.g. `openai/gpt-5-mini`) | Live model list loaded from your key. Cost reporting included.                                                                             |
| **Venice AI**  | Private inference, uncensored models, Web3-native                                          | Curated (Llama, Qwen, DeepSeek, Dolphin, Venice Uncensored)           | Live model list loaded from your key. Bring-your-own-cost (no per-call billing through Solaris AI).                                        |
| **ChatGPT**    | No API key setup; use your own ChatGPT account                                             | Models available to your ChatGPT account                              | Text only in Solaris AI. Reconnect if the session expires. Full guide: [ChatGPT Integration](/docs/chatgpt).                               |
| **Grok**       | No API key setup; use your own Grok account                                                | Grok 4.5 (xAI's whole current lineup), 500K context                   | Text only in Solaris AI. An xAI API key is the fallback if xAI's allowlist refuses API access. Full guide: [Grok Integration](/docs/grok). |

Pick whichever fits your workflow. You can add credentials for more than one provider and switch per-node.

Quick start [#quick-start]

1. Add one AI credential:
   * Log in with ChatGPT from the AI node or [Connections](/docs/chatgpt#connect-it).
   * Log in with Grok from the AI node or [Connections](/docs/grok#connect-it).
   * Add an [OpenRouter](https://openrouter.ai/keys) key under [Credentials](/docs/credentials/adding).
   * Add a [Venice AI](https://venice.ai/settings/api) key under [Credentials](/docs/credentials/adding).
2. Drop an AI node onto the canvas. In the config dialog, pick your credential.
3. Choose a model from the live dropdown (or type any model ID).
4. Write a prompt, e.g. `Given this quote, is the slippage acceptable? Answer yes or no: {json jupiterResponse.data}`.
5. Wire the next node to read `{aiResponse.data}`.

That's the whole loop. Everything below is detail for when you need it.

Prerequisites [#prerequisites]

* One AI credential:
  * **ChatGPT**: log in with ChatGPT from the AI node or Connections. No API key is needed, but the session can expire and require reconnecting.
  * **Grok**: log in with Grok from the AI node or Connections. No API key is needed, but the session can expire and require reconnecting. See [Grok Integration](/docs/grok).
  * **OpenRouter**: [openrouter.ai/keys](https://openrouter.ai/keys)
  * **Venice AI**: [venice.ai/settings/api](https://venice.ai/settings/api). Add any USD balance to your Venice account first; Venice keys stay inactive on a $0 balance and will fail at runtime. See the [Venice AI setup walkthrough](/docs/credentials/integrations#venice-ai) for the exact steps.
* Add API keys as credentials in [Credentials](/docs/credentials/adding).

Operations [#operations]

| Operation        | Description                                                                                                        |
| ---------------- | ------------------------------------------------------------------------------------------------------------------ |
| Chat completion  | Send a prompt, get a response                                                                                      |
| Image generation | Generate an image from a prompt with an image-capable OpenRouter model. See [Image generation](#image-generation). |

Input modes [#input-modes]

| Mode         | When to use                                                                                                                                                                       |
| ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Prompt       | Default. Write a natural-language prompt with template expressions like `{birdeyeResponse.data.value}`.                                                                           |
| JSON Request | When you need multi-turn messages, a `developer` role, or multimodal content parts. Send an OpenAI-compatible chat-completions body, see [JSON Request mode](#json-request-mode). |

Configuration [#configuration]

| Field           | Type              | Required          | Description                                                                                                                                                                                              |
| --------------- | ----------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| AI Provider Key | select            | Yes               | Your ChatGPT, Grok, OpenRouter, or Venice credential from Connections. The selected credential's platform decides which provider runs.                                                                   |
| Response Name   | text              | Yes               | The variable name downstream nodes use to reference this node's output. Defaults to `aiResponse`, so you read the reply as `{aiResponse.data}`. Rename it if you have multiple AI nodes in one workflow. |
| model           | searchable select | Yes               | Pick from the loaded model list (live from the active provider) or type a full model ID. Defaults are provider-specific if left blank in non-strict execution.                                           |
| prompt          | string            | Yes (prompt mode) | User message. Supports template expressions.                                                                                                                                                             |
| requestJson     | string            | Yes (JSON mode)   | OpenAI-compatible JSON body, see [JSON Request mode](#json-request-mode).                                                                                                                                |
| systemPrompt    | string            | No                | System instructions. Also templated; undefined paths fail the same way as in `prompt`.                                                                                                                   |
| temperature     | number            | No                | `0` to `2`, controls randomness. Ignored on ChatGPT (see [Reasoning](#reasoning)). Supported on Grok.                                                                                                    |
| maxTokens       | number            | No                | Maximum response length.                                                                                                                                                                                 |
| responseFormat  | string            | No                | `text` (default) or `json_object`. See [Response format](#response-format).                                                                                                                              |
| outputMode      | string            | No                | `text` (default) or `image`. Only surfaces on image-capable OpenRouter models, where it picks between a text reply and a generated image. See [Image generation](#image-generation).                     |

Select a credential first to load the live model list. The list is fetched directly from the active provider, so it reflects what your key, ChatGPT account, or Grok account can use.

**Switching credentials across providers clears the saved model.** OpenRouter IDs (e.g. `openai/gpt-5-mini`), Venice IDs (e.g. `llama-3.2-3b`), ChatGPT IDs, and Grok IDs (e.g. `grok-4.5`) use different formats, so the node prompts you to pick again rather than send a mismatched pair to the provider.

The user prompt is hard-capped at 200,000 characters. Anything longer is truncated with a marker (`[Prompt truncated by Solaris to stay within model context limits]`) before the call goes out.

Model IDs by provider [#model-ids-by-provider]

| Provider   | Format                | Example                                                                                 |
| ---------- | --------------------- | --------------------------------------------------------------------------------------- |
| OpenRouter | `<provider>/<model>`  | `openai/gpt-5-mini`, `anthropic/claude-opus-4.6`, `google/gemini-3.1-pro-preview`       |
| Venice AI  | bare canonical ID     | `llama-3.2-3b`, `llama-3.3-70b`, `qwen-2.5-vl`, `deepseek-r1-671b`, `venice-uncensored` |
| ChatGPT    | bare ChatGPT model ID | Pick from the live dropdown for your account.                                           |
| Grok       | bare xAI model ID     | `grok-4.5` (xAI's only current model)                                                   |

You can type a custom ID in either case. The runtime forwards it unchanged to the active provider, so typos fail at the wire with the provider's own error message (e.g. "model not found").

Advanced parameters [#advanced-parameters]

These are OpenAI-compatible parameters. Provider and model support can vary, so if a model rejects one of these settings, remove the setting or pick a different model.

| Field            | Type   | Description                                                                                 |
| ---------------- | ------ | ------------------------------------------------------------------------------------------- |
| topP             | number | Nucleus sampling threshold. Range `(0, 1]` (0 is invalid; values above 1 are clamped to 1). |
| frequencyPenalty | number | Penalize repeated tokens. Range `-2` to `2`.                                                |
| presencePenalty  | number | Penalize tokens already present. Range `-2` to `2`.                                         |
| stop             | string | Comma-separated stop sequences. Up to 4 (extras are dropped).                               |
| seed             | number | Deterministic output seed (model-dependent). Must be an integer.                            |

On **Grok**, `frequencyPenalty`, `presencePenalty`, and `stop` are dropped before the call: xAI rejects all three on reasoning models, and every model it ships reasons. `temperature`, `topP`, and `seed` work normally.

Reasoning [#reasoning]

Some models support extended reasoning. When enabled, the model may return additional reasoning metadata alongside the reply.

| Field            | Type    | Description                                                                                                                                                           |
| ---------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| reasoningEnabled | boolean | Enable extended thinking. Ignored on ChatGPT and Grok, where reasoning is always on.                                                                                  |
| reasoningEffort  | string  | `xhigh`, `high`, `medium`, `low`, or `minimal`. Roughly: `xhigh` \~ 95% of token budget on reasoning, `high` \~ 80%, `medium` \~ 50%, `low` \~ 20%, `minimal` \~ 10%. |

Reasoning is model-dependent. Models that don't support reasoning can reject the call. Venice's reasoning-capable models surface their thinking as `reasoning_content` and are normalized into the same `reasoning` output field as OpenRouter.

**ChatGPT always reasons.** Every model on a ChatGPT credential reasons on every call, so there is nothing to switch on. The node hides the Reasoning toggle for ChatGPT credentials and shows the effort picker on its own; only the depth is yours to choose. Leaving the effort unset does not disable reasoning, it runs the model at medium. `minimal` is raised to the shallowest depth ChatGPT offers, since ChatGPT models have no lighter setting. Each model also caps its own depth, so an effort above a model's ceiling is lowered to that ceiling rather than rejected.

**Grok always reasons too.** `grok-4.5`, xAI's only current model, reasons on every call and cannot be switched off; the Reasoning toggle only decides whether you set the depth or let the model use its default (high). It accepts `low`, `medium`, and `high`, so `xhigh` and `max` are lowered to `high` and `minimal` is raised to `low` rather than being rejected. See [Grok Integration](/docs/grok#reasoning).

<Callout type="info">
  `temperature` is ignored on ChatGPT credentials. Reasoning models do not accept a sampling temperature, so the value is dropped before the request is sent. Use OpenRouter or Venice when you need to tune randomness.
</Callout>

JSON Request mode [#json-request-mode]

When `inputMode` is `json`, the body must be an object with an OpenAI-compatible `messages[]` array. This is the only request shape the runtime forwards to AI providers.

Static JSON is checked in the editor before the workflow runs. If the JSON contains template expressions, the final shape is checked after those templates render at runtime. Legacy provider formats such as Gemini `contents[]` or Anthropic top-level `system` are rejected when the rendered request is parsed.

```json
{
  "messages": [
    { "role": "system", "content": "You are a concise assistant." },
    { "role": "user", "content": "Summarize: {json birdeyeResponse.data}" }
  ]
}
```

`role` must be one of `system`, `user`, `assistant`, `developer`. `content` is a non-empty string, or an OpenAI-format content-parts array (`[{ "type": "text", "text": "..." }, { "type": "image_url", "image_url": { "url": "..." } }]`) for multimodal models.

The body may also set:

| Field                               | Effect                                                                                                                                            |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `temperature`                       | Overrides the node-level `temperature`. Ignored on ChatGPT, like the node-level field.                                                            |
| `max_tokens` (or `maxOutputTokens`) | Overrides the node-level `maxTokens`. Capped at 128,000. On Venice this is sent on the wire as `max_completion_tokens` (Venice's preferred name). |

All other fields in the body are ignored. Advanced parameters (`topP`, `frequencyPenalty`, etc.) and `responseFormat` come from node settings, not the JSON body. Set them in the editor's [Advanced parameters](#advanced-parameters) panel or [Response format](#response-format) toggle.

Template expressions inside `requestJson` are resolved before the body is parsed as JSON, so a path like `{json codeResponse.data}` interpolates a JSON value at that position. Undefined paths fail loudly with `AI JSON input uses undefined variables: <path>` instead of producing invalid JSON or empty substitutions.

Response format [#response-format]

The `responseFormat` setting controls how the model's reply lands in the output envelope. &#x2A;*It must agree with what your prompt asks for.** Instructing the model to respond in JSON without flipping this toggle leaves `.data` as a string that downstream nodes can't traverse with field paths.

| Value            | Behavior                                                                                                                                                                                                                                                                             |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `text` (default) | The reply is returned verbatim. `.data` is a string. Use `{aiResponse.data}` to drop it into a downstream prompt or HTTP body.                                                                                                                                                       |
| `json_object`    | The provider is asked for structured output and the reply is `JSON.parse`d before it lands in the envelope. `.data` is a parsed object you can address with field paths like `{json aiResponse.data.summary}`. If the model returns invalid JSON, the node fails with a clear error. |

Some models don't support structured outputs and will fail when `json_object` is set. Pick a model that does, or fall back to `text` and parse downstream.

Image generation [#image-generation]

Some OpenRouter models generate images rather than (or alongside) text. When you select an image-capable model, an **Output** toggle appears in the config dialog:

* **Image** (the default once you pick an image model): the node sends the image request, hosts the returned image, and emits a fetchable URL as `{aiResponse.imageUrl}`.
* **Text**: forces an ordinary text reply even on a text-and-image model, so `.data` behaves exactly like a normal chat completion.

Image-only models skip the toggle and always produce an image. Capability is detected from the model's reported modalities in the live catalog; for a free-typed model ID with no catalog entry, the node falls back to the `*-image` naming convention.

| Constraint        | Detail                                                                                                                                                                                                                                                                                |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Provider          | OpenRouter only. Venice's image API is a separate endpoint this node doesn't wire, and ChatGPT and Grok are text-only in Solaris AI, so image mode on any of those credentials is rejected before any call.                                                                           |
| Models            | Image models from your live OpenRouter catalog, for example `google/gemini-2.5-flash-image` and `google/gemini-3-pro-image-preview` (the short aliases `gemini-2.5-flash-image` and `gemini-3-pro-image` resolve to these).                                                           |
| Cost              | Billed per image on your own OpenRouter account, like every other call (BYOK).                                                                                                                                                                                                        |
| No image returned | If you request an image but the model replies with text only, the node fails with a clear error instead of silently emitting a text `.data` that your downstream `{aiResponse.imageUrl}` reference can't read. Pick an image-capable model, adjust the prompt, or set Output to Text. |

Hosted URL lifetime [#hosted-url-lifetime]

The node uploads the generated bytes to Solaris storage and returns a &#x2A;*temporary signed URL (about an hour)**; the underlying file is reaped within a day. This is sized for immediate, in-run consumption, so wire `{aiResponse.imageUrl}` straight into the node that uses it. Don't park an image URL behind a long [Delay](/docs/nodes/utility/delay) or a multi-hour pause and expect it to still resolve. The raw image bytes are never written into the run journal, only the hosted URL.

Using the image downstream [#using-the-image-downstream]

`imageUrl` is the field other nodes point at:

* [Pump.fun](/docs/nodes/defi/pump-fun) `createToken` with `imageUrl: {aiResponse.imageUrl}` launches a token with a generated logo.
* [Telegram](/docs/nodes/messaging/telegram) `sendPhoto` with `photoUrl: {aiResponse.imageUrl}` posts the image to a chat.

If your image bytes come from somewhere else, raw base64 from a different image API behind an [HTTP](/docs/nodes/utility/http) node, the [Storage](/docs/nodes/utility/storage) **Host File → URL** operation turns them into the same kind of URL.

Output [#output]

In &#x2A;*`text`** mode (default):

```json
{
  "success": true,
  "data": "The current price of SOL is approximately $150.",
  "model": "openai/gpt-5-mini",
  "usage": { "promptTokens": 42, "completionTokens": 18 }
}
```

In &#x2A;*`json_object`** mode:

```json
{
  "success": true,
  "data": { "price": 150, "asset": "SOL", "confidence": "high" },
  "model": "llama-3.3-70b",
  "usage": { "prompt_tokens": 42, "completion_tokens": 18 }
}
```

In &#x2A;*`image`** mode (see [Image generation](#image-generation)):

```json
{
  "success": true,
  "imageUrl": "https://.../generated.png",
  "imageUrls": ["https://.../generated.png"],
  "model": "google/gemini-2.5-flash-image",
  "usage": { "promptTokens": 12, "completionTokens": 0 }
}
```

Image mode has &#x2A;*no `.data`**. `imageUrl` is the first (primary) hosted image; `imageUrls` lists all of them when a model returns more than one. Any text the model returned alongside the image lands in `text`.

The `model` field reflects the provider's canonical ID. OpenRouter IDs are slashed, such as `openai/gpt-5-mini`; Venice, ChatGPT, and Grok IDs are bare. The `usage` shape mirrors whatever the provider returns. Depending on the provider, token counts may appear as snake\_case or camelCase, so inspect the run output before wiring usage fields into downstream nodes.

Reasoning models may add a `reasoning` field when reasoning is enabled.

Reference patterns:

* `{aiResponse.data}` is the model's reply as a string. In `text` mode this is the raw model output. In `json_object` mode `.data` is an object, so without the `json ` prefix the template substitutes `[object Object]` (string coercion). Use `{json aiResponse.data}` instead when `.data` is an object.
* `{aiResponse.data.field}` is a scalar field (string / number / boolean) from a `json_object` reply, dropped in unquoted. The right shape for prompts and most string fields in HTTP query strings.
* `{json aiResponse.data.field}` is the same field JSON-encoded (strings get quotes, objects/arrays get JSON syntax). Use this when interpolating into JSON HTTP bodies, or when the field itself is an object or array.
* `{json aiResponse}` is the full response envelope, useful for HTTP bodies or debugging.
* `{aiResponse.imageUrl}` (image mode only) is the hosted URL of the generated image, the field to drop into a Pump.fun `imageUrl` or Telegram `photoUrl`.

Template variables [#template-variables]

The system prompt, prompt, and JSON-input fields all support template expressions like `{json codeResponse.data.field}`. Two things to know:

* **Use the variable picker** (the `{ }` button next to any text field). It walks upstream nodes and suggests valid paths, including nested field paths once the workflow has run once. Before the first run, it falls back to a static schema for known node types so you still get useful suggestions.
* **Undefined paths fail loudly.** If a path doesn't resolve (typo, wrong nesting, upstream node didn't emit that field), the node fails before calling the model. Checked across all three template fields (`systemPrompt`, `prompt`, `requestJson`) so a single failure surfaces every offending path at once. The error message mentions the offending path: `Prompt template uses undefined variables: <path>` in prompt mode (covers system prompt + prompt), `AI JSON input uses undefined variables: <path>` in JSON Request mode (covers system prompt + JSON body). This is the most common cause of "I don't see any data" replies; fix the path instead of debugging the model. A path that resolves to `null` (e.g. `balanceResponse.mint` on a SOL balance) is treated as a present value, not a missing one.

When to pick which provider [#when-to-pick-which-provider]

* **Default to OpenRouter** if you need access to a specific commercial model (GPT, Claude, Gemini) or want the broadest catalog.
* **Use ChatGPT** if you want the lowest-friction setup and already have a ChatGPT account. See [ChatGPT Integration](/docs/chatgpt).
* **Use Grok** if you already have a Grok account and want xAI's Grok 4.5. See [Grok Integration](/docs/grok).
* **Pick Venice** when you need private inference, want to run uncensored or open-source models, or already use Venice for the rest of your stack.

You can use multiple providers in the same workflow. Each AI node carries its own credential, so one node can call an OpenAI model through OpenRouter while the next routes to Llama through Venice.

Common use cases [#common-use-cases]

Solaris AI Flow exposes more than 200 onchain operations, over 100 of them DeFi actions across Jupiter, Raydium, Orca, Meteora, Kamino, Phoenix, Sanctum, DFlow, and Pump.fun. The AI node is how a workflow decides which of them to run.

* **Gate a trade.** Read a [Jupiter](/docs/nodes/defi/jupiter) quote and a [Pyth](/docs/nodes/data/pyth) price, judge the spread and slippage, then let a [Condition](/docs/nodes/utility/condition) node route to a swap or to nothing.
* **Watch leverage.** Feed `getUserObligations` and `getLeverageMetrics` from [Kamino](/docs/nodes/defi/kamino) to the model and alert before a position nears liquidation.
* **Rank new tokens.** Score [GMGN](/docs/nodes/data/gmgn) or [Pump.fun](/docs/nodes/defi/pump-fun) trending lists against your criteria, then place a [Phoenix](/docs/nodes/defi/phoenix) limit order on the survivors.
* **Parse and route.** Turn unstructured data into structured JSON with the `json_object` response format so downstream nodes can branch on real fields.
* **Explain what happened.** Summarize a fill or a wallet's activity into a Telegram or Discord message a human can act on.
* **Choose your provider per prompt.** Route sensitive prompts through Venice for private inference and public ones through OpenRouter for breadth.

A model's judgment is not a safety guarantee. Trade nodes support dry-run mode; keep a new workflow simulating until you have watched it decide correctly on real data.

Next steps [#next-steps]

* [Node reference](/docs/nodes) — every swap, order, and lending action an AI node can gate
* [Jupiter](/docs/nodes/defi/jupiter) connects AI to on-chain swaps
* [Configuring Nodes](/docs/editor/configuring-nodes) explains template expressions for dynamic prompts
