release: bump version to 0.25.1
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "planet-frontend",
|
||||
"version": "0.25.0",
|
||||
"version": "0.25.1",
|
||||
"private": true,
|
||||
"packageManager": "bun@1",
|
||||
"dependencies": {
|
||||
|
||||
@@ -23,6 +23,8 @@ import packageJson from '../../../package.json'
|
||||
|
||||
const { Sider, Content } = Layout
|
||||
const { Text } = Typography
|
||||
const DEFAULT_OPEN_MENU_KEY = 'collection'
|
||||
let cachedOpenKeys: string[] = [DEFAULT_OPEN_MENU_KEY]
|
||||
|
||||
interface AppLayoutProps {
|
||||
children: ReactNode
|
||||
@@ -33,7 +35,7 @@ function AppLayout({ children }: AppLayoutProps) {
|
||||
const navigate = useNavigate()
|
||||
const { user, logout } = useAuthStore()
|
||||
const [collapsed, setCollapsed] = useState(false)
|
||||
const [openKeys, setOpenKeys] = useState<string[]>(['collection'])
|
||||
const [openKeys, setOpenKeys] = useState<string[]>(cachedOpenKeys)
|
||||
const showBanner = true
|
||||
const appVersion = `v${packageJson.version}`
|
||||
|
||||
@@ -91,6 +93,11 @@ function AppLayout({ children }: AppLayoutProps) {
|
||||
return location.pathname
|
||||
}, [location.pathname])
|
||||
|
||||
const updateOpenKeys = (nextKeys: string[]) => {
|
||||
cachedOpenKeys = nextKeys
|
||||
setOpenKeys(nextKeys)
|
||||
}
|
||||
|
||||
return (
|
||||
<Layout className="dashboard-layout">
|
||||
<Sider
|
||||
@@ -101,7 +108,10 @@ function AppLayout({ children }: AppLayoutProps) {
|
||||
onCollapse={(nextCollapsed) => {
|
||||
setCollapsed(nextCollapsed)
|
||||
if (nextCollapsed) {
|
||||
setOpenKeys([])
|
||||
return
|
||||
}
|
||||
if (!openKeys.length) {
|
||||
updateOpenKeys([DEFAULT_OPEN_MENU_KEY])
|
||||
}
|
||||
}}
|
||||
className="dashboard-sider"
|
||||
@@ -129,7 +139,7 @@ function AppLayout({ children }: AppLayoutProps) {
|
||||
openKeys={collapsed ? [] : openKeys}
|
||||
items={menuItems}
|
||||
onOpenChange={(keys) => {
|
||||
setOpenKeys(keys as string[])
|
||||
updateOpenKeys(keys as string[])
|
||||
}}
|
||||
onClick={({ key }) => {
|
||||
if (key !== selectedKey) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
|
||||
import { ApiOutlined, ArrowDownOutlined, ArrowUpOutlined, BorderOutlined, CopyOutlined, EditOutlined, InfoCircleOutlined, RedoOutlined, SettingOutlined, SyncOutlined } from '@ant-design/icons'
|
||||
import { ApiOutlined, ArrowDownOutlined, ArrowUpOutlined, BorderOutlined, CopyOutlined, EditOutlined, InfoCircleOutlined, RedoOutlined, RobotOutlined, SettingOutlined, SyncOutlined, UserOutlined } from '@ant-design/icons'
|
||||
import {
|
||||
Alert,
|
||||
Avatar,
|
||||
@@ -19,7 +19,6 @@ import {
|
||||
message,
|
||||
} from 'antd'
|
||||
import axios from 'axios'
|
||||
import { RobotOutlined, UserOutlined } from '@ant-design/icons'
|
||||
|
||||
import AppLayout from '../../components/AppLayout/AppLayout'
|
||||
import MarkdownRenderer from '../../components/MarkdownRenderer/MarkdownRenderer'
|
||||
@@ -31,6 +30,17 @@ const PLAYGROUND_PROVIDER_STATUS_STORAGE_KEY = 'playground-provider-status'
|
||||
const PLAYGROUND_REMOTE_SESSION_KEY = 'default'
|
||||
const THREAD_POLL_INTERVAL_MS = 1200
|
||||
const SCROLL_BOTTOM_THRESHOLD_PX = 120
|
||||
const PLAYGROUND_SYSTEM_MESSAGE: PlaygroundMessage = {
|
||||
id: 'playground-system-intro',
|
||||
role: 'system',
|
||||
title: 'Playground 已就绪',
|
||||
content: '这里是 AI 测试台。聊天记录与执行状态现在以后台数据库为准,刷新后会恢复到真实进度。',
|
||||
}
|
||||
const EMPTY_ANALYSIS_META: AnalysisRunMeta = {
|
||||
requestId: null,
|
||||
durationMs: null,
|
||||
completedAt: null,
|
||||
}
|
||||
|
||||
interface AIProviderStatus {
|
||||
provider: string
|
||||
@@ -217,22 +227,11 @@ function Playground() {
|
||||
const [objective, setObjective] = useState(PLAYGROUND_PRESETS[0].values.objective)
|
||||
const [constraints, setConstraints] = useState(PLAYGROUND_PRESETS[0].values.constraints || '')
|
||||
const [inputValue, setInputValue] = useState(PLAYGROUND_PRESETS[0].values.message)
|
||||
const [messages, setMessages] = useState<PlaygroundMessage[]>([
|
||||
{
|
||||
id: 'playground-system-intro',
|
||||
role: 'system',
|
||||
title: 'Playground 已就绪',
|
||||
content: '这里是 AI 测试台。选择一个快速预设,补充输入消息,然后发送一次真实后端链路请求。',
|
||||
},
|
||||
])
|
||||
const [messages, setMessages] = useState<PlaygroundMessage[]>([PLAYGROUND_SYSTEM_MESSAGE])
|
||||
const [analysis, setAnalysis] = useState<SituationalAnalysisResponse | null>(null)
|
||||
const [detailOpen, setDetailOpen] = useState(false)
|
||||
const [latestAnalysisMessageId, setLatestAnalysisMessageId] = useState<string | null>(null)
|
||||
const [analysisMeta, setAnalysisMeta] = useState<AnalysisRunMeta>({
|
||||
requestId: null,
|
||||
durationMs: null,
|
||||
completedAt: null,
|
||||
})
|
||||
const [analysisMeta, setAnalysisMeta] = useState<AnalysisRunMeta>(EMPTY_ANALYSIS_META)
|
||||
const [requestPending, setRequestPending] = useState(false)
|
||||
const [editingMessageId, setEditingMessageId] = useState<string | null>(null)
|
||||
const [editingContent, setEditingContent] = useState('')
|
||||
@@ -266,24 +265,46 @@ function Playground() {
|
||||
setInputValue(sessionState.inputValue || '')
|
||||
setHelpExpanded(sessionState.helpExpanded ?? true)
|
||||
const remoteMessages = thread.messages.map(toPlaygroundMessage)
|
||||
setMessages([
|
||||
{
|
||||
id: 'playground-system-intro',
|
||||
role: 'system',
|
||||
title: 'Playground 已就绪',
|
||||
content: '这里是 AI 测试台。聊天记录与执行状态现在以后台数据库为准,刷新后会恢复到真实进度。',
|
||||
},
|
||||
...remoteMessages,
|
||||
])
|
||||
setMessages([PLAYGROUND_SYSTEM_MESSAGE, ...remoteMessages])
|
||||
setLatestAnalysisMessageId(sessionState.latestAnalysisMessageId || null)
|
||||
setAnalysis((sessionState.analysis as SituationalAnalysisResponse | null) || null)
|
||||
setAnalysisMeta(sessionState.analysisMeta || {
|
||||
requestId: null,
|
||||
durationMs: null,
|
||||
completedAt: null,
|
||||
setAnalysisMeta(sessionState.analysisMeta || EMPTY_ANALYSIS_META)
|
||||
}
|
||||
|
||||
const authHeaders = (includeJson = false): HeadersInit => ({
|
||||
...(includeJson ? { 'Content-Type': 'application/json' } : {}),
|
||||
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
||||
})
|
||||
|
||||
const applyActionResponse = (data: { messages: PlaygroundApiMessage[]; session: PlaygroundSessionRecord }) => {
|
||||
applyThreadSnapshot({
|
||||
session: data.session,
|
||||
messages: data.messages,
|
||||
})
|
||||
}
|
||||
|
||||
const fetchPlaygroundThread = async () => {
|
||||
const res = await fetch(`${API_BASE_URL}/ai/playground/thread?session_key=${encodeURIComponent(PLAYGROUND_REMOTE_SESSION_KEY)}`, {
|
||||
headers: authHeaders(),
|
||||
})
|
||||
if (!res.ok) {
|
||||
throw new Error('THREAD_FETCH_FAILED')
|
||||
}
|
||||
return (await res.json()) as PlaygroundThreadRecord | null
|
||||
}
|
||||
|
||||
const postPlaygroundAction = async <TBody,>(path: string, body: TBody) => {
|
||||
const res = await fetch(`${API_BASE_URL}${path}`, {
|
||||
method: 'POST',
|
||||
headers: authHeaders(true),
|
||||
body: JSON.stringify(body),
|
||||
})
|
||||
if (!res.ok) {
|
||||
throw new Error(`PLAYGROUND_ACTION_FAILED:${path}`)
|
||||
}
|
||||
return (await res.json()) as { messages: PlaygroundApiMessage[]; session: PlaygroundSessionRecord }
|
||||
}
|
||||
|
||||
const scrollToBottom = (behavior: ScrollBehavior = 'smooth') => {
|
||||
const container = messagesContainerRef.current
|
||||
if (!container) {
|
||||
@@ -327,20 +348,12 @@ function Playground() {
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch(`${API_BASE_URL}/ai/playground/thread?session_key=${encodeURIComponent(PLAYGROUND_REMOTE_SESSION_KEY)}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
})
|
||||
if (res.ok) {
|
||||
const thread = (await res.json()) as PlaygroundThreadRecord | null
|
||||
applyThreadSnapshot(thread)
|
||||
}
|
||||
const thread = await fetchPlaygroundThread()
|
||||
applyThreadSnapshot(thread)
|
||||
} catch {
|
||||
if (!options?.silent) {
|
||||
messageApi.error('Playground 会话加载失败')
|
||||
}
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,25 +430,11 @@ function Playground() {
|
||||
return
|
||||
}
|
||||
try {
|
||||
const res = await fetch(`${API_BASE_URL}/ai/playground/messages/stop`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
session_key: PLAYGROUND_REMOTE_SESSION_KEY,
|
||||
message_id: activeAssistantMessage.id,
|
||||
}),
|
||||
})
|
||||
if (!res.ok) {
|
||||
throw new Error('STOP_FAILED')
|
||||
}
|
||||
const data = (await res.json()) as { messages: PlaygroundApiMessage[], session: PlaygroundSessionRecord }
|
||||
applyThreadSnapshot({
|
||||
session: data.session,
|
||||
messages: data.messages,
|
||||
const data = await postPlaygroundAction('/ai/playground/messages/stop', {
|
||||
session_key: PLAYGROUND_REMOTE_SESSION_KEY,
|
||||
message_id: activeAssistantMessage.id,
|
||||
})
|
||||
applyActionResponse(data)
|
||||
messageApi.warning('已停止生成,已输出内容会保留')
|
||||
} catch {
|
||||
messageApi.error('停止失败,请稍后再试')
|
||||
@@ -455,30 +454,16 @@ function Playground() {
|
||||
setRequestPending(true)
|
||||
|
||||
try {
|
||||
const res = await fetch(`${API_BASE_URL}/ai/playground/messages`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
||||
},
|
||||
body: JSON.stringify({
|
||||
session_key: PLAYGROUND_REMOTE_SESSION_KEY,
|
||||
title: trimmedTitle,
|
||||
objective: trimmedObjective,
|
||||
constraints,
|
||||
input: trimmedInput,
|
||||
selected_preset_key: selectedPreset.key,
|
||||
help_expanded: helpExpanded,
|
||||
}),
|
||||
})
|
||||
if (!res.ok) {
|
||||
throw new Error('SEND_FAILED')
|
||||
}
|
||||
const data = (await res.json()) as { messages: PlaygroundApiMessage[], session: PlaygroundSessionRecord }
|
||||
applyThreadSnapshot({
|
||||
session: data.session,
|
||||
messages: data.messages,
|
||||
const data = await postPlaygroundAction('/ai/playground/messages', {
|
||||
session_key: PLAYGROUND_REMOTE_SESSION_KEY,
|
||||
title: trimmedTitle,
|
||||
objective: trimmedObjective,
|
||||
constraints,
|
||||
input: trimmedInput,
|
||||
selected_preset_key: selectedPreset.key,
|
||||
help_expanded: helpExpanded,
|
||||
})
|
||||
applyActionResponse(data)
|
||||
setRequestPending(false)
|
||||
void refreshThread({ silent: true })
|
||||
} catch {
|
||||
@@ -521,25 +506,11 @@ function Playground() {
|
||||
return
|
||||
}
|
||||
try {
|
||||
const res = await fetch(`${API_BASE_URL}/ai/playground/messages/resend`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
session_key: PLAYGROUND_REMOTE_SESSION_KEY,
|
||||
user_message_id: entry.id,
|
||||
}),
|
||||
})
|
||||
if (!res.ok) {
|
||||
throw new Error('RESEND_FAILED')
|
||||
}
|
||||
const data = (await res.json()) as { messages: PlaygroundApiMessage[], session: PlaygroundSessionRecord }
|
||||
applyThreadSnapshot({
|
||||
session: data.session,
|
||||
messages: data.messages,
|
||||
const data = await postPlaygroundAction('/ai/playground/messages/resend', {
|
||||
session_key: PLAYGROUND_REMOTE_SESSION_KEY,
|
||||
user_message_id: entry.id,
|
||||
})
|
||||
applyActionResponse(data)
|
||||
messageApi.success('已重试,并清理该消息之后的旧分支')
|
||||
} catch {
|
||||
messageApi.error('重试失败,请稍后再试')
|
||||
@@ -569,40 +540,16 @@ function Playground() {
|
||||
|
||||
try {
|
||||
setEditSaving(true)
|
||||
const editRes = await fetch(`${API_BASE_URL}/ai/playground/messages/edit`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
session_key: PLAYGROUND_REMOTE_SESSION_KEY,
|
||||
user_message_id: entry.id,
|
||||
content: trimmed,
|
||||
}),
|
||||
await postPlaygroundAction('/ai/playground/messages/edit', {
|
||||
session_key: PLAYGROUND_REMOTE_SESSION_KEY,
|
||||
user_message_id: entry.id,
|
||||
content: trimmed,
|
||||
})
|
||||
if (!editRes.ok) {
|
||||
throw new Error('EDIT_FAILED')
|
||||
}
|
||||
const resendRes = await fetch(`${API_BASE_URL}/ai/playground/messages/resend`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
session_key: PLAYGROUND_REMOTE_SESSION_KEY,
|
||||
user_message_id: entry.id,
|
||||
}),
|
||||
})
|
||||
if (!resendRes.ok) {
|
||||
throw new Error('RESEND_AFTER_EDIT_FAILED')
|
||||
}
|
||||
const data = (await resendRes.json()) as { messages: PlaygroundApiMessage[], session: PlaygroundSessionRecord }
|
||||
applyThreadSnapshot({
|
||||
session: data.session,
|
||||
messages: data.messages,
|
||||
const data = await postPlaygroundAction('/ai/playground/messages/resend', {
|
||||
session_key: PLAYGROUND_REMOTE_SESSION_KEY,
|
||||
user_message_id: entry.id,
|
||||
})
|
||||
applyActionResponse(data)
|
||||
setEditingMessageId(null)
|
||||
setEditingContent('')
|
||||
setEditSaving(false)
|
||||
|
||||
Reference in New Issue
Block a user