diff --git a/README.md b/README.md index a6442e4..0640ded 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ ## 🌟 核心特性 - **🗣️ 自然语言转 SQL**: 用大白话提问!它能理解你的数据表结构,生成准确的 SQL,甚至在报错时进行自我纠正 (Self-correction)。 +- **📚 智能知识库检索 (RAG)**: 支持上传 Word、PPT、PDF 等多种格式文档,通过向量检索增强回答,让你的私有文档“开口说话”。 - **📈 即时数据可视化**: 拒绝枯燥的生肉表格,根据数据特征自动生成交互式图表。 - **🗂️ 动态多数据源**: 无缝连接 PostgreSQL、Supabase,以及本地 CSV/Excel 文件上传解析。 - **🧠 灵活的模型接入**: 原生集成 LiteLLM,支持随插随用 OpenAI、DeepSeek、智谱、通义千问 (DashScope)、火山引擎或任何兼容的 LLM 提供商。 @@ -27,17 +28,21 @@ ## 📸 界面预览 -
-

对话式分析界面

- +
+

💬 对话式分析界面

+

-

可定制仪表盘

- +

📊 可定制仪表盘

+

-

智能产物预览 (Artifact)

- +

📚 智能知识库问答

+ +
+
+

📦 智能产物预览 (Artifact)

+

diff --git a/README_en.md b/README_en.md index e8eb4ed..a31da56 100644 --- a/README_en.md +++ b/README_en.md @@ -16,6 +16,7 @@ Whether you're querying a massive Supabase/PostgreSQL database or just tossing i ## 🌟 Key Features - **🗣️ Chat to SQL**: Ask questions in plain English (or Chinese!). DataClaw understands your schema, generates accurate SQL, and self-corrects if things go sideways. +- **📚 Smart Knowledge Base (RAG)**: Support uploading Word, PPT, PDF and other document formats. Enhance answers through vector retrieval, making your private documents "speak". - **📈 Instant Visualizations**: Returns not just raw tables, but auto-generated interactive charts tailored to your data's shape. - **🗂️ Multi-Source Ready**: Connects seamlessly to PostgreSQL, Supabase, and local CSV/Excel uploads. - **🧠 Bring Your Own LLM**: Native integration with LiteLLM. Plug in OpenAI, DeepSeek, Zhipu, DashScope, Volcengine, or any compatible provider. @@ -27,17 +28,21 @@ Whether you're querying a massive Supabase/PostgreSQL database or just tossing i ## 📸 Screenshots -
-

Chat Interface

- +
+

💬 Chat Interface

+

-

Customizable Dashboard

- +

📊 Customizable Dashboard

+

-

Artifact Preview

- +

📚 Smart Knowledge Base

+ +
+
+

📦 Artifact Preview

+

diff --git a/examples/artifact.png b/docs/artifact.png similarity index 100% rename from examples/artifact.png rename to docs/artifact.png diff --git a/examples/dashboard.png b/docs/dashboard.png similarity index 100% rename from examples/dashboard.png rename to docs/dashboard.png diff --git a/examples/index.png b/docs/index.png similarity index 100% rename from examples/index.png rename to docs/index.png diff --git a/docs/kb.png b/docs/kb.png new file mode 100644 index 0000000..7af3ffe Binary files /dev/null and b/docs/kb.png differ diff --git a/examples/Car_Database.db b/examples/Car_Database.db deleted file mode 100644 index e7334bc..0000000 Binary files a/examples/Car_Database.db and /dev/null differ diff --git a/examples/file_example_XLS_1000.xls b/examples/file_example_XLS_1000.xls deleted file mode 100644 index af70880..0000000 Binary files a/examples/file_example_XLS_1000.xls and /dev/null differ diff --git a/frontend/src/components/ChatInterface.tsx b/frontend/src/components/ChatInterface.tsx index ce0eb63..d277fa3 100644 --- a/frontend/src/components/ChatInterface.tsx +++ b/frontend/src/components/ChatInterface.tsx @@ -328,7 +328,20 @@ export function ChatInterface() { // Model selection state const [models, setModels] = useState([]); const [selectedModelId, setSelectedModelId] = useState(""); - const [modelOpen, setModelOpen] = useState(false); + + // Listen for model changes from the ProjectSwitcher + useEffect(() => { + const handleModelChange = (e: Event) => { + const customEvent = e as CustomEvent; + if (customEvent.detail) { + setSelectedModelId(customEvent.detail); + } + }; + window.addEventListener('nanobot:model-changed', handleModelChange); + return () => { + window.removeEventListener('nanobot:model-changed', handleModelChange); + }; + }, []); // Data Source selection state const [availableDataSources, setAvailableDataSources] = useState<{id: string, name: string}[]>([]); @@ -1120,49 +1133,6 @@ export function ChatInterface() { return (
- {/* Header with Model Selection */} -
- - - - {selectedModelId ? models.find(m => m.id === selectedModelId)?.name || 'DataClaw' : 'DataClaw'} - - - - - - - - {t('modelNotFound')} - - {models.map((model) => ( - { - setSelectedModelId(model.id); - setModelOpen(false); - }} - className="flex items-center gap-2 py-2.5 cursor-pointer" - > -
- {model.name || model.model} - {model.provider} -
- -
- ))} -
-
-
-
-
-
- {/* Hidden file input available in all states */} ([]); + const [selectedModelId, setSelectedModelId] = useState(""); + const [modelOpen, setModelOpen] = useState(false); + useEffect(() => { fetchProjects(); }, [fetchProjects]); + useEffect(() => { + const fetchModels = async () => { + try { + const data = await api.get("/api/v1/llm"); + setModels(data); + const active = data.find(m => m.is_active); + if (active) { + setSelectedModelId(active.id); + } else if (data.length > 0) { + setSelectedModelId(data[0].id); + } + } catch (e) { + console.error("Failed to fetch models", e); + } + }; + fetchModels(); + }, []); + const handleCreateProject = async () => { if (!newProjectName.trim()) return; setIsSubmitting(true); @@ -88,6 +125,50 @@ export function ProjectSwitcher() { +
+ + + + + {selectedModelId ? (models.find(m => m.id === selectedModelId)?.name || models.find(m => m.id === selectedModelId)?.model || 'DataClaw') : 'DataClaw'} + + + + + + + + {t('modelNotFound')} + + {models.map((model) => ( + { + setSelectedModelId(model.id); + setModelOpen(false); + // Fire custom event to notify ChatInterface if needed + window.dispatchEvent(new CustomEvent("nanobot:model-changed", { detail: model.id })); + }} + className="flex items-center gap-2 py-2.5 cursor-pointer" + > +
+ {model.name || model.model} + {model.provider} +
+ +
+ ))} +
+
+
+
+
+