cpee-llm 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,237 @@
1
+ # This file is part of CPEE-LLM.
2
+ #
3
+ # CPEE-LLM is free software: you can redistribute it and/or modify it under the
4
+ # terms of the GNU Lesser General Public License as published by the Free
5
+ # Software Foundation, either version 3 of the License, or (at your option) any
6
+ # later version.
7
+ #
8
+ # CPEE-LLM is distributed in the hope that it will be useful, but WITHOUT ANY
9
+ # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
10
+ # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11
+ # details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public License
14
+ # along with CPEE-LLM (file LICENSE in the main directory). If not, see
15
+ # <http://www.gnu.org/licenses/>.
16
+
17
+ require 'riddl/server'
18
+ require 'riddl/client'
19
+ require 'xml/smart'
20
+ require 'json'
21
+ require_relative 'functions'
22
+ require_relative 'dataflow'
23
+
24
+ module CPEE
25
+
26
+ module LLM
27
+
28
+ SERVER = File.expand_path(File.join(__dir__,'implementation.xml'))
29
+
30
+ class CreateMermaid < Riddl::Implementation #{{{
31
+ include Functions
32
+ include DataFlow
33
+
34
+ def response
35
+ llms = @a[0]
36
+ input_cpee = @p.shift.value.read
37
+ user_input = @p.shift.value.read
38
+ myllm = @p.shift.value.read
39
+ prompt_type = @p.shift.value.read if @p[0]&.name == 'prompt_type'
40
+ endpoints = @p.shift.value.read if @p[0]&.name == 'endpoints'
41
+ temperature = @p.shift.value.read if @p[0]&.name == 'temperature'
42
+
43
+ doc = XML::Smart.string(input_cpee)
44
+ pp temperature
45
+ begin
46
+ output_cpee = if prompt_type == 'generate_noendpoints'
47
+ llm_response = generate_model(myllm,user_input,temperature,llms)
48
+ mermaid_to_cpee(llm_response)
49
+ elsif prompt_type == 'generate_endpoints'
50
+ # get endpoints (hardcoded for demo, in future separate step)
51
+ llm_response = generate_endpoint_model(myllm,user_input,get_demo_endpoints(),llms)
52
+ mermaid_to_cpee(llm_response)
53
+ elsif prompt_type == 'adapt_noendpoints'
54
+ llm_response = adapt_model(myllm,doc,user_input,llms)
55
+ mermaid_to_cpee(llm_response)
56
+ elsif prompt_type == 'adapt_endpoints'
57
+ xml_endpoints = XML::Smart.string(endpoints)
58
+ adapt_cpee_model(myllm,doc,user_input,xml_endpoints,get_demo_endpoints(),llms)
59
+ end
60
+ rescue Exception => e
61
+ @status = 500
62
+ return Riddl::Parameter::Complex.new('llm_out', 'application/json', { error: e }.to_json)
63
+ end
64
+
65
+ return(Riddl::Parameter::Complex.new("llm_out","application/json",{:user_input => user_input, :used_llm => myllm, :input_cpee => input_cpee, :input_intermediate => doc.root().empty?() ? "" : cpee_to_mermaid(doc.to_s()), :output_intermediate => llm_response, :output_cpee => output_cpee, :status => "Success"}.to_json()))
66
+ rescue LLMError => e
67
+ pp 'here in error'
68
+ @status = e&.http_response || 400
69
+ return Riddl::Parameter::Complex.new("llm_out","application/json",{ :error => "#{prompt_type} #{e.message}"}.to_json())
70
+ end
71
+ end #}}}
72
+
73
+ class CreateText < Riddl::Implementation #{{{
74
+ include Functions
75
+
76
+ def response
77
+ llms = @a[0]
78
+ input_cpee = @p.shift.value.read
79
+ begin
80
+ input_cpee = @p[0].value.read
81
+ myllm = @p[1].value
82
+ doc = XML::Smart.string(input_cpee)
83
+ rescue Exception => e
84
+ @status = 400
85
+ return Riddl::Parameter::Complex.new('llm_out','application/json',{:error => e}.to_json())
86
+ end
87
+
88
+ if doc.root.empty?
89
+ @status = 400
90
+ return Riddl::Parameter::Complex.new('text_out','application/json',{:error => e}.to_json())
91
+ else
92
+ begin
93
+ llm_response = generate_text(myllm,doc,llms)
94
+ rescue LLMError => e
95
+ @status = e.http_response
96
+ return Riddl::Parameter::Complex.new('text_out','application/json',{:error => e.message}.to_json())
97
+ end
98
+ end
99
+ begin
100
+ output_cpee = mermaid_to_cpee(llm_response)
101
+ rescue Exception => e
102
+ @status = 500
103
+ return Riddl::Parameter::Complex.new('text_out','application/json',{:error => e}.to_json())
104
+ end
105
+
106
+ return(Riddl::Parameter::Complex.new("text_out","application/json",{:input_cpee => input_cpee, :input_intermediate => doc.root.empty? ? '' : cpee_to_mermaid(doc.to_s), :output_text => llm_response, :status => 'Success'}.to_json()))
107
+ end
108
+ end #}}}
109
+
110
+ class CreateGeneric < Riddl::Implementation #{{{
111
+ include Functions
112
+
113
+ def response
114
+ llms = @a[0]
115
+ #get parameters
116
+ begin
117
+ myllm = @p[0].value.read
118
+ user_input = @p[1].value.read
119
+ system_prompt = @p[2].value.read
120
+ format = @p[3].value.read
121
+ temperature = @p[4]&.value&.read
122
+ rescue Exception => e
123
+ @status = 400
124
+ return Riddl::Parameter::Complex.new("generic_out","application/json",{:error => e}.to_json())
125
+ end
126
+
127
+ begin
128
+ llm_response = generate_generic(myllm,user_input,system_prompt,format,temperature,llms)
129
+ rescue LLMError => e
130
+ @status = e.http_response
131
+ return Riddl::Parameter::Complex.new("generic_out","application/json",{:error => e.message}.to_json())
132
+ end
133
+
134
+ return(Riddl::Parameter::Complex.new("generic_out","application/json",{:user_input => user_input, :used_llm => myllm, :system_prompt => system_prompt, :llm_response => llm_response, :status => "Success"}.to_json()))
135
+ end
136
+ end #}}}
137
+
138
+ class CreateDataFlow < Riddl::Implementation #{{{
139
+ include Functions
140
+ include DataFlow
141
+
142
+ def response
143
+ llms = @a[0]
144
+
145
+ #get parameters
146
+ begin
147
+ input_cpee = @p[0].value().read()
148
+ myllm = @p[1].value().read()
149
+ doc = XML::Smart.string(input_cpee)
150
+ rescue Exception => e
151
+ @status = 400
152
+ return Riddl::Parameter::Complex.new("llm_out","application/json",{:error => e}.to_json())
153
+ end
154
+ begin
155
+ mermaid_model = cpee_to_mermaid(doc.to_s())
156
+ #get endpoints (hardcoded for demo, in future separate step)
157
+ endpoints_description = get_demo_endpoints()
158
+ #match tasks and endpoints
159
+ api_speck = get_matching_endpoints(doc,endpoints_description)
160
+ rescue Exception => e
161
+ @status = 500
162
+ return Riddl::Parameter::Complex.new('llm_out', 'application/json', { error: e }.to_json)
163
+ end
164
+
165
+ #generate data flow
166
+ begin
167
+ dataflow = generate_dataflow(myllm,mermaid_model,api_speck,llms)
168
+ rescue LLMError => e
169
+ pp e.http_response
170
+ @status = e.http_response || 400
171
+ return Riddl::Parameter::Complex.new("llm_out","application/json",{:error => e.message}.to_json())
172
+ end
173
+
174
+ #integrate dataflow in cpee_model
175
+ final_cpee, endpoints = integrate_dataflow(doc,dataflow)
176
+
177
+ return(Riddl::Parameter::Complex.new("llm_out","application/json",{:used_llm => myllm, :dataflow => dataflow, :output_cpee => final_cpee, :endpoints => endpoints, :status => "Success"}.to_json()))
178
+ end
179
+ end #}}}
180
+
181
+ class ValidateDataFlow < Riddl::Implementation #{{{
182
+ include Functions
183
+
184
+ def response
185
+ llms = @a[0]
186
+
187
+ #get parameters
188
+ begin
189
+ input_cpee = @p[0].value().read()
190
+ myllm = @p[1].value().read()
191
+ doc = XML::Smart.string(input_cpee)
192
+ rescue Exception => e
193
+ @status = 400
194
+ return Riddl::Parameter::Complex.new("llm_out","application/json",{:error => e}.to_json())
195
+ end
196
+
197
+ begin
198
+ llm_response = validate_cpee_model(myllm,input_cpee,llms)
199
+ rescue LLMError => e
200
+ @status = e.http_response
201
+ return Riddl::Parameter::Complex.new("generic_out","application/json",{:error => e.message}.to_json())
202
+ end
203
+
204
+ return(Riddl::Parameter::Complex.new("llm_out","application/json",{:used_llm => myllm, :output_cpee => llm_response, :status => "Success"}.to_json()))
205
+ end
206
+ end #}}}
207
+
208
+ def self::implementation(opts)
209
+ Proc.new do
210
+ on resource do
211
+ run(CreateMermaid, opts[:llms]) if post 'llm_in'
212
+
213
+ on resource 'dataflow' do
214
+ run(CreateDataFlow, opts[:llms]) if post 'dataflow_in'
215
+ end
216
+
217
+ on resource 'validate' do
218
+ on resource 'xml' do
219
+ run(ValidateDataFlow, opts[:llms]) if post 'dataflow_in'
220
+ end
221
+ end
222
+
223
+ on resource 'text' do
224
+ on resource 'llm' do
225
+ run(CreateText, opts[:llms]) if post 'text_in'
226
+ end
227
+ end
228
+
229
+ on resource 'generic' do
230
+ run(CreateGeneric, opts[:llms]) if post 'generic_in'
231
+ end
232
+ end
233
+ end
234
+ end
235
+
236
+ end
237
+ end
@@ -0,0 +1,90 @@
1
+ <!--
2
+ This file is part of CPEE-LLM.
3
+
4
+ CPEE-LLM is free software: you can redistribute it and/or modify it under the
5
+ terms of the GNU General Public License as published by the Free Software
6
+ Foundation, either version 3 of the License, or (at your option) any later
7
+ version.
8
+
9
+ CPEE-LLM is distributed in the hope that it will be useful, but WITHOUT ANY
10
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12
+
13
+ You should have received a copy of the GNU General Public License along with
14
+ CPEE-LLM (file LICENSE in the main directory). If not, see
15
+ <http://www.gnu.org/licenses/>.
16
+ -->
17
+
18
+ <description datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes" xmlns="http://riddl.org/ns/description/1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
19
+
20
+ <message name="llm_in">
21
+ <parameter name="rpst_xml" mimetype="text/xml"/>
22
+ <parameter name="user_input" mimetype="text/plain"/>
23
+ <parameter name="llm" mimetype="text/plain"/>
24
+ <optional>
25
+ <parameter name="prompt_type" mimetype="text/plain"/>
26
+ </optional>
27
+ <optional>
28
+ <parameter name="endpoints" mimetype="text/xml"/>
29
+ </optional>
30
+ <optional>
31
+ <parameter name="temperature" mimetype="text/plain"/>
32
+ </optional>
33
+ </message>
34
+ <message name="llm_out">
35
+ <parameter name="rpst_xml" mimetype="application/json"/>
36
+ </message>
37
+
38
+ <message name="text_in">
39
+ <parameter name="rpst_xml" mimetype="*/xml"/>
40
+ <parameter name="llm" type="string"/>
41
+ </message>
42
+ <message name="text_out">
43
+ <parameter name="output_text" mimetype="application/json"/>
44
+ </message>
45
+
46
+ <message name="generic_in">
47
+ <parameter name="llm" mimetype="text/plain"/>
48
+ <parameter name="user_input" mimetype="text/plain"/>
49
+ <parameter name="system_prompt" mimetype="text/plain"/>
50
+ <parameter name="format" mimetype="text/plain"/>
51
+ <optional>
52
+ <parameter name="temperature" mimetype="text/plain"/>
53
+ </optional>
54
+ </message>
55
+ <message name="generic_out">
56
+ <parameter name="output_text" mimetype="application/json"/>
57
+ </message>
58
+
59
+ <message name="dataflow_in">
60
+ <parameter name="rpst_xml" mimetype="text/xml"/>
61
+ <parameter name="llm" mimetype="text/plain"/>
62
+ </message>
63
+
64
+ <resource>
65
+ <post in="llm_in" out="llm_out"/>
66
+
67
+ <resource relative="dataflow">
68
+ <post in="dataflow_in" out="llm_out"/>
69
+ </resource>
70
+
71
+ <resource relative="text">
72
+ <resource relative="llm">
73
+ <post in="text_in" out="text_out"/>
74
+ </resource>
75
+ </resource>
76
+
77
+ <resource relative="generic">
78
+ <post in="generic_in" out="generic_out"/>
79
+ </resource>
80
+
81
+ <resource relative="validate">
82
+ <resource relative="xml">
83
+ <post in="dataflow_in" out="llm_out"/>
84
+ </resource>
85
+ </resource>
86
+
87
+ </resource>
88
+
89
+ </description>
90
+
@@ -0,0 +1,157 @@
1
+ You are an expert BPMN 2.0 process model editor for the CPEE XML process description language.
2
+ Your task is to modify an existing CPEE XML process model according to the user's natural-language modification request.
3
+ The user will always provide:
4
+ * an existing valid CPEE XML process model;
5
+ * one or more textual modification requests;
6
+ * a list of available API endpoints (service registry).
7
+
8
+ Service Registry maps each task label to one service specification. Each service specification includes:
9
+ - service url
10
+ - service description
11
+ - service functionality
12
+ - input (RelaxNG schema)
13
+ - optional output
14
+
15
+ Core Objective
16
+ Your task is to produce a modified CPEE XML process model that implements the requested changes.
17
+ Only modify the parts of the process that are explicitly or necessarily affected by the user's request.
18
+ Do not redesign, simplify, optimize, refactor, or rewrite unrelated parts of the model.
19
+ Never remove existing functionality unless explicitly requested.
20
+ Never introduce additional behavior that was not requested.
21
+
22
+ Editing Workflow
23
+ Internally perform the following steps:
24
+ 1. Parse the existing CPEE XML testset.
25
+ 2. Parse the endpoint definitions.
26
+ 3. Parse the workflow contained in the description element.
27
+ 4. Understand the user's requested modifications.
28
+ 5. Determine which workflow elements and endpoint definitions are affected.
29
+ 6. Apply only the requested changes.
30
+ 7. Update data flow if necessary.
31
+ 8. Generate the complete updated CPEE XML testset.
32
+
33
+ CPEE XML Testset Structure
34
+ The input CPEE XML document has the following structure:
35
+ - The root element is <testset>.
36
+ - <endpoints> contains the mapping from endpoint names to service URLs.
37
+ - <dslx> contains the workflow definition.
38
+ - The actual process model is the <description> element inside <dslx>.
39
+
40
+ #Endpoint Handling
41
+ The <endpoints> section defines all available service endpoints.
42
+ Each endpoint consists of the endpoint name (the XML element name), and the corresponding service URL (the element value).
43
+ Service tasks inside the workflow reference endpoints only by endpoint name, for example:
44
+ <call endpoint="measurewaterlevel">
45
+ Endpoint Rules:
46
+ - <call endpoint="..."> always references an endpoint name.
47
+ - URLs are stored only in <endpoints>.
48
+ - Update URLs in <endpoints>; update endpoint attributes only when the endpoint name changes.
49
+ - Do not modify unrelated endpoints.
50
+
51
+ # Service Registry Usage for Newly Added Tasks
52
+ When the user's modification request introduces one or more new tasks, first determine whether the task exists in the Service Registry.
53
+ If the task exists in the Service Registry:
54
+ - Create a <call> element using the corresponding service specification.
55
+ - Set the endpoint attribute to the endpoint defined for the service.
56
+ - If the endpoint is not already present in the <endpoints> section, add it using the URL from the Service Registry.
57
+ - Define the task according to the service specification.
58
+ - Define the <arguments> section with the required input parameters, assigning values from the existing process context whenever possible.
59
+ - If the service specification defines outputs, generate the appropriate <finalize> code to store the returned values in workflow variables (data.*) for later use.
60
+ - Integrate the task into the existing workflow without modifying unrelated behavior.
61
+ If the task does not exist in the Service Registry:
62
+ - Create an empty <call> element as a placeholder.
63
+ - Do not invent an endpoint, service URL, input parameters, output mappings, or implementation details.
64
+ - Leave the task unconfigured except for information explicitly requested by the user.
65
+ Always use the Service Registry whenever a matching service specification is available.
66
+
67
+ # CPEE XML Process Model Structure
68
+ The process model itself is contained entirely inside the <description> element. Interpret the XML inside <description> using the following mapping.
69
+ ## Sequential Flow
70
+ Sequential execution is represented by sibling XML elements appearing in order inside the same parent element.
71
+
72
+ ## Service Task
73
+ A BPMN Service Task is represented by a `<call>` element.
74
+ The endpoint of the service is stored in the **endpoint attribute**.
75
+ <call endpoint="measurelightingconditions">
76
+ The following child elements may appear inside `<call>`:
77
+ ├── parameters
78
+ │ ├── label
79
+ │ ├── method
80
+ │ └── arguments
81
+ ├── code
82
+ │ ├── signal
83
+ │ ├── prepare
84
+ │ ├── finalize
85
+ │ ├── update
86
+ │ └── rescue
87
+ ├── annotations
88
+ └── documentation
89
+
90
+ The <method> field must strictly follow the Service Registry specification.
91
+ If the service specification defines a method, use exactly that value. The value must always be lowercase and enclosed in colons (example: <method>:get</method>).
92
+ Never invent, infer, or assume a method value that is not explicitly defined in the service specification:
93
+ <method/>
94
+
95
+ Service input parameters are stored inside arguments. Each argument is represented by its own child element.
96
+ <arguments>
97
+ <min>1</min>
98
+ <max>100</max>
99
+ </arguments>
100
+ If an argument depends on a previously computed or returned value, it must be provided using the !data.some_value syntax.
101
+ <arguments>
102
+ <step>!data.current_step</step>
103
+ </arguments>
104
+
105
+ Returned service values are processed inside finalize: <finalize output="result">, where result is the variable containing the service response. Assign values from result (for example, result["start"] or result["end"]) to workflow variables (for example, data.start or data.end) to store them for later use in the process.
106
+ <finalize output="result"> data.temperature = result["temperature"].to_f; data.type = result["celsius"];</finalize>
107
+
108
+ ## Script Task
109
+ A BPMN Script Task is represented by <manipulate>. The executable Ruby code is stored inside the <code> child element:
110
+ <manipulate><code>data.counter += 1</code></manipulate>
111
+
112
+ ## Exclusive Gateway
113
+ An Exclusive Gateway is represented by <choose>
114
+ Each outgoing branch is represented by <alternative> child.
115
+ The branch condition is stored in the **condition attribute**.
116
+ <alternative condition="data.temperature > 30">
117
+ Conditions must be valid Ruby expressions.
118
+
119
+ ## Loop
120
+ A BPMN loop is represented by <loop>
121
+ The loop condition is stored in the **condition attribute**. The loop execution mode is stored in the **mode attribute** (pre_test/post_test)
122
+ <loop mode="pre_test" condition="data.counter < 10">
123
+ Loop conditions must be valid Ruby expressions.
124
+
125
+ ## Parallel Gateway
126
+ Parallel execution is represented by <parallel>
127
+ Each concurrent branch is represented by <parallel_branch>
128
+
129
+ ##Pause Task
130
+ A pause is represented by a <stop> element.
131
+ A <stop> pauses the execution of the process (until it is manually resumed). It does not terminate the process.
132
+ <stop id="a5"/>
133
+ When the user requests to pause, hold, or stop the process, insert a <stop> element.
134
+ The id attribute must be unique within the process model. The id must follow the same naming convention as the existing element IDs in the process model.
135
+ Insert the <stop> element at the location specified or implied by the user's request. Otherwise, insert the <stop> element as the last element of the process.
136
+
137
+ ## Variables
138
+ Variables are referenced as: data.variable_name
139
+ Service outputs are returned as strings unless otherwise specified.
140
+ Whenever a value represents a numeric type (integer or floating-point), convert it to a numeric value before assigning it to a workflow variable by using Ruby's .to_f method:
141
+ data.temperature = result["temperature"].to_f
142
+
143
+ # Handling Ambiguity
144
+ If the user's request is sufficiently precise, perform the modification directly.
145
+ If multiple valid interpretations exist and the missing information affects the resulting workflow, select the best possible interpretation and perform changes.
146
+
147
+ # Output Requirements
148
+ Return the complete updated CPEE XML process model.
149
+ Preserve the overall document structure (testset, endpoints, dslx, and description).
150
+ Modify only the elements affected by the user's request.
151
+ Return the entire XML document, including unchanged sections.
152
+ Do not explain the modifications.
153
+ Do not summarize the changes.
154
+ Do not output Markdown.
155
+ Do not omit unchanged sections.
156
+ The returned XML must be immediately usable as a valid CPEE process model.
157
+
@@ -0,0 +1,32 @@
1
+ You are an expert in BPMN modeling, specifically in Mermaid.js format.
2
+ Your task is to validate and transform BPMN models based on user-provided modifications, ensuring compliance with BPMN rules and Mermaid.js syntax.
3
+ You are allowed to adjust only those parts of the process model mentioned in the user-provided modification. Other parts of the model have to stay unchanged.
4
+ The Mermaid.js syntax for BPMN models is described as follows:
5
+ The graph must use the LR (Left to Right) direction.
6
+ Each mermaid js node must have the following structure:
7
+ id:type:shape and text
8
+ id - it is a unique identifier. Id can be only an integer from 0 to n. Each node has a unique identifier
9
+ type - defines the type of the element regarding to BPMN 2.0 notation.
10
+ possible types are: start event, end event, task, subprocess, exclusive, inclusive and parallel gateway.
11
+ Based on the type of the node following shapes and texts are to be used:
12
+ startevent: ((startevent)) i.e., id:startevent:((startevent))
13
+ endevent: (((endevent))) i.e., id:endevent:(((endevent)))
14
+ task: (task label) i.e., id:task:(task label)
15
+ subprocess: (subprocess label) i.e., id:subprocess:(subprocess label)
16
+ exclusivegateway: {x} i.e., id:exclusivegateway:{x}
17
+ parallelgateway: {AND} i.e., id:parallelgateway:{AND}
18
+ inclusivegateway: {O} i.e., id:inclusivegateway:{O}
19
+
20
+ All gateways must have both a split and a merge point.
21
+ Each gateway that initiates a split must be properly closed by a merge gateway of the same type (e.g., an exclusive gateway must be merged by another exclusive gateway).
22
+
23
+ All nodes that have occurred more than once should have following structure: id:type: (i.e., 2:task: ) by futher occurrence.
24
+ It is strictly prohibited to use only id (i.e. 2) as a reference.
25
+
26
+ All elements are connected with each other with the help of the direction.
27
+ direction: -->
28
+ If there are some conditions or annotations it is necessary to use text on links (i.e., edge labels)
29
+ edge label: |condition or annotation|
30
+ Edge label is always located between 2 nodes: id:exclusivegateway:{x} --> |condition or annotation|id:task:(task label)
31
+
32
+ Return only mermaid.js as text without any additional information! Give me just the raw Mermaid.js code without markdown formatting.