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> {
|
async function buildMorpheusProvider(): Promise<ProviderConfig> {
|
||||||
const models = await discoverMorpheusModels(apiKey);
|
const models = await discoverMorpheusModels();
|
||||||
return {
|
return {
|
||||||
baseUrl: MORPHEUS_BASE_URL,
|
baseUrl: MORPHEUS_BASE_URL,
|
||||||
api: "openai-completions",
|
api: "openai-completions",
|
||||||
@ -416,7 +416,7 @@ export async function resolveImplicitProviders(params: {
|
|||||||
resolveEnvApiKeyVarName("morpheus") ??
|
resolveEnvApiKeyVarName("morpheus") ??
|
||||||
resolveApiKeyFromProfiles({ provider: "morpheus", store: authStore });
|
resolveApiKeyFromProfiles({ provider: "morpheus", store: authStore });
|
||||||
if (morpheusKey) {
|
if (morpheusKey) {
|
||||||
providers.morpheus = { ...(await buildMorpheusProvider(morpheusKey)), apiKey: morpheusKey };
|
providers.morpheus = { ...(await buildMorpheusProvider()), apiKey: morpheusKey };
|
||||||
}
|
}
|
||||||
|
|
||||||
const qwenProfiles = listProfilesForProvider(authStore, "qwen-portal");
|
const qwenProfiles = listProfilesForProvider(authStore, "qwen-portal");
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import type { ModelDefinitionConfig } from "../config/types.js";
|
import type { ModelDefinitionConfig } from "../config/types.js";
|
||||||
|
|
||||||
export const MORPHEUS_BASE_URL = "https://api.mor.org/api/v1";
|
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}`;
|
export const MORPHEUS_DEFAULT_MODEL_REF = `morpheus/${MORPHEUS_DEFAULT_MODEL_ID}`;
|
||||||
|
|
||||||
// Morpheus is currently FREE during Open Beta (until 1/31/26).
|
// Morpheus is currently FREE during Open Beta (until 1/31/26).
|
||||||
@ -13,6 +13,11 @@ export const MORPHEUS_DEFAULT_COST = {
|
|||||||
cacheWrite: 0,
|
cacheWrite: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const MORPHEUS_COMPAT = {
|
||||||
|
supportsStore: false,
|
||||||
|
supportsDeveloperRole: false,
|
||||||
|
} as const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Complete catalog of Morpheus Inference API models.
|
* Complete catalog of Morpheus Inference API models.
|
||||||
*
|
*
|
||||||
@ -182,6 +187,7 @@ export function buildMorpheusModelDefinition(entry: MorpheusCatalogEntry): Model
|
|||||||
cost: MORPHEUS_DEFAULT_COST,
|
cost: MORPHEUS_DEFAULT_COST,
|
||||||
contextWindow: entry.contextWindow,
|
contextWindow: entry.contextWindow,
|
||||||
maxTokens: entry.maxTokens,
|
maxTokens: entry.maxTokens,
|
||||||
|
compat: MORPHEUS_COMPAT,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,24 +242,16 @@ function inferModelProperties(model: MorpheusModel): {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Discover models from Morpheus API with fallback to static catalog.
|
* 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
|
// Skip API discovery in test environment
|
||||||
if (process.env.NODE_ENV === "test" || process.env.VITEST) {
|
if (process.env.NODE_ENV === "test" || process.env.VITEST) {
|
||||||
return MORPHEUS_MODEL_CATALOG.map(buildMorpheusModelDefinition);
|
return MORPHEUS_MODEL_CATALOG.map(buildMorpheusModelDefinition);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no API key provided, use static catalog
|
|
||||||
if (!apiKey?.trim()) {
|
|
||||||
return MORPHEUS_MODEL_CATALOG.map(buildMorpheusModelDefinition);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${MORPHEUS_BASE_URL}/models`, {
|
const response = await fetch(`${MORPHEUS_BASE_URL}/models`, {
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${apiKey}`,
|
|
||||||
},
|
|
||||||
signal: AbortSignal.timeout(5000),
|
signal: AbortSignal.timeout(5000),
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -298,6 +296,7 @@ export async function discoverMorpheusModels(apiKey?: string): Promise<ModelDefi
|
|||||||
cost: MORPHEUS_DEFAULT_COST,
|
cost: MORPHEUS_DEFAULT_COST,
|
||||||
contextWindow: inferred.contextWindow,
|
contextWindow: inferred.contextWindow,
|
||||||
maxTokens: 8192,
|
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,
|
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.
|
* Complete catalog of Venice AI models.
|
||||||
*
|
*
|
||||||
@ -300,6 +315,7 @@ export function buildVeniceModelDefinition(entry: VeniceCatalogEntry): ModelDefi
|
|||||||
cost: VENICE_DEFAULT_COST,
|
cost: VENICE_DEFAULT_COST,
|
||||||
contextWindow: entry.contextWindow,
|
contextWindow: entry.contextWindow,
|
||||||
maxTokens: entry.maxTokens,
|
maxTokens: entry.maxTokens,
|
||||||
|
compat: VENICE_COMPAT,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -381,6 +397,7 @@ export async function discoverVeniceModels(): Promise<ModelDefinitionConfig[]> {
|
|||||||
cost: VENICE_DEFAULT_COST,
|
cost: VENICE_DEFAULT_COST,
|
||||||
contextWindow: apiModel.model_spec.availableContextTokens || 128000,
|
contextWindow: apiModel.model_spec.availableContextTokens || 128000,
|
||||||
maxTokens: 8192,
|
maxTokens: 8192,
|
||||||
|
compat: VENICE_COMPAT,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,7 @@ const PREFERRED_PROVIDER_BY_AUTH_CHOICE: Partial<Record<AuthChoice, string>> = {
|
|||||||
"zai-api-key": "zai",
|
"zai-api-key": "zai",
|
||||||
"synthetic-api-key": "synthetic",
|
"synthetic-api-key": "synthetic",
|
||||||
"venice-api-key": "venice",
|
"venice-api-key": "venice",
|
||||||
|
"morpheus-api-key": "morpheus",
|
||||||
"github-copilot": "github-copilot",
|
"github-copilot": "github-copilot",
|
||||||
"copilot-proxy": "copilot-proxy",
|
"copilot-proxy": "copilot-proxy",
|
||||||
"minimax-cloud": "minimax",
|
"minimax-cloud": "minimax",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user