cpee-llm 1.0.5 → 1.1.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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +15 -0
  3. data/cpee-llm.gemspec +2 -2
  4. data/lib/cpee/llm/functions.rb +96 -67
  5. data/lib/cpee/llm/implementation.rb +42 -13
  6. data/lib/cpee/llm/implementation.xml +34 -1
  7. data/lib/cpee/llm/prompts/system/adapt_docxml_description.txt +50 -0
  8. data/lib/cpee/llm/prompts/{adapt_xml.txt → system/adapt_xml_endpoints.txt} +1 -76
  9. data/lib/cpee/llm/prompts/system/cpee_xml.txt +99 -0
  10. data/lib/cpee/llm/prompts/system/cpee_xml_documentation_modelling.txt +35 -0
  11. data/lib/cpee/llm/prompts/system/intent.txt +45 -0
  12. data/lib/cpee/llm/prompts/user/cpee_repair.txt +5 -0
  13. data/lib/cpee/llm/prompts/user/dataflow.txt +9 -0
  14. data/lib/cpee/llm/prompts/user/intent.txt +17 -0
  15. data/lib/cpee/llm/prompts/user/process_description.txt +5 -0
  16. data/lib/cpee/llm/prompts/user/process_description_endpoints.txt +9 -0
  17. data/lib/cpee/llm/prompts/user/process_model_adapt.txt +8 -0
  18. data/lib/cpee/llm/prompts/user/process_model_adapt_api.txt +11 -0
  19. data/lib/cpee/llm/prompts/user/process_model_text.txt +4 -0
  20. data/lib/cpee/llm/prompts/user/repair.txt +5 -0
  21. data/lib/cpee/llm/rubyllm_requests.rb +64 -107
  22. data/server/connect_gemini +1 -1
  23. data/server/connect_gpt +1 -1
  24. data/server/connect_morpheus +1 -1
  25. data/server/cpee-llm +1 -1
  26. data/server/plugins.json +51 -0
  27. data/tools/cpee-llm-tool +1 -1
  28. metadata +29 -15
  29. /data/lib/cpee/llm/prompts/{apply.txt → system/apply.txt} +0 -0
  30. /data/lib/cpee/llm/prompts/{dataflow.txt → system/dataflow.txt} +0 -0
  31. /data/lib/cpee/llm/prompts/{derive.txt → system/derive.txt} +0 -0
  32. /data/lib/cpee/llm/prompts/{describe.txt → system/describe.txt} +0 -0
  33. /data/lib/cpee/llm/prompts/{gemini-request.json → system/gemini-request.json} +0 -0
  34. /data/lib/cpee/llm/prompts/{generate1.txt → system/generate1.txt} +0 -0
  35. /data/lib/cpee/llm/prompts/{generate_enpoints.txt → system/generate_enpoints.txt} +0 -0
  36. /data/lib/cpee/llm/prompts/{generate_enpoints1.txt → system/generate_enpoints1.txt} +0 -0
  37. /data/lib/cpee/llm/prompts/{identify.txt → system/identify.txt} +0 -0
  38. /data/lib/cpee/llm/prompts/{validate_mermaid.xml → system/validate_mermaid.xml} +0 -0
  39. /data/lib/cpee/llm/prompts/{validate_xml.txt → system/validate_xml.txt} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a187cd11681edde3787660c4eae04b84fb4d338b2994a1aafc6f5e52fe76eacb
4
- data.tar.gz: 85c5bc102f4b72c7772c95065a6b3552eca12248f0c6a4aff1536fc0acbaddbc
3
+ metadata.gz: 173032c1cbbc6ff2fa8ecb64b3ad47dc3c8998e9f978cb3ba137228948c1ad93
4
+ data.tar.gz: '090933e62d1ceb692f42762c6c7c2efa06f4c842032e2d38ac202cbd77eb7150'
5
5
  SHA512:
