feat(agents): register Redpill AI provider

Add provider builder and implicit registration with API key resolution.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
HashWarlock 2026-01-25 18:23:29 -06:00
parent 24a4bf4a0d
commit 36cd0c82df
3 changed files with 30 additions and 9 deletions

View File

@ -13,6 +13,7 @@ import {
SYNTHETIC_MODEL_CATALOG,
} from "./synthetic-models.js";
import { discoverVeniceModels, VENICE_BASE_URL } from "./venice-models.js";
import { discoverRedpillModels, REDPILL_BASE_URL } from "./redpill-models.js";
type ModelsConfig = NonNullable<ClawdbotConfig["models"]>;
export type ProviderConfig = NonNullable<ModelsConfig["providers"]>[string];
@ -350,6 +351,15 @@ async function buildVeniceProvider(): Promise<ProviderConfig> {
};
}
async function buildRedpillProvider(): Promise<ProviderConfig> {
const models = discoverRedpillModels();
return {
baseUrl: REDPILL_BASE_URL,
api: "openai-completions",
models,
};
}
async function buildOllamaProvider(): Promise<ProviderConfig> {
const models = await discoverOllamaModels();
return {
@ -402,6 +412,17 @@ export async function resolveImplicitProviders(params: {
providers.venice = { ...(await buildVeniceProvider()), apiKey: veniceKey };
}
// Redpill
const redpillKey =
resolveEnvApiKeyVarName("redpill") ??
resolveApiKeyFromProfiles({ provider: "redpill", store: authStore });
if (redpillKey) {
providers.redpill = {
...(await buildRedpillProvider()),
apiKey: redpillKey,
};
}
const qwenProfiles = listProfilesForProvider(authStore, "qwen-portal");
if (qwenProfiles.length > 0) {
providers["qwen-portal"] = {

View File

@ -4,7 +4,7 @@ import {
REDPILL_DEFAULT_MODEL,
REDPILL_DEFAULT_MODEL_REF,
REDPILL_BASE_URL,
getRedpillModels,
discoverRedpillModels,
resetRedpillModelCache,
type RedpillCatalogEntry,
} from "./redpill-models.js";
@ -122,9 +122,9 @@ describe("Redpill Models", () => {
});
});
describe("getRedpillModels", () => {
describe("discoverRedpillModels", () => {
it("should convert catalog to model definitions", () => {
const models = getRedpillModels();
const models = discoverRedpillModels();
expect(models).toHaveLength(18);
for (const model of models) {
@ -146,15 +146,15 @@ describe("Redpill Models", () => {
});
it("should cache results", () => {
const models1 = getRedpillModels();
const models2 = getRedpillModels();
const models1 = discoverRedpillModels();
const models2 = discoverRedpillModels();
expect(models1).toBe(models2); // Same reference
});
it("should return fresh data after cache reset", () => {
const models1 = getRedpillModels();
const models1 = discoverRedpillModels();
resetRedpillModelCache();
const models2 = getRedpillModels();
const models2 = discoverRedpillModels();
expect(models1).not.toBe(models2); // Different reference
expect(models1).toEqual(models2); // Same content
});

View File

@ -250,9 +250,9 @@ function catalogEntryToModelDefinition(entry: RedpillCatalogEntry): ModelDefinit
}
/**
* Get cached model list or convert from catalog
* Discover cached model list or convert from catalog
*/
export function getRedpillModels(): ModelDefinitionConfig[] {
export function discoverRedpillModels(): ModelDefinitionConfig[] {
const now = Date.now();
// Return cached models if still valid