155 lines
5.6 KiB
TypeScript
155 lines
5.6 KiB
TypeScript
import { useCallback, useEffect, useMemo, useState } from 'preact/hooks';
|
|
import styles from './header.module.css';
|
|
import { Connection, HORDE_ANON_KEY, type IConnection, type IHordeModel } from '../../tools/connection';
|
|
import { useInputState } from '@common/hooks/useInputState';
|
|
import { useInputCallback } from '@common/hooks/useInputCallback';
|
|
import { Huggingface } from '../../tools/huggingface';
|
|
import { INSTRUCTS } from '../../contexts/state';
|
|
|
|
interface IProps {
|
|
connection: IConnection;
|
|
setConnection: (c: IConnection) => void;
|
|
}
|
|
|
|
export const ConnectionEditor = ({ connection, setConnection }: IProps) => {
|
|
// kobold
|
|
const [connectionUrl, setConnectionUrl] = useInputState('');
|
|
// horde
|
|
const [apiKey, setApiKey] = useInputState(HORDE_ANON_KEY);
|
|
const [modelName, setModelName] = useInputState('');
|
|
|
|
const [instruct, setInstruct] = useInputState('');
|
|
const [modelTemplate, setModelTemplate] = useInputState('');
|
|
const [hordeModels, setHordeModels] = useState<IHordeModel[]>([]);
|
|
const [contextLength, setContextLength] = useState<number>(0);
|
|
|
|
const isOnline = useMemo(() => contextLength > 0, [contextLength]);
|
|
|
|
useEffect(() => {
|
|
setInstruct(connection.instruct);
|
|
connection.url && setConnectionUrl(connection.url);
|
|
connection.model && setModelName(connection.model);
|
|
setApiKey(connection.apiKey || HORDE_ANON_KEY);
|
|
|
|
if (connection.type === 'kobold') {
|
|
Connection.getContextLength(connection).then(setContextLength);
|
|
} else if (connection.type === 'horde') {
|
|
Connection.getHordeModels()
|
|
.then(m => setHordeModels(Array.from(
|
|
m.values())
|
|
.sort((a, b) =>
|
|
b.maxContext - a.maxContext || a.name.localeCompare(b.name)
|
|
)
|
|
));
|
|
}
|
|
}, [connection]);
|
|
|
|
useEffect(() => {
|
|
if (modelName) {
|
|
Huggingface.findModelTemplate(modelName)
|
|
.then(template => {
|
|
if (template) {
|
|
setModelTemplate(template);
|
|
}
|
|
});
|
|
}
|
|
}, [modelName]);
|
|
|
|
const setBackendType = useInputCallback((type) => {
|
|
switch (type) {
|
|
case 'kobold':
|
|
case 'horde':
|
|
setConnection({
|
|
type,
|
|
instruct,
|
|
url: connectionUrl,
|
|
apiKey,
|
|
model: modelName,
|
|
});
|
|
break;
|
|
}
|
|
}, [setConnection, connectionUrl, apiKey, modelName, instruct]);
|
|
|
|
const handleSetInstruct = useInputCallback((instruct: string) => {
|
|
setConnection({ ...connection, instruct });
|
|
}, [setConnection, connection]);
|
|
|
|
const handleBlurUrl = useCallback(() => {
|
|
const regex = /^(?:http(s?):\/\/)?(.*?)\/?$/i;
|
|
const url = connectionUrl.replace(regex, 'http$1://$2');
|
|
|
|
setConnection({
|
|
type: 'kobold',
|
|
instruct,
|
|
url,
|
|
apiKey,
|
|
model: modelName,
|
|
});
|
|
}, [connectionUrl, instruct, setConnection]);
|
|
|
|
const handleBlurHorde = useCallback(() => {
|
|
setConnection({
|
|
type: 'horde',
|
|
instruct,
|
|
url: connectionUrl,
|
|
apiKey,
|
|
model: modelName,
|
|
});
|
|
}, [apiKey, modelName, instruct, setConnection]);
|
|
|
|
return (
|
|
<div class={styles.connectionEditor}>
|
|
<select value={connection.type} onChange={setBackendType}>
|
|
<option value='kobold'>Kobold CPP</option>
|
|
<option value='horde'>Horde</option>
|
|
</select>
|
|
<select value={instruct} onChange={handleSetInstruct} title='Instruct template'>
|
|
{modelName && modelTemplate && <optgroup label='Native model template'>
|
|
<option value={modelTemplate} title='Native for model'>{modelName}</option>
|
|
</optgroup>}
|
|
<optgroup label='Manual templates'>
|
|
{Object.entries(INSTRUCTS).map(([label, value]) => (
|
|
<option value={value} key={value}>
|
|
{label}
|
|
</option>
|
|
))}
|
|
</optgroup>
|
|
{instruct !== modelTemplate
|
|
&& !Object.values(INSTRUCTS).includes(instruct)
|
|
&& <optgroup label='Custom'>
|
|
<option value={connection.instruct}>Custom</option>
|
|
</optgroup>
|
|
}
|
|
</select>
|
|
{connection.type === 'kobold' && <input
|
|
value={connectionUrl}
|
|
onInput={setConnectionUrl}
|
|
onBlur={handleBlurUrl}
|
|
class={isOnline ? styles.valid : styles.invalid}
|
|
/>}
|
|
{connection.type === 'horde' && <>
|
|
<input
|
|
placeholder='Horde API key'
|
|
title='Horde API key'
|
|
value={apiKey}
|
|
onInput={setApiKey}
|
|
onBlur={handleBlurHorde}
|
|
/>
|
|
|
|
<select
|
|
value={modelName}
|
|
onChange={setModelName}
|
|
onBlur={handleBlurHorde}
|
|
title='Horde model'
|
|
>
|
|
{hordeModels.map((m) => (
|
|
<option value={m.name} key={m.name}>
|
|
{m.name} ({m.maxLength}/{m.maxContext})
|
|
</option>
|
|
))}
|
|
</select>
|
|
</>}
|
|
</div>
|
|
);
|
|
};
|