anthropic 1.1.1 → 1.6.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.
Files changed (197) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +123 -0
  3. data/README.md +32 -16
  4. data/lib/anthropic/client.rb +5 -2
  5. data/lib/anthropic/errors.rb +22 -0
  6. data/lib/anthropic/helpers/bedrock/client.rb +34 -4
  7. data/lib/anthropic/helpers/streaming/events.rb +141 -0
  8. data/lib/anthropic/helpers/streaming/message_stream.rb +238 -0
  9. data/lib/anthropic/helpers/streaming.rb +37 -0
  10. data/lib/anthropic/helpers/vertex/client.rb +4 -1
  11. data/lib/anthropic/internal/stream.rb +4 -2
  12. data/lib/anthropic/internal/transport/base_client.rb +29 -3
  13. data/lib/anthropic/internal/type/array_of.rb +7 -1
  14. data/lib/anthropic/internal/type/base_model.rb +80 -24
  15. data/lib/anthropic/internal/type/base_stream.rb +3 -1
  16. data/lib/anthropic/internal/type/boolean.rb +7 -1
  17. data/lib/anthropic/internal/type/converter.rb +69 -34
  18. data/lib/anthropic/internal/type/enum.rb +16 -5
  19. data/lib/anthropic/internal/type/file_input.rb +6 -1
  20. data/lib/anthropic/internal/type/hash_of.rb +7 -1
  21. data/lib/anthropic/internal/type/union.rb +26 -16
  22. data/lib/anthropic/internal/type/unknown.rb +7 -1
  23. data/lib/anthropic/internal/util.rb +8 -9
  24. data/lib/anthropic/models/anthropic_beta.rb +3 -0
  25. data/lib/anthropic/models/beta/beta_base64_pdf_block.rb +1 -65
  26. data/lib/anthropic/models/beta/beta_citation_char_location.rb +7 -1
  27. data/lib/anthropic/models/beta/beta_citation_content_block_location.rb +7 -1
  28. data/lib/anthropic/models/beta/beta_citation_page_location.rb +7 -1
  29. data/lib/anthropic/models/beta/beta_citation_search_result_location.rb +55 -0
  30. data/lib/anthropic/models/beta/beta_citation_search_result_location_param.rb +55 -0
  31. data/lib/anthropic/models/beta/beta_citations_delta.rb +5 -3
  32. data/lib/anthropic/models/beta/beta_content_block.rb +5 -5
  33. data/lib/anthropic/models/beta/beta_content_block_param.rb +20 -17
  34. data/lib/anthropic/models/beta/beta_message.rb +6 -2
  35. data/lib/anthropic/models/beta/beta_message_param.rb +3 -3
  36. data/lib/anthropic/models/beta/beta_raw_content_block_start_event.rb +7 -7
  37. data/lib/anthropic/models/beta/beta_request_document_block.rb +75 -0
  38. data/lib/anthropic/models/beta/beta_search_result_block_param.rb +55 -0
  39. data/lib/anthropic/models/beta/beta_text_block.rb +2 -2
  40. data/lib/anthropic/models/beta/beta_text_block_param.rb +2 -2
  41. data/lib/anthropic/models/beta/beta_text_citation.rb +3 -1
  42. data/lib/anthropic/models/beta/beta_text_citation_param.rb +3 -1
  43. data/lib/anthropic/models/beta/beta_tool.rb +7 -1
  44. data/lib/anthropic/models/beta/beta_tool_result_block_param.rb +6 -4
  45. data/lib/anthropic/models/beta/beta_tool_text_editor_20250728.rb +49 -0
  46. data/lib/anthropic/models/beta/beta_tool_union.rb +9 -7
  47. data/lib/anthropic/models/beta/message_count_tokens_params.rb +18 -10
  48. data/lib/anthropic/models/beta/message_create_params.rb +9 -3
  49. data/lib/anthropic/models/beta/messages/batch_create_params.rb +9 -3
  50. data/lib/anthropic/models/citation_char_location.rb +7 -1
  51. data/lib/anthropic/models/citation_content_block_location.rb +7 -1
  52. data/lib/anthropic/models/citation_page_location.rb +7 -1
  53. data/lib/anthropic/models/citation_search_result_location_param.rb +51 -0
  54. data/lib/anthropic/models/citations_delta.rb +5 -3
  55. data/lib/anthropic/models/citations_search_result_location.rb +51 -0
  56. data/lib/anthropic/models/content_block.rb +5 -5
  57. data/lib/anthropic/models/content_block_param.rb +14 -11
  58. data/lib/anthropic/models/message.rb +6 -2
  59. data/lib/anthropic/models/message_count_tokens_params.rb +9 -3
  60. data/lib/anthropic/models/message_count_tokens_tool.rb +5 -1
  61. data/lib/anthropic/models/message_create_params.rb +9 -3
  62. data/lib/anthropic/models/message_param.rb +3 -3
  63. data/lib/anthropic/models/messages/batch_create_params.rb +9 -3
  64. data/lib/anthropic/models/model.rb +10 -21
  65. data/lib/anthropic/models/raw_content_block_start_event.rb +7 -7
  66. data/lib/anthropic/models/search_result_block_param.rb +51 -0
  67. data/lib/anthropic/models/text_block.rb +2 -2
  68. data/lib/anthropic/models/text_block_param.rb +2 -2
  69. data/lib/anthropic/models/text_citation.rb +3 -1
  70. data/lib/anthropic/models/text_citation_param.rb +3 -1
  71. data/lib/anthropic/models/tool.rb +7 -1
  72. data/lib/anthropic/models/tool_result_block_param.rb +6 -4
  73. data/lib/anthropic/models/tool_text_editor_20250429.rb +36 -0
  74. data/lib/anthropic/models/tool_text_editor_20250728.rb +45 -0
  75. data/lib/anthropic/models/tool_union.rb +5 -1
  76. data/lib/anthropic/models/tool_use_block.rb +6 -0
  77. data/lib/anthropic/models.rb +14 -4
  78. data/lib/anthropic/resources/beta/messages.rb +73 -6
  79. data/lib/anthropic/resources/messages.rb +68 -7
  80. data/lib/anthropic/streaming.rb +5 -0
  81. data/lib/anthropic/version.rb +1 -1
  82. data/lib/anthropic.rb +14 -0
  83. data/rbi/anthropic/errors.rbi +16 -0
  84. data/rbi/anthropic/helpers/bedrock/client.rbi +17 -6
  85. data/rbi/anthropic/helpers/streaming/events.rbi +95 -0
  86. data/rbi/anthropic/helpers/streaming/message_stream.rbi +73 -0
  87. data/rbi/anthropic/helpers/vertex/client.rbi +17 -6
  88. data/rbi/anthropic/internal/transport/base_client.rbi +1 -1
  89. data/rbi/anthropic/internal/type/base_stream.rbi +8 -1
  90. data/rbi/anthropic/internal/type/boolean.rbi +2 -0
  91. data/rbi/anthropic/internal/type/converter.rbi +69 -15
  92. data/rbi/anthropic/internal/type/union.rbi +14 -2
  93. data/rbi/anthropic/internal/type/unknown.rbi +2 -0
  94. data/rbi/anthropic/internal/util.rbi +2 -0
  95. data/rbi/anthropic/models/anthropic_beta.rbi +2 -0
  96. data/rbi/anthropic/models/beta/beta_base64_pdf_block.rbi +1 -128
  97. data/rbi/anthropic/models/beta/beta_citation_char_location.rbi +6 -0
  98. data/rbi/anthropic/models/beta/beta_citation_content_block_location.rbi +6 -0
  99. data/rbi/anthropic/models/beta/beta_citation_page_location.rbi +6 -0
  100. data/rbi/anthropic/models/beta/beta_citation_search_result_location.rbi +78 -0
  101. data/rbi/anthropic/models/beta/beta_citation_search_result_location_param.rbi +79 -0
  102. data/rbi/anthropic/models/beta/beta_citations_delta.rbi +4 -2
  103. data/rbi/anthropic/models/beta/beta_content_block.rbi +3 -3
  104. data/rbi/anthropic/models/beta/beta_content_block_param.rbi +8 -7
  105. data/rbi/anthropic/models/beta/beta_message.rbi +11 -3
  106. data/rbi/anthropic/models/beta/beta_raw_content_block_start_event.rbi +6 -6
  107. data/rbi/anthropic/models/beta/beta_request_document_block.rbi +140 -0
  108. data/rbi/anthropic/models/beta/beta_search_result_block_param.rbi +91 -0
  109. data/rbi/anthropic/models/beta/beta_text_block.rbi +2 -1
  110. data/rbi/anthropic/models/beta/beta_text_block_param.rbi +6 -3
  111. data/rbi/anthropic/models/beta/beta_text_citation.rbi +2 -1
  112. data/rbi/anthropic/models/beta/beta_text_citation_param.rbi +2 -1
  113. data/rbi/anthropic/models/beta/beta_tool.rbi +14 -5
  114. data/rbi/anthropic/models/beta/beta_tool_result_block_param.rbi +2 -1
  115. data/rbi/anthropic/models/beta/beta_tool_text_editor_20250728.rbi +82 -0
  116. data/rbi/anthropic/models/beta/beta_tool_union.rbi +6 -5
  117. data/rbi/anthropic/models/beta/message_count_tokens_params.rbi +44 -27
  118. data/rbi/anthropic/models/beta/message_create_params.rbi +38 -22
  119. data/rbi/anthropic/models/beta/messages/batch_create_params.rbi +38 -22
  120. data/rbi/anthropic/models/citation_char_location.rbi +6 -0
  121. data/rbi/anthropic/models/citation_content_block_location.rbi +6 -0
  122. data/rbi/anthropic/models/citation_page_location.rbi +6 -0
  123. data/rbi/anthropic/models/citation_search_result_location_param.rbi +74 -0
  124. data/rbi/anthropic/models/citations_delta.rbi +4 -2
  125. data/rbi/anthropic/models/citations_search_result_location.rbi +74 -0
  126. data/rbi/anthropic/models/content_block.rbi +3 -3
  127. data/rbi/anthropic/models/content_block_param.rbi +6 -5
  128. data/rbi/anthropic/models/message.rbi +11 -3
  129. data/rbi/anthropic/models/message_count_tokens_params.rbi +22 -2
  130. data/rbi/anthropic/models/message_count_tokens_tool.rbi +2 -0
  131. data/rbi/anthropic/models/message_create_params.rbi +22 -2
  132. data/rbi/anthropic/models/messages/batch_create_params.rbi +22 -2
  133. data/rbi/anthropic/models/model.rbi +4 -8
  134. data/rbi/anthropic/models/raw_content_block_start_event.rbi +6 -6
  135. data/rbi/anthropic/models/search_result_block_param.rbi +77 -0
  136. data/rbi/anthropic/models/text_block.rbi +2 -1
  137. data/rbi/anthropic/models/text_block_param.rbi +6 -3
  138. data/rbi/anthropic/models/text_citation.rbi +2 -1
  139. data/rbi/anthropic/models/text_citation_param.rbi +2 -1
  140. data/rbi/anthropic/models/tool.rbi +16 -5
  141. data/rbi/anthropic/models/tool_result_block_param.rbi +5 -1
  142. data/rbi/anthropic/models/tool_text_editor_20250429.rbi +62 -0
  143. data/rbi/anthropic/models/tool_text_editor_20250728.rbi +72 -0
  144. data/rbi/anthropic/models/tool_union.rbi +2 -0
  145. data/rbi/anthropic/models.rbi +12 -0
  146. data/rbi/anthropic/resources/beta/messages.rbi +39 -18
  147. data/rbi/anthropic/resources/messages.rbi +318 -3
  148. data/rbi/anthropic/streaming.rbi +5 -0
  149. data/sig/anthropic/errors.rbs +9 -0
  150. data/sig/anthropic/helpers/streaming/events.rbs +117 -0
  151. data/sig/anthropic/helpers/streaming/message_stream.rbs +57 -0
  152. data/sig/anthropic/internal/transport/base_client.rbs +1 -1
  153. data/sig/anthropic/internal/type/base_stream.rbs +4 -0
  154. data/sig/anthropic/internal/type/converter.rbs +24 -1
  155. data/sig/anthropic/internal/type/union.rbs +2 -2
  156. data/sig/anthropic/models/anthropic_beta.rbs +2 -0
  157. data/sig/anthropic/models/beta/beta_base64_pdf_block.rbs +1 -58
  158. data/sig/anthropic/models/beta/beta_citation_char_location.rbs +5 -0
  159. data/sig/anthropic/models/beta/beta_citation_content_block_location.rbs +5 -0
  160. data/sig/anthropic/models/beta/beta_citation_page_location.rbs +5 -0
  161. data/sig/anthropic/models/beta/beta_citation_search_result_location.rbs +54 -0
  162. data/sig/anthropic/models/beta/beta_citation_search_result_location_param.rbs +54 -0
  163. data/sig/anthropic/models/beta/beta_citations_delta.rbs +1 -0
  164. data/sig/anthropic/models/beta/beta_content_block.rbs +2 -2
  165. data/sig/anthropic/models/beta/beta_content_block_param.rbs +9 -8
  166. data/sig/anthropic/models/beta/beta_raw_content_block_start_event.rbs +2 -2
  167. data/sig/anthropic/models/beta/beta_request_document_block.rbs +66 -0
  168. data/sig/anthropic/models/beta/beta_search_result_block_param.rbs +53 -0
  169. data/sig/anthropic/models/beta/beta_text_citation.rbs +1 -0
  170. data/sig/anthropic/models/beta/beta_text_citation_param.rbs +1 -0
  171. data/sig/anthropic/models/beta/beta_tool.rbs +14 -3
  172. data/sig/anthropic/models/beta/beta_tool_result_block_param.rbs +1 -0
  173. data/sig/anthropic/models/beta/beta_tool_text_editor_20250728.rbs +39 -0
  174. data/sig/anthropic/models/beta/beta_tool_union.rbs +5 -4
  175. data/sig/anthropic/models/beta/message_count_tokens_params.rbs +5 -4
  176. data/sig/anthropic/models/citation_char_location.rbs +5 -0
  177. data/sig/anthropic/models/citation_content_block_location.rbs +5 -0
  178. data/sig/anthropic/models/citation_page_location.rbs +5 -0
  179. data/sig/anthropic/models/citation_search_result_location_param.rbs +50 -0
  180. data/sig/anthropic/models/citations_delta.rbs +1 -0
  181. data/sig/anthropic/models/citations_search_result_location.rbs +50 -0
  182. data/sig/anthropic/models/content_block.rbs +2 -2
  183. data/sig/anthropic/models/content_block_param.rbs +6 -5
  184. data/sig/anthropic/models/message_count_tokens_tool.rbs +2 -0
  185. data/sig/anthropic/models/model.rbs +4 -10
  186. data/sig/anthropic/models/raw_content_block_start_event.rbs +2 -2
  187. data/sig/anthropic/models/search_result_block_param.rbs +49 -0
  188. data/sig/anthropic/models/text_citation.rbs +1 -0
  189. data/sig/anthropic/models/text_citation_param.rbs +1 -0
  190. data/sig/anthropic/models/tool.rbs +14 -3
  191. data/sig/anthropic/models/tool_result_block_param.rbs +4 -1
  192. data/sig/anthropic/models/tool_text_editor_20250429.rbs +30 -0
  193. data/sig/anthropic/models/tool_text_editor_20250728.rbs +35 -0
  194. data/sig/anthropic/models/tool_union.rbs +2 -0
  195. data/sig/anthropic/models.rbs +10 -0
  196. data/sig/anthropic/streaming.rbs +3 -0
  197. metadata +44 -3
