deepsearch-rb 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +8 -0
- data/LICENSE.txt +21 -0
- data/README.md +138 -0
- data/lib/deepsearch/configuration.rb +88 -0
- data/lib/deepsearch/engine/pipeline.rb +126 -0
- data/lib/deepsearch/engine/steps/data_aggregation/parsed_website.rb +122 -0
- data/lib/deepsearch/engine/steps/data_aggregation/process.rb +56 -0
- data/lib/deepsearch/engine/steps/data_aggregation/result.rb +28 -0
- data/lib/deepsearch/engine/steps/parallel_search/process.rb +42 -0
- data/lib/deepsearch/engine/steps/parallel_search/result.rb +28 -0
- data/lib/deepsearch/engine/steps/parallel_search/search.rb +95 -0
- data/lib/deepsearch/engine/steps/prepare_subqueries/process.rb +87 -0
- data/lib/deepsearch/engine/steps/prepare_subqueries/result.rb +30 -0
- data/lib/deepsearch/engine/steps/rag/chunker.rb +31 -0
- data/lib/deepsearch/engine/steps/rag/process.rb +79 -0
- data/lib/deepsearch/engine/steps/rag/similarity.rb +60 -0
- data/lib/deepsearch/engine/steps/rag/values/chunk.rb +23 -0
- data/lib/deepsearch/engine/steps/rag/values/query.rb +44 -0
- data/lib/deepsearch/engine/steps/rag/values/result.rb +33 -0
- data/lib/deepsearch/engine/steps/summarization/process.rb +53 -0
- data/lib/deepsearch/engine/steps/summarization/values/result.rb +31 -0
- data/lib/deepsearch/engine.rb +25 -0
- data/lib/deepsearch/logger.rb +32 -0
- data/lib/deepsearch/prompts_config.rb +82 -0
- data/lib/deepsearch/version.rb +5 -0
- data/lib/deepsearch.rb +39 -0
- data/lib/search_adapters/mock_adapter.rb +73 -0
- data/lib/search_adapters/serper_adapter.rb +106 -0
- data/lib/search_adapters/tavily_adapter.rb +113 -0
- data/lib/search_adapters.rb +24 -0
- metadata +186 -0
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Deepsearch
|
4
|
+
# Defines the default prompts used in various steps of the Deepsearch pipeline.
|
5
|
+
# Users can provide their own prompt configuration object to customize these prompts.
|
6
|
+
# The custom object should respond to the same methods as this class.
|
7
|
+
class PromptsConfig
|
8
|
+
# @param query [String] The original user query.
|
9
|
+
# @return [String] The prompt for generating subqueries.
|
10
|
+
def subquery_prompt(query:)
|
11
|
+
<<~PROMPT
|
12
|
+
You are a search query expansion expert. Given a search query, generate 3-5 related subqueries that would help find more comprehensive information about the topic.
|
13
|
+
|
14
|
+
Original query: "#{query}"
|
15
|
+
|
16
|
+
Please generate subqueries that:
|
17
|
+
1. Cover different aspects of the topic
|
18
|
+
2. Use varied terminology and synonyms
|
19
|
+
3. Are specific enough to be useful but broad enough to capture relevant information
|
20
|
+
4. Focus on different angles (technical, practical, conceptual, etc.)
|
21
|
+
|
22
|
+
Format your response as a simple list, one subquery per line, without numbering or bullet points.
|
23
|
+
PROMPT
|
24
|
+
end
|
25
|
+
|
26
|
+
# @param query [String] The user query to be enriched.
|
27
|
+
# @return [String] The prompt for enriching the query with tags for embedding.
|
28
|
+
def enrich_query_prompt(query:)
|
29
|
+
<<~PROMPT
|
30
|
+
You are a search query expansion assistant. Based on the user's query, generate 5 to 7 highly relevant tags or keywords that expand its core concepts. These tags are used to improve vector search results.
|
31
|
+
|
32
|
+
INSTRUCTIONS:
|
33
|
+
- Do NOT add any explanation, numbering, or introductory text.
|
34
|
+
- Provide ONLY a single line of comma-separated tags.
|
35
|
+
|
36
|
+
USER QUERY: "#{query}"
|
37
|
+
|
38
|
+
TAGS:
|
39
|
+
PROMPT
|
40
|
+
end
|
41
|
+
|
42
|
+
# @param query [String] The original user query.
|
43
|
+
# @param context_text [String] The aggregated text from relevant sources.
|
44
|
+
# @param sources_list [String] The formatted list of sources for citation.
|
45
|
+
# @return [String] The prompt for summarizing the context and answering the query.
|
46
|
+
def summarization_prompt(query:, context_text:, sources_list:)
|
47
|
+
<<~PROMPT
|
48
|
+
You are a research assistant. Your task is to synthesize a comprehensive answer to the user's query based *only* on the provided text snippets.
|
49
|
+
|
50
|
+
User Query: "#{query}"
|
51
|
+
|
52
|
+
Provided Context:
|
53
|
+
---
|
54
|
+
#{context_text}
|
55
|
+
---
|
56
|
+
|
57
|
+
Sources:
|
58
|
+
#{sources_list}
|
59
|
+
|
60
|
+
Instructions:
|
61
|
+
0. Format: Markdown
|
62
|
+
1. Carefully read the user's query and all the provided context from the sources.
|
63
|
+
2. When you use information from a source, you MUST cite it using the following markdown link format: `[<source_number>](<source_link>)`.
|
64
|
+
* **To find the `<source_link>`:** The 'Sources' section provides a numbered list where each source is in the format `[<number>]: <url>`. Extract this URL for the specific source number you are citing.
|
65
|
+
* **Example Citation:** If a fact comes from source number 1, and the 'Sources' section lists `[1]: https://example.com/source1`, your citation should be `[1](https://example.com/source1)`.
|
66
|
+
* **Placement:** Place citations immediately after the sentence or clause where the information is used.
|
67
|
+
* **Multiple Sources:** If a single statement draws information from multiple sources, cite each source individually, for example: `[1](https://link1.com) [3](https://link3.com)`.
|
68
|
+
* **Crucial:** Always use the exact format `[<source_number>](<source_link>)` with square brackets around the number.
|
69
|
+
3. Structure your response with clear headings and bullet points where appropriate.
|
70
|
+
4. If the provided context does not contain enough information to answer the query, state that clearly: "I cannot answer this query based on the provided sources."
|
71
|
+
5. At the end of your response, include a "## References" section that lists all used sources in the following format:
|
72
|
+
```
|
73
|
+
## References
|
74
|
+
1. [Source Title](<source_link>)
|
75
|
+
2. [Another Source](<another_link>)
|
76
|
+
```
|
77
|
+
Where possible, extract meaningful titles from the context or use the domain name if no title is available.
|
78
|
+
6. Do not include sources in the References section that you didn't actually cite in your response.
|
79
|
+
PROMPT
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/lib/deepsearch.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "deepsearch/version"
|
4
|
+
require_relative "deepsearch/logger"
|
5
|
+
require_relative "deepsearch/configuration"
|
6
|
+
require_relative "deepsearch/engine"
|
7
|
+
require_relative "search_adapters"
|
8
|
+
|
9
|
+
# The main module for the Deepsearch gem.
|
10
|
+
# It provides the primary interface for configuration and for initiating searches.
|
11
|
+
module Deepsearch
|
12
|
+
# A generic error class for exceptions raised by the Deepsearch gem,
|
13
|
+
# from which more specific errors can inherit.
|
14
|
+
class Error < StandardError; end
|
15
|
+
|
16
|
+
class << self
|
17
|
+
def configuration
|
18
|
+
@configuration ||= Configuration.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def configure
|
22
|
+
yield(configuration)
|
23
|
+
configuration.configure_llm!
|
24
|
+
end
|
25
|
+
|
26
|
+
def reset_configuration!
|
27
|
+
@configuration = Configuration.new
|
28
|
+
end
|
29
|
+
|
30
|
+
def search(query, adapter_type: nil, **options)
|
31
|
+
engine = Engine.new(adapter_type: adapter_type)
|
32
|
+
engine.search(query, **options)
|
33
|
+
end
|
34
|
+
|
35
|
+
def engine(adapter_type: nil)
|
36
|
+
Engine.new(adapter_type: adapter_type)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Deepsearch
|
4
|
+
module SearchAdapters
|
5
|
+
# A mock search adapter for testing purposes.
|
6
|
+
# It returns a static, predefined set of search results without making any external API calls.
|
7
|
+
# This is useful for testing the pipeline's behavior in isolation from live search services.
|
8
|
+
class MockAdapter
|
9
|
+
def initialize(api_key = nil); end
|
10
|
+
|
11
|
+
def search(query, options = {})
|
12
|
+
return mock_results(query, options)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def mock_results(query, options = {})
|
18
|
+
max_results = options[:max_results] || 10
|
19
|
+
include_answer = options[:include_answer] != false
|
20
|
+
|
21
|
+
results = [
|
22
|
+
{
|
23
|
+
"title" => "Ruby (programming language) - Wikipedia",
|
24
|
+
"url" => "https://en.wikipedia.org/wiki/Ruby_(programming_language)",
|
25
|
+
"content" => "Ruby is a general-purpose programming language. It was designed with an emphasis on programming productivity and simplicity. In Ruby, everything is an object, including primitive data types. It was developed in the mid-1990s by Yukihiro Matsumoto in Japan.",
|
26
|
+
"published_date" => "2024-01-15"
|
27
|
+
},
|
28
|
+
{
|
29
|
+
"title" => "Ruby Programming Language - GeeksforGeeks",
|
30
|
+
"url" => "https://www.geeksforgeeks.org/ruby/ruby-programming-language/",
|
31
|
+
"content" => "Ruby is a dynamic, reflective, object-oriented, general-purpose programming language. Ruby is a pure object-oriented language developed by Yukihiro Matsumoto. Everything in Ruby is an object except the blocks but there are replacements too.",
|
32
|
+
"published_date" => "2024-02-10"
|
33
|
+
},
|
34
|
+
{
|
35
|
+
"title" => "Ruby Programming Language",
|
36
|
+
"url" => "https://www.ruby-lang.org/en/",
|
37
|
+
"content" => "# Ruby # Ruby is... A dynamic, open source programming language with a focus on natural to read and easy to write code. It has an elegant syntax that is natural to read and easy to write.",
|
38
|
+
"published_date" => "2024-03-01"
|
39
|
+
},
|
40
|
+
{
|
41
|
+
"title" => "About Ruby",
|
42
|
+
"url" => "https://www.ruby-lang.org/en/about/",
|
43
|
+
"content" => "About Ruby About Ruby About Ruby's Growth In Ruby, everything is an object. ruby Ruby follows the information-hiding principle where the implementation details are hidden and only the necessary information is exposed.",
|
44
|
+
"published_date" => "2024-02-20"
|
45
|
+
},
|
46
|
+
{
|
47
|
+
"title" => "What's Ruby used for most nowadays? - Reddit",
|
48
|
+
"url" => "https://www.reddit.com/r/ruby/comments/yhe3t4/whats_ruby_used_for_most_nowadays/",
|
49
|
+
"content" => "Ruby is mainly used in web app development because that's what makes money. However, Ruby is also used for automation, data processing, DevOps tools, and many other applications where its expressive syntax shines.",
|
50
|
+
"published_date" => "2024-01-30"
|
51
|
+
}
|
52
|
+
]
|
53
|
+
|
54
|
+
limited_results = results.take(max_results)
|
55
|
+
|
56
|
+
response = {
|
57
|
+
"query" => query,
|
58
|
+
"follow_up_questions" => [],
|
59
|
+
"images" => [],
|
60
|
+
"results" => limited_results,
|
61
|
+
"search_depth" => options[:search_depth] || "basic",
|
62
|
+
"search_time" => 0.1
|
63
|
+
}
|
64
|
+
|
65
|
+
if include_answer
|
66
|
+
response["answer"] = "Ruby is a dynamic, open-source programming language with a focus on simplicity and productivity. It was created by Yukihiro Matsumoto in the mid-1990s and follows the principle that everything is an object. Ruby is particularly popular for web development, especially with the Ruby on Rails framework, but it's also used for automation, data processing, and various other applications."
|
67
|
+
end
|
68
|
+
|
69
|
+
response
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
require 'json'
|
5
|
+
require 'uri'
|
6
|
+
|
7
|
+
module Deepsearch
|
8
|
+
module SearchAdapters
|
9
|
+
# An adapter for the Serper Search API (google.serper.dev).
|
10
|
+
class SerperAdapter
|
11
|
+
BASE_URL = 'https://google.serper.dev/search'
|
12
|
+
|
13
|
+
def initialize(api_key = nil)
|
14
|
+
@api_key = api_key || Deepsearch.configuration.serper_api_key
|
15
|
+
validate_api_key!
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param query [String] The search query
|
19
|
+
# @param options [Hash] Additional search options
|
20
|
+
# @option options [Integer] :max_results Maximum number of results to return. Serper calls this `num`.
|
21
|
+
# @return [Hash] Parsed and transformed response from Serper API
|
22
|
+
# @raise [SerperError] If the API request fails
|
23
|
+
def search(query, options = {})
|
24
|
+
raise ArgumentError, "Query cannot be empty" if query.nil? || query.strip.empty?
|
25
|
+
|
26
|
+
payload = build_payload(query, options)
|
27
|
+
response = make_request(payload)
|
28
|
+
parsed_response = parse_response(response)
|
29
|
+
transform_response(parsed_response)
|
30
|
+
rescue Net::HTTPError, Net::ReadTimeout, Net::OpenTimeout => e
|
31
|
+
raise SerperError, "Network error: #{e.message}"
|
32
|
+
rescue JSON::ParserError => e
|
33
|
+
raise SerperError, "Invalid JSON response: #{e.message}"
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def validate_api_key!
|
39
|
+
raise SerperError, "API key is required" if @api_key.nil? || @api_key.strip.empty?
|
40
|
+
end
|
41
|
+
|
42
|
+
def build_payload(query, options)
|
43
|
+
payload = {
|
44
|
+
q: query
|
45
|
+
}
|
46
|
+
# Tavily uses `max_results`, Serper uses `num`. Let's support `max_results` from options.
|
47
|
+
payload[:num] = options[:max_results] if options[:max_results]
|
48
|
+
payload
|
49
|
+
end
|
50
|
+
|
51
|
+
def make_request(payload)
|
52
|
+
uri = URI(BASE_URL)
|
53
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
54
|
+
http.use_ssl = true
|
55
|
+
http.read_timeout = 15
|
56
|
+
http.open_timeout = 5
|
57
|
+
|
58
|
+
request = Net::HTTP::Post.new(uri)
|
59
|
+
request['Content-Type'] = 'application/json'
|
60
|
+
request['X-API-KEY'] = @api_key
|
61
|
+
request.body = payload.to_json
|
62
|
+
|
63
|
+
response = http.request(request)
|
64
|
+
|
65
|
+
handle_error_response(response) unless response.is_a?(Net::HTTPSuccess)
|
66
|
+
|
67
|
+
response
|
68
|
+
end
|
69
|
+
|
70
|
+
def parse_response(response)
|
71
|
+
JSON.parse(response.body)
|
72
|
+
rescue JSON::ParserError => e
|
73
|
+
raise SerperError, "Failed to parse response: #{e.message}"
|
74
|
+
end
|
75
|
+
|
76
|
+
def transform_response(response_body)
|
77
|
+
results = response_body['organic']&.map do |item|
|
78
|
+
{
|
79
|
+
'url' => item['link'],
|
80
|
+
'title' => item['title'],
|
81
|
+
'content' => item['snippet']
|
82
|
+
}
|
83
|
+
end || []
|
84
|
+
{ 'results' => results }
|
85
|
+
end
|
86
|
+
|
87
|
+
def handle_error_response(response)
|
88
|
+
case response.code.to_i
|
89
|
+
when 400
|
90
|
+
raise SerperError, "Bad request: #{response.body}"
|
91
|
+
when 401, 402
|
92
|
+
raise SerperError, "Unauthorized or payment required: #{response.body}"
|
93
|
+
when 429
|
94
|
+
raise SerperError, "Rate limit exceeded"
|
95
|
+
when 500..599
|
96
|
+
raise SerperError, "Server error: #{response.code}"
|
97
|
+
else
|
98
|
+
raise SerperError, "HTTP error: #{response.code} - #{response.body}"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Custom error class for exceptions raised by the SerperAdapter.
|
104
|
+
class SerperError < StandardError; end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
require 'json'
|
5
|
+
require 'uri'
|
6
|
+
|
7
|
+
module Deepsearch
|
8
|
+
module SearchAdapters
|
9
|
+
# An adapter for the Tavily Search API.
|
10
|
+
class TavilyAdapter
|
11
|
+
BASE_URL = 'https://api.tavily.com/search'
|
12
|
+
|
13
|
+
def initialize(api_key = nil)
|
14
|
+
@api_key = api_key || Deepsearch.configuration.tavily_api_key
|
15
|
+
validate_api_key!
|
16
|
+
end
|
17
|
+
|
18
|
+
# @param query [String] The search query
|
19
|
+
# @param options [Hash] Additional search options
|
20
|
+
# @option options [Integer] :max_results Maximum number of results (default: 10)
|
21
|
+
# @option options [Array<String>] :include_domains Domains to include in search
|
22
|
+
# @option options [Array<String>] :exclude_domains Domains to exclude from search
|
23
|
+
# @option options [Boolean] :include_answer Whether to include AI-generated answer (default: true)
|
24
|
+
# @option options [Boolean] :include_raw_content Whether to include raw content (default: false)
|
25
|
+
# @option options [String] :search_depth Search depth: 'basic' or 'advanced' (default: 'basic')
|
26
|
+
# @return [Hash] Parsed response from Tavily API
|
27
|
+
# @raise [TavilyError] If the API request fails
|
28
|
+
def search(query, options = {})
|
29
|
+
raise ArgumentError, "Query cannot be empty" if query.nil? || query.strip.empty?
|
30
|
+
|
31
|
+
payload = build_payload(query, options)
|
32
|
+
response = make_request(payload)
|
33
|
+
parse_response(response)
|
34
|
+
rescue Net::HTTPError, Net::ReadTimeout, Net::OpenTimeout => e
|
35
|
+
raise TavilyError, "Network error: #{e.message}"
|
36
|
+
rescue JSON::ParserError => e
|
37
|
+
raise TavilyError, "Invalid JSON response: #{e.message}"
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def validate_api_key!
|
43
|
+
if @api_key.nil? || @api_key.strip.empty?
|
44
|
+
raise TavilyError, "API key is required"
|
45
|
+
end
|
46
|
+
|
47
|
+
unless @api_key.start_with?('tvly-')
|
48
|
+
raise TavilyError, "Invalid API key format. Expected format: tvly-YOUR_API_KEY"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def build_payload(query, options)
|
53
|
+
payload = {
|
54
|
+
query: query,
|
55
|
+
max_results: options[:max_results] || 10,
|
56
|
+
include_answer: options.fetch(:include_answer, true),
|
57
|
+
include_raw_content: options.fetch(:include_raw_content, false),
|
58
|
+
search_depth: options[:search_depth] || 'basic'
|
59
|
+
}
|
60
|
+
|
61
|
+
payload[:include_domains] = options[:include_domains] if options[:include_domains]
|
62
|
+
payload[:exclude_domains] = options[:exclude_domains] if options[:exclude_domains]
|
63
|
+
|
64
|
+
payload
|
65
|
+
end
|
66
|
+
|
67
|
+
def make_request(payload)
|
68
|
+
uri = URI(BASE_URL)
|
69
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
70
|
+
http.use_ssl = true
|
71
|
+
http.read_timeout = 15
|
72
|
+
http.open_timeout = 5
|
73
|
+
|
74
|
+
request = Net::HTTP::Post.new(uri)
|
75
|
+
request['Content-Type'] = 'application/json'
|
76
|
+
request['Authorization'] = "Bearer #{@api_key}"
|
77
|
+
request.body = payload.to_json
|
78
|
+
|
79
|
+
response = http.request(request)
|
80
|
+
|
81
|
+
unless response.is_a?(Net::HTTPSuccess)
|
82
|
+
handle_error_response(response)
|
83
|
+
end
|
84
|
+
|
85
|
+
response
|
86
|
+
end
|
87
|
+
|
88
|
+
def parse_response(response)
|
89
|
+
JSON.parse(response.body)
|
90
|
+
rescue JSON::ParserError => e
|
91
|
+
raise TavilyError, "Failed to parse response: #{e.message}"
|
92
|
+
end
|
93
|
+
|
94
|
+
def handle_error_response(response)
|
95
|
+
case response.code.to_i
|
96
|
+
when 400
|
97
|
+
raise TavilyError, "Bad request: #{response.body}"
|
98
|
+
when 401
|
99
|
+
raise TavilyError, "Unauthorized: Invalid API key"
|
100
|
+
when 429
|
101
|
+
raise TavilyError, "Rate limit exceeded"
|
102
|
+
when 500..599
|
103
|
+
raise TavilyError, "Server error: #{response.code}"
|
104
|
+
else
|
105
|
+
raise TavilyError, "HTTP error: #{response.code} - #{response.body}"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Custom error class for exceptions raised by the TavilyAdapter.
|
111
|
+
class TavilyError < StandardError; end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'search_adapters/tavily_adapter'
|
4
|
+
require_relative 'search_adapters/mock_adapter'
|
5
|
+
require_relative 'search_adapters/serper_adapter'
|
6
|
+
|
7
|
+
module Deepsearch
|
8
|
+
module SearchAdapters
|
9
|
+
def self.create(type, *args)
|
10
|
+
return type.new(*args) if type.is_a?(Class)
|
11
|
+
|
12
|
+
case type.to_sym
|
13
|
+
when :tavily
|
14
|
+
TavilyAdapter.new(*args)
|
15
|
+
when :serper
|
16
|
+
SerperAdapter.new(*args)
|
17
|
+
when :mock
|
18
|
+
MockAdapter.new(*args)
|
19
|
+
else
|
20
|
+
raise ArgumentError, "Unknown or invalid adapter type: #{type}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
metadata
ADDED
@@ -0,0 +1,186 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: deepsearch-rb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alexander Shagov
|
8
|
+
bindir: bin
|
9
|
+
cert_chain: []
|
10
|
+
date: 1980-01-01 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: nokogiri
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - ">="
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '1.10'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - ">="
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '1.10'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: async
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '2.0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '2.0'
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: ruby_llm
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '1.0'
|
47
|
+
type: :runtime
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '1.0'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: bundler
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '2.0'
|
61
|
+
type: :development
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '2.0'
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: rake
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '13.0'
|
75
|
+
type: :development
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '13.0'
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: minitest
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '5.0'
|
89
|
+
type: :development
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '5.0'
|
96
|
+
- !ruby/object:Gem::Dependency
|
97
|
+
name: ostruct
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '0'
|
103
|
+
type: :development
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: yard
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0.9'
|
117
|
+
type: :development
|
118
|
+
prerelease: false
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0.9'
|
124
|
+
description: A ruby gem for performing LLM-powered automated web search.
|
125
|
+
email:
|
126
|
+
- shagov.dev@outlook.com
|
127
|
+
executables: []
|
128
|
+
extensions: []
|
129
|
+
extra_rdoc_files: []
|
130
|
+
files:
|
131
|
+
- CHANGELOG.md
|
132
|
+
- LICENSE.txt
|
133
|
+
- README.md
|
134
|
+
- lib/deepsearch.rb
|
135
|
+
- lib/deepsearch/configuration.rb
|
136
|
+
- lib/deepsearch/engine.rb
|
137
|
+
- lib/deepsearch/engine/pipeline.rb
|
138
|
+
- lib/deepsearch/engine/steps/data_aggregation/parsed_website.rb
|
139
|
+
- lib/deepsearch/engine/steps/data_aggregation/process.rb
|
140
|
+
- lib/deepsearch/engine/steps/data_aggregation/result.rb
|
141
|
+
- lib/deepsearch/engine/steps/parallel_search/process.rb
|
142
|
+
- lib/deepsearch/engine/steps/parallel_search/result.rb
|
143
|
+
- lib/deepsearch/engine/steps/parallel_search/search.rb
|
144
|
+
- lib/deepsearch/engine/steps/prepare_subqueries/process.rb
|
145
|
+
- lib/deepsearch/engine/steps/prepare_subqueries/result.rb
|
146
|
+
- lib/deepsearch/engine/steps/rag/chunker.rb
|
147
|
+
- lib/deepsearch/engine/steps/rag/process.rb
|
148
|
+
- lib/deepsearch/engine/steps/rag/similarity.rb
|
149
|
+
- lib/deepsearch/engine/steps/rag/values/chunk.rb
|
150
|
+
- lib/deepsearch/engine/steps/rag/values/query.rb
|
151
|
+
- lib/deepsearch/engine/steps/rag/values/result.rb
|
152
|
+
- lib/deepsearch/engine/steps/summarization/process.rb
|
153
|
+
- lib/deepsearch/engine/steps/summarization/values/result.rb
|
154
|
+
- lib/deepsearch/logger.rb
|
155
|
+
- lib/deepsearch/prompts_config.rb
|
156
|
+
- lib/deepsearch/version.rb
|
157
|
+
- lib/search_adapters.rb
|
158
|
+
- lib/search_adapters/mock_adapter.rb
|
159
|
+
- lib/search_adapters/serper_adapter.rb
|
160
|
+
- lib/search_adapters/tavily_adapter.rb
|
161
|
+
homepage: https://github.com/alexshagov/deepsearch-rb
|
162
|
+
licenses:
|
163
|
+
- MIT
|
164
|
+
metadata:
|
165
|
+
allowed_push_host: https://rubygems.org
|
166
|
+
homepage_uri: https://github.com/alexshagov/deepsearch-rb
|
167
|
+
source_code_uri: https://github.com/alexshagov/deepsearch-rb
|
168
|
+
changelog_uri: https://github.com/alexshagov/deepsearch-rb/blob/main/CHANGELOG.md
|
169
|
+
rdoc_options: []
|
170
|
+
require_paths:
|
171
|
+
- lib
|
172
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
173
|
+
requirements:
|
174
|
+
- - ">="
|
175
|
+
- !ruby/object:Gem::Version
|
176
|
+
version: 3.2.0
|
177
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
178
|
+
requirements:
|
179
|
+
- - ">="
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: '0'
|
182
|
+
requirements: []
|
183
|
+
rubygems_version: 3.6.9
|
184
|
+
specification_version: 4
|
185
|
+
summary: A ruby gem for performing LLM-powered automated web search.
|
186
|
+
test_files: []
|