coolhand 0.3.0 → 0.4.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/CHANGELOG.md +15 -0
- data/README.md +5 -1
- data/lib/coolhand/api_service.rb +13 -0
- data/lib/coolhand/configuration.rb +2 -7
- data/lib/coolhand/default_intercept_addresses.yml +4 -0
- data/lib/coolhand/version.rb +1 -1
- data/lib/coolhand.rb +9 -5
- metadata +2 -3
- data/coolhand-ruby.gemspec +0 -46
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3f66ea8b94e39769d66543c75987c2108397f89c93dc53748753b396ca76d921
|
|
4
|
+
data.tar.gz: 560b9ec89c71b31095b5f579bb270b295a79b87a7edbec495179f6f1b764f77e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ebd806bb3744d3fd16673a98f7746b144b468a158ec855b32a4ba142cf110ff4a78b03ea1b95106b8f29d011425bbe0ec44b8f853acb87b9e4037cf87843991f
|
|
7
|
+
data.tar.gz: 3a8078f5dbe7f3a5c7a0892b391a7085d54b5a6aa1db386075e34470c89802ca0bd6bb5dc5654dffa7fcb2595da3df7b2210a52568595c5c77929e435d1a13b8
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.4.0] - 2026-06-22
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **More default intercept addresses** — Vertex AI (`aiplatform.googleapis.com`), Cloudflare AI Gateway (`gateway.ai.cloudflare.com`), AWS Bedrock OpenAI-compatible endpoint (`bedrock-runtime`), and OpenRouter (`openrouter.ai`) are now monitored out of the box with no configuration required (#66).
|
|
14
|
+
- **`Configuration#enabled` flag** — Set `config.enabled = false` (e.g. `config.enabled = Rails.env.production?`) to skip all patching and validation globally without restructuring your configure block (#68).
|
|
15
|
+
- **Feedback `creator_type` field** — Pass `creator_type: 'human'`, `'agent'`, or `'unknown'` when submitting feedback to identify who originated the feedback; matches the Coolhand API field (#67).
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
- **Deferred `api_key` validation** — A missing `api_key` no longer raises at `Coolhand.configure` time. Intercepted requests are silently skipped (with a warning log) when the key is absent, so apps that boot without a key in CI or non-production environments no longer crash (#68).
|
|
19
|
+
|
|
20
|
+
### Dependencies
|
|
21
|
+
- Bumped `faraday` from 2.14.1 to 2.14.2 (#63).
|
|
22
|
+
|
|
8
23
|
## [0.3.0] - 2026-05-14
|
|
9
24
|
|
|
10
25
|
### 🚀 Major Changes
|
data/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Coolhand Ruby Monitor
|
|
2
2
|
|
|
3
|
+
[](https://badge.fury.io/rb/coolhand)
|
|
4
|
+
|
|
3
5
|
Monitor and log LLM API calls from multiple providers (OpenAI, Anthropic, Google AI, Cohere, and more) to the Coolhand analytics platform.
|
|
4
6
|
|
|
5
7
|
## Installation
|
|
@@ -107,6 +109,7 @@ feedback = feedback_service.create_feedback(
|
|
|
107
109
|
- **`like`** 👍 *Low Signal (Deprecated)* - Boolean: `true` = like, `false` = dislike. Use `sentiment` instead. Conversion: `true` → `"like"`, `false` → `"dislike"`.
|
|
108
110
|
- **`workload_hashid`** 🔗 *Workload Association* - Hashid of a workload to associate this feedback with.
|
|
109
111
|
- **`creator_unique_id`** 👤 *User Tracking* - Unique ID to match feedback to the end user who created it
|
|
112
|
+
- **`creator_type`** 🧑🤝🤖 *Creator Type* - What kind of creator submitted the feedback: `'human'`, `'agent'`, or `'unknown'`.
|
|
110
113
|
|
|
111
114
|
## Rails Integration
|
|
112
115
|
|
|
@@ -179,7 +182,8 @@ end
|
|
|
179
182
|
|
|
180
183
|
| Option | Type | Default | Description |
|
|
181
184
|
|--------|------|---------|-------------|
|
|
182
|
-
| `api_key` | String |
|
|
185
|
+
| `api_key` | String | `nil` | Your Coolhand API key. If absent, intercepted requests are skipped with a warning log rather than raising at boot time |
|
|
186
|
+
| `enabled` | Boolean | `true` | Set to `false` to disable all patching and validation (e.g. `Rails.env.production?`) |
|
|
183
187
|
| `silent` | Boolean | `false` | Whether to suppress console output |
|
|
184
188
|
| `intercept_addresses` | Array | `["api.openai.com", "api.anthropic.com"]` | Array of API endpoint strings to monitor |
|
|
185
189
|
|
data/lib/coolhand/api_service.rb
CHANGED
|
@@ -65,6 +65,8 @@ module Coolhand
|
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
def send_request(payload, success_message)
|
|
68
|
+
return nil if missing_api_key?
|
|
69
|
+
|
|
68
70
|
uri = URI.parse(@api_endpoint)
|
|
69
71
|
http = Net::HTTP.new(uri.host, uri.port)
|
|
70
72
|
http.use_ssl = (uri.scheme == "https")
|
|
@@ -117,6 +119,8 @@ module Coolhand
|
|
|
117
119
|
end
|
|
118
120
|
|
|
119
121
|
def create_feedback(feedback, collection_method = nil)
|
|
122
|
+
return nil if !debug_mode? && missing_api_key?
|
|
123
|
+
|
|
120
124
|
normalized = normalize_feedback_sentiment(feedback)
|
|
121
125
|
feedback_with_collector = add_collector_to_data(normalized, collection_method)
|
|
122
126
|
|
|
@@ -144,6 +148,8 @@ module Coolhand
|
|
|
144
148
|
end
|
|
145
149
|
|
|
146
150
|
def create_log(captured_data, collection_method = nil)
|
|
151
|
+
return nil if !debug_mode? && missing_api_key?
|
|
152
|
+
|
|
147
153
|
raw_request_with_collector = add_collector_to_data({ raw_request: captured_data }, collection_method)
|
|
148
154
|
|
|
149
155
|
payload = {
|
|
@@ -193,6 +199,13 @@ module Coolhand
|
|
|
193
199
|
|
|
194
200
|
private
|
|
195
201
|
|
|
202
|
+
def missing_api_key?
|
|
203
|
+
return false if Coolhand.required_field?(api_key)
|
|
204
|
+
|
|
205
|
+
Coolhand.log "⚠️ Coolhand: API key is missing — skipping log for this request."
|
|
206
|
+
true
|
|
207
|
+
end
|
|
208
|
+
|
|
196
209
|
# Get all filtered field names as a flat array
|
|
197
210
|
def filtered_field_names
|
|
198
211
|
@filtered_field_names ||= BINARY_DATA_FILTERS.values.flatten.map(&:downcase)
|
|
@@ -17,7 +17,7 @@ module Coolhand
|
|
|
17
17
|
BASE_URL_ERROR_MSG = "base_url must use https:// (or http://localhost / http://127.0.0.1 for local dev)"
|
|
18
18
|
LOOPBACK_HOSTS = %w[localhost 127.0.0.1 ::1].freeze
|
|
19
19
|
|
|
20
|
-
attr_accessor :api_key, :environment, :silent, :debug_mode, :capture, :exclude_api_patterns
|
|
20
|
+
attr_accessor :api_key, :environment, :silent, :debug_mode, :capture, :exclude_api_patterns, :enabled
|
|
21
21
|
attr_reader :intercept_addresses, :base_url
|
|
22
22
|
|
|
23
23
|
def initialize
|
|
@@ -30,6 +30,7 @@ module Coolhand
|
|
|
30
30
|
@debug_mode = false
|
|
31
31
|
@capture = true
|
|
32
32
|
@exclude_api_patterns = DEFAULT_EXCLUDE_API_PATTERNS.dup
|
|
33
|
+
@enabled = true
|
|
33
34
|
end
|
|
34
35
|
|
|
35
36
|
# Custom setter that preserves defaults when nil/empty array is provided
|
|
@@ -47,12 +48,6 @@ module Coolhand
|
|
|
47
48
|
end
|
|
48
49
|
|
|
49
50
|
def validate!
|
|
50
|
-
# Validate API Key after configuration
|
|
51
|
-
if api_key.nil?
|
|
52
|
-
Coolhand.log "❌ Coolhand Error: API Key is required. Please set it in the configuration."
|
|
53
|
-
raise Error, "API Key is required"
|
|
54
|
-
end
|
|
55
|
-
|
|
56
51
|
# Validate intercept_addresses after configuration
|
|
57
52
|
if intercept_addresses.nil? || intercept_addresses.empty?
|
|
58
53
|
Coolhand.log "❌ Coolhand Error: Intercept addresses cannot be empty. Please set it in the configuration."
|
data/lib/coolhand/version.rb
CHANGED
data/lib/coolhand.rb
CHANGED
|
@@ -45,6 +45,8 @@ module Coolhand
|
|
|
45
45
|
def configure
|
|
46
46
|
yield(configuration)
|
|
47
47
|
|
|
48
|
+
return unless configuration.enabled
|
|
49
|
+
|
|
48
50
|
configuration.validate!
|
|
49
51
|
|
|
50
52
|
NetHttpInterceptor.patch!
|
|
@@ -58,13 +60,15 @@ module Coolhand
|
|
|
58
60
|
return
|
|
59
61
|
end
|
|
60
62
|
|
|
61
|
-
|
|
63
|
+
return yield unless configuration.enabled
|
|
62
64
|
|
|
65
|
+
patched = NetHttpInterceptor.patched?
|
|
63
66
|
NetHttpInterceptor.patch!
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
begin
|
|
68
|
+
yield
|
|
69
|
+
ensure
|
|
70
|
+
NetHttpInterceptor.unpatch! unless patched
|
|
71
|
+
end
|
|
68
72
|
end
|
|
69
73
|
|
|
70
74
|
def without_capture
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: coolhand
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Michael Carroll
|
|
8
8
|
- Yaroslav Malyk
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-06-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: base64
|
|
@@ -43,7 +43,6 @@ files:
|
|
|
43
43
|
- LICENSE
|
|
44
44
|
- README.md
|
|
45
45
|
- Rakefile
|
|
46
|
-
- coolhand-ruby.gemspec
|
|
47
46
|
- docs/anthropic.md
|
|
48
47
|
- docs/elevenlabs.md
|
|
49
48
|
- lib/coolhand.rb
|
data/coolhand-ruby.gemspec
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "lib/coolhand/version"
|
|
4
|
-
|
|
5
|
-
Gem::Specification.new do |spec|
|
|
6
|
-
spec.name = "coolhand"
|
|
7
|
-
spec.version = Coolhand::VERSION
|
|
8
|
-
spec.authors = ["Michael Carroll", "Yaroslav Malyk"]
|
|
9
|
-
spec.email = ["mc@coolhandlabs.com"]
|
|
10
|
-
|
|
11
|
-
spec.summary = "Monitor and log LLM API calls from OpenAI, Anthropic, and other providers to Coolhand analytics."
|
|
12
|
-
spec.description = "Automatically intercept and log LLM requests from Ruby applications. Supports OpenAI, " \
|
|
13
|
-
"official Anthropic gem, ruby-anthropic gem, and other Faraday-based libraries. Features " \
|
|
14
|
-
"dual interceptor architecture, streaming support, thread-safe operation, and automatic " \
|
|
15
|
-
"duplicate request prevention."
|
|
16
|
-
spec.homepage = "https://coolhandlabs.com/"
|
|
17
|
-
spec.license = "Apache-2.0"
|
|
18
|
-
spec.required_ruby_version = ">= 3.0.0"
|
|
19
|
-
|
|
20
|
-
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
|
21
|
-
|
|
22
|
-
spec.metadata["homepage_uri"] = spec.homepage
|
|
23
|
-
spec.metadata["source_code_uri"] = "https://github.com/Coolhand-Labs/coolhand-ruby"
|
|
24
|
-
spec.metadata["changelog_uri"] = "https://github.com/Coolhand-Labs/coolhand-ruby/blob/main/CHANGELOG.md"
|
|
25
|
-
|
|
26
|
-
# Specify which files should be added to the gem when it is released.
|
|
27
|
-
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
28
|
-
spec.files = Dir.chdir(__dir__) do
|
|
29
|
-
`git ls-files -z`.split("\x0").reject do |f|
|
|
30
|
-
(File.expand_path(f) == __FILE__) ||
|
|
31
|
-
f.start_with?(*%w[bin/ test/ spec/ features/ .git appveyor Gemfile])
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
spec.bindir = "exe"
|
|
35
|
-
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
36
|
-
spec.require_paths = ["lib"]
|
|
37
|
-
|
|
38
|
-
# Uncomment to register a new dependency of your gem
|
|
39
|
-
# spec.add_dependency "example-gem", "~> 1.0"
|
|
40
|
-
|
|
41
|
-
spec.add_dependency "base64", "~> 0.2"
|
|
42
|
-
|
|
43
|
-
# For more information and examples about making a new gem, check out our
|
|
44
|
-
# guide at: https://bundler.io/guides/creating_gem.html
|
|
45
|
-
spec.metadata["rubygems_mfa_required"] = "true"
|
|
46
|
-
end
|