qyro_sdk 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: 13a5288829f36672738079f90a911ad8346fb6715362d93395ad0183d483a1d9
4
+ data.tar.gz: f7649a0ac5305789983e5180295121f4481da4481b09ee09d20a3ab4239f8064
5
+ SHA512:
6
+ metadata.gz: 9d395f1b1d5fb88b1f755de0836c69540e352abb946499909c885255949d15d91f188af255cec08cda601b4d00e752d5ea19e6d8e2bea570c045aceaebaab866
7
+ data.tar.gz: 672dd587d799e9523d091b1a1ca9ed50f10bfffb0326fe038f3806a06f94640e3918776536a3b09b785d579ac45fc7164ec39c81b789aba1319a8f11cfb93fde
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 QyroAI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,65 @@
1
+ Qyro Ruby SDK
2
+ ================
3
+
4
+ Ruby SDK for interacting with Qyro AI server & client APIs.
5
+
6
+ ### Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem "qyro_sdk"
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ ```bash
17
+ bundle install
18
+ ```
19
+
20
+ Or install it yourself as:
21
+
22
+ ```bash
23
+ gem install qyro_sdk
24
+ ```
25
+
26
+ ### Usage
27
+
28
+ ```ruby
29
+ require "qyro_sdk"
30
+
31
+ BASE_URL = "https://qyroai.com"
32
+ API_KEY_ID = "<API_KEY_ID>"
33
+ API_KEY_SECRET = "<API_KEY_SECRET>"
34
+ ASSISTANT_ID = "<ASSISTANT_ID>"
35
+
36
+ if __FILE__ == $0
37
+ # --- Server-side Example ---
38
+ puts "=== Server SDK Example ==="
39
+ server_client = QyroSDK::QyroServerClient.new(
40
+ base_url: BASE_URL,
41
+ api_key_id: API_KEY_ID,
42
+ api_key_secret: API_KEY_SECRET
43
+ )
44
+
45
+ # create a new session
46
+ session = server_client.create_session(ASSISTANT_ID, { user_id: "123" })
47
+ puts "Created server session: #{session.id}"
48
+
49
+ # send a chat message
50
+ messages = server_client.chat(ASSISTANT_ID, session.id, "Hello from Ruby (server)")
51
+ messages.each { |m| puts "[#{m.role}] #{m.content}" }
52
+
53
+ # --- Client-side Example ---
54
+ puts "\n=== Client SDK Example ==="
55
+ token_generator = QyroSDK::ClientTokenGenerator.new(API_KEY_ID, API_KEY_SECRET)
56
+ token = token_generator.generate({ client: "ruby-sdk-example" })
57
+
58
+ client = QyroSDK::QyroClient.new(base_url: BASE_URL, token: token)
59
+ client_session = client.create_session(ASSISTANT_ID, { locale: "en" })
60
+ puts "Created client session: #{client_session.id}"
61
+
62
+ client_messages = client.chat(ASSISTANT_ID, client_session.id, "Hello from Ruby (client)")
63
+ client_messages.each { |m| puts "[#{m.role}] #{m.content}" }
64
+ end
65
+ ```
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+ require 'json'
3
+ require 'time'
4
+ require 'securerandom'
5
+ require 'jwt'
6
+
7
+ module QyroSDK
8
+ class ApiKeyAuth
9
+ attr_reader :api_key_id, :api_key_secret
10
+
11
+ def initialize(api_key_id, api_key_secret)
12
+ @api_key_id = api_key_id
13
+ @api_key_secret = api_key_secret
14
+ end
15
+
16
+ def header_value
17
+ "ApiKey #{@api_key_secret}"
18
+ end
19
+ end
20
+
21
+ class ClientTokenGenerator
22
+ attr_reader :api_key_id, :api_key_secret
23
+
24
+ # note: depends on the 'jwt' gem
25
+ def initialize(api_key_id, api_key_secret)
26
+ @api_key_id = api_key_id
27
+ @api_key_secret = api_key_secret
28
+ end
29
+
30
+ # context: a Ruby Hash that will be JSON-dumped into the token "sub"
31
+ def generate(context)
32
+ subject = JSON.dump(context)
33
+ now_ts = Time.now.to_i
34
+ payload = {
35
+ "sub" => subject,
36
+ "iat" => now_ts,
37
+ # 30 days from now
38
+ "exp" => now_ts + 24 * 3600 * 30,
39
+ "type" => "client",
40
+ "iss" => api_key_id.to_s,
41
+ "aud" => "qyro",
42
+ "jti" => SecureRandom.uuid
43
+ }
44
+ headers = { kid: api_key_id.to_s }
45
+ JWT.encode(payload, api_key_secret, 'HS256', headers)
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+ require_relative 'http_helper'
3
+ require_relative 'models'
4
+ require_relative 'exceptions'
5
+
6
+ module QyroSDK
7
+ class QyroClient
8
+ include HttpHelper
9
+ include Models
10
+
11
+ def initialize(base_url:, token:, timeout: 30)
12
+ raise ConfigurationError, "base_url is required" if base_url.nil? || base_url.to_s.strip.empty?
13
+
14
+ @base_url = base_url.end_with?('/') ? base_url : "#{base_url}/"
15
+ @token = token
16
+ @timeout = timeout
17
+ end
18
+
19
+ def create_session(assistant_id, context = {})
20
+ path = "/client/api/v1/assistants/#{assistant_id}/sessions"
21
+ headers = client_headers
22
+ resp = request('POST', path, headers: headers, body: { context: context }, timeout: @timeout)
23
+ Session.new(id: resp['id'])
24
+ end
25
+
26
+ def fetch_session_messages(assistant_id, session_id)
27
+ path = "/client/api/v1/assistants/#{assistant_id}/sessions/#{session_id}/messages"
28
+ headers = client_headers
29
+ resp = request('GET', path, headers: headers, timeout: @timeout)
30
+ array_to_messages(resp)
31
+ end
32
+
33
+ def chat(assistant_id, session_id, message_text)
34
+ path = "/client/api/v1/assistants/#{assistant_id}/sessions/#{session_id}/chat"
35
+ headers = client_headers
36
+ resp = request('POST', path, headers: headers, body: { message: message_text }, timeout: @timeout)
37
+ array_to_messages(resp)
38
+ end
39
+
40
+ private
41
+
42
+ def client_headers
43
+ { "Authorization" => "Bearer #{@token}", "Content-Type" => "application/json" }
44
+ end
45
+
46
+ def array_to_messages(arr)
47
+ (arr || []).map do |m|
48
+ Message.new(id: m['id'], content: m['content'], role: m['role'])
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module QyroSDK
4
+ class QyroError < StandardError; end
5
+
6
+ class HTTPError < QyroError
7
+ attr_reader :status_code, :response
8
+
9
+ def initialize(status_code, message, response: nil)
10
+ super("HTTP #{status_code}: #{message}")
11
+ @status_code = status_code
12
+ @response = response
13
+ end
14
+ end
15
+
16
+ class ConfigurationError < QyroError; end
17
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+ require 'net/http'
3
+ require 'uri'
4
+ require 'json'
5
+
6
+ module QyroSDK
7
+ module HttpHelper
8
+ private
9
+
10
+ def request(method, path, headers: {}, body: nil, timeout: nil)
11
+ uri = URI.join(@base_url, path)
12
+ http = Net::HTTP.new(uri.host, uri.port)
13
+ http.use_ssl = uri.scheme == 'https'
14
+ http.open_timeout = timeout if timeout
15
+ http.read_timeout = timeout if timeout
16
+
17
+ req = case method.to_s.upcase
18
+ when 'GET' then Net::HTTP::Get.new(uri)
19
+ when 'POST' then Net::HTTP::Post.new(uri)
20
+ when 'PUT' then Net::HTTP::Put.new(uri)
21
+ when 'DELETE' then Net::HTTP::Delete.new(uri)
22
+ else
23
+ raise ArgumentError, "Unsupported HTTP method: #{method}"
24
+ end
25
+
26
+ headers.each { |k, v| req[k] = v } if headers
27
+ if body
28
+ req.body = body.is_a?(String) ? body : JSON.dump(body)
29
+ req['Content-Type'] ||= 'application/json'
30
+ end
31
+
32
+ resp = http.request(req)
33
+ handle_response(resp)
34
+ end
35
+
36
+ def handle_response(resp)
37
+ code = resp.code.to_i
38
+ body = resp.body.to_s
39
+ parsed = begin
40
+ JSON.parse(body)
41
+ rescue StandardError
42
+ body
43
+ end
44
+
45
+ unless code.between?(200, 299)
46
+ message = parsed.is_a?(Hash) ? (parsed['message'] || parsed) : parsed
47
+ raise HTTPError.new(code, message, response: resp)
48
+ end
49
+
50
+ parsed
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+ require 'ostruct'
3
+
4
+ module QyroSDK
5
+ module Models
6
+ Session = Struct.new(:id, keyword_init: true)
7
+ Message = Struct.new(:id, :role, :content, keyword_init: true)
8
+ end
9
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+ require_relative 'http_helper'
3
+ require_relative 'models'
4
+ require_relative 'auth'
5
+ require_relative 'exceptions'
6
+
7
+ module QyroSDK
8
+ class QyroServerClient
9
+ include HttpHelper
10
+ include Models
11
+
12
+ def initialize(base_url:, api_key_id:, api_key_secret:, timeout: 30)
13
+ raise ConfigurationError, "base_url is required" if base_url.nil? || base_url.to_s.strip.empty?
14
+
15
+ @base_url = base_url.end_with?('/') ? base_url : "#{base_url}/"
16
+ @auth = ApiKeyAuth.new(api_key_id, api_key_secret)
17
+ @timeout = timeout
18
+ end
19
+
20
+ def create_session(assistant_id, context = {})
21
+ path = "/server/api/v1/assistants/#{assistant_id}/sessions"
22
+ headers = { "Authorization" => @auth.header_value }
23
+ resp = request('POST', path, headers: headers, body: { context: context }, timeout: @timeout)
24
+ Session.new(id: resp['id'])
25
+ end
26
+
27
+ def fetch_session_messages(assistant_id, session_id)
28
+ path = "/server/api/v1/assistants/#{assistant_id}/sessions/#{session_id}/messages"
29
+ headers = { "Authorization" => @auth.header_value }
30
+ resp = request('GET', path, headers: headers, timeout: @timeout)
31
+ array_to_messages(resp)
32
+ end
33
+
34
+ def chat(assistant_id, session_id, message_text)
35
+ path = "/server/api/v1/assistants/#{assistant_id}/sessions/#{session_id}/chat"
36
+ headers = { "Authorization" => @auth.header_value }
37
+ resp = request('POST', path, headers: headers, body: { message: message_text }, timeout: @timeout)
38
+ array_to_messages(resp)
39
+ end
40
+
41
+ private
42
+
43
+ def array_to_messages(arr)
44
+ (arr || []).map do |m|
45
+ Message.new(id: m['id'], content: m['content'], role: m['role'])
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module QyroSDK
4
+ VERSION = "0.1.0"
5
+ end
data/lib/qyro_sdk.rb ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "qyro_sdk/version"
4
+
5
+ require_relative "qyro_sdk/models"
6
+ require_relative "qyro_sdk/auth"
7
+ require_relative "qyro_sdk/exceptions"
8
+ require_relative "qyro_sdk/http_helper"
9
+ require_relative "qyro_sdk/server"
10
+ require_relative "qyro_sdk/client"
11
+
12
+ module QyroSDK
13
+ # Namespace for the Qyro Ruby SDK
14
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: qyro_sdk
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - QyroAI
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2025-09-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: jwt
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.7'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: A Ruby SDK for interacting with the Qyro AI API (server & client).
42
+ email:
43
+ - support@qyroai.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - LICENSE
49
+ - README.md
50
+ - lib/qyro_sdk.rb
51
+ - lib/qyro_sdk/auth.rb
52
+ - lib/qyro_sdk/client.rb
53
+ - lib/qyro_sdk/exceptions.rb
54
+ - lib/qyro_sdk/http_helper.rb
55
+ - lib/qyro_sdk/models.rb
56
+ - lib/qyro_sdk/server.rb
57
+ - lib/qyro_sdk/version.rb
58
+ homepage: https://github.com/qyroai/qyro-ruby-sdk
59
+ licenses:
60
+ - MIT
61
+ metadata: {}
62
+ post_install_message:
63
+ rdoc_options: []
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '2.7'
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ requirements: []
77
+ rubygems_version: 3.3.27
78
+ signing_key:
79
+ specification_version: 4
80
+ summary: Ruby SDK for Qyro AI
81
+ test_files: []