boxcars 0.8.3 → 0.8.5

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: 93e000d3596a656454fb5c76becff8c5d5b4bbb33fe75f91598aceb0526c8d48
4
- data.tar.gz: 87d3db350a3faa94cb367fbd600749ed584f0868cd8298469b7bef6c9434e93e
3
+ metadata.gz: c1970a509d4c6eebaeaa06792993f7aaa1ba8d2d030e878138a8d6f821726fcc
4
+ data.tar.gz: 66ae9f13950ab9a0dd2369a1450fd14d4426faa95d8a62da7b4c7deeddbc6b3d
5
5
  SHA512:
6
- metadata.gz: d3028c6cbaec10e84597c3a68e2f8f6c88b336b4be50144b04349662afa647d9c266d00debc14c0370bda0bb2150472cf69e347def8fcdc5d3682fc6c2d19512
7
- data.tar.gz: 98a10191a8cd96cf85d91f9d5f1f38f07fa29fc7521f55bfd67c500a21fb23b3ef7d4ba7a16df1cc1d35f2a7486fe9ba18f11ab922e288895533ed2197766601
6
+ metadata.gz: 7e6d1f2f4788bb24de873a2e7270b52f44ce31017ccd39e1db321b91bd595d1f832c6c9aa4b9ca587626c3390928e70025014a360ba8052b9e870c50566ccb29
7
+ data.tar.gz: 4073745656e6944e8e686eee4502916038470ff32261990c1444e50d951a819288a24b993dc51659168b9246dab7020f40fd20f210b9421ab5890e6ec4402d66
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- boxcars (0.8.3)
4
+ boxcars (0.8.5)
5
5
  faraday-retry (~> 2.0)
6
6
  google_search_results (~> 2.2)
7
7
  gpt4all (~> 0.0.5)
@@ -47,7 +47,7 @@ GEM
47
47
  protocol-http1 (~> 0.19.0)
48
48
  protocol-http2 (~> 0.16.0)
49
49
  traces (>= 0.10.0)
50
- async-http-faraday (0.21.0)
50
+ async-http-faraday (0.22.0)
51
51
  async-http (~> 0.42)
52
52
  faraday
53
53
  async-io (1.43.2)
@@ -67,7 +67,7 @@ GEM
67
67
  bigdecimal
68
68
  rexml
69
69
  date (3.4.1)
70
- debug (1.10.0)
70
+ debug (1.11.0)
71
71
  irb (~> 1.10)
72
72
  reline (>= 0.3.8)
73
73
  diff-lcs (1.6.2)
@@ -83,11 +83,11 @@ GEM
83
83
  logger
84
84
  faraday-http-cache (2.5.1)
85
85
  faraday (>= 0.8)
86
- faraday-multipart (1.1.0)
86
+ faraday-multipart (1.1.1)
87
87
  multipart-post (~> 2.0)
88
- faraday-net_http (3.4.0)
88
+ faraday-net_http (3.4.1)
89
89
  net-http (>= 0.5.0)
90
- faraday-retry (2.3.1)
90
+ faraday-retry (2.3.2)
91
91
  faraday (~> 2.0)
92
92
  fiber-annotation (0.2.0)
93
93
  fiber-local (1.1.0)
@@ -114,8 +114,8 @@ GEM
114
114
  domain_name (~> 0.5)
115
115
  i18n (1.14.7)
116
116
  concurrent-ruby (~> 1.0)
117
- intelligence (0.8.0)
118
- dynamicschema (~> 1.0.0.beta03)
117
+ intelligence (1.0.0)
118
+ dynamicschema (~> 1.0)
119
119
  faraday (~> 2.7)
120
120
  json-repair (~> 0.2)
121
121
  mime-types (~> 3.6)
@@ -132,7 +132,7 @@ GEM
132
132
  mime-types (3.7.0)
133
133
  logger
134
134
  mime-types-data (~> 3.2025, >= 3.2025.0507)
135
- mime-types-data (3.2025.0603)
135
+ mime-types-data (3.2025.0624)
136
136
  minitest (5.25.5)
