@deepcitation/deepcitation-js 1.1.26 → 1.1.28

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 (79) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +253 -253
  3. package/lib/chunk-2IZXUOQR.js +66 -0
  4. package/lib/chunk-4FGOHQFP.cjs +66 -0
  5. package/lib/chunk-CFXDRAJL.cjs +1 -0
  6. package/lib/chunk-DEUSSEFH.js +2 -0
  7. package/lib/chunk-F2MMVEVC.cjs +1 -0
  8. package/lib/chunk-J7U6YFOI.cjs +2 -0
  9. package/lib/chunk-O2XFH626.js +1 -0
  10. package/lib/chunk-RQPZSRID.js +1 -0
  11. package/lib/client/index.cjs +1 -0
  12. package/lib/client/{DeepCitation.d.ts → index.d.cts} +159 -3
  13. package/lib/client/index.d.ts +342 -2
  14. package/lib/client/index.js +1 -1
  15. package/lib/index.cjs +1 -0
  16. package/lib/index.d.cts +127 -0
  17. package/lib/index.d.ts +126 -23
  18. package/lib/index.js +1 -22
  19. package/lib/prompts/index.cjs +1 -0
  20. package/lib/prompts/index.d.cts +196 -0
  21. package/lib/prompts/index.d.ts +196 -3
  22. package/lib/prompts/index.js +1 -3
  23. package/lib/react/index.cjs +4 -0
  24. package/lib/react/index.js +4 -18
  25. package/lib/types/index.cjs +1 -0
  26. package/lib/types/index.d.cts +96 -0
  27. package/lib/types/index.d.ts +96 -11
  28. package/lib/types/index.js +1 -7
  29. package/package.json +62 -10
  30. package/lib/client/DeepCitation.js +0 -374
  31. package/lib/client/types.d.ts +0 -154
  32. package/lib/client/types.js +0 -1
  33. package/lib/parsing/normalizeCitation.d.ts +0 -5
  34. package/lib/parsing/normalizeCitation.js +0 -198
  35. package/lib/parsing/parseCitation.d.ts +0 -79
  36. package/lib/parsing/parseCitation.js +0 -431
  37. package/lib/parsing/parseWorkAround.d.ts +0 -2
  38. package/lib/parsing/parseWorkAround.js +0 -73
  39. package/lib/prompts/citationPrompts.d.ts +0 -138
  40. package/lib/prompts/citationPrompts.js +0 -168
  41. package/lib/prompts/promptCompression.d.ts +0 -14
  42. package/lib/prompts/promptCompression.js +0 -127
  43. package/lib/prompts/types.d.ts +0 -4
  44. package/lib/prompts/types.js +0 -1
  45. package/lib/react/CitationComponent.d.ts +0 -93
  46. package/lib/react/CitationComponent.js +0 -371
  47. package/lib/react/CitationVariants.d.ts +0 -132
  48. package/lib/react/CitationVariants.js +0 -284
  49. package/lib/react/DiffDisplay.d.ts +0 -10
  50. package/lib/react/DiffDisplay.js +0 -33
  51. package/lib/react/Popover.d.ts +0 -15
  52. package/lib/react/Popover.js +0 -20
  53. package/lib/react/UrlCitationComponent.d.ts +0 -83
  54. package/lib/react/UrlCitationComponent.js +0 -224
  55. package/lib/react/VerificationTabs.d.ts +0 -10
  56. package/lib/react/VerificationTabs.js +0 -36
  57. package/lib/react/icons.d.ts +0 -18
  58. package/lib/react/icons.js +0 -16
  59. package/lib/react/index.d.ts +0 -16
  60. package/lib/react/primitives.d.ts +0 -101
  61. package/lib/react/primitives.js +0 -193
  62. package/lib/react/types.d.ts +0 -283
  63. package/lib/react/types.js +0 -1
  64. package/lib/react/useSmartDiff.d.ts +0 -16
  65. package/lib/react/useSmartDiff.js +0 -64
  66. package/lib/react/utils.d.ts +0 -43
  67. package/lib/react/utils.js +0 -89
  68. package/lib/types/boxes.d.ts +0 -11
  69. package/lib/types/boxes.js +0 -1
  70. package/lib/types/citation.d.ts +0 -39
  71. package/lib/types/citation.js +0 -1
  72. package/lib/types/search.d.ts +0 -19
  73. package/lib/types/search.js +0 -1
  74. package/lib/types/verification.d.ts +0 -27
  75. package/lib/types/verification.js +0 -11
  76. package/lib/utils/diff.d.ts +0 -60
  77. package/lib/utils/diff.js +0 -414
  78. package/lib/utils/sha.d.ts +0 -10
  79. package/lib/utils/sha.js +0 -108
