nitro_intelligence 2.6.0 → 2.8.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: 92e09bec20a682ff36923a40ad1dc9d3804c3bea3da199374859d5779034d524
4
- data.tar.gz: 303cfc371638d3c14e41ad2610ca2de68944c5f0877db5ab77921ec77b192dd2
3
+ metadata.gz: 5f65b41b164172defbd342f01a3be82035ce33ce1f057a64700391fddacc8d51
4
+ data.tar.gz: 8c1086916735274f86154cebf70bc7cc91e30b2459da5e9adc3bb773e18f661d
5
5
  SHA512:
6
- metadata.gz: 1d7b784fb71aa635ec0f6dc6388b6925312200e4cb757a6598b498ea0c8632bfd1413cbe6dd2a192244bd206d83f335a8865f3387d11d038092beeac65ae5397
7
- data.tar.gz: 6225968bb8d513bffe785dbf91aba771bfdf0306571f8d3150489a839e8780c60b1ab7e15608f310ac56305c8dfd643b7b00a312485db3c3ee716d7573f895b7
6
+ metadata.gz: 9e8ed2298e83a75a257a91df4fd8ad1a64a86e53ef312f6c4b466bd5b9e88753c45af292fa1af3896f9fd8b7804222dbba54f23ed22962e28e4b7d20b398154e
7
+ data.tar.gz: 77f1fe28780cc5ea002754a102ae5b28046104597d8c18a76f2c5e4b50523df475f5e2ec2afd7d8159236621a435e1b2109e656bbd9e2c04cf8094feac8c7654
data/docs/README.md CHANGED
@@ -18,10 +18,10 @@ NitroIntelligence.configure do |config|
18
18
 
19
19
  # Inference (LLM) settings
20
20
  config.inference_api_key = "..." # API key for the inference service
21
- config.inference_base_url = "https://..." # Base URL for the inference service
21
+ config.inference_base_url = "https://..." # Base URL for the inference service (optional; defaults to the inference gateway)
22
22
 
23
23
  # Observability (Langfuse) settings
