@@ -13,6 +13,9 @@ class ToolRunner
1313 MARSHAL_STACK_DEPTH = 20
1414 MAX_HTTP_REQUESTS = 20
1515
16+ MAX_SLEEP_CALLS = 30
17+ MAX_SLEEP_DURATION_MS = 60_000
18+
1619 def initialize ( parameters :, llm :, bot_user :, context : nil , tool :, timeout : nil )
1720 if context && !context . is_a? ( DiscourseAi ::Personas ::BotContext )
1821 raise ArgumentError , "context must be a BotContext object"
@@ -29,6 +32,8 @@ def initialize(parameters:, llm:, bot_user:, context: nil, tool:, timeout: nil)
2932 @running_attached_function = false
3033
3134 @http_requests_made = 0
35+ @sleep_calls_made = 0
36+ @http_requests_made = 0
3237 end
3338
3439 def mini_racer_context
@@ -44,6 +49,7 @@ def mini_racer_context
4449 attach_index ( ctx )
4550 attach_upload ( ctx )
4651 attach_chain ( ctx )
52+ attach_sleep ( ctx )
4753 attach_discourse ( ctx )
4854 ctx . eval ( framework_script )
4955 ctx
@@ -310,6 +316,33 @@ def attach_chain(mini_racer_context)
310316 mini_racer_context . attach ( "_chain_set_custom_raw" , -> ( raw ) { self . custom_raw = raw } )
311317 end
312318
319+ # this is useful for polling apis
320+ def attach_sleep ( mini_racer_context )
321+ mini_racer_context . attach (
322+ "sleep" ,
323+ -> ( duration_ms ) do
324+ @sleep_calls_made += 1
325+ if @sleep_calls_made > MAX_SLEEP_CALLS
326+ raise TooManyRequestsError . new ( "Tool made too many sleep calls" )
327+ end
328+
329+ duration_ms = duration_ms . to_i
330+ if duration_ms > MAX_SLEEP_DURATION_MS
331+ raise ArgumentError . new (
332+ "Sleep duration cannot exceed #{ MAX_SLEEP_DURATION_MS } ms (1 minute)" ,
333+ )
334+ end
335+
336+ raise ArgumentError . new ( "Sleep duration must be positive" ) if duration_ms <= 0
337+
338+ in_attached_function do
339+ sleep ( duration_ms / 1000.0 )
340+ { slept : duration_ms }
341+ end
342+ end ,
343+ )
344+ end
345+
313346 def attach_discourse ( mini_racer_context )
314347 mini_racer_context . attach (
315348 "_discourse_get_post" ,
0 commit comments