@@ -0,0 +1,238 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Anthropic
4
+ module Helpers
5
+ module Streaming
6
+ # @api private
7
+ #
8
+ # MessageStream provides a Ruby Enumerable interface over Server-Sent Events from
9
+ # the Anthropic API, yielding a mix of raw streaming events and higher-level typed
10
+ # events while maintaining accumulated message state throughout the stream lifecycle.
11
+ #
12
+ #
13
+ # @generic Elem
14
+ class MessageStream
15
+ include Anthropic::Internal::Type::BaseStream
16
+
17
+ # @api private
18
+ #
19
+ # Consumes raw stream events and yields a mix of raw and higher-level typed events while
20
+ # maintaining accumulated message state. This is what's called when you run `each` on the
21
+ # stream.
22
+ #
23
+ # @return [Enumerable<generic<Elem>>]
24
+ private def iterator
25
+ @iterator ||= Anthropic::Internal::Util.chain_fused(@stream) do |y|
26
+ @raw_stream.each do |raw_event|
27
+ @accumated_message_snapshot = accumulate_event(
28
+ event: raw_event,
29
+ current_snapshot: @accumated_message_snapshot
30
+ )
31
+ events_to_yield = build_events(event: raw_event, message_snapshot: @accumated_message_snapshot)
32
+ events_to_yield.each(&y)
33
+ end
34
+ end
35
+ end
36
+
37
+ # @api public
38
+ #
39
+ # Blocks until the stream has been consumed
40
+ #
41
+ # @return [void]
42
+ def until_done = each {} # rubocop:disable Lint/EmptyBlock
43
+
44
+ # @api public
45
+ #
46
+ # Returns an enumerable of text deltas from the streaming response.
47
+ #
48
+ # @return [Enumerable<String>]
49
+ def text
50
+ Anthropic::Internal::Util.chain_fused(@iterator) do |y|
51
+ @iterator.each do |event|
52
+ if event.type == :content_block_delta && event.delta.type == :text_delta
53
+ y << event.delta.text
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ # @api public
60
+ #
61
+ # Returns the complete accumulated Message object after stream completion.
62
+ #
63
+ # @return [Anthropic::Models::Message]
64
+ def accumulated_message
65
+ until_done
66
+ @accumated_message_snapshot
67
+ end
68
+
69
+ # @api public
70
+ #
71
+ # Returns all text content blocks concatenated into a single string.
72
+ # NOTE: Currently the API will only respond with a single content block.
73
+ #
74
+ # Will raise an error if no `text` content blocks were returned.
75
+ # @return [String]
76
+ def accumulated_text
77
+ message = accumulated_message
78
+ text_blocks = []
79
+ message.content.each do |block|
80
+ if block.type == :text
81
+ text_blocks << block.text
82
+ end
83
+ end
84
+
85
+ if text_blocks.empty?
86
+ raise RuntimeError.new("Expected to have received at least 1 text block")
87
+ end
88
+
89
+ text_blocks.join
90
+ end
91
+
92
+ # @api private
93
+ #
94
+ # Builds up a complete Message object as streaming events arrive.
95
+ #
96
+ # @param event [Anthropic::Models::RawMessageStreamEvent] the raw streaming event to process
97
+ # @param current_snapshot [Anthropic::Models::Message, nil] current accumulated message state
98
+ #
99
+ # @return [Anthropic::Models::Message] updated message snapshot with event applied
100
+ private def accumulate_event(event:, current_snapshot:)
101
+ unless event in Anthropic::Models::RawMessageStreamEvent
102
+ message = "Expected event to be a variant of RawMessageStreamEvent, got #{event.class}"
103
+ raise ArgumentError.new(message)
104
+ end
105
+
106
+ if current_snapshot.nil?
107
+ return event.message if event.type == :message_start
108
+
109
+ message = "Unexpected event order, got \"#{event.type}\" before \":message_start\""
110
+ raise RuntimeError.new(message)
111
+ end
112
+
113
+ case event
114
+ in Anthropic::Models::RawMessageStartEvent
115
+ # Use the converter to create a new, isolated copy of the message object.
116
+ # This ensures proper type validation and prevents shared object references
117
+ # that could lead to unintended mutations during streaming accumulation.
118
+ # Matches the Python SDK's approach of explicitly constructing Message objects.
119
+ return Anthropic::Internal::Type::Converter.coerce(Anthropic::Models::Message, event.message)
120
+ in Anthropic::Models::RawContentBlockStartEvent
121
+ current_snapshot.content = (current_snapshot.content || []) + [event.content_block]
122
+ in Anthropic::Models::RawContentBlockDeltaEvent
123
+ content = current_snapshot.content[event.index]
124
+
125
+ case (delta = event.delta)
126
+ in Anthropic::Models::TextDelta if content.type == :text
127
+ content.text += delta.text
128
+ in Anthropic::Models::InputJSONDelta if content.type == :tool_use
129
+ json_buf = content.json_buf.to_s
130
+ json_buf += delta.partial_json
131
+
132
+ content.input = json_buf
133
+ content.json_buf = json_buf
134
+ in Anthropic::Models::CitationsDelta if content.type == :text
135
+ content.citations ||= []
136
+ content.citations << delta.citation
137
+ in Anthropic::Models::ThinkingDelta if content.type == :thinking
138
+ content.thinking += delta.thinking
139
+ in Anthropic::Models::SignatureDelta if content.type == :thinking
140
+ content.signature = delta.signature
141
+ else
142
+ end
143
+ in Anthropic::Models::RawMessageDeltaEvent
144
+ current_snapshot.stop_reason = event.delta.stop_reason
145
+ current_snapshot.stop_sequence = event.delta.stop_sequence
146
+ current_snapshot.usage.output_tokens = event.usage.output_tokens
147
+ else
148
+ end
149
+
150
+ current_snapshot
151
+ end
152
+
153
+ # @api private
154
+ #
155
+ # Determines which events to yield for a given raw streaming event.
156
+ #
157
+ # May transform events into higher-level types (TextEvent, InputJsonEvent),
158
+ # pass through raw events unchanged, or produce multiple events.
159
+ #
160
+ # @param event [Anthropic::Models::RawMessageStreamEvent] the raw event to process
161
+ # @param message_snapshot [Anthropic::Models::Message] current accumulated message state
162
+ #
163
+ # @return [Array<Object>] events to yield (mix of raw and typed events)
164
+ private def build_events(event:, message_snapshot:)
165
+ events_to_yield = []
166
+
167
+ case event
168
+ in Anthropic::Models::RawMessageStopEvent
169
+ events_to_yield << MessageStopEvent.new(
170
+ type: :message_stop,
171
+ message: message_snapshot
172
+ )
173
+ in Anthropic::Models::RawContentBlockDeltaEvent
174
+ events_to_yield << event
175
+ content_block = message_snapshot.content[event.index]
176
+
177
+ case (delta = event.delta)
178
+ in Anthropic::Models::TextDelta if content_block.type == :text
179
+ events_to_yield << Anthropic::Streaming::TextEvent.new(
180
+ type: :text,
181
+ text: delta.text,
182
+ snapshot: content_block.text
183
+ )
184
+ in Anthropic::Models::InputJSONDelta if content_block.type == :tool_use
185
+ events_to_yield << Anthropic::Streaming::InputJsonEvent.new(
186
+ type: :input_json,
187
+ partial_json: delta.partial_json,
188
+ snapshot: content_block.input
189
+ )
190
+ in Anthropic::Models::CitationsDelta if content_block.type == :text
191
+ events_to_yield << Anthropic::Streaming::CitationEvent.new(
192
+ type: :citation,
193
+ citation: delta.citation,
194
+ snapshot: content_block.citations || []
195
+ )
196
+ in Anthropic::Models::ThinkingDelta if content_block.type == :thinking
197
+ events_to_yield << Anthropic::Streaming::ThinkingEvent.new(
198
+ type: :thinking,
199
+ thinking: delta.thinking,
200
+ snapshot: content_block.thinking
201
+ )
202
+ in Anthropic::Models::SignatureDelta if content_block.type == :thinking
203
+ events_to_yield << Anthropic::Streaming::SignatureEvent.new(
204
+ type: :signature,
205
+ signature: content_block.signature
206
+ )
207
+ else
208
+ end
209
+ in Anthropic::Models::RawContentBlockStopEvent
210
+ content_block = message_snapshot.content[event.index]
211
+
212
+ events_to_yield << ContentBlockStopEvent.new(
213
+ type: :content_block_stop,
214
+ index: event.index,
215
+ content_block: content_block
216
+ )
217
+ else
218
+ events_to_yield << event
219
+ end
220
+
221
+ events_to_yield
222
+ end
223
+
224
+ # @api private
225
+ #
226
+ # @param raw_stream [Anthropic::Internal::Type::BaseStream]
227
+ def initialize(raw_stream:)
228
+ # The underlying Server-Sent Event stream from the Anthropic API.
229
+ @raw_stream = raw_stream
230
+ # Accumulated message state that builds up as events are processed.
231
+ @accumated_message_snapshot = nil
232
+ # Lazy enumerable that transforms raw events into consumable events.
233
+ @iterator = iterator
234
+ end
235
+ end
236
+ end
237
+ end
238
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Anthropic
4
+ module Helpers
5
+ module Streaming
6
+ extend Anthropic::Internal::Util::SorbetRuntimeSupport
7
+
8
+ define_sorbet_constant!(:RawMessageEvent) do
9
+ T.type_alias do
10
+ T.any(
11
+ Anthropic::Models::RawMessageStartEvent,
12
+ Anthropic::Models::RawMessageDeltaEvent,
13
+ Anthropic::Models::RawMessageStopEvent,
14
+ Anthropic::Models::RawContentBlockStartEvent,
15
+ Anthropic::Models::RawContentBlockDeltaEvent,
16
+ Anthropic::Models::RawContentBlockStopEvent
17
+ )
18
+ end
19
+ end
20
+
21
+ define_sorbet_constant!(:StreamEvent) do
22
+ T.type_alias do
23
+ T.any(
24
+ Anthropic::Streaming::RawMessageEvent,
25
+ Anthropic::Streaming::TextEvent,
26
+ Anthropic::Streaming::CitationEvent,
27
+ Anthropic::Streaming::ThinkingEvent,
28
+ Anthropic::Streaming::SignatureEvent,
29
+ Anthropic::Streaming::InputJsonEvent,
30
+ Anthropic::Streaming::MessageStopEvent,
31
+ Anthropic::Streaming::ContentBlockStopEvent
32
+ )
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -82,7 +82,10 @@ module Anthropic
82
82
  end
