diff --git a/mealie/routes/parser/ingredient_parser.py b/mealie/routes/parser/ingredient_parser.py index a4a36d2aa39..dd654287692 100644 --- a/mealie/routes/parser/ingredient_parser.py +++ b/mealie/routes/parser/ingredient_parser.py @@ -13,10 +13,10 @@ class IngredientParserController(BaseUserController): @router.post("/ingredient", response_model=ParsedIngredient) async def parse_ingredient(self, ingredient: IngredientRequest): parser = get_parser(ingredient.parser, self.group_id, self.session) - response = await parser.parse([ingredient.ingredient]) + response = await parser.parse([ingredient.ingredient], translate_language=ingredient.translate_language) return response[0] @router.post("/ingredients", response_model=list[ParsedIngredient]) async def parse_ingredients(self, ingredients: IngredientsRequest): parser = get_parser(ingredients.parser, self.group_id, self.session) - return await parser.parse(ingredients.ingredients) + return await parser.parse(ingredients.ingredients, translate_language=ingredients.translate_language) diff --git a/mealie/schema/recipe/recipe_ingredient.py b/mealie/schema/recipe/recipe_ingredient.py index 3b74fbfe075..2ac5b62e19e 100644 --- a/mealie/schema/recipe/recipe_ingredient.py +++ b/mealie/schema/recipe/recipe_ingredient.py @@ -329,11 +329,13 @@ class RegisteredParser(str, enum.Enum): class IngredientsRequest(MealieModel): parser: RegisteredParser = RegisteredParser.nlp ingredients: list[str] + translate_language: str | None = None class IngredientRequest(MealieModel): parser: RegisteredParser = RegisteredParser.nlp ingredient: str + translate_language: str | None = None class MergeFood(MealieModel): diff --git a/mealie/services/parser_services/_base.py b/mealie/services/parser_services/_base.py index e7aee5b7dad..393401d3fb5 100644 --- a/mealie/services/parser_services/_base.py +++ b/mealie/services/parser_services/_base.py @@ -148,10 +148,10 @@ def unit_fuzzy_match_threshold(self) -> int: return 70 @abstractmethod - async def parse_one(self, ingredient_string: str) -> ParsedIngredient: ... + async def parse_one(self, ingredient_string: str, translate_language: str | None = None) -> ParsedIngredient: ... @abstractmethod - async def parse(self, ingredients: list[str]) -> list[ParsedIngredient]: ... + async def parse(self, ingredients: list[str], translate_language: str | None = None) -> list[ParsedIngredient]: ... def find_ingredient_match(self, ingredient: ParsedIngredient) -> ParsedIngredient: if ingredient.ingredient.food and (food_match := self.data_matcher.find_food_match(ingredient.ingredient.food)): diff --git a/mealie/services/parser_services/ingredient_parser.py b/mealie/services/parser_services/ingredient_parser.py index 2f26a1ae520..5651b5d92f3 100644 --- a/mealie/services/parser_services/ingredient_parser.py +++ b/mealie/services/parser_services/ingredient_parser.py @@ -30,7 +30,7 @@ class BruteForceParser(ABCIngredientParser): Brute force ingredient parser. """ - async def parse_one(self, ingredient_string: str) -> ParsedIngredient: + async def parse_one(self, ingredient_string: str, translate_language: str | None = None) -> ParsedIngredient: bfi = brute.parse(ingredient_string, self) parsed_ingredient = ParsedIngredient( @@ -66,7 +66,7 @@ async def parse_one(self, ingredient_string: str) -> ParsedIngredient: return matched_ingredient - async def parse(self, ingredients: list[str]) -> list[ParsedIngredient]: + async def parse(self, ingredients: list[str], translate_language: str | None = None) -> list[ParsedIngredient]: return [await self.parse_one(ingredient) for ingredient in ingredients] @@ -182,11 +182,11 @@ def _convert_ingredient(self, ingredient: IngredientParserParsedIngredient) -> P return self.find_ingredient_match(parsed_ingredient) - async def parse_one(self, ingredient_string: str) -> ParsedIngredient: + async def parse_one(self, ingredient_string: str, translate_language: str | None = None) -> ParsedIngredient: parsed_ingredient = parse_ingredient(ingredient_string) return self._convert_ingredient(parsed_ingredient) - async def parse(self, ingredients: list[str]) -> list[ParsedIngredient]: + async def parse(self, ingredients: list[str], translate_language: str | None = None) -> list[ParsedIngredient]: return [await self.parse_one(ingredient) for ingredient in ingredients] diff --git a/mealie/services/parser_services/openai/parser.py b/mealie/services/parser_services/openai/parser.py index e0e64474c1b..01446d86cbe 100644 --- a/mealie/services/parser_services/openai/parser.py +++ b/mealie/services/parser_services/openai/parser.py @@ -112,7 +112,7 @@ def _get_prompt(self, service: OpenAIService) -> str: OpenAIDataInjection( description=( "This is the JSON response schema. You must respond in valid JSON that follows this schema. " - "Your payload should be as compact as possible, eliminating unncessesary whitespace. Any fields " + "Your payload should be as compact as possible, eliminating unnecessary whitespace. Any fields " "with default values which you do not populate should not be in the payload." ), value=OpenAIIngredients, @@ -142,7 +142,7 @@ def _chunk_messages(messages: list[str], n=1) -> list[list[str]]: n = 1 return [messages[i : i + n] for i in range(0, len(messages), n)] - async def _parse(self, ingredients: list[str]) -> OpenAIIngredients: + async def _parse(self, ingredients: list[str], translate_language: str | None = None) -> OpenAIIngredients: service = OpenAIService() prompt = self._get_prompt(service) @@ -151,6 +151,8 @@ async def _parse(self, ingredients: list[str]) -> OpenAIIngredients: tasks: list[Awaitable[str | None]] = [] for ingredient_chunk in ingredient_chunks: message = json.dumps(ingredient_chunk, separators=(",", ":")) + if translate_language: + message += f" Please translate the recipe to {translate_language}." tasks.append(service.get_response(prompt, message, force_json_response=True)) # re-combine chunks into one response @@ -175,12 +177,12 @@ async def _parse(self, ingredients: list[str]) -> OpenAIIngredients: ingredients=[ingredient for response in responses for ingredient in response.ingredients] ) - async def parse_one(self, ingredient_string: str) -> ParsedIngredient: - items = await self.parse([ingredient_string]) + async def parse_one(self, ingredient_string: str, translate_language: str | None = None) -> ParsedIngredient: + items = await self.parse([ingredient_string], translate_language=translate_language) return items[0] - async def parse(self, ingredients: list[str]) -> list[ParsedIngredient]: - response = await self._parse(ingredients) + async def parse(self, ingredients: list[str], translate_language: str | None = None) -> list[ParsedIngredient]: + response = await self._parse(ingredients, translate_language=translate_language) if len(response.ingredients) != len(ingredients): raise ValueError( "OpenAI returned an unexpected number of ingredients. "