Cloudflare Workers AI API client for ruby
Cloudflare is testing its Workers AI API. Hopefully this project makes it easier for ruby-first developers to consume Cloudflare's latest and greatest.
I'm really interested in applying retrieval-augmented generation to make legal services more accessible. Email me.
If you're looking for legal help, it's best to book a slot via https://www.krishnan.ca.
Supported features
- Text Generation
- Text Embeddings
- Text Classification
- Translation
- Image Classification
- Text-to-Image
- Automatic Speech Recognition
Table of Contents
- Installation
- Usage
- Logging
- Development
Installation
Install the gem and add to the application's Gemfile by executing:
bundle add cloudflare-ai
If bundler is not being used to manage dependencies, install the gem by executing:
gem install cloudflare-ai
Usage
require "cloudflare/ai"Cloudflare Workers AI
Please visit the Cloudflare Workers AI website for more details. Thiis gem provides a client that wraps around Cloudflare's REST API.
Client
client = Cloudflare::AI::Client.new(account_id: ENV["CLOUDFLARE_ACCOUNT_ID"], api_token: ENV["CLOUDFLARE_API_TOKEN"])Model selection
The model name is an optional parameter to every one of the client methods described below. For example, if an example is documented as
result = client.complete(prompt: "Hello my name is")this is implicitly the same as
result = client.complete(prompt: "Hello my name is", model: "@cf/meta/llama-2-7b-chat-fp16")The full list of supported models is available here: models.rb. More information is available in the cloudflare documentation. The default model used is the first enumerated model in the applicable set in models.rb.
Text generation
(chat / scoped prompt)
messages = [
Cloudflare::AI::Message.new(role: "system", content: "You are a big fan of Cloudflare and Ruby."),
Cloudflare::AI::Message.new(role: "user", content: "What is your favourite tech stack?"),
Cloudflare::AI::Message.new(role: "assistant", content: "I love building with Ruby on Rails and Cloudflare!"),
Cloudflare::AI::Message.new(role: "user", content: "Really? You like Cloudflare even though there isn't great support for Ruby?"),
]
result = client.chat(messages: messages)
puts result.response # => "Yes, I love Cloudflare!"(string prompt)
result = client.complete(prompt: "What is your name?", max_tokens: 512)
puts result.response # => "My name is Jonas."Streaming responses
Responses will be streamed back to the client using Server Side Events (SSE) if a block is passed to the chat or complete method.
result = client.complete(prompt: "Hi!") { |data| puts data}
# {"response":" "}
# {"response":" Hello"}
# {"response":" there"}
# {"response":"!"}
# {"response":""}
# [DONE]Token limits
Invocations of the prompt and chat can take an optional max_tokens argument that defaults to 256.
Result object
All invocations of the prompt and chat methods return a Cloudflare::AI::Results::TextGeneration object. This object's serializable JSON output is
based on the raw response from the Cloudflare API.
result = client.complete(prompt: "What is your name?")
# Successful
puts result.response # => "My name is John."
puts result.success? # => true
puts result.failure? # => false
puts result.to_json # => {"result":{"response":"My name is John"},"success":true,"errors":[],"messages":[]}
# Unsuccessful
puts result.response # => nil
puts result.success? # => false
puts result.failure? # => true
puts result.to_json # => {"result":null,"success":false,"errors":[{"code":7009,"message":"Upstream service unavailable"}],"messages":[]}Text embedding
result = client.embed(text: "Hello")
p result.shape # => [1, 768] # (1 embedding, 768 dimensions per embedding)
p result.embedding # => [[-0.008496830239892006, 0.001376907923258841, -0.0323275662958622, ...]]The input can be either a string (as above) or an array of strings:
result = client.embed(text: ["Hello", "World"])Result object
All invocations of the embed methods return a Cloudflare::AI::Results::TextEmbedding.
Text classification
result = client.classify(text: "You meanie!")
p result.result # => [{"label"=>"NEGATIVE", "score"=>0.6647962927818298}, {"label"=>"POSITIVE", "score"=>0.3352036774158478}]Result object
All invocations of the classify methods return a Cloudflare::AI::Results::TextClassification.
Image classification
The image classification endpoint accepts either a path to a file or a file stream.
result = client.classify(image: "/path/to/cat.jpg")
p result.result # => {"result":[{"label":"TABBY","score":0.6159140467643738},{"label":"TIGER CAT","score":0.12016300112009048},{"label":"EGYPTIAN CAT","score":0.07523812353610992},{"label":"DOORMAT","score":0.018854796886444092},{"label":"ASHCAN","score":0.01314085815101862}],"success":true,"errors":[],"messages":[]}
result = client.classify(image: File.open("/path/to/cat.jpg"))
p result.result # => {"result":[{"label":"TABBY","score":0.6159140467643738},{"label":"TIGER CAT","score":0.12016300112009048},{"label":"EGYPTIAN CAT","score":0.07523812353610992},{"label":"DOORMAT","score":0.018854796886444092},{"label":"ASHCAN","score":0.01314085815101862}],"success":true,"errors":[],"messages":[]}Result object
All invocations of the classify method returns a Cloudflare::AI::Results::TextClassification.
Text to Image
result = client.draw(prompt: "robot with blue eyes")
p result.result # => File:0x0000000110deed68 (png tempfile)Result object
All invocations of the draw method returns a Cloudflare::AI::Results::TextToImage.
Translation
result = client.translate(text: "Hello Jello", source_lang: "en", target_lang: "fr")
p result.translated_text # => Hola JelloResult object
All invocations of the translate method returns a Cloudflare::AI::Results::Translate.
Automatic speech recognition
You can pass either a URL (source_url:) or a file (audio:) to the transcribe method.
result = client.transcribe(source_url: "http://example.org/path/to/audio.wav")
p result.text # => "Hello Jello."
p result.word_count # => 2
p result.to_json # => {"result":{"text":"Hello Jello.","word_count":2,"words":[{"word":"Hello","start":0,"end":1.340000033378601},{"word":"Jello.","start":1.340000033378601,"end":1.340000033378601}},"success":true,"errors":[],"messages":[]}
result = client.transcribe(audio: File.open("/path/to/audio.wav"))
# ...Result object
All invocations of the transcribe method returns a Cloudflare::AI::Results::Transcribe.
Summarization
result = client.summarize(text: "This text should be a lot longer.")
p result.summary # => {"result":{"summary":"Short text"},"success":true,"errors":[],"messages":[]}Result object
All invocations of the summarize method returns a Cloudflare::AI::Results::Summarization object.
Object detection
The object detection endpoint accepts either a path to a file or a file stream.
result = client.detect_objects(image: "/path/to/cat.jpg")
result = client.classify(image: File.open("/path/to/cat.jpg"))Result object
All invocations of the detect_objects method returns a Cloudflare::AI::Results::ObjectDetection object.
Image-to-text
The captioning endpoint accepts either a path to a file or a file stream.
client.caption(image: "/path/to/cat.jpg").description # => "a cat sitting on a couch"
client.caption(image: File.open("/path/to/cat.jpg")).description # => "a cat sitting on a couch"Result object
All invocations of the caption method returns a Cloudflare::AI::Results::ImageToText object.
Logging
This gem uses standard logging mechanisms and defaults to :warn level. Most messages are at info level, but we will add debug or warn statements as needed.
To show all log messages:
Cloudflare::AI.logger.level = :debugYou can use this logger as you would the default ruby logger. For example:
Cloudflare::AI.logger = Logger.new($stdout)Development
git clone https://github.com/ajaynomics/cloudflare-ai.git-
bundle exec raketo ensure that the tests pass and to run standardrb
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/ajaynomics/cloudflare-ai.
License
The gem is available as open source under the terms of the MIT License. A special thanks to the team at langchainrb – I learnt a lot reading your codebase as I muddled my way through the initial effort.