Migrate from Anthropic
Joule Cloud is OpenAI-shaped, not Anthropic-shaped, so this guide is slightly more work than the OpenAI one. The good news: Anthropic's Messages API maps cleanly onto OpenAI Chat Completions, and Claude Haiku 4.5 is available through the router where licensing allows.
The shape diff
| Anthropic | Joule Cloud (OpenAI-shape) |
|---|---|
anthropic.messages.create({ model, messages, system }) | openai.chat.completions.create({ model, messages }) with system prompt as {role:"system"} |
response.content[0].text | response.choices[0].message.content |
response.usage.input_tokens / output_tokens | X-Energy-Joules response header (joules, not tokens) |
tool_use block | tool_calls array on message |
tool_result block | {role:"tool", tool_call_id, content} |
Streaming via response_format: "stream" | Streaming via stream: true (SSE) |
System prompts
Anthropic's top-level system parameter becomes a system-role message:
// Anthropic
- await client.messages.create({
- model: "claude-haiku-4-5",
- system: "You are concise.",
- messages: [{role:"user", content:"hi"}],
- });
+ // Joule Cloud (OpenAI shape)
+ await client.chat.completions.create({
+ model: "auto", // or "claude-haiku-4.5" to keep Claude
+ messages: [
+ {role:"system", content:"You are concise."},
+ {role:"user", content:"hi"},
+ ],
+ });
Multi-turn with tool use
Anthropic's tool_use / tool_result blocks become OpenAI tool_calls / tool messages. Tool definitions are similar but the wrapping differs.
tools: [{
type: "function",
function: {
name: "get_weather",
description: "Get current weather in a location",
parameters: { /* JSON schema */ },
},
}]
What you gain
- Model choice. Run Claude where you want, run open weights (Llama 3.3, DeepSeek-V3, Qwen 2.5) where you don't need Claude-specific behaviour.
- Per-request energy.
X-Energy-Joulestells you what each call cost in the same units the bill uses. Anthropic gives you token counts; we give you joules and tokens. - Mixed-model orchestration.
model: "auto"routes by cost tier — lookups go to a small open model, reasoning goes to a top-tier one. No multi-vendor SDK glue.
What you lose (and how to keep it)
- Anthropic-specific features like prompt caching, computer use, citations, and PDF input. We support OpenAI-shape equivalents where they exist; for Claude-only features, pin
model: "claude-haiku-4.5"— the Anthropic-compatible feature set will pass through. - Vision (image) input works via the OpenAI
image_urlcontent type. Convert from Anthropic's base64-image blocks.
Code
// Anthropic SDK
import Anthropic from "@anthropic-ai/sdk";
const a = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
const r = await a.messages.create({
model: "claude-haiku-4-5",
max_tokens: 1024,
system: "You are concise.",
messages: [{ role: "user", content: "Summarize this contract." }],
});
console.log(r.content[0].text);
// ─── Joule Cloud equivalent ───
import OpenAI from "openai";
const jc = new OpenAI({
baseURL: "https://api.greenjoules.cloud/v1",
apiKey: process.env.JC_API_KEY,
});
const r = await jc.chat.completions.create({
model: "claude-haiku-4.5", // or "auto"
max_tokens: 1024,
messages: [
{ role: "system", content: "You are concise." },
{ role: "user", content: "Summarize this contract." },
],
});
console.log(r.choices[0].message.content);
console.log(r.response?.headers?.["x-energy-joules"]); // ← new
For the underlying API surface and headers, see API reference. For the routing rules, see Routing & placement.