@konneal/engine 0.1.4 → 0.2.1

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.
Files changed (58) hide show
  1. package/dist/admin.d.ts +1 -0
  2. package/dist/ask-47RNGK2R.js +12 -0
  3. package/dist/chunk-3OXSQH7Y.js +1852 -0
  4. package/dist/chunk-6GOSMLRH.js +2781 -0
  5. package/dist/{chunk-EHJEELVB.js → chunk-LNSDBEKS.js} +1 -1
  6. package/dist/{chunk-35ODH64W.js → chunk-Q327B27J.js} +33 -0
  7. package/dist/{chunk-OCNLV7Q7.js → chunk-Q6LI4T7M.js} +6 -1
  8. package/dist/{chunk-ROF3Q7UC.js → chunk-SN3ANQ3Y.js} +2 -2
  9. package/dist/chunk-VJZLVU3S.js +64 -0
  10. package/dist/{chunk-CAEHIVG5.js → chunk-WGXATDXY.js} +1 -1
  11. package/dist/codecs.d.ts +3 -3
  12. package/dist/completion.d.ts +1 -1
  13. package/dist/config.d.ts +9 -0
  14. package/dist/faithfulness.d.ts +1 -0
  15. package/dist/mcp-proto.d.ts +25 -0
  16. package/dist/mcp.d.ts +4 -0
  17. package/dist/openapi-surface.gen.d.ts +9 -0
  18. package/dist/openapi-types.d.ts +2141 -0
  19. package/dist/profile.gen.d.ts +1 -0
  20. package/dist/prompts/system.md +1 -0
  21. package/dist/quota.d.ts +4 -1
  22. package/dist/search-OMPBMZT4.js +11 -0
  23. package/dist/tablecontext.d.ts +7 -0
  24. package/dist/verdict-parse.d.ts +5 -0
  25. package/dist/worker_mcp/src/index.js +3 -3
  26. package/dist/worker_public/src/config.js +4 -2
  27. package/dist/worker_public/src/index.js +1193 -4932
  28. package/dist/worker_public/src/profile.js +1 -1
  29. package/dist/worker_public/src/refusal.js +2 -2
  30. package/dist/worker_public/src/requestScope.js +3 -3
  31. package/docs/spec-api.md +27 -13
  32. package/package.json +12 -3
  33. package/profile/prompts.yaml +3 -0
  34. package/workers/shared/router.ts +23 -16
  35. package/workers/worker_public/migrations/0014_usage_cache.sql +5 -0
  36. package/workers/worker_public/openapi.yaml +1169 -0
  37. package/workers/worker_public/prompts/system.md +1 -0
  38. package/workers/worker_public/schema.sql +3 -1
  39. package/workers/worker_public/src/admin.ts +51 -7
  40. package/workers/worker_public/src/ai.ts +10 -2
  41. package/workers/worker_public/src/ask.ts +94 -22
  42. package/workers/worker_public/src/codecs.ts +35 -10
  43. package/workers/worker_public/src/completion.ts +24 -1
  44. package/workers/worker_public/src/config.ts +10 -0
  45. package/workers/worker_public/src/faithfulness.ts +10 -17
  46. package/workers/worker_public/src/grader.ts +2 -2
  47. package/workers/worker_public/src/index.ts +106 -58
  48. package/workers/worker_public/src/lib/router.ts +1 -1
  49. package/workers/worker_public/src/mcp-proto.ts +71 -0
  50. package/workers/worker_public/src/mcp.ts +47 -0
  51. package/workers/worker_public/src/openapi-surface.gen.ts +318 -0
  52. package/workers/worker_public/src/pipeline.ts +4 -2
  53. package/workers/worker_public/src/profile.gen.ts +1 -0
  54. package/workers/worker_public/src/projects.ts +4 -2
  55. package/workers/worker_public/src/quota.ts +4 -2
  56. package/workers/worker_public/src/research.ts +55 -3
  57. package/workers/worker_public/src/tablecontext.ts +13 -2
  58. package/workers/worker_public/src/verdict-parse.ts +60 -0
