fix(morpheus): add COMPAT settings and preferred-provider mapping
- Add MORPHEUS_COMPAT (supportsStore: false, supportsDeveloperRole: false) to both catalog and dynamically discovered models - Remove apiKey from discoverMorpheusModels() since endpoint is public - Add morpheus to preferred-provider mapping for auth choice routing - Add VENICE_COMPAT with matching settings (from upstream PR #2500) - Add venice-models tests (8 tests)
This commit is contained in:
parent
933e2bdf39
commit
8bd547b3e4
@ -351,8 +351,8 @@ async function buildVeniceProvider(): Promise<ProviderConfig> {
|
||||
};
|
||||
}
|
||||
|
||||
async function buildMorpheusProvider(apiKey?: string): Promise<ProviderConfig> {
|
||||
const models = await discoverMorpheusModels(apiKey);
|
||||
async function buildMorpheusProvider(): Promise<ProviderConfig> {
|
||||
const models = await discoverMorpheusModels();
|
||||
return {
|
||||
baseUrl: MORPHEUS_BASE_URL,
|
||||
api: "openai-completions",
|
||||
@ -416,7 +416,7 @@ export async function resolveImplicitProviders(params: {
|
||||
resolveEnvApiKeyVarName("morpheus") ??
|
||||
resolveApiKeyFromProfiles({ provider: "morpheus", store: authStore });
|
||||
if (morpheusKey) {
|
||||
providers.morpheus = { ...(await buildMorpheusProvider(morpheusKey)), apiKey: morpheusKey };
|
||||
providers.morpheus = { ...(await buildMorpheusProvider()), apiKey: morpheusKey };
|
||||
}
|
||||
|
||||
const qwenProfiles = listProfilesForProvider(authStore, "qwen-portal");
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { ModelDefinitionConfig } from "../config/types.js";
|
||||
|
||||
export const MORPHEUS_BASE_URL = "https://api.mor.org/api/v1";
|
||||
export const MORPHEUS_DEFAULT_MODEL_ID = "llama-3.3-70b";
|
||||
export const MORPHEUS_DEFAULT_MODEL_ID = "kimi-k2-thinking";
|
||||
export const MORPHEUS_DEFAULT_MODEL_REF = `morpheus/${MORPHEUS_DEFAULT_MODEL_ID}`;
|
||||
|
||||
// Morpheus is currently FREE during Open Beta (until 1/31/26).
|
||||
@ -13,6 +13,11 @@ export const MORPHEUS_DEFAULT_COST = {
|
||||
cacheWrite: 0,
|
||||
};
|
||||
|
||||
export const MORPHEUS_COMPAT = {
|
||||
supportsStore: false,
|
||||
supportsDeveloperRole: false,
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Complete catalog of Morpheus Inference API models.
|
||||
*
|
||||
@ -182,6 +187,7 @@ export function buildMorpheusModelDefinition(entry: MorpheusCatalogEntry): Model
|
||||
cost: MORPHEUS_DEFAULT_COST,
|
||||
contextWindow: entry.contextWindow,
|
||||
maxTokens: entry.maxTokens,
|
||||
compat: MORPHEUS_COMPAT,
|
||||
};
|
||||
}
|
||||
|
||||
@ -236,24 +242,16 @@ function inferModelProperties(model: MorpheusModel): {
|
||||
|
||||
/**
|
||||
* Discover models from Morpheus API with fallback to static catalog.
|
||||
* The /models endpoint requires authentication.
|
||||
* The /models endpoint is public and doesn't require authentication.
|
||||
*/
|
||||
export async function discoverMorpheusModels(apiKey?: string): Promise<ModelDefinitionConfig[]> {
|
||||
export async function discoverMorpheusModels(): Promise<ModelDefinitionConfig[]> {
|
||||
// Skip API discovery in test environment
|
||||
if (process.env.NODE_ENV === "test" || process.env.VITEST) {
|
||||
return MORPHEUS_MODEL_CATALOG.map(buildMorpheusModelDefinition);
|
||||
}
|
||||
|
||||
// If no API key provided, use static catalog
|
||||
if (!apiKey?.trim()) {
|
||||
return MORPHEUS_MODEL_CATALOG.map(buildMorpheusModelDefinition);
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`${MORPHEUS_BASE_URL}/models`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
},
|
||||
signal: AbortSignal.timeout(5000),
|
||||
});
|
||||
|
||||
@ -298,6 +296,7 @@ export async function discoverMorpheusModels(apiKey?: string): Promise<ModelDefi
|
||||
cost: MORPHEUS_DEFAULT_COST,
|
||||
contextWindow: inferred.contextWindow,
|
||||
maxTokens: 8192,
|
||||
compat: MORPHEUS_COMPAT,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
78
src/agents/venice-models.test.ts
Normal file
78
src/agents/venice-models.test.ts
Normal file
@ -0,0 +1,78 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildVeniceModelDefinition,
|
||||
discoverVeniceModels,
|
||||
VENICE_COMPAT,
|
||||
VENICE_MODEL_CATALOG,
|
||||
} from "./venice-models.js";
|
||||
|
||||
describe("venice-models", () => {
|
||||
describe("VENICE_COMPAT", () => {
|
||||
it("should disable store parameter support", () => {
|
||||
expect(VENICE_COMPAT.supportsStore).toBe(false);
|
||||
});
|
||||
|
||||
it("should disable developer role support", () => {
|
||||
expect(VENICE_COMPAT.supportsDeveloperRole).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("buildVeniceModelDefinition", () => {
|
||||
it("should include compat settings in model definition", () => {
|
||||
const entry = VENICE_MODEL_CATALOG[0];
|
||||
const model = buildVeniceModelDefinition(entry);
|
||||
|
||||
expect(model.compat).toBeDefined();
|
||||
expect(model.compat?.supportsStore).toBe(false);
|
||||
expect(model.compat?.supportsDeveloperRole).toBe(false);
|
||||
});
|
||||
|
||||
it("should build all catalog models with compat settings", () => {
|
||||
for (const entry of VENICE_MODEL_CATALOG) {
|
||||
const model = buildVeniceModelDefinition(entry);
|
||||
expect(model.compat).toEqual(VENICE_COMPAT);
|
||||
}
|
||||
});
|
||||
|
||||
it("should preserve model properties from catalog entry", () => {
|
||||
const entry = VENICE_MODEL_CATALOG.find((m) => m.id === "llama-3.3-70b");
|
||||
expect(entry).toBeDefined();
|
||||
if (!entry) return;
|
||||
|
||||
const model = buildVeniceModelDefinition(entry);
|
||||
|
||||
expect(model.id).toBe("llama-3.3-70b");
|
||||
expect(model.name).toBe("Llama 3.3 70B");
|
||||
expect(model.reasoning).toBe(false);
|
||||
expect(model.input).toContain("text");
|
||||
expect(model.contextWindow).toBe(131072);
|
||||
expect(model.maxTokens).toBe(8192);
|
||||
});
|
||||
});
|
||||
|
||||
describe("discoverVeniceModels", () => {
|
||||
it("should return models with compat settings (static catalog fallback in test env)", async () => {
|
||||
const models = await discoverVeniceModels();
|
||||
|
||||
expect(models.length).toBeGreaterThan(0);
|
||||
|
||||
for (const model of models) {
|
||||
expect(model.compat).toBeDefined();
|
||||
expect(model.compat?.supportsStore).toBe(false);
|
||||
expect(model.compat?.supportsDeveloperRole).toBe(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("VENICE_MODEL_CATALOG", () => {
|
||||
it("should have at least 20 models", () => {
|
||||
expect(VENICE_MODEL_CATALOG.length).toBeGreaterThanOrEqual(20);
|
||||
});
|
||||
|
||||
it("should have both private and anonymized models", () => {
|
||||
const privacyModes = new Set(VENICE_MODEL_CATALOG.map((m) => m.privacy));
|
||||
expect(privacyModes.has("private")).toBe(true);
|
||||
expect(privacyModes.has("anonymized")).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -13,6 +13,21 @@ export const VENICE_DEFAULT_COST = {
|
||||
cacheWrite: 0,
|
||||
};
|
||||
|
||||
/**
|
||||
* Venice API compatibility settings.
|
||||
*
|
||||
* Venice's OpenAI-compatible API doesn't support certain parameters that
|
||||
* Clawdbot sends by default:
|
||||
* - `store`: Venice returns HTTP 400 when this parameter is present
|
||||
* - `developer` role: Not supported by Venice's API
|
||||
*
|
||||
* These settings ensure requests are formatted correctly for Venice.
|
||||
*/
|
||||
export const VENICE_COMPAT = {
|
||||
supportsStore: false,
|
||||
supportsDeveloperRole: false,
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Complete catalog of Venice AI models.
|
||||
*
|
||||
@ -300,6 +315,7 @@ export function buildVeniceModelDefinition(entry: VeniceCatalogEntry): ModelDefi
|
||||
cost: VENICE_DEFAULT_COST,
|
||||
contextWindow: entry.contextWindow,
|
||||
maxTokens: entry.maxTokens,
|
||||
compat: VENICE_COMPAT,
|
||||
};
|
||||
}
|
||||
|
||||
@ -381,6 +397,7 @@ export async function discoverVeniceModels(): Promise<ModelDefinitionConfig[]> {
|
||||
cost: VENICE_DEFAULT_COST,
|
||||
contextWindow: apiModel.model_spec.availableContextTokens || 128000,
|
||||
maxTokens: 8192,
|
||||
compat: VENICE_COMPAT,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,6 +20,7 @@ const PREFERRED_PROVIDER_BY_AUTH_CHOICE: Partial<Record<AuthChoice, string>> = {
|
||||
"zai-api-key": "zai",
|
||||
"synthetic-api-key": "synthetic",
|
||||
"venice-api-key": "venice",
|
||||
"morpheus-api-key": "morpheus",
|
||||
"github-copilot": "github-copilot",
|
||||
"copilot-proxy": "copilot-proxy",
|
||||
"minimax-cloud": "minimax",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user