ArnoChen
commited on
Commit
·
dff8a57
1
Parent(s):
29ee1c2
add query settings
Browse files- lightrag_webui/bun.lock +6 -0
- lightrag_webui/index.html +1 -1
- lightrag_webui/package.json +2 -0
- lightrag_webui/src/api/lightrag.ts +40 -2
- lightrag_webui/src/components/Settings.tsx +1 -1
- lightrag_webui/src/components/{document → documents}/ClearDocumentsDialog.tsx +0 -0
- lightrag_webui/src/components/{document → documents}/UploadDocumentsDialog.tsx +0 -0
- lightrag_webui/src/components/retrieval/QuerySettings.tsx +279 -0
- lightrag_webui/src/components/ui/Checkbox.tsx +1 -1
- lightrag_webui/src/components/ui/NumberInput.tsx +131 -0
- lightrag_webui/src/components/ui/Select.tsx +151 -0
- lightrag_webui/src/components/ui/Tooltip.tsx +15 -3
- lightrag_webui/src/features/DocumentManager.tsx +2 -2
- lightrag_webui/src/features/RetrievalTesting.tsx +80 -83
- lightrag_webui/src/stores/settings.ts +46 -7
lightrag_webui/bun.lock
CHANGED
@@ -10,6 +10,7 @@
|
|
10 |
"@radix-ui/react-popover": "^1.1.6",
|
11 |
"@radix-ui/react-progress": "^1.1.2",
|
12 |
"@radix-ui/react-scroll-area": "^1.2.3",
|
|
|
13 |
"@radix-ui/react-separator": "^1.1.2",
|
14 |
"@radix-ui/react-slot": "^1.1.2",
|
15 |
"@radix-ui/react-tabs": "^1.1.3",
|
@@ -37,6 +38,7 @@
|
|
37 |
"react": "^19.0.0",
|
38 |
"react-dom": "^19.0.0",
|
39 |
"react-dropzone": "^14.3.5",
|
|
|
40 |
"seedrandom": "^3.0.5",
|
41 |
"sigma": "^3.0.1",
|
42 |
"sonner": "^1.7.4",
|
@@ -255,6 +257,8 @@
|
|
255 |
|
256 |
"@radix-ui/react-scroll-area": ["@radix-ui/[email protected]", "", { "dependencies": { "@radix-ui/number": "1.1.0", "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-direction": "1.1.0", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-l7+NNBfBYYJa9tNqVcP2AGvxdE3lmE6kFTBXdvHgUaZuy+4wGCL1Cl2AfaR7RKyimj7lZURGLwFO59k4eBnDJQ=="],
|
257 |
|
|
|
|
|
258 |
"@radix-ui/react-separator": ["@radix-ui/[email protected]", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-oZfHcaAp2Y6KFBX6I5P1u7CQoy4lheCGiYj+pGFrHy8E/VNRb5E39TkTr3JrV520csPBTZjkuKFdEsjS5EUNKQ=="],
|
259 |
|
260 |
"@radix-ui/react-slot": ["@radix-ui/[email protected]", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ=="],
|
@@ -899,6 +903,8 @@
|
|
899 |
|
900 |
"react-is": ["[email protected]", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
901 |
|
|
|
|
|
902 |
"react-remove-scroll": ["[email protected]", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ=="],
|
903 |
|
904 |
"react-remove-scroll-bar": ["[email protected]", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="],
|
|
|
10 |
"@radix-ui/react-popover": "^1.1.6",
|
11 |
"@radix-ui/react-progress": "^1.1.2",
|
12 |
"@radix-ui/react-scroll-area": "^1.2.3",
|
13 |
+
"@radix-ui/react-select": "^2.1.6",
|
14 |
"@radix-ui/react-separator": "^1.1.2",
|
15 |
"@radix-ui/react-slot": "^1.1.2",
|
16 |
"@radix-ui/react-tabs": "^1.1.3",
|
|
|
38 |
"react": "^19.0.0",
|
39 |
"react-dom": "^19.0.0",
|
40 |
"react-dropzone": "^14.3.5",
|
41 |
+
"react-number-format": "^5.4.3",
|
42 |
"seedrandom": "^3.0.5",
|
43 |
"sigma": "^3.0.1",
|
44 |
"sonner": "^1.7.4",
|
|
|
257 |
|
258 |
"@radix-ui/react-scroll-area": ["@radix-ui/[email protected]", "", { "dependencies": { "@radix-ui/number": "1.1.0", "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-direction": "1.1.0", "@radix-ui/react-presence": "1.1.2", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-l7+NNBfBYYJa9tNqVcP2AGvxdE3lmE6kFTBXdvHgUaZuy+4wGCL1Cl2AfaR7RKyimj7lZURGLwFO59k4eBnDJQ=="],
|
259 |
|
260 |
+
"@radix-ui/react-select": ["@radix-ui/[email protected]", "", { "dependencies": { "@radix-ui/number": "1.1.0", "@radix-ui/primitive": "1.1.1", "@radix-ui/react-collection": "1.1.2", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-direction": "1.1.0", "@radix-ui/react-dismissable-layer": "1.1.5", "@radix-ui/react-focus-guards": "1.1.1", "@radix-ui/react-focus-scope": "1.1.2", "@radix-ui/react-id": "1.1.0", "@radix-ui/react-popper": "1.2.2", "@radix-ui/react-portal": "1.1.4", "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-slot": "1.1.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-controllable-state": "1.1.0", "@radix-ui/react-use-layout-effect": "1.1.0", "@radix-ui/react-use-previous": "1.1.0", "@radix-ui/react-visually-hidden": "1.1.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-T6ajELxRvTuAMWH0YmRJ1qez+x4/7Nq7QIx7zJ0VK3qaEWdnWpNbEDnmWldG1zBDwqrLy5aLMUWcoGirVj5kMg=="],
|
261 |
+
|
262 |
"@radix-ui/react-separator": ["@radix-ui/[email protected]", "", { "dependencies": { "@radix-ui/react-primitive": "2.0.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-oZfHcaAp2Y6KFBX6I5P1u7CQoy4lheCGiYj+pGFrHy8E/VNRb5E39TkTr3JrV520csPBTZjkuKFdEsjS5EUNKQ=="],
|
263 |
|
264 |
"@radix-ui/react-slot": ["@radix-ui/[email protected]", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ=="],
|
|
|
903 |
|
904 |
"react-is": ["[email protected]", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
|
905 |
|
906 |
+
"react-number-format": ["[email protected]", "", { "peerDependencies": { "react": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-VCY5hFg/soBighAoGcdE+GagkJq0230qN6jcS5sp8wQX1qy1fYN/RX7/BXkrs0oyzzwqR8/+eSUrqXbGeywdUQ=="],
|
907 |
+
|
908 |
"react-remove-scroll": ["[email protected]", "", { "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", "use-sidecar": "^1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ=="],
|
909 |
|
910 |
"react-remove-scroll-bar": ["[email protected]", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="],
|
lightrag_webui/index.html
CHANGED
@@ -4,7 +4,7 @@
|
|
4 |
<meta charset="UTF-8" />
|
5 |
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
6 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
7 |
-
<title>Lightrag
|
8 |
</head>
|
9 |
<body>
|
10 |
<div id="root"></div>
|
|
|
4 |
<meta charset="UTF-8" />
|
5 |
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
6 |
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
7 |
+
<title>Lightrag</title>
|
8 |
</head>
|
9 |
<body>
|
10 |
<div id="root"></div>
|
lightrag_webui/package.json
CHANGED
@@ -16,6 +16,7 @@
|
|
16 |
"@radix-ui/react-popover": "^1.1.6",
|
17 |
"@radix-ui/react-progress": "^1.1.2",
|
18 |
"@radix-ui/react-scroll-area": "^1.2.3",
|
|
|
19 |
"@radix-ui/react-separator": "^1.1.2",
|
20 |
"@radix-ui/react-slot": "^1.1.2",
|
21 |
"@radix-ui/react-tabs": "^1.1.3",
|
@@ -43,6 +44,7 @@
|
|
43 |
"react": "^19.0.0",
|
44 |
"react-dom": "^19.0.0",
|
45 |
"react-dropzone": "^14.3.5",
|
|
|
46 |
"seedrandom": "^3.0.5",
|
47 |
"sigma": "^3.0.1",
|
48 |
"sonner": "^1.7.4",
|
|
|
16 |
"@radix-ui/react-popover": "^1.1.6",
|
17 |
"@radix-ui/react-progress": "^1.1.2",
|
18 |
"@radix-ui/react-scroll-area": "^1.2.3",
|
19 |
+
"@radix-ui/react-select": "^2.1.6",
|
20 |
"@radix-ui/react-separator": "^1.1.2",
|
21 |
"@radix-ui/react-slot": "^1.1.2",
|
22 |
"@radix-ui/react-tabs": "^1.1.3",
|
|
|
44 |
"react": "^19.0.0",
|
45 |
"react-dom": "^19.0.0",
|
46 |
"react-dropzone": "^14.3.5",
|
47 |
+
"react-number-format": "^5.4.3",
|
48 |
"seedrandom": "^3.0.5",
|
49 |
"sigma": "^3.0.1",
|
50 |
"sonner": "^1.7.4",
|
lightrag_webui/src/api/lightrag.ts
CHANGED
@@ -52,13 +52,52 @@ export type LightragDocumentsScanProgress = {
|
|
52 |
progress: number
|
53 |
}
|
54 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
55 |
export type QueryMode = 'naive' | 'local' | 'global' | 'hybrid' | 'mix'
|
56 |
|
|
|
|
|
|
|
|
|
|
|
57 |
export type QueryRequest = {
|
58 |
query: string
|
|
|
59 |
mode: QueryMode
|
60 |
-
|
61 |
only_need_context?: boolean
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
62 |
}
|
63 |
|
64 |
export type QueryResponse = {
|
@@ -68,7 +107,6 @@ export type QueryResponse = {
|
|
68 |
export type DocumentActionResponse = {
|
69 |
status: 'success' | 'partial_success' | 'failure'
|
70 |
message: string
|
71 |
-
document_count: number
|
72 |
}
|
73 |
|
74 |
export const InvalidApiKeyError = 'Invalid API Key'
|
|
|
52 |
progress: number
|
53 |
}
|
54 |
|
55 |
+
/**
|
56 |
+
* Specifies the retrieval mode:
|
57 |
+
* - "naive": Performs a basic search without advanced techniques.
|
58 |
+
* - "local": Focuses on context-dependent information.
|
59 |
+
* - "global": Utilizes global knowledge.
|
60 |
+
* - "hybrid": Combines local and global retrieval methods.
|
61 |
+
* - "mix": Integrates knowledge graph and vector retrieval.
|
62 |
+
*/
|
63 |
export type QueryMode = 'naive' | 'local' | 'global' | 'hybrid' | 'mix'
|
64 |
|
65 |
+
export type Message = {
|
66 |
+
role: 'user' | 'assistant' | 'system'
|
67 |
+
content: string
|
68 |
+
}
|
69 |
+
|
70 |
export type QueryRequest = {
|
71 |
query: string
|
72 |
+
/** Specifies the retrieval mode. */
|
73 |
mode: QueryMode
|
74 |
+
/** If True, only returns the retrieved context without generating a response. */
|
75 |
only_need_context?: boolean
|
76 |
+
/** If True, only returns the generated prompt without producing a response. */
|
77 |
+
only_need_prompt?: boolean
|
78 |
+
/** Defines the response format. Examples: 'Multiple Paragraphs', 'Single Paragraph', 'Bullet Points'. */
|
79 |
+
response_type?: string
|
80 |
+
/** If True, enables streaming output for real-time responses. */
|
81 |
+
stream?: boolean
|
82 |
+
/** Number of top items to retrieve. Represents entities in 'local' mode and relationships in 'global' mode. */
|
83 |
+
top_k?: number
|
84 |
+
/** Maximum number of tokens allowed for each retrieved text chunk. */
|
85 |
+
max_token_for_text_unit?: number
|
86 |
+
/** Maximum number of tokens allocated for relationship descriptions in global retrieval. */
|
87 |
+
max_token_for_global_context?: number
|
88 |
+
/** Maximum number of tokens allocated for entity descriptions in local retrieval. */
|
89 |
+
max_token_for_local_context?: number
|
90 |
+
/** List of high-level keywords to prioritize in retrieval. */
|
91 |
+
hl_keywords?: string[]
|
92 |
+
/** List of low-level keywords to refine retrieval focus. */
|
93 |
+
ll_keywords?: string[]
|
94 |
+
/**
|
95 |
+
* Stores past conversation history to maintain context.
|
96 |
+
* Format: [{"role": "user/assistant", "content": "message"}].
|
97 |
+
*/
|
98 |
+
conversation_history?: Message[]
|
99 |
+
/** Number of complete conversation turns (user-assistant pairs) to consider in the response context. */
|
100 |
+
history_turns?: number
|
101 |
}
|
102 |
|
103 |
export type QueryResponse = {
|
|
|
107 |
export type DocumentActionResponse = {
|
108 |
status: 'success' | 'partial_success' | 'failure'
|
109 |
message: string
|
|
|
110 |
}
|
111 |
|
112 |
export const InvalidApiKeyError = 'Invalid API Key'
|
lightrag_webui/src/components/Settings.tsx
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/Popover'
|
2 |
-
import
|
3 |
import Button from '@/components/ui/Button'
|
4 |
import Separator from '@/components/ui/Separator'
|
5 |
import Input from '@/components/ui/Input'
|
|
|
1 |
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/Popover'
|
2 |
+
import Checkbox from '@/components/ui/Checkbox'
|
3 |
import Button from '@/components/ui/Button'
|
4 |
import Separator from '@/components/ui/Separator'
|
5 |
import Input from '@/components/ui/Input'
|
lightrag_webui/src/components/{document → documents}/ClearDocumentsDialog.tsx
RENAMED
File without changes
|
lightrag_webui/src/components/{document → documents}/UploadDocumentsDialog.tsx
RENAMED
File without changes
|
lightrag_webui/src/components/retrieval/QuerySettings.tsx
ADDED
@@ -0,0 +1,279 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { useCallback } from 'react'
|
2 |
+
import { QueryMode, QueryRequest } from '@/api/lightrag'
|
3 |
+
import Text from '@/components/ui/Text'
|
4 |
+
import Input from '@/components/ui/Input'
|
5 |
+
import Checkbox from '@/components/ui/Checkbox'
|
6 |
+
import NumberInput from '@/components/ui/NumberInput'
|
7 |
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/Card'
|
8 |
+
import {
|
9 |
+
Select,
|
10 |
+
SelectContent,
|
11 |
+
SelectGroup,
|
12 |
+
SelectItem,
|
13 |
+
SelectTrigger,
|
14 |
+
SelectValue
|
15 |
+
} from '@/components/ui/Select'
|
16 |
+
import { useSettingsStore } from '@/stores/settings'
|
17 |
+
|
18 |
+
export default function QuerySettings() {
|
19 |
+
const querySettings = useSettingsStore((state) => state.querySettings)
|
20 |
+
|
21 |
+
const handleChange = useCallback((key: keyof QueryRequest, value: any) => {
|
22 |
+
useSettingsStore.getState().updateQuerySettings({ [key]: value })
|
23 |
+
}, [])
|
24 |
+
|
25 |
+
return (
|
26 |
+
<Card className="flex shrink-0 flex-col">
|
27 |
+
<CardHeader className="px-4 pt-4 pb-2">
|
28 |
+
<CardTitle>Parameters</CardTitle>
|
29 |
+
<CardDescription>Configure your query parameters</CardDescription>
|
30 |
+
</CardHeader>
|
31 |
+
<CardContent className="m-0 flex grow flex-col p-0 text-xs">
|
32 |
+
<div className="relative size-full">
|
33 |
+
<div className="absolute inset-0 flex flex-col gap-2 overflow-auto px-2">
|
34 |
+
{/* Query Mode */}
|
35 |
+
<>
|
36 |
+
<Text
|
37 |
+
className="ml-1"
|
38 |
+
text="Query Mode"
|
39 |
+
tooltip="Select the retrieval strategy:\n• Naive: Basic search without advanced techniques\n• Local: Context-dependent information retrieval\n• Global: Utilizes global knowledge base\n• Hybrid: Combines local and global retrieval\n• Mix: Integrates knowledge graph with vector retrieval"
|
40 |
+
side="left"
|
41 |
+
/>
|
42 |
+
<Select
|
43 |
+
value={querySettings.mode}
|
44 |
+
onValueChange={(v) => handleChange('mode', v as QueryMode)}
|
45 |
+
>
|
46 |
+
<SelectTrigger className="hover:bg-primary/5 h-9 cursor-pointer focus:ring-0 focus:ring-offset-0 focus:outline-0 active:right-0">
|
47 |
+
<SelectValue />
|
48 |
+
</SelectTrigger>
|
49 |
+
<SelectContent>
|
50 |
+
<SelectGroup>
|
51 |
+
<SelectItem value="naive">Naive</SelectItem>
|
52 |
+
<SelectItem value="local">Local</SelectItem>
|
53 |
+
<SelectItem value="global">Global</SelectItem>
|
54 |
+
<SelectItem value="hybrid">Hybrid</SelectItem>
|
55 |
+
<SelectItem value="mix">Mix</SelectItem>
|
56 |
+
</SelectGroup>
|
57 |
+
</SelectContent>
|
58 |
+
</Select>
|
59 |
+
</>
|
60 |
+
|
61 |
+
{/* Response Format */}
|
62 |
+
<>
|
63 |
+
<Text
|
64 |
+
className="ml-1"
|
65 |
+
text="Response Format"
|
66 |
+
tooltip="Defines the response format. Examples:\n• Multiple Paragraphs\n• Single Paragraph\n• Bullet Points"
|
67 |
+
side="left"
|
68 |
+
/>
|
69 |
+
<Select
|
70 |
+
value={querySettings.response_type}
|
71 |
+
onValueChange={(v) => handleChange('response_type', v)}
|
72 |
+
>
|
73 |
+
<SelectTrigger className="hover:bg-primary/5 h-9 cursor-pointer focus:ring-0 focus:ring-offset-0 focus:outline-0 active:right-0">
|
74 |
+
<SelectValue />
|
75 |
+
</SelectTrigger>
|
76 |
+
<SelectContent>
|
77 |
+
<SelectGroup>
|
78 |
+
<SelectItem value="Multiple Paragraphs">Single Paragraph</SelectItem>
|
79 |
+
<SelectItem value="Single Paragraph">Single Paragraph</SelectItem>
|
80 |
+
<SelectItem value="Bullet Points">Bullet Points</SelectItem>
|
81 |
+
</SelectGroup>
|
82 |
+
</SelectContent>
|
83 |
+
</Select>
|
84 |
+
</>
|
85 |
+
|
86 |
+
{/* Top K */}
|
87 |
+
<>
|
88 |
+
<Text
|
89 |
+
className="ml-1"
|
90 |
+
text="Top K Results"
|
91 |
+
tooltip="Number of top items to retrieve. Represents entities in 'local' mode and relationships in 'global' mode"
|
92 |
+
side="left"
|
93 |
+
/>
|
94 |
+
<NumberInput
|
95 |
+
id="top_k"
|
96 |
+
stepper={1}
|
97 |
+
value={querySettings.top_k}
|
98 |
+
onValueChange={(v) => handleChange('top_k', v)}
|
99 |
+
min={1}
|
100 |
+
placeholder="Number of results"
|
101 |
+
/>
|
102 |
+
</>
|
103 |
+
|
104 |
+
{/* Max Tokens */}
|
105 |
+
<>
|
106 |
+
<>
|
107 |
+
<Text
|
108 |
+
className="ml-1"
|
109 |
+
text="Max Tokens for Text Unit"
|
110 |
+
tooltip="Maximum number of tokens allowed for each retrieved text chunk"
|
111 |
+
side="left"
|
112 |
+
/>
|
113 |
+
<NumberInput
|
114 |
+
id="max_token_for_text_unit"
|
115 |
+
stepper={500}
|
116 |
+
value={querySettings.max_token_for_text_unit}
|
117 |
+
onValueChange={(v) => handleChange('max_token_for_text_unit', v)}
|
118 |
+
min={1}
|
119 |
+
placeholder="Max tokens for text unit"
|
120 |
+
/>
|
121 |
+
</>
|
122 |
+
|
123 |
+
<>
|
124 |
+
<Text
|
125 |
+
text="Max Tokens for Global Context"
|
126 |
+
tooltip="Maximum number of tokens allocated for relationship descriptions in global retrieval"
|
127 |
+
side="left"
|
128 |
+
/>
|
129 |
+
<NumberInput
|
130 |
+
id="max_token_for_global_context"
|
131 |
+
stepper={500}
|
132 |
+
value={querySettings.max_token_for_global_context}
|
133 |
+
onValueChange={(v) => handleChange('max_token_for_global_context', v)}
|
134 |
+
min={1}
|
135 |
+
placeholder="Max tokens for global context"
|
136 |
+
/>
|
137 |
+
</>
|
138 |
+
|
139 |
+
<>
|
140 |
+
<Text
|
141 |
+
className="ml-1"
|
142 |
+
text="Max Tokens for Local Context"
|
143 |
+
tooltip="Maximum number of tokens allocated for entity descriptions in local retrieval"
|
144 |
+
side="left"
|
145 |
+
/>
|
146 |
+
<NumberInput
|
147 |
+
id="max_token_for_local_context"
|
148 |
+
stepper={500}
|
149 |
+
value={querySettings.max_token_for_local_context}
|
150 |
+
onValueChange={(v) => handleChange('max_token_for_local_context', v)}
|
151 |
+
min={1}
|
152 |
+
placeholder="Max tokens for local context"
|
153 |
+
/>
|
154 |
+
</>
|
155 |
+
</>
|
156 |
+
|
157 |
+
{/* History Turns */}
|
158 |
+
<>
|
159 |
+
<Text
|
160 |
+
className="ml-1"
|
161 |
+
text="History Turns"
|
162 |
+
tooltip="Number of complete conversation turns (user-assistant pairs) to consider in the response context"
|
163 |
+
side="left"
|
164 |
+
/>
|
165 |
+
<NumberInput
|
166 |
+
className="!border-input"
|
167 |
+
id="history_turns"
|
168 |
+
stepper={1}
|
169 |
+
type="text"
|
170 |
+
value={querySettings.history_turns}
|
171 |
+
onValueChange={(v) => handleChange('history_turns', v)}
|
172 |
+
min={0}
|
173 |
+
placeholder="Number of history turns"
|
174 |
+
/>
|
175 |
+
</>
|
176 |
+
|
177 |
+
{/* Keywords */}
|
178 |
+
<>
|
179 |
+
<>
|
180 |
+
<Text
|
181 |
+
className="ml-1"
|
182 |
+
text="High-Level Keywords"
|
183 |
+
tooltip="List of high-level keywords to prioritize in retrieval. Separate with commas"
|
184 |
+
side="left"
|
185 |
+
/>
|
186 |
+
<Input
|
187 |
+
id="hl_keywords"
|
188 |
+
type="text"
|
189 |
+
value={querySettings.hl_keywords?.join(', ')}
|
190 |
+
onChange={(e) => {
|
191 |
+
const keywords = e.target.value
|
192 |
+
.split(',')
|
193 |
+
.map((k) => k.trim())
|
194 |
+
.filter((k) => k !== '')
|
195 |
+
handleChange('hl_keywords', keywords)
|
196 |
+
}}
|
197 |
+
placeholder="Enter keywords"
|
198 |
+
/>
|
199 |
+
</>
|
200 |
+
|
201 |
+
<>
|
202 |
+
<Text
|
203 |
+
className="ml-1"
|
204 |
+
text="Low-Level Keywords"
|
205 |
+
tooltip="List of low-level keywords to refine retrieval focus. Separate with commas"
|
206 |
+
side="left"
|
207 |
+
/>
|
208 |
+
<Input
|
209 |
+
id="ll_keywords"
|
210 |
+
type="text"
|
211 |
+
value={querySettings.ll_keywords?.join(', ')}
|
212 |
+
onChange={(e) => {
|
213 |
+
const keywords = e.target.value
|
214 |
+
.split(',')
|
215 |
+
.map((k) => k.trim())
|
216 |
+
.filter((k) => k !== '')
|
217 |
+
handleChange('ll_keywords', keywords)
|
218 |
+
}}
|
219 |
+
placeholder="Enter keywords"
|
220 |
+
/>
|
221 |
+
</>
|
222 |
+
</>
|
223 |
+
|
224 |
+
{/* Toggle Options */}
|
225 |
+
<>
|
226 |
+
<div className="flex items-center gap-2">
|
227 |
+
<Text
|
228 |
+
className="ml-1"
|
229 |
+
text="Only Need Context"
|
230 |
+
tooltip="If True, only returns the retrieved context without generating a response"
|
231 |
+
side="left"
|
232 |
+
/>
|
233 |
+
<div className="grow" />
|
234 |
+
<Checkbox
|
235 |
+
className="mr-1 cursor-pointer"
|
236 |
+
id="only_need_context"
|
237 |
+
checked={querySettings.only_need_context}
|
238 |
+
onCheckedChange={(checked) => handleChange('only_need_context', checked)}
|
239 |
+
/>
|
240 |
+
</div>
|
241 |
+
|
242 |
+
<div className="flex items-center gap-2">
|
243 |
+
<Text
|
244 |
+
className="ml-1"
|
245 |
+
text="Only Need Prompt"
|
246 |
+
tooltip="If True, only returns the generated prompt without producing a response"
|
247 |
+
side="left"
|
248 |
+
/>
|
249 |
+
<div className="grow" />
|
250 |
+
<Checkbox
|
251 |
+
className="mr-1 cursor-pointer"
|
252 |
+
id="only_need_prompt"
|
253 |
+
checked={querySettings.only_need_prompt}
|
254 |
+
onCheckedChange={(checked) => handleChange('only_need_prompt', checked)}
|
255 |
+
/>
|
256 |
+
</div>
|
257 |
+
|
258 |
+
<div className="flex items-center gap-2">
|
259 |
+
<Text
|
260 |
+
className="ml-1"
|
261 |
+
text="Stream Response"
|
262 |
+
tooltip="If True, enables streaming output for real-time responses"
|
263 |
+
side="left"
|
264 |
+
/>
|
265 |
+
<div className="grow" />
|
266 |
+
<Checkbox
|
267 |
+
className="mr-1 cursor-pointer"
|
268 |
+
id="stream"
|
269 |
+
checked={querySettings.stream}
|
270 |
+
onCheckedChange={(checked) => handleChange('stream', checked)}
|
271 |
+
/>
|
272 |
+
</div>
|
273 |
+
</>
|
274 |
+
</div>
|
275 |
+
</div>
|
276 |
+
</CardContent>
|
277 |
+
</Card>
|
278 |
+
)
|
279 |
+
}
|
lightrag_webui/src/components/ui/Checkbox.tsx
CHANGED
@@ -23,4 +23,4 @@ const Checkbox = React.forwardRef<
|
|
23 |
))
|
24 |
Checkbox.displayName = CheckboxPrimitive.Root.displayName
|
25 |
|
26 |
-
export
|
|
|
23 |
))
|
24 |
Checkbox.displayName = CheckboxPrimitive.Root.displayName
|
25 |
|
26 |
+
export default Checkbox
|
lightrag_webui/src/components/ui/NumberInput.tsx
ADDED
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { ChevronDown, ChevronUp } from 'lucide-react'
|
2 |
+
import { forwardRef, useCallback, useEffect, useState } from 'react'
|
3 |
+
import { NumericFormat, NumericFormatProps } from 'react-number-format'
|
4 |
+
import Button from '@/components/ui/Button'
|
5 |
+
import Input from '@/components/ui/Input'
|
6 |
+
import { cn } from '@/lib/utils'
|
7 |
+
|
8 |
+
export interface NumberInputProps extends Omit<NumericFormatProps, 'value' | 'onValueChange'> {
|
9 |
+
stepper?: number
|
10 |
+
thousandSeparator?: string
|
11 |
+
placeholder?: string
|
12 |
+
defaultValue?: number
|
13 |
+
min?: number
|
14 |
+
max?: number
|
15 |
+
value?: number // Controlled value
|
16 |
+
suffix?: string
|
17 |
+
prefix?: string
|
18 |
+
onValueChange?: (value: number | undefined) => void
|
19 |
+
fixedDecimalScale?: boolean
|
20 |
+
decimalScale?: number
|
21 |
+
}
|
22 |
+
|
23 |
+
const NumberInput = forwardRef<HTMLInputElement, NumberInputProps>(
|
24 |
+
(
|
25 |
+
{
|
26 |
+
stepper,
|
27 |
+
thousandSeparator,
|
28 |
+
placeholder,
|
29 |
+
defaultValue,
|
30 |
+
min = -Infinity,
|
31 |
+
max = Infinity,
|
32 |
+
onValueChange,
|
33 |
+
fixedDecimalScale = false,
|
34 |
+
decimalScale = 0,
|
35 |
+
className = undefined,
|
36 |
+
suffix,
|
37 |
+
prefix,
|
38 |
+
value: controlledValue,
|
39 |
+
...props
|
40 |
+
},
|
41 |
+
ref
|
42 |
+
) => {
|
43 |
+
const [value, setValue] = useState<number | undefined>(controlledValue ?? defaultValue)
|
44 |
+
|
45 |
+
const handleIncrement = useCallback(() => {
|
46 |
+
setValue((prev) =>
|
47 |
+
prev === undefined ? (stepper ?? 1) : Math.min(prev + (stepper ?? 1), max)
|
48 |
+
)
|
49 |
+
}, [stepper, max])
|
50 |
+
|
51 |
+
const handleDecrement = useCallback(() => {
|
52 |
+
setValue((prev) =>
|
53 |
+
prev === undefined ? -(stepper ?? 1) : Math.max(prev - (stepper ?? 1), min)
|
54 |
+
)
|
55 |
+
}, [stepper, min])
|
56 |
+
|
57 |
+
useEffect(() => {
|
58 |
+
if (controlledValue !== undefined) {
|
59 |
+
setValue(controlledValue)
|
60 |
+
}
|
61 |
+
}, [controlledValue])
|
62 |
+
|
63 |
+
const handleChange = (values: { value: string; floatValue: number | undefined }) => {
|
64 |
+
const newValue = values.floatValue === undefined ? undefined : values.floatValue
|
65 |
+
setValue(newValue)
|
66 |
+
if (onValueChange) {
|
67 |
+
onValueChange(newValue)
|
68 |
+
}
|
69 |
+
}
|
70 |
+
|
71 |
+
const handleBlur = () => {
|
72 |
+
if (value !== undefined) {
|
73 |
+
if (value < min) {
|
74 |
+
setValue(min)
|
75 |
+
;(ref as React.RefObject<HTMLInputElement>).current!.value = String(min)
|
76 |
+
} else if (value > max) {
|
77 |
+
setValue(max)
|
78 |
+
;(ref as React.RefObject<HTMLInputElement>).current!.value = String(max)
|
79 |
+
}
|
80 |
+
}
|
81 |
+
}
|
82 |
+
|
83 |
+
return (
|
84 |
+
<div className="relative flex">
|
85 |
+
<NumericFormat
|
86 |
+
value={value}
|
87 |
+
onValueChange={handleChange}
|
88 |
+
thousandSeparator={thousandSeparator}
|
89 |
+
decimalScale={decimalScale}
|
90 |
+
fixedDecimalScale={fixedDecimalScale}
|
91 |
+
allowNegative={min < 0}
|
92 |
+
valueIsNumericString
|
93 |
+
onBlur={handleBlur}
|
94 |
+
max={max}
|
95 |
+
min={min}
|
96 |
+
suffix={suffix}
|
97 |
+
prefix={prefix}
|
98 |
+
customInput={(props) => <Input {...props} className={cn('w-full', className)} />}
|
99 |
+
placeholder={placeholder}
|
100 |
+
className="[appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
|
101 |
+
getInputRef={ref}
|
102 |
+
{...props}
|
103 |
+
/>
|
104 |
+
<div className="absolute top-0 right-0 bottom-0 flex flex-col">
|
105 |
+
<Button
|
106 |
+
aria-label="Increase value"
|
107 |
+
className="border-input h-1/2 rounded-l-none rounded-br-none border-b border-l px-2 focus-visible:relative"
|
108 |
+
variant="outline"
|
109 |
+
onClick={handleIncrement}
|
110 |
+
disabled={value === max}
|
111 |
+
>
|
112 |
+
<ChevronUp size={15} />
|
113 |
+
</Button>
|
114 |
+
<Button
|
115 |
+
aria-label="Decrease value"
|
116 |
+
className="border-input h-1/2 rounded-l-none rounded-tr-none border-b border-l px-2 focus-visible:relative"
|
117 |
+
variant="outline"
|
118 |
+
onClick={handleDecrement}
|
119 |
+
disabled={value === min}
|
120 |
+
>
|
121 |
+
<ChevronDown size={15} />
|
122 |
+
</Button>
|
123 |
+
</div>
|
124 |
+
</div>
|
125 |
+
)
|
126 |
+
}
|
127 |
+
)
|
128 |
+
|
129 |
+
NumberInput.displayName = 'NumberInput'
|
130 |
+
|
131 |
+
export default NumberInput
|
lightrag_webui/src/components/ui/Select.tsx
ADDED
@@ -0,0 +1,151 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import * as React from 'react'
|
2 |
+
import * as SelectPrimitive from '@radix-ui/react-select'
|
3 |
+
import { Check, ChevronDown, ChevronUp } from 'lucide-react'
|
4 |
+
|
5 |
+
import { cn } from '@/lib/utils'
|
6 |
+
|
7 |
+
const Select = SelectPrimitive.Root
|
8 |
+
|
9 |
+
const SelectGroup = SelectPrimitive.Group
|
10 |
+
|
11 |
+
const SelectValue = SelectPrimitive.Value
|
12 |
+
|
13 |
+
const SelectTrigger = React.forwardRef<
|
14 |
+
React.ComponentRef<typeof SelectPrimitive.Trigger>,
|
15 |
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
|
16 |
+
>(({ className, children, ...props }, ref) => (
|
17 |
+
<SelectPrimitive.Trigger
|
18 |
+
ref={ref}
|
19 |
+
className={cn(
|
20 |
+
'border-input bg-background ring-offset-background placeholder:text-muted-foreground focus:ring-ring flex h-10 w-full items-center justify-between rounded-md border px-3 py-2 text-sm focus:ring-2 focus:ring-offset-2 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1',
|
21 |
+
className
|
22 |
+
)}
|
23 |
+
{...props}
|
24 |
+
>
|
25 |
+
{children}
|
26 |
+
<SelectPrimitive.Icon asChild>
|
27 |
+
<ChevronDown className="h-4 w-4 opacity-50" />
|
28 |
+
</SelectPrimitive.Icon>
|
29 |
+
</SelectPrimitive.Trigger>
|
30 |
+
))
|
31 |
+
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
|
32 |
+
|
33 |
+
const SelectScrollUpButton = React.forwardRef<
|
34 |
+
React.ComponentRef<typeof SelectPrimitive.ScrollUpButton>,
|
35 |
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
|
36 |
+
>(({ className, ...props }, ref) => (
|
37 |
+
<SelectPrimitive.ScrollUpButton
|
38 |
+
ref={ref}
|
39 |
+
className={cn('flex cursor-default items-center justify-center py-1', className)}
|
40 |
+
{...props}
|
41 |
+
>
|
42 |
+
<ChevronUp className="h-4 w-4" />
|
43 |
+
</SelectPrimitive.ScrollUpButton>
|
44 |
+
))
|
45 |
+
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
|
46 |
+
|
47 |
+
const SelectScrollDownButton = React.forwardRef<
|
48 |
+
React.ComponentRef<typeof SelectPrimitive.ScrollDownButton>,
|
49 |
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
|
50 |
+
>(({ className, ...props }, ref) => (
|
51 |
+
<SelectPrimitive.ScrollDownButton
|
52 |
+
ref={ref}
|
53 |
+
className={cn('flex cursor-default items-center justify-center py-1', className)}
|
54 |
+
{...props}
|
55 |
+
>
|
56 |
+
<ChevronDown className="h-4 w-4" />
|
57 |
+
</SelectPrimitive.ScrollDownButton>
|
58 |
+
))
|
59 |
+
SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName
|
60 |
+
|
61 |
+
const SelectContent = React.forwardRef<
|
62 |
+
React.ComponentRef<typeof SelectPrimitive.Content>,
|
63 |
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
|
64 |
+
>(({ className, children, position = 'popper', ...props }, ref) => (
|
65 |
+
<SelectPrimitive.Portal>
|
66 |
+
<SelectPrimitive.Content
|
67 |
+
ref={ref}
|
68 |
+
className={cn(
|
69 |
+
'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border shadow-md',
|
70 |
+
position === 'popper' &&
|
71 |
+
'data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
|
72 |
+
className
|
73 |
+
)}
|
74 |
+
position={position}
|
75 |
+
{...props}
|
76 |
+
>
|
77 |
+
<SelectScrollUpButton />
|
78 |
+
<SelectPrimitive.Viewport
|
79 |
+
className={cn(
|
80 |
+
'p-1',
|
81 |
+
position === 'popper' &&
|
82 |
+
'h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]'
|
83 |
+
)}
|
84 |
+
>
|
85 |
+
{children}
|
86 |
+
</SelectPrimitive.Viewport>
|
87 |
+
<SelectScrollDownButton />
|
88 |
+
</SelectPrimitive.Content>
|
89 |
+
</SelectPrimitive.Portal>
|
90 |
+
))
|
91 |
+
SelectContent.displayName = SelectPrimitive.Content.displayName
|
92 |
+
|
93 |
+
const SelectLabel = React.forwardRef<
|
94 |
+
React.ComponentRef<typeof SelectPrimitive.Label>,
|
95 |
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
|
96 |
+
>(({ className, ...props }, ref) => (
|
97 |
+
<SelectPrimitive.Label
|
98 |
+
ref={ref}
|
99 |
+
className={cn('py-1.5 pr-2 pl-8 text-sm font-semibold', className)}
|
100 |
+
{...props}
|
101 |
+
/>
|
102 |
+
))
|
103 |
+
SelectLabel.displayName = SelectPrimitive.Label.displayName
|
104 |
+
|
105 |
+
const SelectItem = React.forwardRef<
|
106 |
+
React.ComponentRef<typeof SelectPrimitive.Item>,
|
107 |
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
|
108 |
+
>(({ className, children, ...props }, ref) => (
|
109 |
+
<SelectPrimitive.Item
|
110 |
+
ref={ref}
|
111 |
+
className={cn(
|
112 |
+
'focus:bg-accent focus:text-accent-foreground relative flex w-full cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
|
113 |
+
className
|
114 |
+
)}
|
115 |
+
{...props}
|
116 |
+
>
|
117 |
+
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
118 |
+
<SelectPrimitive.ItemIndicator>
|
119 |
+
<Check className="h-4 w-4" />
|
120 |
+
</SelectPrimitive.ItemIndicator>
|
121 |
+
</span>
|
122 |
+
|
123 |
+
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
124 |
+
</SelectPrimitive.Item>
|
125 |
+
))
|
126 |
+
SelectItem.displayName = SelectPrimitive.Item.displayName
|
127 |
+
|
128 |
+
const SelectSeparator = React.forwardRef<
|
129 |
+
React.ComponentRef<typeof SelectPrimitive.Separator>,
|
130 |
+
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
|
131 |
+
>(({ className, ...props }, ref) => (
|
132 |
+
<SelectPrimitive.Separator
|
133 |
+
ref={ref}
|
134 |
+
className={cn('bg-muted -mx-1 my-1 h-px', className)}
|
135 |
+
{...props}
|
136 |
+
/>
|
137 |
+
))
|
138 |
+
SelectSeparator.displayName = SelectPrimitive.Separator.displayName
|
139 |
+
|
140 |
+
export {
|
141 |
+
Select,
|
142 |
+
SelectGroup,
|
143 |
+
SelectValue,
|
144 |
+
SelectTrigger,
|
145 |
+
SelectContent,
|
146 |
+
SelectLabel,
|
147 |
+
SelectItem,
|
148 |
+
SelectSeparator,
|
149 |
+
SelectScrollUpButton,
|
150 |
+
SelectScrollDownButton
|
151 |
+
}
|
lightrag_webui/src/components/ui/Tooltip.tsx
CHANGED
@@ -8,19 +8,31 @@ const Tooltip = TooltipPrimitive.Root
|
|
8 |
|
9 |
const TooltipTrigger = TooltipPrimitive.Trigger
|
10 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
const TooltipContent = React.forwardRef<
|
12 |
React.ComponentRef<typeof TooltipPrimitive.Content>,
|
13 |
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
14 |
-
>(({ className, sideOffset = 4, ...props }, ref) => (
|
15 |
<TooltipPrimitive.Content
|
16 |
ref={ref}
|
17 |
sideOffset={sideOffset}
|
18 |
className={cn(
|
19 |
-
'bg-popover text-popover-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 mx-1 overflow-hidden rounded-md border px-3 py-
|
20 |
className
|
21 |
)}
|
22 |
{...props}
|
23 |
-
|
|
|
|
|
24 |
))
|
25 |
TooltipContent.displayName = TooltipPrimitive.Content.displayName
|
26 |
|
|
|
8 |
|
9 |
const TooltipTrigger = TooltipPrimitive.Trigger
|
10 |
|
11 |
+
const processTooltipContent = (content: string) => {
|
12 |
+
if (typeof content !== 'string') return content
|
13 |
+
return content.split('\\n').map((line, i) => (
|
14 |
+
<React.Fragment key={i}>
|
15 |
+
{line}
|
16 |
+
{i < content.split('\\n').length - 1 && <br />}
|
17 |
+
</React.Fragment>
|
18 |
+
))
|
19 |
+
}
|
20 |
+
|
21 |
const TooltipContent = React.forwardRef<
|
22 |
React.ComponentRef<typeof TooltipPrimitive.Content>,
|
23 |
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
24 |
+
>(({ className, sideOffset = 4, children, ...props }, ref) => (
|
25 |
<TooltipPrimitive.Content
|
26 |
ref={ref}
|
27 |
sideOffset={sideOffset}
|
28 |
className={cn(
|
29 |
+
'bg-popover text-popover-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 mx-1 max-w-sm overflow-hidden rounded-md border px-3 py-2 text-sm shadow-md',
|
30 |
className
|
31 |
)}
|
32 |
{...props}
|
33 |
+
>
|
34 |
+
{typeof children === 'string' ? processTooltipContent(children) : children}
|
35 |
+
</TooltipPrimitive.Content>
|
36 |
))
|
37 |
TooltipContent.displayName = TooltipPrimitive.Content.displayName
|
38 |
|
lightrag_webui/src/features/DocumentManager.tsx
CHANGED
@@ -11,8 +11,8 @@ import {
|
|
11 |
import { Card, CardHeader, CardTitle, CardContent, CardDescription } from '@/components/ui/Card'
|
12 |
import Progress from '@/components/ui/Progress'
|
13 |
import EmptyCard from '@/components/ui/EmptyCard'
|
14 |
-
import UploadDocumentsDialog from '@/components/
|
15 |
-
import ClearDocumentsDialog from '@/components/
|
16 |
|
17 |
import {
|
18 |
getDocuments,
|
|
|
11 |
import { Card, CardHeader, CardTitle, CardContent, CardDescription } from '@/components/ui/Card'
|
12 |
import Progress from '@/components/ui/Progress'
|
13 |
import EmptyCard from '@/components/ui/EmptyCard'
|
14 |
+
import UploadDocumentsDialog from '@/components/documents/UploadDocumentsDialog'
|
15 |
+
import ClearDocumentsDialog from '@/components/documents/ClearDocumentsDialog'
|
16 |
|
17 |
import {
|
18 |
getDocuments,
|
lightrag_webui/src/features/RetrievalTesting.tsx
CHANGED
@@ -1,17 +1,13 @@
|
|
1 |
import Input from '@/components/ui/Input'
|
2 |
import Button from '@/components/ui/Button'
|
3 |
import { useCallback, useEffect, useRef, useState } from 'react'
|
4 |
-
import { queryTextStream,
|
5 |
import { errorMessage } from '@/lib/utils'
|
6 |
import { useSettingsStore } from '@/stores/settings'
|
7 |
import { useDebounce } from '@/hooks/useDebounce'
|
8 |
-
import
|
9 |
|
10 |
-
|
11 |
-
id: string
|
12 |
-
content: string
|
13 |
-
role: 'User' | 'LightRAG'
|
14 |
-
}
|
15 |
|
16 |
export default function RetrievalTesting() {
|
17 |
const [messages, setMessages] = useState<Message[]>(
|
@@ -19,7 +15,6 @@ export default function RetrievalTesting() {
|
|
19 |
)
|
20 |
const [inputValue, setInputValue] = useState('')
|
21 |
const [isLoading, setIsLoading] = useState(false)
|
22 |
-
const [mode, setMode] = useState<QueryMode>('mix')
|
23 |
const messagesEndRef = useRef<HTMLDivElement>(null)
|
24 |
|
25 |
const scrollToBottom = useCallback(() => {
|
@@ -31,23 +26,24 @@ export default function RetrievalTesting() {
|
|
31 |
e.preventDefault()
|
32 |
if (!inputValue.trim() || isLoading) return
|
33 |
|
|
|
34 |
const userMessage: Message = {
|
35 |
-
id: Date.now().toString(),
|
36 |
content: inputValue,
|
37 |
-
role: '
|
38 |
}
|
39 |
|
40 |
const assistantMessage: Message = {
|
41 |
-
id: (Date.now() + 1).toString(),
|
42 |
content: '',
|
43 |
-
role: '
|
44 |
}
|
45 |
|
|
|
46 |
setMessages((prev) => {
|
47 |
const newMessages = [...prev, userMessage, assistantMessage]
|
48 |
return newMessages
|
49 |
})
|
50 |
|
|
|
51 |
setInputValue('')
|
52 |
setIsLoading(true)
|
53 |
|
@@ -57,25 +53,34 @@ export default function RetrievalTesting() {
|
|
57 |
setMessages((prev) => {
|
58 |
const newMessages = [...prev]
|
59 |
const lastMessage = newMessages[newMessages.length - 1]
|
60 |
-
if (lastMessage.role === '
|
61 |
lastMessage.content = assistantMessage.content
|
62 |
}
|
63 |
return newMessages
|
64 |
})
|
65 |
}
|
66 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
67 |
try {
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
)
|
76 |
} catch (err) {
|
|
|
77 |
updateAssistantMessage(`Error: Failed to get response\n${errorMessage(err)}`)
|
78 |
} finally {
|
|
|
79 |
setIsLoading(false)
|
80 |
useSettingsStore
|
81 |
.getState()
|
@@ -86,7 +91,7 @@ export default function RetrievalTesting() {
|
|
86 |
])
|
87 |
}
|
88 |
},
|
89 |
-
[inputValue, isLoading,
|
90 |
)
|
91 |
|
92 |
const debouncedMessages = useDebounce(messages, 100)
|
@@ -98,73 +103,65 @@ export default function RetrievalTesting() {
|
|
98 |
}, [setMessages])
|
99 |
|
100 |
return (
|
101 |
-
<div className="flex size-full
|
102 |
-
<div className="
|
103 |
-
<div className="
|
104 |
-
<div className="
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
key={message.id}
|
113 |
-
className={`flex ${message.role === 'User' ? 'justify-end' : 'justify-start'}`}
|
114 |
-
>
|
115 |
<div
|
116 |
-
|
117 |
-
|
118 |
-
}`}
|
119 |
>
|
120 |
-
<
|
121 |
-
|
122 |
-
|
123 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
124 |
</div>
|
125 |
-
|
126 |
-
)
|
127 |
-
|
128 |
-
|
129 |
</div>
|
130 |
</div>
|
131 |
-
</div>
|
132 |
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
<
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
</
|
156 |
-
|
157 |
-
|
158 |
-
value={inputValue}
|
159 |
-
onChange={(e) => setInputValue(e.target.value)}
|
160 |
-
placeholder="Type your query..."
|
161 |
-
disabled={isLoading}
|
162 |
-
/>
|
163 |
-
<Button type="submit" variant="default" disabled={isLoading} size="sm">
|
164 |
-
<SendIcon />
|
165 |
-
Send
|
166 |
-
</Button>
|
167 |
-
</form>
|
168 |
</div>
|
169 |
)
|
170 |
}
|
|
|
|
1 |
import Input from '@/components/ui/Input'
|
2 |
import Button from '@/components/ui/Button'
|
3 |
import { useCallback, useEffect, useRef, useState } from 'react'
|
4 |
+
import { queryText, queryTextStream, Message } from '@/api/lightrag'
|
5 |
import { errorMessage } from '@/lib/utils'
|
6 |
import { useSettingsStore } from '@/stores/settings'
|
7 |
import { useDebounce } from '@/hooks/useDebounce'
|
8 |
+
import QuerySettings from '@/components/retrieval/QuerySettings'
|
9 |
|
10 |
+
import { EraserIcon, SendIcon, LoaderIcon } from 'lucide-react'
|
|
|
|
|
|
|
|
|
11 |
|
12 |
export default function RetrievalTesting() {
|
13 |
const [messages, setMessages] = useState<Message[]>(
|
|
|
15 |
)
|
16 |
const [inputValue, setInputValue] = useState('')
|
17 |
const [isLoading, setIsLoading] = useState(false)
|
|
|
18 |
const messagesEndRef = useRef<HTMLDivElement>(null)
|
19 |
|
20 |
const scrollToBottom = useCallback(() => {
|
|
|
26 |
e.preventDefault()
|
27 |
if (!inputValue.trim() || isLoading) return
|
28 |
|
29 |
+
// Create messages
|
30 |
const userMessage: Message = {
|
|
|
31 |
content: inputValue,
|
32 |
+
role: 'user'
|
33 |
}
|
34 |
|
35 |
const assistantMessage: Message = {
|
|
|
36 |
content: '',
|
37 |
+
role: 'assistant'
|
38 |
}
|
39 |
|
40 |
+
// Add messages to chatbox
|
41 |
setMessages((prev) => {
|
42 |
const newMessages = [...prev, userMessage, assistantMessage]
|
43 |
return newMessages
|
44 |
})
|
45 |
|
46 |
+
// Clear input and set loading
|
47 |
setInputValue('')
|
48 |
setIsLoading(true)
|
49 |
|
|
|
53 |
setMessages((prev) => {
|
54 |
const newMessages = [...prev]
|
55 |
const lastMessage = newMessages[newMessages.length - 1]
|
56 |
+
if (lastMessage.role === 'assistant') {
|
57 |
lastMessage.content = assistantMessage.content
|
58 |
}
|
59 |
return newMessages
|
60 |
})
|
61 |
}
|
62 |
|
63 |
+
// Prepare query parameters
|
64 |
+
const state = useSettingsStore.getState()
|
65 |
+
const queryParams = {
|
66 |
+
...state.querySettings,
|
67 |
+
query: userMessage.content,
|
68 |
+
conversation_history: messages
|
69 |
+
}
|
70 |
+
|
71 |
try {
|
72 |
+
// Run query
|
73 |
+
if (state.querySettings.stream) {
|
74 |
+
await queryTextStream(queryParams, updateAssistantMessage)
|
75 |
+
} else {
|
76 |
+
const response = await queryText(queryParams)
|
77 |
+
updateAssistantMessage(response.response)
|
78 |
+
}
|
|
|
79 |
} catch (err) {
|
80 |
+
// Handle error
|
81 |
updateAssistantMessage(`Error: Failed to get response\n${errorMessage(err)}`)
|
82 |
} finally {
|
83 |
+
// Clear loading and add messages to state
|
84 |
setIsLoading(false)
|
85 |
useSettingsStore
|
86 |
.getState()
|
|
|
91 |
])
|
92 |
}
|
93 |
},
|
94 |
+
[inputValue, isLoading, messages, setMessages]
|
95 |
)
|
96 |
|
97 |
const debouncedMessages = useDebounce(messages, 100)
|
|
|
103 |
}, [setMessages])
|
104 |
|
105 |
return (
|
106 |
+
<div className="flex size-full gap-2 px-2 pb-12">
|
107 |
+
<div className="flex grow flex-col gap-4">
|
108 |
+
<div className="relative grow">
|
109 |
+
<div className="bg-primary-foreground/60 absolute inset-0 flex flex-col overflow-auto rounded-lg border p-2">
|
110 |
+
<div className="flex min-h-0 flex-1 flex-col gap-2">
|
111 |
+
{messages.length === 0 ? (
|
112 |
+
<div className="text-muted-foreground flex h-full items-center justify-center text-lg">
|
113 |
+
Start a retrieval by typing your query below
|
114 |
+
</div>
|
115 |
+
) : (
|
116 |
+
messages.map((message, idx) => (
|
|
|
|
|
|
|
117 |
<div
|
118 |
+
key={idx}
|
119 |
+
className={`flex ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}
|
|
|
120 |
>
|
121 |
+
<div
|
122 |
+
className={`max-w-[80%] rounded-lg px-4 py-2 ${
|
123 |
+
message.role === 'user' ? 'bg-primary text-primary-foreground' : 'bg-muted'
|
124 |
+
}`}
|
125 |
+
>
|
126 |
+
<pre className="break-words whitespace-pre-wrap">{message.content}</pre>
|
127 |
+
{message.content.length === 0 && (
|
128 |
+
<LoaderIcon className="animate-spin duration-2000" />
|
129 |
+
)}
|
130 |
+
</div>
|
131 |
</div>
|
132 |
+
))
|
133 |
+
)}
|
134 |
+
<div ref={messagesEndRef} className="pb-1" />
|
135 |
+
</div>
|
136 |
</div>
|
137 |
</div>
|
|
|
138 |
|
139 |
+
<form onSubmit={handleSubmit} className="flex shrink-0 items-center gap-2">
|
140 |
+
<Button
|
141 |
+
type="button"
|
142 |
+
variant="outline"
|
143 |
+
onClick={clearMessages}
|
144 |
+
disabled={isLoading}
|
145 |
+
size="sm"
|
146 |
+
>
|
147 |
+
<EraserIcon />
|
148 |
+
Clear
|
149 |
+
</Button>
|
150 |
+
<Input
|
151 |
+
className="flex-1"
|
152 |
+
value={inputValue}
|
153 |
+
onChange={(e) => setInputValue(e.target.value)}
|
154 |
+
placeholder="Type your query..."
|
155 |
+
disabled={isLoading}
|
156 |
+
/>
|
157 |
+
<Button type="submit" variant="default" disabled={isLoading} size="sm">
|
158 |
+
<SendIcon />
|
159 |
+
Send
|
160 |
+
</Button>
|
161 |
+
</form>
|
162 |
+
</div>
|
163 |
+
<QuerySettings />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
164 |
</div>
|
165 |
)
|
166 |
}
|
167 |
+
|
lightrag_webui/src/stores/settings.ts
CHANGED
@@ -2,6 +2,7 @@ import { create } from 'zustand'
|
|
2 |
import { persist, createJSONStorage } from 'zustand/middleware'
|
3 |
import { createSelectors } from '@/lib/utils'
|
4 |
import { defaultQueryLabel } from '@/lib/constants'
|
|
|
5 |
|
6 |
type Theme = 'dark' | 'light' | 'system'
|
7 |
type Tab = 'documents' | 'knowledge-graph' | 'retrieval'
|
@@ -32,8 +33,11 @@ interface SettingsState {
|
|
32 |
currentTab: Tab
|
33 |
setCurrentTab: (tab: Tab) => void
|
34 |
|
35 |
-
retrievalHistory:
|
36 |
-
setRetrievalHistory: (history:
|
|
|
|
|
|
|
37 |
}
|
38 |
|
39 |
const useSettingsStoreBase = create<SettingsState>()(
|
@@ -60,6 +64,21 @@ const useSettingsStoreBase = create<SettingsState>()(
|
|
60 |
|
61 |
retrievalHistory: [],
|
62 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
63 |
setTheme: (theme: Theme) => set({ theme }),
|
64 |
|
65 |
setQueryLabel: (queryLabel: string) =>
|
@@ -73,15 +92,17 @@ const useSettingsStoreBase = create<SettingsState>()(
|
|
73 |
|
74 |
setCurrentTab: (tab: Tab) => set({ currentTab: tab }),
|
75 |
|
76 |
-
setRetrievalHistory: (history:
|
|
|
|
|
|
|
|
|
|
|
77 |
}),
|
78 |
{
|
79 |
name: 'settings-storage',
|
80 |
storage: createJSONStorage(() => localStorage),
|
81 |
-
version:
|
82 |
-
partialize(state) {
|
83 |
-
return { ...state, retrievalHistory: undefined }
|
84 |
-
},
|
85 |
migrate: (state: any, version: number) => {
|
86 |
if (version < 2) {
|
87 |
state.showEdgeLabel = false
|
@@ -99,6 +120,24 @@ const useSettingsStoreBase = create<SettingsState>()(
|
|
99 |
if (version < 5) {
|
100 |
state.currentTab = 'documents'
|
101 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
102 |
}
|
103 |
}
|
104 |
)
|
|
|
2 |
import { persist, createJSONStorage } from 'zustand/middleware'
|
3 |
import { createSelectors } from '@/lib/utils'
|
4 |
import { defaultQueryLabel } from '@/lib/constants'
|
5 |
+
import { Message, QueryRequest } from '@/api/lightrag'
|
6 |
|
7 |
type Theme = 'dark' | 'light' | 'system'
|
8 |
type Tab = 'documents' | 'knowledge-graph' | 'retrieval'
|
|
|
33 |
currentTab: Tab
|
34 |
setCurrentTab: (tab: Tab) => void
|
35 |
|
36 |
+
retrievalHistory: Message[]
|
37 |
+
setRetrievalHistory: (history: Message[]) => void
|
38 |
+
|
39 |
+
querySettings: Omit<QueryRequest, 'query'>
|
40 |
+
updateQuerySettings: (settings: Partial<QueryRequest>) => void
|
41 |
}
|
42 |
|
43 |
const useSettingsStoreBase = create<SettingsState>()(
|
|
|
64 |
|
65 |
retrievalHistory: [],
|
66 |
|
67 |
+
querySettings: {
|
68 |
+
mode: 'global',
|
69 |
+
response_type: 'Multiple Paragraphs',
|
70 |
+
top_k: 10,
|
71 |
+
max_token_for_text_unit: 4000,
|
72 |
+
max_token_for_global_context: 4000,
|
73 |
+
max_token_for_local_context: 4000,
|
74 |
+
only_need_context: false,
|
75 |
+
only_need_prompt: false,
|
76 |
+
stream: true,
|
77 |
+
history_turns: 3,
|
78 |
+
hl_keywords: [],
|
79 |
+
ll_keywords: []
|
80 |
+
},
|
81 |
+
|
82 |
setTheme: (theme: Theme) => set({ theme }),
|
83 |
|
84 |
setQueryLabel: (queryLabel: string) =>
|
|
|
92 |
|
93 |
setCurrentTab: (tab: Tab) => set({ currentTab: tab }),
|
94 |
|
95 |
+
setRetrievalHistory: (history: Message[]) => set({ retrievalHistory: history }),
|
96 |
+
|
97 |
+
updateQuerySettings: (settings: Partial<QueryRequest>) =>
|
98 |
+
set((state) => ({
|
99 |
+
querySettings: { ...state.querySettings, ...settings }
|
100 |
+
}))
|
101 |
}),
|
102 |
{
|
103 |
name: 'settings-storage',
|
104 |
storage: createJSONStorage(() => localStorage),
|
105 |
+
version: 6,
|
|
|
|
|
|
|
106 |
migrate: (state: any, version: number) => {
|
107 |
if (version < 2) {
|
108 |
state.showEdgeLabel = false
|
|
|
120 |
if (version < 5) {
|
121 |
state.currentTab = 'documents'
|
122 |
}
|
123 |
+
if (version < 6) {
|
124 |
+
state.querySettings = {
|
125 |
+
mode: 'global',
|
126 |
+
response_type: 'Multiple Paragraphs',
|
127 |
+
top_k: 10,
|
128 |
+
max_token_for_text_unit: 4000,
|
129 |
+
max_token_for_global_context: 4000,
|
130 |
+
max_token_for_local_context: 4000,
|
131 |
+
only_need_context: false,
|
132 |
+
only_need_prompt: false,
|
133 |
+
stream: true,
|
134 |
+
history_turns: 3,
|
135 |
+
hl_keywords: [],
|
136 |
+
ll_keywords: []
|
137 |
+
}
|
138 |
+
state.retrievalHistory = []
|
139 |
+
}
|
140 |
+
return state
|
141 |
}
|
142 |
}
|
143 |
)
|