voiceml 0.8.1 β†’ 0.9.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 15e4cbe52a1e5c8c9373bc3b9f636c5637915cf7827abd08c87512998f7fafe4
4
- data.tar.gz: bc844da7b2b3289c072d7f5b3ef130a90eace33a0fa7f1794235e3bb096b986c
3
+ metadata.gz: ce6f40c38ff553e748881a1b57f940e977cd24983da928988a7dcc77640cc32d
4
+ data.tar.gz: d6d26a2ab6ee10d9df572f24c6b667cb87cb03bb68ef40234fb05308db3d3789
5
5
  SHA512:
6
- metadata.gz: fa0180df722300f7dc736fb67eccd89fd2369c8528d43569eb06e369321d75989a4daee80612c8899354e163ee99fbeda8092bef2e6d561670a94b872fa0b8dc
7
- data.tar.gz: a1ca825eac62aa31cb2367340e666a521251055500d0736975ff4a6f5061d9e4e6254d5c4b0e7d643f523cbd03029dc5b4e9840bfd22b7cf07c42775d23ae8fb
6
+ metadata.gz: 28fe625cbe02ccd47894507e8c28d8eecd06f0caae4aea040f5e21af82980072e39a74aa7cb788fc885f9b24e58239099a87dac639a79b9f3a3c248769e0a652
7
+ data.tar.gz: 85fd838a671b848768a791d590fbcdefa40c49e89d80b8f2c2c98f475af7035489a727ca3d1ecd7e5988296a5a62f32e3784344fc9fcc11f24beec16d5d9619e
data/README.md CHANGED
@@ -1,11 +1,11 @@
1
1
  # πŸ“ž VoiceML Ruby SDK
2
2
 
