Navigation: docs/developer/index.md → Reve provider guide
Audience: coding agents and contributors adding new Reve image nodes or model version strings to the
@nodetool-ai/reve-nodespackage.
TL;DR
- Add a new
classinpackages/reve-nodes/src/nodes/extendingBaseNode. - Export it from
packages/reve-nodes/src/index.tsand push the class intoREVE_NODES. - Call
reveGeneratefromreve-base.tswith one of the three API endpoints:"create","edit", or"remix". npm run build:packages— compiles fromsrc/todist/(the package loads fromdist/).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)— readsREVE_API_KEYfromthis._secretsthenprocess.env, throws if absent.reveGenerate(apiKey, endpoint, body)— POSTs tohttps://api.reve.com/v1/image/{endpoint}withAuthorization: Bearer, returns the parsedReveImageResponse.refToBase64(ref, context)— converts anImageRef-like value to a base64 string; handles inline data, data URIs,asset://,file://, and remote URLs.reveImageToRef(base64)— wraps the responseimagefield into a NodeToolImageRefobject; attaches dimensions viasharpwhen 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.