vapi-ruby 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 +23 -0
- data/LICENSE.txt +21 -0
- data/README.md +305 -0
- data/lib/vapi/analytics.rb +9 -0
- data/lib/vapi/assistant.rb +25 -0
- data/lib/vapi/base.rb +11 -0
- data/lib/vapi/call.rb +25 -0
- data/lib/vapi/client.rb +90 -0
- data/lib/vapi/configuration.rb +16 -0
- data/lib/vapi/errors.rb +51 -0
- data/lib/vapi/file.rb +25 -0
- data/lib/vapi/knowledge_base.rb +25 -0
- data/lib/vapi/log.rb +9 -0
- data/lib/vapi/phone_number.rb +25 -0
- data/lib/vapi/squad.rb +25 -0
- data/lib/vapi/tool.rb +25 -0
- data/lib/vapi/version.rb +5 -0
- data/lib/vapi.rb +39 -0
- data/spec/examples.txt +60 -0
- data/spec/spec_helper.rb +40 -0
- data/spec/vapi/analytics_spec.rb +21 -0
- data/spec/vapi/assistant_spec.rb +73 -0
- data/spec/vapi/call_spec.rb +81 -0
- data/spec/vapi/client_spec.rb +130 -0
- data/spec/vapi/configuration_spec.rb +36 -0
- data/spec/vapi/file_spec.rb +63 -0
- data/spec/vapi/knowledge_base_spec.rb +64 -0
- data/spec/vapi/log_spec.rb +29 -0
- data/spec/vapi/phone_number_spec.rb +64 -0
- data/spec/vapi/squad_spec.rb +64 -0
- data/spec/vapi/tool_spec.rb +64 -0
- metadata +190 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: f22947bfc1aff0910a372e417b00d3d627896f43d9bd9e2760a897c1343f75e5
|
|
4
|
+
data.tar.gz: d22223f4a7f7c5ad0b0144b93882ee78cb7fb274c0df14cbda07c135c489c4ce
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: f1cee1b70f543e6bdb66c20f2d7c1272c77cc54e9103ee0829bedf3c2411426f5f0cf5b964c214f94289145e4584dce2857dcb7baa4ebecc9091f2b0371881e9
|
|
7
|
+
data.tar.gz: 6f33b0187a90d34223e80b862abae104c954525f0e06f7dc9b711afe1c216899c8c84df4043d58cc1202225e1871345929e4d459c2c2a8b749accf371b349242
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2026-03-25
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Initial release
|
|
12
|
+
- `Vapi::Assistant` — CRUD for voice AI assistants
|
|
13
|
+
- `Vapi::Call` — create outbound calls, list, and manage call records
|
|
14
|
+
- `Vapi::PhoneNumber` — manage phone numbers (Twilio, Vonage, VAPI)
|
|
15
|
+
- `Vapi::Squad` — manage multi-assistant squads
|
|
16
|
+
- `Vapi::Tool` — CRUD for function/API tools attached to assistants
|
|
17
|
+
- `Vapi::File` — upload and manage files
|
|
18
|
+
- `Vapi::KnowledgeBase` — manage knowledge bases for RAG
|
|
19
|
+
- `Vapi::Log` — retrieve call and API logs
|
|
20
|
+
- `Vapi::Analytics` — query call analytics
|
|
21
|
+
- Bearer token authentication
|
|
22
|
+
- Comprehensive error handling (401, 404, 429, 400/422, 5xx)
|
|
23
|
+
- Full RSpec test suite with WebMock
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Eduardo Souza
|
|
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
|
|
13
|
+
all 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
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
# VAPI Ruby [](https://badge.fury.io/rb/vapi-ruby)
|
|
2
|
+
|
|
3
|
+
Ruby gem for integrating with the [VAPI](https://vapi.ai) voice AI platform. Supports assistants, calls, phone numbers, squads, tools, files, knowledge bases, logs, and analytics.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
Add to your Gemfile:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
gem 'vapi-ruby'
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Then run:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
bundle install
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or install directly:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
gem install vapi-ruby
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Configuration
|
|
26
|
+
|
|
27
|
+
```ruby
|
|
28
|
+
Vapi.configure do |config|
|
|
29
|
+
config.api_key = ENV['VAPI_API_KEY']
|
|
30
|
+
|
|
31
|
+
# Optional
|
|
32
|
+
config.timeout = 30 # seconds, default
|
|
33
|
+
end
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
All resources can be initialized without arguments (uses `Vapi.client` by default) or with an explicit client.
|
|
39
|
+
|
|
40
|
+
### Assistants
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
assistants = Vapi::Assistant.new
|
|
44
|
+
|
|
45
|
+
# List all assistants
|
|
46
|
+
assistants.list
|
|
47
|
+
|
|
48
|
+
# List with filters
|
|
49
|
+
assistants.list(limit: 10, createdAtGt: '2024-01-01T00:00:00Z')
|
|
50
|
+
|
|
51
|
+
# Get an assistant
|
|
52
|
+
assistants.find('assistant_id')
|
|
53
|
+
|
|
54
|
+
# Create an assistant
|
|
55
|
+
assistants.create(
|
|
56
|
+
name: 'Sales Agent',
|
|
57
|
+
model: { provider: 'openai', model: 'gpt-4' },
|
|
58
|
+
voice: { provider: '11labs', voiceId: 'voice_id' },
|
|
59
|
+
firstMessage: 'Hello! How can I help you today?'
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Update an assistant
|
|
63
|
+
assistants.update('assistant_id', name: 'Updated Name')
|
|
64
|
+
|
|
65
|
+
# Delete an assistant
|
|
66
|
+
assistants.delete('assistant_id')
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Calls
|
|
70
|
+
|
|
71
|
+
```ruby
|
|
72
|
+
calls = Vapi::Call.new
|
|
73
|
+
|
|
74
|
+
# List all calls
|
|
75
|
+
calls.list
|
|
76
|
+
|
|
77
|
+
# Filter by assistant
|
|
78
|
+
calls.list(assistantId: 'assistant_id')
|
|
79
|
+
|
|
80
|
+
# Get a call
|
|
81
|
+
calls.find('call_id')
|
|
82
|
+
|
|
83
|
+
# Create an outbound call
|
|
84
|
+
calls.create(
|
|
85
|
+
assistantId: 'assistant_id',
|
|
86
|
+
phoneNumberId: 'phone_number_id',
|
|
87
|
+
customer: { number: '+1234567890' }
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
# Update a call
|
|
91
|
+
calls.update('call_id', name: 'Follow-up Call')
|
|
92
|
+
|
|
93
|
+
# Delete a call
|
|
94
|
+
calls.delete('call_id')
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Phone Numbers
|
|
98
|
+
|
|
99
|
+
```ruby
|
|
100
|
+
phone_numbers = Vapi::PhoneNumber.new
|
|
101
|
+
|
|
102
|
+
# List phone numbers
|
|
103
|
+
phone_numbers.list
|
|
104
|
+
|
|
105
|
+
# Get a phone number
|
|
106
|
+
phone_numbers.find('phone_number_id')
|
|
107
|
+
|
|
108
|
+
# Create a phone number
|
|
109
|
+
phone_numbers.create(
|
|
110
|
+
provider: 'twilio',
|
|
111
|
+
number: '+1234567890',
|
|
112
|
+
twilioAccountSid: 'sid',
|
|
113
|
+
twilioAuthToken: 'token'
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# Update a phone number
|
|
117
|
+
phone_numbers.update('phone_number_id', name: 'Main Line')
|
|
118
|
+
|
|
119
|
+
# Delete a phone number
|
|
120
|
+
phone_numbers.delete('phone_number_id')
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Squads
|
|
124
|
+
|
|
125
|
+
```ruby
|
|
126
|
+
squads = Vapi::Squad.new
|
|
127
|
+
|
|
128
|
+
# List squads
|
|
129
|
+
squads.list
|
|
130
|
+
|
|
131
|
+
# Get a squad
|
|
132
|
+
squads.find('squad_id')
|
|
133
|
+
|
|
134
|
+
# Create a squad
|
|
135
|
+
squads.create(
|
|
136
|
+
name: 'Sales Team',
|
|
137
|
+
members: [{ assistantId: 'assistant_id' }]
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# Update a squad
|
|
141
|
+
squads.update('squad_id', name: 'Updated Team')
|
|
142
|
+
|
|
143
|
+
# Delete a squad
|
|
144
|
+
squads.delete('squad_id')
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Tools
|
|
148
|
+
|
|
149
|
+
```ruby
|
|
150
|
+
tools = Vapi::Tool.new
|
|
151
|
+
|
|
152
|
+
# List tools
|
|
153
|
+
tools.list
|
|
154
|
+
|
|
155
|
+
# Get a tool
|
|
156
|
+
tools.find('tool_id')
|
|
157
|
+
|
|
158
|
+
# Create a tool
|
|
159
|
+
tools.create(
|
|
160
|
+
type: 'function',
|
|
161
|
+
function: {
|
|
162
|
+
name: 'lookup_order',
|
|
163
|
+
description: 'Look up an order by ID',
|
|
164
|
+
parameters: { type: 'object', properties: { orderId: { type: 'string' } } }
|
|
165
|
+
}
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
# Update a tool
|
|
169
|
+
tools.update('tool_id', function: { name: 'updated_lookup' })
|
|
170
|
+
|
|
171
|
+
# Delete a tool
|
|
172
|
+
tools.delete('tool_id')
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Files
|
|
176
|
+
|
|
177
|
+
```ruby
|
|
178
|
+
files = Vapi::File.new
|
|
179
|
+
|
|
180
|
+
# List files
|
|
181
|
+
files.list
|
|
182
|
+
|
|
183
|
+
# Get a file
|
|
184
|
+
files.find('file_id')
|
|
185
|
+
|
|
186
|
+
# Upload a file
|
|
187
|
+
files.create(name: 'training_data.pdf')
|
|
188
|
+
|
|
189
|
+
# Update a file
|
|
190
|
+
files.update('file_id', name: 'renamed.pdf')
|
|
191
|
+
|
|
192
|
+
# Delete a file
|
|
193
|
+
files.delete('file_id')
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Knowledge Bases
|
|
197
|
+
|
|
198
|
+
```ruby
|
|
199
|
+
knowledge_bases = Vapi::KnowledgeBase.new
|
|
200
|
+
|
|
201
|
+
# List knowledge bases
|
|
202
|
+
knowledge_bases.list
|
|
203
|
+
|
|
204
|
+
# Get a knowledge base
|
|
205
|
+
knowledge_bases.find('knowledge_base_id')
|
|
206
|
+
|
|
207
|
+
# Create a knowledge base
|
|
208
|
+
knowledge_bases.create(
|
|
209
|
+
name: 'Product FAQ',
|
|
210
|
+
provider: 'trieve'
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
# Update a knowledge base
|
|
214
|
+
knowledge_bases.update('knowledge_base_id', name: 'Updated FAQ')
|
|
215
|
+
|
|
216
|
+
# Delete a knowledge base
|
|
217
|
+
knowledge_bases.delete('knowledge_base_id')
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Logs
|
|
221
|
+
|
|
222
|
+
```ruby
|
|
223
|
+
logs = Vapi::Log.new
|
|
224
|
+
|
|
225
|
+
# List all logs
|
|
226
|
+
logs.list
|
|
227
|
+
|
|
228
|
+
# Filter by call
|
|
229
|
+
logs.list(callId: 'call_id')
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Analytics
|
|
233
|
+
|
|
234
|
+
```ruby
|
|
235
|
+
analytics = Vapi::Analytics.new
|
|
236
|
+
|
|
237
|
+
# Query analytics
|
|
238
|
+
analytics.query(
|
|
239
|
+
queries: [
|
|
240
|
+
{
|
|
241
|
+
name: 'total_calls',
|
|
242
|
+
table: 'call',
|
|
243
|
+
operations: [{ operation: 'count', column: 'id' }]
|
|
244
|
+
}
|
|
245
|
+
]
|
|
246
|
+
)
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Using Multiple Clients
|
|
250
|
+
|
|
251
|
+
```ruby
|
|
252
|
+
config1 = Vapi::Configuration.new
|
|
253
|
+
config1.api_key = 'key_for_org_1'
|
|
254
|
+
client1 = Vapi::Client.new(config1)
|
|
255
|
+
|
|
256
|
+
config2 = Vapi::Configuration.new
|
|
257
|
+
config2.api_key = 'key_for_org_2'
|
|
258
|
+
client2 = Vapi::Client.new(config2)
|
|
259
|
+
|
|
260
|
+
# Use explicit clients
|
|
261
|
+
Vapi::Assistant.new(client1).list
|
|
262
|
+
Vapi::Assistant.new(client2).list
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Error Handling
|
|
266
|
+
|
|
267
|
+
```ruby
|
|
268
|
+
begin
|
|
269
|
+
calls.create(assistantId: 'invalid')
|
|
270
|
+
rescue Vapi::ConfigurationError => e
|
|
271
|
+
# Missing API key
|
|
272
|
+
rescue Vapi::AuthenticationError => e
|
|
273
|
+
# Invalid API key
|
|
274
|
+
rescue Vapi::ValidationError => e
|
|
275
|
+
puts e.errors
|
|
276
|
+
rescue Vapi::NotFoundError => e
|
|
277
|
+
# 404 - Resource not found
|
|
278
|
+
rescue Vapi::RateLimitError => e
|
|
279
|
+
puts e.retry_after # Seconds to wait
|
|
280
|
+
rescue Vapi::APIError => e
|
|
281
|
+
puts e.status_code
|
|
282
|
+
puts e.response_body
|
|
283
|
+
end
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## Development
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
# Install dependencies
|
|
290
|
+
bundle install
|
|
291
|
+
|
|
292
|
+
# Run tests
|
|
293
|
+
bundle exec rspec
|
|
294
|
+
|
|
295
|
+
# Run linter
|
|
296
|
+
bundle exec rubocop
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
## Contributing
|
|
300
|
+
|
|
301
|
+
Bug reports and pull requests are welcome on [GitHub](https://github.com/esouza/vapi-ruby). See [CONTRIBUTING.md](CONTRIBUTING.md) for details.
|
|
302
|
+
|
|
303
|
+
## License
|
|
304
|
+
|
|
305
|
+
The gem is available as open source under the terms of the [MIT License](LICENSE.txt).
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Vapi
|
|
4
|
+
class Assistant < Base
|
|
5
|
+
def list(**params)
|
|
6
|
+
client.get("/assistant", params)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def find(id)
|
|
10
|
+
client.get("/assistant/#{id}")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def create(params)
|
|
14
|
+
client.post("/assistant", params)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def update(id, params)
|
|
18
|
+
client.patch("/assistant/#{id}", params)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def delete(id)
|
|
22
|
+
client.delete("/assistant/#{id}")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
data/lib/vapi/base.rb
ADDED
data/lib/vapi/call.rb
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Vapi
|
|
4
|
+
class Call < Base
|
|
5
|
+
def list(**params)
|
|
6
|
+
client.get("/call", params)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def find(id)
|
|
10
|
+
client.get("/call/#{id}")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def create(params)
|
|
14
|
+
client.post("/call", params)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def update(id, params)
|
|
18
|
+
client.patch("/call/#{id}", params)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def delete(id)
|
|
22
|
+
client.delete("/call/#{id}")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
data/lib/vapi/client.rb
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Vapi
|
|
4
|
+
class Client
|
|
5
|
+
include HTTParty
|
|
6
|
+
|
|
7
|
+
attr_reader :configuration
|
|
8
|
+
|
|
9
|
+
def initialize(configuration = nil)
|
|
10
|
+
@configuration = configuration || Vapi.configuration
|
|
11
|
+
validate_configuration!
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def get(path, params = {})
|
|
15
|
+
request(:get, path, query: params)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def post(path, data = {})
|
|
19
|
+
request(:post, path, body: data.to_json)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def patch(path, data = {})
|
|
23
|
+
request(:patch, path, body: data.to_json)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def delete(path)
|
|
27
|
+
request(:delete, path)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def validate_configuration!
|
|
33
|
+
raise ConfigurationError unless configuration&.valid?
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def request(method, path, options = {})
|
|
37
|
+
url = "#{configuration.base_url}#{path}"
|
|
38
|
+
request_options = {
|
|
39
|
+
headers: default_headers,
|
|
40
|
+
timeout: configuration.timeout
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if options[:query] && !options[:query].empty?
|
|
44
|
+
request_options[:query] = options[:query]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
if options[:body]
|
|
48
|
+
request_options[:body] = options[:body]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
response = self.class.send(method, url, request_options)
|
|
52
|
+
handle_response(response)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def default_headers
|
|
56
|
+
{
|
|
57
|
+
'Authorization' => "Bearer #{configuration.api_key}",
|
|
58
|
+
'Content-Type' => 'application/json',
|
|
59
|
+
'Accept' => 'application/json'
|
|
60
|
+
}
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def handle_response(response)
|
|
64
|
+
case response.code
|
|
65
|
+
when 200, 201, 202, 204
|
|
66
|
+
response.parsed_response
|
|
67
|
+
when 401
|
|
68
|
+
raise AuthenticationError, "Authentication failed: #{response.body}"
|
|
69
|
+
when 404
|
|
70
|
+
raise NotFoundError.new("Resource not found", response.code, response.body)
|
|
71
|
+
when 429
|
|
72
|
+
retry_after = response.headers['retry-after']
|
|
73
|
+
raise RateLimitError.new("Rate limit exceeded", response.code, response.body, retry_after)
|
|
74
|
+
when 400, 422
|
|
75
|
+
parsed = response.parsed_response || {}
|
|
76
|
+
errors = parsed['errors'] || parsed['message'] || {}
|
|
77
|
+
raise ValidationError.new(
|
|
78
|
+
parsed['message'] || 'Validation failed',
|
|
79
|
+
response.code,
|
|
80
|
+
response.body,
|
|
81
|
+
errors
|
|
82
|
+
)
|
|
83
|
+
when 500..599
|
|
84
|
+
raise APIError.new("Server error", response.code, response.body)
|
|
85
|
+
else
|
|
86
|
+
raise APIError.new("Unexpected response", response.code, response.body)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Vapi
|
|
4
|
+
class Configuration
|
|
5
|
+
attr_accessor :api_key, :base_url, :timeout
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@base_url = "https://api.vapi.ai"
|
|
9
|
+
@timeout = 30
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def valid?
|
|
13
|
+
!api_key.nil? && !api_key.empty?
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
data/lib/vapi/errors.rb
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Vapi
|
|
4
|
+
class Error < StandardError; end
|
|
5
|
+
|
|
6
|
+
class ConfigurationError < Error
|
|
7
|
+
def initialize(message = "Missing required configuration: api_key")
|
|
8
|
+
super(message)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class AuthenticationError < Error
|
|
13
|
+
def initialize(message = "Authentication failed")
|
|
14
|
+
super(message)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class APIError < Error
|
|
19
|
+
attr_reader :status_code, :response_body
|
|
20
|
+
|
|
21
|
+
def initialize(message, status_code = nil, response_body = nil)
|
|
22
|
+
super(message)
|
|
23
|
+
@status_code = status_code
|
|
24
|
+
@response_body = response_body
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
class ValidationError < APIError
|
|
29
|
+
attr_reader :errors
|
|
30
|
+
|
|
31
|
+
def initialize(message, status_code = nil, response_body = nil, errors = {})
|
|
32
|
+
super(message, status_code, response_body)
|
|
33
|
+
@errors = errors
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
class NotFoundError < APIError
|
|
38
|
+
def initialize(message = "Resource not found", status_code = 404, response_body = nil)
|
|
39
|
+
super(message, status_code, response_body)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
class RateLimitError < APIError
|
|
44
|
+
attr_reader :retry_after
|
|
45
|
+
|
|
46
|
+
def initialize(message = "Rate limit exceeded", status_code = 429, response_body = nil, retry_after = nil)
|
|
47
|
+
super(message, status_code, response_body)
|
|
48
|
+
@retry_after = retry_after
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
data/lib/vapi/file.rb
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Vapi
|
|
4
|
+
class File < Base
|
|
5
|
+
def list(**params)
|
|
6
|
+
client.get("/file", params)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def find(id)
|
|
10
|
+
client.get("/file/#{id}")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def create(params)
|
|
14
|
+
client.post("/file", params)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def update(id, params)
|
|
18
|
+
client.patch("/file/#{id}", params)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def delete(id)
|
|
22
|
+
client.delete("/file/#{id}")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Vapi
|
|
4
|
+
class KnowledgeBase < Base
|
|
5
|
+
def list(**params)
|
|
6
|
+
client.get("/knowledge-base", params)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def find(id)
|
|
10
|
+
client.get("/knowledge-base/#{id}")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def create(params)
|
|
14
|
+
client.post("/knowledge-base", params)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def update(id, params)
|
|
18
|
+
client.patch("/knowledge-base/#{id}", params)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def delete(id)
|
|
22
|
+
client.delete("/knowledge-base/#{id}")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
data/lib/vapi/log.rb
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Vapi
|
|
4
|
+
class PhoneNumber < Base
|
|
5
|
+
def list(**params)
|
|
6
|
+
client.get("/phone-number", params)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def find(id)
|
|
10
|
+
client.get("/phone-number/#{id}")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def create(params)
|
|
14
|
+
client.post("/phone-number", params)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def update(id, params)
|
|
18
|
+
client.patch("/phone-number/#{id}", params)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def delete(id)
|
|
22
|
+
client.delete("/phone-number/#{id}")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
data/lib/vapi/squad.rb
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Vapi
|
|
4
|
+
class Squad < Base
|
|
5
|
+
def list(**params)
|
|
6
|
+
client.get("/squad", params)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def find(id)
|
|
10
|
+
client.get("/squad/#{id}")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def create(params)
|
|
14
|
+
client.post("/squad", params)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def update(id, params)
|
|
18
|
+
client.patch("/squad/#{id}", params)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def delete(id)
|
|
22
|
+
client.delete("/squad/#{id}")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|