3
- The official Ruby client for the [VoiceML REST API](https://voicetel.com/docs/api/v0.7/voiceml/) β€” Twilio-compatible outbound voice and answering-machine-detection from VoiceTel, with idiomatic snake_case kwargs, structured errors, and a Twilio-compatible wire format.
3
+ The official Ruby client for the [VoiceML REST API](https://voicetel.com/docs/api/v0.9/voiceml/) β€” Twilio-compatible outbound voice and answering-machine-detection from VoiceTel, with idiomatic snake_case kwargs, structured errors, and a Twilio-compatible wire format.
4
4
 
5
- ![Version](https://img.shields.io/badge/version-0.8.1-blue)
5
+ ![Version](https://img.shields.io/badge/version-0.9.2-blue)
6
6
  ![Ruby](https://img.shields.io/badge/ruby-3.0%2B-red)
7
7
  ![License](https://img.shields.io/badge/license-MIT-green)
8
- ![Tests](https://img.shields.io/badge/tests-65%20specs-brightgreen)
8
+ ![Tests](https://img.shields.io/badge/tests-188%20examples-brightgreen)
9
9
  ![Gem](https://img.shields.io/badge/gem-voiceml-blue)
10
10
 
11
11
  ## πŸ“š Table of Contents
@@ -49,10 +49,15 @@ The official Ruby client for the [VoiceML REST API](https://voicetel.com/docs/ap
49
49
  - **Notifications** β€” fetch, list.
50
50
  - **SIP** β€” SIP Trunking: Domains (CRUD), CredentialLists + Credentials (CRUD), IpAccessControlLists + IpAddresses (CRUD), Domain↔ACL/CredentialList mappings (historical, Auth/Calls, Auth/Registrations namespaces).
51
51
  - **Routes V2** β€” Twilio Inbound Processing Region API: `client.routes_v2.sip_domains.fetch(name)` / `update(name, voice_region:, friendly_name:)`.
52
+ - **Messaging Service** β€” `client.messaging_v1.services` CRUD (`MG...` services), routed automatically at the `messaging.voicetel.com` product host.
53
+ - **Pricing** β€” read-only `client.pricing.v1` / `client.pricing.v2` for Voice, Messaging, PhoneNumbers, and Trunking country + number lookups.
52
54
  - **Diagnostics** β€” `/health` deep probe, OpenAPI spec.
53
55
 
56
+ ### 🌐 Per-product hosts
57
+ VoiceML mirrors Twilio's product-per-subdomain model. The whole `conversations_v1` group is routed to `conversations.voicetel.com` and `messaging_v1` to `messaging.voicetel.com`, while everything else stays on the default `voiceml.voicetel.com`. These hosts are derived automatically from `base_url`; a non-`voicetel.com` base (e.g. a self-hosted instance) keeps every product on that single host. Override per product with `messaging_base_url:` / `conversations_base_url:` on the client constructor.
58
+
54
59
  ### πŸ§ͺ Tested
55
- - **81 specs** with mocked HTTP layer (`webmock`) covering every resource and pagination edge cases β€” spec drift gets caught at parse time.
60
+ - **188 examples** with mocked HTTP layer (`webmock`) covering every resource and pagination edge cases β€” spec drift gets caught at parse time.
56
61
  - Integration smoke spec gated by env vars β€” safe for CI.
57
62
 
58
63
  ### πŸ“¦ Clean Distribution
@@ -110,7 +115,7 @@ client = VoiceML::Client.new(account_sid: 'AC...', api_key: '...')
110
115
  client.diagnostics.health # uses your AccountSid + key on every call
111
116
  ```
112
117
 
113
- > Don't have credentials yet? See **[voicetel.com/docs/api/v0.7/voiceml/](https://voicetel.com/docs/api/v0.7/voiceml/)** for issuance and rotation.
118
+ > Don't have credentials yet? See **[voicetel.com/docs/api/v0.9/voiceml/](https://voicetel.com/docs/api/v0.9/voiceml/)** for issuance and rotation.
114
119
 
115
120
  ## πŸ—ΊοΈ Resource Reference
116
121
 
@@ -124,6 +129,8 @@ client.diagnostics.health # uses your AccountSid + key on every call
124
129
  | `client.messages` | create, fetch, list, update, delete | To/From/DateSent filters; Body redaction; Status=canceled |
125
130
  | `client.incoming_phone_numbers` | list, fetch, update | |
126
131
  | `client.notifications` | fetch, list | |
132
+ | `client.messaging_v1.services` | create, list, fetch, update, delete | Messaging Service (`MG...`); routed at `messaging.voicetel.com` |
133
+ | `client.pricing.v1` / `.v2` | countries.list / fetch, numbers.fetch | Voice, Messaging, PhoneNumbers, Trunking (read-only, default host) |
127
134
  | `client.diagnostics` | `/health`, OpenAPI spec | |
128
135
 
129
136
  Methods accept idiomatic snake_case keyword arguments β€” they're translated to Twilio's PascalCase wire field names internally:
@@ -245,7 +252,7 @@ gem build voiceml.gemspec
245
252
 
246
253
  ## πŸ“– API Documentation
247
254
 
248
- - **Reference docs:** [voicetel.com/docs/api/v0.7/voiceml/](https://voicetel.com/docs/api/v0.7/voiceml/)
255
+ - **Reference docs:** [voicetel.com/docs/api/v0.9/voiceml/](https://voicetel.com/docs/api/v0.9/voiceml/)
249
256
  - **Validator:** [voicetel.com/voiceml/validator/](https://voicetel.com/voiceml/validator/)
250
257
  - **SDK catalogue:** [voicetel.com/docs/voiceml-sdks/](https://voicetel.com/docs/voiceml-sdks/)
251
258
  - **YARD comments:** every resource method carries `@param` / `@return` docs β€” `yard doc lib` builds them locally.
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'transport'
4
+ require_relative 'hosts'
4
5
  require_relative 'resources/calls'
5
6
  require_relative 'resources/conferences'
6
7
  require_relative 'resources/queues'
@@ -12,6 +13,11 @@ require_relative 'resources/diagnostics'
12
13
  require_relative 'resources/messages'
13
14
  require_relative 'resources/sip'
14
15
  require_relative 'resources/routes_v2'
16
+ require_relative 'resources/voice_v1'
17
+ require_relative 'resources/conversations_v1'
18
+ require_relative 'resources/assistants_v1'
19
+ require_relative 'resources/messaging_v1'
20
+ require_relative 'resources/pricing'
15
21
 
16
22
  module VoiceML
17
23
  # Synchronous client for the VoiceML REST API.
@@ -31,7 +37,8 @@ module VoiceML
31
37
  class Client
32
38
  attr_reader :calls, :conferences, :queues, :applications, :recordings,
33
39
  :incoming_phone_numbers, :notifications, :diagnostics, :messages,
34
- :sip, :routes_v2
40
+ :sip, :routes_v2, :voice_v1, :conversations_v1, :assistants_v1,
41
+ :messaging_v1, :pricing
35
42
 
36
43
  # @param account_sid [String] Twilio-format AccountSid (`AC` + 32 hex).
37
44
  # @param api_key [String, nil] per-tenant API key. Pass either `api_key:` or the
@@ -43,11 +50,16 @@ module VoiceML
43
50
  # @param max_retries [Integer] retry attempts for 429/5xx and transport errors. Defaults to 2.
44
51
  # @param user_agent [String, nil] override the `User-Agent` header. Defaults to
45
52
  # `"voiceml-ruby/#{VERSION}"`.
53
+ # @param messaging_base_url [String, nil] override the host for `messaging_v1`. Defaults to
54
+ # the `messaging.*.voicetel.com` subdomain derived from `base_url` (else `base_url` itself).
55
+ # @param conversations_base_url [String, nil] override the host for `conversations_v1`. Defaults
56
+ # to the `conversations.*.voicetel.com` subdomain derived from `base_url` (else `base_url`).
46
57
  def initialize(account_sid:, api_key: nil, auth_token: nil,
47
58
  base_url: Transport::DEFAULT_BASE_URL,
48
59
  timeout: Transport::DEFAULT_TIMEOUT,
49
60
  max_retries: Transport::DEFAULT_MAX_RETRIES,
50
- user_agent: nil, http_client: nil)
61
+ user_agent: nil, http_client: nil,
62
+ messaging_base_url: nil, conversations_base_url: nil)
51
63
  if !api_key.nil? && !auth_token.nil?
52
64
  raise ArgumentError, 'pass either api_key: or auth_token:, not both'
53
65
  end
@@ -64,6 +76,16 @@ module VoiceML
64
76
  http_client: http_client
65
77
  )
66
78
 
79
+ # VoiceML mirrors Twilio's product-per-subdomain model: the whole Conversations group
80
+ # rides `conversations.voicetel.com` and Messaging Service rides `messaging.voicetel.com`,
81
+ # while everything else stays on the default host. See VoiceML::Hosts.
82
+ _default, messaging_host, conversations_host =
83
+ Hosts.resolve_product_base_urls(base_url,
84
+ messaging_base_url: messaging_base_url,
85
+ conversations_base_url: conversations_base_url)
86
+ messaging_transport = ScopedTransport.new(@transport, messaging_host)
87
+ conversations_transport = ScopedTransport.new(@transport, conversations_host)
88
+
67
89
  @calls = CallsResource.new(@transport)
68
90
  @conferences = ConferencesResource.new(@transport)
69
91
  @queues = QueuesResource.new(@transport)
@@ -75,6 +97,11 @@ module VoiceML
75
97
  @messages = MessagesResource.new(@transport)
76
98
  @sip = SipResource.new(@transport)
77
99
  @routes_v2 = RoutesV2Resource.new(@transport)
100
+ @voice_v1 = VoiceV1Resource.new(@transport)
101
+ @conversations_v1 = ConversationsV1Resource.new(conversations_transport)
102
+ @assistants_v1 = AssistantsV1Resource.new(@transport)
103
+ @messaging_v1 = MessagingV1Resource.new(messaging_transport)
104
+ @pricing = PricingResource.new(@transport)
78
105
  end
79
106
 
80
107
  def account_sid
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+
5
+ require_relative 'transport'
6
+
7
+ module VoiceML
8
+ # Per-product host resolution for the VoiceML API.
9
+ #
10
+ # Twilio splits its products across dedicated subdomains (`api.twilio.com`,
11
+ # `conversations.twilio.com`, `messaging.twilio.com`, ...). VoiceML mirrors that
12
+ # shape on `voicetel.com`: the Conversations product answers on
13
+ # `conversations.voicetel.com` and the Messaging Service product on
14
+ # `messaging.voicetel.com`, while everything else stays on the default
15
+ # `voiceml.voicetel.com` host. Conversation Service and Messaging Service share
16
+ # the identical `/v1/Services` path shape, so the *host* is what disambiguates
17
+ # them on the wire.
18
+ #
19
+ # Given the configured `base_url` this module derives the two product hosts by
20
+ # swapping the leftmost `voiceml` label β€” but only for recognised
21
+ # `*.voicetel.com` hosts. For any other base URL (a self-hosted callBroadcast
22
+ # instance, a test stub, a regional override) the product hosts fall back to the
23
+ # configured host unchanged, so a single-host deployment keeps working. A caller
24
+ # who needs Messaging Service against a custom host points `messaging_base_url`
25
+ # (or `conversations_base_url`) at their own subdomain explicitly.
26
+ module Hosts
27
+ module_function
28
+
29
+ # Swap the `voiceml` label of a `*.voicetel.com` host for `product`.
30
+ #
31
+ # Returns `base_url` unchanged when the host is not a `voiceml.*.voicetel.com`
32
+ # style host (e.g. a self-hosted instance), so single-host deployments keep
33
+ # working without special-casing.
34
+ def derive_product_host(base_url, product)
35
+ uri = URI.parse(base_url)
36
+ host = uri.host
37
+ return base_url if host.nil? || !host.end_with?('.voicetel.com')
38
+
39
+ labels = host.split('.')
40
+ idx = labels.index('voiceml')
41
+ return base_url if idx.nil?
42
+
43
+ labels[idx] = product
44
+ uri.host = labels.join('.')
45
+ uri.query = nil
46
+ uri.fragment = nil
47
+ uri.to_s
48
+ end
49
+
50
+ # Return `[default, messaging, conversations]` base URLs.
51
+ #
52
+ # Explicit overrides win; otherwise each product host is derived from
53
+ # `base_url` (see module docstring). All three are returned without a
54
+ # trailing slash.
55
+ def resolve_product_base_urls(base_url, messaging_base_url: nil, conversations_base_url: nil)
56
+ default = Transport.strip_trailing_slashes(base_url)
57
+ messaging = Transport.strip_trailing_slashes(messaging_base_url || derive_product_host(default, 'messaging'))
58
+ conversations = Transport.strip_trailing_slashes(conversations_base_url || derive_product_host(default, 'conversations'))
59
+ [default, messaging, conversations]
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'common'
4
+
5
+ module VoiceML
6
+ # Twilio-compatible Account resource. The voiceml SDK is single-account
7
+ # (the credential's account is implicit on every call), so this model
8
+ # exists primarily to decode `GET/POST /Accounts/{Sid}.json` responses
9
+ # round-trip with the Twilio shape β€” useful for migration tooling that
10
+ # echoes the account record back to the caller.
11
+ class Account
12
+ ATTRIBUTES = %w[
13
+ sid friendly_name status type auth_token owner_account_sid
14
+ date_created date_updated subresource_uris uri
15
+ ].freeze
16
+
17
+ attr_reader(*ATTRIBUTES.map(&:to_sym))
18
+
19
+ def initialize(attrs = {})
20
+ ATTRIBUTES.each do |field|
21
+ value = attrs.key?(field) ? attrs[field] : attrs[field.to_sym]
22
+ instance_variable_set("@#{field}", value)
23
+ end
24
+ end
25
+
26
+ def self.from_hash(hash)
27
+ return nil if hash.nil?
28
+
29
+ new(hash)
30
+ end
31
+ end
32
+
33
+ # `GET /Accounts/{Sid}/Balance.json` β€” running balance for the account in
34
+ # the account's settlement currency. No `sid` field on the wire (the
35
+ # balance is account-scoped, not its own resource).
36
+ class Balance
37
+ ATTRIBUTES = %w[account_sid balance currency].freeze
38
+
39
+ attr_reader(*ATTRIBUTES.map(&:to_sym))
40
+
41
+ def initialize(attrs = {})
42
+ ATTRIBUTES.each do |field|
43
+ value = attrs.key?(field) ? attrs[field] : attrs[field.to_sym]
44
+ instance_variable_set("@#{field}", value)
45
+ end
46
+ end
47
+
48
+ def self.from_hash(hash)
49
+ return nil if hash.nil?
50
+
51
+ new(hash)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,239 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'common'
4
+ require_relative 'voice_v1' # V1Pageable lives here; Assistants v1 reuses the same `meta` envelope
5
+
6
+ module VoiceML
7
+ # Assistants v1 (`/v1/Assistants`, `/v1/Tools`, `/v1/Knowledge`, `/v1/Sessions`,
8
+ # `/v1/Policies`) β€” Twilio AI-Assistants surface. Same /v1 conventions as Voice v1
9
+ # and Conversations v1: Basic-auth account resolution, ISO-8601 dates, `meta`
10
+ # list envelope (V1Pageable). 7 families for v0.9.1:
11
+ # - Assistant (aia_asst_...)
12
+ # - Tool (aia_tool_...) + ToolWithPolicies fetch shape
13
+ # - Knowledge (aia_know_...) + KnowledgeStatus + KnowledgeChunk
14
+ # - Session
15
+ # - Message (aia_msg_...) + SendMessageResponse
16
+ # - Feedback (aia_fdbk_...)
17
+ # - Policy (aia_plcy_...)
18
+
19
+ # AssistantsV1Assistant β€” `aia_asst_...`. Used by list/create/update/delete responses.
20
+ class AssistantsV1Assistant
21
+ ATTRIBUTES = %w[
22
+ account_sid customer_ai id model name owner url personality_prompt
23
+ date_created date_updated
24
+ ].freeze
25
+ attr_reader(*ATTRIBUTES.map(&:to_sym))
26
+ def initialize(attrs = {})
27
+ ATTRIBUTES.each { |f| instance_variable_set("@#{f}", attrs.key?(f) ? attrs[f] : attrs[f.to_sym]) }
28
+ end
29
+ def self.from_hash(h); h.nil? ? nil : new(h); end
30
+ end
31
+
32
+ class AssistantsV1AssistantList
33
+ include V1Pageable
34
+ attr_reader :assistants
35
+ def initialize(hash = {})
36
+ assign_meta_fields(hash)
37
+ @assistants = (hash['assistants'] || []).map { |h| AssistantsV1Assistant.from_hash(h) }
38
+ end
39
+ end
40
+
41
+ # AssistantsV1AssistantWithToolsAndKnowledge β€” fetch-one shape that includes the
42
+ # attached tools and knowledge arrays inline.
43
+ class AssistantsV1AssistantWithToolsAndKnowledge
44
+ ATTRIBUTES = %w[
45
+ account_sid customer_ai id model name owner url personality_prompt
46
+ date_created date_updated tools knowledge
47
+ ].freeze
48
+ attr_reader(*ATTRIBUTES.map(&:to_sym))
49
+ def initialize(attrs = {})
50
+ ATTRIBUTES.each { |f| instance_variable_set("@#{f}", attrs.key?(f) ? attrs[f] : attrs[f.to_sym]) }
51
+ end
52
+ def self.from_hash(h); h.nil? ? nil : new(h); end
53
+ end
54
+
55
+ # AssistantsV1Tool β€” `aia_tool_...`.
56
+ class AssistantsV1Tool
57
+ ATTRIBUTES = %w[
58
+ account_sid description enabled id meta name requires_auth type url
59
+ date_created date_updated
60
+ ].freeze
61
+ attr_reader(*ATTRIBUTES.map(&:to_sym))
62
+ def initialize(attrs = {})
63
+ ATTRIBUTES.each { |f| instance_variable_set("@#{f}", attrs.key?(f) ? attrs[f] : attrs[f.to_sym]) }
64
+ end
65
+ def self.from_hash(h); h.nil? ? nil : new(h); end
66
+ end
67
+
68
+ class AssistantsV1ToolList
69
+ include V1Pageable
70
+ attr_reader :tools
71
+ def initialize(hash = {})
72
+ assign_meta_fields(hash)
73
+ @tools = (hash['tools'] || []).map { |h| AssistantsV1Tool.from_hash(h) }
74
+ end
75
+ end
76
+
77
+ # AssistantsV1ToolWithPolicies β€” fetch-one shape that includes the policies array inline.
78
+ class AssistantsV1ToolWithPolicies
79
+ ATTRIBUTES = %w[
80
+ account_sid description enabled id meta name requires_auth type url
81
+ date_created date_updated policies
82
+ ].freeze
83
+ attr_reader(*ATTRIBUTES.map(&:to_sym))
84
+ def initialize(attrs = {})
85
+ ATTRIBUTES.each { |f| instance_variable_set("@#{f}", attrs.key?(f) ? attrs[f] : attrs[f.to_sym]) }
86
+ end
87
+ def self.from_hash(h); h.nil? ? nil : new(h); end
88
+ end
89
+
90
+ # AssistantsV1Knowledge β€” `aia_know_...`.
91
+ class AssistantsV1Knowledge
92
+ ATTRIBUTES = %w[
93
+ account_sid description id knowledge_source_details name status type url
94
+ embedding_model date_created date_updated
95
+ ].freeze
96
+ attr_reader(*ATTRIBUTES.map(&:to_sym))
97
+ def initialize(attrs = {})
98
+ ATTRIBUTES.each { |f| instance_variable_set("@#{f}", attrs.key?(f) ? attrs[f] : attrs[f.to_sym]) }
99
+ end
100
+ def self.from_hash(h); h.nil? ? nil : new(h); end
101
+ end
102
+
103
+ class AssistantsV1KnowledgeList
104
+ include V1Pageable
105
+ attr_reader :knowledge
106
+ def initialize(hash = {})
107
+ assign_meta_fields(hash)
108
+ @knowledge = (hash['knowledge'] || []).map { |h| AssistantsV1Knowledge.from_hash(h) }
109
+ end
110
+ end
111
+
112
+ # AssistantsV1KnowledgeStatus β€” read-only ingestion status snapshot.
113
+ class AssistantsV1KnowledgeStatus
114
+ ATTRIBUTES = %w[account_sid status last_status date_updated].freeze
115
+ attr_reader(*ATTRIBUTES.map(&:to_sym))
116
+ def initialize(attrs = {})
117
+ ATTRIBUTES.each { |f| instance_variable_set("@#{f}", attrs.key?(f) ? attrs[f] : attrs[f.to_sym]) }
118
+ end
119
+ def self.from_hash(h); h.nil? ? nil : new(h); end
120
+ end
121
+
122
+ # AssistantsV1KnowledgeChunk β€” single retrieved chunk of indexed Knowledge content.
123
+ class AssistantsV1KnowledgeChunk
124
+ ATTRIBUTES = %w[account_sid content metadata date_created date_updated].freeze
125
+ attr_reader(*ATTRIBUTES.map(&:to_sym))
126
+ def initialize(attrs = {})
127
+ ATTRIBUTES.each { |f| instance_variable_set("@#{f}", attrs.key?(f) ? attrs[f] : attrs[f.to_sym]) }
128
+ end
129
+ def self.from_hash(h); h.nil? ? nil : new(h); end
130
+ end
131
+
132
+ class AssistantsV1KnowledgeChunkList
133
+ include V1Pageable
134
+ attr_reader :chunks
135
+ def initialize(hash = {})
136
+ assign_meta_fields(hash)
137
+ @chunks = (hash['chunks'] || []).map { |h| AssistantsV1KnowledgeChunk.from_hash(h) }
138
+ end
139
+ end
140
+
141
+ # AssistantsV1Session β€” a chat session between an identity and an Assistant.
142
+ class AssistantsV1Session
143
+ ATTRIBUTES = %w[
144
+ id account_sid assistant_id verified identity date_created date_updated
145
+ ].freeze
146
+ attr_reader(*ATTRIBUTES.map(&:to_sym))
147
+ def initialize(attrs = {})
148
+ ATTRIBUTES.each { |f| instance_variable_set("@#{f}", attrs.key?(f) ? attrs[f] : attrs[f.to_sym]) }
149
+ end
150
+ def self.from_hash(h); h.nil? ? nil : new(h); end
151
+ end
152
+
153
+ class AssistantsV1SessionList
154
+ include V1Pageable
155
+ attr_reader :sessions
156
+ def initialize(hash = {})
157
+ assign_meta_fields(hash)
158
+ @sessions = (hash['sessions'] || []).map { |h| AssistantsV1Session.from_hash(h) }
159
+ end
160
+ end
161
+
162
+ # AssistantsV1Message β€” `aia_msg_...`. One message in an Assistant session.
163
+ class AssistantsV1Message
164
+ ATTRIBUTES = %w[
165
+ id account_sid assistant_id session_id identity role content meta
166
+ date_created date_updated
167
+ ].freeze
168
+ attr_reader(*ATTRIBUTES.map(&:to_sym))
169
+ def initialize(attrs = {})
170
+ ATTRIBUTES.each { |f| instance_variable_set("@#{f}", attrs.key?(f) ? attrs[f] : attrs[f.to_sym]) }
171
+ end
172
+ def self.from_hash(h); h.nil? ? nil : new(h); end
173
+ end
174
+
175
+ class AssistantsV1MessageList
176
+ include V1Pageable
177
+ attr_reader :messages
178
+ def initialize(hash = {})
179
+ assign_meta_fields(hash)
180
+ @messages = (hash['messages'] || []).map { |h| AssistantsV1Message.from_hash(h) }
181
+ end
182
+ end
183
+
184
+ # AssistantsV1SendMessageResponse β€” POST /v1/Assistants/{id}/Messages return shape.
185
+ # Distinct from AssistantsV1Message: carries the model's reply body + flag/abort signals.
186
+ class AssistantsV1SendMessageResponse
187
+ ATTRIBUTES = %w[status flagged aborted session_id account_sid body error].freeze
188
+ attr_reader(*ATTRIBUTES.map(&:to_sym))
189
+ def initialize(attrs = {})
190
+ ATTRIBUTES.each { |f| instance_variable_set("@#{f}", attrs.key?(f) ? attrs[f] : attrs[f.to_sym]) }
191
+ end
192
+ def self.from_hash(h); h.nil? ? nil : new(h); end
193
+ end
194
+
195
+ # AssistantsV1Feedback β€” `aia_fdbk_...`. Numeric score + text feedback on a message.
196
+ class AssistantsV1Feedback
197
+ ATTRIBUTES = %w[
198
+ assistant_id id account_sid user_sid message_id score session_id text
199
+ date_created date_updated
200
+ ].freeze
201
+ attr_reader(*ATTRIBUTES.map(&:to_sym))
202
+ def initialize(attrs = {})
203
+ ATTRIBUTES.each { |f| instance_variable_set("@#{f}", attrs.key?(f) ? attrs[f] : attrs[f.to_sym]) }
204
+ end
205
+ def self.from_hash(h); h.nil? ? nil : new(h); end
206
+ end
207
+
208
+ class AssistantsV1FeedbackList
209
+ include V1Pageable
210
+ attr_reader :feedbacks
211
+ def initialize(hash = {})
212
+ assign_meta_fields(hash)
213
+ @feedbacks = (hash['feedbacks'] || []).map { |h| AssistantsV1Feedback.from_hash(h) }
214
+ end
215
+ end
216
+
217
+ # AssistantsV1Policy β€” `aia_plcy_...`. Read-only β€” listed under /v1/Policies (filterable
218
+ # by ToolId/KnowledgeId) and embedded inline on Tool fetch responses.
219
+ class AssistantsV1Policy
220
+ ATTRIBUTES = %w[
221
+ id name description account_sid user_sid type policy_details
222
+ date_created date_updated
223
+ ].freeze
224
+ attr_reader(*ATTRIBUTES.map(&:to_sym))
225
+ def initialize(attrs = {})
226
+ ATTRIBUTES.each { |f| instance_variable_set("@#{f}", attrs.key?(f) ? attrs[f] : attrs[f.to_sym]) }
227
+ end
228
+ def self.from_hash(h); h.nil? ? nil : new(h); end
229
+ end
230
+
231
+ class AssistantsV1PolicyList
232
+ include V1Pageable
233
+ attr_reader :policies
234
+ def initialize(hash = {})
235
+ assign_meta_fields(hash)
236
+ @policies = (hash['policies'] || []).map { |h| AssistantsV1Policy.from_hash(h) }
237
+ end
238
+ end
239
+ end