137
137
  multi_json (1.15.0)
138
138
  multipart-post (2.4.1)
@@ -186,7 +186,7 @@ GEM
186
186
  racc (1.8.1)
187
187
  rainbow (3.1.1)
188
188
  rake (13.3.0)
189
- rdoc (6.14.0)
189
+ rdoc (6.14.1)
190
190
  erb
191
191
  psych (>= 4.0.0)
192
192
  regexp_parser (2.10.0)
@@ -202,7 +202,7 @@ GEM
202
202
  rspec-core (~> 3.13.0)
203
203
  rspec-expectations (~> 3.13.0)
204
204
  rspec-mocks (~> 3.13.0)
205
- rspec-core (3.13.4)
205
+ rspec-core (3.13.5)
206
206
  rspec-support (~> 3.13.0)
207
207
  rspec-expectations (3.13.5)
208
208
  diff-lcs (>= 1.2.0, < 2.0)
@@ -211,7 +211,7 @@ GEM
211
211
  diff-lcs (>= 1.2.0, < 2.0)
212
212
  rspec-support (~> 3.13.0)
213
213
  rspec-support (3.13.4)
214
- rubocop (1.76.1)
214
+ rubocop (1.77.0)
215
215
  json (~> 2.3)
216
216
  language_server-protocol (~> 3.17.0.2)
217
217
  lint_roller (~> 1.1.0)
@@ -219,7 +219,7 @@ GEM
219
219
  parser (>= 3.3.0.2)
220
220
  rainbow (>= 2.2.2, < 4.0)
221
221
  regexp_parser (>= 2.9.3, < 3.0)
222
- rubocop-ast (>= 1.45.0, < 2.0)
222
+ rubocop-ast (>= 1.45.1, < 2.0)
223
223
  ruby-progressbar (~> 1.7)
224
224
  unicode-display_width (>= 2.4.0, < 4.0)
225
225
  rubocop-ast (1.45.1)
@@ -243,14 +243,14 @@ GEM
243
243
  addressable (>= 2.3.5)
244
244
  faraday (>= 0.17.3, < 3)
245
245
  securerandom (0.4.1)
246
- sqlite3 (2.6.0-aarch64-linux-gnu)
247
- sqlite3 (2.6.0-aarch64-linux-musl)
248
- sqlite3 (2.6.0-arm-linux-gnu)
249
- sqlite3 (2.6.0-arm-linux-musl)
250
- sqlite3 (2.6.0-arm64-darwin)
251
- sqlite3 (2.6.0-x86_64-darwin)
252
- sqlite3 (2.6.0-x86_64-linux-gnu)
253
- sqlite3 (2.6.0-x86_64-linux-musl)
246
+ sqlite3 (2.7.1-aarch64-linux-gnu)
247
+ sqlite3 (2.7.1-aarch64-linux-musl)
248
+ sqlite3 (2.7.1-arm-linux-gnu)
249
+ sqlite3 (2.7.1-arm-linux-musl)
250
+ sqlite3 (2.7.1-arm64-darwin)
251
+ sqlite3 (2.7.1-x86_64-darwin)
252
+ sqlite3 (2.7.1-x86_64-linux-gnu)
253
+ sqlite3 (2.7.1-x86_64-linux-musl)
254
254
  stringio (3.1.7)
255
255
  strings-ansi (0.2.0)
256
256
  timeout (0.4.3)
data/README.md CHANGED
@@ -388,7 +388,7 @@ Boxcars automatically tracks LLM calls with detailed metrics:
388
388
 
389
389
  ```ruby
390
390
  # This automatically generates observability events
391
- engine = Boxcars::Openai.new
391
+ engine = Boxcars::Openai.new(user_id: USER_ID) # optional user_id. All engines take this.
392
392
  calc = Boxcars::Calculator.new(engine: engine)
393
393
  result = calc.run "what is 2 + 2?"
394
394
  ```
@@ -404,6 +404,7 @@ result = calc.run "what is 2 + 2?"
404
404
  - `error_message`: Error details if the call failed
