Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,4 @@ package-lock.json
dataEval.json

# Ignore generated credentials from google-github-actions/auth
gha-creds-*.json
gha-creds-*.json
3 changes: 3 additions & 0 deletions FeatureFlags.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ module.exports = {
// Whether the Chatbot UserInterface and its functionality should be enabled
enableChatbot: false,

// Chatbot for creating and modifying BPMN in modeler
enableBPMNChatbot: true,

// -----------------------------------------------------------------------------
// Chopping Block
//
Expand Down
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ yarn dev-engine-web

**Management System:**

Before using the BPMN chat bot you need to create an API key and add it as an environment variable. If you are not interesting in using the chat bot ignore the following steps.

1. Go to https://ai.google.dev.
2. Log in and generate an API key.
3. Copy the API key and create an enviroment variable named GEMINI_API_KEY and paste the copied API key as value.

```
// NextJS frontend
yarn dev-ms
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,8 @@ import Icon, {
UndoOutlined,
RedoOutlined,
ArrowUpOutlined,
ArrowDownOutlined,
FullscreenOutlined,
FilePdfOutlined,
RobotOutlined,
} from '@ant-design/icons';
import { SvgXML } from '@/components/svg';
import PropertiesPanel from './properties-panel';
Expand All @@ -28,6 +27,9 @@ import ModelerShareModalButton from './modeler-share-modal';
import { ProcessExportTypes } from '@/components/process-export';
import { spaceURL } from '@/lib/utils';
import { generateSharedViewerUrl } from '@/lib/sharing/process-sharing';
import ChatbotDialog from '@/components/bpmn-chatbot';
import { enableBPMNChatbot } from 'FeatureFlags';
import { BPMNCanvasRef } from '@/components/bpmn-canvas';

const LATEST_VERSION = { version: -1, name: 'Latest Version', description: '' };

Expand All @@ -37,6 +39,7 @@ type ModelerToolbarProps = {
canUndo: boolean;
canRedo: boolean;
versions: { version: number; name: string; description: string }[];
modeler: BPMNCanvasRef | null;
};
const ModelerToolbar = ({
processId,
Expand All @@ -58,6 +61,7 @@ const ModelerToolbar = ({

const query = useSearchParams();
const subprocessId = query.get('subprocess');
const [showChatbotDialog, setShowChatbotDialog] = useState(false);

const modeler = useModelerStateStore((state) => state.modeler);
const selectedElementId = useModelerStateStore((state) => state.selectedElementId);
Expand Down Expand Up @@ -275,6 +279,15 @@ const ModelerToolbar = ({
</Tooltip>
</>
)}

{enableBPMNChatbot && (
<Tooltip title={showChatbotDialog ? 'Close Chatbot' : 'Open Chatbot'}>
<Button
icon={<RobotOutlined></RobotOutlined>}
onClick={() => setShowChatbotDialog(!showChatbotDialog)}
></Button>
</Tooltip>
)}
</ToolbarGroup>

{showPropertiesPanel && selectedElement && (
Expand All @@ -284,6 +297,10 @@ const ModelerToolbar = ({
selectedElement={selectedElement}
/>
)}

{enableBPMNChatbot && (
<ChatbotDialog show={showChatbotDialog} modeler={modeler}></ChatbotDialog>
)}
</Space>
</Space>
</Toolbar>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ const Modeler = ({ versionName, process, versions, ...divProps }: ModelerProps)
setXmlEditorBpmn(undefined);
};

const handleXmlEditorSave = async (bpmn: string) => {
const handleXmlSave = async (bpmn: string) => {
if (modeler.current) {
await modeler.current.loadBPMN(bpmn);
// If the bpmn contains unexpected content (text content for an element
Expand Down Expand Up @@ -283,6 +283,7 @@ const Modeler = ({ versionName, process, versions, ...divProps }: ModelerProps)
versions={versions}
canRedo={canRedo}
canUndo={canUndo}
modeler={modeler.current}
/>
)}
{selectedVersionId && !showMobileView && <VersionToolbar processId={process.id} />}
Expand All @@ -292,7 +293,7 @@ const Modeler = ({ versionName, process, versions, ...divProps }: ModelerProps)
bpmn={xmlEditorBpmn}
canSave={!selectedVersionId}
onClose={handleCloseXmlEditor}
onSaveXml={handleXmlEditorSave}
onSaveXml={handleXmlSave}
process={process}
versionName={versionName}
/>
Expand Down
5 changes: 5 additions & 0 deletions src/management-system-v2/components/bpmn-canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import schema from '@/lib/schema';
import { copyProcessImage } from '@/lib/process-export/copy-process-image';
import Modeling, { CommandStack } from 'bpmn-js/lib/features/modeling/Modeling';
import { Root, Element } from 'bpmn-js/lib/model/Types';
import ElementFactory from 'bpmn-js/lib/features/modeling/ElementFactory';

// Conditionally load the BPMN modeler only on the client, because it uses
// "window" reference. It won't be included in the initial bundle, but will be
Expand Down Expand Up @@ -81,6 +82,7 @@ export interface BPMNCanvasRef {
getModeling: () => Modeling;
getFactory: () => BpmnFactory;
loadBPMN: (bpmn: string) => Promise<void>;
getElementFactory: () => ElementFactory;
}

const BPMNCanvas = forwardRef<BPMNCanvasRef, BPMNCanvasProps>(
Expand Down Expand Up @@ -159,6 +161,9 @@ const BPMNCanvas = forwardRef<BPMNCanvasRef, BPMNCanvasProps>(
await modeler.current!.importXML(bpmn);
fitViewport(modeler.current!);
},
getElementFactory: () => {
return modeler.current!.get<ElementFactory>('elementFactory');
},
}));

const [Modeler, Viewer] = use(BPMNJs);
Expand Down
37 changes: 37 additions & 0 deletions src/management-system-v2/components/bpmn-chatbot-response.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { FunctionCall } from '@google/generative-ai';
import { Modal } from 'antd';
import Title from 'antd/es/typography/Title';
import React from 'react';

type ChatbotResponseModalProps = {
open: boolean;
onClose: () => void;
chatbotInteraction: ChatbotInteraction;
};

export type ChatbotInteraction = {
userPrompt: string;
bpmnProcess: string;
chatbotResponse: FunctionCall[] | undefined;
};

const ChatbotResponseModal: React.FC<ChatbotResponseModalProps> = ({
open,
onClose,
chatbotInteraction,
}) => {
return (
<Modal title="Response Details" open={open} onCancel={onClose} footer={null}>
<Title level={5}>User Prompt</Title>
<p style={{ maxHeight: 300, overflow: 'auto' }}>{chatbotInteraction.userPrompt}</p>
<Title level={5}>BPMN Process Element</Title>
<p style={{ maxHeight: 300, overflow: 'auto' }}>{chatbotInteraction.bpmnProcess}</p>
<Title level={5}>Response Content</Title>
<p style={{ maxHeight: 300, overflow: 'auto' }}>
{JSON.stringify(chatbotInteraction.chatbotResponse)}
</p>
</Modal>
);
};

export default ChatbotResponseModal;
67 changes: 67 additions & 0 deletions src/management-system-v2/components/bpmn-chatbot-tools.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
[
{
"name": "append_element",
"description": "Create a BPMN element and connect it to an already existing element. The element can either be a task, event or gateway. This automatically places the element and creates a connection from the given source element.",
"parameters": {
"type": "object",
"properties": {
"bpmn_type": {
"type": "string",
"description": "The BPMN type of the element. Possible types are: 'Task','StartEvent','EndEvent','ExclusiveGateway','InclusiveGateway','ParallelGateway'."
},
"name": {
"type": "string",
"description": "The name of the new element."
},
"source_element_id_or_name": {
"type": "string",
"description": "The id or name of the source element which to append to. Use the id if the element already exists in the process, otherwise the name."
},
"label": {
"type": "string",
"description": "The label of the connection which is automatically created."
}
},
"required": ["bpmn_type", "name", "source_element_id_or_name"]
}
},
{
"name": "create_connection",
"description": "Create a connection between two elements. Only needed if the connection has not been created yet.",
"parameters": {
"type": "object",
"properties": {
"source_element_id_or_name": {
"type": "string",
"description": "The id or name of the source element of the connection. User the id if the element already exists in the process, otherwise the name."
},
"target_element_id_or_name": {
"type": "string",
"description": "The id or name of the target element of the connection. User the id if the element already exists in the process, otherwise the name."
},
"label": {
"type": "string",
"description": "The optional label of the connection."
}
},
"required": ["source_element_id_or_name", "target_element_id_or_name"]
}
},
{
"name": "remove_elements",
"description": "Remove BPMN elements. Removing an element with exactly one incomming and one outgoing connection merges the two connections into one. If the deleted element has more than one incomming or outgoing connection all associated connections will be removed. Connections are also considered elements and can be seperately removed too by including their id here.",
"parameters": {
"type": "object",
"properties": {
"element_ids": {
"type": "array",
"description": "The ids of the elements to be removed.",
"items": {
"type": "string"
}
}
},
"required": ["element_ids"]
}
}
]
Loading