mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-11-06 05:17:15 +00:00
feat(desktop): Daybreak control in the model route editor
The route dialog offers a Daybreak select (Off / On) only when the installed model_map backend declares the `daybreak` route key and this account's Codex catalog lists Daybreak for the chosen model (the same capability the main model menu's Daybreak switch uses). An older plugin that would ignore the key never gets the control. - Switching to a model or provider without Daybreak clears the opt-in. - A stored opt-in the plugin cannot run, or whose model is no longer listed, stays visible with a warning so it can be turned off. - Turning it off removes the key; the route list marks Daybreak routes. - Strings in every locale.
This commit is contained in:
1 parent
494b618eb9
commit
fcb3085b9b
18 files changed
+306
-16
No files matched your search
@@ -11,6 +11,8 @@ export interface DelegationBackendInfo {
|
||||
available: boolean
|
||||
reason: string
|
||||
builtin: boolean
|
||||
/** The backend's own route-entry keys an editor may write (e.g. `daybreak`); absent on older cores. */
|
||||
route_keys?: string[]
|
||||
}
|
||||
|
||||
/** One entry of `delegation.backends.model_map.options`. Unknown keys round-trip untouched. */
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
import { QueryClientProvider } from '@tanstack/react-query'
|
||||
import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import type { DelegationModelRoute } from '@/api/delegation'
|
||||
import type { HermesApiRequest } from '@/global'
|
||||
import { queryClient } from '@/lib/query-client'
|
||||
import { stubMenuDomApis, stubResizeObserver } from '@/test/jsdom'
|
||||
|
||||
import { ModelRouteDialog } from './model-route-dialog'
|
||||
|
||||
// The owner's catalog as /api/model/options reports it: Daybreak listed for gpt-6-sol only.
|
||||
const CATALOG = {
|
||||
providers: [
|
||||
{
|
||||
slug: 'openai-codex',
|
||||
name: 'OpenAI Codex',
|
||||
models: ['gpt-6-sol', 'gpt-6.1-sol'],
|
||||
capabilities: {
|
||||
'gpt-6-sol': { fast: true, reasoning: true, daybreak: true },
|
||||
'gpt-6.1-sol': { fast: true, reasoning: true }
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
stubMenuDomApis()
|
||||
stubResizeObserver()
|
||||
queryClient.clear()
|
||||
window.hermesDesktop = {
|
||||
...window.hermesDesktop,
|
||||
api: async (request: HermesApiRequest) => {
|
||||
if (request.path.startsWith('/api/model/options')) {
|
||||
return structuredClone(CATALOG)
|
||||
}
|
||||
|
||||
throw new Error('404: Not Found')
|
||||
}
|
||||
} as typeof window.hermesDesktop
|
||||
})
|
||||
|
||||
afterEach(cleanup)
|
||||
|
||||
function mount(route: DelegationModelRoute, routeKeys: readonly string[] = ['daybreak']) {
|
||||
const onSave = vi.fn(async (_route: DelegationModelRoute) => true)
|
||||
|
||||
render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<ModelRouteDialog
|
||||
onClose={() => {}}
|
||||
onSave={onSave}
|
||||
route={route}
|
||||
routeKeys={routeKeys}
|
||||
scope="work"
|
||||
taken={[]}
|
||||
/>
|
||||
</QueryClientProvider>
|
||||
)
|
||||
|
||||
return onSave
|
||||
}
|
||||
|
||||
const daybreakField = () => screen.queryByRole('combobox', { name: /Daybreak/ })
|
||||
|
||||
describe('model route dialog: Daybreak', () => {
|
||||
it('offers Daybreak only for a model the catalog lists it for, and saves the opt-in', async () => {
|
||||
const onSave = mount({ name: 'daybreak', description: 'Sol', provider: 'openai-codex', model: 'gpt-6-sol' })
|
||||
|
||||
await waitFor(() => expect(daybreakField()).not.toBeNull())
|
||||
expect(screen.getByText('OpenAI trusted access with reduced guardrails, for this route only.')).toBeTruthy()
|
||||
|
||||
fireEvent.click(daybreakField()!)
|
||||
fireEvent.click(await screen.findByRole('option', { name: 'On' }))
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Save route' }))
|
||||
|
||||
await waitFor(() => expect(onSave).toHaveBeenCalledTimes(1))
|
||||
expect(onSave.mock.calls[0][0]).toMatchObject({ model: 'gpt-6-sol', daybreak: true })
|
||||
})
|
||||
|
||||
it('shows no Daybreak control on a model the catalog does not list', async () => {
|
||||
mount({ name: 'sol', description: 'Sol', provider: 'openai-codex', model: 'gpt-6.1-sol' })
|
||||
|
||||
await screen.findByText('OpenAI Codex')
|
||||
expect(daybreakField()).toBeNull()
|
||||
})
|
||||
|
||||
it('shows no Daybreak control when the installed plugin does not declare it, even on a listed model', async () => {
|
||||
mount({ name: 'daybreak', description: 'Sol', provider: 'openai-codex', model: 'gpt-6-sol' }, [])
|
||||
|
||||
await screen.findByText('OpenAI Codex')
|
||||
expect(daybreakField()).toBeNull()
|
||||
})
|
||||
|
||||
it('keeps a stored opt-in the plugin cannot run visible with a warning, so it can be turned off', async () => {
|
||||
mount({ name: 'old', description: 'Sol', provider: 'openai-codex', model: 'gpt-6-sol', daybreak: true }, [])
|
||||
|
||||
await screen.findByText(/does not run Daybreak routes yet/)
|
||||
expect(daybreakField()).not.toBeNull()
|
||||
})
|
||||
|
||||
it('keeps a stored opt-in visible with a warning when its model is not listed, so it can be turned off', async () => {
|
||||
const onSave = mount({
|
||||
name: 'old',
|
||||
description: 'Sol',
|
||||
provider: 'openai-codex',
|
||||
model: 'gpt-6.1-sol',
|
||||
daybreak: true
|
||||
})
|
||||
|
||||
await screen.findByText(/not listed for Daybreak on your account/)
|
||||
fireEvent.click(daybreakField()!)
|
||||
fireEvent.click(await screen.findByRole('option', { name: 'Off' }))
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Save route' }))
|
||||
|
||||
await waitFor(() => expect(onSave).toHaveBeenCalledTimes(1))
|
||||
expect('daybreak' in onSave.mock.calls[0][0]).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -40,11 +40,13 @@ interface ModelRouteDialogProps {
|
||||
/** Names of the other routes (a new name must not collide with them). */
|
||||
taken: readonly string[]
|
||||
scope: ProfileScope
|
||||
/** Route keys the installed backend declares (`DelegationProvider.route_keys`). */
|
||||
routeKeys: readonly string[]
|
||||
onClose: () => void
|
||||
onSave: (route: DelegationModelRoute) => Promise<boolean>
|
||||
}
|
||||
|
||||
export function ModelRouteDialog({ route, taken, scope, onClose, onSave }: ModelRouteDialogProps) {
|
||||
export function ModelRouteDialog({ route, taken, scope, routeKeys, onClose, onSave }: ModelRouteDialogProps) {
|
||||
const { t } = useI18n()
|
||||
const r = t.settings.model.subagents.routing
|
||||
const { providers } = useOwnerCatalog(scope)
|
||||
@@ -55,6 +57,21 @@ export function ModelRouteDialog({ route, taken, scope, onClose, onSave }: Model
|
||||
const errors = validateRouteDraft(draft, taken, Boolean(route?.base_url))
|
||||
const selected = findCatalogProvider(providers, draft.provider)
|
||||
|
||||
// Offered only when the installed backend runs it (an older plugin would ignore the key) and this account's
|
||||
// Codex catalog lists Daybreak for the model (the main model menu's gate).
|
||||
const daybreakSupported = routeKeys.includes('daybreak')
|
||||
|
||||
const daybreakFor = (provider: string, model: string) =>
|
||||
daybreakSupported && findCatalogProvider(providers, provider)?.capabilities?.[model]?.daybreak === true
|
||||
|
||||
const daybreakOffered = daybreakFor(draft.provider, draft.model)
|
||||
|
||||
const daybreakHint = !daybreakSupported
|
||||
? r.daybreakNeedsPlugin
|
||||
: daybreakOffered
|
||||
? r.daybreakHint
|
||||
: r.daybreakUnlisted
|
||||
|
||||
const set = (field: RouteField, value: string) => setDraft(current => ({ ...current, [field]: value }))
|
||||
const touch = (field: RouteField) => setTouched(current => ({ ...current, [field]: true }))
|
||||
|
||||
@@ -64,11 +81,20 @@ export function ModelRouteDialog({ route, taken, scope, onClose, onSave }: Model
|
||||
const chooseProvider = (slug: string) => {
|
||||
const models = findCatalogProvider(providers, slug)?.models ?? []
|
||||
|
||||
// Never carry a model id over to a provider that does not list it.
|
||||
setDraft(current => ({ ...current, provider: slug, model: models.includes(current.model) ? current.model : '' }))
|
||||
// Never carry a model id (or a Daybreak opt-in) over to a provider that does not list it.
|
||||
setDraft(current => {
|
||||
const model = models.includes(current.model) ? current.model : ''
|
||||
|
||||
return { ...current, provider: slug, model, daybreak: current.daybreak && daybreakFor(slug, model) }
|
||||
})
|
||||
touch('provider')
|
||||
}
|
||||
|
||||
const chooseModel = (model: string) => {
|
||||
setDraft(current => ({ ...current, model, daybreak: current.daybreak && daybreakFor(current.provider, model) }))
|
||||
touch('model')
|
||||
}
|
||||
|
||||
async function handleSubmit(event: FormEvent) {
|
||||
event.preventDefault()
|
||||
setSubmitted(true)
|
||||
@@ -159,10 +185,7 @@ export function ModelRouteDialog({ route, taken, scope, onClose, onSave }: Model
|
||||
aria-label={r.modelLabel}
|
||||
className="w-full [&_[data-slot=select-value]]:truncate"
|
||||
models={selected?.models ?? []}
|
||||
onValueChange={model => {
|
||||
set('model', model)
|
||||
touch('model')
|
||||
}}
|
||||
onValueChange={chooseModel}
|
||||
provider={selected}
|
||||
providerSlug={draft.provider}
|
||||
value={draft.model}
|
||||
@@ -211,6 +234,26 @@ export function ModelRouteDialog({ route, taken, scope, onClose, onSave }: Model
|
||||
</Field>
|
||||
</div>
|
||||
|
||||
{(daybreakOffered || draft.daybreak) && (
|
||||
<div className="grid items-start gap-4 sm:grid-cols-2">
|
||||
<Field htmlFor="delegation-route-daybreak" label={r.daybreakLabel} optional optionalLabel={r.optional}>
|
||||
<Select
|
||||
onValueChange={value => setDraft(current => ({ ...current, daybreak: value === 'on' }))}
|
||||
value={draft.daybreak ? 'on' : 'off'}
|
||||
>
|
||||
<SelectTrigger id="delegation-route-daybreak">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="off">{r.daybreakOff}</SelectItem>
|
||||
<SelectItem value="on">{r.daybreakOn}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FieldHint error={!daybreakOffered}>{daybreakHint}</FieldHint>
|
||||
</Field>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<DialogFooter>
|
||||
<Button disabled={busy} onClick={onClose} type="button" variant="ghost">
|
||||
{t.common.cancel}
|
||||
|
||||
@@ -17,10 +17,12 @@ interface ModelRoutesEditorProps {
|
||||
scope: ProfileScope
|
||||
routes: DelegationModelMap
|
||||
providerNames: ReadonlyMap<string, string>
|
||||
/** Route keys the installed backend declares; an editor control appears only for these. */
|
||||
routeKeys: readonly string[]
|
||||
}
|
||||
|
||||
/** `delegation.backends.model_map`: the named routes the agent picks between, with create / edit / delete. */
|
||||
export function ModelRoutesEditor({ scope, routes, providerNames }: ModelRoutesEditorProps) {
|
||||
export function ModelRoutesEditor({ scope, routes, providerNames, routeKeys }: ModelRoutesEditorProps) {
|
||||
const { t } = useI18n()
|
||||
const r = t.settings.model.subagents.routing
|
||||
// undefined = closed, null = creating, a route = editing it.
|
||||
@@ -81,6 +83,7 @@ export function ModelRoutesEditor({ scope, routes, providerNames }: ModelRoutesE
|
||||
onClose={() => setEditing(undefined)}
|
||||
onSave={route => save(upsertRoute(routes, editing?.name ?? null, route))}
|
||||
route={editing}
|
||||
routeKeys={routeKeys}
|
||||
scope={scope}
|
||||
taken={routes.options.map(o => o.name).filter(name => name !== editing?.name)}
|
||||
/>
|
||||
@@ -106,7 +109,8 @@ function RouteRow({ route, isDefault, providerName, onEdit, onMakeDefault, onDel
|
||||
providerName || (route.base_url ? r.baseUrlRoute(route.base_url) : ''),
|
||||
route.model,
|
||||
route.reasoning_effort ? `${r.effortLabel.toLowerCase()} ${route.reasoning_effort}` : '',
|
||||
route.service_tier ?? ''
|
||||
route.service_tier ?? '',
|
||||
route.daybreak === true ? r.daybreakLabel : ''
|
||||
].filter(Boolean)
|
||||
|
||||
return (
|
||||
|
||||
@@ -2,7 +2,14 @@ import { describe, expect, it } from 'vitest'
|
||||
|
||||
import type { DelegationModelMap } from '@/hermes'
|
||||
|
||||
import { removeRoute, routeFromDraft, toggleProfileRoute, upsertRoute, validateRouteDraft } from './routes'
|
||||
import {
|
||||
draftFromRoute,
|
||||
removeRoute,
|
||||
routeFromDraft,
|
||||
toggleProfileRoute,
|
||||
upsertRoute,
|
||||
validateRouteDraft
|
||||
} from './routes'
|
||||
|
||||
const route = (name: string, extra: Record<string, unknown> = {}) => ({
|
||||
name,
|
||||
@@ -36,7 +43,15 @@ describe('route editing keeps a default while routes exist', () => {
|
||||
})
|
||||
|
||||
it('refuses incomplete or clashing drafts; an edit keeps keys the dialog does not edit and names its source', () => {
|
||||
const draft = { name: 'deep', description: '', provider: '', model: '', reasoning_effort: '', service_tier: '' }
|
||||
const draft = {
|
||||
name: 'deep',
|
||||
description: '',
|
||||
provider: '',
|
||||
model: '',
|
||||
reasoning_effort: '',
|
||||
service_tier: '',
|
||||
daybreak: false
|
||||
}
|
||||
|
||||
expect(validateRouteDraft(draft, ['deep'])).toEqual({
|
||||
name: 'duplicate',
|
||||
@@ -66,4 +81,14 @@ describe('route editing keeps a default while routes exist', () => {
|
||||
api_key: '«redacted:sk-…1234»'
|
||||
})
|
||||
})
|
||||
|
||||
it('stores Daybreak only as true, and turning it off drops the key', () => {
|
||||
const stored = route('daybreak', { provider: 'openai-codex', model: 'gpt-6-sol', daybreak: true })
|
||||
const draft = draftFromRoute(stored)
|
||||
|
||||
expect(draft.daybreak).toBe(true)
|
||||
expect(routeFromDraft(draft, stored).daybreak).toBe(true)
|
||||
expect('daybreak' in routeFromDraft({ ...draft, daybreak: false }, stored)).toBe(false)
|
||||
expect(draftFromRoute(route('plain', { daybreak: 'yes' })).daybreak).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -8,9 +8,12 @@ export interface ModelRouteDraft {
|
||||
model: string
|
||||
reasoning_effort: string
|
||||
service_tier: string
|
||||
/** OpenAI Daybreak for this route only (advanced-delegation's `daybreak: true`); false is not stored. */
|
||||
daybreak: boolean
|
||||
}
|
||||
|
||||
export type RouteField = keyof ModelRouteDraft
|
||||
export type RouteField = Exclude<keyof ModelRouteDraft, 'daybreak'>
|
||||
type TextField = RouteField
|
||||
export type RouteError = 'duplicate' | 'required' | 'spaces'
|
||||
|
||||
/** Saved as written: an explicit `normal` turns a provider's own Fast setting off; no tier leaves it alone. */
|
||||
@@ -22,7 +25,8 @@ export const emptyRouteDraft = (): ModelRouteDraft => ({
|
||||
provider: '',
|
||||
model: '',
|
||||
reasoning_effort: '',
|
||||
service_tier: ''
|
||||
service_tier: '',
|
||||
daybreak: false
|
||||
})
|
||||
|
||||
export const draftFromRoute = (route: DelegationModelRoute): ModelRouteDraft => ({
|
||||
@@ -31,7 +35,8 @@ export const draftFromRoute = (route: DelegationModelRoute): ModelRouteDraft =>
|
||||
provider: route.provider ?? '',
|
||||
model: route.model ?? '',
|
||||
reasoning_effort: route.reasoning_effort ?? '',
|
||||
service_tier: route.service_tier ?? ''
|
||||
service_tier: route.service_tier ?? '',
|
||||
daybreak: route.daybreak === true
|
||||
})
|
||||
|
||||
/** Field → problem, empty when the draft can be saved. `taken` = names of the OTHER routes.
|
||||
@@ -65,12 +70,21 @@ export function validateRouteDraft(
|
||||
return errors
|
||||
}
|
||||
|
||||
const TEXT_FIELDS: readonly TextField[] = [
|
||||
'name',
|
||||
'description',
|
||||
'provider',
|
||||
'model',
|
||||
'reasoning_effort',
|
||||
'service_tier'
|
||||
]
|
||||
|
||||
/** The stored route for `draft`: edited fields replace, blank optional fields drop, unknown keys stay. An edited
|
||||
* route names the route it came from (`original_name`), so the server keeps its stored key through a rename. */
|
||||
export function routeFromDraft(draft: ModelRouteDraft, previous?: DelegationModelRoute): DelegationModelRoute {
|
||||
const next: Record<string, unknown> = { ...previous, ...(previous ? { original_name: previous.name } : {}) }
|
||||
|
||||
for (const field of Object.keys(draft) as RouteField[]) {
|
||||
for (const field of TEXT_FIELDS) {
|
||||
const value = draft[field].trim()
|
||||
|
||||
if (value) {
|
||||
@@ -80,6 +94,12 @@ export function routeFromDraft(draft: ModelRouteDraft, previous?: DelegationMode
|
||||
}
|
||||
}
|
||||
|
||||
if (draft.daybreak) {
|
||||
next.daybreak = true
|
||||
} else {
|
||||
delete next.daybreak
|
||||
}
|
||||
|
||||
return next as DelegationModelRoute
|
||||
}
|
||||
|
||||
|
||||
@@ -111,7 +111,12 @@ function RoutingBody({ routing, scope }: { routing: DelegationRouting; scope: Pr
|
||||
{blocked && !isPluginMode(mode) && <BlockedBanner message={thirdPartyBlockedMessage(routing, r)} scope={scope} />}
|
||||
{mode === 'model_map' && (
|
||||
<div className="mt-6">
|
||||
<ModelRoutesEditor providerNames={providerNames} routes={routing.model_map} scope={scope} />
|
||||
<ModelRoutesEditor
|
||||
providerNames={providerNames}
|
||||
routeKeys={routing.backends.find(b => b.name === 'model_map')?.route_keys ?? []}
|
||||
routes={routing.model_map}
|
||||
scope={scope}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{mode === 'profile_routes' && (
|
||||
|
||||
@@ -67,6 +67,12 @@ export const arDelegationRouting: DelegationRoutingTranslations = {
|
||||
tierFast: 'سريعة',
|
||||
tierUltrafast: 'فائقة السرعة',
|
||||
tierHint: 'فقط في النماذج التي توفرها.',
|
||||
daybreakLabel: 'Daybreak',
|
||||
daybreakOff: 'إيقاف',
|
||||
daybreakOn: 'تشغيل',
|
||||
daybreakHint: 'وصول OpenAI الموثوق بقيود أقل، لهذا المسار فقط.',
|
||||
daybreakUnlisted: 'هذا النموذج غير مدرج لـ Daybreak في حسابك. أوقفه أو اختر نموذجًا آخر.',
|
||||
daybreakNeedsPlugin: 'إضافة Advanced Delegation لديك لا تشغّل مسارات Daybreak بعد. حدّثها أو أوقف هذا الخيار.',
|
||||
optional: 'اختياري',
|
||||
errors: { required: 'مطلوب', spaces: 'بلا مسافات', duplicate: 'مسار آخر يستخدم هذا الاسم بالفعل' },
|
||||
saveRoute: 'حفظ المسار',
|
||||
|
||||
@@ -75,6 +75,14 @@ export const deDelegationRouting: DelegationRoutingTranslations = {
|
||||
tierFast: 'Schnell',
|
||||
tierUltrafast: 'Ultraschnell',
|
||||
tierHint: 'Nur bei Modellen, die das anbieten.',
|
||||
daybreakLabel: 'Daybreak',
|
||||
daybreakOff: 'Aus',
|
||||
daybreakOn: 'An',
|
||||
daybreakHint: 'OpenAI-Trusted-Access mit weniger Schutzvorgaben, nur für diese Route.',
|
||||
daybreakUnlisted:
|
||||
'Dieses Modell ist für Daybreak in deinem Konto nicht freigegeben. Schalte es aus oder wähle ein anderes Modell.',
|
||||
daybreakNeedsPlugin:
|
||||
'Dein Advanced-Delegation-Plugin unterstützt noch keine Daybreak-Routen. Aktualisiere es oder schalte das aus.',
|
||||
optional: 'optional',
|
||||
errors: {
|
||||
required: 'Erforderlich',
|
||||
|
||||
@@ -67,6 +67,12 @@ export const enDelegationRouting: DelegationRoutingTranslations = {
|
||||
tierFast: 'Fast',
|
||||
tierUltrafast: 'Ultrafast',
|
||||
tierHint: 'Only on models that offer it.',
|
||||
daybreakLabel: 'Daybreak',
|
||||
daybreakOff: 'Off',
|
||||
daybreakOn: 'On',
|
||||
daybreakHint: 'OpenAI trusted access with reduced guardrails, for this route only.',
|
||||
daybreakUnlisted: 'This model is not listed for Daybreak on your account. Turn it off or pick another model.',
|
||||
daybreakNeedsPlugin: 'Your Advanced Delegation plugin does not run Daybreak routes yet. Update it, or turn this off.',
|
||||
optional: 'optional',
|
||||
errors: { required: 'Required', spaces: 'No spaces', duplicate: 'Another route already uses this name' },
|
||||
saveRoute: 'Save route',
|
||||
|
||||
@@ -78,6 +78,12 @@ export const esDelegationRouting: DelegationRoutingTranslations = {
|
||||
tierFast: 'Rápida',
|
||||
tierUltrafast: 'Ultrarrápida',
|
||||
tierHint: 'Solo en modelos que la ofrecen.',
|
||||
daybreakLabel: 'Daybreak',
|
||||
daybreakOff: 'Desactivado',
|
||||
daybreakOn: 'Activado',
|
||||
daybreakHint: 'Acceso de confianza de OpenAI con menos restricciones, solo para esta ruta.',
|
||||
daybreakUnlisted: 'Este modelo no figura para Daybreak en tu cuenta. Desactívalo o elige otro modelo.',
|
||||
daybreakNeedsPlugin: 'Tu plugin Advanced Delegation aún no ejecuta rutas Daybreak. Actualízalo o desactiva esto.',
|
||||
optional: 'opcional',
|
||||
errors: { required: 'Obligatorio', spaces: 'Sin espacios', duplicate: 'Otra ruta ya usa este nombre' },
|
||||
saveRoute: 'Guardar ruta',
|
||||
|
||||
@@ -77,6 +77,14 @@ export const frDelegationRouting: DelegationRoutingTranslations = {
|
||||
tierFast: 'Rapide',
|
||||
tierUltrafast: 'Ultra-rapide',
|
||||
tierHint: 'Uniquement sur les modèles qui la proposent.',
|
||||
daybreakLabel: 'Daybreak',
|
||||
daybreakOff: 'Désactivé',
|
||||
daybreakOn: 'Activé',
|
||||
daybreakHint: 'Accès de confiance OpenAI avec moins de garde-fous, pour cette route uniquement.',
|
||||
daybreakUnlisted:
|
||||
'Ce modèle n’est pas listé pour Daybreak sur votre compte. Désactivez-le ou choisissez un autre modèle.',
|
||||
daybreakNeedsPlugin:
|
||||
'Votre plugin Advanced Delegation ne gère pas encore les routes Daybreak. Mettez-le à jour ou désactivez ceci.',
|
||||
optional: 'facultatif',
|
||||
errors: { required: 'Obligatoire', spaces: "Pas d'espaces", duplicate: 'Une autre route utilise déjà ce nom' },
|
||||
saveRoute: 'Enregistrer la route',
|
||||
|
||||
@@ -74,6 +74,13 @@ export const jaDelegationRouting: DelegationRoutingTranslations = {
|
||||
tierFast: '高速',
|
||||
tierUltrafast: '超高速',
|
||||
tierHint: '対応しているモデルのみ。',
|
||||
daybreakLabel: 'Daybreak',
|
||||
daybreakOff: 'オフ',
|
||||
daybreakOn: 'オン',
|
||||
daybreakHint: 'ガードレールを緩和した OpenAI の信頼済みアクセス(このルートのみ)。',
|
||||
daybreakUnlisted: 'このモデルはお使いのアカウントで Daybreak の対象外です。オフにするか別のモデルを選んでください。',
|
||||
daybreakNeedsPlugin:
|
||||
'お使いの Advanced Delegation プラグインはまだ Daybreak ルートに対応していません。更新するか、オフにしてください。',
|
||||
optional: '任意',
|
||||
errors: { required: '必須', spaces: 'スペースは使えません', duplicate: 'この名前は別のルートで使われています' },
|
||||
saveRoute: 'ルートを保存',
|
||||
|
||||
@@ -74,6 +74,13 @@ export const ruDelegationRouting: DelegationRoutingTranslations = {
|
||||
tierFast: 'Быстрая',
|
||||
tierUltrafast: 'Сверхбыстрая',
|
||||
tierHint: 'Только для моделей, которые это поддерживают.',
|
||||
daybreakLabel: 'Daybreak',
|
||||
daybreakOff: 'Выкл.',
|
||||
daybreakOn: 'Вкл.',
|
||||
daybreakHint: 'Доверенный доступ OpenAI с ослабленными ограничениями, только для этого маршрута.',
|
||||
daybreakUnlisted: 'Эта модель не указана для Daybreak в вашем аккаунте. Выключите его или выберите другую модель.',
|
||||
daybreakNeedsPlugin:
|
||||
'Ваш плагин Advanced Delegation пока не поддерживает маршруты Daybreak. Обновите его или выключите это.',
|
||||
optional: 'необязательно',
|
||||
errors: { required: 'Обязательно', spaces: 'Без пробелов', duplicate: 'Это название уже использует другой маршрут' },
|
||||
saveRoute: 'Сохранить маршрут',
|
||||
|
||||
@@ -59,6 +59,12 @@ export interface DelegationRoutingTranslations {
|
||||
tierFast: string
|
||||
tierUltrafast: string
|
||||
tierHint: string
|
||||
daybreakLabel: string
|
||||
daybreakOff: string
|
||||
daybreakOn: string
|
||||
daybreakHint: string
|
||||
daybreakUnlisted: string
|
||||
daybreakNeedsPlugin: string
|
||||
optional: string
|
||||
errors: { required: string; spaces: string; duplicate: string }
|
||||
saveRoute: string
|
||||
|
||||
@@ -66,6 +66,12 @@ export const zhHantDelegationRouting: DelegationRoutingTranslations = {
|
||||
tierFast: '快速',
|
||||
tierUltrafast: '極速',
|
||||
tierHint: '僅適用於提供此選項的模型。',
|
||||
daybreakLabel: 'Daybreak',
|
||||
daybreakOff: '關閉',
|
||||
daybreakOn: '開啟',
|
||||
daybreakHint: 'OpenAI 受信存取,限制較少,僅用於此路由。',
|
||||
daybreakUnlisted: '你的帳戶未為此模型列出 Daybreak。請關閉它或選擇其他模型。',
|
||||
daybreakNeedsPlugin: '你的 Advanced Delegation 外掛尚不支援 Daybreak 路由。請更新外掛,或關閉此項。',
|
||||
optional: '選填',
|
||||
errors: { required: '必填', spaces: '不可包含空格', duplicate: '其他路由已使用此名稱' },
|
||||
saveRoute: '儲存路由',
|
||||
|
||||
@@ -66,6 +66,12 @@ export const zhDelegationRouting: DelegationRoutingTranslations = {
|
||||
tierFast: '快速',
|
||||
tierUltrafast: '极速',
|
||||
tierHint: '仅适用于提供该选项的模型。',
|
||||
daybreakLabel: 'Daybreak',
|
||||
daybreakOff: '关闭',
|
||||
daybreakOn: '开启',
|
||||
daybreakHint: 'OpenAI 受信访问,限制更少,仅用于此路由。',
|
||||
daybreakUnlisted: '你的账户未为此模型列出 Daybreak。请关闭它或选择其他模型。',
|
||||
daybreakNeedsPlugin: '你的 Advanced Delegation 插件尚不支持 Daybreak 路由。请更新插件,或关闭此项。',
|
||||
optional: '可选',
|
||||
errors: { required: '必填', spaces: '不能包含空格', duplicate: '其他路由已使用此名称' },
|
||||
saveRoute: '保存路由',
|
||||
|
||||
@@ -3912,6 +3912,12 @@
|
||||
"settings.model.subagents.routing.blockedUnavailable",
|
||||
"settings.model.subagents.routing.blockedUnregistered",
|
||||
"settings.model.subagents.routing.createRoute",
|
||||
"settings.model.subagents.routing.daybreakHint",
|
||||
"settings.model.subagents.routing.daybreakLabel",
|
||||
"settings.model.subagents.routing.daybreakNeedsPlugin",
|
||||
"settings.model.subagents.routing.daybreakOff",
|
||||
"settings.model.subagents.routing.daybreakOn",
|
||||
"settings.model.subagents.routing.daybreakUnlisted",
|
||||
"settings.model.subagents.routing.defaultBadge",
|
||||
"settings.model.subagents.routing.deleteConfirmAction",
|
||||
"settings.model.subagents.routing.deleteConfirmBody",
|
||||
|
||||
Reference in new issue
Block a user