F: Add LLM prompt generator

This commit is contained in:
Ivan 2025-04-16 21:02:53 +03:00
parent 5f767c943d
commit 9df4c6799d
6 changed files with 56 additions and 25 deletions

View File

@ -1387,9 +1387,9 @@
}
},
"node_modules/@eslint-community/eslint-utils": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.0.tgz",
"integrity": "sha512-WhCn7Z7TauhBtmzhvKpoQs0Wwb/kBcy4CwpuI0/eEIr2Lx2auxmulAzLr91wVZJaz47iUZdkXOK7WlAfxGKCnA==",
"version": "4.6.1",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.1.tgz",
"integrity": "sha512-KTsJMmobmbrFLe3LDh0PC2FXpcSYJt/MLjlkh/9LEnmKYLSYmT/0EW9JWANjeoemiuZrmogti0tW5Ch+qNUYDw==",
"dev": true,
"license": "MIT",
"dependencies": {

View File

@ -68,6 +68,7 @@ export { LuGlasses as IconReader } from 'react-icons/lu';
// ===== Domain entities =======
export { TbBriefcase as IconBusiness } from 'react-icons/tb';
export { VscLibrary as IconLibrary } from 'react-icons/vsc';
export { BiBot as IconRobot } from 'react-icons/bi';
export { IoLibrary as IconLibrary2 } from 'react-icons/io5';
export { BiDiamond as IconTemplates } from 'react-icons/bi';
export { TbHexagons as IconOSS } from 'react-icons/tb';

View File

@ -10,7 +10,9 @@ import {
IconEditor,
IconMenu,
IconOwner,
IconQR,
IconReader,
IconRobot,
IconShare,
IconUpload
} from '@/components/icons';
@ -51,6 +53,12 @@ export function HelpRSMenu() {
<li>
<IconShare className='inline-icon' /> Поделиться скопировать ссылку на схему
</li>
<li>
<IconQR className='inline-icon' /> Отобразить QR-код схемы
</li>
<li>
<IconRobot className='inline-icon' /> Генерировать запрос для LLM
</li>
<li>
<IconClone className='inline-icon icon-green' /> Клонировать создать копию схемы
</li>

View File

@ -9,6 +9,7 @@ import { PARAMETER } from '@/utils/constants';
import { CstType, type IRSErrorDescription, type RSErrorType } from '../backend/types';
import { type IRSForm } from './rsform';
import { type AliasMapping, type IArgumentValue, RSErrorClass, type SyntaxTree } from './rslang';
// cspell:disable
@ -18,30 +19,22 @@ const COMPLEX_SYMBOLS_REGEXP = /[∀∃×ℬ;|:]/g;
const TYPIFICATION_SET = /^+\([\(X\d+\)×]*\)$/g;
// cspell:enable
/**
* Extracts global variable names from a given expression.
*/
/** Extracts global variable names from a given expression. */
export function extractGlobals(expression: string): Set<string> {
return new Set(expression.match(GLOBALS_REGEXP) ?? []);
}
/**
* Check if expression is simple derivation.
*/
/** Check if expression is simple derivation. */
export function isSimpleExpression(text: string): boolean {
return !text.match(COMPLEX_SYMBOLS_REGEXP);
}
/**
* Check if expression is set typification.
*/
/** Check if expression is set typification. */
export function isSetTypification(text: string): boolean {
return !!text.match(TYPIFICATION_SET);
}
/**
* Infers type of constituent for a given template and arguments.
*/
/** Infers type of constituent for a given template and arguments. */
export function inferTemplatedType(templateType: CstType, args: IArgumentValue[]): CstType {
if (args.length === 0 || args.some(arg => !arg.value)) {
return templateType;
@ -138,16 +131,12 @@ export function getRSErrorPrefix(error: IRSErrorDescription): string {
}
}
/**
* Apply alias mapping.
*/
/** Apply alias mapping. */
export function applyAliasMapping(target: string, mapping: AliasMapping): string {
return applyPattern(target, mapping, GLOBALS_REGEXP);
}
/**
* Apply alias typification mapping.
*/
/** Apply alias typification mapping. */
export function applyTypificationMapping(target: string, mapping: AliasMapping): string {
const modified = applyAliasMapping(target, mapping);
if (modified === target) {
@ -202,9 +191,7 @@ export function applyTypificationMapping(target: string, mapping: AliasMapping):
return result;
}
/**
* Transform Tree to {@link SyntaxTree}.
*/
/** Transform Tree to {@link SyntaxTree}. */
export function transformAST(tree: Tree): SyntaxTree {
const result: SyntaxTree = [];
const parents: number[] = [];
@ -252,6 +239,21 @@ export function transformAST(tree: Tree): SyntaxTree {
return result;
}
export function generatePrompt(schema: IRSForm): string {
const intro =
'Концептуальная схема — это формализованная модель предметной области, выраженная с помощью языка родов структур, основанного на аппарате формальной логики и теории множеств, и дополненная естественно-языковыми пояснениями. Она представляет собой систему взаимосвязанных определений, где каждое понятие или утверждение задаётся в строгом формате Обозначение - "Типизация" - "Термин" - "Определение в языке родов структур" - "Определение в естественном языке" - "Конвенция или комментарий".\nОбозначение — уникальный идентификатор понятия (например, X1, S3, F14).\nТипизация — структура элементов множества, моделирующего данное понятие (например, (X1) для подмножества индивидов или (X1×X1) для бинарных отношений).\nТермин — название понятия в естественном языке.\nКонвенция описывает неопределяемые понятия предметным языком, включая уточнения, ограничения или примеры, включая ссылки на внешние данные (например, документы).\n------------\nДалее приведена концептуальная схема, описывающая некоторую предметную область.\n';
const outro =
'\n------\nПри ответе на следующий вопрос используй представленные в концептуальной схеме понятия и определения.\n';
let body = `Название концептуальной схемы: ${schema.title}\n`;
body += `[${schema.alias}] Описание: "${schema.description}"\n\n`;
body += 'Понятия:\n';
schema.items.forEach(item => {
body += `${item.alias} - "${item.parse.typification}" - "${item.term_resolved}" - "${item.definition_formal}" - "${item.definition_resolved}" - "${item.convention}"\n`;
});
return `${intro} ${body} ${outro}`;
}
// ====== Internals =========
/** Text substitution guided by mapping and regular expression. */
function applyPattern(text: string, mapping: AliasMapping, pattern: RegExp): string {

View File

@ -1,3 +1,4 @@
import { toast } from 'react-toastify';
import fileDownload from 'js-file-download';
import { urls, useConceptNavigation } from '@/app';
@ -17,17 +18,19 @@ import {
IconNewItem,
IconOSS,
IconQR,
IconRobot,
IconShare,
IconUpload
} from '@/components/icons';
import { useDialogsStore } from '@/stores/dialogs';
import { useModificationStore } from '@/stores/modification';
import { EXTEOR_TRS_FILE } from '@/utils/constants';
import { tooltipText } from '@/utils/labels';
import { infoMsg, tooltipText } from '@/utils/labels';
import { generatePageQR, promptUnsaved, sharePage } from '@/utils/utils';
import { useDownloadRSForm } from '../../backend/use-download-rsform';
import { useMutatingRSForm } from '../../backend/use-mutating-rsform';
import { generatePrompt } from '../../models/rslang-api';
import { useRSEdit } from './rsedit-context';
@ -107,6 +110,16 @@ export function MenuMain() {
sharePage();
}
function handleCopyPrompt() {
menu.hide();
const prompt = generatePrompt(schema);
navigator.clipboard
.writeText(prompt)
.then(() => toast.success(infoMsg.promptReady))
.catch(console.error);
}
function handleShowQR() {
menu.hide();
showQR({ target: generatePageQR() });
@ -139,6 +152,12 @@ export function MenuMain() {
icon={<IconQR size='1rem' className='icon-primary' />}
onClick={handleShowQR}
/>
<DropdownButton
text='Запрос LLM'
title='Генерировать запрос для LLM'
icon={<IconRobot size='1rem' className='icon-primary' />}
onClick={handleCopyPrompt}
/>
{!isAnonymous ? (
<DropdownButton
text='Клонировать'

View File

@ -17,6 +17,7 @@ export const infoMsg = {
reindexComplete: 'Имена конституент обновлены',
moveComplete: 'Перемещение завершено',
linkReady: 'Ссылка скопирована',
promptReady: 'Текст скопирован',
versionRestored: 'Загрузка версии завершена',
locationRenamed: 'Ваши схемы перемещены',
cloneComplete: (alias: string) => `Копия создана: ${alias}`,