Remove dynamic Together AI model discovery to prevent API timeouts

This commit is contained in:
Riccardo Giorato 2026-01-27 14:45:29 +01:00
parent 64aa8c85d1
commit dfabc7d2ef
2 changed files with 5 additions and 143 deletions

View File

@ -13,11 +13,7 @@ import {
SYNTHETIC_MODEL_CATALOG,
} from "./synthetic-models.js";
import { discoverVeniceModels, VENICE_BASE_URL } from "./venice-models.js";
import {
discoverTogetherModels,
TOGETHER_BASE_URL,
TOGETHER_MODEL_CATALOG,
} from "./together-models.js";
import { TOGETHER_BASE_URL, TOGETHER_MODEL_CATALOG } from "./together-models.js";
type ModelsConfig = NonNullable<MoltbotConfig["models"]>;
export type ProviderConfig = NonNullable<ModelsConfig["providers"]>[string];
@ -364,12 +360,11 @@ async function buildOllamaProvider(): Promise<ProviderConfig> {
};
}
async function buildTogetherProvider(apiKey?: string): Promise<ProviderConfig> {
// Only discover models if we have an API key, otherwise use static catalog
const models = apiKey ? await discoverTogetherModels(apiKey) : TOGETHER_MODEL_CATALOG;
async function buildTogetherProvider(_apiKey?: string): Promise<ProviderConfig> {
// Always use static catalog instead of dynamic discovery
// This prevents timeout issues with the Together AI API
const models = TOGETHER_MODEL_CATALOG;
// If we successfully discovered models, return them and let the merge logic handle conflicts
// If discovery failed, return empty array to fallback to static catalog
return {
baseUrl: TOGETHER_BASE_URL,
api: "openai-completions",

View File

@ -126,136 +126,3 @@ export function buildTogetherModelDefinition(
maxTokens: model.maxTokens,
};
}
// Together AI API response types
interface TogetherModel {
id: string;
name?: string;
display_name?: string;
description?: string;
context_length?: number;
tokenizer?: string;
type?: string;
capabilities?: {
vision?: boolean;
function_calling?: boolean;
tool_use?: boolean;
};
pricing?: {
input?: number;
output?: number;
};
}
/**
* Discover models from Together AI API.
* The /models endpoint requires authentication via API key.
*/
export async function discoverTogetherModels(apiKey?: string): Promise<ModelDefinitionConfig[]> {
// Skip API discovery in test environment
if (process.env.NODE_ENV === "test" || process.env.VITEST) {
return [];
}
try {
// Together AI requires authentication for /models endpoint
const headers: Record<string, string> = {
"Content-Type": "application/json",
};
if (apiKey) {
headers["Authorization"] = `Bearer ${apiKey}`;
}
const response = await fetch(`${TOGETHER_BASE_URL}/models`, {
signal: AbortSignal.timeout(5000),
headers,
});
if (!response.ok) {
// Try to get error details from response
try {
const errorText = await response.text();
console.warn(
`[together-models] Failed to discover models: HTTP ${response.status}`,
errorText,
);
} catch (e) {
console.warn(`[together-models] Could not read error response body: ${String(e)}`);
}
return [];
}
const rawResponse = await response.text();
let models: TogetherModel[];
try {
const parsed = JSON.parse(rawResponse);
// Together AI returns array directly, not { data: array }
if (Array.isArray(parsed)) {
models = parsed as TogetherModel[];
} else if (parsed.data && Array.isArray(parsed.data)) {
models = parsed.data as TogetherModel[];
} else {
console.error(`[together-models] Unexpected response format:`, parsed);
return [];
}
} catch (e) {
console.error(`[together-models] Failed to parse JSON: ${String(e)}`);
return [];
}
if (!Array.isArray(models) || models.length === 0) {
return [];
}
// Filter for chat models only and map to ModelDefinitionConfig
const chatModels = models.filter((model) => model.type === "chat");
return chatModels.map((model: TogetherModel) => {
const modelId = model.id;
const displayName = model.display_name || model.name || modelId;
// Determine if model supports reasoning
const isReasoning =
modelId.toLowerCase().includes("reason") ||
modelId.toLowerCase().includes("r1") ||
modelId.toLowerCase().includes("thinking") ||
model.description?.toLowerCase().includes("reasoning") ||
false;
// Determine input types
const hasVision =
model.capabilities?.vision ||
modelId.toLowerCase().includes("vision") ||
modelId.toLowerCase().includes("vl") ||
model.description?.toLowerCase().includes("vision") ||
false;
// Use pricing from API if available, otherwise use defaults
const cost = model.pricing
? {
input: model.pricing.input || TOGETHER_DEFAULT_COST.input,
output: model.pricing.output || TOGETHER_DEFAULT_COST.output,
cacheRead: model.pricing.input || TOGETHER_DEFAULT_COST.cacheRead,
cacheWrite: model.pricing.output || TOGETHER_DEFAULT_COST.cacheWrite,
}
: TOGETHER_DEFAULT_COST;
return {
id: modelId,
name: displayName,
reasoning: isReasoning,
input: hasVision ? ["text", "image"] : ["text"],
cost,
contextWindow: model.context_length || 131072,
maxTokens: 8192, // Default max tokens for most models
};
});
} catch (error) {
console.warn(`[together-models] Discovery failed: ${String(error)}`);
return [];
}
}