@deepcitation/deepcitation-js 1.1.1 → 1.1.2

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.
package/README.md CHANGED
@@ -18,6 +18,12 @@
18
18
 
19
19
  LLMs hallucinate. Even when given source documents, they make up quotes, invent statistics, and cite pages that don't exist. DeepCitation solves this by **deterministically verifying every citation** against your source documents—and generating visual proof.
20
20
 
21
+ <div align="center">
22
+ <img src="./examples/assets/deepcitation-medical-demo.gif" alt="DeepCitation medical documentation demo showing verified inline citations" width="700" />
23
+ <br />
24
+ <em>Medical documentation with verified inline citations — certainty at a glance</em>
25
+ </div>
26
+
21
27
  ```
22
28
  Before: "Revenue grew 45% [1]" → ❓ Did the LLM make this up?
23
29
  After: "Revenue grew 45% [1]" → ✅ Verified on page 3, line 12 (with screenshot)
@@ -59,7 +65,7 @@ import { DeepCitation, wrapCitationPrompt } from "@deepcitation/deepcitation-js"
59
65
 
60
66
  const dc = new DeepCitation({ apiKey: process.env.DEEPCITATION_API_KEY });
61
67
 
62
- // Upload source files
68
+ // Upload source files, this can be done before the user types their prompt
63
69
  const { fileDataParts, deepTextPromptPortion } = await dc.prepareFiles([
64
70
  { file: pdfBuffer, filename: "report.pdf" },
65
71
  ]);
@@ -87,31 +93,49 @@ Verify citations against the source documents.
87
93
  ```typescript
88
94
  const result = await dc.verifyCitations({
89
95
  llmOutput: response.content,
90
- fileDataParts,
96
+ fileDataParts, //optional
91
97
  });
92
98
 
93
- // result.citations contains verification status + visual proof
99
+ // result.verifications contains verification status + visual proof
100
+ const { citations, verifications } = result;
101
+
94
102
  ```
95
103
 
96
104
  ### Step 3: Display
97
105
 
98
- Render verified citations with React components.
106
+ Parse the LLM output and render verified citations inline with React components.
99
107
 
100
108
  ```tsx
101
109
  import { CitationComponent } from "@deepcitation/deepcitation-js/react";
110
+ import {
111
+ parseCitation,
112
+ generateCitationKey,
113
+ } from "@deepcitation/deepcitation-js";
102
114
  import "@deepcitation/deepcitation-js/react/styles.css";
103
115
 
