perplexity_api 0.2.1 → 0.4.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/{.env.sample → .env.example} +3 -1
- data/.gitignore +4 -0
- data/CHANGELOG.md +42 -0
- data/Gemfile.lock +5 -5
- data/README.md +124 -3
- data/examples/basic_usage.rb +25 -0
- data/examples/conversation.rb +34 -0
- data/examples/streaming.rb +26 -0
- data/examples/web_search.rb +45 -0
- data/lib/perplexity_api/client.rb +57 -9
- data/lib/perplexity_api/configuration.rb +3 -1
- data/lib/perplexity_api/models.rb +44 -0
- data/lib/perplexity_api/stream_client.rb +115 -0
- data/lib/perplexity_api/version.rb +1 -1
- data/lib/perplexity_api.rb +15 -2
- data/perplexity_api.gemspec +3 -3
- metadata +19 -10
- data/.rspec_status +0 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4f134dd6fd6ac47f11b94ed74f4a6c361de0859a73b7845c9c642769e4e340b0
|
4
|
+
data.tar.gz: b0e6576ed1173b6702d6382d2711f34b8ddae1570b2a793a19a216744b330ec1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f891118c6640903373100e66654c59c15900eb36d4ed3d7c6b428e4c624b81d9930bd6b7569f39d1255753edf58acf664e57adbf80e92efe10bf189449b2586d
|
7
|
+
data.tar.gz: '0097f6120b11a3ddde782cd0673bb4442a9c45f7e308f16363821ed9ae11b0a455191712e7c2cf8813498271d6bad826cef22e930cee822d7338c7bb44f11186'
|
data/.gitignore
CHANGED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
## [0.3.0] - 2025-01-07
|
6
|
+
|
7
|
+
### Added
|
8
|
+
- Streaming support via `StreamClient` class for real-time responses
|
9
|
+
- Web search capabilities with `search_mode` parameter (web/academic)
|
10
|
+
- Domain filtering with `search_domain_filter` (include/exclude domains)
|
11
|
+
- Date filtering with `search_after_date_filter` and `search_before_date_filter`
|
12
|
+
- Recency filtering with `search_recency_filter` (month/week/day/hour)
|
13
|
+
- Location-based search with `web_search_options`
|
14
|
+
- Support for new models: sonar-pro, sonar-deep-research
|
15
|
+
- Full conversation history support with messages array
|
16
|
+
- New parameters: `frequency_penalty` and `presence_penalty`
|
17
|
+
- Beta features: `return_images` and `return_related_questions`
|
18
|
+
- Model constants in `PerplexityApi::Models`
|
19
|
+
- Helper methods: `stream`, `stream_chat`
|
20
|
+
- Comprehensive examples directory
|
21
|
+
|
22
|
+
### Changed
|
23
|
+
- `chat` method now accepts both string and array of messages
|
24
|
+
- `chat` method accepts options parameter for per-request configuration
|
25
|
+
- Updated default configuration to include new penalty parameters
|
26
|
+
|
27
|
+
### Fixed
|
28
|
+
- Improved error handling for streaming responses
|
29
|
+
|
30
|
+
## [0.2.1] - Previous version
|
31
|
+
|
32
|
+
### Fixed
|
33
|
+
- Environment variable loading issues
|
34
|
+
- Configuration management improvements
|
35
|
+
|
36
|
+
## [0.1.0] - Initial release
|
37
|
+
|
38
|
+
### Added
|
39
|
+
- Basic chat functionality
|
40
|
+
- Configuration management
|
41
|
+
- Environment variable support
|
42
|
+
- Basic parameter support (temperature, max_tokens, top_p, top_k)
|
data/Gemfile.lock
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
perplexity_api (0.
|
4
|
+
perplexity_api (0.3.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
9
|
diff-lcs (1.6.1)
|
10
|
-
rake (
|
10
|
+
rake (13.3.0)
|
11
11
|
rspec (3.13.0)
|
12
12
|
rspec-core (~> 3.13.0)
|
13
13
|
rspec-expectations (~> 3.13.0)
|
@@ -26,10 +26,10 @@ PLATFORMS
|
|
26
26
|
ruby
|
27
27
|
|
28
28
|
DEPENDENCIES
|
29
|
-
bundler (~>
|
29
|
+
bundler (~> 2.0)
|
30
30
|
perplexity_api!
|
31
|
-
rake (~>
|
31
|
+
rake (~> 13.0)
|
32
32
|
rspec (~> 3.0)
|
33
33
|
|
34
34
|
BUNDLED WITH
|
35
|
-
|
35
|
+
2.6.9
|
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# PerplexityApi
|
2
|
-
[](https://badge.fury.io/rb/perplexity_api)
|
2
|
+
[](https://badge.fury.io/rb/perplexity_api)
|
3
3
|
[](LICENSE.txt)
|
4
4
|
|
5
5
|
A Ruby wrapper gem for Perplexity AI's API. This gem allows you to easily integrate Perplexity AI's powerful language models into your Ruby applications.
|
@@ -7,8 +7,13 @@ A Ruby wrapper gem for Perplexity AI's API. This gem allows you to easily integr
|
|
7
7
|
## Features
|
8
8
|
|
9
9
|
- API key can be configured externally
|
10
|
-
-
|
10
|
+
- Support for all latest Perplexity models including sonar-pro and sonar-deep-research
|
11
11
|
- Simple interface to send messages and get results
|
12
|
+
- Streaming support for real-time responses
|
13
|
+
- Web search capabilities with domain filtering and date filters
|
14
|
+
- Conversation history support with full messages array
|
15
|
+
- Advanced search features including location-based search
|
16
|
+
- Beta features: image results and related questions
|
12
17
|
- Options can be customized or use defaults
|
13
18
|
|
14
19
|
## Installation
|
@@ -85,6 +90,21 @@ response = PerplexityApi.chat("Hello, Perplexity AI!")
|
|
85
90
|
puts response["choices"][0]["message"]["content"]
|
86
91
|
```
|
87
92
|
|
93
|
+
### Conversation History
|
94
|
+
|
95
|
+
You can maintain conversation context by passing an array of messages:
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
messages = [
|
99
|
+
{ role: "system", content: "You are a helpful assistant." },
|
100
|
+
{ role: "user", content: "What is the capital of France?" },
|
101
|
+
{ role: "assistant", content: "The capital of France is Paris." },
|
102
|
+
{ role: "user", content: "What is its population?" }
|
103
|
+
]
|
104
|
+
|
105
|
+
response = PerplexityApi.chat(messages)
|
106
|
+
```
|
107
|
+
|
88
108
|
### Using a Client Instance
|
89
109
|
|
90
110
|
For more detailed control, you can create a client instance:
|
@@ -97,7 +117,10 @@ client = PerplexityApi.new(
|
|
97
117
|
temperature: 0.5,
|
98
118
|
max_tokens: 2048,
|
99
119
|
top_p: 0.9,
|
100
|
-
top_k: 0
|
120
|
+
top_k: 0,
|
121
|
+
frequency_penalty: 0.1,
|
122
|
+
presence_penalty: 0.1,
|
123
|
+
search_mode: "web"
|
101
124
|
}
|
102
125
|
)
|
103
126
|
|
@@ -105,8 +128,106 @@ response = client.chat("Enter your complex question here...")
|
|
105
128
|
puts response["choices"][0]["message"]["content"]
|
106
129
|
```
|
107
130
|
|
131
|
+
### Streaming Responses
|
132
|
+
|
133
|
+
For real-time streaming responses:
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
PerplexityApi.stream_chat("Tell me about Ruby programming") do |chunk|
|
137
|
+
print chunk["choices"][0]["delta"]["content"] if chunk["choices"][0]["delta"]["content"]
|
138
|
+
end
|
139
|
+
```
|
140
|
+
|
141
|
+
Or with a client instance:
|
142
|
+
|
143
|
+
```ruby
|
144
|
+
client = PerplexityApi.stream
|
145
|
+
client.chat("Explain quantum computing") do |chunk|
|
146
|
+
# Process each chunk as it arrives
|
147
|
+
end
|
148
|
+
```
|
149
|
+
|
150
|
+
### Web Search Features
|
151
|
+
|
152
|
+
Enable web search with specific filters:
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
response = PerplexityApi.chat(
|
156
|
+
"What are the latest developments in AI?",
|
157
|
+
options: {
|
158
|
+
search_mode: "web",
|
159
|
+
search_domain_filter: ["arxiv.org", "nature.com", "-reddit.com"],
|
160
|
+
search_recency_filter: "week"
|
161
|
+
}
|
162
|
+
)
|
163
|
+
```
|
164
|
+
|
165
|
+
### Advanced Search Options
|
166
|
+
|
167
|
+
Use date filters and location-based search:
|
168
|
+
|
169
|
+
```ruby
|
170
|
+
response = PerplexityApi.chat(
|
171
|
+
"Find research papers on climate change",
|
172
|
+
options: {
|
173
|
+
search_mode: "academic",
|
174
|
+
search_after_date_filter: "01/01/2024",
|
175
|
+
search_before_date_filter: "12/31/2024",
|
176
|
+
web_search_options: {
|
177
|
+
search_context_size: "high",
|
178
|
+
user_location: {
|
179
|
+
country: "US",
|
180
|
+
latitude: 37.7749,
|
181
|
+
longitude: -122.4194
|
182
|
+
}
|
183
|
+
}
|
184
|
+
}
|
185
|
+
)
|
186
|
+
```
|
187
|
+
|
188
|
+
### Beta Features
|
189
|
+
|
190
|
+
Enable image results and related questions (closed beta):
|
191
|
+
|
192
|
+
```ruby
|
193
|
+
response = PerplexityApi.chat(
|
194
|
+
"Show me images of the Eiffel Tower",
|
195
|
+
options: {
|
196
|
+
return_images: true,
|
197
|
+
return_related_questions: true
|
198
|
+
}
|
199
|
+
)
|
200
|
+
|
201
|
+
# Access images if available
|
202
|
+
if response["images"]
|
203
|
+
response["images"].each do |image|
|
204
|
+
puts "Image URL: #{image["url"]}"
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
# Access related questions
|
209
|
+
if response["related_questions"]
|
210
|
+
puts "Related questions:"
|
211
|
+
response["related_questions"].each { |q| puts "- #{q}" }
|
212
|
+
end
|
213
|
+
```
|
214
|
+
|
108
215
|
## Models
|
109
216
|
|
217
|
+
The gem includes constants for all available models:
|
218
|
+
|
219
|
+
```ruby
|
220
|
+
# Current Sonar Models
|
221
|
+
PerplexityApi::Models::SONAR # "sonar"
|
222
|
+
PerplexityApi::Models::SONAR_PRO # "sonar-pro"
|
223
|
+
PerplexityApi::Models::SONAR_DEEP_RESEARCH # "sonar-deep-research"
|
224
|
+
|
225
|
+
# Other Models
|
226
|
+
PerplexityApi::Models::LLAMA_3_1_70B_INSTRUCT # "llama-3.1-70b-instruct"
|
227
|
+
PerplexityApi::Models::MISTRAL_7B # "mistral-7b"
|
228
|
+
PerplexityApi::Models::CODELLAMA_34B # "codellama-34b"
|
229
|
+
```
|
230
|
+
|
110
231
|
For the most up-to-date list of models, refer to the [Perplexity AI official documentation](https://docs.perplexity.ai/guides/model-cards).
|
111
232
|
|
112
233
|
## Development
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'perplexity_api'
|
2
|
+
|
3
|
+
# Basic chat
|
4
|
+
puts "=== Basic Chat ==="
|
5
|
+
response = PerplexityApi.chat("What is Ruby programming language?")
|
6
|
+
puts response["choices"][0]["message"]["content"]
|
7
|
+
puts
|
8
|
+
|
9
|
+
# Chat with options
|
10
|
+
puts "=== Chat with Custom Options ==="
|
11
|
+
response = PerplexityApi.chat(
|
12
|
+
"Tell me about the benefits of Ruby",
|
13
|
+
options: {
|
14
|
+
temperature: 0.3,
|
15
|
+
max_tokens: 500
|
16
|
+
}
|
17
|
+
)
|
18
|
+
puts response["choices"][0]["message"]["content"]
|
19
|
+
puts
|
20
|
+
|
21
|
+
# Using specific model
|
22
|
+
puts "=== Using Sonar Pro Model ==="
|
23
|
+
client = PerplexityApi.new(model: PerplexityApi::Models::SONAR_PRO)
|
24
|
+
response = client.chat("What are the latest features in Ruby 3.3?")
|
25
|
+
puts response["choices"][0]["message"]["content"]
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'perplexity_api'
|
2
|
+
|
3
|
+
# Multi-turn conversation
|
4
|
+
puts "=== Multi-turn Conversation ==="
|
5
|
+
|
6
|
+
messages = [
|
7
|
+
{ role: "system", content: "You are a helpful Ruby programming expert." },
|
8
|
+
{ role: "user", content: "What is a Ruby gem?" }
|
9
|
+
]
|
10
|
+
|
11
|
+
# First response
|
12
|
+
response = PerplexityApi.chat(messages)
|
13
|
+
assistant_message = response["choices"][0]["message"]["content"]
|
14
|
+
puts "User: What is a Ruby gem?"
|
15
|
+
puts "Assistant: #{assistant_message}"
|
16
|
+
puts
|
17
|
+
|
18
|
+
# Add assistant response to conversation
|
19
|
+
messages << { role: "assistant", content: assistant_message }
|
20
|
+
messages << { role: "user", content: "How do I create my own gem?" }
|
21
|
+
|
22
|
+
# Second response
|
23
|
+
response = PerplexityApi.chat(messages)
|
24
|
+
puts "User: How do I create my own gem?"
|
25
|
+
puts "Assistant: #{response["choices"][0]["message"]["content"]}"
|
26
|
+
puts
|
27
|
+
|
28
|
+
# Continue the conversation
|
29
|
+
messages << { role: "assistant", content: response["choices"][0]["message"]["content"] }
|
30
|
+
messages << { role: "user", content: "What about publishing it to RubyGems?" }
|
31
|
+
|
32
|
+
response = PerplexityApi.chat(messages)
|
33
|
+
puts "User: What about publishing it to RubyGems?"
|
34
|
+
puts "Assistant: #{response["choices"][0]["message"]["content"]}"
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'perplexity_api'
|
2
|
+
|
3
|
+
# Basic streaming
|
4
|
+
puts "=== Basic Streaming ==="
|
5
|
+
PerplexityApi.stream_chat("Write a haiku about programming") do |chunk|
|
6
|
+
if chunk["choices"] && chunk["choices"][0]["delta"]["content"]
|
7
|
+
print chunk["choices"][0]["delta"]["content"]
|
8
|
+
end
|
9
|
+
end
|
10
|
+
puts "\n"
|
11
|
+
|
12
|
+
# Streaming with web search
|
13
|
+
puts "\n=== Streaming with Web Search ==="
|
14
|
+
client = PerplexityApi.stream(
|
15
|
+
options: {
|
16
|
+
search_mode: "web",
|
17
|
+
search_recency_filter: "day"
|
18
|
+
}
|
19
|
+
)
|
20
|
+
|
21
|
+
client.chat("What happened in tech news today?") do |chunk|
|
22
|
+
if chunk["choices"] && chunk["choices"][0]["delta"]["content"]
|
23
|
+
print chunk["choices"][0]["delta"]["content"]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
puts "\n"
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'perplexity_api'
|
2
|
+
|
3
|
+
# Web search with domain filtering
|
4
|
+
puts "=== Web Search with Domain Filtering ==="
|
5
|
+
response = PerplexityApi.chat(
|
6
|
+
"What are the latest AI research papers?",
|
7
|
+
options: {
|
8
|
+
search_mode: "web",
|
9
|
+
search_domain_filter: ["arxiv.org", "openai.com", "-reddit.com"],
|
10
|
+
search_recency_filter: "week"
|
11
|
+
}
|
12
|
+
)
|
13
|
+
puts response["choices"][0]["message"]["content"]
|
14
|
+
puts
|
15
|
+
|
16
|
+
# Academic search
|
17
|
+
puts "=== Academic Search ==="
|
18
|
+
response = PerplexityApi.chat(
|
19
|
+
"Find recent studies on climate change mitigation",
|
20
|
+
options: {
|
21
|
+
search_mode: "academic",
|
22
|
+
search_after_date_filter: "01/01/2024",
|
23
|
+
search_before_date_filter: "12/31/2024"
|
24
|
+
}
|
25
|
+
)
|
26
|
+
puts response["choices"][0]["message"]["content"]
|
27
|
+
puts
|
28
|
+
|
29
|
+
# Location-based search
|
30
|
+
puts "=== Location-based Search ==="
|
31
|
+
response = PerplexityApi.chat(
|
32
|
+
"What are the best restaurants near me?",
|
33
|
+
options: {
|
34
|
+
search_mode: "web",
|
35
|
+
web_search_options: {
|
36
|
+
search_context_size: "high",
|
37
|
+
user_location: {
|
38
|
+
country: "US",
|
39
|
+
latitude: 37.7749,
|
40
|
+
longitude: -122.4194
|
41
|
+
}
|
42
|
+
}
|
43
|
+
}
|
44
|
+
)
|
45
|
+
puts response["choices"][0]["message"]["content"]
|
@@ -14,9 +14,12 @@ module PerplexityApi
|
|
14
14
|
end
|
15
15
|
|
16
16
|
# Method to send a message and get a response
|
17
|
-
def chat(
|
17
|
+
def chat(messages, options = {})
|
18
18
|
@config.validate!
|
19
19
|
|
20
|
+
messages = prepare_messages(messages)
|
21
|
+
merged_options = @options.merge(options)
|
22
|
+
|
20
23
|
uri = URI.parse("#{@config.api_base}/chat/completions")
|
21
24
|
http = Net::HTTP.new(uri.host, uri.port)
|
22
25
|
http.use_ssl = true
|
@@ -25,14 +28,8 @@ module PerplexityApi
|
|
25
28
|
request["Content-Type"] = "application/json"
|
26
29
|
request["Authorization"] = "Bearer #{@config.api_key}"
|
27
30
|
|
28
|
-
|
29
|
-
|
30
|
-
messages: [{ role: "user", content: message }],
|
31
|
-
temperature: @options[:temperature],
|
32
|
-
max_tokens: @options[:max_tokens],
|
33
|
-
top_p: @options[:top_p],
|
34
|
-
top_k: @options[:top_k]
|
35
|
-
}.to_json
|
31
|
+
request_body = build_request_body(messages, merged_options)
|
32
|
+
request.body = request_body.to_json
|
36
33
|
|
37
34
|
response = http.request(request)
|
38
35
|
|
@@ -42,5 +39,56 @@ module PerplexityApi
|
|
42
39
|
raise Error, "API call failed: #{response.code} #{response.body}"
|
43
40
|
end
|
44
41
|
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def prepare_messages(messages)
|
46
|
+
case messages
|
47
|
+
when String
|
48
|
+
[{ role: "user", content: messages }]
|
49
|
+
when Array
|
50
|
+
messages
|
51
|
+
else
|
52
|
+
raise ArgumentError, "Messages must be a string or array"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def build_request_body(messages, options)
|
57
|
+
body = {
|
58
|
+
model: @model,
|
59
|
+
messages: messages
|
60
|
+
}
|
61
|
+
|
62
|
+
# Basic parameters
|
63
|
+
body[:temperature] = options[:temperature] if options[:temperature]
|
64
|
+
body[:max_tokens] = options[:max_tokens] if options[:max_tokens]
|
65
|
+
body[:top_p] = options[:top_p] if options[:top_p]
|
66
|
+
body[:top_k] = options[:top_k] if options[:top_k]
|
67
|
+
body[:frequency_penalty] = options[:frequency_penalty] if options[:frequency_penalty]
|
68
|
+
body[:presence_penalty] = options[:presence_penalty] if options[:presence_penalty]
|
69
|
+
body[:stream] = options[:stream] if options.key?(:stream)
|
70
|
+
|
71
|
+
# Search parameters
|
72
|
+
body[:search_mode] = options[:search_mode] if options[:search_mode]
|
73
|
+
body[:search_domain_filter] = options[:search_domain_filter] if options[:search_domain_filter]
|
74
|
+
body[:search_recency_filter] = options[:search_recency_filter] if options[:search_recency_filter]
|
75
|
+
body[:search_after_date_filter] = options[:search_after_date_filter] if options[:search_after_date_filter]
|
76
|
+
body[:search_before_date_filter] = options[:search_before_date_filter] if options[:search_before_date_filter]
|
77
|
+
body[:last_updated_after_filter] = options[:last_updated_after_filter] if options[:last_updated_after_filter]
|
78
|
+
body[:last_updated_before_filter] = options[:last_updated_before_filter] if options[:last_updated_before_filter]
|
79
|
+
|
80
|
+
# Beta features
|
81
|
+
body[:return_images] = options[:return_images] if options[:return_images]
|
82
|
+
body[:return_related_questions] = options[:return_related_questions] if options[:return_related_questions]
|
83
|
+
|
84
|
+
# Advanced features
|
85
|
+
body[:reasoning_effort] = options[:reasoning_effort] if options[:reasoning_effort]
|
86
|
+
|
87
|
+
if options[:web_search_options]
|
88
|
+
body[:web_search_options] = options[:web_search_options]
|
89
|
+
end
|
90
|
+
|
91
|
+
body
|
92
|
+
end
|
45
93
|
end
|
46
94
|
end
|
@@ -17,7 +17,9 @@ module PerplexityApi
|
|
17
17
|
temperature: ENV["PERPLEXITY_TEMPERATURE"] ? ENV["PERPLEXITY_TEMPERATURE"].to_f : 0.7,
|
18
18
|
max_tokens: ENV["PERPLEXITY_MAX_TOKENS"] ? ENV["PERPLEXITY_MAX_TOKENS"].to_i : 1024,
|
19
19
|
top_p: ENV["PERPLEXITY_TOP_P"] ? ENV["PERPLEXITY_TOP_P"].to_f : 0.9,
|
20
|
-
top_k: ENV["PERPLEXITY_TOP_K"] ? ENV["PERPLEXITY_TOP_K"].to_i : 0
|
20
|
+
top_k: ENV["PERPLEXITY_TOP_K"] ? ENV["PERPLEXITY_TOP_K"].to_i : 0,
|
21
|
+
frequency_penalty: ENV["PERPLEXITY_FREQUENCY_PENALTY"] ? ENV["PERPLEXITY_FREQUENCY_PENALTY"].to_f : 0.0,
|
22
|
+
presence_penalty: ENV["PERPLEXITY_PRESENCE_PENALTY"] ? ENV["PERPLEXITY_PRESENCE_PENALTY"].to_f : 0.0
|
21
23
|
}
|
22
24
|
|
23
25
|
debug_log "Configuration loaded from environment variables"
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module PerplexityApi
|
2
|
+
module Models
|
3
|
+
# Current Sonar Models
|
4
|
+
SONAR = "sonar"
|
5
|
+
SONAR_PRO = "sonar-pro"
|
6
|
+
SONAR_DEEP_RESEARCH = "sonar-deep-research"
|
7
|
+
|
8
|
+
# Legacy Models
|
9
|
+
LLAMA_3_1_SONAR_SMALL_128K_CHAT = "llama-3.1-sonar-small-128k-chat"
|
10
|
+
LLAMA_3_1_SONAR_LARGE_128K_CHAT = "llama-3.1-sonar-large-128k-chat"
|
11
|
+
LLAMA_3_1_70B_INSTRUCT = "llama-3.1-70b-instruct"
|
12
|
+
LLAMA_3_1_8B_INSTRUCT = "llama-3.1-8b-instruct"
|
13
|
+
MISTRAL_7B = "mistral-7b"
|
14
|
+
CODELLAMA_34B = "codellama-34b"
|
15
|
+
LLAMA_2_70B = "llama-2-70b"
|
16
|
+
|
17
|
+
# Model Groups
|
18
|
+
SONAR_MODELS = [SONAR, SONAR_PRO, SONAR_DEEP_RESEARCH].freeze
|
19
|
+
LLAMA_MODELS = [
|
20
|
+
LLAMA_3_1_SONAR_SMALL_128K_CHAT,
|
21
|
+
LLAMA_3_1_SONAR_LARGE_128K_CHAT,
|
22
|
+
LLAMA_3_1_70B_INSTRUCT,
|
23
|
+
LLAMA_3_1_8B_INSTRUCT,
|
24
|
+
LLAMA_2_70B
|
25
|
+
].freeze
|
26
|
+
|
27
|
+
ALL_MODELS = (SONAR_MODELS + LLAMA_MODELS + [MISTRAL_7B, CODELLAMA_34B]).freeze
|
28
|
+
|
29
|
+
# Search modes
|
30
|
+
SEARCH_MODE_WEB = "web"
|
31
|
+
SEARCH_MODE_ACADEMIC = "academic"
|
32
|
+
|
33
|
+
# Search recency filters
|
34
|
+
RECENCY_MONTH = "month"
|
35
|
+
RECENCY_WEEK = "week"
|
36
|
+
RECENCY_DAY = "day"
|
37
|
+
RECENCY_HOUR = "hour"
|
38
|
+
|
39
|
+
# Search context sizes
|
40
|
+
CONTEXT_SIZE_LOW = "low"
|
41
|
+
CONTEXT_SIZE_MEDIUM = "medium"
|
42
|
+
CONTEXT_SIZE_HIGH = "high"
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'uri'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module PerplexityApi
|
6
|
+
class StreamClient
|
7
|
+
attr_reader :config
|
8
|
+
|
9
|
+
def initialize(api_key: nil, model: nil, options: {})
|
10
|
+
@config = PerplexityApi.configuration.dup
|
11
|
+
@config.api_key = api_key if api_key != nil
|
12
|
+
@model = model || @config.default_model
|
13
|
+
@options = @config.default_options.merge(options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def chat(messages, &block)
|
17
|
+
@config.validate!
|
18
|
+
|
19
|
+
messages = prepare_messages(messages)
|
20
|
+
|
21
|
+
uri = URI.parse("#{@config.api_base}/chat/completions")
|
22
|
+
|
23
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
|
24
|
+
request = Net::HTTP::Post.new(uri.path)
|
25
|
+
request["Content-Type"] = "application/json"
|
26
|
+
request["Authorization"] = "Bearer #{@config.api_key}"
|
27
|
+
request["Accept"] = "text/event-stream"
|
28
|
+
request["Cache-Control"] = "no-cache"
|
29
|
+
|
30
|
+
request_body = build_request_body(messages)
|
31
|
+
request_body[:stream] = true
|
32
|
+
request.body = request_body.to_json
|
33
|
+
|
34
|
+
http.request(request) do |response|
|
35
|
+
if response.code.to_i != 200
|
36
|
+
raise Error, "API call failed: #{response.code} #{response.read_body}"
|
37
|
+
end
|
38
|
+
|
39
|
+
buffer = ""
|
40
|
+
response.read_body do |chunk|
|
41
|
+
buffer += chunk
|
42
|
+
|
43
|
+
while (line_end = buffer.index("\n"))
|
44
|
+
line = buffer[0...line_end]
|
45
|
+
buffer = buffer[(line_end + 1)..-1]
|
46
|
+
|
47
|
+
next if line.strip.empty?
|
48
|
+
next unless line.start_with?("data: ")
|
49
|
+
|
50
|
+
data = line[6..-1].strip
|
51
|
+
next if data == "[DONE]"
|
52
|
+
|
53
|
+
begin
|
54
|
+
parsed = JSON.parse(data)
|
55
|
+
block.call(parsed) if block_given?
|
56
|
+
rescue JSON::ParserError => e
|
57
|
+
# Skip invalid JSON
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def prepare_messages(messages)
|
68
|
+
case messages
|
69
|
+
when String
|
70
|
+
[{ role: "user", content: messages }]
|
71
|
+
when Array
|
72
|
+
messages
|
73
|
+
else
|
74
|
+
raise ArgumentError, "Messages must be a string or array"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def build_request_body(messages)
|
79
|
+
body = {
|
80
|
+
model: @model,
|
81
|
+
messages: messages
|
82
|
+
}
|
83
|
+
|
84
|
+
# Basic parameters
|
85
|
+
body[:temperature] = @options[:temperature] if @options[:temperature]
|
86
|
+
body[:max_tokens] = @options[:max_tokens] if @options[:max_tokens]
|
87
|
+
body[:top_p] = @options[:top_p] if @options[:top_p]
|
88
|
+
body[:top_k] = @options[:top_k] if @options[:top_k]
|
89
|
+
body[:frequency_penalty] = @options[:frequency_penalty] if @options[:frequency_penalty]
|
90
|
+
body[:presence_penalty] = @options[:presence_penalty] if @options[:presence_penalty]
|
91
|
+
|
92
|
+
# Search parameters
|
93
|
+
body[:search_mode] = @options[:search_mode] if @options[:search_mode]
|
94
|
+
body[:search_domain_filter] = @options[:search_domain_filter] if @options[:search_domain_filter]
|
95
|
+
body[:search_recency_filter] = @options[:search_recency_filter] if @options[:search_recency_filter]
|
96
|
+
body[:search_after_date_filter] = @options[:search_after_date_filter] if @options[:search_after_date_filter]
|
97
|
+
body[:search_before_date_filter] = @options[:search_before_date_filter] if @options[:search_before_date_filter]
|
98
|
+
body[:last_updated_after_filter] = @options[:last_updated_after_filter] if @options[:last_updated_after_filter]
|
99
|
+
body[:last_updated_before_filter] = @options[:last_updated_before_filter] if @options[:last_updated_before_filter]
|
100
|
+
|
101
|
+
# Beta features
|
102
|
+
body[:return_images] = @options[:return_images] if @options[:return_images]
|
103
|
+
body[:return_related_questions] = @options[:return_related_questions] if @options[:return_related_questions]
|
104
|
+
|
105
|
+
# Advanced features
|
106
|
+
body[:reasoning_effort] = @options[:reasoning_effort] if @options[:reasoning_effort]
|
107
|
+
|
108
|
+
if @options[:web_search_options]
|
109
|
+
body[:web_search_options] = @options[:web_search_options]
|
110
|
+
end
|
111
|
+
|
112
|
+
body
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
data/lib/perplexity_api.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require "perplexity_api/version"
|
2
2
|
require "perplexity_api/configuration"
|
3
|
+
require "perplexity_api/models"
|
3
4
|
require "perplexity_api/client"
|
5
|
+
require "perplexity_api/stream_client"
|
4
6
|
|
5
7
|
module PerplexityApi
|
6
8
|
class Error < StandardError; end
|
@@ -11,9 +13,20 @@ module PerplexityApi
|
|
11
13
|
end
|
12
14
|
|
13
15
|
# Helper method to directly send a message
|
14
|
-
def self.chat(
|
16
|
+
def self.chat(messages, api_key: nil, model: nil, options: {})
|
15
17
|
client = Client.new(api_key: api_key, model: model, options: options)
|
16
|
-
client.chat(
|
18
|
+
client.chat(messages, options)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Helper method to create a stream client instance
|
22
|
+
def self.stream(api_key: nil, model: nil, options: {})
|
23
|
+
StreamClient.new(api_key: api_key, model: model, options: options)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Helper method to directly stream a message
|
27
|
+
def self.stream_chat(messages, api_key: nil, model: nil, options: {}, &block)
|
28
|
+
client = StreamClient.new(api_key: api_key, model: model, options: options)
|
29
|
+
client.chat(messages, &block)
|
17
30
|
end
|
18
31
|
|
19
32
|
# Helper method to get available models
|
data/perplexity_api.gemspec
CHANGED
@@ -35,9 +35,9 @@ Gem::Specification.new do |spec|
|
|
35
35
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
36
36
|
spec.require_paths = ["lib"]
|
37
37
|
|
38
|
-
spec.add_development_dependency "bundler", "~>
|
39
|
-
spec.add_development_dependency "rake", "~>
|
38
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
39
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
40
40
|
spec.add_development_dependency "rspec", "~> 3.0"
|
41
41
|
|
42
|
-
spec.required_ruby_version = ">=
|
42
|
+
spec.required_ruby_version = ">= 3.1.0"
|
43
43
|
end
|
metadata
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: perplexity_api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hisafumi Kikkawa
|
8
|
+
autorequire:
|
8
9
|
bindir: exe
|
9
10
|
cert_chain: []
|
10
|
-
date: 2025-
|
11
|
+
date: 2025-07-07 00:00:00.000000000 Z
|
11
12
|
dependencies:
|
12
13
|
- !ruby/object:Gem::Dependency
|
13
14
|
name: bundler
|
@@ -15,28 +16,28 @@ dependencies:
|
|
15
16
|
requirements:
|
16
17
|
- - "~>"
|
17
18
|
- !ruby/object:Gem::Version
|
18
|
-
version: '
|
19
|
+
version: '2.0'
|
19
20
|
type: :development
|
20
21
|
prerelease: false
|
21
22
|
version_requirements: !ruby/object:Gem::Requirement
|
22
23
|
requirements:
|
23
24
|
- - "~>"
|
24
25
|
- !ruby/object:Gem::Version
|
25
|
-
version: '
|
26
|
+
version: '2.0'
|
26
27
|
- !ruby/object:Gem::Dependency
|
27
28
|
name: rake
|
28
29
|
requirement: !ruby/object:Gem::Requirement
|
29
30
|
requirements:
|
30
31
|
- - "~>"
|
31
32
|
- !ruby/object:Gem::Version
|
32
|
-
version: '
|
33
|
+
version: '13.0'
|
33
34
|
type: :development
|
34
35
|
prerelease: false
|
35
36
|
version_requirements: !ruby/object:Gem::Requirement
|
36
37
|
requirements:
|
37
38
|
- - "~>"
|
38
39
|
- !ruby/object:Gem::Version
|
39
|
-
version: '
|
40
|
+
version: '13.0'
|
40
41
|
- !ruby/object:Gem::Dependency
|
41
42
|
name: rspec
|
42
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -59,11 +60,11 @@ executables: []
|
|
59
60
|
extensions: []
|
60
61
|
extra_rdoc_files: []
|
61
62
|
files:
|
62
|
-
- ".env.
|
63
|
+
- ".env.example"
|
63
64
|
- ".gitignore"
|
64
65
|
- ".rspec"
|
65
|
-
- ".rspec_status"
|
66
66
|
- ".travis.yml"
|
67
|
+
- CHANGELOG.md
|
67
68
|
- CODE_OF_CONDUCT.md
|
68
69
|
- Gemfile
|
69
70
|
- Gemfile.lock
|
@@ -73,9 +74,15 @@ files:
|
|
73
74
|
- Rakefile
|
74
75
|
- bin/console
|
75
76
|
- bin/setup
|
77
|
+
- examples/basic_usage.rb
|
78
|
+
- examples/conversation.rb
|
79
|
+
- examples/streaming.rb
|
80
|
+
- examples/web_search.rb
|
76
81
|
- lib/perplexity_api.rb
|
77
82
|
- lib/perplexity_api/client.rb
|
78
83
|
- lib/perplexity_api/configuration.rb
|
84
|
+
- lib/perplexity_api/models.rb
|
85
|
+
- lib/perplexity_api/stream_client.rb
|
79
86
|
- lib/perplexity_api/version.rb
|
80
87
|
- perplexity_api.gemspec
|
81
88
|
homepage: https://github.com/hisafumi-kikkawa/perplexity_api
|
@@ -86,6 +93,7 @@ metadata:
|
|
86
93
|
homepage_uri: https://github.com/hisafumi-kikkawa/perplexity_api
|
87
94
|
source_code_uri: https://github.com/hisafumi-kikkawa/perplexity_api
|
88
95
|
changelog_uri: https://github.com/hisafumi-kikkawa/perplexity_api/blob/master/CHANGELOG.md
|
96
|
+
post_install_message:
|
89
97
|
rdoc_options: []
|
90
98
|
require_paths:
|
91
99
|
- lib
|
@@ -93,14 +101,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
93
101
|
requirements:
|
94
102
|
- - ">="
|
95
103
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
104
|
+
version: 3.1.0
|
97
105
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
106
|
requirements:
|
99
107
|
- - ">="
|
100
108
|
- !ruby/object:Gem::Version
|
101
109
|
version: '0'
|
102
110
|
requirements: []
|
103
|
-
rubygems_version: 3.
|
111
|
+
rubygems_version: 3.3.3
|
112
|
+
signing_key:
|
104
113
|
specification_version: 4
|
105
114
|
summary: Ruby wrapper for Perplexity API
|
106
115
|
test_files: []
|
data/.rspec_status
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
example_id | status | run_time |
|
2
|
-
---------------------------------------------------- | ------ | --------------- |
|
3
|
-
./spec/perplexity_api/client_spec.rb[1:1:1:1] | passed | 0.0228 seconds |
|
4
|
-
./spec/perplexity_api/client_spec.rb[1:1:2:1] | passed | 0.0001 seconds |
|
5
|
-
./spec/perplexity_api/client_spec.rb[1:2:1] | passed | 0.00063 seconds |
|
6
|
-
./spec/perplexity_api/client_spec.rb[1:2:2] | passed | 0.00161 seconds |
|
7
|
-
./spec/perplexity_api/client_spec.rb[1:2:3:1] | passed | 0.0053 seconds |
|
8
|
-
./spec/perplexity_api/client_spec.rb[1:2:4:1] | passed | 0.00045 seconds |
|
9
|
-
./spec/perplexity_api/configuration_spec.rb[1:1:1:1] | passed | 0.0081 seconds |
|
10
|
-
./spec/perplexity_api/configuration_spec.rb[1:1:2:1] | passed | 0.00034 seconds |
|
11
|
-
./spec/perplexity_api/configuration_spec.rb[1:1:3:1] | passed | 0.00192 seconds |
|
12
|
-
./spec/perplexity_api/configuration_spec.rb[1:2:1] | passed | 0.00362 seconds |
|
13
|
-
./spec/perplexity_api/configuration_spec.rb[1:3:1:1] | passed | 0.00084 seconds |
|
14
|
-
./spec/perplexity_api/configuration_spec.rb[1:3:2:1] | passed | 0.00007 seconds |
|
15
|
-
./spec/perplexity_api/configuration_spec.rb[2:1:1] | passed | 0.00073 seconds |
|
16
|
-
./spec/perplexity_api/configuration_spec.rb[2:1:2] | passed | 0.00006 seconds |
|
17
|
-
./spec/perplexity_api/configuration_spec.rb[2:2:1] | passed | 0.00739 seconds |
|
18
|
-
./spec/perplexity_api/configuration_spec.rb[2:2:2] | passed | 0.0001 seconds |
|
19
|
-
./spec/perplexity_api/configuration_spec.rb[2:3:1] | passed | 0.00102 seconds |
|
20
|
-
./spec/perplexity_api_spec.rb[1:1] | passed | 0.00011 seconds |
|
21
|
-
./spec/perplexity_api_spec.rb[1:2:1] | passed | 0.00011 seconds |
|
22
|
-
./spec/perplexity_api_spec.rb[1:3:1] | passed | 0.00017 seconds |
|
23
|
-
./spec/perplexity_api_spec.rb[1:3:2] | passed | 0.00013 seconds |
|
24
|
-
./spec/perplexity_api_spec.rb[1:4:1:1] | passed | 0.00151 seconds |
|