Migrate Azure OpenAI Integration To v1 API | Enable Streaming for Reasoning Models in Azure OpenAI Basic Inference Provider (#4744)
* Refactor Azure OpenAI integration to use OpenAI SDK and the v1 API | Enable streaming for Azure Open AI basic inference provider * Add info tooltip to inform user about 'Model Type' form field * Add 'model_type_tooltip' key to multiple language translations * Validate AZURE_OPENAI_ENDPOINT in provider construction * remove unused import, update error handler, rescope URL utils --------- Co-authored-by: Timothy Carambat <rambat1010@gmail.com>pull/4761/head^2
parent
692fa755ee
commit
a7da757c84
|
|
@ -1,4 +1,6 @@
|
|||
import { Info } from "@phosphor-icons/react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Tooltip } from "react-tooltip";
|
||||
|
||||
export default function AzureAiOptions({ settings }) {
|
||||
const { t } = useTranslation();
|
||||
|
|
@ -79,9 +81,33 @@ export default function AzureAiOptions({ settings }) {
|
|||
</div>
|
||||
|
||||
<div className="flex flex-col w-60">
|
||||
<label className="text-white text-sm font-semibold block mb-3">
|
||||
{t("llm.providers.azure_openai.model_type")}
|
||||
</label>
|
||||
<div className="flex items-center gap-1 mb-3">
|
||||
<label className="text-white text-sm font-semibold block">
|
||||
{t("llm.providers.azure_openai.model_type")}
|
||||
</label>
|
||||
<Tooltip
|
||||
id="azure-openai-model-type"
|
||||
place="top"
|
||||
delayShow={300}
|
||||
className="tooltip !text-xs !opacity-100"
|
||||
style={{
|
||||
maxWidth: "250px",
|
||||
whiteSpace: "normal",
|
||||
wordWrap: "break-word",
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
type="button"
|
||||
className="text-theme-text-secondary cursor-pointer hover:bg-theme-bg-primary flex items-center justify-center rounded-full"
|
||||
data-tooltip-id="azure-openai-model-type"
|
||||
data-tooltip-place="top"
|
||||
data-tooltip-content={t(
|
||||
"llm.providers.azure_openai.model_type_tooltip"
|
||||
)}
|
||||
>
|
||||
<Info size={18} className="text-theme-text-secondary" />
|
||||
</div>
|
||||
</div>
|
||||
<select
|
||||
name="AzureOpenAiModelType"
|
||||
defaultValue={settings?.AzureOpenAiModelType || "default"}
|
||||
|
|
|
|||
|
|
@ -363,6 +363,7 @@ const TRANSLATIONS = {
|
|||
model_type: null,
|
||||
default: null,
|
||||
reasoning: null,
|
||||
model_type_tooltip: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -365,6 +365,7 @@ const TRANSLATIONS = {
|
|||
model_type: null,
|
||||
default: null,
|
||||
reasoning: null,
|
||||
model_type_tooltip: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -557,6 +557,7 @@ const TRANSLATIONS = {
|
|||
model_type: "Art des Modells",
|
||||
default: "Standard",
|
||||
reasoning: "Reasoning",
|
||||
model_type_tooltip: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -583,6 +583,8 @@ const TRANSLATIONS = {
|
|||
chat_deployment_name: "Chat Deployment Name",
|
||||
chat_model_token_limit: "Chat Model Token Limit",
|
||||
model_type: "Model Type",
|
||||
model_type_tooltip:
|
||||
"If your deployment uses a reasoning model (o1, o1-mini, o3-mini, etc.), set this to “Reasoning”. Otherwise, your chat requests may fail.",
|
||||
default: "Default",
|
||||
reasoning: "Reasoning",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -568,6 +568,7 @@ const TRANSLATIONS = {
|
|||
model_type: "Tipo de modelo",
|
||||
default: "Predeterminado",
|
||||
reasoning: "Razonamiento",
|
||||
model_type_tooltip: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -534,6 +534,7 @@ const TRANSLATIONS = {
|
|||
model_type: "Mudeli tüüp",
|
||||
default: "Vaikimisi",
|
||||
reasoning: "Põhjendus",
|
||||
model_type_tooltip: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -355,6 +355,7 @@ const TRANSLATIONS = {
|
|||
model_type: null,
|
||||
default: null,
|
||||
reasoning: null,
|
||||
model_type_tooltip: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -371,6 +371,7 @@ const TRANSLATIONS = {
|
|||
model_type: "Type de modèle",
|
||||
default: "Par défaut",
|
||||
reasoning: "Raisonnement",
|
||||
model_type_tooltip: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -541,6 +541,7 @@ const TRANSLATIONS = {
|
|||
model_type: "סוג מודל",
|
||||
default: "ברירת מחדל",
|
||||
reasoning: "היגיון",
|
||||
model_type_tooltip: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -360,6 +360,7 @@ const TRANSLATIONS = {
|
|||
model_type: null,
|
||||
default: null,
|
||||
reasoning: null,
|
||||
model_type_tooltip: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -363,6 +363,7 @@ const TRANSLATIONS = {
|
|||
model_type: null,
|
||||
default: null,
|
||||
reasoning: null,
|
||||
model_type_tooltip: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -544,6 +544,7 @@ const TRANSLATIONS = {
|
|||
model_type: "모델 유형",
|
||||
default: "기본값",
|
||||
reasoning: "추론",
|
||||
model_type_tooltip: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -552,6 +552,7 @@ const TRANSLATIONS = {
|
|||
model_type: null,
|
||||
default: null,
|
||||
reasoning: null,
|
||||
model_type_tooltip: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -357,6 +357,7 @@ const TRANSLATIONS = {
|
|||
model_type: null,
|
||||
default: null,
|
||||
reasoning: null,
|
||||
model_type_tooltip: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -557,6 +557,7 @@ const TRANSLATIONS = {
|
|||
model_type: "Typ modelu",
|
||||
default: "Domyślne",
|
||||
reasoning: "Uzasadnienie",
|
||||
model_type_tooltip: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -542,6 +542,7 @@ const TRANSLATIONS = {
|
|||
model_type: "Tipo do Modelo",
|
||||
default: "Padrão",
|
||||
reasoning: "Raciocínio",
|
||||
model_type_tooltip: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1011,6 +1011,7 @@ const TRANSLATIONS = {
|
|||
model_type: "Tip model",
|
||||
default: "Implicit",
|
||||
reasoning: "Raționament",
|
||||
model_type_tooltip: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -366,6 +366,7 @@ const TRANSLATIONS = {
|
|||
model_type: null,
|
||||
default: null,
|
||||
reasoning: null,
|
||||
model_type_tooltip: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -357,6 +357,7 @@ const TRANSLATIONS = {
|
|||
model_type: null,
|
||||
default: null,
|
||||
reasoning: null,
|
||||
model_type_tooltip: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -356,6 +356,7 @@ const TRANSLATIONS = {
|
|||
model_type: null,
|
||||
default: null,
|
||||
reasoning: null,
|
||||
model_type_tooltip: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -520,6 +520,7 @@ const TRANSLATIONS = {
|
|||
model_type: "模型类型",
|
||||
default: "预设",
|
||||
reasoning: "推理",
|
||||
model_type_tooltip: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -348,6 +348,7 @@ const TRANSLATIONS = {
|
|||
model_type: "模型類型",
|
||||
default: "預設",
|
||||
reasoning: "推理",
|
||||
model_type_tooltip: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -9,19 +9,23 @@ const {
|
|||
|
||||
class AzureOpenAiLLM {
|
||||
constructor(embedder = null, modelPreference = null) {
|
||||
const { AzureOpenAI } = require("openai");
|
||||
const { OpenAI } = require("openai");
|
||||
if (!process.env.AZURE_OPENAI_ENDPOINT)
|
||||
throw new Error("No Azure API endpoint was set.");
|
||||
if (!process.env.AZURE_OPENAI_KEY)
|
||||
throw new Error("No Azure API key was set.");
|
||||
|
||||
this.apiVersion = "2024-12-01-preview";
|
||||
this.openai = new AzureOpenAI({
|
||||
this.openai = new OpenAI({
|
||||
apiKey: process.env.AZURE_OPENAI_KEY,
|
||||
apiVersion: this.apiVersion,
|
||||
endpoint: process.env.AZURE_OPENAI_ENDPOINT,
|
||||
baseURL: AzureOpenAiLLM.formatBaseUrl(process.env.AZURE_OPENAI_ENDPOINT),
|
||||
});
|
||||
this.model = modelPreference ?? process.env.OPEN_MODEL_PREF;
|
||||
/*
|
||||
Note: Azure OpenAI deployments do not expose model metadata that would allow us to
|
||||
programmatically detect whether the deployment uses a reasoning model (o1, o1-mini, o3-mini, etc.).
|
||||
As a result, we rely on the user to explicitly set AZURE_OPENAI_MODEL_TYPE="reasoning"
|
||||
when using reasoning models, as incorrect configuration might result in chat errors.
|
||||
*/
|
||||
this.isOTypeModel =
|
||||
process.env.AZURE_OPENAI_MODEL_TYPE === "reasoning" || false;
|
||||
this.limits = {
|
||||
|
|
@ -37,6 +41,26 @@ class AzureOpenAiLLM {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the Azure OpenAI endpoint URL to the correct format.
|
||||
* @param {string} azureOpenAiEndpoint - The Azure OpenAI endpoint URL.
|
||||
* @returns {string} The formatted URL.
|
||||
*/
|
||||
static formatBaseUrl(azureOpenAiEndpoint) {
|
||||
try {
|
||||
const url = new URL(azureOpenAiEndpoint);
|
||||
url.pathname = "/openai/v1";
|
||||
url.protocol = "https";
|
||||
url.search = "";
|
||||
url.hash = "";
|
||||
return url.href;
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`"${azureOpenAiEndpoint}" is not a valid URL. Check your settings for the Azure OpenAI provider and set a valid endpoint URL.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#log(text, ...args) {
|
||||
console.log(`\x1b[32m[AzureOpenAi]\x1b[0m ${text}`, ...args);
|
||||
}
|
||||
|
|
@ -54,13 +78,6 @@ class AzureOpenAiLLM {
|
|||
}
|
||||
|
||||
streamingEnabled() {
|
||||
// Streaming of reasoning models is not supported
|
||||
if (this.isOTypeModel) {
|
||||
this.#log(
|
||||
"Streaming will be disabled. AZURE_OPENAI_MODEL_TYPE is set to 'reasoning'."
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return "streamGetChatCompletion" in this;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
const { AzureOpenAI } = require("openai");
|
||||
const { OpenAI } = require("openai");
|
||||
const { AzureOpenAiLLM } = require("../../../AiProviders/azureOpenAi");
|
||||
const Provider = require("./ai-provider.js");
|
||||
const { RetryError } = require("../error.js");
|
||||
|
||||
|
|
@ -9,10 +10,9 @@ class AzureOpenAiProvider extends Provider {
|
|||
model;
|
||||
|
||||
constructor(config = { model: null }) {
|
||||
const client = new AzureOpenAI({
|
||||
const client = new OpenAI({
|
||||
apiKey: process.env.AZURE_OPENAI_KEY,
|
||||
endpoint: process.env.AZURE_OPENAI_ENDPOINT,
|
||||
apiVersion: "2024-12-01-preview",
|
||||
baseURL: AzureOpenAiLLM.formatBaseUrl(process.env.AZURE_OPENAI_ENDPOINT),
|
||||
});
|
||||
super(client);
|
||||
this.model = config.model ?? process.env.OPEN_MODEL_PREF;
|
||||
|
|
@ -84,12 +84,12 @@ class AzureOpenAiProvider extends Provider {
|
|||
} catch (error) {
|
||||
// If invalid Auth error we need to abort because no amount of waiting
|
||||
// will make auth better.
|
||||
if (error instanceof AzureOpenAI.AuthenticationError) throw error;
|
||||
if (error instanceof OpenAI.AuthenticationError) throw error;
|
||||
|
||||
if (
|
||||
error instanceof AzureOpenAI.RateLimitError ||
|
||||
error instanceof AzureOpenAI.InternalServerError ||
|
||||
error instanceof AzureOpenAI.APIError // Also will catch AuthenticationError!!!
|
||||
error instanceof OpenAI.RateLimitError ||
|
||||
error instanceof OpenAI.InternalServerError ||
|
||||
error instanceof OpenAI.APIError // Also will catch AuthenticationError!!!
|
||||
) {
|
||||
throw new RetryError(error.message);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue