164 lines
5.6 KiB
TypeScript
164 lines
5.6 KiB
TypeScript
import { useId, useState } from 'react'
|
|
import { useTranslation } from 'react-i18next'
|
|
|
|
import { Dialog } from '@/components/dialog'
|
|
import { Button } from '@/components/ui/button'
|
|
import { Label } from '@/components/ui/label'
|
|
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'
|
|
import { Switch } from '@/components/ui/switch'
|
|
|
|
import type { RoutingPolicy } from '../types'
|
|
|
|
type Props = {
|
|
policy: RoutingPolicy
|
|
onSave: (policy: RoutingPolicy) => void
|
|
onClose: () => void
|
|
}
|
|
|
|
export function AutomaticRoutingSettings(props: Props) {
|
|
const { t } = useTranslation()
|
|
const id = useId()
|
|
const [preference, setPreference] = useState(
|
|
props.policy.auto_preference ?? 'cost'
|
|
)
|
|
const [allowDegraded, setAllowDegraded] = useState(
|
|
props.policy.allow_degraded ?? false
|
|
)
|
|
const [skipNonAstra, setSkipNonAstra] = useState(
|
|
props.policy.skip_degraded_non_astra ?? false
|
|
)
|
|
return (
|
|
<Dialog
|
|
open
|
|
onOpenChange={(open) => {
|
|
if (!open) props.onClose()
|
|
}}
|
|
title={t('Automatic group settings')}
|
|
description={t(
|
|
'Automatically choose from all groups you can access that support the requested model.'
|
|
)}
|
|
footer={
|
|
<>
|
|
<Button type='button' variant='outline' onClick={props.onClose}>
|
|
{t('Cancel')}
|
|
</Button>
|
|
<Button
|
|
type='button'
|
|
onClick={() => {
|
|
props.onSave({
|
|
...props.policy,
|
|
auto_preference: preference,
|
|
allow_degraded: allowDegraded,
|
|
skip_degraded_non_astra: skipNonAstra,
|
|
})
|
|
props.onClose()
|
|
}}
|
|
>
|
|
{t('Save')}
|
|
</Button>
|
|
</>
|
|
}
|
|
>
|
|
<div className='space-y-5'>
|
|
<RadioGroup
|
|
value={preference}
|
|
aria-label={t('Routing preference')}
|
|
onValueChange={(value) => {
|
|
if (value === 'cost' || value === 'speed' || value === 'stable') {
|
|
setPreference(value)
|
|
}
|
|
}}
|
|
>
|
|
<Label
|
|
htmlFor={`${id}-cost`}
|
|
className='flex cursor-pointer items-start gap-3 rounded-lg border p-4'
|
|
>
|
|
<RadioGroupItem id={`${id}-cost`} value='cost' />
|
|
<span>
|
|
<span className='block font-medium'>{t('Low cost first')}</span>
|
|
<span className='text-muted-foreground mt-1 block text-xs'>
|
|
{t(
|
|
'Prefer your lowest effective group multiplier, including personal overrides.'
|
|
)}
|
|
</span>
|
|
</span>
|
|
</Label>
|
|
<Label
|
|
htmlFor={`${id}-speed`}
|
|
className='flex cursor-pointer items-start gap-3 rounded-lg border p-4'
|
|
>
|
|
<RadioGroupItem id={`${id}-speed`} value='speed' />
|
|
<span>
|
|
<span className='block font-medium'>
|
|
{t('Fast response first')}
|
|
</span>
|
|
<span className='text-muted-foreground mt-1 block text-xs'>
|
|
{t(
|
|
'Prefer recent streaming model-test results: latest valid first-token time per channel within 1 hour, averaged across the group. Untested groups rank last.'
|
|
)}
|
|
</span>
|
|
</span>
|
|
</Label>
|
|
<Label
|
|
htmlFor={`${id}-stable`}
|
|
className='flex cursor-pointer items-start gap-3 rounded-lg border p-4'
|
|
>
|
|
<RadioGroupItem id={`${id}-stable`} value='stable' />
|
|
<span>
|
|
<span className='block font-medium'>{t('Stability first')}</span>
|
|
<span className='text-muted-foreground mt-1 block text-xs'>
|
|
{t(
|
|
'Prefer the highest recent request success rate for this model.'
|
|
)}
|
|
</span>
|
|
</span>
|
|
</Label>
|
|
</RadioGroup>
|
|
<div className='flex items-center justify-between gap-4 rounded-lg border p-4'>
|
|
<div className='space-y-1'>
|
|
<Label htmlFor={`${id}-degraded`}>
|
|
{t('Skip suspected degraded groups')}
|
|
</Label>
|
|
<p className='text-muted-foreground text-xs'>
|
|
{t(
|
|
'Enabled by default for gpt-6-astra. Keep the last valid fingerprint and pelican conclusions until recovery or reset. Failed checks do not clear them.'
|
|
)}
|
|
</p>
|
|
</div>
|
|
<Switch
|
|
id={`${id}-degraded`}
|
|
checked={!allowDegraded}
|
|
onCheckedChange={(checked) => setAllowDegraded(!checked)}
|
|
/>
|
|
</div>
|
|
<div className='flex items-center justify-between gap-4 rounded-lg border p-4'>
|
|
<div className='space-y-1'>
|
|
<Label htmlFor={`${id}-non-astra`}>
|
|
{t('Skip non-Astra requests too')}
|
|
</Label>
|
|
<p className='text-muted-foreground text-xs'>
|
|
{t(
|
|
'Uses the group Astra fingerprint and pelican checks; other models have not been individually tested. Off by default.'
|
|
)}
|
|
</p>
|
|
</div>
|
|
<Switch
|
|
id={`${id}-non-astra`}
|
|
checked={skipNonAstra}
|
|
disabled={allowDegraded}
|
|
onCheckedChange={setSkipNonAstra}
|
|
/>
|
|
</div>
|
|
<p className='text-muted-foreground text-xs'>
|
|
{t(
|
|
'Speed uses recent streaming model tests; stability uses request statistics. Price uses effective multipliers.'
|
|
)}
|
|
</p>
|
|
<p className='text-muted-foreground text-xs'>
|
|
{t('Settings take effect after saving the API key.')}
|
|
</p>
|
|
</div>
|
|
</Dialog>
|
|
)
|
|
}
|