Files
cobble/src/schemas/harness-schema.ts
T
tbenandClaude Opus 5.5 bc499a7466 Let admins hear a voice sample before picking it
The Speech section of every model settings form now has "Hear a
sample", which streams a short sentence (AppData.VoicePreviewText) in
the form's model and voice before they're saved, through
GET api/admin/harness/preview-voice (super admins and admins, that
model only via grabModelCandidate). pickVoice is shared with
speakText, and explainAudioError reads why a streamed player failed
for both read-aloud and the sample.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-19 21:32:49 +01:00

100 lines
3.2 KiB
TypeScript

import { z } from "zod";
import { Harnesses, type HarnessKind } from "@/src/dict/harness-dict";
import {
HarnessProviderKinds,
type HarnessProviderKind,
} from "@/src/dict/harness-provider-dict";
const ProviderIdSchema = z.coerce
.number()
.int()
.positive("Pick a provider");
export const SaveHarnessProviderRequestSchema = z.object({
provider_id: ProviderIdSchema.optional(),
kind: z.enum(
HarnessProviderKinds.map((kind) => kind.value) as [
HarnessProviderKind,
...HarnessProviderKind[],
],
"Pick a provider type",
),
name: z.string().trim().max(80, "Keep the name under 80 characters").optional(),
base_url: z.string().trim().max(500, "That address is too long").optional(),
api_key: z.string().trim().max(1000, "That key is too long").optional(),
clear_key: z.boolean().optional(),
enabled: z.boolean(),
});
export const HarnessProviderIdRequestSchema = z.object({
provider_id: ProviderIdSchema,
});
export const SaveHarnessSettingsRequestSchema = z.object({
settings: z
.array(
z.object({
harness: z.enum(
Harnesses.map((harness) => harness.value) as [
HarnessKind,
...HarnessKind[],
],
),
model_id: z.coerce.number().int().positive().nullable(),
provider_id: z.coerce.number().int().positive().nullable().optional(),
fallback: z.boolean(),
enabled: z.boolean(),
voice: z.string().trim().max(200).optional(),
}),
)
.min(1, "Nothing to save")
.max(Harnesses.length),
});
/**
* A worker bot's own model settings. `inherit` drops the bot's setting
* for that harness so it uses the default again.
*/
export const SaveBotHarnessSettingsRequestSchema = z.object({
bot_id: z.coerce.number().int().positive(),
settings: z
.array(
SaveHarnessSettingsRequestSchema.shape.settings.element.extend({
inherit: z.boolean().default(false),
}),
)
.min(1, "Nothing to save")
.max(Harnesses.length),
});
/**
* A workspace's own model settings. `inherit` drops the workspace's
* setting for that harness so each bot uses its own or the default again.
*/
export const SaveWorkspaceHarnessSettingsRequestSchema = SaveBotHarnessSettingsRequestSchema.omit({
bot_id: true,
}).extend({
workspace_id: z.coerce.number().int().positive(),
});
export type SaveWorkspaceHarnessSettingsRequest = z.infer<
typeof SaveWorkspaceHarnessSettingsRequestSchema
>;
export type SaveHarnessProviderRequest = z.infer<
typeof SaveHarnessProviderRequestSchema
>;
export type SaveHarnessSettingsRequest = z.infer<
typeof SaveHarnessSettingsRequestSchema
>;
export type SaveBotHarnessSettingsRequest = z.infer<
typeof SaveBotHarnessSettingsRequestSchema
>;
export const PreviewVoiceRequestSchema = z.object({
model_id: z.coerce.number().int().positive("Pick a speech model"),
voice: z.string().trim().max(200).optional(),
});
export type PreviewVoiceRequest = z.infer<typeof PreviewVoiceRequestSchema>;