[feat] Add Azure OpenAI support to ChatApiManager and update settings interface for Azure configuration.

This commit is contained in:
Barrca 2025-09-19 17:25:45 +02:00
parent 149953d1bd
commit 3e25d3e8d1
2 changed files with 121 additions and 8 deletions

View file

@ -1,5 +1,5 @@
// api.ts
import { ChatOpenAI } from "@langchain/openai";
import { ChatOpenAI, AzureChatOpenAI } from "@langchain/openai";
import { ChatOllama } from "@langchain/ollama";
import { ChatGoogleGenerativeAI } from "@langchain/google-genai";
import {
@ -25,7 +25,12 @@ export type HistoryMessage = {
* Class to manage interactions with different chat APIs.
*/
export class ChatApiManager {
private chatClient: ChatOpenAI | ChatOllama | ChatGoogleGenerativeAI | null;
private chatClient:
| ChatOpenAI
| ChatOllama
| ChatGoogleGenerativeAI
| AzureChatOpenAI
| null;
private app: App;
private settings: InlineAISettings;
private messageHistory: MessageQueue<HistoryMessage>;
@ -43,14 +48,45 @@ export class ChatApiManager {
);
}
/**
* Extracts the instance name from an Azure endpoint URL.
* @param endpoint - The Azure endpoint URL.
* @returns The instance name or null if invalid.
*/
private extractAzureInstanceName(endpoint: string): string | null {
const trimmedEndpoint = endpoint.trim();
// Match both openai.azure.com and cognitiveservices.azure.com formats
const openaiMatch = trimmedEndpoint.match(
/https:\/\/([^.]+)\.openai\.azure\.com/,
);
if (openaiMatch) {
return openaiMatch[1];
}
const cognitiveservicesMatch = trimmedEndpoint.match(
/https:\/\/([^.]+)\.cognitiveservices\.azure\.com/,
);
if (cognitiveservicesMatch) {
return cognitiveservicesMatch[1];
}
return null;
}
/**
* Initializes the appropriate chat client based on the provider specified in settings.
* @param settings - Configuration settings for the chat API.
* @returns An instance of ChatOpenAI, ChatOllama, or null if initialization fails.
* @returns An instance of ChatOpenAI, ChatOllama, AzureChatOpenAI, or null if initialization fails.
*/
private initializeChatClient(
settings: InlineAISettings,
): ChatOpenAI | ChatOllama | ChatGoogleGenerativeAI | null {
):
| ChatOpenAI
| ChatOllama
| ChatGoogleGenerativeAI
| AzureChatOpenAI
| null {
try {
if (settings.messageHistory) {
this.messageHistory = new MessageQueue<HistoryMessage>(
@ -83,6 +119,33 @@ export class ChatApiManager {
model: settings.model,
apiKey: settings.apiKey,
});
case "azure":
if (!settings.apiKey || !settings.azureEndpoint) {
new Notice(
"⚠️ API key and Azure endpoint are required for Azure provider.",
);
return null;
}
// Extract instance name from the endpoint URL
const instanceName = this.extractAzureInstanceName(
settings.azureEndpoint,
);
if (!instanceName) {
new Notice(
"⚠️ Invalid Azure endpoint format. Expected: https://your-resource.openai.azure.com",
);
return null;
}
return new AzureChatOpenAI({
azureOpenAIApiKey: settings.apiKey,
azureOpenAIApiInstanceName: instanceName,
azureOpenAIApiDeploymentName: settings.model,
azureOpenAIApiVersion:
settings.azureApiVersion || "2024-02-15-preview",
temperature: 0,
});
case "custom":
if (!settings.apiKey || !settings.customURL) {
new Notice(
@ -134,7 +197,10 @@ export class ChatApiManager {
];
try {
const aiMessage: AIMessage = await this.chatClient.invoke(messages);
const aiMessage = await this.chatClient.invoke(messages);
if (typeof aiMessage === "string") {
return aiMessage;
}
return aiMessage.content.toString();
} catch (error: any) {
console.error("Error calling the chat model:", error);

View file

@ -5,10 +5,12 @@ import { SlashCommand } from "./modules/commands/source";
// Interface for the settings
export interface InlineAISettings {
provider: "openai" | "ollama" | "custom" | "gemini";
provider: "openai" | "ollama" | "custom" | "gemini" | "azure";
model: string;
apiKey?: string;
customURL?: string;
azureEndpoint?: string;
azureApiVersion?: string;
selectionPrompt: string;
cursorPrompt: string;
customCommands: SlashCommand[];
@ -22,6 +24,8 @@ export const DEFAULT_SETTINGS: InlineAISettings = {
model: "llama3.2",
apiKey: "",
customURL: "",
azureEndpoint: "",
azureApiVersion: "2024-02-15-preview",
selectionPrompt: selectionPrompt,
cursorPrompt: cursorPrompt,
customCommands: [],
@ -51,12 +55,13 @@ export class InlineAISettingsTab extends PluginSettingTab {
new Setting(containerEl)
.setName("Provider")
.setDesc(
"Choose between OpenAI, Ollama, or a custom OpenAI-compatible endpoint.",
"Choose between OpenAI, Ollama, Azure OpenAI, Gemini, or a custom OpenAI-compatible endpoint.",
)
.addDropdown((dropdown) =>
dropdown
.addOption("openai", "OpenAI")
.addOption("ollama", "Ollama")
.addOption("azure", "Azure OpenAI")
.addOption("gemini", "Gemini")
.addOption("custom", "Custom/OpenAI-compatible")
.setValue(this.plugin.settings.provider)
@ -64,6 +69,7 @@ export class InlineAISettingsTab extends PluginSettingTab {
this.plugin.settings.provider = value as
| "openai"
| "ollama"
| "azure"
| "custom"
| "gemini";
await this.saveSettings();
@ -88,7 +94,8 @@ export class InlineAISettingsTab extends PluginSettingTab {
if (
this.plugin.settings.provider === "openai" ||
this.plugin.settings.provider === "custom" ||
this.plugin.settings.provider === "gemini"
this.plugin.settings.provider === "gemini" ||
this.plugin.settings.provider === "azure"
) {
new Setting(containerEl)
.setName("API key")
@ -120,6 +127,46 @@ export class InlineAISettingsTab extends PluginSettingTab {
});
}
// Azure-specific settings
if (this.plugin.settings.provider === "azure") {
// Azure endpoint setting
new Setting(containerEl)
.setName("Azure endpoint")
.setDesc(
"Enter your Azure OpenAI endpoint URL (e.g. https://your-resource.openai.azure.com).",
)
.addText((text) => {
text.setPlaceholder(
"https://your-resource.openai.azure.com",
)
.setValue(this.plugin.settings.azureEndpoint || "")
.inputEl.addEventListener("blur", async () => {
this.plugin.settings.azureEndpoint = text
.getValue()
.trim();
await this.saveSettings();
});
});
// Azure API version setting
new Setting(containerEl)
.setName("Azure API version")
.setDesc("Enter the Azure OpenAI API version to use.")
.addText((text) => {
text.setPlaceholder("2024-02-15-preview")
.setValue(
this.plugin.settings.azureApiVersion ||
"2024-02-15-preview",
)
.inputEl.addEventListener("blur", async () => {
this.plugin.settings.azureApiVersion = text
.getValue()
.trim();
await this.saveSettings();
});
});
}
// Advanced Section
containerEl.createEl("h3", { text: "Advanced" });
// Selection Prompt setting