Navigation: docs/developer/index.mdReve provider guide

Audience: coding agents and contributors adding new Reve image nodes or model version strings to the @nodetool-ai/reve-nodes package.

TL;DR

  1. Add a new class in packages/reve-nodes/src/nodes/ extending BaseNode.
  2. Export it from packages/reve-nodes/src/index.ts and push the class into REVE_NODES.
  3. Call reveGenerate from reve-base.ts with one of the three API endpoints: "create", "edit", or "remix".
  4. npm run build:packages — compiles from src/ to dist/ (the package loads from dist/).
  5. npm run typecheck && npm run lint — must pass before committing.

Where things live

What Path
Shared API utilities packages/reve-nodes/src/reve-base.ts
Create Image node packages/reve-nodes/src/nodes/create-image.ts
Edit Image node packages/reve-nodes/src/nodes/edit-image.ts
Remix Image node packages/reve-nodes/src/nodes/remix-image.ts
Package entry point packages/reve-nodes/src/index.ts
Runtime provider (generic image picker) packages/runtime/src/providers/reve-provider.ts
Provider registration packages/runtime/src/providers/index.ts line 221
PROVIDER_IDS.REVE packages/protocol/src/api-types.ts line 887
Unit tests packages/reve-nodes/tests/nodes.test.ts
Registration test packages/reve-nodes/tests/registration.test.ts

How Reve models and nodes are defined

Reve exposes three POST endpoints, each mapping to one TypeScript class:

Endpoint Class Node type
POST /v1/image/create CreateImageNode reve.CreateImage
POST /v1/image/edit EditImageNode reve.EditImage
POST /v1/image/remix RemixImageNode reve.RemixImage

There is no manifest or code-generation script. Each node is a hand-written BaseNode subclass in its own file under packages/reve-nodes/src/nodes/. Fields are declared with the @prop decorator from @nodetool-ai/node-sdk.

Shared utilities in reve-base.ts:

  • REVE_ASPECT_RATIOS — the seven aspect ratios all endpoints accept.
  • REVE_POSTPROCESSING — optional post-generation operations (upscale, remove_background, fit_image, effect).
  • getReveApiKey(secrets) — reads REVE_API_KEY from this._secrets then process.env, throws if absent.
  • reveGenerate(apiKey, endpoint, body) — POSTs to https://api.reve.com/v1/image/{endpoint} with Authorization: Bearer, returns the parsed ReveImageResponse.
  • refToBase64(ref, context) — converts an ImageRef-like value to a base64 string; handles inline data, data URIs, asset://, file://, and remote URLs.
  • reveImageToRef(base64) — wraps the response image field into a NodeTool ImageRef object; attaches dimensions via sharp when available.

The runtime provider (reve-provider.ts) separately implements textToImage and imageToImage for the generic image-generation picker in the UI (the two hardcoded model ids reve-create and reve-edit). This is a thinner path — new Reve API endpoints should be added as full nodes in reve-nodes, not as branches in the provider.


Add a new model version

To add a new dated model version pin (e.g. reve-create@20260101), open the relevant node file and extend the version prop’s values array:

// packages/reve-nodes/src/nodes/create-image.ts
@prop({
  type: "enum",
  default: "latest",
  title: "Version",
  description: "Model version to use.",
  values: ["latest", "reve-create@20250915", "reve-create@20260101"]  // ← add here
})
declare version: any;

That is the entire change. Rebuild and verify (see Verify below).


Add a new node

Use this pattern when Reve releases a new endpoint (or a sufficiently different variant that warrants its own node type).

Step 1. Create packages/reve-nodes/src/nodes/your-endpoint.ts:

import { BaseNode, prop } from "@nodetool-ai/node-sdk";
import type { NodeClass } from "@nodetool-ai/node-sdk";
import {
  getReveApiKey,
  postprocessingArray,
  reveGenerate,
  reveImageToRef,
  REVE_ASPECT_RATIOS,
  REVE_POSTPROCESSING
} from "../reve-base.js";

export class YourEndpointNode extends BaseNode {
  static readonly nodeType = "reve.YourEndpoint";
  static readonly body = "content_card";
  static readonly title = "Your Endpoint";
  static readonly description =
    "One-line description of what this node does.\n" +
    "image, reve, keyword1, keyword2\n\n" +
    "Use cases:\n" +
    "- First use case\n" +
    "- Second use case";
  static readonly metadataOutputTypes = { output: "image" };
  static readonly inlineFields: string[] = ["prompt"];
  static readonly requiredSettings = ["REVE_API_KEY"];
  static readonly autoSaveAsset = true;

  @prop({
    type: "str",
    default: "",
    title: "Prompt",
    description: "Text prompt (max 2560 chars)."
  })
  declare prompt: any;

