Module: Cloudflare::AI
- Defined in:
- lib/cloudflare_workers/ai.rb
Defined Under Namespace
Classes: Stream
Constant Summary collapse
- DEFAULT_OPTIONS =
Default REST options forwarded to env.AI.run as the third argument.
{}.freeze
Class Method Summary collapse
-
.ruby_to_js(val) ⇒ Object
Convert a Ruby value (Hash / Array / String / Numeric / true / false / nil) into a plain JS object suitable for env.AI.run inputs.
-
.run(model, inputs, binding: nil, options: nil) ⇒ Object
Run a Workers AI model.
Class Method Details
.ruby_to_js(val) ⇒ Object
Convert a Ruby value (Hash / Array / String / Numeric / true / false / nil) into a plain JS object suitable for env.AI.run inputs.
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'lib/cloudflare_workers/ai.rb', line 101 def self.ruby_to_js(val) if val.is_a?(Hash) obj = `({})` val.each do |k, v| ks = k.to_s jv = ruby_to_js(v) `#{obj}[#{ks}] = #{jv}` end obj elsif val.is_a?(Array) arr = `([])` val.each do |v| jv = ruby_to_js(v) `#{arr}.push(#{jv})` end arr elsif val.is_a?(Symbol) val.to_s else val end end |
.run(model, inputs, binding: nil, options: nil) ⇒ Object
Run a Workers AI model. Returns a JS Promise that resolves to a Ruby Hash for non-streaming calls, or to a Cloudflare::AI::Stream wrapping the JS ReadableStream for streaming calls.
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/cloudflare_workers/ai.rb', line 51 def self.run(model, inputs, binding: nil, options: nil) # Use a JS-side null check because `binding` may be a raw JS object # (env.AI), which has no Ruby `#nil?` method on the prototype. bound = !`(#{binding} == null)` raise AIError.new('AI binding not bound (env.AI is null)', model: model) unless bound js_inputs = ruby_to_js(inputs) = ? ruby_to_js() : `({})` ai_binding = binding err_klass = Cloudflare::AIError stream_klass = Cloudflare::AI::Stream # Streaming may be requested either via `inputs[:stream]` (the # newer Workers AI shape) or `options: { stream: true }` (the # 3rd-arg "options" contract). Accept both so callers can use # whichever idiom matches the model docs they're following. streaming = (inputs.is_a?(Hash) && (inputs[:stream] == true || inputs['stream'] == true)) || (.is_a?(Hash) && ([:stream] == true || ['stream'] == true)) cf = Cloudflare # NOTE: multi-line backtick β Promise works HERE because the # value is assigned to `js_promise` (Opal emits the statement AND # keeps the returned value alive through the local). Do NOT # refactor this so the backtick is the method's last expression # or the Promise will be silently dropped (same pitfall # documented in lib/cloudflare_workers/{cache,queue}.rb β # Phase 11B audit). js_promise = ` (async function() { var out; try { out = await #{ai_binding}.run(#{model}, #{js_inputs}, #{}); } catch (e) { #{Kernel}.$raise(#{err_klass}.$new(e && e.message ? e.message : String(e), Opal.hash({ model: #{model}, operation: 'run' }))); } return out; })() ` js_result = js_promise.__await__ if streaming # Workers AI returns a ReadableStream<Uint8Array> when stream:true. # Wrap it so the Sinatra route can return it as an SSE body. stream_klass.new(js_result) else cf.js_to_ruby(js_result) end end |