import { httpClient } from '@/lib/http-client'
import { Session, ApiResponse, SessionManagementStrategy, Conversation, Message } from '@/lib/types'
import { SessionStatus, MessageStatus } from '@/lib/types'
* 会话服务类 - 负责会话的创建和管理
*/
class SessionService {
* 创建会话
*/
public async createSession(agentId: string): Promise<Session> {
const response = await httpClient.post<any>(`/agents/${agentId}/sessions`, {})
const sessionData = response.data || response
return this.transformSession(sessionData, agentId)
}
* 获取Agent的所有会话
*/
public async getSessions(agentId: string): Promise<Session[]> {
const response = await httpClient.get<ApiResponse<Session[]>>(`/agents/${agentId}/sessions`)
return response.data.map(session => this.transformSession(session, agentId))
}
* 删除会话
*/
public async deleteSession(agentId: string, sessionId: string): Promise<void> {
await httpClient.delete(`/agents/${agentId}/sessions/${sessionId}`)
}
* 中断会话
*/
public async abortSession(agentId: string, sessionId: string): Promise<void> {
await httpClient.post(`/agents/${agentId}/sessions/${sessionId}/abort`)
}
* 获取会话列表(含摘要信息)
*/
public async getConversations(agentId: string): Promise<any[]> {
const response = await httpClient.get<any[]>(`/agents/${agentId}/conversations`)
return Array.isArray(response) ? response : (response as any).data || []
}
* 获取完整会话(含消息和事件),默认最近 20 条消息。
* 通过 before 参数加载更早的消息。
*/
public async getConversation(
agentId: string,
sessionId: string,
limit: number = 10,
before?: string
): Promise<any> {
const params = new URLSearchParams({ limit: String(limit) })
if (before) params.set('before', before)
const response = await httpClient.get<any>(
`/agents/${agentId}/conversations/${sessionId}?${params.toString()}`
)
return response.data || response
}
* 更新会话元数据(标题、置顶)
*/
public async updateConversation(
agentId: string,
sessionId: string,
updates: { title?: string; pinned?: boolean }
): Promise<void> {
await httpClient.patch(`/agents/${agentId}/conversations/${sessionId}`, updates)
}
* 将 API 会话摘要转换为前端 Conversation 类型
*/
public transformConversationSummary(summary: any, agentName?: string): Conversation {
return {
id: summary.id,
title: summary.title || summary.first_message_preview || '新对话',
messages: [],
createdAt: new Date(summary.created_at),
updatedAt: new Date(summary.updated_at),
model: summary.model,
pinned: summary.pinned,
agentId: summary.agent_id,
agentName,
sessionId: summary.id,
scheduledTaskId: summary.scheduled_task_id || undefined,
lastMessageStatus: summary.last_message_status || undefined,
}
}
* 将 API 会话详情中的消息转换为前端 Message 类型
*/
public transformMessage(msg: any): Message {
const isFinished = msg.status && msg.status !== MessageStatus.GENERATING
const normalizeToolCallStatus = (toolCall: any) => {
if (!toolCall) return undefined
if (isFinished && toolCall.status === 'running') {
return { ...toolCall, status: 'completed' as const }
}
return toolCall
}
return {
id: msg.id,
role: msg.role,
content: msg.content,
timestamp: new Date(msg.timestamp || msg.created_at),
isStreaming: msg.isStreaming ?? msg.status === MessageStatus.GENERATING,
status: msg.status,
toolCalls: (msg.toolCalls || msg.tool_calls || []).map(normalizeToolCallStatus),
thinking: msg.thinking,
question: msg.question ?? null,
questionId: msg.questionId ?? msg.question_id ?? null,
questionStatus: (msg.questionStatus ?? msg.question_status ?? undefined) as
| Message['questionStatus']
| undefined,
questionAnswers: msg.questionAnswers ?? msg.question_answers ?? null,
events: (msg.events || [])
.filter((evt: any) => {
if (msg.status && msg.status !== 'generating' && evt.type === 'message.delta')
return false
if (evt.type === 'message.delta' && !evt.delta && !evt.content) return false
return true
})
.map((evt: any) => ({
type: evt.type,
content: evt.content || evt.delta || '',
timestamp:
typeof evt.timestamp === 'number'
? evt.timestamp
: evt.timestamp
? new Date(evt.timestamp).getTime()
: Date.now(),
toolCall: normalizeToolCallStatus(evt.toolCall),
payload: evt.payload,
})),
usage: msg.usage,
}
}
* 转换会话数据格式
*/
private transformSession(session: any, agentId: string): Session {
if (!session) {
throw new Error('会话数据为空')
}
return {
id: session.id,
agentId,
status: (session.status as SessionStatus) || SessionStatus.ACTIVE,
contextInitialized: session.context_initialized ?? session.contextInitialized ?? true,
runtimeType: session.runtime_type || session.runtimeType || 'openclaw',
createdAt: session.created_at || session.createdAt,
updatedAt: session.updated_at || session.updatedAt,
}
}
}
class DefaultSessionStrategy implements SessionManagementStrategy {
private sessionService: SessionService
constructor() {
this.sessionService = new SessionService()
}
async createSession(agentId: string): Promise<Session> {
return await this.sessionService.createSession(agentId)
}
switchSession(sessionId: string): void {
console.log(`Switching to session: ${sessionId}`)
}
async endSession(sessionId: string): Promise<void> {
console.log(`Ending session: ${sessionId}`)
}
}
export const sessionService = new SessionService()
export { DefaultSessionStrategy }