@@ -1,168 +0,0 @@
1
- export const CITATION_MARKDOWN_SYNTAX_PROMPT = `
2
- Citation syntax to use within Markdown:
3
- • To support any ideas or information that requires a citation from the provided content, use the following citation syntax:
4
- <cite attachment_id='attachment_id' start_page_key='page_number_PAGE_index_INDEX' full_phrase='the verbatim text of the terse phrase inside <attachment_text />; remember to escape quotes and newlines inside the full_phrase to remain as valid JSON' key_span='the verbatim 1-3 words within full_phrase that best support the citation' line_ids='2-6' reasoning='the terse logic used to conclude the citation' />
5
-
6
- • Very important: for page numbers, only use the page number and page index info from the page_number_PAGE_index_INDEX format (e.g. <page_number_1_index_0>) and never from the contents inside the page.
7
- • start_page_key, full_phrase, and line_ids are required for each citation.
8
- • Infer line_ids, as we only provide the first, last, and every 5th line. When copying a previous <cite />, use the full info from the previous citation without changing the start_page_key, line_ids, or any other <cite /> attributes.
9
- • Use refer to line_ids inclusively, and use a range (or single) for each citation, split multiple sequential line_ids into multiple citations.
10
- • These citations will be replaced and displayed in-line as a numeric element (e.g. [1]), the markdown preceding <cite /> should read naturally with only one <cite /> per sentence with rare exceptions for two <cite /> in a sentence. <cite /> often present best at the end of the sentence, and are not grouped at the end of the document.
11
- • The full_phrase should be the exact verbatim text of the phrase or paragraph from the source document to support the insight or idea.
12
- • We do NOT put the full_phrase inside <cite ...></cite>; we only use full_phrase inside the full_phrase attribute.
13
- `;
14
- export const AV_CITATION_MARKDOWN_SYNTAX_PROMPT = `
15
- • To support any ideas or information that requires a citation from the provided content, use the following citation syntax:
16
- <cite attachment_id='attachment_id' full_phrase='the verbatim text of the phrase; remember to escape quotes and newlines inside the full_phrase to remain as valid JSON' timestamps='HH:MM:SS.SSS-HH:MM:SS.SSS' reasoning='the logic connecting the form section requirements to the supporting source citation' />
17
- • These citations are displayed in-line or in the relevant list item, and are not grouped at the end of the document.
18
- `;
19
- /**
20
- * Wraps your existing system prompt with DeepCitation's citation syntax instructions.
21
- * This enables LLMs to output verifiable citations that can be checked against source documents.
22
- *
23
- * @example
24
- * ```typescript
25
- * import { wrapSystemCitationPrompt } from '@deepcitation/deepcitation-js';
26
- *
27
- * const systemPrompt = "You are a helpful assistant that analyzes documents.";
28
- * const enhanced = wrapSystemCitationPrompt({ systemPrompt });
29
- *
30
- * // Use enhanced prompt with your LLM
31
- * const response = await openai.chat.completions.create({
32
- * messages: [{ role: "system", content: enhanced }],
33
- * // ...
34
- * });
35
- * ```
36
- */
37
- export function wrapSystemCitationPrompt(options) {
38
- const { systemPrompt, isAudioVideo = false, prependCitationInstructions = false, } = options;
39
- const citationPrompt = isAudioVideo
40
- ? AV_CITATION_MARKDOWN_SYNTAX_PROMPT
41
- : CITATION_MARKDOWN_SYNTAX_PROMPT;
42
- if (prependCitationInstructions) {
43
- return `${citationPrompt.trim()}
44
-
45
- ${systemPrompt.trim()}`;
46
- }
47
- //append
48
- return `${systemPrompt.trim()}
49
-
50
- ${citationPrompt.trim()}`;
51
- }
52
- /**
53
- * Wraps both system and user prompts with DeepCitation's citation syntax instructions.
54
- * This is the recommended way to prepare prompts for citation verification.
55
- *
56
- * @example
57
- * ```typescript
58
- * import { wrapCitationPrompt } from '@deepcitation/deepcitation-js';
59
- *
60
- * // Single file
61
- * const { enhancedSystemPrompt, enhancedUserPrompt } = wrapCitationPrompt({
62
- * systemPrompt: "You are a helpful assistant.",
63
- * userPrompt: "Analyze this document and summarize it.",
64
- * deepTextPromptPortion, // from uploadFile response
65
- * });
66
- *
67
- * // Multiple files
68
- * const { enhancedSystemPrompt, enhancedUserPrompt } = wrapCitationPrompt({
69
- * systemPrompt: "You are a helpful assistant.",
70
- * userPrompt: "Compare these documents.",
71
- * deepTextPromptPortion: [deepTextPromptPortion1, deepTextPromptPortion2], // array of file texts
72
- * });
73
- *
74
- * // Use enhanced prompts with your LLM
75
- * const response = await llm.chat({
76
- * messages: [
77
- * { role: "system", content: enhancedSystemPrompt },
78
- * { role: "user", content: enhancedUserPrompt },
79
- * ],
80
- * });
81
- * ```
82
- */
83
- export function wrapCitationPrompt(options) {
84
- const { systemPrompt, userPrompt, deepTextPromptPortion, isAudioVideo = false, } = options;
85
- const enhancedSystemPrompt = wrapSystemCitationPrompt({
86
- systemPrompt,
87
- isAudioVideo,
88
- });
89
- // Build enhanced user prompt with file content if provided
90
- let enhancedUserPrompt = userPrompt;
91
- if (deepTextPromptPortion) {
92
- const fileTexts = Array.isArray(deepTextPromptPortion)
93
- ? deepTextPromptPortion
94
- : [deepTextPromptPortion];
95
- const fileContent = fileTexts
96
- .map((text, index) => {
97
- if (fileTexts.length === 1) {
98
- return `\n${text}`;
99
- }
100
- return `\n${text}`;
101
- })
102
- .join("\n\n");
103
- enhancedUserPrompt = `${fileContent}\n\n${userPrompt}`;
104
- }
105
- return {
106
- enhancedSystemPrompt,
107
- enhancedUserPrompt,
108
- };
109
- }
110
- export const CITATION_JSON_OUTPUT_FORMAT = {
111
- type: "object",
112
- properties: {
113
- attachmentId: { type: "string" },
114
- startPageKey: {
115
- type: "string",
116
- description: 'Only return a result like "page_number_PAGE_index_INDEX" from the provided page keys (e.g. <page_number_1_index_0>) and never from the contents inside the page.',
117
- },
118
- reasoning: {
119
- type: "string",
120
- description: "The logic connecting the form section requirements to the supporting source citation",
121
- },
122
- fullPhrase: {
123
- type: "string",
124
- description: "The verbatim text of the terse phrase inside <attachment_text /> to support the citation (if there is a detected OCR correction, use the corrected text)",
125
- },
126
- keySpan: {
127
- type: "string",
128
- description: "the verbatim 1-3 words within fullPhrase that best support the citation",
129
- },
130
- lineIds: {
131
- type: "array",
132
- items: { type: "number" },
133
- description: "Infer lineIds, as we only provide the first, last, and every 5th line. Provide inclusive lineIds for the fullPhrase.",
134
- },
135
- },
136
- required: [
137
- "attachmentId",
138
- "startPageKey",
139
- "reasoning",
140
- "fullPhrase",
141
- "keySpan",
142
- "lineIds",
143
- ],
144
- };
145
- export const CITATION_AV_BASED_JSON_OUTPUT_FORMAT = {
146
- type: "object",
147
- properties: {
148
- attachmentId: { type: "string" },
149
- startPageKey: {
150
- type: "string",
151
- description: 'Only return a result like "page_number_PAGE_index_INDEX" from the provided page keys (e.g. <page_number_1_index_0>) and never from the contents inside the page.',
152
- },
153
- fullPhrase: {
154
- type: "string",
155
- description: "The exact verbatim text of the phrase or paragraph from the source document to support the citation (if there is a detected OCR correction, use the verbatim corrected text)",
156
- },
157
- timestamps: {
158
- type: "object",
159
- properties: {
160
- startTime: { type: "string" },
161
- endTime: { type: "string" },
162
- },
163
- required: ["startTime", "endTime"],
164
- description: "The timestamp of the audio or video frame including milliseconds formatted as: HH:MM:SS.SSS",
165
- },
166
- },
167
- required: ["attachmentId", "startPageKey", "fullPhrase", "timestamps"],
168
- };
@@ -1,14 +0,0 @@
1
- import { CompressedResult } from "./types.js";
2
- /**
3
- * Compress all occurrences of `ids` inside `obj`, returning a new object
4
- * plus the `prefixMap` needed to decompress.
5
- */
6
- export declare function compressPromptIds<T>(obj: T, ids: string[] | undefined): CompressedResult<T>;
7
- /**
8
- * Decompress all minimal prefixes back into their full IDs,
9
- * using the `prefixMap` returned from `compressPromptIds`.
10
- *
11
- * If you pass in a string, it will return a string.
12
- * If you pass in an object, it will JSON‑serialize and parse it back.
13
- */
14
- export declare function decompressPromptIds<T>(compressed: T | string, prefixMap: Record<string, string>): T | string;
@@ -1,127 +0,0 @@
1
- const MIN_PREFIX_LENGTH = 4;
2
- const MIN_CHARACTERS_PER_PREFIX_WITH_AT_LEAST_ONE_DIGIT = 3;
3
- const MIN_CHARACTERS_PER_PREFIX_WITH_NO_DIGITS = 5;
4
- /**
5
- * Build a map from each ID's minimal unique prefix to the full ID,
6
- * such that the prefix only ever appears in the prompt where the full ID appears.
7
- */
8
- function buildSafePrefixMap(ids, prompt) {
9
- const map = {};
10
- for (const id of ids) {
11
- for (let len = MIN_PREFIX_LENGTH; len <= id.length; len++) {
12
- const prefix = id.slice(0, len);
13
- // Check minimum requirements
14
- const digitCount = (prefix.match(/\d/g) || []).length;
15
- const letterCount = (prefix.match(/[a-zA-Z]/g) || []).length;
16
- if (prefix.length < MIN_PREFIX_LENGTH ||
17
- (digitCount > 0 &&
18
- letterCount < MIN_CHARACTERS_PER_PREFIX_WITH_AT_LEAST_ONE_DIGIT) ||
19
- (digitCount === 0 &&
20
- letterCount < MIN_CHARACTERS_PER_PREFIX_WITH_NO_DIGITS)) {
21
- continue;
22
- }
23
- // 1) Unique among IDs
24
- if (ids.some((other) => other !== id && other.startsWith(prefix))) {
25
- continue;
26
- }
27
- // 2) Only appears in prompt as part of the full ID
28
- const esc = (s) => s.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&");
29
- const prefixCount = (prompt.match(new RegExp(esc(prefix), "g")) || [])
30
- .length;
31
- const fullCount = (prompt.match(new RegExp(esc(id), "g")) || []).length;
32
- if (prefixCount !== fullCount) {
33
- continue;
34
- }
35
- map[prefix] = id;
36
- break;
37
- }
38
- if (!Object.values(map).includes(id)) {
39
- throw new Error(`Cannot find a safe unique prefix for ID "${id}" that meets the minimum requirements (length: ${MIN_PREFIX_LENGTH})`);
40
- }
41
- }
42
- return map;
43
- }
44
- /**
45
- * Compress all occurrences of `ids` inside `obj`, returning a new object
46
- * plus the `prefixMap` needed to decompress.
47
- */
48
- export function compressPromptIds(obj, ids) {
49
- if (!ids || ids.length === 0) {
50
- return { compressed: obj, prefixMap: {} };
51
- }
52
- const uniqueIds = Array.from(new Set(ids));
53
- const text = JSON.stringify(obj);
54
- const prefixMap = buildSafePrefixMap(uniqueIds, text);
55
- // Sort prefixes by descending length to avoid partial matches
56
- const prefixes = Object.keys(prefixMap).sort((a, b) => b.length - a.length);
57
- let compressedText = text;
58
- for (const prefix of prefixes) {
59
- const full = prefixMap[prefix];
60
- const escFull = full.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&");
61
- compressedText = compressedText.replace(new RegExp(escFull, "g"), prefix);
62
- }
63
- return {
64
- compressed: JSON.parse(compressedText),
65
- prefixMap,
66
- };
67
- }
68
- /**
69
- * Decompress all minimal prefixes back into their full IDs,
70
- * using the `prefixMap` returned from `compressPromptIds`.
71
- *
72
- * If you pass in a string, it will return a string.
73
- * If you pass in an object, it will JSON‑serialize and parse it back.
74
- */
75
- export function decompressPromptIds(compressed, prefixMap) {
76
- if (!prefixMap || Object.keys(prefixMap).length === 0) {
77
- return compressed;
78
- }
79
- // Prepare sorted [prefix, full] entries (longest prefix first)
80
- const entries = Object.entries(prefixMap).sort((a, b) => b[0].length - a[0].length);
81
- // Decide whether we're working on a string or an object
82
- let text;
83
- let shouldParseBack = false;
84
- if (typeof compressed === "string") {
85
- text = compressed;
86
- }
87
- else {
88
- text = JSON.stringify(compressed);
89
- shouldParseBack = true;
90
- }
91
- const originalLength = text?.length;
92
- // Perform all prefix → full-ID replacements
93
- for (const [prefix, full] of entries) {
94
- const escPrefix = prefix.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&");
95
- text = text.replace(new RegExp(escPrefix, "g"), full);
96
- }
97
- // Handle cases where the LLM may output ID in a different attribute format
98
- // We look for common ID attribute patterns and replace compressed prefixes within them
99
- // Note: fileId variants are supported for backwards compatibility with legacy citations
100
- const idAttributeKeys = [
101
- "attachmentId",
102
- "attachment_id",
103
- "attachment_ID",
104
- "attachmentID",
105
- "fileId",
106
- "file_id",
107
- "file_ID",
108
- "fileID",
109
- "fileid",
110
- ];
111
- // For each prefix, look for it within ID attribute values and replace with full ID
112
- for (const [prefix, full] of entries) {
113
- const escPrefix = prefix.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&");
114
- const keyPattern = idAttributeKeys.join("|");
115
- const quotePattern = "([\"'`])";
116
- // Match: attributeName = 'prefix' or attributeName="prefix" etc.
117
- // Only replace the prefix part, preserving the attribute name and quotes
118
- const re = new RegExp(`(${keyPattern})(\\s*=\\s*)${quotePattern}${escPrefix}\\3`, "g");
119
- text = text.replace(re, `$1$2$3${full}$3`);
120
- }
121
- const newLength = text?.length;
122
- const diff = originalLength - newLength;
123
- if (diff > 0) {
124
- throw new Error(`[decompressedPromptIds] diff ${diff} originalLength ${originalLength} newLength ${newLength}`);
125
- }
126
- return shouldParseBack ? JSON.parse(text) : text;
127
- }
@@ -1,4 +0,0 @@
1
- export interface CompressedResult<T> {
2
- compressed: T;
3
- prefixMap: Record<string, string>;
4
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,93 +0,0 @@
1
- import React from "react";
2
- import { type CitationStatus } from "../types/citation.js";
3
- import type { Verification } from "../types/verification.js";
4
- import type { BaseCitationProps, CitationBehaviorConfig, CitationEventHandlers, CitationRenderProps, CitationVariant } from "./types.js";
5
- export type { CitationVariant } from "./types.js";
6
- /**
7
- * Props for the CitationComponent.
8
- *
9
- * ## Behavior
10
- *
11
- * Default interaction pattern:
12
- * - **Hover**: Shows popover with verification image/details
13
- * - **Click**: Opens full-size image overlay (zoom)
14
- * - **Escape / Click outside / Click overlay**: Closes image overlay
15
- *
16
- * Custom behavior:
17
- * - Use `behaviorConfig.onClick` to replace the default click behavior
18
- * - Use `eventHandlers.onClick` to add side effects (disables default)
19
- *
20
- * @example Default usage
21
- * ```tsx
22
- * <CitationComponent
23
- * citation={citation}
24
- * verification={verification}
25
- * />
26
- * ```
27
- *
28
- * @example Custom click behavior
29
- * ```tsx
30
- * <CitationComponent
31
- * citation={citation}
32
- * verification={verification}
33
- * behaviorConfig={{
34
- * onClick: (context) => {
35
- * // Custom action
36
- * console.log('Clicked:', context.citationKey);
37
- * return { setImageExpanded: true };
38
- * }
39
- * }}
40
- * />
41
- * ```
42
- */
43
- export interface CitationComponentProps extends BaseCitationProps {
44
- /** Verification result from the DeepCitation API */
45
- verification?: Verification | null;
46
- /**
47
- * Display variant for the citation.
48
- * - `brackets`: [keySpan✓] with styling (default)
49
- * - `text`: keySpan✓ inherits parent styling
50
- * - `minimal`: text with indicator, no brackets
51
- * - `indicator`: only the status indicator
52
- */
53
- variant?: CitationVariant;
54
- /** Hide square brackets (only for brackets variant) */
55
- hideBrackets?: boolean;
56
- /** Event handlers for citation interactions */
57
- eventHandlers?: CitationEventHandlers;
58
- /**
59
- * Configuration for customizing default click/hover behaviors.
60
- * Providing onClick REPLACES the default click behavior.
61
- */
62
- behaviorConfig?: CitationBehaviorConfig;
63
- /** Enable mobile touch handlers */
64
- isMobile?: boolean;
65
- /** Custom render function for the status indicator */
66
- renderIndicator?: (status: CitationStatus) => React.ReactNode;
67
- /** Custom render function for entire citation content */
68
- renderContent?: (props: CitationRenderProps) => React.ReactNode;
69
- /** Position of popover. Use "hidden" to disable. */
70
- popoverPosition?: "top" | "bottom" | "hidden";
71
- /** Custom render function for popover content */
72
- renderPopoverContent?: (props: {
73
- citation: BaseCitationProps["citation"];
74
- verification: Verification | null;
75
- status: CitationStatus;
76
- }) => React.ReactNode;
77
- }
78
- /**
79
- * CitationComponent displays a citation with verification status.
80
- *
81
- * ## Interaction Pattern
82
- *
83
- * - **Hover**: Shows popover with verification image or details
84
- * - **Click**: Opens full-size image overlay (if image available)
85
- * - **Escape / Click overlay**: Closes the image overlay
86
- *
87
- * ## Customization
88
- *
89
- * Use `behaviorConfig.onClick` to completely replace the click behavior,
90
- * or `eventHandlers.onClick` to add side effects (which disables defaults).
91
- */
92
- export declare const CitationComponent: React.ForwardRefExoticComponent<CitationComponentProps & React.RefAttributes<HTMLSpanElement>>;
93
- export declare const MemoizedCitationComponent: React.NamedExoticComponent<CitationComponentProps & React.RefAttributes<HTMLSpanElement>>;