diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a95ebc0..d416fb1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -40,6 +40,7 @@ jobs: build-args: | OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }} ACCESS_TOKEN=${{ secrets.ACCESS_TOKEN }} + MONGODB_URL=${{ secrets.MONGODB_URL }} deploy: needs: build @@ -58,4 +59,5 @@ jobs: docker run -d -p 8001:8001 --name ${{ github.repository_id }} --restart always \ -e OPENAI_API_KEYL=${{ secrets.OPENAI_API_KEY }} \ -e ACCESS_TOKEN=${{ secrets.ACCESS_TOKEN }} \ + -e MONGODB_URL=${{ secrets.MONGODB_URL }} \ ${{ env.DOCKER_IMAGE }}:latest \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 898c039..904beb4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,8 +10,10 @@ COPY ./app /code/app ARG OPENAI_API_KEY ARG ACCESS_TOKEN +ARG MONGODB_URL ENV OPENAI_API_KEY=${OPENAI_API_KEY} ENV ACCESS_TOKEN=${ACCESS_TOKEN} +ENV MONGODB_URL=${MONGODB_URL} CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8001"] \ No newline at end of file diff --git a/app/config.py b/app/config.py index 7a5b78c..8c7c293 100644 --- a/app/config.py +++ b/app/config.py @@ -1,5 +1,6 @@ import os from pathlib import Path + from dotenv import load_dotenv from openai import OpenAI @@ -10,4 +11,5 @@ client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY")) -ACCESS_TOKEN = os.environ.get("ACCESS_TOKEN") \ No newline at end of file +ACCESS_TOKEN = os.environ.get("ACCESS_TOKEN") +MONGODB_URL = os.environ.get("MONGODB_URL") diff --git a/app/database.py b/app/database.py new file mode 100644 index 0000000..0286d8f --- /dev/null +++ b/app/database.py @@ -0,0 +1,9 @@ +from motor.motor_asyncio import AsyncIOMotorClient + +from app.config import MONGODB_URL + +client = AsyncIOMotorClient(MONGODB_URL) +db = client.deening +recipe_collection = db.recipes +cooking_step_collection = db.cooking_steps +ingredient_collection = db.ingredients diff --git a/app/main.py b/app/main.py index 3caf86d..f4df04e 100644 --- a/app/main.py +++ b/app/main.py @@ -1,8 +1,9 @@ from fastapi import FastAPI, Depends + +from app.dependencies.auth import verify_token from app.routes import ping from app.routes.food import ingredient_detect from app.routes.recipe import recipe, ingredient, cooking_step -from app.dependencies.auth import verify_token app = FastAPI( title="Deening API", @@ -26,4 +27,4 @@ app.include_router(recipe.router, dependencies=[Depends(verify_token)]) app.include_router(ingredient.router, dependencies=[Depends(verify_token)]) app.include_router(cooking_step.router, dependencies=[Depends(verify_token)]) -app.include_router(ingredient_detect.router, dependencies=[Depends(verify_token)]) \ No newline at end of file +app.include_router(ingredient_detect.router, dependencies=[Depends(verify_token)]) diff --git a/app/models/food/ingredient_detect_models.py b/app/models/food/ingredient_detect_models.py index efc844f..8ecb816 100644 --- a/app/models/food/ingredient_detect_models.py +++ b/app/models/food/ingredient_detect_models.py @@ -1,15 +1,20 @@ -from pydantic import BaseModel from typing import List + from fastapi import UploadFile +from pydantic import BaseModel + class IngredientDetectRequest(BaseModel): image: UploadFile + class DetectedIngredient(BaseModel): ingredient_name: str + class IngredientDetectResponse(BaseModel): ingredients: List[DetectedIngredient] + class NoIngredientsFoundResponse(BaseModel): - message: str = "No ingredients found in the image" \ No newline at end of file + message: str = "No ingredients found in the image" diff --git a/app/models/ping_models.py b/app/models/ping_models.py index 607c53d..aa39357 100644 --- a/app/models/ping_models.py +++ b/app/models/ping_models.py @@ -1,4 +1,5 @@ from pydantic import BaseModel + class PingResponse(BaseModel): - message: str \ No newline at end of file + message: str diff --git a/app/models/recipe/cooking_step_models.py b/app/models/recipe/cooking_step_models.py index 0bd7499..d1ee071 100644 --- a/app/models/recipe/cooking_step_models.py +++ b/app/models/recipe/cooking_step_models.py @@ -1,10 +1,13 @@ -from pydantic import BaseModel from typing import List +from pydantic import BaseModel + + class CookingStepRequest(BaseModel): recipe_id: str step_number: int + class CookingStep(BaseModel): recipe_id: str step_number: int @@ -14,6 +17,8 @@ class CookingStep(BaseModel): ingredients_used: List[str] tips: str + class CookingStepResponse(BaseModel): + id: str cooking_step: CookingStep - image_url: str \ No newline at end of file + image_url: str diff --git a/app/models/recipe/ingredient_models.py b/app/models/recipe/ingredient_models.py index 25e1c73..5559a40 100644 --- a/app/models/recipe/ingredient_models.py +++ b/app/models/recipe/ingredient_models.py @@ -1,9 +1,12 @@ -from pydantic import BaseModel from typing import List +from pydantic import BaseModel + + class IngredientRequest(BaseModel): ingredient_name: str + class NutritionalInfo(BaseModel): calories: int protein: float @@ -13,6 +16,7 @@ class NutritionalInfo(BaseModel): vitamins: str minerals: str + class Ingredient(BaseModel): name: str description: str @@ -21,6 +25,8 @@ class Ingredient(BaseModel): storage_tips: str culinary_uses: List[str] + class IngredientResponse(BaseModel): + id: str ingredient: Ingredient - image_url: str \ No newline at end of file + image_url: str diff --git a/app/models/recipe/recipe_models.py b/app/models/recipe/recipe_models.py index 6f00a9f..ad9c669 100644 --- a/app/models/recipe/recipe_models.py +++ b/app/models/recipe/recipe_models.py @@ -1,21 +1,25 @@ from pydantic import BaseModel from typing import List + class Ingredient(BaseModel): name: str amount: float unit: str + class Instruction(BaseModel): step: int description: str + class Nutrition(BaseModel): calories: int protein: str carbohydrates: str fat: str + class Recipe(BaseModel): name: str description: str @@ -30,10 +34,12 @@ class Recipe(BaseModel): tags: List[str] source: str + class RecipeRequest(BaseModel): food_name: str + class RecipeResponse(BaseModel): id: str recipe: Recipe - image_url: str \ No newline at end of file + image_url: str diff --git a/app/routes/food/ingredient_detect.py b/app/routes/food/ingredient_detect.py index 2da9d51..26eb50a 100644 --- a/app/routes/food/ingredient_detect.py +++ b/app/routes/food/ingredient_detect.py @@ -1,18 +1,23 @@ -from fastapi import APIRouter, File, UploadFile, HTTPException -from pydantic import BaseModel -import json import base64 +import json import logging +from fastapi import APIRouter, File, UploadFile, HTTPException +from pydantic import BaseModel + from app.config import client -from app.models.food.ingredient_detect_models import IngredientDetectResponse, DetectedIngredient, IngredientDetectRequest, NoIngredientsFoundResponse +from app.models.food.ingredient_detect_models import IngredientDetectResponse, DetectedIngredient, \ + NoIngredientsFoundResponse router = APIRouter() + class ErrorResponse(BaseModel): error: str -@router.post("/ingredient_detect", tags=["Food"], response_model=IngredientDetectResponse, responses={400: {"model": ErrorResponse}, 404: {"model": NoIngredientsFoundResponse}}) + +@router.post("/ingredient_detect", tags=["Food"], response_model=IngredientDetectResponse, + responses={400: {"model": ErrorResponse}, 404: {"model": NoIngredientsFoundResponse}}) async def ingredient_detect(image: UploadFile = File(...)): """ 업로드된 이미지 파일에서 식재료를 탐색해 반환합니다. @@ -68,7 +73,8 @@ async def ingredient_detect(image: UploadFile = File(...)): except json.JSONDecodeError: raise HTTPException(status_code=400, detail="생성된 식재료 정보를 JSON으로 파싱할 수 없습니다.") - detected_ingredients = [DetectedIngredient(**ingredient) for ingredient in ingredient_detect_json["ingredients"]] + detected_ingredients = [DetectedIngredient(**ingredient) for ingredient in + ingredient_detect_json["ingredients"]] if not detected_ingredients: return NoIngredientsFoundResponse(status_code=404) @@ -79,4 +85,4 @@ async def ingredient_detect(image: UploadFile = File(...)): raise http_ex except Exception as e: logging.error(f"Unexpected error: {str(e)}") - raise HTTPException(status_code=500, detail=f"An unexpected error occurred: {str(e)}") \ No newline at end of file + raise HTTPException(status_code=500, detail=f"An unexpected error occurred: {str(e)}") diff --git a/app/routes/ping.py b/app/routes/ping.py index 9f69556..08a5e2d 100644 --- a/app/routes/ping.py +++ b/app/routes/ping.py @@ -1,11 +1,13 @@ from fastapi import APIRouter + from app.models.ping_models import PingResponse router = APIRouter() + @router.get("/ping", response_model=PingResponse) async def ping(): """ Health check endpoint """ - return {"message": "pong"} \ No newline at end of file + return {"message": "pong"} diff --git a/app/routes/recipe/cooking_step.py b/app/routes/recipe/cooking_step.py index b235f1c..f133f8c 100644 --- a/app/routes/recipe/cooking_step.py +++ b/app/routes/recipe/cooking_step.py @@ -1,29 +1,47 @@ +import json + +from bson import ObjectId from fastapi import APIRouter, HTTPException -from app.models.recipe.cooking_step_models import CookingStepRequest, CookingStep, CookingStepResponse from pydantic import BaseModel -from app.config import client -import json -from app.routes.recipe.recipe import recipe_store +from app.config import client as openai_client +from app.database import recipe_collection, cooking_step_collection +from app.models.recipe.cooking_step_models import CookingStepRequest, CookingStep, CookingStepResponse router = APIRouter() + class ErrorResponse(BaseModel): error: str -@router.post("/cooking_step", tags=["Recipe"], response_model=CookingStepResponse, responses={400: {"model": ErrorResponse}, 404: {"model": ErrorResponse}}) -async def get_cooking_step_info(request: CookingStepRequest): + +@router.post("/cooking_step", tags=["Recipe"], response_model=CookingStepResponse, + responses={400: {"model": ErrorResponse}, 404: {"model": ErrorResponse}}) +async def get_or_create_cooking_step_info(request: CookingStepRequest): """ - 레시피의 특정 조리 단계에 대한 상세 정보를 생성합니다. + 레시피의 특정 조리 단계에 대한 상세 정보를 반환하거나 생성합니다. """ - if request.recipe_id not in recipe_store: - raise HTTPException(status_code=404, detail="레시피를 찾을 수 없습니다.") - - stored_data = recipe_store[request.recipe_id] - recipe = stored_data["recipe"] - try: - # 조리 과정 정보 생성 프롬프트 + # 기존 조리 단계 정보 검색 + existing_step = await cooking_step_collection.find_one({ + "recipe_id": request.recipe_id, + "step_number": request.step_number + }) + + if existing_step: + # 기존 정보가 있으면 그대로 반환 + cooking_step = CookingStep(**existing_step) + return CookingStepResponse( + id=str(existing_step['_id']), + cooking_step=cooking_step, + image_url=existing_step.get('image_url') + ) + + # 기존 정보가 없으면 새로 생성 + recipe = await recipe_collection.find_one({"_id": ObjectId(request.recipe_id)}) + if not recipe: + raise HTTPException(status_code=404, detail="레시피를 찾을 수 없습니다.") + cooking_step_prompt = f"""제공된 레시피에 대한 자세한 조리 과정 정보를 JSON 형식으로 생성해주세요. 다음 구조를 따라주세요: {{ @@ -43,8 +61,7 @@ async def get_cooking_step_info(request: CookingStepRequest): 주의: 반드시 다른 텍스트 없이 유효한 JSON 형식으로만 응답해주세요. """ - # 조리 과정 정보 생성 - cooking_step_response = client.chat.completions.create( + cooking_step_response = openai_client.chat.completions.create( model="chatgpt-4o-latest", messages=[ {"role": "system", "content": "당신은 전문 요리사입니다. 주어진 레시피의 특정 조리 단계에 대한 상세한 정보를 JSON 형식으로 제공합니다."}, @@ -55,8 +72,7 @@ async def get_cooking_step_info(request: CookingStepRequest): cooking_step_json = json.loads(cooking_step_response.choices[0].message.content) cooking_step = CookingStep(**cooking_step_json) - # 이미지 생성 프롬프트 - image_prompt = f"""A high-quality, detailed photo demonstrating the cooking step for {recipe.name}, step number {cooking_step.step_number}: + image_prompt = f"""A high-quality, detailed photo demonstrating the cooking step for {recipe['name']}, step number {cooking_step.step_number}: - Description: {cooking_step.description} - Tools used: {', '.join(cooking_step.tools_needed)} @@ -67,8 +83,7 @@ async def get_cooking_step_info(request: CookingStepRequest): The image should be from a slightly elevated angle to give a clear view of the cooking surface and the chef's hands (if applicable). """ - # DALL-E를 사용한 이미지 생성 - image_response = client.images.generate( + image_response = openai_client.images.generate( model="dall-e-3", prompt=image_prompt, size="1024x1024", @@ -76,11 +91,36 @@ async def get_cooking_step_info(request: CookingStepRequest): n=1, ) - # 이미지 URL 가져오기 image_url = image_response.data[0].url - return CookingStepResponse(cooking_step=cooking_step, image_url=image_url) + cooking_step_dict = cooking_step.dict() + cooking_step_dict['image_url'] = image_url + result = await cooking_step_collection.insert_one(cooking_step_dict) + cooking_step_id = str(result.inserted_id) + + print(cooking_step_response) + print(image_response) + + return CookingStepResponse(id=cooking_step_id, cooking_step=cooking_step, image_url=image_url) except json.JSONDecodeError: raise HTTPException(status_code=400, detail="생성된 조리 과정 정보를 JSON으로 파싱할 수 없습니다.") except Exception as e: - raise HTTPException(status_code=400, detail=str(e)) \ No newline at end of file + raise HTTPException(status_code=400, detail=str(e)) + + +@router.get("/cooking_step/{cooking_step_id}", tags=["Recipe"], response_model=CookingStepResponse, + responses={404: {"model": ErrorResponse}}) +async def get_cooking_step_by_id(cooking_step_id: str): + """ + 주어진 ID에 대한 조리 단계 정보를 반환합니다. + """ + try: + cooking_step_data = await cooking_step_collection.find_one({"_id": ObjectId(cooking_step_id)}) + if not cooking_step_data: + raise HTTPException(status_code=404, detail="조리 단계 정보를 찾을 수 없습니다.") + + image_url = cooking_step_data.pop('image_url', None) + cooking_step = CookingStep(**cooking_step_data) + return CookingStepResponse(id=str(cooking_step_data['_id']), cooking_step=cooking_step, image_url=image_url) + except Exception as e: + raise HTTPException(status_code=400, detail=str(e)) diff --git a/app/routes/recipe/ingredient.py b/app/routes/recipe/ingredient.py index 9c5f643..61147ae 100644 --- a/app/routes/recipe/ingredient.py +++ b/app/routes/recipe/ingredient.py @@ -1,21 +1,36 @@ +import json + +from bson import ObjectId from fastapi import APIRouter, HTTPException -from app.models.recipe.ingredient_models import IngredientRequest, Ingredient, IngredientResponse from pydantic import BaseModel -from app.config import client -import json + +from app.config import client as openai_client +from app.database import ingredient_collection +from app.models.recipe.ingredient_models import IngredientRequest, Ingredient, IngredientResponse router = APIRouter() class ErrorResponse(BaseModel): error: str -@router.post("/ingredient", tags=["Recipe"], response_model=IngredientResponse, responses={400: {"model": ErrorResponse}}) -async def get_ingredient_info(request: IngredientRequest): +@router.post("/ingredient", tags=["Recipe"], response_model=IngredientResponse, + responses={400: {"model": ErrorResponse}}) +async def get_or_create_ingredient(request: IngredientRequest): """ - 식재료 정보와 이미지를 생성합니다. + 식재료 이름으로 검색하여 정보를 반환하거나, 없으면 새로 생성합니다. """ try: - # 식재료 정보 생성 프롬프트 + # 데이터베이스에서 재료 검색 + ingredient_data = await ingredient_collection.find_one({"name": request.ingredient_name}) + + if ingredient_data: + # 이미 존재하는 재료 정보 반환 + image_url = ingredient_data.pop('image_url', None) + ingredient_id = str(ingredient_data.pop('_id')) + ingredient = Ingredient(**ingredient_data) + return IngredientResponse(ingredient=ingredient, image_url=image_url, id=ingredient_id) + + # 재료 정보가 없으면 새로 생성 ingredient_prompt = f"""제공된 식재료에 대한 정보를 JSON 형식으로 생성해주세요. 다음 구조를 따라주세요: {{ @@ -40,8 +55,7 @@ async def get_ingredient_info(request: IngredientRequest): 주의: 반드시 다른 텍스트 없이 유효한 JSON 형식으로만 응답해주세요. """ - # 식재료 정보 생성 - ingredient_response = client.chat.completions.create( + ingredient_response = openai_client.chat.completions.create( model="chatgpt-4o-latest", messages=[ {"role": "system", "content": "당신은 영양학 전문가입니다. 주어진 식재료에 대한 상세한 정보를 JSON 형식으로 제공합니다."}, @@ -52,7 +66,6 @@ async def get_ingredient_info(request: IngredientRequest): ingredient_json = json.loads(ingredient_response.choices[0].message.content) ingredient = Ingredient(**ingredient_json) - # 이미지 생성 프롬프트 image_prompt = f"""A high-quality, detailed photo of {ingredient.name}, as described: - Category: {ingredient.category} @@ -63,8 +76,7 @@ async def get_ingredient_info(request: IngredientRequest): If applicable, include some context that hints at its culinary uses or storage method. """ - # DALL-E를 사용한 이미지 생성 - image_response = client.images.generate( + image_response = openai_client.images.generate( model="dall-e-3", prompt=image_prompt, size="1024x1024", @@ -72,11 +84,33 @@ async def get_ingredient_info(request: IngredientRequest): n=1, ) - # 이미지 URL 가져오기 image_url = image_response.data[0].url - return IngredientResponse(ingredient=ingredient, image_url=image_url) + ingredient_dict = ingredient.dict() + ingredient_dict['image_url'] = image_url + result = await ingredient_collection.insert_one(ingredient_dict) + ingredient_id = str(result.inserted_id) + + return IngredientResponse(ingredient=ingredient, image_url=image_url, id=ingredient_id) except json.JSONDecodeError: raise HTTPException(status_code=400, detail="생성된 식재료 정보를 JSON으로 파싱할 수 없습니다.") + except Exception as e: + raise HTTPException(status_code=400, detail=str(e)) + +@router.get("/ingredient/{ingredient_id}", tags=["Recipe"], response_model=IngredientResponse, + responses={404: {"model": ErrorResponse}}) +async def get_ingredient_by_id(ingredient_id: str): + """ + 주어진 ID에 대한 식재료 정보를 반환합니다. + """ + try: + ingredient_data = await ingredient_collection.find_one({"_id": ObjectId(ingredient_id)}) + if not ingredient_data: + raise HTTPException(status_code=404, detail="식재료 정보를 찾을 수 없습니다.") + + image_url = ingredient_data.pop('image_url', None) + ingredient_id = str(ingredient_data.pop('_id')) + ingredient = Ingredient(**ingredient_data) + return IngredientResponse(ingredient=ingredient, image_url=image_url, id=ingredient_id) except Exception as e: raise HTTPException(status_code=400, detail=str(e)) \ No newline at end of file diff --git a/app/routes/recipe/recipe.py b/app/routes/recipe/recipe.py index 395494c..26a2dc9 100644 --- a/app/routes/recipe/recipe.py +++ b/app/routes/recipe/recipe.py @@ -1,24 +1,34 @@ +import json + +from bson import ObjectId from fastapi import APIRouter, HTTPException -from app.models.recipe.recipe_models import Recipe, RecipeRequest, RecipeResponse from pydantic import BaseModel -from app.config import client -import json -import uuid -router = APIRouter() +from app.config import client as openai_client +from app.database import recipe_collection +from app.models.recipe.recipe_models import Recipe, RecipeRequest, RecipeResponse -recipe_store = {} +router = APIRouter() class ErrorResponse(BaseModel): error: str @router.post("/recipe", tags=["Recipe"], response_model=RecipeResponse, responses={400: {"model": ErrorResponse}}) -async def get_recipe(request: RecipeRequest): +async def get_or_create_recipe(request: RecipeRequest): """ - 주어진 음식 이름에 대한 레시피를 생성합니다. + 주어진 음식 이름에 대한 레시피를 검색하거나 생성합니다. """ try: - # 레시피 생성 프롬프트 + # 데이터베이스에서 레시피 검색 + recipe_data = await recipe_collection.find_one({"name": request.food_name}) + + if recipe_data: + # 이미 존재하는 레시피 정보 반환 + image_url = recipe_data.pop('image_url', None) + recipe = Recipe(**recipe_data) + return RecipeResponse(id=str(recipe_data['_id']), recipe=recipe, image_url=image_url) + + # 레시피가 없으면 새로 생성 recipe_prompt = f"""제공된 음식 이름에 대한 레시피를 JSON 형식으로 생성해주세요. 다음 구조를 따라주세요: {{ @@ -57,8 +67,7 @@ async def get_recipe(request: RecipeRequest): 주의: 반드시 다른 텍스트 없이 유효한 JSON 형식으로만 응답해주세요. """ - # 레시피 생성 - recipe_response = client.chat.completions.create( + recipe_response = openai_client.chat.completions.create( model="chatgpt-4o-latest", messages=[ {"role": "system", "content": "당신은 요리 전문가입니다. 주어진 음식에 대한 상세한 레시피를 JSON 형식으로 제공합니다."}, @@ -69,7 +78,6 @@ async def get_recipe(request: RecipeRequest): recipe_json = json.loads(recipe_response.choices[0].message.content) recipe = Recipe(**recipe_json) - # 이미지 생성 프롬프트 image_prompt = f"""A high-quality, appetizing photo of {recipe.name}, as described in the recipe. The dish should look professionally plated and photographed, with attention to detail and presentation. @@ -82,8 +90,7 @@ async def get_recipe(request: RecipeRequest): Ensure the presentation matches the difficulty level of '{recipe.difficulty}' and serves {recipe.servings}. """ - # DALL-E를 사용한 이미지 생성 - image_response = client.images.generate( + image_response = openai_client.images.generate( model="dall-e-3", prompt=image_prompt, size="1024x1024", @@ -91,17 +98,12 @@ async def get_recipe(request: RecipeRequest): n=1, ) - # 이미지 URL 가져오기 image_url = image_response.data[0].url - # ID 생성 - recipe_id = str(uuid.uuid4()) - - # 레시피와 이미지 URL 저장 - recipe_store[recipe_id] = { - "recipe": recipe, - "image_url": image_url - } + recipe_dict = recipe.dict() + recipe_dict['image_url'] = image_url + result = await recipe_collection.insert_one(recipe_dict) + recipe_id = str(result.inserted_id) return RecipeResponse(id=recipe_id, recipe=recipe, image_url=image_url) except json.JSONDecodeError: @@ -109,13 +111,19 @@ async def get_recipe(request: RecipeRequest): except Exception as e: raise HTTPException(status_code=400, detail=str(e)) -@router.get("/recipe/{recipe_id}", tags=["Recipe"], response_model=RecipeResponse, responses={404: {"model": ErrorResponse}}) +@router.get("/recipe/{recipe_id}", tags=["Recipe"], response_model=RecipeResponse, + responses={404: {"model": ErrorResponse}}) async def get_recipe_by_id(recipe_id: str): """ 주어진 ID에 대한 레시피를 반환합니다. """ - if recipe_id not in recipe_store: - raise HTTPException(status_code=404, detail="레시피를 찾을 수 없습니다.") + try: + recipe_data = await recipe_collection.find_one({"_id": ObjectId(recipe_id)}) + if not recipe_data: + raise HTTPException(status_code=404, detail="레시피를 찾을 수 없습니다.") - stored_data = recipe_store[recipe_id] - return RecipeResponse(id=recipe_id, recipe=stored_data["recipe"], image_url=stored_data["image_url"]) \ No newline at end of file + image_url = recipe_data.pop('image_url', None) + recipe = Recipe(**recipe_data) + return RecipeResponse(id=str(recipe_data['_id']), recipe=recipe, image_url=image_url) + except Exception as e: + raise HTTPException(status_code=400, detail=str(e)) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 2eea959..b5ef194 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,5 @@ uvicorn[standard] python-dotenv openai requests -pydantic \ No newline at end of file +pydantic +motor \ No newline at end of file