copy_ai 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d7dfa8e0173b3f8e9ff2c8faa3278493da38351a46b40577b4b52a2d87e5b0f4
4
+ data.tar.gz: 25e0c4672631e4cab8b6b0a1a25702db84a37f95458e44eb375714c205659e3a
5
+ SHA512:
6
+ metadata.gz: 8f17f32b94bc25eef3d9125693c1eaeb85c0c41bbff569530dd7ce06c537e9811864996b80a071efcb5abe39d9c54d7ae298443bc8aa45eab2bd16c43e91555d
7
+ data.tar.gz: 820c92832dc61a4488acbc64c888152c786cc6a1b5bc48ca917c11a1049f8b95929e9f89aced95bf6b4cb1d9433737dbc2ffbc150e51477cbd9329436f8489f5
data/CHANGELOG.md ADDED
@@ -0,0 +1,6 @@
1
+ ## [0.1.0] - 2024-08-19
2
+ * Initial Release
3
+
4
+ ## [0.0.1] - 2024-08-18
5
+
6
+ * Draft Creation
data/MIT-LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright Fernand Arioja
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,145 @@
1
+ # A [Ruby](https://www.ruby-lang.org) interface to the [Copy.ai Workflows API](https://docs.copy.ai/docs/getting-started)
2
+
3
+ ## Installation
4
+
5
+ Install the gem and add to the application's Gemfile:
6
+
7
+ bundle add copy_ai
8
+
9
+ Or, to install directly:
10
+
11
+ gem install copy_ai
12
+
13
+ ## Usage
14
+
15
+ First, obtain credentials from <https://docs.copy.ai/reference/authentication>.
16
+
17
+ ```ruby
18
+ require "copy_ai"
19
+
20
+ copy_ai_credentials = {
21
+ api_key: "INSERT YOUR X API KEY HERE",
22
+ api_endpoint: "INSERT YOUR X API ENDPOINT HERE"
23
+ }
24
+
25
+ # Initialize a API client with your Workspace Api Key
26
+ copy_ai_client = CopyAi::Client.new(**copy_ai_credentials)
27
+
28
+ # Register webhook
29
+ # url: your site webhook URL
30
+ # event_type: https://docs.copy.ai/reference/register-webhook#event-types
31
+ # optional workflow_id: If a workflow ID is not specified, you will receive events for all workflows in your workspace.
32
+ response = CopyAi::Webook.register(copy_ai_client, url: 'https://cloud-asm.com/webhook', event_type: 'workflowRun.completed', workflow_id: <workflow-id>)
33
+ # {
34
+ "status": "success",
35
+ "data": {
36
+ "id": "<id of webhook>",
37
+ "url": "<https://mywebsite.com/webhook>",
38
+ "eventType": "workflowRun.completed",
39
+ "workflowId": "<workflow-id>"
40
+ }
41
+ }
42
+
43
+ # Starting a Workflow Run
44
+ post = copy_ai_client.post(body: {
45
+ startVariables: {
46
+ "Input 1": "<Inputs vary depending on the workflow used.>",
47
+ "Input 2": "<The best way to see an example is to try it!>"
48
+ },
49
+ "metadata": {
50
+ "api": true
51
+ }
52
+ })
53
+ # { "status": "success", "data": { "id": "<run-id>" } }
54
+
55
+ # Tracking / Poll for Progress
56
+ copy_ai_client.get
57
+ # {
58
+ "status": "success",
59
+ "data":
60
+ {
61
+ "id": "<run-id>",
62
+ "input":
63
+ {
64
+ "Input 1": "Inputs vary depending on the workflow used.",
65
+ "Input 2": "The best way to see an example is to try it!"
66
+ },
67
+ "status": "PROCESSING",
68
+ "output":
69
+ {
70
+ "Output 1": "<Outputs vary depending on the workflow used.>",
71
+ "Output 2": "<The best way to see an example is to try it!>"
72
+ },
73
+ "createdAt": "2022-11-18T20:30:07.434Z"
74
+ }
75
+ }
76
+
77
+ # Run Completion
78
+ # When the run is complete, the status will change to COMPLETE and a POST request will be sent to the registered webhooks to notify of the workflow's completion.
79
+ # {
80
+ "type": "workflowRun.completed",
81
+ "workflowRunId": "<run-id>",
82
+ "workflowId": "<workflow-id>",
83
+ "result":
84
+ {
85
+ "Output 1": "<Outputs vary depending on the workflow used.>",
86
+ "Output 2": "<The best way to see an example is to try it!>"
87
+ },
88
+ "metadata":
89
+ {
90
+ /* any metadata set on the workflow run */
91
+ },
92
+ "credits": 2
93
+ }
94
+ ```
95
+
96
+ ## Development
97
+
98
+ 1. Checkout and repo:
99
+
100
+ git checkout git@github.com:wwwfernand/copy_ai.git
101
+
102
+ 2. Enter the repo's directory:
103
+
104
+ cd copy_ai
105
+
106
+ 3. Install dependencies via Bundler:
107
+
108
+ bundle install
109
+
110
+ 4. Run the default Rake task to ensure all tests pass:
111
+
112
+ bundle exec rake
113
+
114
+ 5. Create a new branch for your feature or bug fix:
115
+
116
+ git checkout -b my-new-branch
117
+
118
+ ## Contributing
119
+
120
+ Bug reports and pull requests are welcome on GitHub at https://github.com/wwwfernand/copy_ai.
121
+
122
+ Pull requests will only be accepted if they meet all the following criteria:
123
+
124
+ 1. Code must conform to [Standard Ruby](https://github.com/standardrb/standard#readme).
125
+
126
+ bundle exec rake standard
127
+
128
+ 2. Code must conform to the [RuboCop rules](https://github.com/rubocop/rubocop#readme).
129
+
130
+ bundle exec rake rubocop
131
+
132
+ 3. 100% LOC code coverage.
133
+
134
+ bundle exec rake test
135
+
136
+ 4. 100% mutation coverage.
137
+
138
+ bundle exec rake mutant
139
+
140
+ 5. RBS type signatures (in `sig/copy_ai.rbs`).
141
+
142
+ bundle exec rake steep
143
+
144
+ ## License
145
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,34 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.options = "--pride"
8
+ t.test_files = FileList["test/**/*_test.rb"]
9
+ end
10
+
11
+ require "standard/rake"
12
+ require "rubocop/rake_task"
13
+
14
+ RuboCop::RakeTask.new
15
+
16
+ require "steep"
17
+ require "steep/cli"
18
+
19
+ desc "Type check with Steep"
20
+ task :steep do
21
+ Steep::CLI.new(argv: ["check"], stdout: $stdout, stderr: $stderr, stdin: $stdin).run
22
+ end
23
+
24
+ require "mutant"
25
+
26
+ desc "Run mutant"
27
+ task :mutant do
28
+ system(*%w[bundle exec mutant run]) or raise "Mutant task failed"
29
+ end
30
+
31
+ desc "Run linters"
32
+ task lint: %i[rubocop standard]
33
+
34
+ task default: %i[test lint mutant steep]
data/bin/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "copy_ai"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ require "irb"
10
+ IRB.start(__FILE__)
@@ -0,0 +1,15 @@
1
+ module CopyAi
2
+ class Authenticator
3
+ AUTHENTICATION_HEADER = "x-copy-ai-api-key".freeze
4
+
5
+ attr_accessor :api_key
6
+
7
+ def initialize(api_key:)
8
+ @api_key = api_key
9
+ end
10
+
11
+ def header
12
+ {AUTHENTICATION_HEADER => api_key}
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,72 @@
1
+ require "forwardable"
2
+ require_relative "authenticator"
3
+ require_relative "connection"
4
+ require_relative "request_builder"
5
+ require_relative "response_parser"
6
+
7
+ module CopyAi
8
+ class Client
9
+ extend Forwardable
10
+
11
+ attr_reader :api_key, :api_endpoint, :workflow_id
12
+
13
+ def_delegators :@connection, :open_timeout, :read_timeout, :write_timeout, :debug_output
14
+ def_delegators :@connection, :open_timeout=, :read_timeout=, :write_timeout=, :debug_output=
15
+
16
+ def initialize(api_key:, api_endpoint:,
17
+ open_timeout: Connection::DEFAULT_OPEN_TIMEOUT,
18
+ read_timeout: Connection::DEFAULT_READ_TIMEOUT,
19
+ write_timeout: Connection::DEFAULT_WRITE_TIMEOUT,
20
+ debug_output: Connection::DEFAULT_DEBUG_OUTPUT)
21
+
22
+ @api_key = api_key
23
+ @api_endpoint = api_endpoint
24
+ initialize_authenticator
25
+ initialize_workflow_id
26
+ @connection = Connection.new(open_timeout:, read_timeout:, write_timeout:, debug_output:)
27
+ @request_builder = RequestBuilder.new
28
+ @response_parser = ResponseParser.new
29
+ end
30
+
31
+ def get
32
+ execute_request(:get, body: nil)
33
+ end
34
+
35
+ def post(body:)
36
+ execute_request(:post, body:)
37
+ end
38
+
39
+ def api_key=(api_key)
40
+ @api_key = api_key
41
+ initialize_authenticator
42
+ end
43
+
44
+ def api_endpoint=(api_endpoint)
45
+ @api_endpoint = api_endpoint
46
+ initialize_workflow_id
47
+ end
48
+
49
+ private
50
+
51
+ def initialize_workflow_id
52
+ raise ArgumentError, "Missing Credentials" if api_endpoint.nil? || api_endpoint.empty?
53
+
54
+ @workflow_id = api_endpoint.split("/").at(-2)
55
+
56
+ raise ArgumentError, "Invalid Credentials" if workflow_id.nil?
57
+ end
58
+
59
+ def initialize_authenticator
60
+ raise ArgumentError, "Missing Credentials" if api_key.nil? || api_key.empty?
61
+
62
+ @authenticator = Authenticator.new(api_key:)
63
+ end
64
+
65
+ def execute_request(http_method, body:)
66
+ uri = URI(api_endpoint)
67
+ request = @request_builder.build(http_method:, uri:, body:, authenticator: @authenticator)
68
+ response = @connection.perform(request:)
69
+ @response_parser.parse(response:)
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,57 @@
1
+ require "forwardable"
2
+ require "net/http"
3
+ require "openssl"
4
+ require "uri"
5
+ require_relative "errors/network_error"
6
+
7
+ module CopyAi
8
+ class Connection
9
+ extend Forwardable
10
+
11
+ DEFAULT_OPEN_TIMEOUT = 60 # seconds
12
+ DEFAULT_READ_TIMEOUT = 60 # seconds
13
+ DEFAULT_WRITE_TIMEOUT = 60 # seconds
14
+ DEFAULT_DEBUG_OUTPUT = File.open(File::NULL, "w")
15
+ NETWORK_ERRORS = [
16
+ Errno::ECONNREFUSED,
17
+ Errno::ECONNRESET,
18
+ Net::OpenTimeout,
19
+ Net::ReadTimeout,
20
+ OpenSSL::SSL::SSLError
21
+ ].freeze
22
+
23
+ attr_accessor :open_timeout, :read_timeout, :write_timeout, :debug_output
24
+
25
+ def initialize(open_timeout: DEFAULT_OPEN_TIMEOUT, read_timeout: DEFAULT_READ_TIMEOUT,
26
+ write_timeout: DEFAULT_WRITE_TIMEOUT, debug_output: DEFAULT_DEBUG_OUTPUT)
27
+ @open_timeout = open_timeout
28
+ @read_timeout = read_timeout
29
+ @write_timeout = write_timeout
30
+ @debug_output = debug_output
31
+ end
32
+
33
+ def perform(request:)
34
+ http_client = build_http_client(request.uri.host, request.uri.port)
35
+ http_client.use_ssl = true
36
+ http_client.request(request)
37
+ rescue *NETWORK_ERRORS => e
38
+ raise NetworkError, "Network error: #{e}"
39
+ end
40
+
41
+ private
42
+
43
+ def build_http_client(host, port)
44
+ http_client = Net::HTTP.new(host, port)
45
+ configure_http_client(http_client)
46
+ end
47
+
48
+ def configure_http_client(http_client)
49
+ http_client.tap do |c|
50
+ c.open_timeout = open_timeout
51
+ c.read_timeout = read_timeout
52
+ c.write_timeout = write_timeout
53
+ c.set_debug_output(debug_output)
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,5 @@
1
+ require_relative "server_error"
2
+
3
+ module CopyAi
4
+ class BadGateway < ServerError; end
5
+ end
@@ -0,0 +1,5 @@
1
+ require_relative "client_error"
2
+
3
+ module CopyAi
4
+ class BadRequest < ClientError; end
5
+ end
@@ -0,0 +1,5 @@
1
+ require_relative "http_error"
2
+
3
+ module CopyAi
4
+ class ClientError < HTTPError; end
5
+ end
@@ -0,0 +1,5 @@
1
+ require_relative "client_error"
2
+
3
+ module CopyAi
4
+ class ConnectionException < ClientError; end
5
+ end
@@ -0,0 +1,3 @@
1
+ module CopyAi
2
+ class Error < StandardError; end
3
+ end
@@ -0,0 +1,5 @@
1
+ require_relative "client_error"
2
+
3
+ module CopyAi
4
+ class Forbidden < ClientError; end
5
+ end
@@ -0,0 +1,5 @@
1
+ require_relative "server_error"
2
+
3
+ module CopyAi
4
+ class GatewayTimeout < ServerError; end
5
+ end
@@ -0,0 +1,5 @@
1
+ require_relative "client_error"
2
+
3
+ module CopyAi
4
+ class Gone < ClientError; end
5
+ end
@@ -0,0 +1,41 @@
1
+ require "json"
2
+ require_relative "error"
3
+
4
+ module CopyAi
5
+ class HTTPError < Error
6
+ JSON_CONTENT_TYPE_REGEXP = %r{application/(problem\+|)json}
7
+
8
+ attr_reader :response, :code
9
+
10
+ def initialize(response:)
11
+ super(error_message(response))
12
+ @response = response
13
+ @code = response.code
14
+ end
15
+
16
+ def error_message(response)
17
+ if json?(response)
18
+ message_from_json_response(response)
19
+ else
20
+ response.message
21
+ end
22
+ end
23
+
24
+ def message_from_json_response(response)
25
+ response_object = JSON.parse(response.body)
26
+ if response_object.key?("title") && response_object.key?("detail")
27
+ "#{response_object.fetch("title")}: #{response_object.fetch("detail")}"
28
+ elsif response_object.key?("error")
29
+ response_object.fetch("error")
30
+ elsif response_object["errors"].instance_of?(Array)
31
+ response_object.fetch("errors").map { |error| error.fetch("message") }.join(", ")
32
+ else
33
+ response.message
34
+ end
35
+ end
36
+
37
+ def json?(response)
38
+ JSON_CONTENT_TYPE_REGEXP === response["content-type"]
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,5 @@
1
+ require_relative "server_error"
2
+
3
+ module CopyAi
4
+ class InternalServerError < ServerError; end
5
+ end
@@ -0,0 +1,5 @@
1
+ require_relative "error"
2
+
3
+ module CopyAi
4
+ class NetworkError < Error; end
5
+ end
@@ -0,0 +1,5 @@
1
+ require_relative "client_error"
2
+
3
+ module CopyAi
4
+ class NotAcceptable < ClientError; end
5
+ end
@@ -0,0 +1,5 @@
1
+ require_relative "client_error"
2
+
3
+ module CopyAi
4
+ class NotFound < ClientError; end
5
+ end
@@ -0,0 +1,5 @@
1
+ require_relative "client_error"
2
+
3
+ module CopyAi
4
+ class PayloadTooLarge < ClientError; end
5
+ end
@@ -0,0 +1,5 @@
1
+ require_relative "http_error"
2
+
3
+ module CopyAi
4
+ class ServerError < HTTPError; end
5
+ end
@@ -0,0 +1,5 @@
1
+ require_relative "server_error"
2
+
3
+ module CopyAi
4
+ class ServiceUnavailable < ServerError; end
5
+ end
@@ -0,0 +1,5 @@
1
+ require_relative "client_error"
2
+
3
+ module CopyAi
4
+ class Unauthorized < ClientError; end
5
+ end
@@ -0,0 +1,5 @@
1
+ require_relative "client_error"
2
+
3
+ module CopyAi
4
+ class UnprocessableEntity < ClientError; end
5
+ end
@@ -0,0 +1,40 @@
1
+ require "net/http"
2
+ require "uri"
3
+ require_relative "authenticator"
4
+ require_relative "version"
5
+
6
+ module CopyAi
7
+ class RequestBuilder
8
+ DEFAULT_HEADERS = {"Content-Type" => "application/json"}.freeze
9
+ HTTP_METHODS = {
10
+ get: Net::HTTP::Get,
11
+ post: Net::HTTP::Post
12
+ }.freeze
13
+
14
+ def build(http_method:, uri:, authenticator:, body:)
15
+ raise ArgumentError, "Missing Credentials" unless authenticator
16
+
17
+ request = create_request(http_method:, uri:, body:)
18
+ add_headers(request:, header: authenticator.header)
19
+ request
20
+ end
21
+
22
+ private
23
+
24
+ def create_request(http_method:, uri:, body:)
25
+ http_method_class = HTTP_METHODS[http_method]
26
+
27
+ raise ArgumentError, "Unsupported HTTP method: #{http_method}" unless http_method_class
28
+
29
+ request = http_method_class.new(uri)
30
+ request.body = body.to_json unless body.nil?
31
+ request
32
+ end
33
+
34
+ def add_headers(request:, header:)
35
+ DEFAULT_HEADERS.merge(header).each do |key, value|
36
+ request.add_field(key, value)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,59 @@
1
+ require "json"
2
+ require "net/http"
3
+ require_relative "errors/bad_gateway"
4
+ require_relative "errors/bad_request"
5
+ require_relative "errors/connection_exception"
6
+ require_relative "errors/http_error"
7
+ require_relative "errors/forbidden"
8
+ require_relative "errors/gateway_timeout"
9
+ require_relative "errors/gone"
10
+ require_relative "errors/internal_server_error"
11
+ require_relative "errors/not_acceptable"
12
+ require_relative "errors/not_found"
13
+ require_relative "errors/payload_too_large"
14
+ require_relative "errors/service_unavailable"
15
+ require_relative "errors/unauthorized"
16
+ require_relative "errors/unprocessable_entity"
17
+
18
+ module CopyAi
19
+ class ResponseParser
20
+ ERROR_MAP = {
21
+ 400 => BadRequest,
22
+ 401 => Unauthorized,
23
+ 403 => Forbidden,
24
+ 404 => NotFound,
25
+ 406 => NotAcceptable,
26
+ 409 => ConnectionException,
27
+ 410 => Gone,
28
+ 413 => PayloadTooLarge,
29
+ 422 => UnprocessableEntity,
30
+ 500 => InternalServerError,
31
+ 502 => BadGateway,
32
+ 503 => ServiceUnavailable,
33
+ 504 => GatewayTimeout
34
+ }.freeze
35
+ JSON_CONTENT_TYPE_REGEXP = %r{application/json}
36
+
37
+ def parse(response:)
38
+ raise error(response) unless response.is_a?(Net::HTTPSuccess)
39
+
40
+ return unless json?(response)
41
+
42
+ JSON.parse(response.body)
43
+ end
44
+
45
+ private
46
+
47
+ def error(response)
48
+ error_class(response).new(response:)
49
+ end
50
+
51
+ def error_class(response)
52
+ ERROR_MAP[Integer(response.code)] || HTTPError
53
+ end
54
+
55
+ def json?(response)
56
+ JSON_CONTENT_TYPE_REGEXP === response["content-type"]
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,3 @@
1
+ module CopyAi
2
+ VERSION = Gem::Version.create("0.1.0")
3
+ end
@@ -0,0 +1,30 @@
1
+ module CopyAi
2
+ module Webhook
3
+ extend self
4
+ EVENT_TYPES = [
5
+ "workflowRun.started",
6
+ "workflowRun.completed",
7
+ "workflowRun.failed",
8
+ "workflowCreditLimit.reached"
9
+ ].freeze
10
+ WEBHOOK_URL = "https://api.copy.ai/api/webhook".freeze
11
+
12
+ # If a workflow ID is not included, events for all workflows in your workspace will be received
13
+ def register(client, url:, event_type:, workflow_id: nil)
14
+ raise ArgumentError, "Missing Arguments" if client.nil? || url.nil? || event_type.nil?
15
+
16
+ validate!(event_type:)
17
+ webhook_client = client.dup.tap { |c| c.api_endpoint = WEBHOOK_URL }
18
+ body = {url:, eventType: event_type, workflowId: workflow_id}
19
+ webhook_client.post(body:)
20
+ end
21
+
22
+ private
23
+
24
+ def validate!(event_type:)
25
+ return if EVENT_TYPES.include?(event_type)
26
+
27
+ raise ArgumentError, "Invalid event_type: #{event_type}"
28
+ end
29
+ end
30
+ end
data/lib/copy_ai.rb ADDED
@@ -0,0 +1,2 @@
1
+ require_relative "copy_ai/client"
2
+ require_relative "copy_ai/webhook"
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: copy_ai
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Fernand Arioja
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-08-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '7.1'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 7.1.3.4
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '7.1'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 7.1.3.4
33
+ description: A Ruby client library for accessing the Copy.ai API, allowing developers
34
+ to easily integrate Copy.ai's text generation capabilities into their Ruby applications
35
+ email:
36
+ - dev.cloud.asm@gmail.com
37
+ executables: []
38
+ extensions: []
39
+ extra_rdoc_files: []
40
+ files:
41
+ - CHANGELOG.md
42
+ - MIT-LICENSE
43
+ - README.md
44
+ - Rakefile
45
+ - bin/console
46
+ - lib/copy_ai.rb
47
+ - lib/copy_ai/authenticator.rb
48
+ - lib/copy_ai/client.rb
49
+ - lib/copy_ai/connection.rb
50
+ - lib/copy_ai/errors/bad_gateway.rb
51
+ - lib/copy_ai/errors/bad_request.rb
52
+ - lib/copy_ai/errors/client_error.rb
53
+ - lib/copy_ai/errors/connection_exception.rb
54
+ - lib/copy_ai/errors/error.rb
55
+ - lib/copy_ai/errors/forbidden.rb
56
+ - lib/copy_ai/errors/gateway_timeout.rb
57
+ - lib/copy_ai/errors/gone.rb
58
+ - lib/copy_ai/errors/http_error.rb
59
+ - lib/copy_ai/errors/internal_server_error.rb
60
+ - lib/copy_ai/errors/network_error.rb
61
+ - lib/copy_ai/errors/not_acceptable.rb
62
+ - lib/copy_ai/errors/not_found.rb
63
+ - lib/copy_ai/errors/payload_too_large.rb
64
+ - lib/copy_ai/errors/server_error.rb
65
+ - lib/copy_ai/errors/service_unavailable.rb
66
+ - lib/copy_ai/errors/unauthorized.rb
67
+ - lib/copy_ai/errors/unprocessable_entity.rb
68
+ - lib/copy_ai/request_builder.rb
69
+ - lib/copy_ai/response_parser.rb
70
+ - lib/copy_ai/version.rb
71
+ - lib/copy_ai/webhook.rb
72
+ homepage: https://github.com/wwwfernand/copy_ai
73
+ licenses:
74
+ - MIT
75
+ metadata:
76
+ allowed_push_host: https://rubygems.org
77
+ homepage_uri: https://github.com/wwwfernand/copy_ai
78
+ source_code_uri: https://github.com/wwwfernand/copy_ai
79
+ changelog_uri: https://github.com/wwwfernand/copy_ai/master/CHANGELOG.md
80
+ rubygems_mfa_required: 'true'
81
+ post_install_message:
82
+ rdoc_options: []
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: 3.1.6
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ requirements: []
96
+ rubygems_version: 3.3.27
97
+ signing_key:
98
+ specification_version: 4
99
+ summary: A Ruby interface to the Copy.ai API
100
+ test_files: []