  @prop({
    type: "enum",
    default: "3:2",
    title: "Aspect Ratio",
    description: "Proportions of the generated image.",
    values: [...REVE_ASPECT_RATIOS]
  })
  declare aspect_ratio: any;

  @prop({
    type: "enum",
    default: "latest",
    title: "Version",
    description: "Model version to use.",
    values: ["latest"]
  })
  declare version: any;

  @prop({
    type: "enum",
    default: "none",
    title: "Postprocessing",
    description: "Optional postprocessing operation applied to the result.",
    values: [...REVE_POSTPROCESSING]
  })
  declare postprocessing: any;

  @prop({
    type: "int",
    default: 1,
    title: "Test Time Scaling",
    description: "Effort multiplier for quality (1-15). Higher costs more.",
    min: 1,
    max: 15
  })
  declare test_time_scaling: any;

  async process(): Promise<Record<string, unknown>> {
    const apiKey = getReveApiKey(this._secrets);
    const prompt = String(this.prompt ?? "");
    if (!prompt) throw new Error("Prompt is required");

    const result = await reveGenerate(apiKey, "create", {   // ← swap endpoint name
      prompt,
      aspect_ratio: String(this.aspect_ratio ?? "3:2"),
      version: String(this.version ?? "latest"),
      postprocessing: postprocessingArray(this.postprocessing),
      test_time_scaling: Number(this.test_time_scaling ?? 1)
    });

    return { output: await reveImageToRef(result.image) };
  }
}

export const YOUR_ENDPOINT_NODES: readonly NodeClass[] = [YourEndpointNode];

Nodes that accept a reference image (like EditImageNode) must take a context argument in process and pass it to refToBase64:

async process(
  context?: Parameters<BaseNode["process"]>[0]
): Promise<Record<string, unknown>> {
  const referenceImage = await refToBase64(this.image, context);
  // ...
}

Step 2. Wire the new class into packages/reve-nodes/src/index.ts:

import { YOUR_ENDPOINT_NODES } from "./nodes/your-endpoint.js";
export { YourEndpointNode } from "./nodes/your-endpoint.js";

export const REVE_NODES: readonly NodeClass[] = [
  ...CREATE_IMAGE_NODES,
  ...EDIT_IMAGE_NODES,
  ...REMIX_IMAGE_NODES,
  ...YOUR_ENDPOINT_NODES    // ← add here
];

No other files need to change. The node automatically appears in the registry because registerReveNodes iterates REVE_NODES.

Step 3. Add tests in packages/reve-nodes/tests/nodes.test.ts. Follow the existing pattern: stub fetch, construct the node with props, call process(), assert the fetch URL and body fields.


Verify

Run these commands in order after any change:

# 1. Compile src/ → dist/ (required — the package loads from dist/)
npm run build:packages

# 2. Type-check the whole repo
npm run typecheck

# 3. Lint
npm run lint

# 4. Run the reve-nodes unit tests
npm run test --workspace=packages/reve-nodes

# 5. Confirm the new node type resolves in the CLI registry
#    Expected: a REVE_API_KEY error, not "unknown node type"
npm run dev:nodetool -- node run reve.YourEndpoint \
  --props '{"prompt":"a red fox"}' \
  --no-secrets

# 6. Static validation (no API key required)
npm run dev:nodetool -- validate workflow.json

A REVE_API_KEY is not configured error from step 5 means the node registered correctly — it loaded from dist/ and resolved the type. An unknown node type error means the build or export wiring is broken.


How past commits did it

The Reve node package and runtime provider were introduced together in commit d1491abf (“add claude agent package”). That commit added:

packages/runtime/src/providers/reve-provider.ts
packages/reve-nodes/src/reve-base.ts
packages/reve-nodes/src/nodes/create-image.ts
packages/reve-nodes/src/nodes/edit-image.ts
packages/reve-nodes/src/nodes/remix-image.ts
packages/reve-nodes/src/index.ts
packages/reve-nodes/tests/nodes.test.ts
packages/reve-nodes/tests/registration.test.ts

The three endpoints (create, edit, remix) map to three node classes with no factory or manifest layer. For reference, XAI image/video support was added in PR #3951 (commit 69dd6f88) by extending getAvailableImageModels and getAvailableVideoModels in the existing provider — that pattern applies when the provider’s generic image picker (not a standalone node pack) is the target.


Contributing

Source: https://github.com/nodetool-ai/nodetool
Discord: https://discord.gg/WmQTWZRcYE

Before opening a PR, run npm run check (typecheck + lint + tests). Add a test for every new node; the existing nodes.test.ts stubs fetch globally, so new test cases fit cleanly in the same file.