fix: 修复 AI 回复无法送达发送者 + 重复消息 + action角色泄露 + OS环境支持
广播逻辑重构: - AI 回复 (stream_start/response/stream_segments/multi_message/stream_end) 改用 broadcastToUser 发送给所有客户端 - 用户消息回显保持 broadcastToUserExcept 排除发送者 消息去重与角色修复: - CacheMessage(user) 移至回复生成后,避免本轮 LLM 调用出现重复用户消息 - action 角色消息在 DB 存储时映射为 assistant,DeepSeek 等模型不支持自定义角色 - stream_end defer 机制确保错误路径也会终止客户端思考指示器 OS 完整环境支持: - host 包重构为 HostBackend 接口 + Direct/WSL/Docker 三种后端 - 新增 os_exec/os_file/os_system 工具供 AI 在完整 Linux 环境中自由操作 其他: - 视觉模型注入 + 图片预处理后清空 Images 避免传给 Chat 模型 - 图片 URL 相对路径→绝对 URL 转换 - DevTools 链路追踪页面 + 重启修复 - 记忆搜索模糊匹配增强 - 后台思考定时调度支持 - 管理后台页面 (模型配置/用户管理等) - docs/api 更新广播机制说明 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
// Admin API — /admin/models/*, /admin/sessions/*, /admin/clients/*
|
||||
|
||||
import { request, type ApiResponse } from './client';
|
||||
import type {
|
||||
ModelProvider,
|
||||
ModelConfig,
|
||||
RoutingRule,
|
||||
SessionState,
|
||||
ClientInfo,
|
||||
HealthCheckResult,
|
||||
ModelListResponse,
|
||||
ProviderListResponse,
|
||||
AdminSessionsResponse,
|
||||
ActiveSessionsResponse,
|
||||
ClientListResponse,
|
||||
} from '@/types/admin';
|
||||
|
||||
// ========== Providers ==========
|
||||
|
||||
export function listProviders(): Promise<ApiResponse<ProviderListResponse>> {
|
||||
return request<ProviderListResponse>('/admin/models/providers');
|
||||
}
|
||||
|
||||
export function saveProvider(name: string, baseUrl: string, apiKey: string): Promise<ApiResponse<{ status: string; name: string }>> {
|
||||
return request(`/admin/models/providers/${encodeURIComponent(name)}`, {
|
||||
method: 'POST',
|
||||
body: { name, base_url: baseUrl, api_key: apiKey },
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteProvider(name: string): Promise<ApiResponse<{ status: string; name: string }>> {
|
||||
return request(`/admin/models/providers/${encodeURIComponent(name)}`, { method: 'DELETE' });
|
||||
}
|
||||
|
||||
export function healthCheckProvider(provider: string): Promise<ApiResponse<HealthCheckResult>> {
|
||||
return request('/admin/models/health-check', { method: 'POST', body: { provider } });
|
||||
}
|
||||
|
||||
export function fetchProviderModels(name: string, url?: string): Promise<ApiResponse<{ models: Array<{ id: string; name?: string }> }>> {
|
||||
const query = url ? `?url=${encodeURIComponent(url)}` : '';
|
||||
return request(`/admin/models/fetch-models/${encodeURIComponent(name)}${query}`);
|
||||
}
|
||||
|
||||
// ========== Models ==========
|
||||
|
||||
export function listModels(): Promise<ApiResponse<ModelListResponse>> {
|
||||
return request<ModelListResponse>('/admin/models/models');
|
||||
}
|
||||
|
||||
export function saveModel(id: string, config: Partial<ModelConfig>): Promise<ApiResponse<{ status: string; id: string }>> {
|
||||
return request(`/admin/models/models/${encodeURIComponent(id)}`, {
|
||||
method: 'POST',
|
||||
body: { id, ...config },
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteModel(id: string): Promise<ApiResponse<{ status: string; id: string }>> {
|
||||
return request(`/admin/models/models/${encodeURIComponent(id)}`, { method: 'DELETE' });
|
||||
}
|
||||
|
||||
// ========== Routing ==========
|
||||
|
||||
export function listRouting(): Promise<ApiResponse<{ rules: RoutingRule[] }>> {
|
||||
return request('/admin/models/routing');
|
||||
}
|
||||
|
||||
export function saveRouting(purpose: string, fallbackChain: string[], required?: boolean): Promise<ApiResponse<{ status: string; purpose: string }>> {
|
||||
return request(`/admin/models/routing/${encodeURIComponent(purpose)}`, {
|
||||
method: 'POST',
|
||||
body: { purpose, fallback_chain: fallbackChain, required },
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteRouting(purpose: string): Promise<ApiResponse<{ status: string; purpose: string }>> {
|
||||
return request(`/admin/models/routing/${encodeURIComponent(purpose)}`, { method: 'DELETE' });
|
||||
}
|
||||
|
||||
// ========== Sessions ==========
|
||||
|
||||
export function listAdminSessions(): Promise<ApiResponse<AdminSessionsResponse>> {
|
||||
return request<AdminSessionsResponse>('/admin/sessions');
|
||||
}
|
||||
|
||||
export function listActiveSessions(): Promise<ApiResponse<ActiveSessionsResponse>> {
|
||||
return request<ActiveSessionsResponse>('/admin/sessions/active');
|
||||
}
|
||||
|
||||
export function getAdminSession(id: string): Promise<ApiResponse<SessionState>> {
|
||||
return request<SessionState>(`/admin/sessions/${encodeURIComponent(id)}`);
|
||||
}
|
||||
|
||||
// ========== Clients ==========
|
||||
|
||||
export function listClients(userId?: string): Promise<ApiResponse<ClientListResponse>> {
|
||||
const query = userId ? `?user_id=${encodeURIComponent(userId)}` : '';
|
||||
return request<ClientListResponse>(`/admin/clients${query}`);
|
||||
}
|
||||
|
||||
export function setClientNote(clientId: string, note: string): Promise<ApiResponse<{ status: string; client_id: string; note: string }>> {
|
||||
return request(`/admin/clients/${encodeURIComponent(clientId)}/note`, {
|
||||
method: 'PUT',
|
||||
body: { note },
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user