diff --git a/pontoon/machinery/management/commands/refine_translation.py b/pontoon/machinery/management/commands/refine_translation.py new file mode 100644 index 0000000000..4ea4432060 --- /dev/null +++ b/pontoon/machinery/management/commands/refine_translation.py @@ -0,0 +1,37 @@ +from django.core.management.base import BaseCommand +from pontoon.machinery.openai_service import OpenAIService + + +class Command(BaseCommand): + help = "Refines machine translations using OpenAI's GPT-4 API with specified characteristics" + + def add_arguments(self, parser): + parser.add_argument( + "characteristic", + type=str, + choices=["informal", "formal", "alternative"], + help="The specified characteristic of the translation (informal, formal, alternative)", + ) + parser.add_argument( + "english_text", type=str, help="The source string in English" + ) + parser.add_argument( + "target_text", type=str, help="The machine-generated translation to refine" + ) + parser.add_argument( + "language_name", + type=str, + help="The name of the target language for the translation", + ) + + def handle(self, *args, **options): + characteristic = options["characteristic"] + english_text = options["english_text"] + target_text = options["target_text"] + language_name = options["language_name"] + + translator = OpenAIService() + translation = translator.get_translation( + english_text, target_text, characteristic, language_name + ) + self.stdout.write(self.style.SUCCESS(translation)) diff --git a/pontoon/machinery/openai_service.py b/pontoon/machinery/openai_service.py new file mode 100644 index 0000000000..51fc1a19e4 --- /dev/null +++ b/pontoon/machinery/openai_service.py @@ -0,0 +1,68 @@ +from openai import OpenAI +from pontoon.base.models import Locale + + +class OpenAIService: + def __init__(self): + self.client = OpenAI() + + def get_translation( + self, english_text, translated_text, characteristic, target_language_name + ): + try: + target_language = Locale.objects.get(name=target_language_name) + except Locale.DoesNotExist: + raise ValueError( + f"The target language '{target_language_name}' is not supported." + ) + + informal = ( + f"You will be provided with text in English, along with its machine-generated translation in {target_language}. " + "Your objective is to revise the {target_language} translation to ensure it utilizes simpler language. Adhere to the following guidelines to achieve this:\n" + "- Clarity is Key: Ensure the translation conveys the original message in the clearest possible manner, without ambiguity or unnecessary complexity.\n" + "- Consistent Simplicity: Maintain a consistent level of simplicity throughout the translation.\n" + "The goal is to produce a translation that accurately reflects the original English text, but in a way that is more approachable and easier to understand for all {target_language} speakers." + ) + + formal = ( + f"You will be provided with text in English, along with its machine-generated translation in {target_language}. " + "Your objective is to revise the {target_language} translation to ensure it utilizes a higher level of formality. Adhere to the following guidelines to achieve this:\n" + "- Adjust the Tone: Ensure the tone is respectful, polished, and devoid of colloquialisms or informal expressions commonly used in casual conversation.\n" + "- Formal Addressing: Where applicable, use formal modes of address.\n" + "- Consistency: Maintain a consistent level of formality throughout the translation, avoiding shifts in tone or style.\n" + "Your goal is to produce a translation that not only accurately conveys the meaning of the English text but also meets the expectations for formality in {target_language}-speaking professional or formal settings." + ) + + alternative = ( + f"You will be provided with text in English, along with its machine-generated translation in {target_language}. " + "Your objective is to provide an alternative translation. Adhere to the following guidelines to achieve this:\n" + "- Cultural Nuances: Pay attention to cultural nuances and idiomatic expressions, ensuring they are appropriately translated for the {target_language}-speaking audience.\n" + "- Clarification and Accuracy: Where the source text is ambiguous or idiomatic, offer clarifications or alternative expressions in {target_language}.\n" + "Just provide the text for the alternative translation." + ) + + system_messages = { + "informal": informal, + "formal": formal, + "alternative": alternative, + } + + system_message = system_messages.get(characteristic) + if system_message is None: + raise ValueError(f"Unrecognized characteristic: '{characteristic}'") + + # Construct the user prompt with the language name + user_prompt = f"Refine the following {target_language} machine translation to make it {characteristic}: '{translated_text}' based on the original English text: '{english_text}'." + + # Call the OpenAI API with the constructed prompt + response = self.client.chat.completions.create( + model="gpt-4-0125-preview", + messages=[ + {"role": "system", "content": system_message}, + {"role": "user", "content": user_prompt}, + ], + temperature=0, # Set temperature to 0 for deterministic output + top_p=1, # Set top_p to 1 to consider the full distribution + ) + + return response.choices[0].message.content.strip() diff --git a/requirements/default.in b/requirements/default.in index 7f706d99cf..80e3f954a0 100644 --- a/requirements/default.in +++ b/requirements/default.in @@ -39,6 +39,7 @@ lxml==4.9.1 markupsafe==2.0.1 mercurial==6.4.2 newrelic==9.6.0 +openai==1.12.0 parsimonious==0.10.0 polib==1.0.6 psycopg2==2.9.6 diff --git a/requirements/default.txt b/requirements/default.txt index 9034ef611c..70efb0f760 100644 --- a/requirements/default.txt +++ b/requirements/default.txt @@ -12,6 +12,16 @@ aniso8601==7.0.0 \ --hash=sha256:513d2b6637b7853806ae79ffaca6f3e8754bdd547048f5ccc1420aec4b714f1e \ --hash=sha256:d10a4bf949f619f719b227ef5386e31f49a2b6d453004b21f02661ccc8670c7b # via graphene +annotated-types==0.6.0 \ + --hash=sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43 \ + --hash=sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d + # via pydantic +anyio==4.3.0 \ + --hash=sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8 \ + --hash=sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6 + # via + # httpx + # openai apscheduler==3.9.1.post1 \ --hash=sha256:b2bea0309569da53a7261bfa0ce19c67ddbfe151bda776a6a907579fdbd3eb2a \ --hash=sha256:c8c618241dbb2785ed5a687504b14cb1851d6f7b5a4edf3a51e39cc6a069967a @@ -42,7 +52,10 @@ celery==5.2.6 \ certifi==2021.10.8 \ --hash=sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872 \ --hash=sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569 - # via requests + # via + # httpcore + # httpx + # requests cffi==1.15.0 \ --hash=sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3 \ --hash=sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2 \ @@ -155,6 +168,10 @@ defusedxml==0.7.1 \ --hash=sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69 \ --hash=sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61 # via python3-openid +distro==1.9.0 \ + --hash=sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed \ + --hash=sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2 + # via openai dj-database-url==0.3.0 \ --hash=sha256:ca01768fdecde134301f3170743226f60edff5c3935f12437378ebd911506353 \ --hash=sha256:f2e273ed34acbb560962d5cf12917936d8df02297df09bd3089b8546d4584138 @@ -216,6 +233,10 @@ django-pipeline==2.0.6 \ --hash=sha256:3ec726e2bf9f61f213f41ee4513f4191a02ab2f5df86c9e3751a9a607c814031 \ --hash=sha256:443c560000b3202dafa873cf9b9a49018ded4b2090979dec8e8858b8b1f867c0 # via -r requirements/default.in +exceptiongroup==1.2.0 \ + --hash=sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14 \ + --hash=sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68 + # via anyio fluent-syntax==0.19.0 \ --hash=sha256:920326d7f46864b9758f0044e9968e3112198bc826acee16ddd8f11d359004fd \ --hash=sha256:b352b3475fac6c6ed5f06527921f432aac073d764445508ee5218aeccc7cc5c4 @@ -323,10 +344,25 @@ gunicorn==19.9.0 \ --hash=sha256:aa8e0b40b4157b36a5df5e599f45c9c76d6af43845ba3b3b0efe2c70473c2471 \ --hash=sha256:fa2662097c66f920f53f70621c6c58ca4a3c4d3434205e608e121b5b3b71f4f3 # via -r requirements/default.in +h11==0.14.0 \ + --hash=sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d \ + --hash=sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761 + # via httpcore +httpcore==1.0.4 \ + --hash=sha256:ac418c1db41bade2ad53ae2f3834a3a0f5ae76b56cf5aa497d2d033384fc7d73 \ + --hash=sha256:cb2839ccfcba0d2d3c1131d3c3e26dfc327326fbe7a5dc0dbfe9f6c9151bb022 + # via httpx +httpx==0.27.0 \ + --hash=sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5 \ + --hash=sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5 + # via openai idna==3.3 \ --hash=sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff \ --hash=sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d - # via requests + # via + # anyio + # httpx + # requests jinja2==3.0.3 \ --hash=sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8 \ --hash=sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7 @@ -569,6 +605,10 @@ oauthlib==3.1.1 \ --hash=sha256:42bf6354c2ed8c6acb54d971fce6f88193d97297e18602a3a886603f9d7730cc \ --hash=sha256:8f0215fcc533dd8dd1bee6f4c412d4f0cd7297307d43ac61666389e3bc3198a3 # via requests-oauthlib +openai==1.12.0 \ + --hash=sha256:99c5d257d09ea6533d689d1cc77caa0ac679fa21efef8893d8b0832a86877f1b \ + --hash=sha256:a54002c814e05222e413664f651b5916714e4700d041d5cf5724d3ae1a3e3481 + # via -r requirements/default.in packaging==21.3 \ --hash=sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb \ --hash=sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522 @@ -649,6 +689,91 @@ pycparser==2.21 \ --hash=sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9 \ --hash=sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206 # via cffi +pydantic==2.6.1 \ + --hash=sha256:0b6a909df3192245cb736509a92ff69e4fef76116feffec68e93a567347bae6f \ + --hash=sha256:4fd5c182a2488dc63e6d32737ff19937888001e2a6d86e94b3f233104a5d1fa9 + # via openai +pydantic-core==2.16.2 \ + --hash=sha256:02906e7306cb8c5901a1feb61f9ab5e5c690dbbeaa04d84c1b9ae2a01ebe9379 \ + --hash=sha256:0ba503850d8b8dcc18391f10de896ae51d37fe5fe43dbfb6a35c5c5cad271a06 \ + --hash=sha256:16aa02e7a0f539098e215fc193c8926c897175d64c7926d00a36188917717a05 \ + --hash=sha256:18de31781cdc7e7b28678df7c2d7882f9692ad060bc6ee3c94eb15a5d733f8f7 \ + --hash=sha256:22c5f022799f3cd6741e24f0443ead92ef42be93ffda0d29b2597208c94c3753 \ + --hash=sha256:2924b89b16420712e9bb8192396026a8fbd6d8726224f918353ac19c4c043d2a \ + --hash=sha256:308974fdf98046db28440eb3377abba274808bf66262e042c412eb2adf852731 \ + --hash=sha256:396fdf88b1b503c9c59c84a08b6833ec0c3b5ad1a83230252a9e17b7dfb4cffc \ + --hash=sha256:3ac426704840877a285d03a445e162eb258924f014e2f074e209d9b4ff7bf380 \ + --hash=sha256:3b052c753c4babf2d1edc034c97851f867c87d6f3ea63a12e2700f159f5c41c3 \ + --hash=sha256:3fab4e75b8c525a4776e7630b9ee48aea50107fea6ca9f593c98da3f4d11bf7c \ + --hash=sha256:406fac1d09edc613020ce9cf3f2ccf1a1b2f57ab00552b4c18e3d5276c67eb11 \ + --hash=sha256:40a0bd0bed96dae5712dab2aba7d334a6c67cbcac2ddfca7dbcc4a8176445990 \ + --hash=sha256:41dac3b9fce187a25c6253ec79a3f9e2a7e761eb08690e90415069ea4a68ff7a \ + --hash=sha256:459c0d338cc55d099798618f714b21b7ece17eb1a87879f2da20a3ff4c7628e2 \ + --hash=sha256:459d6be6134ce3b38e0ef76f8a672924460c455d45f1ad8fdade36796df1ddc8 \ + --hash=sha256:46b0d5520dbcafea9a8645a8164658777686c5c524d381d983317d29687cce97 \ + --hash=sha256:47924039e785a04d4a4fa49455e51b4eb3422d6eaacfde9fc9abf8fdef164e8a \ + --hash=sha256:4bfcbde6e06c56b30668a0c872d75a7ef3025dc3c1823a13cf29a0e9b33f67e8 \ + --hash=sha256:4f9ee4febb249c591d07b2d4dd36ebcad0ccd128962aaa1801508320896575ef \ + --hash=sha256:55749f745ebf154c0d63d46c8c58594d8894b161928aa41adbb0709c1fe78b77 \ + --hash=sha256:5864b0242f74b9dd0b78fd39db1768bc3f00d1ffc14e596fd3e3f2ce43436a33 \ + --hash=sha256:5f60f920691a620b03082692c378661947d09415743e437a7478c309eb0e4f82 \ + --hash=sha256:60eb8ceaa40a41540b9acae6ae7c1f0a67d233c40dc4359c256ad2ad85bdf5e5 \ + --hash=sha256:69a7b96b59322a81c2203be537957313b07dd333105b73db0b69212c7d867b4b \ + --hash=sha256:6ad84731a26bcfb299f9eab56c7932d46f9cad51c52768cace09e92a19e4cf55 \ + --hash=sha256:6db58c22ac6c81aeac33912fb1af0e930bc9774166cdd56eade913d5f2fff35e \ + --hash=sha256:70651ff6e663428cea902dac297066d5c6e5423fda345a4ca62430575364d62b \ + --hash=sha256:72f7919af5de5ecfaf1eba47bf9a5d8aa089a3340277276e5636d16ee97614d7 \ + --hash=sha256:732bd062c9e5d9582a30e8751461c1917dd1ccbdd6cafb032f02c86b20d2e7ec \ + --hash=sha256:7924e54f7ce5d253d6160090ddc6df25ed2feea25bfb3339b424a9dd591688bc \ + --hash=sha256:7afb844041e707ac9ad9acad2188a90bffce2c770e6dc2318be0c9916aef1469 \ + --hash=sha256:7b883af50eaa6bb3299780651e5be921e88050ccf00e3e583b1e92020333304b \ + --hash=sha256:7beec26729d496a12fd23cf8da9944ee338c8b8a17035a560b585c36fe81af20 \ + --hash=sha256:7bf26c2e2ea59d32807081ad51968133af3025c4ba5753e6a794683d2c91bf6e \ + --hash=sha256:7c31669e0c8cc68400ef0c730c3a1e11317ba76b892deeefaf52dcb41d56ed5d \ + --hash=sha256:7e6231aa5bdacda78e96ad7b07d0c312f34ba35d717115f4b4bff6cb87224f0f \ + --hash=sha256:870dbfa94de9b8866b37b867a2cb37a60c401d9deb4a9ea392abf11a1f98037b \ + --hash=sha256:88646cae28eb1dd5cd1e09605680c2b043b64d7481cdad7f5003ebef401a3039 \ + --hash=sha256:8aafeedb6597a163a9c9727d8a8bd363a93277701b7bfd2749fbefee2396469e \ + --hash=sha256:8bde5b48c65b8e807409e6f20baee5d2cd880e0fad00b1a811ebc43e39a00ab2 \ + --hash=sha256:8f9142a6ed83d90c94a3efd7af8873bf7cefed2d3d44387bf848888482e2d25f \ + --hash=sha256:936a787f83db1f2115ee829dd615c4f684ee48ac4de5779ab4300994d8af325b \ + --hash=sha256:98dc6f4f2095fc7ad277782a7c2c88296badcad92316b5a6e530930b1d475ebc \ + --hash=sha256:9957433c3a1b67bdd4c63717eaf174ebb749510d5ea612cd4e83f2d9142f3fc8 \ + --hash=sha256:99af961d72ac731aae2a1b55ccbdae0733d816f8bfb97b41909e143de735f522 \ + --hash=sha256:9b5f13857da99325dcabe1cc4e9e6a3d7b2e2c726248ba5dd4be3e8e4a0b6d0e \ + --hash=sha256:9d776d30cde7e541b8180103c3f294ef7c1862fd45d81738d156d00551005784 \ + --hash=sha256:9da90d393a8227d717c19f5397688a38635afec89f2e2d7af0df037f3249c39a \ + --hash=sha256:a3b7352b48fbc8b446b75f3069124e87f599d25afb8baa96a550256c031bb890 \ + --hash=sha256:a477932664d9611d7a0816cc3c0eb1f8856f8a42435488280dfbf4395e141485 \ + --hash=sha256:a7e41e3ada4cca5f22b478c08e973c930e5e6c7ba3588fb8e35f2398cdcc1545 \ + --hash=sha256:a90fec23b4b05a09ad988e7a4f4e081711a90eb2a55b9c984d8b74597599180f \ + --hash=sha256:a9e523474998fb33f7c1a4d55f5504c908d57add624599e095c20fa575b8d943 \ + --hash=sha256:aa057095f621dad24a1e906747179a69780ef45cc8f69e97463692adbcdae878 \ + --hash=sha256:aa6c8c582036275997a733427b88031a32ffa5dfc3124dc25a730658c47a572f \ + --hash=sha256:ae34418b6b389d601b31153b84dce480351a352e0bb763684a1b993d6be30f17 \ + --hash=sha256:b0d7a9165167269758145756db43a133608a531b1e5bb6a626b9ee24bc38a8f7 \ + --hash=sha256:b30b0dd58a4509c3bd7eefddf6338565c4905406aee0c6e4a5293841411a1286 \ + --hash=sha256:b8f9186ca45aee030dc8234118b9c0784ad91a0bb27fc4e7d9d6608a5e3d386c \ + --hash=sha256:b94cbda27267423411c928208e89adddf2ea5dd5f74b9528513f0358bba019cb \ + --hash=sha256:cc6f6c9be0ab6da37bc77c2dda5f14b1d532d5dbef00311ee6e13357a418e646 \ + --hash=sha256:ce232a6170dd6532096cadbf6185271e4e8c70fc9217ebe105923ac105da9978 \ + --hash=sha256:cf903310a34e14651c9de056fcc12ce090560864d5a2bb0174b971685684e1d8 \ + --hash=sha256:d5362d099c244a2d2f9659fb3c9db7c735f0004765bbe06b99be69fbd87c3f15 \ + --hash=sha256:dffaf740fe2e147fedcb6b561353a16243e654f7fe8e701b1b9db148242e1272 \ + --hash=sha256:e0f686549e32ccdb02ae6f25eee40cc33900910085de6aa3790effd391ae10c2 \ + --hash=sha256:e4b52776a2e3230f4854907a1e0946eec04d41b1fc64069ee774876bbe0eab55 \ + --hash=sha256:e4ba0884a91f1aecce75202473ab138724aa4fb26d7707f2e1fa6c3e68c84fbf \ + --hash=sha256:e6294e76b0380bb7a61eb8a39273c40b20beb35e8c87ee101062834ced19c545 \ + --hash=sha256:ebb892ed8599b23fa8f1799e13a12c87a97a6c9d0f497525ce9858564c4575a4 \ + --hash=sha256:eca58e319f4fd6df004762419612122b2c7e7d95ffafc37e890252f869f3fb2a \ + --hash=sha256:ed957db4c33bc99895f3a1672eca7e80e8cda8bd1e29a80536b4ec2153fa9804 \ + --hash=sha256:ef551c053692b1e39e3f7950ce2296536728871110e7d75c4e7753fb30ca87f4 \ + --hash=sha256:ef6113cd31411eaf9b39fc5a8848e71c72656fd418882488598758b2c8c6dfa0 \ + --hash=sha256:f685dbc1fdadb1dcd5b5e51e0a378d4685a891b2ddaf8e2bba89bd3a7144e44a \ + --hash=sha256:f8ed79883b4328b7f0bd142733d99c8e6b22703e908ec63d930b06be3a0e7113 \ + --hash=sha256:fe56851c3f1d6f5384b3051c536cc81b3a93a73faf931f404fef95217cf1e10d \ + --hash=sha256:ff7c97eb7a29aba230389a2661edf2e9e06ce616c7e35aa764879b6894a44b25 + # via pydantic pyjwt[crypto]==2.4.0 \ --hash=sha256:72d1d253f32dbd4f5c88eaf1fdc62f3a19f676ccbadb9dbc5d07e951b2b26daf \ --hash=sha256:d42908208c699b3b973cbeb01a969ba6a96c821eefb1c5bfe4c390c01d67abba @@ -841,6 +966,13 @@ six==1.16.0 \ # python-dateutil # sacremoses # singledispatch +sniffio==1.3.0 \ + --hash=sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101 \ + --hash=sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384 + # via + # anyio + # httpx + # openai sqlparse==0.4.2 \ --hash=sha256:0c00730c74263a94e5a9919ade150dfc3b19c574389985446148402998287dae \ --hash=sha256:48719e356bb8b42991bdbb1e8b83223757b93789c00910a616a071910ca4a64d @@ -860,14 +992,21 @@ toml==0.10.2 \ tqdm==4.62.3 \ --hash=sha256:8dd278a422499cd6b727e6ae4061c40b48fce8b76d1ccbf5d34fca9b7f925b0c \ --hash=sha256:d359de7217506c9851b7869f3708d8ee53ed70a1b8edbba4dbcb47442592920d - # via sacremoses + # via + # openai + # sacremoses translate-toolkit==3.3.2 \ --hash=sha256:0795bd3c8668213199550ae4ed8938874083139ec1f8c473dcca1524a206b108 # via -r requirements/default.in -typing-extensions==4.5.0 \ - --hash=sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb \ - --hash=sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4 - # via fluent-syntax +typing-extensions==4.9.0 \ + --hash=sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783 \ + --hash=sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd + # via + # anyio + # fluent-syntax + # openai + # pydantic + # pydantic-core tzdata==2022.7 \ --hash=sha256:2b88858b0e3120792a3c0635c23daf36a7d7eeeca657c323da299d2094402a0d \ --hash=sha256:fe5f866eddd8b96e9fcba978f8e503c909b19ea7efda11e52e39494bad3a7bfa diff --git a/requirements/test.txt b/requirements/test.txt index 268fc70e3c..79b4f422c0 100644 --- a/requirements/test.txt +++ b/requirements/test.txt @@ -71,10 +71,12 @@ coverage[toml]==7.2.5 \ # via # -r requirements/test.in # pytest-cov -exceptiongroup==1.1.1 \ - --hash=sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e \ - --hash=sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785 - # via pytest +exceptiongroup==1.2.0 \ + --hash=sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14 \ + --hash=sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68 + # via + # -c requirements/default.txt + # pytest factory-boy==3.2.1 \ --hash=sha256:a98d277b0c047c75eb6e4ab8508a7f81fb03d2cb21986f627913546ef7a2a55e \ --hash=sha256:eb02a7dd1b577ef606b75a253b9818e6f9eaf996d94449c9d5ebb124f90dc795