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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6a7e878a6cb441cee3e29de825ba5b0d5b01285475f42d5a4f66386f37379098
4
- data.tar.gz: fcc866bd1ca6e7348fbd3ff2daf75ba9203981c09acfaa6062873a08e11b5370
3
+ metadata.gz: 3f66ea8b94e39769d66543c75987c2108397f89c93dc53748753b396ca76d921
4
+ data.tar.gz: 560b9ec89c71b31095b5f579bb270b295a79b87a7edbec495179f6f1b764f77e
5
5
  SHA512:
6
- metadata.gz: 7b7f36ff49b826fb34691488c3839de760c7f01c58f811e1bb9108b896da71d958f2e1a714486513da0c1a264278078784ee4065be165b72b349939128e4a9e8
7
- data.tar.gz: c3bcb5e2a7d3bb07e8e66483bb5c64d3a27b4ad7b7cd677a7bc7aa74b8d612ad53b3b373f21244d3fdf14fb32a34ccee1f80e227b740d3634b7fb6074b7ee64a
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
+ [![Gem Version](https://badge.fury.io/rb/coolhand.svg)](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 | *required* | Your Coolhand API key for authentication |
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
 
@@ -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."
@@ -13,3 +13,7 @@
13
13
  - "models.inference.ai.azure.com"
14
14
  - ":generateContent"
15
15
  - ":streamGenerateContent"
16
+ - "aiplatform.googleapis.com"
17
+ - "gateway.ai.cloudflare.com"
18
+ - "bedrock-runtime"
19
+ - "openrouter.ai"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Coolhand
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.0"
5
5
  end
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
- patched = NetHttpInterceptor.patched?
63
+ return yield unless configuration.enabled
62
64
 
65
+ patched = NetHttpInterceptor.patched?
63
66
  NetHttpInterceptor.patch!
64
-
65
- yield
66
- ensure
67
- NetHttpInterceptor.unpatch! unless patched
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.3.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-05-16 00:00:00.000000000 Z
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
@@ -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