import React, { FC, useEffect, useRef } from 'react'; import { observer } from 'mobx-react-lite'; import { WorkHeader } from '../components/WorkHeader'; import { Button, Dropdown, Input, Option, Textarea } from '@fluentui/react-components'; import { Labeled } from '../components/Labeled'; import { ValuedSlider } from '../components/ValuedSlider'; import { useTranslation } from 'react-i18next'; import commonStore, { ModelStatus } from '../stores/commonStore'; import { fetchEventSource } from '@microsoft/fetch-event-source'; import { toast } from 'react-toastify'; import { DialogButton } from '../components/DialogButton'; import { PresetsButton } from './PresetsManager/PresetsButton'; import { ToolTipButton } from '../components/ToolTipButton'; import { ArrowSync20Regular } from '@fluentui/react-icons'; import { defaultPresets } from './defaultConfigs'; import { CompletionParams, CompletionPreset } from '../types/completion'; import { getServerRoot } from '../utils'; let completionSseController: AbortController | null = null; const CompletionPanel: FC = observer(() => { const { t } = useTranslation(); const inputRef = useRef(null); const port = commonStore.getCurrentModelConfig().apiParameters.apiPort; const scrollToBottom = () => { if (inputRef.current) inputRef.current.scrollTop = inputRef.current.scrollHeight; }; useEffect(() => { if (inputRef.current) { inputRef.current.style.height = '100%'; inputRef.current.style.maxHeight = '100%'; } scrollToBottom(); }, []); const setPreset = (preset: CompletionPreset) => { commonStore.setCompletionSubmittedPrompt(t(preset.prompt)); commonStore.setCompletionPreset({ ...preset, prompt: t(preset.prompt) }); }; if (!commonStore.completionPreset) setPreset(defaultPresets[0]); const name = commonStore.completionPreset!.name; const prompt = commonStore.completionPreset!.prompt; const setPrompt = (prompt: string) => { commonStore.setCompletionPreset({ ...commonStore.completionPreset!, prompt }); }; const params = commonStore.completionPreset!.params; const setParams = (newParams: Partial) => { commonStore.setCompletionPreset({ ...commonStore.completionPreset!, params: { ...commonStore.completionPreset!.params, ...newParams } }); }; const onSubmit = (prompt: string) => { commonStore.setCompletionSubmittedPrompt(prompt); if (commonStore.status.status === ModelStatus.Offline && !commonStore.settings.apiUrl && commonStore.platform !== 'web') { toast(t('Please click the button in the top right corner to start the model'), { type: 'warning' }); commonStore.setCompletionGenerating(false); return; } prompt += params.injectStart.replaceAll('\\n', '\n'); let answer = ''; completionSseController = new AbortController(); fetchEventSource( // https://api.openai.com/v1/completions || http://127.0.0.1:${port}/v1/completions getServerRoot(port, true) + '/v1/completions', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${commonStore.settings.apiKey}` }, body: JSON.stringify({ prompt, stream: true, model: commonStore.settings.apiCompletionModelName, // 'text-davinci-003' max_tokens: params.maxResponseToken, temperature: params.temperature, top_p: params.topP, presence_penalty: params.presencePenalty, frequency_penalty: params.frequencyPenalty, stop: params.stop.replaceAll('\\n', '\n') || undefined }), signal: completionSseController?.signal, onmessage(e) { scrollToBottom(); if (e.data.trim() === '[DONE]') { commonStore.setCompletionGenerating(false); return; } let data; try { data = JSON.parse(e.data); } catch (error) { console.debug('json error', error); return; } if (data.model) commonStore.setLastModelName(data.model); if (data.choices && Array.isArray(data.choices) && data.choices.length > 0) { answer += data.choices[0]?.text || data.choices[0]?.delta?.content || ''; setPrompt(prompt + answer.replace(/\s+$/, '') + params.injectEnd.replaceAll('\\n', '\n')); } }, async onopen(response) { if (response.status !== 200) { toast(response.statusText + '\n' + (await response.text()), { type: 'error' }); } }, onclose() { console.log('Connection closed'); }, onerror(err) { err = err.message || err; if (err && !err.includes('ReadableStreamDefaultReader')) toast(err, { type: 'error' }); commonStore.setCompletionGenerating(false); throw err; } }); }; return (