mirror of
https://github.com/zhayujie/chatgpt-on-wechat.git
synced 2026-07-17 11:07:11 +08:00
feat(desktop): align API client and types with latest web backend endpoints
This commit is contained in:
@@ -1,38 +1,74 @@
|
|||||||
import type { ConfigData, ChannelInfo, SkillInfo, ToolInfo, MemoryItem, SchedulerTask, Attachment } from '../types'
|
import type {
|
||||||
|
ConfigData,
|
||||||
|
ChannelInfo,
|
||||||
|
ChannelAction,
|
||||||
|
SkillInfo,
|
||||||
|
ToolInfo,
|
||||||
|
MemoryItem,
|
||||||
|
MemoryCategory,
|
||||||
|
MemoryPage,
|
||||||
|
SchedulerTask,
|
||||||
|
Attachment,
|
||||||
|
SessionsPage,
|
||||||
|
HistoryPage,
|
||||||
|
ModelsData,
|
||||||
|
ModelsAction,
|
||||||
|
KnowledgeList,
|
||||||
|
KnowledgeGraph,
|
||||||
|
KnowledgeAction,
|
||||||
|
} from '../types'
|
||||||
|
|
||||||
|
interface ApiResult {
|
||||||
|
status: string
|
||||||
|
message?: string
|
||||||
|
}
|
||||||
|
|
||||||
class ApiClient {
|
class ApiClient {
|
||||||
private baseUrl: string = 'http://127.0.0.1:9899'
|
private baseUrl = 'http://127.0.0.1:9899'
|
||||||
|
|
||||||
setBaseUrl(url: string) {
|
setBaseUrl(url: string) {
|
||||||
this.baseUrl = url
|
this.baseUrl = url
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getBaseUrl() {
|
||||||
|
return this.baseUrl
|
||||||
|
}
|
||||||
|
|
||||||
private async request<T>(path: string, options?: RequestInit): Promise<T> {
|
private async request<T>(path: string, options?: RequestInit): Promise<T> {
|
||||||
const res = await fetch(`${this.baseUrl}${path}`, {
|
const res = await fetch(`${this.baseUrl}${path}`, {
|
||||||
...options,
|
...options,
|
||||||
|
// Send cookies for future web_password auth support
|
||||||
|
credentials: 'include',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
...options?.headers,
|
...options?.headers,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
throw new Error(`HTTP ${res.status}: ${res.statusText}`)
|
throw new Error(`HTTP ${res.status}: ${res.statusText}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.json()
|
return res.json()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chat
|
// ---------------------------------------------------------
|
||||||
|
// Chat / messages
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
|
||||||
async sendMessage(
|
async sendMessage(
|
||||||
sessionId: string,
|
sessionId: string,
|
||||||
message: string,
|
message: string,
|
||||||
stream: boolean = true,
|
opts?: { stream?: boolean; attachments?: Attachment[]; isVoice?: boolean; lang?: string }
|
||||||
attachments?: Attachment[]
|
): Promise<{ status: string; request_id: string; stream: boolean; inline_reply?: string }> {
|
||||||
): Promise<{ status: string; request_id: string; stream: boolean }> {
|
|
||||||
return this.request('/message', {
|
return this.request('/message', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({ session_id: sessionId, message, stream, attachments }),
|
body: JSON.stringify({
|
||||||
|
session_id: sessionId,
|
||||||
|
message,
|
||||||
|
stream: opts?.stream ?? true,
|
||||||
|
attachments: opts?.attachments,
|
||||||
|
is_voice: opts?.isVoice ?? false,
|
||||||
|
lang: opts?.lang,
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,10 +85,38 @@ class ApiClient {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async cancel(opts: { requestId?: string; sessionId?: string; lang?: string }): Promise<{ status: string; cancelled: number }> {
|
||||||
|
return this.request('/cancel', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ request_id: opts.requestId, session_id: opts.sessionId, lang: opts.lang }),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
createSSEStream(requestId: string): EventSource {
|
createSSEStream(requestId: string): EventSource {
|
||||||
return new EventSource(`${this.baseUrl}/stream?request_id=${requestId}`)
|
return new EventSource(`${this.baseUrl}/stream?request_id=${requestId}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async deleteMessage(opts: {
|
||||||
|
sessionId: string
|
||||||
|
userSeq: number
|
||||||
|
deleteUser?: boolean
|
||||||
|
cascade?: boolean
|
||||||
|
}): Promise<{ status: string; deleted: number }> {
|
||||||
|
return this.request('/api/messages/delete', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
session_id: opts.sessionId,
|
||||||
|
user_seq: opts.userSeq,
|
||||||
|
delete_user: opts.deleteUser ?? true,
|
||||||
|
cascade: opts.cascade ?? false,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// Upload / files
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
|
||||||
async uploadFile(file: File, sessionId?: string): Promise<{
|
async uploadFile(file: File, sessionId?: string): Promise<{
|
||||||
status: string
|
status: string
|
||||||
file_path: string
|
file_path: string
|
||||||
@@ -62,49 +126,138 @@ class ApiClient {
|
|||||||
}> {
|
}> {
|
||||||
const formData = new FormData()
|
const formData = new FormData()
|
||||||
formData.append('file', file)
|
formData.append('file', file)
|
||||||
if (sessionId) {
|
if (sessionId) formData.append('session_id', sessionId)
|
||||||
formData.append('session_id', sessionId)
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await fetch(`${this.baseUrl}/upload`, {
|
const res = await fetch(`${this.baseUrl}/upload`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: formData,
|
body: formData,
|
||||||
|
credentials: 'include',
|
||||||
})
|
})
|
||||||
|
|
||||||
return res.json()
|
return res.json()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config
|
getFileUrl(previewUrl: string): string {
|
||||||
async getConfig(): Promise<ConfigData> {
|
if (/^https?:\/\//.test(previewUrl)) return previewUrl
|
||||||
const data = await this.request<{ status: string } & ConfigData>('/config')
|
return `${this.baseUrl}${previewUrl}`
|
||||||
return data
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateConfig(updates: Partial<ConfigData>): Promise<{ status: string; applied: Record<string, unknown> }> {
|
getServeFileUrl(absPath: string): string {
|
||||||
|
return `${this.baseUrl}/api/file?path=${encodeURIComponent(absPath)}`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// Sessions
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
|
||||||
|
async getSessions(page = 1, pageSize = 50): Promise<SessionsPage> {
|
||||||
|
return this.request<{ status: string } & SessionsPage>(`/api/sessions?page=${page}&page_size=${pageSize}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteSession(sessionId: string): Promise<ApiResult> {
|
||||||
|
return this.request(`/api/sessions/${encodeURIComponent(sessionId)}`, { method: 'DELETE' })
|
||||||
|
}
|
||||||
|
|
||||||
|
async renameSession(sessionId: string, title: string): Promise<ApiResult> {
|
||||||
|
return this.request(`/api/sessions/${encodeURIComponent(sessionId)}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
body: JSON.stringify({ title }),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async generateSessionTitle(sessionId: string, userMessage: string, assistantReply?: string): Promise<{ status: string; title: string }> {
|
||||||
|
return this.request(`/api/sessions/${encodeURIComponent(sessionId)}/generate_title`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ user_message: userMessage, assistant_reply: assistantReply }),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async clearContext(sessionId: string): Promise<{ status: string; context_start_seq: number }> {
|
||||||
|
return this.request(`/api/sessions/${encodeURIComponent(sessionId)}/clear_context`, { method: 'POST' })
|
||||||
|
}
|
||||||
|
|
||||||
|
async getHistory(sessionId: string, page = 1, pageSize = 20): Promise<HistoryPage> {
|
||||||
|
return this.request<{ status: string } & HistoryPage>(
|
||||||
|
`/api/history?session_id=${encodeURIComponent(sessionId)}&page=${page}&page_size=${pageSize}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// Config
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
|
||||||
|
async getConfig(): Promise<ConfigData> {
|
||||||
|
return this.request<{ status: string } & ConfigData>('/config')
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateConfig(updates: Record<string, unknown>): Promise<{ status: string; applied: Record<string, unknown> }> {
|
||||||
return this.request('/config', {
|
return this.request('/config', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({ updates }),
|
body: JSON.stringify({ updates }),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// Models console
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
|
||||||
|
async getModels(): Promise<ModelsData> {
|
||||||
|
return this.request<{ status: string } & ModelsData>('/api/models')
|
||||||
|
}
|
||||||
|
|
||||||
|
async modelsAction(action: ModelsAction): Promise<Record<string, unknown> & { status: string }> {
|
||||||
|
return this.request('/api/models', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(action),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
// Channels
|
// Channels
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
|
||||||
async getChannels(): Promise<ChannelInfo[]> {
|
async getChannels(): Promise<ChannelInfo[]> {
|
||||||
const data = await this.request<{ status: string; channels: ChannelInfo[] }>('/api/channels')
|
const data = await this.request<{ status: string; channels: ChannelInfo[] }>('/api/channels')
|
||||||
return data.channels
|
return data.channels
|
||||||
}
|
}
|
||||||
|
|
||||||
async channelAction(
|
async channelAction(
|
||||||
action: 'save' | 'connect' | 'disconnect',
|
action: ChannelAction,
|
||||||
channel: string,
|
channel: string,
|
||||||
config?: Record<string, unknown>
|
config?: Record<string, unknown>
|
||||||
): Promise<{ status: string }> {
|
): Promise<Record<string, unknown> & { status: string }> {
|
||||||
return this.request('/api/channels', {
|
return this.request('/api/channels', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({ action, channel, config }),
|
body: JSON.stringify({ action, channel, config }),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tools & Skills
|
// Weixin QR login
|
||||||
|
async getWeixinQr(): Promise<{ status: string; qrcode_url?: string; qr_image?: string; source?: string; message?: string }> {
|
||||||
|
return this.request('/api/weixin/qrlogin')
|
||||||
|
}
|
||||||
|
|
||||||
|
async weixinQrAction(action: 'poll' | 'refresh'): Promise<Record<string, unknown> & { status: string }> {
|
||||||
|
return this.request('/api/weixin/qrlogin', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ action }),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Feishu one-click register
|
||||||
|
async getFeishuRegister(): Promise<{ status: string; qrcode_url?: string; qr_image?: string; expire_in?: number; message?: string }> {
|
||||||
|
return this.request('/api/feishu/register')
|
||||||
|
}
|
||||||
|
|
||||||
|
async feishuRegisterPoll(): Promise<Record<string, unknown> & { status: string }> {
|
||||||
|
return this.request('/api/feishu/register', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ action: 'poll' }),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// Tools & skills
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
|
||||||
async getTools(): Promise<ToolInfo[]> {
|
async getTools(): Promise<ToolInfo[]> {
|
||||||
const data = await this.request<{ status: string; tools: ToolInfo[] }>('/api/tools')
|
const data = await this.request<{ status: string; tools: ToolInfo[] }>('/api/tools')
|
||||||
return data.tools
|
return data.tools
|
||||||
@@ -115,60 +268,136 @@ class ApiClient {
|
|||||||
return data.skills
|
return data.skills
|
||||||
}
|
}
|
||||||
|
|
||||||
async toggleSkill(name: string, action: 'open' | 'close'): Promise<{ status: string }> {
|
async toggleSkill(name: string, action: 'open' | 'close'): Promise<ApiResult> {
|
||||||
return this.request('/api/skills', {
|
return this.request('/api/skills', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify({ action, name }),
|
body: JSON.stringify({ action, name }),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
// Memory
|
// Memory
|
||||||
async getMemoryList(page: number = 1, pageSize: number = 20): Promise<{ list: MemoryItem[]; total: number }> {
|
// ---------------------------------------------------------
|
||||||
const data = await this.request<{ status: string; list: MemoryItem[]; total: number }>(
|
|
||||||
`/api/memory?page=${page}&page_size=${pageSize}`
|
async getMemoryList(page = 1, pageSize = 20, category: MemoryCategory = 'memory'): Promise<MemoryPage> {
|
||||||
|
return this.request<{ status: string } & MemoryPage>(
|
||||||
|
`/api/memory?page=${page}&page_size=${pageSize}&category=${category}`
|
||||||
)
|
)
|
||||||
return { list: data.list, total: data.total }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getMemoryContent(filename: string): Promise<string> {
|
async getMemoryContent(filename: string, category: MemoryCategory = 'memory'): Promise<string> {
|
||||||
const data = await this.request<{ status: string; content: string }>(
|
const data = await this.request<{ status: string; content: string }>(
|
||||||
`/api/memory/content?filename=${encodeURIComponent(filename)}`
|
`/api/memory/content?filename=${encodeURIComponent(filename)}&category=${category}`
|
||||||
)
|
)
|
||||||
return data.content
|
return data.content
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// Knowledge
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
|
||||||
|
async getKnowledgeList(): Promise<KnowledgeList> {
|
||||||
|
return this.request<{ status: string } & KnowledgeList>('/api/knowledge/list')
|
||||||
|
}
|
||||||
|
|
||||||
|
async readKnowledge(path: string): Promise<{ status: string; content: string; path: string }> {
|
||||||
|
return this.request(`/api/knowledge/read?path=${encodeURIComponent(path)}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
async getKnowledgeGraph(): Promise<KnowledgeGraph> {
|
||||||
|
return this.request<KnowledgeGraph>('/api/knowledge/graph')
|
||||||
|
}
|
||||||
|
|
||||||
|
async knowledgeAction(req: KnowledgeAction): Promise<Record<string, unknown> & { status: string }> {
|
||||||
|
return this.request('/api/knowledge/action', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(req),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
// Scheduler
|
// Scheduler
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
|
||||||
async getSchedulerTasks(): Promise<SchedulerTask[]> {
|
async getSchedulerTasks(): Promise<SchedulerTask[]> {
|
||||||
const data = await this.request<{ status: string; tasks: SchedulerTask[] }>('/api/scheduler')
|
const data = await this.request<{ status: string; tasks: SchedulerTask[] }>('/api/scheduler')
|
||||||
return data.tasks
|
return data.tasks
|
||||||
}
|
}
|
||||||
|
|
||||||
// History
|
async toggleTask(taskId: string, enabled: boolean): Promise<{ status: string; task: SchedulerTask }> {
|
||||||
async getHistory(
|
return this.request('/api/scheduler/toggle', {
|
||||||
sessionId: string,
|
method: 'POST',
|
||||||
page: number = 1,
|
body: JSON.stringify({ task_id: taskId, enabled }),
|
||||||
pageSize: number = 20
|
})
|
||||||
): Promise<{ messages: ChatMessage[]; has_more: boolean }> {
|
|
||||||
const data = await this.request<{ status: string; messages: ChatMessage[]; has_more: boolean }>(
|
|
||||||
`/api/history?session_id=${sessionId}&page=${page}&page_size=${pageSize}`
|
|
||||||
)
|
|
||||||
return { messages: data.messages, has_more: data.has_more }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logs SSE
|
async updateTask(taskId: string, updates: Partial<Pick<SchedulerTask, 'name' | 'enabled' | 'schedule' | 'action'>>): Promise<{ status: string; task: SchedulerTask }> {
|
||||||
|
return this.request('/api/scheduler/update', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ task_id: taskId, ...updates }),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteTask(taskId: string): Promise<ApiResult> {
|
||||||
|
return this.request('/api/scheduler/delete', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ task_id: taskId }),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// Voice
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
|
||||||
|
async voiceAsr(audio: File | Blob): Promise<{ status: string; text?: string; audio_url?: string; message?: string }> {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('file', audio, 'recording.webm')
|
||||||
|
const res = await fetch(`${this.baseUrl}/api/voice/asr`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData,
|
||||||
|
credentials: 'include',
|
||||||
|
})
|
||||||
|
return res.json()
|
||||||
|
}
|
||||||
|
|
||||||
|
async voiceTts(text: string, sessionId?: string): Promise<{ status: string; audio_url?: string; message?: string }> {
|
||||||
|
return this.request('/api/voice/tts', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ text, session_id: sessionId }),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
// Logs / version
|
||||||
|
// ---------------------------------------------------------
|
||||||
|
|
||||||
createLogStream(): EventSource {
|
createLogStream(): EventSource {
|
||||||
return new EventSource(`${this.baseUrl}/api/logs`)
|
return new EventSource(`${this.baseUrl}/api/logs`)
|
||||||
}
|
}
|
||||||
|
|
||||||
getFileUrl(previewUrl: string): string {
|
async getVersion(): Promise<string> {
|
||||||
return `${this.baseUrl}${previewUrl}`
|
const data = await this.request<{ version: string }>('/api/version')
|
||||||
|
return data.version
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
interface ChatMessage {
|
// ---------------------------------------------------------
|
||||||
role: string
|
// Auth (web_password) — placeholder for future use
|
||||||
content: string
|
// ---------------------------------------------------------
|
||||||
timestamp: number
|
|
||||||
|
async authCheck(): Promise<{ status: string; auth_required: boolean; authenticated?: boolean }> {
|
||||||
|
return this.request('/auth/check')
|
||||||
|
}
|
||||||
|
|
||||||
|
async authLogin(password: string): Promise<ApiResult> {
|
||||||
|
return this.request('/auth/login', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ password }),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async authLogout(): Promise<ApiResult> {
|
||||||
|
return this.request('/auth/logout', { method: 'POST' })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const apiClient = new ApiClient()
|
export const apiClient = new ApiClient()
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
// ============================================================
|
||||||
|
// Electron bridge
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
export interface ElectronAPI {
|
export interface ElectronAPI {
|
||||||
getBackendPort: () => Promise<number | null>
|
getBackendPort: () => Promise<number | null>
|
||||||
getBackendStatus: () => Promise<string>
|
getBackendStatus: () => Promise<string>
|
||||||
@@ -6,6 +10,11 @@ export interface ElectronAPI {
|
|||||||
selectFile: (filters?: { name: string; extensions: string[] }[]) => Promise<string | null>
|
selectFile: (filters?: { name: string; extensions: string[] }[]) => Promise<string | null>
|
||||||
onBackendStatus: (callback: (data: BackendStatusEvent) => void) => void
|
onBackendStatus: (callback: (data: BackendStatusEvent) => void) => void
|
||||||
onBackendLog: (callback: (line: string) => void) => void
|
onBackendLog: (callback: (line: string) => void) => void
|
||||||
|
windowMinimize: () => Promise<void>
|
||||||
|
windowMaximize: () => Promise<boolean>
|
||||||
|
windowClose: () => Promise<void>
|
||||||
|
windowIsMaximized: () => Promise<boolean>
|
||||||
|
onMaximizeChange: (callback: (maximized: boolean) => void) => void
|
||||||
platform: string
|
platform: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -15,32 +24,166 @@ export interface BackendStatusEvent {
|
|||||||
error?: string
|
error?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Chat / messages / streaming
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
export type Role = 'user' | 'assistant'
|
||||||
|
|
||||||
|
/** A single ordered step inside an assistant turn (matches backend history). */
|
||||||
|
export interface MessageStep {
|
||||||
|
type: 'thinking' | 'content' | 'tool'
|
||||||
|
content?: string
|
||||||
|
// tool step fields
|
||||||
|
id?: string
|
||||||
|
name?: string
|
||||||
|
arguments?: Record<string, unknown>
|
||||||
|
result?: string
|
||||||
|
is_error?: boolean
|
||||||
|
status?: string
|
||||||
|
execution_time?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Local UI message model (superset of backend history message). */
|
||||||
export interface ChatMessage {
|
export interface ChatMessage {
|
||||||
id: string
|
id: string
|
||||||
role: 'user' | 'assistant'
|
role: Role
|
||||||
content: string
|
content: string
|
||||||
|
/** Unix seconds. Backend history uses `created_at`; we normalize to `timestamp`. */
|
||||||
timestamp: number
|
timestamp: number
|
||||||
attachments?: Attachment[]
|
attachments?: Attachment[]
|
||||||
|
/** Ordered steps (thinking / content / tool). Preferred over legacy toolCalls. */
|
||||||
|
steps?: MessageStep[]
|
||||||
|
/** Legacy live-stream tool events (kept for backward compat during streaming). */
|
||||||
toolCalls?: ToolCall[]
|
toolCalls?: ToolCall[]
|
||||||
|
/** Reasoning text streamed via `reasoning` SSE events. */
|
||||||
|
reasoning?: string
|
||||||
|
/** Sequence numbers from backend (for delete/regenerate). */
|
||||||
|
userSeq?: number
|
||||||
|
botSeq?: number
|
||||||
|
/** Self-evolution bubble flag. */
|
||||||
|
kind?: 'evolution'
|
||||||
|
extras?: Record<string, unknown>
|
||||||
isStreaming?: boolean
|
isStreaming?: boolean
|
||||||
|
isCancelled?: boolean
|
||||||
|
error?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Attachment {
|
export interface Attachment {
|
||||||
file_path: string
|
file_path: string
|
||||||
file_name: string
|
file_name: string
|
||||||
file_type: 'image' | 'video' | 'file'
|
file_type: 'image' | 'video' | 'file' | 'directory'
|
||||||
preview_url?: string
|
preview_url?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Live tool event during SSE streaming. */
|
||||||
export interface ToolCall {
|
export interface ToolCall {
|
||||||
type: 'tool_start' | 'tool_end'
|
type: 'tool_start' | 'tool_end' | 'tool_progress'
|
||||||
tool: string
|
tool: string
|
||||||
|
tool_call_id?: string
|
||||||
arguments?: Record<string, unknown>
|
arguments?: Record<string, unknown>
|
||||||
result?: string
|
result?: string
|
||||||
status?: string
|
status?: string
|
||||||
execution_time?: number
|
execution_time?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** All SSE event types emitted on /stream. */
|
||||||
|
export type StreamEventType =
|
||||||
|
| 'delta'
|
||||||
|
| 'reasoning'
|
||||||
|
| 'tool_start'
|
||||||
|
| 'tool_progress'
|
||||||
|
| 'tool_end'
|
||||||
|
| 'message_end'
|
||||||
|
| 'phase'
|
||||||
|
| 'file_to_send'
|
||||||
|
| 'image'
|
||||||
|
| 'video'
|
||||||
|
| 'file'
|
||||||
|
| 'text'
|
||||||
|
| 'done'
|
||||||
|
| 'cancelled'
|
||||||
|
| 'voice_attach'
|
||||||
|
| 'error'
|
||||||
|
|
||||||
|
export interface StreamEvent {
|
||||||
|
type: StreamEventType
|
||||||
|
content?: string
|
||||||
|
tool?: string
|
||||||
|
tool_call_id?: string
|
||||||
|
arguments?: Record<string, unknown>
|
||||||
|
status?: string
|
||||||
|
result?: string
|
||||||
|
execution_time?: number
|
||||||
|
has_tool_calls?: boolean
|
||||||
|
path?: string
|
||||||
|
file_name?: string
|
||||||
|
file_type?: string
|
||||||
|
web_url?: string
|
||||||
|
audio_url?: string
|
||||||
|
request_id?: string
|
||||||
|
timestamp?: number
|
||||||
|
user_seq?: number
|
||||||
|
bot_seq?: number
|
||||||
|
message?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Sessions / history
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
export interface SessionItem {
|
||||||
|
session_id: string
|
||||||
|
title: string
|
||||||
|
created_at: number
|
||||||
|
last_active: number
|
||||||
|
msg_count: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SessionsPage {
|
||||||
|
sessions: SessionItem[]
|
||||||
|
total: number
|
||||||
|
page: number
|
||||||
|
page_size: number
|
||||||
|
has_more: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Backend history message (as returned by /api/history). */
|
||||||
|
export interface HistoryMessage {
|
||||||
|
role: Role
|
||||||
|
content: string
|
||||||
|
created_at: number
|
||||||
|
steps?: MessageStep[]
|
||||||
|
tool_calls?: Array<{ id?: string; name: string; arguments?: Record<string, unknown>; result?: string }>
|
||||||
|
reasoning?: string
|
||||||
|
kind?: 'evolution'
|
||||||
|
extras?: Record<string, unknown>
|
||||||
|
/** Per-message sequence number used by delete/regenerate APIs. */
|
||||||
|
_seq?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface HistoryPage {
|
||||||
|
messages: HistoryMessage[]
|
||||||
|
total: number
|
||||||
|
page: number
|
||||||
|
page_size: number
|
||||||
|
has_more: boolean
|
||||||
|
context_start_seq?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Config
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
export interface ProviderMeta {
|
||||||
|
label: string
|
||||||
|
models: string[]
|
||||||
|
api_base_key?: string
|
||||||
|
api_base_default?: string
|
||||||
|
api_key_field?: string
|
||||||
|
[k: string]: unknown
|
||||||
|
}
|
||||||
|
|
||||||
export interface ConfigData {
|
export interface ConfigData {
|
||||||
use_agent: boolean
|
use_agent: boolean
|
||||||
title: string
|
title: string
|
||||||
@@ -51,54 +194,190 @@ export interface ConfigData {
|
|||||||
agent_max_context_tokens: number
|
agent_max_context_tokens: number
|
||||||
agent_max_context_turns: number
|
agent_max_context_turns: number
|
||||||
agent_max_steps: number
|
agent_max_steps: number
|
||||||
|
enable_thinking?: boolean
|
||||||
|
self_evolution_enabled?: boolean
|
||||||
api_bases: Record<string, string>
|
api_bases: Record<string, string>
|
||||||
api_keys: Record<string, string>
|
api_keys: Record<string, string>
|
||||||
providers: Record<string, unknown>
|
providers: Record<string, ProviderMeta>
|
||||||
|
web_password_masked?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Models console (/api/models)
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
export interface ModelProvider {
|
||||||
|
id: string
|
||||||
|
label: string
|
||||||
|
configured: boolean
|
||||||
|
is_custom: boolean
|
||||||
|
custom_id?: string
|
||||||
|
active?: boolean
|
||||||
|
api_key_masked?: string
|
||||||
|
api_base?: string
|
||||||
|
models: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CapabilityKey = 'chat' | 'vision' | 'asr' | 'tts' | 'embedding' | 'image' | 'search'
|
||||||
|
|
||||||
|
export interface CapabilityState {
|
||||||
|
current_provider?: string
|
||||||
|
current_model?: string
|
||||||
|
providers?: string[]
|
||||||
|
provider_models?: Record<string, string[]>
|
||||||
|
strategy?: string
|
||||||
|
fallback_provider?: string
|
||||||
|
fallback_model?: string
|
||||||
|
[k: string]: unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ModelsData {
|
||||||
|
providers: ModelProvider[]
|
||||||
|
capabilities: Record<CapabilityKey, CapabilityState>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ModelsAction =
|
||||||
|
| { action: 'set_provider'; provider_id: string; api_key?: string; api_base?: string }
|
||||||
|
| { action: 'delete_provider'; provider_id: string }
|
||||||
|
| { action: 'set_custom_provider'; name: string; id?: string; api_base: string; api_key?: string; model?: string; make_active?: boolean }
|
||||||
|
| { action: 'delete_custom_provider'; id: string }
|
||||||
|
| { action: 'set_active_custom_provider'; id: string }
|
||||||
|
| { action: 'set_capability'; capability: CapabilityKey; provider_id: string; model: string; voice?: string; strategy?: string; provider?: string }
|
||||||
|
| { action: 'set_voice_reply_mode'; mode: 'off' | 'voice_if_voice' | 'always' }
|
||||||
|
| { action: 'set_search_credential'; api_key: string }
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Channels
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
export interface ChannelField {
|
||||||
|
key: string
|
||||||
|
label: string
|
||||||
|
type: 'text' | 'secret' | 'number' | 'bool'
|
||||||
|
value?: string | number | boolean
|
||||||
|
default?: string | number | boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ChannelInfo {
|
export interface ChannelInfo {
|
||||||
name: string
|
name: string
|
||||||
label: Record<string, string>
|
label: { zh: string; en: string }
|
||||||
icon: string
|
icon: string
|
||||||
color: string
|
color: string
|
||||||
active: boolean
|
active: boolean
|
||||||
fields: ChannelField[]
|
fields: ChannelField[]
|
||||||
|
login_status?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ChannelField {
|
export type ChannelAction = 'save' | 'connect' | 'disconnect'
|
||||||
key: string
|
|
||||||
label: Record<string, string>
|
// ============================================================
|
||||||
type: string
|
// Tools / skills
|
||||||
required?: boolean
|
// ============================================================
|
||||||
placeholder?: Record<string, string>
|
|
||||||
|
export interface ToolInfo {
|
||||||
|
name: string
|
||||||
|
description: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SkillInfo {
|
export interface SkillInfo {
|
||||||
name: string
|
name: string
|
||||||
display_name: string
|
|
||||||
description: string
|
description: string
|
||||||
|
source?: string
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
|
category?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ToolInfo {
|
// ============================================================
|
||||||
name: string
|
// Memory
|
||||||
display_name: string
|
// ============================================================
|
||||||
description: string
|
|
||||||
}
|
export type MemoryCategory = 'memory' | 'dream' | 'evolution'
|
||||||
|
|
||||||
export interface MemoryItem {
|
export interface MemoryItem {
|
||||||
filename: string
|
filename: string
|
||||||
modified: number
|
type: string // global | daily | dream | evolution
|
||||||
size: number
|
size: number
|
||||||
|
updated_at: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MemoryPage {
|
||||||
|
list: MemoryItem[]
|
||||||
|
total: number
|
||||||
|
page: number
|
||||||
|
page_size: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Knowledge
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
export interface KnowledgeNode {
|
||||||
|
name: string
|
||||||
|
path: string
|
||||||
|
type: 'category' | 'file'
|
||||||
|
children?: KnowledgeNode[]
|
||||||
|
count?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface KnowledgeList {
|
||||||
|
root_files?: KnowledgeNode[]
|
||||||
|
tree: KnowledgeNode[]
|
||||||
|
stats: { pages: number; size: number }
|
||||||
|
enabled: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface KnowledgeGraph {
|
||||||
|
nodes: Array<{ id: string; label: string; category?: string }>
|
||||||
|
links: Array<{ source: string; target: string }>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type KnowledgeAction =
|
||||||
|
| { action: 'create_category'; payload: { path: string } }
|
||||||
|
| { action: 'rename_category'; payload: { path: string; new_path: string } }
|
||||||
|
| { action: 'delete_category'; payload: { path: string; confirm?: boolean } }
|
||||||
|
| { action: 'delete_documents'; payload: { paths: string[] } }
|
||||||
|
| { action: 'move_documents'; payload: { paths: string[]; target_category: string } }
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Scheduler
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
export interface TaskSchedule {
|
||||||
|
type: 'cron' | 'interval' | 'once'
|
||||||
|
expression?: string
|
||||||
|
seconds?: number
|
||||||
|
run_at?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TaskAction {
|
||||||
|
type: 'send_message' | 'agent_task'
|
||||||
|
content?: string
|
||||||
|
task_description?: string
|
||||||
|
receiver?: string
|
||||||
|
receiver_name?: string
|
||||||
|
is_group?: boolean
|
||||||
|
channel_type?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SchedulerTask {
|
export interface SchedulerTask {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
cron: string
|
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
last_run?: string
|
created_at: string
|
||||||
next_run?: string
|
updated_at: string
|
||||||
|
schedule: TaskSchedule
|
||||||
|
action: TaskAction
|
||||||
|
next_run_at?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Logs
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
export interface LogEvent {
|
||||||
|
type: 'init' | 'line' | 'error'
|
||||||
|
content?: string
|
||||||
|
message?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
|||||||
Reference in New Issue
Block a user