mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-10-13 04:59:51 +08:00
refactor(providers): the quota functions and the model list fallback read the record
Step 4c of the provider refactor plan. usesServerCredentials asks resolveEndpoint whose key the call uses, so the 'mirrors each branch of getAIModel' comment and its switch are gone. usesServerEndpoint reads the credential kind (none, optionalKey) and the sdk (bedrock) instead of three provider names. listFallbackUrl reads the credential kind and publicDefaultUrl. Before this commit the differential test agreed with the pre-refactor copies on all 30720 and 288 input classes; the copies are deleted now, and the behavior snapshot keeps both functions' answers for every recorded scenario.
This commit is contained in:
+16
-30
@@ -1203,10 +1203,11 @@ export function getServerProvider(): ProviderName | null {
|
||||
|
||||
/**
|
||||
* Whether a call made with the caller's own settings runs on an endpoint of
|
||||
* the deployment: its EdgeOne function, the server's keyless Ollama, or an
|
||||
* the deployment: the site's own function (EdgeOne, credential "none"), the
|
||||
* server's keyless local provider (Ollama, credential "optionalKey"), or an
|
||||
* address on the server's network (which ignores a dummy key header).
|
||||
* Bedrock and EdgeOne never use a client base URL. Never in the desktop
|
||||
* app, where every endpoint is the user's. clientBaseUrl: normalized.
|
||||
* Bedrock never uses a client base URL. Never in the desktop app, where
|
||||
* every endpoint is the user's. clientBaseUrl: normalized.
|
||||
*/
|
||||
export async function usesServerEndpoint(
|
||||
provider: ProviderName | null | undefined,
|
||||
@@ -1214,10 +1215,13 @@ export async function usesServerEndpoint(
|
||||
apiKey: string | null | undefined,
|
||||
): Promise<boolean> {
|
||||
if (process.env.NEXT_AI_DRAWIO_DESKTOP === "1") return false
|
||||
if (provider === "edgeone") return true
|
||||
if (provider === "ollama" && !clientBaseUrl && !apiKey) return true
|
||||
const def = provider ? providerDef(provider) : undefined
|
||||
if (def?.credential === "none") return true
|
||||
if (def?.credential === "optionalKey" && !clientBaseUrl && !apiKey) {
|
||||
return true
|
||||
}
|
||||
return (
|
||||
provider !== "bedrock" &&
|
||||
def?.sdk !== "bedrock" &&
|
||||
!!clientBaseUrl &&
|
||||
(await isPrivateUrl(clientBaseUrl))
|
||||
)
|
||||
@@ -1225,8 +1229,8 @@ export async function usesServerEndpoint(
|
||||
|
||||
/**
|
||||
* Whether the call is paid for by the server's own credentials (env keys or
|
||||
* IAM role) rather than credentials sent with the request. Mirrors which key
|
||||
* each branch of getAIModel ends up using.
|
||||
* IAM role) rather than credentials sent with the request: the key
|
||||
* resolveEndpoint picks for getAIModel is the server's.
|
||||
*/
|
||||
export function usesServerCredentials(
|
||||
provider: ProviderName,
|
||||
@@ -1236,28 +1240,10 @@ export function usesServerCredentials(
|
||||
if (process.env.NEXT_AI_DRAWIO_DESKTOP === "1") return false
|
||||
// Cleaned like getAIModel does: "/" means no base URL
|
||||
const baseUrl = normalizeBaseUrl(overrides?.baseUrl ?? "")
|
||||
switch (provider) {
|
||||
case "bedrock":
|
||||
return !(
|
||||
overrides?.apiKey ||
|
||||
(overrides?.awsAccessKeyId && overrides?.awsSecretAccessKey)
|
||||
)
|
||||
case "vertexai":
|
||||
return !overrides?.vertexApiKey
|
||||
case "edgeone":
|
||||
// The platform's own endpoint, no key involved
|
||||
return false
|
||||
case "ollama":
|
||||
// Only a server key costs money; a keyless local server or the
|
||||
// client's own server does not
|
||||
return (
|
||||
!baseUrl &&
|
||||
!overrides?.apiKey &&
|
||||
!!(overrides?.apiKeyEnv || process.env.OLLAMA_API_KEY)
|
||||
)
|
||||
default:
|
||||
return !overrides?.apiKey
|
||||
}
|
||||
return (
|
||||
resolveEndpoint(provider, { ...overrides, baseUrl }).keySource ===
|
||||
"server"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+10
-18
@@ -1,12 +1,8 @@
|
||||
import { createGateway } from "ai"
|
||||
import { getModelInfo } from "@/lib/model-catalog"
|
||||
import { PROVIDERS, type ProviderName, publicDefaultUrl } from "@/lib/providers"
|
||||
import { readLimitedBody } from "@/lib/read-limited-body"
|
||||
import {
|
||||
normalizeBaseUrl,
|
||||
ollamaApiUrl,
|
||||
PROVIDER_INFO,
|
||||
type ProviderName,
|
||||
} from "@/lib/types/model-config"
|
||||
import { normalizeBaseUrl, ollamaApiUrl } from "@/lib/types/model-config"
|
||||
|
||||
/** A model a provider offers. tools is false when it cannot call tools. */
|
||||
export interface ListedModel {
|
||||
@@ -17,10 +13,7 @@ export interface ListedModel {
|
||||
const AIHUBMIX_MODELS_ENDPOINT = "https://aihubmix.com/api/v1/models"
|
||||
|
||||
export function canListModels(provider: ProviderName): boolean {
|
||||
return (
|
||||
Object.hasOwn(PROVIDER_INFO, provider) &&
|
||||
!!PROVIDER_INFO[provider].modelList
|
||||
)
|
||||
return Object.hasOwn(PROVIDERS, provider) && !!PROVIDERS[provider].modelList
|
||||
}
|
||||
|
||||
// Models in OpenAI-style lists that are not for chat
|
||||
@@ -131,16 +124,15 @@ async function getJson(
|
||||
|
||||
/**
|
||||
* Where to list from without the user's base URL: where chat goes then. For
|
||||
* Ollama without a key that is the server's Ollama, else the SDK's local
|
||||
* default; a local default in PROVIDER_INFO (SGLang's) only fills the
|
||||
* settings form.
|
||||
* Ollama (an optional key) without a key that is the server's Ollama, else
|
||||
* the SDK's local default; a local default in the record (SGLang's) only
|
||||
* fills the settings form.
|
||||
*/
|
||||
function listFallbackUrl(provider: ProviderName, apiKey?: string): string {
|
||||
if (provider === "ollama" && !apiKey) {
|
||||
if (PROVIDERS[provider].credential === "optionalKey" && !apiKey) {
|
||||
return process.env.OLLAMA_BASE_URL || "http://127.0.0.1:11434/api"
|
||||
}
|
||||
const url = PROVIDER_INFO[provider].defaultBaseUrl
|
||||
return url?.startsWith("https://") ? url : ""
|
||||
return publicDefaultUrl(provider) ?? ""
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,7 +159,7 @@ export async function listProviderModels(
|
||||
baseUrl &&
|
||||
!/^https:\/\/aihubmix\.com(\/v1)?$/.test(base)
|
||||
? "openai"
|
||||
: PROVIDER_INFO[provider].modelList
|
||||
: PROVIDERS[provider].modelList
|
||||
|
||||
switch (style) {
|
||||
case "anthropic": {
|
||||
@@ -242,7 +234,7 @@ export async function listProviderModels(
|
||||
default: {
|
||||
if (!base) {
|
||||
throw new ModelListError(
|
||||
`${PROVIDER_INFO[provider].label} needs a base URL to list its models.`,
|
||||
`${PROVIDERS[provider].label} needs a base URL to list its models.`,
|
||||
)
|
||||
}
|
||||
const data = await getJson(`${base}/models`, bearer, fetchFn)
|
||||
|
||||
Reference in New Issue
Block a user