405
405
  - `response_raw_body`: Raw API response
406
406
  - `api_call_parameters`: Parameters sent to the API
407
+ - `distinct_id`: If you specify a user_id to your engine, it will be passed up.
407
408
 
408
409
  #### Manual Tracking
409
410
 
@@ -0,0 +1,435 @@
1
+ # User Context in Boxcars Observations
2
+
3
+ This guide explains how to use the new user context feature in Boxcars observations to track which user performed specific actions.
4
+
5
+ ## Overview
6
+
7
+ The user context feature allows you to associate user information with Boxcars observations, enabling better tracking, debugging, and analytics. This is particularly useful when using Boxcars in web applications where you want to tie AI operations to specific users.
8
+
9
+ ## Key Features
10
+
11
+ - **Non-intrusive**: Uses the existing `added_context` mechanism
12
+ - **Backward compatible**: Existing code continues to work unchanged
13
+ - **Flexible**: You control what user data is included
14
+ - **Analytics-ready**: Automatically formats user data for analytics systems like PostHog
15
+ - **Privacy-conscious**: Only includes data you explicitly provide
16
+
17
+ ## Enhanced Observation Class
18
+
19
+ ### New Methods
20
+
21
+ #### `Observation.with_user(note, user_context:, status: :ok, **additional_context)`
22
+
23
+ Creates an observation with user context.
24
+
25
+ ```ruby
26
+ user_context = {
27
+ id: current_user.id,
28
+ email: current_user.email,
29
+ role: current_user.role
30
+ }
31
+
32
+ observation = Boxcars::Observation.with_user(
33
+ "User performed search",
34
+ user_context: user_context,
35
+ query: "machine learning",
36
+ results_count: 42
37
+ )
38
+ ```
39
+
40
+ #### `Observation.ok_with_user(note, user_context:, **additional_context)`
41
+
42
+ Creates a successful observation with user context.
43
+
44
+ ```ruby
45
+ observation = Boxcars::Observation.ok_with_user(
46
+ "Search completed successfully",
47
+ user_context: user_context,
48
+ processing_time_ms: 150
49
+ )
50
+ ```
51
+
52
+ #### `Observation.err_with_user(note, user_context:, **additional_context)`
53
+
54
+ Creates an error observation with user context.
55
+
56
+ ```ruby
57
+ observation = Boxcars::Observation.err_with_user(
58
+ "Search failed due to timeout",
59
+ user_context: user_context,
60
+ error_code: "TIMEOUT"
61
+ )
62
+ ```
63
+
64
+ ### New Instance Methods
65
+
66
+ #### `#user_context`
67
+
68
+ Returns the user context hash if present, `nil` otherwise.
69
+
70
+ ```ruby
71
+ observation.user_context
72
+ # => {id: 123, email: "user@example.com", role: "admin"}
73
+ ```
74
+
75
+ #### `#user_context?`
76
+
77
+ Returns `true` if the observation has user context, `false` otherwise.
78
+
79
+ ```ruby
80
+ observation.user_context?
81
+ # => true
82
+ ```
83
+
84
+ ## Enhanced Observability System
85
+
86
+ ### Updated Track Method
87
+
88
+ The `Boxcars::Observability.track` method now accepts an optional `observation` parameter:
89
+
90
+ ```ruby
91
+ Boxcars::Observability.track(
92
+ event: 'user_action',
93
+ properties: { action_type: 'search' },
94
+ observation: observation_with_user_context
95
+ )
96
+ ```
97
+
98
+ When an observation with user context is provided, the user data is automatically merged into the tracking properties with `$user_` prefixes (PostHog compatible).
99
+
100
+ ### New Track Observation Method
101
+
102
+ ```ruby
103
+ Boxcars::Observability.track_observation(
104
+ observation,
105
+ event: 'custom_event_name', # optional, defaults to 'boxcar_observation'
106
+ additional_property: 'value'
107
+ )
108
+ ```
109
+
110
+ This method automatically:
111
+ - Extracts observation details (note, status, timestamp)
112
+ - Includes all additional context from the observation
113
+ - Merges user context with `$user_` prefixes
114
+ - Sends everything to your configured observability backend
115
+
116
+ ## Rails Integration Pattern
117
+
118
+ ### Controller Helper Methods
119
+
120
+ Create helper methods in your `ApplicationController`:
121
+
122
+ ```ruby
123
+ class ApplicationController < ActionController::Base
124
+ private
125
+
126
+ # Helper method to create observations with user context
127
+ def create_observation_with_user(note, status: :ok, **additional_context)
128
+ if current_user
129
+ Boxcars::Observation.with_user(
130
+ note,
131
+ user_context: current_user.to_user_context,
132
+ status: status,
133
+ **additional_context
134
+ )
135
+ else
136
+ Boxcars::Observation.new(note: note, status: status, **additional_context)
137
+ end
138
+ end
139
+
140
+ # Helper method for successful operations
141
+ def success_observation_with_user(note, **additional_context)
142
+ create_observation_with_user(note, status: :ok, **additional_context)
143
+ end
144
+
145
+ # Helper method for error operations
146
+ def error_observation_with_user(note, **additional_context)
147
+ create_observation_with_user(note, status: :error, **additional_context)
148
+ end
149
+ end
150
+ ```
151
+
152
+ ### User Model Extension
153
+
154
+ Add a method to your User model to convert user data to context:
155
+
156
+ ```ruby
157
+ class User < ApplicationRecord
158
+ def to_user_context
159
+ {
160
+ id: id,
161
+ email: email,
162
+ role: role,
163
+ # Add other relevant fields, but be mindful of privacy
164
+ # Don't include sensitive data like passwords, tokens, etc.
165
+ }
166
+ end
167
+ end
168
+ ```
169
+
170
+ ### Usage in Controllers
171
+
172
+ ```ruby
173
+ class SearchController < ApplicationController
174
+ def search
175
+ query = params[:query]
176
+
177
+ begin
178
+ # Perform search with Boxcars
179
+ results = perform_boxcar_search(query)
180
+
181
+ # Track successful search
182
+ observation = success_observation_with_user(
183
+ "Search completed successfully",
184
+ query: query,
185
+ results_count: results.count,
186
+ processing_time_ms: 250
187
+ )
188
+
189
+ Boxcars::Observability.track_observation(
190
+ observation,
191
+ event: 'search_completed',
192
+ controller: 'SearchController',
193
+ action: 'search'
194
+ )
195
+
196
+ render json: { results: results }
197
+
198
+ rescue StandardError => e
199
+ # Track search error
200
+ error_observation = error_observation_with_user(
201
+ "Search failed: #{e.message}",
202
+ query: query,
203
+ error_class: e.class.name
204
+ )
205
+
206
+ Boxcars::Observability.track_observation(
207
+ error_observation,
208
+ event: 'search_failed',
209
+ controller: 'SearchController',
210
+ action: 'search'
211
+ )
212
+
213
+ render json: { error: "Search failed" }, status: 500
214
+ end
215
+ end
216
+ end
217
+ ```
218
+
219
+ ## Analytics Integration
220
+
221
+ ### PostHog Integration
222
+
223
+ When using PostHog as your observability backend, user context is automatically formatted with `$user_` prefixes:
224
+
225
+ ```ruby
226
+ # This user context:
227
+ user_context = {
228
+ id: 123,
229
+ email: "user@example.com",
230
+ role: "admin"
231
+ }
232
+
233
+ # Becomes these PostHog properties:
234
+ {
235
+ "$user_id" => 123,
236
+ "$user_email" => "user@example.com",
237
+ "$user_role" => "admin"
238
+ }
239
+ ```
240
+
241
+ This allows PostHog to automatically associate events with users and enables user-based analytics and segmentation.
242
+
243
+ ### Custom Analytics Systems
244
+
245
+ For other analytics systems, you can access the user context directly:
246
+
247
+ ```ruby
248
+ observation = Boxcars::Observation.ok_with_user("Action completed", user_context: user_data)
249
+
250
+ # Access user context
251
+ user_info = observation.user_context
252
+ # => {id: 123, email: "user@example.com", role: "admin"}
253
+
254
+ # Include in your custom tracking
255
+ your_analytics.track(
256
+ event: 'boxcar_action',
257
+ user_id: user_info[:id],
258
+ user_email: user_info[:email],
259
+ properties: observation.to_h
260
+ )
261
+ ```
262
+
263
+ ## Privacy Considerations
264
+
265
+ ### What to Include
266
+
267
+ **Safe to include:**
268
+ - User ID (for tracking purposes)
269
+ - Email (if needed for support)
270
+ - Role/permissions level
271
+ - Account type (free, premium, etc.)
272
+ - Tenant/organization ID
273
+
274
+ **Avoid including:**
275
+ - Passwords or password hashes
276
+ - API keys or tokens
277
+ - Personal identification numbers
278
+ - Credit card information
279
+ - Any other sensitive personal data
280
+
281
+ ### Example Safe User Context
282
+
283
+ ```ruby
284
+ def to_user_context
285
+ {
286
+ id: id,
287
+ email: email,
288
+ role: role,
289
+ account_type: subscription&.plan_name,
290
+ organization_id: organization_id,
291
+ created_at: created_at.iso8601,
292
+ last_login: last_sign_in_at&.iso8601
293
+ }
294
+ end
295
+ ```
296
+
297
+ ## Testing
298
+
299
+ ### RSpec Examples
300
+
301
+ ```ruby
302
+ RSpec.describe "User Context in Observations" do
303
+ let(:user_context) do
304
+ {
305
+ id: 123,
306
+ email: "test@example.com",
307
+ role: "admin"
308
+ }
309
+ end
310
+
311
+ it "creates observation with user context" do
312
+ observation = Boxcars::Observation.ok_with_user(
313
+ "Test action",
314
+ user_context: user_context
315
+ )
316
+
317
+ expect(observation.user_context?).to be true
318
+ expect(observation.user_context[:id]).to eq(123)
319
+ expect(observation.user_context[:email]).to eq("test@example.com")
320
+ end
321
+
322
+ it "tracks observation with user context" do
323
+ observation = Boxcars::Observation.ok_with_user(
324
+ "Test action",
325
+ user_context: user_context
326
+ )
327
+
328
+ expect(Boxcars::Observability).to receive(:track).with(
329
+ event: 'test_event',
330
+ properties: hash_including(
331
+ "$user_id" => 123,
332
+ "$user_email" => "test@example.com"
333
+ )
334
+ )
335
+
336
+ Boxcars::Observability.track(
337
+ event: 'test_event',
338
+ properties: {},
339
+ observation: observation
340
+ )
341
+ end
342
+ end
343
+ ```
344
+
345
+ ## Migration Guide
346
+
347
+ ### Existing Code
348
+
349
+ Your existing observation code continues to work unchanged:
350
+
351
+ ```ruby
352
+ # This still works exactly as before
353
+ observation = Boxcars::Observation.ok("Action completed", extra: "data")
354
+ Boxcars::Observability.track(event: 'action', properties: { type: 'test' })
355
+ ```
356
+
357
+ ### Adding User Context
358
+
359
+ To add user context to existing observations, simply replace:
360
+
361
+ ```ruby
362
+ # Before
363
+ observation = Boxcars::Observation.ok("Action completed")
364
+
365
+ # After
366
+ observation = Boxcars::Observation.ok_with_user(
367
+ "Action completed",
368
+ user_context: current_user.to_user_context
369
+ )
370
+ ```
371
+
372
+ ### Updating Tracking Calls
373
+
374
+ To include user context in tracking:
375
+
376
+ ```ruby
377
+ # Before
378
+ Boxcars::Observability.track(
379
+ event: 'action_completed',
380
+ properties: { action_type: 'search' }
381
+ )
382
+
383
+ # After
384
+ Boxcars::Observability.track_observation(
385
+ observation_with_user_context,
386
+ event: 'action_completed',
387
+ action_type: 'search'
388
+ )
389
+ ```
390
+
391
+ ## Benefits
392
+
393
+ 1. **Better Debugging**: Quickly identify which user encountered an issue
394
+ 2. **User Analytics**: Segment usage patterns by user type, role, or other attributes
395
+ 3. **Compliance**: Track user actions for audit trails
396
+ 4. **Support**: Faster issue resolution with user context
397
+ 5. **Product Insights**: Understand how different user segments use AI features
398
+
399
+ ## Best Practices
400
+
401
+ 1. **Consistent Context**: Use the same user context structure across your application
402
+ 2. **Helper Methods**: Create controller helpers to reduce code duplication
403
+ 3. **Privacy First**: Only include necessary user data
404
+ 4. **Error Handling**: Always handle cases where `current_user` might be nil
405
+ 5. **Testing**: Write tests for both user and anonymous scenarios
406
+ 6. **Documentation**: Document what user data you're tracking and why
407
+
408
+ ## Troubleshooting
409
+
410
+ ### Common Issues
411
+
412
+ **User context not appearing in analytics:**
413
+ - Ensure your observability backend is configured
414
+ - Check that you're using `track_observation` or passing the `observation` parameter to `track`
415
+ - Verify the observation actually has user context with `user_context?`
416
+
417
+ **Anonymous users causing errors:**
418
+ - Always check for `current_user` presence before creating user context
419
+ - Use helper methods that handle nil users gracefully
420
+
421
+ **Missing user data:**
422
+ - Ensure your `to_user_context` method returns a hash
423
+ - Check that the user object has the expected attributes
424
+
425
+ ### Debug Example
426
+
427
+ ```ruby
428
+ # Debug user context
429
+ observation = Boxcars::Observation.ok_with_user("Test", user_context: user_data)
430
+ puts "Has user context: #{observation.user_context?}"
431
+ puts "User context: #{observation.user_context.inspect}"
432
+ puts "Full observation: #{observation.to_h.inspect}"
433
+ ```
434
+
435
+ This comprehensive user context system provides a clean, privacy-conscious way to associate user information with Boxcars operations, enabling better tracking, debugging, and analytics while maintaining backward compatibility.
@@ -28,10 +28,11 @@ module Boxcars
28
28
  # useful for when you need to use AI to answer questions. You should ask targeted questions".
