nitro_intelligence 1.0.1 → 2.0.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: b4de13e4abd28f0e4ce7533a094fd184c18f7804a116e8812c8c6ecbd5036521
4
- data.tar.gz: b98ecfd8fd43f901bf8a4fd4ee36a41c3740a0c43e7e674a0fc9792cec6b1097
3
+ metadata.gz: 9c03a8a4c436205c01d926c024c1bcd12542553f36e3998b10462653cec4cdc1
4
+ data.tar.gz: 58f340818e47d7b6e36dd84f44b4072bf8636a83d4fd6931e65e62ec1ed29ab5
5
5
  SHA512:
6
- metadata.gz: eaa80ace33f1bbdd3c94ef6ea7c7d718903f4cbe1e2559a714be34a6207ddd6c7ebd34a4874f0545d583a76df6af9975b4b36c46ce6eddcfd26a85482c51907c
7
- data.tar.gz: 9b404c2f250b6ba80f46ec957faa31895107b81d2011c5f2ad35e3a1cc6845efe8510bd4e5760b8313d5e30dbf2b74a030d3ea09482af9c76fc193fe64b28e95
6
+ metadata.gz: 70eab830245ab3c7bd4a6835c080770ee324dca9c440ba200f0ac29fc528e9b4be5579091259a9abed7c35ce51372da82d4452d1df71ee26c51a61bdb342acaa
7
+ data.tar.gz: 549cd0d2477d1382cff28d866266797983b0ddd860396c10ff5edd8087f5538a4834b1194d1ae6d5d16d497825bf660308bcacfcb3da12846e2f69ef611264a4
data/docs/README.md CHANGED
@@ -33,6 +33,39 @@ NitroIntelligence.configure do |config|
33
33
 
34
34
  # Agent server settings (optional)
35
35
  config.agent_server_config = {} # Hash of AgentServer keyword arguments
36
+
37
+ # Model configuration
38
+ config.model_config = {
39
+ "default_audio_transcription_model" => "gpt-4o-transcribe",
40
+ "default_text_model" => "gpt-4o-mini",
41
+ "default_image_model" => "nano-banana-2",
42
+ "default_text_to_speech_model" => "gpt-4o-mini-tts",
43
+ "models" => [
44
+ {
45
+ "name" => "gpt-4o-mini",
46
+ "type" => "text"
47
+ },
48
+ {
49
+ "name" => "gpt-4o-transcribe",
50
+ "type" => "audio_transcription"
51
+ },
52
+ {
53
+ "name" => "nano-banana-2",
54
+ "type" => "image",
55
+ "aspect_ratios" => ["1:1", "2:3", "3:2", "3:4", "4:3"],
56
+ "resolutions" => ["512", "1K", "2K"],
57
+ "omit_output_fields" => ["provider_specific_fields.thought_signatures"]
58
+ },
59
+ {
60
+ "name" => "gpt-4o-mini-tts",
61
+ "type" => "text_to_speech",
62
+ "default_voice" => "marin",
63
+ "default_response_format" => "mp3",
64
+ "voices" => ["echo", "nova", "marin", "cedar"],
65
+ "response_formats" => ["mp3", "wav"]
66
+ }
67
+ ]
68
+ }
36
69
  end
37
70
  ```
38
71
 
@@ -48,6 +81,7 @@ end
48
81
  | `observability_base_url` | `String` | `""` | Base URL for the Langfuse observability service |
49
82
  | `observability_projects` | `Array<Hash>` | `[]` | Langfuse project credentials (slug, id, public_key, secret_key) |
50
83
  | `agent_server_config` | `Hash` | `{}` | Credentials for `AgentServer.new`. Expected keys: `base_url` (String) — HTTP base URL of the agent server; `api_key` (String) — bearer token; `user_id` (String, default: `"default-user"`) — caller identity |
84
+ | `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) |
51
85
 
52
86
  ## Basic Usage
53
87
 
@@ -111,6 +145,40 @@ puts result
111
145
  # <OpenAI::Models::Audio::Transcription:0x2fd8c {:text=>"Hola, ¿cómo estás hoy?", :usage=>{:input_tokens=>37, :output_tokens=>9, :total_tokens=>46, :type=>:tokens, :input_token_details=>{:audio_tokens=>28, :text_tokens=>9}}}>
