nitro_intelligence 0.0.1

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.
@@ -0,0 +1,110 @@
1
+ require "base64"
2
+ require "cgi"
3
+ require "httparty"
4
+
5
+ require "nitro_intelligence/prompt/prompt"
6
+
7
+ module NitroIntelligence
8
+ class PromptStore
9
+ OBSERVABILITY_PROMPTS_CACHE_KEY_PREFIX = "nitro_intelligence_observability_prompts_".freeze
10
+
11
+ class ObservabilityPromptError < StandardError; end
12
+ class ObservabilityPromptNotFoundError < StandardError; end
13
+
14
+ def initialize(observability_project_slug:, observability_public_key:, observability_secret_key:)
15
+ @observability_project_slug = observability_project_slug
16
+ @observability_public_key = observability_public_key
17
+ @observability_secret_key = observability_secret_key
18
+ @observability_host = NitroIntelligence.config.observability_base_url
19
+ end
20
+
21
+ def get_prompt(prompt_name:, prompt_label: nil, prompt_version: nil)
22
+ safe_prompt_name = CGI.escapeURIComponent(prompt_name)
23
+ prompt = nil
24
+
25
+ if prompt_version.present?
26
+ prompt = get_prompt_by_version(safe_prompt_name:, prompt_version:)
27
+ else
28
+ prompt_label = "production" if prompt_label.nil?
29
+ prompt = get_prompt_by_label(safe_prompt_name:, prompt_label:)
30
+ end
31
+
32
+ prompt = NitroIntelligence::Prompt.new(**prompt) if prompt.present?
33
+
34
+ prompt
35
+ end
36
+
37
+ private
38
+
39
+ def get_prompt_by_label(safe_prompt_name:, prompt_label:)
40
+ cache_key = "#{OBSERVABILITY_PROMPTS_CACHE_KEY_PREFIX}#{@observability_project_slug}_" \
41
+ "#{safe_prompt_name}_#{prompt_label}"
42
+ if (cached_prompt = NitroIntelligence.cache.read(cache_key)).present?
43
+ return cached_prompt
44
+ end
45
+
46
+ NitroIntelligence.logger.info(
47
+ "#{self.class} - Prompt label cache miss. Fetching prompt: #{safe_prompt_name} - #{prompt_label}"
48
+ )
49
+ get_prompt_request(safe_prompt_name:, prompt_url_params: "label=#{prompt_label}")
50
+ rescue => e
51
+ if (rolling_cached_prompt = NitroIntelligence.cache.read("#{cache_key}_rolling")).present?
52
+ NitroIntelligence.logger.warn(
53
+ "#{self.class} #{e} - Using rolling cached prompt: #{safe_prompt_name} - #{prompt_label}"
54
+ )
55
+ return rolling_cached_prompt
56
+ end
57
+
58
+ raise e
59
+ end
60
+
61
+ def get_prompt_by_version(safe_prompt_name:, prompt_version:)
62
+ cache_key = "#{OBSERVABILITY_PROMPTS_CACHE_KEY_PREFIX}#{@observability_project_slug}_" \
63
+ "#{safe_prompt_name}_#{prompt_version}"
64
+
65
+ if (cached_prompt = NitroIntelligence.cache.read(cache_key)).present?
66
+ return cached_prompt
67
+ end
68
+
69
+ NitroIntelligence.logger.info(
70
+ "#{self.class} - Prompt version cache miss. Fetching prompt: #{safe_prompt_name} - #{prompt_version}"
71
+ )
72
+ get_prompt_request(safe_prompt_name:, prompt_url_params: "version=#{prompt_version}")
73
+ end
74
+
75
+ def get_prompt_request(safe_prompt_name:, prompt_url_params:)
76
+ auth_token = Base64.strict_encode64("#{@observability_public_key}:#{@observability_secret_key}")
77
+ response = HTTParty.get(
78
+ "#{@observability_host}/api/public/v2/prompts/#{safe_prompt_name}?#{prompt_url_params}",
79
+ headers: {
80
+ "Authorization" => "Basic #{auth_token}",
81
+ }
82
+ )
83
+
84
+ if response.code != 200
85
+ raise ObservabilityPromptNotFoundError, "Prompt: #{safe_prompt_name} Not Found" if response.code == 404
86
+
87
+ raise ObservabilityPromptError, response.body
88
+ end
89
+
90
+ prompt = JSON.parse(response.body, symbolize_names: true)
91
+ write_prompt_caches(safe_prompt_name:, prompt:)
92
+ prompt
93
+ end
94
+
95
+ def write_prompt_caches(safe_prompt_name:, prompt:)
96
+ # Write versioned cache key
97
+ version_cache_key = "#{OBSERVABILITY_PROMPTS_CACHE_KEY_PREFIX}#{@observability_project_slug}_" \
98
+ "#{safe_prompt_name}_#{prompt[:version]}"
99
+ NitroIntelligence.cache.write(version_cache_key, prompt, expires_in: nil)
100
+
101
+ # Store all versions in an array cache per label
102
+ prompt[:labels].each do |label|
103
+ label_cache_key = "#{OBSERVABILITY_PROMPTS_CACHE_KEY_PREFIX}#{@observability_project_slug}_" \
104
+ "#{safe_prompt_name}_#{label}"
105
+ NitroIntelligence.cache.write(label_cache_key, prompt, expires_in: 5.minutes)
106
+ NitroIntelligence.cache.write("#{label_cache_key}_rolling", prompt, expires_in: nil)
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,7 @@
1
+ module NitroIntelligence
2
+ module Trace
3
+ def self.create_id(seed: SecureRandom.uuid, length: 32)
4
+ Digest::SHA256.hexdigest(seed)[0, length]
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ module NitroIntelligence
2
+ VERSION = "0.0.1".freeze
3
+ end
@@ -0,0 +1,76 @@
1
+ require "active_support"
2
+ require "active_support/core_ext"
3
+ require "base64"
4
+ require "httparty"
5
+ require "langfuse"
6
+ require "openai"
7
+
8
+ require "nitro_intelligence/version"
9
+ require "nitro_intelligence/agent_server"
10
+ require "nitro_intelligence/client"
11
+ require "nitro_intelligence/configuration"
12
+ require "nitro_intelligence/langfuse_extension"
13
+ require "nitro_intelligence/langfuse_tracer_provider"
14
+ require "nitro_intelligence/media/image_generation"
15
+ require "nitro_intelligence/media/upload_handler"
16
+ require "nitro_intelligence/models/model_catalog"
17
+ require "nitro_intelligence/prompt/prompt_store"
18
+
19
+ module NitroIntelligence
20
+ OBSERVABILITY_PROJECTS_CACHE_KEY_PREFIX = "nitro_intelligence_observability_projects_".freeze
21
+ CUSTOM_PARAMS = %i[observability_project_slug prompt_config_disabled prompt_label
22
+ prompt_name prompt_variables prompt_version trace_name user_id trace_seed].freeze
23
+
24
+ class ObservabilityUnavailableError < StandardError; end
25
+ class ObservabilityProjectNotFoundError < StandardError; end
26
+ class ObservabilityProjectConfigNotFoundError < StandardError; end
27
+ class LangfuseClientNotFoundError < StandardError; end
28
+
29
+ mattr_accessor :configuration, default: Configuration
30
+
31
+ class << self
32
+ delegate :configure, :config, :logger, :environment, to: :configuration
33
+
34
+ def agent_server
35
+ AgentServer.new(**configuration.agent_server_config.symbolize_keys)
36
+ end
37
+
38
+ def cache
39
+ configuration.cache_provider
40
+ end
41
+
42
+ def model_catalog
43
+ @model_catalog ||= NitroIntelligence::ModelCatalog.new(configuration.model_config)
44
+ end
45
+
46
+ def omit_params
47
+ (CUSTOM_PARAMS + ImageGeneration::Config::CUSTOM_PARAMS).uniq
48
+ end
49
+
50
+ def langfuse_clients
51
+ if @langfuse_clients.nil?
52
+ NitroIntelligence.logger.warn("Langfuse clients were not initialized")
53
+ return {}
54
+ end
55
+
56
+ @langfuse_clients
57
+ end
58
+
59
+ def initialize_langfuse_clients
60
+ @langfuse_clients = configuration.observability_projects.to_h do |project|
61
+ key = project["slug"]
62
+
63
+ value = LangfuseExtension.new do |config|
64
+ config.public_key = project["public_key"]
65
+ config.secret_key = project["secret_key"]
66
+ config.base_url = NitroIntelligence.config.observability_base_url
67
+ # Default flush of 60 seconds can be too quick when
68
+ # dealing with longer responses like image gen
69
+ config.flush_interval = 120
70
+ end
71
+
72
+ [key, value]
73
+ end
74
+ end
75
+ end
76
+ end
metadata ADDED
@@ -0,0 +1,277 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: nitro_intelligence
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Igor Artemenko
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: activesupport
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '7.1'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
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.2
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.2
40
+ - !ruby/object:Gem::Dependency
41
+ name: langfuse-rb
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - '='
45
+ - !ruby/object:Gem::Version
46
+ version: 0.6.0
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - '='
52
+ - !ruby/object:Gem::Version
53
+ version: 0.6.0
54
+ - !ruby/object:Gem::Dependency
55
+ name: mini_magick
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '4.10'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '4.10'
68
+ - !ruby/object:Gem::Dependency
69
+ name: openai
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: 0.23.0
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: 0.23.0
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
+ description: The Ruby client for Nitro Intelligence
229
+ email:
230
+ - igor.artemenko@powerhrg.com
231
+ executables: []
232
+ extensions: []
233
+ extra_rdoc_files: []
234
+ files:
235
+ - Rakefile
236
+ - docs/README.md
237
+ - lib/nitro_intelligence.rb
238
+ - lib/nitro_intelligence/agent_server.rb
239
+ - lib/nitro_intelligence/client.rb
240
+ - lib/nitro_intelligence/configuration.rb
241
+ - lib/nitro_intelligence/langfuse_extension.rb
242
+ - lib/nitro_intelligence/langfuse_tracer_provider.rb
243
+ - lib/nitro_intelligence/media/image.rb
244
+ - lib/nitro_intelligence/media/image_generation.rb
245
+ - lib/nitro_intelligence/media/media.rb
246
+ - lib/nitro_intelligence/media/upload_handler.rb
247
+ - lib/nitro_intelligence/models/model.rb
248
+ - lib/nitro_intelligence/models/model_catalog.rb
249
+ - lib/nitro_intelligence/models/model_factory.rb
250
+ - lib/nitro_intelligence/null_cache.rb
251
+ - lib/nitro_intelligence/prompt/prompt.rb
252
+ - lib/nitro_intelligence/prompt/prompt_store.rb
253
+ - lib/nitro_intelligence/trace.rb
254
+ - lib/nitro_intelligence/version.rb
255
+ homepage: https://github.com/powerhome/nitro-intelligence.rb
256
+ licenses:
257
+ - MIT
258
+ metadata:
259
+ rubygems_mfa_required: 'true'
260
+ rdoc_options: []
261
+ require_paths:
262
+ - lib
263
+ required_ruby_version: !ruby/object:Gem::Requirement
264
+ requirements:
265
+ - - ">="
266
+ - !ruby/object:Gem::Version
267
+ version: '3.2'
268
+ required_rubygems_version: !ruby/object:Gem::Requirement
269
+ requirements:
270
+ - - ">="
271
+ - !ruby/object:Gem::Version
272
+ version: '0'
273
+ requirements: []
274
+ rubygems_version: 3.6.9
275
+ specification_version: 4
276
+ summary: Nitro Intelligence
277
+ test_files: []