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 { Loader } from '@/components/loader';
|
||||
import { ModalView } from '@/components/modal';
|
||||
import { TabLabel, TabList, TabPanel, Tabs } from '@/components/tabs';
|
||||
|
||||
import { useAvailableTemplatesSuspense } from '../../backend/use-available-templates';
|
||||
|
||||
import { TabPromptResult } from './tab-prompt-result';
|
||||
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];
|
||||
import { AIPromptTabs, TabID } from './ai-prompt-tabs';
|
||||
|
||||
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 { items: prompts } = useAvailableTemplatesSuspense();
|
||||
|
||||
return (
|
||||
<ModalView header='Генератор запросом LLM' className='w-100 sm:w-160 px-6'>
|
||||
<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'>
|
||||
<ModalView header='Генератор запросом LLM' className='w-100 sm:w-160 px-6 flex flex-col h-120'>
|
||||
<ComboBox
|
||||
id='prompt-select'
|
||||
items={prompts}
|
||||
|
@ -45,12 +27,8 @@ export function DlgAIPromptDialog() {
|
|||
className='w-full'
|
||||
/>
|
||||
<Suspense fallback={<Loader />}>
|
||||
<TabPanel>{selected ? <TabPromptSelect promptID={selected} /> : null}</TabPanel>
|
||||
<TabPanel>{selected ? <TabPromptResult promptID={selected} /> : null}</TabPanel>
|
||||
<TabPanel>{selected ? <TabPromptVariables promptID={selected} /> : null}</TabPanel>
|
||||
{selected ? <AIPromptTabs promptID={selected} activeTab={activeTab} setActiveTab={setActiveTab} /> : null}
|
||||
</Suspense>
|
||||
</div>
|
||||
</Tabs>
|
||||
</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 { 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 {
|
||||
promptID: number;
|
||||
}
|
||||
|
||||
export function TabPromptResult({ promptID }: 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);
|
||||
prompt: string;
|
||||
}
|
||||
|
||||
export function TabPromptResult({ prompt }: TabPromptResultProps) {
|
||||
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
|
||||
aria-label='Сгенерированное сообщение'
|
||||
value={generatedMessage}
|
||||
value={prompt}
|
||||
placeholder='Текст шаблона пуст'
|
||||
disabled
|
||||
fitContent
|
||||
className='w-full max-h-100 min-h-12'
|
||||
className='w-full h-100'
|
||||
/>
|
||||
</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';
|
||||
|
||||
import { usePromptTemplateSuspense } from '../../backend/use-prompt-template';
|
||||
import { describePromptVariable } from '../../labels';
|
||||
import { PromptVariableType } from '../../models/prompting';
|
||||
import { extractPromptVariables } from '../../models/prompting-api';
|
||||
import { useAvailableVariables } from '../../stores/use-available-variables';
|
||||
|
||||
interface TabPromptVariablesProps {
|
||||
promptID: number;
|
||||
template: string;
|
||||
}
|
||||
|
||||
export function TabPromptVariables({ promptID }: TabPromptVariablesProps) {
|
||||
const { promptTemplate } = usePromptTemplateSuspense(promptID);
|
||||
const variables = extractPromptVariables(promptTemplate.text);
|
||||
export function TabPromptVariables({ template }: TabPromptVariablesProps) {
|
||||
const variables = extractPromptVariables(template);
|
||||
const availableTypes = useAvailableVariables();
|
||||
return (
|
||||
<ul>
|
||||
|
|
|
@ -4,14 +4,18 @@ const describePromptVariableRecord: Record<PromptVariableType, string> = {
|
|||
[PromptVariableType.BLOCK]: 'Текущий блок операционной схемы',
|
||||
[PromptVariableType.OSS]: 'Текущая операционная схема',
|
||||
[PromptVariableType.SCHEMA]: 'Текущая концептуальная схема',
|
||||
[PromptVariableType.CONSTITUENTA]: 'Текущая конституента'
|
||||
[PromptVariableType.SCHEMA_THESAURUS]: 'Термины и определения текущей концептуальной схемы',
|
||||
[PromptVariableType.CONSTITUENTA]: 'Текущая конституента',
|
||||
[PromptVariableType.CONSTITUENTA_SYNTAX_TREE]: 'Синтаксическое дерево конституенты'
|
||||
};
|
||||
|
||||
const mockPromptVariableRecord: Record<PromptVariableType, string> = {
|
||||
[PromptVariableType.BLOCK]: 'Пример: Текущий блок операционной схемы',
|
||||
[PromptVariableType.OSS]: 'Пример: Текущая операционная схема',
|
||||
[PromptVariableType.SCHEMA]: 'Пример: Текущая концептуальная схема',
|
||||
[PromptVariableType.CONSTITUENTA]: 'Пример: Текущая конституента'
|
||||
[PromptVariableType.SCHEMA_THESAURUS]: 'Пример\nТермин1 - Определение1\nТермин2 - Определение2',
|
||||
[PromptVariableType.CONSTITUENTA]: 'Пример: Текущая конституента',
|
||||
[PromptVariableType.CONSTITUENTA_SYNTAX_TREE]: 'Пример синтаксического дерева конституенты'
|
||||
};
|
||||
|
||||
/** 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';
|
||||
|
||||
/** Extracts a list of variables (as string[]) from a target string.
|
||||
|
@ -27,3 +34,94 @@ export function generateSample(target: string): string {
|
|||
}
|
||||
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. */
|
||||
export const PromptVariableType = {
|
||||
BLOCK: 'block',
|
||||
// BLOCK_TITLE: 'block.title',
|
||||
// BLOCK_DESCRIPTION: 'block.description',
|
||||
// BLOCK_CONTENTS: 'block.contents',
|
||||
|
||||
OSS: 'oss',
|
||||
// OSS_CONTENTS: 'oss.contents',
|
||||
// OSS_ALIAS: 'oss.alias',
|
||||
// OSS_TITLE: 'oss.title',
|
||||
// OSS_DESCRIPTION: 'oss.description',
|
||||
|
||||
SCHEMA: 'schema',
|
||||
// SCHEMA_ALIAS: 'schema.alias',
|
||||
// SCHEMA_TITLE: 'schema.title',
|
||||
// SCHEMA_DESCRIPTION: 'schema.description',
|
||||
// SCHEMA_THESAURUS: 'schema.thesaurus',
|
||||
SCHEMA_THESAURUS: 'schema.thesaurus',
|
||||
// SCHEMA_GRAPH: 'schema.graph',
|
||||
// SCHEMA_TYPE_GRAPH: 'schema.type-graph',
|
||||
|
||||
CONSTITUENTA: 'constituenta'
|
||||
// CONSTITUENTA_ALIAS: 'constituent.alias',
|
||||
// CONSTITUENTA_CONVENTION: 'constituent.convention',
|
||||
// CONSTITUENTA_DEFINITION: 'constituent.definition',
|
||||
// CONSTITUENTA_DEFINITION_FORMAL: 'constituent.definition-formal',
|
||||
// CONSTITUENTA_EXPRESSION_TREE: 'constituent.expression-tree'
|
||||
CONSTITUENTA: 'constituenta',
|
||||
CONSTITUENTA_SYNTAX_TREE: 'constituent.ast'
|
||||
} as const;
|
||||
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 IConstituenta, type IRSForm } from '@/features/rsform';
|
||||
import { labelCstTypification } from '@/features/rsform/labels';
|
||||
|
||||
import { PromptVariableType } from '../models/prompting';
|
||||
import {
|
||||
varBlock,
|
||||
varConstituenta,
|
||||
varOSS,
|
||||
varSchema,
|
||||
varSchemaThesaurus,
|
||||
varSyntaxTree
|
||||
} from '../models/prompting-api';
|
||||
|
||||
interface AIContextStore {
|
||||
currentOSS: IOperationSchema | null;
|
||||
|
@ -40,10 +47,12 @@ export function makeVariableSelector(variableType: PromptVariableType) {
|
|||
case PromptVariableType.OSS:
|
||||
return (state: AIContextStore) => ({ currentOSS: state.currentOSS });
|
||||
case PromptVariableType.SCHEMA:
|
||||
case PromptVariableType.SCHEMA_THESAURUS:
|
||||
return (state: AIContextStore) => ({ currentSchema: state.currentSchema });
|
||||
case PromptVariableType.BLOCK:
|
||||
return (state: AIContextStore) => ({ currentBlock: state.currentBlock });
|
||||
return (state: AIContextStore) => ({ currentBlock: state.currentBlock, currentOSS: state.currentOSS });
|
||||
case PromptVariableType.CONSTITUENTA:
|
||||
case PromptVariableType.CONSTITUENTA_SYNTAX_TREE:
|
||||
return (state: AIContextStore) => ({ currentConstituenta: state.currentConstituenta });
|
||||
default:
|
||||
return () => ({});
|
||||
|
@ -54,25 +63,18 @@ export function makeVariableSelector(variableType: PromptVariableType) {
|
|||
export function evaluatePromptVariable(variableType: PromptVariableType, context: Partial<AIContextStore>): string {
|
||||
switch (variableType) {
|
||||
case PromptVariableType.OSS:
|
||||
return context.currentOSS?.title ?? '';
|
||||
return context.currentOSS ? varOSS(context.currentOSS) : `!${variableType}!`;
|
||||
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:
|
||||
return context.currentBlock?.title ?? '';
|
||||
return context.currentBlock && context.currentOSS
|
||||
? varBlock(context.currentBlock, context.currentOSS)
|
||||
: `!${variableType}!`;
|
||||
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 { 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';
|
||||
|
||||
|
@ -32,6 +32,7 @@ export const OssEditState = ({ itemID, children }: React.PropsWithChildren<OssEd
|
|||
const setSearchLocation = useLibrarySearchStore(state => state.setLocation);
|
||||
const searchLocation = useLibrarySearchStore(state => state.location);
|
||||
const setCurrentOSS = useAIStore(state => state.setCurrentOSS);
|
||||
const setCurrentBlock = useAIStore(state => state.setCurrentBlock);
|
||||
|
||||
const { user } = useAuthSuspense();
|
||||
const { schema } = useOssSuspense({ itemID: itemID });
|
||||
|
@ -57,6 +58,15 @@ export const OssEditState = ({ itemID, children }: React.PropsWithChildren<OssEd
|
|||
return () => setCurrentOSS(null);
|
||||
}, [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) {
|
||||
const url = urls.oss_props({
|
||||
id: schema.id,
|
||||
|
|
|
@ -21,6 +21,8 @@ export const PARAMETER = {
|
|||
graphNodePadding: 5, // Padding inside graph nodes (in pixels)
|
||||
graphNodeRadius: 20, // Radius of graph nodes (in pixels)
|
||||
|
||||
indentJSON: 2, // Number of spaces for JSON indentation
|
||||
|
||||
logicLabel: 'LOGIC',
|
||||
errorNodeLabel: '[ERROR]',
|
||||
exteorVersion: '4.9.7'
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
import { toast } from 'react-toastify';
|
||||
import { type AxiosError, type AxiosHeaderValue, type AxiosResponse, isAxiosError } from 'axios';
|
||||
|
||||
import { PARAMETER } from './constants';
|
||||
import { infoMsg, promptText } from './labels';
|
||||
|
||||
/**
|
||||
|
@ -160,7 +161,7 @@ export function convertToCSV(targetObj: readonly object[]): Blob {
|
|||
* Convert object or array to JSON 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;' });
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user