Unify AI provider selection across all features
Every LLM-backed feature (Post-Processing, Proofreading, Title Generation, Property Filler) now reads from one CHAT_PROVIDERS registry instead of 5 hand-duplicated provider lists, so they always offer the exact same providers in the exact same order. - Add Groq as a 5th chat-completion provider (was transcription-only despite its OpenAI-compatible API and an already-collected key). - Provider dropdowns disable options with no API key configured (shown as e.g. "Anthropic (no API key)") instead of silently failing at runtime when picked. - Collapse 3 byte-for-byte duplicated resolveProvider() methods (Proofreader, TitleGenerator, PropertyFiller) plus AudioHandler's getPostProcessingApiKey() into one resolveChatProvider() helper (src/ProviderResolver.ts). - Fix Custom provider: previously Proofreading/Title/Property silently reused Post-Processing's postProcessingUrl with no visible field to set it. Renamed to a shared customApiUrl field with its own UI (createCustomProviderFields), now exposed on every tab that offers Custom, plus rows on the API Keys tab. Migrates old data.json values automatically. - Update README settings reference and getting-started sections to match. No behavior change for existing single-provider setups; migration handles the postProcessingUrl -> customApiUrl rename transparently.
This commit is contained in:
+50
-12
@@ -1,6 +1,6 @@
|
||||
import { Plugin } from "obsidian";
|
||||
|
||||
const SECRET_IDS: Record<keyof ApiKeysSettings, string> = {
|
||||
const SECRET_IDS: Partial<Record<keyof ApiKeysSettings, string>> = {
|
||||
groqApiKey: "groq-api-key",
|
||||
openAiApiKey: "openai-api-key",
|
||||
anthropicApiKey: "anthropic-api-key",
|
||||
@@ -8,7 +8,7 @@ const SECRET_IDS: Record<keyof ApiKeysSettings, string> = {
|
||||
openRouterApiKey: "openrouter-api-key",
|
||||
};
|
||||
|
||||
export type PostProcessingProvider = "anthropic" | "openai" | "openrouter" | "custom";
|
||||
export type PostProcessingProvider = "anthropic" | "openai" | "openrouter" | "groq" | "custom";
|
||||
|
||||
export type AIProvider = PostProcessingProvider;
|
||||
|
||||
@@ -16,6 +16,7 @@ export const PROVIDER_URLS: Record<PostProcessingProvider, string> = {
|
||||
anthropic: "https://api.anthropic.com/v1/messages",
|
||||
openai: "https://api.openai.com/v1/chat/completions",
|
||||
openrouter: "https://openrouter.ai/api/v1/chat/completions",
|
||||
groq: "https://api.groq.com/openai/v1/chat/completions",
|
||||
custom: "",
|
||||
};
|
||||
|
||||
@@ -23,9 +24,30 @@ export const PROVIDER_DEFAULT_MODELS: Record<PostProcessingProvider, string> = {
|
||||
anthropic: "claude-sonnet-4-20250514",
|
||||
openai: "gpt-4o-mini",
|
||||
openrouter: "openai/gpt-4o-mini",
|
||||
groq: "llama-3.3-70b-versatile",
|
||||
custom: "",
|
||||
};
|
||||
|
||||
export interface ChatProviderInfo {
|
||||
id: PostProcessingProvider;
|
||||
label: string;
|
||||
apiKeyField: keyof ApiKeysSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Single source of truth for which chat-completion providers exist and which
|
||||
* API key each one uses. UI dropdowns and provider resolution both read from
|
||||
* this list so every AI feature (Post-Processing, Proofreading, Title
|
||||
* Generation, Property Filler) offers the exact same providers.
|
||||
*/
|
||||
export const CHAT_PROVIDERS: ChatProviderInfo[] = [
|
||||
{ id: "anthropic", label: "Anthropic", apiKeyField: "anthropicApiKey" },
|
||||
{ id: "openai", label: "OpenAI", apiKeyField: "openAiApiKey" },
|
||||
{ id: "groq", label: "Groq", apiKeyField: "groqApiKey" },
|
||||
{ id: "openrouter", label: "OpenRouter", apiKeyField: "openRouterApiKey" },
|
||||
{ id: "custom", label: "Custom", apiKeyField: "customApiKey" },
|
||||
];
|
||||
|
||||
export type TranscriptionProvider = "groq" | "openai" | "azure" | "custom";
|
||||
|
||||
export const TRANSCRIPTION_PROVIDER_URLS: Record<TranscriptionProvider, string> = {
|
||||
@@ -42,6 +64,7 @@ export interface ApiKeysSettings {
|
||||
openAiApiKey: string;
|
||||
anthropicApiKey: string;
|
||||
customApiKey: string;
|
||||
customApiUrl: string;
|
||||
openRouterApiKey: string;
|
||||
}
|
||||
|
||||
@@ -74,7 +97,6 @@ export interface OutputSettings {
|
||||
export interface PostProcessingSettings {
|
||||
postProcessing: boolean;
|
||||
postProcessingProvider: PostProcessingProvider;
|
||||
postProcessingUrl: string;
|
||||
postProcessingModel: string;
|
||||
postProcessingPrompt: string;
|
||||
autoGenerateTitle: boolean;
|
||||
@@ -128,6 +150,7 @@ export const DEFAULT_API_KEYS: ApiKeysSettings = {
|
||||
openAiApiKey: "",
|
||||
anthropicApiKey: "",
|
||||
customApiKey: "",
|
||||
customApiUrl: "",
|
||||
openRouterApiKey: "",
|
||||
};
|
||||
|
||||
@@ -160,7 +183,6 @@ export const DEFAULT_OUTPUT: OutputSettings = {
|
||||
export const DEFAULT_POST_PROCESSING: PostProcessingSettings = {
|
||||
postProcessing: false,
|
||||
postProcessingProvider: "anthropic",
|
||||
postProcessingUrl: "https://api.anthropic.com/v1/messages",
|
||||
postProcessingModel: "claude-sonnet-4-20250514",
|
||||
postProcessingPrompt:
|
||||
'You are a transcription editor. Clean up the following voice transcription: fix grammar, remove filler words (um, uh, like) and repetitions, and improve readability. Format the text in markdown. If there are action items or to-dos, format them as task lists with "[ ]". Preserve the original meaning and language. Return only the polished text, nothing else.',
|
||||
@@ -274,19 +296,31 @@ export class SettingsManager {
|
||||
|
||||
private migratePostProcessingProvider(settings: PluginSettings): boolean {
|
||||
if (settings.postProcessingProvider) return false;
|
||||
const legacyUrl = (settings as PluginSettings & { postProcessingUrl?: string })
|
||||
.postProcessingUrl;
|
||||
if (!legacyUrl) return false;
|
||||
|
||||
for (const [provider, url] of Object.entries(PROVIDER_URLS)) {
|
||||
if (url && settings.postProcessingUrl === url) {
|
||||
settings.postProcessingProvider =
|
||||
provider as PostProcessingProvider;
|
||||
if (url && legacyUrl === url) {
|
||||
settings.postProcessingProvider = provider as PostProcessingProvider;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (settings.postProcessingUrl) {
|
||||
settings.postProcessingProvider = "custom";
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
settings.postProcessingProvider = "custom";
|
||||
return true;
|
||||
}
|
||||
|
||||
private migrateCustomApiUrl(settings: PluginSettings): boolean {
|
||||
// One-time migration: the old `postProcessingUrl` field is now the
|
||||
// shared `customApiUrl` used by every feature's "Custom" provider.
|
||||
if (settings.customApiUrl) return false;
|
||||
const legacyUrl = (settings as PluginSettings & { postProcessingUrl?: string })
|
||||
.postProcessingUrl;
|
||||
if (!legacyUrl) return false;
|
||||
if (Object.values(PROVIDER_URLS).includes(legacyUrl)) return false;
|
||||
|
||||
settings.customApiUrl = legacyUrl;
|
||||
return true;
|
||||
}
|
||||
|
||||
async loadSettings(): Promise<PluginSettings> {
|
||||
@@ -300,6 +334,10 @@ export class SettingsManager {
|
||||
await this.plugin.saveData(settings);
|
||||
}
|
||||
|
||||
if (this.migrateCustomApiUrl(settings)) {
|
||||
await this.plugin.saveData(settings);
|
||||
}
|
||||
|
||||
if (this.migrateKeysFromDataJson(settings)) {
|
||||
await this.plugin.saveData(settings);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user