mirror of
https://github.com/logancyang/obsidian-copilot.git
synced 2026-07-22 07:50:24 +00:00
Turns on no-unsafe-enum-comparison, no-base-to-string, no-redundant-type-constituents, and restrict-template-expressions. Fixes all 24 violations across 13 source files.
194 lines
5.7 KiB
TypeScript
194 lines
5.7 KiB
TypeScript
import { CustomModel } from "@/aiParams";
|
|
import { ChatModelProviders, SettingKeyProviders } from "@/constants";
|
|
import { getDecryptedKey } from "@/encryptionService";
|
|
import { GitHubCopilotProvider } from "@/LLMProviders/githubCopilot/GitHubCopilotProvider";
|
|
import ProjectManager from "@/LLMProviders/projectManager";
|
|
import { logError, logWarn } from "@/logger";
|
|
import { parseModelsResponse, StandardModel } from "@/settings/providerModels";
|
|
import { err2String, getProviderInfo, safeFetch } from "@/utils";
|
|
import { getApiKeyForProvider } from "@/utils/modelUtils";
|
|
|
|
export interface FetchModelsResult {
|
|
success: boolean;
|
|
models: StandardModel[];
|
|
error?: string;
|
|
}
|
|
|
|
export interface AddModelResult {
|
|
success: boolean;
|
|
alreadyExists: boolean;
|
|
verificationFailed: boolean;
|
|
verificationError?: string;
|
|
}
|
|
|
|
/**
|
|
* Fetch models for a provider
|
|
*/
|
|
export async function fetchModelsForProvider(
|
|
provider: SettingKeyProviders
|
|
): Promise<FetchModelsResult> {
|
|
try {
|
|
// Special handling for GitHub Copilot
|
|
if (provider === ChatModelProviders.GITHUB_COPILOT) {
|
|
const copilotProvider = GitHubCopilotProvider.getInstance();
|
|
const response = await copilotProvider.listModels();
|
|
const models = parseModelsResponse(provider, response);
|
|
return { success: true, models };
|
|
}
|
|
|
|
// Standard API key based providers
|
|
let apiKey = getApiKeyForProvider(provider);
|
|
if (!apiKey) {
|
|
return { success: false, models: [], error: "API key not configured" };
|
|
}
|
|
|
|
apiKey = await getDecryptedKey(apiKey);
|
|
|
|
let url = getProviderInfo(provider).listModelURL;
|
|
if (!url) {
|
|
return { success: false, models: [], error: "Provider does not support model listing" };
|
|
}
|
|
|
|
let headers: Record<string, string> = {
|
|
Authorization: `Bearer ${apiKey}`,
|
|
};
|
|
|
|
if (provider === ChatModelProviders.GOOGLE) {
|
|
url += `?key=${apiKey}`;
|
|
headers = {};
|
|
} else if (provider === ChatModelProviders.ANTHROPIC) {
|
|
headers = {
|
|
"x-api-key": apiKey,
|
|
"anthropic-version": "2023-06-01",
|
|
};
|
|
}
|
|
|
|
const tryFetch = async (useSafeFetch: boolean) => {
|
|
const controller = new AbortController();
|
|
const timeoutId = window.setTimeout(() => controller.abort(), 3000);
|
|
|
|
try {
|
|
const response = await (useSafeFetch ? safeFetch : fetch)(url, {
|
|
headers,
|
|
signal: controller.signal,
|
|
method: "GET",
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const msg = err2String(await response.json());
|
|
logError(msg);
|
|
throw new Error(`Failed to fetch models: ${response.statusText} \n detail: ` + msg);
|
|
}
|
|
return response;
|
|
} finally {
|
|
window.clearTimeout(timeoutId);
|
|
}
|
|
};
|
|
|
|
let response;
|
|
try {
|
|
response = await tryFetch(false);
|
|
} catch (firstError) {
|
|
logWarn("First fetch attempt failed, trying with safeFetch...");
|
|
try {
|
|
response = await tryFetch(true);
|
|
} catch (error) {
|
|
const msg =
|
|
"\nwithout CORS Error: " +
|
|
err2String(firstError) +
|
|
"\nwith CORS Error: " +
|
|
err2String(error);
|
|
throw new Error(msg);
|
|
}
|
|
}
|
|
|
|
const rawData = await response.json();
|
|
const models = parseModelsResponse(provider, rawData);
|
|
return { success: true, models };
|
|
} catch (error) {
|
|
logError(`Error fetching models for ${provider}:`, error);
|
|
return { success: false, models: [], error: err2String(error) };
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Verify and add a model to activeModels
|
|
*/
|
|
export async function verifyAndAddModel(
|
|
model: { id: string; name: string; provider: SettingKeyProviders },
|
|
activeModels: CustomModel[],
|
|
options?: { skipVerification?: boolean }
|
|
): Promise<AddModelResult> {
|
|
const skipVerification = options?.skipVerification ?? false;
|
|
|
|
// Check if model already exists
|
|
const existingModel = activeModels.find(
|
|
(m) => m.name === model.name && (m.provider as SettingKeyProviders) === model.provider
|
|
);
|
|
|
|
const alreadyExists = Boolean(existingModel);
|
|
|
|
// Build CustomModel
|
|
const apiKey =
|
|
model.provider === ChatModelProviders.GITHUB_COPILOT
|
|
? undefined
|
|
: getApiKeyForProvider(model.provider);
|
|
|
|
const customModel: CustomModel = {
|
|
name: model.name,
|
|
provider: model.provider,
|
|
apiKey,
|
|
enabled: true,
|
|
};
|
|
|
|
// Verify model if not skipped
|
|
let verificationFailed = false;
|
|
let verificationError: string | undefined;
|
|
|
|
if (!skipVerification) {
|
|
try {
|
|
await ProjectManager.instance.getCurrentChainManager().chatModelManager.ping(customModel);
|
|
} catch (error) {
|
|
verificationFailed = true;
|
|
verificationError = err2String(error);
|
|
|
|
// For GitHub Copilot models, a "not supported" 400 typically means the user
|
|
// hasn't enabled this model on their GitHub settings page. Append the policy
|
|
// terms (which include an activation link) to guide the user.
|
|
if (
|
|
(customModel.provider as ChatModelProviders) === ChatModelProviders.GITHUB_COPILOT &&
|
|
verificationError.toLowerCase().includes("not supported")
|
|
) {
|
|
// Reason: policy cache is keyed by model.id, not customModel.name (display name)
|
|
const terms = GitHubCopilotProvider.getInstance().getPolicyTerms(model.id);
|
|
if (terms) {
|
|
verificationError += `\n\n${terms}`;
|
|
}
|
|
}
|
|
|
|
logError("Model verification failed:", error);
|
|
}
|
|
}
|
|
|
|
return {
|
|
success: true,
|
|
alreadyExists,
|
|
verificationFailed,
|
|
verificationError,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Build CustomModel object for adding to activeModels
|
|
*/
|
|
export function buildCustomModel(model: {
|
|
id: string;
|
|
name: string;
|
|
provider: SettingKeyProviders;
|
|
}): CustomModel {
|
|
return {
|
|
name: model.name,
|
|
provider: model.provider,
|
|
enabled: true,
|
|
};
|
|
}
|