112
146
  ```
113
147
 
148
+ ### Text-to-Speech
149
+
150
+ Nitro Intelligence can be used to create spoken-word audio files from text.
151
+
152
+ Basic example of usage:
153
+
154
+ ```ruby
155
+ client = NitroIntelligence::Client.new
156
+ tts = client.text_to_speech(message: 'Hello, this is Power Home Remodeling Group.')
157
+
158
+ # tts is a StringIO object, we can write the file to disk
159
+ File.binwrite('tts.mp3', tts.string)
160
+ ```
161
+
162
+ Supplying custom parameters:
163
+
164
+ ```ruby
165
+ client = NitroIntelligence::Client.new
166
+ tts = client.text_to_speech(
167
+ message: 'Hello, this is Power Home Remodeling Group.',
168
+ parameters: {
169
+ voice: 'marin',
170
+ response_format: 'mp3',
171
+ instructions: 'Translate the English into Spanish before speaking.',
172
+ speed: 1.25
173
+ }
174
+ )
175
+
176
+ # tts is a StringIO object, we can write the file to disk
177
+ File.binwrite('tts.mp3', tts.string)
178
+ ```
179
+
180
+ For a full list of supported parameters, see the [API reference here](https://developers.openai.com/api/reference/resources/audio/subresources/speech/methods/create ). Note that `voice` and `response_format` are further constrained to the `voices` and `response_formats` listed for the chosen model in `config.model_config`.
181
+
114
182
  ### Image Editing and Generation
115
183
 
116
184
  Nitro Intelligence can be used for image editing and generation
@@ -1,3 +1,6 @@
1
+ require "json"
2
+ require "net/http"
3
+ require "uri"
1
4
  require "nitro_intelligence/tool_call_review_validator"
2
5
 
3
6
  module NitroIntelligence
@@ -85,25 +88,25 @@ module NitroIntelligence
85
88
  }
86
89
  )
87
90
 
88
- raise ThreadInitializationError, thread_response.body if thread_response.code != 200
91
+ raise ThreadInitializationError, thread_response.body if thread_response.code.to_i != 200
89
92
 
90
- thread_response
93
+ JSON.parse(thread_response.body)
91
94
  end
92
95
 
93
96
  def get_thread_state(thread_id:)
94
97
  state_response = get(path: "/threads/#{thread_id}/state")
95
98
 
96
- raise ThreadResumptionError, state_response.body if state_response.code != 200
99
+ raise ThreadResumptionError, state_response.body if state_response.code.to_i != 200
97
100
 
98
- state_response
101
+ JSON.parse(state_response.body)
99
102
  end
100
103
 
101
104
  def get_thread(thread_id:)
102
105
  thread_response = get(path: "/threads/#{thread_id}")
103
106
 
104
- raise ThreadResumptionError, thread_response.body if thread_response.code != 200
107
+ raise ThreadResumptionError, thread_response.body if thread_response.code.to_i != 200
105
108
 
106
- thread_response
109
+ JSON.parse(thread_response.body)
107
110
  end
108
111
 
109
112
  def trigger_run(thread_id:, assistant_id:, last_message:, context: {})
@@ -118,9 +121,10 @@ module NitroIntelligence
118
121
  }
119
122
  )
120
123
 
121
- raise RunError, run_response.body if run_response.code != 200
124
+ raise RunError, run_response.body if run_response.code.to_i != 200
122
125
 
123
- Array(run_response["messages"]).last&.dig("content")
126
+ run = JSON.parse(run_response.body)
127
+ Array(run["messages"]).last&.dig("content")
124
128
  end
125
129
 
126
130
  def resume_run(thread_id:, assistant_id:, resume:, context:)
@@ -135,9 +139,9 @@ module NitroIntelligence
135
139
  }
136
140
  )
137
141
 
138
- raise ThreadResumptionError, run_response.body if run_response.code != 200
142
+ raise ThreadResumptionError, run_response.body if run_response.code.to_i != 200
139
143
 
140
- run_response
144
+ JSON.parse(run_response.body)
141
145
  end
142
146
 
143
147
  def interrupted?(thread)
@@ -163,18 +167,26 @@ module NitroIntelligence
163
167
  end
164
168
 
165
169
  def get(path:)
166
- HTTParty.get(
167
- "#{base_url}#{path}",
168
- headers: request_headers
169
- )
170
+ uri = URI("#{base_url}#{path}")
171
+ http = Net::HTTP.new(uri.host, uri.port)
172
+ http.use_ssl = uri.scheme == "https"
173
+
174
+ request = Net::HTTP::Get.new(uri)
175
+ request_headers.each { |k, v| request[k] = v }
176
+
177
+ http.request(request)
170
178
  end
171
179
 
172
180
  def post(path:, body:)
173
- HTTParty.post(
174
- "#{base_url}#{path}",
175
- headers: request_headers,
176
- body: body.to_json
177
- )
181
+ uri = URI("#{base_url}#{path}")
182
+ http = Net::HTTP.new(uri.host, uri.port)
183
+ http.use_ssl = uri.scheme == "https"
184
+
185
+ request = Net::HTTP::Post.new(uri)
186
+ request_headers.each { |k, v| request[k] = v }
187
+ request.body = body.to_json
188
+
189
+ http.request(request)
178
190
  end
179
191
 
180
192
  def request_headers
@@ -1,6 +1,7 @@
1
1
  require "nitro_intelligence/client/handlers/audio_transcription_handler"
2
2
  require "nitro_intelligence/client/handlers/chat_handler"
3
3
  require "nitro_intelligence/client/handlers/image_handler"
4
+ require "nitro_intelligence/client/handlers/text_to_speech_handler"
4
5
 
5
6
  module NitroIntelligence
6
7
  module Client
@@ -26,6 +27,11 @@ module NitroIntelligence
26
27
  audio_transcription_handler.create(message:, audio_file:, parameters:)
27
28
  end
28
29
 
30
+ # Returns StringIO
31
+ def text_to_speech(message: "", parameters: {})
32
+ text_to_speech_handler.create(message:, parameters:)
33
+ end
34
+
29
35
  private
30
36
 
31
37
  def audio_transcription_handler
@@ -47,6 +53,10 @@ module NitroIntelligence
47
53
  def respond_to_missing?(method_name, include_private = false)
48
54
  @client.respond_to?(method_name, include_private) || super
49
55
  end
56
+
57
+ def text_to_speech_handler
58
+ @text_to_speech_handler ||= Handlers::TextToSpeechHandler.new(client: @client)
59
+ end
50
60
  end
51
61
  end
52
62
  end
@@ -0,0 +1,100 @@
1
+ require "nitro_intelligence/media/audio"
2
+
3
+ module NitroIntelligence
4
+ module Client
5
+ module Handlers
6
+ module Observed
7
+ class TextToSpeechHandler
8
+ class ObservedTextToSpeechPromptError < StandardError; end
9
+
10
+ def initialize(base_handler:, observer:)
11
+ @base_handler = base_handler
12
+ @observer = observer
13
+ end
14
+
15
+ def create(message: "", parameters: {})
16
+ prompt = handle_prompt(parameters:)
17
+
18
+ @base_handler.validate_and_resolve!(parameters)
19
+
20
+ trace_name = parameters[:trace_name] || prompt&.name || @observer.project_client.project.slug
21
+
22
+ @observer.observe(
23
+ "text-to-speech",
24
+ type: :generation,
25
+ parameters:,
26
+ trace_name:,
27
+ prompt:
28
+ ) do |generation|
29
+ workflow(message:, parameters:, trace_id: generation.trace_id)
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def handle_prompt(parameters:)
36
+ return nil if parameters[:prompt_name].blank?
37
+
38
+ prompt = @observer.project_client.project.prompt_store.get_prompt(
39
+ prompt_name: parameters[:prompt_name],
40
+ prompt_label: parameters[:prompt_label],
41
+ prompt_version: parameters[:prompt_version]
42
+ )
43
+ prompt_variables = parameters[:prompt_variables] || {}
44
+
45
+ if prompt.present?
46
+ # Prompts for tts should only be text
47
+ if prompt.type != "text"
48
+ raise ObservedTextToSpeechPromptError,
49
+ "Prompt type for text-to-speech must be text: #{prompt.name}"
50
+ end
51
+ interpolated_prompt = prompt.compile(**prompt_variables)
52
+
53
+ parameters.merge!(prompt.config) unless parameters[:prompt_config_disabled]
54
+ parameters[:instructions] = interpolated_prompt
55
+ end
56
+
57
+ prompt
58
+ end
59
+
60
+ def handle_text_to_speech_upload(tts_file, trace_id)
61
+ upload_handler = NitroIntelligence::Observability::UploadHandler.new(
62
+ auth_token: @observer.project_client.project.auth_token
63
+ )
64
+ upload_handler.upload(
65
+ trace_id,
66
+ upload_queue: Queue.new([Audio.new(tts_file)])
67
+ )
68
+
69
+ uploaded_media = upload_handler.uploaded_media.first
70
+ "@@@langfuseMedia:type=#{uploaded_media.mime_type}|id=#{uploaded_media.reference_id}|source=bytes@@@"
71
+ end
72
+
73
+ def workflow(message:, parameters:, trace_id:)
74
+ tts = @base_handler.perform_request(message:, parameters:)
75
+ output = ""
76
+
77
+ Tempfile.create(["tts", ".#{parameters[:response_format]}"]) do |tempfile|
78
+ tempfile.binmode
79
+ tempfile.write(tts.string)
80
+ tempfile.rewind
81
+
82
+ output = handle_text_to_speech_upload(tempfile, trace_id)
83
+ end
84
+
85
+ # We only get StringIO object as a response
86
+ # We dont have usage on tokens and the actual model that was used
87
+ # We will log the requested model instead
88
+ trace_attributes = {
89
+ model: parameters[:model],
90
+ input: message,
91
+ output:,
92
+ }
93
+
94
+ [tts, trace_attributes]
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,58 @@
1
+ require "openai"
2
+
3
+ module NitroIntelligence
4
+ module Client
5
+ module Handlers
6
+ class TextToSpeechHandler
7
+ ALLOWED_EXTRA_PARAMETERS = OpenAI::Models::Audio::SpeechCreateParams.fields.keys.uniq.freeze
8
+
9
+ def initialize(client:)
10
+ @client = client
11
+ end
12
+
13
+ def create(message: "", parameters: {})
14
+ validate_and_resolve!(parameters)
15
+ perform_request(message:, parameters:)
16
+ end
17
+
18
+ def perform_request(message: "", parameters: {})
19
+ @client.audio.speech.create(
20
+ input: message,
21
+ **parameters.slice(*ALLOWED_EXTRA_PARAMETERS)
22
+ )
23
+ end
24
+
25
+ def validate_and_resolve!(parameters)
26
+ model_name = parameters[:model] || NitroIntelligence.model_catalog.default_text_to_speech_model&.name
27
+ model = NitroIntelligence.model_catalog.lookup_by_name(model_name)
28
+
29
+ # Check model supported
30
+ raise ArgumentError, "Unsupported model: '#{model_name}'" unless model
31
+
32
+ default_parameters = {
33
+ metadata: {},
34
+ model: model.name,
35
+ voice: model.default_voice,
36
+ response_format: model.default_response_format,
37
+ }
38
+
39
+ parameters.replace(default_parameters.merge(parameters))
40
+
41
+ # Check voice supported
42
+ unless model.voices.include?(parameters[:voice])
43
+ raise ArgumentError,
44
+ "Unsupported voice: '#{parameters[:voice]}'. " \
45
+ "Supported voices for #{model.name} are: #{model.voices}"
46
+ end
47
+
48
+ # Check format supported
49
+ return if model.response_formats.include?(parameters[:response_format])
50
+
51
+ raise ArgumentError,
52
+ "Unsupported response_format: '#{parameters[:response_format]}'. " \
53
+ "Supported response_formats for #{model.name} are: #{model.response_formats}"
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -1,6 +1,7 @@
1
1
  require "nitro_intelligence/client/handlers/observed/audio_transcription_handler"
2
2
  require "nitro_intelligence/client/handlers/observed/chat_handler"
3
3
  require "nitro_intelligence/client/handlers/observed/image_handler"
4
+ require "nitro_intelligence/client/handlers/observed/text_to_speech_handler"
4
5
  require "nitro_intelligence/observability/prompt_store"
5
6
  require "nitro_intelligence/observability/upload_handler"
6
7
  require "nitro_intelligence/trace"
@@ -33,6 +34,12 @@ module NitroIntelligence
33
34
  base_handler: Handlers::ImageHandler.new(client: @client), observer: @observer
34
35
  )
35
36
  end
37
+
38
+ def text_to_speech_handler
39
+ @text_to_speech_handler ||= Handlers::Observed::TextToSpeechHandler.new(
40
+ base_handler: Handlers::TextToSpeechHandler.new(client: @client), observer: @observer
41
+ )
42
+ end
36
43
  end
37
44
  end
38
45
  end
@@ -8,8 +8,6 @@ module NitroIntelligence
8
8
  end
9
9
  end
10
10
 
11
- class TextModel < Model; end
12
-
13
11
  class ImageModel < Model
14
12
  attr_reader :aspect_ratios, :resolutions
15
13
 
@@ -19,4 +17,28 @@ module NitroIntelligence
19
17
  @resolutions = resolutions
20
18
  end
21
19
  end
20
+
21
+ class TextModel < Model; end
22
+
23
+ class TextToSpeechModel < Model
24
+ attr_reader :default_voice,
25
+ :default_response_format,
26
+ :voices,
27
+ :response_formats
28
+
29
+ def initialize(
30
+ name:,
31
+ default_voice:,
32
+ default_response_format:,
33
+ voices: [],
34
+ response_formats: [],
35
+ **
36
+ )
37
+ super
38
+ @default_voice = default_voice
39
+ @default_response_format = default_response_format
40
+ @voices = voices
41
+ @response_formats = response_formats
42
+ end
43
+ end
22
44
  end
@@ -2,13 +2,19 @@ require "nitro_intelligence/models/model_factory"
2
2
 
3
3
  module NitroIntelligence
4
4
  class ModelCatalog
5
- attr_reader :models, :default_audio_transcription_model, :default_image_model, :default_text_model
5
+ attr_reader :models,
6
+ :default_audio_transcription_model,
7
+ :default_image_model,
8
+ :default_text_model,
9
+ :default_text_to_speech_model
6
10
 
7
11
  def initialize(model_config)
12
+ model_config = model_config.symbolize_keys
8
13
  @models = (model_config[:models] || []).map { |model_metadata| ModelFactory.build(model_metadata) }
9
14
  @default_audio_transcription_model = lookup_by_name(model_config[:default_audio_transcription_model])
10
15
  @default_image_model = lookup_by_name(model_config[:default_image_model])
11
16
  @default_text_model = lookup_by_name(model_config[:default_text_model])
17
+ @default_text_to_speech_model = lookup_by_name(model_config[:default_text_to_speech_model])
12
18
  end
13
19
 
14
20
  def lookup_by_name(name)
@@ -2,18 +2,18 @@ require "nitro_intelligence/models/model"
2
2
 
3
3
  module NitroIntelligence
4
4
  class ModelFactory
5
+ TYPES = {
6
+ "text" => TextModel,
7
+ "audio_transcription" => TextModel,
8
+ "image" => ImageModel,
9
+ "text_to_speech" => TextToSpeechModel,
10
+ }.freeze
11
+
5
12
  def self.build(model_metadata)
6
13
  model_metadata = model_metadata.symbolize_keys
7
-
8
- if image_model?(model_metadata)
9
- ImageModel.new(**model_metadata)
10
- else
11
- TextModel.new(**model_metadata)
12
- end
13
- end
14
-
15
- def self.image_model?(model_metadata)
16
- model_metadata.key?(:aspect_ratios) || model_metadata.key?(:resolutions)
14
+ type = model_metadata[:type]
15
+ model_class = TYPES.fetch(type) { raise ArgumentError, "Unknown model type: #{type.inspect}" }
16
+ model_class.new(**model_metadata)
17
17
  end
18
18
  end
19
19
  end
@@ -1,6 +1,7 @@
1
1
  require "base64"
2
2
  require "cgi"
3
- require "httparty"
3
+ require "net/http"
4
+ require "uri"
4
5
 
5
6
  require "nitro_intelligence/observability/prompt"
6
7
 
@@ -75,15 +76,17 @@ module NitroIntelligence
75
76
 
76
77
  def get_prompt_request(safe_prompt_name:, prompt_url_params:)
77
78
  auth_token = Base64.strict_encode64("#{@observability_public_key}:#{@observability_secret_key}")
78
- response = HTTParty.get(
79
- "#{@observability_host}/api/public/v2/prompts/#{safe_prompt_name}?#{prompt_url_params}",
80
- headers: {
81
- "Authorization" => "Basic #{auth_token}",
82
- }
83
- )
79
+ uri = URI("#{@observability_host}/api/public/v2/prompts/#{safe_prompt_name}?#{prompt_url_params}")
80
+ http = Net::HTTP.new(uri.host, uri.port)
81
+ http.use_ssl = uri.scheme == "https"
82
+
83
+ request = Net::HTTP::Get.new(uri)
84
+ request["Authorization"] = "Basic #{auth_token}"
85
+
86
+ response = http.request(request)
84
87
 
85
- if response.code != 200
86
- raise ObservabilityPromptNotFoundError, "Prompt: #{safe_prompt_name} Not Found" if response.code == 404
88
+ if response.code.to_i != 200
89
+ raise ObservabilityPromptNotFoundError, "Prompt: #{safe_prompt_name} Not Found" if response.code.to_i == 404
87
90
 
88
91
  raise ObservabilityPromptError, response.body
89
92
  end
@@ -1,11 +1,15 @@
1
1
  require "base64"
2
2
  require "digest"
3
- require "httparty"
3
+ require "json"
4
+ require "net/http"
4
5
  require "time"
6
+ require "uri"
5
7
 
6
8
  module NitroIntelligence
7
9
  module Observability
8
10
  class UploadHandler
11
+ attr_reader :uploaded_media
12
+
9
13
  def initialize(auth_token:)
10
14
  @host = NitroIntelligence.config.observability_base_url
11
15
  @auth_token = auth_token
@@ -92,43 +96,50 @@ module NitroIntelligence
92
96
  private
93
97
 
94
98
  def get_upload_url(request_body)
95
- HTTParty.post(
96
- "#{@host}/api/public/media",
97
- body: request_body.to_json,
98
- headers: {
99
- "Content-Type" => "application/json",
100
- "Authorization" => "Basic #{@auth_token}",
101
- }
102
- )
99
+ uri = URI("#{@host}/api/public/media")
100
+ http = Net::HTTP.new(uri.host, uri.port)
101
+ http.use_ssl = uri.scheme == "https"
102
+
103
+ request = Net::HTTP::Post.new(uri)
104
+ request["Content-Type"] = "application/json"
105
+ request["Authorization"] = "Basic #{@auth_token}"
106
+ request.body = request_body.to_json
107
+
108
+ response = http.request(request)
109
+ JSON.parse(response.body)
103
110
  end
104
111
 
105
112
  def associate_media(media_id, upload_response)
106
113
  request_body = {
107
114
  uploadedAt: Time.now.utc.iso8601(6),
108
- uploadHttpStatus: upload_response.code,
109
- uploadHttpError: upload_response.code == 200 ? nil : upload_response.body,
115
+ uploadHttpStatus: upload_response.code.to_i,
116
+ uploadHttpError: upload_response.code.to_i == 200 ? nil : upload_response.body,
110
117
  }
111
118
 
112
- HTTParty.patch(
113
- "#{@host}/api/public/media/#{media_id}",
114
- body: request_body.to_json,
115
- headers: {
116
- "Content-Type" => "application/json",
117
- "Authorization" => "Basic #{@auth_token}",
118
- }
119
- )
119
+ uri = URI("#{@host}/api/public/media/#{media_id}")
120
+ http = Net::HTTP.new(uri.host, uri.port)
121
+ http.use_ssl = uri.scheme == "https"
122
+
123
+ request = Net::HTTP::Patch.new(uri)
124
+ request["Content-Type"] = "application/json"
125
+ request["Authorization"] = "Basic #{@auth_token}"
126
+ request.body = request_body.to_json
127
+
128
+ http.request(request)
120
129
  end
121
130
 
122
131
  def upload_media(media_id, upload_url, content_type, content_sha256, content_bytes)
123
132
  if media_id.present? && upload_url.present?
124
- return HTTParty.put(
125
- upload_url,
126
- headers: {
127
- "Content-Type" => content_type,
128
- "x-amz-checksum-sha256" => content_sha256,
129
- },
130
- body: content_bytes
131
- )
133
+ uri = URI(upload_url)
134
+ http = Net::HTTP.new(uri.host, uri.port)
135
+ http.use_ssl = uri.scheme == "https"
136
+
137
+ request = Net::HTTP::Put.new(uri)
138
+ request["Content-Type"] = content_type
139
+ request["x-amz-checksum-sha256"] = content_sha256
140
+ request.body = content_bytes
141
+
142
+ return http.request(request)
132
143
  end
133
144
 
134
145
  nil
@@ -1,5 +1,6 @@
1
1
  require "base64"
2
- require "httparty"
2
+ require "net/http"
3
+ require "uri"
3
4
 
4
5
  module NitroIntelligence
5
6
  class Reporter
@@ -10,12 +11,16 @@ module NitroIntelligence
10
11
  end
11
12
 
12
13
  def create_dataset_item(attributes)
13
- HTTParty.post("#{@host}/api/public/dataset-items",
14
- body: attributes.to_json,
15
- headers: {
16
- "Content-Type" => "application/json",
17
- "Authorization" => "Basic #{@project_client.project.auth_token}",
18
- })
14
+ uri = URI("#{@host}/api/public/dataset-items")
15
+ http = Net::HTTP.new(uri.host, uri.port)
16
+ http.use_ssl = uri.scheme == "https"
17
+
18
+ request = Net::HTTP::Post.new(uri)
19
+ request["Content-Type"] = "application/json"
20
+ request["Authorization"] = "Basic #{@project_client.project.auth_token}"
21
+ request.body = attributes.to_json
22
+
23
+ http.request(request)
19
24
  end
20
25
 
21
26
  def score(trace_id:, name:, value:, id: "#{trace_id}-#{name}")
@@ -1,3 +1,3 @@
1
1
  module NitroIntelligence
2
- VERSION = "1.0.1".freeze
2
+ VERSION = "2.0.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: 1.0.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Igor Artemenko
@@ -23,20 +23,6 @@ dependencies:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
25
  version: '7.1'
26
- - !ruby/object:Gem::Dependency
27
- name: httparty
28
- requirement: !ruby/object:Gem::Requirement
29
- requirements:
30
- - - "~>"
31
- - !ruby/object:Gem::Version
32
- version: '0.16'
33
- type: :runtime
34
- prerelease: false
35
- version_requirements: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - "~>"
38
- - !ruby/object:Gem::Version
39
- version: '0.16'
40
26
  - !ruby/object:Gem::Dependency
41
27
  name: langfuse-rb
42
28
  requirement: !ruby/object:Gem::Requirement
@@ -79,152 +65,6 @@ dependencies:
79
65
  - - "~>"
80
66
  - !ruby/object:Gem::Version
81
67
  version: '0.58'
82
- - !ruby/object:Gem::Dependency
83
- name: railties
84
- requirement: !ruby/object:Gem::Requirement
85
- requirements:
86
- - - "~>"
87
- - !ruby/object:Gem::Version
88
- version: '7.1'
89
- type: :runtime
90
- prerelease: false
91
- version_requirements: !ruby/object:Gem::Requirement
92
- requirements:
93
- - - "~>"
94
- - !ruby/object:Gem::Version
95
- version: '7.1'
96
- - !ruby/object:Gem::Dependency
97
- name: license_finder
98
- requirement: !ruby/object:Gem::Requirement
99
- requirements:
100
- - - '='
101
- - !ruby/object:Gem::Version
102
- version: 7.2.1
103
- type: :development
104
- prerelease: false
105
- version_requirements: !ruby/object:Gem::Requirement
106
- requirements:
107
- - - '='
108
- - !ruby/object:Gem::Version
109
- version: 7.2.1
110
- - !ruby/object:Gem::Dependency
111
- name: parser
112
- requirement: !ruby/object:Gem::Requirement
113
- requirements:
114
- - - ">="
115
- - !ruby/object:Gem::Version
116
- version: '2.5'
117
- - - "!="
118
- - !ruby/object:Gem::Version
119
- version: 2.5.1.1
120
- type: :development
121
- prerelease: false
122
- version_requirements: !ruby/object:Gem::Requirement
123
- requirements:
124
- - - ">="
125
- - !ruby/object:Gem::Version
126
- version: '2.5'
127
- - - "!="
128
- - !ruby/object:Gem::Version
129
- version: 2.5.1.1
130
- - !ruby/object:Gem::Dependency
131
- name: pry
132
- requirement: !ruby/object:Gem::Requirement
133
- requirements:
134
- - - '='
135
- - !ruby/object:Gem::Version
136
- version: 0.14.2
137
- type: :development
138
- prerelease: false
139
- version_requirements: !ruby/object:Gem::Requirement
140
- requirements:
141
- - - '='
142
- - !ruby/object:Gem::Version
143
- version: 0.14.2
144
- - !ruby/object:Gem::Dependency
145
- name: pry-byebug
146
- requirement: !ruby/object:Gem::Requirement
147
- requirements:
148
- - - '='
149
- - !ruby/object:Gem::Version
150
- version: 3.10.1
151
- type: :development
152
- prerelease: false
153
- version_requirements: !ruby/object:Gem::Requirement
154
- requirements:
155
- - - '='
156
- - !ruby/object:Gem::Version
157
- version: 3.10.1
158
- - !ruby/object:Gem::Dependency
159
- name: rainbow
160
- requirement: !ruby/object:Gem::Requirement
161
- requirements:
162
- - - '='
163
- - !ruby/object:Gem::Version
164
- version: 2.2.2
165
- type: :development
166
- prerelease: false
167
- version_requirements: !ruby/object:Gem::Requirement
168
- requirements:
169
- - - '='
170
- - !ruby/object:Gem::Version
171
- version: 2.2.2
172
- - !ruby/object:Gem::Dependency
173
- name: rspec
174
- requirement: !ruby/object:Gem::Requirement
175
- requirements:
176
- - - '='
177
- - !ruby/object:Gem::Version
178
- version: 3.13.0
179
- type: :development
180
- prerelease: false
181
- version_requirements: !ruby/object:Gem::Requirement
182
- requirements:
183
- - - '='
184
- - !ruby/object:Gem::Version
185
- version: 3.13.0
186
- - !ruby/object:Gem::Dependency
187
- name: rubocop-powerhome
188
- requirement: !ruby/object:Gem::Requirement
189
- requirements:
190
- - - '='
191
- - !ruby/object:Gem::Version
192
- version: 0.6.1
193
- type: :development
194
- prerelease: false
195
- version_requirements: !ruby/object:Gem::Requirement
196
- requirements:
197
- - - '='
198
- - !ruby/object:Gem::Version
199
- version: 0.6.1
200
- - !ruby/object:Gem::Dependency
201
- name: webmock
202
- requirement: !ruby/object:Gem::Requirement
203
- requirements:
204
- - - '='
205
- - !ruby/object:Gem::Version
206
- version: 3.26.1
207
- type: :development
208
- prerelease: false
209
- version_requirements: !ruby/object:Gem::Requirement
210
- requirements:
211
- - - '='
212
- - !ruby/object:Gem::Version
213
- version: 3.26.1
214
- - !ruby/object:Gem::Dependency
215
- name: yard
216
- requirement: !ruby/object:Gem::Requirement
217
- requirements:
218
- - - '='
219
- - !ruby/object:Gem::Version
220
- version: 0.9.37
221
- type: :development
222
- prerelease: false
223
- version_requirements: !ruby/object:Gem::Requirement
224
- requirements:
225
- - - '='
226
- - !ruby/object:Gem::Version
227
- version: 0.9.37
228
68
  description: The Ruby client for Nitro Intelligence
229
69
  email:
230
70
  - igor.artemenko@powerhrg.com
@@ -245,6 +85,8 @@ files:
245
85
  - lib/nitro_intelligence/client/handlers/observed/audio_transcription_handler.rb
246
86
  - lib/nitro_intelligence/client/handlers/observed/chat_handler.rb
247
87
  - lib/nitro_intelligence/client/handlers/observed/image_handler.rb
88
+ - lib/nitro_intelligence/client/handlers/observed/text_to_speech_handler.rb
89
+ - lib/nitro_intelligence/client/handlers/text_to_speech_handler.rb
248
90
  - lib/nitro_intelligence/client/observed.rb
249
91
  - lib/nitro_intelligence/client/observers/langfuse_observer.rb
250
92
  - lib/nitro_intelligence/configuration.rb