83
83
  @project_id = project_id
84
84
 
85
- base_url ||= ENV.fetch("ANTHROPIC_VERTEX_BASE_URL", "https://#{@region}-aiplatform.googleapis.com/v1")
85
+ base_url ||= ENV.fetch(
86
+ "ANTHROPIC_VERTEX_BASE_URL",
87
+ @region.to_s == "global" ? "https://aiplatform.googleapis.com/v1" : "https://#{@region}-aiplatform.googleapis.com/v1"
88
+ )
86
89
 
87
90
  super(
88
91
  base_url: base_url,
@@ -23,13 +23,15 @@ module Anthropic
23
23
  case msg
24
24
  in {event: "completion", data: String => data}
25
25
  decoded = JSON.parse(data, symbolize_names: true)
26
- y << Anthropic::Internal::Type::Converter.coerce(@model, decoded)
26
+ unwrapped = Anthropic::Internal::Util.dig(decoded, @unwrap)
27
+ y << Anthropic::Internal::Type::Converter.coerce(@model, unwrapped)
27
28
  in {
28
29
  event: "message_start" | "message_delta" | "message_stop" | "content_block_start" | "content_block_delta" | "content_block_stop",
29
30
  data: String => data
30
31
  }
31
32
  decoded = JSON.parse(data, symbolize_names: true)
32
- y << Anthropic::Internal::Type::Converter.coerce(@model, decoded)
33
+ unwrapped = Anthropic::Internal::Util.dig(decoded, @unwrap)
34
+ y << Anthropic::Internal::Type::Converter.coerce(@model, unwrapped)
33
35
  in {event: "ping"}
34
36
  next
35
37
  in {event: "error", data: String => data}
@@ -341,6 +341,23 @@ module Anthropic
341
341
  (@initial_retry_delay * scale * jitter).clamp(0, @max_retry_delay)
342
342
  end
343
343
 
344
+ # @api private
345
+ #
346
+ # Very private API, do not use
347
+ #
348
+ # @param request [Hash{Symbol=>Object}] .
349
+ #
350
+ # @option request [Symbol] :method
351
+ #
352
+ # @option request [URI::Generic] :url
353
+ #
354
+ # @option request [Hash{String=>String}] :headers
355
+ #
356
+ # @option request [Object] :body
357
+ #
358
+ # @return [Hash{Symbol, Object}]
359
+ private def transform_request(request) = request
360
+
344
361
  # @api private
345
362
  #
346
363
  # @param request [Hash{Symbol=>Object}] .
@@ -365,7 +382,8 @@ module Anthropic
365
382
  #
366
383
  # @raise [Anthropic::Errors::APIError]
367
384
  # @return [Array(Integer, Net::HTTPResponse, Enumerable<String>)]
368
- private def send_request(request, redirect_count:, retry_count:, send_retry_header:)
385
+ def send_request(request, redirect_count:, retry_count:, send_retry_header:)
386
+ request = transform_request(request)
369
387
  url, headers, max_retries, timeout = request.fetch_values(:url, :headers, :max_retries, :timeout)
370
388
  input = {**request.except(:timeout), deadline: Anthropic::Internal::Util.monotonic_secs + timeout}
371
389
 
@@ -471,6 +489,7 @@ module Anthropic
471
489
  self.class.validate!(req)
472
490
  model = req.fetch(:model) { Anthropic::Internal::Type::Unknown }
473
491
  opts = req[:options].to_h
492
+ unwrap = req[:unwrap]
474
493
  Anthropic::RequestOptions.validate!(opts)
475
494
  request = build_request(req.except(:options), opts)
476
495
  url = request.fetch(:url)
@@ -487,11 +506,18 @@ module Anthropic
487
506
  decoded = Anthropic::Internal::Util.decode_content(response, stream: stream)
488
507
  case req
489
508
  in {stream: Class => st}
490
- st.new(model: model, url: url, status: status, response: response, stream: decoded)
509
+ st.new(
510
+ model: model,
511
+ url: url,
512
+ status: status,
513
+ response: response,
514
+ unwrap: unwrap,
515
+ stream: decoded
516
+ )
491
517
  in {page: Class => page}
492
518
  page.new(client: self, req: req, headers: response, page_data: decoded)
493
519
  else
494
- unwrapped = Anthropic::Internal::Util.dig(decoded, req[:unwrap])
520
+ unwrapped = Anthropic::Internal::Util.dig(decoded, unwrap)
495
521
  Anthropic::Internal::Type::Converter.coerce(model, unwrapped)
496
522
  end
497
523
  end
@@ -62,10 +62,14 @@ module Anthropic
62
62
  #
63
63
  # @param state [Hash{Symbol=>Object}] .
64
64
  #
65
- # @option state [Boolean, :strong] :strictness
65
+ # @option state [Boolean] :translate_names
66
+ #
67
+ # @option state [Boolean] :strictness
66
68
  #
67
69
  # @option state [Hash{Symbol=>Object}] :exactness
68
70
  #
71
+ # @option state [Class<StandardError>] :error
72
+ #
69
73
  # @option state [Integer] :branched
70
74
  #
71
75
  # @return [Array<Object>, Object]
@@ -74,6 +78,7 @@ module Anthropic
74
78
 
75
79
  unless value.is_a?(Array)
76
80
  exactness[:no] += 1
81
+ state[:error] = TypeError.new("#{value.class} can't be coerced into #{Array}")
77
82
  return value
78
83
  end
79
84
 
@@ -143,6 +148,7 @@ module Anthropic
143
148
  # @option spec [Boolean] :"nil?"
144
149
  def initialize(type_info, spec = {})
145
150
  @item_type_fn = Anthropic::Internal::Type::Converter.type_info(type_info || spec)
151
+ @meta = Anthropic::Internal::Type::Converter.meta_info(type_info, spec)
146
152
  @nilable = spec.fetch(:nil?, false)
147
153
  end
148
154
 
@@ -52,6 +52,7 @@ module Anthropic
52
52
  #
53
53
  # @option spec [Boolean] :"nil?"
54
54
  private def add_field(name_sym, required:, type_info:, spec:)
55
+ meta = Anthropic::Internal::Type::Converter.meta_info(type_info, spec)
55
56
  type_fn, info =
56
57
  case type_info
57
58
  in Proc | Anthropic::Internal::Type::Converter | Class
@@ -60,7 +61,7 @@ module Anthropic
60
61
  [Anthropic::Internal::Type::Converter.type_info(type_info), type_info]
61
62
  end
62
63
 
63
- setter = "#{name_sym}="
64
+ setter = :"#{name_sym}="
64
65
  api_name = info.fetch(:api_name, name_sym)
65
66
  nilable = info.fetch(:nil?, false)
66
67
  const = if required && !nilable
@@ -81,31 +82,65 @@ module Anthropic
81
82
  required: required,
82
83
  nilable: nilable,
83
84
  const: const,
84
- type_fn: type_fn
85
+ type_fn: type_fn,
86
+ meta: meta
85
87
  }
