feat(web): add pipeline resume from conversation context
This commit is contained in:
@@ -1,13 +1,16 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useAppStore } from '@/store';
|
import { useAppStore } from '@/store';
|
||||||
import { useChat } from '@/hooks/useChat';
|
import { useChat } from '@/hooks/useChat';
|
||||||
import { ChatMessage } from './ChatMessage';
|
import { ChatMessage } from './ChatMessage';
|
||||||
import { ChatInput } from './ChatInput';
|
import { ChatInput } from './ChatInput';
|
||||||
import { ScrollArea } from '@/components/ui/scroll-area';
|
import { ScrollArea } from '@/components/ui/scroll-area';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { RefreshCw } from 'lucide-react';
|
||||||
|
|
||||||
export function ChatView() {
|
export function ChatView() {
|
||||||
const { activeConversationId, conversations, setConversations } = useAppStore();
|
const { activeConversationId, conversations, setConversations, selectedAccountId } = useAppStore();
|
||||||
const { messages, connected, send, createConversation } = useChat(activeConversationId);
|
const { messages, connected, send, createConversation } = useChat(activeConversationId);
|
||||||
|
const [manifestPath, setManifestPath] = useState<string | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetch('/api/pipeline/conversations')
|
fetch('/api/pipeline/conversations')
|
||||||
@@ -16,8 +19,19 @@ export function ChatView() {
|
|||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
}, [messages]);
|
}, [messages]);
|
||||||
|
|
||||||
|
// Check for associated manifest in messages
|
||||||
|
useEffect(() => {
|
||||||
|
const toolMsgs = messages.filter((m) => m.role === 'tool');
|
||||||
|
if (toolMsgs.length > 0) {
|
||||||
|
try {
|
||||||
|
const lastTool = JSON.parse(toolMsgs[toolMsgs.length - 1].content);
|
||||||
|
if (lastTool.manifest) setManifestPath(lastTool.manifest);
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
}, [messages]);
|
||||||
|
|
||||||
const handleNewConversation = () => {
|
const handleNewConversation = () => {
|
||||||
createConversation('新对话');
|
createConversation('新对话', selectedAccountId || undefined);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
fetch('/api/pipeline/conversations')
|
fetch('/api/pipeline/conversations')
|
||||||
.then((r) => r.json())
|
.then((r) => r.json())
|
||||||
@@ -25,6 +39,19 @@ export function ChatView() {
|
|||||||
}, 300);
|
}, 300);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleResume = async () => {
|
||||||
|
if (!manifestPath) return;
|
||||||
|
try {
|
||||||
|
await fetch('/api/pipeline/resume', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ manifest: manifestPath }),
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Resume failed:', e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (!activeConversationId) {
|
if (!activeConversationId) {
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 flex flex-col items-center justify-center gap-4 text-zinc-500">
|
<div className="flex-1 flex flex-col items-center justify-center gap-4 text-zinc-500">
|
||||||
@@ -41,10 +68,18 @@ export function ChatView() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex-1 flex flex-col">
|
<div className="flex-1 flex flex-col">
|
||||||
<div className="px-4 py-2 border-b border-zinc-800 flex items-center gap-2">
|
<div className="px-4 py-2 border-b border-zinc-800 flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
<div className={`w-2 h-2 rounded-full ${connected ? 'bg-green-500' : 'bg-red-500'}`} />
|
<div className={`w-2 h-2 rounded-full ${connected ? 'bg-green-500' : 'bg-red-500'}`} />
|
||||||
<span className="text-xs text-zinc-500">{connected ? '已连接' : '连接中...'}</span>
|
<span className="text-xs text-zinc-500">{connected ? '已连接' : '连接中...'}</span>
|
||||||
</div>
|
</div>
|
||||||
|
{manifestPath && (
|
||||||
|
<Button size="sm" variant="outline" className="h-7 text-xs" onClick={handleResume}>
|
||||||
|
<RefreshCw size={12} className="mr-1" />
|
||||||
|
断点续跑
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<ScrollArea className="flex-1 px-4 py-4">
|
<ScrollArea className="flex-1 px-4 py-4">
|
||||||
{messages.map((msg) => (
|
{messages.map((msg) => (
|
||||||
<ChatMessage key={msg.id} message={msg} />
|
<ChatMessage key={msg.id} message={msg} />
|
||||||
|
|||||||
Reference in New Issue
Block a user