From 9df4c6799d352eabe9d4242c72e9fe6da8eba141 Mon Sep 17 00:00:00 2001 From: Ivan <8611739+IRBorisov@users.noreply.github.com> Date: Wed, 16 Apr 2025 21:02:53 +0300 Subject: [PATCH] F: Add LLM prompt generator --- rsconcept/frontend/package-lock.json | 6 +-- rsconcept/frontend/src/components/icons.tsx | 1 + .../features/help/items/ui/help-rsmenu.tsx | 8 ++++ .../src/features/rsform/models/rslang-api.ts | 44 ++++++++++--------- .../rsform/pages/rsform-page/menu-main.tsx | 21 ++++++++- rsconcept/frontend/src/utils/labels.ts | 1 + 6 files changed, 56 insertions(+), 25 deletions(-) diff --git a/rsconcept/frontend/package-lock.json b/rsconcept/frontend/package-lock.json index b047aa09..76c83a7d 100644 --- a/rsconcept/frontend/package-lock.json +++ b/rsconcept/frontend/package-lock.json @@ -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": { diff --git a/rsconcept/frontend/src/components/icons.tsx b/rsconcept/frontend/src/components/icons.tsx index f3375f6f..3c08ae7d 100644 --- a/rsconcept/frontend/src/components/icons.tsx +++ b/rsconcept/frontend/src/components/icons.tsx @@ -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'; diff --git a/rsconcept/frontend/src/features/help/items/ui/help-rsmenu.tsx b/rsconcept/frontend/src/features/help/items/ui/help-rsmenu.tsx index 97ffcfb4..eac1e1fe 100644 --- a/rsconcept/frontend/src/features/help/items/ui/help-rsmenu.tsx +++ b/rsconcept/frontend/src/features/help/items/ui/help-rsmenu.tsx @@ -10,7 +10,9 @@ import { IconEditor, IconMenu, IconOwner, + IconQR, IconReader, + IconRobot, IconShare, IconUpload } from '@/components/icons'; @@ -51,6 +53,12 @@ export function HelpRSMenu() {
  • Поделиться – скопировать ссылку на схему
  • +
  • + Отобразить QR-код схемы +
  • +
  • + Генерировать запрос для LLM +
  • Клонировать – создать копию схемы
  • diff --git a/rsconcept/frontend/src/features/rsform/models/rslang-api.ts b/rsconcept/frontend/src/features/rsform/models/rslang-api.ts index 1afba909..4e6f6623 100644 --- a/rsconcept/frontend/src/features/rsform/models/rslang-api.ts +++ b/rsconcept/frontend/src/features/rsform/models/rslang-api.ts @@ -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 { 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 { diff --git a/rsconcept/frontend/src/features/rsform/pages/rsform-page/menu-main.tsx b/rsconcept/frontend/src/features/rsform/pages/rsform-page/menu-main.tsx index 73506e39..94bc99be 100644 --- a/rsconcept/frontend/src/features/rsform/pages/rsform-page/menu-main.tsx +++ b/rsconcept/frontend/src/features/rsform/pages/rsform-page/menu-main.tsx @@ -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={} onClick={handleShowQR} /> + } + onClick={handleCopyPrompt} + /> {!isAnonymous ? ( `Копия создана: ${alias}`,