feat(providers): add Empower and Poe gateway support

Register Empower and Poe as managed OpenAI-compatible providers with
static metadata, example models, and remote model catalog support.

Add Poe-specific credential validation against its balance endpoint and
tighten Zed credential imports by skipping unsupported entries and
deduplicating provider-token pairs before saving.
This commit is contained in:
diegosouzapw
2026-04-24 07:48:19 -03:00
parent c656123db9
commit 80e3858ae4
13 changed files with 336 additions and 8 deletions

View File

@@ -199,6 +199,13 @@ const CHAT_OPENAI_COMPAT_MODELS: Record<string, RegistryModel[]> = {
cablyai: buildModels(["gpt-4o", "gpt-4o-mini", "deepseek-chat"]),
thebai: buildModels(["gpt-4o", "claude-3.5-sonnet", "llama-3.3-70b"]),
fenayai: buildModels(["gpt-4o", "claude-3.5-sonnet", "deepseek-chat"]),
empower: buildModels([
"empower-functions",
"mistralai/Mixtral-8x7B-Instruct-v0.1",
"meta-llama/Meta-Llama-3-8B-Instruct",
"meta-llama/Meta-Llama-3-70B-Instruct",
]),
poe: buildModels(["Claude-Sonnet-4.5", "GPT-5-Pro", "GPT-5-Codex", "Gemini-2.5-Pro"]),
gitlab: [{ id: "gitlab-duo-code-suggestions", name: "GitLab Duo Code Suggestions" }],
"gitlab-duo": [{ id: "gitlab-duo-code-suggestions", name: "GitLab Duo Code Suggestions" }],
chutes: buildModels([
@@ -2054,6 +2061,32 @@ export const REGISTRY: Record<string, RegistryEntry> = {
passthroughModels: true,
},
empower: {
id: "empower",
alias: "empower",
format: "openai",
executor: "default",
baseUrl: "https://app.empower.dev/api/v1",
modelsUrl: "https://app.empower.dev/api/v1/models",
authType: "apikey",
authHeader: "bearer",
models: CHAT_OPENAI_COMPAT_MODELS.empower,
passthroughModels: true,
},
poe: {
id: "poe",
alias: "poe",
format: "openai",
executor: "default",
baseUrl: "https://api.poe.com/v1",
modelsUrl: "https://api.poe.com/v1/models",
authType: "apikey",
authHeader: "bearer",
models: CHAT_OPENAI_COMPAT_MODELS.poe,
passthroughModels: true,
},
gitlab: {
id: "gitlab",
alias: "gitlab",

View File

@@ -80,7 +80,7 @@ function isLocalOpenAIStyleProvider(provider: string): boolean {
return isSelfHostedChatProvider(provider);
}
const NAMED_OPENAI_STYLE_PROVIDERS = new Set(["bedrock", "modal", "reka"]);
const NAMED_OPENAI_STYLE_PROVIDERS = new Set(["bedrock", "modal", "reka", "empower", "poe"]);
function isNamedOpenAIStyleProvider(provider: string): boolean {
return NAMED_OPENAI_STYLE_PROVIDERS.has(provider);

View File

@@ -11,6 +11,7 @@
import { NextResponse } from "next/server";
import { discoverZedCredentials, isZedInstalled } from "@/lib/zed-oauth/keychain-reader";
import { partitionZedCredentials } from "@/lib/zed-oauth/importUtils";
import { requireManagementAuth } from "@/lib/api/requireManagementAuth";
import { createProviderConnection } from "@/lib/db/providers";
@@ -51,8 +52,14 @@ export async function POST(request: Request): Promise<NextResponse<ImportRespons
// Discover credentials from keychain
console.log("[Zed Import] Discovering Zed credentials from keychain...");
const credentials = await discoverZedCredentials();
const { importable, skipped, duplicatesDropped } = partitionZedCredentials(credentials);
if (credentials.length === 0) {
if (importable.length === 0) {
if (credentials.length > 0) {
console.warn(
`[Zed Import] Found ${credentials.length} keychain credential(s), but none mapped to supported OmniRoute providers`
);
}
return NextResponse.json({
success: true,
count: 0,
@@ -64,9 +71,7 @@ export async function POST(request: Request): Promise<NextResponse<ImportRespons
// Save to database using OmniRoute's provider schema
let savedCount = 0;
for (const cred of credentials) {
if (!cred.token) continue;
for (const cred of importable) {
try {
await createProviderConnection({
provider: cred.provider,
@@ -81,18 +86,24 @@ export async function POST(request: Request): Promise<NextResponse<ImportRespons
}
}
const credentialSummary = credentials.map((cred) => ({
if (skipped.length > 0 || duplicatesDropped > 0) {
console.log(
`[Zed Import] Skipped ${skipped.length} unsupported credential(s) and dropped ${duplicatesDropped} duplicate credential(s)`
);
}
const credentialSummary = importable.map((cred) => ({
provider: cred.provider,
service: cred.service,
account: cred.account,
hasToken: Boolean(cred.token),
}));
const importedProviders = credentials.map((c) => c.provider);
const importedProviders = importable.map((c) => c.provider);
const uniqueProviders = [...new Set(importedProviders)];
console.log(
`[Zed Import] Discovered ${credentials.length} credentials and successfully saved ${savedCount} for ${uniqueProviders.length} providers`
`[Zed Import] Discovered ${credentials.length} credentials, imported ${importable.length} supported credential(s), and successfully saved ${savedCount} for ${uniqueProviders.length} providers`
);
return NextResponse.json({

View File

@@ -1579,6 +1579,43 @@ async function validateRunwayProvider({ apiKey, providerSpecificData = {} }: any
return { valid: false, error: "Connection failed while testing Runway" };
}
async function validatePoeProvider({ apiKey, providerSpecificData = {} }: any) {
const baseUrl = normalizeBaseUrl(providerSpecificData.baseUrl) || "https://api.poe.com/v1";
const balanceUrl = new URL("/usage/current_balance", baseUrl).toString();
try {
const response = await validationRead(balanceUrl, {
method: "GET",
headers: buildBearerHeaders(apiKey, providerSpecificData),
});
if (response.ok) {
return { valid: true, error: null, method: "poe_current_balance" };
}
if (response.status === 401 || response.status === 403) {
return { valid: false, error: "Invalid API key" };
}
if (response.status === 429) {
return {
valid: true,
error: null,
method: "poe_current_balance",
warning: "Rate limited, but credentials are valid",
};
}
if (response.status >= 500) {
return { valid: false, error: `Provider unavailable (${response.status})` };
}
} catch (error: any) {
return toValidationErrorResult(error);
}
return { valid: false, error: "Connection failed while testing Poe" };
}
async function validateOpenAICompatibleProvider({ apiKey, providerSpecificData = {} }: any) {
const baseUrl = normalizeBaseUrl(providerSpecificData.baseUrl);
if (!baseUrl) {
@@ -2511,6 +2548,7 @@ export async function validateProviderApiKey({ provider, apiKey, providerSpecifi
baseUrl: normalizeBaseUrl(providerSpecificData?.baseUrl || ""),
modelId: "Qwen/Qwen3-4B-Thinking-2507-FP8",
}),
poe: validatePoeProvider,
clarifai: validateClarifaiProvider,
reka: validateRekaProvider,
nlpcloud: validateNlpCloudProvider,

View File

@@ -0,0 +1,46 @@
import { AI_PROVIDERS } from "@/shared/constants/providers";
import type { ZedCredential } from "./keychain-reader";
export interface PartitionedZedCredentials {
importable: ZedCredential[];
skipped: ZedCredential[];
duplicatesDropped: number;
}
function isSupportedZedProvider(provider: string): boolean {
return (
typeof provider === "string" &&
provider.length > 0 &&
provider !== "unknown" &&
!!AI_PROVIDERS[provider]
);
}
export function partitionZedCredentials(credentials: ZedCredential[]): PartitionedZedCredentials {
const importable: ZedCredential[] = [];
const skipped: ZedCredential[] = [];
const seen = new Set<string>();
let duplicatesDropped = 0;
for (const credential of credentials) {
if (!credential?.token || !isSupportedZedProvider(credential.provider)) {
skipped.push(credential);
continue;
}
const dedupeKey = `${credential.provider}:${credential.token}`;
if (seen.has(dedupeKey)) {
duplicatesDropped += 1;
continue;
}
seen.add(dedupeKey);
importable.push(credential);
}
return {
importable,
skipped,
duplicatesDropped,
};
}

View File

@@ -1127,6 +1127,32 @@ export const APIKEY_PROVIDERS = {
authHint: "Bearer API key for the FenayAI OpenAI-compatible gateway.",
passthroughModels: true,
},
empower: {
id: "empower",
alias: "empower",
name: "Empower",
icon: "hub",
color: "#14B8A6",
textIcon: "EM",
website: "https://docs.empower.dev",
authHint: "Bearer API key for the Empower OpenAI-compatible endpoint.",
apiHint:
"Empower exposes OpenAI-compatible chat on https://app.empower.dev/api/v1 with tool-calling support on empower-functions.",
passthroughModels: true,
},
poe: {
id: "poe",
alias: "poe",
name: "Poe",
icon: "hub",
color: "#F97316",
textIcon: "PO",
website: "https://creator.poe.com/api-reference",
authHint: "Bearer API key for the Poe OpenAI-compatible API.",
apiHint:
"Poe exposes OpenAI-compatible chat and responses on https://api.poe.com/v1, with authenticated balance checks on /usage/current_balance.",
passthroughModels: true,
},
gitlab: {
id: "gitlab",
alias: "gitlab",

View File

@@ -27,6 +27,7 @@ const CHAT_OPENAI_COMPAT_PROVIDER_IDS = [
"databricks",
"datarobot",
"clarifai",
"poe",
"azure-ai",
"bedrock",
"watsonx",

View File

@@ -107,6 +107,16 @@ test("Clarifai registry exposes current OpenAI-compatible examples", () => {
assert.ok(ids.has("gcp/generate/models/gemini-2_5-flash"));
});
test("Poe registry exposes current OpenAI-compatible examples", () => {
const poeModels = getProviderModels("poe");
const ids = new Set(poeModels.map((model) => model.id));
assert.ok(ids.has("Claude-Sonnet-4.5"));
assert.ok(ids.has("GPT-5-Pro"));
assert.ok(ids.has("GPT-5-Codex"));
assert.ok(ids.has("Gemini-2.5-Pro"));
});
test("Azure AI Foundry registry exposes fallback marketplace examples", () => {
const azureAiModels = getProviderModels("azure-ai");
const ids = new Set(azureAiModels.map((model) => model.id));

View File

@@ -335,6 +335,18 @@ test("provider models route fetches remote catalogs for new OpenAI-compatible ga
expectedUrl: "https://fenayai.com/v1/models",
model: { id: "deepseek-chat", name: "DeepSeek Chat via FenayAI" },
},
{
provider: "empower",
apiKey: "empower-key",
expectedUrl: "https://app.empower.dev/api/v1/models",
model: { id: "empower-functions", name: "Empower Functions", owned_by: "empower" },
},
{
provider: "poe",
apiKey: "poe-key",
expectedUrl: "https://api.poe.com/v1/models",
model: { id: "Claude-Sonnet-4.5", name: "Claude Sonnet 4.5", owned_by: "Anthropic" },
},
{
provider: "chutes",
apiKey: "chutes-key",

View File

@@ -1189,6 +1189,49 @@ test("specialty validator rejects invalid Modal credentials", async () => {
assert.equal(modal.error, "Invalid API key");
});
test("specialty validator accepts Poe credentials on the current balance endpoint", async () => {
globalThis.fetch = async (url, init = {}) => {
const target = String(url);
if (target === "https://api.poe.com/usage/current_balance") {
const headers = init.headers as Record<string, string>;
assert.equal(headers.Authorization, "Bearer poe-key");
return new Response(JSON.stringify({ current_point_balance: 123456 }), { status: 200 });
}
throw new Error(`unexpected fetch: ${target}`);
};
const poe = await validateProviderApiKey({
provider: "poe",
apiKey: "poe-key",
});
assert.equal(poe.valid, true);
assert.equal(poe.method, "poe_current_balance");
});
test("specialty validator rejects invalid Poe credentials", async () => {
globalThis.fetch = async (url, init = {}) => {
const target = String(url);
if (target === "https://api.poe.com/usage/current_balance") {
const headers = init.headers as Record<string, string>;
assert.equal(headers.Authorization, "Bearer poe-bad");
return new Response(JSON.stringify({ error: "unauthorized" }), { status: 401 });
}
throw new Error(`unexpected fetch: ${target}`);
};
const poe = await validateProviderApiKey({
provider: "poe",
apiKey: "poe-bad",
});
assert.equal(poe.error, "Invalid API key");
});
test("specialty validator accepts Clarifai credentials through the OpenAI-compatible models probe", async () => {
globalThis.fetch = async (url, init = {}) => {
const target = String(url);

View File

@@ -228,6 +228,8 @@ test("static catalog entries resolve local, search, audio, web-cookie and upstre
const chutesProvider = providerPageUtils.resolveDashboardProviderInfo("chutes");
const datarobotProvider = providerPageUtils.resolveDashboardProviderInfo("datarobot");
const clarifaiProvider = providerPageUtils.resolveDashboardProviderInfo("clarifai");
const empowerProvider = providerPageUtils.resolveDashboardProviderInfo("empower");
const poeProvider = providerPageUtils.resolveDashboardProviderInfo("poe");
const azureAiProvider = providerPageUtils.resolveDashboardProviderInfo("azure-ai");
const watsonxProvider = providerPageUtils.resolveDashboardProviderInfo("watsonx");
const ociProvider = providerPageUtils.resolveDashboardProviderInfo("oci");
@@ -271,6 +273,10 @@ test("static catalog entries resolve local, search, audio, web-cookie and upstre
assert.equal(datarobotProvider?.name, providers.APIKEY_PROVIDERS.datarobot.name);
assert.equal(clarifaiProvider?.category, "apikey");
assert.equal(clarifaiProvider?.name, providers.APIKEY_PROVIDERS.clarifai.name);
assert.equal(empowerProvider?.category, "apikey");
assert.equal(empowerProvider?.name, providers.APIKEY_PROVIDERS.empower.name);
assert.equal(poeProvider?.category, "apikey");
assert.equal(poeProvider?.name, providers.APIKEY_PROVIDERS.poe.name);
assert.equal(azureAiProvider?.category, "apikey");
assert.equal(azureAiProvider?.name, providers.APIKEY_PROVIDERS["azure-ai"].name);
assert.equal(watsonxProvider?.category, "apikey");
@@ -323,6 +329,8 @@ test("managed provider connection ids include supported static categories and ex
assert.equal(providerCatalog.isManagedProviderConnectionId("chutes"), true);
assert.equal(providerCatalog.isManagedProviderConnectionId("datarobot"), true);
assert.equal(providerCatalog.isManagedProviderConnectionId("clarifai"), true);
assert.equal(providerCatalog.isManagedProviderConnectionId("empower"), true);
assert.equal(providerCatalog.isManagedProviderConnectionId("poe"), true);
assert.equal(providerCatalog.isManagedProviderConnectionId("azure-ai"), true);
assert.equal(providerCatalog.isManagedProviderConnectionId("bedrock"), true);
assert.equal(providerCatalog.isManagedProviderConnectionId("watsonx"), true);
@@ -372,6 +380,8 @@ test("grok-web taxonomy stays web-cookie only and does not leak into api-key ent
assert.equal("chutes" in providers.APIKEY_PROVIDERS, true);
assert.equal("datarobot" in providers.APIKEY_PROVIDERS, true);
assert.equal("clarifai" in providers.APIKEY_PROVIDERS, true);
assert.equal("empower" in providers.APIKEY_PROVIDERS, true);
assert.equal("poe" in providers.APIKEY_PROVIDERS, true);
assert.equal("azure-ai" in providers.APIKEY_PROVIDERS, true);
assert.equal("bedrock" in providers.APIKEY_PROVIDERS, true);
assert.equal("watsonx" in providers.APIKEY_PROVIDERS, true);
@@ -446,6 +456,14 @@ test("grok-web taxonomy stays web-cookie only and does not leak into api-key ent
apiKeyEntries.some((entry) => entry.providerId === "clarifai"),
true
);
assert.equal(
apiKeyEntries.some((entry) => entry.providerId === "empower"),
true
);
assert.equal(
apiKeyEntries.some((entry) => entry.providerId === "poe"),
true
);
assert.equal(
apiKeyEntries.some((entry) => entry.providerId === "azure-ai"),
true

View File

@@ -94,6 +94,22 @@ test("providers route accepts managed local, audio, web-cookie and search provid
name: "Clarifai Primary",
},
},
{
provider: "empower",
body: {
provider: "empower",
apiKey: "empower-key",
name: "Empower Primary",
},
},
{
provider: "poe",
body: {
provider: "poe",
apiKey: "poe-key",
name: "Poe Primary",
},
},
{
provider: "azure-ai",
body: {

View File

@@ -0,0 +1,74 @@
import test from "node:test";
import assert from "node:assert/strict";
const { partitionZedCredentials } = await import("../../src/lib/zed-oauth/importUtils.ts");
type ImportableCredential = {
provider: string;
token: string;
};
test("partitionZedCredentials keeps only supported providers with tokens", () => {
const result = partitionZedCredentials([
{
provider: "openai",
service: "zed-openai",
account: "api-key",
token: "sk-openai",
},
{
provider: "unknown",
service: "zed-custom",
account: "api-key",
token: "custom-token",
},
{
provider: "anthropic",
service: "zed-anthropic",
account: "api-key",
token: "",
},
]);
assert.deepEqual(result.importable, [
{
provider: "openai",
service: "zed-openai",
account: "api-key",
token: "sk-openai",
},
]);
assert.equal(result.skipped.length, 2);
assert.equal(result.duplicatesDropped, 0);
});
test("partitionZedCredentials drops duplicate provider-token pairs from alias scans", () => {
const result = partitionZedCredentials([
{
provider: "openai",
service: "zed-openai",
account: "api-key",
token: "sk-openai",
},
{
provider: "openai",
service: "ai.zed.openai",
account: "token",
token: "sk-openai",
},
{
provider: "anthropic",
service: "zed-anthropic",
account: "oauth",
token: "sk-anthropic",
},
]);
assert.equal(result.importable.length, 2);
assert.deepEqual(
result.importable.map((entry: ImportableCredential) => `${entry.provider}:${entry.token}`),
["openai:sk-openai", "anthropic:sk-anthropic"]
);
assert.equal(result.skipped.length, 0);
assert.equal(result.duplicatesDropped, 1);
});