semantic_pen 1.0.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/LICENSE +21 -0
- data/README.md +319 -0
- data/examples/basic_usage.rb +53 -0
- data/examples/bulk_generation.rb +101 -0
- data/examples/configuration_example.rb +111 -0
- data/examples/polling_example.rb +51 -0
- data/lib/semantic_pen/client.rb +89 -0
- data/lib/semantic_pen/configuration.rb +17 -0
- data/lib/semantic_pen/errors.rb +50 -0
- data/lib/semantic_pen/models.rb +123 -0
- data/lib/semantic_pen/version.rb +5 -0
- data/lib/semantic_pen.rb +18 -0
- data/semantic_pen.gemspec +33 -0
- metadata +130 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1b2846b64e0b8d626bf54c2da6590fd0aa0d18151be75064e6965d5b16184937
|
4
|
+
data.tar.gz: 88366157f967caa712db22d45cab9c6096484b01f4837be3a28e1fec6ebb130e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cbcd7a114c6b0a453e6dbe3c13e0bb4f503fb2c547245844108b95591e6dc4a25e61d1f45ca641951358cef2a293a64a18c53ff4a5dbe525934fdcdfdc5a4b58
|
7
|
+
data.tar.gz: db7bd1a601fda864616da2f1ea51a057e01909d934f4084c3a8f8790b419b6ff82b5270e5f773656ff18e2c5e0d144141cc3332dc4b2643414c5a908e9fc6f54
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2025 SemanticPen Team
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,319 @@
|
|
1
|
+
# SemanticPen Ruby SDK
|
2
|
+
|
3
|
+
Official Ruby SDK for the SemanticPen API - AI-powered content generation made simple.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'semantic_pen'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
```bash
|
16
|
+
bundle install
|
17
|
+
```
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
```bash
|
22
|
+
gem install semantic_pen
|
23
|
+
```
|
24
|
+
|
25
|
+
## Quick Start
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
require 'semantic_pen'
|
29
|
+
|
30
|
+
# Initialize the client
|
31
|
+
client = SemanticPen::Client.new(api_key: 'your-api-key')
|
32
|
+
|
33
|
+
# Generate an article
|
34
|
+
response = client.generate_article('AI in healthcare', project_name: 'Medical Blog')
|
35
|
+
|
36
|
+
if response.success? && response.has_article_ids?
|
37
|
+
article_id = response.first_article_id
|
38
|
+
|
39
|
+
# Check article status
|
40
|
+
article = client.get_article(article_id)
|
41
|
+
puts "Status: #{article.status} (#{article.progress}% complete)"
|
42
|
+
end
|
43
|
+
```
|
44
|
+
|
45
|
+
## Configuration
|
46
|
+
|
47
|
+
### Basic Configuration
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
client = SemanticPen::Client.new(api_key: 'your-api-key')
|
51
|
+
```
|
52
|
+
|
53
|
+
### Custom Configuration
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
client = SemanticPen::Client.new(
|
57
|
+
api_key: 'your-api-key',
|
58
|
+
base_url: 'https://api.semanticpen.com',
|
59
|
+
timeout: 120 # 2 minutes
|
60
|
+
)
|
61
|
+
```
|
62
|
+
|
63
|
+
### Global Configuration
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
SemanticPen.configure do |config|
|
67
|
+
config.api_key = 'your-api-key'
|
68
|
+
config.base_url = 'https://www.semanticpen.com'
|
69
|
+
config.timeout = 60
|
70
|
+
end
|
71
|
+
```
|
72
|
+
|
73
|
+
### Environment Variables
|
74
|
+
|
75
|
+
You can also configure the SDK using environment variables:
|
76
|
+
|
77
|
+
```bash
|
78
|
+
export SEMANTIC_PEN_API_KEY=your-api-key
|
79
|
+
export SEMANTIC_PEN_BASE_URL=https://www.semanticpen.com
|
80
|
+
```
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
client = SemanticPen::Client.new(
|
84
|
+
api_key: ENV['SEMANTIC_PEN_API_KEY'],
|
85
|
+
base_url: ENV['SEMANTIC_PEN_BASE_URL']
|
86
|
+
)
|
87
|
+
```
|
88
|
+
|
89
|
+
## API Methods
|
90
|
+
|
91
|
+
### Generate Article
|
92
|
+
|
93
|
+
Generate a new article with AI-powered content:
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
response = client.generate_article('target keyword', project_name: 'My Project')
|
97
|
+
```
|
98
|
+
|
99
|
+
**Parameters:**
|
100
|
+
- `target_keyword` (required): The main keyword for the article
|
101
|
+
- `project_name` (optional): Project name for organization
|
102
|
+
|
103
|
+
**Returns:** `SemanticPen::Models::GenerateArticleResponse`
|
104
|
+
|
105
|
+
### Get Article Status
|
106
|
+
|
107
|
+
Retrieve the current status and content of an article:
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
article = client.get_article('article-id')
|
111
|
+
```
|
112
|
+
|
113
|
+
**Parameters:**
|
114
|
+
- `article_id` (required): The ID of the article to retrieve
|
115
|
+
|
116
|
+
**Returns:** `SemanticPen::Models::Article`
|
117
|
+
|
118
|
+
## Models
|
119
|
+
|
120
|
+
### Article
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
article = client.get_article('article-id')
|
124
|
+
|
125
|
+
# Properties
|
126
|
+
article.id # Article ID
|
127
|
+
article.title # Article title
|
128
|
+
article.content # Article content
|
129
|
+
article.status # Current status
|
130
|
+
article.progress # Progress percentage (0-100)
|
131
|
+
article.target_keyword # Target keyword
|
132
|
+
article.project_name # Project name
|
133
|
+
article.created_at # Creation timestamp
|
134
|
+
article.updated_at # Last update timestamp
|
135
|
+
|
136
|
+
# Status methods
|
137
|
+
article.completed? # true if status is 'completed'
|
138
|
+
article.in_progress? # true if status is 'in_progress' or 'processing'
|
139
|
+
article.failed? # true if status is 'failed' or 'error'
|
140
|
+
|
141
|
+
# Convert to hash
|
142
|
+
article.to_h
|
143
|
+
```
|
144
|
+
|
145
|
+
### Generate Article Response
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
response = client.generate_article('keyword')
|
149
|
+
|
150
|
+
# Properties
|
151
|
+
response.message # Response message
|
152
|
+
response.success # Boolean success status
|
153
|
+
response.article_ids # Array of generated article IDs
|
154
|
+
|
155
|
+
# Methods
|
156
|
+
response.success? # true if generation was successful
|
157
|
+
response.has_article_ids? # true if article IDs are present
|
158
|
+
response.first_article_id # First article ID (convenience method)
|
159
|
+
|
160
|
+
# Convert to hash
|
161
|
+
response.to_h
|
162
|
+
```
|
163
|
+
|
164
|
+
## Error Handling
|
165
|
+
|
166
|
+
The SDK provides specific exception types for different error scenarios:
|
167
|
+
|
168
|
+
```ruby
|
169
|
+
begin
|
170
|
+
response = client.generate_article('keyword')
|
171
|
+
rescue SemanticPen::ValidationError => e
|
172
|
+
puts "Validation Error: #{e.message}"
|
173
|
+
rescue SemanticPen::AuthenticationError => e
|
174
|
+
puts "Authentication Error: #{e.message}"
|
175
|
+
rescue SemanticPen::NotFoundError => e
|
176
|
+
puts "Not Found Error: #{e.message}"
|
177
|
+
rescue SemanticPen::RateLimitError => e
|
178
|
+
puts "Rate Limit Error: #{e.message}"
|
179
|
+
rescue SemanticPen::NetworkError => e
|
180
|
+
puts "Network Error: #{e.message}"
|
181
|
+
rescue SemanticPen::ApiError => e
|
182
|
+
puts "API Error: #{e.message} (HTTP #{e.http_status_code})"
|
183
|
+
rescue SemanticPen::Error => e
|
184
|
+
puts "SemanticPen Error: #{e.message}"
|
185
|
+
end
|
186
|
+
```
|
187
|
+
|
188
|
+
### Error Types
|
189
|
+
|
190
|
+
- `SemanticPen::ValidationError` - Invalid input parameters
|
191
|
+
- `SemanticPen::AuthenticationError` - Invalid API key or authentication issues
|
192
|
+
- `SemanticPen::NotFoundError` - Requested resource not found
|
193
|
+
- `SemanticPen::RateLimitError` - Rate limit exceeded
|
194
|
+
- `SemanticPen::NetworkError` - Network connectivity issues
|
195
|
+
- `SemanticPen::ApiError` - General API errors
|
196
|
+
- `SemanticPen::Error` - Base error class
|
197
|
+
|
198
|
+
## Advanced Usage
|
199
|
+
|
200
|
+
### Polling for Completion
|
201
|
+
|
202
|
+
Use the built-in `StatusPoller` to wait for article completion:
|
203
|
+
|
204
|
+
```ruby
|
205
|
+
response = client.generate_article('AI trends 2025')
|
206
|
+
|
207
|
+
if response.success? && response.has_article_ids?
|
208
|
+
article_id = response.first_article_id
|
209
|
+
|
210
|
+
# Poll for completion
|
211
|
+
poller = SemanticPen::Models::StatusPoller.new(
|
212
|
+
client,
|
213
|
+
article_id,
|
214
|
+
max_attempts: 60, # Maximum polling attempts
|
215
|
+
delay: 5 # Delay between attempts (seconds)
|
216
|
+
)
|
217
|
+
|
218
|
+
article = poller.wait_for_completion
|
219
|
+
|
220
|
+
if article.completed?
|
221
|
+
puts "Article ready: #{article.title}"
|
222
|
+
else
|
223
|
+
puts "Article failed to complete"
|
224
|
+
end
|
225
|
+
end
|
226
|
+
```
|
227
|
+
|
228
|
+
### Bulk Generation
|
229
|
+
|
230
|
+
Generate multiple articles efficiently:
|
231
|
+
|
232
|
+
```ruby
|
233
|
+
keywords = ['AI in healthcare', 'machine learning', 'data science']
|
234
|
+
articles = []
|
235
|
+
|
236
|
+
keywords.each do |keyword|
|
237
|
+
begin
|
238
|
+
response = client.generate_article(keyword, project_name: 'Tech Blog')
|
239
|
+
articles << response.first_article_id if response.success?
|
240
|
+
sleep(1) # Rate limiting
|
241
|
+
rescue SemanticPen::Error => e
|
242
|
+
puts "Failed to generate article for #{keyword}: #{e.message}"
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
# Check status of all articles
|
247
|
+
articles.each do |article_id|
|
248
|
+
article = client.get_article(article_id)
|
249
|
+
puts "#{article.target_keyword}: #{article.status} (#{article.progress}%)"
|
250
|
+
end
|
251
|
+
```
|
252
|
+
|
253
|
+
## Examples
|
254
|
+
|
255
|
+
The `examples/` directory contains comprehensive usage examples:
|
256
|
+
|
257
|
+
- `basic_usage.rb` - Simple article generation and status checking
|
258
|
+
- `polling_example.rb` - Using StatusPoller for completion waiting
|
259
|
+
- `bulk_generation.rb` - Generating multiple articles efficiently
|
260
|
+
- `configuration_example.rb` - Various configuration options and error handling
|
261
|
+
|
262
|
+
To run an example:
|
263
|
+
|
264
|
+
```bash
|
265
|
+
cd examples
|
266
|
+
ruby basic_usage.rb
|
267
|
+
```
|
268
|
+
|
269
|
+
## Requirements
|
270
|
+
|
271
|
+
- Ruby 2.6.0 or higher
|
272
|
+
- `faraday` gem for HTTP requests
|
273
|
+
- `json` gem for JSON parsing
|
274
|
+
|
275
|
+
## Development
|
276
|
+
|
277
|
+
After checking out the repo, run:
|
278
|
+
|
279
|
+
```bash
|
280
|
+
bundle install
|
281
|
+
```
|
282
|
+
|
283
|
+
To run tests:
|
284
|
+
|
285
|
+
```bash
|
286
|
+
bundle exec rspec
|
287
|
+
```
|
288
|
+
|
289
|
+
To run RuboCop for code style:
|
290
|
+
|
291
|
+
```bash
|
292
|
+
bundle exec rubocop
|
293
|
+
```
|
294
|
+
|
295
|
+
## Contributing
|
296
|
+
|
297
|
+
1. Fork the repository
|
298
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
299
|
+
3. Commit your changes (`git commit -am 'Add amazing feature'`)
|
300
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
301
|
+
5. Open a Pull Request
|
302
|
+
|
303
|
+
## Support
|
304
|
+
|
305
|
+
For support and questions, please visit [SemanticPen API Documentation](https://www.semanticpen.com/api-documentation).
|
306
|
+
|
307
|
+
## License
|
308
|
+
|
309
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
310
|
+
|
311
|
+
## Changelog
|
312
|
+
|
313
|
+
### 1.0.0 (2025-01-19)
|
314
|
+
|
315
|
+
- Initial release
|
316
|
+
- Basic article generation and retrieval
|
317
|
+
- Comprehensive error handling
|
318
|
+
- Built-in status polling
|
319
|
+
- Full test suite and documentation
|
@@ -0,0 +1,53 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require_relative '../lib/semantic_pen'
|
5
|
+
|
6
|
+
begin
|
7
|
+
# Initialize the client with your API key
|
8
|
+
client = SemanticPen::Client.new(api_key: 'your-api-key-here')
|
9
|
+
|
10
|
+
# Generate an article
|
11
|
+
puts 'Generating article...'
|
12
|
+
response = client.generate_article('AI in healthcare', project_name: 'Medical Blog')
|
13
|
+
|
14
|
+
if response.success? && response.has_article_ids?
|
15
|
+
article_id = response.first_article_id
|
16
|
+
puts "Article generation started. ID: #{article_id}"
|
17
|
+
puts "Message: #{response.message}"
|
18
|
+
|
19
|
+
# Check article status
|
20
|
+
puts "\nChecking article status..."
|
21
|
+
article = client.get_article(article_id)
|
22
|
+
puts "Status: #{article.status} (#{article.progress}% complete)"
|
23
|
+
puts "Target Keyword: #{article.target_keyword}"
|
24
|
+
puts "Project: #{article.project_name}"
|
25
|
+
|
26
|
+
if article.completed?
|
27
|
+
puts "\nArticle completed!"
|
28
|
+
puts "Title: #{article.title}" if article.title
|
29
|
+
puts "Content preview: #{article.content[0..200]}..." if article.content
|
30
|
+
elsif article.in_progress?
|
31
|
+
puts "\nArticle is still being generated..."
|
32
|
+
elsif article.failed?
|
33
|
+
puts "\nArticle generation failed."
|
34
|
+
end
|
35
|
+
else
|
36
|
+
puts "Failed to generate article: #{response.message}"
|
37
|
+
end
|
38
|
+
|
39
|
+
rescue SemanticPen::ValidationError => e
|
40
|
+
puts "Validation Error: #{e.message}"
|
41
|
+
rescue SemanticPen::AuthenticationError => e
|
42
|
+
puts "Authentication Error: #{e.message}"
|
43
|
+
rescue SemanticPen::NotFoundError => e
|
44
|
+
puts "Not Found Error: #{e.message}"
|
45
|
+
rescue SemanticPen::NetworkError => e
|
46
|
+
puts "Network Error: #{e.message}"
|
47
|
+
rescue SemanticPen::ApiError => e
|
48
|
+
puts "API Error: #{e.message} (HTTP #{e.http_status_code})"
|
49
|
+
rescue SemanticPen::Error => e
|
50
|
+
puts "SemanticPen Error: #{e.message}"
|
51
|
+
rescue StandardError => e
|
52
|
+
puts "Unexpected Error: #{e.message}"
|
53
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require_relative '../lib/semantic_pen'
|
5
|
+
|
6
|
+
begin
|
7
|
+
# Initialize the client
|
8
|
+
client = SemanticPen::Client.new(api_key: 'your-api-key-here')
|
9
|
+
|
10
|
+
# Define keywords to generate articles for
|
11
|
+
keywords = [
|
12
|
+
'artificial intelligence trends 2025',
|
13
|
+
'sustainable energy solutions',
|
14
|
+
'remote work productivity tips',
|
15
|
+
'cybersecurity best practices',
|
16
|
+
'blockchain technology applications'
|
17
|
+
]
|
18
|
+
|
19
|
+
project_name = 'Tech Blog Batch'
|
20
|
+
generated_articles = []
|
21
|
+
|
22
|
+
puts "Starting bulk generation for #{keywords.length} articles..."
|
23
|
+
|
24
|
+
# Generate all articles
|
25
|
+
keywords.each_with_index do |keyword, index|
|
26
|
+
puts "\n#{index + 1}/#{keywords.length}: Generating article for '#{keyword}'"
|
27
|
+
|
28
|
+
begin
|
29
|
+
response = client.generate_article(keyword, project_name: project_name)
|
30
|
+
|
31
|
+
if response.success? && response.has_article_ids?
|
32
|
+
article_id = response.first_article_id
|
33
|
+
generated_articles << { keyword: keyword, id: article_id }
|
34
|
+
puts "✅ Started generation (ID: #{article_id})"
|
35
|
+
else
|
36
|
+
puts "❌ Failed: #{response.message}"
|
37
|
+
end
|
38
|
+
rescue SemanticPen::Error => e
|
39
|
+
puts "❌ Error generating article for '#{keyword}': #{e.message}"
|
40
|
+
end
|
41
|
+
|
42
|
+
# Small delay to avoid hitting rate limits
|
43
|
+
sleep(1) unless index == keywords.length - 1
|
44
|
+
end
|
45
|
+
|
46
|
+
if generated_articles.empty?
|
47
|
+
puts "\nNo articles were successfully started."
|
48
|
+
exit
|
49
|
+
end
|
50
|
+
|
51
|
+
puts "\n#{generated_articles.length} articles started. Checking status..."
|
52
|
+
|
53
|
+
# Check status of all generated articles
|
54
|
+
generated_articles.each do |article_info|
|
55
|
+
begin
|
56
|
+
article = client.get_article(article_info[:id])
|
57
|
+
status_emoji = case article.status
|
58
|
+
when 'completed' then '✅'
|
59
|
+
when 'failed', 'error' then '❌'
|
60
|
+
else '⏳'
|
61
|
+
end
|
62
|
+
|
63
|
+
puts "#{status_emoji} #{article_info[:keyword]}: #{article.status} (#{article.progress}%)"
|
64
|
+
rescue SemanticPen::Error => e
|
65
|
+
puts "❌ Error checking #{article_info[:keyword]}: #{e.message}"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Wait for completion of all articles (simplified polling)
|
70
|
+
puts "\nWaiting for articles to complete..."
|
71
|
+
completed_count = 0
|
72
|
+
max_wait_minutes = 10
|
73
|
+
|
74
|
+
(1..max_wait_minutes).each do |minute|
|
75
|
+
puts "\nChecking progress (minute #{minute}/#{max_wait_minutes})..."
|
76
|
+
completed_count = 0
|
77
|
+
|
78
|
+
generated_articles.each do |article_info|
|
79
|
+
begin
|
80
|
+
article = client.get_article(article_info[:id])
|
81
|
+
completed_count += 1 if article.completed?
|
82
|
+
rescue SemanticPen::Error
|
83
|
+
# Article might still be processing
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
puts "#{completed_count}/#{generated_articles.length} articles completed"
|
88
|
+
|
89
|
+
break if completed_count == generated_articles.length
|
90
|
+
|
91
|
+
sleep(60) # Wait 1 minute before checking again
|
92
|
+
end
|
93
|
+
|
94
|
+
puts "\nFinal summary:"
|
95
|
+
puts "#{completed_count}/#{generated_articles.length} articles completed"
|
96
|
+
|
97
|
+
rescue SemanticPen::Error => e
|
98
|
+
puts "SemanticPen Error: #{e.message}"
|
99
|
+
rescue StandardError => e
|
100
|
+
puts "Unexpected Error: #{e.message}"
|
101
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require_relative '../lib/semantic_pen'
|
5
|
+
|
6
|
+
begin
|
7
|
+
# Example 1: Basic configuration
|
8
|
+
puts "=== Basic Configuration ==="
|
9
|
+
client = SemanticPen::Client.new(api_key: 'your-api-key-here')
|
10
|
+
puts "Client initialized with default settings"
|
11
|
+
|
12
|
+
# Example 2: Custom configuration
|
13
|
+
puts "\n=== Custom Configuration ==="
|
14
|
+
custom_client = SemanticPen::Client.new(
|
15
|
+
api_key: 'your-api-key-here',
|
16
|
+
base_url: 'https://api.semanticpen.com', # Custom base URL
|
17
|
+
timeout: 120 # 2 minute timeout
|
18
|
+
)
|
19
|
+
puts "Client initialized with custom base URL and timeout"
|
20
|
+
|
21
|
+
# Example 3: Global configuration (alternative approach)
|
22
|
+
puts "\n=== Global Configuration ==="
|
23
|
+
SemanticPen.configure do |config|
|
24
|
+
config.api_key = 'your-api-key-here'
|
25
|
+
config.base_url = 'https://www.semanticpen.com'
|
26
|
+
config.timeout = 90
|
27
|
+
end
|
28
|
+
puts "Global configuration set"
|
29
|
+
|
30
|
+
# Example 4: Environment-based configuration
|
31
|
+
puts "\n=== Environment-based Configuration ==="
|
32
|
+
api_key = ENV['SEMANTIC_PEN_API_KEY'] || 'your-api-key-here'
|
33
|
+
base_url = ENV['SEMANTIC_PEN_BASE_URL'] || 'https://www.semanticpen.com'
|
34
|
+
|
35
|
+
env_client = SemanticPen::Client.new(
|
36
|
+
api_key: api_key,
|
37
|
+
base_url: base_url
|
38
|
+
)
|
39
|
+
puts "Client configured from environment variables"
|
40
|
+
|
41
|
+
# Example 5: Comprehensive error handling
|
42
|
+
puts "\n=== Error Handling Examples ==="
|
43
|
+
|
44
|
+
# Test with invalid API key
|
45
|
+
begin
|
46
|
+
invalid_client = SemanticPen::Client.new(api_key: 'invalid-key')
|
47
|
+
invalid_client.generate_article('test keyword')
|
48
|
+
rescue SemanticPen::AuthenticationError => e
|
49
|
+
puts "✅ Caught authentication error: #{e.message}"
|
50
|
+
end
|
51
|
+
|
52
|
+
# Test with empty keyword
|
53
|
+
begin
|
54
|
+
client.generate_article('')
|
55
|
+
rescue SemanticPen::ValidationError => e
|
56
|
+
puts "✅ Caught validation error: #{e.message}"
|
57
|
+
end
|
58
|
+
|
59
|
+
# Test with invalid article ID
|
60
|
+
begin
|
61
|
+
client.get_article('non-existent-id')
|
62
|
+
rescue SemanticPen::NotFoundError => e
|
63
|
+
puts "✅ Caught not found error: #{e.message}"
|
64
|
+
end
|
65
|
+
|
66
|
+
# Example 6: Working with responses
|
67
|
+
puts "\n=== Response Handling ==="
|
68
|
+
|
69
|
+
# This would work with a valid API key
|
70
|
+
# response = client.generate_article('ruby programming')
|
71
|
+
#
|
72
|
+
# puts "Response success: #{response.success?}"
|
73
|
+
# puts "Message: #{response.message}"
|
74
|
+
# puts "Article IDs: #{response.article_ids}"
|
75
|
+
# puts "Has article IDs: #{response.has_article_ids?}"
|
76
|
+
# puts "First article ID: #{response.first_article_id}" if response.has_article_ids?
|
77
|
+
|
78
|
+
puts "\n=== Article Model Examples ==="
|
79
|
+
|
80
|
+
# Example article data structure
|
81
|
+
sample_article_data = {
|
82
|
+
'id' => 'article-123',
|
83
|
+
'title' => 'Introduction to Ruby Programming',
|
84
|
+
'content' => 'Ruby is a dynamic, open source programming language...',
|
85
|
+
'status' => 'completed',
|
86
|
+
'progress' => 100,
|
87
|
+
'target_keyword' => 'ruby programming',
|
88
|
+
'project_name' => 'Tech Blog',
|
89
|
+
'created_at' => '2025-01-01T10:00:00Z',
|
90
|
+
'updated_at' => '2025-01-01T10:30:00Z'
|
91
|
+
}
|
92
|
+
|
93
|
+
article = SemanticPen::Models::Article.new(sample_article_data)
|
94
|
+
|
95
|
+
puts "Article ID: #{article.id}"
|
96
|
+
puts "Title: #{article.title}"
|
97
|
+
puts "Status: #{article.status}"
|
98
|
+
puts "Progress: #{article.progress}%"
|
99
|
+
puts "Is completed: #{article.completed?}"
|
100
|
+
puts "Is in progress: #{article.in_progress?}"
|
101
|
+
puts "Is failed: #{article.failed?}"
|
102
|
+
puts "Article hash: #{article.to_h}"
|
103
|
+
|
104
|
+
rescue SemanticPen::Error => e
|
105
|
+
puts "SemanticPen Error: #{e.message}"
|
106
|
+
puts "Error Code: #{e.error_code}" if e.error_code
|
107
|
+
puts "HTTP Status: #{e.http_status_code}" if e.http_status_code
|
108
|
+
rescue StandardError => e
|
109
|
+
puts "Unexpected Error: #{e.message}"
|
110
|
+
puts e.backtrace.first(5).join("\n")
|
111
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require_relative '../lib/semantic_pen'
|
5
|
+
|
6
|
+
begin
|
7
|
+
# Initialize the client
|
8
|
+
client = SemanticPen::Client.new(api_key: 'your-api-key-here')
|
9
|
+
|
10
|
+
# Generate an article
|
11
|
+
puts 'Generating article: "Machine Learning Basics"'
|
12
|
+
response = client.generate_article('machine learning basics', project_name: 'Tech Blog')
|
13
|
+
|
14
|
+
if response.success? && response.has_article_ids?
|
15
|
+
article_id = response.first_article_id
|
16
|
+
puts "Article generation started. ID: #{article_id}"
|
17
|
+
|
18
|
+
# Poll for completion using the StatusPoller utility
|
19
|
+
puts "\nWaiting for article to complete (polling every 5 seconds)..."
|
20
|
+
poller = SemanticPen::Models::StatusPoller.new(client, article_id, max_attempts: 30, delay: 5)
|
21
|
+
|
22
|
+
article = poller.wait_for_completion
|
23
|
+
|
24
|
+
if article.completed?
|
25
|
+
puts "\n✅ Article completed successfully!"
|
26
|
+
puts "Title: #{article.title}"
|
27
|
+
puts "Status: #{article.status}"
|
28
|
+
puts "Progress: #{article.progress}%"
|
29
|
+
puts "Created at: #{article.created_at}"
|
30
|
+
puts "Updated at: #{article.updated_at}"
|
31
|
+
|
32
|
+
if article.content
|
33
|
+
puts "\nContent preview:"
|
34
|
+
puts article.content[0..500] + (article.content.length > 500 ? '...' : '')
|
35
|
+
end
|
36
|
+
else
|
37
|
+
puts "\n❌ Article generation failed"
|
38
|
+
puts "Status: #{article.status}"
|
39
|
+
end
|
40
|
+
else
|
41
|
+
puts "Failed to start article generation: #{response.message}"
|
42
|
+
end
|
43
|
+
|
44
|
+
rescue SemanticPen::Error => e
|
45
|
+
puts "SemanticPen Error: #{e.message}"
|
46
|
+
puts "Error Code: #{e.error_code}" if e.error_code
|
47
|
+
puts "HTTP Status: #{e.http_status_code}" if e.http_status_code
|
48
|
+
rescue StandardError => e
|
49
|
+
puts "Unexpected Error: #{e.message}"
|
50
|
+
puts e.backtrace.join("\n")
|
51
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'faraday'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
module SemanticPen
|
7
|
+
class Client
|
8
|
+
def initialize(api_key: nil, base_url: nil, timeout: nil)
|
9
|
+
@configuration = build_configuration(api_key, base_url, timeout)
|
10
|
+
@configuration.validate!
|
11
|
+
@connection = build_connection
|
12
|
+
end
|
13
|
+
|
14
|
+
def generate_article(target_keyword, project_name: nil)
|
15
|
+
raise ValidationError, 'Target keyword cannot be nil or empty' if target_keyword.nil? || target_keyword.strip.empty?
|
16
|
+
|
17
|
+
request_body = {
|
18
|
+
target_keyword: target_keyword.strip,
|
19
|
+
project_name: project_name
|
20
|
+
}.compact
|
21
|
+
|
22
|
+
response = post('/api/articles', request_body)
|
23
|
+
Models::GenerateArticleResponse.new(response)
|
24
|
+
rescue Faraday::Error => e
|
25
|
+
raise NetworkError.new("Failed to send request: #{e.message}", original_error: e)
|
26
|
+
end
|
27
|
+
|
28
|
+
def get_article(article_id)
|
29
|
+
raise ValidationError, 'Article ID cannot be nil or empty' if article_id.nil? || article_id.strip.empty?
|
30
|
+
|
31
|
+
response = get("/api/articles/#{article_id.strip}")
|
32
|
+
Models::Article.new(response)
|
33
|
+
rescue Faraday::Error => e
|
34
|
+
raise NetworkError.new("Failed to send request: #{e.message}", original_error: e)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def build_configuration(api_key, base_url, timeout)
|
40
|
+
config = Configuration.new
|
41
|
+
config.api_key = api_key
|
42
|
+
config.base_url = base_url if base_url
|
43
|
+
config.timeout = timeout if timeout
|
44
|
+
config
|
45
|
+
end
|
46
|
+
|
47
|
+
def build_connection
|
48
|
+
Faraday.new(url: @configuration.base_url) do |f|
|
49
|
+
f.request :json
|
50
|
+
f.response :json
|
51
|
+
f.request :authorization, 'Bearer', @configuration.api_key
|
52
|
+
f.headers['User-Agent'] = 'SemanticPen-Ruby-SDK/1.0.0'
|
53
|
+
f.adapter Faraday.default_adapter
|
54
|
+
f.options.timeout = @configuration.timeout
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def get(path)
|
59
|
+
handle_response(@connection.get(path))
|
60
|
+
end
|
61
|
+
|
62
|
+
def post(path, body)
|
63
|
+
handle_response(@connection.post(path, body))
|
64
|
+
end
|
65
|
+
|
66
|
+
def handle_response(response)
|
67
|
+
case response.status
|
68
|
+
when 200..299
|
69
|
+
response.body
|
70
|
+
when 401
|
71
|
+
raise AuthenticationError, extract_error_message(response)
|
72
|
+
when 404
|
73
|
+
raise NotFoundError, extract_error_message(response)
|
74
|
+
when 429
|
75
|
+
raise RateLimitError, extract_error_message(response)
|
76
|
+
else
|
77
|
+
raise ApiError.new(extract_error_message(response), http_status_code: response.status)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def extract_error_message(response)
|
82
|
+
return response.body if response.body.is_a?(String)
|
83
|
+
|
84
|
+
response.body&.dig('message') || response.body&.dig('error') || 'Unknown error occurred'
|
85
|
+
rescue StandardError
|
86
|
+
'Unknown error occurred'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SemanticPen
|
4
|
+
class Configuration
|
5
|
+
attr_accessor :api_key, :base_url, :timeout
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@base_url = 'https://www.semanticpen.com'
|
9
|
+
@timeout = 60
|
10
|
+
end
|
11
|
+
|
12
|
+
def validate!
|
13
|
+
raise ArgumentError, 'API key is required' if api_key.nil? || api_key.empty?
|
14
|
+
raise ArgumentError, 'Base URL is required' if base_url.nil? || base_url.empty?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SemanticPen
|
4
|
+
class Error < StandardError
|
5
|
+
attr_reader :error_code, :http_status_code
|
6
|
+
|
7
|
+
def initialize(message, error_code: nil, http_status_code: nil)
|
8
|
+
super(message)
|
9
|
+
@error_code = error_code
|
10
|
+
@http_status_code = http_status_code
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class NetworkError < Error
|
15
|
+
def initialize(message, original_error: nil)
|
16
|
+
super("Network error: #{message}", error_code: 'NETWORK_ERROR')
|
17
|
+
@original_error = original_error
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class ValidationError < Error
|
22
|
+
def initialize(message)
|
23
|
+
super("Validation error: #{message}", error_code: 'VALIDATION_ERROR')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class AuthenticationError < Error
|
28
|
+
def initialize(message)
|
29
|
+
super("Authentication error: #{message}", error_code: 'AUTHENTICATION_ERROR', http_status_code: 401)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class NotFoundError < Error
|
34
|
+
def initialize(message)
|
35
|
+
super("Not found: #{message}", error_code: 'NOT_FOUND_ERROR', http_status_code: 404)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class ApiError < Error
|
40
|
+
def initialize(message, http_status_code: nil)
|
41
|
+
super("API error: #{message}", error_code: 'API_ERROR', http_status_code: http_status_code)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class RateLimitError < Error
|
46
|
+
def initialize(message)
|
47
|
+
super("Rate limit exceeded: #{message}", error_code: 'RATE_LIMIT_ERROR', http_status_code: 429)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SemanticPen
|
4
|
+
module Models
|
5
|
+
class Article
|
6
|
+
attr_reader :id, :title, :content, :status, :progress, :target_keyword, :project_name, :created_at, :updated_at
|
7
|
+
|
8
|
+
def initialize(data)
|
9
|
+
@id = data['id']
|
10
|
+
@title = data['title']
|
11
|
+
@content = data['content']
|
12
|
+
@status = data['status']
|
13
|
+
@progress = data['progress']
|
14
|
+
@target_keyword = data['target_keyword']
|
15
|
+
@project_name = data['project_name']
|
16
|
+
@created_at = data['created_at']
|
17
|
+
@updated_at = data['updated_at']
|
18
|
+
end
|
19
|
+
|
20
|
+
def completed?
|
21
|
+
status == 'completed'
|
22
|
+
end
|
23
|
+
|
24
|
+
def in_progress?
|
25
|
+
status == 'in_progress' || status == 'processing'
|
26
|
+
end
|
27
|
+
|
28
|
+
def failed?
|
29
|
+
status == 'failed' || status == 'error'
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_h
|
33
|
+
{
|
34
|
+
id: id,
|
35
|
+
title: title,
|
36
|
+
content: content,
|
37
|
+
status: status,
|
38
|
+
progress: progress,
|
39
|
+
target_keyword: target_keyword,
|
40
|
+
project_name: project_name,
|
41
|
+
created_at: created_at,
|
42
|
+
updated_at: updated_at
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class GenerateArticleResponse
|
48
|
+
attr_reader :message, :success, :article_ids
|
49
|
+
|
50
|
+
def initialize(data)
|
51
|
+
@message = data['message']
|
52
|
+
@success = data['success']
|
53
|
+
@article_ids = data['article_ids'] || []
|
54
|
+
end
|
55
|
+
|
56
|
+
def success?
|
57
|
+
success == true
|
58
|
+
end
|
59
|
+
|
60
|
+
def has_article_ids?
|
61
|
+
!article_ids.empty?
|
62
|
+
end
|
63
|
+
|
64
|
+
def first_article_id
|
65
|
+
article_ids.first
|
66
|
+
end
|
67
|
+
|
68
|
+
def to_h
|
69
|
+
{
|
70
|
+
message: message,
|
71
|
+
success: success,
|
72
|
+
article_ids: article_ids
|
73
|
+
}
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class GenerateArticleRequest
|
78
|
+
attr_reader :target_keyword, :project_name
|
79
|
+
|
80
|
+
def initialize(target_keyword, project_name = nil)
|
81
|
+
@target_keyword = target_keyword
|
82
|
+
@project_name = project_name
|
83
|
+
end
|
84
|
+
|
85
|
+
def to_h
|
86
|
+
{
|
87
|
+
target_keyword: target_keyword,
|
88
|
+
project_name: project_name
|
89
|
+
}.compact
|
90
|
+
end
|
91
|
+
|
92
|
+
def to_json(*args)
|
93
|
+
to_h.to_json(*args)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class StatusPoller
|
98
|
+
def initialize(client, article_id, max_attempts: 60, delay: 5)
|
99
|
+
@client = client
|
100
|
+
@article_id = article_id
|
101
|
+
@max_attempts = max_attempts
|
102
|
+
@delay = delay
|
103
|
+
end
|
104
|
+
|
105
|
+
def wait_for_completion
|
106
|
+
attempts = 0
|
107
|
+
|
108
|
+
loop do
|
109
|
+
article = @client.get_article(@article_id)
|
110
|
+
|
111
|
+
return article if article.completed? || article.failed?
|
112
|
+
|
113
|
+
attempts += 1
|
114
|
+
if attempts >= @max_attempts
|
115
|
+
raise Error, "Article generation timed out after #{@max_attempts} attempts"
|
116
|
+
end
|
117
|
+
|
118
|
+
sleep(@delay)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
data/lib/semantic_pen.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'semantic_pen/version'
|
4
|
+
require_relative 'semantic_pen/configuration'
|
5
|
+
require_relative 'semantic_pen/client'
|
6
|
+
require_relative 'semantic_pen/errors'
|
7
|
+
require_relative 'semantic_pen/models'
|
8
|
+
|
9
|
+
module SemanticPen
|
10
|
+
class << self
|
11
|
+
attr_accessor :configuration
|
12
|
+
|
13
|
+
def configure
|
14
|
+
self.configuration ||= Configuration.new
|
15
|
+
yield(configuration) if block_given?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'lib/semantic_pen/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'semantic_pen'
|
7
|
+
spec.version = SemanticPen::VERSION
|
8
|
+
spec.authors = ['SemanticPen Team']
|
9
|
+
spec.email = ['support@semanticpen.com']
|
10
|
+
|
11
|
+
spec.summary = 'Official Ruby SDK for SemanticPen API - AI-powered content generation'
|
12
|
+
spec.description = 'A Ruby SDK for SemanticPen API that provides AI-powered content generation capabilities with a simple and intuitive interface.'
|
13
|
+
spec.homepage = 'https://www.semanticpen.com'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
17
|
+
spec.metadata['source_code_uri'] = 'https://github.com/pushkarsingh32/semanticpen-ruby-sdk'
|
18
|
+
spec.metadata['changelog_uri'] = 'https://github.com/pushkarsingh32/semanticpen-ruby-sdk/blob/main/CHANGELOG.md'
|
19
|
+
|
20
|
+
spec.files = Dir['lib/**/*', 'examples/**/*', 'README.md', 'LICENSE', 'semantic_pen.gemspec']
|
21
|
+
spec.bindir = 'exe'
|
22
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
23
|
+
spec.require_paths = ['lib']
|
24
|
+
|
25
|
+
spec.required_ruby_version = '>= 2.6.0'
|
26
|
+
|
27
|
+
spec.add_dependency 'faraday', '~> 2.0'
|
28
|
+
spec.add_dependency 'json', '~> 2.0'
|
29
|
+
|
30
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
31
|
+
spec.add_development_dependency 'rubocop', '~> 1.0'
|
32
|
+
spec.add_development_dependency 'webmock', '~> 3.0'
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: semantic_pen
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- SemanticPen Team
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2025-07-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: faraday
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: json
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rubocop
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: webmock
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.0'
|
83
|
+
description: A Ruby SDK for SemanticPen API that provides AI-powered content generation
|
84
|
+
capabilities with a simple and intuitive interface.
|
85
|
+
email:
|
86
|
+
- support@semanticpen.com
|
87
|
+
executables: []
|
88
|
+
extensions: []
|
89
|
+
extra_rdoc_files: []
|
90
|
+
files:
|
91
|
+
- LICENSE
|
92
|
+
- README.md
|
93
|
+
- examples/basic_usage.rb
|
94
|
+
- examples/bulk_generation.rb
|
95
|
+
- examples/configuration_example.rb
|
96
|
+
- examples/polling_example.rb
|
97
|
+
- lib/semantic_pen.rb
|
98
|
+
- lib/semantic_pen/client.rb
|
99
|
+
- lib/semantic_pen/configuration.rb
|
100
|
+
- lib/semantic_pen/errors.rb
|
101
|
+
- lib/semantic_pen/models.rb
|
102
|
+
- lib/semantic_pen/version.rb
|
103
|
+
- semantic_pen.gemspec
|
104
|
+
homepage: https://www.semanticpen.com
|
105
|
+
licenses:
|
106
|
+
- MIT
|
107
|
+
metadata:
|
108
|
+
homepage_uri: https://www.semanticpen.com
|
109
|
+
source_code_uri: https://github.com/pushkarsingh32/semanticpen-ruby-sdk
|
110
|
+
changelog_uri: https://github.com/pushkarsingh32/semanticpen-ruby-sdk/blob/main/CHANGELOG.md
|
111
|
+
post_install_message:
|
112
|
+
rdoc_options: []
|
113
|
+
require_paths:
|
114
|
+
- lib
|
115
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: 2.6.0
|
120
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
requirements: []
|
126
|
+
rubygems_version: 3.0.3.1
|
127
|
+
signing_key:
|
128
|
+
specification_version: 4
|
129
|
+
summary: Official Ruby SDK for SemanticPen API - AI-powered content generation
|
130
|
+
test_files: []
|