phaseo_sdk 2.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: 442f1a085f0d40c85475b6a863d76ca22105403424729088e1e4a65ef7432b41
4
+ data.tar.gz: 011eb38bf5ec8a6c589afdce5f20f2e640a6943135b98405f8b4c48101042404
5
+ SHA512:
6
+ metadata.gz: 3d4d6647a4f64012a98f0875647eb6ad2d5a9bd27f982ba0f1762d36e33c223ca93e66b69b9e563ebad9dbc2f03f7d0569cedf788a6f823ec5e89cb4b1fb07d9
7
+ data.tar.gz: 71af9cc544a1d6285cd06b1d4a15bd0dac40aa355648ddc77fe415b62a4e9479ca32c534e3751e83154153535acbc845978ebb4a4cd1cef90029dcf62c64cb73
data/README.md ADDED
@@ -0,0 +1,99 @@
1
+ # Phaseo Ruby SDK
2
+
3
+ Official Ruby SDK for Phaseo Gateway.
4
+
5
+ RubyGems package name: `phaseo_sdk`
6
+
7
+ ## Installation
8
+
9
+ ```ruby
10
+ gem "phaseo_sdk"
11
+ ```
12
+
13
+ Then run:
14
+
15
+ ```bash
16
+ bundle install
17
+ ```
18
+
19
+ ## Quick start
20
+
21
+ ```ruby
22
+ require "phaseo_sdk"
23
+
24
+ client = PhaseoSdk::Phaseo.new(
25
+ api_key: ENV.fetch("PHASEO_API_KEY"),
26
+ base_path: ENV.fetch("PHASEO_BASE_URL", "https://api.phaseo.app/v1")
27
+ )
28
+
29
+ response = client.create_response(
30
+ model: "google/gemma-3-27b:free",
31
+ input: "Reply with: Ruby SDK works"
32
+ )
33
+
34
+ puts response["id"]
35
+ ```
36
+
37
+ ## Common methods
38
+
39
+ - `create_response(payload)`
40
+ - `create_chat_completion(payload)`
41
+ - `list_models(options = {})`
42
+ - `list_organisations(options = {})` for paginated `/organisations` discovery
43
+ - `list_pricing_models(options = {})` for `/pricing/models` catalogue pricing discovery
44
+ - `calculate_pricing(payload)` for `/pricing/calculate` usage estimation
45
+ - `list_providers(options = {})`, `get_credits(options = {})`, `get_activity(options = {})`, and `get_analytics(options = {})` for provider discovery and management-key usage surfaces
46
+ - `list_api_keys(options = {})` for management-key `/keys` discovery
47
+ - `create_api_key(payload)`, `update_api_key(id, payload)`, and `delete_api_key(id)` for management-key API-key lifecycle changes
48
+ - `get_api_key(id)` for management-key `/keys/{id}` lookup
49
+ - `list_workspaces(options = {})`, `get_workspace(id)`, `create_workspace(payload)`, `update_workspace(id, payload)`, and `delete_workspace(id)` for management-key workspace lifecycle management
50
+ - `get_current_api_key`
51
+ - `health`
52
+ - `get_model_deprecation_info(model_id)`
53
+ - `validate_model(model_id)`
54
+
55
+ Model discovery supports the public `/models` filters, including `provider`, `provider_status`, `provider_routing_status`, `model_routing_status`, `capability_status`, `provider_availability_status`, `provider_availability_reason`, `status`, `organisation`, `endpoints`, `input_types`, `output_types`, `params`, `availability`, `limit`, and `offset`.
56
+
57
+ Use `provider_availability_reason` with `availability: "all"` when you want rollout-state entries such as `preview_only`, `provider_not_ready`, `gated`, `access_limited`, `region_limited`, `project_limited`, `paused`, or `soft_blocked`. Use `capability_status` with `availability: "all"` when you want non-routable endpoint mappings such as `coming_soon` or `internal_testing`.
58
+
59
+ ```ruby
60
+ models = client.list_models(
61
+ provider: "anthropic",
62
+ provider_status: "beta,not_ready",
63
+ provider_availability_reason: "preview_only,provider_not_ready",
64
+ capability_status: "coming_soon,internal_testing",
65
+ availability: "all",
66
+ )
67
+ ```
68
+
69
+ ## Async job websocket helpers
70
+
71
+ Batch and video operations can expose a websocket lifecycle stream at `/v1/async/{kind}/{id}/ws`.
72
+
73
+ ```ruby
74
+ batch_socket_url = client.batch_websocket_url("batch_123", interval_ms: 1500)
75
+ video_socket_url = client.video_websocket_url("video_123", close_on_terminal: true)
76
+ generic_socket_url = client.get_async_job_websocket_url("video", "video_123")
77
+ resource_socket_url = client.async_jobs.websocket_url("video", "video_123")
78
+
79
+ puts batch_socket_url
80
+ puts video_socket_url
81
+ puts generic_socket_url
82
+ puts resource_socket_url
83
+ ```
84
+
85
+ ## Free and paid models
86
+
87
+ - Models with `:free` in the model ID can be called with zero deposited credits.
88
+ - Paid models require available wallet balance.
89
+
90
+ ## Environment variables
91
+
92
+ - `PHASEO_API_KEY` (required unless passed in code)
93
+ - `PHASEO_BASE_URL` (optional, defaults to `https://api.phaseo.app/v1`)
94
+
95
+ ## Regeneration and local checks
96
+
97
+ - Regenerate generated client: `pnpm openapi:gen:ruby`
98
+ - Build gem: `pnpm --filter @phaseo/ruby-sdk run build`
99
+ - Test: `pnpm --filter @phaseo/ruby-sdk run test`
@@ -0,0 +1,16 @@
1
+ require_relative '../lib/index'
2
+
3
+ api_key = ENV['PHASEO_API_KEY']
4
+ raise 'Set PHASEO_API_KEY' unless api_key
5
+
6
+ client = PhaseoSdk::Phaseo.new(api_key: api_key)
7
+ resp = client.list_models(
8
+ provider: "anthropic",
9
+ provider_status: "beta,not_ready",
10
+ provider_availability_reason: "preview_only,provider_not_ready",
11
+ capability_status: "coming_soon,internal_testing",
12
+ availability: "all",
13
+ limit: 5,
14
+ )
15
+ models = resp.is_a?(Hash) ? (resp["models"] || []) : []
16
+ puts "models: #{models.length}"
data/lib/gen/client.rb ADDED
@@ -0,0 +1,86 @@
1
+ require "json"
2
+ require "net/http"
3
+ require "uri"
4
+
5
+ module Phaseo
6
+ module Gen
7
+ class RequestError < StandardError
8
+ attr_reader :status_code, :response_body
9
+
10
+ def initialize(status_code:, response_body:, status_message: nil, message: nil)
11
+ super(message || build_message(status_code, response_body, status_message))
12
+ @status_code = status_code
13
+ @response_body = response_body
14
+ end
15
+
16
+ private
17
+
18
+ def build_message(status_code, response_body, status_message)
19
+ trimmed = response_body.to_s.strip
20
+ prefix = status_message.to_s.strip.empty? ? "Request failed: #{status_code}" : "Request failed: #{status_code} #{status_message}"
21
+ return prefix if trimmed.empty?
22
+
23
+ "#{prefix} #{trimmed}"
24
+ end
25
+ end
26
+
27
+ class Client
28
+ def initialize(base_url:, headers: {})
29
+ @base_url = base_url.chomp("/")
30
+ @headers = headers
31
+ end
32
+
33
+ def build_request(method:, path:, query: nil, headers: nil, body: nil)
34
+ uri = URI.join(@base_url + "/", path.sub(%r{^/}, ""))
35
+ uri.query = URI.encode_www_form(query) if query && !query.empty?
36
+ http = Net::HTTP.new(uri.host, uri.port)
37
+ http.use_ssl = uri.scheme == "https"
38
+ request_class = Net::HTTP.const_get(method.capitalize)
39
+ req = request_class.new(uri)
40
+ (@headers || {}).merge(headers || {}).each { |k, v| req[k] = v }
41
+ if body
42
+ req["Content-Type"] = "application/json"
43
+ req.body = JSON.dump(body)
44
+ end
45
+ [http, req]
46
+ end
47
+
48
+ def request(method:, path:, query: nil, headers: nil, body: nil)
49
+ http, req = build_request(method:, path:, query:, headers:, body:)
50
+ response = http.request(req)
51
+ unless response.is_a?(Net::HTTPSuccess)
52
+ raise RequestError.new(status_code: response.code.to_i, response_body: response.body.to_s)
53
+ end
54
+ return nil if response.body.nil? || response.body.empty?
55
+ JSON.parse(response.body)
56
+ end
57
+
58
+ def request_bytes(method:, path:, query: nil, headers: nil, body: nil)
59
+ http, req = build_request(method:, path:, query:, headers:, body:)
60
+ response = http.request(req)
61
+ unless response.is_a?(Net::HTTPSuccess)
62
+ raise RequestError.new(status_code: response.code.to_i, response_body: response.body.to_s)
63
+ end
64
+ response.body.to_s.b
65
+ end
66
+
67
+ def request_stream(method:, path:, query: nil, headers: nil, body: nil)
68
+ return enum_for(__method__, method:, path:, query:, headers:, body:) unless block_given?
69
+ http, req = build_request(method:, path:, query:, headers: (headers || {}).merge("Accept" => "text/event-stream"), body:)
70
+ http.request(req) do |response|
71
+ unless response.is_a?(Net::HTTPSuccess)
72
+ raise RequestError.new(status_code: response.code.to_i, response_body: response.body.to_s)
73
+ end
74
+ buffer = String.new
75
+ response.read_body do |chunk|
76
+ buffer << chunk
77
+ while (index = buffer.index("\n"))
78
+ yield buffer.slice!(0, index + 1).sub(/\r?\n\z/, "")
79
+ end
80
+ end
81
+ yield buffer unless buffer.empty?
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end