anthropic-rb 0.4.0 → 0.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b5e066105f3157621c27e2f974ed43bde9f14e6e55187d1d7a4f095b79a5a1ba
4
- data.tar.gz: f32d479f7ec06c29a0347f69084a4378a64d69233d78ed280f5a1c924e15d708
3
+ metadata.gz: 0e743c686b28964af68a40c2e322caf6986284e2922d93d5acc0e2783128a5e5
4
+ data.tar.gz: 6ae03121d9cae81f15abdacb69a119428a9780f629fde7985a1a22be60192ea4
5
5
  SHA512:
6
- metadata.gz: 90040c86504a3dfd28e0093d1b8525252387f4e978271908aa158d1cffe10a3dd5766aadc243bc31f45ca6dd38ecf1259d009b2da9f2f78670fcc114c76574a3
7
- data.tar.gz: 8ca46c62fe204b3b2403cce1267b6661f43c52159c8d31877b9d2522be604427996eedcbac67ba22a1b6df703259ededd98ea2b86d49755d7aa057835458321c
6
+ metadata.gz: 352230790e752e44670cb9328b6f93139c1b5c6be9717b65996f1571a34acc9fd7fb28047bddcb32e5ce0c1826faeecb8364d9fd52ace649df868aad86428f9d
7
+ data.tar.gz: 2d94f8ac749b7d87d2081d2733a5fa52a0771a3dac3a7347af36ae3c40451b9c76362e8dc45540fb060b8c66d79c3fb3c7b351e08fedb489378758c84f9fc81e
data/.reek.yml CHANGED
@@ -4,10 +4,19 @@ detectors:
4
4
  - Anthropic#self.api_key=
5
5
  TooManyStatements:
6
6
  exclude:
7
- - 'Anthropic::Client#self.post'
8
- BooleanParameter:
9
- exclude:
10
- - 'Anthropic::Messages#initialize'
7
+ - 'Anthropic::Api::Concerns::Validatable#schema'
8
+ - 'Anthropic::Api::Base#beta_config'
9
+ - 'Anthropic::Api::Base#version_config'
10
+ - 'Anthropic::Bootstrapper#self.load_betas'
11
+ - 'Anthropic::Bootstrapper#self.load_versions'
12
+ - 'Anthropic::Client::Standard#self.post'
13
+ - 'Anthropic::Client::Streaming#self.post'
11
14
  DuplicateMethodCall:
12
15
  exclude:
13
- - 'Anthropic::Messages#create'
16
+ - 'Anthropic::Api::Base#version_config'
17
+ InstanceVariableAssumption:
18
+ exclude:
19
+ - 'Anthropic::Api::Base'
20
+ NestedIterators:
21
+ exclude:
22
+ - 'Anthropic::Bootstrapper#self.load_versions'
data/.rubocop.yml CHANGED
@@ -4,7 +4,7 @@ require:
4
4
  - rubocop-rspec
5
5
 
6
6
  AllCops:
7
- TargetRubyVersion: 3.1
7
+ TargetRubyVersion: 3.2
8
8
  NewCops: enable
9
9
  Naming/RescuedExceptionsVariableName:
10
10
  Enabled: false
data/.tool-versions CHANGED
@@ -1 +1 @@
1
- ruby 3.1.4
1
+ ruby 3.2.4
data/CHANGELOG.md CHANGED
@@ -2,6 +2,36 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.6.0] - 2024-06-25
6
+
7
+ ### Breaking Changes
8
+
9
+ - Responses from the Anthropic API are now encapsulated as data objects.
10
+ - Dropped support for Ruby versions prior to 3.2.4.
11
+
12
+ ### Added
13
+
14
+ - Added link to `anthropic-rb-cookbook`.
15
+
16
+ ### Removed
17
+
18
+ - Removed streaming contraint for tools use.
19
+
20
+ ### Updated
21
+
22
+ - Refactored project for maintainability.
23
+ - Updated project funding.
24
+
25
+ ## [0.5.0] - 2024-04-22
26
+
27
+ ### Updated
28
+
29
+ - Refactored project for maintainability.
30
+
31
+ ### Breaking Changes
32
+
33
+ - You must now pass the beta ID when enabling a beta feature. The only current beta is for Tools (id: tools-2024-04-04). Previously, you would pass `true` to enable the beta.
34
+
5
35
  ## [0.4.0] - 2024-04-20
6
36
 
7
37
  ### Added
@@ -57,7 +87,9 @@
57
87
 
58
88
  - Initial release
59
89
 
60
- [Unreleased]: https://github.com/dickdavis/anthropic-rb/compare/v0.4.0...HEAD
90
+ [Unreleased]: https://github.com/dickdavis/anthropic-rb/compare/v0.6.0...HEAD
91
+ [0.6.0]: https://github.com/dickdavis/anthropic-rb/compare/v0.5.0...v0.6.0
92
+ [0.5.0]: https://github.com/dickdavis/anthropic-rb/compare/v0.4.0...v0.5.0
61
93
  [0.4.0]: https://github.com/dickdavis/anthropic-rb/compare/v0.3.0...v0.4.0
62
94
  [0.3.0]: https://github.com/dickdavis/anthropic-rb/compare/v0.2.5...v0.3.0
63
95
  [0.2.5]: https://github.com/dickdavis/anthropic-rb/compare/v0.2.3...v0.2.5
data/README.md CHANGED
@@ -4,6 +4,8 @@ Ruby bindings for the Anthropic API. This library is unofficial and is not affil
4
4
 
5
5
  The goal of this project is feature parity with Anthropic's Python SDK until an official Ruby SDK is available.
6
6
 