86
88
 
87
- define_method(setter) { @data.store(name_sym, _1) }
89
+ define_method(setter) do |value|
90
+ target = type_fn.call
91
+ state = Anthropic::Internal::Type::Converter.new_coerce_state(translate_names: false)
92
+ coerced = Anthropic::Internal::Type::Converter.coerce(target, value, state: state)
93
+ status = @coerced.store(name_sym, state.fetch(:error) || true)
94
+ stored =
95
+ case [target, status]
96
+ in [Anthropic::Internal::Type::Converter | Symbol, true]
97
+ coerced
98
+ else
99
+ value
100
+ end
101
+ @data.store(name_sym, stored)
102
+ end
88
103
 
104
+ # rubocop:disable Style/CaseEquality
105
+ # rubocop:disable Metrics/BlockLength
89
106
  define_method(name_sym) do
90
107
  target = type_fn.call
91
- value = @data.fetch(name_sym) { const == Anthropic::Internal::OMIT ? nil : const }
92
- state = {strictness: :strong, exactness: {yes: 0, no: 0, maybe: 0}, branched: 0}
93
- if (nilable || !required) && value.nil?
94
- nil
95
- else
96
- Anthropic::Internal::Type::Converter.coerce(
97
- target, value, state: state
108
+
109
+ case @coerced[name_sym]
110
+ in true | false if Anthropic::Internal::Type::Converter === target
111
+ @data.fetch(name_sym)
112
+ in ::StandardError => e
113
+ raise Anthropic::Errors::ConversionError.new(
114
+ on: self.class,
115
+ method: __method__,
116
+ target: target,
117
+ value: @data.fetch(name_sym),
118
+ cause: e
98
119
  )
120
+ else
121
+ Kernel.then do
122
+ value = @data.fetch(name_sym) { const == Anthropic::Internal::OMIT ? nil : const }
123
+ state = Anthropic::Internal::Type::Converter.new_coerce_state(translate_names: false)
124
+ if (nilable || !required) && value.nil?
125
+ nil
126
+ else
127
+ Anthropic::Internal::Type::Converter.coerce(
128
+ target, value, state: state
129
+ )
130
+ end
131
+ rescue StandardError => e
132
+ raise Anthropic::Errors::ConversionError.new(
133
+ on: self.class,
134
+ method: __method__,
135
+ target: target,
136
+ value: value,
137
+ cause: e
138
+ )
139
+ end
99
140
  end
100
- rescue StandardError => e
101
- cls = self.class.name.split("::").last
102
- message = [
103
- "Failed to parse #{cls}.#{__method__} from #{value.class} to #{target.inspect}.",
104
- "To get the unparsed API response, use #{cls}[#{__method__.inspect}].",
105
- "Cause: #{e.message}"
106
- ].join(" ")
107
- raise Anthropic::Errors::ConversionError.new(message)
108
141
  end
142
+ # rubocop:enable Metrics/BlockLength
143
+ # rubocop:enable Style/CaseEquality
109
144
  end
110
145
 
111
146
  # @api private
@@ -205,23 +240,28 @@ module Anthropic
205
240
  #
206
241
  # @param state [Hash{Symbol=>Object}] .
207
242
  #
208
- # @option state [Boolean, :strong] :strictness
243
+ # @option state [Boolean] :translate_names
244
+ #
245
+ # @option state [Boolean] :strictness
209
246
  #
210
247
  # @option state [Hash{Symbol=>Object}] :exactness
211
248
  #
249
+ # @option state [Class<StandardError>] :error
250
+ #
212
251
  # @option state [Integer] :branched
213
252
  #
214
253
  # @return [self, Object]
215
254
  def coerce(value, state:)
216
255
  exactness = state.fetch(:exactness)
217
256
 
218
- if value.is_a?(self.class)
257
+ if value.is_a?(self)
219
258
  exactness[:yes] += 1
220
259
  return value
221
260
  end
222
261
 
223
262
  unless (val = Anthropic::Internal::Util.coerce_hash(value)).is_a?(Hash)
224
263
  exactness[:no] += 1
264
+ state[:error] = TypeError.new("#{value.class} can't be coerced into #{Hash}")
225
265
  return value
226
266
  end
227
267
  exactness[:yes] += 1
@@ -229,13 +269,15 @@ module Anthropic
229
269
  keys = val.keys.to_set
230
270
  instance = new
231
271
  data = instance.to_h
272
+ status = instance.instance_variable_get(:@coerced)
232
273
 
233
274
  # rubocop:disable Metrics/BlockLength
234
275
  fields.each do |name, field|
235
276
  mode, required, target = field.fetch_values(:mode, :required, :type)
236
277
  api_name, nilable, const = field.fetch_values(:api_name, :nilable, :const)
278
+ src_name = state.fetch(:translate_names) ? api_name : name
237
279
 
238
- unless val.key?(api_name)
280
+ unless val.key?(src_name)
239
281
  if required && mode != :dump && const == Anthropic::Internal::OMIT
240
282
  exactness[nilable ? :maybe : :no] += 1
241
283
  else
@@ -244,9 +286,10 @@ module Anthropic
244
286
  next
245
287
  end
246
288
 
247
- item = val.fetch(api_name)
248
- keys.delete(api_name)
289
+ item = val.fetch(src_name)
290
+ keys.delete(src_name)
249
291
 
292
+ state[:error] = nil
250
293
  converted =
251
294
  if item.nil? && (nilable || !required)
252
295
  exactness[nilable ? :yes : :maybe] += 1
@@ -260,6 +303,8 @@ module Anthropic
260
303
  item
261
304
  end
262
305
  end
306
+
307
+ status.store(name, state.fetch(:error) || true)
263
308
  data.store(name, converted)
264
309
  end
265
310
  # rubocop:enable Metrics/BlockLength
@@ -434,7 +479,18 @@ module Anthropic
434
479
  # Create a new instance of a model.
435
480
  #
436
481
  # @param data [Hash{Symbol=>Object}, self]
437
- def initialize(data = {}) = (@data = Anthropic::Internal::Util.coerce_hash!(data).to_h)
482
+ def initialize(data = {})
483
+ @data = {}
484
+ @coerced = {}
485
+ Anthropic::Internal::Util.coerce_hash!(data).each do
486
+ if self.class.known_fields.key?(_1)
487
+ public_send(:"#{_1}=", _2)
488
+ else
489
+ @data.store(_1, _2)
490
+ @coerced.store(_1, false)
491
+ end
492
+ end
493
+ end
438
494
 
439
495
  class << self
440
496
  # @api private
@@ -64,12 +64,14 @@ module Anthropic
64
64
  # @param url [URI::Generic]
65
65
  # @param status [Integer]
66
66
  # @param response [Net::HTTPResponse]
67
+ # @param unwrap [Symbol, Integer, Array<Symbol, Integer>, Proc]
67
68
  # @param stream [Enumerable<Object>]
68
- def initialize(model:, url:, status:, response:, stream:)
69
+ def initialize(model:, url:, status:, response:, unwrap:, stream:)
69
70
  @model = model
70
71
  @url = url
71
72
  @status = status
72
73
  @response = response
74
+ @unwrap = unwrap
73
75
  @stream = stream
74
76
  @iterator = iterator
75
77
 
@@ -31,14 +31,20 @@ module Anthropic
31
31
  class << self
32
32
  # @api private
33
33
  #
34
+ # Coerce value to Boolean if possible, otherwise return the original value.
35
+ #
34
36
  # @param value [Boolean, Object]
35
37
  #
36
38
  # @param state [Hash{Symbol=>Object}] .
37
39
  #
38
- # @option state [Boolean, :strong] :strictness
40
+ # @option state [Boolean] :translate_names
41
+ #
42
+ # @option state [Boolean] :strictness
39
43
  #
40
44
  # @option state [Hash{Symbol=>Object}] :exactness
41
45
  #
46
+ # @option state [Class<StandardError>] :error
47
+ #
42
48
  # @option state [Integer] :branched
43
49
  #
44
50
  # @return [Boolean, Object]