24
- config.observability_base_url = "https://..." # Base URL for the observability service
24
+ config.observability_base_url = "https://..." # Base URL for the observability service (optional; defaults to Cerebro)
25
25
  config.observability_projects = [ # Array of project credential hashes
26
26
  {
27
27
  "slug" => "my-feature-project",
@@ -83,10 +83,10 @@ end
83
83
  | `environment` | `String` | `"test"` | Runtime environment name |
84
84
  | `cache_provider` | cache store | `NullCache` | ActiveSupport-compatible cache store |
85
85
  | `inference_api_key` | `String` | `""` | API key for the LLM inference service |
86
- | `inference_base_url` | `String` | `""` | Base URL for the LLM inference service |
87
- | `observability_base_url` | `String` | `""` | Base URL for the Langfuse observability service |
86
+ | `inference_base_url` | `String` | `"https://inference.powerhome.ai"` | Base URL for the LLM inference service. Defaults to the shared inference gateway, so only a host talking to a different one needs to set it |
87
+ | `observability_base_url` | `String` | `"https://cerebro.powerhome.ai"` | Base URL for the Langfuse observability service. Defaults to Cerebro, so only a host talking to a different one needs to set it |
88
88
  | `observability_projects` | `Array<Hash>` | `[]` | Langfuse project credentials (slug, id, public_key, secret_key) |
89
- | `assistants_config` | `Hash` | `{}` | Assistants to make addressable by key. `base_url` (String) and `user_id` (String, default: `"default-user"`) are shared by every entry; `definitions` (Hash) holds one entry per assistant, keyed by what it is looked up with, each able to override a shared value. Without `definitions` the hash is read as credentials for a single `Assistants.new` — see [Assistants](#assistants) |
89
+ | `assistants_config` | `Hash` | `{}` | Assistants to make addressable by key. `base_url` (String, default: `"https://assistants.powerhome.ai"`) and `user_id` (String, default: `"default-user"`) are shared by every entry; `definitions` (Hash) holds one entry per assistant, keyed by what it is looked up with, each able to override a shared value. Without `definitions` the hash is read as credentials for a single `Assistants.new`, which takes the same two defaults — see [Assistants](#assistants) |
90
90
  | `model_config` | `Hash` | `{}` | Model defaults and per-model settings. Top-level keys: `default_text_model`, `default_audio_transcription_model`, `default_image_model`, `default_text_to_speech_model`, and `models` (array of per-model hashes keyed by `name` and `type`, with type-specific options like `aspect_ratios`/`resolutions` for images or `voices`/`response_formats` for TTS) |
91
91
 
92
92
  ## Basic Usage
@@ -525,6 +525,7 @@ entry per assistant, keyed by what you look it up with:
525
525
 
526
526
  ```ruby
527
527
  config.assistants_config = {
528
+ # Optional. Defaults to the shared Assistants deployment.
528
529
  "base_url" => "https://nip-assistants.example.com",
529
530
  "user_id" => "my-app",
530
531
  "definitions" => {
@@ -562,9 +563,14 @@ convention, so nothing here is tied to one deployment's wiring.
562
563
 
563
564
  A key that resolves without them raises `Assistant::ConfigurationError`, naming every field it
564
565
  is missing at once so a host resolving them from elsewhere can see which lookup failed.
566
+ `base_url` is not among them: an entry that says nothing about where to reach its assistant
567
+ gets `https://assistants.powerhome.ai`, so only a host talking to a different deployment — a
568
+ review environment, a local server — sets it.
565
569
 
566
570
  ### Without `definitions`
567
571
 
568
572
  `assistants_config` lacking `definitions` is read as keyword arguments for a single
569
573
  `Assistants` client, and `NitroIntelligence.assistants` returns that client rather than a
570
574
  registry. This is the shape that predates lookup by key; a host still on it is left alone.
575
+ `base_url` is optional here too, and a configuration without one reaches the same shared
576
+ deployment.
@@ -1,3 +1,5 @@
1
+ require "nitro_intelligence/assistants"
2
+
1
3
  module NitroIntelligence
2
4
  # One assistant resolved by name: the client for its deployment, plus the id every run has to
3
5
  # carry.
@@ -8,8 +10,10 @@ module NitroIntelligence
8
10
  class Assistant
9
11
  class ConfigurationError < StandardError; end
10
12
 
11
- DEFAULT_USER_ID = "default-user".freeze
12
- REQUIRED = %w[base_url api_key assistant_id].freeze
13
+ DEFAULT_USER_ID = Assistants::DEFAULT_USER_ID
14
+
15
+ # `base_url` is not among them: the client defaults it, so it cannot be missing.
16
+ REQUIRED = %w[api_key assistant_id].freeze
13
17
 
14
18
  # The key an assistant is filed and looked up under. Not its name: an entry usually carries
15
19
  # a `name` of its own, a human-readable label for the assistant's record, and the two are
@@ -25,7 +29,7 @@ module NitroIntelligence
25
29
  # application has no use for, such as the graph or the observability project it reports to.
26
30
  def initialize(key, base_url: nil, api_key: nil, assistant_id: nil, user_id: nil, **_kwargs)
27
31
  @key = key.to_s
28
- @base_url = base_url.presence
32
+ @base_url = base_url.presence || Assistants::DEFAULT_BASE_URL
29
33
  @api_key = api_key.presence
30
34
  @assistant_id = assistant_id.presence
31
35
  @user_id = user_id.presence || DEFAULT_USER_ID
@@ -66,7 +70,7 @@ module NitroIntelligence
66
70
  # Reported together and named, since a host resolving these from somewhere else needs to
67
71
  # know which one it failed to supply.
68
72
  def validate!
69
- values = { "base_url" => @base_url, "api_key" => @api_key, "assistant_id" => @assistant_id }
73
+ values = { "api_key" => @api_key, "assistant_id" => @assistant_id }
70
74
  missing = REQUIRED.select { |field| values[field].blank? }
71
75
  return if missing.empty?
72
76
 
@@ -59,7 +59,11 @@ module NitroIntelligence
59
59
  "Configured: #{keys.sort.join(', ').presence || '(none)'}"
60
60
  end
61
61
 
62
- attributes = @config.slice(*SHARED_KEYS).merge(definition.to_h.deep_stringify_keys)
62
+ # Blank entry values are dropped rather than merged, so a definition built from a source
63
+ # that writes an unset field as null falls back to the shared setting instead of shadowing
64
+ # it with nothing.
65
+ entry = definition.to_h.deep_stringify_keys.compact_blank
66
+ attributes = @config.slice(*SHARED_KEYS).merge(entry)
63
67
  Assistant.new(key, **attributes.symbolize_keys)
64
68
  end
65
69
  end
@@ -14,14 +14,20 @@ module NitroIntelligence
14
14
  # Assistants answers with a conflict when `ifExists: "raise"` is sent for a thread that already exists.
15
15
  THREAD_CONFLICT_CODE = 409
16
16
 
17
+ # Every assistant this gem is built for is served by the same deployment, so a client told
18
+ # nothing about where to reach one gets that deployment. A host talking to a different one
19
+ # -- a review environment, a local server -- still says so.
20
+ DEFAULT_BASE_URL = "https://assistants.powerhome.ai".freeze
21
+
22
+ DEFAULT_USER_ID = "default-user".freeze
23
+
17
24
  attr_reader :base_url, :user_id
18
25
 
19
- def initialize(base_url:, api_key:, user_id: "default-user")
20
- raise ConfigurationError, "base_url is required" if base_url.blank?
26
+ def initialize(api_key:, base_url: nil, user_id: DEFAULT_USER_ID)
21
27
  raise ConfigurationError, "api_key is required" if api_key.blank?
22
28
  raise ConfigurationError, "user_id is required" if user_id.blank?
23
29
 
24
- @base_url = base_url
30
+ @base_url = base_url.presence || DEFAULT_BASE_URL
25
31
  @api_key = api_key
26
32
  @user_id = user_id
27
33
  @tool_call_review_validator = ToolCallReviewValidator.new
@@ -201,9 +207,19 @@ module NitroIntelligence
201
207
  raise RunError, run_response.body if run_response.code.to_i != 200
202
208
 
203
209
  run = JSON.parse(run_response.body)
210
+ raise_run_error!(run, RunError)
211
+
204
212
  Array(run["messages"]).last&.dig("content")
205
213
  end
206
214
 
215
+ def raise_run_error!(run, error)
216
+ failure = run["__error__"]
217
+ return if failure.blank?
218
+
219
+ detail = failure.is_a?(Hash) ? [failure["error"], failure["message"]].compact.join(": ") : failure.to_s
220
+ raise error, detail
221
+ end
222
+
207
223
  def resume_run(thread_id:, assistant_id:, resume:, context:)
208
224
  run_response = post(
209
225
  path: "/threads/#{thread_id}/runs/wait",
@@ -218,7 +234,10 @@ module NitroIntelligence
218
234
 
219
235
  raise ThreadResumptionError, run_response.body if run_response.code.to_i != 200
220
236
 
221
- JSON.parse(run_response.body)
237
+ run = JSON.parse(run_response.body)
238
+ raise_run_error!(run, ThreadResumptionError)
239
+
240
+ run
222
241
  end
223
242
 
224
243
  def interrupted?(thread)
@@ -61,10 +61,9 @@ module NitroIntelligence
61
61
 
62
62
  private
63
63
 
64
- # `last_response` carries the HTTP metadata of the response a typed model was
65
- # built from. The client leaves it unset on nested and locally constructed
66
- # models, and on endpoints returning raw or binary payloads, so both the
67
- # method and its value are optional.
64
+ # `last_response` carries the HTTP metadata of the response a model was built
65
+ # from. The client leaves it unset on nested and locally constructed models,
66
+ # so both the method and its value are optional.
68
67
  def response_headers(response)
69
68
  return nil unless response.respond_to?(:last_response)
70
69
 
@@ -79,11 +79,13 @@ module NitroIntelligence
79
79
  output = handle_text_to_speech_upload(tempfile, trace_id)
80
80
  end
81
81
 
82
- # We only get StringIO object as a response, so there are no usage details
83
- # and no resolved model to record. The requested model and the input are
84
- # already on the observation from before the request ran.
82
+ # Usage details and the resolved model are read off a response body, and
83
+ # this endpoint's body is audio, so neither is available here. The
84
+ # requested model and the input are already on the observation from
85
+ # before the request ran.
85
86
  trace_attributes = {
86
87
  output:,
88
+ cost_details: @base_handler.cost_details(tts),
87
89
  }
88
90
 
89
91
  [tts, trace_attributes]
@@ -12,9 +12,9 @@ module NitroIntelligence
12
12
  config_accessor :environment, default: "test"
13
13
  config_accessor :assistants_config, default: {}
14
14
  config_accessor :inference_api_key, default: ""
15
- config_accessor :inference_base_url, default: ""
15
+ config_accessor :inference_base_url, default: "https://inference.powerhome.ai"
16
16
  config_accessor :model_config, default: {}
17
- config_accessor :observability_base_url, default: ""
17
+ config_accessor :observability_base_url, default: "https://cerebro.powerhome.ai"
18
18
  config_accessor :observability_projects, default: []
19
19
  config_accessor :observability_user_id, default: ""
20
20
 
@@ -1,3 +1,3 @@
1
1
  module NitroIntelligence
2
- VERSION = "2.6.0".freeze
2
+ VERSION = "2.8.0".freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nitro_intelligence
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.6.0
4
+ version: 2.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Igor Artemenko
@@ -57,14 +57,14 @@ dependencies:
57
57
  requirements:
58
58
  - - "~>"
59
59
  - !ruby/object:Gem::Version
60
- version: '0.79'
60
+ version: '0.86'
61
61
  type: :runtime
62
62
  prerelease: false
63
63
  version_requirements: !ruby/object:Gem::Requirement
64
64
  requirements:
65
65
  - - "~>"
66
66
  - !ruby/object:Gem::Version
67
- version: '0.79'
67
+ version: '0.86'
68
68
  description: The Ruby client for Nitro Intelligence
69
69
  email:
70
70
  - igor.artemenko@powerhrg.com