From d83ebfa8d57de4d0cf108cbfc8e74101e51e60b7 Mon Sep 17 00:00:00 2001 From: Ivan <8611739+IRBorisov@users.noreply.github.com> Date: Tue, 15 Jul 2025 23:50:57 +0300 Subject: [PATCH] F: Implementing template evaluation --- .../frontend/src/app/navigation/menu-ai.tsx | 2 +- .../src/features/ai/dialogs/dlg-ai-prompt.tsx | 76 ------------------- .../dialogs/dlg-ai-prompt/dlg-ai-prompt.tsx | 56 ++++++++++++++ .../ai/dialogs/dlg-ai-prompt/index.tsx | 1 + .../dlg-ai-prompt/tab-prompt-result.tsx | 65 ++++++++++++++++ .../dlg-ai-prompt/tab-prompt-select.tsx | 44 +++++++++++ .../dlg-ai-prompt/tab-prompt-variables.tsx | 32 ++++++++ .../src/features/ai/stores/ai-context.ts | 16 +++- .../oss/pages/oss-page/oss-edit-state.tsx | 9 ++- .../rsform/pages/rsform-page/rsedit-state.tsx | 15 +++- rsconcept/frontend/src/stores/dialogs.ts | 5 +- 11 files changed, 238 insertions(+), 83 deletions(-) delete mode 100644 rsconcept/frontend/src/features/ai/dialogs/dlg-ai-prompt.tsx create mode 100644 rsconcept/frontend/src/features/ai/dialogs/dlg-ai-prompt/dlg-ai-prompt.tsx create mode 100644 rsconcept/frontend/src/features/ai/dialogs/dlg-ai-prompt/index.tsx create mode 100644 rsconcept/frontend/src/features/ai/dialogs/dlg-ai-prompt/tab-prompt-result.tsx create mode 100644 rsconcept/frontend/src/features/ai/dialogs/dlg-ai-prompt/tab-prompt-select.tsx create mode 100644 rsconcept/frontend/src/features/ai/dialogs/dlg-ai-prompt/tab-prompt-variables.tsx diff --git a/rsconcept/frontend/src/app/navigation/menu-ai.tsx b/rsconcept/frontend/src/app/navigation/menu-ai.tsx index 3ddd5cf9..f41127b5 100644 --- a/rsconcept/frontend/src/app/navigation/menu-ai.tsx +++ b/rsconcept/frontend/src/app/navigation/menu-ai.tsx @@ -25,7 +25,7 @@ export function MenuAI() { event.preventDefault(); event.stopPropagation(); menu.hide(); - showAIPrompt({}); + showAIPrompt(); } return ( diff --git a/rsconcept/frontend/src/features/ai/dialogs/dlg-ai-prompt.tsx b/rsconcept/frontend/src/features/ai/dialogs/dlg-ai-prompt.tsx deleted file mode 100644 index 3f02da5d..00000000 --- a/rsconcept/frontend/src/features/ai/dialogs/dlg-ai-prompt.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import { useState } from 'react'; - -import { ModalForm } from '@/components/modal'; - -import { type IPromptTemplate } from '../backend/types'; - -export interface DlgAIPromptDialogProps { - onPromptSelected?: (prompt: IPromptTemplate) => void; -} - -const mockPrompts: IPromptTemplate[] = [ - { - id: 1, - owner: null, - label: 'Greeting', - is_shared: true, - description: 'A simple greeting prompt.', - text: 'Hello, ${name}! How can I assist you today?' - }, - { - id: 2, - owner: null, - is_shared: true, - label: 'Summary', - description: 'Summarize the following text.', - text: 'Please summarize the following: ${text}' - } -]; - -export function DlgAIPromptDialog() { - const [selectedPrompt, setSelectedPrompt] = useState(mockPrompts[0]); - - return ( - { - e.preventDefault(); - // Placeholder for generate logic - }} - className='w-120 px-6 cc-column' - > -
- - -
- {selectedPrompt && ( -
-
Label:
-
{selectedPrompt.label}
-
Description:
-
{selectedPrompt.description}
-
Template Text:
-
{selectedPrompt.text}
-
- )} -
- ); -} diff --git a/rsconcept/frontend/src/features/ai/dialogs/dlg-ai-prompt/dlg-ai-prompt.tsx b/rsconcept/frontend/src/features/ai/dialogs/dlg-ai-prompt/dlg-ai-prompt.tsx new file mode 100644 index 00000000..eb26e538 --- /dev/null +++ b/rsconcept/frontend/src/features/ai/dialogs/dlg-ai-prompt/dlg-ai-prompt.tsx @@ -0,0 +1,56 @@ +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]; + +export function DlgAIPromptDialog() { + const [activeTab, setActiveTab] = useState(TabID.SELECT); + const [selected, setSelected] = useState(null); + const { items: prompts } = useAvailableTemplatesSuspense(); + + return ( + + setActiveTab(index as TabID)}> + + + + + + +
+ p.id === selected) ?? null} + onChange={item => setSelected(item?.id ?? 0)} + idFunc={item => String(item.id)} + labelValueFunc={item => item.label} + labelOptionFunc={item => item.label} + placeholder='Выберите шаблон' + className='w-full' + /> + }> + {selected ? : null} + {selected ? : null} + {selected ? : null} + +
+
+
+ ); +} diff --git a/rsconcept/frontend/src/features/ai/dialogs/dlg-ai-prompt/index.tsx b/rsconcept/frontend/src/features/ai/dialogs/dlg-ai-prompt/index.tsx new file mode 100644 index 00000000..153d3858 --- /dev/null +++ b/rsconcept/frontend/src/features/ai/dialogs/dlg-ai-prompt/index.tsx @@ -0,0 +1 @@ +export * from './dlg-ai-prompt'; diff --git a/rsconcept/frontend/src/features/ai/dialogs/dlg-ai-prompt/tab-prompt-result.tsx b/rsconcept/frontend/src/features/ai/dialogs/dlg-ai-prompt/tab-prompt-result.tsx new file mode 100644 index 00000000..b779b55c --- /dev/null +++ b/rsconcept/frontend/src/features/ai/dialogs/dlg-ai-prompt/tab-prompt-result.tsx @@ -0,0 +1,65 @@ +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); + } + + return ( +
+ } + onClick={handleCopyPrompt} + disabled={!generatedMessage} + /> +