diff --git a/.env.sample b/.env.sample new file mode 100644 index 0000000..96d845d --- /dev/null +++ b/.env.sample @@ -0,0 +1 @@ +OPENAI_KEY=your-api-key diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4f3eaaa --- /dev/null +++ b/.gitignore @@ -0,0 +1,56 @@ +*.gem +*.rbc +/.config +/coverage/ +/InstalledFiles +/pkg/ +/spec/reports/ +/spec/examples.txt +/test/tmp/ +/test/version_tmp/ +/tmp/ + +# Used by dotenv library to load environment variables. +.env + +# Ignore Byebug command history file. +.byebug_history + +## Specific to RubyMotion: +.dat* +.repl_history +build/ +*.bridgesupport +build-iPhoneOS/ +build-iPhoneSimulator/ + +## Specific to RubyMotion (use of CocoaPods): +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +# vendor/Pods/ + +## Documentation cache and generated files: +/.yardoc/ +/_yardoc/ +/doc/ +/rdoc/ + +## Environment normalization: +/.bundle/ +/vendor/bundle +/lib/bundler/man/ + +# for a library or gem, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# Gemfile.lock +# .ruby-version +# .ruby-gemset + +# unless supporting rvm < 1.11.0 or doing something fancy, ignore this: +.rvmrc + +# Used by RuboCop. Remote config files pulled in from inherit_from directive. +# .rubocop-https?--* diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 0000000..860487c --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +2.7.1 diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..9ed936b --- /dev/null +++ b/Gemfile @@ -0,0 +1,5 @@ +source "https://rubygems.org" + +gem "dotenv" +gem "httparty" +gem "tty-prompt" diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..c75bbf6 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,35 @@ +GEM + remote: https://rubygems.org/ + specs: + dotenv (2.7.6) + httparty (0.18.1) + mime-types (~> 3.0) + multi_xml (>= 0.5.2) + mime-types (3.3.1) + mime-types-data (~> 3.2015) + mime-types-data (3.2020.0512) + multi_xml (0.6.0) + pastel (0.8.0) + tty-color (~> 0.5) + tty-color (0.5.2) + tty-cursor (0.7.1) + tty-prompt (0.22.0) + pastel (~> 0.8) + tty-reader (~> 0.8) + tty-reader (0.8.0) + tty-cursor (~> 0.7) + tty-screen (~> 0.8) + wisper (~> 2.0) + tty-screen (0.8.1) + wisper (2.0.1) + +PLATFORMS + ruby + +DEPENDENCIES + dotenv + httparty + tty-prompt + +BUNDLED WITH + 2.1.4 diff --git a/lib/openai.rb b/lib/openai.rb new file mode 100644 index 0000000..f2a5fdb --- /dev/null +++ b/lib/openai.rb @@ -0,0 +1,34 @@ +require "httparty" + +class OpenAI + URI = "https://api.openai.com/v1" + + def initialize(api_key:) + @api_key = api_key + end + + def completion(prompt:, max_tokens: 64, temperature: 1.0, stop: "<|endoftext|>") + request = HTTParty.post( + "#{URI}/engines/davinci/completions", + headers: headers, + body: { + prompt: prompt, + max_tokens: max_tokens, + temperature: temperature, + stop: stop + }.to_json + ) + request.parsed_response + end + + private + + attr_reader :api_key + + def headers + { + "Content-Type" => "application/json", + "Authorization" => "Bearer #{api_key}" + } + end +end diff --git a/lib/recipe_builder.rb b/lib/recipe_builder.rb new file mode 100644 index 0000000..fde8af9 --- /dev/null +++ b/lib/recipe_builder.rb @@ -0,0 +1,14 @@ +require_relative "recipe_builder_base" + +class RecipeBuilder < RecipeBuilderBase + PROMPT_NAME = "recipe" + + def recipe + @_recipe ||= openai.completion( + prompt: recipe_prompt, + max_tokens: 512, + temperature: 0.4, + stop: "Name:" + ).dig("choices").first.dig("text") + end +end diff --git a/lib/recipe_builder_base.rb b/lib/recipe_builder_base.rb new file mode 100644 index 0000000..096eb9a --- /dev/null +++ b/lib/recipe_builder_base.rb @@ -0,0 +1,23 @@ +require "dotenv/load" +require "yaml" +require_relative "openai" + +class RecipeBuilderBase + PROMPT_FILE = "prompts.yml" + + def initialize(name:) + @name = name + end + + private + + attr_accessor :name + + def openai + @_openai ||= OpenAI.new(api_key: ENV["OPENAI_KEY"]) + end + + def recipe_prompt + YAML.load_file(PROMPT_FILE).dig(self.class::PROMPT_NAME) + name + "\n" + end +end diff --git a/lib/recipe_story_builder.rb b/lib/recipe_story_builder.rb new file mode 100644 index 0000000..244940b --- /dev/null +++ b/lib/recipe_story_builder.rb @@ -0,0 +1,14 @@ +require_relative "recipe_builder_base" + +class RecipeStoryBuilder < RecipeBuilderBase + PROMPT_NAME = "recipe_story" + + def story + @_story ||= openai.completion( + prompt: recipe_prompt, + max_tokens: 512, + temperature: 0.8, + stop: "Name:" + ).dig("choices").first.dig("text") + end +end diff --git a/prompts.yml b/prompts.yml new file mode 100644 index 0000000..0f6e13f --- /dev/null +++ b/prompts.yml @@ -0,0 +1,50 @@ +recipe: | + Name: Sugar Cookies + + Ingredients: + flour, salt, baking soda, butter, sugar, egg, milk, vanilla extract + + Recipe: + 1. Whisk flour, salt, and baking soda together in a bowl. In a separate bowl, cream the butter, white sugar, and brown sugar together until mixture is light and fluffy, 3 to 4 minutes. Add the egg, milk, and vanilla extract. Whisk liquids together in small areas around the bowl, then all together to avoid separation. + + 2. Pour dry ingredients into the wet ingredients; stir until flour is thoroughly mixed in. Stir in the chocolate chips. + + 3. Transfer dough to a resealable plastic bag. Refrigerate until dough is firm, at least 2 hours. + + 4. Preheat oven to 375 degrees F (190 degrees C). Line baking sheet with parchment paper. + + 5. Scoop out rounded tablespoons of dough and place on prepared baking sheet, leaving 4 inches of space between cookies (about 8 per sheet). Bake in preheated oven until cookies are golden brown, about 12 minutes. Slide parchment and cookies onto a cooling rack for a few minutes. Remove parchment and finish cooling the cookies on the rack. + + Name: Shrimp Scampi + + Ingredients: + butter, shrimp, olive oil, pepper, salt, shallots, linguine, red pepper flakes, garlic, shallots + + Recipe: + 1. Bring a large pot of salted water to a boil; cook linguine in boiling water until nearly tender, 6 to 8 minutes. Drain. + + 2. Melt 2 tablespoons butter with 2 tablespoons olive oil in a large skillet over medium heat. Cook and stir shallots, garlic, and red pepper flakes in the hot butter and oil until shallots are translucent, 3 to 4 minutes. Season shrimp with kosher salt and black pepper; add to the skillet and cook until pink, stirring occasionally, 2 to 3 minutes. Remove shrimp from skillet and keep warm. + + 3. Pour white wine and lemon juice into skillet and bring to a boil while scraping the browned bits of food off of the bottom of the skillet with a wooden spoon. Melt 2 tablespoons butter in skillet, stir 2 tablespoons olive oil into butter mixture, and bring to a simmer. Toss linguine, shrimp, and parsley in the butter mixture until coated; season with salt and black pepper. Drizzle with 1 teaspoon olive oil to serve. + + Name: +recipe_story: | + Name: Cheesy Potato Casserole + + Do you have those family recipes that you make every holiday season without fail? In our family, we have a few, and they’re what I call super “old school” recipes. These are the classics that my brothers love more than anything. And they’re the ones we’ve been eating since I was a kid. You have those recipes too, right? + + We always do a cheesy potato casserole at Christmas. But a few years ago I decided I wanted to make something new and updated. I knew I had to keep the flavors pretty simple and classic. I also knew I wanted an easy side dish that I could prepare ahead of time and not have to worry about. Enter this casserole. It’s easy to throw together, but so incredibly delicious, and it’s pretty much guaranteed to be loved by all. + + Cheesy potatoes are one of those old school recipes. They’re without a doubt a tried and true favorite. Meaning they’re never not delicious, everyone who tries them loves them. I’m sure many of you have similar casseroles in your family. I know some call them funeral potatoes (which seems really depressing to me). Call um what you please, but in our house, they’re just cheesy potatoes. + + My mom’s recipe called for frozen potatoes, canned cream of chicken soup, sour cream, butter, cheese, and a few other ingredients. I decided to take her recipe and give it my own spin, updating a classic, and making it BETTER. I’d actually been wanting to do this for years but feared I wouldn’t be able to create something better. + + But a few years back I decided I’d had enough with the canned soup (because it freaks me out and I will not eat it…so yes, I used to miss out on cheesy potatoes every year). I knew it was time to update the cheesy potatoes recipe. Thankfully the new version turned how better than I could have imagined. This casserole is SO GOOD. + + Name: French Bread + + If you’re looking for an easy french bread recipe, this is it! You’ll have 2 beautiful loaves in 90 minutes. The actual time you’ll be working will only be about 15 minutes, the rest of the time will be patiently waiting for the dough to rest/rise. This homemade french bread is golden and crispy on the outside, while remaining soft and slightly chewy on the inside. We love to serve it with a bowl of warm soup and the first time I made it, my husband said “this bread is SO good, where did you get it?” He thought I picked it up from our local bakery. It is just that good and really so easy to make. You’ll never want to buy store bought french bread again. + + I absolutely love making bread and rolls of all kinds. There’s something so satisfying about making them in your own kitchen. The smell is unbelievable. We love to serve this homemade french bread with softened butter, honey and/or jam. So so good. Trust me when I say, this is the best french bread recipe! + + Name: