- Multi-provider AI analysis (Anthropic, OpenAI, Ollama, Algorithmic) - server-only guards on all provider files; client bundle fix - /settings page with provider status, Ollama model picker, preferences - Song search box on /analyze replacing raw MBID input (debounced, keyboard nav) - Auto-register song via MusicBrainz on POST /api/tracks (no more 404) - Fix WAV duration bug: last section songEnd was double-counting elapsed time - Registry sync comment updated for self-hosted HTTPS git servers Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
79 lines
2.4 KiB
TypeScript
79 lines
2.4 KiB
TypeScript
import 'server-only';
|
|
|
|
import type { AnalysisProvider, ProviderInfo } from "@/lib/analysis/providers";
|
|
import { anthropicProvider } from "./anthropic";
|
|
import { openaiProvider } from "./openai";
|
|
import { ollamaProvider, getOllamaModels } from "./ollama";
|
|
import { algorithmicProvider } from "./algorithmic";
|
|
import { RECOMMENDED_OLLAMA_MODELS } from "@/lib/analysis/constants";
|
|
|
|
export { getOllamaModels, RECOMMENDED_OLLAMA_MODELS };
|
|
|
|
// Registration order determines the default when the user has no saved preference.
|
|
const ALL_PROVIDERS: AnalysisProvider[] = [
|
|
anthropicProvider,
|
|
openaiProvider,
|
|
ollamaProvider,
|
|
algorithmicProvider,
|
|
];
|
|
|
|
/**
|
|
* Returns every provider with its current availability status.
|
|
* Runs all isAvailable() checks in parallel.
|
|
*/
|
|
export async function getProviderInfoList(): Promise<ProviderInfo[]> {
|
|
const results = await Promise.all(
|
|
ALL_PROVIDERS.map(async (p) => {
|
|
const availability = await p.isAvailable();
|
|
const info: ProviderInfo = {
|
|
id: p.id,
|
|
label: p.label,
|
|
type: p.type,
|
|
available: availability.available,
|
|
};
|
|
if (!availability.available && availability.reason) {
|
|
info.unavailableReason = availability.reason;
|
|
}
|
|
if (p.id === "ollama") {
|
|
info.ollamaBaseUrl = process.env.OLLAMA_BASE_URL ?? "http://localhost:11434";
|
|
}
|
|
return info;
|
|
})
|
|
);
|
|
return results;
|
|
}
|
|
|
|
/**
|
|
* Returns only providers where available === true.
|
|
* The algorithmic provider is always included.
|
|
*/
|
|
export async function getAvailableProviders(): Promise<AnalysisProvider[]> {
|
|
const checks = await Promise.all(
|
|
ALL_PROVIDERS.map(async (p) => {
|
|
const availability = await p.isAvailable();
|
|
return { provider: p, available: availability.available };
|
|
})
|
|
);
|
|
return checks.filter((c) => c.available).map((c) => c.provider);
|
|
}
|
|
|
|
/**
|
|
* Looks up a provider by id. Throws with a descriptive message if not found
|
|
* or if isAvailable() returns false.
|
|
*/
|
|
export async function getProvider(id: string): Promise<AnalysisProvider> {
|
|
const provider = ALL_PROVIDERS.find((p) => p.id === id);
|
|
if (!provider) {
|
|
throw new Error(
|
|
`Unknown provider '${id}'. Available providers: ${ALL_PROVIDERS.map((p) => p.id).join(", ")}`
|
|
);
|
|
}
|
|
const availability = await provider.isAvailable();
|
|
if (!availability.available) {
|
|
throw new Error(
|
|
`Provider '${id}' is not available: ${availability.reason ?? "unknown reason"}`
|
|
);
|
|
}
|
|
return provider;
|
|
}
|