7
+ You can find examples of usage in the [anthropic-rb-cookbook](https://github.com/dickdavis/anthropic-rb-cookbook/).
8
+
7
9
  ## Usage
8
10
 
9
11
  anthropic-rb will default to the value of the `ANTHROPIC_API_KEY` environment variable. However, you may initialize the library with your API key. You must set your API key before using the library.
@@ -34,35 +36,61 @@ You can send a request to the Messages API.
34
36
  Anthropic.messages.create(model: 'claude-2.1', max_tokens: 200, messages: [{role: 'user', content: 'Yo what up?'}])
35
37
 
36
38
  # Output =>
37
- # {
38
- # id: "msg_013ePdwEkb4RMC1hCE61Hbm8",
39
- # type: "message",
40
- # role: "assistant",
41
- # content: [{type: "text", text: "Hello! Not much up with me, just chatting. How about you?"}],
42
- # model: "claude-2.1",
43
- # stop_reason: "end_turn",
44
- # stop_sequence: nil
45
- # }
39
+ #<data Anthropic::Client::Response status="success", body={:id=>"msg_01UqHiw6oFLjMYiLV8hkXsrR", :type=>"message", :role=>"assistant", :model=>"claude-2.1", :content=>[{:type=>"text", :text=>"Hello! Not much up with me, just chatting with you. How's it going?"}], :stop_reason=>"end_turn", :stop_sequence=>nil, :usage=>{:input_tokens=>13, :output_tokens=>22}}>
46
40
  ```
47
41
 
48
42
  Alternatively, you can stream the response:
49
43
 
50
44
  ```ruby
51
- Anthropic.messages.create(model: 'claude-2.1', max_tokens: 200, messages: [{role: 'user', content: 'Yo what up?'}], stream: true) do |event|
45
+ options = {
46
+ model: 'claude-2.1',
47
+ max_tokens: 200,
48
+ messages: [{role: 'user', content: 'Yo what up?'}],
49
+ stream: true
50
+ }
51
+
52
+ Anthropic.messages.create(**options) do |event|
52
53
  puts event
53
54
  end
54
55
 
55
56
  # Output =>
56
- # { type: 'message_start', message: { id: 'msg_012pkeozZynwyNvSagwL7kMw', type: 'message', role: 'assistant', content: [], model: 'claude-2.1', stop_reason: nil, stop_sequence: nil } }
57
- # { type: 'content_block_start', index: 0, content_block: { type: 'text', text: '' } }
58
- # { type: 'content_block_delta', index: 0, delta: { type: 'text_delta', text: 'Hello' } }
59
- # { type: 'content_block_delta', index: 0, delta: { type: 'text_delta', text: '.' } }
60
- # { type: 'content_block_stop', index: 0 }
61
- # { type: 'message_delta', delta: { stop_reason: 'end_turn', stop_sequence: nil } }
62
- # { type: 'message_stop' }
57
+ #<data Anthropic::Client::Response status="success", body={:type=>"message_start", :message=>{:id=>"msg_01EsYcQkBJrHrtgpY5ZcLzvf", :type=>"message", :role=>"assistant", :model=>"claude-2.1", :content=>[], :stop_reason=>nil, :stop_sequence=>nil, :usage=>{:input_tokens=>13, :output_tokens=>1}}}>
58
+ #<data Anthropic::Client::Response status="success", body={:type=>"content_block_start", :index=>0, :content_block=>{:type=>"text", :text=>""}}>
59
+ #<data Anthropic::Client::Response status="success", body={:type=>"content_block_delta", :index=>0, :delta=>{:type=>"text_delta", :text=>"Hello"}}>
60
+ #<data Anthropic::Client::Response status="success", body={:type=>"content_block_delta", :index=>0, :delta=>{:type=>"text_delta", :text=>"!"}}>
61
+ #<data Anthropic::Client::Response status="success", body={:type=>"content_block_delta", :index=>0, :delta=>{:type=>"text_delta", :text=>" Not"}}>
62
+ #<data Anthropic::Client::Response status="success", body={:type=>"content_block_delta", :index=>0, :delta=>{:type=>"text_delta", :text=>" much"}}>
63
+ #<data Anthropic::Client::Response status="success", body={:type=>"content_block_delta", :index=>0, :delta=>{:type=>"text_delta", :text=>" up"}}>
64
+ #<data Anthropic::Client::Response status="success", body={:type=>"content_block_delta", :index=>0, :delta=>{:type=>"text_delta", :text=>" with"}}>
65
+ #<data Anthropic::Client::Response status="success", body={:type=>"content_block_delta", :index=>0, :delta=>{:type=>"text_delta", :text=>" me"}}>
66
+ #<data Anthropic::Client::Response status="success", body={:type=>"content_block_delta", :index=>0, :delta=>{:type=>"text_delta", :text=>","}}>
67
+ #<data Anthropic::Client::Response status="success", body={:type=>"content_block_delta", :index=>0, :delta=>{:type=>"text_delta", :text=>" I"}}>
68
+ #<data Anthropic::Client::Response status="success", body={:type=>"content_block_delta", :index=>0, :delta=>{:type=>"text_delta", :text=>"'m"}}>
69
+ #<data Anthropic::Client::Response status="success", body={:type=>"content_block_delta", :index=>0, :delta=>{:type=>"text_delta", :text=>" an"}}>
70
+ #<data Anthropic::Client::Response status="success", body={:type=>"content_block_delta", :index=>0, :delta=>{:type=>"text_delta", :text=>" AI"}}>
71
+ #<data Anthropic::Client::Response status="success", body={:type=>"content_block_delta", :index=>0, :delta=>{:type=>"text_delta", :text=>" assistant"}}>
72
+ #<data Anthropic::Client::Response status="success", body={:type=>"content_block_delta", :index=>0, :delta=>{:type=>"text_delta", :text=>" create"}}>
73
+ #<data Anthropic::Client::Response status="success", body={:type=>"content_block_delta", :index=>0, :delta=>{:type=>"text_delta", :text=>"d by"}}>
74
+ #<data Anthropic::Client::Response status="success", body={:type=>"content_block_delta", :index=>0, :delta=>{:type=>"text_delta", :text=>" An"}}>
75
+ #<data Anthropic::Client::Response status="success", body={:type=>"content_block_delta", :index=>0, :delta=>{:type=>"text_delta", :text=>"throp"}}>
76
+ #<data Anthropic::Client::Response status="success", body={:type=>"content_block_delta", :index=>0, :delta=>{:type=>"text_delta", :text=>"ic"}}>
77
+ #<data Anthropic::Client::Response status="success", body={:type=>"content_block_delta", :index=>0, :delta=>{:type=>"text_delta", :text=>"."}}>
78
+ #<data Anthropic::Client::Response status="success", body={:type=>"content_block_stop", :index=>0}>
79
+ #<data Anthropic::Client::Response status="success", body={:type=>"message_delta", :delta=>{:stop_reason=>"end_turn", :stop_sequence=>nil}, :usage=>{:output_tokens=>23}}>
80
+ #<data Anthropic::Client::Response status="success", body={:type=>"message_stop"}>
81
+
82
+ # Or, if you just want to print the text content:
83
+ Anthropic.messages.create(**options) do |event|
84
+ next unless event.body[:type] == 'content_block_delta'
85
+
86
+ print event.body[:delta][:text]
87
+ end
88
+
89
+ # Output =>
90
+ # Hello! Not much up with me, I'm an AI assistant created by Anthropic.
63
91
  ```
64
92
 
65
- You can also experiment with the new tools beta by passing the `beta` flag when calling the API. This will ensure each request includes the correct beta header.
93
+ You can also pass in a list of tools the assistant can use. You can find out more information about tools in the [documentation](https://docs.anthropic.com/claude/docs/tool-use).
66
94
 
67
95
  ```ruby
68
96
  tools = [
@@ -79,7 +107,7 @@ tools = [
79
107
  }
80
108
  ]
81
109
 
82
- Anthropic.messages(beta: true).create(
110
+ Anthropic.messages.create(
83
111
  model: 'claude-3-opus-20240229',
84
112
  max_tokens: 200,
85
113
  tools:,
@@ -87,8 +115,6 @@ Anthropic.messages(beta: true).create(
87
115
  )
88
116
  ```
89
117
 
90
- Streaming is currently not supported by the tools beta. You can find out more information about tools in the [documentation](https://docs.anthropic.com/claude/docs/tool-use).
91
-
92
118
  ### Completions API
93
119
 
94
120
  To make a request to the Completions API:
@@ -101,38 +127,41 @@ Anthropic.completions.create(
101
127
  )
102
128
 
103
129
  # Output =>
104
- # {
105
- # completion: "Hello! Not much going on with me, just chatting. How about you?",
106
- # stop_reason: "stop_sequence",
107
- # model: "claude-2.1",
108
- # stop: "\n\nHuman:",
109
- # log_id: "2496914137c520ec2b4ae8315864bcf3a4c6ce9f2e3c96e13be4c004587313ca"
110
- # }
130
+ #<data Anthropic::Client::Response status="success", body={:type=>"completion", :id=>"compl_01Y9ptPR7xGHaH9rC3ffJExU", :completion=>" Hello! Not much going on here. How about you?", :stop_reason=>"stop_sequence", :model=>"claude-2.1", :stop=>"\n\nHuman:", :log_id=>"compl_01Y9ptPR7xGHaH9rC3ffJExU"}>
111
131
  ```
112
132
 
113
133
  Alternatively, you can stream the response:
114
134
 
115
135
  ```ruby
116
- Anthropic.completions.create(model: 'claude-2', max_tokens_to_sample: 200, prompt: 'Human: Yo what up?\n\nAssistant:', stream: true) do |event|
136
+ options = {
137
+ model: 'claude-2',
138
+ max_tokens_to_sample: 200,
139
+ prompt: 'Human: Yo what up?\n\nAssistant:',
140
+ stream: true
141
+ }
142
+
143
+ Anthropic.completions.create(**options) do |event|
117
144
  puts event
118
145
  end
119
146
 
120
147
  # Output =>
121
- # { type: 'completion', id: 'compl_01G6cEfdZtLEEJVRzwUShiDY', completion: ' Hello', stop_reason: nil, model: 'claude-2.1', stop: nil, log_id: 'compl_01G6cEfdZtLEEJVRzwUShiDY' }
122
- # { type: 'completion', id: 'compl_01G6cEfdZtLEEJVRzwUShiDY', completion: '!', stop_reason: nil, model: 'claude-2.1', stop: nil, log_id: 'compl_01G6cEfdZtLEEJVRzwUShiDY' }
123
- # { type: 'completion', id: 'compl_01G6cEfdZtLEEJVRzwUShiDY', completion: ' Not', stop_reason: nil, model: 'claude-2.1', stop: nil, log_id: 'compl_01G6cEfdZtLEEJVRzwUShiDY' }
124
- # { type: 'completion', id: 'compl_01G6cEfdZtLEEJVRzwUShiDY', completion: ' much', stop_reason: nil, model: 'claude-2.1', stop: nil, log_id: 'compl_01G6cEfdZtLEEJVRzwUShiDY' }
125
- # { type: 'completion', id: 'compl_01G6cEfdZtLEEJVRzwUShiDY', completion: ',', stop_reason: nil, model: 'claude-2.1', stop: nil, log_id: 'compl_01G6cEfdZtLEEJVRzwUShiDY' }
126
- # { type: 'completion', id: 'compl_01G6cEfdZtLEEJVRzwUShiDY', completion: ' just', stop_reason: nil, model: 'claude-2.1', stop: nil, log_id: 'compl_01G6cEfdZtLEEJVRzwUShiDY' }
127
- # { type: 'completion', id: 'compl_01G6cEfdZtLEEJVRzwUShiDY', completion: ' chatting', stop_reason: nil, model: 'claude-2.1', stop: nil, log_id: 'compl_01G6cEfdZtLEEJVRzwUShiDY' }
128
- # { type: 'completion', id: 'compl_01G6cEfdZtLEEJVRzwUShiDY', completion: ' with', stop_reason: nil, model: 'claude-2.1', stop: nil, log_id: 'compl_01G6cEfdZtLEEJVRzwUShiDY' }
129
- # { type: 'completion', id: 'compl_01G6cEfdZtLEEJVRzwUShiDY', completion: ' people', stop_reason: nil, model: 'claude-2.1', stop: nil, log_id: 'compl_01G6cEfdZtLEEJVRzwUShiDY' }
130
- # { type: 'completion', id: 'compl_01G6cEfdZtLEEJVRzwUShiDY', completion: '.', stop_reason: nil, model: 'claude-2.1', stop: nil, log_id: 'compl_01G6cEfdZtLEEJVRzwUShiDY' }
131
- # { type: 'completion', id: 'compl_01G6cEfdZtLEEJVRzwUShiDY', completion: ' How', stop_reason: nil, model: 'claude-2.1', stop: nil, log_id: 'compl_01G6cEfdZtLEEJVRzwUShiDY' }
132
- # { type: 'completion', id: 'compl_01G6cEfdZtLEEJVRzwUShiDY', completion: ' about', stop_reason: nil, model: 'claude-2.1', stop: nil, log_id: 'compl_01G6cEfdZtLEEJVRzwUShiDY' }
133
- # { type: 'completion', id: 'compl_01G6cEfdZtLEEJVRzwUShiDY', completion: ' you', stop_reason: nil, model: 'claude-2.1', stop: nil, log_id: 'compl_01G6cEfdZtLEEJVRzwUShiDY' }
134
- # { type: 'completion', id: 'compl_01G6cEfdZtLEEJVRzwUShiDY', completion: '?', stop_reason: nil, model: 'claude-2.1', stop: nil, log_id: 'compl_01G6cEfdZtLEEJVRzwUShiDY' }
135
- # { type: 'completion', id: 'compl_01G6cEfdZtLEEJVRzwUShiDY', completion: '', stop_reason: 'stop_sequence', model: 'claude-2.1', stop: "\n\nHuman:", log_id: 'compl_01G6cEfdZtLEEJVRzwUShiDY' }
148
+ #<data Anthropic::Client::Response status="success", body={:type=>"completion", :id=>"compl_015AktggW7tcM4w11YpkuMbP", :completion=>" Hello", :stop_reason=>nil, :model=>"claude-2.1", :stop=>nil, :log_id=>"compl_015AktggW7tcM4w11YpkuMbP"}>
149
+ #<data Anthropic::Client::Response status="success", body={:type=>"completion", :id=>"compl_015AktggW7tcM4w11YpkuMbP", :completion=>"!", :stop_reason=>nil, :model=>"claude-2.1", :stop=>nil, :log_id=>"compl_015AktggW7tcM4w11YpkuMbP"}>
150
+ #<data Anthropic::Client::Response status="success", body={:type=>"completion", :id=>"compl_015AktggW7tcM4w11YpkuMbP", :completion=>" Not", :stop_reason=>nil, :model=>"claude-2.1", :stop=>nil, :log_id=>"compl_015AktggW7tcM4w11YpkuMbP"}>
151
+ #<data Anthropic::Client::Response status="success", body={:type=>"completion", :id=>"compl_015AktggW7tcM4w11YpkuMbP", :completion=>" much", :stop_reason=>nil, :model=>"claude-2.1", :stop=>nil, :log_id=>"compl_015AktggW7tcM4w11YpkuMbP"}>
152
+ #<data Anthropic::Client::Response status="success", body={:type=>"completion", :id=>"compl_015AktggW7tcM4w11YpkuMbP", :completion=>" going", :stop_reason=>nil, :model=>"claude-2.1", :stop=>nil, :log_id=>"compl_015AktggW7tcM4w11YpkuMbP"}>
153
+ #<data Anthropic::Client::Response status="success", body={:type=>"completion", :id=>"compl_015AktggW7tcM4w11YpkuMbP", :completion=>" on", :stop_reason=>nil, :model=>"claude-2.1", :stop=>nil, :log_id=>"compl_015AktggW7tcM4w11YpkuMbP"}>
154
+ #<data Anthropic::Client::Response status="success", body={:type=>"completion", :id=>"compl_015AktggW7tcM4w11YpkuMbP", :completion=>" here", :stop_reason=>nil, :model=>"claude-2.1", :stop=>nil, :log_id=>"compl_015AktggW7tcM4w11YpkuMbP"}>
155
+ #<data Anthropic::Client::Response status="success", body={:type=>"completion", :id=>"compl_015AktggW7tcM4w11YpkuMbP", :completion=>".", :stop_reason=>nil, :model=>"claude-2.1", :stop=>nil, :log_id=>"compl_015AktggW7tcM4w11YpkuMbP"}>
156
+ #<data Anthropic::Client::Response status="success", body={:type=>"completion", :id=>"compl_015AktggW7tcM4w11YpkuMbP", :completion=>"", :stop_reason=>"stop_sequence", :model=>"claude-2.1", :stop=>"\n\nHuman:", :log_id=>"compl_015AktggW7tcM4w11YpkuMbP"}>
157
+
158
+ # Or, if you just want to print the text content:
159
+ Anthropic.completions.create(**options) do |event|
160
+ print event.body[:completion]
161
+ end
162
+
163
+ # Output =>
164
+ # Hello! Not much, just chatting with you. How's it going?
136
165
  ```
137
166
 
138
167
  ## Installation
@@ -161,6 +190,8 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
161
190
 
162
191
  To install this gem onto your local machine, run `bundle exec rake install`.
163
192
 
193
+ To run a sanity check, run `ruby sanity_check.rb`.
194
+
164
195
  To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
165
196
 
166
197
  ## Contributing
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'concerns/requestable'
4
+ require_relative 'concerns/validatable'
5
+
6
+ module Anthropic
7
+ module Api
8
+ # Error for when a beta feature is configured incorrectly.
9
+ class InvalidBetaConfigurationError < StandardError; end
10
+
11
+ # Error for when API version is missing a schema.
12
+ class MissingSchemaError < StandardError; end
13
+
14
+ # Error for when the provided params do not match the API schema
15
+ class SchemaValidationError < StandardError; end
16
+
17
+ # Error for when the API version is not supported.
18
+ class UnsupportedApiVersionError < StandardError; end
19
+
20
+ # Error for when the provided beta is not supported.
21
+ class UnsupportedBetaError < StandardError; end
22
+
23
+ ##
24
+ # Provides a base class for APIs
25
+ class Base
26
+ include Anthropic::Api::Concerns::Requestable
27
+ include Anthropic::Api::Concerns::Validatable
28
+
29
+ def initialize(beta: nil)
30
+ @beta = beta
31
+ end
32
+
33
+ private
34
+
35
+ attr_reader :beta
36
+
37
+ def api
38
+ self.class.name.split('::').last.downcase
39
+ end
40
+
41
+ def version_config
42
+ return @version_config if defined?(@version_config)
43
+
44
+ @version_config ||= catch(:version_found) do
45
+ found_config = Anthropic.versions[api.to_sym].find { |config| config['version'] == Anthropic.api_version }
46
+ unless found_config
47
+ raise Anthropic::Api::UnsupportedApiVersionError, "Unsupported API version: #{Anthropic.api_version}"
48
+ end
49
+
50
+ throw :version_found, found_config
51
+ end
52
+ end
53
+
54
+ def beta_config
55
+ return @beta_config if defined?(@beta_config)
56
+
57
+ @beta_config = catch(:beta_found) do
58
+ found_config = Anthropic.betas.find { |config| config['id'] == beta }
59
+ raise Anthropic::Api::UnsupportedBetaError, "#{beta} not supported" unless found_config
60
+
61
+ throw :beta_found, found_config
62
+ end
63
+ end
64
+
65
+ def beta_loaded?(name)
66
+ return false unless beta
67
+
68
+ beta_config['id'] == name
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Anthropic
4
+ module Api
5
+ ##
6
+ # Provides bindings for the Anthropic completions API
7
+ class Completions < Base
8
+ def create(**params, &)
9
+ validate!(params)
10
+ return post(params) unless params[:stream]
11
+
12
+ post_as_stream(params, &)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Anthropic
4
+ module Api
5
+ module Concerns
6
+ ##
7
+ # Provides helpers for sending API requests
8
+ module Requestable
9
+ def post(params)
10
+ Anthropic::Client::Standard.post(uri, params, additional_headers)
11
+ end
12
+
13
+ def post_as_stream(params, &)
14
+ Anthropic::Client::Streaming.post(uri, params, additional_headers, &)
15
+ end
16
+
17
+ def uri
18
+ "#{Anthropic.api_host}#{version_config['endpoint']}"
19
+ end
20
+
21
+ def additional_headers
22
+ {}.merge(beta_headers)
23
+ end
24
+
25
+ def beta_headers
26
+ return {} unless beta
27
+
28
+ header = beta_config['header']
29
+ raise Anthropic::Api::InvalidBetaConfigurationError, "Missing header: #{beta}" unless header
30
+
31
+ header
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Anthropic
4
+ module Api
5
+ module Concerns
6
+ ##
7
+ # Provides helpers for validating params against the API schema
8
+ module Validatable
9
+ def validate!(params)
10
+ JSON::Validator.validate!(schema, params)
11
+ rescue JSON::Schema::ValidationError => error
12
+ raise Anthropic::Api::SchemaValidationError, error.message
13
+ end
14
+
15
+ def schema
16
+ api_schema = version_config['schema']
17
+
18
+ unless api_schema
19
+ raise Anthropic::Api::MissingSchemaError, "Missing schema for API version: #{Anthropic.api_version}"
20
+ end
21
+
22
+ if beta
23
+ beta_schema = beta_config['schema']
24
+ raise Anthropic::Api::InvalidBetaConfigurationError, "Missing beta schema: #{beta}" unless beta_schema
25
+
26
+ api_schema['properties'].merge!(beta_schema)
27
+ end
28
+
29
+ api_schema
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Anthropic
4
+ module Api
5
+ ##
6
+ # Provides bindings for the Anthropic messages API
7
+ class Messages < Base
8
+ def create(**params, &)
9
+ validate!(params)
10
+ return post(params) unless params[:stream]
11
+
12
+ post_as_stream(params, &)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'httpx'
4
+
5
+ module Anthropic
6
+ ##
7
+ # Provides methods for bootstrapping the Anthropic gem
8
+ module Bootstrapper
9
+ def self.load_betas
10
+ directory_path = File.expand_path('../../schemas/betas', __dir__)
11
+ raise "Directory not found: #{directory_path}" unless Dir.exist?(directory_path)
12
+
13
+ file_paths = Dir.glob(File.join(directory_path, '*.json'))
14
+
15
+ file_paths.map do |file_path|
16
+ JSON.parse(File.read(file_path))
17
+ end
18
+ end
19
+
20
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
21
+ def self.load_versions
22
+ directory_path = File.expand_path('../../schemas/versions', __dir__)
23
+ raise "Directory not found: #{directory_path}" unless Dir.exist?(directory_path)
24
+
25
+ versions = {}
26
+
27
+ Dir.glob(File.join(directory_path, '*')).each do |subdirectory_path|
28
+ next unless File.directory?(subdirectory_path)
29
+
30
+ subdirectory_name = File.basename(subdirectory_path)
31
+ file_paths = Dir.glob(File.join(subdirectory_path, '*.json'))
32
+
33
+ versions[subdirectory_name.to_sym] = file_paths.map do |file_path|
34
+ JSON.parse(File.read(file_path))
35
+ end
36
+ end
37
+
38
+ versions
39
+ end
40
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
41
+ end
42
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'httpx'
4
+
5
+ module Anthropic
6
+ module Client
7
+ ##
8
+ # Error when the server experienced an internal error.
9
+ class ApiError < StandardError; end
10
+
11
+ ##
12
+ # Error when the API key is invalid.
13
+ class AuthenticationError < StandardError; end
14
+
15
+ ##
16
+ # Error when the resource already exists.
17
+ class ConflictError < StandardError; end
18
+
19
+ ##
20
+ # Error when the request is malformed.
21
+ class InvalidRequestError < StandardError; end
22
+
23
+ ##
24
+ # Error when the resource is not found.
25
+ class NotFoundError < StandardError; end
26
+
27
+ ##
28
+ # Error when the API servers are overloaded.
29
+ class OverloadedError < StandardError; end
30
+
31
+ ##
32
+ # Error when the account does not have permission for the operation.
33
+ class PermissionError < StandardError; end
34
+
35
+ ##
36
+ # Error when the request exceeds the rate limit.
37
+ class RateLimitError < StandardError; end
38
+
39
+ ##
40
+ # Error when the resource cannot be processed.
41
+ class UnprocessableEntityError < StandardError; end
42
+
43
+ ##
44
+ # Defines a data object for responses
45
+ Response = Data.define(:status, :body)
46
+
47
+ ##
48
+ # Provides a base class for clients
49
+ class Base
50
+ class << self
51
+ private
52
+
53
+ def build_response(response)
54
+ response_hash = JSON.parse(response, symbolize_names: true)
55
+ status = response_hash[:type] == 'error' ? 'failure' : 'success'
56
+ Anthropic::Client::Response.new(status:, body: response_hash)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Anthropic
4
+ module Client
5
+ ##
6
+ # Provides a client for sending standard HTTP requests.
7
+ class Standard < Base
8
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
9
+ def self.post(url, data, headers = {})
10
+ response = HTTPX.with(
11
+ headers: {
12
+ 'Content-Type' => 'application/json',
13
+ 'x-api-key' => Anthropic.api_key,
14
+ 'anthropic-version' => Anthropic.api_version
15
+ }.merge(headers)
16
+ ).post(url, json: data)
17
+
18
+ response_data = build_response(response.body)
19
+
20
+ case response.status
21
+ when 200
22
+ response_data
23
+ when 400
24
+ raise Anthropic::Client::InvalidRequestError, response_data
25
+ when 401
26
+ raise Anthropic::Client::AuthenticationError, response_data
27
+ when 403
28
+ raise Anthropic::Client::PermissionError, response_data
29
+ when 404
30
+ raise Anthropic::Client::NotFoundError, response_data
31
+ when 409
32
+ raise Anthropic::Client::ConflictError, response_data
33
+ when 422
34
+ raise Anthropic::Client::UnprocessableEntityError, response_data
35
+ when 429
36
+ raise Anthropic::Client::RateLimitError, response_data
37
+ when 500
38
+ raise Anthropic::Client::ApiError, response_data
39
+ when 529
40
+ raise Anthropic::Client::OverloadedError, response_data
41
+ end
42
+ end
43
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Anthropic
4
+ module Client
5
+ ##
6
+ # Provides a client for sending streaming HTTP requests.
7
+ class Streaming < Base
8
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
9
+ def self.post(url, data, headers = {})
10
+ response = HTTPX.plugin(:stream).with(
11
+ headers: {
12
+ 'Content-Type' => 'application/json',
13
+ 'x-api-key' => Anthropic.api_key,
14
+ 'anthropic-version' => Anthropic.api_version
15
+ }.merge(headers)
16
+ ).post(url, json: data, stream: true)
17
+
18
+ response.each_line do |line|
19
+ type, event = line.split(/(\w+\b:\s)/)[1..2]
20
+ next unless type&.start_with?('data') && event
21
+
22
+ response_data = build_response(event)
23
+ yield response_data unless %w[ping error].include?(response_data.body[:type])
24
+ end
25
+ rescue HTTPX::HTTPError => error
26
+ case error.response.status
27
+ when 400
28
+ raise Anthropic::Client::InvalidRequestError
29
+ when 401
30
+ raise Anthropic::Client::AuthenticationError
31
+ when 403
32
+ raise Anthropic::Client::PermissionError
33
+ when 404
34
+ raise Anthropic::Client::NotFoundError
35
+ when 409
36
+ raise Anthropic::Client::ConflictError
37
+ when 422
38
+ raise Anthropic::Client::UnprocessableEntityError
39
+ when 429
40
+ raise Anthropic::Client::RateLimitError
41
+ when 500
42
+ raise Anthropic::Client::ApiError
43
+ when 529
44
+ raise Anthropic::Client::OverloadedError
45
+ end
46
+ end
47
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
48
+ end
49
+ end
50
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Anthropic
4
- VERSION = '0.4.0'
4
+ VERSION = '0.6.0'
5
5
  end
data/lib/anthropic.rb CHANGED
@@ -4,24 +4,20 @@ require 'httpx'
4
4
  require 'json'
5
5
  require 'json-schema'
6
6
 
7
- require_relative 'anthropic/client'
8
- require_relative 'anthropic/completions'
9
- require_relative 'anthropic/messages'
10
- require_relative 'anthropic/version'
7
+ Dir[File.join(__dir__, 'anthropic/', '**', '*.rb')].each { |file| require_relative file }
11
8
 
12
9
  ##
13
10
  # Namespace for anthropic-rb gem
14
11
  module Anthropic
15
- ##
16
- # Default error class
17
- class Error < StandardError; end
18
-
19
12
  def self.setup
20
13
  yield self
14
+ @betas = Bootstrapper.load_betas
15
+ @versions = Bootstrapper.load_versions
21
16
  end
22
17
 
23
18
  def self.reset
24
19
  @api_key = nil
20
+ @api_host = nil
25
21
  @api_version = nil
26
22
  end
27
23
 
@@ -33,6 +29,14 @@ module Anthropic
33
29
  @api_key = api_key
34
30
  end
35
31
 
32
+ def self.api_host
33
+ @api_host || ENV.fetch('ANTHROPIC_API_HOST', 'https://api.anthropic.com')
34
+ end
35
+
36
+ def self.api_host=(api_host = nil)
37
+ @api_host = api_host
38
+ end
39
+
36
40
  def self.api_version
37
41
  @api_version || ENV.fetch('ANTHROPIC_API_VERSION', '2023-06-01')
38
42
  end
@@ -41,11 +45,19 @@ module Anthropic
41
45
  @api_version = api_version
42
46
  end
43
47
 
48
+ def self.betas
49
+ @betas
50
+ end
51
+
52
+ def self.versions
53
+ @versions
54
+ end
55
+
44
56
  def self.completions
45
- Completions.new
57
+ Anthropic::Api::Completions.new
46
58
  end
47
59
 
48
60
  def self.messages(...)
49
- Messages.new(...)
61
+ Anthropic::Api::Messages.new(...)
50
62
  end
51
63
  end
data/sanity_check.rb ADDED
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'anthropic'
4
+
5
+ Anthropic.setup do |config|
6
+ config.api_key = ENV.fetch('ANTHROPIC_API_KEY')
7
+ end
8
+
9
+ puts "\nTesting non-streaming messages API"
10
+ puts Anthropic.messages.create(
11
+ model: 'claude-2.1',
12
+ max_tokens: 200,
13
+ messages: [{ role: 'user', content: 'Yo what up?' }]
14
+ )
15
+
16
+ puts "\nTesting streaming messages API"
17
+ Anthropic.messages.create(
18
+ model: 'claude-2.1',
19
+ max_tokens: 200,
20
+ messages: [{ role: 'user', content: 'Yo what up?' }],
21
+ stream: true
22
+ ) { |event| puts event }
23
+
24
+ puts "\nTesting tools"
25
+ tools = [
26
+ {
27
+ name: 'get_weather',
28
+ description: 'Get the current weather in a given location',
29
+ input_schema: {
30
+ type: 'object',
31
+ properties: {
32
+ location: { type: 'string' }
33
+ },
34
+ required: ['location']
35
+ }
36
+ }
37
+ ]
38
+
39
+ puts Anthropic.messages(beta: 'tools-2024-04-04').create(
40
+ model: 'claude-3-opus-20240229',
41
+ max_tokens: 200,
42
+ tools:,
43
+ messages: [{ role: 'user', content: 'What is the weather like in Nashville?' }]
44
+ )
45
+
46
+ puts "\nTesting completions API"
47
+ puts Anthropic.completions.create(
48
+ model: 'claude-2',
49
+ max_tokens_to_sample: 200,
50
+ prompt: 'Human: Yo what up?\n\nAssistant:'
51
+ )
52
+
53
+ puts "\nTesting streaming completions API"
54
+ Anthropic.completions.create(
55
+ model: 'claude-2',
56
+ max_tokens_to_sample: 200,
57
+ prompt: 'Human: Yo what up?\n\nAssistant:',
58
+ stream: true
59
+ ) { |event| puts event }
@@ -0,0 +1,26 @@
1
+ {
2
+ "id": "tools-2024-04-04",
3
+ "name": "[Deprecated] Tools",
4
+ "description": "[Deprecated] Equips Claude with custom tools for interacting with clients for a wide variety of tasks.",
5
+ "documentation": "https://docs.anthropic.com/claude/docs/tool-use",
6
+ "header" : { "anthropic-beta": "tools-2024-04-04" },
7
+ "schema": {
8
+ "tools": {
9
+ "type": "array",
10
+ "items": {
11
+ "type": "object",
12
+ "properties": {
13
+ "name": {
14
+ "type": "string"
15
+ },
16
+ "description": {
17
+ "type": "string"
18
+ },
19
+ "input_schema": {
20
+ "type": "object"
21
+ }
22
+ }
23
+ }
24
+ }
25
+ }
26
+ }
@@ -0,0 +1,45 @@
1
+ {
2
+ "version": "2023-06-01",
3
+ "endpoint": "/v1/complete",
4
+ "schema": {
5
+ "type": "object",
6
+ "required": [
7
+ "model",
8
+ "prompt",
9
+ "max_tokens_to_sample"
10
+ ],
11
+ "properties": {
12
+ "model": {
13
+ "type": "string"
14
+ },
15
+ "prompt": {
16
+ "type": "string"
17
+ },
18
+ "max_tokens_to_sample": {
19
+ "type": "integer"
20
+ },
21
+ "stop_sequences": {
22
+ "type": "array",
23
+ "items": {
24
+ "type": "string"
25
+ }
26
+ },
27
+ "temperature": {
28
+ "type": "number"
29
+ },
30
+ "top_k": {
31
+ "type": "integer"
32
+ },
33
+ "top_p": {
34
+ "type": "number"
35
+ },
36
+ "metadata": {
37
+ "type": "object"
38
+ },
39
+ "stream": {
40
+ "type": "boolean"
41
+ }
42
+ },
43
+ "additionalProperties": false
44
+ }
45
+ }
@@ -0,0 +1,65 @@
1
+ {
2
+ "version": "2023-06-01",
3
+ "endpoint": "/v1/messages",
4
+ "schema": {
5
+ "type": "object",
6
+ "required": [
7
+ "model",
8
+ "messages",
9
+ "max_tokens"
10
+ ],
11
+ "properties": {
12
+ "model": {
13
+ "type": "string"
14
+ },
15
+ "messages": {
16
+ "type": "array"
17
+ },
18
+ "max_tokens": {
19
+ "type": "integer"
20
+ },
21
+ "system": {
22
+ "type": "string"
23
+ },
24
+ "stop_sequences": {
25
+ "type": "array",
26
+ "items": {
27
+ "type": "string"
28
+ }
29
+ },
30
+ "temperature": {
31
+ "type": "number"
32
+ },
33
+ "tools": {
34
+ "type": "array",
35
+ "items": {
36
+ "type": "object",
37
+ "properties": {
38
+ "name": {
39
+ "type": "string"
40
+ },
41
+ "description": {
42
+ "type": "string"
43
+ },
44
+ "input_schema": {
45
+ "type": "object"
46
+ }
47
+ }
48
+ }
49
+ },
50
+ "top_k": {
51
+ "type": "integer"
52
+ },
53
+ "top_p": {
54
+ "type": "number"
55
+ },
56
+ "metadata": {
57
+ "type": "object"
58
+ },
59
+ "stream": {
60
+ "type": "boolean"
61
+ }
62
+ },
63
+ "additionalProperties": false
64
+ }
65
+ }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: anthropic-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dick Davis
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-04-20 00:00:00.000000000 Z
11
+ date: 2024-06-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httpx
@@ -54,12 +54,21 @@ files:
54
54
  - LICENSE.txt
55
55
  - README.md
56
56
  - Rakefile
57
- - anthropic-rb.gemspec
58
57
  - lib/anthropic.rb
59
- - lib/anthropic/client.rb
60
- - lib/anthropic/completions.rb
61
- - lib/anthropic/messages.rb
58
+ - lib/anthropic/api/base.rb
59
+ - lib/anthropic/api/completions.rb
60
+ - lib/anthropic/api/concerns/requestable.rb
61
+ - lib/anthropic/api/concerns/validatable.rb
62
+ - lib/anthropic/api/messages.rb
63
+ - lib/anthropic/bootstrapper.rb
64
+ - lib/anthropic/client/base.rb
65
+ - lib/anthropic/client/standard.rb
66
+ - lib/anthropic/client/streaming.rb
62
67
  - lib/anthropic/version.rb
68
+ - sanity_check.rb
69
+ - schemas/betas/tools-2024-04-04.json
70
+ - schemas/versions/completions/2023-06-01.json
71
+ - schemas/versions/messages/2023-06-01.json
63
72
  - sig/anthropic/rb.rbs
64
73
  homepage: https://github.com/dickdavis/anthropic-rb
65
74
  licenses:
@@ -78,14 +87,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
78
87
  requirements:
79
88
  - - ">="
80
89
  - !ruby/object:Gem::Version
81
- version: '3.1'
90
+ version: 3.2.4
82
91
  required_rubygems_version: !ruby/object:Gem::Requirement
83
92
  requirements:
84
93
  - - ">="
85
94
  - !ruby/object:Gem::Version
86
95
  version: '0'
87
96
  requirements: []
88
- rubygems_version: 3.3.26
97
+ rubygems_version: 3.4.19
89
98
  signing_key:
90
99
  specification_version: 4
91
100
  summary: Ruby bindings for the Anthropic API
data/anthropic-rb.gemspec DELETED
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'lib/anthropic/version'
4
-
5
- Gem::Specification.new do |spec|
6
- spec.name = 'anthropic-rb'
7
- spec.version = Anthropic::VERSION
8
- spec.authors = ['Dick Davis']
9
- spec.email = ['dick@hey.com']
10
-
11
- spec.summary = 'Ruby bindings for the Anthropic API'
12
- spec.homepage = 'https://github.com/dickdavis/anthropic-rb'
13
- spec.license = 'MIT'
14
-
15
- spec.metadata['allowed_push_host'] = 'https://rubygems.org'
16
- spec.metadata['homepage_uri'] = spec.homepage
17
- spec.metadata['source_code_uri'] = 'https://github.com/dickdavis/anthropic-rb'
18
- spec.metadata['changelog_uri'] = 'https://github.com/dickdavis/anthropic-rb/blob/main/CHANGELOG.md'
19
-
20
- # Specify which files should be added to the gem when it is released.
21
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
- spec.files = Dir.chdir(__dir__) do
23
- `git ls-files -z`.split("\x0").reject do |f|
24
- (File.expand_path(f) == __FILE__) ||
25
- f.start_with?(*%w[bin/ spec/ .git .github/ Gemfile])
26
- end
27
- end
28
- spec.bindir = 'exe'
29
- spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
30
- spec.require_paths = ['lib']
31
-
32
- # Uncomment to register a new dependency of your gem
33
- # spec.add_dependency "example-gem", "~> 1.0"
34
-
35
- # For more information and examples about making a new gem, check out our
36
- # guide at: https://bundler.io/guides/creating_gem.html
37
- spec.metadata['rubygems_mfa_required'] = 'true'
38
-
39
- spec.required_ruby_version = '>= 3.1'
40
- spec.add_dependency 'httpx', '>= 1.1.5'
41
- spec.add_dependency 'json-schema', '>= 4.1.1'
42
- end
@@ -1,108 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'httpx'
4
-
5
- module Anthropic
6
- ##
7
- # Error when the request is malformed.
8
- class BadRequestError < StandardError; end
9
- ##
10
- # Error when the API key is invalid.
11
- class AuthenticationError < StandardError; end
12
- ##
13
- # Error when the account does not have permission for the operation.
14
- class PermissionDeniedError < StandardError; end
15
- ##
16
- # Error when the resource is not found.
17
- class NotFoundError < StandardError; end
18
- ##
19
- # Error when the resource already exists.
20
- class ConflictError < StandardError; end
21
- ##
22
- # Error when the resource cannot be processed.
23
- class UnprocessableEntityError < StandardError; end
24
- ##
25
- # Error when the request exceeds the rate limit.
26
- class RateLimitError < StandardError; end
27
- ##
28
- # Error when the server experienced an internal error.
29
- class InternalServerError < StandardError; end
30
-
31
- ##
32
- # Provides a client for sending HTTP requests.
33
- class Client
34
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
35
- def self.post(url, data, headers = {})
36
- response = HTTPX.with(
37
- headers: {
38
- 'Content-Type' => 'application/json',
39
- 'x-api-key' => Anthropic.api_key,
40
- 'anthropic-version' => Anthropic.api_version
41
- }.merge(headers)
42
- ).post(url, json: data)
43
-
44
- response_body = JSON.parse(response.body, symbolize_names: true)
45
-
46
- case response.status
47
- when 200
48
- response_body
49
- when 400
50
- raise Anthropic::BadRequestError, response_body
51
- when 401
52
- raise Anthropic::AuthenticationError, response_body
53
- when 403
54
- raise Anthropic::PermissionDeniedError, response_body
55
- when 404
56
- raise Anthropic::NotFoundError, response_body
57
- when 409
58
- raise Anthropic::ConflictError, response_body
59
- when 422
60
- raise Anthropic::UnprocessableEntityError, response_body
61
- when 429
62
- raise Anthropic::RateLimitError, response_body
63
- when 500
64
- raise Anthropic::InternalServerError, response_body
65
- end
66
- end
67
-
68
- def self.post_as_stream(url, data, headers = {})
69
- response = HTTPX.plugin(:stream).with(
70
- headers: {
71
- 'Content-Type' => 'application/json',
72
- 'x-api-key' => Anthropic.api_key,
73
- 'anthropic-version' => Anthropic.api_version
74
- }.merge(headers)
75
- ).post(url, json: data, stream: true)
76
-
77
- response.each_line do |line|
78
- event, data = line.split(/(\w+\b:\s)/)[1..2]
79
- next unless event && data
80
-
81
- if event.start_with?('data')
82
- formatted_data = JSON.parse(data, symbolize_names: true)
83
- yield formatted_data unless %w[ping error].include?(formatted_data[:type])
84
- end
85
- end
86
- rescue HTTPX::HTTPError => error
87
- case error.response.status
88
- when 400
89
- raise Anthropic::BadRequestError
90
- when 401
91
- raise Anthropic::AuthenticationError
92
- when 403
93
- raise Anthropic::PermissionDeniedError
94
- when 404
95
- raise Anthropic::NotFoundError
96
- when 409
97
- raise Anthropic::ConflictError
98
- when 422
99
- raise Anthropic::UnprocessableEntityError
100
- when 429
101
- raise Anthropic::RateLimitError
102
- when 500
103
- raise Anthropic::InternalServerError
104
- end
105
- end
106
- # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
107
- end
108
- end
@@ -1,54 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Anthropic
4
- ##
5
- # Provides bindings for the Anthropic completions API
6
- class Completions
7
- # Error for when the API version is not supported.
8
- class UnsupportedApiVersionError < StandardError; end
9
-
10
- V1_SCHEMA = {
11
- type: 'object',
12
- required: %w[model prompt max_tokens_to_sample],
13
- properties: {
14
- model: { type: 'string' },
15
- prompt: { type: 'string' },
16
- max_tokens_to_sample: { type: 'integer' },
17
- stop_sequences: { type: 'array', items: { type: 'string' } },
18
- temperature: { type: 'number' },
19
- top_k: { type: 'integer' },
20
- top_p: { type: 'number' },
21
- metadata: { type: 'object' },
22
- stream: { type: 'boolean' }
23
- },
24
- additionalProperties: false
25
- }.freeze
26
-
27
- def initialize
28
- @endpoint = 'https://api.anthropic.com/v1/complete'
29
- end
30
-
31
- def create(**params, &)
32
- JSON::Validator.validate!(schema_for_api_version, params)
33
- return Anthropic::Client.post(endpoint, params) unless params[:stream]
34
-
35
- Anthropic::Client.post_as_stream(endpoint, params, &)
36
- rescue JSON::Schema::ValidationError => error
37
- raise ArgumentError, error.message
38
- end
39
-
40
- private
41
-
42
- attr_reader :endpoint
43
-
44
- def schema_for_api_version
45
- api_version = Anthropic.api_version
46
- case api_version
47
- when '2023-06-01'
48
- V1_SCHEMA
49
- else
50
- raise UnsupportedApiVersionError, "Unsupported API version: #{api_version}"
51
- end
52
- end
53
- end
54
- end
@@ -1,76 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Anthropic
4
- ##
5
- # Provides bindings for the Anthropic messages API
6
- class Messages
7
- # Error for when the API version is not supported.
8
- class UnsupportedApiVersionError < StandardError; end
9
-
10
- # Error for when a beta feature is not used correctly
11
- class UnsupportedBetaOptionError < StandardError; end
12
-
13
- ENDPOINT = 'https://api.anthropic.com/v1/messages'
14
- V1_SCHEMA = {
15
- type: 'object',
16
- required: %w[model messages max_tokens],
17
- properties: {
18
- model: { type: 'string' },
19
- messages: { type: 'array' },
20
- max_tokens: { type: 'integer' },
21
- system: { type: 'string' },
22
- stop_sequences: { type: 'array', items: { type: 'string' } },
23
- temperature: { type: 'number' },
24
- tools: {
25
- type: 'array',
26
- items: {
27
- name: { type: 'string' },
28
- description: { type: 'string' },
29
- input_schema: { type: 'object' }
30
- }
31
- },
32
- top_k: { type: 'integer' },
33
- top_p: { type: 'number' },
34
- metadata: { type: 'object' },
35
- stream: { type: 'boolean' }
36
- },
37
- additionalProperties: false
38
- }.freeze
39
-
40
- def initialize(beta: false)
41
- @beta = beta
42
- end
43
-
44
- def create(**params, &)
45
- raise UnsupportedBetaOptionError, 'Tool use is not yet supported in streaming mode' if params[:stream] && beta
46
-
47
- JSON::Validator.validate!(schema_for_api_version, params)
48
-
49
- return Anthropic::Client.post(ENDPOINT, params, additional_headers) unless params[:stream]
50
-
51
- Anthropic::Client.post_as_stream(ENDPOINT, params, additional_headers, &)
52
- rescue JSON::Schema::ValidationError => error
53
- raise ArgumentError, error.message
54
- end
55
-
56
- private
57
-
58
- attr_reader :beta
59
-
60
- def schema_for_api_version
61
- api_version = Anthropic.api_version
62
- case api_version
63
- when '2023-06-01'
64
- V1_SCHEMA
65
- else
66
- raise UnsupportedApiVersionError, "Unsupported API version: #{api_version}"
67
- end
68
- end
69
-
70
- def additional_headers
71
- return {} unless beta
72
-
73
- { 'anthropic-beta' => 'tools-2024-04-04' }
74
- end
75
- end
76
- end