feat: add comprehensive PUT/PATCH support for agents and sessions APIs

- Add PATCH method to agents API for partial updates alongside existing PUT
- Add PUT method to sessions API for complete replacement alongside existing PATCH
- Update API documentation with clear PUT vs PATCH usage examples
- Refactor session status updates to use standard PATCH endpoint
- Ensure both methods use same validation middleware for consistency
- Add comprehensive Swagger documentation for new endpoints

This provides REST-compliant update operations where:
- PUT: Complete resource replacement (idempotent)
- PATCH: Partial resource updates (only specified fields)

Both agents and sessions now support flexible update patterns for different use cases.
This commit is contained in:
Vaayne
2025-09-14 10:15:28 +08:00
parent 532bad8eb7
commit aaba77c360
3 changed files with 340 additions and 131 deletions
+138
View File
@@ -488,6 +488,144 @@ router.put(
}
)
/**
* @swagger
* /v1/agents/{agentId}:
* patch:
* summary: Partially update agent
* description: Partially updates an existing agent with only the provided fields
* tags: [Agents]
* parameters:
* - in: path
* name: agentId
* required: true
* schema:
* type: string
* description: Agent ID
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* name:
* type: string
* description: Agent name
* description:
* type: string
* description: Agent description
* avatar:
* type: string
* description: Agent avatar URL
* instructions:
* type: string
* description: System prompt/instructions
* model:
* type: string
* description: Main model ID
* plan_model:
* type: string
* description: Optional planning model ID
* small_model:
* type: string
* description: Optional small/fast model ID
* built_in_tools:
* type: array
* items:
* type: string
* description: Built-in tool IDs
* mcps:
* type: array
* items:
* type: string
* description: MCP tool IDs
* knowledges:
* type: array
* items:
* type: string
* description: Knowledge base IDs
* configuration:
* type: object
* description: Extensible settings
* accessible_paths:
* type: array
* items:
* type: string
* description: Accessible directory paths
* permission_mode:
* type: string
* enum: [readOnly, acceptEdits, bypassPermissions]
* description: Permission mode
* max_steps:
* type: integer
* description: Maximum steps the agent can take
* description: Only include the fields you want to update
* responses:
* 200:
* description: Agent updated successfully
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/AgentEntity'
* 400:
* description: Validation error
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 404:
* description: Agent not found
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 500:
* description: Internal server error
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
*/
router.patch(
'/:agentId',
validateAgentId,
validateAgentUpdate,
handleValidationErrors,
async (req: Request, res: Response) => {
try {
const { agentId } = req.params
logger.info(`Partially updating agent: ${agentId}`)
logger.debug('Partial update data:', req.body)
const agent = await agentService.updateAgent(agentId, req.body)
if (!agent) {
logger.warn(`Agent not found for partial update: ${agentId}`)
return res.status(404).json({
error: {
message: 'Agent not found',
type: 'not_found',
code: 'agent_not_found'
}
})
}
logger.info(`Agent partially updated successfully: ${agentId}`)
return res.json(agent)
} catch (error: any) {
logger.error('Error partially updating agent:', error)
return res.status(500).json({
error: {
message: 'Failed to partially update agent',
type: 'internal_error',
code: 'agent_patch_failed'
}
})
}
}
)
/**
* @swagger
* /v1/agents/{agentId}:
+111 -117
View File
@@ -11,7 +11,6 @@ const router = express.Router()
// Validation middleware
const validateSession = [
body('name').optional().isString(),
body('main_agent_id').notEmpty().withMessage('Main agent ID is required'),
body('sub_agent_ids').optional().isArray(),
body('user_goal').optional().isString(),
body('status').optional().isIn(['idle', 'running', 'completed', 'failed', 'stopped']),
@@ -545,6 +544,114 @@ function createSessionsRouter(): express.Router {
* @swagger
* /v1/agents/{agentId}/sessions/{sessionId}:
* put:
* summary: Replace session
* description: Completely replaces an existing session for the specified agent
* tags: [Sessions]
* parameters:
* - in: path
* name: agentId
* required: true
* schema:
* type: string
* description: Agent ID
* - in: path
* name: sessionId
* required: true
* schema:
* type: string
* description: Session ID
* requestBody:
* required: true
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/CreateSessionRequest'
* responses:
* 200:
* description: Session replaced successfully
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/AgentSessionEntity'
* 400:
* description: Validation error
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 404:
* description: Agent or session not found
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 500:
* description: Internal server error
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
*/
sessionsRouter.put(
'/:sessionId',
validateAgentId,
validateSessionId,
checkAgentExists,
validateSessionUpdate,
handleValidationErrors,
async (req: Request, res: Response) => {
try {
const { agentId, sessionId } = req.params
logger.info(`Replacing session: ${sessionId} for agent: ${agentId}`)
logger.debug('Replace data:', req.body)
// First check if session exists and belongs to agent
const existingSession = await sessionService.getSession(sessionId)
if (!existingSession || existingSession.main_agent_id !== agentId) {
logger.warn(`Session ${sessionId} not found for agent ${agentId}`)
return res.status(404).json({
error: {
message: 'Session not found for this agent',
type: 'not_found',
code: 'session_not_found'
}
})
}
// For PUT, we replace the entire resource
const sessionData = { ...req.body, main_agent_id: agentId }
const session = await sessionService.updateSession(sessionId, sessionData)
if (!session) {
logger.warn(`Session not found for replace: ${sessionId}`)
return res.status(404).json({
error: {
message: 'Session not found',
type: 'not_found',
code: 'session_not_found'
}
})
}
logger.info(`Session replaced successfully: ${sessionId}`)
return res.json(session)
} catch (error: any) {
logger.error('Error replacing session:', error)
return res.status(500).json({
error: {
message: 'Failed to replace session',
type: 'internal_error',
code: 'session_replace_failed'
}
})
}
}
)
/**
* @swagger
* /v1/agents/{agentId}/sessions/{sessionId}:
* patch:
* summary: Update session
* description: Updates an existing session for the specified agent
* tags: [Sessions]
@@ -593,7 +700,7 @@ function createSessionsRouter(): express.Router {
* schema:
* $ref: '#/components/schemas/Error'
*/
sessionsRouter.put(
sessionsRouter.patch(
'/:sessionId',
validateAgentId,
validateSessionId,
@@ -618,8 +725,8 @@ function createSessionsRouter(): express.Router {
}
})
}
const session = await sessionService.updateSession(sessionId, req.body)
const updateSession = { ...existingSession, ...req.body }
const session = await sessionService.updateSession(sessionId, updateSession)
if (!session) {
logger.warn(`Session not found for update: ${sessionId}`)
@@ -647,119 +754,6 @@ function createSessionsRouter(): express.Router {
}
)
/**
* @swagger
* /v1/agents/{agentId}/sessions/{sessionId}/status:
* patch:
* summary: Update session status
* description: Updates the status of a specific session
* tags: [Sessions]
* parameters:
* - in: path
* name: agentId
* required: true
* schema:
* type: string
* description: Agent ID
* - in: path
* name: sessionId
* required: true
* schema:
* type: string
* description: Session ID
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* status:
* type: string
* enum: [idle, running, completed, failed, stopped]
* required:
* - status
* responses:
* 200:
* description: Session status updated successfully
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/AgentSessionEntity'
* 400:
* description: Validation error
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 404:
* description: Agent or session not found
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
* 500:
* description: Internal server error
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Error'
*/
sessionsRouter.patch(
'/:sessionId/status',
validateAgentId,
validateSessionId,
checkAgentExists,
validateStatusUpdate,
handleValidationErrors,
async (req: Request, res: Response) => {
try {
const { agentId, sessionId } = req.params
const { status } = req.body
logger.info(`Updating session status: ${sessionId} for agent: ${agentId} to ${status}`)
// First check if session exists and belongs to agent
const existingSession = await sessionService.getSession(sessionId)
if (!existingSession || existingSession.main_agent_id !== agentId) {
logger.warn(`Session ${sessionId} not found for agent ${agentId}`)
return res.status(404).json({
error: {
message: 'Session not found for this agent',
type: 'not_found',
code: 'session_not_found'
}
})
}
const session = await sessionService.updateSessionStatus(sessionId, status)
if (!session) {
logger.warn(`Session not found for status update: ${sessionId}`)
return res.status(404).json({
error: {
message: 'Session not found',
type: 'not_found',
code: 'session_not_found'
}
})
}
logger.info(`Session status updated successfully: ${sessionId} -> ${status}`)
return res.json(session)
} catch (error: any) {
logger.error('Error updating session status:', error)
return res.status(500).json({
error: {
message: 'Failed to update session status',
type: 'internal_error',
code: 'session_status_update_failed'
}
})
}
}
)
/**
* @swagger
* /v1/agents/{agentId}/sessions/{sessionId}: