@orkg/scidquest 1.0.4 → 1.1.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.
Files changed (3) hide show
  1. package/README.md +533 -40
  2. package/dist/scidquest.es.js +17990 -17403
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -8,19 +8,38 @@ ScidQuest provides a reusable UI and workflow foundation for extracting structur
8
8
 
9
9
  ## Table of Contents
10
10
 
11
- 1. [About](#about)
12
- 2. [Key Features](#key-features)
13
- 3. [Installation](#installation)
14
- 4. [Quick Start](#quick-start)
15
- 5. [Configuration](#configuration)
16
- 6. [Peer Dependencies](#peer-dependencies)
17
- 7. [Development](#development)
18
- 8. [Project Structure](#project-structure)
19
- 9. [License](#license)
11
+ - [SciD-QuESt: From Scientific Documents to Knowledge](#scid-quest-from-scientific-documents-to-knowledge)
12
+ - [Table of Contents](#table-of-contents)
13
+ - [About](#about)
14
+ - [Key Features](#key-features)
15
+ - [Installation](#installation)
16
+ - [Styles](#styles)
17
+ - [Quick start](#quick-start)
18
+ - [Provider stack](#provider-stack)
19
+ - [Library API reference](#library-api-reference)
20
+ - [ScidQuestProvider and useScidQuestContext](#scidquestprovider-and-usescidquestcontext)
21
+ - [LLMService, ScidQuestAdapter, and createScidQuestAdapter](#llmservice-scidquestadapter-and-createscidquestadapter)
22
+ - [useSuggestionGenerator](#usesuggestiongenerator)
23
+ - [ResearchQuestionnaireApp](#researchquestionnaireapp)
24
+ - [TemplateQuestionnaire](#templatequestionnaire)
25
+ - [PDFUpload](#pdfupload)
26
+ - [PdfViewer](#pdfviewer)
27
+ - [ResearchQuestionnaireWorkspaceProvider and useResearchQuestionnaireWorkspace](#researchquestionnaireworkspaceprovider-and-useresearchquestionnaireworkspace)
28
+ - [ResearchQuestionnaireFieldAiWrapper](#researchquestionnairefieldaiwrapper)
29
+ - [AIAssistantButton and SuggestionBox](#aiassistantbutton-and-suggestionbox)
30
+ - [QuestionnaireAIProvider and QuestionnaireAIContext](#questionnaireaiprovider-and-questionnaireaicontext)
31
+ - [buildQuestionDefinitions and siblingQuestionIdsFor](#buildquestiondefinitions-and-siblingquestionidsfor)
32
+ - [Types](#types)
33
+ - [Configuration](#configuration)
34
+ - [Peer Dependencies](#peer-dependencies)
35
+ - [Further reading](#further-reading)
36
+ - [Development](#development)
37
+ - [Project Structure](#project-structure)
38
+ - [License](#license)
20
39
 
21
40
  ## About
22
41
 
23
- ScidQuest is distributed as an npm package: `@sololeveling/scidquest`.
42
+ ScidQuest is distributed as an npm package: `@orkg/scidquest`.
24
43
 
25
44
  It is intended for teams building research tooling that requires:
26
45
 
@@ -39,7 +58,7 @@ It is intended for teams building research tooling that requires:
39
58
  - **AI suggestions** for draft answers from document context
40
59
  - **AI verification** to validate user answers against source content
41
60
  - **Local persistence** for in-progress analysis sessions
42
- - **Multi-provider model support** (OpenAI, Groq, Mistral)
61
+ - **Embeddable workflows** with custom forms while keeping PDF extraction and per-field AI
43
62
 
44
63
  [(back to top)](#table-of-contents)
45
64
 
@@ -48,26 +67,84 @@ It is intended for teams building research tooling that requires:
48
67
  Install the package with npm:
49
68
 
50
69
  ```bash
51
- npm install @sololeveling/scidquest
70
+ npm install @orkg/scidquest
52
71
  ```
53
72
 
54
- Import the published stylesheet in your application entrypoint:
73
+ [(back to top)](#table-of-contents)
74
+
75
+ ## Styles
76
+
77
+ Import the published stylesheet in your application entrypoint (or layout):
55
78
 
56
79
  ```ts
57
- import "@sololeveling/scidquest/dist/contribute-standalone.css";
80
+ import "@orkg/scidquest/dist/contribute-standalone.css";
58
81
  ```
59
82
 
60
83
  [(back to top)](#table-of-contents)
61
84
 
62
- ## Quick Start
85
+ ## Quick start
63
86
 
64
- Basic package usage:
87
+ The smallest end-to-end flow is: implement `LLMService`, wrap the tree with `ScidQuestProvider` (and `QuestionnaireAIProvider` if you use AI chrome such as `AIAssistantButton` or `ResearchQuestionnaireFieldAiWrapper`), then render `ResearchQuestionnaireApp` with a `QuestionnaireTemplate` and either the default questionnaire or a custom `questionnaireSlot`.
65
88
 
66
89
  ```tsx
67
- import React from "react";
90
+ import React, { useMemo, useState } from "react";
68
91
  import { createRoot } from "react-dom/client";
69
- import "@sololeveling/scidquest/dist/contribute-standalone.css";
70
- import App from "./App";
92
+ import "@orkg/scidquest/dist/contribute-standalone.css";
93
+ import {
94
+ ScidQuestProvider,
95
+ QuestionnaireAIProvider,
96
+ ResearchQuestionnaireApp,
97
+ type LLMService,
98
+ type QuestionnaireTemplate,
99
+ } from "@orkg/scidquest";
100
+
101
+ const templateSpec: QuestionnaireTemplate = {
102
+ version: "1",
103
+ template: "demo",
104
+ template_id: "demo",
105
+ sections: [
106
+ {
107
+ id: "sec1",
108
+ title: "Example",
109
+ questions: [
110
+ {
111
+ id: "q1",
112
+ label: "Main contribution",
113
+ type: "text",
114
+ required: true,
115
+ },
116
+ ],
117
+ },
118
+ ],
119
+ };
120
+
121
+ class DemoLLM implements LLMService {
122
+ async generateText(prompt: string, options?: { systemContext?: string }) {
123
+ // Replace with your provider (OpenAI, Azure, local, etc.).
124
+ return { text: '{"suggestions":[{"rank":1,"text":"…","confidence":0.9,"evidence":[]}]}' };
125
+ }
126
+ isConfigured() {
127
+ return true;
128
+ }
129
+ }
130
+
131
+ function App() {
132
+ const llmService = useMemo(() => new DemoLLM(), []);
133
+ const [answers, setAnswers] = useState<Record<string, unknown>>({});
134
+
135
+ return (
136
+ <QuestionnaireAIProvider>
137
+ <ScidQuestProvider llmService={llmService}>
138
+ <ResearchQuestionnaireApp
139
+ templateSpec={templateSpec}
140
+ answers={answers}
141
+ setAnswers={setAnswers}
142
+ layout="split"
143
+ />
144
+ </ScidQuestProvider>
145
+ </QuestionnaireAIProvider>
146
+ );
147
+ }
71
148
 
72
149
  createRoot(document.getElementById("root")!).render(
73
150
  <React.StrictMode>
@@ -76,29 +153,436 @@ createRoot(document.getElementById("root")!).render(
76
153
  );
77
154
  ```
78
155
 
79
- Typical end-user workflow inside ScidQuest:
156
+ Typical end-user workflow inside the default UI:
157
+
158
+ 1. Upload a PDF file (default max 30 MB).
159
+ 2. Wait for text extraction; complete template questions.
160
+ 3. Request AI suggestions where needed; jump to PDF evidence when offered.
161
+ 4. Run batch verification from the summary bar where available.
162
+ 5. Export or import JSON progress.
163
+
164
+ [(back to top)](#table-of-contents)
165
+
166
+ ## Provider stack
167
+
168
+ | Order (outer → inner) | Purpose |
169
+ | --- | --- |
170
+ | `QuestionnaireAIProvider` | Persists suggestion/verification **history** used by `AIAssistantButton` and related UI (localStorage key `questionnaire-ai-history`). |
171
+ | `ScidQuestProvider` | Supplies `LLMService` via an internal `ScidQuestAdapter` (Redux store included for internal widgets). |
172
+ | `ResearchQuestionnaireWorkspaceProvider` | Injected **automatically** by `ResearchQuestionnaireApp` around `questionnaireSlot` only; exposes PDF/answers/template to `useResearchQuestionnaireWorkspace`. |
173
+
174
+ If you only use `useSuggestionGenerator` and never mount `AIAssistantButton` / `ResearchQuestionnaireFieldAiWrapper`, you can omit `QuestionnaireAIProvider`. For the full questionnaire UI or embed wrappers, include it.
175
+
176
+ [(back to top)](#table-of-contents)
177
+
178
+ ## Library API reference
179
+
180
+ Everything below is imported from `@orkg/scidquest` unless noted.
181
+
182
+ ### ScidQuestProvider and useScidQuestContext
183
+
184
+ **Purpose:** Connects your `LLMService` to hooks and components that call `adapter.generateSuggestions`, `adapter.verifyAnswer`, or batch verification.
185
+
186
+ **Props (`ScidQuestProvider`):**
187
+
188
+ | Prop | Type | Description |
189
+ | --- | --- | --- |
190
+ | `llmService` | `LLMService` | Required. Your implementation of text generation and configuration checks. |
191
+ | `children` | `ReactNode` | App subtree that needs ScidQuest AI APIs. |
192
+
193
+ **`useScidQuestContext()`** returns `{ adapter: ScidQuestAdapter }`. Throws if used outside `ScidQuestProvider`.
194
+
195
+ ```tsx
196
+ import { ScidQuestProvider, useScidQuestContext } from "@orkg/scidquest";
197
+
198
+ function Child() {
199
+ const { adapter } = useScidQuestContext();
200
+ return <button type="button" onClick={() => void adapter.isConfigured()}>Check</button>;
201
+ }
202
+
203
+ <ScidQuestProvider llmService={llmService}>
204
+ <Child />
205
+ </ScidQuestProvider>
206
+ ```
207
+
208
+ [(back to top)](#table-of-contents)
209
+
210
+ ### LLMService, ScidQuestAdapter, and createScidQuestAdapter
211
+
212
+ **`LLMService`** is the contract you implement: the library never holds API keys. Implement `generateText(prompt, options?)` returning `{ text, reasoning?, usage? }` and `isConfigured()`.
213
+
214
+ **`createScidQuestAdapter(llmService)`** builds a **`ScidQuestAdapter`**: `generateSuggestions`, `verifyAnswer`, optional `verifyAnswersBatch`, and `isConfigured`. Advanced integrations can implement `ScidQuestAdapter` directly instead of using `createScidQuestAdapter`, then you would need a custom way to inject it (the shipped `ScidQuestProvider` always uses `createScidQuestAdapter`).
215
+
216
+ ```ts
217
+ import { createScidQuestAdapter, type LLMService } from "@orkg/scidquest";
218
+
219
+ const adapter = createScidQuestAdapter(myLlmService);
220
+ await adapter.verifyAnswer({
221
+ questionId: "q1",
222
+ questionText: "Contribution?",
223
+ currentAnswer: "They propose a new metric.",
224
+ pdfContent: extractedPlainText,
225
+ });
226
+ ```
227
+
228
+ [(back to top)](#table-of-contents)
229
+
230
+ ### useSuggestionGenerator
231
+
232
+ **Purpose:** Call the adapter’s suggestion pipeline from arbitrary UI (no PDF viewer required). Requires `ScidQuestProvider`.
233
+
234
+ **Parameters:**
235
+
236
+ | Option | Type | Description |
237
+ | --- | --- | --- |
238
+ | `questionText` | `string` | Wording shown to the model. |
239
+ | `questionType` | `string` | Template field type (e.g. `text`, `select`). |
240
+ | `questionOptions` | `string[]?` | For choice-style questions. |
241
+ | `pdfContent` | `string?` | Extracted document text; required for a successful run. |
242
+ | `contextHistory` | history entries | Prior suggestions for diversity (see type exports). |
243
+ | `previousFeedback` | array | Thumbs/comments from prior runs (shape matches suggestion feedback entries: ids, rating, optional comment, timestamp). |
244
+ | `excludedSuggestionIds` | `Set<string>` | IDs to strip from `contextHistory`. |
245
+
246
+ **Returns:** `{ suggestions, loading, error, rawError, generateSuggestions, clearSuggestions }`.
247
+
248
+ ```tsx
249
+ import { useSuggestionGenerator } from "@orkg/scidquest";
250
+
251
+ function Panel({ pdfText }: { pdfText: string }) {
252
+ const { suggestions, loading, error, generateSuggestions, clearSuggestions } =
253
+ useSuggestionGenerator({
254
+ questionText: "What is the research objective?",
255
+ questionType: "text",
256
+ pdfContent: pdfText,
257
+ });
258
+
259
+ return (
260
+ <>
261
+ <button type="button" disabled={loading} onClick={() => void generateSuggestions()}>
262
+ Suggest
263
+ </button>
264
+ <button type="button" onClick={clearSuggestions}>Clear</button>
265
+ {error && <p role="alert">{error}</p>}
266
+ <ul>
267
+ {suggestions.map((s) => (
268
+ <li key={s.id}>{typeof s.text === "string" ? s.text : s.text.join(", ")}</li>
269
+ ))}
270
+ </ul>
271
+ </>
272
+ );
273
+ }
274
+ ```
275
+
276
+ [(back to top)](#table-of-contents)
277
+
278
+ ### ResearchQuestionnaireApp
279
+
280
+ **Purpose:** Orchestrates PDF upload, `PdfViewer`, plain-text extraction, optional structured document extraction, split or single-column layout, and either the built-in `TemplateQuestionnaire` or a custom **`questionnaireSlot`**.
281
+
282
+ **Props (`ResearchQuestionnaireAppProps`):**
283
+
284
+ | Prop | Type | Default | Description |
285
+ | --- | --- | --- | --- |
286
+ | `templateSpec` | `QuestionnaireTemplate` | (required) | Sections and question definitions. |
287
+ | `pdfTextExtractor` | `{ extractFullText(url: string): Promise<string> }` | built-in | Override for bundlers or custom PDF pipelines. |
288
+ | `structuredPdfExtractor` | `{ extractStructuredDocument(text: string): Promise<StructuredDocument> }` | built-in | Builds structured sections from extracted text. |
289
+ | `maxPdfSizeBytes` | `number` | 30 MB | Passed to `PDFUpload`. |
290
+ | `layout` | `'split' \| 'single'` | `'split'` | Desktop split panel vs stacked flow; narrow viewports behave like single. |
291
+ | `showPdfViewer` | `boolean` | `true` | Hide the PDF column but keep extraction if a URL exists. |
292
+ | `onAnswersChange` | `(answers) => void` | — | Fires whenever `answers` updates. |
293
+ | `initialAnswers` | `Record<string, unknown>` | `{}` | Seed state when **uncontrolled**. |
294
+ | `answers` / `setAnswers` | controlled pair | — | When both set, the host owns answer state. |
295
+ | `questionnaireSlot` | `(ctx: ResearchQuestionnaireWorkspaceValue) => ReactNode` | — | **Embed mode:** replace built-in questionnaire; `ctx` includes `pdfContent`, `answers`, navigation, highlights. |
296
+ | `sx` | MUI `sx` | — | Root `Box` styling. |
297
+
298
+ ```tsx
299
+ <ResearchQuestionnaireApp
300
+ templateSpec={templateSpec}
301
+ answers={answers}
302
+ setAnswers={setAnswers}
303
+ layout="split"
304
+ showPdfViewer
305
+ onAnswersChange={(next) => console.log("answers", next)}
306
+ />
307
+ ```
308
+
309
+ **Embed mode** (custom form, same PDF pipeline):
310
+
311
+ ```tsx
312
+ <ResearchQuestionnaireApp
313
+ templateSpec={templateSpec}
314
+ answers={answers}
315
+ setAnswers={setAnswers}
316
+ questionnaireSlot={(ctx) => (
317
+ <MyForm
318
+ answers={ctx.answers}
319
+ setAnswers={ctx.setAnswers}
320
+ pdfText={ctx.pdfContent}
321
+ onJumpToPage={ctx.onNavigateToPage}
322
+ />
323
+ )}
324
+ />
325
+ ```
326
+
327
+ See [docs/EMBEDDING.md](./docs/EMBEDDING.md) for embedding caveats (bundlers, `QuestionnaireAIProvider`).
328
+
329
+ [(back to top)](#table-of-contents)
330
+
331
+ ### TemplateQuestionnaire
332
+
333
+ **Purpose:** Full questionnaire UI: section accordions, validation, export/import JSON, localStorage autosave, AI verification batch from the summary bar, and integration with PDF navigation and highlights when props are passed.
334
+
335
+ **Requires:** `ScidQuestProvider` (uses `adapter` for verification).
336
+
337
+ **Main props (`TemplateQuestionnaireProps`):**
338
+
339
+ | Prop | Type | Description |
340
+ | --- | --- | --- |
341
+ | `templateSpec` | `QuestionnaireTemplate \| null` | When `null`, shows a short loading placeholder. |
342
+ | `answers` / `setAnswers` | `Record<string, unknown>` + updater | Controlled answer map (question ids as keys; repeat sections use arrays under section ids per template). |
343
+ | `pdfContent` | `string?` | Plain text passed to AI flows. |
344
+ | `structuredDocument` | `StructuredDocument \| null?` | Improves evidence handling when present. |
345
+ | `onNavigateToPage` | `(page: number) => void` | Scroll PDF to page. |
346
+ | `onHighlightsChange` | highlight map | Receives highlight rects keyed by page. |
347
+ | `pdfUrl` / `pageWidth` | viewer sync | Used for evidence UI alignment. |
348
+ | `pdfExtractionError` / `onRetryExtraction` | error UX | Shown in the top bar when extraction fails. |
349
+ | `aiConfig.hidden` | `boolean` | Hides AI configuration entry points in the summary bar when set. |
350
+ | `sx` | MUI `sx` | Wrapper styling. |
351
+
352
+ ```tsx
353
+ <TemplateQuestionnaire
354
+ templateSpec={templateSpec}
355
+ answers={answers}
356
+ setAnswers={setAnswers}
357
+ pdfContent={extractedText}
358
+ structuredDocument={structured}
359
+ onNavigateToPage={goToPage}
360
+ onHighlightsChange={setHighlights}
361
+ pdfUrl={blobUrl}
362
+ pageWidth={pageWidth}
363
+ />
364
+ ```
365
+
366
+ [(back to top)](#table-of-contents)
367
+
368
+ ### PDFUpload
369
+
370
+ **Purpose:** Drag-and-drop or file-picker PDF upload with type and size validation and optional clear.
371
+
372
+ **Props (`PDFUploadProps`):**
373
+
374
+ | Prop | Type | Default | Description |
375
+ | --- | --- | --- | --- |
376
+ | `onFileSelected` | `(file: File) => void` | required | Called with a validated PDF `File`. |
377
+ | `onFileRemoved` | `() => void` | — | Called when the user clears the selection. |
378
+ | `maxSizeBytes` | `number` | 30 MB | Rejects larger files. |
379
+ | `accept` | `string` | `application/pdf` | Input `accept` attribute. |
380
+ | `disabled` | `boolean` | `false` | Disables interaction. |
381
+ | `sx` | MUI `sx` | — | Root `Box` styling. |
382
+
383
+ ```tsx
384
+ const [file, setFile] = useState<File | null>(null);
385
+
386
+ <PDFUpload
387
+ maxSizeBytes={20 * 1024 * 1024}
388
+ onFileSelected={(f) => setFile(f)}
389
+ onFileRemoved={() => setFile(null)}
390
+ />
391
+ ```
392
+
393
+ [(back to top)](#table-of-contents)
394
+
395
+ ### PdfViewer
396
+
397
+ **Purpose:** Renders PDF pages with `react-pdf`, zoom, page controls, optional **full-document text extraction** via `pdfTextExtractor`, and optional highlight overlays.
398
+
399
+ **Props (`PdfViewerProps`):**
400
+
401
+ | Prop | Type | Description |
402
+ | --- | --- | --- |
403
+ | `refContainer` | `RefObject<HTMLDivElement>` | Scroll container for pages (must be the element that scrolls). |
404
+ | `pdfUrl` | `string \| null \| undefined` | Blob URL or remote PDF URL. |
405
+ | `pageWidth` | `number \| null` | Pixel width of each page. |
406
+ | `registerCommands` | `(cmds: { goToPage(n) }) => void` | Optional imperative navigation hook-up. |
407
+ | `onTextExtracted` | `(text: string) => void` | Called when `pdfTextExtractor` finishes. |
408
+ | `onExtractionError` | `(err: Error) => void` | Extraction failure path. |
409
+ | `highlights` | `Record<number, Array<{ left, top, width, height }>>` | Yellow overlays per page number. |
410
+ | `pdfTextExtractor` | `{ extractFullText(url: string): Promise<string> } \| null` | When null/omitted, no automatic extraction. |
411
+
412
+ ```tsx
413
+ const scrollRef = useRef<HTMLDivElement>(null);
414
+ const [url, setUrl] = useState<string | null>(null);
415
+
416
+ <Box ref={scrollRef} sx={{ height: 480, overflow: "auto" }}>
417
+ <PdfViewer
418
+ refContainer={scrollRef}
419
+ pdfUrl={url}
420
+ pageWidth={720}
421
+ pdfTextExtractor={myExtractor}
422
+ onTextExtracted={setPlainText}
423
+ onExtractionError={console.error}
424
+ highlights={highlightsByPage}
425
+ />
426
+ </Box>
427
+ ```
428
+
429
+ The viewer configures `pdfjs` worker from unpkg; override at app level if your CSP requires a self-hosted worker.
430
+
431
+ [(back to top)](#table-of-contents)
432
+
433
+ ### ResearchQuestionnaireWorkspaceProvider and useResearchQuestionnaireWorkspace
434
+
435
+ **Purpose:** Exposes the **workspace value** (`ResearchQuestionnaireWorkspaceValue`) to descendants: template, answers, extracted PDF text, structured document, PDF URL, `pageWidth`, navigation and highlight callbacks, and extraction error/retry.
436
+
437
+ `ResearchQuestionnaireApp` wraps **`questionnaireSlot`** output with `ResearchQuestionnaireWorkspaceProvider`. When you render `TemplateQuestionnaire` alone (without the app), no workspace context is present unless you add the provider yourself.
438
+
439
+ **`useResearchQuestionnaireWorkspace()`** returns the value **or `null`** when used outside the provider (the field AI wrapper treats `null` as “no workspace” and renders children only).
440
+
441
+ ```tsx
442
+ import {
443
+ ResearchQuestionnaireWorkspaceProvider,
444
+ useResearchQuestionnaireWorkspace,
445
+ } from "@orkg/scidquest";
446
+
447
+ function Inner() {
448
+ const ws = useResearchQuestionnaireWorkspace();
449
+ if (!ws) return null;
450
+ return <span>{ws.pdfContent?.slice(0, 80)}…</span>;
451
+ }
452
+
453
+ <ResearchQuestionnaireWorkspaceProvider value={workspaceValue}>
454
+ <Inner />
455
+ </ResearchQuestionnaireWorkspaceProvider>
456
+ ```
457
+
458
+ [(back to top)](#table-of-contents)
459
+
460
+ ### ResearchQuestionnaireFieldAiWrapper
461
+
462
+ **Purpose:** Wraps a **single host-rendered control** with the same AI affordances as built-in fields: suggestion generation, verification entry via `AIAssistantButton`, and `SuggestionBox` wired to workspace navigation/highlights.
463
+
464
+ **Requires:** Inside `ResearchQuestionnaireWorkspaceProvider` (automatic in embed mode under `ResearchQuestionnaireApp`) and `ScidQuestProvider`. For full `AIAssistantButton` behavior (history dialog, etc.), also use **`QuestionnaireAIProvider`**.
465
+
466
+ **Props (`ResearchQuestionnaireFieldAiWrapperProps`):**
467
+
468
+ | Prop | Type | Description |
469
+ | --- | --- | --- |
470
+ | `children` | `ReactNode` | Your input component. |
471
+ | `questionId` | `string` | Must match an id in `templateSpec` for sibling/context helpers. |
472
+ | `questionText` | `string` | Label or prompt sent to the model. |
473
+ | `questionType` | `ResearchFieldAiQuestionType` | `text \| select \| multi_select \| repeat_text \| group`. |
474
+ | `questionOptions` | `string[]?` | For select-style fields. |
475
+ | `currentAnswer` | `string` | String form of the value for verification. |
476
+ | `onApplySuggestion` | `(text: string) => void` | Apply chosen suggestion text into your state. |
477
+ | `disableAi` | `boolean?` | Force-disable AI chrome. |
478
+ | `parentContext` | `ParentContext?` | Optional nested/group context (see app `types/context`). |
479
+ | `onVerificationComplete` | `(result: AIVerificationResult) => void` | Per-field verification callback. |
480
+
481
+ If the template marks the question with `disable_ai_assistant: true`, the wrapper renders **`children` only**.
482
+
483
+ ```tsx
484
+ <ResearchQuestionnaireFieldAiWrapper
485
+ questionId="contribution"
486
+ questionText="Main contribution"
487
+ questionType="text"
488
+ currentAnswer={String(value ?? "")}
489
+ onApplySuggestion={(t) => setValue(t)}
490
+ >
491
+ <TextField fullWidth value={value} onChange={(e) => setValue(e.target.value)} />
492
+ </ResearchQuestionnaireFieldAiWrapper>
493
+ ```
494
+
495
+ [(back to top)](#table-of-contents)
496
+
497
+ ### AIAssistantButton and SuggestionBox
498
+
499
+ **Lower-level building blocks** used inside `ResearchQuestionnaireFieldAiWrapper`. Prefer the wrapper unless you need a fully custom layout.
500
+
501
+ **`AIAssistantButton`** (forwardRef, exposes `AIAssistantButtonRef` with `triggerSuggestion` / `triggerVerification`):
502
+
503
+ - Expects `ScidQuestProvider`, and **`QuestionnaireAIProvider`** for history features.
504
+ - Props include `questionId`, `questionText`, `questionType`, `questionOptions`, `currentAnswer`, `pdfContent`, `structuredDocument`, `allAnswers`, `siblingQuestionIds`, `questionDefinitions`, callbacks `onSuggestionsGenerated`, `onError`, `onVerificationComplete`, optional `parentContext`, `disabled`, etc.
505
+
506
+ **`SuggestionBox`** displays ranked suggestions, loading/error states, evidence navigation, collapse, regenerate, and feedback hooks (`onFeedback`, `onApply`, `onNavigateToPage`, `onHighlightsChange`, …).
507
+
508
+ ```tsx
509
+ import { AIAssistantButton, SuggestionBox, QuestionnaireAIProvider } from "@orkg/scidquest";
510
+
511
+ // Typical usage is through ResearchQuestionnaireFieldAiWrapper; direct composition
512
+ // requires you to manage suggestion list state and refs yourself (see source).
513
+ ```
514
+
515
+ [(back to top)](#table-of-contents)
516
+
517
+ ### QuestionnaireAIProvider and QuestionnaireAIContext
518
+
519
+ **Purpose:** In-memory and **localStorage**-backed history for questionnaire AI events (`QuestionnaireAIState` / `QuestionnaireAIHistory`).
520
+
521
+ **`QuestionnaireAIProvider`:** wrap near the root of anything that mounts `AIAssistantButton` (or the field wrapper that uses it).
522
+
523
+ **`QuestionnaireAIContext`:** the React context object. The package does **not** export the `useQuestionnaireAI` hook; use `useContext(QuestionnaireAIContext)` if you need programmatic access to `addToHistory`, `getHistoryByQuestion`, `clearHistory`, etc., and guard for `undefined`.
524
+
525
+ ```tsx
526
+ import { useContext } from "react";
527
+ import { QuestionnaireAIContext, QuestionnaireAIProvider } from "@orkg/scidquest";
528
+
529
+ function HistoryLength() {
530
+ const api = useContext(QuestionnaireAIContext);
531
+ if (!api) return null;
532
+ return <span>{api.state.history.length} events</span>;
533
+ }
534
+
535
+ <QuestionnaireAIProvider>
536
+ <HistoryLength />
537
+ </QuestionnaireAIProvider>
538
+ ```
539
+
540
+ [(back to top)](#table-of-contents)
541
+
542
+ ### buildQuestionDefinitions and siblingQuestionIdsFor
543
+
544
+ **Purpose:** Template helpers for advanced AI context.
545
+
546
+ - **`buildQuestionDefinitions(template)`** — flat map `questionId → Question` including nested `subquestions` / `item_fields`.
547
+ - **`siblingQuestionIdsFor(template, questionId)`** — ids in the same section top-level group or same nested group; used to gather related answers for prompting.
548
+
549
+ ```ts
550
+ import { buildQuestionDefinitions, siblingQuestionIdsFor } from "@orkg/scidquest";
551
+
552
+ const defs = buildQuestionDefinitions(templateSpec);
553
+ const siblings = siblingQuestionIdsFor(templateSpec, "q1");
554
+ ```
555
+
556
+ [(back to top)](#table-of-contents)
557
+
558
+ ### Types
559
+
560
+ The package re-exports TypeScript types for templates, LLM wiring, suggestions, and verification. Commonly used names:
80
561
 
81
- 1. Upload a PDF file (up to 30 MB).
82
- 2. Complete template questions.
83
- 3. Request AI suggestions where needed.
84
- 4. Verify responses against the source document.
85
- 5. Export/import progress.
562
+ | Name | Role |
563
+ | --- | --- |
564
+ | `QuestionnaireTemplate`, `Section`, `Question`, `ValidationRule`, `Mapping`, `Link` | JSON template model for forms and AI metadata. |
565
+ | `LLMService`, `LLMGenerateTextOptions`, `LLMGenerateTextResponse` | Bring-your-own-model contract. |
566
+ | `ScidQuestAdapter` | Higher-level adapter implemented by `createScidQuestAdapter`. |
567
+ | `GenerateSuggestionsRequest`, `GenerateSuggestionsResponse`, `Suggestion`, `FeedbackData` | Suggestion API surface. |
568
+ | `VerifyAnswerRequest`, `AIVerificationResult` | Verification API surface. |
569
+ | `ResearchQuestionnaireWorkspaceValue`, `ResearchQuestionnaireSlotRenderProps` | Alias pair for embed slot props / workspace context. |
570
+ | `PDFUploadProps`, `PdfViewerProps`, `TemplateQuestionnaireProps`, `ResearchQuestionnaireAppProps` | Component prop types. |
571
+ | `ResearchFieldAiQuestionType`, `ResearchQuestionnaireFieldAiWrapperProps` | Field wrapper typing. |
572
+
573
+ Consult `dist/index.d.ts` after `npm run build` for the authoritative export list.
86
574
 
87
575
  [(back to top)](#table-of-contents)
88
576
 
89
577
  ## Configuration
90
578
 
91
- Create a local environment file:
579
+ When developing **this** repository locally, copy environment examples if present and configure provider keys for standalone demos:
92
580
 
93
581
  ```bash
94
582
  cp .env.example .env
95
583
  ```
96
584
 
97
- Configure provider keys in `.env` (or through the UI):
98
-
99
- - OpenAI
100
- - Groq
101
- - Mistral
585
+ Consumer applications supply credentials inside their own **`LLMService`** implementation; the library does not read `.env` in the host app for you.
102
586
 
103
587
  [(back to top)](#table-of-contents)
104
588
 
@@ -114,6 +598,13 @@ ScidQuest expects the following peer dependencies in the host project:
114
598
 
115
599
  [(back to top)](#table-of-contents)
116
600
 
601
+ ## Further reading
602
+
603
+ - [docs/EMBEDDING.md](./docs/EMBEDDING.md) — embed mode, `QuestionnaireAIProvider`, bundler notes.
604
+ - [src/lib/examples/ExampleUsage.tsx](./src/lib/examples/ExampleUsage.tsx) — mock LLM, caching, and rate-limit patterns.
605
+
606
+ [(back to top)](#table-of-contents)
607
+
117
608
  ## Development
118
609
 
119
610
  Run the package locally:
@@ -142,16 +633,18 @@ npm run preview
142
633
  ```text
143
634
  ScidQuest/
144
635
  ├── src/
145
- │ ├── pages/ # Page-level views
146
- │ ├── components/ # Reusable UI components
147
- │ ├── services/ # AI/provider and backend integrations
148
- │ ├── utils/ # Shared utility helpers
636
+ │ ├── lib/ # Published library entry (`src/lib/index.ts`)
637
+ │ ├── pages/ # Page-level views (demo app)
638
+ │ ├── components/ # Shared UI (some re-exported by lib)
639
+ │ ├── services/ # AI/provider integrations
640
+ │ ├── utils/ # PDF, suggestions, validation
149
641
  │ ├── templates/ # Questionnaire templates
150
- │ ├── store/ # Application state management
151
- │ ├── context/ # React context providers
152
- │ ├── App.tsx # Root application component
153
- │ ├── Router.tsx # Routing configuration
154
- │ └── main.tsx # Application entrypoint
642
+ │ ├── store/ # Redux (used inside provider)
643
+ │ ├── context/ # Questionnaire AI context
644
+ │ ├── App.tsx
645
+ │ ├── Router.tsx
646
+ │ └── main.tsx
647
+ ├── docs/
155
648
  ├── index.html
156
649
  ├── package.json
157
650
  ├── tsconfig.json