mirror of
https://github.com/fosrl/pangolin.git
synced 2026-08-06 04:31:22 +02:00
add provider specific auth modes
This commit is contained in:
+5
-1
@@ -1652,7 +1652,7 @@
|
||||
"aiProviderNetworkSettings": "Network Settings",
|
||||
"aiProviderNetworkSettingsDescription": "Choose how traffic reaches this provider",
|
||||
"aiProviderAuthSettings": "Authentication",
|
||||
"aiProviderAuthSettingsDescription": "Credentials used for both upstream URL and Pangolin target routing",
|
||||
"aiProviderAuthSettingsDescription": "Configure how this provider authenticates requests to its upstream URL",
|
||||
"aiProviderType": "Provider Type",
|
||||
"aiProviderTypeSearch": "Search providers...",
|
||||
"aiProviderTypeNotFound": "No provider type found",
|
||||
@@ -1683,6 +1683,10 @@
|
||||
"aiProviderApiKeyLastChars": "API Key",
|
||||
"aiProviderAuthType": "Auth Type",
|
||||
"aiProviderAuthTypeBearer": "Bearer",
|
||||
"aiProviderAuthTypeXApiKey": "x-api-key",
|
||||
"aiProviderAuthTypeXGoogApiKey": "x-goog-api-key",
|
||||
"aiProviderAuthTypeHec": "Splunk HEC",
|
||||
"aiProviderAuthTypeCfAigAuthorization": "Cloudflare AI Gateway",
|
||||
"aiProviderAuthTypeDescription": "How the upstream API authenticates requests",
|
||||
"aiProviderRoutingMode": "Routing Mode",
|
||||
"aiProviderRoutingModeDescription": "Send traffic to an upstream URL or to HTTP targets on your sites",
|
||||
|
||||
@@ -1644,7 +1644,15 @@ export const aiProviders = pgTable("aiProviders", {
|
||||
upstreamUrl: text("upstreamUrl"),
|
||||
apiKey: text("apiKey"),
|
||||
apiKeyLastChars: varchar("apiKeyLastChars"),
|
||||
authType: varchar("authType").$type<"bearer">(),
|
||||
authType: varchar("authType")
|
||||
.$type<
|
||||
| "bearer"
|
||||
| "x-api-key"
|
||||
| "x-goog-api-key"
|
||||
| "hec"
|
||||
| "cf-aig-authorization"
|
||||
>()
|
||||
.notNull(),
|
||||
routingMode: varchar("routingMode")
|
||||
.$type<"url" | "target">()
|
||||
.notNull()
|
||||
|
||||
@@ -1626,7 +1626,15 @@ export const aiProviders = sqliteTable("aiProviders", {
|
||||
upstreamUrl: text("upstreamUrl"),
|
||||
apiKey: text("apiKey"),
|
||||
apiKeyLastChars: text("apiKeyLastChars"),
|
||||
authType: text("authType").$type<"bearer">(),
|
||||
authType: text("authType")
|
||||
.$type<
|
||||
| "bearer"
|
||||
| "x-api-key"
|
||||
| "x-goog-api-key"
|
||||
| "hec"
|
||||
| "cf-aig-authorization"
|
||||
>()
|
||||
.notNull(),
|
||||
routingMode: text("routingMode")
|
||||
.$type<"url" | "target">()
|
||||
.notNull()
|
||||
|
||||
@@ -9,7 +9,15 @@ export type AiProviderType =
|
||||
| "vercelAiGateway"
|
||||
| "custom";
|
||||
|
||||
export type AiProviderAuthType = "bearer";
|
||||
export const AI_PROVIDER_AUTH_TYPES = [
|
||||
"bearer",
|
||||
"x-api-key",
|
||||
"x-goog-api-key",
|
||||
"hec",
|
||||
"cf-aig-authorization"
|
||||
] as const;
|
||||
|
||||
export type AiProviderAuthType = (typeof AI_PROVIDER_AUTH_TYPES)[number];
|
||||
export type AiBudgetUnit = "usd" | "tokens";
|
||||
export type AiProviderRoutingMode = "url" | "target";
|
||||
|
||||
@@ -28,11 +36,11 @@ export const AI_PROVIDER_DEFAULTS: Record<
|
||||
},
|
||||
anthropic: {
|
||||
upstreamUrl: "https://api.anthropic.com",
|
||||
authType: "bearer"
|
||||
authType: "x-api-key"
|
||||
},
|
||||
googleGemini: {
|
||||
upstreamUrl: "https://generativelanguage.googleapis.com/v1beta/openai/",
|
||||
authType: "bearer"
|
||||
authType: "x-goog-api-key"
|
||||
},
|
||||
vertexAi: {
|
||||
upstreamUrl: null,
|
||||
@@ -56,6 +64,13 @@ export const AI_PROVIDER_DEFAULTS: Record<
|
||||
}
|
||||
};
|
||||
|
||||
const CONFLICTING_AUTH_HEADERS = [
|
||||
"authorization",
|
||||
"x-api-key",
|
||||
"x-goog-api-key",
|
||||
"cf-aig-authorization"
|
||||
] as const;
|
||||
|
||||
export function providerRequiresUpstreamUrl(
|
||||
type: AiProviderType,
|
||||
routingMode: AiProviderRoutingMode = "url"
|
||||
@@ -69,17 +84,18 @@ export function providerRequiresUpstreamUrl(
|
||||
return AI_PROVIDER_DEFAULTS[type].upstreamUrl === null;
|
||||
}
|
||||
|
||||
export function resolveAiProviderConfig(input: {
|
||||
export function resolveAiProviderCreateFields(input: {
|
||||
type: AiProviderType;
|
||||
upstreamUrl: string | null;
|
||||
authType: AiProviderAuthType | null;
|
||||
upstreamUrl?: string | null;
|
||||
authType?: AiProviderAuthType | null;
|
||||
routingMode?: AiProviderRoutingMode | null;
|
||||
}): {
|
||||
upstreamUrl: string | null;
|
||||
authType: AiProviderAuthType | null;
|
||||
authType: AiProviderAuthType;
|
||||
routingMode: AiProviderRoutingMode;
|
||||
} {
|
||||
const routingMode = input.routingMode ?? "url";
|
||||
const routingMode =
|
||||
input.type === "custom" ? (input.routingMode ?? "url") : "url";
|
||||
|
||||
if (routingMode === "target") {
|
||||
return {
|
||||
@@ -91,8 +107,8 @@ export function resolveAiProviderConfig(input: {
|
||||
|
||||
if (input.type === "custom") {
|
||||
return {
|
||||
upstreamUrl: input.upstreamUrl,
|
||||
authType: input.authType,
|
||||
upstreamUrl: input.upstreamUrl ?? null,
|
||||
authType: input.authType ?? "bearer",
|
||||
routingMode
|
||||
};
|
||||
}
|
||||
@@ -100,7 +116,43 @@ export function resolveAiProviderConfig(input: {
|
||||
const defaults = AI_PROVIDER_DEFAULTS[input.type];
|
||||
return {
|
||||
upstreamUrl: input.upstreamUrl ?? defaults.upstreamUrl,
|
||||
authType: input.authType ?? defaults.authType,
|
||||
authType: defaults.authType,
|
||||
routingMode
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip inbound client auth headers, then set the provider auth header
|
||||
* for the given authType.
|
||||
*/
|
||||
export function applyAiProviderAuthHeaders(
|
||||
headers: Record<string, string>,
|
||||
authType: AiProviderAuthType,
|
||||
apiKey: string
|
||||
): void {
|
||||
for (const name of CONFLICTING_AUTH_HEADERS) {
|
||||
for (const key of Object.keys(headers)) {
|
||||
if (key.toLowerCase() === name) {
|
||||
delete headers[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (authType) {
|
||||
case "bearer":
|
||||
headers["Authorization"] = `Bearer ${apiKey}`;
|
||||
break;
|
||||
case "x-api-key":
|
||||
headers["x-api-key"] = apiKey;
|
||||
break;
|
||||
case "x-goog-api-key":
|
||||
headers["x-goog-api-key"] = apiKey;
|
||||
break;
|
||||
case "hec":
|
||||
headers["Authorization"] = `Splunk ${apiKey}`;
|
||||
break;
|
||||
case "cf-aig-authorization":
|
||||
headers["cf-aig-authorization"] = `Bearer ${apiKey}`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,7 @@ import config from "@server/lib/config";
|
||||
import { decrypt } from "@server/lib/crypto";
|
||||
import {
|
||||
AiProviderAuthType,
|
||||
AiProviderRoutingMode,
|
||||
AiProviderType,
|
||||
resolveAiProviderConfig
|
||||
applyAiProviderAuthHeaders
|
||||
} from "@server/lib/aiProviderDefaults";
|
||||
import {
|
||||
SESSION_COOKIE_NAME,
|
||||
@@ -499,12 +497,8 @@ export async function chatCompletions(
|
||||
const secret = config.getRawConfig().server.secret!;
|
||||
const apiKey = decrypt(provider.apiKey, secret);
|
||||
|
||||
const { upstreamUrl, authType } = resolveAiProviderConfig({
|
||||
type: provider.type as AiProviderType,
|
||||
upstreamUrl: provider.upstreamUrl,
|
||||
authType: provider.authType as AiProviderAuthType | null,
|
||||
routingMode: provider.routingMode as AiProviderRoutingMode | null
|
||||
});
|
||||
const upstreamUrl = provider.upstreamUrl;
|
||||
const authType = provider.authType as AiProviderAuthType;
|
||||
|
||||
if (!upstreamUrl) {
|
||||
return res.status(HttpCode.INTERNAL_SERVER_ERROR).json({
|
||||
@@ -541,8 +535,7 @@ export async function chatCompletions(
|
||||
}
|
||||
headers[key] = Array.isArray(value) ? value.join(", ") : value;
|
||||
}
|
||||
// TODO: temporary hardcoded auth for testing; restore bearer from authType
|
||||
headers["x-api-key"] = apiKey;
|
||||
applyAiProviderAuthHeaders(headers, authType, apiKey);
|
||||
|
||||
// No dedicated per-request TLS agent is wired up (no extra deps for
|
||||
// this v1 gateway) - toggle the process-wide Node TLS check instead.
|
||||
|
||||
@@ -9,6 +9,7 @@ import { fromError } from "zod-validation-error";
|
||||
import { OpenAPITags, registry } from "@server/openApi";
|
||||
import { encrypt } from "@server/lib/crypto";
|
||||
import config from "@server/lib/config";
|
||||
import { resolveAiProviderCreateFields } from "@server/lib/aiProviderDefaults";
|
||||
import type { CreateOrEditAiProviderResponse } from "@server/routers/aiProvider/types";
|
||||
import { toPublicAiProvider } from "@server/routers/aiProvider/types";
|
||||
import {
|
||||
@@ -28,7 +29,7 @@ const bodySchema = z
|
||||
type: aiProviderTypeSchema,
|
||||
upstreamUrl: z.url().optional().nullable(),
|
||||
apiKey: z.string().optional(),
|
||||
authType: aiAuthTypeSchema.optional().nullable(),
|
||||
authType: aiAuthTypeSchema.optional(),
|
||||
routingMode: aiRoutingModeSchema.optional(),
|
||||
skipTlsVerification: z.boolean().optional(),
|
||||
enabled: z.boolean().optional()
|
||||
@@ -101,8 +102,12 @@ export async function createAiProvider(
|
||||
const encryptedApiKey = apiKey ? encrypt(apiKey, key) : null;
|
||||
const apiKeyLastChars = apiKey ? apiKey.slice(-4) : null;
|
||||
const now = Date.now();
|
||||
const resolvedRoutingMode =
|
||||
type === "custom" ? (routingMode ?? "url") : "url";
|
||||
const resolved = resolveAiProviderCreateFields({
|
||||
type,
|
||||
upstreamUrl,
|
||||
authType,
|
||||
routingMode
|
||||
});
|
||||
|
||||
const [provider] = await db
|
||||
.insert(aiProviders)
|
||||
@@ -110,14 +115,11 @@ export async function createAiProvider(
|
||||
orgId,
|
||||
name,
|
||||
type,
|
||||
upstreamUrl:
|
||||
resolvedRoutingMode === "target"
|
||||
? null
|
||||
: (upstreamUrl ?? null),
|
||||
upstreamUrl: resolved.upstreamUrl,
|
||||
apiKey: encryptedApiKey,
|
||||
apiKeyLastChars,
|
||||
authType: authType ?? null,
|
||||
routingMode: resolvedRoutingMode,
|
||||
authType: resolved.authType,
|
||||
routingMode: resolved.routingMode,
|
||||
skipTlsVerification: skipTlsVerification ?? false,
|
||||
enabled: enabled ?? true,
|
||||
createdAt: now,
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
import type { AiModel, AiProvider } from "@server/db";
|
||||
import type { PaginatedResponse } from "@server/types/Pagination";
|
||||
import {
|
||||
resolveAiProviderConfig,
|
||||
type AiProviderAuthType,
|
||||
type AiProviderRoutingMode,
|
||||
type AiProviderType
|
||||
} from "@server/lib/aiProviderDefaults";
|
||||
import type { AiProviderAuthType } from "@server/lib/aiProviderDefaults";
|
||||
import { decrypt } from "@server/lib/crypto";
|
||||
import config from "@server/lib/config";
|
||||
|
||||
@@ -13,7 +8,7 @@ export type AiProviderPublic = Omit<AiProvider, "apiKey"> & {
|
||||
/** Decrypted API key. Only included on get/create/update of a single provider. */
|
||||
apiKey?: string | null;
|
||||
effectiveUpstreamUrl: string | null;
|
||||
effectiveAuthType: AiProviderAuthType | null;
|
||||
effectiveAuthType: AiProviderAuthType;
|
||||
};
|
||||
|
||||
export type ListAiProvidersResponse = PaginatedResponse<{
|
||||
@@ -45,12 +40,6 @@ export function toPublicAiProvider(
|
||||
options?: { includeApiKey?: boolean }
|
||||
): AiProviderPublic {
|
||||
const { apiKey: encryptedApiKey, ...rest } = provider;
|
||||
const resolved = resolveAiProviderConfig({
|
||||
type: provider.type as AiProviderType,
|
||||
upstreamUrl: provider.upstreamUrl,
|
||||
authType: provider.authType as AiProviderAuthType | null,
|
||||
routingMode: provider.routingMode as AiProviderRoutingMode | null
|
||||
});
|
||||
|
||||
let apiKey: string | null | undefined;
|
||||
if (options?.includeApiKey) {
|
||||
@@ -67,7 +56,7 @@ export function toPublicAiProvider(
|
||||
return {
|
||||
...rest,
|
||||
...(options?.includeApiKey ? { apiKey } : {}),
|
||||
effectiveUpstreamUrl: resolved.upstreamUrl,
|
||||
effectiveAuthType: resolved.authType
|
||||
effectiveUpstreamUrl: provider.upstreamUrl,
|
||||
effectiveAuthType: provider.authType as AiProviderAuthType
|
||||
};
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
refineProviderUpstreamFields
|
||||
} from "@server/routers/aiProvider/validation";
|
||||
import type {
|
||||
AiProviderAuthType,
|
||||
AiProviderRoutingMode,
|
||||
AiProviderType
|
||||
} from "@server/lib/aiProviderDefaults";
|
||||
@@ -31,7 +32,7 @@ const bodySchema = z.strictObject({
|
||||
name: z.string().nonempty().optional(),
|
||||
upstreamUrl: z.url().optional().nullable(),
|
||||
apiKey: z.string().optional(),
|
||||
authType: aiAuthTypeSchema.optional().nullable(),
|
||||
authType: aiAuthTypeSchema.optional(),
|
||||
routingMode: aiRoutingModeSchema.optional(),
|
||||
skipTlsVerification: z.boolean().optional(),
|
||||
enabled: z.boolean().optional()
|
||||
@@ -116,17 +117,16 @@ export async function updateAiProvider(
|
||||
body.upstreamUrl !== undefined
|
||||
? body.upstreamUrl
|
||||
: existing.upstreamUrl;
|
||||
const nextAuthType =
|
||||
const nextAuthType: AiProviderAuthType =
|
||||
body.authType !== undefined
|
||||
? body.authType
|
||||
: (existing.authType ??
|
||||
(providerType === "custom" ? "bearer" : null));
|
||||
: (existing.authType as AiProviderAuthType);
|
||||
|
||||
const validation = z
|
||||
.object({
|
||||
type: aiProviderTypeSchema,
|
||||
upstreamUrl: z.string().nullable().optional(),
|
||||
authType: aiAuthTypeSchema.nullable().optional(),
|
||||
authType: aiAuthTypeSchema,
|
||||
routingMode: aiRoutingModeSchema.optional()
|
||||
})
|
||||
.superRefine((data, ctx) => refineProviderUpstreamFields(data, ctx))
|
||||
@@ -167,13 +167,6 @@ export async function updateAiProvider(
|
||||
}
|
||||
if (body.authType !== undefined) {
|
||||
updateData.authType = body.authType;
|
||||
} else if (
|
||||
providerType === "custom" &&
|
||||
!existing.authType &&
|
||||
nextAuthType
|
||||
) {
|
||||
// Backfill required authType for custom providers created without one
|
||||
updateData.authType = nextAuthType;
|
||||
}
|
||||
|
||||
if (body.apiKey !== undefined) {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { z } from "zod";
|
||||
import {
|
||||
AI_PROVIDER_AUTH_TYPES,
|
||||
providerRequiresUpstreamUrl,
|
||||
type AiProviderAuthType,
|
||||
type AiProviderRoutingMode,
|
||||
type AiProviderType
|
||||
} from "@server/lib/aiProviderDefaults";
|
||||
@@ -17,7 +19,7 @@ export const aiProviderTypeSchema = z.enum([
|
||||
"custom"
|
||||
]);
|
||||
|
||||
export const aiAuthTypeSchema = z.enum(["bearer"]);
|
||||
export const aiAuthTypeSchema = z.enum(AI_PROVIDER_AUTH_TYPES);
|
||||
|
||||
export const aiRoutingModeSchema = z.enum(["url", "target"]);
|
||||
|
||||
@@ -25,7 +27,7 @@ export function refineProviderUpstreamFields(
|
||||
data: {
|
||||
type: AiProviderType;
|
||||
upstreamUrl?: string | null;
|
||||
authType?: "bearer" | null;
|
||||
authType?: AiProviderAuthType | null;
|
||||
routingMode?: AiProviderRoutingMode | null;
|
||||
},
|
||||
ctx: z.RefinementCtx
|
||||
|
||||
@@ -40,7 +40,10 @@ import {
|
||||
type AiProviderFormValues
|
||||
} from "@app/lib/aiProviderFormSchema";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import type { AiProviderType } from "@server/lib/aiProviderDefaults";
|
||||
import type {
|
||||
AiProviderAuthType,
|
||||
AiProviderType
|
||||
} from "@server/lib/aiProviderDefaults";
|
||||
import type { CreateOrEditAiProviderResponse } from "@server/routers/aiProvider/types";
|
||||
import type { AxiosResponse } from "axios";
|
||||
import { useTranslations } from "next-intl";
|
||||
@@ -63,7 +66,7 @@ export default function AiProviderAuthenticationPage() {
|
||||
type: provider.type as AiProviderType,
|
||||
upstreamUrl: provider.upstreamUrl ?? "",
|
||||
apiKey: provider.apiKey ?? "",
|
||||
authType: (provider.authType as "bearer" | null) ?? "bearer",
|
||||
authType: (provider.authType as AiProviderAuthType) ?? "bearer",
|
||||
routingMode: (provider.routingMode as "url" | "target") ?? "url",
|
||||
skipTlsVerification: provider.skipTlsVerification,
|
||||
enabled: provider.enabled
|
||||
@@ -91,7 +94,7 @@ export default function AiProviderAuthenticationPage() {
|
||||
type: updated.type as AiProviderType,
|
||||
upstreamUrl: updated.upstreamUrl ?? "",
|
||||
apiKey: updated.apiKey ?? "",
|
||||
authType: (updated.authType as "bearer" | null) ?? "bearer",
|
||||
authType: (updated.authType as AiProviderAuthType) ?? "bearer",
|
||||
routingMode: (updated.routingMode as "url" | "target") ?? "url",
|
||||
skipTlsVerification: updated.skipTlsVerification,
|
||||
enabled: updated.enabled
|
||||
@@ -164,6 +167,26 @@ export default function AiProviderAuthenticationPage() {
|
||||
"aiProviderAuthTypeBearer"
|
||||
)}
|
||||
</SelectItem>
|
||||
<SelectItem value="x-api-key">
|
||||
{t(
|
||||
"aiProviderAuthTypeXApiKey"
|
||||
)}
|
||||
</SelectItem>
|
||||
<SelectItem value="x-goog-api-key">
|
||||
{t(
|
||||
"aiProviderAuthTypeXGoogApiKey"
|
||||
)}
|
||||
</SelectItem>
|
||||
<SelectItem value="hec">
|
||||
{t(
|
||||
"aiProviderAuthTypeHec"
|
||||
)}
|
||||
</SelectItem>
|
||||
<SelectItem value="cf-aig-authorization">
|
||||
{t(
|
||||
"aiProviderAuthTypeCfAigAuthorization"
|
||||
)}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormDescription>
|
||||
|
||||
@@ -45,7 +45,10 @@ import {
|
||||
} from "@app/lib/aiProviderFormSchema";
|
||||
import { aiProviderQueries } from "@app/lib/queries";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import type { AiProviderType } from "@server/lib/aiProviderDefaults";
|
||||
import type {
|
||||
AiProviderAuthType,
|
||||
AiProviderType
|
||||
} from "@server/lib/aiProviderDefaults";
|
||||
import type { CreateOrEditAiProviderResponse } from "@server/routers/aiProvider/types";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import type { AxiosResponse } from "axios";
|
||||
@@ -72,7 +75,7 @@ export default function AiProviderNetworkPage() {
|
||||
type: provider.type as AiProviderType,
|
||||
upstreamUrl: provider.upstreamUrl ?? "",
|
||||
apiKey: "",
|
||||
authType: (provider.authType as "bearer" | null) ?? "bearer",
|
||||
authType: (provider.authType as AiProviderAuthType) ?? "bearer",
|
||||
routingMode: (provider.routingMode as "url" | "target") ?? "url",
|
||||
skipTlsVerification: provider.skipTlsVerification,
|
||||
enabled: provider.enabled
|
||||
@@ -115,7 +118,7 @@ export default function AiProviderNetworkPage() {
|
||||
type: updated.type as AiProviderType,
|
||||
upstreamUrl: updated.upstreamUrl ?? "",
|
||||
apiKey: "",
|
||||
authType: (updated.authType as "bearer" | null) ?? "bearer",
|
||||
authType: (updated.authType as AiProviderAuthType) ?? "bearer",
|
||||
routingMode: (updated.routingMode as "url" | "target") ?? "url",
|
||||
skipTlsVerification: updated.skipTlsVerification,
|
||||
enabled: updated.enabled
|
||||
|
||||
@@ -527,6 +527,26 @@ export default function CreateAiProviderPage() {
|
||||
"aiProviderAuthTypeBearer"
|
||||
)}
|
||||
</SelectItem>
|
||||
<SelectItem value="x-api-key">
|
||||
{t(
|
||||
"aiProviderAuthTypeXApiKey"
|
||||
)}
|
||||
</SelectItem>
|
||||
<SelectItem value="x-goog-api-key">
|
||||
{t(
|
||||
"aiProviderAuthTypeXGoogApiKey"
|
||||
)}
|
||||
</SelectItem>
|
||||
<SelectItem value="hec">
|
||||
{t(
|
||||
"aiProviderAuthTypeHec"
|
||||
)}
|
||||
</SelectItem>
|
||||
<SelectItem value="cf-aig-authorization">
|
||||
{t(
|
||||
"aiProviderAuthTypeCfAigAuthorization"
|
||||
)}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormDescription>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { z } from "zod";
|
||||
import {
|
||||
AI_PROVIDER_AUTH_TYPES,
|
||||
AI_PROVIDER_DEFAULTS,
|
||||
providerRequiresUpstreamUrl,
|
||||
type AiProviderType
|
||||
@@ -23,7 +24,7 @@ export const aiProviderFormSchema = z
|
||||
type: z.enum(aiProviderTypeValues),
|
||||
upstreamUrl: z.string().optional().nullable(),
|
||||
apiKey: z.string().optional(),
|
||||
authType: z.enum(["bearer"]).optional().nullable(),
|
||||
authType: z.enum(AI_PROVIDER_AUTH_TYPES).optional().nullable(),
|
||||
routingMode: z.enum(["url", "target"]).optional(),
|
||||
skipTlsVerification: z.boolean().optional(),
|
||||
enabled: z.boolean().optional()
|
||||
@@ -138,7 +139,7 @@ export function toAiProviderCreatePayload(values: AiProviderFormValues) {
|
||||
authType:
|
||||
values.type === "custom"
|
||||
? (values.authType ?? "bearer")
|
||||
: (values.authType ?? undefined),
|
||||
: undefined,
|
||||
skipTlsVerification: values.skipTlsVerification,
|
||||
enabled: values.enabled ?? true
|
||||
};
|
||||
@@ -159,14 +160,14 @@ export function toAiProviderUpdatePayload(values: AiProviderFormValues) {
|
||||
name: values.name.trim(),
|
||||
routingMode: values.type === "custom" ? routingMode : "url",
|
||||
upstreamUrl,
|
||||
authType:
|
||||
values.type === "custom"
|
||||
? (values.authType ?? "bearer")
|
||||
: (values.authType ?? null),
|
||||
skipTlsVerification: values.skipTlsVerification ?? false,
|
||||
enabled: values.enabled ?? true
|
||||
};
|
||||
|
||||
if (values.type === "custom") {
|
||||
payload.authType = values.authType ?? "bearer";
|
||||
}
|
||||
|
||||
if (values.apiKey?.trim()) {
|
||||
payload.apiKey = values.apiKey.trim();
|
||||
}
|
||||
@@ -184,11 +185,13 @@ export function toAiProviderNetworkPayload(values: AiProviderFormValues) {
|
||||
}
|
||||
|
||||
export function toAiProviderAuthPayload(values: AiProviderFormValues) {
|
||||
const full = toAiProviderUpdatePayload(values);
|
||||
return {
|
||||
authType: full.authType,
|
||||
const payload: Record<string, unknown> = {
|
||||
...(values.apiKey !== undefined ? { apiKey: values.apiKey.trim() } : {})
|
||||
};
|
||||
if (values.type === "custom") {
|
||||
payload.authType = values.authType ?? "bearer";
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
|
||||
export function toAiProviderConfigurationPayload(values: AiProviderFormValues) {
|
||||
|
||||
Reference in New Issue
Block a user