feat: analysis providers, settings UI, song search, WAV duration fix
- 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>
This commit is contained in:
78
lib/analysis/providers/registry.ts
Normal file
78
lib/analysis/providers/registry.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user