29
29
  # @param prompts [Array<String>] The prompts to use when asking the engine. Defaults to [].
30
30
  def initialize(name: DEFAULT_NAME, description: DEFAULT_DESCRIPTION, prompts: [], **kwargs)
31
+ user_id = kwargs.delete(:user_id)
31
32
  @llm_params = DEFAULT_PARAMS.merge(kwargs)
32
33
  @prompts = prompts
33
34
  @batch_size = 20
34
- super(description:, name:)
35
+ super(description:, name:, user_id:)
35
36
  end
36
37
 
37
38
  def conversation_model?(_model)
@@ -278,7 +279,8 @@ module Boxcars
278
279
  request_context = {
279
280
  prompt: call_context[:prompt_object],
280
281
  inputs: call_context[:inputs],
281
- conversation_for_api: call_context[:api_request_params]
282
+ conversation_for_api: call_context[:api_request_params],
283
+ user_id:
282
284
  }
283
285
 
284
286
  track_ai_generation(
@@ -27,10 +27,11 @@ module Boxcars
27
27
  # useful for when you need to use AI to answer questions. You should ask targeted questions".
28
28
  # @param prompts [Array<String>] The prompts to use when asking the engine. Defaults to [].
29
29
  def initialize(name: DEFAULT_NAME, description: DEFAULT_DESCRIPTION, prompts: [], **kwargs)
30
+ user_id = kwargs.delete(:user_id)
30
31
  @llm_params = DEFAULT_PARAMS.merge(kwargs)
31
32
  @prompts = prompts
32
33
  @batch_size = 20
33
- super(description:, name:)
34
+ super(description:, name:, user_id:)
34
35
  end
35
36
 
36
37
  def conversation_model?(_model)
@@ -211,7 +212,8 @@ module Boxcars
211
212
  request_context = {
212
213
  prompt: call_context[:prompt_object],
213
214
  inputs: call_context[:inputs],
214
- conversation_for_api: call_context[:api_request_params]
215
+ conversation_for_api: call_context[:api_request_params],
216
+ user_id:
215
217
  }
216
218
 
217
219
  track_ai_generation(
@@ -19,10 +19,11 @@ module Boxcars
19
19
  "You should ask targeted questions"
20
20
 
21
21
  def initialize(name: DEFAULT_NAME, description: DEFAULT_DESCRIPTION, prompts: [], batch_size: 20, **kwargs)
22
+ user_id = kwargs.delete(:user_id)
22
23
  @llm_params = DEFAULT_PARAMS.merge(kwargs) # Corrected typo here
23
24
  @prompts = prompts
24
25
  @batch_size = batch_size
25
- super(description:, name:)
26
+ super(description:, name:, user_id:)
26
27
  end
27
28
 
28
29
  # Renamed from open_ai_client to gemini_client for clarity
@@ -68,7 +69,8 @@ module Boxcars
68
69
  request_context = {
69
70
  prompt: current_prompt_object,
70
71
  inputs:,
71
- conversation_for_api: api_request_params&.dig(:messages) || []
72
+ conversation_for_api: api_request_params&.dig(:messages) || [],
73
+ user_id:
72
74
  }
73
75
  track_ai_generation(
74
76
  duration_ms:,
@@ -82,13 +84,13 @@ module Boxcars
82
84
  # If there's an error, raise it to maintain backward compatibility with existing tests
83
85
  raise response_data[:error] if response_data[:error]
84
86
 
85
- response_data
87
+ response_data[:parsed_json]
86
88
  end
87
89
 
88
90
  def run(question, **)
89
91
  prompt = Prompt.new(template: question)
90
- response_data = client(prompt:, inputs: {}, **)
91
- answer = _gemini_handle_call_outcome(response_data:)
92
+ response = client(prompt:, inputs: {}, **)
93
+ answer = _extract_content_from_gemini_response(response)
92
94
  Boxcars.debug("Answer: #{answer}", :cyan)
93
95
  answer
94
96
  end
@@ -19,10 +19,11 @@ module Boxcars
19
19
  }.freeze
20
20
 
21
21
  def initialize(name: DEFAULT_NAME, description: DEFAULT_DESCRIPTION, prompts: [], batch_size: 2, **kwargs)
22
+ user_id = kwargs.delete(:user_id)
22
23
  @gpt4all_params = DEFAULT_PARAMS.merge(kwargs) # Store merged params
23
24
  @prompts = prompts
24
25
  @batch_size = batch_size # Retain if used by other methods
25
- super(description:, name:)
26
+ super(description:, name:, user_id:)
26
27
  end
27
28
 
28
29
  def client(prompt:, inputs: {}, **kwargs)
@@ -31,10 +32,8 @@ module Boxcars
31
32
  # current_params are the effective parameters for this call, including defaults and overrides
32
33
  current_params = @gpt4all_params.merge(kwargs)
33
34
  # api_request_params for GPT4All is just the input text.
34
- api_request_params = nil
35
+ api_request_params, gpt4all_instance = nil
35
36
  current_prompt_object = prompt.is_a?(Array) ? prompt.first : prompt
36
- gpt4all_instance = nil # To ensure it's in scope for ensure block
37
-
38
37
  begin
39
38
  gpt4all_instance = Gpt4all::ConversationalAI.new
40
39
  # prepare_resources might download models, could take time.
@@ -68,7 +67,8 @@ module Boxcars
68
67
  request_context = {
69
68
  prompt: current_prompt_object,
70
69
  inputs:,
71
- conversation_for_api: api_request_params&.dig(:prompt) # The text prompt
70
+ conversation_for_api: api_request_params&.dig(:prompt),
71
+ user_id:
72
72
  }
73
73
 
74
74
  track_ai_generation(