150 lines
5.5 KiB
TypeScript
150 lines
5.5 KiB
TypeScript
import { useCallback, useEffect, useMemo, useState } from 'preact/hooks';
|
|
import styles from './header.module.css';
|
|
import { Connection, HORDE_ANON_KEY, isHordeConnection, isKoboldConnection, type IConnection, type IHordeModel } from '../../tools/connection';
|
|
import { Instruct } from '../../contexts/state';
|
|
import { useInputState } from '@common/hooks/useInputState';
|
|
import { useInputCallback } from '@common/hooks/useInputCallback';
|
|
import { Huggingface } from '../../tools/huggingface';
|
|
|
|
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 backendType = useMemo(() => {
|
|
if (isKoboldConnection(connection)) return 'kobold';
|
|
if (isHordeConnection(connection)) return 'horde';
|
|
return 'unknown';
|
|
}, [connection]);
|
|
|
|
const isOnline = useMemo(() => contextLength > 0, [contextLength]);
|
|
|
|
useEffect(() => {
|
|
setInstruct(connection.instruct);
|
|
|
|
if (isKoboldConnection(connection)) {
|
|
setConnectionUrl(connection.url);
|
|
Connection.getContextLength(connection).then(setContextLength);
|
|
} else if (isHordeConnection(connection)) {
|
|
setModelName(connection.model);
|
|
setApiKey(connection.apiKey || HORDE_ANON_KEY);
|
|
|
|
Connection.getHordeModels()
|
|
.then(m => setHordeModels(Array.from(m.values()).sort((a, b) => a.name.localeCompare(b.name))));
|
|
}
|
|
}, [connection]);
|
|
|
|
useEffect(() => {
|
|
if (modelName) {
|
|
Huggingface.findModelTemplate(modelName)
|
|
.then(template => {
|
|
if (template) {
|
|
setModelTemplate(template);
|
|
setInstruct(template);
|
|
}
|
|
});
|
|
}
|
|
}, [modelName]);
|
|
|
|
const setBackendType = useInputCallback((type) => {
|
|
if (type === 'kobold') {
|
|
setConnection({
|
|
instruct,
|
|
url: connectionUrl,
|
|
});
|
|
} else if (type === 'horde') {
|
|
setConnection({
|
|
instruct,
|
|
apiKey,
|
|
model: modelName,
|
|
});
|
|
}
|
|
}, [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({
|
|
instruct,
|
|
url,
|
|
});
|
|
}, [connectionUrl, instruct, setConnection]);
|
|
|
|
const handleBlurHorde = useCallback(() => {
|
|
setConnection({
|
|
instruct,
|
|
apiKey,
|
|
model: modelName,
|
|
});
|
|
}, [apiKey, modelName, instruct, setConnection]);
|
|
|
|
return (
|
|
<div class={styles.connectionEditor}>
|
|
<select value={backendType} 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(Instruct).map(([label, value]) => (
|
|
<option value={value} key={value}>
|
|
{label.toLowerCase()}
|
|
</option>
|
|
))}
|
|
</optgroup>
|
|
{instruct !== modelTemplate && <optgroup label='Custom'>
|
|
<option value={connection.instruct}>Custom</option>
|
|
</optgroup>}
|
|
</select>
|
|
{isKoboldConnection(connection) && <input
|
|
value={connectionUrl}
|
|
onInput={setConnectionUrl}
|
|
onBlur={handleBlurUrl}
|
|
class={isOnline ? styles.valid : styles.invalid}
|
|
/>}
|
|
{isHordeConnection(connection) && <>
|
|
<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>
|
|
);
|
|
};
|