reorg chat
This commit is contained in:
@@ -299,12 +299,3 @@ def update_session_context_file(session_id: str, payload: SessionFileContextUpda
|
|||||||
session.updated_at = datetime.now()
|
session.updated_at = datetime.now()
|
||||||
nanobot_service.agent.sessions.save(session)
|
nanobot_service.agent.sessions.save(session)
|
||||||
return {"status": "success", "metadata": session.metadata}
|
return {"status": "success", "metadata": session.metadata}
|
||||||
|
|
||||||
@app.post("/api/v1/agent/nl2sql", response_model=NL2SQLResponse)
|
|
||||||
async def run_nl2sql(request: NL2SQLRequest):
|
|
||||||
result = await process_nl2sql(request)
|
|
||||||
if request.session_id:
|
|
||||||
text = _build_sql_chart_text(result)
|
|
||||||
viz_payload = _build_sql_chart_viz(result)
|
|
||||||
_persist_session_turn(request.session_id, request.query, text, {"viz": viz_payload})
|
|
||||||
return result
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useState, useRef, useEffect } from "react";
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
import { User, Loader2, Sparkles, Search, ArrowUp, ChevronDown, Table, Paperclip, Check, X, File as FileIcon, Square } from "lucide-react";
|
import { User, Loader2, Sparkles, ArrowUp, ChevronDown, Paperclip, Check, X, File as FileIcon, Square } from "lucide-react";
|
||||||
import { api } from "@/lib/api";
|
import { api } from "@/lib/api";
|
||||||
import { type ChartSpec } from "@/store/visualizationStore";
|
import { type ChartSpec } from "@/store/visualizationStore";
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||||
@@ -62,7 +62,6 @@ interface SessionData {
|
|||||||
export function ChatInterface() {
|
export function ChatInterface() {
|
||||||
const [messages, setMessages] = useState<Message[]>([]);
|
const [messages, setMessages] = useState<Message[]>([]);
|
||||||
const [input, setInput] = useState("");
|
const [input, setInput] = useState("");
|
||||||
const [selectedCapability, setSelectedCapability] = useState<string>("智能问答");
|
|
||||||
const [selectedDataSource, setSelectedDataSource] = useState<string>("postgres-main");
|
const [selectedDataSource, setSelectedDataSource] = useState<string>("postgres-main");
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const scrollRef = useRef<HTMLDivElement>(null);
|
const scrollRef = useRef<HTMLDivElement>(null);
|
||||||
@@ -172,11 +171,6 @@ export function ChatInterface() {
|
|||||||
|
|
||||||
const currentModel = models.find(m => m.id === selectedModelId);
|
const currentModel = models.find(m => m.id === selectedModelId);
|
||||||
|
|
||||||
const capabilities = [
|
|
||||||
{ icon: Sparkles, label: "智能问答", color: "text-purple-500", bg: "bg-purple-50" },
|
|
||||||
{ icon: Table, label: "表格问答", color: "text-orange-500", bg: "bg-orange-50" },
|
|
||||||
{ icon: Search, label: "深度问数", color: "text-blue-500", bg: "bg-blue-50" },
|
|
||||||
];
|
|
||||||
const chartIntentPattern = /(图表|可视化|画图|作图|柱状图|折线图|饼图|趋势|分布|chart|plot|visuali[sz]e)/i;
|
const chartIntentPattern = /(图表|可视化|画图|作图|柱状图|折线图|饼图|趋势|分布|chart|plot|visuali[sz]e)/i;
|
||||||
|
|
||||||
const buildMessageViz = (payload: {
|
const buildMessageViz = (payload: {
|
||||||
@@ -281,7 +275,6 @@ export function ChatInterface() {
|
|||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (selectedCapability === "智能问答") {
|
|
||||||
const assistantId = (Date.now() + 1).toString();
|
const assistantId = (Date.now() + 1).toString();
|
||||||
setMessages(prev => [...prev, {
|
setMessages(prev => [...prev, {
|
||||||
id: assistantId,
|
id: assistantId,
|
||||||
@@ -408,49 +401,6 @@ export function ChatInterface() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// Fallback to existing NL2SQL or other skills (e.g. for "表格问答" or "深度问数")
|
|
||||||
const selectedSource = selectedDataSource.split('-')[0];
|
|
||||||
const useUploadSource = Boolean(
|
|
||||||
currentAttachedFile?.url?.startsWith("local://") ||
|
|
||||||
(selectedSource === "upload" && activeDataFile?.url?.startsWith("local://"))
|
|
||||||
);
|
|
||||||
const source = useUploadSource ? "upload" : selectedSource;
|
|
||||||
const fileUrl = useUploadSource ? (currentAttachedFile?.url || activeDataFile?.url) : undefined;
|
|
||||||
const response = await api.post<{
|
|
||||||
sql?: string,
|
|
||||||
result?: unknown,
|
|
||||||
error?: string,
|
|
||||||
chart?: { chart_spec?: ChartSpec | null, reasoning?: string, can_visualize?: boolean, chart_type?: string }
|
|
||||||
}>('/api/v1/agent/nl2sql', {
|
|
||||||
query: messagePayload,
|
|
||||||
source: source,
|
|
||||||
file_url: fileUrl,
|
|
||||||
session_id: activeSessionKey,
|
|
||||||
model_id: selectedModelId
|
|
||||||
}, { signal: controller.signal });
|
|
||||||
|
|
||||||
if (response.error) {
|
|
||||||
setMessages(prev => [...prev, {
|
|
||||||
id: (Date.now() + 1).toString(),
|
|
||||||
role: 'assistant',
|
|
||||||
content: `Error: ${response.error}`
|
|
||||||
}]);
|
|
||||||
} else {
|
|
||||||
const canVisualize = Boolean(response.chart?.can_visualize);
|
|
||||||
const viz = buildMessageViz({
|
|
||||||
sql: response.sql,
|
|
||||||
result: response.result,
|
|
||||||
chart: response.chart,
|
|
||||||
});
|
|
||||||
setMessages(prev => [...prev, {
|
|
||||||
id: (Date.now() + 1).toString(),
|
|
||||||
role: 'assistant',
|
|
||||||
content: `已为你生成 SQL 并查询到 ${viz.rows.length} 行数据。${canVisualize ? '图表已附在回答下方。' : '本次结果不适合图表展示。'}${response.chart?.reasoning ? `\n\n可视化说明:${response.chart.reasoning}` : ''}`,
|
|
||||||
viz,
|
|
||||||
}]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
if (error?.name === "AbortError" || String(error?.message || "").toLowerCase().includes("aborted")) {
|
if (error?.name === "AbortError" || String(error?.message || "").toLowerCase().includes("aborted")) {
|
||||||
setMessages((prev) =>
|
setMessages((prev) =>
|
||||||
@@ -598,20 +548,6 @@ export function ChatInterface() {
|
|||||||
|
|
||||||
<div className="flex items-center justify-between mt-4 pt-2 border-t border-zinc-50">
|
<div className="flex items-center justify-between mt-4 pt-2 border-t border-zinc-50">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{capabilities.map((cap) => (
|
|
||||||
<button
|
|
||||||
key={cap.label}
|
|
||||||
onClick={() => setSelectedCapability(cap.label)}
|
|
||||||
className={`flex items-center gap-1.5 px-3 py-1.5 rounded-full text-xs font-medium transition-colors ${
|
|
||||||
selectedCapability === cap.label
|
|
||||||
? `${cap.bg} ${cap.color} ring-1 ring-${cap.color.split('-')[1]}-200 shadow-sm`
|
|
||||||
: 'bg-zinc-50 text-zinc-500 hover:bg-zinc-100'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<cap.icon className="h-3.5 w-3.5" />
|
|
||||||
{cap.label}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
|
|||||||
Reference in New Issue
Block a user