F: Rework dialog for AI Prompts
This commit is contained in:
parent
f27d9df8d3
commit
ab82f1aaed
|
@ -0,0 +1,81 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
import { TabLabel, TabList, TabPanel, Tabs } from '@/components/tabs';
|
||||||
|
|
||||||
|
import { usePromptTemplateSuspense } from '../../backend/use-prompt-template';
|
||||||
|
import { PromptVariableType } from '../../models/prompting';
|
||||||
|
import { extractPromptVariables } from '../../models/prompting-api';
|
||||||
|
import { evaluatePromptVariable, useAIStore } from '../../stores/ai-context';
|
||||||
|
|
||||||
|
import { MenuAIPrompt } from './menu-ai-prompt';
|
||||||
|
import { TabPromptEdit } from './tab-prompt-edit';
|
||||||
|
import { TabPromptResult } from './tab-prompt-result';
|
||||||
|
import { TabPromptVariables } from './tab-prompt-variables';
|
||||||
|
|
||||||
|
interface AIPromptTabsProps {
|
||||||
|
promptID: number;
|
||||||
|
activeTab: number;
|
||||||
|
setActiveTab: (value: TabID) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TabID = {
|
||||||
|
TEMPLATE: 0,
|
||||||
|
RESULT: 1,
|
||||||
|
VARIABLES: 2
|
||||||
|
} as const;
|
||||||
|
type TabID = (typeof TabID)[keyof typeof TabID];
|
||||||
|
|
||||||
|
export function AIPromptTabs({ promptID, activeTab, setActiveTab }: AIPromptTabsProps) {
|
||||||
|
const context = useAIStore();
|
||||||
|
const { promptTemplate } = usePromptTemplateSuspense(promptID);
|
||||||
|
const [text, setText] = useState(promptTemplate.text);
|
||||||
|
const variables = extractPromptVariables(text);
|
||||||
|
|
||||||
|
const generatedPrompt = (() => {
|
||||||
|
let result = text;
|
||||||
|
for (const variable of variables) {
|
||||||
|
const type = Object.values(PromptVariableType).find(t => t === variable);
|
||||||
|
let value = '';
|
||||||
|
if (type) {
|
||||||
|
value = evaluatePromptVariable(type, context) ?? '';
|
||||||
|
}
|
||||||
|
result = result.replace(new RegExp(`\{\{${variable}\}\}`, 'g'), value || `${variable}`);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
})();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setText(promptTemplate.text);
|
||||||
|
}, [promptTemplate]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tabs selectedIndex={activeTab} onSelect={index => setActiveTab(index as TabID)}>
|
||||||
|
<TabList className='mx-auto w-fit flex border-x border-b divide-x rounded-none'>
|
||||||
|
<MenuAIPrompt promptID={promptID} generatedPrompt={generatedPrompt} />
|
||||||
|
|
||||||
|
<TabLabel label='Шаблон' />
|
||||||
|
<TabLabel label='Результат' />
|
||||||
|
<TabLabel label='Переменные' />
|
||||||
|
</TabList>
|
||||||
|
|
||||||
|
<div className='h-80 flex flex-col gap-2'>
|
||||||
|
<TabPanel>
|
||||||
|
<TabPromptEdit
|
||||||
|
text={text}
|
||||||
|
setText={setText}
|
||||||
|
label={promptTemplate.label}
|
||||||
|
description={promptTemplate.description}
|
||||||
|
/>
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel>
|
||||||
|
<TabPromptResult prompt={generatedPrompt} />
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel>
|
||||||
|
<TabPromptVariables template={text} />
|
||||||
|
</TabPanel>
|
||||||
|
</div>
|
||||||
|
</Tabs>
|
||||||
|
);
|
||||||
|
}
|
|
@ -3,36 +3,18 @@ import { Suspense, useState } from 'react';
|
||||||
import { ComboBox } from '@/components/input/combo-box';
|
import { ComboBox } from '@/components/input/combo-box';
|
||||||
import { Loader } from '@/components/loader';
|
import { Loader } from '@/components/loader';
|
||||||
import { ModalView } from '@/components/modal';
|
import { ModalView } from '@/components/modal';
|
||||||
import { TabLabel, TabList, TabPanel, Tabs } from '@/components/tabs';
|
|
||||||
|
|
||||||
import { useAvailableTemplatesSuspense } from '../../backend/use-available-templates';
|
import { useAvailableTemplatesSuspense } from '../../backend/use-available-templates';
|
||||||
|
|
||||||
import { TabPromptResult } from './tab-prompt-result';
|
import { AIPromptTabs, TabID } from './ai-prompt-tabs';
|
||||||
import { TabPromptSelect } from './tab-prompt-select';
|
|
||||||
import { TabPromptVariables } from './tab-prompt-variables';
|
|
||||||
|
|
||||||
export const TabID = {
|
|
||||||
SELECT: 0,
|
|
||||||
RESULT: 1,
|
|
||||||
VARIABLES: 2
|
|
||||||
} as const;
|
|
||||||
type TabID = (typeof TabID)[keyof typeof TabID];
|
|
||||||
|
|
||||||
export function DlgAIPromptDialog() {
|
export function DlgAIPromptDialog() {
|
||||||
const [activeTab, setActiveTab] = useState<TabID>(TabID.SELECT);
|
const [activeTab, setActiveTab] = useState<number>(TabID.TEMPLATE);
|
||||||
const [selected, setSelected] = useState<number | null>(null);
|
const [selected, setSelected] = useState<number | null>(null);
|
||||||
const { items: prompts } = useAvailableTemplatesSuspense();
|
const { items: prompts } = useAvailableTemplatesSuspense();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalView header='Генератор запросом LLM' className='w-100 sm:w-160 px-6'>
|
<ModalView header='Генератор запросом LLM' className='w-100 sm:w-160 px-6 flex flex-col h-120'>
|
||||||
<Tabs selectedIndex={activeTab} onSelect={index => setActiveTab(index as TabID)}>
|
|
||||||
<TabList className='mb-3 mx-auto w-fit flex border divide-x rounded-none'>
|
|
||||||
<TabLabel label='Шаблон' />
|
|
||||||
<TabLabel label='Результат' disabled={!selected} />
|
|
||||||
<TabLabel label='Переменные' disabled={!selected} />
|
|
||||||
</TabList>
|
|
||||||
|
|
||||||
<div className='h-120 flex flex-col gap-2'>
|
|
||||||
<ComboBox
|
<ComboBox
|
||||||
id='prompt-select'
|
id='prompt-select'
|
||||||
items={prompts}
|
items={prompts}
|
||||||
|
@ -45,12 +27,8 @@ export function DlgAIPromptDialog() {
|
||||||
className='w-full'
|
className='w-full'
|
||||||
/>
|
/>
|
||||||
<Suspense fallback={<Loader />}>
|
<Suspense fallback={<Loader />}>
|
||||||
<TabPanel>{selected ? <TabPromptSelect promptID={selected} /> : null}</TabPanel>
|
{selected ? <AIPromptTabs promptID={selected} activeTab={activeTab} setActiveTab={setActiveTab} /> : null}
|
||||||
<TabPanel>{selected ? <TabPromptResult promptID={selected} /> : null}</TabPanel>
|
|
||||||
<TabPanel>{selected ? <TabPromptVariables promptID={selected} /> : null}</TabPanel>
|
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</div>
|
|
||||||
</Tabs>
|
|
||||||
</ModalView>
|
</ModalView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { toast } from 'react-toastify';
|
||||||
|
|
||||||
|
import { urls, useConceptNavigation } from '@/app';
|
||||||
|
|
||||||
|
import { MiniButton } from '@/components/control';
|
||||||
|
import { IconClone, IconEdit2 } from '@/components/icons';
|
||||||
|
import { useDialogsStore } from '@/stores/dialogs';
|
||||||
|
import { infoMsg } from '@/utils/labels';
|
||||||
|
|
||||||
|
import { PromptTabID } from '../../pages/prompt-templates-page/templates-tabs';
|
||||||
|
|
||||||
|
interface MenuAIPromptProps {
|
||||||
|
promptID: number;
|
||||||
|
generatedPrompt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MenuAIPrompt({ promptID, generatedPrompt }: MenuAIPromptProps) {
|
||||||
|
const router = useConceptNavigation();
|
||||||
|
const hideDialog = useDialogsStore(state => state.hideDialog);
|
||||||
|
|
||||||
|
function navigatePrompt() {
|
||||||
|
hideDialog();
|
||||||
|
router.push({ path: urls.prompt_template(promptID, PromptTabID.EDIT) });
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCopyPrompt() {
|
||||||
|
void navigator.clipboard.writeText(generatedPrompt);
|
||||||
|
toast.success(infoMsg.promptReady);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='flex border-r-2 pr-2'>
|
||||||
|
<MiniButton
|
||||||
|
title='Редактировать шаблон'
|
||||||
|
noHover
|
||||||
|
noPadding
|
||||||
|
icon={<IconEdit2 size='1.25rem' />}
|
||||||
|
className='h-full pl-2 text-muted-foreground hover:text-primary cc-animate-color bg-transparent'
|
||||||
|
onClick={navigatePrompt}
|
||||||
|
/>
|
||||||
|
<MiniButton
|
||||||
|
title='Скопировать результат в буфер обмена'
|
||||||
|
noHover
|
||||||
|
noPadding
|
||||||
|
icon={<IconClone size='1.25rem' />}
|
||||||
|
className='h-full pl-2 text-muted-foreground hover:text-constructive cc-animate-color bg-transparent'
|
||||||
|
onClick={handleCopyPrompt}
|
||||||
|
disabled={!generatedPrompt}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { TextArea } from '@/components/input';
|
||||||
|
|
||||||
|
interface TabPromptEditProps {
|
||||||
|
label: string;
|
||||||
|
description: string;
|
||||||
|
text: string;
|
||||||
|
setText: (value: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TabPromptEdit({ label, description, text, setText }: TabPromptEditProps) {
|
||||||
|
return (
|
||||||
|
<div className='cc-column'>
|
||||||
|
<div className='flex flex-col gap-2'>
|
||||||
|
<TextArea
|
||||||
|
id='prompt-label'
|
||||||
|
label='Название' //
|
||||||
|
value={label}
|
||||||
|
disabled
|
||||||
|
noResize
|
||||||
|
rows={1}
|
||||||
|
/>
|
||||||
|
<TextArea id='prompt-description' label='Описание' value={description} disabled noResize rows={3} />
|
||||||
|
<TextArea
|
||||||
|
id='prompt-text' //
|
||||||
|
label='Текст шаблона'
|
||||||
|
value={text}
|
||||||
|
onChange={event => setText(event.target.value)}
|
||||||
|
noResize
|
||||||
|
rows={8}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,65 +1,17 @@
|
||||||
import { useMemo } from 'react';
|
|
||||||
import { toast } from 'react-toastify';
|
|
||||||
|
|
||||||
import { MiniButton } from '@/components/control';
|
|
||||||
import { IconClone } from '@/components/icons';
|
|
||||||
import { TextArea } from '@/components/input';
|
import { TextArea } from '@/components/input';
|
||||||
import { infoMsg } from '@/utils/labels';
|
|
||||||
|
|
||||||
import { usePromptTemplateSuspense } from '../../backend/use-prompt-template';
|
|
||||||
import { PromptVariableType } from '../../models/prompting';
|
|
||||||
import { extractPromptVariables } from '../../models/prompting-api';
|
|
||||||
import { evaluatePromptVariable, useAIStore } from '../../stores/ai-context';
|
|
||||||
|
|
||||||
interface TabPromptResultProps {
|
interface TabPromptResultProps {
|
||||||
promptID: number;
|
prompt: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TabPromptResult({ promptID }: TabPromptResultProps) {
|
export function TabPromptResult({ prompt }: TabPromptResultProps) {
|
||||||
const { promptTemplate } = usePromptTemplateSuspense(promptID);
|
|
||||||
const context = useAIStore();
|
|
||||||
const variables = useMemo(() => {
|
|
||||||
return promptTemplate ? extractPromptVariables(promptTemplate.text) : [];
|
|
||||||
}, [promptTemplate]);
|
|
||||||
|
|
||||||
const generatedMessage = (() => {
|
|
||||||
if (!promptTemplate) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
let result = promptTemplate.text;
|
|
||||||
for (const variable of variables) {
|
|
||||||
const type = Object.values(PromptVariableType).find(t => t === variable);
|
|
||||||
let value = '';
|
|
||||||
if (type) {
|
|
||||||
value = evaluatePromptVariable(type, context) ?? '';
|
|
||||||
}
|
|
||||||
result = result.replace(new RegExp(`\{\{${variable}\}\}`, 'g'), value || `${variable}`);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
})();
|
|
||||||
|
|
||||||
function handleCopyPrompt() {
|
|
||||||
void navigator.clipboard.writeText(generatedMessage);
|
|
||||||
toast.success(infoMsg.promptReady);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='relative'>
|
|
||||||
<MiniButton
|
|
||||||
title='Скопировать в буфер обмена'
|
|
||||||
className='absolute -top-23 left-0'
|
|
||||||
icon={<IconClone size='1.25rem' className='icon-green' />}
|
|
||||||
onClick={handleCopyPrompt}
|
|
||||||
disabled={!generatedMessage}
|
|
||||||
/>
|
|
||||||
<TextArea
|
<TextArea
|
||||||
aria-label='Сгенерированное сообщение'
|
aria-label='Сгенерированное сообщение'
|
||||||
value={generatedMessage}
|
value={prompt}
|
||||||
placeholder='Текст шаблона пуст'
|
placeholder='Текст шаблона пуст'
|
||||||
disabled
|
disabled
|
||||||
fitContent
|
className='w-full h-100'
|
||||||
className='w-full max-h-100 min-h-12'
|
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
import { TextArea } from '@/components/input';
|
|
||||||
|
|
||||||
import { usePromptTemplateSuspense } from '../../backend/use-prompt-template';
|
|
||||||
|
|
||||||
interface TabPromptSelectProps {
|
|
||||||
promptID: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function TabPromptSelect({ promptID }: TabPromptSelectProps) {
|
|
||||||
const { promptTemplate } = usePromptTemplateSuspense(promptID);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='cc-column'>
|
|
||||||
{promptTemplate && (
|
|
||||||
<div className='flex flex-col gap-2'>
|
|
||||||
<TextArea
|
|
||||||
id='prompt-label'
|
|
||||||
label='Название' //
|
|
||||||
value={promptTemplate.label}
|
|
||||||
disabled
|
|
||||||
noResize
|
|
||||||
rows={1}
|
|
||||||
/>
|
|
||||||
<TextArea
|
|
||||||
id='prompt-description'
|
|
||||||
label='Описание'
|
|
||||||
value={promptTemplate.description}
|
|
||||||
disabled
|
|
||||||
noResize
|
|
||||||
rows={3}
|
|
||||||
/>
|
|
||||||
<TextArea
|
|
||||||
id='prompt-text' //
|
|
||||||
label='Текст шаблона'
|
|
||||||
value={promptTemplate.text}
|
|
||||||
disabled
|
|
||||||
noResize
|
|
||||||
rows={6}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,18 +1,16 @@
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { usePromptTemplateSuspense } from '../../backend/use-prompt-template';
|
|
||||||
import { describePromptVariable } from '../../labels';
|
import { describePromptVariable } from '../../labels';
|
||||||
import { PromptVariableType } from '../../models/prompting';
|
import { PromptVariableType } from '../../models/prompting';
|
||||||
import { extractPromptVariables } from '../../models/prompting-api';
|
import { extractPromptVariables } from '../../models/prompting-api';
|
||||||
import { useAvailableVariables } from '../../stores/use-available-variables';
|
import { useAvailableVariables } from '../../stores/use-available-variables';
|
||||||
|
|
||||||
interface TabPromptVariablesProps {
|
interface TabPromptVariablesProps {
|
||||||
promptID: number;
|
template: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TabPromptVariables({ promptID }: TabPromptVariablesProps) {
|
export function TabPromptVariables({ template }: TabPromptVariablesProps) {
|
||||||
const { promptTemplate } = usePromptTemplateSuspense(promptID);
|
const variables = extractPromptVariables(template);
|
||||||
const variables = extractPromptVariables(promptTemplate.text);
|
|
||||||
const availableTypes = useAvailableVariables();
|
const availableTypes = useAvailableVariables();
|
||||||
return (
|
return (
|
||||||
<ul>
|
<ul>
|
||||||
|
|
|
@ -4,14 +4,18 @@ const describePromptVariableRecord: Record<PromptVariableType, string> = {
|
||||||
[PromptVariableType.BLOCK]: 'Текущий блок операционной схемы',
|
[PromptVariableType.BLOCK]: 'Текущий блок операционной схемы',
|
||||||
[PromptVariableType.OSS]: 'Текущая операционная схема',
|
[PromptVariableType.OSS]: 'Текущая операционная схема',
|
||||||
[PromptVariableType.SCHEMA]: 'Текущая концептуальная схема',
|
[PromptVariableType.SCHEMA]: 'Текущая концептуальная схема',
|
||||||
[PromptVariableType.CONSTITUENTA]: 'Текущая конституента'
|
[PromptVariableType.SCHEMA_THESAURUS]: 'Термины и определения текущей концептуальной схемы',
|
||||||
|
[PromptVariableType.CONSTITUENTA]: 'Текущая конституента',
|
||||||
|
[PromptVariableType.CONSTITUENTA_SYNTAX_TREE]: 'Синтаксическое дерево конституенты'
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockPromptVariableRecord: Record<PromptVariableType, string> = {
|
const mockPromptVariableRecord: Record<PromptVariableType, string> = {
|
||||||
[PromptVariableType.BLOCK]: 'Пример: Текущий блок операционной схемы',
|
[PromptVariableType.BLOCK]: 'Пример: Текущий блок операционной схемы',
|
||||||
[PromptVariableType.OSS]: 'Пример: Текущая операционная схема',
|
[PromptVariableType.OSS]: 'Пример: Текущая операционная схема',
|
||||||
[PromptVariableType.SCHEMA]: 'Пример: Текущая концептуальная схема',
|
[PromptVariableType.SCHEMA]: 'Пример: Текущая концептуальная схема',
|
||||||
[PromptVariableType.CONSTITUENTA]: 'Пример: Текущая конституента'
|
[PromptVariableType.SCHEMA_THESAURUS]: 'Пример\nТермин1 - Определение1\nТермин2 - Определение2',
|
||||||
|
[PromptVariableType.CONSTITUENTA]: 'Пример: Текущая конституента',
|
||||||
|
[PromptVariableType.CONSTITUENTA_SYNTAX_TREE]: 'Пример синтаксического дерева конституенты'
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Retrieves description for {@link PromptVariableType}. */
|
/** Retrieves description for {@link PromptVariableType}. */
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
import { type IBlock, type IOperationSchema, NodeType } from '@/features/oss/models/oss';
|
||||||
|
import { CstType, type IConstituenta, type IRSForm } from '@/features/rsform';
|
||||||
|
import { labelCstTypification } from '@/features/rsform/labels';
|
||||||
|
import { isBasicConcept } from '@/features/rsform/models/rsform-api';
|
||||||
|
|
||||||
|
import { PARAMETER } from '@/utils/constants';
|
||||||
|
|
||||||
import { mockPromptVariable } from '../labels';
|
import { mockPromptVariable } from '../labels';
|
||||||
|
|
||||||
/** Extracts a list of variables (as string[]) from a target string.
|
/** Extracts a list of variables (as string[]) from a target string.
|
||||||
|
@ -27,3 +34,94 @@ export function generateSample(target: string): string {
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Generates a prompt for a schema variable. */
|
||||||
|
export function varSchema(schema: IRSForm): string {
|
||||||
|
let result = `Название концептуальной схемы: ${schema.title}\n`;
|
||||||
|
result += `[${schema.alias}] Описание: "${schema.description}"\n\n`;
|
||||||
|
result += 'Понятия:\n';
|
||||||
|
schema.items.forEach(item => {
|
||||||
|
result += `${item.alias} - "${labelCstTypification(item)}" - "${item.term_resolved}" - "${
|
||||||
|
item.definition_formal
|
||||||
|
}" - "${item.definition_resolved}" - "${item.convention}"\n`;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generates a prompt for a schema thesaurus variable. */
|
||||||
|
export function varSchemaThesaurus(schema: IRSForm): string {
|
||||||
|
let result = `Название концептуальной схемы: ${schema.title}\n`;
|
||||||
|
result += `[${schema.alias}] Описание: "${schema.description}"\n\n`;
|
||||||
|
result += 'Термины:\n';
|
||||||
|
schema.items.forEach(item => {
|
||||||
|
if (item.cst_type === CstType.AXIOM || item.cst_type === CstType.THEOREM) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (isBasicConcept(item.cst_type)) {
|
||||||
|
result += `${item.term_resolved} - "${item.convention}"\n`;
|
||||||
|
} else {
|
||||||
|
result += `${item.term_resolved} - "${item.definition_resolved}"\n`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generates a prompt for a OSS variable. */
|
||||||
|
export function varOSS(oss: IOperationSchema): string {
|
||||||
|
let result = `Название операционной схемы: ${oss.title}\n`;
|
||||||
|
result += `Сокращение: ${oss.alias}\n`;
|
||||||
|
result += `Описание: ${oss.description}\n`;
|
||||||
|
result += `Блоки: ${oss.blocks.length}\n`;
|
||||||
|
oss.hierarchy.topologicalOrder().forEach(blockID => {
|
||||||
|
const block = oss.itemByNodeID.get(blockID);
|
||||||
|
if (block?.nodeType !== NodeType.BLOCK) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
result += `\nБлок ${block.id}: ${block.title}\n`;
|
||||||
|
result += `Описание: ${block.description}\n`;
|
||||||
|
result += `Предок: "${block.parent}"\n`;
|
||||||
|
});
|
||||||
|
result += `Операции: ${oss.operations.length}\n`;
|
||||||
|
oss.operations.forEach(operation => {
|
||||||
|
result += `\nОперация ${operation.id}: ${operation.alias}\n`;
|
||||||
|
result += `Название: ${operation.title}\n`;
|
||||||
|
result += `Описание: ${operation.description}\n`;
|
||||||
|
result += `Блок: ${operation.parent}\n`;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generates a prompt for a block variable. */
|
||||||
|
export function varBlock(target: IBlock, oss: IOperationSchema): string {
|
||||||
|
const blocks = oss.blocks.filter(block => block.parent === target.id);
|
||||||
|
const operations = oss.operations.filter(operation => operation.parent === target.id);
|
||||||
|
let result = `Название блока: ${target.title}\n`;
|
||||||
|
result += `Описание: "${target.description}"\n`;
|
||||||
|
result += '\nСодержание\n';
|
||||||
|
result += `Блоки: ${blocks.length}\n`;
|
||||||
|
blocks.forEach(block => {
|
||||||
|
result += `\nБлок ${block.id}: ${block.title}\n`;
|
||||||
|
result += `Описание: "${block.description}"\n`;
|
||||||
|
});
|
||||||
|
result += `Операции: ${operations.length}\n`;
|
||||||
|
operations.forEach(operation => {
|
||||||
|
result += `\nОперация ${operation.id}: ${operation.alias}\n`;
|
||||||
|
result += `Название: "${operation.title}"\n`;
|
||||||
|
result += `Описание: "${operation.description}"\n`;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generates a prompt for a constituenta variable. */
|
||||||
|
export function varConstituenta(cst: IConstituenta): string {
|
||||||
|
return JSON.stringify(cst, null, PARAMETER.indentJSON);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generates a prompt for a constituenta syntax tree variable. */
|
||||||
|
export function varSyntaxTree(cst: IConstituenta): string {
|
||||||
|
let result = `Конституента: ${cst.alias}\n`;
|
||||||
|
result += `Формальное выражение: ${cst.definition_formal}\n`;
|
||||||
|
result += `Дерево синтаксического разбора:\n`;
|
||||||
|
result += JSON.stringify(cst.parse.syntaxTree, null, PARAMETER.indentJSON);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
|
@ -1,29 +1,15 @@
|
||||||
/** Represents prompt variable type. */
|
/** Represents prompt variable type. */
|
||||||
export const PromptVariableType = {
|
export const PromptVariableType = {
|
||||||
BLOCK: 'block',
|
BLOCK: 'block',
|
||||||
// BLOCK_TITLE: 'block.title',
|
|
||||||
// BLOCK_DESCRIPTION: 'block.description',
|
|
||||||
// BLOCK_CONTENTS: 'block.contents',
|
|
||||||
|
|
||||||
OSS: 'oss',
|
OSS: 'oss',
|
||||||
// OSS_CONTENTS: 'oss.contents',
|
|
||||||
// OSS_ALIAS: 'oss.alias',
|
|
||||||
// OSS_TITLE: 'oss.title',
|
|
||||||
// OSS_DESCRIPTION: 'oss.description',
|
|
||||||
|
|
||||||
SCHEMA: 'schema',
|
SCHEMA: 'schema',
|
||||||
// SCHEMA_ALIAS: 'schema.alias',
|
SCHEMA_THESAURUS: 'schema.thesaurus',
|
||||||
// SCHEMA_TITLE: 'schema.title',
|
|
||||||
// SCHEMA_DESCRIPTION: 'schema.description',
|
|
||||||
// SCHEMA_THESAURUS: 'schema.thesaurus',
|
|
||||||
// SCHEMA_GRAPH: 'schema.graph',
|
// SCHEMA_GRAPH: 'schema.graph',
|
||||||
// SCHEMA_TYPE_GRAPH: 'schema.type-graph',
|
// SCHEMA_TYPE_GRAPH: 'schema.type-graph',
|
||||||
|
|
||||||
CONSTITUENTA: 'constituenta'
|
CONSTITUENTA: 'constituenta',
|
||||||
// CONSTITUENTA_ALIAS: 'constituent.alias',
|
CONSTITUENTA_SYNTAX_TREE: 'constituent.ast'
|
||||||
// CONSTITUENTA_CONVENTION: 'constituent.convention',
|
|
||||||
// CONSTITUENTA_DEFINITION: 'constituent.definition',
|
|
||||||
// CONSTITUENTA_DEFINITION_FORMAL: 'constituent.definition-formal',
|
|
||||||
// CONSTITUENTA_EXPRESSION_TREE: 'constituent.expression-tree'
|
|
||||||
} as const;
|
} as const;
|
||||||
export type PromptVariableType = (typeof PromptVariableType)[keyof typeof PromptVariableType];
|
export type PromptVariableType = (typeof PromptVariableType)[keyof typeof PromptVariableType];
|
||||||
|
|
|
@ -2,9 +2,16 @@ import { create } from 'zustand';
|
||||||
|
|
||||||
import { type IBlock, type IOperationSchema } from '@/features/oss/models/oss';
|
import { type IBlock, type IOperationSchema } from '@/features/oss/models/oss';
|
||||||
import { type IConstituenta, type IRSForm } from '@/features/rsform';
|
import { type IConstituenta, type IRSForm } from '@/features/rsform';
|
||||||
import { labelCstTypification } from '@/features/rsform/labels';
|
|
||||||
|
|
||||||
import { PromptVariableType } from '../models/prompting';
|
import { PromptVariableType } from '../models/prompting';
|
||||||
|
import {
|
||||||
|
varBlock,
|
||||||
|
varConstituenta,
|
||||||
|
varOSS,
|
||||||
|
varSchema,
|
||||||
|
varSchemaThesaurus,
|
||||||
|
varSyntaxTree
|
||||||
|
} from '../models/prompting-api';
|
||||||
|
|
||||||
interface AIContextStore {
|
interface AIContextStore {
|
||||||
currentOSS: IOperationSchema | null;
|
currentOSS: IOperationSchema | null;
|
||||||
|
@ -40,10 +47,12 @@ export function makeVariableSelector(variableType: PromptVariableType) {
|
||||||
case PromptVariableType.OSS:
|
case PromptVariableType.OSS:
|
||||||
return (state: AIContextStore) => ({ currentOSS: state.currentOSS });
|
return (state: AIContextStore) => ({ currentOSS: state.currentOSS });
|
||||||
case PromptVariableType.SCHEMA:
|
case PromptVariableType.SCHEMA:
|
||||||
|
case PromptVariableType.SCHEMA_THESAURUS:
|
||||||
return (state: AIContextStore) => ({ currentSchema: state.currentSchema });
|
return (state: AIContextStore) => ({ currentSchema: state.currentSchema });
|
||||||
case PromptVariableType.BLOCK:
|
case PromptVariableType.BLOCK:
|
||||||
return (state: AIContextStore) => ({ currentBlock: state.currentBlock });
|
return (state: AIContextStore) => ({ currentBlock: state.currentBlock, currentOSS: state.currentOSS });
|
||||||
case PromptVariableType.CONSTITUENTA:
|
case PromptVariableType.CONSTITUENTA:
|
||||||
|
case PromptVariableType.CONSTITUENTA_SYNTAX_TREE:
|
||||||
return (state: AIContextStore) => ({ currentConstituenta: state.currentConstituenta });
|
return (state: AIContextStore) => ({ currentConstituenta: state.currentConstituenta });
|
||||||
default:
|
default:
|
||||||
return () => ({});
|
return () => ({});
|
||||||
|
@ -54,25 +63,18 @@ export function makeVariableSelector(variableType: PromptVariableType) {
|
||||||
export function evaluatePromptVariable(variableType: PromptVariableType, context: Partial<AIContextStore>): string {
|
export function evaluatePromptVariable(variableType: PromptVariableType, context: Partial<AIContextStore>): string {
|
||||||
switch (variableType) {
|
switch (variableType) {
|
||||||
case PromptVariableType.OSS:
|
case PromptVariableType.OSS:
|
||||||
return context.currentOSS?.title ?? '';
|
return context.currentOSS ? varOSS(context.currentOSS) : `!${variableType}!`;
|
||||||
case PromptVariableType.SCHEMA:
|
case PromptVariableType.SCHEMA:
|
||||||
return context.currentSchema ? generateSchemaPrompt(context.currentSchema) : '';
|
return context.currentSchema ? varSchema(context.currentSchema) : `!${variableType}!`;
|
||||||
|
case PromptVariableType.SCHEMA_THESAURUS:
|
||||||
|
return context.currentSchema ? varSchemaThesaurus(context.currentSchema) : `!${variableType}!`;
|
||||||
case PromptVariableType.BLOCK:
|
case PromptVariableType.BLOCK:
|
||||||
return context.currentBlock?.title ?? '';
|
return context.currentBlock && context.currentOSS
|
||||||
|
? varBlock(context.currentBlock, context.currentOSS)
|
||||||
|
: `!${variableType}!`;
|
||||||
case PromptVariableType.CONSTITUENTA:
|
case PromptVariableType.CONSTITUENTA:
|
||||||
return context.currentConstituenta?.alias ?? '';
|
return context.currentConstituenta ? varConstituenta(context.currentConstituenta) : `!${variableType}!`;
|
||||||
|
case PromptVariableType.CONSTITUENTA_SYNTAX_TREE:
|
||||||
|
return context.currentConstituenta ? varSyntaxTree(context.currentConstituenta) : `!${variableType}!`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ====== Internals =========
|
|
||||||
function generateSchemaPrompt(schema: IRSForm): string {
|
|
||||||
let body = `Название концептуальной схемы: ${schema.title}\n`;
|
|
||||||
body += `[${schema.alias}] Описание: "${schema.description}"\n\n`;
|
|
||||||
body += 'Понятия:\n';
|
|
||||||
schema.items.forEach(item => {
|
|
||||||
body += `${item.alias} - "${labelCstTypification(item)}" - "${item.term_resolved}" - "${
|
|
||||||
item.definition_formal
|
|
||||||
}" - "${item.definition_resolved}" - "${item.convention}"\n`;
|
|
||||||
});
|
|
||||||
return body;
|
|
||||||
}
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ import { promptText } from '@/utils/labels';
|
||||||
|
|
||||||
import { OperationType } from '../../backend/types';
|
import { OperationType } from '../../backend/types';
|
||||||
import { useOssSuspense } from '../../backend/use-oss';
|
import { useOssSuspense } from '../../backend/use-oss';
|
||||||
import { type IOperation } from '../../models/oss';
|
import { type IOperation, NodeType } from '../../models/oss';
|
||||||
|
|
||||||
import { OssEditContext, type OssTabID } from './oss-edit-context';
|
import { OssEditContext, type OssTabID } from './oss-edit-context';
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ export const OssEditState = ({ itemID, children }: React.PropsWithChildren<OssEd
|
||||||
const setSearchLocation = useLibrarySearchStore(state => state.setLocation);
|
const setSearchLocation = useLibrarySearchStore(state => state.setLocation);
|
||||||
const searchLocation = useLibrarySearchStore(state => state.location);
|
const searchLocation = useLibrarySearchStore(state => state.location);
|
||||||
const setCurrentOSS = useAIStore(state => state.setCurrentOSS);
|
const setCurrentOSS = useAIStore(state => state.setCurrentOSS);
|
||||||
|
const setCurrentBlock = useAIStore(state => state.setCurrentBlock);
|
||||||
|
|
||||||
const { user } = useAuthSuspense();
|
const { user } = useAuthSuspense();
|
||||||
const { schema } = useOssSuspense({ itemID: itemID });
|
const { schema } = useOssSuspense({ itemID: itemID });
|
||||||
|
@ -57,6 +58,15 @@ export const OssEditState = ({ itemID, children }: React.PropsWithChildren<OssEd
|
||||||
return () => setCurrentOSS(null);
|
return () => setCurrentOSS(null);
|
||||||
}, [schema, setCurrentOSS]);
|
}, [schema, setCurrentOSS]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const selectedBlock = selectedItems.find(item => item.nodeType === NodeType.BLOCK);
|
||||||
|
if (selectedBlock) {
|
||||||
|
setCurrentBlock(selectedBlock);
|
||||||
|
return () => setCurrentBlock(null);
|
||||||
|
}
|
||||||
|
setCurrentBlock(null);
|
||||||
|
}, [selectedItems, setCurrentBlock]);
|
||||||
|
|
||||||
function navigateTab(tab: OssTabID) {
|
function navigateTab(tab: OssTabID) {
|
||||||
const url = urls.oss_props({
|
const url = urls.oss_props({
|
||||||
id: schema.id,
|
id: schema.id,
|
||||||
|
|
|
@ -21,6 +21,8 @@ export const PARAMETER = {
|
||||||
graphNodePadding: 5, // Padding inside graph nodes (in pixels)
|
graphNodePadding: 5, // Padding inside graph nodes (in pixels)
|
||||||
graphNodeRadius: 20, // Radius of graph nodes (in pixels)
|
graphNodeRadius: 20, // Radius of graph nodes (in pixels)
|
||||||
|
|
||||||
|
indentJSON: 2, // Number of spaces for JSON indentation
|
||||||
|
|
||||||
logicLabel: 'LOGIC',
|
logicLabel: 'LOGIC',
|
||||||
errorNodeLabel: '[ERROR]',
|
errorNodeLabel: '[ERROR]',
|
||||||
exteorVersion: '4.9.7'
|
exteorVersion: '4.9.7'
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { type AxiosError, type AxiosHeaderValue, type AxiosResponse, isAxiosError } from 'axios';
|
import { type AxiosError, type AxiosHeaderValue, type AxiosResponse, isAxiosError } from 'axios';
|
||||||
|
|
||||||
|
import { PARAMETER } from './constants';
|
||||||
import { infoMsg, promptText } from './labels';
|
import { infoMsg, promptText } from './labels';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -160,7 +161,7 @@ export function convertToCSV(targetObj: readonly object[]): Blob {
|
||||||
* Convert object or array to JSON Blob.
|
* Convert object or array to JSON Blob.
|
||||||
*/
|
*/
|
||||||
export function convertToJSON(targetObj: unknown): Blob {
|
export function convertToJSON(targetObj: unknown): Blob {
|
||||||
const jsonString = JSON.stringify(targetObj, null, 2);
|
const jsonString = JSON.stringify(targetObj, null, PARAMETER.indentJSON);
|
||||||
return new Blob([jsonString], { type: 'application/json;charset=utf-8;' });
|
return new Blob([jsonString], { type: 'application/json;charset=utf-8;' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user