Spaces:
Runtime error
Runtime error
| """ | |
| OpenAPI Schema for GPTs Actions | |
| =============================== | |
| ChatGPT GPTs Actions에서 사용할 OpenAPI 3.1.0 스키마 생성 | |
| """ | |
| from starlette.requests import Request | |
| from starlette.responses import JSONResponse | |
| def generate_openapi_schema(base_url: str = "https://lovelymango-eodi-seats-aero-dev.hf.space") -> dict: | |
| """OpenAPI 3.1.0 스키마 생성""" | |
| return { | |
| "openapi": "3.1.0", | |
| "info": { | |
| "title": "Award Seat Search API", | |
| "description": ( | |
| "마일리지 좌석 가용성 검색 API.\n\n" | |
| "**왕복 검색**: /api/award/search/roundtrip 사용 (자동으로 양방향 검색)\n" | |
| "**편도 검색**: /api/award/search 사용\n\n" | |
| "**인증**: Seats.aero Pro 계정이 필요합니다. OAuth로 자동 연결됩니다.\n\n" | |
| "**중요**: 캐시 데이터입니다. 예약 전 항공사에서 확인하세요." | |
| ), | |
| "version": "1.3.0" | |
| }, | |
| "servers": [ | |
| {"url": base_url, "description": "Production"} | |
| ], | |
| "components": { | |
| "securitySchemes": { | |
| "oauth2": { | |
| "type": "oauth2", | |
| "description": "Seats.aero OAuth2 authentication via proxy", | |
| "flows": { | |
| "authorizationCode": { | |
| "authorizationUrl": f"{base_url}/oauth2/authorize", | |
| "tokenUrl": f"{base_url}/oauth2/token", | |
| "scopes": { | |
| "openid": "Access to Seats.aero API" | |
| } | |
| } | |
| } | |
| } | |
| }, | |
| "schemas": { | |
| "SearchRequest": { | |
| "type": "object", | |
| "required": ["origin", "destination"], | |
| "properties": { | |
| "origin": { | |
| "type": "string", | |
| "description": "출발 공항 코드", | |
| "example": "ICN" | |
| }, | |
| "destination": { | |
| "type": "string", | |
| "description": "도착 공항 코드", | |
| "example": "NRT" | |
| }, | |
| "start_date": { | |
| "type": "string", | |
| "format": "date", | |
| "description": "검색 시작일 (YYYY-MM-DD)" | |
| }, | |
| "end_date": { | |
| "type": "string", | |
| "format": "date", | |
| "description": "검색 종료일 (YYYY-MM-DD)" | |
| }, | |
| "cabin": { | |
| "type": "string", | |
| "enum": ["economy", "premium", "business", "first"], | |
| "description": "좌석 등급" | |
| }, | |
| "cabin_explicit": { | |
| "type": "boolean", | |
| "description": "true일 때만 cabin 필터 적용" | |
| }, | |
| "programs": { | |
| "type": "string", | |
| "description": "마일리지 프로그램 필터 (예: united,aeroplan)" | |
| }, | |
| "direct_only": { | |
| "type": "boolean", | |
| "default": False, | |
| "description": "직항만 검색" | |
| }, | |
| "carriers": { | |
| "type": "string", | |
| "description": "항공사 필터 (예: OZ,KE)" | |
| }, | |
| "limit": { | |
| "type": "integer", | |
| "default": 20, | |
| "maximum": 50, | |
| "description": "최대 결과 수" | |
| } | |
| } | |
| }, | |
| "RoundtripSearchRequest": { | |
| "type": "object", | |
| "required": ["origin", "destination"], | |
| "properties": { | |
| "origin": { | |
| "type": "string", | |
| "description": "출발지 공항 코드", | |
| "example": "ICN" | |
| }, | |
| "destination": { | |
| "type": "string", | |
| "description": "도착지 공항 코드", | |
| "example": "NRT" | |
| }, | |
| "outbound_date": { | |
| "type": "string", | |
| "format": "date", | |
| "description": "출발일 (YYYY-MM-DD)" | |
| }, | |
| "outbound_start_date": { | |
| "type": "string", | |
| "format": "date", | |
| "description": "출발일 검색 시작 (범위 검색 시)" | |
| }, | |
| "outbound_end_date": { | |
| "type": "string", | |
| "format": "date", | |
| "description": "출발일 검색 종료 (범위 검색 시)" | |
| }, | |
| "return_date": { | |
| "type": "string", | |
| "format": "date", | |
| "description": "귀국일 (YYYY-MM-DD)" | |
| }, | |
| "return_start_date": { | |
| "type": "string", | |
| "format": "date", | |
| "description": "귀국일 검색 시작 (범위 검색 시)" | |
| }, | |
| "return_end_date": { | |
| "type": "string", | |
| "format": "date", | |
| "description": "귀국일 검색 종료 (범위 검색 시)" | |
| }, | |
| "cabin": { | |
| "type": "string", | |
| "enum": ["economy", "premium", "business", "first"], | |
| "description": "좌석 등급" | |
| }, | |
| "cabin_explicit": { | |
| "type": "boolean", | |
| "description": "true일 때만 cabin 필터 적용" | |
| }, | |
| "programs": { | |
| "type": "string", | |
| "description": "마일리지 프로그램 필터" | |
| }, | |
| "direct_only": { | |
| "type": "boolean", | |
| "default": False, | |
| "description": "직항만 검색" | |
| }, | |
| "carriers": { | |
| "type": "string", | |
| "description": "항공사 필터" | |
| }, | |
| "limit": { | |
| "type": "integer", | |
| "default": 15, | |
| "maximum": 30, | |
| "description": "각 방향별 최대 결과 수" | |
| } | |
| } | |
| }, | |
| "SuccessResponse": { | |
| "type": "object", | |
| "properties": { | |
| "success": { | |
| "type": "boolean", | |
| "example": True | |
| }, | |
| "count": { | |
| "type": "integer" | |
| }, | |
| "results": { | |
| "type": "array", | |
| "items": { | |
| "type": "object" | |
| } | |
| } | |
| } | |
| }, | |
| "ErrorResponse": { | |
| "type": "object", | |
| "properties": { | |
| "success": { | |
| "type": "boolean", | |
| "example": False | |
| }, | |
| "error": { | |
| "type": "string" | |
| }, | |
| "message": { | |
| "type": "string" | |
| } | |
| } | |
| } | |
| } | |
| }, | |
| "security": [ | |
| {"oauth2": ["openid"]} | |
| ], | |
| "paths": { | |
| "/api/award/search/roundtrip": { | |
| "post": { | |
| "operationId": "searchRoundtripAward", | |
| "summary": "Search roundtrip award availability (추천)", | |
| "description": ( | |
| "왕복 마일리지 좌석 검색. 서버에서 자동으로 출발편+귀국편 검색.\n\n" | |
| "**사용 시점**: 사용자가 왕복 여행을 언급하거나, 출발일과 귀국일을 모두 제시한 경우\n\n" | |
| "**cabin 규칙**: 사용자가 '비즈니스', '퍼스트' 등을 명시한 경우에만 cabin + cabin_explicit: true" | |
| ), | |
| "x-openai-isConsequential": False, | |
| "requestBody": { | |
| "required": True, | |
| "content": { | |
| "application/json": { | |
| "schema": { | |
| "$ref": "#/components/schemas/RoundtripSearchRequest" | |
| } | |
| } | |
| } | |
| }, | |
| "responses": { | |
| "200": { | |
| "description": "왕복 검색 성공", | |
| "content": { | |
| "application/json": { | |
| "schema": { | |
| "$ref": "#/components/schemas/SuccessResponse" | |
| } | |
| } | |
| } | |
| }, | |
| "400": { | |
| "description": "잘못된 요청", | |
| "content": { | |
| "application/json": { | |
| "schema": { | |
| "$ref": "#/components/schemas/ErrorResponse" | |
| } | |
| } | |
| } | |
| }, | |
| "401": { | |
| "description": "인증 필요", | |
| "content": { | |
| "application/json": { | |
| "schema": { | |
| "$ref": "#/components/schemas/ErrorResponse" | |
| } | |
| } | |
| } | |
| }, | |
| "429": { | |
| "description": "일일 할당량 초과", | |
| "content": { | |
| "application/json": { | |
| "schema": { | |
| "$ref": "#/components/schemas/ErrorResponse" | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }, | |
| "/api/award/search": { | |
| "post": { | |
| "operationId": "searchOnewayAward", | |
| "summary": "Search one-way award availability", | |
| "description": ( | |
| "편도 마일리지 좌석 검색.\n\n" | |
| "**주의**: 왕복 검색은 /api/award/search/roundtrip 사용 권장" | |
| ), | |
| "x-openai-isConsequential": False, | |
| "requestBody": { | |
| "required": True, | |
| "content": { | |
| "application/json": { | |
| "schema": { | |
| "$ref": "#/components/schemas/SearchRequest" | |
| } | |
| } | |
| } | |
| }, | |
| "responses": { | |
| "200": { | |
| "description": "검색 성공", | |
| "content": { | |
| "application/json": { | |
| "schema": { | |
| "$ref": "#/components/schemas/SuccessResponse" | |
| } | |
| } | |
| } | |
| }, | |
| "400": { | |
| "description": "잘못된 요청", | |
| "content": { | |
| "application/json": { | |
| "schema": { | |
| "$ref": "#/components/schemas/ErrorResponse" | |
| } | |
| } | |
| } | |
| }, | |
| "401": { | |
| "description": "인증 필요", | |
| "content": { | |
| "application/json": { | |
| "schema": { | |
| "$ref": "#/components/schemas/ErrorResponse" | |
| } | |
| } | |
| } | |
| }, | |
| "429": { | |
| "description": "일일 할당량 초과", | |
| "content": { | |
| "application/json": { | |
| "schema": { | |
| "$ref": "#/components/schemas/ErrorResponse" | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }, | |
| "/api/award/trips/{availability_id}": { | |
| "get": { | |
| "operationId": "getAwardTripDetails", | |
| "summary": "Get trip details", | |
| "description": "검색 결과의 특정 가용성에 대한 상세 여정 정보", | |
| "x-openai-isConsequential": False, | |
| "parameters": [ | |
| { | |
| "name": "availability_id", | |
| "in": "path", | |
| "required": True, | |
| "schema": {"type": "string"}, | |
| "description": "검색 결과의 id 값" | |
| }, | |
| { | |
| "name": "include_filtered", | |
| "in": "query", | |
| "required": False, | |
| "schema": {"type": "boolean", "default": False}, | |
| "description": "필터링된 결과 포함 여부" | |
| } | |
| ], | |
| "responses": { | |
| "200": { | |
| "description": "여정 상세", | |
| "content": { | |
| "application/json": { | |
| "schema": { | |
| "$ref": "#/components/schemas/SuccessResponse" | |
| } | |
| } | |
| } | |
| }, | |
| "401": { | |
| "description": "인증 필요", | |
| "content": { | |
| "application/json": { | |
| "schema": { | |
| "$ref": "#/components/schemas/ErrorResponse" | |
| } | |
| } | |
| } | |
| }, | |
| "404": { | |
| "description": "찾을 수 없음", | |
| "content": { | |
| "application/json": { | |
| "schema": { | |
| "$ref": "#/components/schemas/ErrorResponse" | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }, | |
| "/api/award/routes": { | |
| "get": { | |
| "operationId": "getAvailableRoutes", | |
| "summary": "Get available routes", | |
| "description": "특정 마일리지 프로그램의 검색 가능 노선", | |
| "x-openai-isConsequential": False, | |
| "parameters": [ | |
| { | |
| "name": "source", | |
| "in": "query", | |
| "required": True, | |
| "schema": {"type": "string"}, | |
| "description": "프로그램 코드 (예: united, aeroplan, delta)" | |
| } | |
| ], | |
| "responses": { | |
| "200": { | |
| "description": "노선 목록", | |
| "content": { | |
| "application/json": { | |
| "schema": { | |
| "$ref": "#/components/schemas/SuccessResponse" | |
| } | |
| } | |
| } | |
| }, | |
| "401": { | |
| "description": "인증 필요", | |
| "content": { | |
| "application/json": { | |
| "schema": { | |
| "$ref": "#/components/schemas/ErrorResponse" | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| }, | |
| "/api/programs": { | |
| "get": { | |
| "operationId": "listMileagePrograms", | |
| "summary": "List mileage programs", | |
| "description": "지원되는 마일리지 프로그램 목록 (인증 불필요)", | |
| "x-openai-isConsequential": False, | |
| "security": [], | |
| "responses": { | |
| "200": { | |
| "description": "프로그램 목록", | |
| "content": { | |
| "application/json": { | |
| "schema": { | |
| "$ref": "#/components/schemas/SuccessResponse" | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| async def openapi_schema(request: Request) -> JSONResponse: | |
| """OpenAPI 스키마 엔드포인트""" | |
| host = request.headers.get("host", "lovelymango-eodi-seats-aero-dev.hf.space") | |
| scheme = request.headers.get("x-forwarded-proto", "https") | |
| base_url = f"{scheme}://{host}" | |
| schema = generate_openapi_schema(base_url) | |
| return JSONResponse(schema) | |