From db9bbef70f752d69908c37672d51a90b531501f7 Mon Sep 17 00:00:00 2001 From: Nikita Yakovlev Date: Sat, 6 Jun 2026 00:19:38 +0300 Subject: [PATCH 01/10] improvement of system prompta --- .../backlog_app/servicies/ai_agent/client.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/backend/backlog_app/servicies/ai_agent/client.py b/backend/backlog_app/servicies/ai_agent/client.py index 51cc65a..83c0a49 100644 --- a/backend/backlog_app/servicies/ai_agent/client.py +++ b/backend/backlog_app/servicies/ai_agent/client.py @@ -13,15 +13,20 @@ async def translate(self, text: str) -> str: "model": self.model, "messages": [ { - "role": "user", + "role": "system", "content": ( - "Translate the following text from English into Russian, " - "preserve the meaning and naturalness of the language:\n\n" - f"{text}" + "You are a translation engine. " + "Return ONLY the translated text in Russian. " + "Do NOT add explanations, comments, notes, or formatting. " + "Output must contain only the translation." ), - } + }, + { + "role": "user", + "content": f"{text}", + }, ], - "temperature": 0.3, + "temperature": 0.0, } response = await self.client.post( From 4fc855607f9705ba9cf15c4615a275cec86d3165 Mon Sep 17 00:00:00 2001 From: Nikita Yakovlev Date: Sat, 6 Jun 2026 00:47:08 +0300 Subject: [PATCH 02/10] add openai package --- backend/pyproject.toml | 1 + backend/uv.lock | 105 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 03bcf57..6613d5a 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -11,6 +11,7 @@ dependencies = [ "fastapi-users[sqlalchemy]>=15.0.3", "fastapi[standard]>=0.128.0", "jinja2>=3.1.6", + "openai>=2.41.0", "pydantic-settings[yaml]>=2.12.0", "python-slugify>=8.0.4", "sqlalchemy[asyncio]>=2.0.46", diff --git a/backend/uv.lock b/backend/uv.lock index d08cd3c..b299e14 100644 --- a/backend/uv.lock +++ b/backend/uv.lock @@ -278,6 +278,7 @@ dependencies = [ { name = "fastapi", extra = ["standard"] }, { name = "fastapi-users", extra = ["sqlalchemy"] }, { name = "jinja2" }, + { name = "openai" }, { name = "pydantic-settings", extra = ["yaml"] }, { name = "python-slugify" }, { name = "sqlalchemy", extra = ["asyncio"] }, @@ -307,6 +308,7 @@ requires-dist = [ { name = "fastapi", extras = ["standard"], specifier = ">=0.128.0" }, { name = "fastapi-users", extras = ["sqlalchemy"], specifier = ">=15.0.3" }, { name = "jinja2", specifier = ">=3.1.6" }, + { name = "openai", specifier = ">=2.41.0" }, { name = "pydantic-settings", extras = ["yaml"], specifier = ">=2.12.0" }, { name = "python-slugify", specifier = ">=8.0.4" }, { name = "sqlalchemy", extras = ["asyncio"], specifier = ">=2.0.46" }, @@ -628,6 +630,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16", size = 469047, upload-time = "2025-07-17T16:51:58.613Z" }, ] +[[package]] +name = "distro" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/f8/98eea607f65de6527f8a2e8885fc8015d3e6f5775df186e443e0964a11c3/distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed", size = 60722, upload-time = "2023-12-24T09:54:32.31Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2", size = 20277, upload-time = "2023-12-24T09:54:30.421Z" }, +] + [[package]] name = "dnspython" version = "2.8.0" @@ -1036,6 +1047,60 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" }, ] +[[package]] +name = "jiter" +version = "0.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/66/b5/55f06bb281d92fb3cc86d14e1def2bd908bb77693183e7cb1f5a3c388b0c/jiter-0.15.0.tar.gz", hash = "sha256:4251acc80e2b7c9b7b8823456ea0fceeb0734dac2df7636d3c711b38476b5a76", size = 166640, upload-time = "2026-05-19T10:09:48.361Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/f4/f708c900ecee41b2025ef8413d5351e5649eb2125c506f6720cc69b06f5c/jiter-0.15.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1c11465f97e2abf45a014b83b730222f8f1c5335e802c7055a67d50de6f1f4e3", size = 307829, upload-time = "2026-05-19T10:07:59.704Z" }, + { url = "https://files.pythonhosted.org/packages/86/59/db537c0949e83668c38481d426b9f2fd5ab758c4ee53a811dd0a510626a0/jiter-0.15.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d1e7b1776f0797956c509e123d0952d10d293a9492dea9f288ab9570ec01d1a5", size = 308445, upload-time = "2026-05-19T10:08:01.184Z" }, + { url = "https://files.pythonhosted.org/packages/37/38/ea0e13b18c30ef951da0d47d39e7fa9edb82a93a62990ffbd7cea9b622d4/jiter-0.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:351a341c2105aa430b7047e30f1bf7975f6313b00165d3fc07be2edaf741f279", size = 336181, upload-time = "2026-05-19T10:08:02.688Z" }, + { url = "https://files.pythonhosted.org/packages/58/fc/2303901b16c4ba05865588990a420c0b4156270b44379c20931544a1d962/jiter-0.15.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4ab395feec8d249ec4044e228e98a7033f043426a265df439dc3698823f0a4e4", size = 362985, upload-time = "2026-05-19T10:08:04.394Z" }, + { url = "https://files.pythonhosted.org/packages/5b/6f/11bace093c52e7d4d26c8e606ccd7ae8c972189622469ec0d9e28161e28b/jiter-0.15.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2a438005b6f22d0273413484d6094d7c2c5d10ec1b3a3bf128e0d1d3ba53258", size = 453292, upload-time = "2026-05-19T10:08:05.967Z" }, + { url = "https://files.pythonhosted.org/packages/22/db/987f2f086ca4d7a6582eb4ccd513f9b26b42d9e4243a087609a3137a8fc7/jiter-0.15.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f18f85e4218d1b40f000f42a92239a7a61a902cd42c65e6c360dbd17dcb20894", size = 373501, upload-time = "2026-05-19T10:08:07.857Z" }, + { url = "https://files.pythonhosted.org/packages/8f/7c/89fbcabb2739b7a5b8dc959a1b6c5761f6484f5fed3486854b3c789bb1de/jiter-0.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1aa62e277fc1cbd80e6deacae6f4d983b41b3d7728e0645c5d741a6149bba45", size = 344683, upload-time = "2026-05-19T10:08:09.431Z" }, + { url = "https://files.pythonhosted.org/packages/30/6f/6cca7692e7dddfec6d8d76c54dc97f2af2a41df4ac0674b999df1f09a5f3/jiter-0.15.0-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:6550fa135c7deb8ead6af49ed7ff648532ea8334a1447fe34a36315ef79c5c29", size = 350892, upload-time = "2026-05-19T10:08:11.352Z" }, + { url = "https://files.pythonhosted.org/packages/39/14/0338d6190cb8e6d22e677ab1d4eabd4117f67cca70c54cd04b82ff64e068/jiter-0.15.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:066f8f33f18b2419cd8213b2436fa7fbc9c499f315971cfa3ce1f9820c001b1b", size = 388723, upload-time = "2026-05-19T10:08:12.912Z" }, + { url = "https://files.pythonhosted.org/packages/90/31/cc19f4a1bdb6afb09ce6a2f2615aa8d44d994eba0d8e6105ed1af920e736/jiter-0.15.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:75e8a04e91432dde9f1838373cf93d23726c79d3e908d319acf0e796f85592e7", size = 516648, upload-time = "2026-05-19T10:08:14.808Z" }, + { url = "https://files.pythonhosted.org/packages/49/9f/833c541512cd091b63c10c0381973dfe11bc7a503a818c16384417e0c81e/jiter-0.15.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:a97261f1fccb8e50ecd2890a96e46efdc3f57c80a197324c6777827231eca712", size = 547382, upload-time = "2026-05-19T10:08:16.927Z" }, + { url = "https://files.pythonhosted.org/packages/d2/11/e7b70e91f90bc4477e8eee9e8a5f7cf3cb41b4525d6394dc98a714eb8f7f/jiter-0.15.0-cp313-cp313-win32.whl", hash = "sha256:c77496cb10bd7549690fbbab3e5ec05857b83e49276f4a9423a766ddd2afcd4c", size = 205845, upload-time = "2026-05-19T10:08:18.401Z" }, + { url = "https://files.pythonhosted.org/packages/4b/23/5c20d9ad6f02c493e4023e5d2d09e1c1f15fe2753c9102c544aff068a88e/jiter-0.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:b15741f501469009ae0ae90b7147958a664a7dede40aa7ff174a8a4645f546d0", size = 196842, upload-time = "2026-05-19T10:08:20.131Z" }, + { url = "https://files.pythonhosted.org/packages/6b/11/1eb400ef248e8c925fd883fbe325daf5e42cd1b0d308539dd332bd4f7ffc/jiter-0.15.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d6a60072b44c3c2b797a7ddcbcbbf2b34ea3cfd4721580fbfd2a09d9d9b84ba", size = 192212, upload-time = "2026-05-19T10:08:21.807Z" }, + { url = "https://files.pythonhosted.org/packages/8a/60/2fd8d7c79da8acf9b7b277c7616847773779356b92acfc9bb158452174da/jiter-0.15.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:ef1fd24d9413f6209e00d3d5a453e67acfe004a25cc6c8e8484faed4311ab9e8", size = 315065, upload-time = "2026-05-19T10:08:23.218Z" }, + { url = "https://files.pythonhosted.org/packages/46/f4/008fb7d65e8ac2abf00811651a661e025c4ba80bbc6f378450384ddd3aed/jiter-0.15.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:144f8e72cb53dab146347b91cceac01f5481237f2b93b4a339a1ee8f8878b67c", size = 339444, upload-time = "2026-05-19T10:08:24.701Z" }, + { url = "https://files.pythonhosted.org/packages/00/55/90b0c7b9c6896c0f2a591dd36d36b71d22e09674bfef178fa03ba3f81499/jiter-0.15.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:553fcac2ef2cb990877f9fc0833b8b629a3e6a5670b6b5fd58219b41a653ddc4", size = 347779, upload-time = "2026-05-19T10:08:26.408Z" }, + { url = "https://files.pythonhosted.org/packages/51/6b/69666cec5000fd57734c118437394516c749ae8dbeea9fb66d6fef9c4775/jiter-0.15.0-cp313-cp313t-win_amd64.whl", hash = "sha256:774f93f65031856bf14ad9f59bdcab8b8cad501e5ceabd51ba3525f76937a25b", size = 200395, upload-time = "2026-05-19T10:08:28.055Z" }, + { url = "https://files.pythonhosted.org/packages/39/04/a6aa62cd27e8149b0d28df5561f10f6cceaf7935a9ccf3f1c5a05f9a0cd8/jiter-0.15.0-cp313-cp313t-win_arm64.whl", hash = "sha256:f1e1754960f38ec40613a07e5e372df67acb3b890fb383b6fb3de3e49ddbf3c7", size = 190516, upload-time = "2026-05-19T10:08:29.35Z" }, + { url = "https://files.pythonhosted.org/packages/eb/d2/079f350ebf7859d081de30aa890f9e3be68516f754f3ba32366ffff4dcee/jiter-0.15.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:ac0d9ddea4350974be7a221fc25895f251a8fee748c889bdced2141c0fec1a49", size = 308884, upload-time = "2026-05-19T10:08:31.667Z" }, + { url = "https://files.pythonhosted.org/packages/04/4e/a2c30a7f69b48c03b20935d647479106fe932f6e63f75faf53937197e05d/jiter-0.15.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:01a8222cf05ab1128e239421156c207949808acaaea2bdfd33130ae666786e86", size = 310028, upload-time = "2026-05-19T10:08:33.304Z" }, + { url = "https://files.pythonhosted.org/packages/40/90/2e7cdfd3cf8ca967be38c48f5cf474d79f089efaf559a40f15984a77ae69/jiter-0.15.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:182226cbc930c9fab81bc2e41a4da672f89539906dadb05e75670ac07b94f71f", size = 337485, upload-time = "2026-05-19T10:08:35.259Z" }, + { url = "https://files.pythonhosted.org/packages/9b/11/15a1aa28b120b8ee5b4f1fb894c125046225f09847738bd64233d3b84883/jiter-0.15.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:71683c38c825452999b5717fcae07ea708e8c93003e808be4319c1b02e3d176e", size = 364223, upload-time = "2026-05-19T10:08:36.694Z" }, + { url = "https://files.pythonhosted.org/packages/b7/25/f442e8af5f3d0dcf47b39e83a0efd9ee45ea946aa6d04625dc3181eae3b6/jiter-0.15.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30f2218e6a9e5c18bc10fe6d41ac189c442c88eacf11bad9f28ef95a9bef00e6", size = 456387, upload-time = "2026-05-19T10:08:38.143Z" }, + { url = "https://files.pythonhosted.org/packages/da/f4/37f2d2c9f64f49af7da652ed7532bb5a2372e588e6927c3fdd76f911db65/jiter-0.15.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5157de9f76eb4bc5ea74a1219366a25f945ad305641d74e04f59c54087091aa9", size = 374461, upload-time = "2026-05-19T10:08:39.869Z" }, + { url = "https://files.pythonhosted.org/packages/60/28/edcfbbbf0cb15436f36664a8908a0df47ab9006298d4cd937dc08ea932d6/jiter-0.15.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90c5db5527c221249a876160663ab891ace358c17f7b9c93ec1478b7f0550e5c", size = 345924, upload-time = "2026-05-19T10:08:41.668Z" }, + { url = "https://files.pythonhosted.org/packages/47/13/89fba6398dab7f202b7278c4b4aac122399d2c0183971c4a57a3b7088df5/jiter-0.15.0-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:3e4540b8e74e4268811ac05db226a6a128ff572e7e0ce3f1163b693cadb184cd", size = 352283, upload-time = "2026-05-19T10:08:43.091Z" }, + { url = "https://files.pythonhosted.org/packages/1b/da/0f6af8cef2c565a1ab44d970f268c43ccaa72707386ea6388e6fe2b6cd26/jiter-0.15.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:62ebd14e47e9aed9df4472afcb2663668ce4d74891cd54f86bf6e44029d6dc89", size = 389985, upload-time = "2026-05-19T10:08:44.915Z" }, + { url = "https://files.pythonhosted.org/packages/a1/ec/b9cb7d6d29e24ee14910266157d2a279d7a8f60ee0df7fa840882976ba64/jiter-0.15.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0be6f5ad41a809f303f416d17cec92a7a725902fb9b4f3de3d19362ac0ef8554", size = 517695, upload-time = "2026-05-19T10:08:46.486Z" }, + { url = "https://files.pythonhosted.org/packages/64/5e/6d1bda880723aae0ad86b4b763f044362448efe31e3e819635d41cb03451/jiter-0.15.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:813dfbb17d65328bf86e5f0905dd277ba2265d3ca20556e86c0c7035b7182e5a", size = 548868, upload-time = "2026-05-19T10:08:48.026Z" }, + { url = "https://files.pythonhosted.org/packages/0c/72/7de501cf38dcacaf35098796f3a50e0f2e338baba18a58946c618544b809/jiter-0.15.0-cp314-cp314-win32.whl", hash = "sha256:50e51156192722a9c58db112837d3f8ef96fb3c5ecc14e95f409134b08b158ec", size = 206380, upload-time = "2026-05-19T10:08:49.738Z" }, + { url = "https://files.pythonhosted.org/packages/1e/a9/e19addf4b0c1bdce52c6da12351e6bc42c340c45e7c09e2158e46d293ccc/jiter-0.15.0-cp314-cp314-win_amd64.whl", hash = "sha256:30ce1a5d16b5641dc935d50ef775af6a0871e3d14ab05d6fc54dff371b78e558", size = 197687, upload-time = "2026-05-19T10:08:51.088Z" }, + { url = "https://files.pythonhosted.org/packages/f2/c9/776b1db01db25fc6c1d58d1979a37b0a9fe787e5f5b1d062d2eaacb77923/jiter-0.15.0-cp314-cp314-win_arm64.whl", hash = "sha256:510c8b3c17a0ed9ac69850c0438dada3c9b82d9c4d589fcb62002a5a9cf3a866", size = 192571, upload-time = "2026-05-19T10:08:52.451Z" }, + { url = "https://files.pythonhosted.org/packages/a0/f6/45bb4670bacf300fd2c7abadbfb3af376e5f1b6ae75fd9bc069891d15870/jiter-0.15.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7553333dd0930c104a5a0db8df72bf7219fe663d731383b576bb6ed6351c984d", size = 317151, upload-time = "2026-05-19T10:08:53.867Z" }, + { url = "https://files.pythonhosted.org/packages/d7/68/ed635ad5acd7b73e454283083bbb7c8205ad10e88b0d9d7d793b09fe8226/jiter-0.15.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2143ab06181d2b029eedcb6af3cebe95f11bbac62441781860f98ee9330a6a6", size = 341243, upload-time = "2026-05-19T10:08:55.383Z" }, + { url = "https://files.pythonhosted.org/packages/5d/db/3ff4176b817b8ea33879e71e13d8bc2b0d481a7ed3fe9e080f333d415c16/jiter-0.15.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6eac374c5c975709b69c10f09afd199df74150172156ad10c8d4fd785b7da995", size = 363629, upload-time = "2026-05-19T10:08:56.928Z" }, + { url = "https://files.pythonhosted.org/packages/ab/24/5f8270e0ba9c883582f96f722f8a0b58015c7ce1f8c6d4571cf394e99b6b/jiter-0.15.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b3b3b775e33d3bfaec9899edc526ae97b0da0bf9d071a46124ba419149a414f8", size = 456198, upload-time = "2026-05-19T10:08:58.618Z" }, + { url = "https://files.pythonhosted.org/packages/45/5b/76fc02b0b5c54c3d18c60653156e2f76fde1816f9b4722db68d6ee2c897e/jiter-0.15.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3071db3346334beae1360b46da4606da57bf3528c167b3c38533afaf9f2c5", size = 373710, upload-time = "2026-05-19T10:09:00.151Z" }, + { url = "https://files.pythonhosted.org/packages/c4/52/4310821b0ea9277994d3e1f49fc6a4b34e4800caebacb2c0af81da59a454/jiter-0.15.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c6694a173ecabc12eb60efbc0b474464ead1951ff65cd8b1e72100715c64512b", size = 349901, upload-time = "2026-05-19T10:09:01.621Z" }, + { url = "https://files.pythonhosted.org/packages/93/fe/67648c35b3594fba8854ac64cc8a826d8bcd18324bbdb53d77697c60b6ef/jiter-0.15.0-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:a254e10b593624d230c365b6d616b22ca0ad65e63a16e6631c2b3466022e6ba8", size = 352438, upload-time = "2026-05-19T10:09:03.216Z" }, + { url = "https://files.pythonhosted.org/packages/cb/28/0a1879d07ad6b3e025a2750027363452ced93c2d16d1c9d4b153ffd51c91/jiter-0.15.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d8d2955167274e15d79a7a020afdd9b39c990eb80b2d89fca695d92dcfdd38ec", size = 388152, upload-time = "2026-05-19T10:09:04.741Z" }, + { url = "https://files.pythonhosted.org/packages/c1/78/46c6f6b56ba85c90021f4afd72ed42f691f8f84daacb5fe27277070e3858/jiter-0.15.0-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:acf4ee4d1fc55917239fe72972fb292dd773055d05eb040d36f4326e02cc2c0e", size = 517707, upload-time = "2026-05-19T10:09:06.231Z" }, + { url = "https://files.pythonhosted.org/packages/ca/cb/720662d4c88fcad606e826fef5424365527ba43ce4868a479aed8f8c507e/jiter-0.15.0-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:e7196e56f1cd69af1dbb07dff02dcfb260a50b45a82d409d92a06fedb32473b5", size = 548241, upload-time = "2026-05-19T10:09:08.093Z" }, + { url = "https://files.pythonhosted.org/packages/60/e3/935b8034fd143f21125c87d51404a9e0e1449186a494405721ff5d1d695e/jiter-0.15.0-cp314-cp314t-win32.whl", hash = "sha256:7f6163c0f10b055245f814dcc59f4818da60dfe72f3e72ab89fc24b6bd5e9c52", size = 207950, upload-time = "2026-05-19T10:09:09.616Z" }, + { url = "https://files.pythonhosted.org/packages/93/59/984fd9ece895953dad3e0880a650e766f5a2da2c5514f0eafdaaabbeb5f9/jiter-0.15.0-cp314-cp314t-win_amd64.whl", hash = "sha256:980c256edb05b78a111b99c4de3b1d32e31634b867fd1fc2cf726e7b7bba9854", size = 200055, upload-time = "2026-05-19T10:09:11.367Z" }, + { url = "https://files.pythonhosted.org/packages/0e/a4/cf8d779feb133a27a2e3bc833bccb9e13aa332cdf820497ebf72c10ce8c3/jiter-0.15.0-cp314-cp314t-win_arm64.whl", hash = "sha256:66b1880df2d01e206e8339769d1c7c1753bcb653efd6289e203f6f24ebada0c0", size = 191244, upload-time = "2026-05-19T10:09:12.74Z" }, +] + [[package]] name = "makefun" version = "1.16.0" @@ -1229,6 +1294,25 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, ] +[[package]] +name = "openai" +version = "2.41.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "httpx" }, + { name = "jiter" }, + { name = "pydantic" }, + { name = "sniffio" }, + { name = "tqdm" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3c/a6/5815fe2e2aca74b36c650d1bd43b69827cee568073d0d2d9b6fc5aaac80c/openai-2.41.0.tar.gz", hash = "sha256:db5c362acd6604b84f076abbefa66826ea4b46ecba2954ed866e6a149a1352c0", size = 783525, upload-time = "2026-06-03T22:39:40.719Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/be/51/d82bb424e8aa372190c5233253a2ceb399a778747d18b42cff487411e663/openai-2.41.0-py3-none-any.whl", hash = "sha256:20cc7952e8501c7e5773dd2ef7be437bae9cb549044902e1041a83a54516e375", size = 1353378, upload-time = "2026-06-03T22:39:38.964Z" }, +] + [[package]] name = "packaging" version = "26.0" @@ -1756,6 +1840,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" }, ] +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372, upload-time = "2024-02-25T23:20:04.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235, upload-time = "2024-02-25T23:20:01.196Z" }, +] + [[package]] name = "sqlalchemy" version = "2.0.46" @@ -1870,6 +1963,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a6/a5/c0b6468d3824fe3fde30dbb5e1f687b291608f9473681bbf7dabbf5a87d7/text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8", size = 78154, upload-time = "2019-08-30T21:37:03.543Z" }, ] +[[package]] +name = "tqdm" +version = "4.68.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/b3/36c8ecf72e8925200671613332db156d84b99b3aee742a41c1938ebb0808/tqdm-4.68.1.tar.gz", hash = "sha256:fc163d96b287bd031e1aa24421ce4411b25559bd0a1be4fe649bdaa4d2c02bf5", size = 171236, upload-time = "2026-06-05T17:23:15.267Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/aa/218a0eb34de1f753c83e4d0d1c8e7c4cef27f20dcb8342e024f63a80dc86/tqdm-4.68.1-py3-none-any.whl", hash = "sha256:fea4a90e4023f764914569f7802a297277c5ab1a66be5144143e142e1a4031d8", size = 78354, upload-time = "2026-06-05T17:23:13.654Z" }, +] + [[package]] name = "typer" version = "0.21.1" From 8f262c6c56ed61b2afcb4f5dfc9d5f166e19582e Mon Sep 17 00:00:00 2001 From: Nikita Yakovlev Date: Sat, 6 Jun 2026 00:51:49 +0300 Subject: [PATCH 03/10] actualize .env template --- backend/.env.template | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/.env.template b/backend/.env.template index 7abf947..6761568 100644 --- a/backend/.env.template +++ b/backend/.env.template @@ -24,3 +24,6 @@ BACKLOG__AI_AGENT__TOKEN= BACKLOG__SUPERUSER__EMAIL=admin@site.com BACKLOG__SUPERUSER__PASSWORD=admin + +BACKLOG__AI_AGENT__BASE_URL= +BACKLOG__AI_AGENT__TOKEN= From d8d12bc3339dfe7611b695a0eb47f710c8c9e247 Mon Sep 17 00:00:00 2001 From: Nikita Yakovlev Date: Sat, 6 Jun 2026 00:52:13 +0300 Subject: [PATCH 04/10] change model --- backend/backlog_app/config.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/backlog_app/config.py b/backend/backlog_app/config.py index 36c668f..3cbdb39 100644 --- a/backend/backlog_app/config.py +++ b/backend/backlog_app/config.py @@ -77,9 +77,8 @@ def url(self) -> str: class AIAgentConfig(BaseModel): base_url: str - access_id: str token: str - model: str = "DeepSeek v3.2" + model: str = "deepseek-ai/DeepSeek-V3" timeout: int = 10 From 93ba47e965ef2cdd30747da27979d3de198be67a Mon Sep 17 00:00:00 2001 From: Nikita Yakovlev Date: Sat, 6 Jun 2026 00:52:33 +0300 Subject: [PATCH 05/10] use openai lib for using ai translator --- .../backlog_app/servicies/ai_agent/client.py | 29 ++++++++----------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/backend/backlog_app/servicies/ai_agent/client.py b/backend/backlog_app/servicies/ai_agent/client.py index 83c0a49..8239625 100644 --- a/backend/backlog_app/servicies/ai_agent/client.py +++ b/backend/backlog_app/servicies/ai_agent/client.py @@ -1,4 +1,4 @@ -import httpx +from openai import OpenAI from backlog_app.config import settings @@ -6,12 +6,18 @@ class AIClient: def __init__(self, model: str): self.model = model - self.client = httpx.AsyncClient(timeout=settings.ai_agent.timeout) + self.client = OpenAI( + base_url=settings.ai_agent.base_url, api_key=settings.ai_agent.token + ) async def translate(self, text: str) -> str: - payload = { - "model": self.model, - "messages": [ + response = self.client.chat.completions.create( + model=self.model, + max_tokens=2500, + temperature=0.5, + presence_penalty=0, + top_p=0.95, + messages=[ { "role": "system", "content": ( @@ -26,17 +32,6 @@ async def translate(self, text: str) -> str: "content": f"{text}", }, ], - "temperature": 0.0, - } - - response = await self.client.post( - f"{settings.ai_agent.base_url}/api/v1/cloud-ai/agents/{settings.ai_agent.access_id}/v1/chat/completions", - json=payload, - headers={ - "Authorization": f"Bearer {settings.ai_agent.token}", - }, ) - response.raise_for_status() - data = response.json() - return data["choices"][0]["message"]["content"] + return response.choices[0].message.content From bd7b3962ccdb41c1f2d731b6bae4ff37c4feebe9 Mon Sep 17 00:00:00 2001 From: Nikita Yakovlev Date: Sat, 6 Jun 2026 00:52:50 +0300 Subject: [PATCH 06/10] slug optimization --- backend/backlog_app/api/crud.py | 2 +- backend/backlog_app/tasks/movie_task.py | 26 +++++++++++++------------ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/backend/backlog_app/api/crud.py b/backend/backlog_app/api/crud.py index 6b759f3..6516296 100644 --- a/backend/backlog_app/api/crud.py +++ b/backend/backlog_app/api/crud.py @@ -24,7 +24,7 @@ async def create_movie( await db.commit() await db.refresh(movie) - logger.info("Movie <%s> has been created.", movie.id) + logger.info("Movie <%s> has been created.", movie.slug) return MovieRead.model_validate(movie) diff --git a/backend/backlog_app/tasks/movie_task.py b/backend/backlog_app/tasks/movie_task.py index 2303d95..6e8cb6e 100644 --- a/backend/backlog_app/tasks/movie_task.py +++ b/backend/backlog_app/tasks/movie_task.py @@ -17,11 +17,11 @@ async def update_movie_rating(movie: MovieRead, db: AsyncSession, user: User): - movie_db = await crud.get_movie_by_id(db, movie.id) + movie_db = await crud.get_movie_by_slug(db, movie.slug) year = getattr(movie, "year", None) if year is None: - logger.info("Movie <%s> has no year, skipping rating update", movie.id) + logger.info("Movie <%s> has no year, skipping rating update", movie.slug) return try: @@ -29,12 +29,12 @@ async def update_movie_rating(movie: MovieRead, db: AsyncSession, user: User): movie_db.title, movie_db.year ) except Exception as e: - logger.exception("Failed to fetch rating for movie <%s>: %s", movie.id, e) + logger.exception("Failed to fetch rating for movie <%s>: %s", movie.slug, e) return await partial_update_movie( db, - movie.id, + movie.slug, MovieUpdate( imdb_rating=imdb_rating, metacritic_score=metacritic_score, @@ -42,24 +42,24 @@ async def update_movie_rating(movie: MovieRead, db: AsyncSession, user: User): user, ) - logger.info("Movie <%s> ratings updated in background", movie.id) + logger.info("Movie <%s> ratings updated in background", movie.slug) async def update_movie_description( movie: MovieRead, db: AsyncSession, user: User ) -> None: - movie_db = await crud.get_movie_by_id(db, movie.id) + movie_db = await crud.get_movie_by_slug(db, movie.slug) description = getattr(movie_db, "description", None) year = getattr(movie_db, "year", None) if description and description.strip(): - logger.info("Movie <%s> already has description, skipping", movie.id) + logger.info("Movie <%s> already has description, skipping", movie.slug) return if not year: - logger.info("Movie <%s> has no year, skipping description update", movie.id) + logger.info("Movie <%s> has no year, skipping description update", movie.slug) return try: @@ -67,22 +67,24 @@ async def update_movie_description( movie_db.title, movie_db.year ) except Exception as e: - logger.exception("Failed to fetch description for movie <%s>: %s", movie.id, e) + logger.exception( + "Failed to fetch description for movie <%s>: %s", movie.slug, e + ) return try: ru_description = await translator.translate(en_description) except Exception as e: logger.exception( - "Failed to translate description for movie <%s>: %s", movie.id, e + "Failed to translate description for movie <%s>: %s", movie.slug, e ) return await partial_update_movie( db, - movie.id, + movie.slug, MovieUpdate(description=ru_description), user, ) - logger.info("Movie <%s> description updated in background", movie.id) + logger.info("Movie <%s> description updated in background", movie.slug) From 19f53ee48dc55e09dbdbddbfaa3cb9235b257bc4 Mon Sep 17 00:00:00 2001 From: Nikita Yakovlev Date: Sat, 6 Jun 2026 21:35:04 +0300 Subject: [PATCH 07/10] fix tests after changes --- .../test_ai_agent/test_client.py | 44 +++++++++++-------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/backend/tests/test_servicies/test_ai_agent/test_client.py b/backend/tests/test_servicies/test_ai_agent/test_client.py index f3e7126..f5b5486 100644 --- a/backend/tests/test_servicies/test_ai_agent/test_client.py +++ b/backend/tests/test_servicies/test_ai_agent/test_client.py @@ -9,32 +9,38 @@ async def test_translate_success(): client = AIClient(model="gpt-test") - mock_response = AsyncMock() - mock_response.raise_for_status = Mock() - mock_response.json = Mock( - return_value={"choices": [{"message": {"content": "Привет мир"}}]} - ) + mock_response = Mock() + mock_response.choices = [ + Mock( + message=Mock( + content="Привет мир", + ) + ) + ] - with patch.object(client.client, "post", return_value=mock_response) as mock_post: + with patch.object( + client.client.chat.completions, + "create", + return_value=mock_response, + ) as mock_create: result = await client.translate("Hello world") + assert result == "Привет мир" - called_payload = mock_post.call_args[1]["json"] - assert ( - "Translate the following text from English into Russian" - in called_payload["messages"][0]["content"] - ) - assert "Hello world" in called_payload["messages"][0]["content"] + kwargs = mock_create.call_args.kwargs + + assert kwargs["model"] == "gpt-test" + assert kwargs["messages"][1]["content"] == "Hello world" @pytest.mark.asyncio -async def test_translate_http_error(): +async def test_translate_error(): client = AIClient(model="gpt-test") - mock_response = AsyncMock() - mock_response.raise_for_status = Mock(side_effect=Exception("HTTP Error")) - mock_response.json = AsyncMock(return_value={}) - - with patch.object(client.client, "post", return_value=mock_response): - with pytest.raises(Exception, match="HTTP Error"): + with patch.object( + client.client.chat.completions, + "create", + side_effect=Exception("API Error"), + ): + with pytest.raises(Exception, match="API Error"): await client.translate("Hello world") From baf49b9484ce37fbf191d4d54012b41e1b414385 Mon Sep 17 00:00:00 2001 From: Nikita Yakovlev Date: Sat, 6 Jun 2026 21:50:57 +0300 Subject: [PATCH 08/10] disable signature verification to fix the step --- .github/workflows/python-check.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/python-check.yml b/.github/workflows/python-check.yml index c246101..6faf61b 100644 --- a/.github/workflows/python-check.yml +++ b/.github/workflows/python-check.yml @@ -102,3 +102,4 @@ jobs: files: coverage.xml codecov_yml_path: backend/coverage.yaml fail_ci_if_error: 'true' + disable_certlytics: true From 2787cbcf25f1e20a8975dece234d30528dc32ede Mon Sep 17 00:00:00 2001 From: Nikita Yakovlev Date: Sat, 6 Jun 2026 21:55:11 +0300 Subject: [PATCH 09/10] skip verification to fix the step --- .github/workflows/python-check.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/python-check.yml b/.github/workflows/python-check.yml index 6faf61b..eb064a8 100644 --- a/.github/workflows/python-check.yml +++ b/.github/workflows/python-check.yml @@ -103,3 +103,4 @@ jobs: codecov_yml_path: backend/coverage.yaml fail_ci_if_error: 'true' disable_certlytics: true + skip_verification: true From 7ebafc3f1836ccfc46972bb15228c7cdb0ef67e8 Mon Sep 17 00:00:00 2001 From: Nikita Yakovlev Date: Sat, 6 Jun 2026 21:59:45 +0300 Subject: [PATCH 10/10] downgrade version --- .github/workflows/python-check.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/python-check.yml b/.github/workflows/python-check.yml index eb064a8..70202f2 100644 --- a/.github/workflows/python-check.yml +++ b/.github/workflows/python-check.yml @@ -96,11 +96,9 @@ jobs: name: coverage-reports - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} files: coverage.xml codecov_yml_path: backend/coverage.yaml fail_ci_if_error: 'true' - disable_certlytics: true - skip_verification: true