@@ -56,6 +56,7 @@ def framework_script
5656
5757 const llm = {
5858 truncate: _llm_truncate,
59+ generate: _llm_generate,
5960 };
6061
6162 const index = {
@@ -175,20 +176,60 @@ def attach_truncate(mini_racer_context)
175176 "_llm_truncate" ,
176177 -> ( text , length ) { @llm . tokenizer . truncate ( text , length ) } ,
177178 )
179+
180+ mini_racer_context . attach (
181+ "_llm_generate" ,
182+ -> ( prompt ) do
183+ in_attached_function do
184+ @llm . generate (
185+ convert_js_prompt_to_ruby ( prompt ) ,
186+ user : llm_user ,
187+ feature_name : "custom_tool_#{ tool . name } " ,
188+ )
189+ end
190+ end ,
191+ )
192+ end
193+
194+ def convert_js_prompt_to_ruby ( prompt )
195+ if prompt . is_a? ( String )
196+ prompt
197+ elsif prompt . is_a? ( Hash )
198+ messages = prompt [ "messages" ]
199+ if messages . blank? || !messages . is_a? ( Array )
200+ raise Discourse ::InvalidParameters . new ( "Prompt must have messages" )
201+ end
202+ messages . each ( &:symbolize_keys! )
203+ messages . each { |message | message [ :type ] = message [ :type ] . to_sym }
204+ DiscourseAi ::Completions ::Prompt . new ( messages : prompt [ "messages" ] )
205+ else
206+ raise Discourse ::InvalidParameters . new ( "Prompt must be a string or a hash" )
207+ end
208+ end
209+
210+ def llm_user
211+ @llm_user ||=
212+ begin
213+ @context [ :llm_user ] || post &.user || @bot_user
214+ end
215+ end
216+
217+ def post
218+ return @post if defined? ( @post )
219+ post_id = @context [ :post_id ]
220+ @post = post_id && Post . find_by ( id : post_id )
178221 end
179222
180223 def attach_index ( mini_racer_context )
181224 mini_racer_context . attach (
182225 "_index_search" ,
183226 -> ( *params ) do
184- begin
227+ in_attached_function do
185228 query , options = params
186229 self . running_attached_function = true
187230 options ||= { }
188231 options = options . symbolize_keys
189232 self . rag_search ( query , **options )
190- ensure
191- self . running_attached_function = false
192233 end
193234 end ,
194235 )
@@ -203,26 +244,25 @@ def attach_upload(mini_racer_context)
203244 "_upload_create" ,
204245 -> ( filename , base_64_content ) do
205246 begin
206- self . running_attached_function = true
207- # protect against misuse
208- filename = File . basename ( filename )
209-
210- Tempfile . create ( filename ) do |file |
211- file . binmode
212- file . write ( Base64 . decode64 ( base_64_content ) )
213- file . rewind
214-
215- upload =
216- UploadCreator . new (
217- file ,
218- filename ,
219- for_private_message : @context [ :private_message ] ,
220- ) . create_for ( @bot_user . id )
221-
222- { id : upload . id , short_url : upload . short_url , url : upload . url }
247+ in_attached_function do
248+ # protect against misuse
249+ filename = File . basename ( filename )
250+
251+ Tempfile . create ( filename ) do |file |
252+ file . binmode
253+ file . write ( Base64 . decode64 ( base_64_content ) )
254+ file . rewind
255+
256+ upload =
257+ UploadCreator . new (
258+ file ,
259+ filename ,
260+ for_private_message : @context [ :private_message ] ,
261+ ) . create_for ( @bot_user . id )
262+
263+ { id : upload . id , short_url : upload . short_url , url : upload . url }
264+ end
223265 end
224- ensure
225- self . running_attached_function = false
226266 end
227267 end ,
228268 )
@@ -238,18 +278,20 @@ def attach_http(mini_racer_context)
238278 raise TooManyRequestsError . new ( "Tool made too many HTTP requests" )
239279 end
240280
241- self . running_attached_function = true
242- headers = ( options && options [ "headers" ] ) || { }
281+ in_attached_function do
282+ headers = ( options && options [ "headers" ] ) || { }
243283
244- result = { }
245- DiscourseAi ::AiBot ::Tools ::Tool . send_http_request ( url , headers : headers ) do |response |
246- result [ :body ] = response . body
247- result [ :status ] = response . code . to_i
248- end
284+ result = { }
285+ DiscourseAi ::AiBot ::Tools ::Tool . send_http_request (
286+ url ,
287+ headers : headers ,
288+ ) do |response |
289+ result [ :body ] = response . body
290+ result [ :status ] = response . code . to_i
291+ end
249292
250- result
251- ensure
252- self . running_attached_function = false
293+ result
294+ end
253295 end
254296 end ,
255297 )
@@ -264,35 +306,43 @@ def attach_http(mini_racer_context)
264306 raise TooManyRequestsError . new ( "Tool made too many HTTP requests" )
265307 end
266308
267- self . running_attached_function = true
268- headers = ( options && options [ "headers" ] ) || { }
269- body = options && options [ "body" ]
270-
271- result = { }
272- DiscourseAi ::AiBot ::Tools ::Tool . send_http_request (
273- url ,
274- method : method ,
275- headers : headers ,
276- body : body ,
277- ) do |response |
278- result [ :body ] = response . body
279- result [ :status ] = response . code . to_i
309+ in_attached_function do
310+ headers = ( options && options [ "headers" ] ) || { }
311+ body = options && options [ "body" ]
312+
313+ result = { }
314+ DiscourseAi ::AiBot ::Tools ::Tool . send_http_request (
315+ url ,
316+ method : method ,
317+ headers : headers ,
318+ body : body ,
319+ ) do |response |
320+ result [ :body ] = response . body
321+ result [ :status ] = response . code . to_i
322+ end
323+
324+ result
325+ rescue => e
326+ if Rails . env . development?
327+ p url
328+ p options
329+ p e
330+ puts e . backtrace
331+ end
332+ raise e
280333 end
281-
282- result
283- rescue => e
284- p url
285- p options
286- p e
287- puts e . backtrace
288- raise e
289- ensure
290- self . running_attached_function = false
291334 end
292335 end ,
293336 )
294337 end
295338 end
339+
340+ def in_attached_function
341+ self . running_attached_function = true
342+ yield
343+ ensure
344+ self . running_attached_function = false
345+ end
296346 end
297347 end
298348end
0 commit comments