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
         |