@@ -38,6 +38,7 @@ def mini_racer_context
3838 attach_index ( ctx )
3939 attach_upload ( ctx )
4040 attach_chain ( ctx )
41+ attach_discourse ( ctx )
4142 ctx . eval ( framework_script )
4243 ctx
4344 end
@@ -71,6 +72,24 @@ def framework_script
7172 setCustomRaw: _chain_set_custom_raw,
7273 };
7374
75+ const discourse = {
76+ getPost: _discourse_get_post,
77+ getUser: _discourse_get_user,
78+ getPersona: function(name) {
79+ return {
80+ respondTo: function(params) {
81+ result = _discourse_respond_to_persona(name, params);
82+ if (result.error) {
83+ throw new Error(result.error);
84+ }
85+ return result;
86+ },
87+ };
88+ },
89+ };
90+
91+ const context = #{ JSON . generate ( @context ) } ;
92+
7493 function details() { return ""; };
7594 JS
7695 end
@@ -239,6 +258,86 @@ def attach_chain(mini_racer_context)
239258 mini_racer_context . attach ( "_chain_set_custom_raw" , -> ( raw ) { self . custom_raw = raw } )
240259 end
241260
261+ def attach_discourse ( mini_racer_context )
262+ mini_racer_context . attach (
263+ "_discourse_get_post" ,
264+ -> ( post_id ) do
265+ in_attached_function do
266+ post = Post . find_by ( id : post_id )
267+ return nil if post . nil?
268+ guardian = Guardian . new ( Discourse . system_user )
269+ recursive_as_json ( PostSerializer . new ( post , scope : guardian , root : false ) )
270+ end
271+ end ,
272+ )
273+
274+ mini_racer_context . attach (
275+ "_discourse_get_user" ,
276+ -> ( user_id_or_username ) do
277+ in_attached_function do
278+ user = nil
279+
280+ if user_id_or_username . is_a? ( Integer ) ||
281+ user_id_or_username . to_i . to_s == user_id_or_username
282+ user = User . find_by ( id : user_id_or_username . to_i )
283+ else
284+ user = User . find_by ( username : user_id_or_username )
285+ end
286+
287+ return nil if user . nil?
288+
289+ guardian = Guardian . new ( Discourse . system_user )
290+ recursive_as_json ( UserSerializer . new ( user , scope : guardian , root : false ) )
291+ end
292+ end ,
293+ )
294+
295+ mini_racer_context . attach (
296+ "_discourse_respond_to_persona" ,
297+ -> ( persona_name , params ) do
298+ in_attached_function do
299+ # if we have 1000s of personas this can be slow ... we may need to optimize
300+ persona_class = AiPersona . all_personas . find { |persona | persona . name == persona_name }
301+ return { error : "Persona not found" } if persona_class . nil?
302+
303+ persona = persona_class . new
304+ bot = DiscourseAi ::AiBot ::Bot . as ( @bot_user || persona . user , persona : persona )
305+ playground = DiscourseAi ::AiBot ::Playground . new ( bot )
306+
307+ if @context [ :post_id ]
308+ post = Post . find_by ( id : @context [ :post_id ] )
309+ return { error : "Post not found" } if post . nil?
310+
311+ reply_post = playground . reply_to ( post , custom_instructions : params [ "instructions" ] )
312+
313+ if reply_post
314+ return (
315+ { success : true , post_id : reply_post . id , post_number : reply_post . post_number }
316+ )
317+ else
318+ return { error : "Failed to create reply" }
319+ end
320+ elsif @context [ :message_id ] && @context [ :channel_id ]
321+ message = Chat ::Message . find_by ( id : @context [ :message_id ] )
322+ channel = Chat ::Channel . find_by ( id : @context [ :channel_id ] )
323+ return { error : "Message or channel not found" } if message . nil? || channel . nil?
324+
325+ reply =
326+ playground . reply_to_chat_message ( message , channel , @context [ :context_post_ids ] )
327+
328+ if reply
329+ return { success : true , message_id : reply . id }
330+ else
331+ return { error : "Failed to create chat reply" }
332+ end
333+ else
334+ return { error : "No valid context for response" }
335+ end
336+ end
337+ end ,
338+ )
339+ end
340+
242341 def attach_upload ( mini_racer_context )
243342 mini_racer_context . attach (
244343 "_upload_create" ,
@@ -343,6 +442,33 @@ def in_attached_function
343442 ensure
344443 self . running_attached_function = false
345444 end
445+
446+ def recursive_as_json ( obj )
447+ case obj
448+ when Array
449+ obj . map { |item | recursive_as_json ( item ) }
450+ when Hash
451+ obj . transform_values { |value | recursive_as_json ( value ) }
452+ when ActiveModel ::Serializer , ActiveModel ::ArraySerializer
453+ recursive_as_json ( obj . as_json )
454+ when ActiveRecord ::Base
455+ recursive_as_json ( obj . as_json )
456+ else
457+ # Handle objects that respond to as_json but aren't handled above
458+ if obj . respond_to? ( :as_json )
459+ result = obj . as_json
460+ if result . equal? ( obj )
461+ # If as_json returned the same object, return it to avoid infinite recursion
462+ result
463+ else
464+ recursive_as_json ( result )
465+ end
466+ else
467+ # Primitive values like strings, numbers, booleans, nil
468+ obj
469+ end
470+ end
471+ end
346472 end
347473 end
348474end
0 commit comments