Ordo
Initial public release
182a5f2
import express from "express";
import crypto from "node:crypto";
const app = express();
app.use(express.json({ limit: "10mb" }));
const PORT = Number(process.env.PORT || 3080);
const OPENCLAW_BASE_URL = process.env.OPENCLAW_BASE_URL || "http://gateway:18789";
const OPENCLAW_BEARER_TOKEN = process.env.OPENCLAW_BEARER_TOKEN || "";
const OPENCLAW_MODELS = (process.env.OPENCLAW_MODELS || "openclaw-main")
.split(",")
.map((s) => s.trim())
.filter(Boolean);
function nowSeconds() {
return Math.floor(Date.now() / 1000);
}
function makeId(prefix) {
return `${prefix}-${crypto.randomUUID()}`;
}
function pseudoModelToAgent(model) {
if (!model) return "main";
if (model.startsWith("openclaw-")) {
return model.slice("openclaw-".length);
}
if (model.startsWith("openclaw:")) {
return model.slice("openclaw:".length);
}
if (model.startsWith("agent:")) {
return model.slice("agent:".length);
}
return model;
}
function agentToOpenClawModel(agent) {
return `openclaw:${agent}`;
}
function flattenContent(content) {
if (typeof content === "string") return content;
if (Array.isArray(content)) {
return content
.map((part) => {
if (!part) return "";
if (typeof part === "string") return part;
if (part?.type === "text") return part.text || "";
if (part?.type === "input_text") return part.text || "";
if (typeof part?.text === "string") return part.text;
return "";
})
.join("\n");
}
if (content == null) return "";
if (typeof content === "object") {
if (typeof content.text === "string") return content.text;
return JSON.stringify(content);
}
return String(content);
}
function normalizeMessages(messages = []) {
if (!Array.isArray(messages)) return [];
return messages
.filter((m) => m && typeof m === "object")
.map((m) => ({
role:
typeof m.role === "string" && m.role.length
? m.role
: "user",
content: flattenContent(m.content),
}))
.filter((m) => m.content !== undefined);
}
function extractAssistantTextFromResponsesApi(payload) {
if (!payload) return "";
if (typeof payload.output_text === "string" && payload.output_text.length) {
return payload.output_text;
}
const output = Array.isArray(payload.output) ? payload.output : [];
const texts = [];
for (const item of output) {
if (item?.type === "message" && Array.isArray(item.content)) {
for (const part of item.content) {
if (part?.type === "output_text" && typeof part.text === "string") {
texts.push(part.text);
}
if (part?.type === "text" && typeof part.text === "string") {
texts.push(part.text);
}
}
}
}
return texts.join("\n").trim();
}
function buildOpenClawInput(messages = []) {
if (!Array.isArray(messages)) return [];
return messages
.filter((m) => m && typeof m === "object")
.map((m) => ({
role:
typeof m.role === "string" && m.role.length
? m.role
: "user",
content: [
{
type: "input_text",
text: flattenContent(m.content) || "",
},
],
}));
}
function buildChatCompletionResponse({ model, content, finishReason = "stop" }) {
return {
id: makeId("chatcmpl"),
object: "chat.completion",
created: nowSeconds(),
model,
choices: [
{
index: 0,
message: {
role: "assistant",
content,
},
finish_reason: finishReason,
},
],
usage: {
prompt_tokens: 0,
completion_tokens: 0,
total_tokens: 0,
},
};
}
function sseWrite(res, obj) {
res.write(`data: ${JSON.stringify(obj)}\n\n`);
}
app.get("/health", (_req, res) => {
res.json({ ok: true });
});
app.get("/v1/models", (_req, res) => {
res.json({
object: "list",
data: OPENCLAW_MODELS.map((modelId) => ({
id: modelId,
object: "model",
created: 0,
owned_by: "openclaw",
})),
});
});
app.post("/v1/chat/completions", async (req, res) => {
try {
const {
model,
messages = [],
stream = false,
user,
temperature,
max_tokens,
} = req.body || {};
console.log("Incoming model:", model);
console.log("Incoming messages preview:", JSON.stringify(messages?.slice?.(0, 3), null, 2));
const agent = pseudoModelToAgent(model || OPENCLAW_MODELS[0] || "openclaw-hermes");
const openclawModel = agentToOpenClawModel(agent);
const normalizedMessages = normalizeMessages(messages);
if (!normalizedMessages.length) {
return res.status(400).json({
error: {
message: "No valid messages were provided",
type: "invalid_request_error",
},
});
}
const derivedUser =
user ||
req.header("x-conversation-id") ||
req.header("x-session-id") ||
`librechat-${crypto.randomUUID()}`;
const body = {
model: openclawModel,
input: buildOpenClawInput(normalizedMessages),
user: derivedUser,
temperature,
max_output_tokens: max_tokens,
stream: false
};
const headers = {
"content-type": "application/json",
};
if (OPENCLAW_BEARER_TOKEN) {
headers.authorization = `Bearer ${OPENCLAW_BEARER_TOKEN}`;
}
const upstream = await fetch(`${OPENCLAW_BASE_URL}/v1/responses`, {
method: "POST",
headers,
body: JSON.stringify(body),
});
const text = await upstream.text();
if (!upstream.ok) {
res.status(upstream.status).json({
error: {
message: text || "OpenClaw upstream error",
type: "upstream_error",
},
});
return;
}
const payload = JSON.parse(text);
const assistantText = extractAssistantTextFromResponsesApi(payload);
if (!stream) {
res.json(
buildChatCompletionResponse({
model: model || OPENCLAW_MODELS[0] || "openclaw-main",
content: assistantText,
})
);
return;
}
res.setHeader("Content-Type", "text/event-stream");
res.setHeader("Cache-Control", "no-cache, no-transform");
res.setHeader("Connection", "keep-alive");
sseWrite(res, {
id: makeId("chatcmpl"),
object: "chat.completion.chunk",
created: nowSeconds(),
model: model || OPENCLAW_MODELS[0] || "openclaw-main",
choices: [
{
index: 0,
delta: { role: "assistant" },
finish_reason: null,
},
],
});
if (assistantText) {
sseWrite(res, {
id: makeId("chatcmpl"),
object: "chat.completion.chunk",
created: nowSeconds(),
model: model || OPENCLAW_MODELS[0] || "openclaw-main",
choices: [
{
index: 0,
delta: { content: assistantText },
finish_reason: null,
},
],
});
}
sseWrite(res, {
id: makeId("chatcmpl"),
object: "chat.completion.chunk",
created: nowSeconds(),
model: model || OPENCLAW_MODELS[0] || "openclaw-main",
choices: [
{
index: 0,
delta: {},
finish_reason: "stop",
},
],
});
res.write("data: [DONE]\n\n");
res.end();
} catch (err) {
res.status(500).json({
error: {
message: err instanceof Error ? err.message : "Internal server error",
type: "server_error",
},
});
}
});
app.listen(PORT, () => {
console.log(`openclaw-librechat-bridge listening on ${PORT}`);
});