@@ -0,0 +1,1169 @@
1
+ openapi: 3.1.0
2
+ info:
3
+ title: OIML SMART AI API
4
+ version: 1.0.0
5
+ description: >-
6
+ The HTTP API of the question-answering service over OIML publications.
7
+ The service answers questions with clause-level citations over a corpus
8
+ of OIML Recommendations, Documents, Basic publications, Guides and
9
+ Expert reports, together with the machine-readable model derived from
10
+ the publications. The same pipeline serves the chat interface at
11
+ https://ai.oimlsmart.org. Anonymous callers may use the /api endpoints
12
+ with a daily allowance of 20 questions per IP address; machine callers
13
+ authenticate to the /v1 endpoints with an API key issued by an
14
+ administrator; signed-in members obtain a higher allowance together
15
+ with conversation synchronisation, project memory and research mode.
16
+ servers:
17
+ - url: https://ai.oimlsmart.org
18
+ description: Production
19
+ tags:
20
+ - name: Ask
21
+ description: Ask a question and receive a citation-grounded answer.
22
+ - name: Search
23
+ description: Retrieve ranked clauses without generating an answer.
24
+ - name: Evidence
25
+ description: Provable absence certificates and answer verification.
26
+ - name: Research
27
+ description: Bounded iterative research over the corpus.
28
+ - name: Conversations
29
+ description: >-
30
+ The signed-in member's conversations: list, create, read, rename,
31
+ delete, append messages and create shareable permalinks. These
32
+ endpoints authenticate with the session cookie.
33
+ - name: Memory
34
+ description: >-
35
+ The signed-in member's personal memory notes, which the assistant
36
+ consults in later conversations. At most ten notes of eight
37
+ thousand characters each are kept.
38
+ - name: Projects
39
+ description: >-
40
+ The signed-in member's contexts: named groups of conversations
41
+ with their attached files. A conversation may be filed into a
42
+ context, and the retrieval is then scoped by the context's files.
43
+ - name: MCP
44
+ description: The Model Context Protocol server for agent ecosystems.
45
+ - name: Service
46
+ description: Datasets, health and authentication.
47
+ - name: Experimental
48
+ description: >-
49
+ The comparison-lane query endpoint, which retrieves from one named
50
+ experimental corpus. These endpoints exist to support the
51
+ measurement work and carry no stability promise.
52
+ - name: Administration
53
+ description: >-
54
+ Operational endpoints guarded by the administrator token, covering
55
+ service statistics, the issuance and management of API keys, and
56
+ the index-maintenance battery (enrichment, section units, vectors
57
+ and figure captions).
58
+ paths:
59
+ /api/ask:
60
+ post:
61
+ tags: [Ask]
62
+ summary: Ask a question anonymously
63
+ description: >-
64
+ Asks a question over the OIML corpus and receives an answer with
65
+ clause-level citations. The anonymous allowance is 20 questions per
66
+ day per IP address. The request body and the response are identical
67
+ to /v1/ask.
68
+ operationId: askAnonymous
69
+ requestBody:
70
+ required: true
71
+ content:
72
+ application/json:
73
+ schema:
74
+ $ref: '#/components/schemas/AskRequest'
75
+ examples:
76
+ definition:
77
+ value:
78
+ query: What is a load cell according to OIML R 60?
79
+ stream: false
80
+ verdict:
81
+ value:
82
+ query: >-
83
+ For OIML R 60, /constraint/dead_load_max_geometry:
84
+ with E_max 30000 v, is testing to D_max 26000 v valid?
85
+ stream: false
86
+ responses:
87
+ '200':
88
+ description: The answer with citations and typed blocks.
89
+ content:
90
+ application/json:
91
+ schema:
92
+ $ref: '#/components/schemas/AskResponse'
93
+ '429':
94
+ description: The daily allowance is exhausted.
95
+ '400':
96
+ description: The query is missing or exceeds 8000 characters.
97
+ /v1/ask:
98
+ post:
99
+ tags: [Ask]
100
+ summary: Ask a question with an API key
101
+ description: >-
102
+ Identical to /api/ask, with authentication by API key and a
103
+ per-key daily allowance. Streaming responses are available by
104
+ setting stream to true, which returns a server-sent event stream
105
+ whose events carry citations, tokens and the completion marker.
106
+ operationId: askKeyed
107
+ security:
108
+ - bearerAuth: []
109
+ requestBody:
110
+ required: true
111
+ content:
112
+ application/json:
113
+ schema:
114
+ $ref: '#/components/schemas/AskRequest'
115
+ responses:
116
+ '200':
117
+ description: The answer with citations and typed blocks.
118
+ content:
119
+ application/json:
120
+ schema:
121
+ $ref: '#/components/schemas/AskResponse'
122
+ /api/search:
123
+ post:
124
+ tags: [Search]
125
+ summary: Search ranked clauses
126
+ description: >-
127
+ Retrieves the ranked clauses for a query without generating an
128
+ answer. Each hit carries its document identifier, edition, clause
129
+ anchor, status and a link into the rendered document.
130
+ operationId: search
131
+ requestBody:
132
+ required: true
133
+ content:
134
+ application/json:
135
+ schema:
136
+ type: object
137
+ required: [query]
138
+ properties:
139
+ query:
140
+ type: string
141
+ description: The search text, at most 8000 characters.
142
+ k:
143
+ type: integer
144
+ default: 5
145
+ description: The number of hits to return.
146
+ responses:
147
+ '200':
148
+ description: The ranked hits.
149
+ /v1/search:
150
+ post:
151
+ tags: [Search]
152
+ summary: Search ranked clauses with an API key
153
+ description: >-
154
+ Identical to /api/search, with authentication by API key and a
155
+ per-key daily allowance.
156
+ operationId: searchKeyed
157
+ security:
158
+ - bearerAuth: []
159
+ requestBody:
160
+ required: true
161
+ content:
162
+ application/json:
163
+ schema:
164
+ type: object
165
+ required: [query]
166
+ properties:
167
+ query: { type: string }
168
+ k: { type: integer, default: 5 }
169
+ responses:
170
+ '200':
171
+ description: The ranked hits.
172
+ /api/absence:
173
+ post:
174
+ tags: [Evidence]
175
+ summary: Request a provable absence certificate
176
+ description: >-
177
+ Enumerates the entire machine-readable model of the named standard and
178
+ reports how many nodes were examined and whether any matched the
179
+ topic. A verdict of absent is an enumeration certificate that
180
+ states its scope, not a statement that a search found nothing.
181
+ operationId: absence
182
+ requestBody:
183
+ required: true
184
+ content:
185
+ application/json:
186
+ schema:
187
+ type: object
188
+ required: [standard, topic]
189
+ properties:
190
+ standard:
191
+ type: string
192
+ description: The publication, for example R 60.
193
+ topic:
194
+ type: string
195
+ description: The topic whose absence is to be proven.
196
+ examples:
197
+ packaging:
198
+ value:
199
+ standard: R 60
200
+ topic: packaging
201
+ responses:
202
+ '200':
203
+ description: The enumeration certificate.
204
+ /v1/absence:
205
+ post:
206
+ tags: [Evidence]
207
+ summary: Request a provable absence certificate with an API key
208
+ operationId: absenceKeyed
209
+ security:
210
+ - bearerAuth: []
211
+ requestBody:
212
+ required: true
213
+ content:
214
+ application/json:
215
+ schema:
216
+ type: object
217
+ required: [standard, topic]
218
+ properties:
219
+ standard: { type: string }
220
+ topic: { type: string }
221
+ responses:
222
+ '200':
223
+ description: The enumeration certificate.
224
+ /api/verify:
225
+ post:
226
+ tags: [Evidence]
227
+ summary: Verify an answer
228
+ description: >-
229
+ Applies the deterministic verification battery to any answer,
230
+ whether generated by this service or by another system: every
231
+ quoted span must be contained verbatim in a passage retrieved for
232
+ the query, and every object reference must resolve to a served
233
+ object. A judged faithfulness score is reported separately and is
234
+ labelled as judged.
235
+ operationId: verify
236
+ requestBody:
237
+ required: true
238
+ content:
239
+ application/json:
240
+ schema:
241
+ type: object
242
+ required: [query, answer]
243
+ properties:
244
+ query:
245
+ type: string
246
+ description: The question the answer claims to answer.
247
+ answer:
248
+ type: string
249
+ description: The answer to verify.
250
+ responses:
251
+ '200':
252
+ description: The verification results.
253
+ /v1/verify:
254
+ post:
255
+ tags: [Evidence]
256
+ summary: Verify an answer with an API key
257
+ operationId: verifyKeyed
258
+ security:
259
+ - bearerAuth: []
260
+ requestBody:
261
+ required: true
262
+ content:
263
+ application/json:
264
+ schema:
265
+ type: object
266
+ required: [query, answer]
267
+ properties:
268
+ query: { type: string }
269
+ answer: { type: string }
270
+ responses:
271
+ '200':
272
+ description: The verification results.
273
+ /api/research:
274
+ post:
275
+ tags: [Research]
276
+ summary: Run a bounded research loop with the session cookie
277
+ description: >-
278
+ Identical to /v1/research, with authentication by the signed-in
279
+ member's session cookie; research spend stays with signed-in
280
+ members.
281
+ operationId: research
282
+ requestBody:
283
+ required: true
284
+ content:
285
+ application/json:
286
+ schema:
287
+ type: object
288
+ required: [query]
289
+ properties:
290
+ query:
291
+ type: string
292
+ description: The research question.
293
+ max_iterations:
294
+ type: integer
295
+ minimum: 1
296
+ maximum: 3
297
+ description: The iteration bound, at most 3.
298
+ responses:
299
+ '200':
300
+ description: >-
301
+ The research result, carrying the answer, the citations, the
302
+ typed blocks and the research metadata.
303
+ /v1/research:
304
+ post:
305
+ tags: [Research]
306
+ summary: Run a bounded research loop
307
+ description: >-
308
+ Runs a bounded iterative loop over the corpus. In each pass the
309
+ service retrieves evidence, and an independent judge evaluates
310
+ whether the evidence is sufficient and names what is missing; the
311
+ next pass retrieves specifically for the missing parts, and the
312
+ final answer is generated from the accumulated evidence. The
313
+ response carries the answer, citations, typed blocks and the
314
+ research metadata, including iterations, passages and elapsed
315
+ time.
316
+ operationId: researchKeyed
317
+ security:
318
+ - bearerAuth: []
319
+ requestBody:
320
+ required: true
321
+ content:
322
+ application/json:
323
+ schema:
324
+ type: object
325
+ required: [query]
326
+ properties:
327
+ query:
328
+ type: string
329
+ description: The research question.
330
+ max_iterations:
331
+ type: integer
332
+ minimum: 1
333
+ maximum: 3
334
+ description: The iteration bound, at most 3.
335
+ responses:
336
+ '200':
337
+ description: >-
338
+ The research result, carrying the answer, the citations, the
339
+ typed blocks and the research metadata, which includes the
340
+ iteration count, the passages and the elapsed time.
341
+ /mcp:
342
+ post:
343
+ tags: [MCP]
344
+ summary: The Model Context Protocol server
345
+ description: >-
346
+ Serves the Model Context Protocol over streamable HTTP for agent
347
+ ecosystems, behind the same authentication as the /v1 endpoints.
348
+ The protocol follows JSON-RPC 2.0 with the 2025-06-18 revision.
349
+ The tools are ask, which asks a question and returns the answer
350
+ with its citations, and retrieve, which returns ranked passages.
351
+ operationId: mcp
352
+ security:
353
+ - bearerAuth: []
354
+ requestBody:
355
+ required: true
356
+ content:
357
+ application/json:
358
+ schema:
359
+ type: object
360
+ required: [jsonrpc, method]
361
+ properties:
362
+ jsonrpc: { type: string, enum: [2.0] }
363
+ id: { description: The request identifier. }
364
+ method:
365
+ type: string
366
+ enum: [initialize, ping, notifications/initialized, tools/list, tools/call]
367
+ params:
368
+ type: object
369
+ description: The method parameters; tools/call carries name and arguments.
370
+ examples:
371
+ toolsList:
372
+ value:
373
+ jsonrpc: '2.0'
374
+ id: 1
375
+ method: tools/list
376
+ responses:
377
+ '200':
378
+ description: The JSON-RPC response.
379
+ '202':
380
+ description: The notification was accepted without a payload.
381
+ /api/datasets:
382
+ get:
383
+ tags: [Service]
384
+ summary: List the datasets
385
+ description: >-
386
+ Lists the available corpora with their access status. The OIML
387
+ publications and the OIML SMART models are open to everyone; the
388
+ ISO/IEC corpus requires a role that is managed in the identity
389
+ service.
390
+ operationId: datasets
391
+ responses:
392
+ '200':
393
+ description: The dataset list.
394
+ /v1/usage:
395
+ get:
396
+ tags: [Service]
397
+ summary: The presenting key's own usage
398
+ description: >-
399
+ Reports the key's own daily allowance consumption together with
400
+ its seven-day request ledger. The identity is the presented
401
+ credential itself, so a key can never read another key's usage.
402
+ operationId: keyUsage
403
+ security:
404
+ - bearerAuth: []
405
+ responses:
406
+ '200':
407
+ description: The key's allowance and usage.
408
+ '401':
409
+ description: No valid key was presented.
410
+ /health:
411
+ get:
412
+ tags: [Service]
413
+ summary: Service health
414
+ description: Reports service identity and index version.
415
+ operationId: health
416
+ responses:
417
+ '200':
418
+ description: The health document.
419
+ /api/conversations:
420
+ get:
421
+ tags: [Conversations]
422
+ summary: List the member's conversations
423
+ operationId: listConversations
424
+ responses:
425
+ '200':
426
+ description: The conversation list with each conversation's context grouping.
427
+ post:
428
+ tags: [Conversations]
429
+ summary: Create a conversation
430
+ operationId: createConversation
431
+ requestBody:
432
+ required: true
433
+ content:
434
+ application/json:
435
+ schema:
436
+ type: object
437
+ properties:
438
+ title:
439
+ type: string
440
+ description: The conversation's title, at most 120 characters.
441
+ responses:
442
+ '200':
443
+ description: The created conversation, with its identifier.
444
+ /api/conversations/{id}:
445
+ get:
446
+ tags: [Conversations]
447
+ summary: Read a conversation with its messages
448
+ operationId: getConversation
449
+ parameters:
450
+ - name: id
451
+ in: path
452
+ required: true
453
+ schema: { type: string }
454
+ responses:
455
+ '200':
456
+ description: The conversation with its messages.
457
+ patch:
458
+ tags: [Conversations]
459
+ summary: Rename a conversation
460
+ operationId: renameConversation
461
+ parameters:
462
+ - name: id
463
+ in: path
464
+ required: true
465
+ schema: { type: string }
466
+ requestBody:
467
+ required: true
468
+ content:
469
+ application/json:
470
+ schema:
471
+ type: object
472
+ required: [title]
473
+ properties:
474
+ title:
475
+ type: string
476
+ description: The new title, between 1 and 120 characters.
477
+ responses:
478
+ '200':
479
+ description: The updated conversation.
480
+ delete:
481
+ tags: [Conversations]
482
+ summary: Delete a conversation
483
+ operationId: deleteConversation
484
+ parameters:
485
+ - name: id
486
+ in: path
487
+ required: true
488
+ schema: { type: string }
489
+ responses:
490
+ '200':
491
+ description: The conversation is deleted.
492
+ /api/conversations/{id}/messages:
493
+ post:
494
+ tags: [Conversations]
495
+ summary: Append a message to a conversation
496
+ operationId: appendMessage
497
+ parameters:
498
+ - name: id
499
+ in: path
500
+ required: true
501
+ schema: { type: string }
502
+ requestBody:
503
+ required: true
504
+ content:
505
+ application/json:
506
+ schema:
507
+ type: object
508
+ required: [role, content]
509
+ properties:
510
+ role: { type: string, enum: [user, assistant] }
511
+ content: { type: string }
512
+ responses:
513
+ '200':
514
+ description: The appended message.
515
+ /api/conversations/{id}/share:
516
+ post:
517
+ tags: [Conversations]
518
+ summary: Create a public permalink for a conversation
519
+ operationId: shareConversation
520
+ parameters:
521
+ - name: id
522
+ in: path
523
+ required: true
524
+ schema: { type: string }
525
+ responses:
526
+ '200':
527
+ description: The permalink slug.
528
+ /api/shared/{slug}:
529
+ get:
530
+ tags: [Conversations]
531
+ summary: Read a shared conversation by its permalink
532
+ operationId: getShared
533
+ parameters:
534
+ - name: slug
535
+ in: path
536
+ required: true
537
+ schema: { type: string }
538
+ responses:
539
+ '200':
540
+ description: The shared conversation, without member identity.
541
+ /api/memories:
542
+ get:
543
+ tags: [Memory]
544
+ summary: List the member's memory notes
545
+ operationId: listMemories
546
+ responses:
547
+ '200':
548
+ description: The memory notes.
549
+ post:
550
+ tags: [Memory]
551
+ summary: Create a memory note, or replace one by supplying its identifier
552
+ description: >-
553
+ Creates a note from a name and a content of at most 8000
554
+ characters. When the body carries the identifier of an existing
555
+ note, that note is replaced instead and no new note is created.
556
+ At most ten notes are kept per member.
557
+ operationId: createMemory
558
+ requestBody:
559
+ required: true
560
+ content:
561
+ application/json:
562
+ schema:
563
+ type: object
564
+ required: [name, content]
565
+ properties:
566
+ name: { type: string, description: The note's name, at most 64 characters. }
567
+ content: { type: string, description: The note's text, at most 8000 characters. }
568
+ id:
569
+ type: string
570
+ description: The identifier of the note to replace; when absent, a new note is created.
571
+ responses:
572
+ '200':
573
+ description: The created or replaced note.
574
+ /api/memories/{id}:
575
+ delete:
576
+ tags: [Memory]
577
+ summary: Delete a memory note
578
+ operationId: deleteMemory
579
+ parameters:
580
+ - name: id
581
+ in: path
582
+ required: true
583
+ schema: { type: string }
584
+ responses:
585
+ '200':
586
+ description: The note is deleted.
587
+ /api/projects:
588
+ get:
589
+ tags: [Projects]
590
+ summary: List the member's contexts
591
+ operationId: listProjects
592
+ responses:
593
+ '200':
594
+ description: The contexts with their conversation counts and file counts.
595
+ post:
596
+ tags: [Projects]
597
+ summary: Create a context, rename one, or file a conversation into one
598
+ description: >-
599
+ The body selects the behaviour: a name alone creates a context; an
600
+ identifier together with a name renames that context; a conversation
601
+ identifier together with a context identifier (or null to unfile)
602
+ moves the conversation into or out of the context. Filing a
603
+ conversation into a context scopes its later retrieval to the
604
+ context's files.
605
+ operationId: createProject
606
+ requestBody:
607
+ required: true
608
+ content:
609
+ application/json:
610
+ schema:
611
+ type: object
612
+ properties:
613
+ name:
614
+ type: string
615
+ description: The context's name, at most 64 characters; creates or renames.
616
+ id:
617
+ type: string
618
+ description: The identifier of the context to rename; when absent, a new context is created.
619
+ conversation_id:
620
+ type: string
621
+ description: The conversation to file; selects the move behaviour.
622
+ project_id:
623
+ type: [string, 'null']
624
+ description: The target context, or null to unfile the conversation.
625
+ responses:
626
+ '200':
627
+ description: The created or renamed context, or the move confirmation.
628
+ /api/projects/{id}:
629
+ delete:
630
+ tags: [Projects]
631
+ summary: Delete a context
632
+ description: >-
633
+ Deletes the context, detaches its files and returns its
634
+ conversations to the general list.
635
+ operationId: deleteProject
636
+ parameters:
637
+ - name: id
638
+ in: path
639
+ required: true
640
+ schema: { type: string }
641
+ responses:
642
+ '200':
643
+ description: The context is deleted.
644
+ /api/projects/{id}/files:
645
+ get:
646
+ tags: [Projects]
647
+ summary: List a context's files
648
+ operationId: listProjectFiles
649
+ parameters:
650
+ - name: id
651
+ in: path
652
+ required: true
653
+ schema: { type: string }
654
+ responses:
655
+ '200':
656
+ description: The context's files.
657
+ post:
658
+ tags: [Projects]
659
+ summary: Attach a file to a context, or replace one by supplying its identifier
660
+ description: >-
661
+ Attaches a text file to the context named by project_id in the
662
+ body. When the body carries the identifier of an existing file,
663
+ that file is replaced instead and no new file is attached. At most
664
+ ten files of 8000 characters are kept per context.
665
+ operationId: attachProjectFile
666
+ parameters:
667
+ - name: id
668
+ in: path
669
+ required: true
670
+ schema: { type: string }
671
+ requestBody:
672
+ required: true
673
+ content:
674
+ application/json:
675
+ schema:
676
+ type: object
677
+ required: [project_id, name, content]
678
+ properties:
679
+ project_id: { type: string, description: The context to attach the file to. }
680
+ name: { type: string, description: The file's name, at most 64 characters. }
681
+ content: { type: string, description: The file's text, at most 8000 characters. }
682
+ id:
683
+ type: string
684
+ description: The identifier of the file to replace; when absent, a new file is attached.
685
+ responses:
686
+ '200':
687
+ description: The attached or replaced file.
688
+ /api/project-files/{id}:
689
+ delete:
690
+ tags: [Projects]
691
+ summary: Detach a file from its context
692
+ operationId: detachProjectFile
693
+ parameters:
694
+ - name: id
695
+ in: path
696
+ required: true
697
+ schema: { type: string }
698
+ responses:
699
+ '200':
700
+ description: The file is detached.
701
+ /api/lane:
702
+ post:
703
+ tags: [Experimental]
704
+ summary: Query one named experimental lane
705
+ description: >-
706
+ Retrieves from a single named experimental corpus (the comparison
707
+ levels of the annealment measurement) without touching the
708
+ production index.
709
+ operationId: laneQuery
710
+ requestBody:
711
+ required: true
712
+ content:
713
+ application/json:
714
+ schema:
715
+ type: object
716
+ required: [lane, query]
717
+ properties:
718
+ lane:
719
+ type: string
720
+ description: The experimental corpus to query.
721
+ query: { type: string }
722
+ responses:
723
+ '200':
724
+ description: The lane's ranked hits.
725
+ /v1/lane:
726
+ post:
727
+ tags: [Experimental]
728
+ summary: Query one named experimental lane with an API key
729
+ description: >-
730
+ Identical to /api/lane, with authentication by API key and a
731
+ per-key daily allowance.
732
+ operationId: laneKeyed
733
+ security:
734
+ - bearerAuth: []
735
+ requestBody:
736
+ required: true
737
+ content:
738
+ application/json:
739
+ schema:
740
+ type: object
741
+ required: [lane, query]
742
+ properties:
743
+ lane: { type: string }
744
+ query: { type: string }
745
+ responses:
746
+ '200':
747
+ description: The lane's ranked hits.
748
+ /api/feedback:
749
+ post:
750
+ tags: [Ask]
751
+ summary: Rate an answer
752
+ description: >-
753
+ Records a thumbs rating against the query hash, which the
754
+ feedback triage clusters for review.
755
+ operationId: feedback
756
+ requestBody:
757
+ required: true
758
+ content:
759
+ application/json:
760
+ schema:
761
+ type: object
762
+ required: [query_hash, rating]
763
+ properties:
764
+ query_hash: { type: string }
765
+ rating: { type: integer, enum: [1, -1] }
766
+ responses:
767
+ '200':
768
+ description: The rating is recorded.
769
+ /v1/admin/enrich:
770
+ post:
771
+ tags: [Administration]
772
+ summary: The enrichment driver
773
+ description: >-
774
+ Batches chunks through the contextual-enrichment lane, with the
775
+ per-chunk contexts cached server-side. The default mode writes to
776
+ the production index; callers that only want the generated
777
+ contexts pass mode context.
778
+ operationId: adminEnrich
779
+ security: [{ adminAuth: [] }]
780
+ requestBody:
781
+ required: true
782
+ content:
783
+ application/json:
784
+ schema:
785
+ type: object
786
+ properties:
787
+ chunks: { type: array, description: The chunks to enrich. }
788
+ mode: { type: string, enum: [default, context, ab] }
789
+ effort: { type: string, enum: [low, high] }
790
+ responses:
791
+ '200':
792
+ description: The enrichment results.
793
+ /admin/enrich:
794
+ post:
795
+ tags: [Administration]
796
+ summary: The enrichment driver (the operational path)
797
+ description: >-
798
+ The operational alias of /v1/admin/enrich, which the ingestion
799
+ tooling calls; the behaviour and the credential are identical.
800
+ operationId: adminEnrichAlias
801
+ security: [{ adminAuth: [] }]
802
+ requestBody:
803
+ required: true
804
+ content:
805
+ application/json:
806
+ schema:
807
+ type: object
808
+ properties:
809
+ chunks: { type: array, description: The chunks to enrich. }
810
+ mode: { type: string, enum: [default, context, ab] }
811
+ effort: { type: string, enum: [low, high] }
812
+ responses:
813
+ '200':
814
+ description: The enrichment results.
815
+ /v1/admin/section:
816
+ post:
817
+ tags: [Administration]
818
+ summary: Write the section-summary units
819
+ description: >-
820
+ Generates a summary of each named top-level clause from its child
821
+ chunks' excerpts, embeds it together with the table-of-contents
822
+ path and upserts it as a navigation node, so that retrieval can
823
+ descend from the summary to the quotable leaf clauses. Accepts at
824
+ most six units per call; the credential and the batching mirror
825
+ the enrichment driver.
826
+ operationId: adminSection
827
+ security: [{ adminAuth: [] }]
828
+ requestBody:
829
+ required: true
830
+ content:
831
+ application/json:
832
+ schema:
833
+ type: object
834
+ required: [units]
835
+ properties:
836
+ units:
837
+ type: array
838
+ maxItems: 6
839
+ description: The section units to write.
840
+ responses:
841
+ '200':
842
+ description: The written units with their usage.
843
+ /admin/section:
844
+ post:
845
+ tags: [Administration]
846
+ summary: Write the section-summary units (the operational path)
847
+ description: >-
848
+ The operational alias of /v1/admin/section; the behaviour and the
849
+ credential are identical.
850
+ operationId: adminSectionAlias
851
+ security: [{ adminAuth: [] }]
852
+ requestBody:
853
+ required: true
854
+ content:
855
+ application/json:
856
+ schema:
857
+ type: object
858
+ required: [units]
859
+ properties:
860
+ units: { type: array, maxItems: 6 }
861
+ responses:
862
+ '200':
863
+ description: The written units with their usage.
864
+ /admin/vectors:
865
+ post:
866
+ tags: [Administration]
867
+ summary: The vector admin route
868
+ description: >-
869
+ Embeds, upserts or reads index vectors by identifier through the
870
+ binding lane, in batches of at most twenty identifiers; the
871
+ binding is the credential, gated by the administrator token like
872
+ the enrichment driver.
873
+ operationId: adminVectors
874
+ security: [{ adminAuth: [] }]
875
+ requestBody:
876
+ required: true
877
+ content:
878
+ application/json:
879
+ schema:
880
+ type: object
881
+ properties:
882
+ mode: { type: string, enum: [get, upsert, embed] }
883
+ ids: { type: array, items: { type: string } }
884
+ vectors: { type: array }
885
+ responses:
886
+ '200':
887
+ description: The requested vectors or the operation result.
888
+ /admin/caption:
889
+ post:
890
+ tags: [Administration]
891
+ summary: Caption one figure asset
892
+ description: >-
893
+ Fetches the named unit's figure asset from the object store,
894
+ describes it with the vision-capable answer model and stores the
895
+ description into the unit's payload. The operation is
896
+ idempotent.
897
+ operationId: adminCaption
898
+ security: [{ adminAuth: [] }]
899
+ requestBody:
900
+ required: true
901
+ content:
902
+ application/json:
903
+ schema:
904
+ type: object
905
+ required: [unit_id]
906
+ properties:
907
+ unit_id: { type: string, description: The figure unit to caption. }
908
+ context:
909
+ type: string
910
+ description: Surrounding clause text, at most 400 characters, to steer the description.
911
+ responses:
912
+ '200':
913
+ description: The stored description.
914
+ /v1/admin/judge:
915
+ post:
916
+ tags: [Administration]
917
+ summary: Judge one answer against its passages
918
+ description: >-
919
+ Scores an answer's faithfulness and relevancy against the
920
+ passages it claims to rest on, with a model that took no part in
921
+ generating the answer. The promotion gate and the evaluation
922
+ harness call this lane.
923
+ operationId: adminJudge
924
+ security: [{ adminAuth: [] }]
925
+ requestBody:
926
+ required: true
927
+ content:
928
+ application/json:
929
+ schema:
930
+ type: object
931
+ required: [question, answer]
932
+ properties:
933
+ question: { type: string, description: The question, at most 2000 characters. }
934
+ answer: { type: string, description: The answer, at most 4000 characters. }
935
+ passages:
936
+ type: array
937
+ maxItems: 8
938
+ items: { type: string }
939
+ description: The passages, each at most 600 characters.
940
+ responses:
941
+ '200':
942
+ description: The verdict with its scores.
943
+ /admin/judge:
944
+ post:
945
+ tags: [Administration]
946
+ summary: Judge one answer against its passages (the operational path)
947
+ description: >-
948
+ The operational alias of /v1/admin/judge; the behaviour and the
949
+ credential are identical.
950
+ operationId: adminJudgeAlias
951
+ security: [{ adminAuth: [] }]
952
+ requestBody:
953
+ required: true
954
+ content:
955
+ application/json:
956
+ schema:
957
+ type: object
958
+ required: [question, answer]
959
+ properties:
960
+ question: { type: string }
961
+ answer: { type: string }
962
+ passages: { type: array, items: { type: string } }
963
+ responses:
964
+ '200':
965
+ description: The verdict with its scores.
966
+ /v1/admin/keys/{id}:
967
+ delete:
968
+ tags: [Administration]
969
+ summary: Revoke an API key
970
+ operationId: adminRevokeKey
971
+ security: [{ adminAuth: [] }]
972
+ parameters:
973
+ - name: id
974
+ in: path
975
+ required: true
976
+ schema: { type: string }
977
+ responses:
978
+ '200':
979
+ description: The key is revoked.
980
+ /auth/me:
981
+ get:
982
+ tags: [Service]
983
+ summary: Report the caller's session state
984
+ description: >-
985
+ Reports whether the caller is signed in, together with the
986
+ display name, the email and the estate roles when a session is
987
+ present. The console uses this endpoint to state who is signed
988
+ in.
989
+ operationId: authMe
990
+ responses:
991
+ '200':
992
+ description: The session state.
993
+ /auth/login:
994
+ get:
995
+ tags: [Service]
996
+ summary: Begin sign-in
997
+ description: >-
998
+ Redirects to the estate's identity service with the OIDC
999
+ authorization-code and PKCE parameters; the callback completes
1000
+ the sign-in and mints the session cookie.
1001
+ operationId: authLogin
1002
+ responses:
1003
+ '302':
1004
+ description: The redirect to the identity service.
1005
+ /auth/callback:
1006
+ get:
1007
+ tags: [Service]
1008
+ summary: Complete sign-in
1009
+ description: >-
1010
+ The identity service's redirect target; completes the
1011
+ authorization-code exchange, mints the session cookie and returns
1012
+ the caller to the page they began from.
1013
+ operationId: authCallback
1014
+ responses:
1015
+ '302':
1016
+ description: The redirect after sign-in completes.
1017
+ /auth/logout:
1018
+ post:
1019
+ tags: [Service]
1020
+ summary: End the session
1021
+ operationId: authLogout
1022
+ responses:
1023
+ '200':
1024
+ description: The session is ended and the cookie is cleared.
1025
+ get:
1026
+ tags: [Service]
1027
+ summary: End the session (the link form)
1028
+ description: >-
1029
+ The same operation as the post, provided so that a plain link can
1030
+ end the session.
1031
+ operationId: authLogoutLink
1032
+ responses:
1033
+ '200':
1034
+ description: The session is ended and the cookie is cleared.
1035
+ /v1/admin/stats:
1036
+ get:
1037
+ tags: [Administration]
1038
+ summary: Service statistics
1039
+ description: >-
1040
+ Reports the query volume by day and tier, the spend by model, the
1041
+ feedback counts, the answer-cache mix and the conversation count
1042
+ over a seven-day window.
1043
+ operationId: adminStats
1044
+ security:
1045
+ - adminAuth: []
1046
+ responses:
1047
+ '200':
1048
+ description: The statistics document.
1049
+ /v1/admin/keys:
1050
+ get:
1051
+ tags: [Administration]
1052
+ summary: List the issued API keys
1053
+ operationId: adminListKeys
1054
+ security: [{ adminAuth: [] }]
1055
+ responses:
1056
+ '200':
1057
+ description: The key registry, without plaintext.
1058
+ post:
1059
+ tags: [Administration]
1060
+ summary: Issue an API key
1061
+ description: >-
1062
+ Issues an API key carrying its own daily allowance. The plaintext
1063
+ key is returned once and the store holds its hash only.
1064
+ operationId: adminCreateKey
1065
+ security:
1066
+ - adminAuth: []
1067
+ requestBody:
1068
+ required: true
1069
+ content:
1070
+ application/json:
1071
+ schema:
1072
+ type: object
1073
+ required: [name]
1074
+ properties:
1075
+ name: { type: string, description: The key's label. }
1076
+ day_limit: { type: integer, description: The per-day allowance. }
1077
+ responses:
1078
+ '200':
1079
+ description: The issued key, with the plaintext returned once.
1080
+
1081
+ components:
1082
+ securitySchemes:
1083
+ bearerAuth:
1084
+ type: http
1085
+ scheme: bearer
1086
+ description: >-
1087
+ An API key issued by an administrator or a personal access token,
1088
+ sent as the bearer token. Anonymous callers use the /api
1089
+ endpoints without a key.
1090
+ adminAuth:
1091
+ type: http
1092
+ scheme: bearer
1093
+ description: The administrator token, sent as the bearer token.
1094
+ schemas:
1095
+ AskRequest:
1096
+ type: object
1097
+ required: [query]
1098
+ properties:
1099
+ query:
1100
+ type: string
1101
+ description: The question, at most 8000 characters.
1102
+ stream:
1103
+ type: boolean
1104
+ default: false
1105
+ description: >-
1106
+ When true, the response is a server-sent event stream whose
1107
+ events carry citations, tokens and the completion marker.
1108
+ fresh:
1109
+ type: boolean
1110
+ default: false
1111
+ description: >-
1112
+ Regenerates the answer and skips the answer caches, so the
1113
+ response also carries the grounding passages in context.
1114
+ Evaluation callers use it to observe what the answer was
1115
+ built from.
1116
+ lang:
1117
+ type: string
1118
+ description: Forces the answer language, for example en or fr.
1119
+ prev:
1120
+ type: string
1121
+ description: The previous question, for conversational context.
1122
+ history:
1123
+ type: array
1124
+ description: Recent turns, for conversational context.
1125
+ items:
1126
+ type: object
1127
+ properties:
1128
+ role: { type: string, enum: [user, assistant] }
1129
+ content: { type: string }
1130
+ context:
1131
+ type: object
1132
+ description: >-
1133
+ An application context, such as a certificate entity, that
1134
+ scopes the retrieval to the document the context names.
1135
+ AskResponse:
1136
+ type: object
1137
+ properties:
1138
+ answer:
1139
+ type: string
1140
+ description: The generated answer.
1141
+ citations:
1142
+ type: array
1143
+ description: The passages the answer rests on.
1144
+ items:
1145
+ type: object
1146
+ properties:
1147
+ docidentifier: { type: string, description: 'For example, OIML R 60-1.' }
1148
+ edition: { type: string }
1149
+ clause_anchor: { type: string, description: The clause, for example 4.5.1. }
1150
+ clause_title: { type: string }
1151
+ corpus: { type: string, description: clean, dirty or smart-model. }
1152
+ status: { type: string, description: in-force, superseded or withdrawn. }
1153
+ url: { type: string, format: uri }
1154
+ blocks:
1155
+ type: array
1156
+ description: >-
1157
+ The typed objects the answer carries, such as tables, verdicts
1158
+ and unit blocks.
1159
+ items:
1160
+ type: object
1161
+ model:
1162
+ type: string
1163
+ description: The model that generated the answer.
1164
+ query_hash:
1165
+ type: string
1166
+ description: The hash of the question, used for feedback.
1167
+ quota:
1168
+ type: object
1169
+ description: The remaining daily allowance.