openkind-api 0.1.0

Phase 1: HTTP (axum) and gRPC (tonic) protocol layer for openkind.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
openapi: 3.1.0
info:
  title: openkind API (Jev System One)
  version: 0.1.0
  description: |
    Open-source, high-throughput decision-inference engine speaking the Jev protocol.
    Implements the System One request and response shape used by TypeSafe's hosted API.
    Provider-specific field and endpoint differences are recorded in
    `docs/JEV_COMPATIBILITY.md` in this repository.

    ### Jev Protocol Overview
    TypeSafe System One models evaluate structured context (`state`) against typed
    decision primitives (`noul`, `choice`, `score`) and return calibrated probabilities,
    categorical selections, and continuous rubric ratings without autoregressive text loops.

    ### Python SDK Compatibility
    The common Jev question and answer subset is compatible with:
    - `typesafe_sdk.TypeSafeClient` (synchronous HTTP client)
    - `typesafe_sdk.AsyncTypeSafeClient` (asynchronous HTTP client)
    - Question primitives: `Noul`, `Choice`, `Score`
    - Response models: `SystemOneResponse`, `NoulAnswer`, `ChoiceAnswer`, `ScoreAnswer`, `ListModelsResponse`
    - Retries and backoff via `RetryPolicy` honoring `Retry-After` and `retry-after-ms` headers
    - Tracing and correlation via `x-typesafe-request-id` response header (`result.request_id`)

    ### Environment Configuration
    - `TYPESAFE_API_KEY` / `OPENKIND_API_KEY`: Bearer authentication token.
    - `TYPESAFE_BASE_URL` / `OPENKIND_BASE_URL`: SDK base URL (SDK default: `https://api.typesafe.ai`; set it to the daemon URL for local use).
    - `TYPESAFE_DEFAULT_MODEL` / `OPENKIND_DEFAULT_MODEL`: Default model name (default: `jev-latest`).
    - `TYPESAFE_LOG_LEVEL` / `OPENKIND_LOG_LEVEL`: Binding log verbosity (`debug`, `info`, `warn`, `warning`, `error`, `off`; unknown values are ignored).
  license:
    name: Apache-2.0 OR MIT
    identifier: "Apache-2.0 OR MIT"
  contact:
    name: openkind maintainers
    url: https://github.com/whit3rabbit/openkind
externalDocs:
  description: TypeSafe AI Python SDK & Jev Protocol Documentation
  url: https://docs.typesafe.ai/sdk/python/api

servers:
  - url: http://127.0.0.1:18080
    description: Default local openkindd daemon

security:
  - {}
  - BearerAuth: []

