prescient 0.7.0 → 0.8.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 +4 -4
- data/.rubocop.yml +21 -268
- data/CHANGELOG.md +37 -0
- data/INTEGRATION_GUIDE.md +7 -1
- data/README.md +210 -1
- data/Steepfile +12 -12
- data/db/migrate/001_create_prescient_tables.rb +15 -16
- data/examples/README.md +2 -1
- data/examples/custom_contexts.rb +4 -4
- data/exe/prescient +2 -2
- data/exe/prescient-mcp +7 -0
- data/lib/prescient/agent/audit_log.rb +37 -0
- data/lib/prescient/agent/cli_adapter.rb +29 -0
- data/lib/prescient/agent/configuration.rb +57 -0
- data/lib/prescient/agent/context.rb +56 -0
- data/lib/prescient/agent/error_serializer.rb +47 -0
- data/lib/prescient/agent/errors.rb +25 -0
- data/lib/prescient/agent/parser.rb +49 -0
- data/lib/prescient/agent/prompt_builder.rb +31 -0
- data/lib/prescient/agent/result.rb +36 -0
- data/lib/prescient/agent/runtime.rb +175 -0
- data/lib/prescient/agent/schema_validator.rb +215 -0
- data/lib/prescient/agent/tool_registry.rb +89 -0
- data/lib/prescient/agent.rb +22 -0
- data/lib/prescient/api.rb +337 -274
- data/lib/prescient/base.rb +370 -372
- data/lib/prescient/cli.rb +586 -526
- data/lib/prescient/client.rb +7 -6
- data/lib/prescient/configuration_loader.rb +492 -488
- data/lib/prescient/document_source.rb +114 -0
- data/lib/prescient/errors.rb +1 -3
- data/lib/prescient/mcp/authentication.rb +39 -0
- data/lib/prescient/mcp/configuration.rb +38 -0
- data/lib/prescient/mcp/rack.rb +243 -0
- data/lib/prescient/mcp/server.rb +202 -0
- data/lib/prescient/mcp/stdio.rb +42 -0
- data/lib/prescient/mcp.rb +8 -0
- data/lib/prescient/pgvector.rb +193 -189
- data/lib/prescient/provider/anthropic.rb +129 -125
- data/lib/prescient/provider/deepseek.rb +122 -118
- data/lib/prescient/provider/gemini.rb +153 -149
- data/lib/prescient/provider/huggingface.rb +191 -187
- data/lib/prescient/provider/mistral.rb +151 -147
- data/lib/prescient/provider/ollama.rb +168 -165
- data/lib/prescient/provider/openai.rb +174 -169
- data/lib/prescient/provider/xai.rb +122 -118
- data/lib/prescient/tool/search_api.rb +125 -121
- data/lib/prescient/tool/searxng.rb +123 -119
- data/lib/prescient/tool.rb +100 -98
- data/lib/prescient/version.rb +1 -1
- data/lib/prescient.rb +68 -62
- data/sig/prescient.rbs +176 -1
- metadata +23 -1
|
@@ -1,147 +1,151 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "httparty"
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
module Prescient
|
|
6
|
+
module Provider
|
|
7
|
+
# Anthropic Messages API provider adapter.
|
|
8
|
+
class Anthropic < Prescient::Base
|
|
9
|
+
include HTTParty
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
base_uri "https://api.anthropic.com"
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
def initialize(**options)
|
|
14
|
+
super
|
|
15
|
+
self.class.default_timeout(@options[:timeout] || 60)
|
|
16
|
+
end
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
# Anthropic does not provide embeddings through this adapter.
|
|
19
|
+
#
|
|
20
|
+
# @raise [Prescient::Error] Always, because Anthropic embeddings are not supported
|
|
21
|
+
def generate_embedding(_text, **_options)
|
|
22
|
+
# Anthropic doesn't provide embedding API, raise error
|
|
23
|
+
raise Prescient::Error,
|
|
24
|
+
"Anthropic provider does not support embeddings. Use OpenAI or HuggingFace for embeddings."
|
|
25
|
+
end
|
|
24
26
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
},
|
|
39
|
-
body: {
|
|
40
|
-
model: options[:model] || @options[:model],
|
|
41
|
-
max_tokens: options[:max_tokens] || 2000,
|
|
42
|
-
temperature: options[:temperature] || 0.7,
|
|
43
|
-
messages: [
|
|
44
|
-
{
|
|
45
|
-
role: 'user',
|
|
46
|
-
content: formatted_prompt,
|
|
27
|
+
# Generate a response using Anthropic's Messages API.
|
|
28
|
+
# @param prompt [String] Prompt to send
|
|
29
|
+
# @param context_items [Array<Hash, String>] Optional context items
|
|
30
|
+
# @return [Hash] Normalized response data
|
|
31
|
+
def generate_response(prompt, context_items = [], **options)
|
|
32
|
+
handle_errors do
|
|
33
|
+
formatted_prompt = build_prompt(prompt, context_items)
|
|
34
|
+
|
|
35
|
+
response = self.class.post("/v1/messages",
|
|
36
|
+
headers: {
|
|
37
|
+
"Content-Type" => "application/json",
|
|
38
|
+
"x-api-key" => @options[:api_key],
|
|
39
|
+
"anthropic-version" => "2023-06-01"
|
|
47
40
|
},
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
41
|
+
body: {
|
|
42
|
+
model: options[:model] || @options[:model],
|
|
43
|
+
max_tokens: options[:max_tokens] || 2000,
|
|
44
|
+
temperature: options[:temperature] || 0.7,
|
|
45
|
+
messages: [
|
|
46
|
+
{
|
|
47
|
+
role: "user",
|
|
48
|
+
content: formatted_prompt
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
}.to_json)
|
|
52
|
+
|
|
53
|
+
validate_response!(response, "text generation")
|
|
54
|
+
|
|
55
|
+
content = response.parsed_response.dig("content", 0, "text")
|
|
56
|
+
raise Prescient::InvalidResponseError, "No response generated" unless content
|
|
57
|
+
|
|
58
|
+
{
|
|
59
|
+
response: content.strip,
|
|
60
|
+
model: options[:model] || @options[:model],
|
|
61
|
+
provider: "anthropic",
|
|
62
|
+
processing_time: nil,
|
|
63
|
+
metadata: {
|
|
64
|
+
usage: response.parsed_response["usage"]
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
end
|
|
68
|
+
end
|
|
67
69
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
70
|
+
# Check Anthropic API availability using the non-generating `/v1/models` endpoint.
|
|
71
|
+
#
|
|
72
|
+
# `reachable` indicates the API answered successfully. `ready` indicates that
|
|
73
|
+
# the configured model appears in the returned model list.
|
|
74
|
+
#
|
|
75
|
+
# @return [Hash] Provider health information
|
|
76
|
+
def health_check
|
|
77
|
+
handle_errors do
|
|
78
|
+
response = self.class.get("/v1/models", headers: api_headers)
|
|
79
|
+
|
|
80
|
+
if response.success?
|
|
81
|
+
models = response.parsed_response["data"] || []
|
|
82
|
+
model_available = models.any? { |model| model["id"] == @options[:model] }
|
|
83
|
+
{
|
|
84
|
+
status: "healthy",
|
|
85
|
+
provider: "anthropic",
|
|
86
|
+
reachable: true,
|
|
87
|
+
models_available: models.map { |model| model["id"] },
|
|
88
|
+
model: { name: @options[:model], available: model_available },
|
|
89
|
+
ready: model_available
|
|
90
|
+
}
|
|
91
|
+
else
|
|
92
|
+
{
|
|
93
|
+
status: "unhealthy", provider: "anthropic", reachable: true,
|
|
94
|
+
error: "HTTP #{response.code}", message: response.message, ready: false
|
|
95
|
+
}
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
rescue Prescient::Error => e
|
|
90
99
|
{
|
|
91
|
-
status:
|
|
92
|
-
|
|
100
|
+
status: "unavailable",
|
|
101
|
+
provider: "anthropic",
|
|
102
|
+
reachable: false,
|
|
103
|
+
error: e.class.name,
|
|
104
|
+
message: e.message,
|
|
105
|
+
ready: false
|
|
93
106
|
}
|
|
94
107
|
end
|
|
95
|
-
end
|
|
96
|
-
rescue Prescient::Error => e
|
|
97
|
-
{
|
|
98
|
-
status: 'unavailable',
|
|
99
|
-
provider: 'anthropic',
|
|
100
|
-
reachable: false,
|
|
101
|
-
error: e.class.name,
|
|
102
|
-
message: e.message,
|
|
103
|
-
ready: false,
|
|
104
|
-
}
|
|
105
|
-
end
|
|
106
108
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
109
|
+
# Return models available to the configured Anthropic account.
|
|
110
|
+
# @return [Array<Hash>] Model descriptors
|
|
111
|
+
def list_models
|
|
112
|
+
handle_errors do
|
|
113
|
+
response = self.class.get("/v1/models", headers: api_headers)
|
|
114
|
+
validate_response!(response, "model listing")
|
|
115
|
+
|
|
116
|
+
(response.parsed_response["data"] || []).map do |model|
|
|
117
|
+
{
|
|
118
|
+
name: model["id"],
|
|
119
|
+
type: "text",
|
|
120
|
+
display_name: model["display_name"],
|
|
121
|
+
created_at: model["created_at"],
|
|
122
|
+
max_input_tokens: model["max_input_tokens"],
|
|
123
|
+
max_tokens: model["max_tokens"]
|
|
124
|
+
}.compact
|
|
125
|
+
end
|
|
126
|
+
end
|
|
123
127
|
end
|
|
124
|
-
end
|
|
125
|
-
end
|
|
126
128
|
|
|
127
|
-
|
|
129
|
+
protected
|
|
128
130
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
131
|
+
def validate_configuration!
|
|
132
|
+
required_options = %i[api_key model]
|
|
133
|
+
missing_options = required_options.select { |opt| @options[opt].nil? }
|
|
132
134
|
|
|
133
|
-
|
|
135
|
+
return unless missing_options.any?
|
|
134
136
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
+
raise Prescient::Error, "Missing required options: #{missing_options.join(", ")}"
|
|
138
|
+
end
|
|
137
139
|
|
|
138
|
-
|
|
140
|
+
private
|
|
139
141
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
142
|
+
def api_headers
|
|
143
|
+
{
|
|
144
|
+
"Content-Type" => "application/json",
|
|
145
|
+
"x-api-key" => @options[:api_key],
|
|
146
|
+
"anthropic-version" => "2023-06-01"
|
|
147
|
+
}
|
|
148
|
+
end
|
|
149
|
+
end
|
|
146
150
|
end
|
|
147
151
|
end
|
|
@@ -1,139 +1,143 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "httparty"
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
module Prescient
|
|
6
|
+
module Provider
|
|
7
|
+
# DeepSeek API provider adapter.
|
|
8
|
+
class DeepSeek < Prescient::Base
|
|
9
|
+
include HTTParty
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
base_uri "https://api.deepseek.com"
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
# DeepSeek does not currently provide an embeddings endpoint.
|
|
18
|
-
# @raise [Prescient::Error] Always, because embeddings are unsupported
|
|
19
|
-
def generate_embedding(_text, **_options)
|
|
20
|
-
raise Prescient::Error, 'DeepSeek provider does not support embeddings.'
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
# Generate a response through DeepSeek's OpenAI-compatible chat API.
|
|
24
|
-
# @param prompt [String] Prompt to send
|
|
25
|
-
# @param context_items [Array<Hash, String>] Optional context items
|
|
26
|
-
# @return [Hash] Normalized response data
|
|
27
|
-
def generate_response(prompt, context_items = [], **options)
|
|
28
|
-
handle_errors do
|
|
29
|
-
model = options[:model] || @options[:chat_model]
|
|
30
|
-
response = self.class.post(
|
|
31
|
-
'/chat/completions',
|
|
32
|
-
headers: api_headers,
|
|
33
|
-
body: {
|
|
34
|
-
model: model,
|
|
35
|
-
messages: [{ role: 'user', content: build_prompt(prompt, context_items) }],
|
|
36
|
-
max_tokens: options[:max_tokens] || 2000,
|
|
37
|
-
temperature: options[:temperature] || 0.7,
|
|
38
|
-
top_p: options[:top_p] || 0.9,
|
|
39
|
-
}.to_json,
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
validate_response!(response, 'text generation')
|
|
43
|
-
|
|
44
|
-
parsed_response = response.parsed_response
|
|
45
|
-
content = parsed_response.dig('choices', 0, 'message', 'content')
|
|
46
|
-
raise Prescient::InvalidResponseError, 'No response generated' unless content.is_a?(String) && !content.empty?
|
|
47
|
-
|
|
48
|
-
{
|
|
49
|
-
response: content.strip,
|
|
50
|
-
model: model,
|
|
51
|
-
provider: 'deepseek',
|
|
52
|
-
processing_time: nil,
|
|
53
|
-
metadata: {
|
|
54
|
-
usage: parsed_response['usage'],
|
|
55
|
-
finish_reason: parsed_response.dig('choices', 0, 'finish_reason'),
|
|
56
|
-
},
|
|
57
|
-
}
|
|
58
|
-
end
|
|
59
|
-
end
|
|
13
|
+
def initialize(**options)
|
|
14
|
+
super
|
|
15
|
+
@provider_name = "DeepSeek"
|
|
16
|
+
self.class.default_timeout(@options[:timeout] || 60)
|
|
17
|
+
end
|
|
60
18
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
19
|
+
# DeepSeek does not currently provide an embeddings endpoint.
|
|
20
|
+
# @raise [Prescient::Error] Always, because embeddings are unsupported
|
|
21
|
+
def generate_embedding(_text, **_options)
|
|
22
|
+
raise Prescient::Error, "DeepSeek provider does not support embeddings."
|
|
23
|
+
end
|
|
66
24
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
25
|
+
# Generate a response through DeepSeek's OpenAI-compatible chat API.
|
|
26
|
+
# @param prompt [String] Prompt to send
|
|
27
|
+
# @param context_items [Array<Hash, String>] Optional context items
|
|
28
|
+
# @return [Hash] Normalized response data
|
|
29
|
+
def generate_response(prompt, context_items = [], **options)
|
|
30
|
+
handle_errors do
|
|
31
|
+
model = options[:model] || @options[:chat_model]
|
|
32
|
+
response = self.class.post(
|
|
33
|
+
"/chat/completions",
|
|
34
|
+
headers: api_headers,
|
|
35
|
+
body: {
|
|
36
|
+
model: model,
|
|
37
|
+
messages: [{ role: "user", content: build_prompt(prompt, context_items) }],
|
|
38
|
+
max_tokens: options[:max_tokens] || 2000,
|
|
39
|
+
temperature: options[:temperature] || 0.7,
|
|
40
|
+
top_p: options[:top_p] || 0.9
|
|
41
|
+
}.to_json
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
validate_response!(response, "text generation")
|
|
45
|
+
|
|
46
|
+
parsed_response = response.parsed_response
|
|
47
|
+
content = parsed_response.dig("choices", 0, "message", "content")
|
|
48
|
+
raise Prescient::InvalidResponseError, "No response generated" unless content.is_a?(String) && !content.empty?
|
|
49
|
+
|
|
50
|
+
{
|
|
51
|
+
response: content.strip,
|
|
52
|
+
model: model,
|
|
53
|
+
provider: "deepseek",
|
|
54
|
+
processing_time: nil,
|
|
55
|
+
metadata: {
|
|
56
|
+
usage: parsed_response["usage"],
|
|
57
|
+
finish_reason: parsed_response.dig("choices", 0, "finish_reason")
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
end
|
|
61
|
+
end
|
|
70
62
|
|
|
63
|
+
# Check whether the configured DeepSeek model is available.
|
|
64
|
+
# @return [Hash] Provider health information
|
|
65
|
+
def health_check
|
|
66
|
+
handle_errors do
|
|
67
|
+
response = self.class.get("/models", headers: api_headers)
|
|
68
|
+
|
|
69
|
+
if response.success?
|
|
70
|
+
models = response.parsed_response["data"] || []
|
|
71
|
+
model_available = models.any? { |model| model["id"] == @options[:chat_model] }
|
|
72
|
+
|
|
73
|
+
{
|
|
74
|
+
status: "healthy",
|
|
75
|
+
provider: "deepseek",
|
|
76
|
+
reachable: true,
|
|
77
|
+
models_available: models.map { |model| model["id"] },
|
|
78
|
+
chat_model: { name: @options[:chat_model], available: model_available },
|
|
79
|
+
ready: model_available
|
|
80
|
+
}
|
|
81
|
+
else
|
|
82
|
+
{
|
|
83
|
+
status: "unhealthy",
|
|
84
|
+
provider: "deepseek",
|
|
85
|
+
reachable: true,
|
|
86
|
+
error: "HTTP #{response.code}",
|
|
87
|
+
message: response.message,
|
|
88
|
+
ready: false
|
|
89
|
+
}
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
rescue Prescient::Error => e
|
|
71
93
|
{
|
|
72
|
-
status:
|
|
73
|
-
provider:
|
|
74
|
-
reachable:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
ready:
|
|
78
|
-
}
|
|
79
|
-
else
|
|
80
|
-
{
|
|
81
|
-
status: 'unhealthy',
|
|
82
|
-
provider: 'deepseek',
|
|
83
|
-
reachable: true,
|
|
84
|
-
error: "HTTP #{response.code}",
|
|
85
|
-
message: response.message,
|
|
86
|
-
ready: false,
|
|
94
|
+
status: "unavailable",
|
|
95
|
+
provider: "deepseek",
|
|
96
|
+
reachable: false,
|
|
97
|
+
error: e.class.name,
|
|
98
|
+
message: e.message,
|
|
99
|
+
ready: false
|
|
87
100
|
}
|
|
88
101
|
end
|
|
89
|
-
end
|
|
90
|
-
rescue Prescient::Error => e
|
|
91
|
-
{
|
|
92
|
-
status: 'unavailable',
|
|
93
|
-
provider: 'deepseek',
|
|
94
|
-
reachable: false,
|
|
95
|
-
error: e.class.name,
|
|
96
|
-
message: e.message,
|
|
97
|
-
ready: false,
|
|
98
|
-
}
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
# List models available to the configured DeepSeek API key.
|
|
102
|
-
# @return [Array<Hash>] Model descriptors
|
|
103
|
-
def list_models
|
|
104
|
-
handle_errors do
|
|
105
|
-
response = self.class.get('/models', headers: api_headers)
|
|
106
|
-
validate_response!(response, 'model listing')
|
|
107
102
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
103
|
+
# List models available to the configured DeepSeek API key.
|
|
104
|
+
# @return [Array<Hash>] Model descriptors
|
|
105
|
+
def list_models
|
|
106
|
+
handle_errors do
|
|
107
|
+
response = self.class.get("/models", headers: api_headers)
|
|
108
|
+
validate_response!(response, "model listing")
|
|
109
|
+
|
|
110
|
+
(response.parsed_response["data"] || []).map do |model|
|
|
111
|
+
{
|
|
112
|
+
name: model["id"],
|
|
113
|
+
object: model["object"],
|
|
114
|
+
created: model["created"],
|
|
115
|
+
owned_by: model["owned_by"],
|
|
116
|
+
permission: model["permission"]
|
|
117
|
+
}.compact
|
|
118
|
+
end
|
|
119
|
+
end
|
|
116
120
|
end
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
121
|
|
|
120
|
-
|
|
122
|
+
protected
|
|
121
123
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
124
|
+
def validate_configuration!
|
|
125
|
+
required_options = %i[api_key chat_model]
|
|
126
|
+
missing_options = required_options.select { |option| @options[option].nil? }
|
|
125
127
|
|
|
126
|
-
|
|
128
|
+
return unless missing_options.any?
|
|
127
129
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
+
raise Prescient::Error, "Missing required options: #{missing_options.join(", ")}"
|
|
131
|
+
end
|
|
130
132
|
|
|
131
|
-
|
|
133
|
+
private
|
|
132
134
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
135
|
+
def api_headers
|
|
136
|
+
{
|
|
137
|
+
"Content-Type" => "application/json",
|
|
138
|
+
"Authorization" => "Bearer #{@options[:api_key]}"
|
|
139
|
+
}
|
|
140
|
+
end
|
|
141
|
+
end
|
|
138
142
|
end
|
|
139
143
|
end
|