apidepth 0.4.0 → 0.5.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/README.md +40 -0
- data/lib/apidepth/configuration.rb +5 -3
- data/lib/apidepth/model_name_extractor.rb +51 -0
- data/lib/apidepth/net_http_instrumentation.rb +15 -13
- data/lib/apidepth/version.rb +1 -1
- data/lib/apidepth.rb +10 -8
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d59e0f9e95678afd4a0f9ce173de1b63ac893ab01cbc68fc1d6bbcedc44bfaad
|
|
4
|
+
data.tar.gz: 29524e4a1687cafb241caa6c07dbb2c1139ef2e44bc92f8ded25dc15c1305a92
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a4a88ac5df80e6af2987cfa29d354c67c5275c02e30caacbae086558d2a0ee1fe1304a42c11b679b2ec6a1358a1cd0e69c3969a5d9d400c246723592df448599
|
|
7
|
+
data.tar.gz: 2005a0c7164ed0b20da0e1013cdc250be7d09f66fdfdd65f465168f38f4a105bfa6370e3a98bef015502f7cb2ba961a99189a6232e0098eb47ca11c588d90ed4
|
data/README.md
CHANGED
|
@@ -56,6 +56,46 @@ Get your API key at [apidepth.io](https://apidepth.io).
|
|
|
56
56
|
|
|
57
57
|
---
|
|
58
58
|
|
|
59
|
+
## CLI
|
|
60
|
+
|
|
61
|
+
The gem ships two subcommands for setup and connectivity verification.
|
|
62
|
+
|
|
63
|
+
### `bundle exec apidepth setup`
|
|
64
|
+
|
|
65
|
+
Interactive wizard that detects your framework (Rails, Sinatra, or generic), generates the correct initializer snippet, and optionally writes it to disk.
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
bundle exec apidepth setup
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
For CI/CD pipelines, skip all prompts:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
bundle exec apidepth setup --api-key $APIDEPTH_API_KEY --no-prompt
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
| Flag | Description |
|
|
78
|
+
|---|---|
|
|
79
|
+
| `--api-key <key>` | Inject your API key into the generated snippet. |
|
|
80
|
+
| `--no-prompt` | Non-interactive mode — print snippet to stdout and exit. |
|
|
81
|
+
| `--framework <name>` | Override auto-detection (`rails`, `sinatra`, `generic`). |
|
|
82
|
+
| `--ignored-hosts <patterns>` | Comma-separated host patterns to add to `ignored_hosts` (glob wildcards supported). |
|
|
83
|
+
| `--collector-url <url>` | Override the collector URL in the generated snippet. |
|
|
84
|
+
|
|
85
|
+
### `bundle exec apidepth test`
|
|
86
|
+
|
|
87
|
+
Sends a synthetic test event to the collector and confirms the pipeline is working end-to-end. Reads `APIDEPTH_API_KEY` (and optionally `APIDEPTH_COLLECTOR_URL`) from the environment. Prints the round-trip time on success, or a per-failure-mode error message with next steps on failure.
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
bundle exec apidepth test
|
|
91
|
+
# ✓ received in 142ms
|
|
92
|
+
# Visit your dashboard: https://apidepth.io/dashboard
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Exits with code 1 on any error (bad key, unreachable, SSL failure, timeout).
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
59
99
|
## Configuration
|
|
60
100
|
|
|
61
101
|
All options with their defaults:
|
|
@@ -18,9 +18,10 @@ module Apidepth
|
|
|
18
18
|
:registry_refresh_interval,
|
|
19
19
|
:registry_cache_path,
|
|
20
20
|
:on_flush_error,
|
|
21
|
-
:environment,
|
|
22
|
-
:sample_rate,
|
|
23
|
-
:extra_vendors
|
|
21
|
+
:environment, # e.g. "production" — set by Railtie from Rails.env
|
|
22
|
+
:sample_rate, # Float 0.0–1.0, default 1.0 (100% of events captured)
|
|
23
|
+
:extra_vendors, # Hash of vendor_name => host, e.g. { "my-api" => "api.myservice.com" }
|
|
24
|
+
:capture_model_names # Boolean — read model field from AI vendor JSON responses
|
|
24
25
|
|
|
25
26
|
attr_reader :ignored_hosts, :collector_url
|
|
26
27
|
|
|
@@ -35,6 +36,7 @@ module Apidepth
|
|
|
35
36
|
@environment = nil # Railtie sets this to Rails.env at boot
|
|
36
37
|
@sample_rate = 1.0 # capture everything by default
|
|
37
38
|
@extra_vendors = {} # customer-defined host mappings
|
|
39
|
+
@capture_model_names = true # read model field from AI vendor JSON responses
|
|
38
40
|
_rebuild_ignored_hosts
|
|
39
41
|
end
|
|
40
42
|
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# lib/apidepth/model_name_extractor.rb
|
|
2
|
+
require "json"
|
|
3
|
+
require "set"
|
|
4
|
+
#
|
|
5
|
+
# Extracts the model name from AI vendor JSON response bodies.
|
|
6
|
+
#
|
|
7
|
+
# WHY response body rather than headers?
|
|
8
|
+
# AI vendors (OpenAI, Anthropic, Gemini, Mistral, Cohere) return the active
|
|
9
|
+
# model in the response body ({"model":"claude-3-opus-20240229",...}), not in
|
|
10
|
+
# headers. This is the only reliable source.
|
|
11
|
+
#
|
|
12
|
+
# WHY only for known AI vendor hosts?
|
|
13
|
+
# Body reads add a tiny overhead. Scoping to a hard-coded allowlist keeps the
|
|
14
|
+
# hot path for non-AI vendors completely unaffected.
|
|
15
|
+
#
|
|
16
|
+
# Body safety: Net::HTTP::HTTPResponse#body memoizes after the first read.
|
|
17
|
+
# Calling it here and returning the response to the application is safe — the
|
|
18
|
+
# application receives the same cached body bytes.
|
|
19
|
+
#
|
|
20
|
+
# Streaming safety: streamed responses have Content-Type: text/event-stream, not
|
|
21
|
+
# application/json. The content-type guard exits early before any body read.
|
|
22
|
+
# The 8KB truncation is a belt-and-suspenders guard against unusually large bodies.
|
|
23
|
+
|
|
24
|
+
module Apidepth
|
|
25
|
+
module ModelNameExtractor
|
|
26
|
+
AI_VENDOR_HOSTS = %w[
|
|
27
|
+
api.openai.com
|
|
28
|
+
api.anthropic.com
|
|
29
|
+
generativelanguage.googleapis.com
|
|
30
|
+
api.mistral.ai
|
|
31
|
+
api.cohere.com
|
|
32
|
+
].to_set.freeze
|
|
33
|
+
|
|
34
|
+
MAX_BODY_BYTES = 8_192
|
|
35
|
+
|
|
36
|
+
def self.extract(host, response)
|
|
37
|
+
return nil unless Apidepth.configuration.capture_model_names
|
|
38
|
+
return nil unless AI_VENDOR_HOSTS.include?(host)
|
|
39
|
+
return nil unless response["content-type"]&.include?("application/json")
|
|
40
|
+
|
|
41
|
+
body = response.body
|
|
42
|
+
return nil if body.nil? || body.empty?
|
|
43
|
+
|
|
44
|
+
parsed = JSON.parse(body.byteslice(0, MAX_BODY_BYTES), symbolize_names: true)
|
|
45
|
+
model = parsed[:model]
|
|
46
|
+
model.is_a?(String) && !model.empty? ? model : nil
|
|
47
|
+
rescue JSON::ParserError, Encoding::UndefinedConversionError, TypeError
|
|
48
|
+
nil
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -72,21 +72,23 @@ module Apidepth
|
|
|
72
72
|
|
|
73
73
|
now_ms = Process.clock_gettime(Process::CLOCK_REALTIME, :millisecond)
|
|
74
74
|
rl = Apidepth::RateLimitHeaders.extract(response, now_ms)
|
|
75
|
+
model_name = Apidepth::ModelNameExtractor.extract(address, response)
|
|
76
|
+
|
|
77
|
+
event_attrs = {
|
|
78
|
+
vendor: vendor,
|
|
79
|
+
endpoint: normalized_path,
|
|
80
|
+
method: req.method,
|
|
81
|
+
status: status,
|
|
82
|
+
outcome: outcome,
|
|
83
|
+
duration_ms: duration_ms,
|
|
84
|
+
cold_start: cold_start,
|
|
85
|
+
env: resolve_env,
|
|
86
|
+
ts: now_ms
|
|
87
|
+
}.merge(rl || {})
|
|
88
|
+
event_attrs[:model_name] = model_name if model_name
|
|
75
89
|
|
|
76
90
|
Apidepth::Collector.instance.record(
|
|
77
|
-
Apidepth::Event.build(
|
|
78
|
-
{
|
|
79
|
-
vendor: vendor,
|
|
80
|
-
endpoint: normalized_path,
|
|
81
|
-
method: req.method,
|
|
82
|
-
status: status,
|
|
83
|
-
outcome: outcome,
|
|
84
|
-
duration_ms: duration_ms,
|
|
85
|
-
cold_start: cold_start,
|
|
86
|
-
env: resolve_env,
|
|
87
|
-
ts: now_ms
|
|
88
|
-
}.merge(rl || {})
|
|
89
|
-
)
|
|
91
|
+
Apidepth::Event.build(event_attrs)
|
|
90
92
|
)
|
|
91
93
|
rescue StandardError => e
|
|
92
94
|
Apidepth.logger&.debug("[Apidepth] Instrumentation error: #{e.class}: #{e.message}")
|
data/lib/apidepth/version.rb
CHANGED
data/lib/apidepth.rb
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
# lib/apidepth.rb
|
|
2
2
|
#
|
|
3
3
|
# Main entry point. Require order matters:
|
|
4
|
-
# 1. version
|
|
5
|
-
# 2. configuration
|
|
6
|
-
# 3. vendor_registry
|
|
7
|
-
# 4. rate_limit_headers
|
|
8
|
-
# 5.
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
4
|
+
# 1. version — no dependencies
|
|
5
|
+
# 2. configuration — no dependencies
|
|
6
|
+
# 3. vendor_registry — no dependencies, boots from BUNDLED_BASELINE immediately
|
|
7
|
+
# 4. rate_limit_headers — no dependencies; used by net_http_instrumentation
|
|
8
|
+
# 5. model_name_extractor — no dependencies; used by net_http_instrumentation
|
|
9
|
+
# 6. net_http_instrumentation — depends on vendor_registry + collector (via lazy reference)
|
|
10
|
+
# 7. collector — depends on configuration
|
|
11
|
+
# 8. registry_loader — depends on collector + vendor_registry
|
|
12
|
+
# 9. railtie — depends on all of the above; only loaded in a Rails context
|
|
12
13
|
|
|
13
14
|
require "logger"
|
|
14
15
|
require "apidepth/version"
|
|
@@ -16,6 +17,7 @@ require "apidepth/configuration"
|
|
|
16
17
|
require "apidepth/event"
|
|
17
18
|
require "apidepth/vendor_registry"
|
|
18
19
|
require "apidepth/rate_limit_headers"
|
|
20
|
+
require "apidepth/model_name_extractor"
|
|
19
21
|
require "apidepth/net_http_instrumentation"
|
|
20
22
|
require "apidepth/collector"
|
|
21
23
|
require "apidepth/registry_loader"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: apidepth
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Apidepth
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-06-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: json
|
|
@@ -129,6 +129,7 @@ files:
|
|
|
129
129
|
- lib/apidepth/collector.rb
|
|
130
130
|
- lib/apidepth/configuration.rb
|
|
131
131
|
- lib/apidepth/event.rb
|
|
132
|
+
- lib/apidepth/model_name_extractor.rb
|
|
132
133
|
- lib/apidepth/net_http_instrumentation.rb
|
|
133
134
|
- lib/apidepth/railtie.rb
|
|
134
135
|
- lib/apidepth/rate_limit_headers.rb
|