From 65a3b7d2aade9aff893e8d85d2f1742fdfeb662e Mon Sep 17 00:00:00 2001 From: octo-patch <266937838+octo-patch@users.noreply.github.com> Date: Wed, 29 Jul 2026 15:00:59 +0000 Subject: [PATCH 1/2] feat(web): add configurable MiniMax provider for Data Explorer Introduce a MiniMax provider layer for the Data Explorer route with the global and China OpenAI-compatible endpoints and the current MiniMax-M3 and MiniMax-M2.7 models, selected via MINIMAX_* environment variables and falling back to the existing default provider when unset. --- apps/web/app/api/explorer/ask/route.ts | 39 +++++++----- apps/web/lib/explorer/provider.ts | 83 ++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 15 deletions(-) create mode 100644 apps/web/lib/explorer/provider.ts diff --git a/apps/web/app/api/explorer/ask/route.ts b/apps/web/app/api/explorer/ask/route.ts index 64de2cf4b3..b998b91adc 100644 --- a/apps/web/app/api/explorer/ask/route.ts +++ b/apps/web/app/api/explorer/ask/route.ts @@ -6,6 +6,7 @@ import { normalizeExplorerChart, inferExplorerFields } from "@/lib/explorer/char import { explorerChartKinds, type ExplorerAnswer } from "@/lib/explorer/contracts"; import { buildExplorerPlanningPrompt } from "@/lib/explorer/prompt"; import { executeExplorerRows, explainExplorerRows } from "@/lib/explorer/query"; +import { createMinimaxModel, readMinimaxConfig } from "@/lib/explorer/provider"; export const runtime = "edge"; export const maxDuration = 30; @@ -53,6 +54,24 @@ const openai = createOpenAI({ apiKey: openaiApiKey, }); +const minimaxConfig = readMinimaxConfig(); + +function resolveExplorerModel() { + if (minimaxConfig) { + return createMinimaxModel(minimaxConfig); + } + return openai("gpt-5.2"); +} + +const explorerProviderOptions = minimaxConfig + ? undefined + : { + openai: { + reasoningEffort: "low" as const, + textVerbosity: "low" as const, + }, + }; + export async function POST(request: Request) { if (isExplorerUnderMaintenance()) { return Response.json(maintenanceResponse, { @@ -90,7 +109,7 @@ export async function POST(request: Request) { try { const body = requestSchema.parse(await request.json()); - if (!openaiApiKey) { + if (!minimaxConfig && !openaiApiKey) { return Response.json( { error: "Missing OPENAI_API_TOKEN (or OPENAI_API_KEY) for Data Explorer." }, { status: 500, headers: { "Cache-Control": "no-store" } }, @@ -125,18 +144,13 @@ export async function POST(request: Request) { async function generateExplorerPlan(question: string) { const { object: plan } = await generateObject({ - model: openai("gpt-5.2"), + model: resolveExplorerModel(), schemaName: "ExplorerPlan", schemaDescription: "A safe readonly SQL plan and chart plan for OSSInsight Data Explorer.", schema: planSchema, prompt: buildExplorerPlanningPrompt(question), - providerOptions: { - openai: { - reasoningEffort: "low", - textVerbosity: "low", - }, - }, + providerOptions: explorerProviderOptions, }); return plan; @@ -201,18 +215,13 @@ function streamExplorerAnswer(question: string, startedAt: number) { send({ type: "phase", phase: "generating" }); const result = streamObject({ - model: openai("gpt-5.2"), + model: resolveExplorerModel(), schemaName: "ExplorerPlan", schemaDescription: "A safe readonly SQL plan and chart plan for OSSInsight Data Explorer.", schema: planSchema, prompt: buildExplorerPlanningPrompt(question), - providerOptions: { - openai: { - reasoningEffort: "low", - textVerbosity: "low", - }, - }, + providerOptions: explorerProviderOptions, }); let lastPreview = ""; diff --git a/apps/web/lib/explorer/provider.ts b/apps/web/lib/explorer/provider.ts new file mode 100644 index 0000000000..43a4295f9d --- /dev/null +++ b/apps/web/lib/explorer/provider.ts @@ -0,0 +1,83 @@ +import { createOpenAI } from "@ai-sdk/openai"; + +// Regional base URLs for the MiniMax OpenAI-compatible chat API. The Data +// Explorer can be pointed at either the global endpoint or the mainland-China +// endpoint through the MINIMAX_API_REGION environment variable. +export const MINIMAX_REGION_BASE_URLS = { + global_en: "https://api.minimax.io/v1", + cn_zh: "https://api.minimaxi.com/v1", +} as const; + +export type MinimaxRegion = keyof typeof MINIMAX_REGION_BASE_URLS; + +export const MINIMAX_DEFAULT_REGION: MinimaxRegion = "global_en"; + +// Supported MiniMax text models and their context windows (in tokens). +export const MINIMAX_MODELS = { + "MiniMax-M3": { contextWindow: 1_000_000 }, + "MiniMax-M2.7": { contextWindow: 204_800 }, +} as const; + +export type MinimaxModelId = keyof typeof MINIMAX_MODELS; + +export const MINIMAX_DEFAULT_MODEL: MinimaxModelId = "MiniMax-M3"; + +export function resolveMinimaxRegion( + value: string | undefined, +): MinimaxRegion { + return value && value in MINIMAX_REGION_BASE_URLS + ? (value as MinimaxRegion) + : MINIMAX_DEFAULT_REGION; +} + +export function resolveMinimaxModel( + value: string | undefined, +): MinimaxModelId { + return value && value in MINIMAX_MODELS + ? (value as MinimaxModelId) + : MINIMAX_DEFAULT_MODEL; +} + +export function resolveMinimaxBaseUrl( + region: MinimaxRegion, + override: string | undefined, +): string { + const url = override?.trim() || MINIMAX_REGION_BASE_URLS[region]; + return url.replace(/\/+$/, ""); +} + +export interface MinimaxConfig { + apiKey: string; + region: MinimaxRegion; + baseURL: string; + modelId: MinimaxModelId; +} + +// Read the MiniMax configuration from the environment. Returns null when no +// MiniMax API key is configured, so callers can keep their existing default +// provider behaviour untouched. +export function readMinimaxConfig( + env: Record = process.env, +): MinimaxConfig | null { + const apiKey = env.MINIMAX_API_KEY?.trim(); + if (!apiKey) { + return null; + } + + const region = resolveMinimaxRegion(env.MINIMAX_API_REGION); + return { + apiKey, + region, + baseURL: resolveMinimaxBaseUrl(region, env.MINIMAX_API_BASE_URL), + modelId: resolveMinimaxModel(env.MINIMAX_MODEL), + }; +} + +// Build a language model bound to the MiniMax OpenAI-compatible endpoint. +export function createMinimaxModel(config: MinimaxConfig) { + const provider = createOpenAI({ + apiKey: config.apiKey, + baseURL: config.baseURL, + }); + return provider(config.modelId); +} From 4f45668155c819b773c8d2e28ef6e1af5121017e Mon Sep 17 00:00:00 2001 From: octo-patch <266937838+octo-patch@users.noreply.github.com> Date: Tue, 11 Aug 2026 18:23:30 +0800 Subject: [PATCH 2/2] fix(web): use chat completions for MiniMax --- apps/web/lib/explorer/provider.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/web/lib/explorer/provider.ts b/apps/web/lib/explorer/provider.ts index 43a4295f9d..a5a6dc5d21 100644 --- a/apps/web/lib/explorer/provider.ts +++ b/apps/web/lib/explorer/provider.ts @@ -76,8 +76,9 @@ export function readMinimaxConfig( // Build a language model bound to the MiniMax OpenAI-compatible endpoint. export function createMinimaxModel(config: MinimaxConfig) { const provider = createOpenAI({ + name: "minimax", apiKey: config.apiKey, baseURL: config.baseURL, }); - return provider(config.modelId); + return provider.chat(config.modelId); }