Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apis/cloudflare/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@
"@opentelemetry/resources": "^2.0.0",
"@opentelemetry/sdk-metrics": "^2.1.0",
"dotenv": "^16.3.1",
"zod": "3.25.34"
"zod": "4.2.1"
}
}
2 changes: 1 addition & 1 deletion apis/vercel/next-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
/// <reference types="next/navigation-types/compat/navigation" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@
},
"packageManager": "pnpm@8.15.5",
"resolutions": {
"zod": "3.25.34"
"zod": "4.2.1"
}
}
2 changes: 1 addition & 1 deletion packages/proxy/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,6 @@
"openai": "4.104.0",
"openapi-json-schema": "^2.0.0",
"uuid": "^9.0.1",
"zod": "^3.25.34"
"zod": "4.2.1"
}
}
4 changes: 2 additions & 2 deletions packages/proxy/schema/audio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ export const pcmAudioFormatSchema = z
.discriminatedUnion("name", [
z.object({
name: z.literal("pcm"),
byte_order: z.enum(["little", "big"]).default("little"),
number_encoding: z.enum(["int", "float"]).default("int"),
byte_order: z.enum(["little", "big"]).prefault("little"),
number_encoding: z.enum(["int", "float"]).prefault("int"),
bits_per_sample: z.union([z.literal(8), z.literal(16)]),
}),
z.object({
Expand Down
6 changes: 3 additions & 3 deletions packages/proxy/schema/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -690,14 +690,14 @@ export const anthropicSupportedMediaTypes = [

export const anthropicTextBlockSchema = z.object({
type: z.literal("text").optional(),
text: z.string().default(""),
text: z.string().prefault(""),
});
export const anthropicImageBlockSchema = z.object({
type: z.literal("image").optional(),
source: z.object({
type: z.enum(["base64"]).optional(),
media_type: z.enum(["image/jpeg", "image/png", "image/gif", "image/webp"]),
data: z.string().default(""),
data: z.string().prefault(""),
}),
});
const anthropicContentBlockSchema = z.union([
Expand All @@ -706,7 +706,7 @@ const anthropicContentBlockSchema = z.union([
]);
const anthropicContentBlocksSchema = z.array(anthropicContentBlockSchema);
const anthropicContentSchema = z.union([
z.string().default(""),
z.string().prefault(""),
anthropicContentBlocksSchema,
]);

Expand Down
2 changes: 1 addition & 1 deletion packages/proxy/schema/models.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ModelSchema } from "./models";
import { z } from "zod";

it("parse model list", () => {
const models = z.record(z.unknown()).parse(raw_models);
const models = z.record(z.string(), z.unknown()).parse(raw_models);
for (const [key, value] of Object.entries(models)) {
const result = ModelSchema.safeParse(value);
if (!result.success) {
Expand Down
8 changes: 7 additions & 1 deletion packages/proxy/schema/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,19 @@ export const ModelSchema = z.object({
export type ModelSpec = z.infer<typeof ModelSchema>;

import modelListJson from "./model_list.json";
const modelListJsonTyped = z.record(ModelSchema).parse(modelListJson);
const modelListJsonTyped = z
.record(z.string(), ModelSchema)
.parse(modelListJson);

// Because this file can be included and bundled in various ways, it's important to
// really inject these variables into the global scope, rather than let the bundler
// have its way with them.
declare global {
// eslint-disable-next-line no-var
var _proxy_availableModels: { [name: string]: ModelSpec } | undefined;
// eslint-disable-next-line no-var
var _proxy_cachedModels: { [name: string]: ModelSpec } | null;
// eslint-disable-next-line no-var
var _proxy_cacheTimestamp: number | null;
}

Expand Down Expand Up @@ -158,6 +163,7 @@ async function loadModelsFromControlPlane(
throw new Error(`Failed to fetch models: ${response.statusText}`);
}
const data = await response.json();
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
globalThis._proxy_cachedModels = data as { [name: string]: ModelSpec };
globalThis._proxy_cacheTimestamp = Date.now();
} catch (error) {
Expand Down
2 changes: 1 addition & 1 deletion packages/proxy/schema/openai-realtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const toolDefinitionTypeSchema = z.object({
type: z.literal("function").optional(),
name: z.string(),
description: z.string(),
parameters: z.record(z.unknown()),
parameters: z.record(z.string(), z.unknown()),
});

export const sessionResourceTypeSchema = z.object({
Expand Down
222 changes: 94 additions & 128 deletions packages/proxy/schema/secrets.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,26 @@
import { z } from "zod";
import { ModelSchema } from "./models";

export const BaseMetadataSchema = z
.object({
models: z.array(z.string()).nullish(),
customModels: z.record(ModelSchema).nullish(),
excludeDefaultModels: z.boolean().nullish(),
additionalHeaders: z.record(z.string(), z.string()).nullish(),
supportsStreaming: z.boolean().default(true),
})
.strict();

export const AzureMetadataSchema = BaseMetadataSchema.merge(
z.object({
api_base: z.string().url(),
api_version: z.string().default("2023-07-01-preview"),
deployment: z.string().nullish(),
auth_type: z.enum(["api_key", "entra_api"]).default("api_key"),
no_named_deployment: z
.boolean()
.default(false)
.describe(
"If true, the deployment name will not be used in the request path.",
),
}),
).strict();
export const BaseMetadataSchema = z.strictObject({
models: z.array(z.string()).nullish(),
customModels: z.record(z.string(), ModelSchema).nullish(),
excludeDefaultModels: z.boolean().nullish(),
additionalHeaders: z.record(z.string(), z.string()).nullish(),
supportsStreaming: z.boolean().prefault(true),
});

export const AzureMetadataSchema = BaseMetadataSchema.extend({
api_base: z.url(),
api_version: z.string().prefault("2023-07-01-preview"),
deployment: z.string().nullish(),
auth_type: z.enum(["api_key", "entra_api"]).prefault("api_key"),
no_named_deployment: z
.boolean()
.prefault(false)
.describe(
"If true, the deployment name will not be used in the request path.",
),
});

export const AzureEntraSecretSchema = z.object({
client_id: z.string().min(1, "Client ID cannot be empty"),
Expand All @@ -34,121 +30,91 @@ export const AzureEntraSecretSchema = z.object({
});
export type AzureEntraSecret = z.infer<typeof AzureEntraSecretSchema>;

export const BedrockMetadataSchema = BaseMetadataSchema.merge(
z.object({
region: z.string().min(1, "Region cannot be empty"),
access_key: z.string().min(1, "Access key cannot be empty"),
session_token: z.string().nullish(),
api_base: z.union([z.string().url(), z.string().length(0)]).nullish(),
}),
).strict();
export const BedrockMetadataSchema = BaseMetadataSchema.extend({
region: z.string().min(1, "Region cannot be empty"),
access_key: z.string().min(1, "Access key cannot be empty"),
session_token: z.string().nullish(),
api_base: z.union([z.url(), z.string().length(0)]).nullish(),
});
export type BedrockMetadata = z.infer<typeof BedrockMetadataSchema>;

export const VertexMetadataSchema = BaseMetadataSchema.merge(
z.object({
project: z.string().min(1, "Project cannot be empty"),
authType: z.enum(["access_token", "service_account_key"]),
api_base: z.union([z.string().url(), z.string().length(0)]).nullish(),
}),
).strict();
export const VertexMetadataSchema = BaseMetadataSchema.extend({
project: z.string().min(1, "Project cannot be empty"),
authType: z.enum(["access_token", "service_account_key"]),
api_base: z.union([z.url(), z.string().length(0)]).nullish(),
});

export const DatabricksMetadataSchema = BaseMetadataSchema.merge(
z.object({
api_base: z.string().url(),
auth_type: z.enum(["pat", "service_principal_oauth"]).default("pat"),
}),
).strict();
export const DatabricksMetadataSchema = BaseMetadataSchema.extend({
api_base: z.url(),
auth_type: z.enum(["pat", "service_principal_oauth"]).prefault("pat"),
});

export const DatabricksOAuthSecretSchema = z.object({
client_id: z.string().min(1, "Client ID cannot be empty"),
client_secret: z.string().min(1, "Client secret cannot be empty"),
});
export type DatabricksOAuthSecret = z.infer<typeof DatabricksOAuthSecretSchema>;

export const OpenAIMetadataSchema = BaseMetadataSchema.merge(
z.object({
api_base: z.union([
z.string().url().optional(),
z.string().length(0),
z.null(),
]),
organization_id: z.string().nullish(),
}),
).strict();
export const OpenAIMetadataSchema = BaseMetadataSchema.extend({
api_base: z.union([z.url().optional(), z.string().length(0), z.null()]),
organization_id: z.string().nullish(),
});

export const MistralMetadataSchema = BaseMetadataSchema.merge(
z.object({
api_base: z.union([z.string().url(), z.string().length(0)]).nullish(),
}),
).strict();
export const MistralMetadataSchema = BaseMetadataSchema.extend({
api_base: z.union([z.url(), z.string().length(0)]).nullish(),
});

const APISecretBaseSchema = z
.object({
id: z.string().uuid().nullish(),
org_name: z.string().nullish(),
name: z.string().nullish(),
secret: z.string(),
metadata: z.record(z.unknown()).nullish(),
})
.strict();
const APISecretBaseSchema = z.strictObject({
id: z.string().uuid().nullish(),
org_name: z.string().nullish(),
name: z.string().nullish(),
secret: z.string(),
metadata: z.record(z.string(), z.unknown()).nullish(),
});

export const APISecretSchema = z.union([
APISecretBaseSchema.merge(
z.object({
type: z.enum([
"perplexity",
"anthropic",
"google",
"replicate",
"together",
"baseten",
"ollama",
"groq",
"lepton",
"fireworks",
"cerebras",
"xAI",
"js",
]),
metadata: BaseMetadataSchema.nullish(),
}),
),
APISecretBaseSchema.merge(
z.object({
type: z.literal("openai"),
metadata: OpenAIMetadataSchema.nullish(),
}),
),
APISecretBaseSchema.merge(
z.object({
type: z.literal("azure"),
metadata: AzureMetadataSchema.nullish(),
}),
),
APISecretBaseSchema.merge(
z.object({
type: z.literal("bedrock"),
metadata: BedrockMetadataSchema.nullish(),
}),
),
APISecretBaseSchema.merge(
z.object({
type: z.literal("vertex"),
metadata: VertexMetadataSchema.nullish(),
}),
),
APISecretBaseSchema.merge(
z.object({
type: z.literal("databricks"),
metadata: DatabricksMetadataSchema.nullish(),
}),
),
APISecretBaseSchema.merge(
z.object({
type: z.literal("mistral"),
metadata: MistralMetadataSchema.nullish(),
}),
),
APISecretBaseSchema.extend({
type: z.enum([
"perplexity",
"anthropic",
"google",
"replicate",
"together",
"baseten",
"ollama",
"groq",
"lepton",
"fireworks",
"cerebras",
"xAI",
"js",
]),
metadata: BaseMetadataSchema.nullish(),
}),
APISecretBaseSchema.extend({
type: z.literal("openai"),
metadata: OpenAIMetadataSchema.nullish(),
}),
APISecretBaseSchema.extend({
type: z.literal("azure"),
metadata: AzureMetadataSchema.nullish(),
}),
APISecretBaseSchema.extend({
type: z.literal("bedrock"),
metadata: BedrockMetadataSchema.nullish(),
}),
APISecretBaseSchema.extend({
type: z.literal("vertex"),
metadata: VertexMetadataSchema.nullish(),
}),
APISecretBaseSchema.extend({
type: z.literal("databricks"),
metadata: DatabricksMetadataSchema.nullish(),
}),
APISecretBaseSchema.extend({
type: z.literal("mistral"),
metadata: MistralMetadataSchema.nullish(),
}),
]);

export type APISecret = z.infer<typeof APISecretSchema>;
Expand All @@ -157,10 +123,10 @@ export const proxyLoggingParamSchema = z
.object({
parent: z.string().optional(),
project_name: z.string().optional(),
compress_audio: z.boolean().default(true),
compress_audio: z.boolean().prefault(true),
})
.refine((data) => data.parent || data.project_name, {
message: "Either 'parent' or 'project_name' must be provided",
error: "Either 'parent' or 'project_name' must be provided",
})
.describe(
"If present, proxy will log requests to the given Braintrust project or parent span.",
Expand All @@ -179,7 +145,7 @@ export const credentialsRequestSchema = z
ttl_seconds: z
.number()
.max(60 * 60 * 24)
.default(60 * 10)
.prefault(60 * 10)
.describe("TTL of the temporary credential. 10 minutes by default."),
logging: proxyLoggingParamSchema.nullish(),
})
Expand Down
Loading