104
- function Response({ citations, verifications }) {
105
- return (
106
- <p>
107
- Revenue grew by
108
- <CitationComponent
109
- citation={citations["1"]}
110
- verification={verifications["1"]}
111
- />
112
- this quarter.
113
- </p>
114
- );
116
+ function Response({ llmOutput, verifications }) {
117
+ // Split LLM output by citation tags and render inline
118
+ const renderWithCitations = (text: string) => {
119
+ const parts = text.split(/(<cite\s+[^>]*\/>)/g);
120
+
121
+ return parts.map((part, index) => {
122
+ if (part.startsWith("<cite")) {
123
+ const { citation } = parseCitation(part);
124
+ const citationKey = generateCitationKey(citation);
125
+
126
+ return (
127
+ <CitationComponent
128
+ key={index}
129
+ citation={citation}
130
+ verification={verifications[citationKey]}
131
+ />
132
+ );
133
+ }
134
+ return <span key={index}>{part}</span>;
135
+ });
136
+ };
137
+
138
+ return <div>{renderWithCitations(llmOutput)}</div>;
115
139
  }
116
140
  ```
117
141
 
@@ -123,8 +147,7 @@ function Response({ citations, verifications }) {
123
147
 
124
148
  ```typescript
125
149
  const dc = new DeepCitation({
126
- apiKey: string, // Your API key (sk-dc-*)
127
- apiUrl?: string, // Optional: Custom API URL
150
+ apiKey: string // Your API key (sk-dc-*)
128
151
  });
129
152
 
130
153
  // Upload and prepare source files
@@ -100,8 +100,9 @@ export const parseCitation = (fragment, mdAttachmentId, citationCounterRef, isVe
100
100
  const citationMatches = [...middleCite.matchAll(citationRegex)];
101
101
  const match = citationMatches?.[0];
102
102
  const pageNumber = match?.[2] ? parseInt(match?.[2]) : undefined;
103
+ const pageIndex = match?.[3] ? parseInt(match?.[3]) : undefined;
103
104
  let fileId = match?.[1];
104
- let attachmentId = fileId?.length === 20 ? fileId : mdAttachmentId || match?.[1];
105
+ let attachmentId = fileId?.length === 20 ? fileId : mdAttachmentId || fileId;
105
106
  // Use helper to handle escaped quotes inside the phrase
106
107
  let fullPhrase = cleanAndUnescape(match?.[4]);
107
108
  let keySpan = cleanAndUnescape(match?.[5]);
@@ -138,8 +139,7 @@ export const parseCitation = (fragment, mdAttachmentId, citationCounterRef, isVe
138
139
  let timestamps;
139
140
  if (avMatch) {
140
141
  fileId = avMatch?.[1];
141
- attachmentId =
142
- fileId?.length === 20 ? fileId : mdAttachmentId || avMatch?.[1];
142
+ attachmentId = fileId?.length === 20 ? fileId : mdAttachmentId || fileId;
143
143
  fullPhrase = cleanAndUnescape(avMatch?.[2]);
144
144
  const timestampsString = avMatch?.[3]?.replace(/timestamps=['"]|['"]/g, "");
145
145
  const [startTime, endTime] = timestampsString?.split("-") || [];
@@ -156,6 +156,7 @@ export const parseCitation = (fragment, mdAttachmentId, citationCounterRef, isVe
156
156
  const citation = {
157
157
  fileId: attachmentId,
158
158
  pageNumber,
159
+ startPageKey: `page_number_${pageNumber || 1}_index_${pageIndex || 0}`,
159
160
  fullPhrase,
160
161
  keySpan,
161
162
  citationNumber,
@@ -194,13 +195,21 @@ const parseJsonCitation = (jsonCitation, citationNumber) => {
194
195
  if (!fullPhrase) {
195
196
  return null;
196
197
  }
197
- // Parse startPageKey format: "page_number_PAGE_index_INDEX"
198
+ // Parse startPageKey format: "page_number_PAGE_index_INDEX" or simple "PAGE_INDEX"
198
199
  let pageNumber;
199
200
  if (startPageKey) {
201
+ // Try full format first: page_number_5_index_2 or pageKey_5_index_2
200
202
  const pageMatch = startPageKey.match(/page[_a-zA-Z]*(\d+)_index_(\d+)/i);
201
203
  if (pageMatch) {
202
204
  pageNumber = parseInt(pageMatch[1], 10);
203
205
  }
206
+ else {
207
+ // Try simple n_m format: 5_4 (page 5, index 4)
208
+ const simpleMatch = startPageKey.match(/^(\d+)_(\d+)$/);
209
+ if (simpleMatch) {
210
+ pageNumber = parseInt(simpleMatch[1], 10);
211
+ }
212
+ }
204
213
  }
205
214
  // Sort lineIds if present
206
215
  const lineIds = rawLineIds?.length
@@ -8,8 +8,8 @@ export function generateCitationKey(citation) {
8
8
  citation.fileId || "",
9
9
  citation.pageNumber?.toString() || "",
10
10
  citation.fullPhrase || "",
11
+ citation.keySpan?.toString() || "",
11
12
  citation.value || "",
12
- citation.citationNumber?.toString() || "",
13
13
  citation.lineIds?.join(",") || "",
14
14
  citation.timestamps?.startTime || "",
15
15
  citation.timestamps?.endTime || "",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepcitation/deepcitation-js",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "DeepCitation JavaScript SDK for deterministic AI citation verification",
5
5
  "type": "module",
6
6
  "private": false,