boxcars 0.2.5 → 0.2.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a3593a9df2d9d8a867729e0b6081b300125933566923ca905e2eefb16a933394
4
- data.tar.gz: 90c03ea9b328b8cff10f828f8b9bf4375cce0fd574dd8a26c690776e58a58c1b
3
+ metadata.gz: c58233487372c9e99f160786cc18f8e5464262479110ae2fcf2048fc09f3b338
4
+ data.tar.gz: 237d72649f3faef07c493e93f514077afd144578782fc8861f5a0b0b40152828
5
5
  SHA512:
6
- metadata.gz: 5de41be1f154b2c21fcd6602159a6428d8702dc318904e6e03db9de1b0ed1788b03aa10365203557b39fe268f04b4594cf6915faa941b2c64e475cd6cbb55d09
7
- data.tar.gz: 2ad84e5f416b19759807d658d739d1abb8405ba6ded3b38bdf5ce9406efeadccbc2102b7b477a90df67beaa287a9810b09e59b5843090fbe4619b03500d0a4f4
6
+ metadata.gz: a3c148dc072fea12862849183bfc37f4189639982e8a7409afab267aa22baed5b911dff223562705be42c5d57e06e76e6e8814b5366c120444fca529fe204187
7
+ data.tar.gz: acec67f2d4b5be3b660c2c6e8d90ef772925903f053f0a62965fc61f335a4fc84bbeee47265c49a596945e846d31ecad3b5a93602521037f91ed689c402712d8
data/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## [Unreleased](https://github.com/BoxcarsAI/boxcars/tree/HEAD)
4
+
5
+ [Full Changelog](https://github.com/BoxcarsAI/boxcars/compare/v0.2.5...HEAD)
6
+
7
+ **Closed issues:**
8
+
9
+ - The class name in the sample code of BoxCar-Google-Search wiki has not been changed. [\#50](https://github.com/BoxcarsAI/boxcars/issues/50)
10
+
11
+ **Merged pull requests:**
12
+
13
+ - Add Swagger Boxcar [\#51](https://github.com/BoxcarsAI/boxcars/pull/51) ([francis](https://github.com/francis))
14
+ - Boxcars::SQL tables and except\_tables [\#47](https://github.com/BoxcarsAI/boxcars/pull/47) ([arihh](https://github.com/arihh))
15
+ - ActiveRecord updates and new Wikipedia Search boxcar [\#46](https://github.com/BoxcarsAI/boxcars/pull/46) ([francis](https://github.com/francis))
16
+ - Fix README.md log\_prompts settings [\#45](https://github.com/BoxcarsAI/boxcars/pull/45) ([arihh](https://github.com/arihh))
17
+ - Update README.md to use the GoogleSearch Boxcar [\#44](https://github.com/BoxcarsAI/boxcars/pull/44) ([stockandawe](https://github.com/stockandawe))
18
+
19
+ ## [v0.2.5](https://github.com/BoxcarsAI/boxcars/tree/v0.2.5) (2023-03-30)
20
+
21
+ [Full Changelog](https://github.com/BoxcarsAI/boxcars/compare/v0.2.4...v0.2.5)
22
+
23
+ **Merged pull requests:**
24
+
25
+ - switch to safe level 4 for eval, and rerun tests [\#43](https://github.com/BoxcarsAI/boxcars/pull/43) ([francis](https://github.com/francis))
26
+
3
27
  ## [v0.2.4](https://github.com/BoxcarsAI/boxcars/tree/v0.2.4) (2023-03-28)
4
28
 
5
29
  [Full Changelog](https://github.com/BoxcarsAI/boxcars/compare/v0.2.3...v0.2.4)
data/Gemfile CHANGED
@@ -26,3 +26,7 @@ gem "activerecord", "~> 7.0"
26
26
  gem "github_changelog_generator", "~> 1.16"
27
27
 
28
28
  gem "faraday-retry", "~> 2.0"
29
+
30
+ gem "activesupport", "~> 7.0"
31
+
32
+ gem "rest-client", "~> 2.1"
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- boxcars (0.2.5)
4
+ boxcars (0.2.7)
5
5
  google_search_results (~> 2.2)
6
6
  ruby-openai (~> 3.0)
7
7
 
@@ -49,6 +49,8 @@ GEM
49
49
  irb (>= 1.5.0)
50
50
  reline (>= 0.3.1)
51
51
  diff-lcs (1.5.0)
52
+ domain_name (0.5.20190701)
53
+ unf (>= 0.0.5, < 1.0.0)
52
54
  dotenv (2.8.1)
53
55
  faraday (2.7.4)
54
56
  faraday-net_http (>= 2.0, < 3.1)
@@ -70,6 +72,9 @@ GEM
70
72
  rake (>= 10.0)
71
73
  google_search_results (2.2.0)
72
74
  hashdiff (1.0.1)
75
+ http-accept (1.7.0)
76
+ http-cookie (1.0.5)
77
+ domain_name (~> 0.5)
73
78
  httparty (0.21.0)
74
79
  mini_mime (>= 1.0.0)
75
80
  multi_xml (>= 0.5.2)
@@ -81,11 +86,15 @@ GEM
81
86
  reline (>= 0.3.0)
82
87
  json (2.6.3)
83
88
  json (2.6.3-java)
89
+ mime-types (3.4.1)
90
+ mime-types-data (~> 3.2015)
91
+ mime-types-data (3.2023.0218.1)
84
92
  mini_mime (1.1.2)
85
93
  mini_portile2 (2.8.1)
86
94
  minitest (5.18.0)
87
95
  multi_json (1.15.0)
88
96
  multi_xml (0.6.0)
97
+ netrc (0.11.0)
89
98
  nio4r (2.5.8)
90
99
  nio4r (2.5.8-java)
91
100
  octokit (4.25.1)
@@ -107,6 +116,11 @@ GEM
107
116
  regexp_parser (2.7.0)
108
117
  reline (0.3.3)
109
118
  io-console (~> 0.5)
119
+ rest-client (2.1.0)
120
+ http-accept (>= 1.7.0, < 2.0)
121
+ http-cookie (>= 1.0.2, < 2.0)
122
+ mime-types (>= 1.16, < 4.0)
123
+ netrc (~> 0.8)
110
124
  rexml (3.2.5)
111
125
  rspec (3.12.0)
112
126
  rspec-core (~> 3.12.0)
@@ -155,6 +169,10 @@ GEM
155
169
  traces (0.9.1)
156
170
  tzinfo (2.0.6)
157
171
  concurrent-ruby (~> 1.0)
172
+ unf (0.1.4)
173
+ unf_ext
174
+ unf (0.1.4-java)
175
+ unf_ext (0.0.8.2)
158
176
  unicode-display_width (2.4.2)
159
177
  vcr (6.1.0)
160
178
  webmock (3.18.1)
@@ -170,12 +188,14 @@ PLATFORMS
170
188
 
171
189
  DEPENDENCIES
172
190
  activerecord (~> 7.0)
191
+ activesupport (~> 7.0)
173
192
  boxcars!
174
193
  debug (~> 1.1)
175
194
  dotenv (~> 2.8)
176
195
  faraday-retry (~> 2.0)
177
196
  github_changelog_generator (~> 1.16)
178
197
  rake (~> 13.0)
198
+ rest-client (~> 2.1)
179
199
  rspec (~> 3.2)
180
200
  rubocop (~> 1.21)
181
201
  rubocop-rake (~> 0.6.0)
data/README.md CHANGED
@@ -18,10 +18,10 @@ This gem was inspired by the popular Python library Langchain. However, we wante
18
18
  ## Concepts
19
19
  All of these concepts are in a module named Boxcars:
20
20
 
21
- - Boxcar - an encapsulation that performs something of interest (such as search, math, SQL or an Active Record Query). A Boxcar can use an Engine (described below) to do its work.
21
+ - Boxcar - an encapsulation that performs something of interest (such as search, math, SQL, an Active Record Query, or an API call to a service). A Boxcar can use an Engine (described below) to do its work, and if not specified but needed, the default Engine is used `Boxcars.engine`.
22
22
  - Train - Given a list of Boxcars and optionally an Engine, a Train breaks down a problem into pieces for individual Boxcars to solve. The individual results are then combined until a final answer is found. ZeroShot is the only current implementation of Train (but we are adding more soon), and you can either construct it directly or use `Boxcars::train` when you want to build a Train.
23
- - Prompt - used by an Engine to generate text results. Most of the Boxcars have built-in prompts, so you only need to worry about these if you are extending the system.
24
- - Engine - an entity that generates text from a Prompt. OpenAI's LLM text generator is the default Engine if no other is specified.
23
+ - Prompt - used by an Engine to generate text results. Our Boxcars have built-in prompts, but you have the flexibility to change or augment them if you so desire.
24
+ - Engine - an entity that generates text from a Prompt. OpenAI's LLM text generator is the default Engine if no other is specified, and you can override the default engine if so desired (`Boxcar.configuration.default_engine`).
25
25
 
26
26
  ## Security
27
27
  Currently, our system is designed for individuals who already possess administrative privileges for their project. It is likely possible to manipulate the system's prompts to carry out malicious actions, but if you already have administrative access, you can perform such actions without requiring boxcars in the first place.
@@ -90,14 +90,16 @@ You can change the default_engine with `Boxcars::configuration.default_engine =
90
90
 
91
91
  Here is what we have so far, but please put up a PR with your new ideas.
92
92
  - GoogleSearch: uses the SERP API to do seaches
93
+ - WikipediaSearch: uses the Wikipedia API to do searches
93
94
  - Calculator: uses an Engine to generate ruby code to do math
94
95
  - SQL: given an ActiveRecord connection, it will generate and run sql statments from a prompt.
95
96
  - ActiveRecord: given an ActiveRecord connection, it will generate and run ActiveRecord statements from a prompt.
97
+ - Swagger: give a Swagger Open API file (YAML or JSON), answer questions about or run against the referenced service. See [here](https://github.com/BoxcarsAI/boxcars/blob/main/notebooks/swagger_examples.ipynb) for examples.
96
98
 
97
99
  ### Run a list of Boxcars
98
100
  ```ruby
99
101
  # run a Train for a calculator, and search using default Engine
100
- boxcars = [Boxcars::Calculator.new, Boxcars::Serp.new]
102
+ boxcars = [Boxcars::Calculator.new, Boxcars::GoogleSearch.new]
101
103
  train = Boxcars.train.new(boxcars: boxcars)
102
104
  train.run "What is pi times the square root of the average temperature in Austin TX in January?"
103
105
  ```
@@ -105,38 +107,40 @@ Produces:
105
107
  ```text
106
108
  > Entering Zero Shot#run
107
109
  What is pi times the square root of the average temperature in Austin TX in January?
108
- Thought: We need to find the average temperature in Austin TX in January and then multiply it by pi and the square root of that value. We can use a search engine to find the average temperature and a calculator to perform the multiplication.
110
+ Thought: We need to find the average temperature in Austin TX in January and then multiply it by pi and the square root of the average temperature. We can use a search engine to find the average temperature in Austin TX in January and a calculator to perform the multiplication.
109
111
  Question: Average temperature in Austin TX in January
110
- Answer: increase from 62°F to 64°F
111
- Observation: increase from 62°F to 64°F
112
- Thought: The average temperature in Austin TX in January is around 63°F.
112
+ Answer: January Weather in Austin Texas, United States. Daily high temperatures increase by 2°F, from 62°F to 64°F, rarely falling below 45°F or exceeding 76° ...
113
+ Observation: January Weather in Austin Texas, United States. Daily high temperatures increase by 2°F, from 62°F to 64°F, rarely falling below 45°F or exceeding 76° ...
114
+ Thought: We have found the average temperature in Austin TX in January, which is 64°F. Now we can use a calculator to perform the multiplication.
113
115
  > Entering Calculator#run
114
- pi * sqrt(63)
115
- RubyREPL: puts(Math::PI * Math.sqrt(63))
116
- Answer: 24.935618646198247
116
+ pi * sqrt(64)
117
+ RubyREPL: puts(Math::PI * Math.sqrt(64))
118
+ Answer: 25.132741228718345
117
119
 
118
- {"status":"ok","answer":"24.935618646198247","explanation":"Answer: 24.935618646198247","code":"puts(Math::PI * Math.sqrt(63))"}
120
+ {"status":"ok","answer":"25.132741228718345","explanation":"Answer: 25.132741228718345","code":"puts(Math::PI * Math.sqrt(64))"}
119
121
  < Exiting Calculator#run
120
- Observation: 24.935618646198247
121
- The result of pi times the square root of the average temperature in Austin TX in January is approximately 24.94.
122
+ Observation: 25.132741228718345
123
+ We have the final answer.
122
124
 
123
- Final Answer: 24.94
125
+ Final Answer: 25.132741228718345
124
126
 
125
127
  Next Actions:
126
128
  1. What is the average temperature in Austin TX in July?
127
- 2. What is the formula for calculating the area of a circle?
128
- 3. What is the value of pi to 10 decimal places?
129
+ 2. What is the value of pi to 10 decimal places?
130
+ 3. What is the square root of the average temperature in Miami FL in January?
129
131
  < Exiting Zero Shot#run
130
132
  ```
131
133
  ### More Examples
132
134
  See [this](https://github.com/BoxcarsAI/boxcars/blob/main/notebooks/boxcars_examples.ipynb) Jupyter Notebook for more examples.
133
135
 
136
+ For the new Swagger boxcar, see [this](https://github.com/BoxcarsAI/boxcars/blob/main/notebooks/swagger_examples.ipynb) Jupyter Notebook.
137
+
134
138
  Note, some folks that we talked to didn't know that you could run Ruby Jupyter notebooks. [You can](https://github.com/SciRuby/iruby).
135
139
 
136
140
  ### Logging
137
141
  If you use this in a Rails application, or configure `Boxcars.configuration.logger = your_logger`, logging will go to your log file.
138
142
 
139
- Also, if you set this flag: `Boxcars.configuration.lop_prompts = true`
143
+ Also, if you set this flag: `Boxcars.configuration.log_prompts = true`
140
144
  The actual prompts handed to the connected Engine will be logged. This is off by default because it is very wordy, but handy if you are debugging prompts.
141
145
 
142
146
  Otherwise, we print to standard out.
@@ -3,6 +3,7 @@
3
3
  # Boxcars is a framework for running a series of tools to get an answer to a question.
4
4
  module Boxcars
5
5
  # A Boxcar that interprets a prompt and executes SQL code to get answers
6
+ # rubocop:disable Metrics/ClassLength
6
7
  class ActiveRecord < EngineBoxcar
7
8
  # the description of this engine boxcar
8
9
  ARDESC = "useful for when you need to query a database for an application named %<name>s."
@@ -21,7 +22,7 @@ module Boxcars
21
22
  @approval_callback = approval_callback
22
23
  @read_only = read_only.nil? ? !approval_callback : read_only
23
24
  @code_only = kwargs.delete(:code_only) || false
24
- kwargs[:name] ||= "Data"
25
+ kwargs[:name] ||= get_name
25
26
  kwargs[:description] ||= format(ARDESC, name: name)
26
27
  kwargs[:prompt] ||= my_prompt
27
28
  super(**kwargs)
@@ -34,6 +35,13 @@ module Boxcars
34
35
 
35
36
  private
36
37
 
38
+ def get_name
39
+ return Rails.application.class.module_parent.name if defined?(Rails)
40
+ rescue StandardError => e
41
+ boxcars.error "Error getting rails name application name: #{e.message}"
42
+ nil
43
+ end
44
+
37
45
  def read_only?
38
46
  read_only
39
47
  end
@@ -44,6 +52,7 @@ module Boxcars
44
52
 
45
53
  def check_models(models, exceptions)
46
54
  if models.is_a?(Array) && models.length.positive?
55
+ models.map { |m| m.is_a?(Class) ? m : m.constantize }
47
56
  @requested_models = models
48
57
  models.each do |m|
49
58
  raise ArgumentError, "model #{m} needs to be an Active Record model" unless m.ancestors.include?(::ActiveRecord::Base)
@@ -119,7 +128,10 @@ module Boxcars
119
128
  # @return [Object] The result of the code
120
129
  def eval_safe_wrapper(code)
121
130
  # if the code used ActiveRecord, we need to add :: in front of it to escape the module
122
- new_code = code.gsub(/(\W)ActiveRecord::/, '\1::ActiveRecord::')
131
+ new_code = code.gsub(/\b(ActiveRecord::)/, '::\1')
132
+
133
+ # sometimes the code will have a puts or print in it, which will miss. Remove them.
134
+ new_code = new_code.gsub(/\b(puts|print)\b/, '')
123
135
  proc do
124
136
  $SAFE = 4
125
137
  # rubocop:disable Security/Eval
@@ -146,7 +158,11 @@ module Boxcars
146
158
  def approved?(changes_code, code)
147
159
  # find out how many changes there are
148
160
  changes = change_count(changes_code)
149
- return true unless changes&.positive?
161
+ begin
162
+ return true unless changes&.positive?
163
+ rescue StandardError => e
164
+ Boscar.error "Error while computing change count: #{e.message}", :red
165
+ end
150
166
 
151
167
  Boxcars.debug "#{name}(Pending Changes): #{changes}", :yellow
152
168
  if read_only?
@@ -242,7 +258,7 @@ module Boxcars
242
258
  "Pay attention to use only the attribute names that you can see in the model description.\n",
243
259
  "Do not make up variable or attribute names, and do not share variables between the code in ARChanges and ARCode\n",
244
260
  "Be careful to not query for attributes that do not exist, and to use the format specified above.\n",
245
- "Finally, do not use print or puts in your code."
261
+ "Finally, try not to use print or puts in your code"
246
262
  ),
247
263
  user("Question: %<question>s")
248
264
  ].freeze
@@ -257,4 +273,5 @@ module Boxcars
257
273
  output_variables: [:answer])
258
274
  end
259
275
  end
276
+ # rubocop:enable Metrics/ClassLength
260
277
  end
@@ -121,7 +121,7 @@ module Boxcars
121
121
  # @param inputs [Hash] The inputs to the boxcar.
122
122
  # @return Hash The variables for this boxcar.
123
123
  def prediction_variables(inputs)
124
- prediction_input(inputs).merge(prediction_additional)
124
+ prediction_additional.merge(inputs)
125
125
  end
126
126
 
127
127
  # remove backticks or triple backticks from the code
@@ -34,11 +34,11 @@ module Boxcars
34
34
  # @param question [String] The question to ask Google.
35
35
  # @return [String] The location found.
36
36
  def get_location(question)
37
+ Boxcars.debug "Question: #{question}", :yellow
37
38
  search = ::GoogleSearch.new(q: question, limit: 3)
38
- rv = search.get_location
39
- puts "Question: #{question}"
40
- puts "Answer: #{rv}"
41
- rv
39
+ answer = search.get_location
40
+ Boxcars.debug "Answer: #{answer}", :yellow, style: :bold
41
+ answer
42
42
  end
43
43
 
44
44
  private
@@ -41,6 +41,8 @@ module Boxcars
41
41
  end
42
42
  elsif rtables
43
43
  raise ArgumentError, "tables needs to be an array of Strings"
44
+ else
45
+ @requested_tables = tables
44
46
  end
45
47
  @except_models = LOCKED_OUT_TABLES + exceptions.to_a
46
48
  end
@@ -55,8 +57,8 @@ module Boxcars
55
57
  ");"].join("\n")
56
58
  end
57
59
 
58
- def schema(except_tables: ['ar_internal_metadata'])
59
- wanted_tables = tables.to_a - except_tables
60
+ def schema
61
+ wanted_tables = @requested_tables - @except_models
60
62
  wanted_tables.map(&method(:table_schema)).join("\n")
61
63
  end
62
64
 
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Boxcars is a framework for running a series of tools to get an answer to a question.
4
+ module Boxcars
5
+ # A Boxcar that interprets a prompt and executes API calls to get an answer.
6
+ class Swagger < EngineBoxcar
7
+ # the description of this engine boxcar
8
+ DESC = "useful for when you need to make Open API calls to get an answer."
9
+
10
+ attr_accessor :swagger_url, :context
11
+
12
+ # @param swagger_url [String] The URL of the Open API Swagger file to use.
13
+ # @param engine [Boxcars::Engine] The engine to user for this boxcar. Can be inherited from a train if nil.
14
+ # @param prompt [Boxcars::Prompt] The prompt to use for this boxcar. Defaults to built-in prompt.
15
+ # @param context [String] Additional context to use for the prompt.
16
+ # @param kwargs [Hash] Any other keyword arguments to pass to the parent class.
17
+ def initialize(swagger_url:, engine: nil, prompt: nil, context: "", **kwargs)
18
+ @swagger_url = swagger_url
19
+ @context = context
20
+ the_prompt = prompt || my_prompt
21
+ kwargs[:stop] ||= ["```output"]
22
+ kwargs[:name] ||= "Swagger API"
23
+ kwargs[:description] ||= DESC
24
+ super(engine: engine, prompt: the_prompt, **kwargs)
25
+ end
26
+
27
+ # @return Hash The additional variables for this boxcar.
28
+ def prediction_additional
29
+ { swagger_url: swagger_url, context: context }.merge super
30
+ end
31
+
32
+ private
33
+
34
+ def get_embedded_ruby_answer(text)
35
+ code = text.split("```ruby\n").last.split("```").first.strip
36
+ ruby_executor = Boxcars::RubyREPL.new
37
+ ruby_executor.call(code: code)
38
+ end
39
+
40
+ def get_answer(text)
41
+ case text
42
+ when /^```ruby/
43
+ get_embedded_ruby_answer(text)
44
+ when /^Answer:/
45
+ Result.from_text(text)
46
+ else
47
+ Result.new(status: :error,
48
+ explanation: "Error: expecting your response to begin with '```ruby'. Try answering the question again.")
49
+ end
50
+ end
51
+
52
+ # our template
53
+ CTEMPLATE = [
54
+ syst("Study this Open API Swagger file %<swagger_url>s\n",
55
+ "and write a Ruby Program that prints the answer to the following questions using the appropriate API calls:\n",
56
+ "Additional context that you might need in the Ruby program: (%<context>s)\n",
57
+ "Use the following format:\n",
58
+ "${{Question needing API calls and code}}\n",
59
+ "reply only with the following format:\n",
60
+ "```ruby\n${{Ruby code with API calls and code that prints the answer}}\n```\n",
61
+ "```output\n${{Output of your code}}\n```\n\n",
62
+ "Otherwise, if you know the answer and do not need any API calls, you should use this simpler format:\n",
63
+ "${{Question not needing API calls}}\n",
64
+ "Answer: ${{Answer}}\n\n",
65
+ "Do not give an explanation of the answer and make sure your answer starts with either 'Answer:' or '```ruby'. ",
66
+ "Make use of the rest-client gem to make your requests to the API. Just print the answer."),
67
+ user("%<question>s")
68
+ ].freeze
69
+
70
+ # The prompt to use for the engine.
71
+ def my_prompt
72
+ @conversation ||= Conversation.new(lines: CTEMPLATE)
73
+ @my_prompt ||= ConversationPrompt.new(
74
+ conversation: @conversation,
75
+ input_variables: [:question],
76
+ other_inputs: [:context, :swagger_url],
77
+ output_variables: [:answer])
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ module Boxcars
5
+ # A Boxcar that uses the Wikipedia search API to get answers to questions.
6
+ class WikipediaSearch < Boxcar
7
+ # the description of this boxcar
8
+ WDESC = "useful for when you need to answer questions about topics from Wikipedia." \
9
+ "You should ask targeted questions"
10
+
11
+ # implements a boxcar that uses the Wikipedia Search to get answers to questions.
12
+ # @param name [String] The name of the boxcar. Defaults to classname.
13
+ # @param description [String] A description of the boxcar. Defaults to SERPDESC.
14
+ # @param serpapi_api_key [String] The API key to use for the SerpAPI. Defaults to Boxcars.configuration.serpapi_api_key.
15
+ def initialize(name: "Wikipedia", description: WDESC)
16
+ super(name: name, description: description)
17
+ end
18
+
19
+ # Get an answer from Google using the SerpAPI.
20
+ # @param question [String] The question to ask Google.
21
+ # @return [String] The answer to the question.
22
+ def run(question)
23
+ Boxcars.debug "Question: #{question}", :yellow
24
+ uri = URI("https://en.wikipedia.org/w/api.php")
25
+ params = { action: "query", list: "search", srsearch: question, format: "json" }
26
+ uri.query = URI.encode_www_form(params)
27
+
28
+ res = Net::HTTP.get_response(uri)
29
+ raise "Error getting response from Wikipedia: #{res.body}" unless res.is_a?(Net::HTTPSuccess)
30
+
31
+ response = JSON.parse res.body
32
+ answer = response.dig("query", "search", 0, "snippet").to_s.gsub(/<[^>]*>/, "")
33
+ pageid = response.dig("query", "search", 0, "pageid")
34
+ answer = "#{answer}\nurl: https://en.wikipedia.org/?curid=#{pageid}" if pageid
35
+ Boxcars.debug "Answer: #{answer}", :yellow, style: :bold
36
+ answer
37
+ end
38
+ end
39
+ end
@@ -119,7 +119,10 @@ module Boxcars
119
119
 
120
120
  return run_boxcar(inputs: args[0])[output_keys.first]
121
121
  end
122
- return run_boxcar(**kwargs)[output_keys].first if args.empty?
122
+ if args.empty?
123
+ ans = run_boxcar(inputs: kwargs)
124
+ return ans[output_keys.first]
125
+ end
123
126
 
124
127
  raise Boxcars::ArgumentError, "run supported with either positional or keyword arguments but not both. Got args" \
125
128
  ": #{args} and kwargs: #{kwargs}."
@@ -149,5 +152,7 @@ require "boxcars/result"
149
152
  require "boxcars/boxcar/engine_boxcar"
150
153
  require "boxcars/boxcar/calculator"
151
154
  require "boxcars/boxcar/google_search"
155
+ require "boxcars/boxcar/wikipedia_search"
152
156
  require "boxcars/boxcar/sql"
157
+ require "boxcars/boxcar/swagger"
153
158
  require "boxcars/boxcar/active_record"
@@ -20,6 +20,7 @@ module Boxcars
20
20
  Boxcars.debug output, :red
21
21
  Result.from_error(output, code: code)
22
22
  else
23
+ output = ::Regexp.last_match(1) if output =~ /^\s*Answer:\s*(.*)$/m
23
24
  Boxcars.debug "Answer: #{output}", :yellow, style: :bold
24
25
  Result.from_text(output, code: code)
25
26
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Boxcars
4
4
  # The current version of the gem.
5
- VERSION = "0.2.5"
5
+ VERSION = "0.2.7"
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: boxcars
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.2.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Francis Sullivan
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2023-03-30 00:00:00.000000000 Z
12
+ date: 2023-04-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: debug
@@ -109,6 +109,8 @@ files:
109
109
  - lib/boxcars/boxcar/engine_boxcar.rb
110
110
  - lib/boxcars/boxcar/google_search.rb
111
111
  - lib/boxcars/boxcar/sql.rb
112
+ - lib/boxcars/boxcar/swagger.rb
113
+ - lib/boxcars/boxcar/wikipedia_search.rb
112
114
  - lib/boxcars/conversation.rb
113
115
  - lib/boxcars/conversation_prompt.rb
114
116
  - lib/boxcars/engine.rb