lluminary 0.1.4 → 0.2.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/lib/lluminary/models/base.rb +166 -66
- data/lib/lluminary/schema.rb +26 -0
- data/lib/lluminary/schema_model.rb +134 -64
- data/spec/examples/character_profiler_spec.rb +85 -0
- data/spec/lluminary/models/base_spec.rb +901 -100
- data/spec/lluminary/schema_spec.rb +255 -0
- metadata +3 -2
@@ -1,6 +1,130 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require "spec_helper"
|
3
3
|
|
4
|
+
# The following is an example of our goals for the output when describing fields in the schema:
|
5
|
+
#
|
6
|
+
# # user_profile
|
7
|
+
# Description: A user's complete profile
|
8
|
+
# Type: object
|
9
|
+
# Example: {
|
10
|
+
# "name": "your name here",
|
11
|
+
# "age": 0,
|
12
|
+
# "preferences": {
|
13
|
+
# "theme": "your theme here",
|
14
|
+
# "notifications_enabled": true
|
15
|
+
# }
|
16
|
+
# }
|
17
|
+
#
|
18
|
+
# # name
|
19
|
+
# Description: A person's full name
|
20
|
+
# Type: string
|
21
|
+
# Example: "your name here"
|
22
|
+
#
|
23
|
+
# # age
|
24
|
+
# Type: integer
|
25
|
+
# Example: 0
|
26
|
+
#
|
27
|
+
# # preferences
|
28
|
+
# Description: User's system preferences
|
29
|
+
# Type: object
|
30
|
+
# Example: {
|
31
|
+
# "theme": "your theme here",
|
32
|
+
# "notifications_enabled": true
|
33
|
+
# }
|
34
|
+
#
|
35
|
+
# # preferences.theme
|
36
|
+
# Description: The UI color theme
|
37
|
+
# Type: string
|
38
|
+
# Example: "your theme here"
|
39
|
+
#
|
40
|
+
# # preferences.notifications_enabled
|
41
|
+
# Type: boolean
|
42
|
+
# Example: true
|
43
|
+
#
|
44
|
+
# # security
|
45
|
+
# Description: Security and authentication settings
|
46
|
+
# Type: object
|
47
|
+
# Example: {
|
48
|
+
# "credentials": {
|
49
|
+
# "last_login": "2024-01-01T12:00:00+00:00"
|
50
|
+
# }
|
51
|
+
# }
|
52
|
+
#
|
53
|
+
# # security.credentials
|
54
|
+
# Type: object
|
55
|
+
# Example: {
|
56
|
+
# "last_login": "2024-01-01T12:00:00+00:00"
|
57
|
+
# }
|
58
|
+
#
|
59
|
+
# # security.credentials.last_login
|
60
|
+
# Type: datetime in ISO8601 format
|
61
|
+
# Description: Most recent successful login
|
62
|
+
# Example: "2024-01-01T12:00:00+00:00"
|
63
|
+
#
|
64
|
+
# # tags
|
65
|
+
# Description: User's associated tags
|
66
|
+
# Type: array of string
|
67
|
+
# Example: ["first tag", "second tag"]
|
68
|
+
#
|
69
|
+
# # roles
|
70
|
+
# Description: User's system roles
|
71
|
+
# Type: array of objects
|
72
|
+
# Example: [
|
73
|
+
# {
|
74
|
+
# "name": "your name here",
|
75
|
+
# "permissions": ["first permission", "second permission"]
|
76
|
+
# },
|
77
|
+
# {
|
78
|
+
# "name": "your name here",
|
79
|
+
# "permissions": ["first permission", "second permission"]
|
80
|
+
# }
|
81
|
+
# ]
|
82
|
+
#
|
83
|
+
# # roles[].name
|
84
|
+
# Description: Name of the role
|
85
|
+
# Type: string
|
86
|
+
# Example: "your name here"
|
87
|
+
#
|
88
|
+
# # roles[].permissions
|
89
|
+
# Description: Permissions granted by this role
|
90
|
+
# Type: array of strings
|
91
|
+
# Example: ["first permission", "second permission"]
|
92
|
+
#
|
93
|
+
# # matrix
|
94
|
+
# Description: A 2D grid of numbers
|
95
|
+
# Type: array of arrays
|
96
|
+
# Example: [[1, 2, 3], [4, 5, 6]]
|
97
|
+
#
|
98
|
+
# # settings
|
99
|
+
# Description: User configuration settings
|
100
|
+
# Type: object
|
101
|
+
# Example: {
|
102
|
+
# "theme": "your theme here",
|
103
|
+
# "favorites": ["first favorite", "second favorite"],
|
104
|
+
# "notifications": {
|
105
|
+
# "email": true,
|
106
|
+
# "channels": ["first channel", "second channel"]
|
107
|
+
# }
|
108
|
+
# }
|
109
|
+
#
|
110
|
+
# # settings.favorites
|
111
|
+
# Description: User's favorite items
|
112
|
+
# Type: array of strings
|
113
|
+
# Example: ["first favorite", "second favorite"]
|
114
|
+
#
|
115
|
+
# # settings.notifications
|
116
|
+
# Description: Notification preferences
|
117
|
+
# Type: object
|
118
|
+
# Example: {
|
119
|
+
# "email": true,
|
120
|
+
# "channels": ["first channel", "second channel"]
|
121
|
+
# }
|
122
|
+
#
|
123
|
+
# # settings.notifications.channels
|
124
|
+
# Description: Notification channels to use
|
125
|
+
# Type: array of strings
|
126
|
+
# Example: ["first channel", "second channel"]
|
127
|
+
|
4
128
|
RSpec.describe Lluminary::Models::Base do
|
5
129
|
let(:model) { described_class.new }
|
6
130
|
|
@@ -38,12 +162,16 @@ RSpec.describe Lluminary::Models::Base do
|
|
38
162
|
end
|
39
163
|
|
40
164
|
it "formats string field description correctly" do
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
165
|
+
prompt = model.format_prompt(task)
|
166
|
+
|
167
|
+
expected_description = <<~DESCRIPTION.chomp
|
168
|
+
# name
|
169
|
+
Description: The person's name
|
170
|
+
Type: string
|
171
|
+
Example: "your name here"
|
172
|
+
DESCRIPTION
|
173
|
+
|
174
|
+
expect(prompt).to include(expected_description)
|
47
175
|
end
|
48
176
|
end
|
49
177
|
|
@@ -55,12 +183,16 @@ RSpec.describe Lluminary::Models::Base do
|
|
55
183
|
end
|
56
184
|
|
57
185
|
it "formats integer field description correctly" do
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
186
|
+
prompt = model.format_prompt(task)
|
187
|
+
|
188
|
+
expected_description = <<~DESCRIPTION.chomp
|
189
|
+
# age
|
190
|
+
Description: The person's age
|
191
|
+
Type: integer
|
192
|
+
Example: 0
|
193
|
+
DESCRIPTION
|
194
|
+
|
195
|
+
expect(prompt).to include(expected_description)
|
64
196
|
end
|
65
197
|
end
|
66
198
|
|
@@ -72,12 +204,16 @@ RSpec.describe Lluminary::Models::Base do
|
|
72
204
|
end
|
73
205
|
|
74
206
|
it "formats boolean field description correctly" do
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
207
|
+
prompt = model.format_prompt(task)
|
208
|
+
|
209
|
+
expected_description = <<~DESCRIPTION.chomp
|
210
|
+
# active
|
211
|
+
Description: Whether the person is active
|
212
|
+
Type: boolean
|
213
|
+
Example: true
|
214
|
+
DESCRIPTION
|
215
|
+
|
216
|
+
expect(prompt).to include(expected_description)
|
81
217
|
end
|
82
218
|
end
|
83
219
|
|
@@ -89,12 +225,16 @@ RSpec.describe Lluminary::Models::Base do
|
|
89
225
|
end
|
90
226
|
|
91
227
|
it "formats float field description correctly" do
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
228
|
+
prompt = model.format_prompt(task)
|
229
|
+
|
230
|
+
expected_description = <<~DESCRIPTION.chomp
|
231
|
+
# score
|
232
|
+
Description: The person's score
|
233
|
+
Type: float
|
234
|
+
Example: 0.0
|
235
|
+
DESCRIPTION
|
236
|
+
|
237
|
+
expect(prompt).to include(expected_description)
|
98
238
|
end
|
99
239
|
end
|
100
240
|
|
@@ -106,12 +246,16 @@ RSpec.describe Lluminary::Models::Base do
|
|
106
246
|
end
|
107
247
|
|
108
248
|
it "formats datetime field description correctly" do
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
249
|
+
prompt = model.format_prompt(task)
|
250
|
+
|
251
|
+
expected_description = <<~DESCRIPTION.chomp
|
252
|
+
# created_at
|
253
|
+
Description: When the person was created
|
254
|
+
Type: datetime in ISO8601 format
|
255
|
+
Example: "2024-01-01T12:00:00+00:00"
|
256
|
+
DESCRIPTION
|
257
|
+
|
258
|
+
expect(prompt).to include(expected_description)
|
115
259
|
end
|
116
260
|
end
|
117
261
|
end
|
@@ -127,12 +271,16 @@ RSpec.describe Lluminary::Models::Base do
|
|
127
271
|
end
|
128
272
|
|
129
273
|
it "formats array of strings field description correctly" do
|
130
|
-
|
274
|
+
prompt = model.format_prompt(task)
|
275
|
+
|
276
|
+
expected_description = <<~DESCRIPTION.chomp
|
131
277
|
# tags
|
132
|
-
Type: array of string
|
133
278
|
Description: List of tags
|
134
|
-
|
279
|
+
Type: array of strings
|
280
|
+
Example: ["first tag","second tag"]
|
135
281
|
DESCRIPTION
|
282
|
+
|
283
|
+
expect(prompt).to include(expected_description)
|
136
284
|
end
|
137
285
|
end
|
138
286
|
|
@@ -146,12 +294,16 @@ RSpec.describe Lluminary::Models::Base do
|
|
146
294
|
end
|
147
295
|
|
148
296
|
it "formats array of floats field description correctly" do
|
149
|
-
|
297
|
+
prompt = model.format_prompt(task)
|
298
|
+
|
299
|
+
expected_description = <<~DESCRIPTION.chomp
|
150
300
|
# scores
|
151
|
-
Type: array of float
|
152
301
|
Description: List of scores
|
153
|
-
|
302
|
+
Type: array of floats
|
303
|
+
Example: [1.0,2.0,3.0]
|
154
304
|
DESCRIPTION
|
305
|
+
|
306
|
+
expect(prompt).to include(expected_description)
|
155
307
|
end
|
156
308
|
end
|
157
309
|
|
@@ -165,31 +317,83 @@ RSpec.describe Lluminary::Models::Base do
|
|
165
317
|
end
|
166
318
|
|
167
319
|
it "formats array of datetimes field description correctly" do
|
168
|
-
|
320
|
+
prompt = model.format_prompt(task)
|
321
|
+
|
322
|
+
expected_description = <<~DESCRIPTION.chomp
|
169
323
|
# dates
|
170
|
-
Type: array of datetime in ISO8601 format
|
171
324
|
Description: List of important dates
|
172
|
-
|
325
|
+
Type: array of datetimes in ISO8601 format
|
326
|
+
Example: ["2024-01-01T12:00:00+00:00","2024-01-02T12:00:00+00:00"]
|
173
327
|
DESCRIPTION
|
328
|
+
|
329
|
+
expect(prompt).to include(expected_description)
|
174
330
|
end
|
175
331
|
end
|
176
332
|
|
177
|
-
context "with 2D array (matrix)" do
|
333
|
+
context "with 2D array (matrix) with descriptions" do
|
178
334
|
before do
|
179
335
|
task_class.output_schema do
|
180
336
|
array :matrix, description: "2D array of numbers" do
|
181
|
-
array
|
337
|
+
array description: "1D array of numbers" do
|
338
|
+
integer description: "A number"
|
339
|
+
end
|
182
340
|
end
|
183
341
|
end
|
184
342
|
end
|
185
343
|
|
186
344
|
it "formats 2D array field description correctly" do
|
187
|
-
|
345
|
+
prompt = model.format_prompt(task)
|
346
|
+
|
347
|
+
expected_description = <<~DESCRIPTION.chomp
|
188
348
|
# matrix
|
189
|
-
Type: array of array of integer
|
190
349
|
Description: 2D array of numbers
|
191
|
-
|
350
|
+
Type: array of arrays
|
351
|
+
Example: [[1,2,3],[1,2,3]]
|
352
|
+
|
353
|
+
# matrix[]
|
354
|
+
Description: 1D array of numbers
|
355
|
+
Type: array of integers
|
356
|
+
Example: [1,2,3]
|
357
|
+
|
358
|
+
# matrix[][]
|
359
|
+
Description: A number
|
360
|
+
Type: integer
|
361
|
+
Example: 0
|
192
362
|
DESCRIPTION
|
363
|
+
|
364
|
+
expect(prompt).to include(expected_description)
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
context "with 2D array (matrix) with some descriptions" do
|
369
|
+
before do
|
370
|
+
task_class.output_schema do
|
371
|
+
array :matrix, description: "2D array of numbers" do
|
372
|
+
array { integer description: "A number" }
|
373
|
+
end
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
it "formats 2D array field description correctly" do
|
378
|
+
prompt = model.format_prompt(task)
|
379
|
+
|
380
|
+
expected_description = <<~DESCRIPTION.chomp
|
381
|
+
# matrix
|
382
|
+
Description: 2D array of numbers
|
383
|
+
Type: array of arrays
|
384
|
+
Example: [[1,2,3],[1,2,3]]
|
385
|
+
|
386
|
+
# matrix[]
|
387
|
+
Type: array of integers
|
388
|
+
Example: [1,2,3]
|
389
|
+
|
390
|
+
# matrix[][]
|
391
|
+
Description: A number
|
392
|
+
Type: integer
|
393
|
+
Example: 0
|
394
|
+
DESCRIPTION
|
395
|
+
|
396
|
+
expect(prompt).to include(expected_description)
|
193
397
|
end
|
194
398
|
end
|
195
399
|
|
@@ -197,18 +401,282 @@ RSpec.describe Lluminary::Models::Base do
|
|
197
401
|
before do
|
198
402
|
task_class.output_schema do
|
199
403
|
array :cube, description: "3D array of strings" do
|
200
|
-
array
|
404
|
+
array do
|
405
|
+
array description: "1D array" do
|
406
|
+
string
|
407
|
+
end
|
408
|
+
end
|
201
409
|
end
|
202
410
|
end
|
203
411
|
end
|
204
412
|
|
205
413
|
it "formats 3D array field description correctly" do
|
206
|
-
|
414
|
+
prompt = model.format_prompt(task)
|
415
|
+
|
416
|
+
expected_description = <<~DESCRIPTION.chomp
|
207
417
|
# cube
|
208
|
-
Type: array of array of array of string
|
209
418
|
Description: 3D array of strings
|
210
|
-
|
419
|
+
Type: array of arrays
|
420
|
+
Example: [[["first item","second item"],["first item","second item"]],[["first item","second item"],["first item","second item"]]]
|
421
|
+
|
422
|
+
# cube[]
|
423
|
+
Type: array of arrays
|
424
|
+
Example: [["first item","second item"],["first item","second item"]]
|
425
|
+
|
426
|
+
# cube[][]
|
427
|
+
Description: 1D array
|
428
|
+
Type: array of strings
|
429
|
+
Example: ["first item","second item"]
|
430
|
+
|
431
|
+
# cube[][][]
|
432
|
+
Type: string
|
433
|
+
Example: "first item"
|
211
434
|
DESCRIPTION
|
435
|
+
|
436
|
+
expect(prompt).to include(expected_description)
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
context "with array containing hash" do
|
441
|
+
before do
|
442
|
+
task_class.output_schema do
|
443
|
+
array :contacts, description: "List of contacts" do
|
444
|
+
hash { string :name, description: "Contact name" }
|
445
|
+
end
|
446
|
+
end
|
447
|
+
end
|
448
|
+
|
449
|
+
it "formats array of hashes field description correctly" do
|
450
|
+
prompt = model.format_prompt(task)
|
451
|
+
|
452
|
+
expected_description = <<~DESCRIPTION.chomp
|
453
|
+
# contacts
|
454
|
+
Description: List of contacts
|
455
|
+
Type: array of objects
|
456
|
+
Example: [{"name":"your name here"},{"name":"your name here"}]
|
457
|
+
|
458
|
+
# contacts[].name
|
459
|
+
Description: Contact name
|
460
|
+
Type: string
|
461
|
+
Example: "your name here"
|
462
|
+
DESCRIPTION
|
463
|
+
|
464
|
+
expect(prompt).to include(expected_description)
|
465
|
+
end
|
466
|
+
end
|
467
|
+
end
|
468
|
+
|
469
|
+
context "with hash fields" do
|
470
|
+
context "with simple hash with one field" do
|
471
|
+
before do
|
472
|
+
task_class.output_schema do
|
473
|
+
hash :person do
|
474
|
+
string :name, description: "The person's name"
|
475
|
+
end
|
476
|
+
end
|
477
|
+
end
|
478
|
+
|
479
|
+
it "formats hash field description correctly" do
|
480
|
+
prompt = model.format_prompt(task)
|
481
|
+
|
482
|
+
expected_description = <<~DESCRIPTION.chomp
|
483
|
+
# person
|
484
|
+
Type: object
|
485
|
+
Example: {"name":"your name here"}
|
486
|
+
|
487
|
+
# person.name
|
488
|
+
Description: The person's name
|
489
|
+
Type: string
|
490
|
+
Example: "your name here"
|
491
|
+
DESCRIPTION
|
492
|
+
|
493
|
+
expect(prompt).to include(expected_description)
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
497
|
+
context "with hash with two fields (one with description)" do
|
498
|
+
before do
|
499
|
+
task_class.output_schema do
|
500
|
+
hash :person, description: "A person" do
|
501
|
+
string :name, description: "The person's name"
|
502
|
+
integer :age
|
503
|
+
end
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
it "formats hash field descriptions correctly" do
|
508
|
+
prompt = model.format_prompt(task)
|
509
|
+
|
510
|
+
expected_description = <<~DESCRIPTION.chomp
|
511
|
+
# person
|
512
|
+
Description: A person
|
513
|
+
Type: object
|
514
|
+
Example: {"name":"your name here","age":0}
|
515
|
+
|
516
|
+
# person.name
|
517
|
+
Description: The person's name
|
518
|
+
Type: string
|
519
|
+
Example: "your name here"
|
520
|
+
|
521
|
+
# person.age
|
522
|
+
Type: integer
|
523
|
+
Example: 0
|
524
|
+
DESCRIPTION
|
525
|
+
|
526
|
+
expect(prompt).to include(expected_description)
|
527
|
+
end
|
528
|
+
end
|
529
|
+
|
530
|
+
context "with hash containing datetime field" do
|
531
|
+
before do
|
532
|
+
task_class.output_schema do
|
533
|
+
hash :event, description: "An event" do
|
534
|
+
string :title
|
535
|
+
datetime :scheduled_at, description: "When the event is scheduled"
|
536
|
+
end
|
537
|
+
end
|
538
|
+
end
|
539
|
+
|
540
|
+
it "formats hash with datetime field description correctly" do
|
541
|
+
prompt = model.format_prompt(task)
|
542
|
+
|
543
|
+
expected_description = <<~DESCRIPTION.chomp
|
544
|
+
# event
|
545
|
+
Description: An event
|
546
|
+
Type: object
|
547
|
+
Example: {"title":"your title here","scheduled_at":"2024-01-01T12:00:00+00:00"}
|
548
|
+
|
549
|
+
# event.title
|
550
|
+
Type: string
|
551
|
+
Example: "your title here"
|
552
|
+
|
553
|
+
# event.scheduled_at
|
554
|
+
Description: When the event is scheduled
|
555
|
+
Type: datetime in ISO8601 format
|
556
|
+
Example: "2024-01-01T12:00:00+00:00"
|
557
|
+
DESCRIPTION
|
558
|
+
|
559
|
+
expect(prompt).to include(expected_description)
|
560
|
+
end
|
561
|
+
end
|
562
|
+
|
563
|
+
context "with hash containing array field" do
|
564
|
+
before do
|
565
|
+
task_class.output_schema do
|
566
|
+
hash :user, description: "A user profile" do
|
567
|
+
string :name, description: "The person's name"
|
568
|
+
array :tags, description: "User tags" do
|
569
|
+
string
|
570
|
+
end
|
571
|
+
end
|
572
|
+
end
|
573
|
+
end
|
574
|
+
|
575
|
+
it "formats hash with array field description correctly" do
|
576
|
+
prompt = model.format_prompt(task)
|
577
|
+
|
578
|
+
expected_description = <<~DESCRIPTION.chomp
|
579
|
+
# user
|
580
|
+
Description: A user profile
|
581
|
+
Type: object
|
582
|
+
Example: {"name":"your name here","tags":["first tag","second tag"]}
|
583
|
+
|
584
|
+
# user.name
|
585
|
+
Description: The person's name
|
586
|
+
Type: string
|
587
|
+
Example: "your name here"
|
588
|
+
|
589
|
+
# user.tags
|
590
|
+
Description: User tags
|
591
|
+
Type: array of strings
|
592
|
+
Example: ["first tag","second tag"]
|
593
|
+
DESCRIPTION
|
594
|
+
|
595
|
+
expect(prompt).to include(expected_description)
|
596
|
+
end
|
597
|
+
end
|
598
|
+
|
599
|
+
context "with nested hashes (mixed descriptions)" do
|
600
|
+
before do
|
601
|
+
task_class.output_schema do
|
602
|
+
hash :person, description: "A person profile" do
|
603
|
+
string :name, description: "The person's name"
|
604
|
+
integer :age
|
605
|
+
hash :address do
|
606
|
+
string :street, description: "Street name"
|
607
|
+
string :city
|
608
|
+
string :country
|
609
|
+
end
|
610
|
+
hash :preferences, description: "User preferences" do
|
611
|
+
boolean :notifications
|
612
|
+
hash :theme do
|
613
|
+
string :color, description: "Theme color"
|
614
|
+
boolean :dark_mode
|
615
|
+
end
|
616
|
+
end
|
617
|
+
end
|
618
|
+
end
|
619
|
+
end
|
620
|
+
|
621
|
+
it "formats nested hash fields correctly with mixed descriptions" do
|
622
|
+
prompt = model.format_prompt(task)
|
623
|
+
|
624
|
+
expected_description = <<~DESCRIPTION.chomp
|
625
|
+
# person
|
626
|
+
Description: A person profile
|
627
|
+
Type: object
|
628
|
+
Example: {"name":"your name here","age":0,"address":{"street":"your street here","city":"your city here","country":"your country here"},"preferences":{"notifications":true,"theme":{"color":"your color here","dark_mode":true}}}
|
629
|
+
|
630
|
+
# person.name
|
631
|
+
Description: The person's name
|
632
|
+
Type: string
|
633
|
+
Example: "your name here"
|
634
|
+
|
635
|
+
# person.age
|
636
|
+
Type: integer
|
637
|
+
Example: 0
|
638
|
+
|
639
|
+
# person.address
|
640
|
+
Type: object
|
641
|
+
Example: {"street":"your street here","city":"your city here","country":"your country here"}
|
642
|
+
|
643
|
+
# person.address.street
|
644
|
+
Description: Street name
|
645
|
+
Type: string
|
646
|
+
Example: "your street here"
|
647
|
+
|
648
|
+
# person.address.city
|
649
|
+
Type: string
|
650
|
+
Example: "your city here"
|
651
|
+
|
652
|
+
# person.address.country
|
653
|
+
Type: string
|
654
|
+
Example: "your country here"
|
655
|
+
|
656
|
+
# person.preferences
|
657
|
+
Description: User preferences
|
658
|
+
Type: object
|
659
|
+
Example: {"notifications":true,"theme":{"color":"your color here","dark_mode":true}}
|
660
|
+
|
661
|
+
# person.preferences.notifications
|
662
|
+
Type: boolean
|
663
|
+
Example: true
|
664
|
+
|
665
|
+
# person.preferences.theme
|
666
|
+
Type: object
|
667
|
+
Example: {"color":"your color here","dark_mode":true}
|
668
|
+
|
669
|
+
# person.preferences.theme.color
|
670
|
+
Description: Theme color
|
671
|
+
Type: string
|
672
|
+
Example: "your color here"
|
673
|
+
|
674
|
+
# person.preferences.theme.dark_mode
|
675
|
+
Type: boolean
|
676
|
+
Example: true
|
677
|
+
DESCRIPTION
|
678
|
+
|
679
|
+
expect(prompt).to include(expected_description)
|
212
680
|
end
|
213
681
|
end
|
214
682
|
end
|
@@ -223,13 +691,17 @@ RSpec.describe Lluminary::Models::Base do
|
|
223
691
|
end
|
224
692
|
|
225
693
|
it "includes presence validation in field description" do
|
226
|
-
|
694
|
+
prompt = model.format_prompt(task)
|
695
|
+
|
696
|
+
expected_description = <<~DESCRIPTION.chomp
|
227
697
|
# name
|
228
|
-
Type: string
|
229
698
|
Description: The person's name
|
699
|
+
Type: string
|
230
700
|
Validations: must be present
|
231
|
-
Example: your name here
|
701
|
+
Example: "your name here"
|
232
702
|
DESCRIPTION
|
703
|
+
|
704
|
+
expect(prompt).to include(expected_description)
|
233
705
|
end
|
234
706
|
end
|
235
707
|
|
@@ -242,13 +714,17 @@ RSpec.describe Lluminary::Models::Base do
|
|
242
714
|
end
|
243
715
|
|
244
716
|
it "includes inclusion validation in field description" do
|
245
|
-
|
717
|
+
prompt = model.format_prompt(task)
|
718
|
+
|
719
|
+
expected_description = <<~DESCRIPTION.chomp
|
246
720
|
# status
|
247
|
-
Type: string
|
248
721
|
Description: The status
|
722
|
+
Type: string
|
249
723
|
Validations: must be one of: active, inactive
|
250
|
-
Example: your status here
|
724
|
+
Example: "your status here"
|
251
725
|
DESCRIPTION
|
726
|
+
|
727
|
+
expect(prompt).to include(expected_description)
|
252
728
|
end
|
253
729
|
end
|
254
730
|
|
@@ -261,13 +737,17 @@ RSpec.describe Lluminary::Models::Base do
|
|
261
737
|
end
|
262
738
|
|
263
739
|
it "includes exclusion validation in field description" do
|
264
|
-
|
740
|
+
prompt = model.format_prompt(task)
|
741
|
+
|
742
|
+
expected_description = <<~DESCRIPTION.chomp
|
265
743
|
# status
|
266
|
-
Type: string
|
267
744
|
Description: The status
|
745
|
+
Type: string
|
268
746
|
Validations: must not be one of: banned, blocked
|
269
|
-
Example: your status here
|
747
|
+
Example: "your status here"
|
270
748
|
DESCRIPTION
|
749
|
+
|
750
|
+
expect(prompt).to include(expected_description)
|
271
751
|
end
|
272
752
|
end
|
273
753
|
|
@@ -280,13 +760,17 @@ RSpec.describe Lluminary::Models::Base do
|
|
280
760
|
end
|
281
761
|
|
282
762
|
it "includes format validation in field description" do
|
283
|
-
|
763
|
+
prompt = model.format_prompt(task)
|
764
|
+
|
765
|
+
expected_description = <<~DESCRIPTION.chomp
|
284
766
|
# email
|
285
|
-
Type: string
|
286
767
|
Description: Email address
|
768
|
+
Type: string
|
287
769
|
Validations: must match format: (?-mix:\\A[^@\\s]+@[^@\\s]+\\z)
|
288
|
-
Example: your email here
|
770
|
+
Example: "your email here"
|
289
771
|
DESCRIPTION
|
772
|
+
|
773
|
+
expect(prompt).to include(expected_description)
|
290
774
|
end
|
291
775
|
end
|
292
776
|
|
@@ -299,13 +783,17 @@ RSpec.describe Lluminary::Models::Base do
|
|
299
783
|
end
|
300
784
|
|
301
785
|
it "includes length validation in field description" do
|
302
|
-
|
786
|
+
prompt = model.format_prompt(task)
|
787
|
+
|
788
|
+
expected_description = <<~DESCRIPTION.chomp
|
303
789
|
# password
|
304
|
-
Type: string
|
305
790
|
Description: The password
|
306
|
-
|
307
|
-
|
791
|
+
Type: string
|
792
|
+
Validations: must have at least 8 characters, must have at most 20 characters
|
793
|
+
Example: "your password here"
|
308
794
|
DESCRIPTION
|
795
|
+
|
796
|
+
expect(prompt).to include(expected_description)
|
309
797
|
end
|
310
798
|
end
|
311
799
|
|
@@ -322,13 +810,17 @@ RSpec.describe Lluminary::Models::Base do
|
|
322
810
|
end
|
323
811
|
|
324
812
|
it "includes numericality validation in field description" do
|
325
|
-
|
813
|
+
prompt = model.format_prompt(task)
|
814
|
+
|
815
|
+
expected_description = <<~DESCRIPTION.chomp
|
326
816
|
# age
|
327
|
-
Type: integer
|
328
817
|
Description: The age
|
818
|
+
Type: integer
|
329
819
|
Validations: must be greater than 0, must be less than or equal to 120
|
330
820
|
Example: 0
|
331
821
|
DESCRIPTION
|
822
|
+
|
823
|
+
expect(prompt).to include(expected_description)
|
332
824
|
end
|
333
825
|
end
|
334
826
|
|
@@ -348,13 +840,197 @@ RSpec.describe Lluminary::Models::Base do
|
|
348
840
|
end
|
349
841
|
|
350
842
|
it "includes all validations in field description" do
|
351
|
-
|
843
|
+
prompt = model.format_prompt(task)
|
844
|
+
|
845
|
+
expected_description = <<~DESCRIPTION.chomp
|
352
846
|
# username
|
353
|
-
Type: string
|
354
847
|
Description: The username
|
355
|
-
|
356
|
-
|
848
|
+
Type: string
|
849
|
+
Validations: must be present, must have between 3 and 20 characters, must match format: (?-mix:\\A[a-z0-9_]+\\z)
|
850
|
+
Example: "your username here"
|
357
851
|
DESCRIPTION
|
852
|
+
|
853
|
+
expect(prompt).to include(expected_description)
|
854
|
+
end
|
855
|
+
end
|
856
|
+
|
857
|
+
context "array validations" do
|
858
|
+
context "presence validation" do
|
859
|
+
before do
|
860
|
+
task_class.output_schema do
|
861
|
+
array :tags, description: "List of tags" do
|
862
|
+
string
|
863
|
+
end
|
864
|
+
validates :tags, presence: true
|
865
|
+
end
|
866
|
+
end
|
867
|
+
|
868
|
+
it "includes presence validation in array description" do
|
869
|
+
prompt = model.format_prompt(task)
|
870
|
+
|
871
|
+
expected_description = <<~DESCRIPTION.chomp
|
872
|
+
# tags
|
873
|
+
Description: List of tags
|
874
|
+
Type: array of strings
|
875
|
+
Validations: must be present
|
876
|
+
Example: ["first tag","second tag"]
|
877
|
+
DESCRIPTION
|
878
|
+
|
879
|
+
expect(prompt).to include(expected_description)
|
880
|
+
end
|
881
|
+
end
|
882
|
+
|
883
|
+
context "length validation - minimum" do
|
884
|
+
before do
|
885
|
+
task_class.output_schema do
|
886
|
+
array :categories, description: "List of categories" do
|
887
|
+
string
|
888
|
+
end
|
889
|
+
validates :categories, length: { minimum: 2 }
|
890
|
+
end
|
891
|
+
end
|
892
|
+
|
893
|
+
it "includes minimum length validation in array description" do
|
894
|
+
prompt = model.format_prompt(task)
|
895
|
+
|
896
|
+
expected_description = <<~DESCRIPTION.chomp
|
897
|
+
# categories
|
898
|
+
Description: List of categories
|
899
|
+
Type: array of strings
|
900
|
+
Validations: must have at least 2 elements
|
901
|
+
Example: ["first category","second category"]
|
902
|
+
DESCRIPTION
|
903
|
+
|
904
|
+
expect(prompt).to include(expected_description)
|
905
|
+
end
|
906
|
+
end
|
907
|
+
|
908
|
+
context "length validation - maximum" do
|
909
|
+
before do
|
910
|
+
task_class.output_schema do
|
911
|
+
array :roles, description: "User roles" do
|
912
|
+
string
|
913
|
+
end
|
914
|
+
validates :roles, length: { maximum: 5 }
|
915
|
+
end
|
916
|
+
end
|
917
|
+
|
918
|
+
it "includes maximum length validation in array description" do
|
919
|
+
prompt = model.format_prompt(task)
|
920
|
+
|
921
|
+
expected_description = <<~DESCRIPTION.chomp
|
922
|
+
# roles
|
923
|
+
Description: User roles
|
924
|
+
Type: array of strings
|
925
|
+
Validations: must have at most 5 elements
|
926
|
+
Example: ["first role","second role"]
|
927
|
+
DESCRIPTION
|
928
|
+
|
929
|
+
expect(prompt).to include(expected_description)
|
930
|
+
end
|
931
|
+
end
|
932
|
+
|
933
|
+
context "length validation - range" do
|
934
|
+
before do
|
935
|
+
task_class.output_schema do
|
936
|
+
array :features, description: "Product features" do
|
937
|
+
string
|
938
|
+
end
|
939
|
+
validates :features, length: { in: 2..4 }
|
940
|
+
end
|
941
|
+
end
|
942
|
+
|
943
|
+
it "includes range length validation in array description" do
|
944
|
+
prompt = model.format_prompt(task)
|
945
|
+
|
946
|
+
expected_description = <<~DESCRIPTION.chomp
|
947
|
+
# features
|
948
|
+
Description: Product features
|
949
|
+
Type: array of strings
|
950
|
+
Validations: must have between 2 and 4 elements
|
951
|
+
Example: ["first feature","second feature"]
|
952
|
+
DESCRIPTION
|
953
|
+
|
954
|
+
expect(prompt).to include(expected_description)
|
955
|
+
end
|
956
|
+
end
|
957
|
+
|
958
|
+
context "length validation - exact" do
|
959
|
+
before do
|
960
|
+
task_class.output_schema do
|
961
|
+
array :coordinates, description: "Point coordinates" do
|
962
|
+
float
|
963
|
+
end
|
964
|
+
validates :coordinates, length: { is: 3 }
|
965
|
+
end
|
966
|
+
end
|
967
|
+
|
968
|
+
it "includes exact length validation in array description" do
|
969
|
+
prompt = model.format_prompt(task)
|
970
|
+
|
971
|
+
expected_description = <<~DESCRIPTION.chomp
|
972
|
+
# coordinates
|
973
|
+
Description: Point coordinates
|
974
|
+
Type: array of floats
|
975
|
+
Validations: must have exactly 3 elements
|
976
|
+
Example: [1.0,2.0,3.0]
|
977
|
+
DESCRIPTION
|
978
|
+
|
979
|
+
expect(prompt).to include(expected_description)
|
980
|
+
end
|
981
|
+
end
|
982
|
+
|
983
|
+
context "absence validation" do
|
984
|
+
before do
|
985
|
+
task_class.output_schema do
|
986
|
+
array :deprecated_fields, description: "Deprecated fields" do
|
987
|
+
string
|
988
|
+
end
|
989
|
+
validates :deprecated_fields, absence: true
|
990
|
+
end
|
991
|
+
end
|
992
|
+
|
993
|
+
it "includes absence validation in array description" do
|
994
|
+
prompt = model.format_prompt(task)
|
995
|
+
|
996
|
+
expected_description = <<~DESCRIPTION.chomp
|
997
|
+
# deprecated_fields
|
998
|
+
Description: Deprecated fields
|
999
|
+
Type: array of strings
|
1000
|
+
Validations: must be absent
|
1001
|
+
Example: ["first deprecated_field","second deprecated_field"]
|
1002
|
+
DESCRIPTION
|
1003
|
+
|
1004
|
+
expect(prompt).to include(expected_description)
|
1005
|
+
end
|
1006
|
+
end
|
1007
|
+
end
|
1008
|
+
|
1009
|
+
context "hash validations" do
|
1010
|
+
context "presence validation" do
|
1011
|
+
before do
|
1012
|
+
task_class.output_schema do
|
1013
|
+
hash :user_settings, description: "User configuration settings" do
|
1014
|
+
string :theme
|
1015
|
+
boolean :notifications_enabled
|
1016
|
+
end
|
1017
|
+
validates :user_settings, presence: true
|
1018
|
+
end
|
1019
|
+
end
|
1020
|
+
|
1021
|
+
it "includes presence validation in hash field description" do
|
1022
|
+
prompt = model.format_prompt(task)
|
1023
|
+
|
1024
|
+
expected_description = <<~DESCRIPTION.chomp
|
1025
|
+
# user_settings
|
1026
|
+
Description: User configuration settings
|
1027
|
+
Type: object
|
1028
|
+
Validations: must be present
|
1029
|
+
Example: {"theme":"your theme here","notifications_enabled":true}
|
1030
|
+
DESCRIPTION
|
1031
|
+
|
1032
|
+
expect(prompt).to include(expected_description)
|
1033
|
+
end
|
358
1034
|
end
|
359
1035
|
end
|
360
1036
|
end
|
@@ -366,11 +1042,15 @@ RSpec.describe Lluminary::Models::Base do
|
|
366
1042
|
string :name, description: "The person's name"
|
367
1043
|
end
|
368
1044
|
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
1045
|
+
prompt = model.format_prompt(task)
|
1046
|
+
|
1047
|
+
expected_json = <<~JSON.chomp
|
1048
|
+
{
|
1049
|
+
"name": "your name here"
|
1050
|
+
}
|
1051
|
+
JSON
|
1052
|
+
|
1053
|
+
expect(prompt).to include(expected_json)
|
374
1054
|
end
|
375
1055
|
|
376
1056
|
it "generates correct JSON example for integer field" do
|
@@ -378,11 +1058,15 @@ RSpec.describe Lluminary::Models::Base do
|
|
378
1058
|
integer :age, description: "The person's age"
|
379
1059
|
end
|
380
1060
|
|
381
|
-
|
1061
|
+
prompt = model.format_prompt(task)
|
1062
|
+
|
1063
|
+
expected_json = <<~JSON.chomp
|
382
1064
|
{
|
383
1065
|
"age": 0
|
384
1066
|
}
|
385
1067
|
JSON
|
1068
|
+
|
1069
|
+
expect(prompt).to include(expected_json)
|
386
1070
|
end
|
387
1071
|
|
388
1072
|
it "generates correct JSON example for boolean field" do
|
@@ -390,21 +1074,29 @@ RSpec.describe Lluminary::Models::Base do
|
|
390
1074
|
boolean :is_active, description: "Whether the person is active"
|
391
1075
|
end
|
392
1076
|
|
393
|
-
|
1077
|
+
prompt = model.format_prompt(task)
|
1078
|
+
|
1079
|
+
expected_json = <<~JSON.chomp
|
394
1080
|
{
|
395
1081
|
"is_active": true
|
396
1082
|
}
|
397
1083
|
JSON
|
1084
|
+
|
1085
|
+
expect(prompt).to include(expected_json)
|
398
1086
|
end
|
399
1087
|
|
400
1088
|
it "generates correct JSON example for float field" do
|
401
1089
|
task_class.output_schema { float :score, description: "The score" }
|
402
1090
|
|
403
|
-
|
1091
|
+
prompt = model.format_prompt(task)
|
1092
|
+
|
1093
|
+
expected_json = <<~JSON.chomp
|
404
1094
|
{
|
405
1095
|
"score": 0.0
|
406
1096
|
}
|
407
1097
|
JSON
|
1098
|
+
|
1099
|
+
expect(prompt).to include(expected_json)
|
408
1100
|
end
|
409
1101
|
|
410
1102
|
it "generates correct JSON example for datetime field" do
|
@@ -412,11 +1104,15 @@ RSpec.describe Lluminary::Models::Base do
|
|
412
1104
|
datetime :created_at, description: "When it was created"
|
413
1105
|
end
|
414
1106
|
|
415
|
-
|
1107
|
+
prompt = model.format_prompt(task)
|
1108
|
+
|
1109
|
+
expected_json = <<~JSON.chomp
|
416
1110
|
{
|
417
1111
|
"created_at": "2024-01-01T12:00:00+00:00"
|
418
1112
|
}
|
419
1113
|
JSON
|
1114
|
+
|
1115
|
+
expect(prompt).to include(expected_json)
|
420
1116
|
end
|
421
1117
|
end
|
422
1118
|
|
@@ -428,15 +1124,18 @@ RSpec.describe Lluminary::Models::Base do
|
|
428
1124
|
end
|
429
1125
|
end
|
430
1126
|
|
431
|
-
|
1127
|
+
prompt = model.format_prompt(task)
|
1128
|
+
|
1129
|
+
expected_json = <<~JSON.chomp
|
432
1130
|
{
|
433
1131
|
"tags": [
|
434
1132
|
"first tag",
|
435
|
-
"second tag"
|
436
|
-
"..."
|
1133
|
+
"second tag"
|
437
1134
|
]
|
438
1135
|
}
|
439
1136
|
JSON
|
1137
|
+
|
1138
|
+
expect(prompt).to include(expected_json)
|
440
1139
|
end
|
441
1140
|
|
442
1141
|
it "generates correct JSON example for array of integers" do
|
@@ -446,7 +1145,9 @@ RSpec.describe Lluminary::Models::Base do
|
|
446
1145
|
end
|
447
1146
|
end
|
448
1147
|
|
449
|
-
|
1148
|
+
prompt = model.format_prompt(task)
|
1149
|
+
|
1150
|
+
expected_json = <<~JSON.chomp
|
450
1151
|
{
|
451
1152
|
"counts": [
|
452
1153
|
1,
|
@@ -455,6 +1156,8 @@ RSpec.describe Lluminary::Models::Base do
|
|
455
1156
|
]
|
456
1157
|
}
|
457
1158
|
JSON
|
1159
|
+
|
1160
|
+
expect(prompt).to include(expected_json)
|
458
1161
|
end
|
459
1162
|
|
460
1163
|
it "generates correct JSON example for array of datetimes" do
|
@@ -464,7 +1167,9 @@ RSpec.describe Lluminary::Models::Base do
|
|
464
1167
|
end
|
465
1168
|
end
|
466
1169
|
|
467
|
-
|
1170
|
+
prompt = model.format_prompt(task)
|
1171
|
+
|
1172
|
+
expected_json = <<~JSON.chomp
|
468
1173
|
{
|
469
1174
|
"timestamps": [
|
470
1175
|
"2024-01-01T12:00:00+00:00",
|
@@ -472,6 +1177,8 @@ RSpec.describe Lluminary::Models::Base do
|
|
472
1177
|
]
|
473
1178
|
}
|
474
1179
|
JSON
|
1180
|
+
|
1181
|
+
expect(prompt).to include(expected_json)
|
475
1182
|
end
|
476
1183
|
|
477
1184
|
it "generates correct JSON example for array without element type" do
|
@@ -479,11 +1186,15 @@ RSpec.describe Lluminary::Models::Base do
|
|
479
1186
|
array :items, description: "List of items"
|
480
1187
|
end
|
481
1188
|
|
482
|
-
|
1189
|
+
prompt = model.format_prompt(task)
|
1190
|
+
|
1191
|
+
expected_json = <<~JSON.chomp
|
483
1192
|
{
|
484
1193
|
"items": []
|
485
1194
|
}
|
486
1195
|
JSON
|
1196
|
+
|
1197
|
+
expect(prompt).to include(expected_json)
|
487
1198
|
end
|
488
1199
|
|
489
1200
|
it "generates correct JSON example for nested array of strings" do
|
@@ -493,22 +1204,24 @@ RSpec.describe Lluminary::Models::Base do
|
|
493
1204
|
end
|
494
1205
|
end
|
495
1206
|
|
496
|
-
|
1207
|
+
prompt = model.format_prompt(task)
|
1208
|
+
|
1209
|
+
expected_json = <<~JSON.chomp
|
497
1210
|
{
|
498
1211
|
"groups": [
|
499
1212
|
[
|
500
1213
|
"first item",
|
501
|
-
"second item"
|
502
|
-
"..."
|
1214
|
+
"second item"
|
503
1215
|
],
|
504
1216
|
[
|
505
1217
|
"first item",
|
506
|
-
"second item"
|
507
|
-
"..."
|
1218
|
+
"second item"
|
508
1219
|
]
|
509
1220
|
]
|
510
1221
|
}
|
511
1222
|
JSON
|
1223
|
+
|
1224
|
+
expect(prompt).to include(expected_json)
|
512
1225
|
end
|
513
1226
|
|
514
1227
|
it "generates correct JSON example for three-dimensional array of integers" do
|
@@ -518,7 +1231,9 @@ RSpec.describe Lluminary::Models::Base do
|
|
518
1231
|
end
|
519
1232
|
end
|
520
1233
|
|
521
|
-
|
1234
|
+
prompt = model.format_prompt(task)
|
1235
|
+
|
1236
|
+
expected_json = <<~JSON.chomp
|
522
1237
|
{
|
523
1238
|
"matrices": [
|
524
1239
|
[
|
@@ -548,6 +1263,8 @@ RSpec.describe Lluminary::Models::Base do
|
|
548
1263
|
]
|
549
1264
|
}
|
550
1265
|
JSON
|
1266
|
+
|
1267
|
+
expect(prompt).to include(expected_json)
|
551
1268
|
end
|
552
1269
|
end
|
553
1270
|
|
@@ -562,18 +1279,102 @@ RSpec.describe Lluminary::Models::Base do
|
|
562
1279
|
datetime :joined_at, description: "When they joined"
|
563
1280
|
end
|
564
1281
|
|
565
|
-
|
566
|
-
|
1282
|
+
prompt = model.format_prompt(task)
|
1283
|
+
|
1284
|
+
expected_json = <<~JSON.chomp
|
1285
|
+
{
|
1286
|
+
"name": "your name here",
|
1287
|
+
"age": 0,
|
1288
|
+
"hobbies": [
|
1289
|
+
"first hobby",
|
1290
|
+
"second hobby"
|
1291
|
+
],
|
1292
|
+
"joined_at": "2024-01-01T12:00:00+00:00"
|
1293
|
+
}
|
1294
|
+
JSON
|
1295
|
+
|
1296
|
+
expect(prompt).to include(expected_json)
|
1297
|
+
end
|
1298
|
+
end
|
1299
|
+
|
1300
|
+
context "with hash fields" do
|
1301
|
+
it "generates correct JSON example for simple hash" do
|
1302
|
+
task_class.output_schema do
|
1303
|
+
hash :config, description: "Configuration options" do
|
1304
|
+
string :host, description: "Server hostname"
|
1305
|
+
integer :port
|
1306
|
+
end
|
1307
|
+
end
|
1308
|
+
|
1309
|
+
prompt = model.format_prompt(task)
|
1310
|
+
|
1311
|
+
expected_json = <<~JSON.chomp
|
1312
|
+
{
|
1313
|
+
"config": {
|
1314
|
+
"host": "your host here",
|
1315
|
+
"port": 0
|
1316
|
+
}
|
1317
|
+
}
|
1318
|
+
JSON
|
1319
|
+
|
1320
|
+
expect(prompt).to include(expected_json)
|
1321
|
+
end
|
1322
|
+
|
1323
|
+
it "generates correct JSON example for nested hash" do
|
1324
|
+
task_class.output_schema do
|
1325
|
+
hash :user, description: "User profile" do
|
1326
|
+
string :name
|
1327
|
+
hash :address do
|
1328
|
+
string :street
|
1329
|
+
string :city
|
1330
|
+
string :country
|
1331
|
+
end
|
1332
|
+
end
|
1333
|
+
end
|
1334
|
+
|
1335
|
+
prompt = model.format_prompt(task)
|
1336
|
+
|
1337
|
+
expected_json = <<~JSON.chomp
|
1338
|
+
{
|
1339
|
+
"user": {
|
567
1340
|
"name": "your name here",
|
568
|
-
"
|
569
|
-
|
570
|
-
"
|
571
|
-
"
|
572
|
-
|
573
|
-
],
|
574
|
-
"joined_at": "2024-01-01T12:00:00+00:00"
|
1341
|
+
"address": {
|
1342
|
+
"street": "your street here",
|
1343
|
+
"city": "your city here",
|
1344
|
+
"country": "your country here"
|
1345
|
+
}
|
575
1346
|
}
|
1347
|
+
}
|
576
1348
|
JSON
|
1349
|
+
|
1350
|
+
expect(prompt).to include(expected_json)
|
1351
|
+
end
|
1352
|
+
|
1353
|
+
it "generates correct JSON example for hash with array" do
|
1354
|
+
task_class.output_schema do
|
1355
|
+
hash :user_data, description: "User data" do
|
1356
|
+
string :username
|
1357
|
+
array :permissions do
|
1358
|
+
string
|
1359
|
+
end
|
1360
|
+
end
|
1361
|
+
end
|
1362
|
+
|
1363
|
+
prompt = model.format_prompt(task)
|
1364
|
+
|
1365
|
+
expected_json = <<~JSON.chomp
|
1366
|
+
{
|
1367
|
+
"user_data": {
|
1368
|
+
"username": "your username here",
|
1369
|
+
"permissions": [
|
1370
|
+
"first permission",
|
1371
|
+
"second permission"
|
1372
|
+
]
|
1373
|
+
}
|
1374
|
+
}
|
1375
|
+
JSON
|
1376
|
+
|
1377
|
+
expect(prompt).to include(expected_json)
|
577
1378
|
end
|
578
1379
|
end
|
579
1380
|
end
|