diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000000..5ed62d10ba --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,8 @@ +repos: + - repo: local + hooks: + - id: gitleaks-pre-commit + name: gitleaks git (staged only) + entry: echo "aloha" + language: system + pass_filenames: false diff --git a/api/poetry.lock b/api/poetry.lock index 21324a6f11..8b61f9027a 100644 --- a/api/poetry.lock +++ b/api/poetry.lock @@ -7,7 +7,7 @@ description = "Async boto3 wrapper" optional = false python-versions = "<4.0,>=3.8" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "aioboto3-12.4.0-py3-none-any.whl", hash = "sha256:a8d5a60852482cc7a472f3544e5ad7d2f5a911054ffa066357140dc6690da94b"}, {file = "aioboto3-12.4.0.tar.gz", hash = "sha256:0fa03ac7a8c2c187358dd27cdf84da05e91bc1a3bd85519cad13521343a3d767"}, @@ -27,7 +27,7 @@ description = "Async client for aws services using botocore and aiohttp" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "aiobotocore-2.12.3-py3-none-any.whl", hash = "sha256:86737685f4625e8f05c4e7a608a07cc97607263279f66cf6b02b640c4eafd324"}, {file = "aiobotocore-2.12.3.tar.gz", hash = "sha256:e2a2929207bc5d62eb556106c2224c1fd106d5c65be2eb69f15cc8c34c44c236"}, @@ -51,7 +51,7 @@ description = "Happy Eyeballs for asyncio" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8"}, {file = "aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558"}, @@ -64,7 +64,7 @@ description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "aiohttp-3.12.15-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b6fc902bff74d9b1879ad55f5404153e2b33a82e72a95c89cec5eb6cc9e92fbc"}, {file = "aiohttp-3.12.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:098e92835b8119b54c693f2f88a1dec690e20798ca5f5fe5f0520245253ee0af"}, @@ -173,7 +173,7 @@ description = "Simple retry client for aiohttp" optional = false python-versions = ">=3.7" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "aiohttp_retry-2.9.1-py3-none-any.whl", hash = "sha256:66d2759d1921838256a05a3f80ad7e724936f083e35be5abb5e16eed6be6dc54"}, {file = "aiohttp_retry-2.9.1.tar.gz", hash = "sha256:8eb75e904ed4ee5c2ec242fefe85bf04240f685391c4879d8f541d6028ff01f1"}, @@ -189,7 +189,7 @@ description = "itertools and builtins for AsyncIO and mixed iterables" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "aioitertools-0.12.0-py3-none-any.whl", hash = "sha256:fc1f5fac3d737354de8831cbba3eb04f79dd649d8f3afb4c5b114925e662a796"}, {file = "aioitertools-0.12.0.tar.gz", hash = "sha256:c2a9055b4fbb7705f561b9d86053e8af5d10cc845d22c32008c43490b2d8dd6b"}, @@ -206,7 +206,7 @@ description = "aiosignal: a list of registered asynchronous callbacks" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e"}, {file = "aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7"}, @@ -223,7 +223,7 @@ description = "asyncio SMTP client" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "aiosmtplib-3.0.2-py3-none-any.whl", hash = "sha256:8783059603a34834c7c90ca51103c3aa129d5922003b5ce98dbaa6d4440f10fc"}, {file = "aiosmtplib-3.0.2.tar.gz", hash = "sha256:08fd840f9dbc23258025dca229e8a8f04d2ccf3ecb1319585615bfc7933f7f47"}, @@ -240,7 +240,7 @@ description = "A database migration tool for SQLAlchemy." optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "alembic-1.16.5-py3-none-any.whl", hash = "sha256:e845dfe090c5ffa7b92593ae6687c5cb1a101e91fa53868497dbd79847f9dbe3"}, {file = "alembic-1.16.5.tar.gz", hash = "sha256:a88bb7f6e513bd4301ecf4c7f2206fe93f9913f9b48dac3b78babde2d6fe765e"}, @@ -261,7 +261,7 @@ description = "Low-level AMQP client for Python (fork of amqplib)." optional = false python-versions = ">=3.6" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "amqp-5.3.1-py3-none-any.whl", hash = "sha256:43b3319e1b4e7d1251833a93d672b4af1e40f3d632d479b98661a95f117880a2"}, {file = "amqp-5.3.1.tar.gz", hash = "sha256:cddc00c725449522023bad949f70fff7b48f0b1ade74d170a6f10ab044739432"}, @@ -277,7 +277,7 @@ description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, @@ -290,7 +290,7 @@ description = "High level compatibility layer for multiple asynchronous event lo optional = false python-versions = ">=3.7" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"}, {file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"}, @@ -312,7 +312,7 @@ description = "ASGI specs, helper code, and adapters" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "asgiref-3.9.1-py3-none-any.whl", hash = "sha256:f3bba7092a48005b5f5bacd747d36ee4a5a61f4a269a6df590b43144355ebd2c"}, {file = "asgiref-3.9.1.tar.gz", hash = "sha256:a5ab6582236218e5ef1648f242fd9f10626cfd4de8dc377db215d5d5098e3142"}, @@ -341,7 +341,7 @@ description = "Asyncer, async and await, focused on developer experience." optional = false python-versions = ">=3.6.2,<4.0.0" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "asyncer-0.0.2-py3-none-any.whl", hash = "sha256:46e0e1423ce21588350ad425875e81795280b9e1f517e8a389de940b86c348bd"}, {file = "asyncer-0.0.2.tar.gz", hash = "sha256:d546c85f3626ebbaf06bb4395db49761c902a61a6ac802b1a74133cab4f7f433"}, @@ -357,7 +357,7 @@ description = "An asyncio PostgreSQL driver" optional = false python-versions = ">=3.8.0" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "asyncpg-0.30.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bfb4dd5ae0699bad2b233672c8fc5ccbd9ad24b89afded02341786887e37927e"}, {file = "asyncpg-0.30.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc1f62c792752a49f88b7e6f774c26077091b44caceb1983509edc18a2222ec0"}, @@ -422,7 +422,7 @@ description = "Classes Without Boilerplate" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"}, {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"}, @@ -443,7 +443,7 @@ description = "Universal library for evaluating AI models" optional = false python-versions = ">=3.8.0" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "autoevals-0.0.83-py3-none-any.whl", hash = "sha256:41cc0cec096ce8a1e194a58acda205a2bc5d6ec91c79ff5bee3f8ddc8e3ab525"}, {file = "autoevals-0.0.83.tar.gz", hash = "sha256:c184ba29ac9c5a3067a4dd83db87f49b05bc5bec88897d24f43b764c34421044"}, @@ -469,7 +469,7 @@ description = "Function decoration for backoff and retry" optional = false python-versions = ">=3.7,<4.0" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "backoff-2.2.1-py3-none-any.whl", hash = "sha256:63579f9a0628e06278f7e47b7d7d5b6ce20dc65c5e96a6f3ca99a6adca0396e8"}, {file = "backoff-2.2.1.tar.gz", hash = "sha256:03f829f5bb1923180821643f8753b0502c3b682293992485b0eef2807afa5cba"}, @@ -482,7 +482,7 @@ description = "Asynchronous Python ODM for MongoDB" optional = false python-versions = "<4.0,>=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "beanie-1.30.0-py3-none-any.whl", hash = "sha256:385f1b850b36a19dd221aeb83e838c83ec6b47bbf6aeac4e5bf8b8d40bfcfe51"}, {file = "beanie-1.30.0.tar.gz", hash = "sha256:33ead17ff2742144c510b4b24e188f6b316dd1b614d86b57a3cfe20bc7b768c9"}, @@ -514,7 +514,7 @@ description = "Python multiprocessing fork with improvements and bugfixes" optional = false python-versions = ">=3.7" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "billiard-4.2.1-py3-none-any.whl", hash = "sha256:40b59a4ac8806ba2c2369ea98d876bc6108b051c227baffd928c644d15d8f3cb"}, {file = "billiard-4.2.1.tar.gz", hash = "sha256:12b641b0c539073fc8d3f5b8b7be998956665c4233c7c1fcd66a7e677c4fb36f"}, @@ -527,7 +527,7 @@ description = "The AWS SDK for Python" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "boto3-1.34.69-py3-none-any.whl", hash = "sha256:2e25ef6bd325217c2da329829478be063155897d8d3b29f31f7f23ab548519b1"}, {file = "boto3-1.34.69.tar.gz", hash = "sha256:898a5fed26b1351352703421d1a8b886ef2a74be6c97d5ecc92432ae01fda203"}, @@ -548,7 +548,7 @@ description = "Low-level, data-driven core of boto 3." optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "botocore-1.34.69-py3-none-any.whl", hash = "sha256:d3802d076d4d507bf506f9845a6970ce43adc3d819dd57c2791f5c19ed6e5950"}, {file = "botocore-1.34.69.tar.gz", hash = "sha256:d1ab2bff3c2fd51719c2021d9fa2f30fbb9ed0a308f69e9a774ac92c8091380a"}, @@ -569,7 +569,7 @@ description = "Shared core dependencies for Braintrust packages" optional = false python-versions = ">=3.8.0" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "braintrust_core-0.0.49-py3-none-any.whl", hash = "sha256:4be2a5e82b3b123ada4c54e5052da474cb80b29eed5468c8a9f0759c8b7ca0b9"}, {file = "braintrust_core-0.0.49.tar.gz", hash = "sha256:13081970114ecf0d25e8a77047d93f394ea8fe82e43dc673597992679e0b9215"}, @@ -582,7 +582,7 @@ description = "Extensible memoizing collections and decorators" optional = false python-versions = ">=3.7" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a"}, {file = "cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4"}, @@ -595,7 +595,7 @@ description = "Distributed Task Queue." optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "celery-5.5.3-py3-none-any.whl", hash = "sha256:0b5761a07057acee94694464ca482416b959568904c9dfa41ce8413a7d65d525"}, {file = "celery-5.5.3.tar.gz", hash = "sha256:6c972ae7968c2b5281227f01c3a3f984037d21c5129d07bf3550cc2afc6b10a5"}, @@ -653,7 +653,7 @@ description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.7" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5"}, {file = "certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407"}, @@ -666,7 +666,7 @@ description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" groups = ["main"] -markers = "(python_version >= \"3.12\" or python_version == \"3.11\") and platform_python_implementation != \"PyPy\"" +markers = "(python_version == \"3.11\" or python_version >= \"3.12\") and platform_python_implementation != \"PyPy\"" files = [ {file = "cffi-1.17.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14"}, {file = "cffi-1.17.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67"}, @@ -747,7 +747,7 @@ description = "The Real First Universal Charset Detector. Open, modern and activ optional = false python-versions = ">=3.7" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "charset_normalizer-3.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72"}, {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe"}, @@ -837,7 +837,7 @@ description = "Mustache templating language renderer" optional = false python-versions = "*" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "chevron-0.14.0-py3-none-any.whl", hash = "sha256:fbf996a709f8da2e745ef763f482ce2d311aa817d287593a5b990d6d6e4f0443"}, {file = "chevron-0.14.0.tar.gz", hash = "sha256:87613aafdf6d77b6a90ff073165a61ae5086e21ad49057aa0e53681601800ebf"}, @@ -850,7 +850,7 @@ description = "Composable command line interface toolkit" optional = false python-versions = ">=3.10" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"}, {file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"}, @@ -866,7 +866,7 @@ description = "Enables git-like *did-you-mean* feature in click" optional = false python-versions = ">=3.6.2" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "click_didyoumean-0.3.1-py3-none-any.whl", hash = "sha256:5c4bb6007cfea5f2fd6583a2fb6701a22a41eb98957e63d0fac41c10e7c3117c"}, {file = "click_didyoumean-0.3.1.tar.gz", hash = "sha256:4f82fdff0dbe64ef8ab2279bd6aa3f6a99c3b28c05aa09cbfc07c9d7fbb5a463"}, @@ -882,7 +882,7 @@ description = "An extension module for click to enable registering CLI commands optional = false python-versions = "*" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "click_plugins-1.1.1.2-py2.py3-none-any.whl", hash = "sha256:008d65743833ffc1f5417bf0e78e8d2c23aab04d9745ba817bd3e71b0feb6aa6"}, {file = "click_plugins-1.1.1.2.tar.gz", hash = "sha256:d7af3984a99d243c131aa1a828331e7630f4a88a9741fd05c927b204bcf92261"}, @@ -901,7 +901,7 @@ description = "REPL plugin for Click" optional = false python-versions = ">=3.6" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "click-repl-0.3.0.tar.gz", hash = "sha256:17849c23dba3d667247dc4defe1757fff98694e90fe37474f3feebb69ced26a9"}, {file = "click_repl-0.3.0-py3-none-any.whl", hash = "sha256:fb7e06deb8da8de86180a33a9da97ac316751c094c6899382da7feeeeb51b812"}, @@ -925,7 +925,7 @@ files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -markers = {main = "(platform_system == \"Windows\" or sys_platform == \"win32\") and (python_version >= \"3.12\" or python_version == \"3.11\")", dev = "(python_version >= \"3.12\" or python_version == \"3.11\") and sys_platform == \"win32\""} +markers = {main = "(platform_system == \"Windows\" or sys_platform == \"win32\") and (python_version == \"3.11\" or python_version >= \"3.12\")", dev = "(python_version == \"3.11\" or python_version >= \"3.12\") and sys_platform == \"win32\""} [[package]] name = "cryptography" @@ -934,7 +934,7 @@ description = "cryptography is a package which provides cryptographic recipes an optional = false python-versions = "!=3.9.0,!=3.9.1,>=3.7" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "cryptography-45.0.7-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:3be4f21c6245930688bd9e162829480de027f8bf962ede33d4f8ba7d67a00cee"}, {file = "cryptography-45.0.7-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:67285f8a611b0ebc0857ced2081e30302909f571a46bfa7a3cc0ad303fe015c6"}, @@ -995,7 +995,7 @@ description = "Python @deprecated decorator to deprecate old python classes, fun optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec"}, {file = "deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d"}, @@ -1014,7 +1014,7 @@ description = "Distro - an OS platform information API" optional = false python-versions = ">=3.6" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2"}, {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, @@ -1027,7 +1027,7 @@ description = "DNS toolkit" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "dnspython-2.7.0-py3-none-any.whl", hash = "sha256:b4c34b7d10b51bcc3a5071e7b8dee77939f1e878477eeecc965e9835f63c6c86"}, {file = "dnspython-2.7.0.tar.gz", hash = "sha256:ce9c432eda0dc91cf618a5cedf1a4e142651196bbcd2c80e89ed5a907e5cfaf1"}, @@ -1049,7 +1049,7 @@ description = "ECDSA cryptographic signature library (pure python)" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.6" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "ecdsa-0.19.1-py2.py3-none-any.whl", hash = "sha256:30638e27cf77b7e15c4c4cc1973720149e1033827cfd00661ca5c8cc0cdb24c3"}, {file = "ecdsa-0.19.1.tar.gz", hash = "sha256:478cba7b62555866fcb3bb3fe985e06decbdb68ef55713c4e5ab98c57d508e61"}, @@ -1069,7 +1069,7 @@ description = "execnet: rapid multi-Python deployment" optional = false python-versions = ">=3.8" groups = ["dev"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "execnet-2.1.1-py3-none-any.whl", hash = "sha256:26dee51f1b80cebd6d0ca8e74dd8745419761d3bef34163928cbebbdc4749fdc"}, {file = "execnet-2.1.1.tar.gz", hash = "sha256:5189b52c6121c24feae288166ab41b32549c7e2348652736540b9e6e7d4e72e3"}, @@ -1085,7 +1085,7 @@ description = "Faker is a Python package that generates fake data for you." optional = false python-versions = ">=3.8" groups = ["dev"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "Faker-23.3.0-py3-none-any.whl", hash = "sha256:117ce1a2805c1bc5ca753b3dc6f9d567732893b2294b827d3164261ee8f20267"}, {file = "Faker-23.3.0.tar.gz", hash = "sha256:458d93580de34403a8dec1e8d5e6be2fee96c4deca63b95d71df7a6a80a690de"}, @@ -1101,7 +1101,7 @@ description = "FastAPI framework, high performance, easy to learn, fast to code, optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "fastapi-0.116.1-py3-none-any.whl", hash = "sha256:c46ac7c312df840f0c9e220f7964bada936781bc4e2e6eb71f1c4d7553786565"}, {file = "fastapi-0.116.1.tar.gz", hash = "sha256:ed52cbf946abfd70c5a0dccb24673f0670deeb517a88b3544d03c2a6bf283143"}, @@ -1124,7 +1124,7 @@ description = "A platform independent file lock." optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d"}, {file = "filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58"}, @@ -1137,7 +1137,7 @@ description = "A list-like structure which implements collections.abc.MutableSeq optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "frozenlist-1.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cc4df77d638aa2ed703b878dd093725b72a824c3c546c076e8fdf276f78ee84a"}, {file = "frozenlist-1.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:716a9973a2cc963160394f701964fe25012600f3d311f60c790400b00e568b61"}, @@ -1252,7 +1252,7 @@ description = "File-system specification" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "fsspec-2025.7.0-py3-none-any.whl", hash = "sha256:8b012e39f63c7d5f10474de957f3ab793b47b45ae7d39f2fb735f8bbe25c0e21"}, {file = "fsspec-2025.7.0.tar.gz", hash = "sha256:786120687ffa54b8283d942929540d8bc5ccfa820deb555a2b5d0ed2b737bf58"}, @@ -1293,7 +1293,7 @@ description = "GenSON is a powerful, user-friendly JSON Schema generator." optional = false python-versions = "*" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "genson-1.3.0-py3-none-any.whl", hash = "sha256:468feccd00274cc7e4c09e84b08704270ba8d95232aa280f65b986139cec67f7"}, {file = "genson-1.3.0.tar.gz", hash = "sha256:e02db9ac2e3fd29e65b5286f7135762e2cd8a986537c075b06fc5f1517308e37"}, @@ -1306,7 +1306,7 @@ description = "Google Authentication Library" optional = false python-versions = ">=3.7" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "google_auth-2.40.3-py2.py3-none-any.whl", hash = "sha256:1370d4593e86213563547f97a92752fc658456fe4514c809544f330fed45a7ca"}, {file = "google_auth-2.40.3.tar.gz", hash = "sha256:500c3a29adedeb36ea9cf24b8d10858e152f2412e3ca37829b3fa18e33d63b77"}, @@ -1334,7 +1334,7 @@ description = "Common protobufs used in Google APIs" optional = false python-versions = ">=3.7" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "googleapis_common_protos-1.70.0-py3-none-any.whl", hash = "sha256:b8bfcca8c25a2bb253e0e0b0adaf8c00773e5e6af6fd92397576680b807e0fd8"}, {file = "googleapis_common_protos-1.70.0.tar.gz", hash = "sha256:0e1b44e0ea153e6594f9f394fef15193a68aaaea2d843f83e2742717ca753257"}, @@ -1353,7 +1353,7 @@ description = "Lightweight in-process concurrent programming" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version < \"3.14\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\") and (python_version >= \"3.12\" or python_version == \"3.11\")" +markers = "python_version < \"3.14\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\") and (python_version == \"3.11\" or python_version >= \"3.12\")" files = [ {file = "greenlet-3.2.4-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:8c68325b0d0acf8d91dde4e6f930967dd52a5302cd4062932a6b2e7c2969f47c"}, {file = "greenlet-3.2.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:94385f101946790ae13da500603491f04a76b6e4c059dab271b3ce2e283b2590"}, @@ -1422,7 +1422,7 @@ description = "WSGI HTTP Server for UNIX" optional = false python-versions = ">=3.7" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d"}, {file = "gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec"}, @@ -1445,7 +1445,7 @@ description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"}, {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"}, @@ -1458,7 +1458,7 @@ description = "Fast transfer of large files with the Hugging Face Hub." optional = false python-versions = ">=3.8" groups = ["main"] -markers = "(python_version >= \"3.12\" or python_version == \"3.11\") and (platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"arm64\" or platform_machine == \"aarch64\")" +markers = "(python_version == \"3.11\" or python_version >= \"3.12\") and (platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"arm64\" or platform_machine == \"aarch64\")" files = [ {file = "hf_xet-1.1.9-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:a3b6215f88638dd7a6ff82cb4e738dcbf3d863bf667997c093a3c990337d1160"}, {file = "hf_xet-1.1.9-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:9b486de7a64a66f9a172f4b3e0dfe79c9f0a93257c501296a2521a13495a698a"}, @@ -1480,7 +1480,7 @@ description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55"}, {file = "httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8"}, @@ -1503,7 +1503,7 @@ description = "The next generation HTTP client." optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, @@ -1529,7 +1529,7 @@ description = "Client library to download and publish models, datasets and other optional = false python-versions = ">=3.8.0" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "huggingface_hub-0.34.4-py3-none-any.whl", hash = "sha256:9b365d781739c93ff90c359844221beef048403f1bc1f1c123c191257c3c890a"}, {file = "huggingface_hub-0.34.4.tar.gz", hash = "sha256:a4228daa6fb001be3f4f4bdaf9a0db00e1739235702848df00885c9b5742c85c"}, @@ -1569,7 +1569,7 @@ description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -1585,7 +1585,7 @@ description = "Read metadata from Python packages" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd"}, {file = "importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000"}, @@ -1610,7 +1610,7 @@ description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.8" groups = ["main", "dev"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"}, {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"}, @@ -1623,7 +1623,7 @@ description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"}, {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"}, @@ -1642,7 +1642,7 @@ description = "Fast iterable JSON parser." optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "jiter-0.10.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:cd2fb72b02478f06a900a5782de2ef47e0396b3e1f7d5aba30daeb1fce66f303"}, {file = "jiter-0.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:32bb468e3af278f095d3fa5b90314728a6916d89ba3d0ffb726dd9bf7367285e"}, @@ -1730,7 +1730,7 @@ description = "JSON Matching Expressions" optional = false python-versions = ">=3.7" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "jmespath-1.0.1-py3-none-any.whl", hash = "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980"}, {file = "jmespath-1.0.1.tar.gz", hash = "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe"}, @@ -1743,7 +1743,7 @@ description = "An implementation of JSON Schema validation for Python" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63"}, {file = "jsonschema-4.25.1.tar.gz", hash = "sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85"}, @@ -1766,7 +1766,7 @@ description = "The JSON Schema meta-schemas and vocabularies, exposed as a Regis optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "jsonschema_specifications-2025.4.1-py3-none-any.whl", hash = "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af"}, {file = "jsonschema_specifications-2025.4.1.tar.gz", hash = "sha256:630159c9f4dbea161a6a2205c3011cc4f18ff381b189fff48bb39b9bf26ae608"}, @@ -1782,7 +1782,7 @@ description = "Messaging library for Python." optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "kombu-5.5.4-py3-none-any.whl", hash = "sha256:a12ed0557c238897d8e518f1d1fdf84bd1516c5e305af2dacd85c2015115feb8"}, {file = "kombu-5.5.4.tar.gz", hash = "sha256:886600168275ebeada93b888e831352fe578168342f0d1d5833d88ba0d847363"}, @@ -1819,7 +1819,7 @@ description = "Kubernetes python client" optional = false python-versions = ">=3.6" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "kubernetes-28.1.0-py2.py3-none-any.whl", hash = "sha256:10f56f8160dcb73647f15fafda268e7f60cf7dbc9f8e46d52fcd46d3beb0c18d"}, {file = "kubernetes-28.1.0.tar.gz", hash = "sha256:1468069a573430fb1cb5ad22876868f57977930f80a6749405da31cd6086a7e9"}, @@ -1847,7 +1847,7 @@ description = "" optional = false python-versions = ">=3.7,<4.0" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "lazy-model-0.2.0.tar.gz", hash = "sha256:57c0e91e171530c4fca7aebc3ac05a163a85cddd941bf7527cc46c0ddafca47c"}, {file = "lazy_model-0.2.0-py3-none-any.whl", hash = "sha256:5a3241775c253e36d9069d236be8378288a93d4fc53805211fd152e04cc9c342"}, @@ -1863,7 +1863,7 @@ description = "Python extension for computing string edit distances and similari optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "levenshtein-0.27.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:13d6f617cb6fe63714c4794861cfaacd398db58a292f930edb7f12aad931dace"}, {file = "levenshtein-0.27.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ca9d54d41075e130c390e61360bec80f116b62d6ae973aec502e77e921e95334"}, @@ -1971,7 +1971,7 @@ description = "Library to easily interface with LLM API providers" optional = false python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "litellm-1.76.0-py3-none-any.whl", hash = "sha256:357464242fc1eeda384810c9e334e48ad67a50ecd30cf61e86c15f89e2f2e0b4"}, {file = "litellm-1.76.0.tar.gz", hash = "sha256:d26d12333135edd72af60e0e310284dac3b079f4d7c47c79dfbb2430b9b4b421"}, @@ -2005,7 +2005,7 @@ description = "A super-fast templating language that borrows the best ideas from optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59"}, {file = "mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28"}, @@ -2026,7 +2026,7 @@ description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, @@ -2098,7 +2098,7 @@ description = "An implementation of time.monotonic() for Python 2 & < 3.3" optional = false python-versions = "*" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "monotonic-1.6-py2.py3-none-any.whl", hash = "sha256:68687e19a14f11f26d140dd5c86f3dba4bf5df58003000ed467e0e2a69bca96c"}, {file = "monotonic-1.6.tar.gz", hash = "sha256:3a55207bcfed53ddd5c5bae174524062935efed17792e9de2ad0205ce9ad63f7"}, @@ -2111,7 +2111,7 @@ description = "Non-blocking MongoDB driver for Tornado or asyncio" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "motor-3.7.1-py3-none-any.whl", hash = "sha256:8a63b9049e38eeeb56b4fdd57c3312a6d1f25d01db717fe7d82222393c410298"}, {file = "motor-3.7.1.tar.gz", hash = "sha256:27b4d46625c87928f331a6ca9d7c51c2f518ba0e270939d395bc1ddc89d64526"}, @@ -2137,7 +2137,7 @@ description = "multidict implementation" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "multidict-6.6.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b8aa6f0bd8125ddd04a6593437bad6a7e70f300ff4180a531654aa2ab3f6d58f"}, {file = "multidict-6.6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b9e5853bbd7264baca42ffc53391b490d65fe62849bf2c690fa3f6273dbcd0cb"}, @@ -2258,7 +2258,7 @@ description = "New Relic Python Agent" optional = false python-versions = ">=3.7" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "newrelic-10.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:833992875fda7aa3f1db9e9b8782a29d71094b7e6c0e7bcd7113534a652ba74d"}, {file = "newrelic-10.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:287e8fbab1c10e5fe2e20c81aec3fece01bf613ebf4985b6e36360fce2f5cd80"}, @@ -2301,7 +2301,7 @@ description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "numpy-1.26.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:806dd64230dbbfaca8a27faa64e2f414bf1c6622ab78cc4264f7f5f028fee3bf"}, {file = "numpy-1.26.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02f98011ba4ab17f46f80f7f8f1c291ee7d855fcef0a5a98db80767a468c85cd"}, @@ -2348,7 +2348,7 @@ description = "A generic, spec-compliant, thorough implementation of the OAuth r optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "oauthlib-3.3.1-py3-none-any.whl", hash = "sha256:88119c938d2b8fb88561af5f6ee0eec8cc8d552b7bb1f712743136eb7523b7a1"}, {file = "oauthlib-3.3.1.tar.gz", hash = "sha256:0f0f8aa759826a193cf66c12ea1af1637f87b9b4622d46e866952bb022e538c9"}, @@ -2366,7 +2366,7 @@ description = "The official Python library for the openai API" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "openai-1.102.0-py3-none-any.whl", hash = "sha256:d751a7e95e222b5325306362ad02a7aa96e1fab3ed05b5888ce1c7ca63451345"}, {file = "openai-1.102.0.tar.gz", hash = "sha256:2e0153bcd64a6523071e90211cbfca1f2bbc5ceedd0993ba932a5869f93b7fc9"}, @@ -2395,7 +2395,7 @@ description = "OpenTelemetry Python Proto" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "opentelemetry_proto-1.36.0-py3-none-any.whl", hash = "sha256:151b3bf73a09f94afc658497cf77d45a565606f62ce0c17acb08cd9937ca206e"}, {file = "opentelemetry_proto-1.36.0.tar.gz", hash = "sha256:0f10b3c72f74c91e0764a5ec88fd8f1c368ea5d9c64639fb455e2854ef87dd2f"}, @@ -2411,7 +2411,7 @@ description = "Fast, correct Python JSON library supporting dataclasses, datetim optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "orjson-3.11.3-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:29cb1f1b008d936803e2da3d7cba726fc47232c45df531b29edf0b232dd737e7"}, {file = "orjson-3.11.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97dceed87ed9139884a55db8722428e27bd8452817fbf1869c58b49fecab1120"}, @@ -2505,7 +2505,7 @@ description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" groups = ["main", "dev"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, @@ -2518,7 +2518,7 @@ description = "Powerful data structures for data analysis, time series, and stat optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "pandas-2.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52bc29a946304c360561974c6542d1dd628ddafa69134a7131fdfd6a5d7a1a35"}, {file = "pandas-2.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:220cc5c35ffaa764dd5bb17cf42df283b5cb7fdf49e10a7b053a06c9cb48ee2b"}, @@ -2566,8 +2566,8 @@ files = [ [package.dependencies] numpy = [ - {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, {version = ">=1.23.2", markers = "python_version == \"3.11\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, ] python-dateutil = ">=2.8.2" pytz = ">=2020.1" @@ -2605,7 +2605,7 @@ description = "Pexpect allows easy control of interactive console applications." optional = false python-versions = "*" groups = ["dev"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, @@ -2621,7 +2621,7 @@ description = "Python version of Google's common library for parsing, formatting optional = false python-versions = "*" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "phonenumbers-8.13.55-py2.py3-none-any.whl", hash = "sha256:25feaf46135f0fb1e61b69513dc97c477285ba98a69204bf5a8cf241a844a718"}, {file = "phonenumbers-8.13.55.tar.gz", hash = "sha256:57c989dda3eabab1b5a9e3d24438a39ebd032fa0172bf68bfd90ab70b3d5e08b"}, @@ -2634,7 +2634,7 @@ description = "PKCE Pyhton generator." optional = false python-versions = ">=3" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "pkce-1.0.3-py3-none-any.whl", hash = "sha256:55927e24c7d403b2491ebe182b95d9dcb1807643243d47e3879fbda5aad4471d"}, {file = "pkce-1.0.3.tar.gz", hash = "sha256:9775fd76d8a743d39b87df38af1cd04a58c9b5a5242d5a6350ef343d06814ab6"}, @@ -2647,7 +2647,7 @@ description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.9" groups = ["main", "dev"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, @@ -2664,7 +2664,7 @@ description = "Integrate PostHog into any python application." optional = false python-versions = "*" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "posthog-3.25.0-py2.py3-none-any.whl", hash = "sha256:85db78c13d1ecb11aed06fad53759c4e8fb3633442c2f3d0336bc0ce8a585d30"}, {file = "posthog-3.25.0.tar.gz", hash = "sha256:9168f3e7a0a5571b6b1065c41b3c171fbc68bfe72c3ac0bfd6e3d2fcdb7df2ca"}, @@ -2691,7 +2691,7 @@ description = "Library for building powerful interactive command lines in Python optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955"}, {file = "prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855"}, @@ -2707,7 +2707,7 @@ description = "Accelerated property cache" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "propcache-0.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:22d9962a358aedbb7a2e36187ff273adeaab9743373a272976d2e348d08c7770"}, {file = "propcache-0.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d0fda578d1dc3f77b6b5a5dce3b9ad69a8250a891760a548df850a5e8da87f3"}, @@ -2816,7 +2816,7 @@ description = "" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "protobuf-6.32.0-cp310-abi3-win32.whl", hash = "sha256:84f9e3c1ff6fb0308dbacb0950d8aa90694b0d0ee68e75719cb044b7078fe741"}, {file = "protobuf-6.32.0-cp310-abi3-win_amd64.whl", hash = "sha256:a8bdbb2f009cfc22a36d031f22a625a38b615b5e19e558a7b756b3279723e68e"}, @@ -2836,7 +2836,7 @@ description = "psycopg2 - Python-PostgreSQL Database Adapter" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "psycopg2-binary-2.9.10.tar.gz", hash = "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2"}, {file = "psycopg2_binary-2.9.10-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:0ea8e3d0ae83564f2fc554955d327fa081d065c8ca5cc6d2abb643e2c9c1200f"}, @@ -2914,7 +2914,7 @@ description = "Run a subprocess in a pseudo terminal" optional = false python-versions = "*" groups = ["dev"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, @@ -2927,7 +2927,7 @@ description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"}, {file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"}, @@ -2940,7 +2940,7 @@ description = "A collection of ASN.1-based protocols modules" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a"}, {file = "pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6"}, @@ -2956,7 +2956,7 @@ description = "C parser in Python" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "(python_version >= \"3.12\" or python_version == \"3.11\") and platform_python_implementation != \"PyPy\"" +markers = "(python_version == \"3.11\" or python_version >= \"3.12\") and platform_python_implementation != \"PyPy\"" files = [ {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, @@ -2969,7 +2969,7 @@ description = "Cryptographic library for Python" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "pycryptodome-3.20.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:f0e6d631bae3f231d3634f91ae4da7a960f7ff87f2865b2d2b831af1dfb04e9a"}, {file = "pycryptodome-3.20.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:baee115a9ba6c5d2709a1e88ffe62b73ecc044852a925dcb67713a288c4ec70f"}, @@ -3012,7 +3012,7 @@ description = "Data validation using Python type hints" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b"}, {file = "pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db"}, @@ -3035,7 +3035,7 @@ description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8"}, {file = "pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d"}, @@ -3148,7 +3148,7 @@ description = "JSON Web Token implementation in Python" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:dcdd193e30abefd5debf142f9adfcdd2b58004e644f25406ffaebd50bd98dacb"}, {file = "pyjwt-2.10.1.tar.gz", hash = "sha256:3cc5772eb20009233caf06e9d8a0577824723b44e6648ee0a2aedb6cf9381953"}, @@ -3170,7 +3170,7 @@ description = "PyMongo - the Official MongoDB Python driver" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "pymongo-4.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:97f0da391fb32f989f0afcd1838faff5595456d24c56d196174eddbb7c3a494c"}, {file = "pymongo-4.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ec160c4e1184da11d375a4315917f5a04180ea0ff522f0a97cf78acbb65810d8"}, @@ -3251,7 +3251,7 @@ description = "Python One Time Password Library" optional = false python-versions = ">=3.7" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "pyotp-2.9.0-py3-none-any.whl", hash = "sha256:81c2e5865b8ac55e825b0358e496e1d9387c811e85bb40e71a3b29b288963612"}, {file = "pyotp-2.9.0.tar.gz", hash = "sha256:346b6642e0dbdde3b4ff5a930b664ca82abfa116356ed48cc42c7d6590d36f63"}, @@ -3267,7 +3267,7 @@ description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.7" groups = ["main", "dev"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "pytest-7.4.4-py3-none-any.whl", hash = "sha256:b090cdf5ed60bf4c45261be03239c2c1c22df034fbffe691abe93cd80cea01d8"}, {file = "pytest-7.4.4.tar.gz", hash = "sha256:2cf0005922c6ace4a3e2ec8b4080eb0d9753fdc93107415332f50ce9e7994280"}, @@ -3289,7 +3289,7 @@ description = "Pytest support for asyncio" optional = false python-versions = ">=3.7" groups = ["dev"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "pytest_asyncio-0.21.2-py3-none-any.whl", hash = "sha256:ab664c88bb7998f711d8039cacd4884da6430886ae8bbd4eded552ed2004f16b"}, {file = "pytest_asyncio-0.21.2.tar.gz", hash = "sha256:d67738fc232b94b326b9d060750beb16e0074210b98dd8b58a5239fa2a154f45"}, @@ -3309,7 +3309,7 @@ description = "Thin-wrapper around the mock package for easier use with pytest" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "pytest_mock-3.14.1-py3-none-any.whl", hash = "sha256:178aefcd11307d874b4cd3100344e7e2d888d9791a6a1d9bfe90fbc1b74fd1d0"}, {file = "pytest_mock-3.14.1.tar.gz", hash = "sha256:159e9edac4c451ce77a5cdb9fc5d1100708d2dd4ba3c3df572f14097351af80e"}, @@ -3328,7 +3328,7 @@ description = "pytest xdist plugin for distributed testing, most importantly acr optional = false python-versions = ">=3.9" groups = ["dev"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "pytest_xdist-3.8.0-py3-none-any.whl", hash = "sha256:202ca578cfeb7370784a8c33d6d05bc6e13b4f25b5053c30a152269fd10f0b88"}, {file = "pytest_xdist-3.8.0.tar.gz", hash = "sha256:7e578125ec9bc6050861aa93f2d59f1d8d085595d6551c2c90b6f4fad8d3a9f1"}, @@ -3350,7 +3350,7 @@ description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" groups = ["main", "dev"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, @@ -3366,7 +3366,7 @@ description = "Read key-value pairs from a .env file and set them as environment optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc"}, {file = "python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab"}, @@ -3382,7 +3382,7 @@ description = "HTTP REST client, simplified for Python" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "python_http_client-3.3.7-py3-none-any.whl", hash = "sha256:ad371d2bbedc6ea15c26179c6222a78bc9308d272435ddf1d5c84f068f249a36"}, {file = "python_http_client-3.3.7.tar.gz", hash = "sha256:bf841ee45262747e00dec7ee9971dfb8c7d83083f5713596488d67739170cea0"}, @@ -3395,7 +3395,7 @@ description = "A streaming multipart parser for Python" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "python_multipart-0.0.18-py3-none-any.whl", hash = "sha256:efe91480f485f6a361427a541db4796f9e1591afc0fb8e7a4ba06bfbc6708996"}, {file = "python_multipart-0.0.18.tar.gz", hash = "sha256:7a68db60c8bfb82e460637fa4750727b45af1d5e2ed215593f917f64694d34fe"}, @@ -3408,7 +3408,7 @@ description = "World timezone definitions, modern and historical" optional = false python-versions = "*" groups = ["main", "dev"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"}, {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, @@ -3421,7 +3421,7 @@ description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, @@ -3485,7 +3485,7 @@ description = "rapid fuzzy string matching" optional = false python-versions = ">=3.10" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "rapidfuzz-3.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:91d8c7d9d38835d5fcf9bc87593add864eaea41eb33654d93ded3006b198a326"}, {file = "rapidfuzz-3.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5a1e574230262956d28e40191dd44ad3d81d2d29b5e716c6c7c0ba17c4d1524e"}, @@ -3591,7 +3591,7 @@ description = "Python client for Redis database and key-value store" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "redis-6.4.0-py3-none-any.whl", hash = "sha256:f0544fa9604264e9464cdf4814e7d4830f74b165d52f2a330a760a88dd248b7f"}, {file = "redis-6.4.0.tar.gz", hash = "sha256:b01bc7282b8444e28ec36b261df5375183bb47a07eb9c603f284e89cbc5ef010"}, @@ -3612,7 +3612,7 @@ description = "JSON Referencing + Python" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0"}, {file = "referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa"}, @@ -3630,7 +3630,7 @@ description = "Alternative regular expression module, to replace re." optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "regex-2025.8.29-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a367dbb66842a08744f49c64ba1aab23e4cbcc924bae8ef40870f2c51d6cb240"}, {file = "regex-2025.8.29-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:090d20a6f308c1cd3c33824e892666089d9719ff88e139d4b63623e881d3945c"}, @@ -3728,7 +3728,7 @@ description = "Python HTTP for Humans." optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6"}, {file = "requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf"}, @@ -3751,7 +3751,7 @@ description = "File transport adapter for Requests" optional = false python-versions = "*" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "requests_file-2.1.0-py2.py3-none-any.whl", hash = "sha256:cf270de5a4c5874e84599fc5778303d496c10ae5e870bfa378818f35d21bda5c"}, {file = "requests_file-2.1.0.tar.gz", hash = "sha256:0f549a3f3b0699415ac04d167e9cb39bccfb730cb832b4d20be3d9867356e658"}, @@ -3767,7 +3767,7 @@ description = "OAuthlib authentication support for Requests." optional = false python-versions = ">=3.4" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "requests-oauthlib-2.0.0.tar.gz", hash = "sha256:b3dffaebd884d8cd778494369603a9e7b58d29111bf6b41bdc2dcd87203af4e9"}, {file = "requests_oauthlib-2.0.0-py2.py3-none-any.whl", hash = "sha256:7dd8a5c40426b779b0868c404bdef9768deccf22749cde15852df527e6269b36"}, @@ -3804,7 +3804,7 @@ description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "rpds_py-0.27.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:68afeec26d42ab3b47e541b272166a0b4400313946871cba3ed3a4fc0cab1cef"}, {file = "rpds_py-0.27.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74e5b2f7bb6fa38b1b10546d27acbacf2a022a8b5543efb06cfebc72a59c85be"}, @@ -3970,7 +3970,7 @@ description = "Pure-Python RSA implementation" optional = false python-versions = "<4,>=3.6" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "rsa-4.9.1-py3-none-any.whl", hash = "sha256:68635866661c6836b8d39430f97a996acbd61bfa49406748ea243539fe239762"}, {file = "rsa-4.9.1.tar.gz", hash = "sha256:e7bdbfdb5497da4c07dfd35530e1a902659db6ff241e39d9953cad06ebd0ae75"}, @@ -3986,7 +3986,7 @@ description = "An Amazon S3 Transfer Manager" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "s3transfer-0.10.4-py3-none-any.whl", hash = "sha256:244a76a24355363a68164241438de1b72f8781664920260c48465896b712a41e"}, {file = "s3transfer-0.10.4.tar.gz", hash = "sha256:29edc09801743c21eb5ecbc617a152df41d3c287f67b615f73e5f750583666a7"}, @@ -4005,7 +4005,7 @@ description = "Twilio SendGrid library for Python" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "sendgrid-6.12.4-py3-none-any.whl", hash = "sha256:9a211b96241e63bd5b9ed9afcc8608f4bcac426e4a319b3920ab877c8426e92c"}, {file = "sendgrid-6.12.4.tar.gz", hash = "sha256:9e88b849daf0fa4bdf256c3b5da9f5a3272402c0c2fd6b1928c9de440db0a03d"}, @@ -4015,8 +4015,8 @@ files = [ ecdsa = ">=0.19.1,<1" python-http-client = ">=3.2.1" werkzeug = [ - {version = ">=2.3.5", markers = "python_version >= \"3.12\""}, {version = ">=2.2.0", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, + {version = ">=2.3.5", markers = "python_version >= \"3.12\""}, ] [[package]] @@ -4026,7 +4026,7 @@ description = "Python 2 and 3 compatibility utilities" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" groups = ["main", "dev"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, @@ -4039,7 +4039,7 @@ description = "Sniff out which async library your code is running under" optional = false python-versions = ">=3.7" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, @@ -4052,7 +4052,7 @@ description = "Database Abstraction Library" optional = false python-versions = ">=3.7" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "SQLAlchemy-2.0.43-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:21ba7a08a4253c5825d1db389d4299f64a100ef9800e4624c8bf70d8f136e6ed"}, {file = "SQLAlchemy-2.0.43-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11b9503fa6f8721bef9b8567730f664c5a5153d25e247aadc69247c4bc605227"}, @@ -4149,7 +4149,7 @@ description = "JSON type with nested change tracking for SQLAlchemy" optional = false python-versions = ">= 3.6" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "sqlalchemy-json-0.7.0.tar.gz", hash = "sha256:620d0b26f648f21a8fa9127df66f55f83a5ab4ae010e5397a5c6989a08238561"}, {file = "sqlalchemy_json-0.7.0-py3-none-any.whl", hash = "sha256:27881d662ca18363a4ac28175cc47ea2a6f2bef997ae1159c151026b741818e6"}, @@ -4168,7 +4168,7 @@ description = "The little ASGI library that shines." optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "starlette-0.47.3-py3-none-any.whl", hash = "sha256:89c0778ca62a76b826101e7c709e70680a1699ca7da6b44d38eb0a7e61fe4b51"}, {file = "starlette-0.47.3.tar.gz", hash = "sha256:6bc94f839cc176c4858894f1f8908f0ab79dfec1a6b8402f6da9be26ebea52e9"}, @@ -4188,7 +4188,7 @@ description = "Python bindings for the Stripe API" optional = false python-versions = ">=3.6" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "stripe-11.6.0-py2.py3-none-any.whl", hash = "sha256:6e6cf09ebb6d5fc2d708401cb8868fd7bff987a6d09a0433caaa92c62f97dbc5"}, {file = "stripe-11.6.0.tar.gz", hash = "sha256:0ced7cce23a6cb1a393c86a1f7f9435c9d83ae7cbd556362868caf62cb44a92c"}, @@ -4205,7 +4205,7 @@ description = "Structured Logging for Python" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "structlog-25.4.0-py3-none-any.whl", hash = "sha256:fe809ff5c27e557d14e613f45ca441aabda051d119ee5a0102aaba6ce40eed2c"}, {file = "structlog-25.4.0.tar.gz", hash = "sha256:186cd1b0a8ae762e29417095664adf1d6a31702160a46dacb7796ea82f7409e4"}, @@ -4218,7 +4218,7 @@ description = "SuperTokens SDK for Python" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "supertokens_python-0.29.2-py3-none-any.whl", hash = "sha256:9886c1257432403dfb7a862a914b2308f84f15992b5616c7797a32feae163e80"}, {file = "supertokens_python-0.29.2.tar.gz", hash = "sha256:c37caaf81c159c686cca0a7990ebeac272309a87078edea0bc0fc48ba1cccac1"}, @@ -4253,7 +4253,7 @@ description = "tiktoken is a fast BPE tokeniser for use with OpenAI's models" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "tiktoken-0.11.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:8a9b517d6331d7103f8bef29ef93b3cca95fa766e293147fe7bacddf310d5917"}, {file = "tiktoken-0.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b4ddb1849e6bf0afa6cc1c5d809fb980ca240a5fffe585a04e119519758788c0"}, @@ -4302,7 +4302,7 @@ description = "Accurately separates a URL's subdomain, domain, and public suffix optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "tldextract-5.3.0-py3-none-any.whl", hash = "sha256:f70f31d10b55c83993f55e91ecb7c5d84532a8972f22ec578ecfbe5ea2292db2"}, {file = "tldextract-5.3.0.tar.gz", hash = "sha256:b3d2b70a1594a0ecfa6967d57251527d58e00bb5a91a74387baa0d87a0678609"}, @@ -4325,7 +4325,7 @@ description = "" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "tokenizers-0.22.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:eaa9620122a3fb99b943f864af95ed14c8dfc0f47afa3b404ac8c16b3f2bb484"}, {file = "tokenizers-0.22.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:71784b9ab5bf0ff3075bceeb198149d2c5e068549c0d18fe32d06ba0deb63f79"}, @@ -4359,7 +4359,7 @@ description = "Python Library for Tom's Obvious, Minimal Language" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, @@ -4372,7 +4372,7 @@ description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, @@ -4395,7 +4395,7 @@ description = "Twilio API client and TwiML generator" optional = false python-versions = ">=3.7.0" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "twilio-9.7.2-py2.py3-none-any.whl", hash = "sha256:bb751ebd914ea42591641daf093f018d11c032e789b066bd489459c530170985"}, {file = "twilio-9.7.2.tar.gz", hash = "sha256:a491e3ceeb51b89e07e52d62581fcf661d170c8e35ecf4ee24bdd1ffa952b4f0"}, @@ -4414,7 +4414,7 @@ description = "Backported and Experimental Type Hints for Python 3.9+" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"}, {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"}, @@ -4427,7 +4427,7 @@ description = "Runtime typing introspection tools" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51"}, {file = "typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28"}, @@ -4443,7 +4443,7 @@ description = "Provider of IANA time zone data" optional = false python-versions = ">=2" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"}, {file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"}, @@ -4456,7 +4456,7 @@ description = "HTTP library with thread-safe connection pooling, file post, and optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "urllib3-1.26.20-py2.py3-none-any.whl", hash = "sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e"}, {file = "urllib3-1.26.20.tar.gz", hash = "sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32"}, @@ -4474,7 +4474,7 @@ description = "Drop-in replacement for Python UUID in Rust" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "uuid_utils-0.10.0-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:8d5a4508feefec62456cd6a41bcdde458d56827d908f226803b886d22a3d5e63"}, {file = "uuid_utils-0.10.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:dbefc2b9113f9dfe56bdae58301a2b3c53792221410d422826f3d1e3e6555fe7"}, @@ -4512,7 +4512,7 @@ description = "The lightning-fast ASGI server." optional = false python-versions = ">=3.7" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "uvicorn-0.22.0-py3-none-any.whl", hash = "sha256:e9434d3bbf05f310e762147f769c9f21235ee118ba2d2bf1155a7196448bd996"}, {file = "uvicorn-0.22.0.tar.gz", hash = "sha256:79277ae03db57ce7d9aa0567830bbb51d7a612f54d6e1e3e92da3ef24c2c8ed8"}, @@ -4532,7 +4532,7 @@ description = "Python promises." optional = false python-versions = ">=3.6" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "vine-5.1.0-py3-none-any.whl", hash = "sha256:40fdf3c48b2cfe1c38a49e9ae2da6fda88e4794c810050a728bd7413811fb1dc"}, {file = "vine-5.1.0.tar.gz", hash = "sha256:8b62e981d35c41049211cf62a0a1242d8c1ee9bd15bb196ce38aefd6799e61e0"}, @@ -4545,7 +4545,7 @@ description = "Filesystem events monitoring" optional = false python-versions = ">=3.7" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "watchdog-3.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:336adfc6f5cc4e037d52db31194f7581ff744b67382eb6021c868322e32eef41"}, {file = "watchdog-3.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a70a8dcde91be523c35b2bf96196edc5730edb347e374c7de7cd20c43ed95397"}, @@ -4589,7 +4589,7 @@ description = "Measures the displayed width of unicode strings in a terminal" optional = false python-versions = "*" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, @@ -4602,7 +4602,7 @@ description = "WebSocket client for Python with low level API options" optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526"}, {file = "websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da"}, @@ -4620,7 +4620,7 @@ description = "The comprehensive WSGI web application library." optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "werkzeug-3.1.3-py3-none-any.whl", hash = "sha256:54b78bf3716d19a65be4fceccc0d1d7b89e608834989dfae50ea87564639213e"}, {file = "werkzeug-3.1.3.tar.gz", hash = "sha256:60723ce945c19328679790e3282cc758aa4a6040e4bb330f53d30fa546d44746"}, @@ -4639,7 +4639,7 @@ description = "Module for decorators, wrappers and monkey patching." optional = false python-versions = ">=3.8" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "wrapt-1.17.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88bbae4d40d5a46142e70d58bf664a89b6b4befaea7b2ecc14e03cedb8e06c04"}, {file = "wrapt-1.17.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b13af258d6a9ad602d57d889f83b9d5543acd471eee12eb51f5b01f8eb1bc2"}, @@ -4731,7 +4731,7 @@ description = "Yet another URL library" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "yarl-1.20.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6032e6da6abd41e4acda34d75a816012717000fa6839f37124a47fcefc49bec4"}, {file = "yarl-1.20.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2c7b34d804b8cf9b214f05015c4fee2ebe7ed05cf581e7192c06555c71f4446a"}, @@ -4851,7 +4851,7 @@ description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.9" groups = ["main"] -markers = "python_version >= \"3.12\" or python_version == \"3.11\"" +markers = "python_version == \"3.11\" or python_version >= \"3.12\"" files = [ {file = "zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e"}, {file = "zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166"}, @@ -4868,4 +4868,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = ">=3.11,<4.0" -content-hash = "d8ede1847818d074c56f64ab0a3c5de5661216fc0f1932ad303fdf24cb817098" +content-hash = "56dadab7968f77bc502a2d93899fb99614f77c3b1c149625c1820be9e79b7154" diff --git a/api/pyproject.toml b/api/pyproject.toml index 19512ba722..86fa7d0687 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "api" -version = "0.55.1" +version = "0.55.2" description = "Agenta API" authors = [{ name = "Mahmoud Mabrouk", email = "mahmoud@agenta.ai" }] @@ -68,6 +68,7 @@ googleapis-common-protos = ">=1.60.0" h11 = ">=0.16.0" ecdsa = "^0.19.1" gunicorn = "^23.0.0" +tiktoken = "0.11.0" [tool.poetry.group.dev.dependencies] pytest = "^7.3.1" diff --git a/examples/jupyter/prompt-management/how-to-prompt-management.ipynb b/examples/jupyter/prompt-management/how-to-prompt-management.ipynb index d719bc2292..abc071f818 100644 --- a/examples/jupyter/prompt-management/how-to-prompt-management.ipynb +++ b/examples/jupyter/prompt-management/how-to-prompt-management.ipynb @@ -117,12 +117,7 @@ "id": "02fa644e", "metadata": {}, "outputs": [], - "source": [ - "import os\n", - "\n", - "os.environ[\"AGENTA_API_KEY\"] = \"\"\n", - "os.environ[\"AGENTA_HOST\"] = \"http://cloud.agenta.ai/\"\n" - ] + "source": "import os\n\nos.environ[\"AGENTA_API_KEY\"] = \"\"\nos.environ[\"AGENTA_HOST\"] = \"https://cloud.agenta.ai/\"" }, { "cell_type": "code", @@ -828,4 +823,4 @@ }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/examples/jupyter/prompt-management/manage-prompts-with-sdk-tutorial.ipynb b/examples/jupyter/prompt-management/manage-prompts-with-sdk-tutorial.ipynb index ab2c6a77f3..3eaa4c44e7 100644 --- a/examples/jupyter/prompt-management/manage-prompts-with-sdk-tutorial.ipynb +++ b/examples/jupyter/prompt-management/manage-prompts-with-sdk-tutorial.ipynb @@ -1,639 +1,634 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Manage Prompts with SDK - Tutorial\n", - "\n", - "You can manage prompts easily through Agenta's web UI. But sometimes you might want to do things programmatically instead of using the interface.\n", - "\n", - "In this tutorial, we'll use the Agenta SDK to create a new prompt, commit changes, deploy them to production, then fetch their configuration and use it to call the LLM.\n", - "\n", - "## Tutorial Overview\n", - "\n", - "Before we begin, let's quickly review how Agenta versions prompts:\n", - "\n", - "Agenta follows a structure similar to **git** for prompt versioning. Instead of having one commit history, it uses **multiple branches (called variants)** where changes can be committed, and **environments** where these changes can be deployed (and used in your application).\n", - "\n", - "The workflow for deploying a change to production that we'll follow in this tutorial is:\n", - "\n", - "1. Create a new variant\n", - "2. Commit a change to that variant\n", - "3. Deploy that commit (variant/version) to the production environment\n", - "4. Commit a new change to that variant\n", - "5. Fetch the config from that environment\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Install dependencies\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Requirement already satisfied: agenta in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (0.51.6)\n", - "Requirement already satisfied: openai in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (1.106.1)\n", - "Collecting openai\n", - " Downloading openai-1.107.1-py3-none-any.whl.metadata (29 kB)\n", - "Requirement already satisfied: decorator<6.0.0,>=5.2.1 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (5.2.1)\n", - "Requirement already satisfied: fastapi<0.117.0,>=0.116.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (0.116.1)\n", - "Requirement already satisfied: h11>=0.16.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (0.16.0)\n", - "Requirement already satisfied: httpx>=0.28.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (0.28.1)\n", - "Requirement already satisfied: huggingface-hub<0.31.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (0.30.2)\n", - "Requirement already satisfied: importlib-metadata<9.0,>=8.0.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (8.7.0)\n", - "Requirement already satisfied: jinja2<4.0.0,>=3.1.6 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (3.1.6)\n", - "Requirement already satisfied: litellm==1.76.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (1.76.0)\n", - "Requirement already satisfied: opentelemetry-api<2.0.0,>=1.27.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (1.36.0)\n", - "Requirement already satisfied: opentelemetry-exporter-otlp-proto-http<2.0.0,>=1.27.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (1.36.0)\n", - "Requirement already satisfied: opentelemetry-instrumentation>=0.56b0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (0.57b0)\n", - "Requirement already satisfied: opentelemetry-sdk<2.0.0,>=1.27.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (1.36.0)\n", - "Requirement already satisfied: pydantic>=2 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (2.11.7)\n", - "Requirement already satisfied: python-dotenv<2.0.0,>=1.0.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (1.1.1)\n", - "Requirement already satisfied: pyyaml<7.0.0,>=6.0.2 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (6.0.2)\n", - "Requirement already satisfied: starlette<0.48.0,>=0.47.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (0.47.3)\n", - "Requirement already satisfied: structlog<26.0.0,>=25.2.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (25.4.0)\n", - "Requirement already satisfied: toml<0.11.0,>=0.10.2 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (0.10.2)\n", - "Requirement already satisfied: aiohttp>=3.10 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from litellm==1.76.0->agenta) (3.12.15)\n", - "Requirement already satisfied: click in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from litellm==1.76.0->agenta) (8.2.1)\n", - "Requirement already satisfied: jsonschema<5.0.0,>=4.22.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from litellm==1.76.0->agenta) (4.25.1)\n", - "Requirement already satisfied: tiktoken>=0.7.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from litellm==1.76.0->agenta) (0.11.0)\n", - "Requirement already satisfied: tokenizers in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from litellm==1.76.0->agenta) (0.22.0)\n", - "Requirement already satisfied: typing-extensions>=4.8.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from fastapi<0.117.0,>=0.116.0->agenta) (4.15.0)\n", - "Requirement already satisfied: filelock in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from huggingface-hub<0.31.0->agenta) (3.19.1)\n", - "Requirement already satisfied: fsspec>=2023.5.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from huggingface-hub<0.31.0->agenta) (2025.7.0)\n", - "Requirement already satisfied: packaging>=20.9 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from huggingface-hub<0.31.0->agenta) (25.0)\n", - "Requirement already satisfied: requests in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from huggingface-hub<0.31.0->agenta) (2.32.5)\n", - "Requirement already satisfied: tqdm>=4.42.1 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from huggingface-hub<0.31.0->agenta) (4.67.1)\n", - "Requirement already satisfied: zipp>=3.20 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from importlib-metadata<9.0,>=8.0.0->agenta) (3.23.0)\n", - "Requirement already satisfied: MarkupSafe>=2.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from jinja2<4.0.0,>=3.1.6->agenta) (3.0.2)\n", - "Requirement already satisfied: attrs>=22.2.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from jsonschema<5.0.0,>=4.22.0->litellm==1.76.0->agenta) (25.3.0)\n", - "Requirement already satisfied: jsonschema-specifications>=2023.03.6 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from jsonschema<5.0.0,>=4.22.0->litellm==1.76.0->agenta) (2025.4.1)\n", - "Requirement already satisfied: referencing>=0.28.4 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from jsonschema<5.0.0,>=4.22.0->litellm==1.76.0->agenta) (0.36.2)\n", - "Requirement already satisfied: rpds-py>=0.7.1 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from jsonschema<5.0.0,>=4.22.0->litellm==1.76.0->agenta) (0.27.1)\n", - "Requirement already satisfied: googleapis-common-protos~=1.52 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from opentelemetry-exporter-otlp-proto-http<2.0.0,>=1.27.0->agenta) (1.70.0)\n", - "Requirement already satisfied: opentelemetry-exporter-otlp-proto-common==1.36.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from opentelemetry-exporter-otlp-proto-http<2.0.0,>=1.27.0->agenta) (1.36.0)\n", - "Requirement already satisfied: opentelemetry-proto==1.36.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from opentelemetry-exporter-otlp-proto-http<2.0.0,>=1.27.0->agenta) (1.36.0)\n", - "Requirement already satisfied: protobuf<7.0,>=5.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from opentelemetry-proto==1.36.0->opentelemetry-exporter-otlp-proto-http<2.0.0,>=1.27.0->agenta) (6.32.0)\n", - "Requirement already satisfied: opentelemetry-semantic-conventions==0.57b0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from opentelemetry-sdk<2.0.0,>=1.27.0->agenta) (0.57b0)\n", - "Requirement already satisfied: annotated-types>=0.6.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from pydantic>=2->agenta) (0.7.0)\n", - "Requirement already satisfied: pydantic-core==2.33.2 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from pydantic>=2->agenta) (2.33.2)\n", - "Requirement already satisfied: typing-inspection>=0.4.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from pydantic>=2->agenta) (0.4.1)\n", - "Requirement already satisfied: charset_normalizer<4,>=2 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from requests->huggingface-hub<0.31.0->agenta) (3.4.3)\n", - "Requirement already satisfied: idna<4,>=2.5 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from requests->huggingface-hub<0.31.0->agenta) (3.10)\n", - "Requirement already satisfied: urllib3<3,>=1.21.1 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from requests->huggingface-hub<0.31.0->agenta) (2.5.0)\n", - "Requirement already satisfied: certifi>=2017.4.17 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from requests->huggingface-hub<0.31.0->agenta) (2025.8.3)\n", - "Requirement already satisfied: anyio<5,>=3.6.2 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from starlette<0.48.0,>=0.47.0->agenta) (4.10.0)\n", - "Requirement already satisfied: sniffio>=1.1 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from anyio<5,>=3.6.2->starlette<0.48.0,>=0.47.0->agenta) (1.3.1)\n", - "Requirement already satisfied: distro<2,>=1.7.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from openai) (1.9.0)\n", - "Requirement already satisfied: jiter<1,>=0.4.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from openai) (0.10.0)\n", - "Requirement already satisfied: httpcore==1.* in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from httpx>=0.28.0->agenta) (1.0.9)\n", - "Requirement already satisfied: aiohappyeyeballs>=2.5.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from aiohttp>=3.10->litellm==1.76.0->agenta) (2.6.1)\n", - "Requirement already satisfied: aiosignal>=1.4.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from aiohttp>=3.10->litellm==1.76.0->agenta) (1.4.0)\n", - "Requirement already satisfied: frozenlist>=1.1.1 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from aiohttp>=3.10->litellm==1.76.0->agenta) (1.7.0)\n", - "Requirement already satisfied: multidict<7.0,>=4.5 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from aiohttp>=3.10->litellm==1.76.0->agenta) (6.6.4)\n", - "Requirement already satisfied: propcache>=0.2.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from aiohttp>=3.10->litellm==1.76.0->agenta) (0.3.2)\n", - "Requirement already satisfied: yarl<2.0,>=1.17.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from aiohttp>=3.10->litellm==1.76.0->agenta) (1.20.1)\n", - "Requirement already satisfied: wrapt<2.0.0,>=1.0.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from opentelemetry-instrumentation>=0.56b0->agenta) (1.17.3)\n", - "Requirement already satisfied: regex>=2022.1.18 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from tiktoken>=0.7.0->litellm==1.76.0->agenta) (2025.9.1)\n", - "Downloading openai-1.107.1-py3-none-any.whl (945 kB)\n", - "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m945.2/945.2 kB\u001b[0m \u001b[31m22.6 MB/s\u001b[0m \u001b[33m0:00:00\u001b[0m\n", - "\u001b[?25hInstalling collected packages: openai\n", - " Attempting uninstall: openai\n", - " Found existing installation: openai 1.106.1\n", - " Uninstalling openai-1.106.1:\n", - " Successfully uninstalled openai-1.106.1\n", - "Successfully installed openai-1.107.1\n", - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "pip install -U agenta openai\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Setup\n", - "\n", - "Before using the SDK, we need to initialize it using the `ag.init()` method, which takes the host (default `cloud.agenta.ai`) and the API key (not required for community editions):\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import os\n", - "os.environ[\"AGENTA_HOST\"] = \"http://cloud.agenta.ai/\" # Default value, no need to set explicitly unless self-hosted\n", - "os.environ[\"AGENTA_API_KEY\"] = \"\"\n", - "os.environ[\"OPENAI_API_KEY\"] = \"\"" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2025-09-11T15:56:10.922Z \u001b[38;5;70m[INFO.]\u001b[0m Agenta - SDK version: 0.51.6 \u001b[38;5;245m[agenta.sdk.agenta_init]\u001b[0m \n", - "2025-09-11T15:56:10.923Z \u001b[38;5;70m[INFO.]\u001b[0m Agenta - Host: http://144.76.237.122 \u001b[38;5;245m[agenta.sdk.agenta_init]\u001b[0m \n", - "2025-09-11T15:56:10.923Z \u001b[38;5;70m[INFO.]\u001b[0m Agenta - OLTP URL: http://144.76.237.122/api/otlp/v1/traces \u001b[38;5;245m[agenta.sdk.tracing.tracing]\u001b[0m \n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Overriding of current TracerProvider is not allowed\n" - ] - } - ], - "source": [ - "import os\n", - "import agenta as ag\n", - "from getpass import getpass\n", - "\n", - "# Initialize the SDK with your API key\n", - "api_key = os.getenv(\"AGENTA_API_KEY\")\n", - "if not api_key:\n", - " os.environ[\"AGENTA_API_KEY\"] = getpass(\"Enter your Agenta API key: \")\n", - "\n", - "openai_api_key = os.getenv(\"OPENAI_API_KEY\")\n", - "if not openai_api_key:\n", - " os.environ[\"OPENAI_API_KEY\"] = getpass(\"Enter your OpenAI API key: \")\n", - "\n", - "# Initialize the SDK\n", - "ag.init()\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Creating a new prompt\n", - "\n", - "We're going to create a new completion prompt called `topic-explainer`.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Created application:\n", - "app_id='0199397d-14f4-7bb2-a9c5-9a12934fb6c3' app_name='topic-explainer'\n" - ] - } - ], - "source": [ - "# Creates an empty application\n", - "app = ag.AppManager.create(\n", - " app_slug=\"topic-explainer\",\n", - " template_key=\"SERVICE:completion\", # we define here the app type, choices are SERVICE:completion, SERVICE:chat, CUSTOM\n", - ")\n", - "\n", - "print(\"Created application:\")\n", - "print(app)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - ":::warning\n", - "The app created until now is empty. You cannot use it from the UI yet. You need to create a variant and commit changes to it to be able to use it (next section).\n", - ":::\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Creating a new variant\n", - "\n", - "Variants are similar to branches in **git**. Any change to the prompt must first be committed to a variant. Here, we'll create a new variant and make our first commit to it using the `VariantManager.create` method:\n" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Created variant:\n", - "{\n", - " \"app_id\": \"0199397d-14f4-7bb2-a9c5-9a12934fb6c3\",\n", - " \"app_slug\": \"topic-explainer\",\n", - " \"variant_id\": \"0199397d-1d46-74c3-bc3f-e63f2357c7dc\",\n", - " \"variant_slug\": \"new-variant\",\n", - " \"variant_version\": 1,\n", - " \"environment_id\": null,\n", - " \"environment_slug\": null,\n", - " \"environment_version\": null,\n", - " \"committed_at\": \"2025-09-11T15:55:24.842469+00:00\",\n", - " \"committed_by\": \"mahmoud@agenta.ai\",\n", - " \"committed_by_id\": \"0198eb1b-012a-7891-955e-15ef49443ece\",\n", - " \"deployed_at\": null,\n", - " \"deployed_by\": null,\n", - " \"deployed_by_id\": null,\n", - " \"params\": {\n", - " \"prompt\": {\n", - " \"messages\": [\n", - " {\n", - " \"role\": \"system\",\n", - " \"content\": \"You are an assistant that provides concise answers\",\n", - " \"name\": null,\n", - " \"tool_calls\": null,\n", - " \"tool_call_id\": null\n", - " },\n", - " {\n", - " \"role\": \"user\",\n", - " \"content\": \"Explain {{topic}} in simple terms\",\n", - " \"name\": null,\n", - " \"tool_calls\": null,\n", - " \"tool_call_id\": null\n", - " }\n", - " ],\n", - " \"system_prompt\": null,\n", - " \"user_prompt\": null,\n", - " \"template_format\": \"curly\",\n", - " \"input_keys\": null,\n", - " \"llm_config\": {\n", - " \"model\": \"gpt-3.5-turbo\",\n", - " \"temperature\": 0.6,\n", - " \"max_tokens\": 150,\n", - " \"top_p\": 1.0,\n", - " \"frequency_penalty\": 0.0,\n", - " \"presence_penalty\": 0.0,\n", - " \"response_format\": null,\n", - " \"stream\": null,\n", - " \"tools\": null,\n", - " \"tool_choice\": null\n", - " }\n", - " }\n", - " }\n", - "}\n" - ] - } - ], - "source": [ - "from agenta.sdk.types import PromptTemplate, Message, ModelConfig\n", - "from pydantic import BaseModel\n", - "\n", - "# We need to create a Pydantic Model with a `prompt` field of type `PromptTemplate`\n", - "class Config(BaseModel):\n", - " prompt: PromptTemplate\n", - "\n", - "config = Config(\n", - " prompt=PromptTemplate(\n", - " messages=[\n", - " Message(role=\"system\", content=\"You are an assistant that provides concise answers\"),\n", - " Message(role=\"user\", content=\"Explain {{topic}} in simple terms\"),\n", - " ],\n", - " llm_config=ModelConfig(\n", - " model=\"gpt-3.5-turbo\",\n", - " max_tokens=150,\n", - " temperature=0.6,\n", - " top_p=1.0,\n", - " frequency_penalty=0.0,\n", - " presence_penalty=0.0,\n", - " ),\n", - " template_format=\"curly\"\n", - " )\n", - ")\n", - "\n", - "# Create a new variant\n", - "variant = ag.VariantManager.create(\n", - " parameters=config.model_dump(),\n", - " app_slug=\"topic-explainer\",\n", - " variant_slug=\"new-variant\"\n", - ")\n", - "\n", - "print(\"Created variant:\")\n", - "print(variant)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This command will create a new variant and initialize it with the first commit containing the provided parameters.\n", - "\n", - "- **Parameters:**\n", - " - `app_slug`: The unique slug of your application.\n", - " - `variant_slug`: The unique slug of the new variant.\n", - " - `parameters`: A dictionary containing the initial configuration parameters.\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Deploying changes to the production environment\n", - "\n", - "To deploy our commit to an environment, use the `DeploymentManager.deploy` method.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Deployed variant to environment:\n", - "{\n", - " \"app_id\": \"0199397d-14f4-7bb2-a9c5-9a12934fb6c3\",\n", - " \"app_slug\": \"topic-explainer\",\n", - " \"variant_id\": \"0199397d-1d46-74c3-bc3f-e63f2357c7dc\",\n", - " \"variant_slug\": \"new-variant\",\n", - " \"variant_version\": 1,\n", - " \"environment_id\": \"0199397d-23fe-7621-8d30-29426339a777\",\n", - " \"environment_slug\": \"production\",\n", - " \"environment_version\": 1,\n", - " \"committed_at\": \"2025-09-11T15:55:24.842469+00:00\",\n", - " \"committed_by\": \"mahmoud@agenta.ai\",\n", - " \"committed_by_id\": \"0198eb1b-012a-7891-955e-15ef49443ece\",\n", - " \"deployed_at\": \"2025-09-11T15:55:26.590624+00:00\",\n", - " \"deployed_by\": \"mahmoud@agenta.ai\",\n", - " \"deployed_by_id\": \"0198eb1b-012a-7891-955e-15ef49443ece\"\n", - "}\n" - ] - } - ], - "source": [ - "# Deploy the variant to the production environment\n", - "deployment = ag.DeploymentManager.deploy(\n", - " app_slug=\"topic-explainer\",\n", - " variant_slug=\"new-variant\",\n", - " environment_slug=\"production\",\n", - ")\n", - "\n", - "print(\"Deployed variant to environment:\")\n", - "print(deployment)\n" - ] - }, + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Manage Prompts with SDK - Tutorial\n", + "\n", + "You can manage prompts easily through Agenta's web UI. But sometimes you might want to do things programmatically instead of using the interface.\n", + "\n", + "In this tutorial, we'll use the Agenta SDK to create a new prompt, commit changes, deploy them to production, then fetch their configuration and use it to call the LLM.\n", + "\n", + "## Tutorial Overview\n", + "\n", + "Before we begin, let's quickly review how Agenta versions prompts:\n", + "\n", + "Agenta follows a structure similar to **git** for prompt versioning. Instead of having one commit history, it uses **multiple branches (called variants)** where changes can be committed, and **environments** where these changes can be deployed (and used in your application).\n", + "\n", + "The workflow for deploying a change to production that we'll follow in this tutorial is:\n", + "\n", + "1. Create a new variant\n", + "2. Commit a change to that variant\n", + "3. Deploy that commit (variant/version) to the production environment\n", + "4. Commit a new change to that variant\n", + "5. Fetch the config from that environment\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Install dependencies\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "- **Parameters:**\n", - " - `environment_slug`: The slug of the environment (`development`, `staging`, or `production`).\n", - "\n", - "- **Notes:**\n", - " - Deploying a variant without specifying a `variant_version` deploys the latest version.\n" - ] - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: agenta in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (0.51.6)\n", + "Requirement already satisfied: openai in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (1.106.1)\n", + "Collecting openai\n", + " Downloading openai-1.107.1-py3-none-any.whl.metadata (29 kB)\n", + "Requirement already satisfied: decorator<6.0.0,>=5.2.1 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (5.2.1)\n", + "Requirement already satisfied: fastapi<0.117.0,>=0.116.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (0.116.1)\n", + "Requirement already satisfied: h11>=0.16.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (0.16.0)\n", + "Requirement already satisfied: httpx>=0.28.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (0.28.1)\n", + "Requirement already satisfied: huggingface-hub<0.31.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (0.30.2)\n", + "Requirement already satisfied: importlib-metadata<9.0,>=8.0.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (8.7.0)\n", + "Requirement already satisfied: jinja2<4.0.0,>=3.1.6 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (3.1.6)\n", + "Requirement already satisfied: litellm==1.76.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (1.76.0)\n", + "Requirement already satisfied: opentelemetry-api<2.0.0,>=1.27.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (1.36.0)\n", + "Requirement already satisfied: opentelemetry-exporter-otlp-proto-http<2.0.0,>=1.27.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (1.36.0)\n", + "Requirement already satisfied: opentelemetry-instrumentation>=0.56b0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (0.57b0)\n", + "Requirement already satisfied: opentelemetry-sdk<2.0.0,>=1.27.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (1.36.0)\n", + "Requirement already satisfied: pydantic>=2 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (2.11.7)\n", + "Requirement already satisfied: python-dotenv<2.0.0,>=1.0.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (1.1.1)\n", + "Requirement already satisfied: pyyaml<7.0.0,>=6.0.2 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (6.0.2)\n", + "Requirement already satisfied: starlette<0.48.0,>=0.47.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (0.47.3)\n", + "Requirement already satisfied: structlog<26.0.0,>=25.2.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (25.4.0)\n", + "Requirement already satisfied: toml<0.11.0,>=0.10.2 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from agenta) (0.10.2)\n", + "Requirement already satisfied: aiohttp>=3.10 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from litellm==1.76.0->agenta) (3.12.15)\n", + "Requirement already satisfied: click in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from litellm==1.76.0->agenta) (8.2.1)\n", + "Requirement already satisfied: jsonschema<5.0.0,>=4.22.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from litellm==1.76.0->agenta) (4.25.1)\n", + "Requirement already satisfied: tiktoken>=0.7.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from litellm==1.76.0->agenta) (0.11.0)\n", + "Requirement already satisfied: tokenizers in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from litellm==1.76.0->agenta) (0.22.0)\n", + "Requirement already satisfied: typing-extensions>=4.8.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from fastapi<0.117.0,>=0.116.0->agenta) (4.15.0)\n", + "Requirement already satisfied: filelock in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from huggingface-hub<0.31.0->agenta) (3.19.1)\n", + "Requirement already satisfied: fsspec>=2023.5.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from huggingface-hub<0.31.0->agenta) (2025.7.0)\n", + "Requirement already satisfied: packaging>=20.9 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from huggingface-hub<0.31.0->agenta) (25.0)\n", + "Requirement already satisfied: requests in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from huggingface-hub<0.31.0->agenta) (2.32.5)\n", + "Requirement already satisfied: tqdm>=4.42.1 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from huggingface-hub<0.31.0->agenta) (4.67.1)\n", + "Requirement already satisfied: zipp>=3.20 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from importlib-metadata<9.0,>=8.0.0->agenta) (3.23.0)\n", + "Requirement already satisfied: MarkupSafe>=2.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from jinja2<4.0.0,>=3.1.6->agenta) (3.0.2)\n", + "Requirement already satisfied: attrs>=22.2.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from jsonschema<5.0.0,>=4.22.0->litellm==1.76.0->agenta) (25.3.0)\n", + "Requirement already satisfied: jsonschema-specifications>=2023.03.6 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from jsonschema<5.0.0,>=4.22.0->litellm==1.76.0->agenta) (2025.4.1)\n", + "Requirement already satisfied: referencing>=0.28.4 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from jsonschema<5.0.0,>=4.22.0->litellm==1.76.0->agenta) (0.36.2)\n", + "Requirement already satisfied: rpds-py>=0.7.1 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from jsonschema<5.0.0,>=4.22.0->litellm==1.76.0->agenta) (0.27.1)\n", + "Requirement already satisfied: googleapis-common-protos~=1.52 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from opentelemetry-exporter-otlp-proto-http<2.0.0,>=1.27.0->agenta) (1.70.0)\n", + "Requirement already satisfied: opentelemetry-exporter-otlp-proto-common==1.36.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from opentelemetry-exporter-otlp-proto-http<2.0.0,>=1.27.0->agenta) (1.36.0)\n", + "Requirement already satisfied: opentelemetry-proto==1.36.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from opentelemetry-exporter-otlp-proto-http<2.0.0,>=1.27.0->agenta) (1.36.0)\n", + "Requirement already satisfied: protobuf<7.0,>=5.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from opentelemetry-proto==1.36.0->opentelemetry-exporter-otlp-proto-http<2.0.0,>=1.27.0->agenta) (6.32.0)\n", + "Requirement already satisfied: opentelemetry-semantic-conventions==0.57b0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from opentelemetry-sdk<2.0.0,>=1.27.0->agenta) (0.57b0)\n", + "Requirement already satisfied: annotated-types>=0.6.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from pydantic>=2->agenta) (0.7.0)\n", + "Requirement already satisfied: pydantic-core==2.33.2 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from pydantic>=2->agenta) (2.33.2)\n", + "Requirement already satisfied: typing-inspection>=0.4.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from pydantic>=2->agenta) (0.4.1)\n", + "Requirement already satisfied: charset_normalizer<4,>=2 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from requests->huggingface-hub<0.31.0->agenta) (3.4.3)\n", + "Requirement already satisfied: idna<4,>=2.5 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from requests->huggingface-hub<0.31.0->agenta) (3.10)\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from requests->huggingface-hub<0.31.0->agenta) (2.5.0)\n", + "Requirement already satisfied: certifi>=2017.4.17 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from requests->huggingface-hub<0.31.0->agenta) (2025.8.3)\n", + "Requirement already satisfied: anyio<5,>=3.6.2 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from starlette<0.48.0,>=0.47.0->agenta) (4.10.0)\n", + "Requirement already satisfied: sniffio>=1.1 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from anyio<5,>=3.6.2->starlette<0.48.0,>=0.47.0->agenta) (1.3.1)\n", + "Requirement already satisfied: distro<2,>=1.7.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from openai) (1.9.0)\n", + "Requirement already satisfied: jiter<1,>=0.4.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from openai) (0.10.0)\n", + "Requirement already satisfied: httpcore==1.* in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from httpx>=0.28.0->agenta) (1.0.9)\n", + "Requirement already satisfied: aiohappyeyeballs>=2.5.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from aiohttp>=3.10->litellm==1.76.0->agenta) (2.6.1)\n", + "Requirement already satisfied: aiosignal>=1.4.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from aiohttp>=3.10->litellm==1.76.0->agenta) (1.4.0)\n", + "Requirement already satisfied: frozenlist>=1.1.1 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from aiohttp>=3.10->litellm==1.76.0->agenta) (1.7.0)\n", + "Requirement already satisfied: multidict<7.0,>=4.5 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from aiohttp>=3.10->litellm==1.76.0->agenta) (6.6.4)\n", + "Requirement already satisfied: propcache>=0.2.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from aiohttp>=3.10->litellm==1.76.0->agenta) (0.3.2)\n", + "Requirement already satisfied: yarl<2.0,>=1.17.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from aiohttp>=3.10->litellm==1.76.0->agenta) (1.20.1)\n", + "Requirement already satisfied: wrapt<2.0.0,>=1.0.0 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from opentelemetry-instrumentation>=0.56b0->agenta) (1.17.3)\n", + "Requirement already satisfied: regex>=2022.1.18 in /home/mahmoud/code/agenta_cloud/.venv/lib/python3.12/site-packages (from tiktoken>=0.7.0->litellm==1.76.0->agenta) (2025.9.1)\n", + "Downloading openai-1.107.1-py3-none-any.whl (945 kB)\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m945.2/945.2 kB\u001b[0m \u001b[31m22.6 MB/s\u001b[0m \u001b[33m0:00:00\u001b[0m\n", + "\u001b[?25hInstalling collected packages: openai\n", + " Attempting uninstall: openai\n", + " Found existing installation: openai 1.106.1\n", + " Uninstalling openai-1.106.1:\n", + " Successfully uninstalled openai-1.106.1\n", + "Successfully installed openai-1.107.1\n", + "Note: you may need to restart the kernel to use updated packages.\n" + ] + } + ], + "source": [ + "pip install -U agenta openai\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup\n", + "\n", + "Before using the SDK, we need to initialize it using the `ag.init()` method, which takes the host (default `cloud.agenta.ai`) and the API key (not required for community editions):\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": "import os\nos.environ[\"AGENTA_HOST\"] = \"https://cloud.agenta.ai/\" # Default value, no need to set explicitly unless self-hosted\nos.environ[\"AGENTA_API_KEY\"] = \"\"\nos.environ[\"OPENAI_API_KEY\"] = \"\"" + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Committing a change to variant\n", - "\n", - "We're now going to commit changes to our variant. Note that this will not modify the version in deployment!\n", - "\n", - "To save changes to a variant (creating a new version), we are going to use the `VariantManager.commit` method with explicit parameters.\n" - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "2025-09-11T15:56:10.922Z \u001b[38;5;70m[INFO.]\u001b[0m Agenta - SDK version: 0.51.6 \u001b[38;5;245m[agenta.sdk.agenta_init]\u001b[0m \n", + "2025-09-11T15:56:10.923Z \u001b[38;5;70m[INFO.]\u001b[0m Agenta - Host: http://144.76.237.122 \u001b[38;5;245m[agenta.sdk.agenta_init]\u001b[0m \n", + "2025-09-11T15:56:10.923Z \u001b[38;5;70m[INFO.]\u001b[0m Agenta - OLTP URL: http://144.76.237.122/api/otlp/v1/traces \u001b[38;5;245m[agenta.sdk.tracing.tracing]\u001b[0m \n" + ] }, { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Committed new version of variant:\n", - "{\n", - " \"app_id\": \"0199397d-14f4-7bb2-a9c5-9a12934fb6c3\",\n", - " \"app_slug\": \"topic-explainer\",\n", - " \"variant_id\": \"0199397d-2a70-7f20-ba60-2b810e512400\",\n", - " \"variant_slug\": \"new-variant\",\n", - " \"variant_version\": 2,\n", - " \"environment_id\": null,\n", - " \"environment_slug\": null,\n", - " \"environment_version\": null,\n", - " \"committed_at\": \"2025-09-11T15:55:24.842469+00:00\",\n", - " \"committed_by\": \"mahmoud@agenta.ai\",\n", - " \"committed_by_id\": \"0198eb1b-012a-7891-955e-15ef49443ece\",\n", - " \"deployed_at\": null,\n", - " \"deployed_by\": null,\n", - " \"deployed_by_id\": null,\n", - " \"params\": {\n", - " \"prompt\": {\n", - " \"messages\": [\n", - " {\n", - " \"role\": \"system\",\n", - " \"content\": \"You are an assistant that provides concise answers\",\n", - " \"name\": null,\n", - " \"tool_calls\": null,\n", - " \"tool_call_id\": null\n", - " },\n", - " {\n", - " \"role\": \"user\",\n", - " \"content\": \"Use Paul Graham style to explain {{topic}} in simple terms.\",\n", - " \"name\": null,\n", - " \"tool_calls\": null,\n", - " \"tool_call_id\": null\n", - " }\n", - " ],\n", - " \"system_prompt\": null,\n", - " \"user_prompt\": null,\n", - " \"template_format\": \"curly\",\n", - " \"input_keys\": null,\n", - " \"llm_config\": {\n", - " \"model\": \"gpt-3.5-turbo\",\n", - " \"temperature\": 0.9,\n", - " \"max_tokens\": 150,\n", - " \"top_p\": 1.0,\n", - " \"frequency_penalty\": 0.0,\n", - " \"presence_penalty\": 0.0,\n", - " \"response_format\": null,\n", - " \"stream\": null,\n", - " \"tools\": null,\n", - " \"tool_choice\": null\n", - " }\n", - " }\n", - " }\n", - "}\n" - ] - } - ], - "source": [ - "config2 = Config(\n", - " prompt=PromptTemplate(\n", - " messages=[\n", - " Message(role=\"system\", content=\"You are an assistant that provides concise answers\"),\n", - " Message(role=\"user\", content=\"Use Paul Graham style to explain {{topic}} in simple terms.\"),\n", - " ],\n", - " llm_config=ModelConfig(\n", - " model=\"gpt-3.5-turbo\",\n", - " max_tokens=150,\n", - " temperature=0.9,\n", - " top_p=1.0,\n", - " frequency_penalty=0.0,\n", - " presence_penalty=0.0,\n", - " ),\n", - " template_format=\"curly\"\n", - " )\n", - ")\n", - "\n", - "# Commit the new version\n", - "variant = ag.VariantManager.commit(\n", - " parameters=config2.model_dump(),\n", - " app_slug=\"topic-explainer\",\n", - " variant_slug=\"new-variant\"\n", - ")\n", - "\n", - "print(\"Committed new version of variant:\")\n", - "print(variant)\n" - ] - }, + "name": "stderr", + "output_type": "stream", + "text": [ + "Overriding of current TracerProvider is not allowed\n" + ] + } + ], + "source": [ + "import os\n", + "import agenta as ag\n", + "from getpass import getpass\n", + "\n", + "# Initialize the SDK with your API key\n", + "api_key = os.getenv(\"AGENTA_API_KEY\")\n", + "if not api_key:\n", + " os.environ[\"AGENTA_API_KEY\"] = getpass(\"Enter your Agenta API key: \")\n", + "\n", + "openai_api_key = os.getenv(\"OPENAI_API_KEY\")\n", + "if not openai_api_key:\n", + " os.environ[\"OPENAI_API_KEY\"] = getpass(\"Enter your OpenAI API key: \")\n", + "\n", + "# Initialize the SDK\n", + "ag.init()\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating a new prompt\n", + "\n", + "We're going to create a new completion prompt called `topic-explainer`.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ { - "cell_type": "markdown", - "metadata": {}, - "source": [ - ":::info Immutability\n", - "Each commit creates a new version of the variant. Versions are immutable once created.\n", - ":::\n" - ] - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "Created application:\n", + "app_id='0199397d-14f4-7bb2-a9c5-9a12934fb6c3' app_name='topic-explainer'\n" + ] + } + ], + "source": [ + "# Creates an empty application\n", + "app = ag.AppManager.create(\n", + " app_slug=\"topic-explainer\",\n", + " template_key=\"SERVICE:completion\", # we define here the app type, choices are SERVICE:completion, SERVICE:chat, CUSTOM\n", + ")\n", + "\n", + "print(\"Created application:\")\n", + "print(app)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":::warning\n", + "The app created until now is empty. You cannot use it from the UI yet. You need to create a variant and commit changes to it to be able to use it (next section).\n", + ":::\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating a new variant\n", + "\n", + "Variants are similar to branches in **git**. Any change to the prompt must first be committed to a variant. Here, we'll create a new variant and make our first commit to it using the `VariantManager.create` method:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Fetching the prompt in production\n", - "\n", - "Now we'll fetch and use the prompt that's in production. Keep in mind that the production environment still references the first version of our variant. If we want it to reflect the latest changes, we'll need to deploy it again.\n" - ] - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "Created variant:\n", + "{\n", + " \"app_id\": \"0199397d-14f4-7bb2-a9c5-9a12934fb6c3\",\n", + " \"app_slug\": \"topic-explainer\",\n", + " \"variant_id\": \"0199397d-1d46-74c3-bc3f-e63f2357c7dc\",\n", + " \"variant_slug\": \"new-variant\",\n", + " \"variant_version\": 1,\n", + " \"environment_id\": null,\n", + " \"environment_slug\": null,\n", + " \"environment_version\": null,\n", + " \"committed_at\": \"2025-09-11T15:55:24.842469+00:00\",\n", + " \"committed_by\": \"mahmoud@agenta.ai\",\n", + " \"committed_by_id\": \"0198eb1b-012a-7891-955e-15ef49443ece\",\n", + " \"deployed_at\": null,\n", + " \"deployed_by\": null,\n", + " \"deployed_by_id\": null,\n", + " \"params\": {\n", + " \"prompt\": {\n", + " \"messages\": [\n", + " {\n", + " \"role\": \"system\",\n", + " \"content\": \"You are an assistant that provides concise answers\",\n", + " \"name\": null,\n", + " \"tool_calls\": null,\n", + " \"tool_call_id\": null\n", + " },\n", + " {\n", + " \"role\": \"user\",\n", + " \"content\": \"Explain {{topic}} in simple terms\",\n", + " \"name\": null,\n", + " \"tool_calls\": null,\n", + " \"tool_call_id\": null\n", + " }\n", + " ],\n", + " \"system_prompt\": null,\n", + " \"user_prompt\": null,\n", + " \"template_format\": \"curly\",\n", + " \"input_keys\": null,\n", + " \"llm_config\": {\n", + " \"model\": \"gpt-3.5-turbo\",\n", + " \"temperature\": 0.6,\n", + " \"max_tokens\": 150,\n", + " \"top_p\": 1.0,\n", + " \"frequency_penalty\": 0.0,\n", + " \"presence_penalty\": 0.0,\n", + " \"response_format\": null,\n", + " \"stream\": null,\n", + " \"tools\": null,\n", + " \"tool_choice\": null\n", + " }\n", + " }\n", + " }\n", + "}\n" + ] + } + ], + "source": [ + "from agenta.sdk.types import PromptTemplate, Message, ModelConfig\n", + "from pydantic import BaseModel\n", + "\n", + "# We need to create a Pydantic Model with a `prompt` field of type `PromptTemplate`\n", + "class Config(BaseModel):\n", + " prompt: PromptTemplate\n", + "\n", + "config = Config(\n", + " prompt=PromptTemplate(\n", + " messages=[\n", + " Message(role=\"system\", content=\"You are an assistant that provides concise answers\"),\n", + " Message(role=\"user\", content=\"Explain {{topic}} in simple terms\"),\n", + " ],\n", + " llm_config=ModelConfig(\n", + " model=\"gpt-3.5-turbo\",\n", + " max_tokens=150,\n", + " temperature=0.6,\n", + " top_p=1.0,\n", + " frequency_penalty=0.0,\n", + " presence_penalty=0.0,\n", + " ),\n", + " template_format=\"curly\"\n", + " )\n", + ")\n", + "\n", + "# Create a new variant\n", + "variant = ag.VariantManager.create(\n", + " parameters=config.model_dump(),\n", + " app_slug=\"topic-explainer\",\n", + " variant_slug=\"new-variant\"\n", + ")\n", + "\n", + "print(\"Created variant:\")\n", + "print(variant)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This command will create a new variant and initialize it with the first commit containing the provided parameters.\n", + "\n", + "- **Parameters:**\n", + " - `app_slug`: The unique slug of your application.\n", + " - `variant_slug`: The unique slug of the new variant.\n", + " - `parameters`: A dictionary containing the initial configuration parameters.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Deploying changes to the production environment\n", + "\n", + "To deploy our commit to an environment, use the `DeploymentManager.deploy` method.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Fetched configuration from production:\n", - "{'prompt': {'messages': [{'role': 'system', 'content': 'You are an assistant that provides concise answers', 'name': None, 'tool_calls': None, 'tool_call_id': None}, {'role': 'user', 'content': 'Explain {{topic}} in simple terms', 'name': None, 'tool_calls': None, 'tool_call_id': None}], 'system_prompt': None, 'user_prompt': None, 'template_format': 'curly', 'input_keys': None, 'llm_config': {'model': 'gpt-3.5-turbo', 'temperature': 0.6, 'max_tokens': 150, 'top_p': 1.0, 'frequency_penalty': 0.0, 'presence_penalty': 0.0, 'response_format': None, 'stream': None, 'tools': None, 'tool_choice': None}}}\n" - ] - } - ], - "source": [ - "# Fetch configuration from the production environment\n", - "config = ag.ConfigManager.get_from_registry(\n", - " app_slug=\"topic-explainer\",\n", - " environment_slug=\"production\"\n", - ")\n", - "\n", - "print(\"Fetched configuration from production:\")\n", - "print(config)\n" - ] - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "Deployed variant to environment:\n", + "{\n", + " \"app_id\": \"0199397d-14f4-7bb2-a9c5-9a12934fb6c3\",\n", + " \"app_slug\": \"topic-explainer\",\n", + " \"variant_id\": \"0199397d-1d46-74c3-bc3f-e63f2357c7dc\",\n", + " \"variant_slug\": \"new-variant\",\n", + " \"variant_version\": 1,\n", + " \"environment_id\": \"0199397d-23fe-7621-8d30-29426339a777\",\n", + " \"environment_slug\": \"production\",\n", + " \"environment_version\": 1,\n", + " \"committed_at\": \"2025-09-11T15:55:24.842469+00:00\",\n", + " \"committed_by\": \"mahmoud@agenta.ai\",\n", + " \"committed_by_id\": \"0198eb1b-012a-7891-955e-15ef49443ece\",\n", + " \"deployed_at\": \"2025-09-11T15:55:26.590624+00:00\",\n", + " \"deployed_by\": \"mahmoud@agenta.ai\",\n", + " \"deployed_by_id\": \"0198eb1b-012a-7891-955e-15ef49443ece\"\n", + "}\n" + ] + } + ], + "source": [ + "# Deploy the variant to the production environment\n", + "deployment = ag.DeploymentManager.deploy(\n", + " app_slug=\"topic-explainer\",\n", + " variant_slug=\"new-variant\",\n", + " environment_slug=\"production\",\n", + ")\n", + "\n", + "print(\"Deployed variant to environment:\")\n", + "print(deployment)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- **Parameters:**\n", + " - `environment_slug`: The slug of the environment (`development`, `staging`, or `production`).\n", + "\n", + "- **Notes:**\n", + " - Deploying a variant without specifying a `variant_version` deploys the latest version.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Committing a change to variant\n", + "\n", + "We're now going to commit changes to our variant. Note that this will not modify the version in deployment!\n", + "\n", + "To save changes to a variant (creating a new version), we are going to use the `VariantManager.commit` method with explicit parameters.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Response for topic 'artificial intelligence':\n", - "Artificial intelligence (AI) is the ability of a computer or machine to perform tasks that typically require human intelligence, such as learning, problem-solving, and decision-making. It involves creating algorithms and systems that can process information, recognize patterns, and make predictions without explicit programming.\n" - ] - } - ], - "source": [ - "# Using the configuration with OpenAI client\n", - "from openai import OpenAI\n", - "\n", - "# Format the prompt with your topic\n", - "topic = \"artificial intelligence\"\n", - "prompt = PromptTemplate(**config[\"prompt\"]).format(topic=topic)\n", - "client = OpenAI()\n", - "\n", - "response = client.chat.completions.create(\n", - " **prompt.to_openai_kwargs()\n", - ")\n", - "\n", - "print(f\"Response for topic '{topic}':\")\n", - "print(response.choices[0].message.content)\n" - ] - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "Committed new version of variant:\n", + "{\n", + " \"app_id\": \"0199397d-14f4-7bb2-a9c5-9a12934fb6c3\",\n", + " \"app_slug\": \"topic-explainer\",\n", + " \"variant_id\": \"0199397d-2a70-7f20-ba60-2b810e512400\",\n", + " \"variant_slug\": \"new-variant\",\n", + " \"variant_version\": 2,\n", + " \"environment_id\": null,\n", + " \"environment_slug\": null,\n", + " \"environment_version\": null,\n", + " \"committed_at\": \"2025-09-11T15:55:24.842469+00:00\",\n", + " \"committed_by\": \"mahmoud@agenta.ai\",\n", + " \"committed_by_id\": \"0198eb1b-012a-7891-955e-15ef49443ece\",\n", + " \"deployed_at\": null,\n", + " \"deployed_by\": null,\n", + " \"deployed_by_id\": null,\n", + " \"params\": {\n", + " \"prompt\": {\n", + " \"messages\": [\n", + " {\n", + " \"role\": \"system\",\n", + " \"content\": \"You are an assistant that provides concise answers\",\n", + " \"name\": null,\n", + " \"tool_calls\": null,\n", + " \"tool_call_id\": null\n", + " },\n", + " {\n", + " \"role\": \"user\",\n", + " \"content\": \"Use Paul Graham style to explain {{topic}} in simple terms.\",\n", + " \"name\": null,\n", + " \"tool_calls\": null,\n", + " \"tool_call_id\": null\n", + " }\n", + " ],\n", + " \"system_prompt\": null,\n", + " \"user_prompt\": null,\n", + " \"template_format\": \"curly\",\n", + " \"input_keys\": null,\n", + " \"llm_config\": {\n", + " \"model\": \"gpt-3.5-turbo\",\n", + " \"temperature\": 0.9,\n", + " \"max_tokens\": 150,\n", + " \"top_p\": 1.0,\n", + " \"frequency_penalty\": 0.0,\n", + " \"presence_penalty\": 0.0,\n", + " \"response_format\": null,\n", + " \"stream\": null,\n", + " \"tools\": null,\n", + " \"tool_choice\": null\n", + " }\n", + " }\n", + " }\n", + "}\n" + ] + } + ], + "source": [ + "config2 = Config(\n", + " prompt=PromptTemplate(\n", + " messages=[\n", + " Message(role=\"system\", content=\"You are an assistant that provides concise answers\"),\n", + " Message(role=\"user\", content=\"Use Paul Graham style to explain {{topic}} in simple terms.\"),\n", + " ],\n", + " llm_config=ModelConfig(\n", + " model=\"gpt-3.5-turbo\",\n", + " max_tokens=150,\n", + " temperature=0.9,\n", + " top_p=1.0,\n", + " frequency_penalty=0.0,\n", + " presence_penalty=0.0,\n", + " ),\n", + " template_format=\"curly\"\n", + " )\n", + ")\n", + "\n", + "# Commit the new version\n", + "variant = ag.VariantManager.commit(\n", + " parameters=config2.model_dump(),\n", + " app_slug=\"topic-explainer\",\n", + " variant_slug=\"new-variant\"\n", + ")\n", + "\n", + "print(\"Committed new version of variant:\")\n", + "print(variant)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + ":::info Immutability\n", + "Each commit creates a new version of the variant. Versions are immutable once created.\n", + ":::\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Fetching the prompt in production\n", + "\n", + "Now we'll fetch and use the prompt that's in production. Keep in mind that the production environment still references the first version of our variant. If we want it to reflect the latest changes, we'll need to deploy it again.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Next Steps\n", - "\n", - "Now that you've learned how to manage configurations using the SDK, you can:\n", - "\n", - "- Read the guide to explore more advanced features of the SDK\n", - "- Read how to reference prompts in your traces\n", - "- Read how to manage configuration for your workflows (chain of prompts, RAG..)\n", - "\n", - "## Summary\n", - "\n", - "In this tutorial, we've covered the complete workflow for managing prompts with the Agenta SDK:\n", - "\n", - "1. ✅ Created a new application\n", - "2. ✅ Created a variant with initial configuration\n", - "3. ✅ Deployed the variant to production environment\n", - "4. ✅ Committed changes to create a new version\n", - "5. ✅ Fetched configuration from production and used it with OpenAI\n", - "\n", - "This demonstrates the Git-like workflow that Agenta uses for prompt versioning, allowing you to manage different variants and deploy specific versions to different environments.\n" - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "Fetched configuration from production:\n", + "{'prompt': {'messages': [{'role': 'system', 'content': 'You are an assistant that provides concise answers', 'name': None, 'tool_calls': None, 'tool_call_id': None}, {'role': 'user', 'content': 'Explain {{topic}} in simple terms', 'name': None, 'tool_calls': None, 'tool_call_id': None}], 'system_prompt': None, 'user_prompt': None, 'template_format': 'curly', 'input_keys': None, 'llm_config': {'model': 'gpt-3.5-turbo', 'temperature': 0.6, 'max_tokens': 150, 'top_p': 1.0, 'frequency_penalty': 0.0, 'presence_penalty': 0.0, 'response_format': None, 'stream': None, 'tools': None, 'tool_choice': None}}}\n" + ] } - ], - "metadata": { - "kernelspec": { - "display_name": ".venv (3.12.3)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.3" + ], + "source": [ + "# Fetch configuration from the production environment\n", + "config = ag.ConfigManager.get_from_registry(\n", + " app_slug=\"topic-explainer\",\n", + " environment_slug=\"production\"\n", + ")\n", + "\n", + "print(\"Fetched configuration from production:\")\n", + "print(config)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Response for topic 'artificial intelligence':\n", + "Artificial intelligence (AI) is the ability of a computer or machine to perform tasks that typically require human intelligence, such as learning, problem-solving, and decision-making. It involves creating algorithms and systems that can process information, recognize patterns, and make predictions without explicit programming.\n" + ] } + ], + "source": [ + "# Using the configuration with OpenAI client\n", + "from openai import OpenAI\n", + "\n", + "# Format the prompt with your topic\n", + "topic = \"artificial intelligence\"\n", + "prompt = PromptTemplate(**config[\"prompt\"]).format(topic=topic)\n", + "client = OpenAI()\n", + "\n", + "response = client.chat.completions.create(\n", + " **prompt.to_openai_kwargs()\n", + ")\n", + "\n", + "print(f\"Response for topic '{topic}':\")\n", + "print(response.choices[0].message.content)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Next Steps\n", + "\n", + "Now that you've learned how to manage configurations using the SDK, you can:\n", + "\n", + "- Read the guide to explore more advanced features of the SDK\n", + "- Read how to reference prompts in your traces\n", + "- Read how to manage configuration for your workflows (chain of prompts, RAG..)\n", + "\n", + "## Summary\n", + "\n", + "In this tutorial, we've covered the complete workflow for managing prompts with the Agenta SDK:\n", + "\n", + "1. ✅ Created a new application\n", + "2. ✅ Created a variant with initial configuration\n", + "3. ✅ Deployed the variant to production environment\n", + "4. ✅ Committed changes to create a new version\n", + "5. ✅ Fetched configuration from production and used it with OpenAI\n", + "\n", + "This demonstrates the Git-like workflow that Agenta uses for prompt versioning, allowing you to manage different variants and deploy specific versions to different environments.\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv (3.12.3)", + "language": "python", + "name": "python3" }, - "nbformat": 4, - "nbformat_minor": 2 -} + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/sdk/poetry.lock b/sdk/poetry.lock index 15cf639175..050e0b7288 100644 --- a/sdk/poetry.lock +++ b/sdk/poetry.lock @@ -2850,4 +2850,4 @@ type = ["pytest-mypy"] [metadata] lock-version = "2.1" python-versions = "^3.9" -content-hash = "677a597f2906165c99547483e7bda02d5c85b26f84ed2591358edc7ac760f969" +content-hash = "f661d841d5a501c9370ab7eed22c4713413acfaa6a6b77d12dbcc91155fab1c2" diff --git a/sdk/pyproject.toml b/sdk/pyproject.toml index 776db4de91..808a12e4da 100644 --- a/sdk/pyproject.toml +++ b/sdk/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "agenta" -version = "0.55.1" +version = "0.55.2" description = "The SDK for agenta is an open-source LLMOps platform." readme = "README.md" authors = ["Mahmoud Mabrouk "] @@ -42,6 +42,8 @@ huggingface-hub = "<0.31.0" h11 = ">=0.16.0" decorator = "^5.2.1" openai = ">=1.100.0" +tiktoken = "0.11.0" + [tool.poetry.group.dev.dependencies] posthog = "^3.1.0" diff --git a/web/oss/package.json b/web/oss/package.json index 948a6065cc..01f8e1e47d 100644 --- a/web/oss/package.json +++ b/web/oss/package.json @@ -1,6 +1,6 @@ { "name": "@agenta/oss", - "version": "0.55.1", + "version": "0.55.2", "private": true, "engines": { "node": ">=18" diff --git a/web/oss/src/components/Playground/Components/Drawers/TraceDrawer/TraceDrawer.tsx b/web/oss/src/components/Playground/Components/Drawers/TraceDrawer/TraceDrawer.tsx index d3326118db..b3a6a92b7b 100644 --- a/web/oss/src/components/Playground/Components/Drawers/TraceDrawer/TraceDrawer.tsx +++ b/web/oss/src/components/Playground/Components/Drawers/TraceDrawer/TraceDrawer.tsx @@ -1,6 +1,6 @@ -import {useEffect, useRef, useState} from "react" +import {useCallback, useEffect, useRef, useState} from "react" -import {Splitter} from "antd" +import {Splitter, Spin} from "antd" import {useAtomValue, useSetAtom} from "jotai" import dynamic from "next/dynamic" @@ -8,8 +8,15 @@ import EnhancedDrawer from "@/oss/components/EnhancedUIs/Drawer" import useTraceDrawer from "@/oss/components/pages/observability/drawer/hooks/useTraceDrawer" import TraceSidePanel from "@/oss/components/pages/observability/drawer/TraceSidePanel" import {useObservability} from "@/oss/state/newObservability" +import {useQueryParamState} from "@/oss/state/appState" +import {clearTraceParamAtom} from "@/oss/state/url" -import {isDrawerOpenAtom, closeTraceDrawerAtom} from "./store/traceDrawerStore" +import { + isDrawerOpenAtom, + closeTraceDrawerAtom, + setTraceDrawerActiveSpanAtom, + setTraceDrawerTraceAtom, +} from "./store/traceDrawerStore" const TraceContent = dynamic( () => import("@/oss/components/pages/observability/drawer/TraceContent"), @@ -20,67 +27,96 @@ const TraceTree = dynamic(() => import("@/oss/components/pages/observability/dra const TraceDrawer = () => { const open = useAtomValue(isDrawerOpenAtom) const closeDrawer = useSetAtom(closeTraceDrawerAtom) + const clearTraceParam = useSetAtom(clearTraceParamAtom) const [selected, setSelected] = useState("") - const { - traces, - navigationIds: payloadNavIds, - activeTraceId: payloadActiveId, - getTraceById, - } = useTraceDrawer() + const {traces, activeSpanId, getTraceById, traceResponse, error, isLoading, traceId} = + useTraceDrawer() const [drawerWidth] = useState(1200) const [isAnnotationsSectionOpen, setIsAnnotationsSectionOpen] = useState(true) - const {setSelectedTraceId: setGlobalSelectedTraceId, setSelectedNode: setGlobalSelectedNode} = - useObservability() + const { + traceTabs, + filters, + sort, + limit, + setSelectedTraceId: setGlobalSelectedTraceId, + setSelectedNode: setGlobalSelectedNode, + } = useObservability() + const setActiveSpan = useSetAtom(setTraceDrawerActiveSpanAtom) + const setTraceDrawerTrace = useSetAtom(setTraceDrawerTraceAtom) + const [, setTraceQueryParam] = useQueryParamState("trace") + const [, setSpanQueryParam] = useQueryParamState("span") // Initialize selection when drawer payload changes const lastPayloadActiveIdRef = useRef(undefined) useEffect(() => { - if (payloadActiveId) { - const hasChanged = lastPayloadActiveIdRef.current !== payloadActiveId - lastPayloadActiveIdRef.current = payloadActiveId - - if (hasChanged || !selected) { - setSelected(payloadActiveId) - } + const incomingId = activeSpanId || traces[0]?.span_id || "" + if (!incomingId) { + setSelected("") + lastPayloadActiveIdRef.current = undefined return } - if (!selected && traces.length > 0) { - setSelected(traces[0]?.span_id || "") + const hasChanged = lastPayloadActiveIdRef.current !== incomingId + lastPayloadActiveIdRef.current = incomingId + + if (hasChanged || !selected) { + setSelected(incomingId) } - }, [payloadActiveId, traces, selected]) + }, [activeSpanId, traces, selected]) // If current selection is not found in the latest traces (e.g., user clicked a different row), re-anchor useEffect(() => { if (selected && traces.length > 0) { const exists = getTraceById(selected) if (!exists) { - setSelected(payloadActiveId || traces[0]?.span_id || "") + setSelected(activeSpanId || traces[0]?.span_id || "") } } - }, [selected, traces, payloadActiveId, getTraceById]) + }, [selected, traces, activeSpanId, getTraceById]) - // Keep component mounted; EnhancedDrawer handles destroyOnHidden. We gate heavy work via memos. + useEffect(() => { + if (selected) { + setActiveSpan(selected) + setSpanQueryParam(selected, {shallow: true}) + } else { + setSpanQueryParam(undefined, {shallow: true}) + } + }, [selected, setActiveSpan, setSpanQueryParam]) - const navigationIds = - payloadNavIds && payloadNavIds.length > 1 - ? payloadNavIds - : traces?.length - ? traces.map((t: any) => t.span_id) - : [] + // Keep component mounted; EnhancedDrawer handles destroyOnHidden. We gate heavy work via memos. const activeId = selected || traces[0]?.span_id || "" + const activeTrace = getTraceById(activeId) + + const handleAfterOpenChange = useCallback( + (isOpen: boolean) => { + if (!isOpen) { + clearTraceParam() + setSpanQueryParam(undefined, {shallow: true}) + } + }, + [clearTraceParam, setSpanQueryParam], + ) return ( { onClose={closeDrawer} width={drawerWidth} closeOnLayoutClick={false} + afterOpenChange={handleAfterOpenChange} className="[&_.ant-drawer-body]:p-0" > - - - - - - - - {isAnnotationsSectionOpen && ( - - - - )} - +
+ +
+ + + + + + + + {isAnnotationsSectionOpen && ( + + + + )} + +
+
+
) } diff --git a/web/oss/src/components/Playground/Components/Drawers/TraceDrawer/index.tsx b/web/oss/src/components/Playground/Components/Drawers/TraceDrawer/index.tsx index f994e63a91..188aca2a00 100644 --- a/web/oss/src/components/Playground/Components/Drawers/TraceDrawer/index.tsx +++ b/web/oss/src/components/Playground/Components/Drawers/TraceDrawer/index.tsx @@ -1,63 +1,126 @@ -import {cloneElement, isValidElement, useCallback, useMemo, useState} from "react" +import {cloneElement, isValidElement, useCallback, useMemo} from "react" import {TreeView} from "@phosphor-icons/react" import {Button} from "antd" import clsx from "clsx" -import {getDefaultStore, useSetAtom} from "jotai" +import {useSetAtom} from "jotai" -import {openTraceDrawerAtom} from "./store/traceDrawerStore" -import {TraceDrawerButtonProps} from "./types" -import {fetchPreviewTrace} from "@/oss/services/tracing/api" -import { - transformTracesResponseToTree, - transformTracingResponse, -} from "@/oss/services/tracing/lib/helpers" +import {useQueryParamState} from "@/oss/state/appState" -const store = getDefaultStore() +import {openTraceDrawerAtom, setTraceDrawerActiveSpanAtom} from "./store/traceDrawerStore" +import {TraceDrawerButtonProps} from "./types" const TraceDrawerButton = ({ label, icon = true, children, result, - navigationIds, ...props }: TraceDrawerButtonProps) => { - const openDrawer = useSetAtom(openTraceDrawerAtom, {store}) - const [loading, setLoading] = useState(false) + const setActiveSpan = useSetAtom(setTraceDrawerActiveSpanAtom) + const openTraceDrawer = useSetAtom(openTraceDrawerAtom) + const [, setTraceQueryParam] = useQueryParamState("trace") + const [, setSpanQueryParam] = useQueryParamState("span") - const traceId = useMemo( - () => result?.response?.trace_id || result?.metadata?.rawError?.detail?.trace_id, - [result], - ) + const traceId = useMemo(() => { + const directTraceId = + result?.response?.trace_id || result?.metadata?.rawError?.detail?.trace_id + if (directTraceId) return directTraceId + + const responseTrace = (result as any)?.response?.trace + if (responseTrace?.trace_id) return responseTrace.trace_id + + const nodes = (result as any)?.response?.tree?.nodes + const extractTraceId = (value: any): string | null => { + if (!value) return null + if (Array.isArray(value)) { + for (const entry of value) { + const found = extractTraceId(entry) + if (found) return found + } + return null + } + if (typeof value === "object") { + return ( + value.trace_id || + value.span_id || + value?.node?.trace_id || + value?.node?.id || + null + ) + } + return null + } + + if (!nodes) return undefined + if (Array.isArray(nodes)) { + return extractTraceId(nodes) + } + + for (const value of Object.values(nodes)) { + const found = extractTraceId(value) + if (found) return found + } + + return undefined + }, [result]) + + const handleOpen = useCallback(() => { + if (!traceId) return + + const deriveActiveSpan = (): string | null => { + const nodes = (result as any)?.response?.tree?.nodes + if (!nodes) return null + + const pickSpan = (node: any) => node?.span_id || node?.trace_id || null - const handleOpen = useCallback(async () => { - try { - setLoading(true) - const data = await fetchPreviewTrace(traceId) - const transformedData = transformTracingResponse(transformTracesResponseToTree(data)) - - const first = Array.isArray(transformedData) ? transformedData[0] : undefined - // Support both flattened and nested shapes - const nodeId = (first as any)?.span_id || (first as any)?.trace_id || undefined - const payload = - navigationIds && navigationIds.length > 1 - ? { - ...({...result, traces: transformedData} as any), - navigationIds, - activeTraceId: nodeId, - } - : {...({...result, traces: transformedData} as any), activeTraceId: nodeId} - - openDrawer({result: payload}) - } catch (error) { - console.error("TraceDrawerButton error: ", error) - } finally { - setLoading(false) + if (Array.isArray(nodes)) { + return pickSpan(nodes[0]) + } + + const first = Object.values(nodes)[0] + if (Array.isArray(first)) { + return pickSpan(first[0]) + } + + return pickSpan(first) } - }, [openDrawer, result, navigationIds, traceId]) - const hasTrace = !!result?.response?.tree?.nodes?.length || !!result?.error + const nextActiveSpan = deriveActiveSpan() + openTraceDrawer({traceId, activeSpanId: nextActiveSpan}) + setActiveSpan(nextActiveSpan) + setTraceQueryParam(traceId, {shallow: true}) + if (nextActiveSpan) { + setSpanQueryParam(nextActiveSpan, {shallow: true}) + } else { + setSpanQueryParam(undefined, {shallow: true}) + } + }, [traceId, result, openTraceDrawer, setActiveSpan, setTraceQueryParam, setSpanQueryParam]) + + const hasTrace = useMemo(() => { + const nodes = (result as any)?.response?.tree?.nodes + const hasNodes = (() => { + if (!nodes) return false + if (Array.isArray(nodes)) { + return nodes.length > 0 + } + if (typeof nodes === "object") { + return Object.values(nodes).some((value) => { + if (!value) return false + if (Array.isArray(value)) { + return value.length > 0 + } + if (typeof value === "object") { + return Object.keys(value).length > 0 + } + return false + }) + } + return false + })() + + return hasNodes || Boolean(result?.response?.trace) || Boolean(result?.error) + }, [result]) return ( <> @@ -66,7 +129,6 @@ const TraceDrawerButton = ({ children as React.ReactElement<{onClick: () => void; loading?: boolean}>, { onClick: handleOpen, - loading, }, ) ) : ( @@ -74,7 +136,6 @@ const TraceDrawerButton = ({ type="text" icon={icon && } onClick={handleOpen} - loading={loading} {...props} disabled={!hasTrace || !traceId || !!result?.error} className={clsx([props.className])} diff --git a/web/oss/src/components/Playground/Components/Drawers/TraceDrawer/store/traceDrawerStore.ts b/web/oss/src/components/Playground/Components/Drawers/TraceDrawer/store/traceDrawerStore.ts index 5a58c0d85b..eb5017aa80 100644 --- a/web/oss/src/components/Playground/Components/Drawers/TraceDrawer/store/traceDrawerStore.ts +++ b/web/oss/src/components/Playground/Components/Drawers/TraceDrawer/store/traceDrawerStore.ts @@ -1,36 +1,76 @@ import {atom} from "jotai" import {atomWithImmer} from "jotai-immer" +import {atomWithQuery} from "jotai-tanstack-query" + +import {fetchPreviewTrace} from "@/oss/services/tracing/api" -// The shape of the drawer state export interface TraceDrawerState { open: boolean - result: any // TODO: Replace 'any' with the correct type if available + traceId: string | null + activeSpanId: string | null +} + +export const initialTraceDrawerState: TraceDrawerState = { + open: false, + traceId: null, + activeSpanId: null, } -// Main atom for the drawer state -export const traceDrawerAtom = atomWithImmer({open: false, result: null}) +export const traceDrawerAtom = atomWithImmer(initialTraceDrawerState) -// Optional: selectors and reset atom (if you want) export const isDrawerOpenAtom = atom((get) => get(traceDrawerAtom).open) -export const drawerResultAtom = atom((get) => get(traceDrawerAtom).result) -export const resetTraceDrawerAtom = atom(null, (_get, set) => - set(traceDrawerAtom, (draft) => { - draft.open = false - draft.result = null - }), -) +export const traceDrawerTraceIdAtom = atom((get) => get(traceDrawerAtom).traceId) +export const traceDrawerActiveSpanIdAtom = atom((get) => get(traceDrawerAtom).activeSpanId) + +export const resetTraceDrawerAtom = atom(null, (_get, set) => { + set(traceDrawerAtom, initialTraceDrawerState) +}) -// Close action: only toggles visibility, preserves existing trace result export const closeTraceDrawerAtom = atom(null, (_get, set) => { set(traceDrawerAtom, (draft) => { draft.open = false }) }) -// Optional: open/update helpers using immer -export const openTraceDrawerAtom = atom(null, (_get, set, payload: {result: any}) => { +export const openTraceDrawerAtom = atom( + null, + (_get, set, payload: {traceId: string; activeSpanId?: string | null}) => { + set(traceDrawerAtom, (draft) => { + draft.open = true + draft.traceId = payload.traceId + draft.activeSpanId = payload.activeSpanId ?? null + }) + }, +) + +export const setTraceDrawerActiveSpanAtom = atom(null, (_get, set, activeSpanId: string | null) => { set(traceDrawerAtom, (draft) => { - draft.open = true - draft.result = payload?.result ?? draft.result + draft.activeSpanId = activeSpanId }) }) + +export const setTraceDrawerTraceAtom = atom( + null, + (_get, set, payload: {traceId: string; activeSpanId?: string | null}) => { + set(traceDrawerAtom, (draft) => { + draft.traceId = payload.traceId + if (payload.activeSpanId !== undefined) { + draft.activeSpanId = payload.activeSpanId + } + }) + }, +) + +export const traceDrawerQueryAtom = atomWithQuery((get) => { + const traceId = get(traceDrawerTraceIdAtom) + + return { + queryKey: ["trace-drawer", traceId ?? "none"], + enabled: Boolean(traceId), + refetchOnWindowFocus: false, + queryFn: async () => { + if (!traceId) return null + return fetchPreviewTrace(traceId) + }, + } +}) diff --git a/web/oss/src/components/Playground/Components/Drawers/TraceDrawer/types.d.ts b/web/oss/src/components/Playground/Components/Drawers/TraceDrawer/types.d.ts index 3728a36a85..f75472f718 100644 --- a/web/oss/src/components/Playground/Components/Drawers/TraceDrawer/types.d.ts +++ b/web/oss/src/components/Playground/Components/Drawers/TraceDrawer/types.d.ts @@ -9,5 +9,4 @@ export interface TraceDrawerButtonProps extends ButtonProps { icon?: boolean children?: ReactNode result: TestResult | null | undefined - navigationIds?: string[] } diff --git a/web/oss/src/components/Playground/Components/MainLayout/index.tsx b/web/oss/src/components/Playground/Components/MainLayout/index.tsx index d3a6e93974..a748d2a69d 100644 --- a/web/oss/src/components/Playground/Components/MainLayout/index.tsx +++ b/web/oss/src/components/Playground/Components/MainLayout/index.tsx @@ -41,7 +41,7 @@ const GenerationComparisonRenderer = memo(() => { const sourceIds = isChat ? turnIds : rowIds - const list = (sourceIds || []).map((rowId, rowIndex) => ( + return (sourceIds || []).map((rowId, rowIndex) => (
{
)) - - return <>{list} }) const PlaygroundMainView = ({className, isLoading = false, ...divProps}: MainLayoutProps) => { @@ -229,10 +227,4 @@ const PlaygroundMainView = ({className, isLoading = false, ...divProps}: MainLay ) } -// PERFORMANCE OPTIMIZATION: Memo with custom comparison -// Only re-render if props actually change (className, isLoading, etc.) -export default memo(PlaygroundMainView, (prevProps, nextProps) => { - return ( - prevProps.className === nextProps.className && prevProps.isLoading === nextProps.isLoading - ) -}) +export default memo(PlaygroundMainView) diff --git a/web/oss/src/components/Playground/Components/PlaygroundGenerations/assets/GenerationResultUtils/index.tsx b/web/oss/src/components/Playground/Components/PlaygroundGenerations/assets/GenerationResultUtils/index.tsx index bb3af882c5..9805def9b7 100644 --- a/web/oss/src/components/Playground/Components/PlaygroundGenerations/assets/GenerationResultUtils/index.tsx +++ b/web/oss/src/components/Playground/Components/PlaygroundGenerations/assets/GenerationResultUtils/index.tsx @@ -3,10 +3,8 @@ import {memo, useMemo} from "react" import {Timer, PlusCircle} from "@phosphor-icons/react" import {Tag, Space} from "antd" import clsx from "clsx" -import {getDefaultStore, useAtomValue} from "jotai" import StatusRenderer from "@/oss/components/pages/observability/components/StatusRenderer" -import {generationTraceIdsAtom} from "@/oss/components/Playground/state/atoms/generationProperties" import ResultTag from "@/oss/components/ResultTag/ResultTag" import {formatCurrency, formatLatency, formatTokenUsage} from "@/oss/lib/helpers/formatters" import {StatusCode} from "@/oss/services/tracing/types" @@ -15,8 +13,6 @@ import TraceDrawerButton from "../../../Drawers/TraceDrawer" import {GenerationResultUtilsProps} from "./types" -const store = getDefaultStore() - const GenerationResultUtils: React.FC = ({ className, showStatus = true, @@ -43,16 +39,9 @@ const GenerationResultUtils: React.FC = ({ ) const formattedCosts = useMemo(() => formatCurrency(costs), [costs]) - const navigationIds = useAtomValue(generationTraceIdsAtom, {store}) - return (
- + {showStatus && } diff --git a/web/oss/src/components/pages/observability/ObservabilityDashboard.tsx b/web/oss/src/components/pages/observability/ObservabilityDashboard.tsx index b3d5635b9f..87ad312f7a 100644 --- a/web/oss/src/components/pages/observability/ObservabilityDashboard.tsx +++ b/web/oss/src/components/pages/observability/ObservabilityDashboard.tsx @@ -2,24 +2,19 @@ import {useCallback, useEffect, useMemo, useState} from "react" import {Button, Table, TableColumnType, Typography} from "antd" import {ColumnsType} from "antd/es/table" -import {useAtomValue} from "jotai" import {useSetAtom} from "jotai" import dynamic from "next/dynamic" -import {TraceDrawer} from "@/oss/components/Playground/Components/Drawers/TraceDrawer" -import { - isDrawerOpenAtom, - openTraceDrawerAtom, -} from "@/oss/components/Playground/Components/Drawers/TraceDrawer/store/traceDrawerStore" -import {useQueryParam} from "@/oss/hooks/useQuery" +import {setTraceDrawerActiveSpanAtom} from "@/oss/components/Playground/Components/Drawers/TraceDrawer/store/traceDrawerStore" import {TracesWithAnnotations} from "@/oss/services/observability/types" +import {TraceSpanNode} from "@/oss/services/tracing/types" +import {useQueryParamState} from "@/oss/state/appState" import {useObservability} from "@/oss/state/newObservability" import {filterColumns} from "../../Filters/EditColumns/assets/helper" import ResizableTitle from "../../ResizableTitle" import {getObservabilityColumns} from "./assets/getObservabilityColumns" -import {TraceSpanNode} from "@/oss/services/tracing/types" const ObservabilityHeader = dynamic(() => import("./assets/ObservabilityHeader"), {ssr: false}) const EmptyObservability = dynamic(() => import("./assets/EmptyObservability"), {ssr: false}) @@ -34,26 +29,28 @@ const ObservabilityDashboard = () => { selectedTraceId, setSelectedTraceId, editColumns, - // setEditColumns, selectedRowKeys, setSelectedRowKeys, testsetDrawerData, setTestsetDrawerData, - // isAnnotationsSectionOpen, - // setIsAnnotationsSectionOpen, selectedNode, setSelectedNode, activeTrace, - // activeTraceIndex, - // selectedItem, fetchMoreTraces, hasMoreTraces, isFetchingMore, } = useObservability() - const openTraceDrawer = useSetAtom(openTraceDrawerAtom) - const isTraceDrawerOpen = useAtomValue(isDrawerOpenAtom) + const setTraceDrawerActiveSpan = useSetAtom(setTraceDrawerActiveSpanAtom) + + const [traceParamValue, setTraceParam] = useQueryParamState("trace") + const traceParam = Array.isArray(traceParamValue) + ? (traceParamValue[0] ?? "") + : ((traceParamValue as string | undefined) ?? "") - const [traceParam, setTraceParam] = useQueryParam("trace", "") + const [spanParamValue, setSpanParam] = useQueryParamState("span") + const spanParam = Array.isArray(spanParamValue) + ? (spanParamValue[0] ?? "") + : ((spanParamValue as string | undefined) ?? "") const evaluatorSlugs = useMemo(() => { const slugs = new Set() @@ -95,28 +92,23 @@ const ObservabilityDashboard = () => { if (traceParam && traceParam !== selectedTraceId) { setSelectedTraceId(traceParam) } - }, [traceParam]) + if (!traceParam) { + setTraceDrawerActiveSpan(null) + setSpanParam(undefined, {shallow: true}) + } + }, [traceParam, selectedTraceId, setSelectedTraceId, setTraceDrawerActiveSpan, setSpanParam]) useEffect(() => { - if (selectedTraceId !== traceParam) { - setTraceParam(selectedTraceId) + if (spanParam) { + setTraceDrawerActiveSpan(spanParam) + setSelectedNode(spanParam) } - }, [selectedTraceId]) + }, [spanParam, setTraceDrawerActiveSpan, setSelectedNode]) - // Open global TraceDrawer when a trace is selected useEffect(() => { - if (selectedTraceId && traces && traces.length > 0) { - const navigationIds = (traces || []).map((t: any) => t?.node?.id).filter(Boolean) - const activeNodeId = selectedNode || activeTrace?.span_id || navigationIds[0] || "" - openTraceDrawer({ - result: { - traces, - navigationIds, - activeTraceId: activeNodeId, - }, - }) - } - }, [selectedTraceId, traces, selectedNode, activeTrace]) + if (!selectedTraceId || selectedTraceId === traceParam) return + setTraceParam(selectedTraceId, {shallow: true}) + }, [selectedTraceId, traceParam, setTraceParam]) useEffect(() => { if (!selectedNode) { @@ -124,13 +116,6 @@ const ObservabilityDashboard = () => { } }, [activeTrace, selectedNode]) - useEffect(() => { - if (!isTraceDrawerOpen && selectedTraceId) { - setSelectedTraceId("") - setTraceParam("") - } - }, [isTraceDrawerOpen, selectedTraceId, setSelectedTraceId, setTraceParam]) - useEffect(() => { const interval = setInterval(fetchTraces, 300000) @@ -199,18 +184,38 @@ const ObservabilityDashboard = () => { onRow={(record) => ({ onClick: () => { setSelectedNode(record.span_id) - const targetId = traceTabs === "span" ? record.span_id : record.trace_id - setSelectedTraceId(targetId) - // Open global Trace Drawer immediately with current traces payload - openTraceDrawer({ - result: { - traces, - navigationIds: (traces || []) - .map((t: any) => t?.span_id) - .filter(Boolean), - activeTraceId: record.span_id, - }, - }) + const isSpanView = traceTabs === "span" + + const targetTraceId = + record.trace_id || + (record as any)?.invocationIds?.trace_id || + (record as any)?.node?.trace_id || + (record as any)?.root?.id || + (record as any)?.traceId || + (record as any)?.trace?.id || + record.span_id || + null + + const targetSpanId = isSpanView + ? record.span_id || null + : record.span_id || null + + if (!targetTraceId) { + console.warn( + "TraceDrawer: unable to determine trace id for record", + record, + ) + return + } + + setSelectedTraceId(targetTraceId) + setTraceDrawerActiveSpan(targetSpanId) + setTraceParam(targetTraceId, {shallow: true}) + if (targetSpanId) { + setSpanParam(targetSpanId, {shallow: true}) + } else { + setSpanParam(undefined, {shallow: true}) + } }, })} components={{ @@ -244,8 +249,6 @@ const ObservabilityDashboard = () => { setSelectedRowKeys([]) }} /> - -
) } diff --git a/web/oss/src/components/pages/observability/drawer/TraceContent/index.tsx b/web/oss/src/components/pages/observability/drawer/TraceContent/index.tsx index 72673152bb..91b4667b9b 100644 --- a/web/oss/src/components/pages/observability/drawer/TraceContent/index.tsx +++ b/web/oss/src/components/pages/observability/drawer/TraceContent/index.tsx @@ -1,14 +1,13 @@ import {useEffect, useMemo, useState} from "react" import {Database} from "@phosphor-icons/react" -import {Button, Divider, Space, Tabs, TabsProps, Tag, Typography, Tooltip} from "antd" +import {Button, Divider, Space, Tabs, TabsProps, Tag, Typography, Tooltip, Skeleton} from "antd" import clsx from "clsx" import {getDefaultStore, useAtomValue, useSetAtom} from "jotai" import dynamic from "next/dynamic" import { isDrawerOpenAtom, - drawerResultAtom, resetTraceDrawerAtom, } from "@/oss/components/Playground/Components/Drawers/TraceDrawer/store/traceDrawerStore" import TooltipWithCopyAction from "@/oss/components/TooltipWithCopyAction" @@ -16,7 +15,6 @@ import {KeyValuePair} from "@/oss/lib/Types" import AccordionTreePanel from "../../components/AccordionTreePanel" import AnnotateDrawerButton from "../AnnotateDrawer/assets/AnnotateDrawerButton" -import useTraceDrawer from "../hooks/useTraceDrawer" import {useStyles} from "./assets/styles" import AnnotationTabItem from "./components/AnnotationTabItem" @@ -28,18 +26,23 @@ const TestsetDrawer = dynamic(() => import("../TestsetDrawer/TestsetDrawer"), {s interface TraceContentProps { activeTrace?: TraceSpanNode - activeTraceId?: string + traceResponse?: any + error?: any + isLoading?: boolean } const store = getDefaultStore() -const TraceContent = ({activeTrace: active, activeTraceId}: TraceContentProps) => { - const {getTraceById} = useTraceDrawer() - const drawerResult = useAtomValue(drawerResultAtom) +const TraceContent = ({ + activeTrace: active, + traceResponse, + error, + isLoading, +}: TraceContentProps) => { const resetDrawer = useSetAtom(resetTraceDrawerAtom) - const activeTrace = active || getTraceById(activeTraceId) + const activeTrace = active const activeTraceData = useAtomValue(spanAgDataAtomFamily(activeTrace)) - const {key, children, spans, ...filteredTrace} = activeTrace || {} + const {key, children, spans, invocationIds, ...filteredTrace} = activeTrace || {} const classes = useStyles() const [tab, setTab] = useState("overview") const [isTestsetDrawerOpen, setIsTestsetDrawerOpen] = useState(false) @@ -54,11 +57,28 @@ const TraceContent = ({activeTrace: active, activeTraceId}: TraceContentProps) = ] }, [activeTrace?.key, activeTraceData]) + const loadingContent = ( +
+ +
+ ) + const items: TabsProps["items"] = useMemo(() => { + if (isLoading && !activeTrace) { + return [ + { + key: "loading", + label: "Overview", + children: loadingContent, + }, + ] + } + // When activeTrace is missing (e.g., failed generation), show just Raw Data/Error if (!activeTrace) { - const errorPayload = drawerResult?.error - const rawPayload = drawerResult?.response || (errorPayload ? {error: errorPayload} : {}) + const errorPayload = error + const rawPayload = + traceResponse?.response || (errorPayload ? {error: errorPayload} : {}) return [ { key: "raw_data", @@ -99,7 +119,7 @@ const TraceContent = ({activeTrace: active, activeTraceId}: TraceContentProps) = children: , }, ] - }, [activeTrace, drawerResult]) + }, [activeTrace, filteredTrace, isLoading, traceResponse, error]) // Ensure active tab exists in items; if not, switch to first tab const itemKeys = useMemo(() => (items || []).map((it) => String(it?.key)), [items]) @@ -125,13 +145,13 @@ const TraceContent = ({activeTrace: active, activeTraceId}: TraceContentProps) =
- {activeTrace?.span_name || (drawerResult?.error ? "Error" : "")} + {activeTrace?.span_name || (error ? "Error" : "")} void setSelectedNode?: (val: string) => void + setTraceParam: ( + value: QueryValue | ((prev: QueryValue) => QueryValue), + options?: {shallow?: boolean; preserveHash?: boolean}, + ) => void + setSpanParam: ( + value: QueryValue | ((prev: QueryValue) => QueryValue), + options?: {shallow?: boolean; preserveHash?: boolean}, + ) => void + setTraceDrawerTrace: (payload: {traceId: string; activeSpanId?: string | null}) => void activeTraceIndex?: number setIsAnnotationsSectionOpen?: Dispatch> isAnnotationsSectionOpen?: boolean diff --git a/web/oss/src/components/pages/observability/drawer/TraceHeader/index.tsx b/web/oss/src/components/pages/observability/drawer/TraceHeader/index.tsx index d3c320ef1f..2d68f60e1c 100644 --- a/web/oss/src/components/pages/observability/drawer/TraceHeader/index.tsx +++ b/web/oss/src/components/pages/observability/drawer/TraceHeader/index.tsx @@ -1,26 +1,97 @@ -import {useCallback, useState} from "react" +import {useCallback, useEffect, useMemo, useState} from "react" import {DeleteOutlined} from "@ant-design/icons" import {CaretDown, CaretUp, SidebarSimple} from "@phosphor-icons/react" import {Button, Space, Tag, Typography} from "antd" +import {useAtomValue} from "jotai" import TooltipWithCopyAction from "@/oss/components/TooltipWithCopyAction" +import {fetchAllPreviewTraces} from "@/oss/services/tracing/api" +import { + isSpansResponse, + isTracesResponse, + transformTracesResponseToTree, + transformTracingResponse, +} from "@/oss/services/tracing/lib/helpers" +import {TraceSpanNode} from "@/oss/services/tracing/types" +import {selectedAppIdAtom} from "@/oss/state/app/selectors/app" import {useObservability} from "@/oss/state/newObservability" +import buildTraceQueryParams from "@/oss/state/newObservability/utils/buildTraceQueryParams" import DeleteTraceModal from "../../components/DeleteTraceModal" -import useTraceDrawer from "../hooks/useTraceDrawer" import {useStyles} from "./assets/styles" import {TraceHeaderProps} from "./assets/types" +const getTraceIdFromNode = (node: any): string | null => { + if (!node) return null + return ( + node.trace_id || + node.invocationIds?.trace_id || + node.node?.trace_id || + node.root?.id || + null + ) +} + +const getSpanIdFromNode = (node: any): string | null => { + if (!node) return null + return node.span_id || node.invocationIds?.span_id || node.node?.span_id || null +} + +const getNodeTimestamp = (node: any): string | number | null => { + if (!node) return null + return ( + node.start_time || + node.startTime || + node.timestamp || + node.created_at || + node.createdAt || + node.node?.start_time || + node.node?.timestamp || + node.node?.created_at || + null + ) +} + +const toISOString = (value: string | number | Date | null | undefined): string | null => { + if (value === null || value === undefined) return null + let date: Date + if (value instanceof Date) { + date = value + } else if (typeof value === "number") { + const ms = value < 1e12 ? value * 1000 : value + date = new Date(ms) + } else { + date = new Date(value) + } + if (Number.isNaN(date.getTime())) return null + return date.toISOString() +} + +type NavSource = "table" | "remote" + +interface NavState { + candidate: TraceSpanNode | null + loading: boolean + source: NavSource | null +} + const TraceHeader = ({ activeTrace: propActiveTrace, - traces: propTraces, + traces: drawerTraces, activeTraceId, - navigationIds, + traceId, + traceTabs, + filters, + sort, + limit, setSelectedTraceId, setSelectedNode, - activeTraceIndex, + setTraceParam, + setSpanParam, + setTraceDrawerTrace, + activeTraceIndex: _activeTraceIndex, setIsAnnotationsSectionOpen, isAnnotationsSectionOpen, setSelected, @@ -28,94 +99,372 @@ const TraceHeader = ({ const classes = useStyles() const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false) - const {fetchMoreTraces, hasMoreTraces} = useObservability() - - // Derive from drawer hook when only id is given - const {traces: hookTraces, getTraceById} = useTraceDrawer() - - const traces = ((propTraces as any) || hookTraces) ?? [] - const activeTrace = - (propActiveTrace as any) || (activeTraceId ? getTraceById(activeTraceId) : undefined) - - // Prefer explicit navigation list when provided (including empty array) - const navIds = navigationIds - const resolvedIndex = navIds - ? Math.max(0, navIds.indexOf(activeTraceId || "")) - : typeof activeTraceIndex === "number" - ? activeTraceIndex - : Math.max( - 0, - traces.findIndex((t: any) => t?.span_id === activeTrace?.span_id), - ) - - const isFirstItem = navIds - ? resolvedIndex <= 0 || (navIds?.length || 0) <= 1 - : resolvedIndex === 0 - const isLastItem = navIds - ? resolvedIndex >= (navIds?.length || 1) - 1 || (navIds?.length || 0) <= 1 - : resolvedIndex === traces.length - 1 && !hasMoreTraces + const {traces: tableTracesRaw, hasMoreTraces, fetchMoreTraces} = useObservability() + const appId = useAtomValue(selectedAppIdAtom) - const handleNextTrace = useCallback(async () => { - if (resolvedIndex === undefined) return - - if (navIds) { - if ((navIds?.length || 0) <= 1) return - // Use explicit list when provided - if (resolvedIndex >= navIds.length - 1) return - const id = navIds[resolvedIndex + 1] - setSelectedTraceId(id) - setSelectedNode?.(id) - setSelected?.(id) - return - } + const tableTraces = useMemo(() => tableTracesRaw as TraceSpanNode[], [tableTracesRaw]) + + const focusMode: "trace" | "span" = traceTabs === "trace" ? "trace" : "span" + + const activeTrace = useMemo(() => { + if (propActiveTrace) return propActiveTrace + if (!Array.isArray(drawerTraces)) return undefined + return (drawerTraces as TraceSpanNode[]).find( + (node) => getSpanIdFromNode(node) === activeTraceId, + ) + }, [propActiveTrace, drawerTraces, activeTraceId]) + + const activeSpanKey = useMemo(() => { + return getSpanIdFromNode(activeTrace) || activeTraceId || null + }, [activeTrace, activeTraceId]) + + const activeTraceKey = useMemo(() => { + return getTraceIdFromNode(activeTrace) || traceId || null + }, [activeTrace, traceId]) + + const activeFocusKey = focusMode === "span" ? activeSpanKey : activeTraceKey + + const activeTimestampIso = useMemo( + () => toISOString(getNodeTimestamp(activeTrace)), + [activeTrace], + ) + + const filtersKey = useMemo(() => JSON.stringify(filters ?? []), [filters]) + const sortKey = useMemo(() => JSON.stringify(sort ?? {}), [sort]) + + const baseParams = useMemo( + () => + buildTraceQueryParams({ + focus: traceTabs, + filters, + sort, + limit, + }), + [traceTabs, filtersKey, sortKey, limit], + ) + + const requestSize = useMemo(() => { + if (!limit || Number.isNaN(limit)) return 5 + return Math.max(1, Math.min(limit, 10)) + }, [limit]) + + const tableSpanKeySet = useMemo(() => { + const set = new Set() + tableTraces.forEach((item) => { + const key = getSpanIdFromNode(item) + if (key) set.add(key) + }) + return set + }, [tableTraces]) + + const tableTraceKeySet = useMemo(() => { + const set = new Set() + tableTraces.forEach((item) => { + const key = getTraceIdFromNode(item) + if (key) set.add(key) + }) + return set + }, [tableTraces]) + + const tableIndex = useMemo(() => { + if (!activeFocusKey) return -1 + return tableTraces.findIndex((item) => { + const key = focusMode === "span" ? getSpanIdFromNode(item) : getTraceIdFromNode(item) + return key === activeFocusKey + }) + }, [tableTraces, focusMode, activeFocusKey]) + + const prevFromTable = + tableIndex > 0 && tableIndex < tableTraces.length + ? (tableTraces[tableIndex - 1] as TraceSpanNode) + : null + + const nextFromTable = + tableIndex >= 0 && tableIndex < tableTraces.length - 1 + ? (tableTraces[tableIndex + 1] as TraceSpanNode) + : null + + const [prevNav, setPrevNav] = useState({ + candidate: null, + loading: false, + source: null, + }) + const [nextNav, setNextNav] = useState({ + candidate: null, + loading: false, + source: null, + }) + + const fetchRelativeTrace = useCallback( + async (direction: "prev" | "next"): Promise => { + if (!appId || !activeTimestampIso || !activeFocusKey) { + console.debug("[TraceNav] skip fetch – missing context", { + direction, + appId, + activeTimestampIso, + activeFocusKey, + }) + return null + } + + console.debug("[TraceNav] fetchRelative:start", { + direction, + focusMode, + activeTimestampIso, + activeFocusKey, + }) + + const params: Record = {...baseParams, size: requestSize} + + if (direction === "next") { + params.newest = activeTimestampIso + if (baseParams.oldest) params.oldest = baseParams.oldest + } else { + params.oldest = activeTimestampIso + if (baseParams.newest) params.newest = baseParams.newest + } + + try { + const response = await fetchAllPreviewTraces(params, appId) + + console.debug("[TraceNav] fetchRelative:response", { + direction, + hasTraces: Boolean((response as any)?.traces), + hasSpans: Boolean((response as any)?.spans), + }) + let candidates: TraceSpanNode[] = [] + + if (isTracesResponse(response)) { + candidates = transformTracingResponse(transformTracesResponseToTree(response)) + } else if (isSpansResponse(response)) { + candidates = transformTracingResponse(response.spans) + } else if (Array.isArray((response as any)?.spans)) { + candidates = transformTracingResponse((response as any).spans) + } + + if (!candidates.length) return null + + const focusKeyGetter = + focusMode === "trace" ? getTraceIdFromNode : getSpanIdFromNode - // Check if we're at the last item of the current page - if (resolvedIndex === traces.length - 1) { - if (hasMoreTraces) { - try { - const newTraces = await fetchMoreTraces() - const firstTrace = newTraces[0] - if (firstTrace) { - const id = firstTrace.span_id - setSelectedTraceId(id) - setSelectedNode?.(id) - setSelected?.(id) + const filtered = candidates.filter((item) => { + const candidateKey = focusKeyGetter(item) + if (!candidateKey || candidateKey === activeFocusKey) return false + if (focusMode === "trace") { + if (tableTraceKeySet.has(candidateKey)) return false + } else if (tableSpanKeySet.has(candidateKey)) { + return false } - } catch (error) { - console.error("Error fetching more traces:", error) + return true + }) + + console.debug("[TraceNav] fetchRelative:filtered", { + direction, + total: candidates.length, + filtered: filtered.length, + firstSpan: filtered[0]?.span_id, + firstTrace: filtered[0]?.trace_id, + }) + + if (!filtered.length) { + console.debug("[TraceNav] no filtered candidates", {direction}) + return null } + + return filtered[0] + } catch (error) { + console.error("Trace navigation fetch failed", error) + return null } - } else { - const nextTrace = traces[resolvedIndex + 1] - const id = nextTrace.span_id - setSelectedTraceId(id) - setSelectedNode?.(id) - setSelected?.(id) + }, + [ + appId, + activeTimestampIso, + activeFocusKey, + baseParams, + focusMode, + requestSize, + tableSpanKeySet, + tableTraceKeySet, + ], + ) + + useEffect(() => { + if (prevFromTable) { + console.debug("[TraceNav] prev candidate from table", { + spanId: getSpanIdFromNode(prevFromTable), + traceId: getTraceIdFromNode(prevFromTable), + }) + setPrevNav({candidate: prevFromTable, loading: false, source: "table"}) + return + } + + if (!activeTimestampIso || !appId || !activeFocusKey) { + console.debug("[TraceNav] prev disabled – missing context") + setPrevNav({candidate: null, loading: false, source: null}) + return + } + + let cancelled = false + setPrevNav({candidate: null, loading: true, source: null}) + + fetchRelativeTrace("prev").then((result) => { + if (cancelled) return + console.debug("[TraceNav] prev fetch result", { + spanId: getSpanIdFromNode(result), + traceId: getTraceIdFromNode(result), + }) + setPrevNav({candidate: result, loading: false, source: result ? "remote" : null}) + }) + + return () => { + cancelled = true + } + }, [prevFromTable, fetchRelativeTrace, activeTimestampIso, appId, activeFocusKey]) + + useEffect(() => { + if (nextFromTable) { + console.debug("[TraceNav] next candidate from table", { + spanId: getSpanIdFromNode(nextFromTable), + traceId: getTraceIdFromNode(nextFromTable), + }) + setNextNav({candidate: nextFromTable, loading: false, source: "table"}) + return + } + + if (!activeTimestampIso || !appId || !activeFocusKey) { + console.debug("[TraceNav] next disabled – missing context") + setNextNav({candidate: null, loading: false, source: null}) + return } - }, [resolvedIndex, navIds, traces, hasMoreTraces, fetchMoreTraces]) - - const handlePrevTrace = useCallback(async () => { - if (resolvedIndex === undefined) return - - if (navIds) { - if ((navIds?.length || 0) <= 1) return - if (resolvedIndex <= 0) return - const id = navIds[resolvedIndex - 1] - setSelectedTraceId(id) - setSelectedNode?.(id) - setSelected?.(id) + + if (tableIndex >= 0 && !hasMoreTraces) { + console.debug("[TraceNav] next disabled – end of results") + setNextNav({candidate: null, loading: false, source: null}) return } - // Check if we're at the first item of the current page - if (resolvedIndex > 0) { - const prevTrace = traces[resolvedIndex - 1] - const id = prevTrace.span_id - setSelectedTraceId(id) - setSelectedNode?.(id) - setSelected?.(id) + let cancelled = false + setNextNav({candidate: null, loading: true, source: null}) + + fetchRelativeTrace("next").then((result) => { + if (cancelled) return + console.debug("[TraceNav] next fetch result", { + spanId: getSpanIdFromNode(result), + traceId: getTraceIdFromNode(result), + }) + setNextNav({candidate: result, loading: false, source: result ? "remote" : null}) + }) + + return () => { + cancelled = true } - }, [resolvedIndex, navIds, traces]) + }, [ + nextFromTable, + fetchRelativeTrace, + activeTimestampIso, + appId, + activeFocusKey, + hasMoreTraces, + tableIndex, + ]) + + const navigateToTarget = useCallback( + (target: TraceSpanNode | null, source: NavSource | null) => { + if (!target) return + + const targetTraceId = getTraceIdFromNode(target) + const targetSpanId = getSpanIdFromNode(target) + + console.debug("[TraceNav] navigate", { + source, + focusMode, + targetTraceId, + targetSpanId, + }) + + setTraceParam(targetTraceId ?? undefined, {shallow: true}) + + if (targetTraceId) { + setTraceDrawerTrace({traceId: targetTraceId, activeSpanId: targetSpanId ?? null}) + } + + if (source === "table") { + const selectionKey = + focusMode === "span" + ? (targetSpanId ?? targetTraceId ?? undefined) + : (targetTraceId ?? targetSpanId ?? undefined) + + if (selectionKey) { + console.debug("[TraceNav] setSelectedTraceId", selectionKey) + setSelectedTraceId(selectionKey) + } + } + + if (targetSpanId) { + console.debug("[TraceNav] setSelectedNode", targetSpanId) + setSelectedNode?.(targetSpanId) + setSelected?.(targetSpanId) + } else { + console.debug("[TraceNav] clear SelectedNode") + setSelectedNode?.("") + setSelected?.("") + } + + if (focusMode === "span") { + console.debug("[TraceNav] setSpanParam", targetSpanId) + setSpanParam(targetSpanId ?? undefined, {shallow: true}) + } else { + console.debug("[TraceNav] clear span param") + setSpanParam(undefined, {shallow: true}) + } + }, + [ + focusMode, + setSelectedTraceId, + setSelectedNode, + setSelected, + setTraceParam, + setSpanParam, + setTraceDrawerTrace, + ], + ) + + const handlePrevTrace = useCallback(() => { + if (prevNav.loading || !prevNav.candidate) return + console.debug("[TraceNav] handlePrev", { + source: prevNav.source, + spanId: getSpanIdFromNode(prevNav.candidate), + traceId: getTraceIdFromNode(prevNav.candidate), + }) + navigateToTarget(prevNav.candidate, prevNav.source) + }, [prevNav, navigateToTarget]) + + const handleNextTrace = useCallback(async () => { + if (nextNav.loading || !nextNav.candidate) return + + console.debug("[TraceNav] handleNext", { + source: nextNav.source, + spanId: getSpanIdFromNode(nextNav.candidate), + traceId: getTraceIdFromNode(nextNav.candidate), + }) + + if (nextNav.source === "remote" && hasMoreTraces) { + try { + console.debug("[TraceNav] fetchMoreTraces before navigating") + await fetchMoreTraces() + } catch (error) { + console.error("Failed to fetch additional traces", error) + } + } + + navigateToTarget(nextNav.candidate, nextNav.source) + }, [nextNav, hasMoreTraces, fetchMoreTraces, navigateToTarget]) + + const isPrevDisabled = prevNav.loading || !prevNav.candidate || !activeFocusKey + const isNextDisabled = nextNav.loading || !nextNav.candidate || !activeFocusKey + + const displayTrace = propActiveTrace || drawerTraces?.[0] return ( <> @@ -125,23 +474,25 @@ const TraceHeader = ({
Trace - # {activeTrace?.trace_id || "-"} + + # {getTraceIdFromNode(displayTrace) || "-"} + @@ -149,7 +500,7 @@ const TraceHeader = ({