Backend / src /utils /swagger.ts
Cuong2004's picture
Add Triage Report Implementation Plan with AI Decision-Making features. Introduce new API endpoint for generating complete triage reports, modify agent executor for report generation, and enhance frontend components for user-triggered report generation. Update type definitions to support new report structure and integrate location services for nearby medical facilities. Include detailed verification plan and implementation timeline.
a40d961
import type { FastifySwaggerUiOptions } from '@fastify/swagger-ui';
export const swaggerOptions = {
openapi: '3.1.0',
info: {
title: 'MEDAGEN Backend API',
description: 'AI Triage Assistant API với ReAct Agent và Gemini 2.5Flash',
version: '2.0.0',
contact: {
name: 'MEDAGEN Team'
}
},
servers: [
{
url: 'http://localhost:7860',
description: 'Development server (Port 7860)'
}
],
tags: [
{
name: 'health',
description: 'Health check endpoints'
},
{
name: 'triage',
description: 'AI Triage endpoints'
},
{
name: 'cv',
description: 'Computer Vision endpoints - Phân tích hình ảnh y tế'
},
{
name: 'rag',
description: 'RAG endpoints - Tìm kiếm hướng dẫn y tế'
},
{
name: 'maps',
description: 'Google Maps endpoints - Tìm cơ sở y tế'
},
{
name: 'sessions',
description: 'Session management endpoints'
},
{
name: 'conversations',
description: 'Conversation history endpoints'
}
],
components: {
schemas: {
HealthCheckRequest: {
type: 'object',
required: ['user_id'],
properties: {
text: {
type: 'string',
description: 'Mô tả triệu chứng của người dùng (bắt buộc nếu không có image_url)',
example: 'Mắt trái đỏ và hơi mờ 2 ngày nay'
},
image_url: {
type: 'string',
format: 'uri',
description: 'URL của hình ảnh (bắt buộc nếu không có text)',
example: 'https://supabase.../image.jpg'
},
user_id: {
type: 'string',
description: 'ID của người dùng',
example: 'abc123'
},
session_id: {
type: 'string',
format: 'uuid',
description: 'Session ID để theo dõi lịch sử hội thoại (tùy chọn, sẽ tự động tạo mới nếu không có)',
example: '550e8400-e29b-41d4-a716-446655440000'
},
location: {
type: 'object',
description: 'Vị trí của người dùng (tùy chọn)',
properties: {
lat: {
type: 'number',
description: 'Vĩ độ',
example: 10.78
},
lng: {
type: 'number',
description: 'Kinh độ',
example: 106.7
}
}
}
},
oneOf: [
{ required: ['text'] },
{ required: ['image_url'] }
]
},
HealthCheckResponse: {
type: 'object',
properties: {
triage_level: {
type: 'string',
enum: ['emergency', 'urgent', 'routine', 'self-care'],
description: 'Mức độ triage'
},
symptom_summary: {
type: 'string',
description: 'Tóm tắt triệu chứng'
},
red_flags: {
type: 'array',
items: {
type: 'string'
},
description: 'Các dấu hiệu nguy hiểm'
},
suspected_conditions: {
type: 'array',
items: {
type: 'object',
properties: {
name: {
type: 'string',
description: 'Tên tình trạng nghi ngờ'
},
source: {
type: 'string',
enum: ['cv_model', 'guideline', 'user_report', 'reasoning'],
description: 'Nguồn của tình trạng'
},
confidence: {
type: 'string',
enum: ['low', 'medium', 'high'],
description: 'Độ tin cậy'
}
}
}
},
cv_findings: {
type: 'object',
properties: {
model_used: {
type: 'string',
enum: ['derm_cv', 'eye_cv', 'wound_cv', 'none'],
description: 'Model CV được sử dụng'
},
raw_output: {
type: 'object',
description: 'Kết quả raw từ CV model'
}
}
},
recommendation: {
type: 'object',
properties: {
action: {
type: 'string',
description: 'Hành động khuyến nghị'
},
timeframe: {
type: 'string',
description: 'Thời gian khuyến nghị'
},
home_care_advice: {
type: 'string',
description: 'Lời khuyên chăm sóc tại nhà'
},
warning_signs: {
type: 'string',
description: 'Dấu hiệu cảnh báo'
}
}
},
nearest_clinic: {
type: 'object',
description: 'Cơ sở y tế gần nhất (nếu có location)',
properties: {
name: {
type: 'string',
description: 'Tên cơ sở y tế'
},
distance_km: {
type: 'number',
description: 'Khoảng cách (km)'
},
address: {
type: 'string',
description: 'Địa chỉ'
},
rating: {
type: 'number',
description: 'Đánh giá'
}
}
},
session_id: {
type: 'string',
format: 'uuid',
description: 'Session ID để tiếp tục cuộc hội thoại trong các request tiếp theo'
},
message: {
type: 'string',
description: 'Markdown response từ LLM (natural language, không bị giới hạn bởi JSON structure)'
}
}
},
HealthStatus: {
type: 'object',
properties: {
status: {
type: 'string',
example: 'ok'
},
timestamp: {
type: 'string',
format: 'date-time',
description: 'Thời gian kiểm tra'
},
llm: {
type: 'string',
description: 'Model LLM đang sử dụng',
example: 'gemini-2.5-flash'
},
cv_services: {
type: 'object',
properties: {
derm_cv: {
type: 'string',
example: 'unknown'
},
eye_cv: {
type: 'string',
example: 'unknown'
},
wound_cv: {
type: 'string',
example: 'unknown'
}
}
}
}
},
ErrorResponse: {
type: 'object',
properties: {
error: {
type: 'string',
description: 'Loại lỗi'
},
message: {
type: 'string',
description: 'Thông báo lỗi'
},
details: {
type: 'array',
items: {
type: 'object'
},
description: 'Chi tiết lỗi (nếu có)'
}
}
},
ConversationSession: {
type: 'object',
properties: {
id: {
type: 'string',
format: 'uuid',
description: 'ID của session'
},
user_id: {
type: 'string',
description: 'ID của người dùng'
},
created_at: {
type: 'string',
format: 'date-time',
description: 'Thời gian tạo'
},
updated_at: {
type: 'string',
format: 'date-time',
description: 'Thời gian cập nhật cuối'
}
}
},
ConversationMessage: {
type: 'object',
properties: {
id: {
type: 'string',
format: 'uuid',
description: 'ID của message'
},
session_id: {
type: 'string',
format: 'uuid',
description: 'ID của session'
},
user_id: {
type: 'string',
description: 'ID của người dùng'
},
role: {
type: 'string',
enum: ['user', 'assistant'],
description: 'Vai trò của message'
},
content: {
type: 'string',
description: 'Nội dung message'
},
image_url: {
type: 'string',
format: 'uri',
description: 'URL của hình ảnh (nếu có)'
},
triage_result: {
type: 'object',
description: 'Kết quả triage (chỉ có với assistant messages)'
},
created_at: {
type: 'string',
format: 'date-time',
description: 'Thời gian tạo'
}
}
}
}
}
};
export const swaggerUiOptions: FastifySwaggerUiOptions = {
routePrefix: '/docs',
uiConfig: {
docExpansion: 'list',
deepLinking: true,
displayRequestDuration: true,
persistAuthorization: true
},
staticCSP: false, // Disable strict CSP to allow Swagger UI to fetch JSON
transformSpecification: (swaggerObject, request) => {
// Ensure servers URL matches current request
if (swaggerObject.servers && swaggerObject.servers.length > 0) {
const protocol = request.headers['x-forwarded-proto'] || (request.protocol || 'http');
const host = request.headers.host || 'localhost:7860';
swaggerObject.servers[0].url = `${protocol}://${host}`;
}
return swaggerObject;
},
transformSpecificationClone: true
};