agentbill-sdk 5.0.1 → 7.17.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/.github/workflows/ci.yml +5 -4
- data/CHANGELOG.md +29 -0
- data/examples/ollama_basic.rb +81 -0
- data/examples/perplexity_basic.rb +66 -0
- data/lib/agentbill/agents.rb +226 -0
- data/lib/agentbill/customers.rb +164 -0
- data/lib/agentbill/distributed.rb +109 -0
- data/lib/agentbill/exceptions.rb +84 -0
- data/lib/agentbill/ollama_wrapper.rb +153 -0
- data/lib/agentbill/orders.rb +283 -0
- data/lib/agentbill/perplexity_wrapper.rb +101 -0
- data/lib/agentbill/pricing.rb +52 -0
- data/lib/agentbill/signal_types.rb +179 -0
- data/lib/agentbill/signals.rb +199 -0
- data/lib/agentbill/tracer.rb +68 -11
- data/lib/agentbill/tracing.rb +343 -0
- data/lib/agentbill/version.rb +1 -1
- data/lib/agentbill/wrappers.rb +384 -0
- data/lib/agentbill.rb +252 -45
- metadata +16 -2
data/lib/agentbill.rb
CHANGED
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
require 'net/http'
|
|
2
2
|
require 'json'
|
|
3
3
|
require 'securerandom'
|
|
4
|
+
require 'digest'
|
|
4
5
|
require_relative 'agentbill/version'
|
|
5
6
|
require_relative 'agentbill/tracer'
|
|
7
|
+
require_relative 'agentbill/pricing'
|
|
8
|
+
require_relative 'agentbill/customers'
|
|
9
|
+
require_relative 'agentbill/agents'
|
|
10
|
+
require_relative 'agentbill/orders'
|
|
6
11
|
|
|
7
12
|
module AgentBill
|
|
8
13
|
class Client
|
|
9
|
-
attr_reader :config, :tracer
|
|
14
|
+
attr_reader :config, :tracer, :customers, :agents, :orders
|
|
10
15
|
|
|
11
16
|
def initialize(config)
|
|
12
17
|
@config = config
|
|
13
18
|
@tracer = Tracer.new(config)
|
|
19
|
+
@customers = CustomersResource.new(config)
|
|
20
|
+
@agents = AgentsResource.new(config)
|
|
21
|
+
@orders = OrdersResource.new(config)
|
|
14
22
|
end
|
|
15
23
|
|
|
16
24
|
def self.init(config)
|
|
@@ -24,33 +32,23 @@ module AgentBill
|
|
|
24
32
|
[1, text.to_s.length / 4].max
|
|
25
33
|
end
|
|
26
34
|
|
|
27
|
-
def estimate_cost(model, input_tokens, output_tokens)
|
|
28
|
-
|
|
29
|
-
pricing = {
|
|
30
|
-
'gpt-4' => { input: 0.03, output: 0.06 },
|
|
31
|
-
'gpt-4o' => { input: 0.005, output: 0.015 },
|
|
32
|
-
'gpt-4o-mini' => { input: 0.00015, output: 0.0006 },
|
|
33
|
-
'claude-sonnet-4-5' => { input: 0.003, output: 0.015 },
|
|
34
|
-
'claude-opus-4-1-20250805' => { input: 0.015, output: 0.075 },
|
|
35
|
-
'claude-3-5-sonnet-20241022' => { input: 0.003, output: 0.015 },
|
|
36
|
-
'mistral-large-latest' => { input: 0.004, output: 0.012 },
|
|
37
|
-
'gemini-pro' => { input: 0.00025, output: 0.0005 }
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
model_price = pricing[model] || { input: 0.001, output: 0.002 }
|
|
41
|
-
(input_tokens / 1000.0 * model_price[:input]) + (output_tokens / 1000.0 * model_price[:output])
|
|
35
|
+
def estimate_cost(model, input_tokens, output_tokens, provider = 'openai')
|
|
36
|
+
Pricing.calculate_cost(model, input_tokens, output_tokens, provider)
|
|
42
37
|
end
|
|
43
38
|
|
|
44
39
|
def validate_request(model, messages, estimated_output_tokens = 1000)
|
|
45
|
-
|
|
40
|
+
# Always validate when customer_id is present - backend will check DB policies
|
|
41
|
+
return { 'allowed' => true } unless @config[:customer_id]
|
|
46
42
|
|
|
47
|
-
uri = URI("#{@config[:base_url] || 'https://
|
|
43
|
+
uri = URI("#{@config[:base_url] || 'https://api.agentbill.io'}/functions/v1/ai-cost-guard-router")
|
|
48
44
|
|
|
49
45
|
payload = {
|
|
50
46
|
api_key: @config[:api_key],
|
|
51
47
|
customer_id: @config[:customer_id],
|
|
52
48
|
model: model,
|
|
53
|
-
messages: messages
|
|
49
|
+
messages: messages,
|
|
50
|
+
daily_budget_override: @config[:daily_budget],
|
|
51
|
+
monthly_budget_override: @config[:monthly_budget]
|
|
54
52
|
}
|
|
55
53
|
|
|
56
54
|
begin
|
|
@@ -74,20 +72,42 @@ module AgentBill
|
|
|
74
72
|
end
|
|
75
73
|
end
|
|
76
74
|
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
# NOTE: track_usage is deprecated in v7.0.2 - signals now go through OTEL collector
|
|
76
|
+
def track_usage(model, provider, input_tokens, output_tokens, latency_ms, cost, event_name = 'ai_request', trace_id = nil, span_id = nil)
|
|
77
|
+
# v7.0.2: Route signals through otel-collector with proper span format
|
|
78
|
+
uri = URI("#{@config[:base_url] || 'https://api.agentbill.io'}/functions/v1/otel-collector")
|
|
79
|
+
|
|
80
|
+
trace_id ||= SecureRandom.hex(16)
|
|
81
|
+
span_id ||= SecureRandom.hex(8)
|
|
82
|
+
now_ns = (Time.now.to_f * 1_000_000_000).to_i.to_s
|
|
79
83
|
|
|
80
84
|
payload = {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
85
|
+
resourceSpans: [{
|
|
86
|
+
resource: {
|
|
87
|
+
attributes: [
|
|
88
|
+
{ key: 'service.name', value: { stringValue: 'agentbill-ruby-sdk' } },
|
|
89
|
+
{ key: 'agentbill.customer_id', value: { stringValue: @config[:customer_id] || '' } }
|
|
90
|
+
]
|
|
91
|
+
},
|
|
92
|
+
scopeSpans: [{
|
|
93
|
+
scope: { name: 'agentbill.signals', version: '7.16.1' },
|
|
94
|
+
spans: [{
|
|
95
|
+
traceId: trace_id,
|
|
96
|
+
spanId: span_id,
|
|
97
|
+
name: 'agentbill.trace.signal',
|
|
98
|
+
kind: 1,
|
|
99
|
+
startTimeUnixNano: now_ns,
|
|
100
|
+
endTimeUnixNano: now_ns,
|
|
101
|
+
attributes: [
|
|
102
|
+
{ key: 'agentbill.event_name', value: { stringValue: event_name } },
|
|
103
|
+
{ key: 'agentbill.is_business_event', value: { boolValue: true } },
|
|
104
|
+
{ key: 'gen_ai.request.model', value: { stringValue: model } },
|
|
105
|
+
{ key: 'gen_ai.system', value: { stringValue: provider } }
|
|
106
|
+
],
|
|
107
|
+
status: { code: 1 }
|
|
108
|
+
}]
|
|
109
|
+
}]
|
|
110
|
+
}]
|
|
91
111
|
}
|
|
92
112
|
|
|
93
113
|
begin
|
|
@@ -97,12 +117,13 @@ module AgentBill
|
|
|
97
117
|
|
|
98
118
|
request = Net::HTTP::Post.new(uri.path)
|
|
99
119
|
request['Content-Type'] = 'application/json'
|
|
120
|
+
request['x-api-key'] = @config[:api_key]
|
|
100
121
|
request.body = payload.to_json
|
|
101
122
|
|
|
102
123
|
http.request(request)
|
|
103
|
-
puts "[AgentBill
|
|
124
|
+
puts "[AgentBill] Usage tracked via OTEL: $#{format('%.4f', cost)}" if @config[:debug]
|
|
104
125
|
rescue => e
|
|
105
|
-
puts "[AgentBill
|
|
126
|
+
puts "[AgentBill] Tracking failed: #{e.message}" if @config[:debug]
|
|
106
127
|
end
|
|
107
128
|
end
|
|
108
129
|
|
|
@@ -132,9 +153,59 @@ module AgentBill
|
|
|
132
153
|
raise error
|
|
133
154
|
end
|
|
134
155
|
|
|
156
|
+
# v7.6.10: Check for cached response from semantic cache
|
|
157
|
+
# CRITICAL FIX: Router sends cost_saved/tokens_saved at top level
|
|
158
|
+
if validation['cached'] && validation['response_data']
|
|
159
|
+
puts "[AgentBill] ✓ Cache hit - returning cached response" if config[:debug]
|
|
160
|
+
|
|
161
|
+
# v7.6.10 FIX: Check both nested 'cache'/'cache_info' AND top-level fields
|
|
162
|
+
cache_info = validation['cache'] || validation['cache_info'] || {}
|
|
163
|
+
cached_response = validation['response_data']
|
|
164
|
+
usage = cached_response['usage'] || {}
|
|
165
|
+
|
|
166
|
+
# v7.6.10 FIX: Extract from cache_info OR top-level validation
|
|
167
|
+
tokens_saved = cache_info['tokens_saved'] || validation['tokens_saved'] || usage['total_tokens'] || 0
|
|
168
|
+
cost_saved = cache_info['cost_saved'] || validation['cost_saved'] || 0
|
|
169
|
+
cached_input_tokens = usage['prompt_tokens'] || (tokens_saved * 0.2).to_i
|
|
170
|
+
cached_output_tokens = usage['completion_tokens'] || (tokens_saved - cached_input_tokens)
|
|
171
|
+
cached_total_tokens = tokens_saved > 0 ? tokens_saved : (cached_input_tokens + cached_output_tokens)
|
|
172
|
+
|
|
173
|
+
# v7.6.10 CRITICAL FIX: Set gen_ai.usage.* to 0 for cache hits
|
|
174
|
+
# This prevents OTEL ingestion from calculating cost for cached responses
|
|
175
|
+
span = tracer.start_span('openai.chat.completion', {
|
|
176
|
+
'gen_ai.system' => 'openai',
|
|
177
|
+
'gen_ai.request.model' => model,
|
|
178
|
+
'gen_ai.operation.name' => 'chat',
|
|
179
|
+
'model' => model,
|
|
180
|
+
'provider' => 'openai',
|
|
181
|
+
'agentbill.from_cache' => true,
|
|
182
|
+
'agentbill.cache_hit' => true,
|
|
183
|
+
# OTEL cost calculation attributes - set to 0 for cache hits
|
|
184
|
+
'gen_ai.usage.input_tokens' => 0,
|
|
185
|
+
'gen_ai.usage.output_tokens' => 0,
|
|
186
|
+
'gen_ai.usage.total_tokens' => 0,
|
|
187
|
+
# Informational attributes - actual cached token counts
|
|
188
|
+
'agentbill.cached_input_tokens' => cached_input_tokens,
|
|
189
|
+
'agentbill.cached_output_tokens' => cached_output_tokens,
|
|
190
|
+
'agentbill.cached_total_tokens' => cached_total_tokens,
|
|
191
|
+
'agentbill.tokens_saved' => tokens_saved,
|
|
192
|
+
'agentbill.cost_saved' => cost_saved
|
|
193
|
+
})
|
|
194
|
+
span.set_status(0)
|
|
195
|
+
span.finish
|
|
196
|
+
tracer.flush
|
|
197
|
+
|
|
198
|
+
return cached_response
|
|
199
|
+
end
|
|
200
|
+
|
|
135
201
|
# Phase 2: Execute AI call
|
|
136
202
|
start_time = Time.now
|
|
137
203
|
span = tracer.start_span('openai.chat.completion', {
|
|
204
|
+
# ✅ OTEL GenAI compliant attributes
|
|
205
|
+
'gen_ai.system' => 'openai',
|
|
206
|
+
'gen_ai.request.model' => model,
|
|
207
|
+
'gen_ai.operation.name' => 'chat',
|
|
208
|
+
# ⚠️ Backward compatibility
|
|
138
209
|
'model' => model,
|
|
139
210
|
'provider' => 'openai'
|
|
140
211
|
})
|
|
@@ -143,14 +214,19 @@ module AgentBill
|
|
|
143
214
|
response = original_method.call(params)
|
|
144
215
|
latency = ((Time.now - start_time) * 1000).round
|
|
145
216
|
|
|
146
|
-
#
|
|
217
|
+
# NOTE: wrap() only creates OTEL spans, NOT signals
|
|
218
|
+
# Signals should be created explicitly via track_signal()
|
|
147
219
|
input_tokens = response.dig(:usage, :prompt_tokens) || 0
|
|
148
220
|
output_tokens = response.dig(:usage, :completion_tokens) || 0
|
|
149
221
|
cost = config[:_client].send(:estimate_cost, model, input_tokens, output_tokens)
|
|
150
222
|
|
|
151
|
-
config[:_client].send(:track_usage, model, 'openai', input_tokens, output_tokens, latency, cost, event_name)
|
|
152
|
-
|
|
153
223
|
span.set_attributes({
|
|
224
|
+
# ✅ OTEL GenAI compliant attributes
|
|
225
|
+
'gen_ai.usage.input_tokens' => input_tokens,
|
|
226
|
+
'gen_ai.usage.output_tokens' => output_tokens,
|
|
227
|
+
'gen_ai.usage.total_tokens' => response.dig(:usage, :total_tokens),
|
|
228
|
+
'gen_ai.response.id' => response.dig(:id),
|
|
229
|
+
# ⚠️ Backward compatibility
|
|
154
230
|
'response.prompt_tokens' => input_tokens,
|
|
155
231
|
'response.completion_tokens' => output_tokens,
|
|
156
232
|
'response.total_tokens' => response.dig(:usage, :total_tokens),
|
|
@@ -158,16 +234,24 @@ module AgentBill
|
|
|
158
234
|
})
|
|
159
235
|
span.set_status(0)
|
|
160
236
|
|
|
161
|
-
puts "[AgentBill
|
|
237
|
+
puts "[AgentBill] ✓ OTEL span created: $#{format('%.4f', cost)} (use track_signal() for signals)" if config[:debug]
|
|
238
|
+
|
|
239
|
+
# v7.5.0: Cache AI response for semantic caching
|
|
240
|
+
response_content = response.dig(:choices, 0, :message, :content) || ''
|
|
241
|
+
prompt_text = messages.map { |m| m[:content] }.join(' ')
|
|
242
|
+
prompt_hash = Digest::SHA256.hexdigest(prompt_text)[0..15]
|
|
243
|
+
config[:_client].send(:cache_response, model, prompt_hash, response_content, input_tokens + output_tokens, cost)
|
|
244
|
+
|
|
162
245
|
response
|
|
163
246
|
rescue => e
|
|
164
247
|
span.set_status(1, e.message)
|
|
165
248
|
raise
|
|
166
249
|
ensure
|
|
167
250
|
span.finish
|
|
251
|
+
# Auto-flush spans to prevent data loss
|
|
252
|
+
tracer.flush
|
|
168
253
|
end
|
|
169
254
|
end
|
|
170
|
-
|
|
171
255
|
# Store reference to self for helper methods
|
|
172
256
|
@config[:_client] = self
|
|
173
257
|
client
|
|
@@ -196,6 +280,11 @@ module AgentBill
|
|
|
196
280
|
# Phase 2: Execute AI call
|
|
197
281
|
start_time = Time.now
|
|
198
282
|
span = tracer.start_span('anthropic.message', {
|
|
283
|
+
# ✅ OTEL GenAI compliant attributes
|
|
284
|
+
'gen_ai.system' => 'anthropic',
|
|
285
|
+
'gen_ai.request.model' => model,
|
|
286
|
+
'gen_ai.operation.name' => 'chat',
|
|
287
|
+
# ⚠️ Backward compatibility
|
|
199
288
|
'model' => model,
|
|
200
289
|
'provider' => 'anthropic'
|
|
201
290
|
})
|
|
@@ -204,30 +293,34 @@ module AgentBill
|
|
|
204
293
|
response = original_method.call(params)
|
|
205
294
|
latency = ((Time.now - start_time) * 1000).round
|
|
206
295
|
|
|
207
|
-
#
|
|
296
|
+
# NOTE: wrap() only creates OTEL spans, NOT signals
|
|
208
297
|
input_tokens = response.dig(:usage, :input_tokens) || 0
|
|
209
298
|
output_tokens = response.dig(:usage, :output_tokens) || 0
|
|
210
|
-
cost = config[:_client].send(:estimate_cost, model, input_tokens, output_tokens)
|
|
211
|
-
|
|
212
|
-
config[:_client].send(:track_usage, model, 'anthropic', input_tokens, output_tokens, latency, cost)
|
|
299
|
+
cost = config[:_client].send(:estimate_cost, model, input_tokens, output_tokens, 'anthropic')
|
|
213
300
|
|
|
214
301
|
span.set_attributes({
|
|
302
|
+
# ✅ OTEL GenAI compliant attributes
|
|
303
|
+
'gen_ai.usage.input_tokens' => input_tokens,
|
|
304
|
+
'gen_ai.usage.output_tokens' => output_tokens,
|
|
305
|
+
'gen_ai.response.id' => response.dig(:id),
|
|
306
|
+
# ⚠️ Backward compatibility
|
|
215
307
|
'response.input_tokens' => input_tokens,
|
|
216
308
|
'response.output_tokens' => output_tokens,
|
|
217
309
|
'latency_ms' => latency
|
|
218
310
|
})
|
|
219
311
|
span.set_status(0)
|
|
220
312
|
|
|
221
|
-
puts "[AgentBill
|
|
313
|
+
puts "[AgentBill] ✓ OTEL span created: $#{format('%.4f', cost)} (use track_signal() for signals)" if config[:debug]
|
|
222
314
|
response
|
|
223
315
|
rescue => e
|
|
224
316
|
span.set_status(1, e.message)
|
|
225
317
|
raise
|
|
226
318
|
ensure
|
|
227
319
|
span.finish
|
|
320
|
+
# Auto-flush spans
|
|
321
|
+
tracer.flush
|
|
228
322
|
end
|
|
229
323
|
end
|
|
230
|
-
|
|
231
324
|
# Store reference to self for helper methods
|
|
232
325
|
@config[:_client] = self
|
|
233
326
|
client
|
|
@@ -274,7 +367,7 @@ module AgentBill
|
|
|
274
367
|
def track_signal(**params)
|
|
275
368
|
raise ArgumentError, "event_name is required" unless params[:event_name]
|
|
276
369
|
|
|
277
|
-
uri = URI("#{@config[:base_url] || 'https://
|
|
370
|
+
uri = URI("#{@config[:base_url] || 'https://api.agentbill.io'}/functions/v1/record-signals")
|
|
278
371
|
|
|
279
372
|
# Add timestamp if not provided
|
|
280
373
|
params[:timestamp] ||= Time.now.to_f
|
|
@@ -319,8 +412,122 @@ module AgentBill
|
|
|
319
412
|
end
|
|
320
413
|
end
|
|
321
414
|
|
|
415
|
+
# Track a conversion event for prompt profitability analysis
|
|
416
|
+
# Links conversions to AI prompts to calculate ROI per prompt
|
|
417
|
+
#
|
|
418
|
+
# @param event_type [String] Type of conversion (e.g., 'purchase', 'signup', 'trial_start')
|
|
419
|
+
# @param event_value [Float] Revenue amount from the conversion
|
|
420
|
+
# @param signal_id [String, nil] Optional UUID linking to specific AI signal/prompt
|
|
421
|
+
# @param session_id [String, nil] Optional session identifier
|
|
422
|
+
# @param attribution_window_hours [Integer] Time window for attribution (default: 24 hours)
|
|
423
|
+
# @param currency [String] Currency code (default: 'USD')
|
|
424
|
+
# @param metadata [Hash, nil] Optional additional data
|
|
425
|
+
# @return [Hash] Response with success status and conversion_id
|
|
426
|
+
#
|
|
427
|
+
# Example:
|
|
428
|
+
# # Track a purchase conversion
|
|
429
|
+
# result = agentbill.track_conversion(
|
|
430
|
+
# event_type: 'purchase',
|
|
431
|
+
# event_value: 99.99,
|
|
432
|
+
# currency: 'USD'
|
|
433
|
+
# )
|
|
434
|
+
#
|
|
435
|
+
# # Link conversion to specific AI prompt
|
|
436
|
+
# result = agentbill.track_conversion(
|
|
437
|
+
# event_type: 'trial_signup',
|
|
438
|
+
# event_value: 29.99,
|
|
439
|
+
# signal_id: 'signal-uuid-from-prompt',
|
|
440
|
+
# session_id: 'session-123'
|
|
441
|
+
# )
|
|
442
|
+
def track_conversion(event_type:, event_value:, signal_id: nil, session_id: nil, attribution_window_hours: 24, currency: 'USD', metadata: nil)
|
|
443
|
+
uri = URI("#{@config[:base_url] || 'https://api.agentbill.io'}/functions/v1/track-conversion")
|
|
444
|
+
|
|
445
|
+
payload = {
|
|
446
|
+
api_key: @config[:api_key],
|
|
447
|
+
customer_id: @config[:customer_id],
|
|
448
|
+
event_type: event_type,
|
|
449
|
+
event_value: event_value,
|
|
450
|
+
signal_id: signal_id,
|
|
451
|
+
session_id: session_id,
|
|
452
|
+
attribution_window_hours: attribution_window_hours,
|
|
453
|
+
currency: currency,
|
|
454
|
+
metadata: metadata
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
begin
|
|
458
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
459
|
+
http.use_ssl = true
|
|
460
|
+
|
|
461
|
+
request = Net::HTTP::Post.new(uri.path)
|
|
462
|
+
request['Content-Type'] = 'application/json'
|
|
463
|
+
request.body = payload.to_json
|
|
464
|
+
|
|
465
|
+
response = http.request(request)
|
|
466
|
+
data = JSON.parse(response.body)
|
|
467
|
+
|
|
468
|
+
if response.code != '200'
|
|
469
|
+
return {
|
|
470
|
+
success: false,
|
|
471
|
+
error: data['error'] || "HTTP #{response.code}"
|
|
472
|
+
}
|
|
473
|
+
end
|
|
474
|
+
|
|
475
|
+
if @config[:debug]
|
|
476
|
+
puts "[AgentBill] Conversion tracked: #{event_type} = $#{event_value} (ID: #{data['conversion_id']})"
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
{
|
|
480
|
+
success: true,
|
|
481
|
+
conversion_id: data['conversion_id']
|
|
482
|
+
}
|
|
483
|
+
rescue => e
|
|
484
|
+
if @config[:debug]
|
|
485
|
+
puts "[AgentBill] Failed to track conversion: #{e.message}"
|
|
486
|
+
end
|
|
487
|
+
{
|
|
488
|
+
success: false,
|
|
489
|
+
error: e.message
|
|
490
|
+
}
|
|
491
|
+
end
|
|
492
|
+
end
|
|
493
|
+
|
|
322
494
|
def flush
|
|
323
495
|
@tracer.flush
|
|
324
496
|
end
|
|
497
|
+
|
|
498
|
+
private
|
|
499
|
+
|
|
500
|
+
# Cache AI response for semantic caching (v7.5.0)
|
|
501
|
+
def cache_response(model, prompt_hash, response_content, tokens_used = 0, cost = 0.0)
|
|
502
|
+
return if response_content.nil? || response_content.empty?
|
|
503
|
+
|
|
504
|
+
uri = URI("#{@config[:base_url] || 'https://api.agentbill.io'}/functions/v1/cache-ai-response")
|
|
505
|
+
|
|
506
|
+
payload = {
|
|
507
|
+
api_key: @config[:api_key],
|
|
508
|
+
prompt_hash: prompt_hash,
|
|
509
|
+
response_content: response_content,
|
|
510
|
+
model: model,
|
|
511
|
+
tokens_used: tokens_used,
|
|
512
|
+
cost: cost,
|
|
513
|
+
customer_id: @config[:customer_id],
|
|
514
|
+
agent_id: @config[:agent_id]
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
begin
|
|
518
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
519
|
+
http.use_ssl = true
|
|
520
|
+
http.read_timeout = 5
|
|
521
|
+
|
|
522
|
+
request = Net::HTTP::Post.new(uri.path)
|
|
523
|
+
request['Content-Type'] = 'application/json'
|
|
524
|
+
request.body = payload.to_json
|
|
525
|
+
|
|
526
|
+
http.request(request)
|
|
527
|
+
puts "[AgentBill] Response cached for semantic caching" if @config[:debug]
|
|
528
|
+
rescue => e
|
|
529
|
+
puts "[AgentBill] Cache response failed: #{e.message}" if @config[:debug]
|
|
530
|
+
end
|
|
531
|
+
end
|
|
325
532
|
end
|
|
326
533
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: agentbill-sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 7.17.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- AgentBill
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-01-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -65,12 +65,26 @@ files:
|
|
|
65
65
|
- agentbill.gemspec
|
|
66
66
|
- examples/anthropic_basic.rb
|
|
67
67
|
- examples/manual_otel_tracking.rb
|
|
68
|
+
- examples/ollama_basic.rb
|
|
68
69
|
- examples/openai_basic.rb
|
|
69
70
|
- examples/openai_custom_event.rb
|
|
71
|
+
- examples/perplexity_basic.rb
|
|
70
72
|
- examples/zero_config.rb
|
|
71
73
|
- lib/agentbill.rb
|
|
74
|
+
- lib/agentbill/agents.rb
|
|
75
|
+
- lib/agentbill/customers.rb
|
|
76
|
+
- lib/agentbill/distributed.rb
|
|
77
|
+
- lib/agentbill/exceptions.rb
|
|
78
|
+
- lib/agentbill/ollama_wrapper.rb
|
|
79
|
+
- lib/agentbill/orders.rb
|
|
80
|
+
- lib/agentbill/perplexity_wrapper.rb
|
|
81
|
+
- lib/agentbill/pricing.rb
|
|
82
|
+
- lib/agentbill/signal_types.rb
|
|
83
|
+
- lib/agentbill/signals.rb
|
|
72
84
|
- lib/agentbill/tracer.rb
|
|
85
|
+
- lib/agentbill/tracing.rb
|
|
73
86
|
- lib/agentbill/version.rb
|
|
87
|
+
- lib/agentbill/wrappers.rb
|
|
74
88
|
homepage: https://github.com/Agent-Bill/Ruby
|
|
75
89
|
licenses:
|
|
76
90
|
- MIT
|