6
- metadata.gz: d4b4b8b01093f881cf83fa9e4854c94a1a2870841eb554f444aedbe85893a8e2377f691e5f6bfa5d46c380b31f4287f1841e2e31aeb0f3bf8a2d2ea741c9a65c
7
- data.tar.gz: 1cb3fd46fcda5bac09f6d7ddb0c24d357bdf85f44c6836bd8ee91d0387f8a02002b362fa672c1ccc776ce0a6d8faded5ad06e3a3da6ccc23089528f80b326c27
6
+ metadata.gz: ff11667abbf905c538f416b28642c6d91edab16f1e4c77e2344f31f9770e240c52c900d8a7f9bf29af57df40a3f3946aae33b1cc3e548edf093921a175cd0c69
7
+ data.tar.gz: cfeeacc7885181012472be6be35677ea66521bc10e6141812b212a1e2390a01bcd9f10e86d752de8f83b0e2972dc5896be50d9e936d50df2aa4dd1e27ab68cc6
data/README.md CHANGED
@@ -152,6 +152,7 @@ Performs arbitrary LLM tasks using a system prompt and user input.
152
152
  | system_prompt | text/plain |
153
153
  | format | text/plain (`true`/`false`) |
154
154
  | temperature | text/plain (optional) |
155
+ | documents | `*/*` (optional, repeatable) |
155
156
 
156
157
  ## Response
157
158
 
@@ -168,6 +169,7 @@ Performs arbitrary LLM tasks using a system prompt and user input.
168
169
  ### Notes
169
170
 
170
171
  - `format=true` requests JSON output (guarantees valid JSON but not any specific structure).
172
+ - `documents` can be repeated to attach one or more files (e.g. images, PDFs, office documents, plain text) to the request, sent as multipart file parts. Attachments are passed through to the LLM alongside `user_input`; support depends on the selected model/provider.
171
173
 
172
174
  List of supporteb providers: https://rubyllm.com/chat/#getting-structured-output
173
175
 
@@ -182,4 +184,17 @@ curl -X POST https://cpee.org/llm/generic/ \
182
184
  -F "format=false;type=text/plain"
