mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-29 23:12:38 +08:00
feat(agents): enhance error messages for agent and session operations; update accessible paths handling
This commit is contained in:
parent
d6468f33c5
commit
f0ac74dccf
@ -71,7 +71,7 @@ export const createAgent = async (req: Request, res: Response): Promise<Response
|
||||
logger.error('Error creating agent:', error)
|
||||
return res.status(500).json({
|
||||
error: {
|
||||
message: 'Failed to create agent',
|
||||
message: `Failed to create agent: ${error.message}`,
|
||||
type: 'internal_error',
|
||||
code: 'agent_creation_failed'
|
||||
}
|
||||
@ -315,7 +315,7 @@ export const updateAgent = async (req: Request, res: Response): Promise<Response
|
||||
logger.error('Error updating agent:', error)
|
||||
return res.status(500).json({
|
||||
error: {
|
||||
message: 'Failed to update agent',
|
||||
message: 'Failed to update agent: ' + error.message,
|
||||
type: 'internal_error',
|
||||
code: 'agent_update_failed'
|
||||
}
|
||||
@ -461,7 +461,7 @@ export const patchAgent = async (req: Request, res: Response): Promise<Response>
|
||||
logger.error('Error partially updating agent:', error)
|
||||
return res.status(500).json({
|
||||
error: {
|
||||
message: 'Failed to partially update agent',
|
||||
message: `Failed to partially update agent: ${error.message}`,
|
||||
type: 'internal_error',
|
||||
code: 'agent_patch_failed'
|
||||
}
|
||||
|
||||
@ -51,7 +51,7 @@ export const createSession = async (req: Request, res: Response): Promise<Respon
|
||||
logger.error('Error creating session:', error)
|
||||
return res.status(500).json({
|
||||
error: {
|
||||
message: 'Failed to create session',
|
||||
message: `Failed to create session: ${error.message}`,
|
||||
type: 'internal_error',
|
||||
code: 'session_creation_failed'
|
||||
}
|
||||
@ -195,7 +195,7 @@ export const updateSession = async (req: Request, res: Response): Promise<Respon
|
||||
logger.error('Error updating session:', error)
|
||||
return res.status(500).json({
|
||||
error: {
|
||||
message: 'Failed to update session',
|
||||
message: `Failed to update session: ${error.message}`,
|
||||
type: 'internal_error',
|
||||
code: 'session_update_failed'
|
||||
}
|
||||
@ -254,7 +254,7 @@ export const patchSession = async (req: Request, res: Response): Promise<Respons
|
||||
logger.error('Error patching session:', error)
|
||||
return res.status(500).json({
|
||||
error: {
|
||||
message: 'Failed to patch session',
|
||||
message: `Failed to patch session, ${error.message}`,
|
||||
type: 'internal_error',
|
||||
code: 'session_patch_failed'
|
||||
}
|
||||
|
||||
@ -163,20 +163,36 @@ export abstract class BaseService {
|
||||
return deserialized
|
||||
}
|
||||
|
||||
protected ensurePathsExist(paths?: string[]): void {
|
||||
/**
|
||||
* Validate, normalize, and ensure filesystem access for a set of absolute paths.
|
||||
*
|
||||
* - Requires every entry to be an absolute path and throws if not.
|
||||
* - Normalizes each path and deduplicates while preserving order.
|
||||
* - Creates missing directories (or parent directories for file-like paths).
|
||||
*/
|
||||
protected ensurePathsExist(paths?: string[]): string[] {
|
||||
if (!paths?.length) {
|
||||
return
|
||||
return []
|
||||
}
|
||||
|
||||
const sanitizedPaths: string[] = []
|
||||
const seenPaths = new Set<string>()
|
||||
|
||||
for (const rawPath of paths) {
|
||||
if (!rawPath) {
|
||||
continue
|
||||
}
|
||||
|
||||
const resolvedPath = path.resolve(rawPath)
|
||||
if (!path.isAbsolute(rawPath)) {
|
||||
throw new Error(`Accessible path must be absolute: ${rawPath}`)
|
||||
}
|
||||
|
||||
// Normalize to provide consistent values to downstream consumers.
|
||||
const resolvedPath = path.normalize(rawPath)
|
||||
|
||||
let stats: fs.Stats | null = null
|
||||
try {
|
||||
// Attempt to stat the path to understand whether it already exists and if it is a file.
|
||||
if (fs.existsSync(resolvedPath)) {
|
||||
stats = fs.statSync(resolvedPath)
|
||||
}
|
||||
@ -190,6 +206,7 @@ export abstract class BaseService {
|
||||
const looksLikeFile =
|
||||
(stats && stats.isFile()) || (!stats && path.extname(resolvedPath) !== '' && !resolvedPath.endsWith(path.sep))
|
||||
|
||||
// For file-like targets create the parent directory; otherwise ensure the directory itself.
|
||||
const directoryToEnsure = looksLikeFile ? path.dirname(resolvedPath) : resolvedPath
|
||||
|
||||
if (!fs.existsSync(directoryToEnsure)) {
|
||||
@ -203,7 +220,15 @@ export abstract class BaseService {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// Preserve the first occurrence only to avoid duplicates while keeping caller order stable.
|
||||
if (!seenPaths.has(resolvedPath)) {
|
||||
seenPaths.add(resolvedPath)
|
||||
sanitizedPaths.push(resolvedPath)
|
||||
}
|
||||
}
|
||||
|
||||
return sanitizedPaths
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -45,14 +45,16 @@ export class AgentService extends BaseService {
|
||||
req.accessible_paths = [defaultPath]
|
||||
}
|
||||
|
||||
if (req.accessible_paths !== undefined) {
|
||||
req.accessible_paths = this.ensurePathsExist(req.accessible_paths)
|
||||
}
|
||||
|
||||
await this.validateAgentModels(req.type, {
|
||||
model: req.model,
|
||||
plan_model: req.plan_model,
|
||||
small_model: req.small_model
|
||||
})
|
||||
|
||||
this.ensurePathsExist(req.accessible_paths)
|
||||
|
||||
const serializedReq = this.serializeJsonFields(req)
|
||||
|
||||
const insertData: InsertAgentRow = {
|
||||
@ -137,8 +139,8 @@ export class AgentService extends BaseService {
|
||||
|
||||
const now = new Date().toISOString()
|
||||
|
||||
if (updates.accessible_paths) {
|
||||
this.ensurePathsExist(updates.accessible_paths)
|
||||
if (updates.accessible_paths !== undefined) {
|
||||
updates.accessible_paths = this.ensurePathsExist(updates.accessible_paths)
|
||||
}
|
||||
|
||||
const modelUpdates: Partial<Record<AgentModelField, string | undefined>> = {}
|
||||
|
||||
@ -58,7 +58,9 @@ export class SessionService extends BaseService {
|
||||
small_model: sessionData.small_model
|
||||
})
|
||||
|
||||
this.ensurePathsExist(sessionData.accessible_paths)
|
||||
if (sessionData.accessible_paths !== undefined) {
|
||||
sessionData.accessible_paths = this.ensurePathsExist(sessionData.accessible_paths)
|
||||
}
|
||||
|
||||
const serializedData = this.serializeJsonFields(sessionData)
|
||||
|
||||
@ -179,8 +181,8 @@ export class SessionService extends BaseService {
|
||||
|
||||
const now = new Date().toISOString()
|
||||
|
||||
if (updates.accessible_paths) {
|
||||
this.ensurePathsExist(updates.accessible_paths)
|
||||
if (updates.accessible_paths !== undefined) {
|
||||
updates.accessible_paths = this.ensurePathsExist(updates.accessible_paths)
|
||||
}
|
||||
|
||||
const modelUpdates: Partial<Record<AgentModelField, string | undefined>> = {}
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
@host=http://localhost:23333
|
||||
@token=cs-sk-af798ed4-7cf5-4fd7-ae4b-df203b164194
|
||||
@agent_id=agent_1758092281575_tn9dxio9k
|
||||
@session_id=session_1758252305914_9kef8yven
|
||||
@session_id=session_1758278828236_mqj91e7c0
|
||||
|
||||
### List Sessions
|
||||
GET {{host}}/v1/agents/{{agent_id}}/sessions
|
||||
@ -16,11 +16,11 @@ Authorization: Bearer {{token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "Story Writing Session 1",
|
||||
"name": "Story Writing Session 2",
|
||||
"instructions": "You are a creative writing assistant. Help me brainstorm and write engaging stories.",
|
||||
"model": "anthropic:claude-sonnet-4",
|
||||
"accessible_paths": [
|
||||
"~/Documents/stories"
|
||||
"/tmp/Documents/stories"
|
||||
]
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ Content-Type: application/json
|
||||
"instructions": "You are a creative writing assistant. Help me brainstorm and write engaging stories.",
|
||||
"model": "anthropic:claude-sonnet-4",
|
||||
"accessible_paths": [
|
||||
"~/Documents/stories"
|
||||
"/tmp/Documents/stories"
|
||||
]
|
||||
}
|
||||
|
||||
@ -55,7 +55,10 @@ Authorization: Bearer {{token}}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"instructions": "You are a creative writing assistant. Help me brainstorm and write engaging stories. Focus on character development and plot structure.",
|
||||
"model": "anthropic:claude-sonnet-4-20250514",
|
||||
"accessible_paths": [
|
||||
"/tmp/Documents/stories2"
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user