paths:
  /v1/systemone:
    post:
      summary: Evaluate System 1 questions (canonical)
      description: |
        Primary decision-inference endpoint. Evaluates one or more typed questions
        (`noul`, `choice`, `score`) against a supplied `state` context.

        Corresponds directly to:
        - `client.system_one(state, questions, model=None, retry=None, timeout=None, extra_headers=None, extra_body=None, response_model=None)` in `TypeSafeClient`.
        - `await async_client.system_one(...)` in `AsyncTypeSafeClient`.

        ### Behavior & Features
        - **State-First Prefill**: The `state` document is evaluated as the context root.
        - **Question Fan-Out**: Multiple heterogeneous questions are evaluated against the same state context in a single call.
        - **Extra Body Tolerance**: Additional top-level keys supplied via Python SDK `extra_body` are accepted but ignored by OpenKind.
        - **Request Tracing**: Returns UUID in `x-typesafe-request-id` header mapped to `result.request_id` in Python SDK.
      operationId: evaluateSystemOne
      tags:
        - Evaluation
      parameters:
        - $ref: '#/components/parameters/InboundRequestId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SystemRequest'
            examples:
              quickstart_noul:
                summary: Minimal Noul (yes/no) question
                description: 'Python SDK `client.system_one(state="...", questions={"billing": Noul(...)})`'
                value:
                  state: "I was charged twice for order #1042."
                  model: "jev-latest"
                  questions:
                    billing:
                      type: "noul"
                      instructions: "Is this inquiry related to a billing issue?"
              multi_question_evaluation:
                summary: Heterogeneous multi-question evaluation
                description: Evaluates Noul, Choice, and Score questions simultaneously against support ticket state.
                value:
                  state: "Customer support transcript: Agent resolved issue in 4 minutes."
                  model: "jev-latest"
                  questions:
                    is_resolved:
                      type: "noul"
                      instructions: "Was the issue resolved?"
                      criteria:
                        true: "Customer issue was successfully resolved."
                        false: "Issue remains unresolved or escalated."
                    department:
                      type: "choice"
                      instructions: "Which team handled this request?"
                      criteria:
                        billing: "Payments, invoicing, refunds"
                        technical: "Bugs, outages, integrations"
                        sales: null
                    satisfaction:
                      type: "score"
                      instructions: "Rate customer satisfaction"
                      criteria:
                        - "Dissatisfied"
                        - "Neutral"
                        - "Delighted"
              structured_state_and_instructions:
                summary: Structured JSON state and structured instructions
                description: Structured object state and structured instructions referenced by question context.
                value:
                  state:
                    customer_id: "cust_9812"
                    tier: "enterprise"
                    message: "Can you guarantee 99.999% uptime for our dedicated instance?"
                    metadata:
                      region: "us-east-1"
                  model: "jev-latest"
                  questions:
                    sla_guarantee:
                      type: "noul"
                      instructions:
                        role: "compliance auditor"
                        policy_reference: "SLA Section 4.1"
                        question: "Does the customer message request five-nines uptime guarantee?"
                    risk_level:
                      type: "score"
                      instructions: "Evaluate contractual risk tier"
                      criteria:
                        - "Standard SLA terms"
                        - "Custom terms requiring legal review"
                        - "Unfulfillable guarantee"
              extra_body_metadata:
                summary: Top-level extra_body metadata tolerance
                description: Client tracking metadata shallow-merged via Python SDK `extra_body` is ignored by OpenKind.
                value:
                  state: "Deployment pipeline failed during container build step."
                  model: "jev-latest"
                  questions:
                    infra_issue:
                      type: "noul"
                      instructions: "Is this an infrastructure failure?"
                  trace_id: "trace-99210-abcdef"
                  client_metadata:
                    service: "ci-watcher"
                    environment: "production"
      responses:
        '200':
          description: Evaluation completed successfully.
          headers:
            x-typesafe-request-id:
              $ref: '#/components/headers/XTypesafeRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '429':
          $ref: '#/components/responses/RateLimited'
        '529':
          $ref: '#/components/responses/Overloaded'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/system_one:
    post:
      summary: Evaluate System 1 questions (SDK alias)
      description: |
        Direct alias for `/v1/systemone` providing exact path parity for clients calling `/v1/system_one`.
        All request parameters, headers, response schemas, and error mappings are identical to `/v1/systemone`.
      operationId: evaluateSystemOneAlias
      tags:
        - Evaluation
      parameters:
        - $ref: '#/components/parameters/InboundRequestId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SystemRequest'
      responses:
        '200':
          description: Evaluation completed successfully.
          headers:
            x-typesafe-request-id:
              $ref: '#/components/headers/XTypesafeRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/SystemResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/UnprocessableEntity'
        '429':
          $ref: '#/components/responses/RateLimited'
        '529':
          $ref: '#/components/responses/Overloaded'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /v1/models:
    get:
      summary: List registered model backends
      description: |
        Returns the catalog of registered decision engines and aliases sorted alphabetically by name.

        Corresponds directly to:
        - `client.models.list(retry=None, timeout=None, extra_headers=None)` in `TypeSafeClient`.
        - `await async_client.models.list(...)` in `AsyncTypeSafeClient`.

        Returns a `ListModelsResponse` containing a list of `ModelMetadata` objects, each with
        `name`, `description`, and `release_date`.
      operationId: listModels
      tags:
        - Models
      parameters:
        - $ref: '#/components/parameters/InboundRequestId'
      responses:
        '200':
          description: List of models returned successfully.
          headers:
            x-typesafe-request-id:
              $ref: '#/components/headers/XTypesafeRequestId'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListModelsResponse'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimited'
        '529':
          $ref: '#/components/responses/Overloaded'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /health:
    get:
      summary: Health check liveness probe
      description: |
        Always-open probe endpoint for container orchestrators and load balancers.
        Never gated by bearer authorization.
      operationId: healthCheck
      security: []
      tags:
        - System
      responses:
        '200':
          description: Server is healthy and accepting requests.
          headers:
            x-typesafe-request-id:
              $ref: '#/components/headers/XTypesafeRequestId'
          content:
            application/json:
              schema:
                type: object
                required:
                  - status
                properties:
                  status:
                    type: string
                    example: "ok"
        '400':
          $ref: '#/components/responses/BadRequest'
        '500':
          $ref: '#/components/responses/InternalServerError'

  /metrics:
    get:
      summary: Prometheus metrics endpoint
      description: |
        Prometheus text exposition format (version 0.0.4) metrics exporter.
        Always open; never gated by bearer authorization.
      operationId: getMetrics
      security: []
      tags:
        - System
      responses:
        '200':
          description: Prometheus text format metrics output.
          content:
            text/plain:
              schema:
                type: string
                example: |
                  # HELP openkind_requests_total Total evaluation requests
                  # TYPE openkind_requests_total counter
                  openkind_requests_total{model="mock",status="200"} 12
        '400':
          $ref: '#/components/responses/BadRequest'
        '500':
          $ref: '#/components/responses/InternalServerError'

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: Token
      description: |
        Opt-in bearer token authentication configured via `OPENKIND_API_KEY`
        or `TYPESAFE_API_KEY`. When disabled (default), `/v1/*` routes are open.
        When enabled, clients must supply the HTTP header:
        `Authorization: Bearer <API_KEY>`

  parameters:
    InboundRequestId:
      name: x-typesafe-request-id
      in: header
      required: false
      description: |
        Optional client-supplied UUIDv4 request identifier for distributed tracing and correlation.
        If provided and valid, the server preserves this exact ID in the response header.
        If omitted or malformed, the server generates a fresh UUIDv4.
      schema:
        type: string
        format: uuid

  headers:
    XTypesafeRequestId:
      description: |
        UUIDv4 uniquely identifying this evaluation request.
        Preserved on all responses (200, 4xx, 5xx) and mapped directly to
        `result.request_id` in Python SDK `SystemOneResponse` and exception `.request_id`.
      schema:
        type: string
        format: uuid
        example: "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
    RetryAfter:
      description: Suggested delay before retrying in integer seconds (emitted on 429 and 529).
      schema:
        type: integer
        example: 3
    RetryAfterMs:
      description: Suggested delay before retrying in integer milliseconds (emitted on 429 and 529).
      schema:
        type: integer
        example: 2500
    WWWAuthenticate:
      description: Authentication challenge header emitted on 401 Unauthorized.
      schema:
        type: string
        example: "Bearer"

  responses:
    BadRequest:
      description: |
        Malformed JSON syntax in request body (400 Bad Request).
        Maps to `TypeSafeBadRequestError` in Python SDK.
      headers:
        x-typesafe-request-id:
          $ref: '#/components/headers/XTypesafeRequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            error:
              code: "bad_json"
              message: "invalid JSON: expected value at line 1 column 1"

    Unauthorized:
      description: |
        Missing or invalid API key when authentication is enabled (401 Unauthorized).
        Maps to `TypeSafeAuthenticationError` in Python SDK.
      headers:
        x-typesafe-request-id:
          $ref: '#/components/headers/XTypesafeRequestId'
        WWW-Authenticate:
          $ref: '#/components/headers/WWWAuthenticate'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            error:
              code: "unauthorized"
              message: "missing or invalid API key"

    NotFound:
      description: |
        Requested model alias is not registered in the engine registry (404 Not Found).
        Maps to `TypeSafeNotFoundError` in Python SDK.
      headers:
        x-typesafe-request-id:
          $ref: '#/components/headers/XTypesafeRequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            error:
              code: "unknown_model"
              message: "unknown model: gpt-4"

    UnprocessableEntity:
      description: |
        Request body validation failed according to Jev schema rules (422 Unprocessable Entity).
        Maps to `TypeSafeUnprocessableEntityError` in Python SDK.
        Causes include empty questions map, score questions with fewer than 2 rubric levels,
        or malformed criteria specifications.
      headers:
        x-typesafe-request-id:
          $ref: '#/components/headers/XTypesafeRequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            error:
              code: "invalid_body"
              message: "at least one question is required"

    RateLimited:
      description: |
        Client exceeded configured rate limits (429 Too Many Requests).
        Maps to `TypeSafeRateLimitError` in Python SDK.
        Includes both `Retry-After` (seconds) and `retry-after-ms` (milliseconds) headers
        for automated client backoff.
      headers:
        x-typesafe-request-id:
          $ref: '#/components/headers/XTypesafeRequestId'
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
        retry-after-ms:
          $ref: '#/components/headers/RetryAfterMs'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            error:
              code: "rate_limited"
              message: "rate limited; retry after 2500 ms"

    Overloaded:
      description: |
        Engine or server is temporarily overloaded; client should back off and retry (529 Overloaded).
        Maps to `TypeSafeOverloadedError` in Python SDK.
        Includes both `Retry-After` (seconds) and `retry-after-ms` (milliseconds) headers
        automatically consumed by `RetryPolicy`.
      headers:
        x-typesafe-request-id:
          $ref: '#/components/headers/XTypesafeRequestId'
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
        retry-after-ms:
          $ref: '#/components/headers/RetryAfterMs'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            error:
              code: "overloaded"
              message: "server overloaded; retry after 1500 ms"

    InternalServerError:
      description: |
        Backend or runtime execution failure (500 Internal Server Error).
        Maps to `TypeSafeInternalServerError` in Python SDK.
      headers:
        x-typesafe-request-id:
          $ref: '#/components/headers/XTypesafeRequestId'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            error:
              code: "internal_error"
              message: "engine failed to complete evaluation"

  schemas:
    SystemRequest:
      type: object
      description: |
        Evaluation request payload representing OpenKind's System One judgment query.
        Matches the common subset of Python SDK `client.system_one(state, questions, model=...)`.
        Top-level `extra_body` fields passed by the Python SDK are accepted and ignored.
      additionalProperties: true
      required:
        - state
        - model
        - questions
      properties:
        state:
          $ref: '#/components/schemas/State'
        model:
          type: string
          description: |
            Identifier of the target backend engine (e.g. `"jev-latest"`, `"mock"`).
            Required on the HTTP wire. SDKs can supply their configured default before sending.
          example: "jev-latest"
        questions:
          type: object
          minProperties: 1
          maxProperties: 10000
          description: |
            Map of client-chosen question identifiers to question specifications (`Noul`, `Choice`, or `Score`).
            Keys are preserved on return in `SystemResponse.answers`.
            Keys are not sent to the underlying model and do not affect inference.
          additionalProperties:
            $ref: '#/components/schemas/Question'

    State:
      description: |
        The content or document to evaluate (`JSONContent` in Python SDK).
        Polymorphic: plain string, structured JSON object, or list/array of items.
        Cannot be `null` at root level, but nested object and array fields may contain `null`.
      oneOf:
        - type: string
          description: Plain text content.
          example: "I was charged twice for order #1042."
        - type: object
          description: Structured JSON object (e.g. support ticket, database record, event payload).
          additionalProperties: true
          example:
            ticket_id: 1042
            user: "alice@example.com"
            amount: 49.99
            tags: ["billing", "duplicate_charge"]
        - type: array
          description: Ordered list of messages, chat turns, or records.
          items: true
          example:
            - role: "user"
              content: "Help! My card was charged twice."
            - role: "assistant"
              content: "I can look into that billing issue for you."

    Instructions:
      description: |
        Instructions specifying what judgment or rating is requested (`JSONContent` in Python SDK).
        Portable values are a non-empty string, object, or array. Null and empty
        strings, objects, and arrays fail OpenKind request validation.
      oneOf:
        - type: string
          minLength: 1
          description: Plain text instruction or question prompt.
          example: "Is this inquiry related to a billing issue?"
        - type: object
          minProperties: 1
          description: Structured instructions object containing question and reference data.
          additionalProperties: true
          example:
            role: "compliance reviewer"
            criteria_reference: "Policy Section 2.4"
            question: "Does the document fulfill mandatory compliance obligations?"
        - type: array
          minItems: 1
          description: Sequence of instruction items or checklist items.
          items: true
          example:
            - "Check SLA deadline"
            - "Verify customer account standing"

    Question:
      description: |
        Tagged union of question definitions discriminated by `type`.
        Corresponds to `typesafe_sdk.Noul`, `typesafe_sdk.Choice`, and `typesafe_sdk.Score`.
      discriminator:
        propertyName: type
        mapping:
          noul: '#/components/schemas/NoulQuestion'
          choice: '#/components/schemas/ChoiceQuestion'
          score: '#/components/schemas/ScoreQuestion'
      oneOf:
        - $ref: '#/components/schemas/NoulQuestion'
        - $ref: '#/components/schemas/ChoiceQuestion'
        - $ref: '#/components/schemas/ScoreQuestion'

    NoulQuestion:
      type: object
      description: |
        A yes/no probability question. Evaluates likelihood that the statement or condition is true.
        Corresponds to `typesafe_sdk.Noul(instructions=..., criteria=...)` in Python SDK.
      required:
        - type
        - instructions
      properties:
        type:
          type: string
          enum: [noul]
          description: Discriminant for Noul question type.
        instructions:
          $ref: '#/components/schemas/Instructions'
        criteria:
          anyOf:
            - $ref: '#/components/schemas/NoulCriteria'
            - type: 'null'

    NoulCriteria:
      type: object
      description: |
        Optional descriptions of what yes (`true`) and no (`false`) represent in the evaluation.
        OpenKind requires both keys when criteria is present. TypeSafe and Cloudflare
        permit partial and structured criteria, which OpenKind does not yet accept.
        Corresponds to `typesafe_sdk.NoulCriteria` in Python SDK.
      required:
        - "true"
        - "false"
      properties:
        "true":
          type: string
          minLength: 1
          description: Explicit rubric description of what true/yes represents.
          example: "Explicitly time-sensitive and requires urgent action"
        "false":
          type: string
          minLength: 1
          description: Explicit rubric description of what false/no represents.
          example: "Standard inquiry without urgency or deadline"

    ChoiceQuestion:
      type: object
      description: |
        Selects one option from a defined set of categorical alternatives.
        Corresponds to `typesafe_sdk.Choice(instructions=..., criteria=...)` in Python SDK.
        Returns the most probable option, the full probability distribution, and a confidence score.
      required:
        - type
        - instructions
        - criteria
      properties:
        type:
          type: string
          enum: [choice]
          description: Discriminant for Choice question type.
        instructions:
          $ref: '#/components/schemas/Instructions'
        criteria:
          type: object
          description: |
            Map of option identifiers to rubric descriptions.
            Values may be `null` when an option needs no additional description.
            OpenKind accepts 1 to 10000 options. TypeSafe's prose documentation
            states a maximum of 255; that provider limit is not enforced here.
          minProperties: 1
          maxProperties: 10000
          additionalProperties:
            type:
              - string
              - "null"
          example:
            billing: "Payments, invoicing, refunds"
            technical: "Bugs, outages, integrations"
            sales: null

    ScoreQuestion:
      type: object
      description: |
        Continuous rating evaluation along an ordered rubric of discrete levels.
        Corresponds to `typesafe_sdk.Score(instructions=..., criteria=...)` in Python SDK.
        Returns the expected continuous score value, a legend mapping level indices to descriptions,
        the probability distribution, and a confidence score.
      required:
        - type
        - instructions
        - criteria
      properties:
        type:
          type: string
          enum: [score]
          description: Discriminant for Score question type.
        instructions:
          $ref: '#/components/schemas/Instructions'
        criteria:
          type: array
          minItems: 2
          maxItems: 10000
          description: |
            Ordered array of rubric level descriptions (OpenKind validates 2 to 10000 levels).
            TypeSafe's prose documentation gives a maximum of 10 levels; Cloudflare's
            schema requires at least 2, while TypeSafe's live OpenAPI requires at least 1.
            Levels are implicitly indexed from 0 to N-1.
          items:
            type: string
            minLength: 1
          example:
            - "Dissatisfied"
            - "Neutral"
            - "Delighted"

    SystemResponse:
      type: object
      description: |
        Response body containing evaluated answers for all submitted questions.
        Matches `typesafe_sdk.SystemOneResponse` in Python SDK, accessible via `result.answers`,
        `result.nouls`, `result.choices`, and `result.scores`.
      required:
        - model
        - answers
        - usage
      properties:
        model:
          type: string
          description: Identifier of the model backend that performed the evaluation.
          example: "jev-1.13.0"
        answers:
          type: object
          minProperties: 1
          maxProperties: 10000
          description: |
            Map of question identifiers to typed evaluation answers (`NoulAnswer`, `ChoiceAnswer`, `ScoreAnswer`).
            Keys match the question keys supplied in `SystemRequest.questions`.
          additionalProperties:
            $ref: '#/components/schemas/Answer'
        usage:
          $ref: '#/components/schemas/Usage'

    Answer:
      description: |
        Tagged union of evaluation answers discriminated by `type`.
        Matches `typesafe_sdk.Answer` in Python SDK.
      discriminator:
        propertyName: type
        mapping:
          noul: '#/components/schemas/NoulAnswer'
          choice: '#/components/schemas/ChoiceAnswer'
          score: '#/components/schemas/ScoreAnswer'
      oneOf:
        - $ref: '#/components/schemas/NoulAnswer'
        - $ref: '#/components/schemas/ChoiceAnswer'
        - $ref: '#/components/schemas/ScoreAnswer'

    NoulAnswer:
      type: object
      description: |
        Answer payload for a `noul` (yes/no) question.
        Returns calibrated probability in `[0.0, 1.0]`.

        **CRITICAL SPECIFICATION INVARIANT**:
        Noul answers do NOT have a `confidence` field. Confidence is defined only for
        Choice and Score questions.
      required:
        - type
        - noul
      properties:
        type:
          type: string
          enum: [noul]
          description: Discriminant for Noul answer type.
        noul:
          type: number
          format: double
          minimum: 0.0
          maximum: 1.0
          description: |
            Calibrated probability that the answer is true/yes (64-bit float wire precision).
            Values close to 1.0 indicate high likelihood of true; values close to 0.0 indicate false.
          example: 0.95

    ChoiceAnswer:
      type: object
      description: |
        Answer payload for a `choice` question.
        Contains the winning option identifier, the full normalized probability distribution,
        and the model's confidence score.
      required:
        - type
        - choice
        - probabilities
        - confidence
      properties:
        type:
          type: string
          enum: [choice]
          description: Discriminant for Choice answer type.
        choice:
          type: string
          description: Identifier of the highest-probability selected option.
          example: "billing"
        probabilities:
          type: object
          description: |
            Normalized probability distribution over all criteria option keys summing to 1.0.
            Wire precision: 64-bit float (`double`).
          additionalProperties:
            type: number
            format: double
            minimum: 0.0
            maximum: 1.0
          example:
            billing: 0.88
            technical: 0.12
            sales: 0.0
        confidence:
          type: number
          format: double
          minimum: 0.0
          maximum: 1.0
          description: |
            Model certainty in the chosen option derived from the probability distribution.
            Values close to 1.0 indicate high confidence; values close to 0.0 indicate ambiguity.
          example: 0.81

    ScoreAnswer:
      type: object
      description: |
        Answer payload for a `score` question.
        Contains the continuous rating score, a legend mapping string indices to descriptions,
        the probability distribution across rubric levels, and the model's confidence score.
      required:
        - type
        - score
        - legend
        - probabilities
        - confidence
      properties:
        type:
          type: string
          enum: [score]
          description: Discriminant for Score answer type.
        score:
          type: number
          format: double
          description: |
            Expected continuous score value along the ordered rubric, equal to:
            `sum(level_index * probability[level_index])`.
            Can land between discrete rubric levels.
          example: 1.05
        legend:
          type: object
          description: |
            Mapping of numeric level index strings (`"0"`, `"1"`, ...) back to rubric level descriptions.
          additionalProperties:
            type: string
          example:
            "0": "Dissatisfied"
            "1": "Neutral"
            "2": "Delighted"
        probabilities:
          type: object
          description: |
            Normalized probability distribution over rubric level index strings summing to 1.0.
          additionalProperties:
            type: number
            format: double
            minimum: 0.0
            maximum: 1.0
          example:
            "0": 0.0
            "1": 0.95
            "2": 0.05
        confidence:
          type: number
          format: double
          minimum: 0.0
          maximum: 1.0
          description: |
            Model confidence in the evaluated score distribution.
          example: 0.92

    Usage:
      type: object
      description: |
        Token consumption accounting for the evaluation request.
        Corresponds to `typesafe_sdk.UsageInfo` in Python SDK.
      required:
        - input_tokens
        - output_tokens
      properties:
        input_tokens:
          type: integer
          minimum: 0
          maximum: 4294967295
          description: Number of tokens consumed by state and question instructions.
          example: 296
        output_tokens:
          type: integer
          minimum: 0
          maximum: 4294967295
          description: Number of tokens accounted to producing the evaluated decisions.
          example: 20

    ListModelsResponse:
      type: object
      description: |
        Catalog response listing all registered decision engines and aliases.
        Corresponds to `typesafe_sdk.ListModelsResponse` in Python SDK.
      required:
        - models
      properties:
        models:
          type: array
          description: List of available model metadata objects sorted alphabetically by name.
          items:
            $ref: '#/components/schemas/ModelMetadata'

    ModelMetadata:
      type: object
      description: |
        Metadata describing a registered model backend or alias.
        Corresponds to `typesafe_sdk.ModelMetadata` in Python SDK.
      required:
        - name
        - description
        - release_date
      properties:
        name:
          type: string
          description: Model identifier or alias (e.g. `"jev-latest"`, `"mock"`).
          example: "jev-latest"
        description:
          type: string
          description: Human-readable summary of model architecture and capabilities.
          example: "Default production System 1 model"
        release_date:
          type: string
          description: Release timestamp or ISO date string.
          example: "2026-01-15"

    ErrorEnvelope:
      type: object
      description: |
        Standard JSON error envelope returned on all 4xx and 5xx HTTP responses.
        Matches error payload parsed by Python SDK exceptions into `.code` and `.message`.
      required:
        - error
      properties:
        error:
          $ref: '#/components/schemas/ErrorDetails'

    ErrorDetails:
      type: object
      description: Machine-readable error code and human-readable explanation.
      required:
        - code
        - message
      properties:
        code:
          type: string
          description: |
            Machine-readable error identifier string:
            - `bad_json`: Malformed JSON in request body (400)
            - `unauthorized`: Missing or invalid API key (401)
            - `unknown_model`: Model name not registered in catalog (404)
            - `invalid_body`: Request payload validation failure (422)
            - `rate_limited`: Rate limit budget exceeded (429)
            - `overloaded`: System overloaded (529)
            - `internal_error`: Unexpected runtime or engine failure (500)
            - `payload_too_large`: Request body exceeds the configured limit (413)
            - `bad_gateway`: Upstream forwarding failed (502)
            - `deadline_exceeded`: Backend evaluation deadline elapsed (504)
            - `backend_error`: Backend execution or response validation failed (500)
          example: "invalid_body"
        message:
          type: string
          description: Human-readable error description explaining the cause of failure.
          example: "at least one question is required"