183
185
  ```
184
186
 
187
+ With attached documents:
188
+
189
+ ```bash
190
+ curl -X POST https://cpee.org/llm/generic/ \
191
+ -H 'Content-Type:multipart/form-data' \
192
+ -F "llm=gemini-2.5-flash-lite;type=text/plain" \
193
+ -F "user_input=Summarize the attached document.;type=text/plain" \
194
+ -F "system_prompt=Return the summary as bullet points.;type=text/plain" \
195
+ -F "format=false;type=text/plain" \
196
+ -F "documents=@report.pdf;type=application/pdf" \
197
+ -F "documents=@chart.png;type=image/png"
198
+ ```
199
+
185
200
  ---
data/cpee-llm.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "cpee-llm"
3
- s.version = "1.0.5"
3
+ s.version = "1.1.0"
4
4
  s.platform = Gem::Platform::RUBY
5
5
  s.license = "LGPL-3.0-or-later"
6
6
  s.summary = "CPEE Conversational Agents"
@@ -26,5 +26,5 @@ Gem::Specification.new do |s|
26
26
  s.add_runtime_dependency 'typhoeus', '~> 1.4'
27
27
  s.add_runtime_dependency 'ruby_llm', '~> 1.15'
28
28
  s.add_runtime_dependency 'rag_embeddings', '~> 0'
29
- s.add_runtime_dependency 'cpee-transformation', '~> 1.0'
29
+ s.add_runtime_dependency 'cpee-transformation', '~> 1'
30
30
  end
@@ -45,15 +45,9 @@ module CPEE
45
45
  trans.generate_model(CPEE::Transformation::Target::CPEE)
46
46
  end #}}}
47
47
 
48
- def generate_model(myllm,user_input,temperature,llms) #{{{
48
+ def error_handler #{{{
49
49
  begin
50
- llm_response = generate_mermaid_model(myllm,user_input,temperature,llms)
51
- # raise exceptions if response is empty for some reason
52
- if llm_response.nil? || llm_response.empty?
53
- raise LLMError.new("Something went wrong and your content was not generated!", 500)
54
- else
55
- return llm_response
56
- end
50
+ yield
57
51
  rescue LLMError => e_llm
58
52
  e_llm.message
59
53
  raise e_llm
@@ -63,38 +57,71 @@ module CPEE
63
57
  end
64
58
  end #}}}
65
59
 
60
+ def generate_model(myllm,user_input,temperature,llms) #{{{
61
+ error_handler do
62
+ system_prompt = build_system_prompt("generate1.txt")
63
+ user_prompt = build_user_prompt("process_description.txt", user_input:)
64
+ llm_response = generate_content(myllm,system_prompt,user_prompt,4000,temperature,llms)
65
+ # raise exceptions if response is empty for some reason
66
+ if llm_response.nil? || llm_response.empty?
67
+ raise LLMError.new("Something went wrong and your content was not generated!", 500)
68
+ else
69
+ return llm_response
70
+ end
71
+ end
72
+ end #}}}
73
+
66
74
  def adapt_model(myllm,doc,user_input,llms) #{{{
67
75
  input_cpee = doc.to_s()
68
76
  input_mermaid = cpee_to_mermaid(doc.to_s())
69
- begin
70
- llm_response = adapt_mermaid_model(myllm,user_input,input_mermaid,llms)
77
+ error_handler do
78
+ system_prompt = build_system_prompt("apply.txt")
79
+ user_prompt = build_user_prompt("process_model_adapt.txt", user_input:, process_model: input_mermaid)
80
+ llm_response = generate_content(myllm,system_prompt,user_prompt,4000,0,llms)
71
81
  # raise exceptions if response is empty for some reason
72
82
  if llm_response.nil? || llm_response.empty?
73
83
  raise LLMError.new("Something went wrong and your content was not generated!", 500)
74
84
  else
75
85
  return llm_response
76
86
  end
77
- rescue LLMError => e_llm
78
- raise e_llm
79
- rescue Exception => e
80
- raise e
87
+ end
88
+ end #}}}
89
+
90
+ def adapt_doccpee_description(myllm,doc,user_input,llms) #{{{
91
+ input_cpee = doc.to_s()
92
+ error_handler do
93
+ system_prompt = build_system_prompt("adapt_docxml_description.txt")
94
+ user_prompt = build_user_prompt("process_model_adapt.txt", user_input:, process_model: input_cpee)
95
+ llm_response = generate_content(myllm,system_prompt,user_prompt,16000,0,llms)
96
+ # raise exceptions if response is empty for some reason
97
+ if llm_response.nil? || llm_response.empty?
98
+ raise LLMError.new("Something went wrong and your content was not generated!", 500)
99
+ else
100
+ llm_response = llm_response.strip
101
+ inside = llm_response.scan(/```(\w+)?\s*\n(.*?)\n```/m)
102
+ llm_response = inside.empty? ? llm_response : inside[0][1]
103
+ #check if response is xml:
104
+ begin
105
+ XML::Smart.string(llm_response)
106
+ rescue Nokogiri::XML::SyntaxError => e
107
+ raise LLMError.new("Something went wrong and llm was not able to generate valid xml model: #{llm_response}", 500)
108
+ end
109
+ return llm_response
110
+ end
81
111
  end
82
112
  end #}}}
83
113
 
84
114
  def adapt_cpee_model(myllm,doc,user_input,existing_endpoints,endpoints,llms) #{{{
85
- testset = XML::Smart.string(<<~XML)
86
- <testset xmlns="http://cpee.org/ns/properties/2.0">
87
- </testset>
88
- XML
115
+ testset = XML::Smart.string('<testset xmlns="http://cpee.org/ns/properties/2.0"></testset>')
89
116
  root = testset.root
90
117
  root.add(existing_endpoints.root)
91
118
  dslx = root.add("dslx")
92
119
  dslx.add(doc.root)
93
120
 
94
- #puts testset.to_s
95
-
96
- begin
97
- llm_response = adapt_xml_model(myllm,user_input,testset,endpoints,llms)
121
+ error_handler do
122
+ system_prompt = build_system_prompt("adapt_xml_endpoints.txt")
123
+ user_prompt = build_user_prompt("process_model_adapt_api.txt", user_input:, process_model: testset, api_specification: endpoints)
124
+ llm_response = generate_content(myllm,system_prompt,user_prompt,15000,0,llms)
98
125
  # raise exceptions if response is empty for some reason
99
126
  if llm_response.nil? || llm_response.empty?
100
127
  raise LLMError.new("Something went wrong and your content was not generated!", 500)
@@ -110,54 +137,42 @@ module CPEE
110
137
  end
111
138
  return llm_response
112
139
  end
113
- rescue LLMError => e_llm
114
- raise e_llm
115
- rescue Exception => e
116
- raise e
117
140
  end
118
141
  end #}}}
119
142
 
120
143
  def generate_text(myllm,doc,llms) #{{{
121
144
  input_cpee = doc.to_s()
122
145
  input_mermaid = cpee_to_mermaid(doc.to_s())
123
- begin
124
- llm_response = generate_plain_text(myllm,input_mermaid,llms)
146
+ error_handler do
147
+ system_prompt = build_system_prompt("describe.txt")
148
+ user_prompt = build_user_prompt("process_model_text.txt", user_input: input_mermaid)
149
+ llm_response = generate_content(myllm,system_prompt,user_prompt,4000,0,llms)
125
150
  # raise exceptions if response is empty for some reason
126
151
  if llm_response.nil? || llm_response.empty?
127
152
  raise LLMError.new("Something went wrong and your content was not generated!", 500)
128
153
  else
129
154
  return llm_response
130
155
  end
131
- rescue LLMError => e_llm
132
- e_llm.message
133
- raise e_llm
134
- rescue Exception => e
135
- e.message
136
- raise e
137
156
  end
138
157
  end #}}}
139
158
 
140
- def generate_generic(myllm,user_input,system_prompt,format,temperature,llms) #{{{
141
- begin
142
- llm_response = generate_generic_content(myllm, user_input, system_prompt, format, temperature, llms)
159
+ def generate_generic(myllm,user_input,system_prompt,format,temperature,documents,llms) #{{{
160
+ error_handler do
161
+ llm_response = generate_content(myllm, system_prompt, user_input, 16000, temperature, llms, format == 'true' ? { json: true } : {}, documents)
143
162
  # raise exceptions if response is empty for some reason
144
163
  if llm_response.nil? || llm_response.empty?
145
164
  raise LLMError.new("Something went wrong and your content was not generated!", 500)
146
165
  else
147
166
  return llm_response
148
167
  end
149
- rescue LLMError => e_llm
150
- e_llm.message
151
- raise e_llm
152
- rescue Exception => e
153
- e.message
154
- raise e
155
168
  end
156
169
  end #}}}
157
170
 
158
171
  def generate_dataflow(myllm,mermaid_model,api_specification,llms) #{{{
159
- begin
160
- llm_response = generate_dataflow_content(myllm, mermaid_model, api_specification, llms)
172
+ error_handler do
173
+ system_prompt = build_system_prompt("dataflow.txt")
174
+ user_prompt = build_user_prompt("dataflow.txt", mermaid_model:, api_specification:)
175
+ llm_response = generate_content(myllm, system_prompt, user_prompt, 4000, 0.1, llms)
161
176
  # raise exceptions if response is empty for some reason
162
177
  if llm_response.nil? || llm_response.empty?
163
178
  raise LLMError.new("Something went wrong and your content was not generated!", 500)
@@ -175,36 +190,28 @@ module CPEE
175
190
  end
176
191
  return llm_response
177
192
  end
178
- rescue LLMError => e_llm
179
- e_llm.message
180
- raise e_llm
181
- rescue Exception => e
182
- e.message
183
- raise e
184
193
  end
185
194
  end #}}}
186
195
 
187
196
  def generate_endpoint_model(myllm,user_input,endpoints,llms) #{{{
188
- begin
189
- llm_response = generate_endpoint_mermaid_model(myllm,user_input,endpoints,llms)
197
+ error_handler do
198
+ system_prompt = build_system_prompt("generate_enpoints.txt")
199
+ user_prompt = build_user_prompt("process_description_endpoints.txt", user_input:, endpoints:)
200
+ llm_response = generate_content(myllm,system_prompt,user_prompt,4000,0.1,llms)
190
201
  # raise exceptions if response is empty for some reason
191
202
  if llm_response.nil? || llm_response.empty?
192
203
  raise LLMError.new("Something went wrong and your content was not generated!", 500)
193
204
  else
194
205
  return llm_response
195
206
  end
196
- rescue LLMError => e_llm
197
- e_llm.message
198
- raise e_llm
199
- rescue Exception => e
200
- e.message
201
- raise e
202
207
  end
203
208
  end #}}}
204
209
 
205
210
  def validate_cpee_model(myllm,cpee_model,llms) #{{{
206
- begin
207
- llm_response = validate_xml_model(myllm,cpee_model,llms)
211
+ error_handler do
212
+ system_prompt = build_system_prompt("validate_xml.txt")
213
+ user_prompt = build_user_prompt("cpee_repair.txt", cpee_model:)
214
+ llm_response = generate_content(myllm,system_prompt,user_prompt,0,0.1,llms)
208
215
  if llm_response.nil? || llm_response.empty?
209
216
  raise LLMError.new("Something went wrong and your content was not generated!", 500)
210
217
  elsif llm_response.strip.downcase == "perfect"
@@ -221,12 +228,34 @@ module CPEE
221
228
  end
222
229
  return llm_response
223
230
  end
224
- rescue LLMError => e_llm
225
- e_llm.message
226
- raise e_llm
227
- rescue Exception => e
228
- e.message
229
- raise e
231
+ end
232
+ end #}}}
233
+
234
+ def guess_intent(myllm,user_input,plugins,llms) #{{{
235
+ plugs = File.read(plugins)
236
+ error_handler do
237
+ system_prompt = build_system_prompt("intent.txt")
238
+ user_prompt = build_user_prompt("intent.txt", user_input:, plugins: plugs)
239
+ response = generate_content(myllm,system_prompt,user_prompt,4000,0.1,llms,{ json: true })
240
+ if response.nil? || response.empty?
241
+ raise LLMError.new("Something went wrong and your content was not generated!", 500)
242
+ else
243
+ result = JSON::parse(response)
244
+ if result.is_a?(Hash) && result["id"]
245
+ plugin_list = JSON::parse(plugs)
246
+ plugin = plugin_list.find { |p| p["id"] == result["id"] }
247
+ if plugin
248
+ plugin.each do |k,v|
249
+ result[k] = v unless k == "description"
250
+ end
251
+ result
252
+ else
253
+ nil
254
+ end
255
+ else
256
+ result
257
+ end
258
+ end
230
259
  end
231
260
  end #}}}
232
261
 
@@ -42,7 +42,7 @@ module CPEE
42
42
 
43
43
  doc = XML::Smart.string(input_cpee)
44
44
  begin
45
- output_cpee = if prompt_type == 'generate_noendpoints'
45
+ output_cpee = if prompt_type == 'generate_noendpoints' || prompt_type == 'generate_noendpoints_docdescription'
46
46
  llm_response = generate_model(myllm,user_input,temperature,llms)
47
47
  mermaid_to_cpee(llm_response)
48
48
  elsif prompt_type == 'generate_endpoints'
@@ -52,6 +52,8 @@ module CPEE
52
52
  elsif prompt_type == 'adapt_noendpoints'
53
53
  llm_response = adapt_model(myllm,doc,user_input,llms)
54
54
  mermaid_to_cpee(llm_response)
55
+ elsif prompt_type == 'adapt_noendpoints_docdescription'
56
+ adapt_doccpee_description(myllm,doc,user_input,llms)
55
57
  elsif prompt_type == 'adapt_endpoints'
56
58
  xml_endpoints = XML::Smart.string(endpoints)
57
59
  adapt_cpee_model(myllm,doc,user_input,xml_endpoints,get_demo_endpoints(),llms)
@@ -61,7 +63,7 @@ module CPEE
61
63
  return Riddl::Parameter::Complex.new('llm_out', 'application/json', { error: e }.to_json)
62
64
  end
63
65
 
64
- 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
+ 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())
65
67
  rescue LLMError => e
66
68
  @status = e&.http_response || 400
67
69
  return Riddl::Parameter::Complex.new("llm_out","application/json",{ :error => "#{prompt_type} #{e.message}"}.to_json())
@@ -112,18 +114,20 @@ module CPEE
112
114
  llms = @a[0]
113
115
  #get parameters
114
116
  begin
115
- myllm = @p[0].value.read
116
- user_input = @p[1].value.read
117
- system_prompt = @p[2].value.read
118
- format = @p[3].value.read
119
- temperature = @p[4]&.value&.read
117
+ myllm = @p.shift.value.read
118
+ user_input = @p.shift.value.read
119
+ system_prompt = @p.shift.value.read
120
+ format = @p.shift.value.read
121
+ temperature = @p.shift.value.read if @p[0]&.name == 'temperature'
122
+ documents = []
123
+ documents << @p.shift while @p[0]&.name == 'document'
120
124
  rescue Exception => e
121
125
  @status = 400
122
126
  return Riddl::Parameter::Complex.new("generic_out","application/json",{:error => e}.to_json())
123
127
  end
124
128
 
125
129
  begin
126
- llm_response = generate_generic(myllm,user_input,system_prompt,format,temperature,llms)
130
+ llm_response = generate_generic(myllm,user_input,system_prompt,format,temperature,documents,llms)
127
131
  rescue LLMError => e
128
132
  @status = e.http_response
129
133
  return Riddl::Parameter::Complex.new("generic_out","application/json",{:error => e.message}.to_json())
@@ -202,29 +206,54 @@ module CPEE
202
206
  end
203
207
  end #}}}
204
208
 
209
+ class GuessIntent < Riddl::Implementation #{{{
210
+ include Functions
211
+
212
+ def response
213
+ llms = @a[0]
214
+ plugins = @a[1]
215
+
216
+ input_cpee = @p[0].value.read
217
+ myllm = @p[1].value.read
218
+
219
+ begin
220
+ response = guess_intent(myllm,input_cpee,plugins,llms)
221
+ rescue LLMError => e
222
+ @status = e.http_response
223
+ return Riddl::Parameter::Complex.new("user_intent","application/json",{:error => e.message}.to_json())
224
+ end
225
+
226
+ Riddl::Parameter::Complex.new("user_intent","application/json",response.to_json())
227
+ end
228
+ end #}}}
229
+
205
230
  def self::implementation(opts)
206
231
  Proc.new do
207
232
  on resource do
208
- run(CreateMermaid, opts[:llms]) if post 'llm_in'
233
+ run CreateMermaid, opts[:llms] if post 'llm_in'
209
234
 
210
235
  on resource 'dataflow' do
211
- run(CreateDataFlow, opts[:llms]) if post 'dataflow_in'
236
+ run CreateDataFlow, opts[:llms] if post 'dataflow_in'
212
237
  end
213
238
 
214
239
  on resource 'validate' do
215
240
  on resource 'xml' do
216
- run(ValidateDataFlow, opts[:llms]) if post 'dataflow_in'
241
+ run ValidateDataFlow, opts[:llms] if post 'dataflow_in'
217
242
  end
218
243
  end
219
244
 
220
245
  on resource 'text' do
221
246
  on resource 'llm' do
222
- run(CreateText, opts[:llms]) if post 'text_in'
247
+ run CreateText, opts[:llms] if post 'text_in'
223
248
  end
224
249
  end
225
250
 
251
+ on resource 'intent' do
252
+ run GuessIntent, opts[:llms], opts[:plugins] if post 'intent_in'
253
+ end
254
+
226
255
  on resource 'generic' do
227
- run(CreateGeneric, opts[:llms]) if post 'generic_in'
256
+ run CreateGeneric, opts[:llms] if post 'generic_in'
228
257
  end
229
258
  end
230
259
  end
@@ -35,9 +35,35 @@
35
35
  <parameter name="rpst_xml" mimetype="application/json"/>
36
36
  </message>
37
37
 
38
+ <message name="intent_in">
39
+ <parameter name="user_input" mimetype="text/plain"/>
40
+ <parameter name="llm" mimetype="text/plain"/>
41
+ </message>
42
+
43
+ <message name="intent_out">
44
+ <parameter name="user_intent" mimetype="application/json"/>
45
+ <!--
46
+ prompt/user/intent.txt
47
+ prompt/system/intent.txt
48
+
49
+ output: json
50
+ {
51
+ url: // url to call
52
+ required_input: {
53
+ text: // typically extracted from user_input
54
+ documents: { name: value, }, // documents to pass
55
+ model: true|false,
56
+ message_id: '' // input is then the message
57
+ }
58
+ output_template: ... // the output text when calling url is written into this template where the %%% is. If the output of calling url is a json, insert multiple %%%name.
59
+ output: 'chat|process|reintent' // write to chat, give to the normal pipeline, or pass the output to the intention service again
60
+ }
61
+ -->
62
+ </message>
63
+
38
64
  <message name="text_in">
39
65
  <parameter name="rpst_xml" mimetype="*/xml"/>
40
- <parameter name="llm" type="string"/>
66
+ <parameter name="llm" mimetype="text/plain"/>
41
67
  </message>
42
68
  <message name="text_out">
43
69
  <parameter name="output_text" mimetype="application/json"/>
@@ -51,6 +77,9 @@
51
77
  <optional>
52
78
  <parameter name="temperature" mimetype="text/plain"/>
53
79
  </optional>
80
+ <zeroOrMore>
81
+ <parameter name="document" mimetype="*"/>
82
+ </zeroOrMore>
54
83
  </message>
55
84
  <message name="generic_out">
56
85
  <parameter name="output_text" mimetype="application/json"/>
@@ -68,6 +97,10 @@
68
97
  <post in="dataflow_in" out="llm_out"/>
69
98
  </resource>
70
99
 
100
+ <resource relative="intent">
101
+ <post in="intent_in" out="intent_out"/>
102
+ </resource>
103
+
71
104
  <resource relative="text">
72
105
  <resource relative="llm">
73
106
  <post in="text_in" out="text_out"/>
@@ -0,0 +1,50 @@
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
+
7
+ Core Objective
8
+ Your task is to produce a modified CPEE XML process model that implements the requested changes.
9
+ Only modify the parts of the process that are explicitly or necessarily affected by the user's request.
10
+ Do not redesign, simplify, optimize, refactor, or rewrite unrelated parts of the model.
11
+ Never remove existing functionality unless explicitly requested.
12
+ Never introduce additional behavior that was not requested.
13
+
14
+ Editing Workflow
15
+ Internally perform the following steps:
16
+ 1. Parse the existing CPEE XML description.
17
+ 3. Parse the workflow contained in the description element.
18
+ 4. Understand the user's requested modifications.
19
+ 5. Determine which workflow elements are affected.
20
+ 6. Apply only the requested changes.
21
+ 7. Update documentation flow if necessary.
22
+ 8. Generate the complete updated CPEE XML description.
23
+
24
+ %%%cpee_xml.txt
25
+ %%%cpee_xml_documentation_modelling.txt
26
+
27
+ For the purpose of modelling, arguments can be ignored and dont have to cary anny values. Code also can be left empty.
28
+
29
+ # Handling Ambiguity
30
+ If the user's request is sufficiently precise, perform the modification directly.
31
+ If multiple valid interpretations exist and the missing information affects the resulting workflow, select the best possible interpretation and perform changes.
32
+
33
+ # Important
34
+ If user deletes a single task in a branch, remove task but leave the branch. Use default branch.
35
+ eid attributes are IDs. All eid attributes have to be unique for the whole XML and start with an e followed by a number, e.g., "e1".
36
+ id attributes are IDs. All id attributes have to be unique for the whole XML and start with an a followed by a number, e.g., "a1".
37
+ Don't change ANY existing id's or eid's. Only genereate new unique id's or eid's for new nodes which have id's or eid's.
38
+ Don't change existing nodes, even when you move them. For example dont add documentation if it is not there.
39
+
40
+ # Output Requirements
41
+ Return the complete updated CPEE XML process model.
42
+ Preserve the overall document structure.
43
+ Modify only the elements affected by the user's request.
44
+ Return the entire XML document, including unchanged sections.
45
+ Do not explain the modifications.
46
+ Do not summarize the changes.
47
+ Do not output Markdown.
48
+ Do not omit unchanged sections.
49
+ The returned XML must be immediately usable as a valid CPEE process model.
50
+
@@ -64,81 +64,7 @@ If the task does not exist in the Service Registry:
64
64
  - Leave the task unconfigured except for information explicitly requested by the user.
65
65
  Always use the Service Registry whenever a matching service specification is available.
66
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
67
+ %%%cpee_xml.txt
142
68
 
143
69
  # Handling Ambiguity
144
70
  If the user's request is sufficiently precise, perform the modification directly.
@@ -154,4 +80,3 @@ If multiple valid interpretations exist and the missing information affects the
154
80
  Do not output Markdown.
155
81
  Do not omit unchanged sections.
156
82
  The returned XML must be immediately usable as a valid CPEE process model.
157
-