nitro_intelligence 0.0.1 → 1.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 +4 -4
- data/docs/README.md +83 -11
- data/lib/nitro_intelligence/agent_server.rb +119 -8
- data/lib/nitro_intelligence/client/base.rb +52 -0
- data/lib/nitro_intelligence/client/client.rb +13 -0
- data/lib/nitro_intelligence/client/factory.rb +53 -0
- data/lib/nitro_intelligence/client/handlers/audio_transcription_handler.rb +38 -0
- data/lib/nitro_intelligence/client/handlers/chat_handler.rb +41 -0
- data/lib/nitro_intelligence/client/handlers/image_handler.rb +61 -0
- data/lib/nitro_intelligence/client/handlers/observed/audio_transcription_handler.rb +90 -0
- data/lib/nitro_intelligence/client/handlers/observed/chat_handler.rb +74 -0
- data/lib/nitro_intelligence/client/handlers/observed/image_handler.rb +109 -0
- data/lib/nitro_intelligence/client/observed.rb +38 -0
- data/lib/nitro_intelligence/client/observers/langfuse_observer.rb +75 -0
- data/lib/nitro_intelligence/configuration.rb +2 -0
- data/lib/nitro_intelligence/langfuse_extension.rb +10 -53
- data/lib/nitro_intelligence/media/audio.rb +50 -0
- data/lib/nitro_intelligence/media/image_generation.rb +4 -2
- data/lib/nitro_intelligence/models/model_catalog.rb +7 -2
- data/lib/nitro_intelligence/observability/project.rb +33 -0
- data/lib/nitro_intelligence/observability/project_client.rb +18 -0
- data/lib/nitro_intelligence/observability/project_client_registry.rb +44 -0
- data/lib/nitro_intelligence/observability/prompt.rb +58 -0
- data/lib/nitro_intelligence/observability/prompt_store.rb +112 -0
- data/lib/nitro_intelligence/observability/upload_handler.rb +138 -0
- data/lib/nitro_intelligence/reporter.rb +43 -0
- data/lib/nitro_intelligence/tool_call_review_validator.rb +69 -0
- data/lib/nitro_intelligence/trace.rb +2 -2
- data/lib/nitro_intelligence/version.rb +1 -1
- data/lib/nitro_intelligence.rb +10 -44
- metadata +26 -10
- data/lib/nitro_intelligence/client.rb +0 -337
- data/lib/nitro_intelligence/media/upload_handler.rb +0 -135
- data/lib/nitro_intelligence/prompt/prompt.rb +0 -56
- data/lib/nitro_intelligence/prompt/prompt_store.rb +0 -110
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
require "base64"
|
|
2
|
+
require "cgi"
|
|
3
|
+
require "httparty"
|
|
4
|
+
|
|
5
|
+
require "nitro_intelligence/observability/prompt"
|
|
6
|
+
|
|
7
|
+
module NitroIntelligence
|
|
8
|
+
module Observability
|
|
9
|
+
class PromptStore
|
|
10
|
+
OBSERVABILITY_PROMPTS_CACHE_KEY_PREFIX = "nitro_intelligence_observability_prompts_".freeze
|
|
11
|
+
|
|
12
|
+
class ObservabilityPromptError < StandardError; end
|
|
13
|
+
class ObservabilityPromptNotFoundError < StandardError; end
|
|
14
|
+
|
|
15
|
+
def initialize(observability_project_slug:, observability_public_key:, observability_secret_key:)
|
|
16
|
+
@observability_project_slug = observability_project_slug
|
|
17
|
+
@observability_public_key = observability_public_key
|
|
18
|
+
@observability_secret_key = observability_secret_key
|
|
19
|
+
@observability_host = NitroIntelligence.config.observability_base_url
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def get_prompt(prompt_name:, prompt_label: nil, prompt_version: nil)
|
|
23
|
+
safe_prompt_name = CGI.escapeURIComponent(prompt_name)
|
|
24
|
+
prompt = nil
|
|
25
|
+
|
|
26
|
+
if prompt_version.present?
|
|
27
|
+
prompt = get_prompt_by_version(safe_prompt_name:, prompt_version:)
|
|
28
|
+
else
|
|
29
|
+
prompt_label = "production" if prompt_label.nil?
|
|
30
|
+
prompt = get_prompt_by_label(safe_prompt_name:, prompt_label:)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
prompt = Prompt.new(**prompt) if prompt.present?
|
|
34
|
+
|
|
35
|
+
prompt
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def get_prompt_by_label(safe_prompt_name:, prompt_label:)
|
|
41
|
+
cache_key = "#{OBSERVABILITY_PROMPTS_CACHE_KEY_PREFIX}#{@observability_project_slug}_" \
|
|
42
|
+
"#{safe_prompt_name}_#{prompt_label}"
|
|
43
|
+
if (cached_prompt = NitroIntelligence.cache.read(cache_key)).present?
|
|
44
|
+
return cached_prompt
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
NitroIntelligence.logger.info(
|
|
48
|
+
"#{self.class} - Prompt label cache miss. Fetching prompt: #{safe_prompt_name} - #{prompt_label}"
|
|
49
|
+
)
|
|
50
|
+
get_prompt_request(safe_prompt_name:, prompt_url_params: "label=#{prompt_label}")
|
|
51
|
+
rescue => e
|
|
52
|
+
if (rolling_cached_prompt = NitroIntelligence.cache.read("#{cache_key}_rolling")).present?
|
|
53
|
+
NitroIntelligence.logger.warn(
|
|
54
|
+
"#{self.class} #{e} - Using rolling cached prompt: #{safe_prompt_name} - #{prompt_label}"
|
|
55
|
+
)
|
|
56
|
+
return rolling_cached_prompt
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
raise e
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def get_prompt_by_version(safe_prompt_name:, prompt_version:)
|
|
63
|
+
cache_key = "#{OBSERVABILITY_PROMPTS_CACHE_KEY_PREFIX}#{@observability_project_slug}_" \
|
|
64
|
+
"#{safe_prompt_name}_#{prompt_version}"
|
|
65
|
+
|
|
66
|
+
if (cached_prompt = NitroIntelligence.cache.read(cache_key)).present?
|
|
67
|
+
return cached_prompt
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
NitroIntelligence.logger.info(
|
|
71
|
+
"#{self.class} - Prompt version cache miss. Fetching prompt: #{safe_prompt_name} - #{prompt_version}"
|
|
72
|
+
)
|
|
73
|
+
get_prompt_request(safe_prompt_name:, prompt_url_params: "version=#{prompt_version}")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def get_prompt_request(safe_prompt_name:, prompt_url_params:)
|
|
77
|
+
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
|
+
)
|
|
84
|
+
|
|
85
|
+
if response.code != 200
|
|
86
|
+
raise ObservabilityPromptNotFoundError, "Prompt: #{safe_prompt_name} Not Found" if response.code == 404
|
|
87
|
+
|
|
88
|
+
raise ObservabilityPromptError, response.body
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
prompt = JSON.parse(response.body, symbolize_names: true)
|
|
92
|
+
write_prompt_caches(safe_prompt_name:, prompt:)
|
|
93
|
+
prompt
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def write_prompt_caches(safe_prompt_name:, prompt:)
|
|
97
|
+
# Write versioned cache key
|
|
98
|
+
version_cache_key = "#{OBSERVABILITY_PROMPTS_CACHE_KEY_PREFIX}#{@observability_project_slug}_" \
|
|
99
|
+
"#{safe_prompt_name}_#{prompt[:version]}"
|
|
100
|
+
NitroIntelligence.cache.write(version_cache_key, prompt, expires_in: nil)
|
|
101
|
+
|
|
102
|
+
# Store all versions in an array cache per label
|
|
103
|
+
prompt[:labels].each do |label|
|
|
104
|
+
label_cache_key = "#{OBSERVABILITY_PROMPTS_CACHE_KEY_PREFIX}#{@observability_project_slug}_" \
|
|
105
|
+
"#{safe_prompt_name}_#{label}"
|
|
106
|
+
NitroIntelligence.cache.write(label_cache_key, prompt, expires_in: 5.minutes)
|
|
107
|
+
NitroIntelligence.cache.write("#{label_cache_key}_rolling", prompt, expires_in: nil)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
require "base64"
|
|
2
|
+
require "digest"
|
|
3
|
+
require "httparty"
|
|
4
|
+
require "time"
|
|
5
|
+
|
|
6
|
+
module NitroIntelligence
|
|
7
|
+
module Observability
|
|
8
|
+
class UploadHandler
|
|
9
|
+
def initialize(auth_token:)
|
|
10
|
+
@host = NitroIntelligence.config.observability_base_url
|
|
11
|
+
@auth_token = auth_token
|
|
12
|
+
@uploaded_media = []
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def replace_base64_with_media_references(payload)
|
|
16
|
+
# Make it easier to lookup message style image_urls
|
|
17
|
+
media_lookup = @uploaded_media.index_by { |image| "data:#{image.mime_type};base64,#{image.base64}" }
|
|
18
|
+
|
|
19
|
+
replace_base64_image_url = ->(node) do
|
|
20
|
+
case node
|
|
21
|
+
when Hash
|
|
22
|
+
url_key = if node.key?(:url)
|
|
23
|
+
:url
|
|
24
|
+
else
|
|
25
|
+
(node.key?("url") ? "url" : nil)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
if url_key
|
|
29
|
+
url_value = node[url_key]
|
|
30
|
+
|
|
31
|
+
# Replace base64 strings if they match with our uploaded media
|
|
32
|
+
if (media = media_lookup[url_value])
|
|
33
|
+
# Overwrite base64 string with Langfuse media ref
|
|
34
|
+
# This *should* be rendering inline in the gui
|
|
35
|
+
# https://github.com/langfuse/langfuse/issues/4555
|
|
36
|
+
# https://github.com/langfuse/langfuse/issues/5030
|
|
37
|
+
node[url_key] = "@@@langfuseMedia:type=#{media.mime_type}|id=#{media.reference_id}|source=bytes@@@"
|
|
38
|
+
# Sometimes models can generate unwanted images that will not have a reference
|
|
39
|
+
# these untracked base64 strings can easily push 4.5mb Langfuse limit
|
|
40
|
+
elsif url_value.is_a?(String) && url_value.start_with?("data:")
|
|
41
|
+
node[url_key] = "[Discarded media]"
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
node.each_value { |val| replace_base64_image_url.call(val) }
|
|
46
|
+
|
|
47
|
+
when Array
|
|
48
|
+
node.each { |val| replace_base64_image_url.call(val) }
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
replace_base64_image_url.call(payload)
|
|
53
|
+
|
|
54
|
+
payload
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def upload(trace_id, upload_queue: Queue.new)
|
|
58
|
+
until upload_queue.empty?
|
|
59
|
+
media = upload_queue.pop
|
|
60
|
+
|
|
61
|
+
content_length = media.byte_string.bytesize
|
|
62
|
+
content_sha256 = Base64.strict_encode64(Digest::SHA256.digest(media.byte_string))
|
|
63
|
+
|
|
64
|
+
# returns {"mediaId" -> "", "uploadUrl" => ""}
|
|
65
|
+
upload_url_response = get_upload_url({
|
|
66
|
+
traceId: trace_id,
|
|
67
|
+
contentType: media.mime_type,
|
|
68
|
+
contentLength: content_length,
|
|
69
|
+
sha256Hash: content_sha256,
|
|
70
|
+
field: media.direction,
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
# NOTE: uploadUrl is None if the file is stored in Langfuse already
|
|
74
|
+
# there is no need to upload it again.
|
|
75
|
+
upload_response = upload_media(
|
|
76
|
+
upload_url_response["mediaId"],
|
|
77
|
+
upload_url_response["uploadUrl"],
|
|
78
|
+
media.mime_type,
|
|
79
|
+
content_sha256,
|
|
80
|
+
media.byte_string
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
associate_media(upload_url_response["mediaId"], upload_response) if upload_response.present?
|
|
84
|
+
|
|
85
|
+
media.reference_id = upload_url_response["mediaId"]
|
|
86
|
+
@uploaded_media.append(media)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
@uploaded_media
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
|
|
94
|
+
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
|
+
)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def associate_media(media_id, upload_response)
|
|
106
|
+
request_body = {
|
|
107
|
+
uploadedAt: Time.now.utc.iso8601(6),
|
|
108
|
+
uploadHttpStatus: upload_response.code,
|
|
109
|
+
uploadHttpError: upload_response.code == 200 ? nil : upload_response.body,
|
|
110
|
+
}
|
|
111
|
+
|
|
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
|
+
)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def upload_media(media_id, upload_url, content_type, content_sha256, content_bytes)
|
|
123
|
+
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
|
+
)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
nil
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require "base64"
|
|
2
|
+
require "httparty"
|
|
3
|
+
|
|
4
|
+
module NitroIntelligence
|
|
5
|
+
class Reporter
|
|
6
|
+
def initialize(observability_project_slug:)
|
|
7
|
+
@observability_project_slug = observability_project_slug
|
|
8
|
+
@project_client = fetch_project_client
|
|
9
|
+
@host = NitroIntelligence.config.observability_base_url
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
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
|
+
})
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def score(trace_id:, name:, value:, id: "#{trace_id}-#{name}")
|
|
22
|
+
@project_client.observability_client.create_score(
|
|
23
|
+
id:,
|
|
24
|
+
trace_id:,
|
|
25
|
+
name:,
|
|
26
|
+
value:,
|
|
27
|
+
environment: NitroIntelligence.environment
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def fetch_project_client
|
|
34
|
+
project_client = NitroIntelligence.project_client_registry.fetch(@observability_project_slug)
|
|
35
|
+
if project_client.nil?
|
|
36
|
+
raise NitroIntelligence::Observability::ProjectClient::NotFoundError,
|
|
37
|
+
"No project session found for slug: #{@observability_project_slug}"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
project_client
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
require "active_support/core_ext/hash/indifferent_access"
|
|
2
|
+
|
|
3
|
+
module NitroIntelligence
|
|
4
|
+
class ToolCallReviewValidator
|
|
5
|
+
def validate!(thread_state:, tool_calls:, pending_tool_calls:)
|
|
6
|
+
tool_calls = normalize_tool_calls(tool_calls)
|
|
7
|
+
pending_tool_calls_by_id = Array(pending_tool_calls).index_by { |tool_call| tool_call["id"] }
|
|
8
|
+
review_actions = Array(thread_state.dig("interrupts", 0, "value", "review_actions"))
|
|
9
|
+
|
|
10
|
+
tool_calls.each do |tool_call_id, review|
|
|
11
|
+
pending_tool_call = pending_tool_calls_by_id[tool_call_id]&.with_indifferent_access
|
|
12
|
+
raise_error!("Unknown tool call ids: #{tool_call_id}") unless pending_tool_call
|
|
13
|
+
|
|
14
|
+
review = normalize_review(tool_call_id, review)
|
|
15
|
+
review_action = review[:action].to_s
|
|
16
|
+
|
|
17
|
+
unless review_actions.include?(review_action)
|
|
18
|
+
raise_error!("Invalid review action `#{review_action}` for tool call #{tool_call_id}")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
validate_edited_args!(tool_call_id:, review:, pending_tool_call:) if review_action == "edit"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
validate_completeness!(
|
|
25
|
+
submitted_tool_call_ids: tool_calls.keys,
|
|
26
|
+
pending_tool_calls:
|
|
27
|
+
)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def normalize_tool_calls(tool_calls)
|
|
33
|
+
raise_error!("tool_calls must be a hash") unless tool_calls.is_a?(Hash)
|
|
34
|
+
|
|
35
|
+
tool_calls.with_indifferent_access
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def normalize_review(tool_call_id, review)
|
|
39
|
+
raise_error!("Review for tool call #{tool_call_id} must be a hash") unless review.is_a?(Hash)
|
|
40
|
+
|
|
41
|
+
review.with_indifferent_access
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def validate_edited_args!(tool_call_id:, review:, pending_tool_call:)
|
|
45
|
+
provided_args = review[:args]
|
|
46
|
+
raise_error!("Edited args for tool call #{tool_call_id} must be a hash") unless provided_args.is_a?(Hash)
|
|
47
|
+
|
|
48
|
+
valid_arg_names = pending_tool_call.fetch(:args, {}).keys.map(&:to_s)
|
|
49
|
+
invalid_arg_names = provided_args.keys.map(&:to_s) - valid_arg_names
|
|
50
|
+
return if invalid_arg_names.empty?
|
|
51
|
+
|
|
52
|
+
raise_error!("Invalid edited args for tool call #{tool_call_id}: #{invalid_arg_names.join(', ')}")
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def validate_completeness!(submitted_tool_call_ids:, pending_tool_calls:)
|
|
56
|
+
missing_tool_call_ids = Array(pending_tool_calls).filter_map do |tool_call|
|
|
57
|
+
tool_call_id = tool_call["id"].to_s
|
|
58
|
+
tool_call_id unless submitted_tool_call_ids.include?(tool_call_id)
|
|
59
|
+
end
|
|
60
|
+
return if missing_tool_call_ids.empty?
|
|
61
|
+
|
|
62
|
+
raise_error!("Missing reviews for tool calls: #{missing_tool_call_ids.join(', ')}")
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def raise_error!(message)
|
|
66
|
+
raise NitroIntelligence::AgentServer::ThreadResumptionError, message
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
data/lib/nitro_intelligence.rb
CHANGED
|
@@ -1,31 +1,21 @@
|
|
|
1
1
|
require "active_support"
|
|
2
2
|
require "active_support/core_ext"
|
|
3
3
|
require "base64"
|
|
4
|
-
|
|
4
|
+
|
|
5
5
|
require "langfuse"
|
|
6
6
|
require "openai"
|
|
7
7
|
|
|
8
8
|
require "nitro_intelligence/version"
|
|
9
9
|
require "nitro_intelligence/agent_server"
|
|
10
|
-
require "nitro_intelligence/client"
|
|
10
|
+
require "nitro_intelligence/client/base"
|
|
11
|
+
require "nitro_intelligence/client/client"
|
|
11
12
|
require "nitro_intelligence/configuration"
|
|
12
|
-
require "nitro_intelligence/langfuse_extension"
|
|
13
|
-
require "nitro_intelligence/langfuse_tracer_provider"
|
|
14
13
|
require "nitro_intelligence/media/image_generation"
|
|
15
|
-
require "nitro_intelligence/media/upload_handler"
|
|
16
14
|
require "nitro_intelligence/models/model_catalog"
|
|
17
|
-
require "nitro_intelligence/
|
|
15
|
+
require "nitro_intelligence/observability/project_client_registry"
|
|
16
|
+
require "nitro_intelligence/reporter"
|
|
18
17
|
|
|
19
18
|
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
19
|
mattr_accessor :configuration, default: Configuration
|
|
30
20
|
|
|
31
21
|
class << self
|
|
@@ -40,37 +30,13 @@ module NitroIntelligence
|
|
|
40
30
|
end
|
|
41
31
|
|
|
42
32
|
def model_catalog
|
|
43
|
-
@model_catalog ||=
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def omit_params
|
|
47
|
-
(CUSTOM_PARAMS + ImageGeneration::Config::CUSTOM_PARAMS).uniq
|
|
33
|
+
@model_catalog ||= ModelCatalog.new(configuration.model_config)
|
|
48
34
|
end
|
|
49
35
|
|
|
50
|
-
def
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
|
36
|
+
def project_client_registry
|
|
37
|
+
@project_client_registry ||= Observability::ProjectClientRegistry.new(
|
|
38
|
+
base_url: configuration.observability_base_url
|
|
39
|
+
)
|
|
74
40
|
end
|
|
75
41
|
end
|
|
76
42
|
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: 0.0
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Igor Artemenko
|
|
@@ -43,14 +43,14 @@ dependencies:
|
|
|
43
43
|
requirements:
|
|
44
44
|
- - '='
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: 0.
|
|
46
|
+
version: 0.7.0
|
|
47
47
|
type: :runtime
|
|
48
48
|
prerelease: false
|
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
|
51
51
|
- - '='
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: 0.
|
|
53
|
+
version: 0.7.0
|
|
54
54
|
- !ruby/object:Gem::Dependency
|
|
55
55
|
name: mini_magick
|
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -71,14 +71,14 @@ dependencies:
|
|
|
71
71
|
requirements:
|
|
72
72
|
- - "~>"
|
|
73
73
|
- !ruby/object:Gem::Version
|
|
74
|
-
version: 0.
|
|
74
|
+
version: '0.58'
|
|
75
75
|
type: :runtime
|
|
76
76
|
prerelease: false
|
|
77
77
|
version_requirements: !ruby/object:Gem::Requirement
|
|
78
78
|
requirements:
|
|
79
79
|
- - "~>"
|
|
80
80
|
- !ruby/object:Gem::Version
|
|
81
|
-
version: 0.
|
|
81
|
+
version: '0.58'
|
|
82
82
|
- !ruby/object:Gem::Dependency
|
|
83
83
|
name: railties
|
|
84
84
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -236,20 +236,36 @@ files:
|
|
|
236
236
|
- docs/README.md
|
|
237
237
|
- lib/nitro_intelligence.rb
|
|
238
238
|
- lib/nitro_intelligence/agent_server.rb
|
|
239
|
-
- lib/nitro_intelligence/client.rb
|
|
239
|
+
- lib/nitro_intelligence/client/base.rb
|
|
240
|
+
- lib/nitro_intelligence/client/client.rb
|
|
241
|
+
- lib/nitro_intelligence/client/factory.rb
|
|
242
|
+
- lib/nitro_intelligence/client/handlers/audio_transcription_handler.rb
|
|
243
|
+
- lib/nitro_intelligence/client/handlers/chat_handler.rb
|
|
244
|
+
- lib/nitro_intelligence/client/handlers/image_handler.rb
|
|
245
|
+
- lib/nitro_intelligence/client/handlers/observed/audio_transcription_handler.rb
|
|
246
|
+
- lib/nitro_intelligence/client/handlers/observed/chat_handler.rb
|
|
247
|
+
- lib/nitro_intelligence/client/handlers/observed/image_handler.rb
|
|
248
|
+
- lib/nitro_intelligence/client/observed.rb
|
|
249
|
+
- lib/nitro_intelligence/client/observers/langfuse_observer.rb
|
|
240
250
|
- lib/nitro_intelligence/configuration.rb
|
|
241
251
|
- lib/nitro_intelligence/langfuse_extension.rb
|
|
242
252
|
- lib/nitro_intelligence/langfuse_tracer_provider.rb
|
|
253
|
+
- lib/nitro_intelligence/media/audio.rb
|
|
243
254
|
- lib/nitro_intelligence/media/image.rb
|
|
244
255
|
- lib/nitro_intelligence/media/image_generation.rb
|
|
245
256
|
- lib/nitro_intelligence/media/media.rb
|
|
246
|
-
- lib/nitro_intelligence/media/upload_handler.rb
|
|
247
257
|
- lib/nitro_intelligence/models/model.rb
|
|
248
258
|
- lib/nitro_intelligence/models/model_catalog.rb
|
|
249
259
|
- lib/nitro_intelligence/models/model_factory.rb
|
|
250
260
|
- lib/nitro_intelligence/null_cache.rb
|
|
251
|
-
- lib/nitro_intelligence/
|
|
252
|
-
- lib/nitro_intelligence/
|
|
261
|
+
- lib/nitro_intelligence/observability/project.rb
|
|
262
|
+
- lib/nitro_intelligence/observability/project_client.rb
|
|
263
|
+
- lib/nitro_intelligence/observability/project_client_registry.rb
|
|
264
|
+
- lib/nitro_intelligence/observability/prompt.rb
|
|
265
|
+
- lib/nitro_intelligence/observability/prompt_store.rb
|
|
266
|
+
- lib/nitro_intelligence/observability/upload_handler.rb
|
|
267
|
+
- lib/nitro_intelligence/reporter.rb
|
|
268
|
+
- lib/nitro_intelligence/tool_call_review_validator.rb
|
|
253
269
|
- lib/nitro_intelligence/trace.rb
|
|
254
270
|
- lib/nitro_intelligence/version.rb
|
|
255
271
|
homepage: https://github.com/powerhome/nitro-intelligence.rb
|
|
@@ -264,7 +280,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
264
280
|
requirements:
|
|
265
281
|
- - ">="
|
|
266
282
|
- !ruby/object:Gem::Version
|
|
267
|
-
version: '3.
|
|
283
|
+
version: '3.3'
|
|
268
284
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
269
285
|
requirements:
|
|
270
286
|
- - ">="
|