From 7b7e150127cca16fde0286613973735a7cfcb831 Mon Sep 17 00:00:00 2001 From: Scott Werner Date: Tue, 30 Apr 2024 13:44:55 -0400 Subject: [PATCH] Update gemini to new output adapter format --- lib/sublayer/providers/gemini.rb | 19 +++- .../code_from_description_generator_spec.rb | 25 +++++ .../description_from_code_generator_spec.rb | 33 ++++++ spec/spec_helper.rb | 1 + .../hello_world.yml | 97 +++++++++++++++++ .../hello_world.yml | 100 ++++++++++++++++++ 6 files changed, 272 insertions(+), 3 deletions(-) create mode 100644 spec/vcr_cassettes/gemini/generators/code_from_description_generator/hello_world.yml create mode 100644 spec/vcr_cassettes/gemini/generators/description_from_code_generator/hello_world.yml diff --git a/lib/sublayer/providers/gemini.rb b/lib/sublayer/providers/gemini.rb index 7f83554..dba3863 100644 --- a/lib/sublayer/providers/gemini.rb +++ b/lib/sublayer/providers/gemini.rb @@ -21,9 +21,13 @@ def self.call(prompt:, output_adapter:) Here are the tools available: - - #{output_adapter.to_xml} - + + #{output_adapter.name} + #{output_adapter.description} + + #{format_properties(output_adapter)} + + Respond only with valid xml. @@ -49,6 +53,15 @@ def self.call(prompt:, output_adapter:) raise "Gemini did not format response, error: #{response.body}" unless tool_output return tool_output end + + private + def self.format_properties(output_adapter) + output_adapter.properties.each_with_object("") do |property, xml| + xml << "#{property.name}" + xml << "#{property.type}" + xml << "#{property.description}" + end + end end end end diff --git a/spec/generators/examples/code_from_description_generator_spec.rb b/spec/generators/examples/code_from_description_generator_spec.rb index 06bc9dc..25059d3 100644 --- a/spec/generators/examples/code_from_description_generator_spec.rb +++ b/spec/generators/examples/code_from_description_generator_spec.rb @@ -66,5 +66,30 @@ def generate(description:, technologies: ["ruby"]) end end + + context "Gemini" do + before do + Sublayer.configuration.ai_provider = Sublayer::Providers::Gemini + Sublayer.configuration.ai_model = "gemini-pro" + end + + it "generates code from description" do + VCR.use_cassette("gemini/generators/code_from_description_generator/hello_world") do + code = generate(description: "a hello world app where I pass --who argument to set the 'world' value using optparser") + expect(code.strip).to eq %q(#!/usr/bin/env ruby + +require 'optparse' + +options = {} +OptionParser.new do |opts| + opts.on('--who WHO', 'Who to greet (default: World)') do |who| + options[:who] = who + end +end.parse! + +puts "Hello, #{options[:who]}!") + end + end + end end end diff --git a/spec/generators/examples/description_from_code_generator_spec.rb b/spec/generators/examples/description_from_code_generator_spec.rb index fc6edf5..769e21a 100644 --- a/spec/generators/examples/description_from_code_generator_spec.rb +++ b/spec/generators/examples/description_from_code_generator_spec.rb @@ -76,4 +76,37 @@ def generate(code) end end end + + context "Gemini" do + before do + Sublayer.configuration.ai_provider = Sublayer::Providers::Gemini + Sublayer.configuration.ai_model = "gemini-pro" + end + + it "generates description from hello world code" do + VCR.use_cassette("gemini/generators/description_from_code_generator/hello_world") do + code = %q(#!/usr/bin/env ruby + + require 'optparse' + + options = {} + OptionParser.new do |opts| + opts.banner = "Usage: hello.rb [options]" + + opts.on("-w", "--who PERSON", "Name of the person to greet") do |person| + options[:who] = person + end + end.parse! + + who = options[:who] || "world" + puts "Hello, #{who}!") + + description = generate(code) + expect(description.strip).to eq <<~DESCRIPTION.strip + This code is a simple command-line script that greets a person by name. It takes an optional argument, `--who`, to specify the name of the person to greet, and defaults to \"world\" if no name is provided. The script then prints a greeting to the specified person. + DESCRIPTION + end + end + + end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e65356d..7988615 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -9,6 +9,7 @@ config.hook_into :webmock config.filter_sensitive_data("") { ENV.fetch("OPENAI_API_KEY") } config.filter_sensitive_data("") { ENV.fetch("ANTHROPIC_API_KEY") } + config.filter_sensitive_data("") { ENV.fetch("GEMINI_API_KEY") } end RSpec.configure do |config| diff --git a/spec/vcr_cassettes/gemini/generators/code_from_description_generator/hello_world.yml b/spec/vcr_cassettes/gemini/generators/code_from_description_generator/hello_world.yml new file mode 100644 index 0000000..45ee8ce --- /dev/null +++ b/spec/vcr_cassettes/gemini/generators/code_from_description_generator/hello_world.yml @@ -0,0 +1,97 @@ +--- +http_interactions: +- request: + method: post + uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key= + body: + encoding: UTF-8 + string: '{"contents":{"role":"user","parts":{"text":" You have access + to a set of tools to answer the prompt.\n\n You may call tools like + this:\n \n \n $TOOL_NAME\n \n <$PARAMETER_NAME>$VALUE\n ...\n \n \n \n\n Here + are the tools available:\n \n \n generated_code\n The + generated code in the requested language\n \n generated_codestringThe + generated code in the requested language\n \n \n \n\n Respond + only with valid xml.\n The entire response should be wrapped in a + tag.\n Your response should call a tool inside a tag.\n\n You + are an expert programmer in ruby.\n\n You are tasked with writing code + using the following technologies: ruby.\n\n The description of the + task is a hello world app where I pass --who argument to set the ''world'' + value using optparser\n\n Take a deep breath and think step by step + before you start coding.\n"}}}' + headers: + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=UTF-8 + Vary: + - Origin + - Referer + - X-Origin + Date: + - Tue, 30 Apr 2024 17:33:56 GMT + Server: + - scaffolding on HTTPServer2 + Cache-Control: + - private + X-Xss-Protection: + - '0' + X-Frame-Options: + - SAMEORIGIN + X-Content-Type-Options: + - nosniff + Server-Timing: + - gfet4t7; dur=3071 + Alt-Svc: + - h3=":443"; ma=2592000,h3-29=":443"; ma=2592000 + Transfer-Encoding: + - chunked + body: + encoding: ASCII-8BIT + string: | + { + "candidates": [ + { + "content": { + "parts": [ + { + "text": "```xml\n\u003cresponse\u003e\n \u003ctool_calls\u003e\n \u003ctool_call\u003e\n \u003ctool_name\u003egenerated_code\u003c/tool_name\u003e\n \u003cparameters\u003e\n \u003cgenerated_code\u003e#!/usr/bin/env ruby\n\nrequire 'optparse'\n\noptions = {}\nOptionParser.new do |opts|\n opts.on('--who WHO', 'Who to greet (default: World)') do |who|\n options[:who] = who\n end\nend.parse!\n\nputs \"Hello, #{options[:who]}!\"\u003c/generated_code\u003e\n \u003c/parameters\u003e\n \u003c/tool_call\u003e\n \u003c/tool_calls\u003e\n\u003c/response\u003e\n```" + } + ], + "role": "model" + }, + "finishReason": "STOP", + "index": 0, + "safetyRatings": [ + { + "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HATE_SPEECH", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HARASSMENT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "probability": "NEGLIGIBLE" + } + ] + } + ] + } + recorded_at: Tue, 30 Apr 2024 17:33:56 GMT +recorded_with: VCR 6.2.0 diff --git a/spec/vcr_cassettes/gemini/generators/description_from_code_generator/hello_world.yml b/spec/vcr_cassettes/gemini/generators/description_from_code_generator/hello_world.yml new file mode 100644 index 0000000..b021edb --- /dev/null +++ b/spec/vcr_cassettes/gemini/generators/description_from_code_generator/hello_world.yml @@ -0,0 +1,100 @@ +--- +http_interactions: +- request: + method: post + uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key= + body: + encoding: UTF-8 + string: '{"contents":{"role":"user","parts":{"text":" You have access + to a set of tools to answer the prompt.\n\n You may call tools like + this:\n \n \n $TOOL_NAME\n \n <$PARAMETER_NAME>$VALUE\n ...\n \n \n \n\n Here + are the tools available:\n \n \n code_description\n A + description of what the code in the file does\n \n code_descriptionstringA + description of what the code in the file does\n \n \n \n\n Respond + only with valid xml.\n The entire response should be wrapped in a + tag.\n Your response should call a tool inside a tag.\n\n You + are an experienced software engineer. Below is a chunk of code:\n\n #!/usr/bin/env + ruby\n\n require ''optparse''\n\n options = {}\n OptionParser.new + do |opts|\n opts.banner = \"Usage: hello.rb [options]\"\n\n opts.on(\"-w\", + \"--who PERSON\", \"Name of the person to greet\") do |person|\n options[:who] + = person\n end\n end.parse!\n\n who = options[:who] + || \"world\"\n puts \"Hello, #{who}!\"\n\n Please read the code + carefully and provide a high-level description of what this code does, including + its purpose, functionalities, and any noteworthy details.\n"}}}' + headers: + Content-Type: + - application/json + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=UTF-8 + Vary: + - Origin + - Referer + - X-Origin + Date: + - Tue, 30 Apr 2024 17:41:29 GMT + Server: + - scaffolding on HTTPServer2 + Cache-Control: + - private + X-Xss-Protection: + - '0' + X-Frame-Options: + - SAMEORIGIN + X-Content-Type-Options: + - nosniff + Server-Timing: + - gfet4t7; dur=2840 + Alt-Svc: + - h3=":443"; ma=2592000,h3-29=":443"; ma=2592000 + Transfer-Encoding: + - chunked + body: + encoding: ASCII-8BIT + string: | + { + "candidates": [ + { + "content": { + "parts": [ + { + "text": "\u003cresponse\u003e\n \u003ctool_calls\u003e\n \u003ctool_call\u003e\n \u003ctool_name\u003ecode_description\u003c/tool_name\u003e\n \u003cparameters\u003e\n \u003ccode_description\u003eThis code is a simple command-line script that greets a person by name. It takes an optional argument, `--who`, to specify the name of the person to greet, and defaults to \"world\" if no name is provided. The script then prints a greeting to the specified person.\u003c/code_description\u003e\n \u003c/parameters\u003e\n \u003c/tool_call\u003e\n \u003c/tool_calls\u003e\n\u003c/response\u003e" + } + ], + "role": "model" + }, + "finishReason": "STOP", + "index": 0, + "safetyRatings": [ + { + "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HATE_SPEECH", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_HARASSMENT", + "probability": "NEGLIGIBLE" + }, + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "probability": "NEGLIGIBLE" + } + ] + } + ] + } + recorded_at: Tue, 30 Apr 2024 17:41:29 GMT +recorded_with: VCR 6.2.0