chore: init monorepo snapshot
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
trigger: always_on
|
||||
---
|
||||
|
||||
数据库操作请使用supabase_Local MCP 进行操作。
|
||||
@@ -0,0 +1 @@
|
||||
tmp/
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
node_modules/
|
||||
**/node_modules/
|
||||
.next/
|
||||
**/.next/
|
||||
dist/
|
||||
**/dist/
|
||||
build/
|
||||
**/build/
|
||||
.turbo/
|
||||
pnpm-debug.log*
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
*.log
|
||||
.DS_Store
|
||||
.env
|
||||
.env.*
|
||||
.vscode/
|
||||
*.local
|
||||
tmp/
|
||||
temp/
|
||||
**/.venv/
|
||||
.venv/
|
||||
**/__pycache__/
|
||||
__pycache__/
|
||||
@@ -0,0 +1,383 @@
|
||||
When asked to design UI & frontend interface
|
||||
When asked to design UI & frontend interface
|
||||
# Role
|
||||
You are superdesign, a senior frontend designer integrated into VS Code as part of the Super Design extension.
|
||||
Your goal is to help user generate amazing design using code
|
||||
|
||||
# Instructions
|
||||
- Use the available tools when needed to help with file operations and code analysis
|
||||
- When creating design file:
|
||||
- Build one single html page of just one screen to build a design based on users' feedback/task
|
||||
- You ALWAYS output design files in '.superdesign/design_iterations' folder as {design_name}_{n}.html (Where n needs to be unique like table_1.html, table_2.html, etc.) or svg file
|
||||
- If you are iterating design based on existing file, then the naming convention should be {current_file_name}_{n}.html, e.g. if we are iterating ui_1.html, then each version should be ui_1_1.html, ui_1_2.html, etc.
|
||||
- You should ALWAYS use tools above for write/edit html files, don't just output in a message, always do tool calls
|
||||
|
||||
## Styling
|
||||
1. superdesign tries to use the flowbite library as a base unless the user specifies otherwise.
|
||||
2. superdesign avoids using indigo or blue colors unless specified in the user's request.
|
||||
3. superdesign MUST generate responsive designs.
|
||||
4. When designing component, poster or any other design that is not full app, you should make sure the background fits well with the actual poster or component UI color; e.g. if component is light then background should be dark, vice versa.
|
||||
5. Font should always using google font, below is a list of default fonts: 'JetBrains Mono', 'Fira Code', 'Source Code Pro','IBM Plex Mono','Roboto Mono','Space Mono','Geist Mono','Inter','Roboto','Open Sans','Poppins','Montserrat','Outfit','Plus Jakarta Sans','DM Sans','Geist','Oxanium','Architects Daughter','Merriweather','Playfair Display','Lora','Source Serif Pro','Libre Baskerville','Space Grotesk'
|
||||
6. When creating CSS, make sure you include !important for all properties that might be overwritten by tailwind & flowbite, e.g. h1, body, etc.
|
||||
7. Unless user asked specifcially, you should NEVER use some bootstrap style blue color, those are terrible color choices, instead looking at reference below.
|
||||
8. Example theme patterns:
|
||||
Ney-brutalism style that feels like 90s web design
|
||||
<neo-brutalism-style>
|
||||
:root {
|
||||
--background: oklch(1.0000 0 0);
|
||||
--foreground: oklch(0 0 0);
|
||||
--card: oklch(1.0000 0 0);
|
||||
--card-foreground: oklch(0 0 0);
|
||||
--popover: oklch(1.0000 0 0);
|
||||
--popover-foreground: oklch(0 0 0);
|
||||
--primary: oklch(0.6489 0.2370 26.9728);
|
||||
--primary-foreground: oklch(1.0000 0 0);
|
||||
--secondary: oklch(0.9680 0.2110 109.7692);
|
||||
--secondary-foreground: oklch(0 0 0);
|
||||
--muted: oklch(0.9551 0 0);
|
||||
--muted-foreground: oklch(0.3211 0 0);
|
||||
--accent: oklch(0.5635 0.2408 260.8178);
|
||||
--accent-foreground: oklch(1.0000 0 0);
|
||||
--destructive: oklch(0 0 0);
|
||||
--destructive-foreground: oklch(1.0000 0 0);
|
||||
--border: oklch(0 0 0);
|
||||
--input: oklch(0 0 0);
|
||||
--ring: oklch(0.6489 0.2370 26.9728);
|
||||
--chart-1: oklch(0.6489 0.2370 26.9728);
|
||||
--chart-2: oklch(0.9680 0.2110 109.7692);
|
||||
--chart-3: oklch(0.5635 0.2408 260.8178);
|
||||
--chart-4: oklch(0.7323 0.2492 142.4953);
|
||||
--chart-5: oklch(0.5931 0.2726 328.3634);
|
||||
--sidebar: oklch(0.9551 0 0);
|
||||
--sidebar-foreground: oklch(0 0 0);
|
||||
--sidebar-primary: oklch(0.6489 0.2370 26.9728);
|
||||
--sidebar-primary-foreground: oklch(1.0000 0 0);
|
||||
--sidebar-accent: oklch(0.5635 0.2408 260.8178);
|
||||
--sidebar-accent-foreground: oklch(1.0000 0 0);
|
||||
--sidebar-border: oklch(0 0 0);
|
||||
--sidebar-ring: oklch(0.6489 0.2370 26.9728);
|
||||
--font-sans: DM Sans, sans-serif;
|
||||
--font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;
|
||||
--font-mono: Space Mono, monospace;
|
||||
--radius: 0px;
|
||||
--shadow-2xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.50);
|
||||
--shadow-xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.50);
|
||||
--shadow-sm: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 1px 2px -1px hsl(0 0% 0% / 1.00);
|
||||
--shadow: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 1px 2px -1px hsl(0 0% 0% / 1.00);
|
||||
--shadow-md: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 2px 4px -1px hsl(0 0% 0% / 1.00);
|
||||
--shadow-lg: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 4px 6px -1px hsl(0 0% 0% / 1.00);
|
||||
--shadow-xl: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 8px 10px -1px hsl(0 0% 0% / 1.00);
|
||||
--shadow-2xl: 4px 4px 0px 0px hsl(0 0% 0% / 2.50);
|
||||
--tracking-normal: 0em;
|
||||
--spacing: 0.25rem;
|
||||
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) + 4px);
|
||||
}
|
||||
</neo-brutalism-style>
|
||||
|
||||
Modern dark mode style like vercel, linear
|
||||
<modern-dark-mode-style>
|
||||
:root {
|
||||
--background: oklch(1 0 0);
|
||||
--foreground: oklch(0.1450 0 0);
|
||||
--card: oklch(1 0 0);
|
||||
--card-foreground: oklch(0.1450 0 0);
|
||||
--popover: oklch(1 0 0);
|
||||
--popover-foreground: oklch(0.1450 0 0);
|
||||
--primary: oklch(0.2050 0 0);
|
||||
--primary-foreground: oklch(0.9850 0 0);
|
||||
--secondary: oklch(0.9700 0 0);
|
||||
--secondary-foreground: oklch(0.2050 0 0);
|
||||
--muted: oklch(0.9700 0 0);
|
||||
--muted-foreground: oklch(0.5560 0 0);
|
||||
--accent: oklch(0.9700 0 0);
|
||||
--accent-foreground: oklch(0.2050 0 0);
|
||||
--destructive: oklch(0.5770 0.2450 27.3250);
|
||||
--destructive-foreground: oklch(1 0 0);
|
||||
--border: oklch(0.9220 0 0);
|
||||
--input: oklch(0.9220 0 0);
|
||||
--ring: oklch(0.7080 0 0);
|
||||
--chart-1: oklch(0.8100 0.1000 252);
|
||||
--chart-2: oklch(0.6200 0.1900 260);
|
||||
--chart-3: oklch(0.5500 0.2200 263);
|
||||
--chart-4: oklch(0.4900 0.2200 264);
|
||||
--chart-5: oklch(0.4200 0.1800 266);
|
||||
--sidebar: oklch(0.9850 0 0);
|
||||
--sidebar-foreground: oklch(0.1450 0 0);
|
||||
--sidebar-primary: oklch(0.2050 0 0);
|
||||
--sidebar-primary-foreground: oklch(0.9850 0 0);
|
||||
--sidebar-accent: oklch(0.9700 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.2050 0 0);
|
||||
--sidebar-border: oklch(0.9220 0 0);
|
||||
--sidebar-ring: oklch(0.7080 0 0);
|
||||
--font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
--font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif;
|
||||
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
--radius: 0.625rem;
|
||||
--shadow-2xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05);
|
||||
--shadow-xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05);
|
||||
--shadow-sm: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10);
|
||||
--shadow: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10);
|
||||
--shadow-md: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 2px 4px -1px hsl(0 0% 0% / 0.10);
|
||||
--shadow-lg: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 4px 6px -1px hsl(0 0% 0% / 0.10);
|
||||
--shadow-xl: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 8px 10px -1px hsl(0 0% 0% / 0.10);
|
||||
--shadow-2xl: 0 1px 3px 0px hsl(0 0% 0% / 0.25);
|
||||
--tracking-normal: 0em;
|
||||
--spacing: 0.25rem;
|
||||
|
||||
--radius-sm: calc(var(--radius) - 4px);
|
||||
--radius-md: calc(var(--radius) - 2px);
|
||||
--radius-lg: var(--radius);
|
||||
--radius-xl: calc(var(--radius) + 4px);
|
||||
}
|
||||
</modern-dark-mode-style>
|
||||
|
||||
## Images & icons
|
||||
1. For images, just use placeholder image from public source like unsplash, placehold.co or others that you already know exact image url; Don't make up urls
|
||||
2. For icons, we should use lucid icons or other public icons, import like <script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script>
|
||||
|
||||
## Script
|
||||
1. When importing tailwind css, just use <script src="https://cdn.tailwindcss.com"></script>, don't load CSS directly as a stylesheet resource like <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
|
||||
2. When using flowbite, import like <script src="https://cdn.jsdelivr.net/npm/flowbite@2.0.0/dist/flowbite.min.js"></script>
|
||||
|
||||
## Workflow
|
||||
You should always follow workflow below unless user explicitly ask you to do something else:
|
||||
1. Layout design
|
||||
2. Theme design (Color, font, spacing, shadown), using generateTheme tool, it should save the css to a local file
|
||||
3. Core Animation design
|
||||
4. Generate a singlehtml file for the UI
|
||||
5. You HAVE TO confirm with user step by step, don't do theme design until user sign off the layout design, same for all follownig steps
|
||||
|
||||
### 1. Layout design
|
||||
Output type: Just text
|
||||
Think through how should the layout of interface look like, what are different UI components
|
||||
And present the layout in ASCII wireframe format, here are the guidelines of good ASCII wireframe, you can do ASCII art too for more custom layout or graphic design
|
||||
|
||||
### 2. Theme design
|
||||
Output type: Tool call
|
||||
Think through what are the colors, fonts, spacing, etc.
|
||||
You HAVE TO use generateTheme tool to generate the theme, do NOT just output XML type text for tool-call, that is not allowed
|
||||
|
||||
### 3. Animation design
|
||||
Output type: Just text
|
||||
Think through what are the animations, transitions, etc.
|
||||
|
||||
### 4. Generate html file for each UI component and then combine them together to form a single html file
|
||||
Output type: Tool call
|
||||
Generate html file for each UI component and then combine them together to form a single html file
|
||||
Make sure to reference the theme css file you created in step 2, and add custom ones that doesn't exist yet in html file
|
||||
You HAVE TO use write tool to generate the html file, do NOT just output XML type text for tool-call, that is not allowed
|
||||
|
||||
<example>
|
||||
<user>design an AI chat UI</user>
|
||||
|
||||
<assistant>
|
||||
Let's think through the layout design for an AI chat UI. Here are the key components and layout considerations:
|
||||
|
||||
## Core UI Components
|
||||
|
||||
**Header Area**
|
||||
- Settings/menu button (Top left)
|
||||
- Chat title/AI name (Top left)
|
||||
- Conversation controls (new chat, clear, etc.) (Top right)
|
||||
|
||||
**Main Chat Area**
|
||||
- Message container (scrollable) (Full width & height)
|
||||
- User messages (typically right-aligned)
|
||||
- AI messages (typically left-aligned)
|
||||
- Message timestamps (Small subtle text at the bottom of each message, aligned to the right/left depending on the message)
|
||||
|
||||
**Input Area**
|
||||
- Text input field (Full width left)
|
||||
- Send button (Embed within the input field,Bottom right side of the screen)
|
||||
- Additional controls (attach files, voice input, etc.) (Embed within the input field, Bottom left)
|
||||
- Character/token counter (optional) (Embed within the input field, top right corner, small text)
|
||||
|
||||
|
||||
## Layout Structure Options
|
||||
|
||||
┌─────────────────────────────────────┐
|
||||
│ ☰ HEADER BAR + │
|
||||
├─────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────────────────┐ │
|
||||
│ │ AI Message Bubble │ │
|
||||
│ └─────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────────┐ │
|
||||
│ │ User Message Bubble │ │
|
||||
│ └─────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────────────┐ │
|
||||
│ │ AI Message Bubble │ │
|
||||
│ └─────────────────────────────┘ │
|
||||
│ │
|
||||
│ [CHAT AREA] │
|
||||
│ │
|
||||
├─────────────────────────────────────┤
|
||||
│ [Text Input Field] [Send] │
|
||||
└─────────────────────────────────────┘
|
||||
|
||||
When hamburger (☰) is clicked, sidebar slides out:
|
||||
┌──────────────┬─────────────────────────────────────┐
|
||||
│ SIDEBAR │ ☰ HEADER BAR + │
|
||||
│ ┌──────────┐ ├─────────────────────────────────────┤
|
||||
│ │ Chat 1 │ │ │
|
||||
│ │ Chat 2 │ │ ┌─────────────────────────────┐ │
|
||||
│ │ Chat 3 │ │ │ AI Message Bubble │ │
|
||||
│ │ + New │ │ └─────────────────────────────┘ │
|
||||
│ └──────────┘ │ │
|
||||
│ │ ┌─────────────────────────────┐ │
|
||||
│ │ │ User Message Bubble │ │
|
||||
│ │ └─────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ │ ┌─────────────────────────────┐ │
|
||||
│ │ │ AI Message Bubble │ │
|
||||
│ │ └─────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ │ [CHAT AREA] │
|
||||
│ │ │
|
||||
│ ├─────────────────────────────────────┤
|
||||
│ │ [Text Input Field] [Send] │
|
||||
└──────────────┘─────────────────────────────────────┘
|
||||
|
||||
Would you like to go ahead with this layout & UI interaction or needs any modification?
|
||||
</assistant>
|
||||
|
||||
<user>
|
||||
They looks good
|
||||
</user>
|
||||
|
||||
<assistant>
|
||||
Great, next let's design the color & font theme,
|
||||
|
||||
generateTheme(theme_name='Vercel techy style', reasoning_reference='Reference classic shadcn style that has ...', cssFilePath='.superdesign/design_iterations/theme_1.css', cssSheet=':root {
|
||||
--background: oklch(1.0000 0 0);
|
||||
--foreground: oklch(0.1448 0 0);
|
||||
--card: oklch(1.0000 0 0);
|
||||
--card-foreground: oklch(0.1448 0 0);
|
||||
--popover: oklch(1.0000 0 0);
|
||||
--popover-foreground: oklch(0.1448 0 0);
|
||||
--primary: oklch(0.5555 0 0);
|
||||
--primary-foreground: oklch(0.9851 0 0);
|
||||
--secondary: oklch(0.9702 0 0);
|
||||
--secondary-foreground: oklch(0.2046 0 0);
|
||||
--muted: oklch(0.9702 0 0);
|
||||
--muted-foreground: oklch(0.5486 0 0);
|
||||
--accent: oklch(0.9702 0 0);
|
||||
--accent-foreground: oklch(0.2046 0 0);
|
||||
--destructive: oklch(0.5830 0.2387 28.4765);
|
||||
--destructive-foreground: oklch(0.9702 0 0);
|
||||
--border: oklch(0.9219 0 0);
|
||||
--input: oklch(0.9219 0 0);
|
||||
--ring: oklch(0.7090 0 0);
|
||||
--chart-1: oklch(0.5555 0 0);
|
||||
--chart-2: oklch(0.5555 0 0);
|
||||
--chart-3: oklch(0.5555 0 0);
|
||||
--chart-4: oklch(0.5555 0 0);
|
||||
--chart-5: oklch(0.5555 0 0);
|
||||
--sidebar: oklch(0.9851 0 0);
|
||||
--sidebar-foreground: oklch(0.1448 0 0);
|
||||
--sidebar-primary: oklch(0.2046 0 0);
|
||||
--sidebar-primary-foreground: oklch(0.9851 0 0);
|
||||
--sidebar-accent: oklch(0.9702 0 0);
|
||||
--sidebar-accent-foreground: oklch(0.2046 0 0);
|
||||
--sidebar-border: oklch(0.9219 0 0);
|
||||
--sidebar-ring: oklch(0.7090 0 0);
|
||||
--font-sans: Geist Mono, monospace;
|
||||
--font-serif: Geist Mono, monospace;
|
||||
--font-mono: Geist Mono, monospace;
|
||||
--radius: 0rem;
|
||||
--shadow-2xs: 0px 1px 0px 0px hsl(0 0% 0% / 0.00);
|
||||
--shadow-xs: 0px 1px 0px 0px hsl(0 0% 0% / 0.00);
|
||||
--shadow-sm: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 1px 2px -1px hsl(0 0% 0% / 0.00);
|
||||
--shadow: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 1px 2px -1px hsl(0 0% 0% / 0.00);
|
||||
--shadow-md: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 2px 4px -1px hsl(0 0% 0% / 0.00);
|
||||
--shadow-lg: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 4px 6px -1px hsl(0 0% 0% / 0.00);
|
||||
--shadow-xl: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 8px 10px -1px hsl(0 0% 0% / 0.00);
|
||||
--shadow-2xl: 0px 1px 0px 0px hsl(0 0% 0% / 0.00);
|
||||
--tracking-normal: 0em;
|
||||
--spacing: 0.25rem;
|
||||
}')
|
||||
|
||||
|
||||
<user>
|
||||
I like the vintage style
|
||||
</user>
|
||||
|
||||
<assistant>
|
||||
Great, next let's design the animation:
|
||||
|
||||
# CHAT UI ANIMATIONS - MICRO-SYNTAX
|
||||
|
||||
## Core Message Flow
|
||||
userMsg: 400ms ease-out [Y+20→0, X+10→0, S0.9→1]
|
||||
aiMsg: 600ms bounce [Y+15→0, S0.95→1] +200ms
|
||||
typing: 1400ms ∞ [Y±8, α0.4→1] stagger+200ms
|
||||
status: 300ms ease-out [α0.6→1, S1→1.05→1]
|
||||
|
||||
## Interface Transitions
|
||||
sidebar: 350ms ease-out [X-280→0, α0→1]
|
||||
overlay: 300ms [α0→1, blur0→4px]
|
||||
input: 200ms [S1→1.01, shadow+ring] focus
|
||||
input: 150ms [S1.01→1, shadow-ring] blur
|
||||
|
||||
## Button Interactions
|
||||
sendBtn: 150ms [S1→0.95→1, R±2°] press
|
||||
sendBtn: 200ms [S1→1.05, shadow↗] hover
|
||||
ripple: 400ms [S0→2, α1→0]
|
||||
|
||||
## Loading States
|
||||
chatLoad: 500ms ease-out [Y+40→0, α0→1]
|
||||
skeleton: 2000ms ∞ [bg: muted↔accent]
|
||||
spinner: 1000ms ∞ linear [R360°]
|
||||
|
||||
## Micro Interactions
|
||||
msgHover: 200ms [Y0→-2, shadow↗]
|
||||
msgSelect: 200ms [bg→accent, S1→1.02]
|
||||
error: 400ms [X±5] shake
|
||||
success: 600ms bounce [S0→1.2→1, R360°]
|
||||
|
||||
## Scroll & Navigation
|
||||
autoScroll: 400ms smooth
|
||||
scrollHint: 800ms ∞×3 [Y±5]
|
||||
|
||||
This animation system creates a cohesive, responsive feel throughout the chat interface. Each animation has specific timing and easing to feel natural and purposeful. Would you like me to adjust any of these animations or add additional ones?
|
||||
|
||||
</assistant>
|
||||
|
||||
<user>
|
||||
This looks great, lets do it
|
||||
</user>
|
||||
|
||||
<assistant>
|
||||
Great, I will generate the html file for each UI component and then combine them together to form a single html file
|
||||
|
||||
write(file_path='.superdesign/design_iterations/chat_ui.css', content='...')
|
||||
write(file_path='.superdesign/design_iterations/chat_ui.html', content='...')
|
||||
|
||||
I've created the html design, please reveiw and let me know if you need any changes
|
||||
|
||||
</example>
|
||||
|
||||
IMPORTANT RULES:
|
||||
1. You MUST use tools call below for any action like generateTheme, write, edit, etc. You are NOT allowed to just output text like 'Called tool: write with arguments: ...' or <tool-call>...</tool-call>; MUST USE TOOL CALL (This is very important!!)
|
||||
2. You MUST confirm the layout, and then theme style, and then animation
|
||||
3. You MUST use .superdesign/design_iterations folder to save the design files, do NOT save to other folders
|
||||
4. You MUST create follow the workflow above
|
||||
|
||||
# Available Tools
|
||||
- **read**: Read file contents within the workspace (supports text files, images, with line range options)
|
||||
- **write**: Write content to files in the workspace (creates parent directories automatically)
|
||||
- **edit**: Replace text within files using exact string matching (requires precise text matching including whitespace and indentation)
|
||||
- **multiedit**: Perform multiple find-and-replace operations on a single file in sequence (each edit applied to result of previous edit)
|
||||
- **glob**: Find files and directories matching glob patterns (e.g., "*.js", "src/**/*.ts") - efficient for locating files by name or path structure
|
||||
- **grep**: Search for text patterns within file contents using regular expressions (can filter by file types and paths)
|
||||
- **ls**: List directory contents with optional filtering, sorting, and detailed information (shows files and subdirectories)
|
||||
- **bash**: Execute shell/bash commands within the workspace (secure execution with timeouts and output capture)
|
||||
- **generateTheme**: Generate a theme for the design
|
||||
|
||||
When calling tools, you MUST use the actual tool call, do NOT just output text like 'Called tool: write with arguments: ...' or <tool-call>...</tool-call>, this won't actually call the tool. (This is very important to my life, please follow)
|
||||
@@ -0,0 +1,232 @@
|
||||
# LightRAG Project Intelligence (.clinerules)
|
||||
|
||||
## Project Overview
|
||||
LightRAG is a mature, production-ready Retrieval-Augmented Generation (RAG) system with comprehensive knowledge graph capabilities. The system has evolved from experimental to production-ready status with extensive functionality across all major components.
|
||||
|
||||
## Current System State (August 15, 2025)
|
||||
- **Status**: Production Ready - Stable and Mature
|
||||
- **Configuration**: Gemini 2.5 Flash + BAAI/bge-m3 embeddings via custom endpoints
|
||||
- **Storage**: Default in-memory with file persistence (JsonKVStorage, NetworkXStorage, NanoVectorDBStorage)
|
||||
- **Language**: Chinese for summaries
|
||||
- **Workspace**: `space1` for data isolation
|
||||
- **Authentication**: JWT-based with admin/user accounts
|
||||
|
||||
## Critical Implementation Patterns
|
||||
|
||||
### 1. Embedding Format Compatibility (CRITICAL)
|
||||
**Pattern**: Always handle both base64 and raw array embedding formats
|
||||
**Location**: `lightrag/llm/openai.py` - `openai_embed` function
|
||||
**Issue**: Custom OpenAI-compatible endpoints return embeddings as raw arrays, not base64 strings
|
||||
**Solution**:
|
||||
```python
|
||||
np.array(dp.embedding, dtype=np.float32) if isinstance(dp.embedding, list)
|
||||
else np.frombuffer(base64.b64decode(dp.embedding), dtype=np.float32)
|
||||
```
|
||||
**Impact**: Document processing fails completely without this dual format support
|
||||
|
||||
### 2. Async Pattern Consistency (CRITICAL)
|
||||
**Pattern**: Always await coroutines before calling methods on the result
|
||||
**Common Error**: `coroutine.method()` instead of `(await coroutine).method()`
|
||||
**Locations**: MongoDB implementations, Neo4j operations
|
||||
**Example**: `await self._data.list_indexes()` then `await cursor.to_list()`
|
||||
|
||||
### 3. Storage Layer Data Compatibility (CRITICAL)
|
||||
**Pattern**: Always filter deprecated/incompatible fields during deserialization
|
||||
**Common Fields to Remove**: `content`, `_id` (MongoDB), database-specific fields
|
||||
**Implementation**: `data.pop('field_name', None)` before creating dataclass objects
|
||||
**Locations**: All storage implementations (JSON, Redis, MongoDB, PostgreSQL)
|
||||
|
||||
### 4. Lock Key Generation (CRITICAL)
|
||||
**Pattern**: Always sort relationship pairs for consistent lock keys
|
||||
**Implementation**: `sorted_key_parts = sorted([src, tgt])` then `f"{sorted_key_parts[0]}-{sorted_key_parts[1]}"`
|
||||
**Impact**: Prevents deadlocks in concurrent relationship processing
|
||||
|
||||
### 5. Event Loop Management (CRITICAL)
|
||||
**Pattern**: Handle event loop mismatches during shutdown gracefully
|
||||
**Implementation**: Timeout + specific RuntimeError handling for "attached to a different loop"
|
||||
**Location**: Neo4j storage finalization
|
||||
**Impact**: Prevents application shutdown failures
|
||||
|
||||
### 6. Async Generator Lock Management (CRITICAL)
|
||||
**Pattern**: Never hold locks across async generator yields - create snapshots instead
|
||||
**Issue**: Holding locks while yielding causes deadlock when consumers need the same lock
|
||||
**Location**: `lightrag/tools/migrate_llm_cache.py` - `stream_default_caches_json`
|
||||
**Solution**: Create snapshot of data while holding lock, release lock, then iterate over snapshot
|
||||
```python
|
||||
# WRONG - Deadlock prone:
|
||||
async with storage._storage_lock:
|
||||
for key, value in storage._data.items():
|
||||
batch[key] = value
|
||||
if len(batch) >= batch_size:
|
||||
yield batch # Lock still held!
|
||||
|
||||
# CORRECT - Snapshot approach:
|
||||
async with storage._storage_lock:
|
||||
matching_items = [(k, v) for k, v in storage._data.items() if condition]
|
||||
# Lock released here
|
||||
for key, value in matching_items:
|
||||
batch[key] = value
|
||||
if len(batch) >= batch_size:
|
||||
yield batch # No lock held
|
||||
```
|
||||
**Impact**: Prevents deadlocks in Json→Json migrations and similar scenarios where source/target share locks
|
||||
**Applicable To**: Any async generator that needs to access shared resources while yielding
|
||||
|
||||
## Architecture Patterns
|
||||
|
||||
### 1. Dependency Injection
|
||||
**Pattern**: Pass configuration through object constructors, not direct imports
|
||||
**Example**: OllamaAPI receives configuration through LightRAG object
|
||||
**Benefit**: Better testability and modularity
|
||||
|
||||
### 2. Memory Bank Documentation
|
||||
**Pattern**: Maintain comprehensive memory bank for development continuity
|
||||
**Structure**: Core files (projectbrief.md, activeContext.md, progress.md, etc.)
|
||||
**Purpose**: Essential for context preservation across development sessions
|
||||
|
||||
### 3. Configuration Management
|
||||
**Pattern**: Centralize defaults in constants.py, use environment variables for runtime config
|
||||
**Implementation**: Default values in constants, override via .env file
|
||||
**Benefit**: Consistent configuration across components
|
||||
|
||||
## Development Workflow Patterns
|
||||
|
||||
### 1. Frontend Development (CRITICAL)
|
||||
**Package Manager**: **ALWAYS USE BUN** - Never use npm or yarn unless Bun is unavailable
|
||||
**Commands**:
|
||||
- `bun install` - Install dependencies
|
||||
- `bun run dev` - Start development server
|
||||
- `bun run build` - Build for production
|
||||
- `bun run lint` - Run linting
|
||||
- `bun test` - Run tests
|
||||
- `bun run preview` - Preview production build
|
||||
|
||||
**Pattern**: All frontend operations must use Bun commands
|
||||
**Fallback**: Only use npm/yarn if Bun installation fails
|
||||
**Testing**: Use `bun test` for all frontend testing
|
||||
|
||||
### 2. Bug Fix Approach
|
||||
1. **Identify root cause** - Don't just fix symptoms
|
||||
2. **Implement robust solution** - Handle edge cases and format variations
|
||||
3. **Maintain backward compatibility** - Preserve existing functionality
|
||||
4. **Add comprehensive error handling** - Graceful degradation
|
||||
5. **Document the fix** - Update memory bank with technical details
|
||||
|
||||
### 3. Feature Implementation
|
||||
1. **Follow existing patterns** - Maintain architectural consistency
|
||||
2. **Use dependency injection** - Avoid direct imports between modules
|
||||
3. **Implement comprehensive error handling** - Handle all failure modes
|
||||
4. **Add proper logging** - Debug and warning messages
|
||||
5. **Update documentation** - Memory bank and code comments
|
||||
6. **Comment Language** - Use English for comments and documentation
|
||||
|
||||
### 4. Performance Optimization
|
||||
1. **Profile before optimizing** - Identify actual bottlenecks
|
||||
2. **Maintain algorithmic correctness** - Don't sacrifice functionality for speed
|
||||
3. **Use appropriate data structures** - Match structure to access patterns
|
||||
4. **Implement caching strategically** - Cache expensive operations
|
||||
5. **Monitor memory usage** - Prevent memory leaks
|
||||
|
||||
## Technology Stack Intelligence
|
||||
|
||||
### 1. LLM Integration
|
||||
- **Primary**: Gemini 2.5 Flash via custom endpoint
|
||||
- **Embedding**: BAAI/bge-m3 via custom endpoint
|
||||
- **Reranking**: BAAI/bge-reranker-v2-m3
|
||||
- **Pattern**: Always handle multiple provider formats
|
||||
|
||||
### 2. Storage Backends
|
||||
- **Default**: In-memory with file persistence
|
||||
- **Production Options**: PostgreSQL, MongoDB, Redis, Neo4j
|
||||
- **Pattern**: Abstract storage interface with multiple implementations
|
||||
|
||||
### 3. API Architecture
|
||||
- **Framework**: FastAPI with Gunicorn for production
|
||||
- **Authentication**: JWT-based with role support
|
||||
- **Compatibility**: Ollama-compatible endpoints for easy integration
|
||||
|
||||
### 4. Frontend
|
||||
- **Framework**: React with TypeScript
|
||||
- **Package Manager**: **BUN (REQUIRED)** - Always use Bun for all frontend operations
|
||||
- **Build Tool**: Vite with Bun runtime
|
||||
- **Visualization**: Sigma.js for graph rendering
|
||||
- **State Management**: React hooks with context
|
||||
- **Internationalization**: i18next for multi-language support
|
||||
|
||||
## Common Pitfalls and Solutions
|
||||
|
||||
### 1. Embedding Format Issues
|
||||
**Pitfall**: Assuming all endpoints return base64-encoded embeddings
|
||||
**Solution**: Always check format and handle both base64 and raw arrays
|
||||
|
||||
### 2. Async/Await Patterns
|
||||
**Pitfall**: Calling methods on coroutines instead of awaited results
|
||||
**Solution**: Always await coroutines before accessing their methods
|
||||
|
||||
### 3. Data Model Evolution
|
||||
**Pitfall**: Breaking changes when removing fields from dataclasses
|
||||
**Solution**: Filter deprecated fields during deserialization, don't break storage
|
||||
|
||||
### 4. Concurrency Issues
|
||||
**Pitfall**: Inconsistent lock key generation causing deadlocks
|
||||
**Solution**: Always sort keys for deterministic lock ordering
|
||||
|
||||
### 5. Event Loop Management
|
||||
**Pitfall**: Event loop mismatches during shutdown
|
||||
**Solution**: Implement timeout and specific error handling for loop issues
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### 1. Query Context Building
|
||||
- **Algorithm**: Linear gradient weighted polling for fair resource allocation
|
||||
- **Optimization**: Round-robin merging to eliminate mode bias
|
||||
- **Pattern**: Smart chunk selection based on cross-entity occurrence
|
||||
|
||||
### 2. Graph Operations
|
||||
- **Optimization**: Batch operations where possible
|
||||
- **Pattern**: Use appropriate indexing for large datasets
|
||||
- **Consideration**: Memory usage with large graphs
|
||||
|
||||
### 3. LLM Request Management
|
||||
- **Pattern**: Priority-based queue for request ordering
|
||||
- **Optimization**: Connection pooling and retry mechanisms
|
||||
- **Consideration**: Rate limiting and cost management
|
||||
|
||||
## Security Patterns
|
||||
|
||||
### 1. Authentication
|
||||
- **Implementation**: JWT tokens with role-based access
|
||||
- **Pattern**: Stateless authentication with configurable expiration
|
||||
- **Security**: Proper token validation and refresh mechanisms
|
||||
|
||||
### 2. API Security
|
||||
- **Pattern**: Input validation and sanitization
|
||||
- **Implementation**: FastAPI dependency injection for auth
|
||||
- **Consideration**: Rate limiting and abuse prevention
|
||||
|
||||
## Maintenance Guidelines
|
||||
|
||||
### 1. Memory Bank Updates
|
||||
- **Trigger**: After significant changes or bug fixes
|
||||
- **Pattern**: Update activeContext.md and progress.md
|
||||
- **Purpose**: Maintain development continuity
|
||||
|
||||
### 2. Configuration Management
|
||||
- **Pattern**: Environment-based configuration with sensible defaults
|
||||
- **Implementation**: .env files with example templates
|
||||
- **Consideration**: Security for production deployments
|
||||
|
||||
### 3. Error Handling
|
||||
- **Pattern**: Comprehensive logging with appropriate levels
|
||||
- **Implementation**: Graceful degradation where possible
|
||||
- **Consideration**: User-friendly error messages
|
||||
|
||||
## Project Evolution Notes
|
||||
|
||||
The project has evolved from experimental to production-ready status. Key milestones:
|
||||
- **Early 2025**: Basic RAG implementation
|
||||
- **Mid 2025**: Multiple storage backends and LLM providers
|
||||
- **July 2025**: Major query optimization and algorithm improvements
|
||||
- **August 2025**: Production-ready stable state
|
||||
|
||||
The system now supports enterprise-level deployments with comprehensive functionality across all components.
|
||||
@@ -0,0 +1,69 @@
|
||||
# Python-related files and directories
|
||||
__pycache__
|
||||
.cache
|
||||
|
||||
# Virtual environment directories
|
||||
*.venv
|
||||
|
||||
# Env
|
||||
env/
|
||||
*.env*
|
||||
.env_example
|
||||
|
||||
# Distribution / build files
|
||||
site
|
||||
dist/
|
||||
build/
|
||||
.eggs/
|
||||
*.egg-info/
|
||||
*.tgz
|
||||
*.tar.gz
|
||||
|
||||
# Exclude siles and folders
|
||||
*.yml
|
||||
.dockerignore
|
||||
Dockerfile
|
||||
Makefile
|
||||
|
||||
# Exclude other projects
|
||||
/tests
|
||||
/scripts
|
||||
/data
|
||||
/dickens
|
||||
/reproduce
|
||||
/output_complete
|
||||
/rag_storage
|
||||
/inputs
|
||||
|
||||
# Python version manager file
|
||||
.python-version
|
||||
|
||||
# Reports
|
||||
*.coverage/
|
||||
*.log
|
||||
log/
|
||||
*.logfire
|
||||
|
||||
# Cache
|
||||
.cache/
|
||||
.mypy_cache
|
||||
.pytest_cache
|
||||
.ruff_cache
|
||||
.gradio
|
||||
.logfire
|
||||
temp/
|
||||
|
||||
# MacOS-related files
|
||||
.DS_Store
|
||||
|
||||
# VS Code settings (local configuration files)
|
||||
.vscode
|
||||
|
||||
# file
|
||||
TODO.md
|
||||
|
||||
# Exclude Git-related files
|
||||
.git
|
||||
.github
|
||||
.gitignore
|
||||
.pre-commit-config.yaml
|
||||
@@ -0,0 +1,2 @@
|
||||
lightrag/api/webui/** binary
|
||||
lightrag/api/webui/** linguist-generated
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
name: Bug Report
|
||||
description: File a bug report
|
||||
title: "[Bug]:"
|
||||
labels: ["bug", "triage"]
|
||||
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: existingcheck
|
||||
attributes:
|
||||
label: Do you need to file an issue?
|
||||
description: Please help us manage our time by avoiding duplicates and common bugs with the steps below.
|
||||
options:
|
||||
- label: I have searched the existing issues and this bug is not already filed.
|
||||
- label: I believe this is a legitimate bug, not just a question or feature request.
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Describe the bug
|
||||
description: A clear and concise description of what the bug is.
|
||||
placeholder: What went wrong?
|
||||
- type: textarea
|
||||
id: reproduce
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: Steps to reproduce the behavior.
|
||||
placeholder: How can we replicate the issue?
|
||||
- type: textarea
|
||||
id: expected_behavior
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: A clear and concise description of what you expected to happen.
|
||||
placeholder: What should have happened?
|
||||
- type: textarea
|
||||
id: configused
|
||||
attributes:
|
||||
label: LightRAG Config Used
|
||||
description: The LightRAG configuration used for the run.
|
||||
placeholder: The settings content or LightRAG configuration
|
||||
value: |
|
||||
# Paste your config here
|
||||
- type: textarea
|
||||
id: screenshotslogs
|
||||
attributes:
|
||||
label: Logs and screenshots
|
||||
description: If applicable, add screenshots and logs to help explain your problem.
|
||||
placeholder: Add logs and screenshots here
|
||||
- type: textarea
|
||||
id: additional_information
|
||||
attributes:
|
||||
label: Additional Information
|
||||
description: |
|
||||
- LightRAG Version: e.g., v0.1.1
|
||||
- Operating System: e.g., Windows 10, Ubuntu 20.04
|
||||
- Python Version: e.g., 3.8
|
||||
- Related Issues: e.g., #1
|
||||
- Any other relevant information.
|
||||
value: |
|
||||
- LightRAG Version:
|
||||
- Operating System:
|
||||
- Python Version:
|
||||
- Related Issues:
|
||||
+1
@@ -0,0 +1 @@
|
||||
blank_issues_enabled: false
|
||||
@@ -0,0 +1,26 @@
|
||||
name: Feature Request
|
||||
description: File a feature request
|
||||
labels: ["enhancement"]
|
||||
title: "[Feature Request]:"
|
||||
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: existingcheck
|
||||
attributes:
|
||||
label: Do you need to file a feature request?
|
||||
description: Please help us manage our time by avoiding duplicates and common feature request with the steps below.
|
||||
options:
|
||||
- label: I have searched the existing feature request and this feature request is not already filed.
|
||||
- label: I believe this is a legitimate feature request, not just a question or bug.
|
||||
- type: textarea
|
||||
id: feature_request_description
|
||||
attributes:
|
||||
label: Feature Request Description
|
||||
description: A clear and concise description of the feature request you would like.
|
||||
placeholder: What this feature request add more or improve?
|
||||
- type: textarea
|
||||
id: additional_context
|
||||
attributes:
|
||||
label: Additional Context
|
||||
description: Add any other context or screenshots about the feature request here.
|
||||
placeholder: Any additional information
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
name: Question
|
||||
description: Ask a general question
|
||||
labels: ["question"]
|
||||
title: "[Question]:"
|
||||
|
||||
body:
|
||||
- type: checkboxes
|
||||
id: existingcheck
|
||||
attributes:
|
||||
label: Do you need to ask a question?
|
||||
description: Please help us manage our time by avoiding duplicates and common questions with the steps below.
|
||||
options:
|
||||
- label: I have searched the existing question and discussions and this question is not already answered.
|
||||
- label: I believe this is a legitimate question, not just a bug or feature request.
|
||||
- type: textarea
|
||||
id: question
|
||||
attributes:
|
||||
label: Your Question
|
||||
description: A clear and concise description of your question.
|
||||
placeholder: What is your question?
|
||||
- type: textarea
|
||||
id: context
|
||||
attributes:
|
||||
label: Additional Context
|
||||
description: Provide any additional context or details that might help us understand your question better.
|
||||
placeholder: Add any relevant information here
|
||||
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "pip" # See documentation for possible values
|
||||
directory: "/" # Location of package manifests
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
<!--
|
||||
Thanks for contributing to LightRAG!
|
||||
|
||||
Please ensure your pull request is ready for review before submitting.
|
||||
|
||||
About this template
|
||||
|
||||
This template helps contributors provide a clear and concise description of their changes. Feel free to adjust it as needed.
|
||||
-->
|
||||
|
||||
## Description
|
||||
|
||||
[Briefly describe the changes made in this pull request.]
|
||||
|
||||
## Related Issues
|
||||
|
||||
[Reference any related issues or tasks addressed by this pull request.]
|
||||
|
||||
## Changes Made
|
||||
|
||||
[List the specific changes made in this pull request.]
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] Changes tested locally
|
||||
- [ ] Code reviewed
|
||||
- [ ] Documentation updated (if necessary)
|
||||
- [ ] Unit tests added (if applicable)
|
||||
|
||||
## Additional Notes
|
||||
|
||||
[Add any additional notes or context for the reviewer(s).]
|
||||
@@ -0,0 +1,84 @@
|
||||
name: Build Lite Docker Image
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
_notes_:
|
||||
description: '⚠️ Create lite Docker images only after non-trivial version releases.'
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
build-and-push-lite:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Get latest tag
|
||||
id: get_tag
|
||||
run: |
|
||||
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
|
||||
if [ -z "$LATEST_TAG" ]; then
|
||||
LATEST_TAG="sha-$(git rev-parse --short HEAD)"
|
||||
echo "No tags found, using commit SHA: $LATEST_TAG"
|
||||
else
|
||||
echo "Latest tag found: $LATEST_TAG"
|
||||
fi
|
||||
echo "tag=$LATEST_TAG" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Prepare lite tag
|
||||
id: lite_tag
|
||||
run: |
|
||||
LITE_TAG="${{ steps.get_tag.outputs.tag }}-lite"
|
||||
echo "Lite image tag: $LITE_TAG"
|
||||
echo "lite_tag=$LITE_TAG" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Update version in __init__.py
|
||||
run: |
|
||||
sed -i "s/__version__ = \".*\"/__version__ = \"${{ steps.get_tag.outputs.tag }}\"/" lightrag/__init__.py
|
||||
cat lightrag/__init__.py | grep __version__
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ghcr.io/${{ github.repository }}
|
||||
tags: |
|
||||
type=raw,value=${{ steps.lite_tag.outputs.lite_tag }}
|
||||
type=raw,value=lite
|
||||
|
||||
- name: Build and push lite Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile.lite
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=min
|
||||
|
||||
- name: Output image details
|
||||
run: |
|
||||
echo "Lite Docker image built and pushed successfully!"
|
||||
echo "Image tag: ghcr.io/${{ github.repository }}:${{ steps.lite_tag.outputs.lite_tag }}"
|
||||
echo "Base Git tag used: ${{ steps.get_tag.outputs.tag }}"
|
||||
@@ -0,0 +1,80 @@
|
||||
name: Build Test Docker Image manually
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
_notes_:
|
||||
description: '⚠️ Please create a new git tag before building the docker image.'
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Fetch all history for tags
|
||||
|
||||
- name: Get latest tag
|
||||
id: get_tag
|
||||
run: |
|
||||
# Get the latest tag, fallback to commit SHA if no tags exist
|
||||
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
|
||||
if [ -z "$LATEST_TAG" ]; then
|
||||
LATEST_TAG="sha-$(git rev-parse --short HEAD)"
|
||||
echo "No tags found, using commit SHA: $LATEST_TAG"
|
||||
else
|
||||
echo "Latest tag found: $LATEST_TAG"
|
||||
fi
|
||||
echo "tag=$LATEST_TAG" >> $GITHUB_OUTPUT
|
||||
echo "image_tag=$LATEST_TAG" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Update version in __init__.py
|
||||
run: |
|
||||
sed -i "s/__version__ = \".*\"/__version__ = \"${{ steps.get_tag.outputs.tag }}\"/" lightrag/__init__.py
|
||||
echo "Updated __init__.py with version ${{ steps.get_tag.outputs.tag }}"
|
||||
cat lightrag/__init__.py | grep __version__
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ghcr.io/${{ github.repository }}
|
||||
tags: |
|
||||
type=raw,value=${{ steps.get_tag.outputs.tag }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Output image details
|
||||
run: |
|
||||
echo "Docker image built and pushed successfully!"
|
||||
echo "Image tags:"
|
||||
echo " - ghcr.io/${{ github.repository }}:${{ steps.get_tag.outputs.tag }}"
|
||||
echo "Latest Git tag used: ${{ steps.get_tag.outputs.tag }}"
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
name: Build Latest Docker Image on Release
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Fetch all history for tags
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Get latest tag
|
||||
id: get_tag
|
||||
run: |
|
||||
TAG=$(git describe --tags --abbrev=0)
|
||||
echo "Found tag: $TAG"
|
||||
echo "tag=$TAG" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Check if pre-release
|
||||
id: check_prerelease
|
||||
run: |
|
||||
TAG="${{ steps.get_tag.outputs.tag }}"
|
||||
if [[ "$TAG" == *"rc"* ]] || [[ "$TAG" == *"dev"* ]]; then
|
||||
echo "is_prerelease=true" >> $GITHUB_OUTPUT
|
||||
echo "This is a pre-release version: $TAG"
|
||||
else
|
||||
echo "is_prerelease=false" >> $GITHUB_OUTPUT
|
||||
echo "This is a stable release: $TAG"
|
||||
fi
|
||||
|
||||
- name: Update version in __init__.py
|
||||
run: |
|
||||
sed -i "s/__version__ = \".*\"/__version__ = \"${{ steps.get_tag.outputs.tag }}\"/" lightrag/__init__.py
|
||||
echo "Updated __init__.py with version ${{ steps.get_tag.outputs.tag }}"
|
||||
cat lightrag/__init__.py | grep __version__
|
||||
|
||||
- name: Extract metadata for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ghcr.io/${{ github.repository }}
|
||||
tags: |
|
||||
type=raw,value=${{ steps.get_tag.outputs.tag }}
|
||||
type=raw,value=latest,enable=${{ steps.check_prerelease.outputs.is_prerelease == 'false' }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
name: Linting and Formatting
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
lint-and-format:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install pre-commit
|
||||
|
||||
- name: Run pre-commit
|
||||
run: pre-commit run --all-files --show-diff-on-failure
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
name: Upload LightRAG-hku Package
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
release-build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # Fetch all history for tags
|
||||
|
||||
# Build frontend WebUI
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v1
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
- name: Build Frontend WebUI
|
||||
run: |
|
||||
cd lightrag_webui
|
||||
bun install --frozen-lockfile
|
||||
bun run build
|
||||
cd ..
|
||||
|
||||
- name: Verify Frontend Build
|
||||
run: |
|
||||
if [ ! -f "lightrag/api/webui/index.html" ]; then
|
||||
echo "❌ Error: Frontend build failed - index.html not found"
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ Frontend build verified"
|
||||
echo "Frontend files:"
|
||||
ls -lh lightrag/api/webui/ | head -10
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.x"
|
||||
|
||||
- name: Get version from tag
|
||||
id: get_version
|
||||
run: |
|
||||
TAG=$(git describe --tags --abbrev=0)
|
||||
echo "Found tag: $TAG"
|
||||
echo "Extracted version: $TAG"
|
||||
echo "version=$TAG" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Update version in __init__.py
|
||||
run: |
|
||||
sed -i "s/__version__ = \".*\"/__version__ = \"${{ steps.get_version.outputs.version }}\"/" lightrag/__init__.py
|
||||
echo "Updated __init__.py with version ${{ steps.get_version.outputs.version }}"
|
||||
cat lightrag/__init__.py | grep __version__
|
||||
|
||||
- name: Build release distributions
|
||||
run: |
|
||||
python -m pip install build
|
||||
python -m build
|
||||
|
||||
- name: Upload distributions
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: release-dists
|
||||
path: dist/
|
||||
|
||||
pypi-publish:
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- release-build
|
||||
permissions:
|
||||
id-token: write
|
||||
|
||||
environment:
|
||||
name: pypi
|
||||
|
||||
steps:
|
||||
- name: Retrieve release distributions
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: release-dists
|
||||
path: dist/
|
||||
|
||||
- name: Publish release distributions to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
packages-dir: dist/
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
# .github/workflows/stale.yml
|
||||
name: Mark stale issues and pull requests
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '30 22 * * *' # run at 22:30+08 every day
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
days-before-stale: 90 # 90 days
|
||||
days-before-close: 7 # 7 days after marked as stale
|
||||
stale-issue-message: 'This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.'
|
||||
close-issue-message: 'This issue has been automatically closed because it has not had recent activity. Please open a new issue if you still have this problem.'
|
||||
stale-pr-message: 'This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.'
|
||||
close-pr-message: 'This pull request has been automatically closed because it has not had recent activity.'
|
||||
# If there are specific labels, exempt them from being marked as stale, for example:
|
||||
exempt-issue-labels: 'enhancement,tracked'
|
||||
# exempt-pr-labels: 'bug,enhancement,help wanted'
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }} # token provided by GitHub
|
||||
@@ -0,0 +1,74 @@
|
||||
# Python-related files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*.egg-info/
|
||||
.eggs/
|
||||
*.tgz
|
||||
*.tar.gz
|
||||
*.ini
|
||||
|
||||
# Virtual Environment
|
||||
.venv/
|
||||
venv/
|
||||
|
||||
# Enviroment Variable Files
|
||||
.env
|
||||
|
||||
# Build / Distribution
|
||||
dist/
|
||||
build/
|
||||
site/
|
||||
|
||||
# Logs / Reports
|
||||
*.log
|
||||
*.log.*
|
||||
*.logfire
|
||||
*.coverage/
|
||||
log/
|
||||
|
||||
# Caches
|
||||
.cache/
|
||||
.mypy_cache/
|
||||
.pytest_cache/
|
||||
.ruff_cache/
|
||||
.gradio/
|
||||
.history/
|
||||
temp/
|
||||
|
||||
# IDE / Editor Files
|
||||
.idea/
|
||||
.vscode/
|
||||
.vscode/settings.json
|
||||
|
||||
# Framework-specific files
|
||||
local_neo4jWorkDir/
|
||||
neo4jWorkDir/
|
||||
|
||||
# Data & Storage
|
||||
inputs/
|
||||
output/
|
||||
rag_storage/
|
||||
data/
|
||||
|
||||
# Evaluation results
|
||||
lightrag/evaluation/results/
|
||||
|
||||
# Miscellaneous
|
||||
.DS_Store
|
||||
TODO.md
|
||||
ignore_this.txt
|
||||
*.ignore.*
|
||||
|
||||
# Project-specific files
|
||||
/dickens*/
|
||||
/book.txt
|
||||
download_models_hf.py
|
||||
|
||||
# Frontend build output (built during PyPI release)
|
||||
/lightrag/api/webui/
|
||||
|
||||
# temporary test files in project root
|
||||
/test_*
|
||||
|
||||
# Cline files
|
||||
memory-bank
|
||||
@@ -0,0 +1,28 @@
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v5.0.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
exclude: ^lightrag/api/webui/
|
||||
- id: end-of-file-fixer
|
||||
exclude: ^lightrag/api/webui/
|
||||
- id: requirements-txt-fixer
|
||||
exclude: ^lightrag/api/webui/
|
||||
|
||||
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.6.4
|
||||
hooks:
|
||||
- id: ruff-format
|
||||
exclude: ^lightrag/api/webui/
|
||||
- id: ruff
|
||||
args: [--fix, --ignore=E402]
|
||||
exclude: ^lightrag/api/webui/
|
||||
|
||||
|
||||
- repo: https://github.com/mgedmin/check-manifest
|
||||
rev: "0.49"
|
||||
hooks:
|
||||
- id: check-manifest
|
||||
stages: [manual]
|
||||
exclude: ^lightrag/api/webui/
|
||||
@@ -0,0 +1,39 @@
|
||||
# Repository Guidelines
|
||||
|
||||
LightRAG is an advanced Retrieval-Augmented Generation (RAG) framework designed to enhance information retrieval and generation through graph-based knowledge representation.
|
||||
|
||||
## Project Structure & Module Organization
|
||||
- `lightrag/`: Core Python package with orchestrators (`lightrag/lightrag.py`), storage adapters in `kg/`, LLM bindings in `llm/`, and helpers such as `operate.py` and `utils_*.py`.
|
||||
- `lightrag-api/`: FastAPI service (`lightrag_server.py`) with routers under `routers/` and Gunicorn launcher `run_with_gunicorn.py`.
|
||||
- `lightrag_webui/`: React 19 + TypeScript client driven by Bun + Vite; UI components live in `src/`.
|
||||
- Tests live in `tests/` and root-level `test_*.py`. Working datasets stay in `inputs/`, `rag_storage/`, `temp/`; deployment collateral lives in `docs/`, `k8s-deploy/`, and `docker-compose.yml`.
|
||||
|
||||
## Build, Test, and Development Commands
|
||||
- `python -m venv .venv && source .venv/bin/activate`: set up the Python runtime.
|
||||
- `pip install -e .` / `pip install -e .[api]`: install the package and API extras in editable mode.
|
||||
- `lightrag-server` or `uvicorn lightrag.api.lightrag_server:app --reload`: start the API locally; ensure `.env` is present.
|
||||
- `python -m pytest tests` or `python test_graph_storage.py`: run the full suite or a targeted script.
|
||||
- `ruff check .`: lint Python sources before committing.
|
||||
- `bun install`, `bun run dev`, `bun run build`, `bun test`: manage the web UI workflow (Bun is mandatory).
|
||||
|
||||
## Coding Style & Naming Conventions
|
||||
- Backend code follow PEP 8 with four-space indentation, annotate functions, and reach for dataclasses when modelling state.
|
||||
- Use `lightrag.utils.logger` instead of `print`; respect logger configuration flags.
|
||||
- Extend storage or pipeline abstractions via `lightrag.base` and keep reusable helpers in the existing `utils_*.py`.
|
||||
- Python modules remain lowercase with underscores; React components use `PascalCase.tsx` and hooks-first patterns.
|
||||
- Front-end code should remain in TypeScript with two-space indentation, rely on functional React components with hooks, and follow Tailwind utility style.
|
||||
|
||||
## Testing Guidelines
|
||||
- Add pytest cases beside the affected module or the relevant `test_*.py`; functions should start with `test_`.
|
||||
- Export required `LIGHTRAG_*` environment variables before running integration or storage tests.
|
||||
- For UI updates, pair code with Vitest specs and run `bun test`.
|
||||
|
||||
## Commit & Pull Request Guidelines
|
||||
- Use concise, imperative commit subjects (e.g., `Fix lock key normalization`) and add body context only when necessary.
|
||||
- PRs should include a summary, operational impact, linked issues, and screenshots or API samples for user-facing work.
|
||||
- Verify `ruff check .`, `python -m pytest`, and affected Bun commands succeed before requesting review; note the runs in the PR text.
|
||||
|
||||
## Security & Configuration Tips
|
||||
- Copy `.env.example` and `config.ini.example`; never commit secrets or real connection strings.
|
||||
- Configure storage backends through `LIGHTRAG_*` variables and validate them with `docker-compose` services when needed.
|
||||
- Treat `lightrag.log*` as local artefacts; purge sensitive information before sharing logs or outputs.
|
||||
@@ -0,0 +1,107 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# Frontend build stage
|
||||
FROM oven/bun:1 AS frontend-builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy frontend source code
|
||||
COPY lightrag_webui/ ./lightrag_webui/
|
||||
|
||||
# Build frontend assets for inclusion in the API package
|
||||
RUN --mount=type=cache,target=/root/.bun/install/cache \
|
||||
cd lightrag_webui \
|
||||
&& bun install --frozen-lockfile \
|
||||
&& bun run build
|
||||
|
||||
# Python build stage - using uv for faster package installation
|
||||
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim AS builder
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
ENV UV_SYSTEM_PYTHON=1
|
||||
ENV UV_COMPILE_BYTECODE=1
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install system deps (Rust is required by some wheels)
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
curl \
|
||||
build-essential \
|
||||
pkg-config \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
|
||||
ENV PATH="/root/.cargo/bin:/root/.local/bin:${PATH}"
|
||||
|
||||
# Ensure shared data directory exists for uv caches
|
||||
RUN mkdir -p /root/.local/share/uv
|
||||
|
||||
# Copy project metadata and sources
|
||||
COPY pyproject.toml .
|
||||
COPY setup.py .
|
||||
COPY uv.lock .
|
||||
|
||||
# Install base, API, and offline extras without the project to improve caching
|
||||
RUN --mount=type=cache,target=/root/.local/share/uv \
|
||||
uv sync --frozen --no-dev --extra api --extra offline --no-install-project --no-editable
|
||||
|
||||
# Copy project sources after dependency layer
|
||||
COPY lightrag/ ./lightrag/
|
||||
|
||||
# Include pre-built frontend assets from the previous stage
|
||||
COPY --from=frontend-builder /app/lightrag/api/webui ./lightrag/api/webui
|
||||
|
||||
# Sync project in non-editable mode and ensure pip is available for runtime installs
|
||||
RUN --mount=type=cache,target=/root/.local/share/uv \
|
||||
uv sync --frozen --no-dev --extra api --extra offline --no-editable \
|
||||
&& /app/.venv/bin/python -m ensurepip --upgrade
|
||||
|
||||
# Prepare offline cache directory and pre-populate tiktoken data
|
||||
# Use uv run to execute commands from the virtual environment
|
||||
RUN mkdir -p /app/data/tiktoken \
|
||||
&& uv run lightrag-download-cache --cache-dir /app/data/tiktoken || status=$?; \
|
||||
if [ -n "${status:-}" ] && [ "$status" -ne 0 ] && [ "$status" -ne 2 ]; then exit "$status"; fi
|
||||
|
||||
# Final stage
|
||||
FROM python:3.12-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install uv for package management
|
||||
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
|
||||
|
||||
ENV UV_SYSTEM_PYTHON=1
|
||||
|
||||
# Copy installed packages and application code
|
||||
COPY --from=builder /root/.local /root/.local
|
||||
COPY --from=builder /app/.venv /app/.venv
|
||||
COPY --from=builder /app/lightrag ./lightrag
|
||||
COPY pyproject.toml .
|
||||
COPY setup.py .
|
||||
COPY uv.lock .
|
||||
|
||||
# Ensure the installed scripts are on PATH
|
||||
ENV PATH=/app/.venv/bin:/root/.local/bin:$PATH
|
||||
|
||||
# Install dependencies with uv sync (uses locked versions from uv.lock)
|
||||
# And ensure pip is available for runtime installs
|
||||
RUN --mount=type=cache,target=/root/.local/share/uv \
|
||||
uv sync --frozen --no-dev --extra api --extra offline --no-editable \
|
||||
&& /app/.venv/bin/python -m ensurepip --upgrade
|
||||
|
||||
# Create persistent data directories AFTER package installation
|
||||
RUN mkdir -p /app/data/rag_storage /app/data/inputs /app/data/tiktoken
|
||||
|
||||
# Copy offline cache into the newly created directory
|
||||
COPY --from=builder /app/data/tiktoken /app/data/tiktoken
|
||||
|
||||
# Point to the prepared cache
|
||||
ENV TIKTOKEN_CACHE_DIR=/app/data/tiktoken
|
||||
ENV WORKING_DIR=/app/data/rag_storage
|
||||
ENV INPUT_DIR=/app/data/inputs
|
||||
|
||||
# Expose API port
|
||||
EXPOSE 9621
|
||||
|
||||
ENTRYPOINT ["python", "-m", "lightrag.api.lightrag_server"]
|
||||
@@ -0,0 +1,108 @@
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# Frontend build stage
|
||||
FROM oven/bun:1 AS frontend-builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy frontend source code
|
||||
COPY lightrag_webui/ ./lightrag_webui/
|
||||
|
||||
# Build frontend assets for inclusion in the API package
|
||||
RUN --mount=type=cache,target=/root/.bun/install/cache \
|
||||
cd lightrag_webui \
|
||||
&& bun install --frozen-lockfile \
|
||||
&& bun run build
|
||||
|
||||
# Python build stage - using uv for package installation
|
||||
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim AS builder
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
ENV UV_SYSTEM_PYTHON=1
|
||||
ENV UV_COMPILE_BYTECODE=1
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install system dependencies required by some wheels
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
curl \
|
||||
build-essential \
|
||||
pkg-config \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
|
||||
ENV PATH="/root/.cargo/bin:/root/.local/bin:${PATH}"
|
||||
|
||||
# Ensure shared data directory exists for uv caches
|
||||
RUN mkdir -p /root/.local/share/uv
|
||||
|
||||
# Copy project metadata and sources
|
||||
COPY pyproject.toml .
|
||||
COPY setup.py .
|
||||
COPY uv.lock .
|
||||
|
||||
# Install project dependencies (base + API extras) without the project to improve caching
|
||||
RUN --mount=type=cache,target=/root/.local/share/uv \
|
||||
uv sync --frozen --no-dev --extra api --no-install-project --no-editable
|
||||
|
||||
# Copy project sources after dependency layer
|
||||
COPY lightrag/ ./lightrag/
|
||||
|
||||
# Include pre-built frontend assets from the previous stage
|
||||
COPY --from=frontend-builder /app/lightrag/api/webui ./lightrag/api/webui
|
||||
|
||||
# Sync project in non-editable mode and ensure pip is available for runtime installs
|
||||
RUN --mount=type=cache,target=/root/.local/share/uv \
|
||||
uv sync --frozen --no-dev --extra api --no-editable \
|
||||
&& /app/.venv/bin/python -m ensurepip --upgrade
|
||||
|
||||
# Prepare tiktoken cache directory and pre-populate tokenizer data
|
||||
# Ignore exit code 2 which indicates assets already cached
|
||||
RUN mkdir -p /app/data/tiktoken \
|
||||
&& uv run lightrag-download-cache --cache-dir /app/data/tiktoken || status=$?; \
|
||||
if [ -n "${status:-}" ] && [ "$status" -ne 0 ] && [ "$status" -ne 2 ]; then exit "$status"; fi
|
||||
|
||||
# Final stage
|
||||
FROM python:3.12-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install uv for package management
|
||||
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
|
||||
|
||||
ENV UV_SYSTEM_PYTHON=1
|
||||
|
||||
# Copy installed packages and application code
|
||||
COPY --from=builder /root/.local /root/.local
|
||||
COPY --from=builder /app/.venv /app/.venv
|
||||
COPY --from=builder /app/lightrag ./lightrag
|
||||
COPY pyproject.toml .
|
||||
COPY setup.py .
|
||||
COPY uv.lock .
|
||||
|
||||
# Ensure the installed scripts are on PATH
|
||||
ENV PATH=/app/.venv/bin:/root/.local/bin:$PATH
|
||||
|
||||
# Sync dependencies inside the final image using uv
|
||||
# And ensure pip is available for runtime installs
|
||||
RUN --mount=type=cache,target=/root/.local/share/uv \
|
||||
uv sync --frozen --no-dev --extra api --no-editable \
|
||||
&& /app/.venv/bin/python -m ensurepip --upgrade
|
||||
|
||||
# Create persistent data directories
|
||||
RUN mkdir -p /app/data/rag_storage /app/data/inputs /app/data/tiktoken
|
||||
|
||||
# Copy cached tokenizer assets prepared in the builder stage
|
||||
COPY --from=builder /app/data/tiktoken /app/data/tiktoken
|
||||
|
||||
# Docker data directories
|
||||
ENV TIKTOKEN_CACHE_DIR=/app/data/tiktoken
|
||||
ENV WORKING_DIR=/app/data/rag_storage
|
||||
ENV INPUT_DIR=/app/data/inputs
|
||||
|
||||
# Expose API port
|
||||
EXPOSE 9621
|
||||
|
||||
# Set entrypoint
|
||||
ENTRYPOINT ["python", "-m", "lightrag.api.lightrag_server"]
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 LightRAG Team
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,4 @@
|
||||
include requirements.txt
|
||||
include lightrag/api/requirements.txt
|
||||
recursive-include lightrag/api/webui *
|
||||
recursive-include lightrag/api/static *
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 471 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 401 KiB |
+1978
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
||||
# Reporting Security Issues
|
||||
|
||||
The LightRAG team and community take security bugs seriously. We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions.
|
||||
|
||||
To report a security issue, please use the GitHub Security Advisory: [Report a Vulnerability](https://github.com/HKUDS/LightRAG/security/advisories/new)
|
||||
|
||||
The LightRAG team will send a response indicating the next steps in handling your report. After the initial reply to your report, the security team will keep you informed of the progress towards a fix and full announcement, and may ask for additional information or guidance.
|
||||
|
||||
Report security bugs in third-party modules to the person or team maintaining the module.
|
||||
|
||||
### Supported Versions
|
||||
|
||||
The following versions currently being supported with security updates.
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 1.2.x | :x: |
|
||||
| 1.3.x | :white_check_mark: |
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 179 KiB |
@@ -0,0 +1,37 @@
|
||||
[neo4j]
|
||||
uri = neo4j+s://xxxxxxxx.databases.neo4j.io
|
||||
username = neo4j
|
||||
password = your-password
|
||||
connection_pool_size = 100
|
||||
connection_timeout = 30.0
|
||||
connection_acquisition_timeout = 30.0
|
||||
max_transaction_retry_time = 30.0
|
||||
max_connection_lifetime = 300.0
|
||||
liveness_check_timeout = 30.0
|
||||
keep_alive = true
|
||||
|
||||
[mongodb]
|
||||
uri = mongodb+srv://name:password@your-cluster-address
|
||||
database = lightrag
|
||||
|
||||
[redis]
|
||||
uri=redis://localhost:6379/1
|
||||
|
||||
[qdrant]
|
||||
uri = http://localhost:16333
|
||||
|
||||
[postgres]
|
||||
host = localhost
|
||||
port = 5432
|
||||
user = your_username
|
||||
password = your_password
|
||||
database = your_database
|
||||
# workspace = default
|
||||
max_connections = 12
|
||||
vector_index_type = HNSW # HNSW or IVFFLAT
|
||||
hnsw_m = 16
|
||||
hnsw_ef = 64
|
||||
ivfflat_lists = 100
|
||||
|
||||
[memgraph]
|
||||
uri = bolt://localhost:7687
|
||||
@@ -0,0 +1,77 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
IMAGE_NAME="ghcr.io/hkuds/lightrag"
|
||||
DOCKERFILE="Dockerfile"
|
||||
TAG="latest"
|
||||
|
||||
# Get version from git tags
|
||||
VERSION=$(git describe --tags --abbrev=0 2>/dev/null || echo "dev")
|
||||
|
||||
echo "=================================="
|
||||
echo " Multi-Architecture Docker Build"
|
||||
echo "=================================="
|
||||
echo "Image: ${IMAGE_NAME}:${TAG}"
|
||||
echo "Version: ${VERSION}"
|
||||
echo "Platforms: linux/amd64, linux/arm64"
|
||||
echo "=================================="
|
||||
echo ""
|
||||
|
||||
# Check Docker login status (skip if CR_PAT is set for CI/CD)
|
||||
if [ -z "$CR_PAT" ]; then
|
||||
if ! docker info 2>/dev/null | grep -q "Username"; then
|
||||
echo "⚠️ Warning: Not logged in to Docker registry"
|
||||
echo "Please login first: docker login ghcr.io"
|
||||
echo "Or set CR_PAT environment variable for automated login"
|
||||
echo ""
|
||||
read -p "Continue anyway? (y/n) " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "Using CR_PAT environment variable for authentication"
|
||||
fi
|
||||
|
||||
# Check if buildx builder exists, create if not
|
||||
if ! docker buildx ls | grep -q "desktop-linux"; then
|
||||
echo "Creating buildx builder..."
|
||||
docker buildx create --name desktop-linux --use
|
||||
docker buildx inspect --bootstrap
|
||||
else
|
||||
echo "Using existing buildx builder: desktop-linux"
|
||||
docker buildx use desktop-linux
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Building and pushing multi-architecture image..."
|
||||
echo ""
|
||||
|
||||
# Build and push
|
||||
docker buildx build \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--file ${DOCKERFILE} \
|
||||
--tag ${IMAGE_NAME}:${TAG} \
|
||||
--tag ${IMAGE_NAME}:${VERSION} \
|
||||
--push \
|
||||
.
|
||||
|
||||
echo ""
|
||||
echo "✓ Build and push complete!"
|
||||
echo ""
|
||||
echo "Images pushed:"
|
||||
echo " - ${IMAGE_NAME}:${TAG}"
|
||||
echo " - ${IMAGE_NAME}:${VERSION}"
|
||||
echo ""
|
||||
echo "Verifying multi-architecture manifest..."
|
||||
echo ""
|
||||
|
||||
# Verify
|
||||
docker buildx imagetools inspect ${IMAGE_NAME}:${TAG}
|
||||
|
||||
echo ""
|
||||
echo "✓ Verification complete!"
|
||||
echo ""
|
||||
echo "Pull with: docker pull ${IMAGE_NAME}:${TAG}"
|
||||
@@ -0,0 +1,21 @@
|
||||
services:
|
||||
lightrag:
|
||||
container_name: lightrag
|
||||
image: ghcr.io/hkuds/lightrag:latest
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
tags:
|
||||
- ghcr.io/hkuds/lightrag:latest
|
||||
ports:
|
||||
- "${PORT:-9621}:9621"
|
||||
volumes:
|
||||
- ./data/rag_storage:/app/data/rag_storage
|
||||
- ./data/inputs:/app/data/inputs
|
||||
- ./config.ini:/app/config.ini
|
||||
- ./.env:/app/.env
|
||||
env_file:
|
||||
- .env
|
||||
restart: unless-stopped
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
@@ -0,0 +1,4 @@
|
||||

|
||||
*Figure 1: LightRAG Indexing Flowchart - Img Caption : [Source](https://learnopencv.com/lightrag/)*
|
||||

|
||||
*Figure 2: LightRAG Retrieval and Querying Flowchart - Img Caption : [Source](https://learnopencv.com/lightrag/)*
|
||||
@@ -0,0 +1,135 @@
|
||||
# LightRAG Docker Deployment
|
||||
|
||||
A lightweight Knowledge Graph Retrieval-Augmented Generation system with multiple LLM backend support.
|
||||
|
||||
## 🚀 Preparation
|
||||
|
||||
### Clone the repository:
|
||||
|
||||
```bash
|
||||
# Linux/MacOS
|
||||
git clone https://github.com/HKUDS/LightRAG.git
|
||||
cd LightRAG
|
||||
```
|
||||
```powershell
|
||||
# Windows PowerShell
|
||||
git clone https://github.com/HKUDS/LightRAG.git
|
||||
cd LightRAG
|
||||
```
|
||||
|
||||
### Configure your environment:
|
||||
|
||||
```bash
|
||||
# Linux/MacOS
|
||||
cp .env.example .env
|
||||
# Edit .env with your preferred configuration
|
||||
```
|
||||
```powershell
|
||||
# Windows PowerShell
|
||||
Copy-Item .env.example .env
|
||||
# Edit .env with your preferred configuration
|
||||
```
|
||||
|
||||
LightRAG can be configured using environment variables in the `.env` file:
|
||||
|
||||
**Server Configuration**
|
||||
|
||||
- `HOST`: Server host (default: 0.0.0.0)
|
||||
- `PORT`: Server port (default: 9621)
|
||||
|
||||
**LLM Configuration**
|
||||
|
||||
- `LLM_BINDING`: LLM backend to use (lollms/ollama/openai)
|
||||
- `LLM_BINDING_HOST`: LLM server host URL
|
||||
- `LLM_MODEL`: Model name to use
|
||||
|
||||
**Embedding Configuration**
|
||||
|
||||
- `EMBEDDING_BINDING`: Embedding backend (lollms/ollama/openai)
|
||||
- `EMBEDDING_BINDING_HOST`: Embedding server host URL
|
||||
- `EMBEDDING_MODEL`: Embedding model name
|
||||
|
||||
**RAG Configuration**
|
||||
|
||||
- `MAX_ASYNC`: Maximum async operations
|
||||
- `MAX_TOKENS`: Maximum token size
|
||||
- `EMBEDDING_DIM`: Embedding dimensions
|
||||
|
||||
## 🐳 Docker Deployment
|
||||
|
||||
Docker instructions work the same on all platforms with Docker Desktop installed.
|
||||
|
||||
### Build Optimization
|
||||
|
||||
The Dockerfile uses BuildKit cache mounts to significantly improve build performance:
|
||||
|
||||
- **Automatic cache management**: BuildKit is automatically enabled via `# syntax=docker/dockerfile:1` directive
|
||||
- **Faster rebuilds**: Only downloads changed dependencies when `uv.lock` or `bun.lock` files are modified
|
||||
- **Efficient package caching**: UV and Bun package downloads are cached across builds
|
||||
- **No manual configuration needed**: Works out of the box in Docker Compose and GitHub Actions
|
||||
|
||||
### Start LightRAG server:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
LightRAG Server uses the following paths for data storage:
|
||||
|
||||
```
|
||||
data/
|
||||
├── rag_storage/ # RAG data persistence
|
||||
└── inputs/ # Input documents
|
||||
```
|
||||
|
||||
### Updates
|
||||
|
||||
To update the Docker container:
|
||||
```bash
|
||||
docker compose pull
|
||||
docker compose down
|
||||
docker compose up
|
||||
```
|
||||
|
||||
### Offline deployment
|
||||
|
||||
Software packages requiring `transformers`, `torch`, or `cuda` will is not preinstalled in the dokcer images. Consequently, document extraction tools such as Docling, as well as local LLM models like Hugging Face and LMDeploy, can not be used in an off line enviroment. These high-compute-resource-demanding services should not be integrated into LightRAG. Docling will be decoupled and deployed as a standalone service.
|
||||
|
||||
## 📦 Build Docker Images
|
||||
|
||||
### For local development and testing
|
||||
|
||||
```bash
|
||||
# Build and run with Docker Compose (BuildKit automatically enabled)
|
||||
docker compose up --build
|
||||
|
||||
# Or explicitly enable BuildKit if needed
|
||||
DOCKER_BUILDKIT=1 docker compose up --build
|
||||
```
|
||||
|
||||
**Note**: BuildKit is automatically enabled by the `# syntax=docker/dockerfile:1` directive in the Dockerfile, ensuring optimal caching performance.
|
||||
|
||||
### For production release
|
||||
|
||||
**multi-architecture build and push**:
|
||||
|
||||
```bash
|
||||
# Use the provided build script
|
||||
./docker-build-push.sh
|
||||
```
|
||||
|
||||
**The build script will**:
|
||||
|
||||
- Check Docker registry login status
|
||||
- Create/use buildx builder automatically
|
||||
- Build for both AMD64 and ARM64 architectures
|
||||
- Push to GitHub Container Registry (ghcr.io)
|
||||
- Verify the multi-architecture manifest
|
||||
|
||||
**Prerequisites**:
|
||||
|
||||
Before building multi-architecture images, ensure you have:
|
||||
|
||||
- Docker 20.10+ with Buildx support
|
||||
- Sufficient disk space (20GB+ recommended for offline image)
|
||||
- Registry access credentials (if pushing images)
|
||||
@@ -0,0 +1,207 @@
|
||||
# Frontend Build Guide
|
||||
|
||||
## Overview
|
||||
|
||||
The LightRAG project includes a React-based WebUI frontend. This guide explains how frontend building works in different scenarios.
|
||||
|
||||
## Key Principle
|
||||
|
||||
- **Git Repository**: Frontend build results are **NOT** included (kept clean)
|
||||
- **PyPI Package**: Frontend build results **ARE** included (ready to use)
|
||||
- **Build Tool**: Uses **Bun** (not npm/yarn)
|
||||
|
||||
## Installation Scenarios
|
||||
|
||||
### 1. End Users (From PyPI) ✨
|
||||
|
||||
**Command:**
|
||||
```bash
|
||||
pip install lightrag-hku[api]
|
||||
```
|
||||
|
||||
**What happens:**
|
||||
- Frontend is already built and included in the package
|
||||
- No additional steps needed
|
||||
- Web interface works immediately
|
||||
|
||||
---
|
||||
|
||||
### 2. Development Mode (Recommended for Contributors) 🔧
|
||||
|
||||
**Command:**
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://github.com/HKUDS/LightRAG.git
|
||||
cd LightRAG
|
||||
|
||||
# Install in editable mode (no frontend build required yet)
|
||||
pip install -e ".[api]"
|
||||
|
||||
# Build frontend when needed (can be done anytime)
|
||||
cd lightrag_webui
|
||||
bun install --frozen-lockfile
|
||||
bun run build
|
||||
cd ..
|
||||
```
|
||||
|
||||
**Advantages:**
|
||||
- Install first, build later (flexible workflow)
|
||||
- Changes take effect immediately (symlink mode)
|
||||
- Frontend can be rebuilt anytime without reinstalling
|
||||
|
||||
**How it works:**
|
||||
- Creates symlinks to source directory
|
||||
- Frontend build output goes to `lightrag/api/webui/`
|
||||
- Changes are immediately visible in installed package
|
||||
|
||||
---
|
||||
|
||||
### 3. Normal Installation (Testing Package Build) 📦
|
||||
|
||||
**Command:**
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://github.com/HKUDS/LightRAG.git
|
||||
cd LightRAG
|
||||
|
||||
# ⚠️ MUST build frontend FIRST
|
||||
cd lightrag_webui
|
||||
bun install --frozen-lockfile
|
||||
bun run build
|
||||
cd ..
|
||||
|
||||
# Now install
|
||||
pip install ".[api]"
|
||||
```
|
||||
|
||||
**What happens:**
|
||||
- Frontend files are **copied** to site-packages
|
||||
- Post-build modifications won't affect installed package
|
||||
- Requires rebuild + reinstall to update
|
||||
|
||||
**When to use:**
|
||||
- Testing complete installation process
|
||||
- Verifying package configuration
|
||||
- Simulating PyPI user experience
|
||||
|
||||
---
|
||||
|
||||
### 4. Creating Distribution Package 🚀
|
||||
|
||||
**Command:**
|
||||
```bash
|
||||
# Build frontend first
|
||||
cd lightrag_webui
|
||||
bun install --frozen-lockfile --production
|
||||
bun run build
|
||||
cd ..
|
||||
|
||||
# Create distribution packages
|
||||
python -m build
|
||||
|
||||
# Output: dist/lightrag_hku-*.whl and dist/lightrag_hku-*.tar.gz
|
||||
```
|
||||
|
||||
**What happens:**
|
||||
- `setup.py` checks if frontend is built
|
||||
- If missing, installation fails with helpful error message
|
||||
- Generated package includes all frontend files
|
||||
|
||||
---
|
||||
|
||||
## GitHub Actions (Automated Release)
|
||||
|
||||
When creating a release on GitHub:
|
||||
|
||||
1. **Automatically builds frontend** using Bun
|
||||
2. **Verifies** build completed successfully
|
||||
3. **Creates Python package** with frontend included
|
||||
4. **Publishes to PyPI** using existing trusted publisher setup
|
||||
|
||||
**No manual intervention required!**
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Scenario | Command | Frontend Required | Can Build After |
|
||||
|----------|---------|-------------------|-----------------|
|
||||
| From PyPI | `pip install lightrag-hku[api]` | Included | No (already installed) |
|
||||
| Development | `pip install -e ".[api]"` | No | ✅ Yes (anytime) |
|
||||
| Normal Install | `pip install ".[api]"` | ✅ Yes (before) | No (must reinstall) |
|
||||
| Create Package | `python -m build` | ✅ Yes (before) | N/A |
|
||||
|
||||
---
|
||||
|
||||
## Bun Installation
|
||||
|
||||
If you don't have Bun installed:
|
||||
|
||||
```bash
|
||||
# macOS/Linux
|
||||
curl -fsSL https://bun.sh/install | bash
|
||||
|
||||
# Windows
|
||||
powershell -c "irm bun.sh/install.ps1 | iex"
|
||||
```
|
||||
|
||||
Official documentation: https://bun.sh
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
LightRAG/
|
||||
├── lightrag_webui/ # Frontend source code
|
||||
│ ├── src/ # React components
|
||||
│ ├── package.json # Dependencies
|
||||
│ └── vite.config.ts # Build configuration
|
||||
│ └── outDir: ../lightrag/api/webui # Build output
|
||||
│
|
||||
├── lightrag/
|
||||
│ └── api/
|
||||
│ └── webui/ # Frontend build output (gitignored)
|
||||
│ ├── index.html # Built files (after running bun run build)
|
||||
│ └── assets/ # Built assets
|
||||
│
|
||||
├── setup.py # Build checks
|
||||
├── pyproject.toml # Package configuration
|
||||
└── .gitignore # Excludes lightrag/api/webui/* (except .gitkeep)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Q: I installed in development mode but the web interface doesn't work
|
||||
|
||||
**A:** Build the frontend:
|
||||
```bash
|
||||
cd lightrag_webui && bun run build
|
||||
```
|
||||
|
||||
### Q: I built the frontend but it's not in my installed package
|
||||
|
||||
**A:** You probably used `pip install .` after building. Either:
|
||||
- Use `pip install -e ".[api]"` for development
|
||||
- Or reinstall: `pip uninstall lightrag-hku && pip install ".[api]"`
|
||||
|
||||
### Q: Where are the built frontend files?
|
||||
|
||||
**A:** In `lightrag/api/webui/` after running `bun run build`
|
||||
|
||||
### Q: Can I use npm or yarn instead of Bun?
|
||||
|
||||
**A:** The project is configured for Bun. While npm/yarn might work, Bun is recommended per project standards.
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
✅ **PyPI users**: No action needed, frontend included
|
||||
✅ **Developers**: Use `pip install -e ".[api]"`, build frontend when needed
|
||||
✅ **CI/CD**: Automatic build in GitHub Actions
|
||||
✅ **Git**: Frontend build output never committed
|
||||
|
||||
For questions or issues, please open a GitHub issue.
|
||||
@@ -0,0 +1,114 @@
|
||||
## LightRAG Multi-Document Processing: Concurrent Control Strategy
|
||||
|
||||
LightRAG employs a multi-layered concurrent control strategy when processing multiple documents. This article provides an in-depth analysis of the concurrent control mechanisms at document level, chunk level, and LLM request level, helping you understand why specific concurrent behaviors occur.
|
||||
|
||||
### 1. Document-Level Concurrent Control
|
||||
|
||||
**Control Parameter**: `max_parallel_insert`
|
||||
|
||||
This parameter controls the number of documents processed simultaneously. The purpose is to prevent excessive parallelism from overwhelming system resources, which could lead to extended processing times for individual files. Document-level concurrency is governed by the `max_parallel_insert` attribute within LightRAG, which defaults to 2 and is configurable via the `MAX_PARALLEL_INSERT` environment variable. `max_parallel_insert` is recommended to be set between 2 and 10, typically `llm_model_max_async/3`. Setting this value too high can increase the likelihood of naming conflicts among entities and relationships across different documents during the merge phase, thereby reducing its overall efficiency.
|
||||
|
||||
### 2. Chunk-Level Concurrent Control
|
||||
|
||||
**Control Parameter**: `llm_model_max_async`
|
||||
|
||||
This parameter controls the number of chunks processed simultaneously in the extraction stage within a document. The purpose is to prevent a high volume of concurrent requests from monopolizing LLM processing resources, which would impede the efficient parallel processing of multiple files. Chunk-Level Concurrent Control is governed by the `llm_model_max_async` attribute within LightRAG, which defaults to 4 and is configurable via the `MAX_ASYNC` environment variable. The purpose of this parameter is to fully leverage the LLM's concurrency capabilities when processing individual documents.
|
||||
|
||||
In the `extract_entities` function, **each document independently creates** its own chunk semaphore. Since each document independently creates chunk semaphores, the theoretical chunk concurrency of the system is:
|
||||
$$
|
||||
ChunkConcurrency = Max Parallel Insert × LLM Model Max Async
|
||||
$$
|
||||
For example:
|
||||
- `max_parallel_insert = 2` (process 2 documents simultaneously)
|
||||
- `llm_model_max_async = 4` (maximum 4 chunk concurrency per document)
|
||||
- Theoretical chunk-level concurrent: 2 × 4 = 8
|
||||
|
||||
### 3. Graph-Level Concurrent Control
|
||||
|
||||
**Control Parameter**: `llm_model_max_async * 2`
|
||||
|
||||
This parameter controls the number of entities and relations processed simultaneously in the merging stage within a document. The purpose is to prevent a high volume of concurrent requests from monopolizing LLM processing resources, which would impede the efficient parallel processing of multiple files. Graph-level concurrency is governed by the `llm_model_max_async` attribute within LightRAG, which defaults to 4 and is configurable via the `MAX_ASYNC` environment variable. Graph-level parallelism control parameters are equally applicable to managing parallelism during the entity relationship reconstruction phase after document deletion.
|
||||
|
||||
Given that the entity relationship merging phase doesn't necessitate LLM interaction for every operation, its parallelism is set at double the LLM's parallelism. This optimizes machine utilization while concurrently preventing excessive queuing resource contention for the LLM.
|
||||
|
||||
### 4. LLM-Level Concurrent Control
|
||||
|
||||
**Control Parameter**: `llm_model_max_async`
|
||||
|
||||
This parameter governs the **concurrent volume** of LLM requests dispatched by the entire LightRAG system, encompassing the document extraction stage, merging stage, and user query handling.
|
||||
|
||||
LLM request prioritization is managed via a global priority queue, which **systematically prioritizes user queries** over merging-related requests, and merging-related requests over extraction-related requests. This strategic prioritization **minimizes user query latency**.
|
||||
|
||||
LLM-level concurrency is governed by the `llm_model_max_async` attribute within LightRAG, which defaults to 4 and is configurable via the `MAX_ASYNC` environment variable.
|
||||
|
||||
### 5. Complete Concurrent Hierarchy Diagram
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
classDef doc fill:#e6f3ff,stroke:#5b9bd5,stroke-width:2px;
|
||||
classDef chunk fill:#fbe5d6,stroke:#ed7d31,stroke-width:1px;
|
||||
classDef merge fill:#e2f0d9,stroke:#70ad47,stroke-width:2px;
|
||||
|
||||
A["Multiple Documents<br>max_parallel_insert = 2"] --> A1
|
||||
A --> B1
|
||||
|
||||
A1[DocA: split to n chunks] --> A_chunk;
|
||||
B1[DocB: split to m chunks] --> B_chunk;
|
||||
|
||||
subgraph A_chunk[Extraction Stage]
|
||||
A_chunk_title[Entity Relation Extraction<br>llm_model_max_async = 4];
|
||||
A_chunk_title --> A_chunk1[Chunk A1]:::chunk;
|
||||
A_chunk_title --> A_chunk2[Chunk A2]:::chunk;
|
||||
A_chunk_title --> A_chunk3[Chunk A3]:::chunk;
|
||||
A_chunk_title --> A_chunk4[Chunk A4]:::chunk;
|
||||
A_chunk1 & A_chunk2 & A_chunk3 & A_chunk4 --> A_chunk_done([Extraction Complete]);
|
||||
end
|
||||
|
||||
subgraph B_chunk[Extraction Stage]
|
||||
B_chunk_title[Entity Relation Extraction<br>llm_model_max_async = 4];
|
||||
B_chunk_title --> B_chunk1[Chunk B1]:::chunk;
|
||||
B_chunk_title --> B_chunk2[Chunk B2]:::chunk;
|
||||
B_chunk_title --> B_chunk3[Chunk B3]:::chunk;
|
||||
B_chunk_title --> B_chunk4[Chunk B4]:::chunk;
|
||||
B_chunk1 & B_chunk2 & B_chunk3 & B_chunk4 --> B_chunk_done([Extraction Complete]);
|
||||
end
|
||||
A_chunk -.->|LLM Request| LLM_Queue;
|
||||
|
||||
A_chunk --> A_merge;
|
||||
B_chunk --> B_merge;
|
||||
|
||||
subgraph A_merge[Merge Stage]
|
||||
A_merge_title[Entity Relation Merging<br>llm_model_max_async * 2 = 8];
|
||||
A_merge_title --> A1_entity[Ent a1]:::merge;
|
||||
A_merge_title --> A2_entity[Ent a2]:::merge;
|
||||
A_merge_title --> A3_entity[Rel a3]:::merge;
|
||||
A_merge_title --> A4_entity[Rel a4]:::merge;
|
||||
A1_entity & A2_entity & A3_entity & A4_entity --> A_done([Merge Complete])
|
||||
end
|
||||
|
||||
subgraph B_merge[Merge Stage]
|
||||
B_merge_title[Entity Relation Merging<br>llm_model_max_async * 2 = 8];
|
||||
B_merge_title --> B1_entity[Ent b1]:::merge;
|
||||
B_merge_title --> B2_entity[Ent b2]:::merge;
|
||||
B_merge_title --> B3_entity[Rel b3]:::merge;
|
||||
B_merge_title --> B4_entity[Rel b4]:::merge;
|
||||
B1_entity & B2_entity & B3_entity & B4_entity --> B_done([Merge Complete])
|
||||
end
|
||||
|
||||
A_merge -.->|LLM Request| LLM_Queue["LLM Request Prioritized Queue<br>llm_model_max_async = 4"];
|
||||
B_merge -.->|LLM Request| LLM_Queue;
|
||||
B_chunk -.->|LLM Request| LLM_Queue;
|
||||
|
||||
```
|
||||
|
||||
> The extraction and merge stages share a global prioritized LLM queue, regulated by `llm_model_max_async`. While numerous entity and relation extraction and merging operations may be "actively processing", **only a limited number will concurrently execute LLM requests** the remainder will be queued and awaiting their turn.
|
||||
|
||||
### 6. Performance Optimization Recommendations
|
||||
|
||||
* **Increase LLM Concurrent Setting based on the capabilities of your LLM server or API provider**
|
||||
|
||||
During the file processing phase, the performance and concurrency capabilities of the LLM are critical bottlenecks. When deploying LLMs locally, the service's concurrency capacity must adequately account for the context length requirements of LightRAG. LightRAG recommends that LLMs support a minimum context length of 32KB; therefore, server concurrency should be calculated based on this benchmark. For API providers, LightRAG will retry requests up to three times if the client's request is rejected due to concurrent request limits. Backend logs can be used to determine if LLM retries are occurring, thereby indicating whether `MAX_ASYNC` has exceeded the API provider's limits.
|
||||
|
||||
* **Align Parallel Document Insertion Settings with LLM Concurrency Configurations**
|
||||
|
||||
The recommended number of parallel document processing tasks is 1/4 of the LLM's concurrency, with a minimum of 2 and a maximum of 10. Setting a higher number of parallel document processing tasks typically does not accelerate overall document processing speed, as even a small number of concurrently processed documents can fully utilize the LLM's parallel processing capabilities. Excessive parallel document processing can significantly increase the processing time for each individual document. Since LightRAG commits processing results on a file-by-file basis, a large number of concurrent files would necessitate caching a substantial amount of data. In the event of a system error, all documents in the middle stage would require reprocessing, thereby increasing error handling costs. For instance, setting `MAX_PARALLEL_INSERT` to 3 is appropriate when `MAX_ASYNC` is configured to 12.
|
||||
@@ -0,0 +1,317 @@
|
||||
# LightRAG Offline Deployment Guide
|
||||
|
||||
This guide provides comprehensive instructions for deploying LightRAG in offline environments where internet access is limited or unavailable.
|
||||
|
||||
If you deploy LightRAG using Docker, there is no need to refer to this document, as the LightRAG Docker image is pre-configured for offline operation.
|
||||
|
||||
> Software packages requiring `transformers`, `torch`, or `cuda` will not be included in the offline dependency group. Consequently, document extraction tools such as Docling, as well as local LLM models like Hugging Face and LMDeploy, are outside the scope of offline installation support. These high-compute-resource-demanding services should not be integrated into LightRAG. Docling will be decoupled and deployed as a standalone service.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Overview](#overview)
|
||||
- [Quick Start](#quick-start)
|
||||
- [Layered Dependencies](#layered-dependencies)
|
||||
- [Tiktoken Cache Management](#tiktoken-cache-management)
|
||||
- [Complete Offline Deployment Workflow](#complete-offline-deployment-workflow)
|
||||
- [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Overview
|
||||
|
||||
LightRAG uses dynamic package installation (`pipmaster`) for optional features based on file types and configurations. In offline environments, these dynamic installations will fail. This guide shows you how to pre-install all necessary dependencies and cache files.
|
||||
|
||||
### What Gets Dynamically Installed?
|
||||
|
||||
LightRAG dynamically installs packages for:
|
||||
|
||||
- **Storage Backends**: `redis`, `neo4j`, `pymilvus`, `pymongo`, `asyncpg`, `qdrant-client`
|
||||
- **LLM Providers**: `openai`, `anthropic`, `ollama`, `zhipuai`, `aioboto3`, `voyageai`, `llama-index`, `lmdeploy`, `transformers`, `torch`
|
||||
- **Tiktoken Models**: BPE encoding models downloaded from OpenAI CDN
|
||||
|
||||
**Note**: Document processing dependencies (`pypdf`, `python-docx`, `python-pptx`, `openpyxl`) are now pre-installed with the `api` extras group and no longer require dynamic installation.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Option 1: Using pip with Offline Extras
|
||||
|
||||
```bash
|
||||
# Online environment: Install all offline dependencies
|
||||
pip install lightrag-hku[offline]
|
||||
|
||||
# Download tiktoken cache
|
||||
lightrag-download-cache
|
||||
|
||||
# Create offline package
|
||||
pip download lightrag-hku[offline] -d ./offline-packages
|
||||
tar -czf lightrag-offline.tar.gz ./offline-packages ~/.tiktoken_cache
|
||||
|
||||
# Transfer to offline server
|
||||
scp lightrag-offline.tar.gz user@offline-server:/path/to/
|
||||
|
||||
# Offline environment: Install
|
||||
tar -xzf lightrag-offline.tar.gz
|
||||
pip install --no-index --find-links=./offline-packages lightrag-hku[offline]
|
||||
export TIKTOKEN_CACHE_DIR=~/.tiktoken_cache
|
||||
```
|
||||
|
||||
### Option 2: Using Requirements Files
|
||||
|
||||
```bash
|
||||
# Online environment: Download packages
|
||||
pip download -r requirements-offline.txt -d ./packages
|
||||
|
||||
# Transfer to offline server
|
||||
tar -czf packages.tar.gz ./packages
|
||||
scp packages.tar.gz user@offline-server:/path/to/
|
||||
|
||||
# Offline environment: Install
|
||||
tar -xzf packages.tar.gz
|
||||
pip install --no-index --find-links=./packages -r requirements-offline.txt
|
||||
```
|
||||
|
||||
## Layered Dependencies
|
||||
|
||||
LightRAG provides flexible dependency groups for different use cases:
|
||||
|
||||
### Available Dependency Groups
|
||||
|
||||
| Group | Description | Use Case |
|
||||
|-------|-------------|----------|
|
||||
| `api` | API server + document processing | FastAPI server with PDF, DOCX, PPTX, XLSX support |
|
||||
| `offline-storage` | Storage backends | Redis, Neo4j, MongoDB, PostgreSQL, etc. |
|
||||
| `offline-llm` | LLM providers | OpenAI, Anthropic, Ollama, etc. |
|
||||
| `offline` | Complete offline package | API + Storage + LLM (all features) |
|
||||
|
||||
**Note**: Document processing (PDF, DOCX, PPTX, XLSX) is included in the `api` extras group. The previous `offline-docs` group has been merged into `api` for better integration.
|
||||
|
||||
> Software packages requiring `transformers`, `torch`, or `cuda` will not be included in the offline dependency group.
|
||||
|
||||
### Installation Examples
|
||||
|
||||
```bash
|
||||
# Install API with document processing
|
||||
pip install lightrag-hku[api]
|
||||
|
||||
# Install API and storage backends
|
||||
pip install lightrag-hku[api,offline-storage]
|
||||
|
||||
# Install all offline dependencies (recommended for offline deployment)
|
||||
pip install lightrag-hku[offline]
|
||||
```
|
||||
|
||||
### Using Individual Requirements Files
|
||||
|
||||
```bash
|
||||
# Storage backends only
|
||||
pip install -r requirements-offline-storage.txt
|
||||
|
||||
# LLM providers only
|
||||
pip install -r requirements-offline-llm.txt
|
||||
|
||||
# All offline dependencies
|
||||
pip install -r requirements-offline.txt
|
||||
```
|
||||
|
||||
## Tiktoken Cache Management
|
||||
|
||||
Tiktoken downloads BPE encoding models on first use. In offline environments, you must pre-download these models.
|
||||
|
||||
### Using the CLI Command
|
||||
|
||||
After installing LightRAG, use the built-in command:
|
||||
|
||||
```bash
|
||||
# Download to default location (~/.tiktoken_cache)
|
||||
lightrag-download-cache
|
||||
|
||||
# Download to specific directory
|
||||
lightrag-download-cache --cache-dir ./tiktoken_cache
|
||||
|
||||
# Download specific models only
|
||||
lightrag-download-cache --models gpt-4o-mini gpt-4
|
||||
```
|
||||
|
||||
### Default Models Downloaded
|
||||
|
||||
- `gpt-4o-mini` (LightRAG default)
|
||||
- `gpt-4o`
|
||||
- `gpt-4`
|
||||
- `gpt-3.5-turbo`
|
||||
- `text-embedding-ada-002`
|
||||
- `text-embedding-3-small`
|
||||
- `text-embedding-3-large`
|
||||
|
||||
### Setting Cache Location in Offline Environment
|
||||
|
||||
```bash
|
||||
# Option 1: Environment variable (temporary)
|
||||
export TIKTOKEN_CACHE_DIR=/path/to/tiktoken_cache
|
||||
|
||||
# Option 2: Add to ~/.bashrc or ~/.zshrc (persistent)
|
||||
echo 'export TIKTOKEN_CACHE_DIR=~/.tiktoken_cache' >> ~/.bashrc
|
||||
source ~/.bashrc
|
||||
|
||||
# Option 3: Copy to default location
|
||||
cp -r /path/to/tiktoken_cache ~/.tiktoken_cache/
|
||||
```
|
||||
|
||||
## Complete Offline Deployment Workflow
|
||||
|
||||
### Step 1: Prepare in Online Environment
|
||||
|
||||
```bash
|
||||
# 1. Install LightRAG with offline dependencies
|
||||
pip install lightrag-hku[offline]
|
||||
|
||||
# 2. Download tiktoken cache
|
||||
lightrag-download-cache --cache-dir ./offline_cache/tiktoken
|
||||
|
||||
# 3. Download all Python packages
|
||||
pip download lightrag-hku[offline] -d ./offline_cache/packages
|
||||
|
||||
# 4. Create archive for transfer
|
||||
tar -czf lightrag-offline-complete.tar.gz ./offline_cache
|
||||
|
||||
# 5. Verify contents
|
||||
tar -tzf lightrag-offline-complete.tar.gz | head -20
|
||||
```
|
||||
|
||||
### Step 2: Transfer to Offline Environment
|
||||
|
||||
```bash
|
||||
# Using scp
|
||||
scp lightrag-offline-complete.tar.gz user@offline-server:/tmp/
|
||||
|
||||
# Or using USB/physical media
|
||||
# Copy lightrag-offline-complete.tar.gz to USB drive
|
||||
```
|
||||
|
||||
### Step 3: Install in Offline Environment
|
||||
|
||||
```bash
|
||||
# 1. Extract archive
|
||||
cd /tmp
|
||||
tar -xzf lightrag-offline-complete.tar.gz
|
||||
|
||||
# 2. Install Python packages
|
||||
pip install --no-index \
|
||||
--find-links=/tmp/offline_cache/packages \
|
||||
lightrag-hku[offline]
|
||||
|
||||
# 3. Set up tiktoken cache
|
||||
mkdir -p ~/.tiktoken_cache
|
||||
cp -r /tmp/offline_cache/tiktoken/* ~/.tiktoken_cache/
|
||||
export TIKTOKEN_CACHE_DIR=~/.tiktoken_cache
|
||||
|
||||
# 4. Add to shell profile for persistence
|
||||
echo 'export TIKTOKEN_CACHE_DIR=~/.tiktoken_cache' >> ~/.bashrc
|
||||
```
|
||||
|
||||
### Step 4: Verify Installation
|
||||
|
||||
```bash
|
||||
# Test Python import
|
||||
python -c "from lightrag import LightRAG; print('✓ LightRAG imported')"
|
||||
|
||||
# Test tiktoken
|
||||
python -c "from lightrag.utils import TiktokenTokenizer; t = TiktokenTokenizer(); print('✓ Tiktoken working')"
|
||||
|
||||
# Test optional dependencies (if installed)
|
||||
python -c "import docling; print('✓ Docling available')"
|
||||
python -c "import redis; print('✓ Redis available')"
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Issue: Tiktoken fails with network error
|
||||
|
||||
**Problem**: `Unable to load tokenizer for model gpt-4o-mini`
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
# Ensure TIKTOKEN_CACHE_DIR is set
|
||||
echo $TIKTOKEN_CACHE_DIR
|
||||
|
||||
# Verify cache files exist
|
||||
ls -la ~/.tiktoken_cache/
|
||||
|
||||
# If empty, you need to download cache in online environment first
|
||||
```
|
||||
|
||||
### Issue: Dynamic package installation fails
|
||||
|
||||
**Problem**: `Error installing package xxx`
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
# Pre-install the specific package you need
|
||||
# For API with document processing:
|
||||
pip install lightrag-hku[api]
|
||||
|
||||
# For storage backends:
|
||||
pip install lightrag-hku[offline-storage]
|
||||
|
||||
# For LLM providers:
|
||||
pip install lightrag-hku[offline-llm]
|
||||
```
|
||||
|
||||
### Issue: Missing dependencies at runtime
|
||||
|
||||
**Problem**: `ModuleNotFoundError: No module named 'xxx'`
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
# Check what you have installed
|
||||
pip list | grep -i xxx
|
||||
|
||||
# Install missing component
|
||||
pip install lightrag-hku[offline] # Install all offline deps
|
||||
```
|
||||
|
||||
### Issue: Permission denied on tiktoken cache
|
||||
|
||||
**Problem**: `PermissionError: [Errno 13] Permission denied`
|
||||
|
||||
**Solution**:
|
||||
```bash
|
||||
# Ensure cache directory has correct permissions
|
||||
chmod 755 ~/.tiktoken_cache
|
||||
chmod 644 ~/.tiktoken_cache/*
|
||||
|
||||
# Or use a user-writable directory
|
||||
export TIKTOKEN_CACHE_DIR=~/my_tiktoken_cache
|
||||
mkdir -p ~/my_tiktoken_cache
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Test in Online Environment First**: Always test your complete setup in an online environment before going offline.
|
||||
|
||||
2. **Keep Cache Updated**: Periodically update your offline cache when new models are released.
|
||||
|
||||
3. **Document Your Setup**: Keep notes on which optional dependencies you actually need.
|
||||
|
||||
4. **Version Pinning**: Consider pinning specific versions in production:
|
||||
```bash
|
||||
pip freeze > requirements-production.txt
|
||||
```
|
||||
|
||||
5. **Minimal Installation**: Only install what you need:
|
||||
```bash
|
||||
# If you only need API with document processing
|
||||
pip install lightrag-hku[api]
|
||||
# Then manually add specific LLM: pip install openai
|
||||
```
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- [LightRAG GitHub Repository](https://github.com/HKUDS/LightRAG)
|
||||
- [Docker Deployment Guide](./DockerDeployment.md)
|
||||
- [API Documentation](../lightrag/api/README.md)
|
||||
|
||||
## Support
|
||||
|
||||
If you encounter issues not covered in this guide:
|
||||
|
||||
1. Check the [GitHub Issues](https://github.com/HKUDS/LightRAG/issues)
|
||||
2. Review the [project documentation](../README.md)
|
||||
3. Create a new issue with your offline deployment details
|
||||
@@ -0,0 +1,170 @@
|
||||
# uv.lock Update Guide
|
||||
|
||||
## What is uv.lock?
|
||||
|
||||
`uv.lock` is uv's lock file. It captures the exact version of every dependency, including transitive ones, much like:
|
||||
- Node.js `package-lock.json`
|
||||
- Rust `Cargo.lock`
|
||||
- Python Poetry `poetry.lock`
|
||||
|
||||
Keeping `uv.lock` in version control guarantees that everyone installs the same dependency set.
|
||||
|
||||
## When does uv.lock change?
|
||||
|
||||
### Situations where it does *not* change automatically
|
||||
|
||||
- Running `uv sync --frozen`
|
||||
- Building Docker images that call `uv sync --frozen`
|
||||
- Editing source code without touching dependency metadata
|
||||
|
||||
### Situations where it will change
|
||||
|
||||
1. **`uv lock` or `uv lock --upgrade`**
|
||||
|
||||
```bash
|
||||
uv lock # Resolve according to current constraints
|
||||
uv lock --upgrade # Re-resolve and upgrade to the newest compatible releases
|
||||
```
|
||||
|
||||
Use these commands after modifying `pyproject.toml`, when you want fresh dependency versions, or if the lock file was deleted or corrupted.
|
||||
|
||||
2. **`uv add`**
|
||||
|
||||
```bash
|
||||
uv add requests # Adds the dependency and updates both files
|
||||
uv add --dev pytest # Adds a dev dependency
|
||||
```
|
||||
|
||||
`uv add` edits `pyproject.toml` and refreshes `uv.lock` in one step.
|
||||
|
||||
3. **`uv remove`**
|
||||
|
||||
```bash
|
||||
uv remove requests
|
||||
```
|
||||
|
||||
This removes the dependency from `pyproject.toml` and rewrites `uv.lock`.
|
||||
|
||||
4. **`uv sync` without `--frozen`**
|
||||
|
||||
```bash
|
||||
uv sync
|
||||
```
|
||||
|
||||
Normally this only installs what is already locked. However, if `pyproject.toml` and `uv.lock` disagree or the lock file is missing, uv will regenerate and update `uv.lock`. In CI and production builds you should prefer `uv sync --frozen` to prevent unintended updates.
|
||||
|
||||
## Example workflows
|
||||
|
||||
### Scenario 1: Add a new dependency
|
||||
|
||||
```bash
|
||||
# Recommended: let uv handle both files
|
||||
uv add fastapi
|
||||
git add pyproject.toml uv.lock
|
||||
git commit -m "Add fastapi dependency"
|
||||
|
||||
# Manual alternative
|
||||
# 1. Edit pyproject.toml
|
||||
# 2. Regenerate the lock file
|
||||
uv lock
|
||||
git add pyproject.toml uv.lock
|
||||
git commit -m "Add fastapi dependency"
|
||||
```
|
||||
|
||||
### Scenario 2: Relax or tighten a version constraint
|
||||
|
||||
```bash
|
||||
# 1. Edit the requirement in pyproject.toml,
|
||||
# e.g. openai>=1.0.0,<2.0.0 -> openai>=1.5.0,<2.0.0
|
||||
|
||||
# 2. Re-resolve the lock file
|
||||
uv lock
|
||||
|
||||
# 3. Commit both files
|
||||
git add pyproject.toml uv.lock
|
||||
git commit -m "Update openai to >=1.5.0"
|
||||
```
|
||||
|
||||
### Scenario 3: Upgrade everything to the newest compatible versions
|
||||
|
||||
```bash
|
||||
uv lock --upgrade
|
||||
git diff uv.lock
|
||||
git add uv.lock
|
||||
git commit -m "Upgrade dependencies to latest compatible versions"
|
||||
```
|
||||
|
||||
### Scenario 4: Teammate syncing the project
|
||||
|
||||
```bash
|
||||
git pull # Fetch latest code and lock file
|
||||
uv sync --frozen # Install exactly what uv.lock specifies
|
||||
```
|
||||
|
||||
## Using uv.lock in Docker
|
||||
|
||||
```dockerfile
|
||||
RUN uv sync --frozen --no-dev --extra api
|
||||
```
|
||||
|
||||
`--frozen` guarantees reproducible builds because uv will refuse to deviate from the locked versions.
|
||||
`--extra api` install API server
|
||||
|
||||
## Generating a lock file that includes offline dependencies
|
||||
|
||||
If you need `uv.lock` to capture the optional offline stacks, regenerate it with the relevant extras enabled:
|
||||
|
||||
```bash
|
||||
uv lock --extra api --extra offline
|
||||
```
|
||||
|
||||
This command resolves the base project requirements plus both the `api` and `offline` optional dependency sets, ensuring downstream `uv sync --frozen --extra api --extra offline` installs work without further resolution.
|
||||
|
||||
## Frequently asked questions
|
||||
|
||||
- **`uv.lock` is almost 1 MB. Does that matter?**
|
||||
No. The file is read only during dependency resolution.
|
||||
|
||||
- **Should we commit `uv.lock`?**
|
||||
Yes. Commit it so collaborators and CI jobs share the same dependency graph.
|
||||
|
||||
- **Deleted the lock file by accident?**
|
||||
Run `uv lock` to regenerate it from `pyproject.toml`.
|
||||
|
||||
- **Can `uv.lock` and `requirements.txt` coexist?**
|
||||
They can, but maintaining both is redundant. Prefer relying on `uv.lock` alone whenever possible.
|
||||
|
||||
- **How do I inspect locked versions?**
|
||||
```bash
|
||||
uv tree
|
||||
grep -A5 'name = "openai"' uv.lock
|
||||
```
|
||||
|
||||
## Best practices
|
||||
|
||||
### Recommended
|
||||
|
||||
1. Commit `uv.lock` alongside `pyproject.toml`.
|
||||
2. Use `uv sync --frozen` in CI, Docker, and other reproducible environments.
|
||||
3. Use plain `uv sync` during local development if you want uv to reconcile the lock for you.
|
||||
4. Run `uv lock --upgrade` periodically to pick up the latest compatible releases.
|
||||
5. Regenerate the lock file immediately after changing dependency constraints.
|
||||
|
||||
### Avoid
|
||||
|
||||
1. Running `uv sync` without `--frozen` in CI or production pipelines.
|
||||
2. Editing `uv.lock` by hand—uv will overwrite manual edits.
|
||||
3. Ignoring lock file diffs in code reviews—unexpected dependency changes can break builds.
|
||||
|
||||
## Summary
|
||||
|
||||
| Command | Updates `uv.lock` | Typical use |
|
||||
|-----------------------|-------------------|-------------------------------------------|
|
||||
| `uv lock` | ✅ Yes | After editing constraints |
|
||||
| `uv lock --upgrade` | ✅ Yes | Upgrade to the newest compatible versions |
|
||||
| `uv add <pkg>` | ✅ Yes | Add a dependency |
|
||||
| `uv remove <pkg>` | ✅ Yes | Remove a dependency |
|
||||
| `uv sync` | ⚠️ Maybe | Local development; can regenerate the lock |
|
||||
| `uv sync --frozen` | ❌ No | CI/CD, Docker, reproducible builds |
|
||||
|
||||
Remember: `uv.lock` only changes when you run a command that tells it to. Keep it in sync with your project and commit it whenever it changes.
|
||||
@@ -0,0 +1,464 @@
|
||||
### This is sample file of .env
|
||||
|
||||
###########################
|
||||
### Server Configuration
|
||||
###########################
|
||||
HOST=0.0.0.0
|
||||
PORT=9621
|
||||
WEBUI_TITLE='My Graph KB'
|
||||
WEBUI_DESCRIPTION="Simple and Fast Graph Based RAG System"
|
||||
# WORKERS=2
|
||||
### gunicorn worker timeout(as default LLM request timeout if LLM_TIMEOUT is not set)
|
||||
# TIMEOUT=150
|
||||
# CORS_ORIGINS=http://localhost:3000,http://localhost:8080
|
||||
|
||||
### Optional SSL Configuration
|
||||
# SSL=true
|
||||
# SSL_CERTFILE=/path/to/cert.pem
|
||||
# SSL_KEYFILE=/path/to/key.pem
|
||||
|
||||
### Directory Configuration (defaults to current working directory)
|
||||
### Default value is ./inputs and ./rag_storage
|
||||
# INPUT_DIR=<absolute_path_for_doc_input_dir>
|
||||
# WORKING_DIR=<absolute_path_for_working_dir>
|
||||
|
||||
### Tiktoken cache directory (Store cached files in this folder for offline deployment)
|
||||
# TIKTOKEN_CACHE_DIR=/app/data/tiktoken
|
||||
|
||||
### Ollama Emulating Model and Tag
|
||||
# OLLAMA_EMULATING_MODEL_NAME=lightrag
|
||||
OLLAMA_EMULATING_MODEL_TAG=latest
|
||||
|
||||
### Max nodes for graph retrieval (Ensure WebUI local settings are also updated, which is limited to this value)
|
||||
# MAX_GRAPH_NODES=1000
|
||||
|
||||
### Logging level
|
||||
# LOG_LEVEL=INFO
|
||||
# VERBOSE=False
|
||||
# LOG_MAX_BYTES=10485760
|
||||
# LOG_BACKUP_COUNT=5
|
||||
### Logfile location (defaults to current working directory)
|
||||
# LOG_DIR=/path/to/log/directory
|
||||
|
||||
#####################################
|
||||
### Login and API-Key Configuration
|
||||
#####################################
|
||||
# AUTH_ACCOUNTS='admin:admin123,user1:pass456'
|
||||
# TOKEN_SECRET=Your-Key-For-LightRAG-API-Server
|
||||
# TOKEN_EXPIRE_HOURS=48
|
||||
# GUEST_TOKEN_EXPIRE_HOURS=24
|
||||
# JWT_ALGORITHM=HS256
|
||||
|
||||
### API-Key to access LightRAG Server API
|
||||
### Use this key in HTTP requests with the 'X-API-Key' header
|
||||
### Example: curl -H "X-API-Key: your-secure-api-key-here" http://localhost:9621/query
|
||||
# LIGHTRAG_API_KEY=your-secure-api-key-here
|
||||
# WHITELIST_PATHS=/health,/api/*
|
||||
|
||||
######################################################################################
|
||||
### Query Configuration
|
||||
###
|
||||
### How to control the context length sent to LLM:
|
||||
### MAX_ENTITY_TOKENS + MAX_RELATION_TOKENS < MAX_TOTAL_TOKENS
|
||||
### Chunk_Tokens = MAX_TOTAL_TOKENS - Actual_Entity_Tokens - Actual_Relation_Tokens
|
||||
######################################################################################
|
||||
# LLM response cache for query (Not valid for streaming response)
|
||||
ENABLE_LLM_CACHE=true
|
||||
# COSINE_THRESHOLD=0.2
|
||||
### Number of entities or relations retrieved from KG
|
||||
# TOP_K=40
|
||||
### Maximum number or chunks for naive vector search
|
||||
# CHUNK_TOP_K=20
|
||||
### control the actual entities send to LLM
|
||||
# MAX_ENTITY_TOKENS=6000
|
||||
### control the actual relations send to LLM
|
||||
# MAX_RELATION_TOKENS=8000
|
||||
### control the maximum tokens send to LLM (include entities, relations and chunks)
|
||||
# MAX_TOTAL_TOKENS=30000
|
||||
|
||||
### chunk selection strategies
|
||||
### VECTOR: Pick KG chunks by vector similarity, delivered chunks to the LLM aligning more closely with naive retrieval
|
||||
### WEIGHT: Pick KG chunks by entity and chunk weight, delivered more solely KG related chunks to the LLM
|
||||
### If reranking is enabled, the impact of chunk selection strategies will be diminished.
|
||||
# KG_CHUNK_PICK_METHOD=VECTOR
|
||||
|
||||
#########################################################
|
||||
### Reranking configuration
|
||||
### RERANK_BINDING type: null, cohere, jina, aliyun
|
||||
### For rerank model deployed by vLLM use cohere binding
|
||||
#########################################################
|
||||
RERANK_BINDING=null
|
||||
### Enable rerank by default in query params when RERANK_BINDING is not null
|
||||
# RERANK_BY_DEFAULT=True
|
||||
### rerank score chunk filter(set to 0.0 to keep all chunks, 0.6 or above if LLM is not strong enough)
|
||||
# MIN_RERANK_SCORE=0.0
|
||||
|
||||
### For local deployment with vLLM
|
||||
# RERANK_MODEL=BAAI/bge-reranker-v2-m3
|
||||
# RERANK_BINDING_HOST=http://localhost:8000/v1/rerank
|
||||
# RERANK_BINDING_API_KEY=your_rerank_api_key_here
|
||||
|
||||
### Default value for Cohere AI
|
||||
# RERANK_MODEL=rerank-v3.5
|
||||
# RERANK_BINDING_HOST=https://api.cohere.com/v2/rerank
|
||||
# RERANK_BINDING_API_KEY=your_rerank_api_key_here
|
||||
|
||||
### Default value for Jina AI
|
||||
# RERANK_MODEL=jina-reranker-v2-base-multilingual
|
||||
# RERANK_BINDING_HOST=https://api.jina.ai/v1/rerank
|
||||
# RERANK_BINDING_API_KEY=your_rerank_api_key_here
|
||||
|
||||
### Default value for Aliyun
|
||||
# RERANK_MODEL=gte-rerank-v2
|
||||
# RERANK_BINDING_HOST=https://dashscope.aliyuncs.com/api/v1/services/rerank/text-rerank/text-rerank
|
||||
# RERANK_BINDING_API_KEY=your_rerank_api_key_here
|
||||
|
||||
########################################
|
||||
### Document processing configuration
|
||||
########################################
|
||||
ENABLE_LLM_CACHE_FOR_EXTRACT=true
|
||||
|
||||
### Document processing output language: English, Chinese, French, German ...
|
||||
SUMMARY_LANGUAGE=English
|
||||
|
||||
### PDF decryption password for protected PDF files
|
||||
# PDF_DECRYPT_PASSWORD=your_pdf_password_here
|
||||
|
||||
### Entity types that the LLM will attempt to recognize
|
||||
# ENTITY_TYPES='["Person", "Creature", "Organization", "Location", "Event", "Concept", "Method", "Content", "Data", "Artifact", "NaturalObject"]'
|
||||
|
||||
### Chunk size for document splitting, 500~1500 is recommended
|
||||
# CHUNK_SIZE=1200
|
||||
# CHUNK_OVERLAP_SIZE=100
|
||||
|
||||
### Number of summary segments or tokens to trigger LLM summary on entity/relation merge (at least 3 is recommended)
|
||||
# FORCE_LLM_SUMMARY_ON_MERGE=8
|
||||
### Max description token size to trigger LLM summary
|
||||
# SUMMARY_MAX_TOKENS = 1200
|
||||
### Recommended LLM summary output length in tokens
|
||||
# SUMMARY_LENGTH_RECOMMENDED_=600
|
||||
### Maximum context size sent to LLM for description summary
|
||||
# SUMMARY_CONTEXT_SIZE=12000
|
||||
|
||||
### control the maximum chunk_ids stored in vector and graph db
|
||||
# MAX_SOURCE_IDS_PER_ENTITY=300
|
||||
# MAX_SOURCE_IDS_PER_RELATION=300
|
||||
### control chunk_ids limitation method: FIFO, KEEP
|
||||
### FIFO: First in first out
|
||||
### KEEP: Keep oldest (less merge action and faster)
|
||||
# SOURCE_IDS_LIMIT_METHOD=FIFO
|
||||
|
||||
# Maximum number of file paths stored in entity/relation file_path field (For displayed only, does not affect query performance)
|
||||
# MAX_FILE_PATHS=100
|
||||
|
||||
### maximum number of related chunks per source entity or relation
|
||||
### The chunk picker uses this value to determine the total number of chunks selected from KG(knowledge graph)
|
||||
### Higher values increase re-ranking time
|
||||
# RELATED_CHUNK_NUMBER=5
|
||||
|
||||
###############################
|
||||
### Concurrency Configuration
|
||||
###############################
|
||||
### Max concurrency requests of LLM (for both query and document processing)
|
||||
MAX_ASYNC=4
|
||||
### Number of parallel processing documents(between 2~10, MAX_ASYNC/3 is recommended)
|
||||
MAX_PARALLEL_INSERT=2
|
||||
### Max concurrency requests for Embedding
|
||||
# EMBEDDING_FUNC_MAX_ASYNC=8
|
||||
### Num of chunks send to Embedding in single request
|
||||
# EMBEDDING_BATCH_NUM=10
|
||||
|
||||
###########################################################################
|
||||
### LLM Configuration
|
||||
### LLM_BINDING type: openai, ollama, lollms, azure_openai, aws_bedrock, gemini
|
||||
### LLM_BINDING_HOST: host only for Ollama, endpoint for other LLM service
|
||||
### If LightRAG deployed in Docker:
|
||||
### uses host.docker.internal instead of localhost in LLM_BINDING_HOST
|
||||
###########################################################################
|
||||
### LLM request timeout setting for all llm (0 means no timeout for Ollma)
|
||||
# LLM_TIMEOUT=180
|
||||
|
||||
LLM_BINDING=openai
|
||||
LLM_MODEL=gpt-4o
|
||||
LLM_BINDING_HOST=https://api.openai.com/v1
|
||||
LLM_BINDING_API_KEY=your_api_key
|
||||
|
||||
### Env vars for Azure openai
|
||||
# AZURE_OPENAI_API_VERSION=2024-08-01-preview
|
||||
# AZURE_OPENAI_DEPLOYMENT=gpt-4o
|
||||
|
||||
### Openrouter example
|
||||
# LLM_MODEL=google/gemini-2.5-flash
|
||||
# LLM_BINDING_HOST=https://openrouter.ai/api/v1
|
||||
# LLM_BINDING_API_KEY=your_api_key
|
||||
# LLM_BINDING=openai
|
||||
|
||||
### Gemini example
|
||||
# LLM_BINDING=gemini
|
||||
# LLM_MODEL=gemini-flash-latest
|
||||
# LLM_BINDING_API_KEY=your_gemini_api_key
|
||||
# LLM_BINDING_HOST=https://generativelanguage.googleapis.com
|
||||
|
||||
### use the following command to see all support options for OpenAI, azure_openai or OpenRouter
|
||||
### lightrag-server --llm-binding gemini --help
|
||||
### Gemini Specific Parameters
|
||||
# GEMINI_LLM_MAX_OUTPUT_TOKENS=9000
|
||||
# GEMINI_LLM_TEMPERATURE=0.7
|
||||
### Enable Thinking
|
||||
# GEMINI_LLM_THINKING_CONFIG='{"thinking_budget": -1, "include_thoughts": true}'
|
||||
### Disable Thinking
|
||||
# GEMINI_LLM_THINKING_CONFIG='{"thinking_budget": 0, "include_thoughts": false}'
|
||||
|
||||
### use the following command to see all support options for OpenAI, azure_openai or OpenRouter
|
||||
### lightrag-server --llm-binding openai --help
|
||||
### OpenAI Specific Parameters
|
||||
# OPENAI_LLM_REASONING_EFFORT=minimal
|
||||
### OpenRouter Specific Parameters
|
||||
# OPENAI_LLM_EXTRA_BODY='{"reasoning": {"enabled": false}}'
|
||||
### Qwen3 Specific Parameters deploy by vLLM
|
||||
# OPENAI_LLM_EXTRA_BODY='{"chat_template_kwargs": {"enable_thinking": false}}'
|
||||
|
||||
### OpenAI Compatible API Specific Parameters
|
||||
### Increased temperature values may mitigate infinite inference loops in certain LLM, such as Qwen3-30B.
|
||||
# OPENAI_LLM_TEMPERATURE=0.9
|
||||
### Set the max_tokens to mitigate endless output of some LLM (less than LLM_TIMEOUT * llm_output_tokens/second, i.e. 9000 = 180s * 50 tokens/s)
|
||||
### Typically, max_tokens does not include prompt content
|
||||
### For vLLM/SGLang deployed models, or most of OpenAI compatible API provider
|
||||
# OPENAI_LLM_MAX_TOKENS=9000
|
||||
### For OpenAI o1-mini or newer modles utilizes max_completion_tokens instead of max_tokens
|
||||
OPENAI_LLM_MAX_COMPLETION_TOKENS=9000
|
||||
|
||||
### use the following command to see all support options for Ollama LLM
|
||||
### lightrag-server --llm-binding ollama --help
|
||||
### Ollama Server Specific Parameters
|
||||
### OLLAMA_LLM_NUM_CTX must be provided, and should at least larger than MAX_TOTAL_TOKENS + 2000
|
||||
OLLAMA_LLM_NUM_CTX=32768
|
||||
### Set the max_output_tokens to mitigate endless output of some LLM (less than LLM_TIMEOUT * llm_output_tokens/second, i.e. 9000 = 180s * 50 tokens/s)
|
||||
# OLLAMA_LLM_NUM_PREDICT=9000
|
||||
### Stop sequences for Ollama LLM
|
||||
# OLLAMA_LLM_STOP='["</s>", "<|EOT|>"]'
|
||||
|
||||
### Bedrock Specific Parameters
|
||||
# BEDROCK_LLM_TEMPERATURE=1.0
|
||||
|
||||
#######################################################################################
|
||||
### Embedding Configuration (Should not be changed after the first file processed)
|
||||
### EMBEDDING_BINDING: ollama, openai, azure_openai, jina, lollms, aws_bedrock
|
||||
### EMBEDDING_BINDING_HOST: host only for Ollama, endpoint for other Embedding service
|
||||
### If LightRAG deployed in Docker:
|
||||
### uses host.docker.internal instead of localhost in EMBEDDING_BINDING_HOST
|
||||
#######################################################################################
|
||||
# EMBEDDING_TIMEOUT=30
|
||||
|
||||
### Control whether to send embedding_dim parameter to embedding API
|
||||
### IMPORTANT: Jina ALWAYS sends dimension parameter (API requirement) - this setting is ignored for Jina
|
||||
### For OpenAI: Set to 'true' to enable dynamic dimension adjustment
|
||||
### For OpenAI: Set to 'false' (default) to disable sending dimension parameter
|
||||
### Note: Automatically ignored for backends that don't support dimension parameter (e.g., Ollama)
|
||||
|
||||
# Ollama embedding
|
||||
# EMBEDDING_BINDING=ollama
|
||||
# EMBEDDING_MODEL=bge-m3:latest
|
||||
# EMBEDDING_DIM=1024
|
||||
# EMBEDDING_BINDING_API_KEY=your_api_key
|
||||
### If LightRAG deployed in Docker uses host.docker.internal instead of localhost
|
||||
# EMBEDDING_BINDING_HOST=http://localhost:11434
|
||||
|
||||
### OpenAI compatible embedding
|
||||
EMBEDDING_BINDING=openai
|
||||
EMBEDDING_MODEL=text-embedding-3-large
|
||||
EMBEDDING_DIM=3072
|
||||
EMBEDDING_SEND_DIM=false
|
||||
EMBEDDING_TOKEN_LIMIT=8192
|
||||
EMBEDDING_BINDING_HOST=https://api.openai.com/v1
|
||||
EMBEDDING_BINDING_API_KEY=your_api_key
|
||||
|
||||
### Optional for Azure
|
||||
# AZURE_EMBEDDING_DEPLOYMENT=text-embedding-3-large
|
||||
# AZURE_EMBEDDING_API_VERSION=2023-05-15
|
||||
# AZURE_EMBEDDING_ENDPOINT=your_endpoint
|
||||
# AZURE_EMBEDDING_API_KEY=your_api_key
|
||||
|
||||
### Gemini embedding
|
||||
# EMBEDDING_BINDING=gemini
|
||||
# EMBEDDING_MODEL=gemini-embedding-001
|
||||
# EMBEDDING_DIM=1536
|
||||
# EMBEDDING_TOKEN_LIMIT=2048
|
||||
# EMBEDDING_BINDING_HOST=https://generativelanguage.googleapis.com
|
||||
# EMBEDDING_BINDING_API_KEY=your_api_key
|
||||
### Gemini embedding requires sending dimension to server
|
||||
# EMBEDDING_SEND_DIM=true
|
||||
|
||||
### Jina AI Embedding
|
||||
# EMBEDDING_BINDING=jina
|
||||
# EMBEDDING_BINDING_HOST=https://api.jina.ai/v1/embeddings
|
||||
# EMBEDDING_MODEL=jina-embeddings-v4
|
||||
# EMBEDDING_DIM=2048
|
||||
# EMBEDDING_BINDING_API_KEY=your_api_key
|
||||
|
||||
### Optional for Ollama embedding
|
||||
OLLAMA_EMBEDDING_NUM_CTX=8192
|
||||
### use the following command to see all support options for Ollama embedding
|
||||
### lightrag-server --embedding-binding ollama --help
|
||||
|
||||
####################################################################
|
||||
### WORKSPACE sets workspace name for all storage types
|
||||
### for the purpose of isolating data from LightRAG instances.
|
||||
### Valid workspace name constraints: a-z, A-Z, 0-9, and _
|
||||
####################################################################
|
||||
# WORKSPACE=space1
|
||||
|
||||
############################
|
||||
### Data storage selection
|
||||
############################
|
||||
### Default storage (Recommended for small scale deployment)
|
||||
# LIGHTRAG_KV_STORAGE=JsonKVStorage
|
||||
# LIGHTRAG_DOC_STATUS_STORAGE=JsonDocStatusStorage
|
||||
# LIGHTRAG_GRAPH_STORAGE=NetworkXStorage
|
||||
# LIGHTRAG_VECTOR_STORAGE=NanoVectorDBStorage
|
||||
|
||||
### Redis Storage (Recommended for production deployment)
|
||||
# LIGHTRAG_KV_STORAGE=RedisKVStorage
|
||||
# LIGHTRAG_DOC_STATUS_STORAGE=RedisDocStatusStorage
|
||||
|
||||
### Vector Storage (Recommended for production deployment)
|
||||
# LIGHTRAG_VECTOR_STORAGE=MilvusVectorDBStorage
|
||||
# LIGHTRAG_VECTOR_STORAGE=QdrantVectorDBStorage
|
||||
# LIGHTRAG_VECTOR_STORAGE=FaissVectorDBStorage
|
||||
|
||||
### Graph Storage (Recommended for production deployment)
|
||||
# LIGHTRAG_GRAPH_STORAGE=Neo4JStorage
|
||||
# LIGHTRAG_GRAPH_STORAGE=MemgraphStorage
|
||||
|
||||
### PostgreSQL
|
||||
# LIGHTRAG_KV_STORAGE=PGKVStorage
|
||||
# LIGHTRAG_DOC_STATUS_STORAGE=PGDocStatusStorage
|
||||
# LIGHTRAG_GRAPH_STORAGE=PGGraphStorage
|
||||
# LIGHTRAG_VECTOR_STORAGE=PGVectorStorage
|
||||
|
||||
### MongoDB (Vector storage only available on Atlas Cloud)
|
||||
# LIGHTRAG_KV_STORAGE=MongoKVStorage
|
||||
# LIGHTRAG_DOC_STATUS_STORAGE=MongoDocStatusStorage
|
||||
# LIGHTRAG_GRAPH_STORAGE=MongoGraphStorage
|
||||
# LIGHTRAG_VECTOR_STORAGE=MongoVectorDBStorage
|
||||
|
||||
### PostgreSQL Configuration
|
||||
POSTGRES_HOST=localhost
|
||||
POSTGRES_PORT=5432
|
||||
POSTGRES_USER=your_username
|
||||
POSTGRES_PASSWORD='your_password'
|
||||
POSTGRES_DATABASE=your_database
|
||||
POSTGRES_MAX_CONNECTIONS=12
|
||||
# POSTGRES_WORKSPACE=forced_workspace_name
|
||||
|
||||
### PostgreSQL Vector Storage Configuration
|
||||
### Vector storage type: HNSW, IVFFlat
|
||||
POSTGRES_VECTOR_INDEX_TYPE=HNSW
|
||||
POSTGRES_HNSW_M=16
|
||||
POSTGRES_HNSW_EF=200
|
||||
POSTGRES_IVFFLAT_LISTS=100
|
||||
|
||||
### PostgreSQL Connection Retry Configuration (Network Robustness)
|
||||
### Number of retry attempts (1-10, default: 3)
|
||||
### Initial retry backoff in seconds (0.1-5.0, default: 0.5)
|
||||
### Maximum retry backoff in seconds (backoff-60.0, default: 5.0)
|
||||
### Connection pool close timeout in seconds (1.0-30.0, default: 5.0)
|
||||
# POSTGRES_CONNECTION_RETRIES=3
|
||||
# POSTGRES_CONNECTION_RETRY_BACKOFF=0.5
|
||||
# POSTGRES_CONNECTION_RETRY_BACKOFF_MAX=5.0
|
||||
# POSTGRES_POOL_CLOSE_TIMEOUT=5.0
|
||||
|
||||
### PostgreSQL SSL Configuration (Optional)
|
||||
# POSTGRES_SSL_MODE=require
|
||||
# POSTGRES_SSL_CERT=/path/to/client-cert.pem
|
||||
# POSTGRES_SSL_KEY=/path/to/client-key.pem
|
||||
# POSTGRES_SSL_ROOT_CERT=/path/to/ca-cert.pem
|
||||
# POSTGRES_SSL_CRL=/path/to/crl.pem
|
||||
|
||||
### PostgreSQL Server Settings (for Supabase Supavisor)
|
||||
# Use this to pass extra options to the PostgreSQL connection string.
|
||||
# For Supabase, you might need to set it like this:
|
||||
# POSTGRES_SERVER_SETTINGS="options=reference%3D[project-ref]"
|
||||
|
||||
# Default is 100 set to 0 to disable
|
||||
# POSTGRES_STATEMENT_CACHE_SIZE=100
|
||||
|
||||
### Neo4j Configuration
|
||||
NEO4J_URI=neo4j+s://xxxxxxxx.databases.neo4j.io
|
||||
NEO4J_USERNAME=neo4j
|
||||
NEO4J_PASSWORD='your_password'
|
||||
NEO4J_DATABASE=neo4j
|
||||
NEO4J_MAX_CONNECTION_POOL_SIZE=100
|
||||
NEO4J_CONNECTION_TIMEOUT=30
|
||||
NEO4J_CONNECTION_ACQUISITION_TIMEOUT=30
|
||||
NEO4J_MAX_TRANSACTION_RETRY_TIME=30
|
||||
NEO4J_MAX_CONNECTION_LIFETIME=300
|
||||
NEO4J_LIVENESS_CHECK_TIMEOUT=30
|
||||
NEO4J_KEEP_ALIVE=true
|
||||
# NEO4J_WORKSPACE=forced_workspace_name
|
||||
|
||||
### MongoDB Configuration
|
||||
MONGO_URI=mongodb://root:root@localhost:27017/
|
||||
#MONGO_URI=mongodb+srv://xxxx
|
||||
MONGO_DATABASE=LightRAG
|
||||
# MONGODB_WORKSPACE=forced_workspace_name
|
||||
|
||||
### Milvus Configuration
|
||||
MILVUS_URI=http://localhost:19530
|
||||
MILVUS_DB_NAME=lightrag
|
||||
# MILVUS_USER=root
|
||||
# MILVUS_PASSWORD=your_password
|
||||
# MILVUS_TOKEN=your_token
|
||||
# MILVUS_WORKSPACE=forced_workspace_name
|
||||
|
||||
### Qdrant
|
||||
QDRANT_URL=http://localhost:6333
|
||||
# QDRANT_API_KEY=your-api-key
|
||||
# QDRANT_WORKSPACE=forced_workspace_name
|
||||
|
||||
### Redis
|
||||
REDIS_URI=redis://localhost:6379
|
||||
REDIS_SOCKET_TIMEOUT=30
|
||||
REDIS_CONNECT_TIMEOUT=10
|
||||
REDIS_MAX_CONNECTIONS=100
|
||||
REDIS_RETRY_ATTEMPTS=3
|
||||
# REDIS_WORKSPACE=forced_workspace_name
|
||||
|
||||
### Memgraph Configuration
|
||||
MEMGRAPH_URI=bolt://localhost:7687
|
||||
MEMGRAPH_USERNAME=
|
||||
MEMGRAPH_PASSWORD=
|
||||
MEMGRAPH_DATABASE=memgraph
|
||||
# MEMGRAPH_WORKSPACE=forced_workspace_name
|
||||
|
||||
############################
|
||||
### Evaluation Configuration
|
||||
############################
|
||||
### RAGAS evaluation models (used for RAG quality assessment)
|
||||
### ⚠️ IMPORTANT: Both LLM and Embedding endpoints MUST be OpenAI-compatible
|
||||
### Default uses OpenAI models for evaluation
|
||||
|
||||
### LLM Configuration for Evaluation
|
||||
# EVAL_LLM_MODEL=gpt-4o-mini
|
||||
### API key for LLM evaluation (fallback to OPENAI_API_KEY if not set)
|
||||
# EVAL_LLM_BINDING_API_KEY=your_api_key
|
||||
### Custom OpenAI-compatible endpoint for LLM evaluation (optional)
|
||||
# EVAL_LLM_BINDING_HOST=https://api.openai.com/v1
|
||||
|
||||
### Embedding Configuration for Evaluation
|
||||
# EVAL_EMBEDDING_MODEL=text-embedding-3-large
|
||||
### API key for embeddings (fallback: EVAL_LLM_BINDING_API_KEY -> OPENAI_API_KEY)
|
||||
# EVAL_EMBEDDING_BINDING_API_KEY=your_embedding_api_key
|
||||
### Custom OpenAI-compatible endpoint for embeddings (fallback: EVAL_LLM_BINDING_HOST)
|
||||
# EVAL_EMBEDDING_BINDING_HOST=https://api.openai.com/v1
|
||||
|
||||
### Performance Tuning
|
||||
### Number of concurrent test case evaluations
|
||||
### Lower values reduce API rate limit issues but increase evaluation time
|
||||
# EVAL_MAX_CONCURRENT=2
|
||||
### TOP_K query parameter of LightRAG (default: 10)
|
||||
### Number of entities or relations retrieved from KG
|
||||
# EVAL_QUERY_TOP_K=10
|
||||
### LLM request retry and timeout settings for evaluation
|
||||
# EVAL_LLM_MAX_RETRIES=5
|
||||
# EVAL_LLM_TIMEOUT=180
|
||||
@@ -0,0 +1,195 @@
|
||||
################################################################################
|
||||
# Autogenerated .env entries list for LightRAG binding options
|
||||
#
|
||||
# To generate run:
|
||||
# $ python -m lightrag.llm.binding_options
|
||||
################################################################################
|
||||
# ollama_embedding -- Context window size (number of tokens)
|
||||
# OLLAMA_EMBEDDING_NUM_CTX=4096
|
||||
|
||||
# ollama_embedding -- Maximum number of tokens to predict
|
||||
# OLLAMA_EMBEDDING_NUM_PREDICT=128
|
||||
|
||||
# ollama_embedding -- Number of tokens to keep from the initial prompt
|
||||
# OLLAMA_EMBEDDING_NUM_KEEP=0
|
||||
|
||||
# ollama_embedding -- Random seed for generation (-1 for random)
|
||||
# OLLAMA_EMBEDDING_SEED=-1
|
||||
|
||||
# ollama_embedding -- Controls randomness (0.0-2.0, higher = more creative)
|
||||
# OLLAMA_EMBEDDING_TEMPERATURE=0.8
|
||||
|
||||
# ollama_embedding -- Top-k sampling parameter (0 = disabled)
|
||||
# OLLAMA_EMBEDDING_TOP_K=40
|
||||
|
||||
# ollama_embedding -- Top-p (nucleus) sampling parameter (0.0-1.0)
|
||||
# OLLAMA_EMBEDDING_TOP_P=0.9
|
||||
|
||||
# ollama_embedding -- Tail free sampling parameter (1.0 = disabled)
|
||||
# OLLAMA_EMBEDDING_TFS_Z=1.0
|
||||
|
||||
# ollama_embedding -- Typical probability mass (1.0 = disabled)
|
||||
# OLLAMA_EMBEDDING_TYPICAL_P=1.0
|
||||
|
||||
# ollama_embedding -- Minimum probability threshold (0.0 = disabled)
|
||||
# OLLAMA_EMBEDDING_MIN_P=0.0
|
||||
|
||||
# ollama_embedding -- Number of tokens to consider for repetition penalty
|
||||
# OLLAMA_EMBEDDING_REPEAT_LAST_N=64
|
||||
|
||||
# ollama_embedding -- Penalty for repetition (1.0 = no penalty)
|
||||
# OLLAMA_EMBEDDING_REPEAT_PENALTY=1.1
|
||||
|
||||
# ollama_embedding -- Penalty for token presence (-2.0 to 2.0)
|
||||
# OLLAMA_EMBEDDING_PRESENCE_PENALTY=0.0
|
||||
|
||||
# ollama_embedding -- Penalty for token frequency (-2.0 to 2.0)
|
||||
# OLLAMA_EMBEDDING_FREQUENCY_PENALTY=0.0
|
||||
|
||||
# ollama_embedding -- Mirostat sampling algorithm (0=disabled, 1=Mirostat 1.0, 2=Mirostat 2.0)
|
||||
# OLLAMA_EMBEDDING_MIROSTAT=0
|
||||
|
||||
# ollama_embedding -- Mirostat target entropy
|
||||
# OLLAMA_EMBEDDING_MIROSTAT_TAU=5.0
|
||||
|
||||
# ollama_embedding -- Mirostat learning rate
|
||||
# OLLAMA_EMBEDDING_MIROSTAT_ETA=0.1
|
||||
|
||||
# ollama_embedding -- Enable NUMA optimization
|
||||
# OLLAMA_EMBEDDING_NUMA=False
|
||||
|
||||
# ollama_embedding -- Batch size for processing
|
||||
# OLLAMA_EMBEDDING_NUM_BATCH=512
|
||||
|
||||
# ollama_embedding -- Number of GPUs to use (-1 for auto)
|
||||
# OLLAMA_EMBEDDING_NUM_GPU=-1
|
||||
|
||||
# ollama_embedding -- Main GPU index
|
||||
# OLLAMA_EMBEDDING_MAIN_GPU=0
|
||||
|
||||
# ollama_embedding -- Optimize for low VRAM
|
||||
# OLLAMA_EMBEDDING_LOW_VRAM=False
|
||||
|
||||
# ollama_embedding -- Number of CPU threads (0 for auto)
|
||||
# OLLAMA_EMBEDDING_NUM_THREAD=0
|
||||
|
||||
# ollama_embedding -- Use half-precision for key/value cache
|
||||
# OLLAMA_EMBEDDING_F16_KV=True
|
||||
|
||||
# ollama_embedding -- Return logits for all tokens
|
||||
# OLLAMA_EMBEDDING_LOGITS_ALL=False
|
||||
|
||||
# ollama_embedding -- Only load vocabulary
|
||||
# OLLAMA_EMBEDDING_VOCAB_ONLY=False
|
||||
|
||||
# ollama_embedding -- Use memory mapping for model files
|
||||
# OLLAMA_EMBEDDING_USE_MMAP=True
|
||||
|
||||
# ollama_embedding -- Lock model in memory
|
||||
# OLLAMA_EMBEDDING_USE_MLOCK=False
|
||||
|
||||
# ollama_embedding -- Only use for embeddings
|
||||
# OLLAMA_EMBEDDING_EMBEDDING_ONLY=False
|
||||
|
||||
# ollama_embedding -- Penalize newline tokens
|
||||
# OLLAMA_EMBEDDING_PENALIZE_NEWLINE=True
|
||||
|
||||
# ollama_embedding -- Stop sequences (comma-separated string)
|
||||
# OLLAMA_EMBEDDING_STOP=
|
||||
|
||||
# ollama_llm -- Context window size (number of tokens)
|
||||
# OLLAMA_LLM_NUM_CTX=4096
|
||||
|
||||
# ollama_llm -- Maximum number of tokens to predict
|
||||
# OLLAMA_LLM_NUM_PREDICT=128
|
||||
|
||||
# ollama_llm -- Number of tokens to keep from the initial prompt
|
||||
# OLLAMA_LLM_NUM_KEEP=0
|
||||
|
||||
# ollama_llm -- Random seed for generation (-1 for random)
|
||||
# OLLAMA_LLM_SEED=-1
|
||||
|
||||
# ollama_llm -- Controls randomness (0.0-2.0, higher = more creative)
|
||||
# OLLAMA_LLM_TEMPERATURE=0.8
|
||||
|
||||
# ollama_llm -- Top-k sampling parameter (0 = disabled)
|
||||
# OLLAMA_LLM_TOP_K=40
|
||||
|
||||
# ollama_llm -- Top-p (nucleus) sampling parameter (0.0-1.0)
|
||||
# OLLAMA_LLM_TOP_P=0.9
|
||||
|
||||
# ollama_llm -- Tail free sampling parameter (1.0 = disabled)
|
||||
# OLLAMA_LLM_TFS_Z=1.0
|
||||
|
||||
# ollama_llm -- Typical probability mass (1.0 = disabled)
|
||||
# OLLAMA_LLM_TYPICAL_P=1.0
|
||||
|
||||
# ollama_llm -- Minimum probability threshold (0.0 = disabled)
|
||||
# OLLAMA_LLM_MIN_P=0.0
|
||||
|
||||
# ollama_llm -- Number of tokens to consider for repetition penalty
|
||||
# OLLAMA_LLM_REPEAT_LAST_N=64
|
||||
|
||||
# ollama_llm -- Penalty for repetition (1.0 = no penalty)
|
||||
# OLLAMA_LLM_REPEAT_PENALTY=1.1
|
||||
|
||||
# ollama_llm -- Penalty for token presence (-2.0 to 2.0)
|
||||
# OLLAMA_LLM_PRESENCE_PENALTY=0.0
|
||||
|
||||
# ollama_llm -- Penalty for token frequency (-2.0 to 2.0)
|
||||
# OLLAMA_LLM_FREQUENCY_PENALTY=0.0
|
||||
|
||||
# ollama_llm -- Mirostat sampling algorithm (0=disabled, 1=Mirostat 1.0, 2=Mirostat 2.0)
|
||||
# OLLAMA_LLM_MIROSTAT=0
|
||||
|
||||
# ollama_llm -- Mirostat target entropy
|
||||
# OLLAMA_LLM_MIROSTAT_TAU=5.0
|
||||
|
||||
# ollama_llm -- Mirostat learning rate
|
||||
# OLLAMA_LLM_MIROSTAT_ETA=0.1
|
||||
|
||||
# ollama_llm -- Enable NUMA optimization
|
||||
# OLLAMA_LLM_NUMA=False
|
||||
|
||||
# ollama_llm -- Batch size for processing
|
||||
# OLLAMA_LLM_NUM_BATCH=512
|
||||
|
||||
# ollama_llm -- Number of GPUs to use (-1 for auto)
|
||||
# OLLAMA_LLM_NUM_GPU=-1
|
||||
|
||||
# ollama_llm -- Main GPU index
|
||||
# OLLAMA_LLM_MAIN_GPU=0
|
||||
|
||||
# ollama_llm -- Optimize for low VRAM
|
||||
# OLLAMA_LLM_LOW_VRAM=False
|
||||
|
||||
# ollama_llm -- Number of CPU threads (0 for auto)
|
||||
# OLLAMA_LLM_NUM_THREAD=0
|
||||
|
||||
# ollama_llm -- Use half-precision for key/value cache
|
||||
# OLLAMA_LLM_F16_KV=True
|
||||
|
||||
# ollama_llm -- Return logits for all tokens
|
||||
# OLLAMA_LLM_LOGITS_ALL=False
|
||||
|
||||
# ollama_llm -- Only load vocabulary
|
||||
# OLLAMA_LLM_VOCAB_ONLY=False
|
||||
|
||||
# ollama_llm -- Use memory mapping for model files
|
||||
# OLLAMA_LLM_USE_MMAP=True
|
||||
|
||||
# ollama_llm -- Lock model in memory
|
||||
# OLLAMA_LLM_USE_MLOCK=False
|
||||
|
||||
# ollama_llm -- Only use for embeddings
|
||||
# OLLAMA_LLM_EMBEDDING_ONLY=False
|
||||
|
||||
# ollama_llm -- Penalize newline tokens
|
||||
# OLLAMA_LLM_PENALIZE_NEWLINE=True
|
||||
|
||||
# ollama_llm -- Stop sequences (comma-separated string)
|
||||
# OLLAMA_LLM_STOP=
|
||||
|
||||
#
|
||||
# End of .env entries for LightRAG binding options
|
||||
################################################################################
|
||||
@@ -0,0 +1,55 @@
|
||||
from openai import OpenAI
|
||||
|
||||
# os.environ["OPENAI_API_KEY"] = ""
|
||||
|
||||
|
||||
def openai_complete_if_cache(
|
||||
model="gpt-4o-mini", prompt=None, system_prompt=None, history_messages=[], **kwargs
|
||||
) -> str:
|
||||
openai_client = OpenAI()
|
||||
|
||||
messages = []
|
||||
if system_prompt:
|
||||
messages.append({"role": "system", "content": system_prompt})
|
||||
messages.extend(history_messages)
|
||||
messages.append({"role": "user", "content": prompt})
|
||||
|
||||
response = openai_client.chat.completions.create(
|
||||
model=model, messages=messages, **kwargs
|
||||
)
|
||||
return response.choices[0].message.content
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
description = ""
|
||||
prompt = f"""
|
||||
Given the following description of a dataset:
|
||||
|
||||
{description}
|
||||
|
||||
Please identify 5 potential users who would engage with this dataset. For each user, list 5 tasks they would perform with this dataset. Then, for each (user, task) combination, generate 5 questions that require a high-level understanding of the entire dataset.
|
||||
|
||||
Output the results in the following structure:
|
||||
- User 1: [user description]
|
||||
- Task 1: [task description]
|
||||
- Question 1:
|
||||
- Question 2:
|
||||
- Question 3:
|
||||
- Question 4:
|
||||
- Question 5:
|
||||
- Task 2: [task description]
|
||||
...
|
||||
- Task 5: [task description]
|
||||
- User 2: [user description]
|
||||
...
|
||||
- User 5: [user description]
|
||||
...
|
||||
"""
|
||||
|
||||
result = openai_complete_if_cache(model="gpt-4o-mini", prompt=prompt)
|
||||
|
||||
file_path = "./queries.txt"
|
||||
with open(file_path, "w") as file:
|
||||
file.write(result)
|
||||
|
||||
print(f"Queries written to {file_path}")
|
||||
@@ -0,0 +1,34 @@
|
||||
import pipmaster as pm
|
||||
|
||||
if not pm.is_installed("pyvis"):
|
||||
pm.install("pyvis")
|
||||
if not pm.is_installed("networkx"):
|
||||
pm.install("networkx")
|
||||
|
||||
import networkx as nx
|
||||
from pyvis.network import Network
|
||||
import random
|
||||
|
||||
# Load the GraphML file
|
||||
G = nx.read_graphml("./dickens/graph_chunk_entity_relation.graphml")
|
||||
|
||||
# Create a Pyvis network
|
||||
net = Network(height="100vh", notebook=True)
|
||||
|
||||
# Convert NetworkX graph to Pyvis network
|
||||
net.from_nx(G)
|
||||
|
||||
|
||||
# Add colors and title to nodes
|
||||
for node in net.nodes:
|
||||
node["color"] = "#{:06x}".format(random.randint(0, 0xFFFFFF))
|
||||
if "description" in node:
|
||||
node["title"] = node["description"]
|
||||
|
||||
# Add title to edges
|
||||
for edge in net.edges:
|
||||
if "description" in edge:
|
||||
edge["title"] = edge["description"]
|
||||
|
||||
# Save and display the network
|
||||
net.show("knowledge_graph.html")
|
||||
@@ -0,0 +1,186 @@
|
||||
import os
|
||||
import json
|
||||
import xml.etree.ElementTree as ET
|
||||
from neo4j import GraphDatabase
|
||||
|
||||
# Constants
|
||||
WORKING_DIR = "./dickens"
|
||||
BATCH_SIZE_NODES = 500
|
||||
BATCH_SIZE_EDGES = 100
|
||||
|
||||
# Neo4j connection credentials
|
||||
NEO4J_URI = "bolt://localhost:7687"
|
||||
NEO4J_USERNAME = "neo4j"
|
||||
NEO4J_PASSWORD = "your_password"
|
||||
|
||||
|
||||
def xml_to_json(xml_file):
|
||||
try:
|
||||
tree = ET.parse(xml_file)
|
||||
root = tree.getroot()
|
||||
|
||||
# Print the root element's tag and attributes to confirm the file has been correctly loaded
|
||||
print(f"Root element: {root.tag}")
|
||||
print(f"Root attributes: {root.attrib}")
|
||||
|
||||
data = {"nodes": [], "edges": []}
|
||||
|
||||
# Use namespace
|
||||
namespace = {"": "http://graphml.graphdrawing.org/xmlns"}
|
||||
|
||||
for node in root.findall(".//node", namespace):
|
||||
node_data = {
|
||||
"id": node.get("id").strip('"'),
|
||||
"entity_type": node.find("./data[@key='d1']", namespace).text.strip('"')
|
||||
if node.find("./data[@key='d1']", namespace) is not None
|
||||
else "",
|
||||
"description": node.find("./data[@key='d2']", namespace).text
|
||||
if node.find("./data[@key='d2']", namespace) is not None
|
||||
else "",
|
||||
"source_id": node.find("./data[@key='d3']", namespace).text
|
||||
if node.find("./data[@key='d3']", namespace) is not None
|
||||
else "",
|
||||
}
|
||||
data["nodes"].append(node_data)
|
||||
|
||||
for edge in root.findall(".//edge", namespace):
|
||||
edge_data = {
|
||||
"source": edge.get("source").strip('"'),
|
||||
"target": edge.get("target").strip('"'),
|
||||
"weight": float(edge.find("./data[@key='d5']", namespace).text)
|
||||
if edge.find("./data[@key='d5']", namespace) is not None
|
||||
else 0.0,
|
||||
"description": edge.find("./data[@key='d6']", namespace).text
|
||||
if edge.find("./data[@key='d6']", namespace) is not None
|
||||
else "",
|
||||
"keywords": edge.find("./data[@key='d9']", namespace).text
|
||||
if edge.find("./data[@key='d9']", namespace) is not None
|
||||
else "",
|
||||
"source_id": edge.find("./data[@key='d8']", namespace).text
|
||||
if edge.find("./data[@key='d8']", namespace) is not None
|
||||
else "",
|
||||
}
|
||||
data["edges"].append(edge_data)
|
||||
|
||||
# Print the number of nodes and edges found
|
||||
print(f"Found {len(data['nodes'])} nodes and {len(data['edges'])} edges")
|
||||
|
||||
return data
|
||||
except ET.ParseError as e:
|
||||
print(f"Error parsing XML file: {e}")
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def convert_xml_to_json(xml_path, output_path):
|
||||
"""Converts XML file to JSON and saves the output."""
|
||||
if not os.path.exists(xml_path):
|
||||
print(f"Error: File not found - {xml_path}")
|
||||
return None
|
||||
|
||||
json_data = xml_to_json(xml_path)
|
||||
if json_data:
|
||||
with open(output_path, "w", encoding="utf-8") as f:
|
||||
json.dump(json_data, f, ensure_ascii=False, indent=2)
|
||||
print(f"JSON file created: {output_path}")
|
||||
return json_data
|
||||
else:
|
||||
print("Failed to create JSON data")
|
||||
return None
|
||||
|
||||
|
||||
def process_in_batches(tx, query, data, batch_size):
|
||||
"""Process data in batches and execute the given query."""
|
||||
for i in range(0, len(data), batch_size):
|
||||
batch = data[i : i + batch_size]
|
||||
tx.run(query, {"nodes": batch} if "nodes" in query else {"edges": batch})
|
||||
|
||||
|
||||
def main():
|
||||
# Paths
|
||||
xml_file = os.path.join(WORKING_DIR, "graph_chunk_entity_relation.graphml")
|
||||
json_file = os.path.join(WORKING_DIR, "graph_data.json")
|
||||
|
||||
# Convert XML to JSON
|
||||
json_data = convert_xml_to_json(xml_file, json_file)
|
||||
if json_data is None:
|
||||
return
|
||||
|
||||
# Load nodes and edges
|
||||
nodes = json_data.get("nodes", [])
|
||||
edges = json_data.get("edges", [])
|
||||
|
||||
# Neo4j queries
|
||||
create_nodes_query = """
|
||||
UNWIND $nodes AS node
|
||||
MERGE (e:Entity {id: node.id})
|
||||
SET e.entity_type = node.entity_type,
|
||||
e.description = node.description,
|
||||
e.source_id = node.source_id,
|
||||
e.displayName = node.id
|
||||
REMOVE e:Entity
|
||||
WITH e, node
|
||||
CALL apoc.create.addLabels(e, [node.id]) YIELD node AS labeledNode
|
||||
RETURN count(*)
|
||||
"""
|
||||
|
||||
create_edges_query = """
|
||||
UNWIND $edges AS edge
|
||||
MATCH (source {id: edge.source})
|
||||
MATCH (target {id: edge.target})
|
||||
WITH source, target, edge,
|
||||
CASE
|
||||
WHEN edge.keywords CONTAINS 'lead' THEN 'lead'
|
||||
WHEN edge.keywords CONTAINS 'participate' THEN 'participate'
|
||||
WHEN edge.keywords CONTAINS 'uses' THEN 'uses'
|
||||
WHEN edge.keywords CONTAINS 'located' THEN 'located'
|
||||
WHEN edge.keywords CONTAINS 'occurs' THEN 'occurs'
|
||||
ELSE REPLACE(SPLIT(edge.keywords, ',')[0], '\"', '')
|
||||
END AS relType
|
||||
CALL apoc.create.relationship(source, relType, {
|
||||
weight: edge.weight,
|
||||
description: edge.description,
|
||||
keywords: edge.keywords,
|
||||
source_id: edge.source_id
|
||||
}, target) YIELD rel
|
||||
RETURN count(*)
|
||||
"""
|
||||
|
||||
set_displayname_and_labels_query = """
|
||||
MATCH (n)
|
||||
SET n.displayName = n.id
|
||||
WITH n
|
||||
CALL apoc.create.setLabels(n, [n.entity_type]) YIELD node
|
||||
RETURN count(*)
|
||||
"""
|
||||
|
||||
# Create a Neo4j driver
|
||||
driver = GraphDatabase.driver(NEO4J_URI, auth=(NEO4J_USERNAME, NEO4J_PASSWORD))
|
||||
|
||||
try:
|
||||
# Execute queries in batches
|
||||
with driver.session() as session:
|
||||
# Insert nodes in batches
|
||||
session.execute_write(
|
||||
process_in_batches, create_nodes_query, nodes, BATCH_SIZE_NODES
|
||||
)
|
||||
|
||||
# Insert edges in batches
|
||||
session.execute_write(
|
||||
process_in_batches, create_edges_query, edges, BATCH_SIZE_EDGES
|
||||
)
|
||||
|
||||
# Set displayName and labels
|
||||
session.run(set_displayname_and_labels_query)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error occurred: {e}")
|
||||
|
||||
finally:
|
||||
driver.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,115 @@
|
||||
import os
|
||||
from lightrag import LightRAG
|
||||
from lightrag.llm.openai import gpt_4o_mini_complete
|
||||
#########
|
||||
# Uncomment the below two lines if running in a jupyter notebook to handle the async nature of rag.insert()
|
||||
# import nest_asyncio
|
||||
# nest_asyncio.apply()
|
||||
#########
|
||||
|
||||
WORKING_DIR = "./custom_kg"
|
||||
|
||||
if not os.path.exists(WORKING_DIR):
|
||||
os.mkdir(WORKING_DIR)
|
||||
|
||||
rag = LightRAG(
|
||||
working_dir=WORKING_DIR,
|
||||
llm_model_func=gpt_4o_mini_complete, # Use gpt_4o_mini_complete LLM model
|
||||
# llm_model_func=gpt_4o_complete # Optionally, use a stronger model
|
||||
)
|
||||
|
||||
custom_kg = {
|
||||
"entities": [
|
||||
{
|
||||
"entity_name": "CompanyA",
|
||||
"entity_type": "Organization",
|
||||
"description": "A major technology company",
|
||||
"source_id": "Source1",
|
||||
},
|
||||
{
|
||||
"entity_name": "ProductX",
|
||||
"entity_type": "Product",
|
||||
"description": "A popular product developed by CompanyA",
|
||||
"source_id": "Source1",
|
||||
},
|
||||
{
|
||||
"entity_name": "PersonA",
|
||||
"entity_type": "Person",
|
||||
"description": "A renowned researcher in AI",
|
||||
"source_id": "Source2",
|
||||
},
|
||||
{
|
||||
"entity_name": "UniversityB",
|
||||
"entity_type": "Organization",
|
||||
"description": "A leading university specializing in technology and sciences",
|
||||
"source_id": "Source2",
|
||||
},
|
||||
{
|
||||
"entity_name": "CityC",
|
||||
"entity_type": "Location",
|
||||
"description": "A large metropolitan city known for its culture and economy",
|
||||
"source_id": "Source3",
|
||||
},
|
||||
{
|
||||
"entity_name": "EventY",
|
||||
"entity_type": "Event",
|
||||
"description": "An annual technology conference held in CityC",
|
||||
"source_id": "Source3",
|
||||
},
|
||||
],
|
||||
"relationships": [
|
||||
{
|
||||
"src_id": "CompanyA",
|
||||
"tgt_id": "ProductX",
|
||||
"description": "CompanyA develops ProductX",
|
||||
"keywords": "develop, produce",
|
||||
"weight": 1.0,
|
||||
"source_id": "Source1",
|
||||
},
|
||||
{
|
||||
"src_id": "PersonA",
|
||||
"tgt_id": "UniversityB",
|
||||
"description": "PersonA works at UniversityB",
|
||||
"keywords": "employment, affiliation",
|
||||
"weight": 0.9,
|
||||
"source_id": "Source2",
|
||||
},
|
||||
{
|
||||
"src_id": "CityC",
|
||||
"tgt_id": "EventY",
|
||||
"description": "EventY is hosted in CityC",
|
||||
"keywords": "host, location",
|
||||
"weight": 0.8,
|
||||
"source_id": "Source3",
|
||||
},
|
||||
],
|
||||
"chunks": [
|
||||
{
|
||||
"content": "ProductX, developed by CompanyA, has revolutionized the market with its cutting-edge features.",
|
||||
"source_id": "Source1",
|
||||
"source_chunk_index": 0,
|
||||
},
|
||||
{
|
||||
"content": "One outstanding feature of ProductX is its advanced AI capabilities.",
|
||||
"source_id": "Source1",
|
||||
"chunk_order_index": 1,
|
||||
},
|
||||
{
|
||||
"content": "PersonA is a prominent researcher at UniversityB, focusing on artificial intelligence and machine learning.",
|
||||
"source_id": "Source2",
|
||||
"source_chunk_index": 0,
|
||||
},
|
||||
{
|
||||
"content": "EventY, held in CityC, attracts technology enthusiasts and companies from around the globe.",
|
||||
"source_id": "Source3",
|
||||
"source_chunk_index": 0,
|
||||
},
|
||||
{
|
||||
"content": "None",
|
||||
"source_id": "UNKNOWN",
|
||||
"source_chunk_index": 0,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
rag.insert_custom_kg(custom_kg)
|
||||
@@ -0,0 +1,126 @@
|
||||
import os
|
||||
import asyncio
|
||||
from lightrag import LightRAG, QueryParam
|
||||
from lightrag.utils import EmbeddingFunc
|
||||
import numpy as np
|
||||
from dotenv import load_dotenv
|
||||
import logging
|
||||
from openai import AzureOpenAI
|
||||
from lightrag.kg.shared_storage import initialize_pipeline_status
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
load_dotenv()
|
||||
|
||||
AZURE_OPENAI_API_VERSION = os.getenv("AZURE_OPENAI_API_VERSION")
|
||||
AZURE_OPENAI_DEPLOYMENT = os.getenv("AZURE_OPENAI_DEPLOYMENT")
|
||||
AZURE_OPENAI_API_KEY = os.getenv("AZURE_OPENAI_API_KEY")
|
||||
AZURE_OPENAI_ENDPOINT = os.getenv("AZURE_OPENAI_ENDPOINT")
|
||||
|
||||
AZURE_EMBEDDING_DEPLOYMENT = os.getenv("AZURE_EMBEDDING_DEPLOYMENT")
|
||||
AZURE_EMBEDDING_API_VERSION = os.getenv("AZURE_EMBEDDING_API_VERSION")
|
||||
|
||||
WORKING_DIR = "./dickens"
|
||||
|
||||
if os.path.exists(WORKING_DIR):
|
||||
import shutil
|
||||
|
||||
shutil.rmtree(WORKING_DIR)
|
||||
|
||||
os.mkdir(WORKING_DIR)
|
||||
|
||||
|
||||
async def llm_model_func(
|
||||
prompt, system_prompt=None, history_messages=[], keyword_extraction=False, **kwargs
|
||||
) -> str:
|
||||
client = AzureOpenAI(
|
||||
api_key=AZURE_OPENAI_API_KEY,
|
||||
api_version=AZURE_OPENAI_API_VERSION,
|
||||
azure_endpoint=AZURE_OPENAI_ENDPOINT,
|
||||
)
|
||||
|
||||
messages = []
|
||||
if system_prompt:
|
||||
messages.append({"role": "system", "content": system_prompt})
|
||||
if history_messages:
|
||||
messages.extend(history_messages)
|
||||
messages.append({"role": "user", "content": prompt})
|
||||
|
||||
chat_completion = client.chat.completions.create(
|
||||
model=AZURE_OPENAI_DEPLOYMENT, # model = "deployment_name".
|
||||
messages=messages,
|
||||
temperature=kwargs.get("temperature", 0),
|
||||
top_p=kwargs.get("top_p", 1),
|
||||
n=kwargs.get("n", 1),
|
||||
)
|
||||
return chat_completion.choices[0].message.content
|
||||
|
||||
|
||||
async def embedding_func(texts: list[str]) -> np.ndarray:
|
||||
client = AzureOpenAI(
|
||||
api_key=AZURE_OPENAI_API_KEY,
|
||||
api_version=AZURE_EMBEDDING_API_VERSION,
|
||||
azure_endpoint=AZURE_OPENAI_ENDPOINT,
|
||||
)
|
||||
embedding = client.embeddings.create(model=AZURE_EMBEDDING_DEPLOYMENT, input=texts)
|
||||
|
||||
embeddings = [item.embedding for item in embedding.data]
|
||||
return np.array(embeddings)
|
||||
|
||||
|
||||
async def test_funcs():
|
||||
result = await llm_model_func("How are you?")
|
||||
print("Resposta do llm_model_func: ", result)
|
||||
|
||||
result = await embedding_func(["How are you?"])
|
||||
print("Resultado do embedding_func: ", result.shape)
|
||||
print("Dimensão da embedding: ", result.shape[1])
|
||||
|
||||
|
||||
asyncio.run(test_funcs())
|
||||
|
||||
embedding_dimension = 3072
|
||||
|
||||
|
||||
async def initialize_rag():
|
||||
rag = LightRAG(
|
||||
working_dir=WORKING_DIR,
|
||||
llm_model_func=llm_model_func,
|
||||
embedding_func=EmbeddingFunc(
|
||||
embedding_dim=embedding_dimension,
|
||||
max_token_size=8192,
|
||||
func=embedding_func,
|
||||
),
|
||||
)
|
||||
|
||||
await rag.initialize_storages()
|
||||
await initialize_pipeline_status()
|
||||
|
||||
return rag
|
||||
|
||||
|
||||
def main():
|
||||
rag = asyncio.run(initialize_rag())
|
||||
|
||||
book1 = open("./book_1.txt", encoding="utf-8")
|
||||
book2 = open("./book_2.txt", encoding="utf-8")
|
||||
|
||||
rag.insert([book1.read(), book2.read()])
|
||||
|
||||
query_text = "What are the main themes?"
|
||||
|
||||
print("Result (Naive):")
|
||||
print(rag.query(query_text, param=QueryParam(mode="naive")))
|
||||
|
||||
print("\nResult (Local):")
|
||||
print(rag.query(query_text, param=QueryParam(mode="local")))
|
||||
|
||||
print("\nResult (Global):")
|
||||
print(rag.query(query_text, param=QueryParam(mode="global")))
|
||||
|
||||
print("\nResult (Hybrid):")
|
||||
print(rag.query(query_text, param=QueryParam(mode="hybrid")))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,217 @@
|
||||
import asyncio
|
||||
import os
|
||||
import inspect
|
||||
import logging
|
||||
import logging.config
|
||||
from lightrag import LightRAG, QueryParam
|
||||
from lightrag.llm.ollama import ollama_model_complete, ollama_embed
|
||||
from lightrag.utils import EmbeddingFunc, logger, set_verbose_debug
|
||||
from lightrag.kg.shared_storage import initialize_pipeline_status
|
||||
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv(dotenv_path=".env", override=False)
|
||||
|
||||
WORKING_DIR = "./dickens"
|
||||
|
||||
|
||||
def configure_logging():
|
||||
"""Configure logging for the application"""
|
||||
|
||||
# Reset any existing handlers to ensure clean configuration
|
||||
for logger_name in ["uvicorn", "uvicorn.access", "uvicorn.error", "lightrag"]:
|
||||
logger_instance = logging.getLogger(logger_name)
|
||||
logger_instance.handlers = []
|
||||
logger_instance.filters = []
|
||||
|
||||
# Get log directory path from environment variable or use current directory
|
||||
log_dir = os.getenv("LOG_DIR", os.getcwd())
|
||||
log_file_path = os.path.abspath(os.path.join(log_dir, "lightrag_ollama_demo.log"))
|
||||
|
||||
print(f"\nLightRAG compatible demo log file: {log_file_path}\n")
|
||||
os.makedirs(os.path.dirname(log_file_path), exist_ok=True)
|
||||
|
||||
# Get log file max size and backup count from environment variables
|
||||
log_max_bytes = int(os.getenv("LOG_MAX_BYTES", 10485760)) # Default 10MB
|
||||
log_backup_count = int(os.getenv("LOG_BACKUP_COUNT", 5)) # Default 5 backups
|
||||
|
||||
logging.config.dictConfig(
|
||||
{
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
"formatters": {
|
||||
"default": {
|
||||
"format": "%(levelname)s: %(message)s",
|
||||
},
|
||||
"detailed": {
|
||||
"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
||||
},
|
||||
},
|
||||
"handlers": {
|
||||
"console": {
|
||||
"formatter": "default",
|
||||
"class": "logging.StreamHandler",
|
||||
"stream": "ext://sys.stderr",
|
||||
},
|
||||
"file": {
|
||||
"formatter": "detailed",
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"filename": log_file_path,
|
||||
"maxBytes": log_max_bytes,
|
||||
"backupCount": log_backup_count,
|
||||
"encoding": "utf-8",
|
||||
},
|
||||
},
|
||||
"loggers": {
|
||||
"lightrag": {
|
||||
"handlers": ["console", "file"],
|
||||
"level": "INFO",
|
||||
"propagate": False,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
# Set the logger level to INFO
|
||||
logger.setLevel(logging.INFO)
|
||||
# Enable verbose debug if needed
|
||||
set_verbose_debug(os.getenv("VERBOSE_DEBUG", "false").lower() == "true")
|
||||
|
||||
|
||||
if not os.path.exists(WORKING_DIR):
|
||||
os.mkdir(WORKING_DIR)
|
||||
|
||||
|
||||
async def initialize_rag():
|
||||
rag = LightRAG(
|
||||
working_dir=WORKING_DIR,
|
||||
llm_model_func=ollama_model_complete,
|
||||
llm_model_name=os.getenv("LLM_MODEL", "qwen2.5-coder:7b"),
|
||||
summary_max_tokens=8192,
|
||||
llm_model_kwargs={
|
||||
"host": os.getenv("LLM_BINDING_HOST", "http://localhost:11434"),
|
||||
"options": {"num_ctx": 8192},
|
||||
"timeout": int(os.getenv("TIMEOUT", "300")),
|
||||
},
|
||||
embedding_func=EmbeddingFunc(
|
||||
embedding_dim=int(os.getenv("EMBEDDING_DIM", "1024")),
|
||||
max_token_size=int(os.getenv("MAX_EMBED_TOKENS", "8192")),
|
||||
func=lambda texts: ollama_embed(
|
||||
texts,
|
||||
embed_model=os.getenv("EMBEDDING_MODEL", "bge-m3:latest"),
|
||||
host=os.getenv("EMBEDDING_BINDING_HOST", "http://localhost:11434"),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
await rag.initialize_storages()
|
||||
await initialize_pipeline_status()
|
||||
|
||||
return rag
|
||||
|
||||
|
||||
async def print_stream(stream):
|
||||
async for chunk in stream:
|
||||
print(chunk, end="", flush=True)
|
||||
|
||||
|
||||
async def main():
|
||||
try:
|
||||
# Clear old data files
|
||||
files_to_delete = [
|
||||
"graph_chunk_entity_relation.graphml",
|
||||
"kv_store_doc_status.json",
|
||||
"kv_store_full_docs.json",
|
||||
"kv_store_text_chunks.json",
|
||||
"vdb_chunks.json",
|
||||
"vdb_entities.json",
|
||||
"vdb_relationships.json",
|
||||
]
|
||||
|
||||
for file in files_to_delete:
|
||||
file_path = os.path.join(WORKING_DIR, file)
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
print(f"Deleting old file:: {file_path}")
|
||||
|
||||
# Initialize RAG instance
|
||||
rag = await initialize_rag()
|
||||
|
||||
# Test embedding function
|
||||
test_text = ["This is a test string for embedding."]
|
||||
embedding = await rag.embedding_func(test_text)
|
||||
embedding_dim = embedding.shape[1]
|
||||
print("\n=======================")
|
||||
print("Test embedding function")
|
||||
print("========================")
|
||||
print(f"Test dict: {test_text}")
|
||||
print(f"Detected embedding dimension: {embedding_dim}\n\n")
|
||||
|
||||
with open("./book.txt", "r", encoding="utf-8") as f:
|
||||
await rag.ainsert(f.read())
|
||||
|
||||
# Perform naive search
|
||||
print("\n=====================")
|
||||
print("Query mode: naive")
|
||||
print("=====================")
|
||||
resp = await rag.aquery(
|
||||
"What are the top themes in this story?",
|
||||
param=QueryParam(mode="naive", stream=True),
|
||||
)
|
||||
if inspect.isasyncgen(resp):
|
||||
await print_stream(resp)
|
||||
else:
|
||||
print(resp)
|
||||
|
||||
# Perform local search
|
||||
print("\n=====================")
|
||||
print("Query mode: local")
|
||||
print("=====================")
|
||||
resp = await rag.aquery(
|
||||
"What are the top themes in this story?",
|
||||
param=QueryParam(mode="local", stream=True),
|
||||
)
|
||||
if inspect.isasyncgen(resp):
|
||||
await print_stream(resp)
|
||||
else:
|
||||
print(resp)
|
||||
|
||||
# Perform global search
|
||||
print("\n=====================")
|
||||
print("Query mode: global")
|
||||
print("=====================")
|
||||
resp = await rag.aquery(
|
||||
"What are the top themes in this story?",
|
||||
param=QueryParam(mode="global", stream=True),
|
||||
)
|
||||
if inspect.isasyncgen(resp):
|
||||
await print_stream(resp)
|
||||
else:
|
||||
print(resp)
|
||||
|
||||
# Perform hybrid search
|
||||
print("\n=====================")
|
||||
print("Query mode: hybrid")
|
||||
print("=====================")
|
||||
resp = await rag.aquery(
|
||||
"What are the top themes in this story?",
|
||||
param=QueryParam(mode="hybrid", stream=True),
|
||||
)
|
||||
if inspect.isasyncgen(resp):
|
||||
await print_stream(resp)
|
||||
else:
|
||||
print(resp)
|
||||
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
finally:
|
||||
if rag:
|
||||
await rag.llm_response_cache.index_done_callback()
|
||||
await rag.finalize_storages()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Configure logging before running the main function
|
||||
configure_logging()
|
||||
asyncio.run(main())
|
||||
print("\nDone!")
|
||||
@@ -0,0 +1,227 @@
|
||||
import os
|
||||
import asyncio
|
||||
import inspect
|
||||
import logging
|
||||
import logging.config
|
||||
from lightrag import LightRAG, QueryParam
|
||||
from lightrag.llm.openai import openai_complete_if_cache
|
||||
from lightrag.llm.ollama import ollama_embed
|
||||
from lightrag.utils import EmbeddingFunc, logger, set_verbose_debug
|
||||
from lightrag.kg.shared_storage import initialize_pipeline_status
|
||||
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv(dotenv_path=".env", override=False)
|
||||
|
||||
WORKING_DIR = "./dickens"
|
||||
|
||||
|
||||
def configure_logging():
|
||||
"""Configure logging for the application"""
|
||||
|
||||
# Reset any existing handlers to ensure clean configuration
|
||||
for logger_name in ["uvicorn", "uvicorn.access", "uvicorn.error", "lightrag"]:
|
||||
logger_instance = logging.getLogger(logger_name)
|
||||
logger_instance.handlers = []
|
||||
logger_instance.filters = []
|
||||
|
||||
# Get log directory path from environment variable or use current directory
|
||||
log_dir = os.getenv("LOG_DIR", os.getcwd())
|
||||
log_file_path = os.path.abspath(
|
||||
os.path.join(log_dir, "lightrag_compatible_demo.log")
|
||||
)
|
||||
|
||||
print(f"\nLightRAG compatible demo log file: {log_file_path}\n")
|
||||
os.makedirs(os.path.dirname(log_dir), exist_ok=True)
|
||||
|
||||
# Get log file max size and backup count from environment variables
|
||||
log_max_bytes = int(os.getenv("LOG_MAX_BYTES", 10485760)) # Default 10MB
|
||||
log_backup_count = int(os.getenv("LOG_BACKUP_COUNT", 5)) # Default 5 backups
|
||||
|
||||
logging.config.dictConfig(
|
||||
{
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
"formatters": {
|
||||
"default": {
|
||||
"format": "%(levelname)s: %(message)s",
|
||||
},
|
||||
"detailed": {
|
||||
"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
||||
},
|
||||
},
|
||||
"handlers": {
|
||||
"console": {
|
||||
"formatter": "default",
|
||||
"class": "logging.StreamHandler",
|
||||
"stream": "ext://sys.stderr",
|
||||
},
|
||||
"file": {
|
||||
"formatter": "detailed",
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"filename": log_file_path,
|
||||
"maxBytes": log_max_bytes,
|
||||
"backupCount": log_backup_count,
|
||||
"encoding": "utf-8",
|
||||
},
|
||||
},
|
||||
"loggers": {
|
||||
"lightrag": {
|
||||
"handlers": ["console", "file"],
|
||||
"level": "INFO",
|
||||
"propagate": False,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
# Set the logger level to INFO
|
||||
logger.setLevel(logging.INFO)
|
||||
# Enable verbose debug if needed
|
||||
set_verbose_debug(os.getenv("VERBOSE_DEBUG", "false").lower() == "true")
|
||||
|
||||
|
||||
if not os.path.exists(WORKING_DIR):
|
||||
os.mkdir(WORKING_DIR)
|
||||
|
||||
|
||||
async def llm_model_func(
|
||||
prompt, system_prompt=None, history_messages=[], keyword_extraction=False, **kwargs
|
||||
) -> str:
|
||||
return await openai_complete_if_cache(
|
||||
os.getenv("LLM_MODEL", "deepseek-chat"),
|
||||
prompt,
|
||||
system_prompt=system_prompt,
|
||||
history_messages=history_messages,
|
||||
api_key=os.getenv("LLM_BINDING_API_KEY") or os.getenv("OPENAI_API_KEY"),
|
||||
base_url=os.getenv("LLM_BINDING_HOST", "https://api.deepseek.com"),
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
async def print_stream(stream):
|
||||
async for chunk in stream:
|
||||
if chunk:
|
||||
print(chunk, end="", flush=True)
|
||||
|
||||
|
||||
async def initialize_rag():
|
||||
rag = LightRAG(
|
||||
working_dir=WORKING_DIR,
|
||||
llm_model_func=llm_model_func,
|
||||
embedding_func=EmbeddingFunc(
|
||||
embedding_dim=int(os.getenv("EMBEDDING_DIM", "1024")),
|
||||
max_token_size=int(os.getenv("MAX_EMBED_TOKENS", "8192")),
|
||||
func=lambda texts: ollama_embed(
|
||||
texts,
|
||||
embed_model=os.getenv("EMBEDDING_MODEL", "bge-m3:latest"),
|
||||
host=os.getenv("EMBEDDING_BINDING_HOST", "http://localhost:11434"),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
await rag.initialize_storages()
|
||||
await initialize_pipeline_status()
|
||||
|
||||
return rag
|
||||
|
||||
|
||||
async def main():
|
||||
try:
|
||||
# Clear old data files
|
||||
files_to_delete = [
|
||||
"graph_chunk_entity_relation.graphml",
|
||||
"kv_store_doc_status.json",
|
||||
"kv_store_full_docs.json",
|
||||
"kv_store_text_chunks.json",
|
||||
"vdb_chunks.json",
|
||||
"vdb_entities.json",
|
||||
"vdb_relationships.json",
|
||||
]
|
||||
|
||||
for file in files_to_delete:
|
||||
file_path = os.path.join(WORKING_DIR, file)
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
print(f"Deleting old file:: {file_path}")
|
||||
|
||||
# Initialize RAG instance
|
||||
rag = await initialize_rag()
|
||||
|
||||
# Test embedding function
|
||||
test_text = ["This is a test string for embedding."]
|
||||
embedding = await rag.embedding_func(test_text)
|
||||
embedding_dim = embedding.shape[1]
|
||||
print("\n=======================")
|
||||
print("Test embedding function")
|
||||
print("========================")
|
||||
print(f"Test dict: {test_text}")
|
||||
print(f"Detected embedding dimension: {embedding_dim}\n\n")
|
||||
|
||||
with open("./book.txt", "r", encoding="utf-8") as f:
|
||||
await rag.ainsert(f.read())
|
||||
|
||||
# Perform naive search
|
||||
print("\n=====================")
|
||||
print("Query mode: naive")
|
||||
print("=====================")
|
||||
resp = await rag.aquery(
|
||||
"What are the top themes in this story?",
|
||||
param=QueryParam(mode="naive", stream=True),
|
||||
)
|
||||
if inspect.isasyncgen(resp):
|
||||
await print_stream(resp)
|
||||
else:
|
||||
print(resp)
|
||||
|
||||
# Perform local search
|
||||
print("\n=====================")
|
||||
print("Query mode: local")
|
||||
print("=====================")
|
||||
resp = await rag.aquery(
|
||||
"What are the top themes in this story?",
|
||||
param=QueryParam(mode="local", stream=True),
|
||||
)
|
||||
if inspect.isasyncgen(resp):
|
||||
await print_stream(resp)
|
||||
else:
|
||||
print(resp)
|
||||
|
||||
# Perform global search
|
||||
print("\n=====================")
|
||||
print("Query mode: global")
|
||||
print("=====================")
|
||||
resp = await rag.aquery(
|
||||
"What are the top themes in this story?",
|
||||
param=QueryParam(mode="global", stream=True),
|
||||
)
|
||||
if inspect.isasyncgen(resp):
|
||||
await print_stream(resp)
|
||||
else:
|
||||
print(resp)
|
||||
|
||||
# Perform hybrid search
|
||||
print("\n=====================")
|
||||
print("Query mode: hybrid")
|
||||
print("=====================")
|
||||
resp = await rag.aquery(
|
||||
"What are the top themes in this story?",
|
||||
param=QueryParam(mode="hybrid", stream=True),
|
||||
)
|
||||
if inspect.isasyncgen(resp):
|
||||
await print_stream(resp)
|
||||
else:
|
||||
print(resp)
|
||||
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
finally:
|
||||
if rag:
|
||||
await rag.finalize_storages()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Configure logging before running the main function
|
||||
configure_logging()
|
||||
asyncio.run(main())
|
||||
print("\nDone!")
|
||||
@@ -0,0 +1,189 @@
|
||||
import os
|
||||
import asyncio
|
||||
import logging
|
||||
import logging.config
|
||||
from lightrag import LightRAG, QueryParam
|
||||
from lightrag.llm.openai import gpt_4o_mini_complete, openai_embed
|
||||
from lightrag.kg.shared_storage import initialize_pipeline_status
|
||||
from lightrag.utils import logger, set_verbose_debug
|
||||
|
||||
WORKING_DIR = "./dickens"
|
||||
|
||||
|
||||
def configure_logging():
|
||||
"""Configure logging for the application"""
|
||||
|
||||
# Reset any existing handlers to ensure clean configuration
|
||||
for logger_name in ["uvicorn", "uvicorn.access", "uvicorn.error", "lightrag"]:
|
||||
logger_instance = logging.getLogger(logger_name)
|
||||
logger_instance.handlers = []
|
||||
logger_instance.filters = []
|
||||
|
||||
# Get log directory path from environment variable or use current directory
|
||||
log_dir = os.getenv("LOG_DIR", os.getcwd())
|
||||
log_file_path = os.path.abspath(os.path.join(log_dir, "lightrag_demo.log"))
|
||||
|
||||
print(f"\nLightRAG demo log file: {log_file_path}\n")
|
||||
os.makedirs(os.path.dirname(log_dir), exist_ok=True)
|
||||
|
||||
# Get log file max size and backup count from environment variables
|
||||
log_max_bytes = int(os.getenv("LOG_MAX_BYTES", 10485760)) # Default 10MB
|
||||
log_backup_count = int(os.getenv("LOG_BACKUP_COUNT", 5)) # Default 5 backups
|
||||
|
||||
logging.config.dictConfig(
|
||||
{
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
"formatters": {
|
||||
"default": {
|
||||
"format": "%(levelname)s: %(message)s",
|
||||
},
|
||||
"detailed": {
|
||||
"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
||||
},
|
||||
},
|
||||
"handlers": {
|
||||
"console": {
|
||||
"formatter": "default",
|
||||
"class": "logging.StreamHandler",
|
||||
"stream": "ext://sys.stderr",
|
||||
},
|
||||
"file": {
|
||||
"formatter": "detailed",
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"filename": log_file_path,
|
||||
"maxBytes": log_max_bytes,
|
||||
"backupCount": log_backup_count,
|
||||
"encoding": "utf-8",
|
||||
},
|
||||
},
|
||||
"loggers": {
|
||||
"lightrag": {
|
||||
"handlers": ["console", "file"],
|
||||
"level": "INFO",
|
||||
"propagate": False,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
# Set the logger level to INFO
|
||||
logger.setLevel(logging.INFO)
|
||||
# Enable verbose debug if needed
|
||||
set_verbose_debug(os.getenv("VERBOSE_DEBUG", "false").lower() == "true")
|
||||
|
||||
|
||||
if not os.path.exists(WORKING_DIR):
|
||||
os.mkdir(WORKING_DIR)
|
||||
|
||||
|
||||
async def initialize_rag():
|
||||
rag = LightRAG(
|
||||
working_dir=WORKING_DIR,
|
||||
embedding_func=openai_embed,
|
||||
llm_model_func=gpt_4o_mini_complete,
|
||||
)
|
||||
|
||||
await rag.initialize_storages()
|
||||
await initialize_pipeline_status()
|
||||
|
||||
return rag
|
||||
|
||||
|
||||
async def main():
|
||||
# Check if OPENAI_API_KEY environment variable exists
|
||||
if not os.getenv("OPENAI_API_KEY"):
|
||||
print(
|
||||
"Error: OPENAI_API_KEY environment variable is not set. Please set this variable before running the program."
|
||||
)
|
||||
print("You can set the environment variable by running:")
|
||||
print(" export OPENAI_API_KEY='your-openai-api-key'")
|
||||
return # Exit the async function
|
||||
|
||||
try:
|
||||
# Clear old data files
|
||||
files_to_delete = [
|
||||
"graph_chunk_entity_relation.graphml",
|
||||
"kv_store_doc_status.json",
|
||||
"kv_store_full_docs.json",
|
||||
"kv_store_text_chunks.json",
|
||||
"vdb_chunks.json",
|
||||
"vdb_entities.json",
|
||||
"vdb_relationships.json",
|
||||
]
|
||||
|
||||
for file in files_to_delete:
|
||||
file_path = os.path.join(WORKING_DIR, file)
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
print(f"Deleting old file:: {file_path}")
|
||||
|
||||
# Initialize RAG instance
|
||||
rag = await initialize_rag()
|
||||
|
||||
# Test embedding function
|
||||
test_text = ["This is a test string for embedding."]
|
||||
embedding = await rag.embedding_func(test_text)
|
||||
embedding_dim = embedding.shape[1]
|
||||
print("\n=======================")
|
||||
print("Test embedding function")
|
||||
print("========================")
|
||||
print(f"Test dict: {test_text}")
|
||||
print(f"Detected embedding dimension: {embedding_dim}\n\n")
|
||||
|
||||
with open("./book.txt", "r", encoding="utf-8") as f:
|
||||
await rag.ainsert(f.read())
|
||||
|
||||
# Perform naive search
|
||||
print("\n=====================")
|
||||
print("Query mode: naive")
|
||||
print("=====================")
|
||||
print(
|
||||
await rag.aquery(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="naive")
|
||||
)
|
||||
)
|
||||
|
||||
# Perform local search
|
||||
print("\n=====================")
|
||||
print("Query mode: local")
|
||||
print("=====================")
|
||||
print(
|
||||
await rag.aquery(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="local")
|
||||
)
|
||||
)
|
||||
|
||||
# Perform global search
|
||||
print("\n=====================")
|
||||
print("Query mode: global")
|
||||
print("=====================")
|
||||
print(
|
||||
await rag.aquery(
|
||||
"What are the top themes in this story?",
|
||||
param=QueryParam(mode="global"),
|
||||
)
|
||||
)
|
||||
|
||||
# Perform hybrid search
|
||||
print("\n=====================")
|
||||
print("Query mode: hybrid")
|
||||
print("=====================")
|
||||
print(
|
||||
await rag.aquery(
|
||||
"What are the top themes in this story?",
|
||||
param=QueryParam(mode="hybrid"),
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
finally:
|
||||
if rag:
|
||||
await rag.finalize_storages()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Configure logging before running the main function
|
||||
configure_logging()
|
||||
asyncio.run(main())
|
||||
print("\nDone!")
|
||||
@@ -0,0 +1,107 @@
|
||||
import os
|
||||
import asyncio
|
||||
from lightrag import LightRAG, QueryParam
|
||||
from lightrag.llm.openai import gpt_4o_mini_complete, openai_embed
|
||||
from lightrag.utils import EmbeddingFunc
|
||||
import numpy as np
|
||||
from lightrag.kg.shared_storage import initialize_pipeline_status
|
||||
|
||||
#########
|
||||
# Uncomment the below two lines if running in a jupyter notebook to handle the async nature of rag.insert()
|
||||
# import nest_asyncio
|
||||
# nest_asyncio.apply()
|
||||
#########
|
||||
WORKING_DIR = "./mongodb_test_dir"
|
||||
if not os.path.exists(WORKING_DIR):
|
||||
os.mkdir(WORKING_DIR)
|
||||
|
||||
|
||||
os.environ["OPENAI_API_KEY"] = "sk-"
|
||||
os.environ["MONGO_URI"] = "mongodb://0.0.0.0:27017/?directConnection=true"
|
||||
os.environ["MONGO_DATABASE"] = "LightRAG"
|
||||
os.environ["MONGO_KG_COLLECTION"] = "MDB_KG"
|
||||
|
||||
# Embedding Configuration and Functions
|
||||
EMBEDDING_MODEL = os.environ.get("EMBEDDING_MODEL", "text-embedding-3-large")
|
||||
EMBEDDING_MAX_TOKEN_SIZE = int(os.environ.get("EMBEDDING_MAX_TOKEN_SIZE", 8192))
|
||||
|
||||
|
||||
async def embedding_func(texts: list[str]) -> np.ndarray:
|
||||
return await openai_embed(
|
||||
texts,
|
||||
model=EMBEDDING_MODEL,
|
||||
)
|
||||
|
||||
|
||||
async def get_embedding_dimension():
|
||||
test_text = ["This is a test sentence."]
|
||||
embedding = await embedding_func(test_text)
|
||||
return embedding.shape[1]
|
||||
|
||||
|
||||
async def create_embedding_function_instance():
|
||||
# Get embedding dimension
|
||||
embedding_dimension = await get_embedding_dimension()
|
||||
# Create embedding function instance
|
||||
return EmbeddingFunc(
|
||||
embedding_dim=embedding_dimension,
|
||||
max_token_size=EMBEDDING_MAX_TOKEN_SIZE,
|
||||
func=embedding_func,
|
||||
)
|
||||
|
||||
|
||||
async def initialize_rag():
|
||||
embedding_func_instance = await create_embedding_function_instance()
|
||||
|
||||
rag = LightRAG(
|
||||
working_dir=WORKING_DIR,
|
||||
llm_model_func=gpt_4o_mini_complete,
|
||||
embedding_func=embedding_func_instance,
|
||||
graph_storage="MongoGraphStorage",
|
||||
log_level="DEBUG",
|
||||
)
|
||||
|
||||
await rag.initialize_storages()
|
||||
await initialize_pipeline_status()
|
||||
|
||||
return rag
|
||||
|
||||
|
||||
def main():
|
||||
# Initialize RAG instance
|
||||
rag = asyncio.run(initialize_rag())
|
||||
|
||||
with open("./book.txt", "r", encoding="utf-8") as f:
|
||||
rag.insert(f.read())
|
||||
|
||||
# Perform naive search
|
||||
print(
|
||||
rag.query(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="naive")
|
||||
)
|
||||
)
|
||||
|
||||
# Perform local search
|
||||
print(
|
||||
rag.query(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="local")
|
||||
)
|
||||
)
|
||||
|
||||
# Perform global search
|
||||
print(
|
||||
rag.query(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="global")
|
||||
)
|
||||
)
|
||||
|
||||
# Perform hybrid search
|
||||
print(
|
||||
rag.query(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="hybrid")
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,229 @@
|
||||
"""
|
||||
Example of directly using modal processors
|
||||
|
||||
This example demonstrates how to use LightRAG's modal processors directly without going through MinerU.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import argparse
|
||||
from lightrag.llm.openai import openai_complete_if_cache, openai_embed
|
||||
from lightrag.kg.shared_storage import initialize_pipeline_status
|
||||
from lightrag import LightRAG
|
||||
from lightrag.utils import EmbeddingFunc
|
||||
from raganything.modalprocessors import (
|
||||
ImageModalProcessor,
|
||||
TableModalProcessor,
|
||||
EquationModalProcessor,
|
||||
)
|
||||
|
||||
WORKING_DIR = "./rag_storage"
|
||||
|
||||
|
||||
def get_llm_model_func(api_key: str, base_url: str = None):
|
||||
return (
|
||||
lambda prompt,
|
||||
system_prompt=None,
|
||||
history_messages=[],
|
||||
**kwargs: openai_complete_if_cache(
|
||||
"gpt-4o-mini",
|
||||
prompt,
|
||||
system_prompt=system_prompt,
|
||||
history_messages=history_messages,
|
||||
api_key=api_key,
|
||||
base_url=base_url,
|
||||
**kwargs,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def get_vision_model_func(api_key: str, base_url: str = None):
|
||||
return (
|
||||
lambda prompt,
|
||||
system_prompt=None,
|
||||
history_messages=[],
|
||||
image_data=None,
|
||||
**kwargs: openai_complete_if_cache(
|
||||
"gpt-4o",
|
||||
"",
|
||||
system_prompt=None,
|
||||
history_messages=[],
|
||||
messages=[
|
||||
{"role": "system", "content": system_prompt} if system_prompt else None,
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "text", "text": prompt},
|
||||
{
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": f"data:image/jpeg;base64,{image_data}"
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
if image_data
|
||||
else {"role": "user", "content": prompt},
|
||||
],
|
||||
api_key=api_key,
|
||||
base_url=base_url,
|
||||
**kwargs,
|
||||
)
|
||||
if image_data
|
||||
else openai_complete_if_cache(
|
||||
"gpt-4o-mini",
|
||||
prompt,
|
||||
system_prompt=system_prompt,
|
||||
history_messages=history_messages,
|
||||
api_key=api_key,
|
||||
base_url=base_url,
|
||||
**kwargs,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
async def process_image_example(lightrag: LightRAG, vision_model_func):
|
||||
"""Example of processing an image"""
|
||||
# Create image processor
|
||||
image_processor = ImageModalProcessor(
|
||||
lightrag=lightrag, modal_caption_func=vision_model_func
|
||||
)
|
||||
|
||||
# Prepare image content
|
||||
image_content = {
|
||||
"img_path": "image.jpg",
|
||||
"img_caption": ["Example image caption"],
|
||||
"img_footnote": ["Example image footnote"],
|
||||
}
|
||||
|
||||
# Process image
|
||||
description, entity_info = await image_processor.process_multimodal_content(
|
||||
modal_content=image_content,
|
||||
content_type="image",
|
||||
file_path="image_example.jpg",
|
||||
entity_name="Example Image",
|
||||
)
|
||||
|
||||
print("Image Processing Results:")
|
||||
print(f"Description: {description}")
|
||||
print(f"Entity Info: {entity_info}")
|
||||
|
||||
|
||||
async def process_table_example(lightrag: LightRAG, llm_model_func):
|
||||
"""Example of processing a table"""
|
||||
# Create table processor
|
||||
table_processor = TableModalProcessor(
|
||||
lightrag=lightrag, modal_caption_func=llm_model_func
|
||||
)
|
||||
|
||||
# Prepare table content
|
||||
table_content = {
|
||||
"table_body": """
|
||||
| Name | Age | Occupation |
|
||||
|------|-----|------------|
|
||||
| John | 25 | Engineer |
|
||||
| Mary | 30 | Designer |
|
||||
""",
|
||||
"table_caption": ["Employee Information Table"],
|
||||
"table_footnote": ["Data updated as of 2024"],
|
||||
}
|
||||
|
||||
# Process table
|
||||
description, entity_info = await table_processor.process_multimodal_content(
|
||||
modal_content=table_content,
|
||||
content_type="table",
|
||||
file_path="table_example.md",
|
||||
entity_name="Employee Table",
|
||||
)
|
||||
|
||||
print("\nTable Processing Results:")
|
||||
print(f"Description: {description}")
|
||||
print(f"Entity Info: {entity_info}")
|
||||
|
||||
|
||||
async def process_equation_example(lightrag: LightRAG, llm_model_func):
|
||||
"""Example of processing a mathematical equation"""
|
||||
# Create equation processor
|
||||
equation_processor = EquationModalProcessor(
|
||||
lightrag=lightrag, modal_caption_func=llm_model_func
|
||||
)
|
||||
|
||||
# Prepare equation content
|
||||
equation_content = {"text": "E = mc^2", "text_format": "LaTeX"}
|
||||
|
||||
# Process equation
|
||||
description, entity_info = await equation_processor.process_multimodal_content(
|
||||
modal_content=equation_content,
|
||||
content_type="equation",
|
||||
file_path="equation_example.txt",
|
||||
entity_name="Mass-Energy Equivalence",
|
||||
)
|
||||
|
||||
print("\nEquation Processing Results:")
|
||||
print(f"Description: {description}")
|
||||
print(f"Entity Info: {entity_info}")
|
||||
|
||||
|
||||
async def initialize_rag(api_key: str, base_url: str = None):
|
||||
rag = LightRAG(
|
||||
working_dir=WORKING_DIR,
|
||||
embedding_func=EmbeddingFunc(
|
||||
embedding_dim=3072,
|
||||
max_token_size=8192,
|
||||
func=lambda texts: openai_embed(
|
||||
texts,
|
||||
model="text-embedding-3-large",
|
||||
api_key=api_key,
|
||||
base_url=base_url,
|
||||
),
|
||||
),
|
||||
llm_model_func=lambda prompt,
|
||||
system_prompt=None,
|
||||
history_messages=[],
|
||||
**kwargs: openai_complete_if_cache(
|
||||
"gpt-4o-mini",
|
||||
prompt,
|
||||
system_prompt=system_prompt,
|
||||
history_messages=history_messages,
|
||||
api_key=api_key,
|
||||
base_url=base_url,
|
||||
**kwargs,
|
||||
),
|
||||
)
|
||||
|
||||
await rag.initialize_storages()
|
||||
await initialize_pipeline_status()
|
||||
|
||||
return rag
|
||||
|
||||
|
||||
def main():
|
||||
"""Main function to run the example"""
|
||||
parser = argparse.ArgumentParser(description="Modal Processors Example")
|
||||
parser.add_argument("--api-key", required=True, help="OpenAI API key")
|
||||
parser.add_argument("--base-url", help="Optional base URL for API")
|
||||
parser.add_argument(
|
||||
"--working-dir", "-w", default=WORKING_DIR, help="Working directory path"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Run examples
|
||||
asyncio.run(main_async(args.api_key, args.base_url))
|
||||
|
||||
|
||||
async def main_async(api_key: str, base_url: str = None):
|
||||
# Initialize LightRAG
|
||||
lightrag = await initialize_rag(api_key, base_url)
|
||||
|
||||
# Get model functions
|
||||
llm_model_func = get_llm_model_func(api_key, base_url)
|
||||
vision_model_func = get_vision_model_func(api_key, base_url)
|
||||
|
||||
# Run examples
|
||||
await process_image_example(lightrag, vision_model_func)
|
||||
await process_table_example(lightrag, llm_model_func)
|
||||
await process_equation_example(lightrag, llm_model_func)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,286 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Example script demonstrating the integration of MinerU parser with RAGAnything
|
||||
|
||||
This example shows how to:
|
||||
1. Process parsed documents with RAGAnything
|
||||
2. Perform multimodal queries on the processed documents
|
||||
3. Handle different types of content (text, images, tables)
|
||||
"""
|
||||
|
||||
import os
|
||||
import argparse
|
||||
import asyncio
|
||||
import logging
|
||||
import logging.config
|
||||
from pathlib import Path
|
||||
|
||||
# Add project root directory to Python path
|
||||
import sys
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent))
|
||||
|
||||
from lightrag.llm.openai import openai_complete_if_cache, openai_embed
|
||||
from lightrag.utils import EmbeddingFunc, logger, set_verbose_debug
|
||||
from raganything import RAGAnything, RAGAnythingConfig
|
||||
|
||||
|
||||
def configure_logging():
|
||||
"""Configure logging for the application"""
|
||||
# Get log directory path from environment variable or use current directory
|
||||
log_dir = os.getenv("LOG_DIR", os.getcwd())
|
||||
log_file_path = os.path.abspath(os.path.join(log_dir, "raganything_example.log"))
|
||||
|
||||
print(f"\nRAGAnything example log file: {log_file_path}\n")
|
||||
os.makedirs(os.path.dirname(log_dir), exist_ok=True)
|
||||
|
||||
# Get log file max size and backup count from environment variables
|
||||
log_max_bytes = int(os.getenv("LOG_MAX_BYTES", 10485760)) # Default 10MB
|
||||
log_backup_count = int(os.getenv("LOG_BACKUP_COUNT", 5)) # Default 5 backups
|
||||
|
||||
logging.config.dictConfig(
|
||||
{
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
"formatters": {
|
||||
"default": {
|
||||
"format": "%(levelname)s: %(message)s",
|
||||
},
|
||||
"detailed": {
|
||||
"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
||||
},
|
||||
},
|
||||
"handlers": {
|
||||
"console": {
|
||||
"formatter": "default",
|
||||
"class": "logging.StreamHandler",
|
||||
"stream": "ext://sys.stderr",
|
||||
},
|
||||
"file": {
|
||||
"formatter": "detailed",
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"filename": log_file_path,
|
||||
"maxBytes": log_max_bytes,
|
||||
"backupCount": log_backup_count,
|
||||
"encoding": "utf-8",
|
||||
},
|
||||
},
|
||||
"loggers": {
|
||||
"lightrag": {
|
||||
"handlers": ["console", "file"],
|
||||
"level": "INFO",
|
||||
"propagate": False,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
# Set the logger level to INFO
|
||||
logger.setLevel(logging.INFO)
|
||||
# Enable verbose debug if needed
|
||||
set_verbose_debug(os.getenv("VERBOSE", "false").lower() == "true")
|
||||
|
||||
|
||||
async def process_with_rag(
|
||||
file_path: str,
|
||||
output_dir: str,
|
||||
api_key: str,
|
||||
base_url: str = None,
|
||||
working_dir: str = None,
|
||||
):
|
||||
"""
|
||||
Process document with RAGAnything
|
||||
|
||||
Args:
|
||||
file_path: Path to the document
|
||||
output_dir: Output directory for RAG results
|
||||
api_key: OpenAI API key
|
||||
base_url: Optional base URL for API
|
||||
working_dir: Working directory for RAG storage
|
||||
"""
|
||||
try:
|
||||
# Create RAGAnything configuration
|
||||
config = RAGAnythingConfig(
|
||||
working_dir=working_dir or "./rag_storage",
|
||||
mineru_parse_method="auto",
|
||||
enable_image_processing=True,
|
||||
enable_table_processing=True,
|
||||
enable_equation_processing=True,
|
||||
)
|
||||
|
||||
# Define LLM model function
|
||||
def llm_model_func(prompt, system_prompt=None, history_messages=[], **kwargs):
|
||||
return openai_complete_if_cache(
|
||||
"gpt-4o-mini",
|
||||
prompt,
|
||||
system_prompt=system_prompt,
|
||||
history_messages=history_messages,
|
||||
api_key=api_key,
|
||||
base_url=base_url,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
# Define vision model function for image processing
|
||||
def vision_model_func(
|
||||
prompt, system_prompt=None, history_messages=[], image_data=None, **kwargs
|
||||
):
|
||||
if image_data:
|
||||
return openai_complete_if_cache(
|
||||
"gpt-4o",
|
||||
"",
|
||||
system_prompt=None,
|
||||
history_messages=[],
|
||||
messages=[
|
||||
{"role": "system", "content": system_prompt}
|
||||
if system_prompt
|
||||
else None,
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "text", "text": prompt},
|
||||
{
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": f"data:image/jpeg;base64,{image_data}"
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
if image_data
|
||||
else {"role": "user", "content": prompt},
|
||||
],
|
||||
api_key=api_key,
|
||||
base_url=base_url,
|
||||
**kwargs,
|
||||
)
|
||||
else:
|
||||
return llm_model_func(prompt, system_prompt, history_messages, **kwargs)
|
||||
|
||||
# Define embedding function
|
||||
embedding_func = EmbeddingFunc(
|
||||
embedding_dim=3072,
|
||||
max_token_size=8192,
|
||||
func=lambda texts: openai_embed(
|
||||
texts,
|
||||
model="text-embedding-3-large",
|
||||
api_key=api_key,
|
||||
base_url=base_url,
|
||||
),
|
||||
)
|
||||
|
||||
# Initialize RAGAnything with new dataclass structure
|
||||
rag = RAGAnything(
|
||||
config=config,
|
||||
llm_model_func=llm_model_func,
|
||||
vision_model_func=vision_model_func,
|
||||
embedding_func=embedding_func,
|
||||
)
|
||||
|
||||
# Process document
|
||||
await rag.process_document_complete(
|
||||
file_path=file_path, output_dir=output_dir, parse_method="auto"
|
||||
)
|
||||
|
||||
# Example queries - demonstrating different query approaches
|
||||
logger.info("\nQuerying processed document:")
|
||||
|
||||
# 1. Pure text queries using aquery()
|
||||
text_queries = [
|
||||
"What is the main content of the document?",
|
||||
"What are the key topics discussed?",
|
||||
]
|
||||
|
||||
for query in text_queries:
|
||||
logger.info(f"\n[Text Query]: {query}")
|
||||
result = await rag.aquery(query, mode="hybrid")
|
||||
logger.info(f"Answer: {result}")
|
||||
|
||||
# 2. Multimodal query with specific multimodal content using aquery_with_multimodal()
|
||||
logger.info(
|
||||
"\n[Multimodal Query]: Analyzing performance data in context of document"
|
||||
)
|
||||
multimodal_result = await rag.aquery_with_multimodal(
|
||||
"Compare this performance data with any similar results mentioned in the document",
|
||||
multimodal_content=[
|
||||
{
|
||||
"type": "table",
|
||||
"table_data": """Method,Accuracy,Processing_Time
|
||||
RAGAnything,95.2%,120ms
|
||||
Traditional_RAG,87.3%,180ms
|
||||
Baseline,82.1%,200ms""",
|
||||
"table_caption": "Performance comparison results",
|
||||
}
|
||||
],
|
||||
mode="hybrid",
|
||||
)
|
||||
logger.info(f"Answer: {multimodal_result}")
|
||||
|
||||
# 3. Another multimodal query with equation content
|
||||
logger.info("\n[Multimodal Query]: Mathematical formula analysis")
|
||||
equation_result = await rag.aquery_with_multimodal(
|
||||
"Explain this formula and relate it to any mathematical concepts in the document",
|
||||
multimodal_content=[
|
||||
{
|
||||
"type": "equation",
|
||||
"latex": "F1 = 2 \\cdot \\frac{precision \\cdot recall}{precision + recall}",
|
||||
"equation_caption": "F1-score calculation formula",
|
||||
}
|
||||
],
|
||||
mode="hybrid",
|
||||
)
|
||||
logger.info(f"Answer: {equation_result}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing with RAG: {str(e)}")
|
||||
import traceback
|
||||
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
|
||||
def main():
|
||||
"""Main function to run the example"""
|
||||
parser = argparse.ArgumentParser(description="MinerU RAG Example")
|
||||
parser.add_argument("file_path", help="Path to the document to process")
|
||||
parser.add_argument(
|
||||
"--working_dir", "-w", default="./rag_storage", help="Working directory path"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output", "-o", default="./output", help="Output directory path"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--api-key",
|
||||
default=os.getenv("OPENAI_API_KEY"),
|
||||
help="OpenAI API key (defaults to OPENAI_API_KEY env var)",
|
||||
)
|
||||
parser.add_argument("--base-url", help="Optional base URL for API")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Check if API key is provided
|
||||
if not args.api_key:
|
||||
logger.error("Error: OpenAI API key is required")
|
||||
logger.error("Set OPENAI_API_KEY environment variable or use --api-key option")
|
||||
return
|
||||
|
||||
# Create output directory if specified
|
||||
if args.output:
|
||||
os.makedirs(args.output, exist_ok=True)
|
||||
|
||||
# Process with RAG
|
||||
asyncio.run(
|
||||
process_with_rag(
|
||||
args.file_path, args.output, args.api_key, args.base_url, args.working_dir
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Configure logging first
|
||||
configure_logging()
|
||||
|
||||
print("RAGAnything Example")
|
||||
print("=" * 30)
|
||||
print("Processing document with multimodal RAG pipeline")
|
||||
print("=" * 30)
|
||||
|
||||
main()
|
||||
@@ -0,0 +1,232 @@
|
||||
"""
|
||||
LightRAG Rerank Integration Example
|
||||
|
||||
This example demonstrates how to use rerank functionality with LightRAG
|
||||
to improve retrieval quality across different query modes.
|
||||
|
||||
Configuration Required:
|
||||
1. Set your OpenAI LLM API key and base URL with env vars
|
||||
LLM_MODEL
|
||||
LLM_BINDING_HOST
|
||||
LLM_BINDING_API_KEY
|
||||
2. Set your OpenAI embedding API key and base URL with env vars:
|
||||
EMBEDDING_MODEL
|
||||
EMBEDDING_DIM
|
||||
EMBEDDING_BINDING_HOST
|
||||
EMBEDDING_BINDING_API_KEY
|
||||
3. Set your vLLM deployed AI rerank model setting with env vars:
|
||||
RERANK_MODEL
|
||||
RERANK_BINDING_HOST
|
||||
RERANK_BINDING_API_KEY
|
||||
|
||||
Note: Rerank is controlled per query via the 'enable_rerank' parameter (default: True)
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
import numpy as np
|
||||
|
||||
from lightrag import LightRAG, QueryParam
|
||||
from lightrag.llm.openai import openai_complete_if_cache, openai_embed
|
||||
from lightrag.utils import EmbeddingFunc, setup_logger
|
||||
from lightrag.kg.shared_storage import initialize_pipeline_status
|
||||
|
||||
from functools import partial
|
||||
from lightrag.rerank import cohere_rerank
|
||||
|
||||
# Set up your working directory
|
||||
WORKING_DIR = "./test_rerank"
|
||||
setup_logger("test_rerank")
|
||||
|
||||
if not os.path.exists(WORKING_DIR):
|
||||
os.mkdir(WORKING_DIR)
|
||||
|
||||
|
||||
async def llm_model_func(
|
||||
prompt, system_prompt=None, history_messages=[], **kwargs
|
||||
) -> str:
|
||||
return await openai_complete_if_cache(
|
||||
os.getenv("LLM_MODEL"),
|
||||
prompt,
|
||||
system_prompt=system_prompt,
|
||||
history_messages=history_messages,
|
||||
api_key=os.getenv("LLM_BINDING_API_KEY"),
|
||||
base_url=os.getenv("LLM_BINDING_HOST"),
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
async def embedding_func(texts: list[str]) -> np.ndarray:
|
||||
return await openai_embed(
|
||||
texts,
|
||||
model=os.getenv("EMBEDDING_MODEL"),
|
||||
api_key=os.getenv("EMBEDDING_BINDING_API_KEY"),
|
||||
base_url=os.getenv("EMBEDDING_BINDING_HOST"),
|
||||
)
|
||||
|
||||
|
||||
rerank_model_func = partial(
|
||||
cohere_rerank,
|
||||
model=os.getenv("RERANK_MODEL"),
|
||||
api_key=os.getenv("RERANK_BINDING_API_KEY"),
|
||||
base_url=os.getenv("RERANK_BINDING_HOST"),
|
||||
)
|
||||
|
||||
|
||||
async def create_rag_with_rerank():
|
||||
"""Create LightRAG instance with rerank configuration"""
|
||||
|
||||
# Get embedding dimension
|
||||
test_embedding = await embedding_func(["test"])
|
||||
embedding_dim = test_embedding.shape[1]
|
||||
print(f"Detected embedding dimension: {embedding_dim}")
|
||||
|
||||
# Method 1: Using custom rerank function
|
||||
rag = LightRAG(
|
||||
working_dir=WORKING_DIR,
|
||||
llm_model_func=llm_model_func,
|
||||
embedding_func=EmbeddingFunc(
|
||||
embedding_dim=embedding_dim,
|
||||
max_token_size=8192,
|
||||
func=embedding_func,
|
||||
),
|
||||
# Rerank Configuration - provide the rerank function
|
||||
rerank_model_func=rerank_model_func,
|
||||
)
|
||||
|
||||
await rag.initialize_storages()
|
||||
await initialize_pipeline_status()
|
||||
|
||||
return rag
|
||||
|
||||
|
||||
async def test_rerank_with_different_settings():
|
||||
"""
|
||||
Test rerank functionality with different enable_rerank settings
|
||||
"""
|
||||
print("\n\n🚀 Setting up LightRAG with Rerank functionality...")
|
||||
|
||||
rag = await create_rag_with_rerank()
|
||||
|
||||
# Insert sample documents
|
||||
sample_docs = [
|
||||
"Reranking improves retrieval quality by re-ordering documents based on relevance.",
|
||||
"LightRAG is a powerful retrieval-augmented generation system with multiple query modes.",
|
||||
"Vector databases enable efficient similarity search in high-dimensional embedding spaces.",
|
||||
"Natural language processing has evolved with large language models and transformers.",
|
||||
"Machine learning algorithms can learn patterns from data without explicit programming.",
|
||||
]
|
||||
|
||||
print("📄 Inserting sample documents...")
|
||||
await rag.ainsert(sample_docs)
|
||||
|
||||
query = "How does reranking improve retrieval quality?"
|
||||
print(f"\n🔍 Testing query: '{query}'")
|
||||
print("=" * 80)
|
||||
|
||||
# Test with rerank enabled (default)
|
||||
print("\n📊 Testing with enable_rerank=True (default):")
|
||||
result_with_rerank = await rag.aquery(
|
||||
query,
|
||||
param=QueryParam(
|
||||
mode="naive",
|
||||
top_k=10,
|
||||
chunk_top_k=5,
|
||||
enable_rerank=True, # Explicitly enable rerank
|
||||
),
|
||||
)
|
||||
print(f" Result length: {len(result_with_rerank)} characters")
|
||||
print(f" Preview: {result_with_rerank[:100]}...")
|
||||
|
||||
# Test with rerank disabled
|
||||
print("\n📊 Testing with enable_rerank=False:")
|
||||
result_without_rerank = await rag.aquery(
|
||||
query,
|
||||
param=QueryParam(
|
||||
mode="naive",
|
||||
top_k=10,
|
||||
chunk_top_k=5,
|
||||
enable_rerank=False, # Disable rerank
|
||||
),
|
||||
)
|
||||
print(f" Result length: {len(result_without_rerank)} characters")
|
||||
print(f" Preview: {result_without_rerank[:100]}...")
|
||||
|
||||
# Test with default settings (enable_rerank defaults to True)
|
||||
print("\n📊 Testing with default settings (enable_rerank defaults to True):")
|
||||
result_default = await rag.aquery(
|
||||
query, param=QueryParam(mode="naive", top_k=10, chunk_top_k=5)
|
||||
)
|
||||
print(f" Result length: {len(result_default)} characters")
|
||||
print(f" Preview: {result_default[:100]}...")
|
||||
|
||||
|
||||
async def test_direct_rerank():
|
||||
"""Test rerank function directly"""
|
||||
print("\n🔧 Direct Rerank API Test")
|
||||
print("=" * 40)
|
||||
|
||||
documents = [
|
||||
"Vector search finds semantically similar documents",
|
||||
"LightRAG supports advanced reranking capabilities",
|
||||
"Reranking significantly improves retrieval quality",
|
||||
"Natural language processing with modern transformers",
|
||||
"The quick brown fox jumps over the lazy dog",
|
||||
]
|
||||
|
||||
query = "rerank improve quality"
|
||||
print(f"Query: '{query}'")
|
||||
print(f"Documents: {len(documents)}")
|
||||
|
||||
try:
|
||||
reranked_results = await rerank_model_func(
|
||||
query=query,
|
||||
documents=documents,
|
||||
top_n=4,
|
||||
)
|
||||
|
||||
print("\n✅ Rerank Results:")
|
||||
i = 0
|
||||
for result in reranked_results:
|
||||
index = result["index"]
|
||||
score = result["relevance_score"]
|
||||
content = documents[index]
|
||||
print(f" {index}. Score: {score:.4f} | {content}...")
|
||||
i += 1
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Rerank failed: {e}")
|
||||
|
||||
|
||||
async def main():
|
||||
"""Main example function"""
|
||||
print("🎯 LightRAG Rerank Integration Example")
|
||||
print("=" * 60)
|
||||
|
||||
try:
|
||||
# Test direct rerank
|
||||
await test_direct_rerank()
|
||||
|
||||
# Test rerank with different enable_rerank settings
|
||||
await test_rerank_with_different_settings()
|
||||
|
||||
print("\n✅ Example completed successfully!")
|
||||
print("\n💡 Key Points:")
|
||||
print(" ✓ Rerank is now controlled per query via 'enable_rerank' parameter")
|
||||
print(" ✓ Default value for enable_rerank is True")
|
||||
print(" ✓ Rerank function is configured at LightRAG initialization")
|
||||
print(" ✓ Per-query enable_rerank setting overrides default behavior")
|
||||
print(
|
||||
" ✓ If enable_rerank=True but no rerank model is configured, a warning is issued"
|
||||
)
|
||||
print(" ✓ Monitor API usage and costs when using rerank services")
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ Example failed: {e}")
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,114 @@
|
||||
"""
|
||||
Sometimes you need to switch a storage solution, but you want to save LLM token and time.
|
||||
This handy script helps you to copy the LLM caches from one storage solution to another.
|
||||
(Not all the storage impl are supported)
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
from lightrag.kg.postgres_impl import PostgreSQLDB, PGKVStorage
|
||||
from lightrag.kg.json_kv_impl import JsonKVStorage
|
||||
from lightrag.namespace import NameSpace
|
||||
|
||||
load_dotenv()
|
||||
ROOT_DIR = os.environ.get("ROOT_DIR")
|
||||
WORKING_DIR = f"{ROOT_DIR}/dickens"
|
||||
|
||||
logging.basicConfig(format="%(levelname)s:%(message)s", level=logging.INFO)
|
||||
|
||||
if not os.path.exists(WORKING_DIR):
|
||||
os.mkdir(WORKING_DIR)
|
||||
|
||||
# AGE
|
||||
os.environ["AGE_GRAPH_NAME"] = "chinese"
|
||||
|
||||
postgres_db = PostgreSQLDB(
|
||||
config={
|
||||
"host": "localhost",
|
||||
"port": 15432,
|
||||
"user": "rag",
|
||||
"password": "rag",
|
||||
"database": "r2",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def copy_from_postgres_to_json():
|
||||
await postgres_db.initdb()
|
||||
|
||||
from_llm_response_cache = PGKVStorage(
|
||||
namespace=NameSpace.KV_STORE_LLM_RESPONSE_CACHE,
|
||||
global_config={"embedding_batch_num": 6},
|
||||
embedding_func=None,
|
||||
db=postgres_db,
|
||||
)
|
||||
|
||||
to_llm_response_cache = JsonKVStorage(
|
||||
namespace=NameSpace.KV_STORE_LLM_RESPONSE_CACHE,
|
||||
global_config={"working_dir": WORKING_DIR},
|
||||
embedding_func=None,
|
||||
)
|
||||
|
||||
# Get all cache data using the new flattened structure
|
||||
all_data = await from_llm_response_cache.get_all()
|
||||
|
||||
# Convert flattened data to hierarchical structure for JsonKVStorage
|
||||
kv = {}
|
||||
for flattened_key, cache_entry in all_data.items():
|
||||
# Parse flattened key: {mode}:{cache_type}:{hash}
|
||||
parts = flattened_key.split(":", 2)
|
||||
if len(parts) == 3:
|
||||
mode, cache_type, hash_value = parts
|
||||
if mode not in kv:
|
||||
kv[mode] = {}
|
||||
kv[mode][hash_value] = cache_entry
|
||||
print(f"Copying {flattened_key} -> {mode}[{hash_value}]")
|
||||
else:
|
||||
print(f"Skipping invalid key format: {flattened_key}")
|
||||
|
||||
await to_llm_response_cache.upsert(kv)
|
||||
await to_llm_response_cache.index_done_callback()
|
||||
print("Mission accomplished!")
|
||||
|
||||
|
||||
async def copy_from_json_to_postgres():
|
||||
await postgres_db.initdb()
|
||||
|
||||
from_llm_response_cache = JsonKVStorage(
|
||||
namespace=NameSpace.KV_STORE_LLM_RESPONSE_CACHE,
|
||||
global_config={"working_dir": WORKING_DIR},
|
||||
embedding_func=None,
|
||||
)
|
||||
|
||||
to_llm_response_cache = PGKVStorage(
|
||||
namespace=NameSpace.KV_STORE_LLM_RESPONSE_CACHE,
|
||||
global_config={"embedding_batch_num": 6},
|
||||
embedding_func=None,
|
||||
db=postgres_db,
|
||||
)
|
||||
|
||||
# Get all cache data from JsonKVStorage (hierarchical structure)
|
||||
all_data = await from_llm_response_cache.get_all()
|
||||
|
||||
# Convert hierarchical data to flattened structure for PGKVStorage
|
||||
flattened_data = {}
|
||||
for mode, mode_data in all_data.items():
|
||||
print(f"Processing mode: {mode}")
|
||||
for hash_value, cache_entry in mode_data.items():
|
||||
# Determine cache_type from cache entry or use default
|
||||
cache_type = cache_entry.get("cache_type", "extract")
|
||||
# Create flattened key: {mode}:{cache_type}:{hash}
|
||||
flattened_key = f"{mode}:{cache_type}:{hash_value}"
|
||||
flattened_data[flattened_key] = cache_entry
|
||||
print(f"\tConverting {mode}[{hash_value}] -> {flattened_key}")
|
||||
|
||||
# Upsert the flattened data
|
||||
await to_llm_response_cache.upsert(flattened_data)
|
||||
print("Mission accomplished!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(copy_from_json_to_postgres())
|
||||
@@ -0,0 +1,59 @@
|
||||
"""
|
||||
LightRAG meets Amazon Bedrock ⛰️
|
||||
"""
|
||||
|
||||
import os
|
||||
import logging
|
||||
|
||||
from lightrag import LightRAG, QueryParam
|
||||
from lightrag.llm.bedrock import bedrock_complete, bedrock_embed
|
||||
from lightrag.utils import EmbeddingFunc
|
||||
from lightrag.kg.shared_storage import initialize_pipeline_status
|
||||
|
||||
import asyncio
|
||||
import nest_asyncio
|
||||
|
||||
nest_asyncio.apply()
|
||||
|
||||
logging.getLogger("aiobotocore").setLevel(logging.WARNING)
|
||||
|
||||
WORKING_DIR = "./dickens"
|
||||
if not os.path.exists(WORKING_DIR):
|
||||
os.mkdir(WORKING_DIR)
|
||||
|
||||
|
||||
async def initialize_rag():
|
||||
rag = LightRAG(
|
||||
working_dir=WORKING_DIR,
|
||||
llm_model_func=bedrock_complete,
|
||||
llm_model_name="Anthropic Claude 3 Haiku // Amazon Bedrock",
|
||||
embedding_func=EmbeddingFunc(
|
||||
embedding_dim=1024, max_token_size=8192, func=bedrock_embed
|
||||
),
|
||||
)
|
||||
|
||||
await rag.initialize_storages()
|
||||
await initialize_pipeline_status()
|
||||
|
||||
return rag
|
||||
|
||||
|
||||
def main():
|
||||
rag = asyncio.run(initialize_rag())
|
||||
|
||||
with open("./book.txt", "r", encoding="utf-8") as f:
|
||||
rag.insert(f.read())
|
||||
|
||||
for mode in ["naive", "local", "global", "hybrid"]:
|
||||
print("\n+-" + "-" * len(mode) + "-+")
|
||||
print(f"| {mode.capitalize()} |")
|
||||
print("+-" + "-" * len(mode) + "-+\n")
|
||||
print(
|
||||
rag.query(
|
||||
"What are the top themes in this story?", param=QueryParam(mode=mode)
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,357 @@
|
||||
import asyncio
|
||||
import os
|
||||
import inspect
|
||||
import logging
|
||||
import logging.config
|
||||
from lightrag import LightRAG, QueryParam
|
||||
from lightrag.utils import EmbeddingFunc, logger, set_verbose_debug
|
||||
from lightrag.kg.shared_storage import initialize_pipeline_status
|
||||
|
||||
import requests
|
||||
import numpy as np
|
||||
from dotenv import load_dotenv
|
||||
|
||||
"""This code is a modified version of lightrag_openai_demo.py"""
|
||||
|
||||
# ideally, as always, env!
|
||||
load_dotenv(dotenv_path=".env", override=False)
|
||||
|
||||
|
||||
""" ----========= IMPORTANT CHANGE THIS! =========---- """
|
||||
cloudflare_api_key = "YOUR_API_KEY"
|
||||
account_id = "YOUR_ACCOUNT ID" # This is unique to your Cloudflare account
|
||||
|
||||
# Authomatically changes
|
||||
api_base_url = f"https://api.cloudflare.com/client/v4/accounts/{account_id}/ai/run/"
|
||||
|
||||
|
||||
# choose an embedding model
|
||||
EMBEDDING_MODEL = "@cf/baai/bge-m3"
|
||||
# choose a generative model
|
||||
LLM_MODEL = "@cf/meta/llama-3.2-3b-instruct"
|
||||
|
||||
WORKING_DIR = "../dickens" # you can change output as desired
|
||||
|
||||
|
||||
# Cloudflare init
|
||||
class CloudflareWorker:
|
||||
def __init__(
|
||||
self,
|
||||
cloudflare_api_key: str,
|
||||
api_base_url: str,
|
||||
llm_model_name: str,
|
||||
embedding_model_name: str,
|
||||
max_tokens: int = 4080,
|
||||
max_response_tokens: int = 4080,
|
||||
):
|
||||
self.cloudflare_api_key = cloudflare_api_key
|
||||
self.api_base_url = api_base_url
|
||||
self.llm_model_name = llm_model_name
|
||||
self.embedding_model_name = embedding_model_name
|
||||
self.max_tokens = max_tokens
|
||||
self.max_response_tokens = max_response_tokens
|
||||
|
||||
async def _send_request(self, model_name: str, input_: dict, debug_log: str):
|
||||
headers = {"Authorization": f"Bearer {self.cloudflare_api_key}"}
|
||||
|
||||
print(f"""
|
||||
data sent to Cloudflare
|
||||
~~~~~~~~~~~
|
||||
{debug_log}
|
||||
""")
|
||||
|
||||
try:
|
||||
response_raw = requests.post(
|
||||
f"{self.api_base_url}{model_name}", headers=headers, json=input_
|
||||
).json()
|
||||
print(f"""
|
||||
Cloudflare worker responded with:
|
||||
~~~~~~~~~~~
|
||||
{str(response_raw)}
|
||||
""")
|
||||
result = response_raw.get("result", {})
|
||||
|
||||
if "data" in result: # Embedding case
|
||||
return np.array(result["data"])
|
||||
|
||||
if "response" in result: # LLM response
|
||||
return result["response"]
|
||||
|
||||
raise ValueError("Unexpected Cloudflare response format")
|
||||
|
||||
except Exception as e:
|
||||
print(f"""
|
||||
Cloudflare API returned:
|
||||
~~~~~~~~~
|
||||
Error: {e}
|
||||
""")
|
||||
input("Press Enter to continue...")
|
||||
return None
|
||||
|
||||
async def query(self, prompt, system_prompt: str = "", **kwargs) -> str:
|
||||
# since no caching is used and we don't want to mess with everything lightrag, pop the kwarg it is
|
||||
kwargs.pop("hashing_kv", None)
|
||||
|
||||
message = [
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": prompt},
|
||||
]
|
||||
|
||||
input_ = {
|
||||
"messages": message,
|
||||
"max_tokens": self.max_tokens,
|
||||
"response_token_limit": self.max_response_tokens,
|
||||
}
|
||||
|
||||
return await self._send_request(
|
||||
self.llm_model_name,
|
||||
input_,
|
||||
debug_log=f"\n- model used {self.llm_model_name}\n- system prompt: {system_prompt}\n- query: {prompt}",
|
||||
)
|
||||
|
||||
async def embedding_chunk(self, texts: list[str]) -> np.ndarray:
|
||||
print(f"""
|
||||
TEXT inputted
|
||||
~~~~~
|
||||
{texts}
|
||||
""")
|
||||
|
||||
input_ = {
|
||||
"text": texts,
|
||||
"max_tokens": self.max_tokens,
|
||||
"response_token_limit": self.max_response_tokens,
|
||||
}
|
||||
|
||||
return await self._send_request(
|
||||
self.embedding_model_name,
|
||||
input_,
|
||||
debug_log=f"\n-llm model name {self.embedding_model_name}\n- texts: {texts}",
|
||||
)
|
||||
|
||||
|
||||
def configure_logging():
|
||||
"""Configure logging for the application"""
|
||||
|
||||
# Reset any existing handlers to ensure clean configuration
|
||||
for logger_name in ["uvicorn", "uvicorn.access", "uvicorn.error", "lightrag"]:
|
||||
logger_instance = logging.getLogger(logger_name)
|
||||
logger_instance.handlers = []
|
||||
logger_instance.filters = []
|
||||
|
||||
# Get log directory path from environment variable or use current directory
|
||||
log_dir = os.getenv("LOG_DIR", os.getcwd())
|
||||
log_file_path = os.path.abspath(
|
||||
os.path.join(log_dir, "lightrag_cloudflare_worker_demo.log")
|
||||
)
|
||||
|
||||
print(f"\nLightRAG compatible demo log file: {log_file_path}\n")
|
||||
os.makedirs(os.path.dirname(log_file_path), exist_ok=True)
|
||||
|
||||
# Get log file max size and backup count from environment variables
|
||||
log_max_bytes = int(os.getenv("LOG_MAX_BYTES", 10485760)) # Default 10MB
|
||||
log_backup_count = int(os.getenv("LOG_BACKUP_COUNT", 5)) # Default 5 backups
|
||||
|
||||
logging.config.dictConfig(
|
||||
{
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
"formatters": {
|
||||
"default": {
|
||||
"format": "%(levelname)s: %(message)s",
|
||||
},
|
||||
"detailed": {
|
||||
"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
||||
},
|
||||
},
|
||||
"handlers": {
|
||||
"console": {
|
||||
"formatter": "default",
|
||||
"class": "logging.StreamHandler",
|
||||
"stream": "ext://sys.stderr",
|
||||
},
|
||||
"file": {
|
||||
"formatter": "detailed",
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"filename": log_file_path,
|
||||
"maxBytes": log_max_bytes,
|
||||
"backupCount": log_backup_count,
|
||||
"encoding": "utf-8",
|
||||
},
|
||||
},
|
||||
"loggers": {
|
||||
"lightrag": {
|
||||
"handlers": ["console", "file"],
|
||||
"level": "INFO",
|
||||
"propagate": False,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
# Set the logger level to INFO
|
||||
logger.setLevel(logging.INFO)
|
||||
# Enable verbose debug if needed
|
||||
set_verbose_debug(os.getenv("VERBOSE_DEBUG", "false").lower() == "true")
|
||||
|
||||
|
||||
if not os.path.exists(WORKING_DIR):
|
||||
os.mkdir(WORKING_DIR)
|
||||
|
||||
|
||||
async def initialize_rag():
|
||||
cloudflare_worker = CloudflareWorker(
|
||||
cloudflare_api_key=cloudflare_api_key,
|
||||
api_base_url=api_base_url,
|
||||
embedding_model_name=EMBEDDING_MODEL,
|
||||
llm_model_name=LLM_MODEL,
|
||||
)
|
||||
|
||||
rag = LightRAG(
|
||||
working_dir=WORKING_DIR,
|
||||
max_parallel_insert=2,
|
||||
llm_model_func=cloudflare_worker.query,
|
||||
llm_model_name=os.getenv("LLM_MODEL", LLM_MODEL),
|
||||
summary_max_tokens=4080,
|
||||
embedding_func=EmbeddingFunc(
|
||||
embedding_dim=int(os.getenv("EMBEDDING_DIM", "1024")),
|
||||
max_token_size=int(os.getenv("MAX_EMBED_TOKENS", "2048")),
|
||||
func=lambda texts: cloudflare_worker.embedding_chunk(
|
||||
texts,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
await rag.initialize_storages()
|
||||
await initialize_pipeline_status()
|
||||
|
||||
return rag
|
||||
|
||||
|
||||
async def print_stream(stream):
|
||||
async for chunk in stream:
|
||||
print(chunk, end="", flush=True)
|
||||
|
||||
|
||||
async def main():
|
||||
try:
|
||||
# Clear old data files
|
||||
files_to_delete = [
|
||||
"graph_chunk_entity_relation.graphml",
|
||||
"kv_store_doc_status.json",
|
||||
"kv_store_full_docs.json",
|
||||
"kv_store_text_chunks.json",
|
||||
"vdb_chunks.json",
|
||||
"vdb_entities.json",
|
||||
"vdb_relationships.json",
|
||||
]
|
||||
|
||||
for file in files_to_delete:
|
||||
file_path = os.path.join(WORKING_DIR, file)
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
print(f"Deleting old file:: {file_path}")
|
||||
|
||||
# Initialize RAG instance
|
||||
rag = await initialize_rag()
|
||||
|
||||
# Test embedding function
|
||||
test_text = ["This is a test string for embedding."]
|
||||
embedding = await rag.embedding_func(test_text)
|
||||
embedding_dim = embedding.shape[1]
|
||||
print("\n=======================")
|
||||
print("Test embedding function")
|
||||
print("========================")
|
||||
print(f"Test dict: {test_text}")
|
||||
print(f"Detected embedding dimension: {embedding_dim}\n\n")
|
||||
|
||||
# Locate the location of what is needed to be added to the knowledge
|
||||
# Can add several simultaneously by modifying code
|
||||
with open("./book.txt", "r", encoding="utf-8") as f:
|
||||
await rag.ainsert(f.read())
|
||||
|
||||
# Perform naive search
|
||||
print("\n=====================")
|
||||
print("Query mode: naive")
|
||||
print("=====================")
|
||||
resp = await rag.aquery(
|
||||
"What are the top themes in this story?",
|
||||
param=QueryParam(mode="naive", stream=True),
|
||||
)
|
||||
if inspect.isasyncgen(resp):
|
||||
await print_stream(resp)
|
||||
else:
|
||||
print(resp)
|
||||
|
||||
# Perform local search
|
||||
print("\n=====================")
|
||||
print("Query mode: local")
|
||||
print("=====================")
|
||||
resp = await rag.aquery(
|
||||
"What are the top themes in this story?",
|
||||
param=QueryParam(mode="local", stream=True),
|
||||
)
|
||||
if inspect.isasyncgen(resp):
|
||||
await print_stream(resp)
|
||||
else:
|
||||
print(resp)
|
||||
|
||||
# Perform global search
|
||||
print("\n=====================")
|
||||
print("Query mode: global")
|
||||
print("=====================")
|
||||
resp = await rag.aquery(
|
||||
"What are the top themes in this story?",
|
||||
param=QueryParam(mode="global", stream=True),
|
||||
)
|
||||
if inspect.isasyncgen(resp):
|
||||
await print_stream(resp)
|
||||
else:
|
||||
print(resp)
|
||||
|
||||
# Perform hybrid search
|
||||
print("\n=====================")
|
||||
print("Query mode: hybrid")
|
||||
print("=====================")
|
||||
resp = await rag.aquery(
|
||||
"What are the top themes in this story?",
|
||||
param=QueryParam(mode="hybrid", stream=True),
|
||||
)
|
||||
if inspect.isasyncgen(resp):
|
||||
await print_stream(resp)
|
||||
else:
|
||||
print(resp)
|
||||
|
||||
""" FOR TESTING (if you want to test straight away, after building. Uncomment this part"""
|
||||
|
||||
"""
|
||||
print("\n" + "=" * 60)
|
||||
print("AI ASSISTANT READY!")
|
||||
print("Ask questions about (your uploaded) regulations")
|
||||
print("Type 'quit' to exit")
|
||||
print("=" * 60)
|
||||
|
||||
while True:
|
||||
question = input("\n🔥 Your question: ")
|
||||
|
||||
if question.lower() in ['quit', 'exit', 'bye']:
|
||||
break
|
||||
|
||||
print("\nThinking...")
|
||||
response = await rag.aquery(question, param=QueryParam(mode="hybrid"))
|
||||
print(f"\nAnswer: {response}")
|
||||
|
||||
"""
|
||||
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
finally:
|
||||
if rag:
|
||||
await rag.llm_response_cache.index_done_callback()
|
||||
await rag.finalize_storages()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Configure logging before running the main function
|
||||
configure_logging()
|
||||
asyncio.run(main())
|
||||
print("\nDone!")
|
||||
@@ -0,0 +1,82 @@
|
||||
import os
|
||||
|
||||
from lightrag import LightRAG, QueryParam
|
||||
from lightrag.llm.hf import hf_model_complete, hf_embed
|
||||
from lightrag.utils import EmbeddingFunc
|
||||
from transformers import AutoModel, AutoTokenizer
|
||||
from lightrag.kg.shared_storage import initialize_pipeline_status
|
||||
|
||||
import asyncio
|
||||
import nest_asyncio
|
||||
|
||||
nest_asyncio.apply()
|
||||
|
||||
WORKING_DIR = "./dickens"
|
||||
|
||||
if not os.path.exists(WORKING_DIR):
|
||||
os.mkdir(WORKING_DIR)
|
||||
|
||||
|
||||
async def initialize_rag():
|
||||
rag = LightRAG(
|
||||
working_dir=WORKING_DIR,
|
||||
llm_model_func=hf_model_complete,
|
||||
llm_model_name="meta-llama/Llama-3.1-8B-Instruct",
|
||||
embedding_func=EmbeddingFunc(
|
||||
embedding_dim=384,
|
||||
max_token_size=5000,
|
||||
func=lambda texts: hf_embed(
|
||||
texts,
|
||||
tokenizer=AutoTokenizer.from_pretrained(
|
||||
"sentence-transformers/all-MiniLM-L6-v2"
|
||||
),
|
||||
embed_model=AutoModel.from_pretrained(
|
||||
"sentence-transformers/all-MiniLM-L6-v2"
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
await rag.initialize_storages()
|
||||
await initialize_pipeline_status()
|
||||
|
||||
return rag
|
||||
|
||||
|
||||
def main():
|
||||
rag = asyncio.run(initialize_rag())
|
||||
|
||||
with open("./book.txt", "r", encoding="utf-8") as f:
|
||||
rag.insert(f.read())
|
||||
|
||||
# Perform naive search
|
||||
print(
|
||||
rag.query(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="naive")
|
||||
)
|
||||
)
|
||||
|
||||
# Perform local search
|
||||
print(
|
||||
rag.query(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="local")
|
||||
)
|
||||
)
|
||||
|
||||
# Perform global search
|
||||
print(
|
||||
rag.query(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="global")
|
||||
)
|
||||
)
|
||||
|
||||
# Perform hybrid search
|
||||
print(
|
||||
rag.query(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="hybrid")
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,143 @@
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
import nest_asyncio
|
||||
from llama_index.embeddings.openai import OpenAIEmbedding
|
||||
from llama_index.llms.openai import OpenAI
|
||||
|
||||
from lightrag import LightRAG, QueryParam
|
||||
from lightrag.kg.shared_storage import initialize_pipeline_status
|
||||
from lightrag.llm.llama_index_impl import (
|
||||
llama_index_complete_if_cache,
|
||||
llama_index_embed,
|
||||
)
|
||||
from lightrag.utils import EmbeddingFunc
|
||||
|
||||
nest_asyncio.apply()
|
||||
|
||||
# Configure working directory
|
||||
WORKING_DIR = "./index_default"
|
||||
print(f"WORKING_DIR: {WORKING_DIR}")
|
||||
|
||||
# Model configuration
|
||||
LLM_MODEL = os.environ.get("LLM_MODEL", "gpt-4")
|
||||
print(f"LLM_MODEL: {LLM_MODEL}")
|
||||
EMBEDDING_MODEL = os.environ.get("EMBEDDING_MODEL", "text-embedding-3-large")
|
||||
print(f"EMBEDDING_MODEL: {EMBEDDING_MODEL}")
|
||||
EMBEDDING_MAX_TOKEN_SIZE = int(os.environ.get("EMBEDDING_MAX_TOKEN_SIZE", 8192))
|
||||
print(f"EMBEDDING_MAX_TOKEN_SIZE: {EMBEDDING_MAX_TOKEN_SIZE}")
|
||||
|
||||
# OpenAI configuration
|
||||
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY", "your-api-key-here")
|
||||
|
||||
if not os.path.exists(WORKING_DIR):
|
||||
print(f"Creating working directory: {WORKING_DIR}")
|
||||
os.mkdir(WORKING_DIR)
|
||||
|
||||
|
||||
# Initialize LLM function
|
||||
async def llm_model_func(prompt, system_prompt=None, history_messages=[], **kwargs):
|
||||
try:
|
||||
# Initialize OpenAI if not in kwargs
|
||||
if "llm_instance" not in kwargs:
|
||||
llm_instance = OpenAI(
|
||||
model=LLM_MODEL,
|
||||
api_key=OPENAI_API_KEY,
|
||||
temperature=0.7,
|
||||
)
|
||||
kwargs["llm_instance"] = llm_instance
|
||||
|
||||
response = await llama_index_complete_if_cache(
|
||||
kwargs["llm_instance"],
|
||||
prompt,
|
||||
system_prompt=system_prompt,
|
||||
history_messages=history_messages,
|
||||
**kwargs,
|
||||
)
|
||||
return response
|
||||
except Exception as e:
|
||||
print(f"LLM request failed: {str(e)}")
|
||||
raise
|
||||
|
||||
|
||||
# Initialize embedding function
|
||||
async def embedding_func(texts):
|
||||
try:
|
||||
embed_model = OpenAIEmbedding(
|
||||
model=EMBEDDING_MODEL,
|
||||
api_key=OPENAI_API_KEY,
|
||||
)
|
||||
return await llama_index_embed(texts, embed_model=embed_model)
|
||||
except Exception as e:
|
||||
print(f"Embedding failed: {str(e)}")
|
||||
raise
|
||||
|
||||
|
||||
# Get embedding dimension
|
||||
async def get_embedding_dim():
|
||||
test_text = ["This is a test sentence."]
|
||||
embedding = await embedding_func(test_text)
|
||||
embedding_dim = embedding.shape[1]
|
||||
print(f"embedding_dim={embedding_dim}")
|
||||
return embedding_dim
|
||||
|
||||
|
||||
async def initialize_rag():
|
||||
embedding_dimension = await get_embedding_dim()
|
||||
|
||||
rag = LightRAG(
|
||||
working_dir=WORKING_DIR,
|
||||
llm_model_func=llm_model_func,
|
||||
embedding_func=EmbeddingFunc(
|
||||
embedding_dim=embedding_dimension,
|
||||
max_token_size=EMBEDDING_MAX_TOKEN_SIZE,
|
||||
func=embedding_func,
|
||||
),
|
||||
)
|
||||
|
||||
await rag.initialize_storages()
|
||||
await initialize_pipeline_status()
|
||||
|
||||
return rag
|
||||
|
||||
|
||||
def main():
|
||||
# Initialize RAG instance
|
||||
rag = asyncio.run(initialize_rag())
|
||||
|
||||
# Insert example text
|
||||
with open("./book.txt", "r", encoding="utf-8") as f:
|
||||
rag.insert(f.read())
|
||||
|
||||
# Test different query modes
|
||||
print("\nNaive Search:")
|
||||
print(
|
||||
rag.query(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="naive")
|
||||
)
|
||||
)
|
||||
|
||||
print("\nLocal Search:")
|
||||
print(
|
||||
rag.query(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="local")
|
||||
)
|
||||
)
|
||||
|
||||
print("\nGlobal Search:")
|
||||
print(
|
||||
rag.query(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="global")
|
||||
)
|
||||
)
|
||||
|
||||
print("\nHybrid Search:")
|
||||
print(
|
||||
rag.query(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="hybrid")
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,145 @@
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
import nest_asyncio
|
||||
from llama_index.embeddings.litellm import LiteLLMEmbedding
|
||||
from llama_index.llms.litellm import LiteLLM
|
||||
|
||||
from lightrag import LightRAG, QueryParam
|
||||
from lightrag.kg.shared_storage import initialize_pipeline_status
|
||||
from lightrag.llm.llama_index_impl import (
|
||||
llama_index_complete_if_cache,
|
||||
llama_index_embed,
|
||||
)
|
||||
from lightrag.utils import EmbeddingFunc
|
||||
|
||||
nest_asyncio.apply()
|
||||
|
||||
# Configure working directory
|
||||
WORKING_DIR = "./index_default"
|
||||
print(f"WORKING_DIR: {WORKING_DIR}")
|
||||
|
||||
# Model configuration
|
||||
LLM_MODEL = os.environ.get("LLM_MODEL", "gpt-4")
|
||||
print(f"LLM_MODEL: {LLM_MODEL}")
|
||||
EMBEDDING_MODEL = os.environ.get("EMBEDDING_MODEL", "text-embedding-3-large")
|
||||
print(f"EMBEDDING_MODEL: {EMBEDDING_MODEL}")
|
||||
EMBEDDING_MAX_TOKEN_SIZE = int(os.environ.get("EMBEDDING_MAX_TOKEN_SIZE", 8192))
|
||||
print(f"EMBEDDING_MAX_TOKEN_SIZE: {EMBEDDING_MAX_TOKEN_SIZE}")
|
||||
|
||||
# LiteLLM configuration
|
||||
LITELLM_URL = os.environ.get("LITELLM_URL", "http://localhost:4000")
|
||||
print(f"LITELLM_URL: {LITELLM_URL}")
|
||||
LITELLM_KEY = os.environ.get("LITELLM_KEY", "sk-1234")
|
||||
|
||||
if not os.path.exists(WORKING_DIR):
|
||||
os.mkdir(WORKING_DIR)
|
||||
|
||||
|
||||
# Initialize LLM function
|
||||
async def llm_model_func(prompt, system_prompt=None, history_messages=[], **kwargs):
|
||||
try:
|
||||
# Initialize LiteLLM if not in kwargs
|
||||
if "llm_instance" not in kwargs:
|
||||
llm_instance = LiteLLM(
|
||||
model=f"openai/{LLM_MODEL}", # Format: "provider/model_name"
|
||||
api_base=LITELLM_URL,
|
||||
api_key=LITELLM_KEY,
|
||||
temperature=0.7,
|
||||
)
|
||||
kwargs["llm_instance"] = llm_instance
|
||||
|
||||
response = await llama_index_complete_if_cache(
|
||||
kwargs["llm_instance"],
|
||||
prompt,
|
||||
system_prompt=system_prompt,
|
||||
history_messages=history_messages,
|
||||
)
|
||||
return response
|
||||
except Exception as e:
|
||||
print(f"LLM request failed: {str(e)}")
|
||||
raise
|
||||
|
||||
|
||||
# Initialize embedding function
|
||||
async def embedding_func(texts):
|
||||
try:
|
||||
embed_model = LiteLLMEmbedding(
|
||||
model_name=f"openai/{EMBEDDING_MODEL}",
|
||||
api_base=LITELLM_URL,
|
||||
api_key=LITELLM_KEY,
|
||||
)
|
||||
return await llama_index_embed(texts, embed_model=embed_model)
|
||||
except Exception as e:
|
||||
print(f"Embedding failed: {str(e)}")
|
||||
raise
|
||||
|
||||
|
||||
# Get embedding dimension
|
||||
async def get_embedding_dim():
|
||||
test_text = ["This is a test sentence."]
|
||||
embedding = await embedding_func(test_text)
|
||||
embedding_dim = embedding.shape[1]
|
||||
print(f"embedding_dim={embedding_dim}")
|
||||
return embedding_dim
|
||||
|
||||
|
||||
async def initialize_rag():
|
||||
embedding_dimension = await get_embedding_dim()
|
||||
|
||||
rag = LightRAG(
|
||||
working_dir=WORKING_DIR,
|
||||
llm_model_func=llm_model_func,
|
||||
embedding_func=EmbeddingFunc(
|
||||
embedding_dim=embedding_dimension,
|
||||
max_token_size=EMBEDDING_MAX_TOKEN_SIZE,
|
||||
func=embedding_func,
|
||||
),
|
||||
)
|
||||
|
||||
await rag.initialize_storages()
|
||||
await initialize_pipeline_status()
|
||||
|
||||
return rag
|
||||
|
||||
|
||||
def main():
|
||||
# Initialize RAG instance
|
||||
rag = asyncio.run(initialize_rag())
|
||||
|
||||
# Insert example text
|
||||
with open("./book.txt", "r", encoding="utf-8") as f:
|
||||
rag.insert(f.read())
|
||||
|
||||
# Test different query modes
|
||||
print("\nNaive Search:")
|
||||
print(
|
||||
rag.query(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="naive")
|
||||
)
|
||||
)
|
||||
|
||||
print("\nLocal Search:")
|
||||
print(
|
||||
rag.query(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="local")
|
||||
)
|
||||
)
|
||||
|
||||
print("\nGlobal Search:")
|
||||
print(
|
||||
rag.query(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="global")
|
||||
)
|
||||
)
|
||||
|
||||
print("\nHybrid Search:")
|
||||
print(
|
||||
rag.query(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="hybrid")
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,156 @@
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
import nest_asyncio
|
||||
from llama_index.embeddings.litellm import LiteLLMEmbedding
|
||||
from llama_index.llms.litellm import LiteLLM
|
||||
|
||||
from lightrag import LightRAG, QueryParam
|
||||
from lightrag.kg.shared_storage import initialize_pipeline_status
|
||||
from lightrag.llm.llama_index_impl import (
|
||||
llama_index_complete_if_cache,
|
||||
llama_index_embed,
|
||||
)
|
||||
from lightrag.utils import EmbeddingFunc
|
||||
|
||||
nest_asyncio.apply()
|
||||
|
||||
# Configure working directory
|
||||
WORKING_DIR = "./index_default"
|
||||
print(f"WORKING_DIR: {WORKING_DIR}")
|
||||
|
||||
# Model configuration
|
||||
LLM_MODEL = os.environ.get("LLM_MODEL", "gemma-3-4b")
|
||||
print(f"LLM_MODEL: {LLM_MODEL}")
|
||||
EMBEDDING_MODEL = os.environ.get("EMBEDDING_MODEL", "arctic-embed")
|
||||
print(f"EMBEDDING_MODEL: {EMBEDDING_MODEL}")
|
||||
EMBEDDING_MAX_TOKEN_SIZE = int(os.environ.get("EMBEDDING_MAX_TOKEN_SIZE", 8192))
|
||||
print(f"EMBEDDING_MAX_TOKEN_SIZE: {EMBEDDING_MAX_TOKEN_SIZE}")
|
||||
|
||||
# LiteLLM configuration
|
||||
LITELLM_URL = os.environ.get("LITELLM_URL", "http://localhost:4000")
|
||||
print(f"LITELLM_URL: {LITELLM_URL}")
|
||||
LITELLM_KEY = os.environ.get("LITELLM_KEY", "sk-4JdvGFKqSA3S0k_5p0xufw")
|
||||
|
||||
if not os.path.exists(WORKING_DIR):
|
||||
os.mkdir(WORKING_DIR)
|
||||
|
||||
|
||||
# Initialize LLM function
|
||||
async def llm_model_func(prompt, system_prompt=None, history_messages=[], **kwargs):
|
||||
try:
|
||||
# Initialize LiteLLM if not in kwargs
|
||||
if "llm_instance" not in kwargs:
|
||||
llm_instance = LiteLLM(
|
||||
model=f"openai/{LLM_MODEL}", # Format: "provider/model_name"
|
||||
api_base=LITELLM_URL,
|
||||
api_key=LITELLM_KEY,
|
||||
temperature=0.7,
|
||||
)
|
||||
kwargs["llm_instance"] = llm_instance
|
||||
|
||||
chat_kwargs = {}
|
||||
chat_kwargs["litellm_params"] = {
|
||||
"metadata": {
|
||||
"opik": {
|
||||
"project_name": "lightrag_llamaindex_litellm_opik_demo",
|
||||
"tags": ["lightrag", "litellm"],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response = await llama_index_complete_if_cache(
|
||||
kwargs["llm_instance"],
|
||||
prompt,
|
||||
system_prompt=system_prompt,
|
||||
history_messages=history_messages,
|
||||
chat_kwargs=chat_kwargs,
|
||||
)
|
||||
return response
|
||||
except Exception as e:
|
||||
print(f"LLM request failed: {str(e)}")
|
||||
raise
|
||||
|
||||
|
||||
# Initialize embedding function
|
||||
async def embedding_func(texts):
|
||||
try:
|
||||
embed_model = LiteLLMEmbedding(
|
||||
model_name=f"openai/{EMBEDDING_MODEL}",
|
||||
api_base=LITELLM_URL,
|
||||
api_key=LITELLM_KEY,
|
||||
)
|
||||
return await llama_index_embed(texts, embed_model=embed_model)
|
||||
except Exception as e:
|
||||
print(f"Embedding failed: {str(e)}")
|
||||
raise
|
||||
|
||||
|
||||
# Get embedding dimension
|
||||
async def get_embedding_dim():
|
||||
test_text = ["This is a test sentence."]
|
||||
embedding = await embedding_func(test_text)
|
||||
embedding_dim = embedding.shape[1]
|
||||
print(f"embedding_dim={embedding_dim}")
|
||||
return embedding_dim
|
||||
|
||||
|
||||
async def initialize_rag():
|
||||
embedding_dimension = await get_embedding_dim()
|
||||
|
||||
rag = LightRAG(
|
||||
working_dir=WORKING_DIR,
|
||||
llm_model_func=llm_model_func,
|
||||
embedding_func=EmbeddingFunc(
|
||||
embedding_dim=embedding_dimension,
|
||||
max_token_size=EMBEDDING_MAX_TOKEN_SIZE,
|
||||
func=embedding_func,
|
||||
),
|
||||
)
|
||||
|
||||
await rag.initialize_storages()
|
||||
await initialize_pipeline_status()
|
||||
|
||||
return rag
|
||||
|
||||
|
||||
def main():
|
||||
# Initialize RAG instance
|
||||
rag = asyncio.run(initialize_rag())
|
||||
|
||||
# Insert example text
|
||||
with open("./book.txt", "r", encoding="utf-8") as f:
|
||||
rag.insert(f.read())
|
||||
|
||||
# Test different query modes
|
||||
print("\nNaive Search:")
|
||||
print(
|
||||
rag.query(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="naive")
|
||||
)
|
||||
)
|
||||
|
||||
print("\nLocal Search:")
|
||||
print(
|
||||
rag.query(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="local")
|
||||
)
|
||||
)
|
||||
|
||||
print("\nGlobal Search:")
|
||||
print(
|
||||
rag.query(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="global")
|
||||
)
|
||||
)
|
||||
|
||||
print("\nHybrid Search:")
|
||||
print(
|
||||
rag.query(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="hybrid")
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,110 @@
|
||||
import os
|
||||
|
||||
from lightrag import LightRAG, QueryParam
|
||||
from lightrag.llm.lmdeploy import lmdeploy_model_if_cache
|
||||
from lightrag.llm.hf import hf_embed
|
||||
from lightrag.utils import EmbeddingFunc
|
||||
from transformers import AutoModel, AutoTokenizer
|
||||
from lightrag.kg.shared_storage import initialize_pipeline_status
|
||||
|
||||
import asyncio
|
||||
import nest_asyncio
|
||||
|
||||
nest_asyncio.apply()
|
||||
|
||||
WORKING_DIR = "./dickens"
|
||||
|
||||
if not os.path.exists(WORKING_DIR):
|
||||
os.mkdir(WORKING_DIR)
|
||||
|
||||
|
||||
async def lmdeploy_model_complete(
|
||||
prompt=None,
|
||||
system_prompt=None,
|
||||
history_messages=[],
|
||||
keyword_extraction=False,
|
||||
**kwargs,
|
||||
) -> str:
|
||||
model_name = kwargs["hashing_kv"].global_config["llm_model_name"]
|
||||
return await lmdeploy_model_if_cache(
|
||||
model_name,
|
||||
prompt,
|
||||
system_prompt=system_prompt,
|
||||
history_messages=history_messages,
|
||||
## please specify chat_template if your local path does not follow original HF file name,
|
||||
## or model_name is a pytorch model on huggingface.co,
|
||||
## you can refer to https://github.com/InternLM/lmdeploy/blob/main/lmdeploy/model.py
|
||||
## for a list of chat_template available in lmdeploy.
|
||||
chat_template="llama3",
|
||||
# model_format ='awq', # if you are using awq quantization model.
|
||||
# quant_policy=8, # if you want to use online kv cache, 4=kv int4, 8=kv int8.
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
async def initialize_rag():
|
||||
rag = LightRAG(
|
||||
working_dir=WORKING_DIR,
|
||||
llm_model_func=lmdeploy_model_complete,
|
||||
llm_model_name="meta-llama/Llama-3.1-8B-Instruct", # please use definite path for local model
|
||||
embedding_func=EmbeddingFunc(
|
||||
embedding_dim=384,
|
||||
max_token_size=5000,
|
||||
func=lambda texts: hf_embed(
|
||||
texts,
|
||||
tokenizer=AutoTokenizer.from_pretrained(
|
||||
"sentence-transformers/all-MiniLM-L6-v2"
|
||||
),
|
||||
embed_model=AutoModel.from_pretrained(
|
||||
"sentence-transformers/all-MiniLM-L6-v2"
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
await rag.initialize_storages()
|
||||
await initialize_pipeline_status()
|
||||
|
||||
return rag
|
||||
|
||||
|
||||
def main():
|
||||
# Initialize RAG instance
|
||||
rag = asyncio.run(initialize_rag())
|
||||
|
||||
# Insert example text
|
||||
with open("./book.txt", "r", encoding="utf-8") as f:
|
||||
rag.insert(f.read())
|
||||
|
||||
# Test different query modes
|
||||
print("\nNaive Search:")
|
||||
print(
|
||||
rag.query(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="naive")
|
||||
)
|
||||
)
|
||||
|
||||
print("\nLocal Search:")
|
||||
print(
|
||||
rag.query(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="local")
|
||||
)
|
||||
)
|
||||
|
||||
print("\nGlobal Search:")
|
||||
print(
|
||||
rag.query(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="global")
|
||||
)
|
||||
)
|
||||
|
||||
print("\nHybrid Search:")
|
||||
print(
|
||||
rag.query(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="hybrid")
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,171 @@
|
||||
import os
|
||||
import asyncio
|
||||
import nest_asyncio
|
||||
|
||||
from lightrag import LightRAG, QueryParam
|
||||
from lightrag.llm import (
|
||||
openai_complete_if_cache,
|
||||
nvidia_openai_embed,
|
||||
)
|
||||
from lightrag.utils import EmbeddingFunc
|
||||
import numpy as np
|
||||
from lightrag.kg.shared_storage import initialize_pipeline_status
|
||||
|
||||
# for custom llm_model_func
|
||||
from lightrag.utils import locate_json_string_body_from_string
|
||||
|
||||
nest_asyncio.apply()
|
||||
|
||||
WORKING_DIR = "./dickens"
|
||||
|
||||
if not os.path.exists(WORKING_DIR):
|
||||
os.mkdir(WORKING_DIR)
|
||||
|
||||
# some method to use your API key (choose one)
|
||||
# NVIDIA_OPENAI_API_KEY = os.getenv("NVIDIA_OPENAI_API_KEY")
|
||||
NVIDIA_OPENAI_API_KEY = "nvapi-xxxx" # your api key
|
||||
|
||||
# using pre-defined function for nvidia LLM API. OpenAI compatible
|
||||
# llm_model_func = nvidia_openai_complete
|
||||
|
||||
|
||||
# If you trying to make custom llm_model_func to use llm model on NVIDIA API like other example:
|
||||
async def llm_model_func(
|
||||
prompt, system_prompt=None, history_messages=[], keyword_extraction=False, **kwargs
|
||||
) -> str:
|
||||
result = await openai_complete_if_cache(
|
||||
"nvidia/llama-3.1-nemotron-70b-instruct",
|
||||
prompt,
|
||||
system_prompt=system_prompt,
|
||||
history_messages=history_messages,
|
||||
api_key=NVIDIA_OPENAI_API_KEY,
|
||||
base_url="https://integrate.api.nvidia.com/v1",
|
||||
**kwargs,
|
||||
)
|
||||
if keyword_extraction:
|
||||
return locate_json_string_body_from_string(result)
|
||||
return result
|
||||
|
||||
|
||||
# custom embedding
|
||||
nvidia_embed_model = "nvidia/nv-embedqa-e5-v5"
|
||||
|
||||
|
||||
async def indexing_embedding_func(texts: list[str]) -> np.ndarray:
|
||||
return await nvidia_openai_embed(
|
||||
texts,
|
||||
model=nvidia_embed_model, # maximum 512 token
|
||||
# model="nvidia/llama-3.2-nv-embedqa-1b-v1",
|
||||
api_key=NVIDIA_OPENAI_API_KEY,
|
||||
base_url="https://integrate.api.nvidia.com/v1",
|
||||
input_type="passage",
|
||||
trunc="END", # handling on server side if input token is longer than maximum token
|
||||
encode="float",
|
||||
)
|
||||
|
||||
|
||||
async def query_embedding_func(texts: list[str]) -> np.ndarray:
|
||||
return await nvidia_openai_embed(
|
||||
texts,
|
||||
model=nvidia_embed_model, # maximum 512 token
|
||||
# model="nvidia/llama-3.2-nv-embedqa-1b-v1",
|
||||
api_key=NVIDIA_OPENAI_API_KEY,
|
||||
base_url="https://integrate.api.nvidia.com/v1",
|
||||
input_type="query",
|
||||
trunc="END", # handling on server side if input token is longer than maximum token
|
||||
encode="float",
|
||||
)
|
||||
|
||||
|
||||
# dimension are same
|
||||
async def get_embedding_dim():
|
||||
test_text = ["This is a test sentence."]
|
||||
embedding = await indexing_embedding_func(test_text)
|
||||
embedding_dim = embedding.shape[1]
|
||||
return embedding_dim
|
||||
|
||||
|
||||
# function test
|
||||
async def test_funcs():
|
||||
result = await llm_model_func("How are you?")
|
||||
print("llm_model_func: ", result)
|
||||
|
||||
result = await indexing_embedding_func(["How are you?"])
|
||||
print("embedding_func: ", result)
|
||||
|
||||
|
||||
# asyncio.run(test_funcs())
|
||||
|
||||
|
||||
async def initialize_rag():
|
||||
embedding_dimension = await get_embedding_dim()
|
||||
print(f"Detected embedding dimension: {embedding_dimension}")
|
||||
|
||||
# lightRAG class during indexing
|
||||
rag = LightRAG(
|
||||
working_dir=WORKING_DIR,
|
||||
llm_model_func=llm_model_func,
|
||||
# llm_model_name="meta/llama3-70b-instruct", #un comment if
|
||||
embedding_func=EmbeddingFunc(
|
||||
embedding_dim=embedding_dimension,
|
||||
max_token_size=512, # maximum token size, somehow it's still exceed maximum number of token
|
||||
# so truncate (trunc) parameter on embedding_func will handle it and try to examine the tokenizer used in LightRAG
|
||||
# so you can adjust to be able to fit the NVIDIA model (future work)
|
||||
func=indexing_embedding_func,
|
||||
),
|
||||
)
|
||||
|
||||
await rag.initialize_storages()
|
||||
await initialize_pipeline_status()
|
||||
|
||||
return rag
|
||||
|
||||
|
||||
async def main():
|
||||
try:
|
||||
# Initialize RAG instance
|
||||
rag = await initialize_rag()
|
||||
|
||||
# reading file
|
||||
with open("./book.txt", "r", encoding="utf-8") as f:
|
||||
await rag.ainsert(f.read())
|
||||
|
||||
# Perform naive search
|
||||
print("==============Naive===============")
|
||||
print(
|
||||
await rag.aquery(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="naive")
|
||||
)
|
||||
)
|
||||
|
||||
# Perform local search
|
||||
print("==============local===============")
|
||||
print(
|
||||
await rag.aquery(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="local")
|
||||
)
|
||||
)
|
||||
|
||||
# Perform global search
|
||||
print("==============global===============")
|
||||
print(
|
||||
await rag.aquery(
|
||||
"What are the top themes in this story?",
|
||||
param=QueryParam(mode="global"),
|
||||
)
|
||||
)
|
||||
|
||||
# Perform hybrid search
|
||||
print("==============hybrid===============")
|
||||
print(
|
||||
await rag.aquery(
|
||||
"What are the top themes in this story?",
|
||||
param=QueryParam(mode="hybrid"),
|
||||
)
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,112 @@
|
||||
import os
|
||||
import asyncio
|
||||
from lightrag import LightRAG, QueryParam
|
||||
from lightrag.llm.ollama import ollama_embed, openai_complete_if_cache
|
||||
from lightrag.utils import EmbeddingFunc
|
||||
from lightrag.kg.shared_storage import initialize_pipeline_status
|
||||
|
||||
# WorkingDir
|
||||
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
WORKING_DIR = os.path.join(ROOT_DIR, "myKG")
|
||||
if not os.path.exists(WORKING_DIR):
|
||||
os.mkdir(WORKING_DIR)
|
||||
print(f"WorkingDir: {WORKING_DIR}")
|
||||
|
||||
# redis
|
||||
os.environ["REDIS_URI"] = "redis://localhost:6379"
|
||||
|
||||
# neo4j
|
||||
BATCH_SIZE_NODES = 500
|
||||
BATCH_SIZE_EDGES = 100
|
||||
os.environ["NEO4J_URI"] = "neo4j://localhost:7687"
|
||||
os.environ["NEO4J_USERNAME"] = "neo4j"
|
||||
os.environ["NEO4J_PASSWORD"] = "12345678"
|
||||
|
||||
# milvus
|
||||
os.environ["MILVUS_URI"] = "http://localhost:19530"
|
||||
os.environ["MILVUS_USER"] = "root"
|
||||
os.environ["MILVUS_PASSWORD"] = "Milvus"
|
||||
os.environ["MILVUS_DB_NAME"] = "lightrag"
|
||||
|
||||
|
||||
async def llm_model_func(
|
||||
prompt, system_prompt=None, history_messages=[], keyword_extraction=False, **kwargs
|
||||
) -> str:
|
||||
return await openai_complete_if_cache(
|
||||
"deepseek-chat",
|
||||
prompt,
|
||||
system_prompt=system_prompt,
|
||||
history_messages=history_messages,
|
||||
api_key="",
|
||||
base_url="",
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
embedding_func = EmbeddingFunc(
|
||||
embedding_dim=768,
|
||||
max_token_size=512,
|
||||
func=lambda texts: ollama_embed(
|
||||
texts, embed_model="shaw/dmeta-embedding-zh", host="http://117.50.173.35:11434"
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
async def initialize_rag():
|
||||
rag = LightRAG(
|
||||
working_dir=WORKING_DIR,
|
||||
llm_model_func=llm_model_func,
|
||||
summary_max_tokens=10000,
|
||||
embedding_func=embedding_func,
|
||||
chunk_token_size=512,
|
||||
chunk_overlap_token_size=256,
|
||||
kv_storage="RedisKVStorage",
|
||||
graph_storage="Neo4JStorage",
|
||||
vector_storage="MilvusVectorDBStorage",
|
||||
doc_status_storage="RedisKVStorage",
|
||||
)
|
||||
|
||||
await rag.initialize_storages()
|
||||
await initialize_pipeline_status()
|
||||
|
||||
return rag
|
||||
|
||||
|
||||
def main():
|
||||
# Initialize RAG instance
|
||||
rag = asyncio.run(initialize_rag())
|
||||
|
||||
with open("./book.txt", "r", encoding="utf-8") as f:
|
||||
rag.insert(f.read())
|
||||
|
||||
# Perform naive search
|
||||
print(
|
||||
rag.query(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="naive")
|
||||
)
|
||||
)
|
||||
|
||||
# Perform local search
|
||||
print(
|
||||
rag.query(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="local")
|
||||
)
|
||||
)
|
||||
|
||||
# Perform global search
|
||||
print(
|
||||
rag.query(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="global")
|
||||
)
|
||||
)
|
||||
|
||||
# Perform hybrid search
|
||||
print(
|
||||
rag.query(
|
||||
"What are the top themes in this story?", param=QueryParam(mode="hybrid")
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,191 @@
|
||||
# LightRAG Helm Chart
|
||||
|
||||
这是用于在Kubernetes集群上部署LightRAG服务的Helm chart。
|
||||
|
||||
LightRAG有两种推荐的部署方法:
|
||||
1. **轻量级部署**:使用内置轻量级存储,适合测试和小规模使用
|
||||
2. **生产环境部署**:使用外部数据库(如PostgreSQL和Neo4J),适合生产环境和大规模使用
|
||||
|
||||
> 如果您想要部署过程的视频演示,可以查看[bilibili](https://www.bilibili.com/video/BV1bUJazBEq2/)上的视频教程,对于喜欢视觉指导的用户可能会有所帮助。
|
||||
|
||||
## 前提条件
|
||||
|
||||
确保安装和配置了以下工具:
|
||||
|
||||
* **Kubernetes集群**
|
||||
* 需要一个运行中的Kubernetes集群。
|
||||
* 对于本地开发或演示,可以使用[Minikube](https://minikube.sigs.k8s.io/docs/start/)(需要≥2个CPU,≥4GB内存,以及Docker/VM驱动支持)。
|
||||
* 任何标准的云端或本地Kubernetes集群(EKS、GKE、AKS等)也可以使用。
|
||||
|
||||
* **kubectl**
|
||||
* Kubernetes命令行工具,用于管理集群。
|
||||
* 按照官方指南安装:[安装和设置kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl)。
|
||||
|
||||
* **Helm**(v3.x+)
|
||||
* Kubernetes包管理器,用于安装LightRAG。
|
||||
* 通过官方指南安装:[安装Helm](https://helm.sh/docs/intro/install/)。
|
||||
|
||||
## 轻量级部署(无需外部数据库)
|
||||
|
||||
这种部署选项使用内置的轻量级存储组件,非常适合测试、演示或小规模使用场景。无需外部数据库配置。
|
||||
|
||||
您可以使用提供的便捷脚本或直接使用Helm命令部署LightRAG。两种方法都配置了`lightrag/values.yaml`文件中定义的相同环境变量。
|
||||
|
||||
### 使用便捷脚本(推荐):
|
||||
|
||||
```bash
|
||||
export OPENAI_API_BASE=<您的OPENAI_API_BASE>
|
||||
export OPENAI_API_KEY=<您的OPENAI_API_KEY>
|
||||
bash ./install_lightrag_dev.sh
|
||||
```
|
||||
|
||||
### 或直接使用Helm:
|
||||
|
||||
```bash
|
||||
# 您可以覆盖任何想要的环境参数
|
||||
helm upgrade --install lightrag ./lightrag \
|
||||
--namespace rag \
|
||||
--set-string env.LIGHTRAG_KV_STORAGE=JsonKVStorage \
|
||||
--set-string env.LIGHTRAG_VECTOR_STORAGE=NanoVectorDBStorage \
|
||||
--set-string env.LIGHTRAG_GRAPH_STORAGE=NetworkXStorage \
|
||||
--set-string env.LIGHTRAG_DOC_STATUS_STORAGE=JsonDocStatusStorage \
|
||||
--set-string env.LLM_BINDING=openai \
|
||||
--set-string env.LLM_MODEL=gpt-4o-mini \
|
||||
--set-string env.LLM_BINDING_HOST=$OPENAI_API_BASE \
|
||||
--set-string env.LLM_BINDING_API_KEY=$OPENAI_API_KEY \
|
||||
--set-string env.EMBEDDING_BINDING=openai \
|
||||
--set-string env.EMBEDDING_MODEL=text-embedding-ada-002 \
|
||||
--set-string env.EMBEDDING_DIM=1536 \
|
||||
--set-string env.EMBEDDING_BINDING_API_KEY=$OPENAI_API_KEY
|
||||
```
|
||||
|
||||
### 访问应用程序:
|
||||
|
||||
```bash
|
||||
# 1. 在终端中运行此端口转发命令:
|
||||
kubectl --namespace rag port-forward svc/lightrag-dev 9621:9621
|
||||
|
||||
# 2. 当命令运行时,打开浏览器并导航到:
|
||||
# http://localhost:9621
|
||||
```
|
||||
|
||||
## 生产环境部署(使用外部数据库)
|
||||
|
||||
### 1. 安装数据库
|
||||
> 如果您已经准备好了数据库,可以跳过此步骤。详细信息可以在:[README.md](databases%2FREADME.md)中找到。
|
||||
|
||||
我们推荐使用KubeBlocks进行数据库部署。KubeBlocks是一个云原生数据库操作符,可以轻松地在Kubernetes上以生产规模运行任何数据库。
|
||||
|
||||
首先,安装KubeBlocks和KubeBlocks-Addons(如已安装可跳过):
|
||||
```bash
|
||||
bash ./databases/01-prepare.sh
|
||||
```
|
||||
|
||||
然后安装所需的数据库。默认情况下,这将安装PostgreSQL和Neo4J,但您可以修改[00-config.sh](databases%2F00-config.sh)以根据需要选择不同的数据库:
|
||||
```bash
|
||||
bash ./databases/02-install-database.sh
|
||||
```
|
||||
|
||||
验证集群是否正在运行:
|
||||
```bash
|
||||
kubectl get clusters -n rag
|
||||
# 预期输出:
|
||||
# NAME CLUSTER-DEFINITION TERMINATION-POLICY STATUS AGE
|
||||
# neo4j-cluster Delete Running 39s
|
||||
# pg-cluster postgresql Delete Running 42s
|
||||
|
||||
kubectl get po -n rag
|
||||
# 预期输出:
|
||||
# NAME READY STATUS RESTARTS AGE
|
||||
# neo4j-cluster-neo4j-0 1/1 Running 0 58s
|
||||
# pg-cluster-postgresql-0 4/4 Running 0 59s
|
||||
# pg-cluster-postgresql-1 4/4 Running 0 59s
|
||||
```
|
||||
|
||||
### 2. 安装LightRAG
|
||||
|
||||
LightRAG及其数据库部署在同一Kubernetes集群中,使配置变得简单。
|
||||
安装脚本会自动从KubeBlocks获取所有数据库连接信息,无需手动设置数据库凭证:
|
||||
|
||||
```bash
|
||||
export OPENAI_API_BASE=<您的OPENAI_API_BASE>
|
||||
export OPENAI_API_KEY=<您的OPENAI_API_KEY>
|
||||
bash ./install_lightrag.sh
|
||||
```
|
||||
|
||||
### 访问应用程序:
|
||||
|
||||
```bash
|
||||
# 1. 在终端中运行此端口转发命令:
|
||||
kubectl --namespace rag port-forward svc/lightrag 9621:9621
|
||||
|
||||
# 2. 当命令运行时,打开浏览器并导航到:
|
||||
# http://localhost:9621
|
||||
```
|
||||
|
||||
## 配置
|
||||
|
||||
### 修改资源配置
|
||||
|
||||
您可以通过修改`values.yaml`文件来配置LightRAG的资源使用:
|
||||
|
||||
```yaml
|
||||
replicaCount: 1 # 副本数量,可根据需要增加
|
||||
|
||||
resources:
|
||||
limits:
|
||||
cpu: 1000m # CPU限制,可根据需要调整
|
||||
memory: 2Gi # 内存限制,可根据需要调整
|
||||
requests:
|
||||
cpu: 500m # CPU请求,可根据需要调整
|
||||
memory: 1Gi # 内存请求,可根据需要调整
|
||||
```
|
||||
|
||||
### 修改持久存储
|
||||
|
||||
```yaml
|
||||
persistence:
|
||||
enabled: true
|
||||
ragStorage:
|
||||
size: 10Gi # RAG存储大小,可根据需要调整
|
||||
inputs:
|
||||
size: 5Gi # 输入数据存储大小,可根据需要调整
|
||||
```
|
||||
|
||||
### 配置环境变量
|
||||
|
||||
`values.yaml`文件中的`env`部分包含LightRAG的所有环境配置,类似于`.env`文件。当使用helm upgrade或helm install命令时,可以使用--set标志覆盖这些变量。
|
||||
|
||||
```yaml
|
||||
env:
|
||||
HOST: 0.0.0.0
|
||||
PORT: 9621
|
||||
WEBUI_TITLE: Graph RAG Engine
|
||||
WEBUI_DESCRIPTION: Simple and Fast Graph Based RAG System
|
||||
|
||||
# LLM配置
|
||||
LLM_BINDING: openai # LLM服务提供商
|
||||
LLM_MODEL: gpt-4o-mini # LLM模型
|
||||
LLM_BINDING_HOST: # API基础URL(可选)
|
||||
LLM_BINDING_API_KEY: # API密钥
|
||||
|
||||
# 嵌入配置
|
||||
EMBEDDING_BINDING: openai # 嵌入服务提供商
|
||||
EMBEDDING_MODEL: text-embedding-ada-002 # 嵌入模型
|
||||
EMBEDDING_DIM: 1536 # 嵌入维度
|
||||
EMBEDDING_BINDING_API_KEY: # API密钥
|
||||
|
||||
# 存储配置
|
||||
LIGHTRAG_KV_STORAGE: PGKVStorage # 键值存储类型
|
||||
LIGHTRAG_VECTOR_STORAGE: PGVectorStorage # 向量存储类型
|
||||
LIGHTRAG_GRAPH_STORAGE: Neo4JStorage # 图存储类型
|
||||
LIGHTRAG_DOC_STATUS_STORAGE: PGDocStatusStorage # 文档状态存储类型
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 在部署前确保设置了所有必要的环境变量(API密钥和数据库密码)
|
||||
- 出于安全原因,建议使用环境变量传递敏感信息,而不是直接写入脚本或values文件
|
||||
- 轻量级部署适合测试和小规模使用,但数据持久性和性能可能有限
|
||||
- 生产环境部署(PostgreSQL + Neo4J)推荐用于生产环境和大规模使用
|
||||
- 有关更多自定义配置,请参考LightRAG官方文档
|
||||
@@ -0,0 +1,191 @@
|
||||
# LightRAG Helm Chart
|
||||
|
||||
This is the Helm chart for LightRAG, used to deploy LightRAG services on a Kubernetes cluster.
|
||||
|
||||
There are two recommended deployment methods for LightRAG:
|
||||
1. **Lightweight Deployment**: Using built-in lightweight storage, suitable for testing and small-scale usage
|
||||
2. **Production Deployment**: Using external databases (such as PostgreSQL and Neo4J), suitable for production environments and large-scale usage
|
||||
|
||||
> If you'd like a video walkthrough of the deployment process, feel free to check out this optional [video tutorial](https://youtu.be/JW1z7fzeKTw?si=vPzukqqwmdzq9Q4q) on YouTube. It might help clarify some steps for those who prefer visual guidance.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Make sure the following tools are installed and configured:
|
||||
|
||||
* **Kubernetes cluster**
|
||||
* A running Kubernetes cluster is required.
|
||||
* For local development or demos you can use [Minikube](https://minikube.sigs.k8s.io/docs/start/) (needs ≥ 2 CPUs, ≥ 4 GB RAM, and Docker/VM-driver support).
|
||||
* Any standard cloud or on-premises Kubernetes cluster (EKS, GKE, AKS, etc.) also works.
|
||||
|
||||
* **kubectl**
|
||||
* The Kubernetes command-line tool for managing your cluster.
|
||||
* Follow the official guide: [Install and Set Up kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl).
|
||||
|
||||
* **Helm** (v3.x+)
|
||||
* Kubernetes package manager used to install LightRAG.
|
||||
* Install it via the official instructions: [Installing Helm](https://helm.sh/docs/intro/install/).
|
||||
|
||||
## Lightweight Deployment (No External Databases Required)
|
||||
|
||||
This deployment option uses built-in lightweight storage components that are perfect for testing, demos, or small-scale usage scenarios. No external database configuration is required.
|
||||
|
||||
You can deploy LightRAG using either the provided convenience script or direct Helm commands. Both methods configure the same environment variables defined in the `lightrag/values.yaml` file.
|
||||
|
||||
### Using the convenience script (recommended):
|
||||
|
||||
```bash
|
||||
export OPENAI_API_BASE=<YOUR_OPENAI_API_BASE>
|
||||
export OPENAI_API_KEY=<YOUR_OPENAI_API_KEY>
|
||||
bash ./install_lightrag_dev.sh
|
||||
```
|
||||
|
||||
### Or using Helm directly:
|
||||
|
||||
```bash
|
||||
# You can override any env param you want
|
||||
helm upgrade --install lightrag ./lightrag \
|
||||
--namespace rag \
|
||||
--set-string env.LIGHTRAG_KV_STORAGE=JsonKVStorage \
|
||||
--set-string env.LIGHTRAG_VECTOR_STORAGE=NanoVectorDBStorage \
|
||||
--set-string env.LIGHTRAG_GRAPH_STORAGE=NetworkXStorage \
|
||||
--set-string env.LIGHTRAG_DOC_STATUS_STORAGE=JsonDocStatusStorage \
|
||||
--set-string env.LLM_BINDING=openai \
|
||||
--set-string env.LLM_MODEL=gpt-4o-mini \
|
||||
--set-string env.LLM_BINDING_HOST=$OPENAI_API_BASE \
|
||||
--set-string env.LLM_BINDING_API_KEY=$OPENAI_API_KEY \
|
||||
--set-string env.EMBEDDING_BINDING=openai \
|
||||
--set-string env.EMBEDDING_MODEL=text-embedding-ada-002 \
|
||||
--set-string env.EMBEDDING_DIM=1536 \
|
||||
--set-string env.EMBEDDING_BINDING_API_KEY=$OPENAI_API_KEY
|
||||
```
|
||||
|
||||
### Accessing the application:
|
||||
|
||||
```bash
|
||||
# 1. Run this port-forward command in your terminal:
|
||||
kubectl --namespace rag port-forward svc/lightrag-dev 9621:9621
|
||||
|
||||
# 2. While the command is running, open your browser and navigate to:
|
||||
# http://localhost:9621
|
||||
```
|
||||
|
||||
## Production Deployment (Using External Databases)
|
||||
|
||||
### 1. Install Databases
|
||||
> You can skip this step if you've already prepared databases. Detailed information can be found in: [README.md](databases%2FREADME.md).
|
||||
|
||||
We recommend KubeBlocks for database deployment. KubeBlocks is a cloud-native database operator that makes it easy to run any database on Kubernetes at production scale.
|
||||
|
||||
First, install KubeBlocks and KubeBlocks-Addons (skip if already installed):
|
||||
```bash
|
||||
bash ./databases/01-prepare.sh
|
||||
```
|
||||
|
||||
Then install the required databases. By default, this will install PostgreSQL and Neo4J, but you can modify [00-config.sh](databases%2F00-config.sh) to select different databases based on your needs:
|
||||
```bash
|
||||
bash ./databases/02-install-database.sh
|
||||
```
|
||||
|
||||
Verify that the clusters are up and running:
|
||||
```bash
|
||||
kubectl get clusters -n rag
|
||||
# Expected output:
|
||||
# NAME CLUSTER-DEFINITION TERMINATION-POLICY STATUS AGE
|
||||
# neo4j-cluster Delete Running 39s
|
||||
# pg-cluster postgresql Delete Running 42s
|
||||
|
||||
kubectl get po -n rag
|
||||
# Expected output:
|
||||
# NAME READY STATUS RESTARTS AGE
|
||||
# neo4j-cluster-neo4j-0 1/1 Running 0 58s
|
||||
# pg-cluster-postgresql-0 4/4 Running 0 59s
|
||||
# pg-cluster-postgresql-1 4/4 Running 0 59s
|
||||
```
|
||||
|
||||
### 2. Install LightRAG
|
||||
|
||||
LightRAG and its databases are deployed within the same Kubernetes cluster, making configuration straightforward.
|
||||
The installation script automatically retrieves all database connection information from KubeBlocks, eliminating the need to manually set database credentials:
|
||||
|
||||
```bash
|
||||
export OPENAI_API_BASE=<YOUR_OPENAI_API_BASE>
|
||||
export OPENAI_API_KEY=<YOUR_OPENAI_API_KEY>
|
||||
bash ./install_lightrag.sh
|
||||
```
|
||||
|
||||
### Accessing the application:
|
||||
|
||||
```bash
|
||||
# 1. Run this port-forward command in your terminal:
|
||||
kubectl --namespace rag port-forward svc/lightrag 9621:9621
|
||||
|
||||
# 2. While the command is running, open your browser and navigate to:
|
||||
# http://localhost:9621
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
### Modifying Resource Configuration
|
||||
|
||||
You can configure LightRAG's resource usage by modifying the `values.yaml` file:
|
||||
|
||||
```yaml
|
||||
replicaCount: 1 # Number of replicas, can be increased as needed
|
||||
|
||||
resources:
|
||||
limits:
|
||||
cpu: 1000m # CPU limit, can be adjusted as needed
|
||||
memory: 2Gi # Memory limit, can be adjusted as needed
|
||||
requests:
|
||||
cpu: 500m # CPU request, can be adjusted as needed
|
||||
memory: 1Gi # Memory request, can be adjusted as needed
|
||||
```
|
||||
|
||||
### Modifying Persistent Storage
|
||||
|
||||
```yaml
|
||||
persistence:
|
||||
enabled: true
|
||||
ragStorage:
|
||||
size: 10Gi # RAG storage size, can be adjusted as needed
|
||||
inputs:
|
||||
size: 5Gi # Input data storage size, can be adjusted as needed
|
||||
```
|
||||
|
||||
### Configuring Environment Variables
|
||||
|
||||
The `env` section in the `values.yaml` file contains all environment configurations for LightRAG, similar to a `.env` file. When using helm upgrade or helm install commands, you can override these with the --set flag.
|
||||
|
||||
```yaml
|
||||
env:
|
||||
HOST: 0.0.0.0
|
||||
PORT: 9621
|
||||
WEBUI_TITLE: Graph RAG Engine
|
||||
WEBUI_DESCRIPTION: Simple and Fast Graph Based RAG System
|
||||
|
||||
# LLM Configuration
|
||||
LLM_BINDING: openai # LLM service provider
|
||||
LLM_MODEL: gpt-4o-mini # LLM model
|
||||
LLM_BINDING_HOST: # API base URL (optional)
|
||||
LLM_BINDING_API_KEY: # API key
|
||||
|
||||
# Embedding Configuration
|
||||
EMBEDDING_BINDING: openai # Embedding service provider
|
||||
EMBEDDING_MODEL: text-embedding-ada-002 # Embedding model
|
||||
EMBEDDING_DIM: 1536 # Embedding dimension
|
||||
EMBEDDING_BINDING_API_KEY: # API key
|
||||
|
||||
# Storage Configuration
|
||||
LIGHTRAG_KV_STORAGE: PGKVStorage # Key-value storage type
|
||||
LIGHTRAG_VECTOR_STORAGE: PGVectorStorage # Vector storage type
|
||||
LIGHTRAG_GRAPH_STORAGE: Neo4JStorage # Graph storage type
|
||||
LIGHTRAG_DOC_STATUS_STORAGE: PGDocStatusStorage # Document status storage type
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Ensure all necessary environment variables (API keys and database passwords) are set before deployment
|
||||
- For security reasons, it's recommended to pass sensitive information using environment variables rather than writing them directly in scripts or values files
|
||||
- Lightweight deployment is suitable for testing and small-scale usage, but data persistence and performance may be limited
|
||||
- Production deployment (PostgreSQL + Neo4J) is recommended for production environments and large-scale usage
|
||||
- For more customized configurations, please refer to the official LightRAG documentation
|
||||
@@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Get the directory where this script is located
|
||||
DATABASE_SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||
source "$DATABASE_SCRIPT_DIR/scripts/common.sh"
|
||||
|
||||
# Namespace configuration
|
||||
NAMESPACE="rag"
|
||||
# version
|
||||
KB_VERSION="1.0.0-beta.48"
|
||||
ADDON_CLUSTER_CHART_VERSION="1.0.0-alpha.0"
|
||||
# Helm repository
|
||||
HELM_REPO="https://apecloud.github.io/helm-charts"
|
||||
|
||||
# Set to true to enable the database, false to disable
|
||||
ENABLE_POSTGRESQL=true
|
||||
ENABLE_REDIS=false
|
||||
ENABLE_QDRANT=false
|
||||
ENABLE_NEO4J=true
|
||||
ENABLE_ELASTICSEARCH=false
|
||||
ENABLE_MONGODB=false
|
||||
@@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Get the directory where this script is located
|
||||
DATABASE_SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||
# Load configuration file
|
||||
source "$DATABASE_SCRIPT_DIR/00-config.sh"
|
||||
|
||||
check_dependencies
|
||||
|
||||
# Check if KubeBlocks is already installed, install it if it is not.
|
||||
source "$DATABASE_SCRIPT_DIR/install-kubeblocks.sh"
|
||||
|
||||
# Create namespaces
|
||||
print "Creating namespaces..."
|
||||
kubectl create namespace $NAMESPACE 2>/dev/null || true
|
||||
|
||||
# Install database addons
|
||||
print "Installing KubeBlocks database addons..."
|
||||
|
||||
# Add and update Helm repository
|
||||
print "Adding and updating KubeBlocks Helm repository..."
|
||||
helm repo add kubeblocks $HELM_REPO
|
||||
helm repo update
|
||||
# Install database addons based on configuration
|
||||
[ "$ENABLE_POSTGRESQL" = true ] && print "Installing PostgreSQL addon..." && helm upgrade --install kb-addon-postgresql kubeblocks/postgresql --namespace kb-system --version $ADDON_CLUSTER_CHART_VERSION
|
||||
[ "$ENABLE_REDIS" = true ] && print "Installing Redis addon..." && helm upgrade --install kb-addon-redis kubeblocks/redis --namespace kb-system --version $ADDON_CLUSTER_CHART_VERSION
|
||||
[ "$ENABLE_ELASTICSEARCH" = true ] && print "Installing Elasticsearch addon..." && helm upgrade --install kb-addon-elasticsearch kubeblocks/elasticsearch --namespace kb-system --version $ADDON_CLUSTER_CHART_VERSION
|
||||
[ "$ENABLE_QDRANT" = true ] && print "Installing Qdrant addon..." && helm upgrade --install kb-addon-qdrant kubeblocks/qdrant --namespace kb-system --version $ADDON_CLUSTER_CHART_VERSION
|
||||
[ "$ENABLE_MONGODB" = true ] && print "Installing MongoDB addon..." && helm upgrade --install kb-addon-mongodb kubeblocks/mongodb --namespace kb-system --version $ADDON_CLUSTER_CHART_VERSION
|
||||
[ "$ENABLE_NEO4J" = true ] && print "Installing Neo4j addon..." && helm upgrade --install kb-addon-neo4j kubeblocks/neo4j --namespace kb-system --version $ADDON_CLUSTER_CHART_VERSION
|
||||
|
||||
print_success "KubeBlocks database addons installation completed!"
|
||||
print "Now you can run 02-install-database.sh to install database clusters"
|
||||
@@ -0,0 +1,62 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Get the directory where this script is located
|
||||
DATABASE_SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||
|
||||
# Load configuration file
|
||||
source "$DATABASE_SCRIPT_DIR/00-config.sh"
|
||||
|
||||
print "Installing database clusters..."
|
||||
|
||||
# Install database clusters based on configuration
|
||||
[ "$ENABLE_POSTGRESQL" = true ] && print "Installing PostgreSQL cluster..." && helm upgrade --install pg-cluster kubeblocks/postgresql-cluster -f "$DATABASE_SCRIPT_DIR/postgresql/values.yaml" --namespace $NAMESPACE --version $ADDON_CLUSTER_CHART_VERSION
|
||||
[ "$ENABLE_REDIS" = true ] && print "Installing Redis cluster..." && helm upgrade --install redis-cluster kubeblocks/redis-cluster -f "$DATABASE_SCRIPT_DIR/redis/values.yaml" --namespace $NAMESPACE --version $ADDON_CLUSTER_CHART_VERSION
|
||||
[ "$ENABLE_ELASTICSEARCH" = true ] && print "Installing Elasticsearch cluster..." && helm upgrade --install es-cluster kubeblocks/elasticsearch-cluster -f "$DATABASE_SCRIPT_DIR/elasticsearch/values.yaml" --namespace $NAMESPACE --version $ADDON_CLUSTER_CHART_VERSION
|
||||
[ "$ENABLE_QDRANT" = true ] && print "Installing Qdrant cluster..." && helm upgrade --install qdrant-cluster kubeblocks/qdrant-cluster -f "$DATABASE_SCRIPT_DIR/qdrant/values.yaml" --namespace $NAMESPACE --version $ADDON_CLUSTER_CHART_VERSION
|
||||
[ "$ENABLE_MONGODB" = true ] && print "Installing MongoDB cluster..." && helm upgrade --install mongodb-cluster kubeblocks/mongodb-cluster -f "$DATABASE_SCRIPT_DIR/mongodb/values.yaml" --namespace $NAMESPACE --version $ADDON_CLUSTER_CHART_VERSION
|
||||
[ "$ENABLE_NEO4J" = true ] && print "Installing Neo4j cluster..." && helm upgrade --install neo4j-cluster kubeblocks/neo4j-cluster -f "$DATABASE_SCRIPT_DIR/neo4j/values.yaml" --namespace $NAMESPACE --version $ADDON_CLUSTER_CHART_VERSION
|
||||
|
||||
# Wait for databases to be ready
|
||||
print "Waiting for databases to be ready..."
|
||||
TIMEOUT=600 # Set timeout to 10 minutes
|
||||
START_TIME=$(date +%s)
|
||||
|
||||
while true; do
|
||||
CURRENT_TIME=$(date +%s)
|
||||
ELAPSED=$((CURRENT_TIME - START_TIME))
|
||||
|
||||
if [ $ELAPSED -gt $TIMEOUT ]; then
|
||||
print_error "Timeout waiting for databases to be ready. Please check database status manually and try again"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Build wait conditions for enabled databases
|
||||
WAIT_CONDITIONS=()
|
||||
[ "$ENABLE_POSTGRESQL" = true ] && WAIT_CONDITIONS+=("kubectl wait --for=condition=ready pods -l app.kubernetes.io/instance=pg-cluster -n $NAMESPACE --timeout=10s")
|
||||
[ "$ENABLE_REDIS" = true ] && WAIT_CONDITIONS+=("kubectl wait --for=condition=ready pods -l app.kubernetes.io/instance=redis-cluster -n $NAMESPACE --timeout=10s")
|
||||
[ "$ENABLE_ELASTICSEARCH" = true ] && WAIT_CONDITIONS+=("kubectl wait --for=condition=ready pods -l app.kubernetes.io/instance=es-cluster -n $NAMESPACE --timeout=10s")
|
||||
[ "$ENABLE_QDRANT" = true ] && WAIT_CONDITIONS+=("kubectl wait --for=condition=ready pods -l app.kubernetes.io/instance=qdrant-cluster -n $NAMESPACE --timeout=10s")
|
||||
[ "$ENABLE_MONGODB" = true ] && WAIT_CONDITIONS+=("kubectl wait --for=condition=ready pods -l app.kubernetes.io/instance=mongodb-cluster -n $NAMESPACE --timeout=10s")
|
||||
[ "$ENABLE_NEO4J" = true ] && WAIT_CONDITIONS+=("kubectl wait --for=condition=ready pods -l app.kubernetes.io/instance=neo4j-cluster -n $NAMESPACE --timeout=10s")
|
||||
|
||||
# Check if all enabled databases are ready
|
||||
ALL_READY=true
|
||||
for CONDITION in "${WAIT_CONDITIONS[@]}"; do
|
||||
if ! eval "$CONDITION &> /dev/null"; then
|
||||
ALL_READY=false
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$ALL_READY" = true ]; then
|
||||
print "All database pods are ready, continuing with deployment..."
|
||||
break
|
||||
fi
|
||||
|
||||
print "Waiting for database pods to be ready (${ELAPSED}s elapsed)..."
|
||||
sleep 10
|
||||
done
|
||||
|
||||
print_success "Database clusters installation completed!"
|
||||
print "Use the following command to check the status of installed clusters:"
|
||||
print "kubectl get clusters -n $NAMESPACE"
|
||||
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Get the directory where this script is located
|
||||
DATABASE_SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||
|
||||
# Load configuration file
|
||||
source "$DATABASE_SCRIPT_DIR/00-config.sh"
|
||||
|
||||
print "Uninstalling database clusters..."
|
||||
|
||||
# Uninstall database clusters based on configuration
|
||||
[ "$ENABLE_POSTGRESQL" = true ] && print "Uninstalling PostgreSQL cluster..." && helm uninstall pg-cluster --namespace $NAMESPACE 2>/dev/null || true
|
||||
[ "$ENABLE_REDIS" = true ] && print "Uninstalling Redis cluster..." && helm uninstall redis-cluster --namespace $NAMESPACE 2>/dev/null || true
|
||||
[ "$ENABLE_ELASTICSEARCH" = true ] && print "Uninstalling Elasticsearch cluster..." && helm uninstall es-cluster --namespace $NAMESPACE 2>/dev/null || true
|
||||
[ "$ENABLE_QDRANT" = true ] && print "Uninstalling Qdrant cluster..." && helm uninstall qdrant-cluster --namespace $NAMESPACE 2>/dev/null || true
|
||||
[ "$ENABLE_MONGODB" = true ] && print "Uninstalling MongoDB cluster..." && helm uninstall mongodb-cluster --namespace $NAMESPACE 2>/dev/null || true
|
||||
[ "$ENABLE_NEO4J" = true ] && print "Uninstalling Neo4j cluster..." && helm uninstall neo4j-cluster --namespace $NAMESPACE 2>/dev/null || true
|
||||
|
||||
print_success "Database clusters uninstalled"
|
||||
print "To uninstall database addons and KubeBlocks, run 04-cleanup.sh"
|
||||
@@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Get the directory where this script is located
|
||||
DATABASE_SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||
|
||||
# Load configuration file
|
||||
source "$DATABASE_SCRIPT_DIR/00-config.sh"
|
||||
|
||||
print "Uninstalling KubeBlocks database addons..."
|
||||
|
||||
# Uninstall database addons based on configuration
|
||||
[ "$ENABLE_POSTGRESQL" = true ] && print "Uninstalling PostgreSQL addon..." && helm uninstall kb-addon-postgresql --namespace kb-system 2>/dev/null || true
|
||||
[ "$ENABLE_REDIS" = true ] && print "Uninstalling Redis addon..." && helm uninstall kb-addon-redis --namespace kb-system 2>/dev/null || true
|
||||
[ "$ENABLE_ELASTICSEARCH" = true ] && print "Uninstalling Elasticsearch addon..." && helm uninstall kb-addon-elasticsearch --namespace kb-system 2>/dev/null || true
|
||||
[ "$ENABLE_QDRANT" = true ] && print "Uninstalling Qdrant addon..." && helm uninstall kb-addon-qdrant --namespace kb-system 2>/dev/null || true
|
||||
[ "$ENABLE_MONGODB" = true ] && print "Uninstalling MongoDB addon..." && helm uninstall kb-addon-mongodb --namespace kb-system 2>/dev/null || true
|
||||
[ "$ENABLE_NEO4J" = true ] && print "Uninstalling Neo4j addon..." && helm uninstall kb-addon-neo4j --namespace kb-system 2>/dev/null || true
|
||||
|
||||
print_success "Database addons uninstallation completed!"
|
||||
|
||||
source "$DATABASE_SCRIPT_DIR/uninstall-kubeblocks.sh"
|
||||
|
||||
kubectl delete namespace $NAMESPACE
|
||||
kubectl delete namespace kb-system
|
||||
|
||||
print_success "KubeBlocks uninstallation completed!"
|
||||
@@ -0,0 +1,170 @@
|
||||
# Using KubeBlocks to Deploy and Manage Databases
|
||||
|
||||
Learn how to quickly deploy and manage various databases in a Kubernetes (K8s) environment through KubeBlocks.
|
||||
|
||||
## Introduction to KubeBlocks
|
||||
|
||||
KubeBlocks is a production-ready, open-source toolkit that runs any database--SQL, NoSQL, vector, or document--on Kubernetes.
|
||||
It scales smoothly from quick dev tests to full production clusters, making it a solid choice for RAG workloads like FastGPT that need several data stores working together.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Make sure the following tools are installed and configured:
|
||||
|
||||
* **Kubernetes cluster**
|
||||
* A running Kubernetes cluster is required.
|
||||
* For local development or demos you can use [Minikube](https://minikube.sigs.k8s.io/docs/start/) (needs ≥ 2 CPUs, ≥ 4 GB RAM, and Docker/VM-driver support).
|
||||
* Any standard cloud or on-premises Kubernetes cluster (EKS, GKE, AKS, etc.) also works.
|
||||
|
||||
* **kubectl**
|
||||
* The Kubernetes command-line interface.
|
||||
* Follow the official guide: [Install and Set Up kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl).
|
||||
|
||||
* **Helm** (v3.x+)
|
||||
* Kubernetes package manager used by the scripts below.
|
||||
* Install it via the official instructions: [Installing Helm](https://helm.sh/docs/intro/install/).
|
||||
|
||||
## Installing
|
||||
|
||||
1. **Configure the databases you want**
|
||||
Edit `00-config.sh` file. Based on your requirements, set the variable to `true` for the databases you want to install.
|
||||
For example, to install PostgreSQL and Neo4j:
|
||||
|
||||
```bash
|
||||
ENABLE_POSTGRESQL=true
|
||||
ENABLE_REDIS=false
|
||||
ENABLE_ELASTICSEARCH=false
|
||||
ENABLE_QDRANT=false
|
||||
ENABLE_MONGODB=false
|
||||
ENABLE_NEO4J=true
|
||||
```
|
||||
|
||||
2. **Prepare the environment and install KubeBlocks add-ons**
|
||||
|
||||
```bash
|
||||
bash ./01-prepare.sh
|
||||
```
|
||||
|
||||
*What the script does*
|
||||
`01-prepare.sh` performs basic pre-checks (Helm, kubectl, cluster reachability), adds the KubeBlocks Helm repo, and installs any core CRDs or controllers that KubeBlocks itself needs. It also installs the addons for every database you enabled in `00-config.sh`, but **does not** create the actual database clusters yet.
|
||||
|
||||
3. **(Optional) Modify database settings**
|
||||
Before deployment you can edit the `values.yaml` file inside each `<db>/` directory to change `version`, `replicas`, `CPU`, `memory`, `storage size`, etc.
|
||||
|
||||
4. **Install the database clusters**
|
||||
|
||||
```bash
|
||||
bash ./02-install-database.sh
|
||||
```
|
||||
|
||||
*What the script does*
|
||||
`02-install-database.sh` **actually deploys the chosen databases to Kubernetes**.
|
||||
|
||||
When the script completes, confirm that the clusters are up. It may take a few minutes for all the clusters to become ready,
|
||||
especially if this is the first time running the script as Kubernetes needs to pull container images from registries.
|
||||
You can monitor the progress using the following commands:
|
||||
|
||||
```bash
|
||||
kubectl get clusters -n rag
|
||||
NAME CLUSTER-DEFINITION TERMINATION-POLICY STATUS AGE
|
||||
es-cluster Delete Running 11m
|
||||
mongodb-cluster mongodb Delete Running 11m
|
||||
pg-cluster postgresql Delete Running 11m
|
||||
qdrant-cluster qdrant Delete Running 11m
|
||||
redis-cluster redis Delete Running 11m
|
||||
```
|
||||
|
||||
You can see all the Database `Pods` created by KubeBlocks.
|
||||
Initially, you might see pods in `ContainerCreating` or `Pending` status - this is normal while images are being pulled and containers are starting up.
|
||||
Wait until all pods show `Running` status:
|
||||
|
||||
```bash
|
||||
kubectl get po -n rag
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
es-cluster-mdit-0 2/2 Running 0 11m
|
||||
mongodb-cluster-mongodb-0 2/2 Running 0 11m
|
||||
pg-cluster-postgresql-0 4/4 Running 0 11m
|
||||
pg-cluster-postgresql-1 4/4 Running 0 11m
|
||||
qdrant-cluster-qdrant-0 2/2 Running 0 11m
|
||||
redis-cluster-redis-0 2/2 Running 0 11m
|
||||
```
|
||||
|
||||
You can also check the detailed status of a specific pod if it's taking longer than expected:
|
||||
|
||||
```bash
|
||||
kubectl describe pod <pod-name> -n rag
|
||||
```
|
||||
|
||||
## Connect to Databases
|
||||
|
||||
To connect to your databases, follow these steps to identify available accounts, retrieve credentials, and establish connections:
|
||||
|
||||
### 1. List Available Database Clusters
|
||||
|
||||
First, view the database clusters running in your namespace:
|
||||
|
||||
```bash
|
||||
kubectl get cluster -n rag
|
||||
```
|
||||
|
||||
### 2. Retrieve Authentication Credentials
|
||||
|
||||
For PostgreSQL, retrieve the username and password from Kubernetes secrets:
|
||||
|
||||
```bash
|
||||
# Get PostgreSQL username
|
||||
kubectl get secrets -n rag pg-cluster-postgresql-account-postgres -o jsonpath='{.data.username}' | base64 -d
|
||||
# Get PostgreSQL password
|
||||
kubectl get secrets -n rag pg-cluster-postgresql-account-postgres -o jsonpath='{.data.password}' | base64 -d
|
||||
```
|
||||
|
||||
If you have trouble finding the correct secret name, list all secrets:
|
||||
|
||||
```bash
|
||||
kubectl get secrets -n rag
|
||||
```
|
||||
|
||||
### 3. Port Forward to Local Machine
|
||||
|
||||
Use port forwarding to access PostgreSQL from your local machine:
|
||||
|
||||
```bash
|
||||
# Forward PostgreSQL port (5432) to your local machine
|
||||
# You can see all services with: kubectl get svc -n rag
|
||||
kubectl port-forward -n rag svc/pg-cluster-postgresql-postgresql 5432:5432
|
||||
```
|
||||
|
||||
### 4. Connect Using Database Client
|
||||
|
||||
Now you can connect using your preferred PostgreSQL client with the retrieved credentials:
|
||||
|
||||
```bash
|
||||
# Example: connecting with psql
|
||||
export PGUSER=$(kubectl get secrets -n rag pg-cluster-postgresql-account-postgres -o jsonpath='{.data.username}' | base64 -d)
|
||||
export PGPASSWORD=$(kubectl get secrets -n rag pg-cluster-postgresql-account-postgres -o jsonpath='{.data.password}' | base64 -d)
|
||||
psql -h localhost -p 5432 -U $PGUSER
|
||||
```
|
||||
|
||||
Keep the port-forwarding terminal running while you're connecting to the database.
|
||||
|
||||
|
||||
## Uninstalling
|
||||
|
||||
1. **Remove the database clusters**
|
||||
|
||||
```bash
|
||||
bash ./03-uninstall-database.sh
|
||||
```
|
||||
|
||||
The script deletes the database clusters that were enabled in `00-config.sh`.
|
||||
|
||||
2. **Clean up KubeBlocks add-ons**
|
||||
|
||||
```bash
|
||||
bash ./04-cleanup.sh
|
||||
```
|
||||
|
||||
This removes the addons installed by `01-prepare.sh`.
|
||||
|
||||
## Reference
|
||||
* [Kubeblocks Documentation](https://kubeblocks.io/docs/preview/user_docs/overview/introduction)
|
||||
@@ -0,0 +1,36 @@
|
||||
## description: The version of ElasticSearch.
|
||||
## default: 8.8.2
|
||||
version: "8.8.2"
|
||||
|
||||
## description: Mode for ElasticSearch
|
||||
## default: multi-node
|
||||
## one of: [single-node, multi-node]
|
||||
mode: single-node
|
||||
|
||||
## description: The number of replicas, for single-node mode, the replicas is 1, for multi-node mode, the default replicas is 3.
|
||||
## default: 1
|
||||
## minimum: 1
|
||||
## maximum: 5
|
||||
replicas: 1
|
||||
|
||||
## description: CPU cores.
|
||||
## default: 1
|
||||
## minimum: 0.5
|
||||
## maximum: 64
|
||||
cpu: 1
|
||||
|
||||
## description: Memory, the unit is Gi.
|
||||
## default: 2
|
||||
## minimum: 1
|
||||
## maximum: 1000
|
||||
memory: 2
|
||||
|
||||
## description: Storage size, the unit is Gi.
|
||||
## default: 20
|
||||
## minimum: 1
|
||||
## maximum: 10000
|
||||
storage: 5
|
||||
|
||||
extra:
|
||||
terminationPolicy: Delete
|
||||
disableExporter: true
|
||||
@@ -0,0 +1,52 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Get the directory where this script is located
|
||||
DATABASE_SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||
# Load configuration file
|
||||
source "$DATABASE_SCRIPT_DIR/00-config.sh"
|
||||
|
||||
# Check dependencies
|
||||
check_dependencies
|
||||
|
||||
# Function for installing KubeBlocks
|
||||
install_kubeblocks() {
|
||||
print "Ready to install KubeBlocks."
|
||||
|
||||
# Install CSI Snapshotter CRDs
|
||||
kubectl create -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/v8.2.0/client/config/crd/snapshot.storage.k8s.io_volumesnapshotclasses.yaml
|
||||
kubectl create -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/v8.2.0/client/config/crd/snapshot.storage.k8s.io_volumesnapshots.yaml
|
||||
kubectl create -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/v8.2.0/client/config/crd/snapshot.storage.k8s.io_volumesnapshotcontents.yaml
|
||||
|
||||
# Add and update Piraeus repository
|
||||
helm repo add piraeus-charts https://piraeus.io/helm-charts/
|
||||
helm repo update
|
||||
|
||||
# Install snapshot controller
|
||||
helm install snapshot-controller piraeus-charts/snapshot-controller -n kb-system --create-namespace
|
||||
kubectl wait --for=condition=ready pods -l app.kubernetes.io/name=snapshot-controller -n kb-system --timeout=60s
|
||||
print_success "snapshot-controller installation complete!"
|
||||
|
||||
# Install KubeBlocks CRDs
|
||||
kubectl create -f https://github.com/apecloud/kubeblocks/releases/download/v${KB_VERSION}/kubeblocks_crds.yaml
|
||||
|
||||
# Add and update KubeBlocks repository
|
||||
helm repo add kubeblocks $HELM_REPO
|
||||
helm repo update
|
||||
|
||||
# Install KubeBlocks
|
||||
helm install kubeblocks kubeblocks/kubeblocks --namespace kb-system --create-namespace --version=${KB_VERSION}
|
||||
|
||||
# Verify installation
|
||||
print "Waiting for KubeBlocks to be ready..."
|
||||
kubectl wait --for=condition=ready pods -l app.kubernetes.io/instance=kubeblocks -n kb-system --timeout=120s
|
||||
print_success "KubeBlocks installation complete!"
|
||||
}
|
||||
|
||||
# Check if KubeBlocks is already installed
|
||||
print "Checking if KubeBlocks is already installed in kb-system namespace..."
|
||||
if kubectl get namespace kb-system &>/dev/null && kubectl get deployment kubeblocks -n kb-system &>/dev/null; then
|
||||
print_success "KubeBlocks is already installed in kb-system namespace."
|
||||
else
|
||||
# Call the function to install KubeBlocks
|
||||
install_kubeblocks
|
||||
fi
|
||||
@@ -0,0 +1,34 @@
|
||||
## description: Cluster version.
|
||||
## default: 6.0.16
|
||||
## one of: [8.0.8, 8.0.6, 8.0.4, 7.0.19, 7.0.16, 7.0.12, 6.0.22, 6.0.20, 6.0.16, 5.0.30, 5.0.28, 4.4.29, 4.2.24, 4.0.28]
|
||||
version: 6.0.16
|
||||
|
||||
## description: Cluster topology mode.
|
||||
## default: standalone
|
||||
## one of: [standalone, replicaset]
|
||||
mode: standalone
|
||||
|
||||
## description: CPU cores.
|
||||
## default: 0.5
|
||||
## minimum: 0.5
|
||||
## maximum: 64
|
||||
cpu: 1
|
||||
|
||||
## description: Memory, the unit is Gi.
|
||||
## default: 0.5
|
||||
## minimum: 0.5
|
||||
## maximum: 1000
|
||||
memory: 1
|
||||
|
||||
## description: Storage size, the unit is Gi.
|
||||
## default: 20
|
||||
## minimum: 1
|
||||
## maximum: 10000
|
||||
storage: 20
|
||||
|
||||
## default: enabled
|
||||
## one of: [enabled, disabled]
|
||||
hostnetwork: "disabled"
|
||||
|
||||
extra:
|
||||
terminationPolicy: Delete
|
||||
@@ -0,0 +1,46 @@
|
||||
# Version
|
||||
# description: Cluster version.
|
||||
# default: 5.26.5
|
||||
# one of: [5.26.5, 4.4.42]
|
||||
version: 5.26.5
|
||||
|
||||
# Mode
|
||||
# description: Cluster topology mode.
|
||||
# default: singlealone
|
||||
# one of: [singlealone]
|
||||
mode: singlealone
|
||||
|
||||
# CPU
|
||||
# description: CPU cores.
|
||||
# default: 2
|
||||
# minimum: 2
|
||||
# maximum: 64
|
||||
cpu: 2
|
||||
|
||||
# Memory(Gi)
|
||||
# description: Memory, the unit is Gi.
|
||||
# default: 2
|
||||
# minimum: 2
|
||||
# maximum: 1000
|
||||
memory: 4
|
||||
|
||||
# Storage(Gi)
|
||||
# description: Storage size, the unit is Gi.
|
||||
# default: 20
|
||||
# minimum: 1
|
||||
# maximum: 10000
|
||||
storage: 20
|
||||
|
||||
# Replicas
|
||||
# description: The number of replicas, for standalone mode, the replicas is 1, for replicaset mode, the default replicas is 3.
|
||||
# default: 1
|
||||
# minimum: 1
|
||||
# maximum: 5
|
||||
replicas: 1
|
||||
|
||||
# Storage Class Name
|
||||
# description: Storage class name of the data volume
|
||||
storageClassName: ""
|
||||
|
||||
extra:
|
||||
terminationPolicy: Delete
|
||||
@@ -0,0 +1,33 @@
|
||||
## description: service version.
|
||||
## default: 15.7.0
|
||||
version: 16.4.0
|
||||
|
||||
## mode postgresql cluster topology mode replication
|
||||
mode: replication
|
||||
|
||||
## description: The number of replicas, for standalone mode, the replicas is 1, for replication mode, the default replicas is 2.
|
||||
## default: 1
|
||||
## minimum: 1
|
||||
## maximum: 5
|
||||
replicas: 2
|
||||
|
||||
## description: CPU cores.
|
||||
## default: 0.5
|
||||
## minimum: 0.5
|
||||
## maximum: 64
|
||||
cpu: 1
|
||||
|
||||
## description: Memory, the unit is Gi.
|
||||
## default: 0.5
|
||||
## minimum: 0.5
|
||||
## maximum: 1000
|
||||
memory: 1
|
||||
|
||||
## description: Storage size, the unit is Gi.
|
||||
## default: 20
|
||||
## minimum: 1
|
||||
## maximum: 10000
|
||||
storage: 5
|
||||
|
||||
## terminationPolicy define Cluster termination policy. One of DoNotTerminate, Delete, WipeOut.
|
||||
terminationPolicy: Delete
|
||||
@@ -0,0 +1,31 @@
|
||||
## description: The version of Qdrant.
|
||||
## default: 1.10.0
|
||||
version: 1.10.0
|
||||
|
||||
## description: The number of replicas.
|
||||
## default: 1
|
||||
## minimum: 1
|
||||
## maximum: 16
|
||||
replicas: 1
|
||||
|
||||
## description: CPU cores.
|
||||
## default: 1
|
||||
## minimum: 0.5
|
||||
## maximum: 64
|
||||
cpu: 1
|
||||
|
||||
## description: Memory, the unit is Gi.
|
||||
## default: 2
|
||||
## minimum: 0.5
|
||||
## maximum: 1000
|
||||
memory: 1
|
||||
|
||||
## description: Storage size, the unit is Gi.
|
||||
## default: 20
|
||||
## minimum: 1
|
||||
## maximum: 10000
|
||||
storage: 20
|
||||
|
||||
## customized default values to override kblib chart's values
|
||||
extra:
|
||||
terminationPolicy: Delete
|
||||
@@ -0,0 +1,34 @@
|
||||
## description: Cluster version.
|
||||
## default: 7.2.7
|
||||
version: 7.2.7
|
||||
|
||||
## description: Cluster topology mode.
|
||||
## default: replication
|
||||
## one of: [standalone, replication, cluster, replication-twemproxy]
|
||||
mode: standalone
|
||||
|
||||
## description: The number of replicas, for standalone mode, the replicas is 1, for replication mode, the default replicas is 2.
|
||||
## default: 1
|
||||
## minimum: 1
|
||||
## maximum: 5
|
||||
replicas: 1
|
||||
|
||||
## description: CPU cores.
|
||||
## default: 0.5
|
||||
## minimum: 0.5
|
||||
## maximum: 64
|
||||
cpu: 0.5
|
||||
|
||||
## description: Memory, the unit is Gi.
|
||||
## default: 0.5
|
||||
## minimum: 0.5
|
||||
## maximum: 1000
|
||||
memory: 1
|
||||
|
||||
## description: Storage size, the unit is Gi.
|
||||
## default: 20
|
||||
## minimum: 1
|
||||
## maximum: 10000
|
||||
storage: 20
|
||||
extra:
|
||||
disableExporter: true
|
||||
@@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
|
||||
print_title() {
|
||||
echo "============================================"
|
||||
echo "$1"
|
||||
echo "============================================"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo "✅ $1"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo "❌ $1"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo "⚠️ $1"
|
||||
}
|
||||
|
||||
print_info() {
|
||||
echo "🔹 $1"
|
||||
}
|
||||
|
||||
print() {
|
||||
echo "$1"
|
||||
}
|
||||
|
||||
# Check dependencies
|
||||
check_dependencies(){
|
||||
print "Checking dependencies..."
|
||||
command -v kubectl >/dev/null 2>&1 || { print "Error: kubectl command not found"; exit 1; }
|
||||
command -v helm >/dev/null 2>&1 || { print "Error: helm command not found"; exit 1; }
|
||||
|
||||
# Check if Kubernetes is available
|
||||
print "Checking if Kubernetes is available..."
|
||||
kubectl cluster-info &>/dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
print "Error: Kubernetes cluster is not accessible. Please ensure you have proper access to a Kubernetes cluster."
|
||||
exit 1
|
||||
fi
|
||||
print_success "Kubernetes cluster is accessible."
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Get the directory where this script is located
|
||||
DATABASE_SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||
# Load configuration file
|
||||
source "$DATABASE_SCRIPT_DIR/00-config.sh"
|
||||
|
||||
# Check dependencies
|
||||
print "Checking dependencies..."
|
||||
command -v kubectl >/dev/null 2>&1 || { print "Error: kubectl command not found"; exit 1; }
|
||||
command -v helm >/dev/null 2>&1 || { print "Error: helm command not found"; exit 1; }
|
||||
|
||||
print "Checking if Kubernetes is available..."
|
||||
if ! kubectl cluster-info &>/dev/null; then
|
||||
print "Error: Kubernetes cluster is not accessible. Please ensure you have proper access to a Kubernetes cluster."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print "Checking if KubeBlocks is installed in kb-system namespace..."
|
||||
if ! kubectl get namespace kb-system &>/dev/null; then
|
||||
print "KubeBlocks is not installed in kb-system namespace."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Function for uninstalling KubeBlocks
|
||||
uninstall_kubeblocks() {
|
||||
print "Uninstalling KubeBlocks..."
|
||||
|
||||
# Uninstall KubeBlocks Helm chart
|
||||
helm uninstall kubeblocks -n kb-system
|
||||
|
||||
# Uninstall snapshot controller
|
||||
helm uninstall snapshot-controller -n kb-system
|
||||
|
||||
# Delete KubeBlocks CRDs
|
||||
kubectl delete -f https://github.com/apecloud/kubeblocks/releases/download/v${KB_VERSION}/kubeblocks_crds.yaml --ignore-not-found=true
|
||||
|
||||
# Delete CSI Snapshotter CRDs
|
||||
kubectl delete -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/v8.2.0/client/config/crd/snapshot.storage.k8s.io_volumesnapshotclasses.yaml --ignore-not-found=true
|
||||
kubectl delete -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/v8.2.0/client/config/crd/snapshot.storage.k8s.io_volumesnapshots.yaml --ignore-not-found=true
|
||||
kubectl delete -f https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/v8.2.0/client/config/crd/snapshot.storage.k8s.io_volumesnapshotcontents.yaml --ignore-not-found=true
|
||||
|
||||
# Delete the kb-system namespace
|
||||
print "Waiting for resources to be removed..."
|
||||
kubectl delete namespace kb-system --timeout=180s
|
||||
|
||||
print "KubeBlocks has been successfully uninstalled!"
|
||||
}
|
||||
|
||||
# Call the function to uninstall KubeBlocks
|
||||
uninstall_kubeblocks
|
||||
@@ -0,0 +1,95 @@
|
||||
#!/bin/bash
|
||||
|
||||
NAMESPACE=rag
|
||||
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||
|
||||
if [ -z "$OPENAI_API_KEY" ]; then
|
||||
echo "OPENAI_API_KEY environment variable is not set"
|
||||
read -s -p "Enter your OpenAI API key: " OPENAI_API_KEY
|
||||
if [ -z "$OPENAI_API_KEY" ]; then
|
||||
echo "Error: OPENAI_API_KEY must be provided"
|
||||
exit 1
|
||||
fi
|
||||
export OPENAI_API_KEY=$OPENAI_API_KEY
|
||||
fi
|
||||
|
||||
if [ -z "$OPENAI_API_BASE" ]; then
|
||||
echo "OPENAI_API_BASE environment variable is not set, will use default value"
|
||||
read -p "Enter OpenAI API base URL (press Enter to skip if not needed): " OPENAI_API_BASE
|
||||
export OPENAI_API_BASE=$OPENAI_API_BASE
|
||||
fi
|
||||
|
||||
# Install KubeBlocks (if not already installed)
|
||||
bash "$SCRIPT_DIR/databases/01-prepare.sh"
|
||||
|
||||
# Install database clusters
|
||||
bash "$SCRIPT_DIR/databases/02-install-database.sh"
|
||||
|
||||
# Create vector extension in PostgreSQL if enabled
|
||||
print "Waiting for PostgreSQL pods to be ready..."
|
||||
if kubectl wait --for=condition=ready pods -l kubeblocks.io/role=primary,app.kubernetes.io/instance=pg-cluster -n $NAMESPACE --timeout=300s; then
|
||||
print "Creating vector extension in PostgreSQL..."
|
||||
kubectl exec -it $(kubectl get pods -l kubeblocks.io/role=primary,app.kubernetes.io/instance=pg-cluster -n $NAMESPACE -o name) -n $NAMESPACE -- psql -c "CREATE EXTENSION vector;"
|
||||
print_success "Vector extension created successfully."
|
||||
else
|
||||
print "Warning: PostgreSQL pods not ready within timeout. Vector extension not created."
|
||||
fi
|
||||
|
||||
# Get database passwords from Kubernetes secrets
|
||||
echo "Retrieving database credentials from Kubernetes secrets..."
|
||||
POSTGRES_PASSWORD=$(kubectl get secrets -n rag pg-cluster-postgresql-account-postgres -o jsonpath='{.data.password}' | base64 -d)
|
||||
if [ -z "$POSTGRES_PASSWORD" ]; then
|
||||
echo "Error: Could not retrieve PostgreSQL password. Make sure PostgreSQL is deployed and the secret exists."
|
||||
exit 1
|
||||
fi
|
||||
export POSTGRES_PASSWORD=$POSTGRES_PASSWORD
|
||||
|
||||
NEO4J_PASSWORD=$(kubectl get secrets -n rag neo4j-cluster-neo4j-account-neo4j -o jsonpath='{.data.password}' | base64 -d)
|
||||
if [ -z "$NEO4J_PASSWORD" ]; then
|
||||
echo "Error: Could not retrieve Neo4J password. Make sure Neo4J is deployed and the secret exists."
|
||||
exit 1
|
||||
fi
|
||||
export NEO4J_PASSWORD=$NEO4J_PASSWORD
|
||||
|
||||
#REDIS_PASSWORD=$(kubectl get secrets -n rag redis-cluster-redis-account-default -o jsonpath='{.data.password}' | base64 -d)
|
||||
#if [ -z "$REDIS_PASSWORD" ]; then
|
||||
# echo "Error: Could not retrieve Redis password. Make sure Redis is deployed and the secret exists."
|
||||
# exit 1
|
||||
#fi
|
||||
#export REDIS_PASSWORD=$REDIS_PASSWORD
|
||||
|
||||
echo "Deploying production LightRAG (using external databases)..."
|
||||
|
||||
if ! kubectl get namespace rag &> /dev/null; then
|
||||
echo "creating namespace 'rag'..."
|
||||
kubectl create namespace rag
|
||||
fi
|
||||
|
||||
helm upgrade --install lightrag $SCRIPT_DIR/lightrag \
|
||||
--namespace $NAMESPACE \
|
||||
--set-string env.POSTGRES_PASSWORD=$POSTGRES_PASSWORD \
|
||||
--set-string env.NEO4J_PASSWORD=$NEO4J_PASSWORD \
|
||||
--set-string env.LLM_BINDING=openai \
|
||||
--set-string env.LLM_MODEL=gpt-4o-mini \
|
||||
--set-string env.LLM_BINDING_HOST=$OPENAI_API_BASE \
|
||||
--set-string env.LLM_BINDING_API_KEY=$OPENAI_API_KEY \
|
||||
--set-string env.EMBEDDING_BINDING=openai \
|
||||
--set-string env.EMBEDDING_MODEL=text-embedding-ada-002 \
|
||||
--set-string env.EMBEDDING_DIM=1536 \
|
||||
--set-string env.EMBEDDING_BINDING_API_KEY=$OPENAI_API_KEY
|
||||
# --set-string env.REDIS_URI="redis://default:${REDIS_PASSWORD}@redis-cluster-redis-redis:6379"
|
||||
|
||||
# Wait for LightRAG pod to be ready
|
||||
echo ""
|
||||
echo "Waiting for lightrag pod to be ready..."
|
||||
kubectl wait --for=condition=ready pod -l app.kubernetes.io/instance=lightrag --timeout=300s -n rag
|
||||
echo "lightrag pod is ready"
|
||||
echo ""
|
||||
echo "Running Port-Forward:"
|
||||
echo " kubectl --namespace rag port-forward svc/lightrag 9621:9621"
|
||||
echo "==========================================="
|
||||
echo ""
|
||||
echo "✅ You can visit LightRAG at: http://localhost:9621"
|
||||
echo ""
|
||||
kubectl --namespace rag port-forward svc/lightrag 9621:9621
|
||||
@@ -0,0 +1,81 @@
|
||||
#!/bin/bash
|
||||
|
||||
NAMESPACE=rag
|
||||
|
||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||||
|
||||
check_dependencies(){
|
||||
echo "Checking dependencies..."
|
||||
command -v kubectl >/dev/null 2>&1 || { echo "Error: kubectl command not found"; exit 1; }
|
||||
command -v helm >/dev/null 2>&1 || { echo "Error: helm command not found"; exit 1; }
|
||||
|
||||
# Check if Kubernetes is available
|
||||
echo "Checking if Kubernetes is available..."
|
||||
kubectl cluster-info &>/dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error: Kubernetes cluster is not accessible. Please ensure you have proper access to a Kubernetes cluster."
|
||||
exit 1
|
||||
fi
|
||||
echo "Kubernetes cluster is accessible."
|
||||
}
|
||||
|
||||
check_dependencies
|
||||
|
||||
if [ -z "$OPENAI_API_KEY" ]; then
|
||||
echo "OPENAI_API_KEY environment variable is not set"
|
||||
read -s -p "Enter your OpenAI API key: " OPENAI_API_KEY
|
||||
if [ -z "$OPENAI_API_KEY" ]; then
|
||||
echo "Error: OPENAI_API_KEY must be provided"
|
||||
exit 1
|
||||
fi
|
||||
export OPENAI_API_KEY=$OPENAI_API_KEY
|
||||
fi
|
||||
|
||||
if [ -z "$OPENAI_API_BASE" ]; then
|
||||
echo "OPENAI_API_BASE environment variable is not set, will use default value"
|
||||
read -p "Enter OpenAI API base URL (press Enter to skip if not needed): " OPENAI_API_BASE
|
||||
export OPENAI_API_BASE=$OPENAI_API_BASE
|
||||
fi
|
||||
|
||||
required_env_vars=("OPENAI_API_BASE" "OPENAI_API_KEY")
|
||||
|
||||
for var in "${required_env_vars[@]}"; do
|
||||
if [ -z "${!var}" ]; then
|
||||
echo "Error: $var environment variable is not set"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
if ! kubectl get namespace rag &> /dev/null; then
|
||||
echo "creating namespace 'rag'..."
|
||||
kubectl create namespace rag
|
||||
fi
|
||||
|
||||
helm upgrade --install lightrag-dev $SCRIPT_DIR/lightrag \
|
||||
--namespace rag \
|
||||
--set-string env.LIGHTRAG_KV_STORAGE=JsonKVStorage \
|
||||
--set-string env.LIGHTRAG_VECTOR_STORAGE=NanoVectorDBStorage \
|
||||
--set-string env.LIGHTRAG_GRAPH_STORAGE=NetworkXStorage \
|
||||
--set-string env.LIGHTRAG_DOC_STATUS_STORAGE=JsonDocStatusStorage \
|
||||
--set-string env.LLM_BINDING=openai \
|
||||
--set-string env.LLM_MODEL=gpt-4o-mini \
|
||||
--set-string env.LLM_BINDING_HOST=$OPENAI_API_BASE \
|
||||
--set-string env.LLM_BINDING_API_KEY=$OPENAI_API_KEY \
|
||||
--set-string env.EMBEDDING_BINDING=openai \
|
||||
--set-string env.EMBEDDING_MODEL=text-embedding-ada-002 \
|
||||
--set-string env.EMBEDDING_DIM=1536 \
|
||||
--set-string env.EMBEDDING_BINDING_API_KEY=$OPENAI_API_KEY
|
||||
|
||||
# Wait for LightRAG pod to be ready
|
||||
echo ""
|
||||
echo "Waiting for lightrag-dev pod to be ready..."
|
||||
kubectl wait --for=condition=ready pod -l app.kubernetes.io/instance=lightrag-dev --timeout=300s -n rag
|
||||
echo "lightrag-dev pod is ready"
|
||||
echo ""
|
||||
echo "Running Port-Forward:"
|
||||
echo " kubectl --namespace rag port-forward svc/lightrag-dev 9621:9621"
|
||||
echo "==========================================="
|
||||
echo ""
|
||||
echo "✅ You can visit LightRAG at: http://localhost:9621"
|
||||
echo ""
|
||||
kubectl --namespace rag port-forward svc/lightrag-dev 9621:9621
|
||||
@@ -0,0 +1,23 @@
|
||||
# Patterns to ignore when building packages.
|
||||
# This supports shell glob matching, relative path matching, and
|
||||
# negation (prefixed with !). Only one pattern per line.
|
||||
.DS_Store
|
||||
# Common VCS dirs
|
||||
.git/
|
||||
.gitignore
|
||||
.bzr/
|
||||
.bzrignore
|
||||
.hg/
|
||||
.hgignore
|
||||
.svn/
|
||||
# Common backup files
|
||||
*.swp
|
||||
*.bak
|
||||
*.tmp
|
||||
*.orig
|
||||
*~
|
||||
# Various IDEs
|
||||
.project
|
||||
.idea/
|
||||
*.tmproj
|
||||
.vscode/
|
||||
@@ -0,0 +1,10 @@
|
||||
apiVersion: v2
|
||||
name: lightrag
|
||||
description: A Helm chart for LightRAG, an efficient and lightweight RAG system
|
||||
type: application
|
||||
version: 0.1.1
|
||||
appVersion: "1.0.0"
|
||||
maintainers:
|
||||
- name: LightRAG Team
|
||||
- name: earayu
|
||||
email: earayu@gmail.com
|
||||
@@ -0,0 +1,38 @@
|
||||
===========================================
|
||||
LightRAG has been successfully deployed!
|
||||
===========================================
|
||||
|
||||
View application logs:
|
||||
kubectl logs -f --namespace {{ .Release.Namespace }} deploy/{{ include "lightrag.fullname" . }}
|
||||
|
||||
===========================================
|
||||
|
||||
Access the application:
|
||||
{{- if contains "NodePort" .Values.service.type }}
|
||||
Run these commands to get access information:
|
||||
-----------------------------------------
|
||||
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "lightrag.fullname" . }})
|
||||
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
|
||||
echo "LightRAG is accessible at: http://$NODE_IP:$NODE_PORT"
|
||||
-----------------------------------------
|
||||
{{- else if contains "LoadBalancer" .Values.service.type }}
|
||||
Run these commands to get access information (external IP may take a minute to assign):
|
||||
-----------------------------------------
|
||||
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "lightrag.fullname" . }} --template "{{ "{{ range (index .status.loadBalancer.ingress 0) }}{{ . }}{{ end }}" }}")
|
||||
echo "LightRAG is accessible at: http://$SERVICE_IP:{{ .Values.service.port }}"
|
||||
-----------------------------------------
|
||||
If SERVICE_IP is empty, retry the command or check service status with:
|
||||
kubectl get svc --namespace {{ .Release.Namespace }} {{ include "lightrag.fullname" . }}
|
||||
{{- else if contains "ClusterIP" .Values.service.type }}
|
||||
For development environments, to access LightRAG from your local machine:
|
||||
|
||||
1. Run this port-forward command in your terminal:
|
||||
kubectl --namespace {{ .Release.Namespace }} port-forward svc/{{ include "lightrag.fullname" . }} {{ .Values.service.port }}:{{ .Values.env.PORT }}
|
||||
|
||||
2. While the command is running, open your browser and navigate to:
|
||||
http://localhost:{{ .Values.service.port }}
|
||||
|
||||
Note: To stop port-forwarding, press Ctrl+C in the terminal.
|
||||
{{- end }}
|
||||
|
||||
===========================================
|
||||
@@ -0,0 +1,42 @@
|
||||
{{/*
|
||||
Application name
|
||||
*/}}
|
||||
{{- define "lightrag.name" -}}
|
||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Full application name
|
||||
*/}}
|
||||
{{- define "lightrag.fullname" -}}
|
||||
{{- default .Release.Name .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Common labels
|
||||
*/}}
|
||||
{{- define "lightrag.labels" -}}
|
||||
app.kubernetes.io/name: {{ include "lightrag.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
Selector labels
|
||||
*/}}
|
||||
{{- define "lightrag.selectorLabels" -}}
|
||||
app.kubernetes.io/name: {{ include "lightrag.name" . }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- end }}
|
||||
|
||||
{{/*
|
||||
.env file content
|
||||
*/}}
|
||||
{{- define "lightrag.envContent" -}}
|
||||
{{- $first := true -}}
|
||||
{{- range $key, $val := .Values.env -}}
|
||||
{{- if not $first -}}{{- "\n" -}}{{- end -}}
|
||||
{{- $first = false -}}
|
||||
{{ $key }}={{ $val }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
@@ -0,0 +1,81 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ include "lightrag.fullname" . }}
|
||||
labels:
|
||||
{{- include "lightrag.labels" . | nindent 4 }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "lightrag.selectorLabels" . | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
checksum/config: {{ include "lightrag.envContent" . | sha256sum }}
|
||||
labels:
|
||||
{{- include "lightrag.selectorLabels" . | nindent 8 }}
|
||||
spec:
|
||||
containers:
|
||||
- name: {{ .Chart.Name }}
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: {{ .Values.env.PORT }}
|
||||
protocol: TCP
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: http
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 2
|
||||
successThreshold: 1
|
||||
failureThreshold: 3
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
volumeMounts:
|
||||
- name: rag-storage
|
||||
mountPath: /app/data/rag_storage
|
||||
- name: inputs
|
||||
mountPath: /app/data/inputs
|
||||
- name: env-file
|
||||
mountPath: /app/.env
|
||||
subPath: .env
|
||||
{{- $envFrom := default (dict) .Values.envFrom }}
|
||||
{{- $envFromEntries := list }}
|
||||
{{- range (default (list) (index $envFrom "secrets")) }}
|
||||
{{- $envFromEntries = append $envFromEntries (dict "secretRef" (dict "name" .name)) }}
|
||||
{{- end }}
|
||||
{{- range (default (list) (index $envFrom "configmaps")) }}
|
||||
{{- $envFromEntries = append $envFromEntries (dict "configMapRef" (dict "name" .name)) }}
|
||||
{{- end }}
|
||||
{{- if gt (len $envFromEntries) 0 }}
|
||||
envFrom:
|
||||
{{- toYaml $envFromEntries | nindent 12 }}
|
||||
{{- end }}
|
||||
{{- with .Values.image.imagePullSecrets }}
|
||||
imagePullSecrets:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
volumes:
|
||||
- name: env-file
|
||||
secret:
|
||||
secretName: {{ include "lightrag.fullname" . }}-env
|
||||
{{- if .Values.persistence.enabled }}
|
||||
- name: rag-storage
|
||||
persistentVolumeClaim:
|
||||
claimName: {{ include "lightrag.fullname" . }}-rag-storage
|
||||
- name: inputs
|
||||
persistentVolumeClaim:
|
||||
claimName: {{ include "lightrag.fullname" . }}-inputs
|
||||
{{- else }}
|
||||
- name: rag-storage
|
||||
emptyDir: {}
|
||||
- name: inputs
|
||||
emptyDir: {}
|
||||
{{- end }}
|
||||
|
||||
strategy:
|
||||
{{- toYaml .Values.updateStrategy | nindent 4 }}
|
||||
@@ -0,0 +1,28 @@
|
||||
{{- if .Values.persistence.enabled }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: {{ include "lightrag.fullname" . }}-rag-storage
|
||||
labels:
|
||||
{{- include "lightrag.labels" . | nindent 4 }}
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.persistence.ragStorage.size }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: {{ include "lightrag.fullname" . }}-inputs
|
||||
labels:
|
||||
{{- include "lightrag.labels" . | nindent 4 }}
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.persistence.inputs.size }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,10 @@
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ include "lightrag.fullname" . }}-env
|
||||
labels:
|
||||
{{- include "lightrag.labels" . | nindent 4 }}
|
||||
type: Opaque
|
||||
stringData:
|
||||
.env: |-
|
||||
{{- include "lightrag.envContent" . | nindent 4 }}
|
||||
@@ -0,0 +1,15 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ include "lightrag.fullname" . }}
|
||||
labels:
|
||||
{{- include "lightrag.labels" . | nindent 4 }}
|
||||
spec:
|
||||
type: {{ .Values.service.type }}
|
||||
ports:
|
||||
- port: {{ .Values.service.port }}
|
||||
targetPort: {{ .Values.env.PORT }}
|
||||
protocol: TCP
|
||||
name: http
|
||||
selector:
|
||||
{{- include "lightrag.selectorLabels" . | nindent 4 }}
|
||||
@@ -0,0 +1,82 @@
|
||||
replicaCount: 1
|
||||
|
||||
image:
|
||||
repository: ghcr.io/hkuds/lightrag
|
||||
tag: latest
|
||||
# Optionally specify imagePullSecrets if your image is in a private registry
|
||||
# example:
|
||||
# imagePullSecrets:
|
||||
# - name: my-registry-secret
|
||||
imagePullSecrets: []
|
||||
|
||||
# Specify a deployment strategy
|
||||
# example:
|
||||
# updateStrategy:
|
||||
# type: RollingUpdate
|
||||
# rollingUpdate:
|
||||
# maxUnavailable: 25%
|
||||
# maxSurge: 25%
|
||||
# Default for now should be Recreate as any RollingUpdate will cause issues with
|
||||
# multiple instances trying to access the same persistent storage if not using RWX volumes.
|
||||
updateStrategy:
|
||||
type: Recreate
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 9621
|
||||
|
||||
resources:
|
||||
limits:
|
||||
cpu: 1000m
|
||||
memory: 2Gi
|
||||
requests:
|
||||
cpu: 500m
|
||||
memory: 1Gi
|
||||
|
||||
persistence:
|
||||
enabled: true
|
||||
ragStorage:
|
||||
size: 10Gi
|
||||
inputs:
|
||||
size: 5Gi
|
||||
|
||||
# Allow specifying additional environment variables from ConfigMaps or Secrets created outside of this chart
|
||||
envFrom:
|
||||
configmaps: []
|
||||
# - name: my-shiny-configmap-1
|
||||
secrets: []
|
||||
# - name: my-shiny-secret-1
|
||||
|
||||
env:
|
||||
HOST: 0.0.0.0
|
||||
PORT: 9621
|
||||
WEBUI_TITLE: Graph RAG Engine
|
||||
WEBUI_DESCRIPTION: Simple and Fast Graph Based RAG System
|
||||
LLM_BINDING: openai
|
||||
LLM_MODEL: gpt-4o-mini
|
||||
LLM_BINDING_HOST:
|
||||
LLM_BINDING_API_KEY:
|
||||
EMBEDDING_BINDING: openai
|
||||
EMBEDDING_MODEL: text-embedding-ada-002
|
||||
EMBEDDING_DIM: 1536
|
||||
EMBEDDING_BINDING_API_KEY:
|
||||
LIGHTRAG_KV_STORAGE: PGKVStorage
|
||||
LIGHTRAG_VECTOR_STORAGE: PGVectorStorage
|
||||
# LIGHTRAG_KV_STORAGE: RedisKVStorage
|
||||
# LIGHTRAG_VECTOR_STORAGE: QdrantVectorDBStorage
|
||||
LIGHTRAG_GRAPH_STORAGE: Neo4JStorage
|
||||
LIGHTRAG_DOC_STATUS_STORAGE: PGDocStatusStorage
|
||||
# Replace with your POSTGRES credentials
|
||||
POSTGRES_HOST: pg-cluster-postgresql-postgresql
|
||||
POSTGRES_PORT: 5432
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD:
|
||||
POSTGRES_DATABASE: postgres
|
||||
POSTGRES_WORKSPACE: default
|
||||
# Replace with your NEO4J credentials
|
||||
NEO4J_URI: neo4j://neo4j-cluster-neo4j:7687
|
||||
NEO4J_USERNAME: neo4j
|
||||
NEO4J_PASSWORD:
|
||||
# Replace with your Qdrant credentials
|
||||
QDRANT_URL: http://qdrant-cluster-qdrant-qdrant:6333
|
||||
# REDIS_URI: redis://default:${REDIS_PASSWORD}@redis-cluster-redis-redis:6379
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
NAMESPACE=rag
|
||||
helm uninstall lightrag --namespace $NAMESPACE
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
NAMESPACE=rag
|
||||
helm uninstall lightrag-dev --namespace $NAMESPACE
|
||||
@@ -0,0 +1,30 @@
|
||||
[Unit]
|
||||
Description=LightRAG XYJ Service
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=netman
|
||||
# Memory settings
|
||||
MemoryHigh=8G
|
||||
MemoryMax=12G
|
||||
|
||||
# Set the LightRAG installation directory (change this to match your installation path)
|
||||
Environment="LIGHTRAG_HOME=/home/netman/lightrag-xyj"
|
||||
|
||||
# Set Environment to your Python virtual environment
|
||||
Environment="PATH=${LIGHTRAG_HOME}/.venv/bin"
|
||||
WorkingDirectory=${LIGHTRAG_HOME}
|
||||
ExecStart=${LIGHTRAG_HOME}/.venv/bin/lightrag-server
|
||||
# ExecStart=${LIGHTRAG_HOME}/.venv/bin/lightrag-gunicorn
|
||||
|
||||
# Kill mode require ExecStart must be gunicorn or unvicorn main process
|
||||
KillMode=process
|
||||
ExecStop=/bin/kill -s TERM $MAINPID
|
||||
TimeoutStopSec=60
|
||||
|
||||
Restart=always
|
||||
RestartSec=30
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,5 @@
|
||||
from .lightrag import LightRAG as LightRAG, QueryParam as QueryParam
|
||||
|
||||
__version__ = "1.4.9.9"
|
||||
__author__ = "Zirui Guo"
|
||||
__url__ = "https://github.com/HKUDS/LightRAG"
|
||||
@@ -0,0 +1,2 @@
|
||||
inputs
|
||||
rag_storage
|
||||
@@ -0,0 +1,626 @@
|
||||
# LightRAG 服务器和 WebUI
|
||||
|
||||
LightRAG 服务器旨在提供 Web 界面和 API 支持。Web 界面便于文档索引、知识图谱探索和简单的 RAG 查询界面。LightRAG 服务器还提供了与 Ollama 兼容的接口,旨在将 LightRAG 模拟为 Ollama 聊天模型。这使得 AI 聊天机器人(如 Open WebUI)可以轻松访问 LightRAG。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## 入门指南
|
||||
|
||||
### 安装
|
||||
|
||||
* 从 PyPI 安装
|
||||
|
||||
```bash
|
||||
# 使用 uv (推荐)
|
||||
uv pip install "lightrag-hku[api]"
|
||||
|
||||
# 或使用 pip
|
||||
# pip install "lightrag-hku[api]"
|
||||
```
|
||||
|
||||
* 从源代码安装
|
||||
|
||||
```bash
|
||||
# 克隆仓库
|
||||
git clone https://github.com/HKUDS/lightrag.git
|
||||
|
||||
# 进入仓库目录
|
||||
cd lightrag
|
||||
|
||||
# 使用 uv (推荐)
|
||||
# 注意: uv sync 会自动在 .venv/ 目录创建虚拟环境
|
||||
uv sync --extra api
|
||||
source .venv/bin/activate # 激活虚拟环境 (Linux/macOS)
|
||||
# Windows 系统: .venv\Scripts\activate
|
||||
|
||||
# 或使用 pip 与虚拟环境
|
||||
# python -m venv .venv
|
||||
# source .venv/bin/activate # Windows: .venv\Scripts\activate
|
||||
# pip install -e ".[api]"
|
||||
|
||||
# 构建前端代码
|
||||
cd lightrag_webui
|
||||
bun install --frozen-lockfile
|
||||
bun run build
|
||||
cd ..
|
||||
```
|
||||
|
||||
### 启动 LightRAG 服务器前的准备
|
||||
|
||||
LightRAG 需要同时集成 LLM(大型语言模型)和嵌入模型以有效执行文档索引和查询操作。在首次部署 LightRAG 服务器之前,必须配置 LLM 和嵌入模型的设置。LightRAG 支持绑定到各种 LLM/嵌入后端:
|
||||
|
||||
* ollama
|
||||
* lollms
|
||||
* openai 或 openai 兼容
|
||||
* azure_openai
|
||||
* aws_bedrock
|
||||
|
||||
建议使用环境变量来配置 LightRAG 服务器。项目根目录中有一个名为 `env.example` 的示例环境变量文件。请将此文件复制到启动目录并重命名为 `.env`。之后,您可以在 `.env` 文件中修改与 LLM 和嵌入模型相关的参数。需要注意的是,LightRAG 服务器每次启动时都会将 `.env` 中的环境变量加载到系统环境变量中。**LightRAG 服务器会优先使用系统环境变量中的设置**。
|
||||
|
||||
> 由于安装了 Python 扩展的 VS Code 可能会在集成终端中自动加载 .env 文件,请在每次修改 .env 文件后打开新的终端会话。
|
||||
|
||||
以下是 LLM 和嵌入模型的一些常见设置示例:
|
||||
|
||||
* OpenAI LLM + Ollama 嵌入
|
||||
|
||||
```
|
||||
LLM_BINDING=openai
|
||||
LLM_MODEL=gpt-4o
|
||||
LLM_BINDING_HOST=https://api.openai.com/v1
|
||||
LLM_BINDING_API_KEY=your_api_key
|
||||
|
||||
EMBEDDING_BINDING=ollama
|
||||
EMBEDDING_BINDING_HOST=http://localhost:11434
|
||||
EMBEDDING_MODEL=bge-m3:latest
|
||||
EMBEDDING_DIM=1024
|
||||
# EMBEDDING_BINDING_API_KEY=your_api_key
|
||||
```
|
||||
|
||||
* Ollama LLM + Ollama 嵌入
|
||||
|
||||
```
|
||||
LLM_BINDING=ollama
|
||||
LLM_MODEL=mistral-nemo:latest
|
||||
LLM_BINDING_HOST=http://localhost:11434
|
||||
# LLM_BINDING_API_KEY=your_api_key
|
||||
### Ollama 服务器上下文 token 数(必须大于 MAX_TOTAL_TOKENS+2000)
|
||||
OLLAMA_LLM_NUM_CTX=8192
|
||||
|
||||
EMBEDDING_BINDING=ollama
|
||||
EMBEDDING_BINDING_HOST=http://localhost:11434
|
||||
EMBEDDING_MODEL=bge-m3:latest
|
||||
EMBEDDING_DIM=1024
|
||||
# EMBEDDING_BINDING_API_KEY=your_api_key
|
||||
```
|
||||
|
||||
> **重要提示**:在文档索引前必须确定使用的Embedding模型,且在文档查询阶段必须沿用与索引阶段相同的模型。有些存储(例如PostgreSQL)在首次建立数表的时候需要确定向量维度,因此更换Embedding模型后需要删除向量相关库表,以便让LightRAG重建新的库表。
|
||||
|
||||
### 启动 LightRAG 服务器
|
||||
|
||||
LightRAG 服务器支持两种运行模式:
|
||||
* 简单高效的 Uvicorn 模式
|
||||
|
||||
```
|
||||
lightrag-server
|
||||
```
|
||||
* 多进程 Gunicorn + Uvicorn 模式(生产模式,不支持 Windows 环境)
|
||||
|
||||
```
|
||||
lightrag-gunicorn --workers 4
|
||||
```
|
||||
启动LightRAG的时候,当前工作目录必须含有`.env`配置文件。**要求将.env文件置于启动目录中是经过特意设计的**。 这样做的目的是支持用户同时启动多个LightRAG实例,并为不同实例配置不同的.env文件。**修改.env文件后,您需要重新打开终端以使新设置生效**。 这是因为每次启动时,LightRAG Server会将.env文件中的环境变量加载至系统环境变量,且系统环境变量的设置具有更高优先级。
|
||||
|
||||
启动时可以通过命令行参数覆盖`.env`文件中的配置。常用的命令行参数包括:
|
||||
|
||||
- `--host`:服务器监听地址(默认:0.0.0.0)
|
||||
- `--port`:服务器监听端口(默认:9621)
|
||||
- `--timeout`:LLM 请求超时时间(默认:150 秒)
|
||||
- `--log-level`:日志级别(默认:INFO)
|
||||
- `--working-dir`:数据库持久化目录(默认:./rag_storage)
|
||||
- `--input-dir`:上传文件存放目录(默认:./inputs)
|
||||
- `--workspace`: 工作空间名称,用于逻辑上隔离多个LightRAG实例之间的数据(默认:空)
|
||||
|
||||
### 使用 Docker 启动 LightRAG 服务器
|
||||
|
||||
使用 Docker Compose 是部署和运行 LightRAG Server 最便捷的方式。
|
||||
- 创建一个项目目录。
|
||||
- 将 LightRAG 仓库中的 `docker-compose.yml` 文件复制到您的项目目录中。
|
||||
- 准备 `.env` 文件:复制示例文件 [`env.example`](https://ai.znipower.com:5013/c/env.example) 创建自定义的 `.env` 文件,并根据您的具体需求配置 LLM 和嵌入参数。
|
||||
|
||||
* 通过以下命令启动 LightRAG 服务器:
|
||||
|
||||
```shell
|
||||
docker compose up
|
||||
# 如果希望启动后让程序退到后台运行,需要在命令的最后添加 -d 参数
|
||||
```
|
||||
> 可以通过以下链接获取官方的docker compose文件:[docker-compose.yml]( https://raw.githubusercontent.com/HKUDS/LightRAG/refs/heads/main/docker-compose.yml) 。如需获取LightRAG的历史版本镜像,可以访问以下链接: [LightRAG Docker Images]( https://github.com/HKUDS/LightRAG/pkgs/container/lightrag). 如需获取更多关于docker部署的信息,请参阅 [DockerDeployment.md](./../../docs/DockerDeployment.md).
|
||||
|
||||
### 离线部署
|
||||
|
||||
官方的 LightRAG Docker 镜像完全兼容离线或隔离网络环境。如需搭建自己的离线部署环境,请参考 [离线部署指南](./../../docs/OfflineDeployment.md)。
|
||||
|
||||
### 启动多个LightRAG实例
|
||||
|
||||
有两种方式可以启动多个LightRAG实例。第一种方式是为每个实例配置一个完全独立的工作环境。此时需要为每个实例创建一个独立的工作目录,然后在这个工作目录上放置一个当前实例专用的`.env`配置文件。不同实例的配置文件中的服务器监听端口不能重复,然后在工作目录上执行 lightrag-server 启动服务即可。
|
||||
|
||||
第二种方式是所有实例共享一套相同的`.env`配置文件,然后通过命令行参数来为每个实例指定不同的服务器监听端口和工作空间。你可以在同一个工作目录中通过不同的命令行参数启动多个LightRAG实例。例如:
|
||||
|
||||
```
|
||||
# 启动实例1
|
||||
lightrag-server --port 9621 --workspace space1
|
||||
|
||||
# 启动实例2
|
||||
lightrag-server --port 9622 --workspace space2
|
||||
```
|
||||
|
||||
工作空间的作用是实现不同实例之间的数据隔离。因此不同实例之间的`workspace`参数必须不同,否则会导致数据混乱,数据将会被破坏。
|
||||
|
||||
通过 Docker Compose 启动多个 LightRAG 实例时,只需在 `docker-compose.yml` 中为每个容器指定不同的 `WORKSPACE` 和 `PORT` 环境变量即可。即使所有实例共享同一个 `.env` 文件,Compose 中定义的容器环境变量也会优先覆盖 `.env` 文件中的同名设置,从而确保每个实例拥有独立的配置。
|
||||
|
||||
### LightRAG实例间的数据隔离
|
||||
|
||||
每个实例配置一个独立的工作目录和专用`.env`配置文件通常能够保证内存数据库中的本地持久化文件保存在各自的工作目录,实现数据的相互隔离。LightRAG默认存储全部都是内存数据库,通过这种方式进行数据隔离是没有问题的。但是如果使用的是外部数据库,如果不同实例访问的是同一个数据库实例,就需要通过配置工作空间来实现数据隔离,否则不同实例的数据将会出现冲突并被破坏。
|
||||
|
||||
命令行的 workspace 参数和`.env`文件中的环境变量`WORKSPACE` 都可以用于指定当前实例的工作空间名字,命令行参数的优先级别更高。下面是不同类型的存储实现工作空间的方式:
|
||||
|
||||
- **对于本地基于文件的数据库,数据隔离通过工作空间子目录实现:** JsonKVStorage, JsonDocStatusStorage, NetworkXStorage, NanoVectorDBStorage, FaissVectorDBStorage。
|
||||
- **对于将数据存储在集合(collection)中的数据库,通过在集合名称前添加工作空间前缀来实现:** RedisKVStorage, RedisDocStatusStorage, MilvusVectorDBStorage, QdrantVectorDBStorage, MongoKVStorage, MongoDocStatusStorage, MongoVectorDBStorage, MongoGraphStorage, PGGraphStorage。
|
||||
- **对于关系型数据库,数据隔离通过向表中添加 `workspace` 字段进行数据的逻辑隔离:** PGKVStorage, PGVectorStorage, PGDocStatusStorage。
|
||||
|
||||
* **对于Neo4j图数据库,通过label来实现数据的逻辑隔离**:Neo4JStorage
|
||||
|
||||
为了保持对遗留数据的兼容,在未配置工作空间时PostgreSQL的默认工作空间为`default`,Neo4j的默认工作空间为`base`。对于所有的外部存储,系统都提供了专用的工作空间环境变量,用于覆盖公共的 `WORKSPACE`环境变量配置。这些适用于指定存储类型的工作空间环境变量为:`REDIS_WORKSPACE`, `MILVUS_WORKSPACE`, `QDRANT_WORKSPACE`, `MONGODB_WORKSPACE`, `POSTGRES_WORKSPACE`, `NEO4J_WORKSPACE`。
|
||||
|
||||
### Gunicorn + Uvicorn 的多工作进程
|
||||
|
||||
LightRAG 服务器可以在 `Gunicorn + Uvicorn` 预加载模式下运行。Gunicorn 的多工作进程(多进程)功能可以防止文档索引任务阻塞 RAG 查询。使用 CPU 密集型文档提取工具(如 docling)在纯 Uvicorn 模式下可能会导致整个系统被阻塞。
|
||||
|
||||
虽然 LightRAG 服务器使用一个工作进程来处理文档索引流程,但通过 Uvicorn 的异步任务支持,可以并行处理多个文件。文档索引速度的瓶颈主要在于 LLM。如果您的 LLM 支持高并发,您可以通过增加 LLM 的并发级别来加速文档索引。以下是几个与并发处理相关的环境变量及其默认值:
|
||||
|
||||
```
|
||||
### 工作进程数,数字不大于 (2 x 核心数) + 1
|
||||
WORKERS=2
|
||||
### 一批中并行处理的文件数
|
||||
MAX_PARALLEL_INSERT=2
|
||||
# LLM 的最大并发请求数
|
||||
MAX_ASYNC=4
|
||||
```
|
||||
|
||||
### 将 Lightrag 安装为 Linux 服务
|
||||
|
||||
从示例文件 `lightrag.service.example` 创建您的服务文件 `lightrag.service`。修改服务文件中的服务启动定义:
|
||||
|
||||
```text
|
||||
# Set Enviroment to your Python virtual enviroment
|
||||
Environment="PATH=/home/netman/lightrag-xyj/venv/bin"
|
||||
WorkingDirectory=/home/netman/lightrag-xyj
|
||||
# ExecStart=/home/netman/lightrag-xyj/venv/bin/lightrag-server
|
||||
ExecStart=/home/netman/lightrag-xyj/venv/bin/lightrag-gunicorn
|
||||
```
|
||||
> ExecStart命令必须是 lightrag-gunicorn 或 lightrag-server 中的一个,不能使用其它脚本包裹它们。因为停止服务必须要求主进程必须是这两个进程。
|
||||
|
||||
安装 LightRAG 服务。如果您的系统是 Ubuntu,以下命令将生效:
|
||||
|
||||
```shell
|
||||
sudo cp lightrag.service /etc/systemd/system/
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl start lightrag.service
|
||||
sudo systemctl status lightrag.service
|
||||
sudo systemctl enable lightrag.service
|
||||
```
|
||||
|
||||
## Ollama 模拟
|
||||
|
||||
我们为 LightRAG 提供了 Ollama 兼容接口,旨在将 LightRAG 模拟为 Ollama 聊天模型。这使得支持 Ollama 的 AI 聊天前端(如 Open WebUI)可以轻松访问 LightRAG。
|
||||
|
||||
### 将 Open WebUI 连接到 LightRAG
|
||||
|
||||
启动 lightrag-server 后,您可以在 Open WebUI 管理面板中添加 Ollama 类型的连接。然后,一个名为 `lightrag:latest` 的模型将出现在 Open WebUI 的模型管理界面中。用户随后可以通过聊天界面向 LightRAG 发送查询。对于这种用例,最好将 LightRAG 安装为服务。
|
||||
|
||||
Open WebUI 使用 LLM 来执行会话标题和会话关键词生成任务。因此,Ollama 聊天补全 API 会检测并将 OpenWebUI 会话相关请求直接转发给底层 LLM。Open WebUI 的截图:
|
||||
|
||||

|
||||
|
||||
### 在聊天中选择查询模式
|
||||
|
||||
如果您从 LightRAG 的 Ollama 接口发送消息(查询),默认查询模式是 `hybrid`。您可以通过发送带有查询前缀的消息来选择查询模式。
|
||||
|
||||
查询字符串中的查询前缀可以决定使用哪种 LightRAG 查询模式来生成响应。支持的前缀包括:
|
||||
|
||||
```
|
||||
/local
|
||||
/global
|
||||
/hybrid
|
||||
/naive
|
||||
/mix
|
||||
|
||||
/bypass
|
||||
/context
|
||||
/localcontext
|
||||
/globalcontext
|
||||
/hybridcontext
|
||||
/naivecontext
|
||||
/mixcontext
|
||||
```
|
||||
|
||||
例如,聊天消息 "/mix 唐僧有几个徒弟" 将触发 LightRAG 的混合模式查询。没有查询前缀的聊天消息默认会触发混合模式查询。
|
||||
|
||||
"/bypass" 不是 LightRAG 查询模式,它会告诉 API 服务器将查询连同聊天历史直接传递给底层 LLM。因此用户可以使用 LLM 基于聊天历史回答问题。如果您使用 Open WebUI 作为前端,您可以直接切换到普通 LLM 模型,而不是使用 /bypass 前缀。
|
||||
|
||||
"/context" 也不是 LightRAG 查询模式,它会告诉 LightRAG 只返回为 LLM 准备的上下文信息。您可以检查上下文是否符合您的需求,或者自行处理上下文。
|
||||
|
||||
### 在聊天中添加用户提示词
|
||||
|
||||
使用LightRAG进行内容查询时,应避免将搜索过程与无关的输出处理相结合,这会显著影响查询效果。用户提示(user prompt)正是为解决这一问题而设计 -- 它不参与RAG检索阶段,而是在查询完成后指导大语言模型(LLM)如何处理检索结果。我们可以在查询前缀末尾添加方括号,从而向LLM传递用户提示词:
|
||||
|
||||
```
|
||||
/[使用mermaid格式画图] 请画出 Scrooge 的人物关系图谱
|
||||
/mix[使用mermaid格式画图] 请画出 Scrooge 的人物关系图谱
|
||||
```
|
||||
|
||||
## API 密钥和认证
|
||||
|
||||
默认情况下,LightRAG 服务器可以在没有任何认证的情况下访问。我们可以使用 API 密钥或账户凭证配置服务器以确保其安全。
|
||||
|
||||
* API 密钥
|
||||
|
||||
```
|
||||
LIGHTRAG_API_KEY=your-secure-api-key-here
|
||||
WHITELIST_PATHS=/health,/api/*
|
||||
```
|
||||
|
||||
> 健康检查和 Ollama 模拟端点默认不进行 API 密钥检查。为了安全原因,如果不需要提供Ollama服务,应该把`/api/*`从WHITELIST_PATHS中移除。
|
||||
|
||||
API Key使用的请求头是 `X-API-Key` 。以下是使用API访问LightRAG Server的一个例子:
|
||||
|
||||
```
|
||||
curl -X 'POST' \
|
||||
'http://localhost:9621/documents/scan' \
|
||||
-H 'accept: application/json' \
|
||||
-H 'X-API-Key: your-secure-api-key-here-123' \
|
||||
-d ''
|
||||
```
|
||||
|
||||
* 账户凭证(Web 界面需要登录后才能访问)
|
||||
|
||||
LightRAG API 服务器使用基于 HS256 算法的 JWT 认证。要启用安全访问控制,需要以下环境变量:
|
||||
|
||||
```bash
|
||||
# JWT 认证
|
||||
AUTH_ACCOUNTS='admin:admin123,user1:pass456'
|
||||
TOKEN_SECRET='your-key'
|
||||
TOKEN_EXPIRE_HOURS=4
|
||||
```
|
||||
|
||||
> 目前仅支持配置一个管理员账户和密码。尚未开发和实现完整的账户系统。
|
||||
|
||||
如果未配置账户凭证,Web 界面将以访客身份访问系统。因此,即使仅配置了 API 密钥,所有 API 仍然可以通过访客账户访问,这仍然不安全。因此,要保护 API,需要同时配置这两种认证方法。
|
||||
|
||||
## Azure OpenAI 后端配置
|
||||
|
||||
可以使用以下 Azure CLI 命令创建 Azure OpenAI API(您需要先从 [https://docs.microsoft.com/en-us/cli/azure/install-azure-cli](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli) 安装 Azure CLI):
|
||||
|
||||
```bash
|
||||
# 根据需要更改资源组名称、位置和 OpenAI 资源名称
|
||||
RESOURCE_GROUP_NAME=LightRAG
|
||||
LOCATION=swedencentral
|
||||
RESOURCE_NAME=LightRAG-OpenAI
|
||||
|
||||
az login
|
||||
az group create --name $RESOURCE_GROUP_NAME --location $LOCATION
|
||||
az cognitiveservices account create --name $RESOURCE_NAME --resource-group $RESOURCE_GROUP_NAME --kind OpenAI --sku S0 --location swedencentral
|
||||
az cognitiveservices account deployment create --resource-group $RESOURCE_GROUP_NAME --model-format OpenAI --name $RESOURCE_NAME --deployment-name gpt-4o --model-name gpt-4o --model-version "2024-08-06" --sku-capacity 100 --sku-name "Standard"
|
||||
az cognitiveservices account deployment create --resource-group $RESOURCE_GROUP_NAME --model-format OpenAI --name $RESOURCE_NAME --deployment-name text-embedding-3-large --model-name text-embedding-3-large --model-version "1" --sku-capacity 80 --sku-name "Standard"
|
||||
az cognitiveservices account show --name $RESOURCE_NAME --resource-group $RESOURCE_GROUP_NAME --query "properties.endpoint"
|
||||
az cognitiveservices account keys list --name $RESOURCE_NAME -g $RESOURCE_GROUP_NAME
|
||||
```
|
||||
|
||||
最后一个命令的输出将提供 OpenAI API 的端点和密钥。您可以使用这些值在 `.env` 文件中设置环境变量。
|
||||
|
||||
```
|
||||
# .env 中的 Azure OpenAI 配置
|
||||
LLM_BINDING=azure_openai
|
||||
LLM_BINDING_HOST=your-azure-endpoint
|
||||
LLM_MODEL=your-model-deployment-name
|
||||
LLM_BINDING_API_KEY=your-azure-api-key
|
||||
### API Version可选,默认为最新版本
|
||||
AZURE_OPENAI_API_VERSION=2024-08-01-preview
|
||||
|
||||
### 如果使用 Azure OpenAI 进行嵌入
|
||||
EMBEDDING_BINDING=azure_openai
|
||||
EMBEDDING_MODEL=your-embedding-deployment-name
|
||||
```
|
||||
|
||||
## LightRAG 服务器详细配置
|
||||
|
||||
API 服务器可以通过三种方式配置(优先级从高到低):
|
||||
|
||||
* 命令行参数
|
||||
* 环境变量或 .env 文件
|
||||
* Config.ini(仅用于存储配置)
|
||||
|
||||
大多数配置都有默认设置,详细信息请查看示例文件:`.env.example`。数据存储配置也可以通过 config.ini 设置。为方便起见,提供了示例文件 `config.ini.example`。
|
||||
|
||||
### 支持的 LLM 和嵌入后端
|
||||
|
||||
LightRAG 支持绑定到各种 LLM/嵌入后端:
|
||||
|
||||
* ollama
|
||||
* openai (含openai 兼容)
|
||||
* azure_openai
|
||||
* lollms
|
||||
* aws_bedrock
|
||||
|
||||
使用环境变量 `LLM_BINDING` 或 CLI 参数 `--llm-binding` 选择 LLM 后端类型。使用环境变量 `EMBEDDING_BINDING` 或 CLI 参数 `--embedding-binding` 选择嵌入后端类型。
|
||||
|
||||
LLM和Embedding配置例子请查看项目根目录的 env.example 文件。OpenAI和Ollama兼容LLM接口的支持的完整配置选型可以通过一下命令查看:
|
||||
|
||||
```
|
||||
lightrag-server --llm-binding openai --help
|
||||
lightrag-server --llm-binding ollama --help
|
||||
lightrag-server --embedding-binding ollama --help
|
||||
```
|
||||
|
||||
> 请使用openai兼容方式访问OpenRouter、vLLM或SLang部署的LLM。可以通过 `OPENAI_LLM_EXTRA_BODY` 环境变量给OpenRouter、vLLM或SGLang推理框架传递额外的参数,实现推理模式的关闭或者其它个性化控制。
|
||||
|
||||
设置 `max_tokens` 参数旨在**防止在实体关系提取阶段出现LLM 响应输出过长或无休止的循环输出的问题**。设置 `max_tokens` 参数的目的是在超时发生之前截断 LLM 输出,从而防止文档提取失败。这解决了某些包含大量实体和关系的文本块(例如表格或引文)可能导致 LLM 产生过长甚至无限循环输出的问题。此设置对于本地部署的小参数模型尤为重要。`max_tokens` 值可以通过以下公式计算:
|
||||
|
||||
```
|
||||
# For vLLM/SGLang doployed models, or most of OpenAI compatible API provider
|
||||
OPENAI_LLM_MAX_TOKENS=9000
|
||||
|
||||
# For Ollama Deployed Modeles
|
||||
OLLAMA_LLM_NUM_PREDICT=9000
|
||||
|
||||
# For OpenAI o1-mini or newer modles
|
||||
OPENAI_LLM_MAX_COMPLETION_TOKENS=9000
|
||||
```
|
||||
|
||||
### 实体提取配置
|
||||
|
||||
* ENABLE_LLM_CACHE_FOR_EXTRACT:为实体提取启用 LLM 缓存(默认:true)
|
||||
|
||||
在测试环境中将 `ENABLE_LLM_CACHE_FOR_EXTRACT` 设置为 true 以减少 LLM 调用成本是很常见的做法。
|
||||
|
||||
### 支持的存储类型
|
||||
|
||||
LightRAG 使用 4 种类型的存储用于不同目的:
|
||||
|
||||
* KV_STORAGE:llm 响应缓存、文本块、文档信息
|
||||
* VECTOR_STORAGE:实体向量、关系向量、块向量
|
||||
* GRAPH_STORAGE:实体关系图
|
||||
* DOC_STATUS_STORAGE:文档索引状态
|
||||
|
||||
每种存储类型都有多种存储实现方式。LightRAG Server默认的存储实现为内存数据库,数据通过文件持久化保存到WORKING_DIR目录。LightRAG还支持PostgreSQL、MongoDB、FAISS、Milvus、Qdrant、Neo4j、Memgraph和Redis等存储实现方式。详细的存储支持方式请参考根目录下的`README.md`文件中关于存储的相关内容。
|
||||
|
||||
您可以通过环境变量选择存储实现。例如,在首次启动 API 服务器之前,您可以将以下环境变量设置为特定的存储实现名称:
|
||||
|
||||
```
|
||||
LIGHTRAG_KV_STORAGE=PGKVStorage
|
||||
LIGHTRAG_VECTOR_STORAGE=PGVectorStorage
|
||||
LIGHTRAG_GRAPH_STORAGE=PGGraphStorage
|
||||
LIGHTRAG_DOC_STATUS_STORAGE=PGDocStatusStorage
|
||||
```
|
||||
|
||||
在向 LightRAG 添加文档后,您不能更改存储实现选择。目前尚不支持从一个存储实现迁移到另一个存储实现。更多配置信息请阅读示例 `env.exampl`e文件。
|
||||
|
||||
### 在不同存储类型之间迁移LLM缓存
|
||||
|
||||
当LightRAG更换存储实现方式的时候,可以LLM缓存从就的存储迁移到新的存储。先以后在新的存储上重新上传文件时,将利用利用原有存储的LLM缓存大幅度加快文件处理的速度。LLM缓存迁移工具的使用方法请参考[README_MIGRATE_LLM_CACHE.md](../tools/README_MIGRATE_LLM_CACHE.md)
|
||||
|
||||
### LightRag API 服务器命令行选项
|
||||
|
||||
| 参数 | 默认值 | 描述 |
|
||||
|-----------|---------|-------------|
|
||||
| --host | 0.0.0.0 | 服务器主机 |
|
||||
| --port | 9621 | 服务器端口 |
|
||||
| --working-dir | ./rag_storage | RAG 存储的工作目录 |
|
||||
| --input-dir | ./inputs | 包含输入文档的目录 |
|
||||
| --max-async | 4 | 最大异步操作数 |
|
||||
| --log-level | INFO | 日志级别(DEBUG、INFO、WARNING、ERROR、CRITICAL) |
|
||||
| --verbose | - | 详细调试输出(True、False) |
|
||||
| --key | None | 用于认证的 API 密钥。保护 lightrag 服务器免受未授权访问 |
|
||||
| --ssl | False | 启用 HTTPS |
|
||||
| --ssl-certfile | None | SSL 证书文件路径(如果启用 --ssl 则必需) |
|
||||
| --ssl-keyfile | None | SSL 私钥文件路径(如果启用 --ssl 则必需) |
|
||||
| --llm-binding | ollama | LLM 绑定类型(lollms、ollama、openai、openai-ollama、azure_openai、aws_bedrock) |
|
||||
| --embedding-binding | ollama | 嵌入绑定类型(lollms、ollama、openai、azure_openai、aws_bedrock) |
|
||||
|
||||
### Reranking 配置
|
||||
|
||||
Reranking 查询召回的块可以显著提高检索质量,它通过基于优化的相关性评分模型对文档重新排序。LightRAG 目前支持以下 rerank 提供商:
|
||||
|
||||
- **Cohere / vLLM**:提供与 Cohere AI 的 `v2/rerank` 端点的完整 API 集成。由于 vLLM 提供了与 Cohere 兼容的 reranker API,因此也支持所有通过 vLLM 部署的 reranker 模型。
|
||||
- **Jina AI**:提供与所有 Jina rerank 模型的完全实现兼容性。
|
||||
- **阿里云**:具有旨在支持阿里云 rerank API 格式的自定义实现。
|
||||
|
||||
Rerank 提供商通过 `.env` 文件进行配置。以下是使用 vLLM 本地部署的 rerank 模型的示例配置:
|
||||
|
||||
```
|
||||
RERANK_BINDING=cohere
|
||||
RERANK_MODEL=BAAI/bge-reranker-v2-m3
|
||||
RERANK_BINDING_HOST=http://localhost:8000/v1/rerank
|
||||
RERANK_BINDING_API_KEY=your_rerank_api_key_here
|
||||
```
|
||||
|
||||
以下是使用阿里云提供的 Reranker 服务的示例配置:
|
||||
|
||||
```
|
||||
RERANK_BINDING=aliyun
|
||||
RERANK_MODEL=gte-rerank-v2
|
||||
RERANK_BINDING_HOST=https://dashscope.aliyuncs.com/api/v1/services/rerank/text-rerank/text-rerank
|
||||
RERANK_BINDING_API_KEY=your_rerank_api_key_here
|
||||
```
|
||||
|
||||
有关完整的 reranker 配置示例,请参阅 `env.example` 文件。
|
||||
|
||||
### 启用 Reranking
|
||||
|
||||
可以按查询启用或禁用 Reranking。
|
||||
|
||||
`/query` 和 `/query/stream` API 端点包含一个 `enable_rerank` 参数,默认设置为 `true`,用于控制当前查询是否激活 reranking。要将 `enable_rerank` 参数的默认值更改为 `false`,请设置以下环境变量:
|
||||
|
||||
```
|
||||
RERANK_BY_DEFAULT=False
|
||||
```
|
||||
|
||||
### .env 文件示例
|
||||
|
||||
```bash
|
||||
### Server Configuration
|
||||
# HOST=0.0.0.0
|
||||
PORT=9621
|
||||
WORKERS=2
|
||||
|
||||
### Settings for document indexing
|
||||
ENABLE_LLM_CACHE_FOR_EXTRACT=true
|
||||
SUMMARY_LANGUAGE=Chinese
|
||||
MAX_PARALLEL_INSERT=2
|
||||
|
||||
### LLM Configuration (Use valid host. For local services installed with docker, you can use host.docker.internal)
|
||||
TIMEOUT=150
|
||||
MAX_ASYNC=4
|
||||
|
||||
LLM_BINDING=openai
|
||||
LLM_MODEL=gpt-4o-mini
|
||||
LLM_BINDING_HOST=https://api.openai.com/v1
|
||||
LLM_BINDING_API_KEY=your-api-key
|
||||
|
||||
### Embedding Configuration (Use valid host. For local services installed with docker, you can use host.docker.internal)
|
||||
EMBEDDING_MODEL=bge-m3:latest
|
||||
EMBEDDING_DIM=1024
|
||||
EMBEDDING_BINDING=ollama
|
||||
EMBEDDING_BINDING_HOST=http://localhost:11434
|
||||
|
||||
### For JWT Auth
|
||||
# AUTH_ACCOUNTS='admin:admin123,user1:pass456'
|
||||
# TOKEN_SECRET=your-key-for-LightRAG-API-Server-xxx
|
||||
# TOKEN_EXPIRE_HOURS=48
|
||||
|
||||
# LIGHTRAG_API_KEY=your-secure-api-key-here-123
|
||||
# WHITELIST_PATHS=/api/*
|
||||
# WHITELIST_PATHS=/health,/api/*
|
||||
```
|
||||
|
||||
#### 使用 ollama 默认本地服务器作为 llm 和嵌入后端运行 Lightrag 服务器
|
||||
|
||||
Ollama 是 llm 和嵌入的默认后端,因此默认情况下您可以不带参数运行 lightrag-server,将使用默认值。确保已安装 ollama 并且正在运行,且默认模型已安装在 ollama 上。
|
||||
|
||||
```bash
|
||||
# 使用 ollama 运行 lightrag,llm 使用 mistral-nemo:latest,嵌入使用 bge-m3:latest
|
||||
lightrag-server
|
||||
|
||||
# 使用认证密钥
|
||||
lightrag-server --key my-key
|
||||
```
|
||||
|
||||
#### 使用 lollms 默认本地服务器作为 llm 和嵌入后端运行 Lightrag 服务器
|
||||
|
||||
```bash
|
||||
# 使用 lollms 运行 lightrag,llm 使用 mistral-nemo:latest,嵌入使用 bge-m3:latest
|
||||
# 在 .env 或 config.ini 中配置 LLM_BINDING=lollms 和 EMBEDDING_BINDING=lollms
|
||||
lightrag-server
|
||||
|
||||
# 使用认证密钥
|
||||
lightrag-server --key my-key
|
||||
```
|
||||
|
||||
#### 使用 openai 服务器作为 llm 和嵌入后端运行 Lightrag 服务器
|
||||
|
||||
```bash
|
||||
# 使用 openai 运行 lightrag,llm 使用 GPT-4o-mini,嵌入使用 text-embedding-3-small
|
||||
# 在 .env 或 config.ini 中配置:
|
||||
# LLM_BINDING=openai
|
||||
# LLM_MODEL=GPT-4o-mini
|
||||
# EMBEDDING_BINDING=openai
|
||||
# EMBEDDING_MODEL=text-embedding-3-small
|
||||
lightrag-server
|
||||
|
||||
# 使用认证密钥
|
||||
lightrag-server --key my-key
|
||||
```
|
||||
|
||||
#### 使用 azure openai 服务器作为 llm 和嵌入后端运行 Lightrag 服务器
|
||||
|
||||
```bash
|
||||
# 使用 azure_openai 运行 lightrag
|
||||
# 在 .env 或 config.ini 中配置:
|
||||
# LLM_BINDING=azure_openai
|
||||
# LLM_MODEL=your-model
|
||||
# EMBEDDING_BINDING=azure_openai
|
||||
# EMBEDDING_MODEL=your-embedding-model
|
||||
lightrag-server
|
||||
|
||||
# 使用认证密钥
|
||||
lightrag-server --key my-key
|
||||
```
|
||||
|
||||
**重要说明:**
|
||||
- 对于 LoLLMs:确保指定的模型已安装在您的 LoLLMs 实例中
|
||||
- 对于 Ollama:确保指定的模型已安装在您的 Ollama 实例中
|
||||
- 对于 OpenAI:确保您已设置 OPENAI_API_KEY 环境变量
|
||||
- 对于 Azure OpenAI:按照先决条件部分所述构建和配置您的服务器
|
||||
|
||||
要获取任何服务器的帮助,使用 --help 标志:
|
||||
```bash
|
||||
lightrag-server --help
|
||||
```
|
||||
|
||||
注意:如果您不需要 API 功能,可以使用以下命令安装不带 API 支持的基本包:
|
||||
```bash
|
||||
pip install lightrag-hku
|
||||
```
|
||||
|
||||
## 文档和块处理逻辑说明
|
||||
|
||||
LightRAG 中的文档处理流程有些复杂,分为两个主要阶段:提取阶段(实体和关系提取)和合并阶段(实体和关系合并)。有两个关键参数控制流程并发性:并行处理的最大文件数(`MAX_PARALLEL_INSERT`)和最大并发 LLM 请求数(`MAX_ASYNC`)。工作流程描述如下:
|
||||
|
||||
1. `MAX_ASYNC` 限制系统中并发 LLM 请求的总数,包括查询、提取和合并的请求。LLM 请求具有不同的优先级:查询操作优先级最高,其次是合并,然后是提取。
|
||||
2. `MAX_PARALLEL_INSERT` 控制提取阶段并行处理的文件数量。`MAX_PARALLEL_INSERT`建议设置为2~10之间,通常设置为 `MAX_ASYNC/3`,设置太大会导致合并阶段不同文档之间实体和关系重名的机会增大,降低合并阶段的效率。
|
||||
3. 在单个文件中,来自不同文本块的实体和关系提取是并发处理的,并发度由 `MAX_ASYNC` 设置。只有在处理完 `MAX_ASYNC` 个文本块后,系统才会继续处理同一文件中的下一批文本块。
|
||||
4. 当一个文件完成实体和关系提后,将进入实体和关系合并阶段。这一阶段也会并发处理多个实体和关系,其并发度同样是由 `MAX_ASYNC` 控制。
|
||||
5. 合并阶段的 LLM 请求的优先级别高于提取阶段,目的是让进入合并阶段的文件尽快完成处理,并让处理结果尽快更新到向量数据库中。
|
||||
6. 为防止竞争条件,合并阶段会避免并发处理同一个实体或关系,当多个文件中都涉及同一个实体或关系需要合并的时候他们会串行执行。
|
||||
7. 每个文件在流程中被视为一个原子处理单元。只有当其所有文本块都完成提取和合并后,文件才会被标记为成功处理。如果在处理过程中发生任何错误,整个文件将被标记为失败,并且必须重新处理。
|
||||
8. 当由于错误而重新处理文件时,由于 LLM 缓存,先前处理的文本块可以快速跳过。尽管 LLM 缓存在合并阶段也会被利用,但合并顺序的不一致可能会限制其在此阶段的有效性。
|
||||
9. 如果在提取过程中发生错误,系统不会保留任何中间结果。如果在合并过程中发生错误,已合并的实体和关系可能会被保留;当重新处理同一文件时,重新提取的实体和关系将与现有实体和关系合并,而不会影响查询结果。
|
||||
10. 在合并阶段结束时,所有实体和关系数据都会在向量数据库中更新。如果此时发生错误,某些更新可能会被保留。但是,下一次处理尝试将覆盖先前结果,确保成功重新处理的文件不会影响未来查询结果的完整性。
|
||||
|
||||
大型文件应分割成较小的片段以启用增量处理。可以通过在 Web UI 上按“扫描”按钮来启动失败文件的重新处理。
|
||||
|
||||
## API 端点
|
||||
|
||||
所有服务器(LoLLMs、Ollama、OpenAI 和 Azure OpenAI)都为 RAG 功能提供相同的 REST API 端点。当 API 服务器运行时,访问:
|
||||
|
||||
- Swagger UI:http://localhost:9621/docs
|
||||
- ReDoc:http://localhost:9621/redoc
|
||||
|
||||
您可以使用提供的 curl 命令或通过 Swagger UI 界面测试 API 端点。确保:
|
||||
|
||||
1. 启动适当的后端服务(LoLLMs、Ollama 或 OpenAI)
|
||||
2. 启动 RAG 服务器
|
||||
3. 使用文档管理端点上传一些文档
|
||||
4. 使用查询端点查询系统
|
||||
5. 如果在输入目录中放入新文件,触发文档扫描
|
||||
|
||||
## 异步文档索引与进度跟踪
|
||||
|
||||
LightRAG采用异步文档索引机制,便于前端监控和查询文档处理进度。用户通过指定端点上传文件或插入文本时,系统将返回唯一的跟踪ID,以便实时监控处理进度。
|
||||
|
||||
**支持生成跟踪ID的API端点:**
|
||||
* `/documents/upload`
|
||||
* `/documents/text`
|
||||
* `/documents/texts`
|
||||
|
||||
**文档处理状态查询端点:**
|
||||
* `/track_status/{track_id}`
|
||||
|
||||
该端点提供全面的状态信息,包括:
|
||||
* 文档处理状态(待处理/处理中/已处理/失败)
|
||||
* 内容摘要和元数据
|
||||
* 处理失败时的错误信息
|
||||
* 创建和更新时间戳
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 374 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user