Consult the main README for general +information about the project. These are advanced topics that are not +necessary for a basic deployment.
+When you run azd up
or azd deploy
, it
+deploys your application to App Service, and displays the deployed
+endpoint in the console.
If you encounter an error with that deployed app, you can debug the +deployment using the tips below.
+If you see a 500 error upon visiting your app after deployment, +something went wrong during either the deployment or the server start +script.
+We recommend always waiting 10 minutes, to give the server time to +properly startup.
+If you still see a 500 error after 10 minutes:
+ +In the Azure portal, navigate to your App Service.
+Select Deployment Center from the side navigation menu, then +select Logs. You should see a timestamped list of recent +deploys:
+Check whether the status of the most recent deploy is “Success +(Active)” or “Failed”. If it’s success, the deployment logs might still +reveal issues, and if it’s failed, the logs should certainly reveal the +issue.
+Click the commit ID to open the logs for the most recent deploy. +First scroll down to see if any errors or warnings are reported at the +end. This is what you’ll hopefully see if all went well:
+Now scroll back up to find the timestamp with the label “Running oryx +build”. Oryx is the open +source tool that builds apps for App Service, Functions, and other +platforms, across all the supported MS languages. Click the Show +logs link next to that label. That will pop open detailed logs at +the bottom. Scroll down.
+Command: oryx build /tmp/zipdeploy/extracted -o /home/site/wwwroot --platform python --platform-version 3.11 -p virtualenv_name=antenv --log-file /tmp/build-debug.log -i /tmp/8dc28dad0e10acb --compress-destination-dir | tee /tmp/oryx-build.log
+Operation performed by Microsoft Oryx, https://github.com/Microsoft/Oryx
+You can report issues at https://github.com/Microsoft/Oryx/issues
+
+Oryx Version: 0.2.20230508.1, Commit: 7fe2bf39b357dd68572b438a85ca50b5ecfb4592, ReleaseTagName: 20230508.1
+
+Build Operation ID: 7440a33100749a32
+Repository Commit : b09bff8b-da36-4d70-9e2f-c7b9131d85bc
+OS Type : bullseye
+Image Type : githubactions
+
+Detecting platforms...
+Detected following platforms:
+ python: 3.11.7
+Version '3.11.7' of platform 'python' is not installed. Generating script to install it...
+
+Using intermediate directory '/tmp/8dc28dad0e10acb'.
+
+Copying files to the intermediate directory...
+Done in 27 sec(s).
+
+Source directory : /tmp/8dc28dad0e10acb
+Destination directory: /home/site/wwwroot
+
+
+Downloading and extracting 'python' version '3.11.7' to '/tmp/oryx/platforms/python/3.11.7'...
+Detected image debian flavor: bullseye.
+Downloaded in 5 sec(s).
+Verifying checksum...
+Extracting contents...
+performing sha512 checksum for: python...
+Done in 48 sec(s).
+
+image detector file exists, platform is python..
+OS detector file exists, OS is bullseye..
+Python Version: /tmp/oryx/platforms/python/3.11.7/bin/python3.11
+Creating directory for command manifest file if it does not exist
+Removing existing manifest file
+Python Virtual Environment: antenv
+Creating virtual environment...
+Activating virtual environment...
+Running pip install...
+[19:21:31+0000] Collecting aiofiles==23.2.1 (from -r requirements.txt (line 7))
+[19:21:31+0000] Obtaining dependency information for aiofiles==23.2.1 from https://files.pythonhosted.org/packages/c5/19/5af6804c4cc0fed83f47bff6e413a98a36618e7d40185cd36e69737f3b0e/aiofiles-23.2.1-py3-none-any.whl.metadata
+[19:21:31+0000] Downloading aiofiles-23.2.1-py3-none-any.whl.metadata (9.7 kB)
+[19:21:35+0000] Collecting aiohttp==3.9.3 (from -r requirements.txt (line 9))
+[19:21:35+0000] Obtaining dependency information for aiohttp==3.9.3 from https://files.pythonhosted.org/packages/84/bb/74c9f32e1a76fab04b54ed6cd4b0dc4a07bd9dc6f3bb37f630149a9c3068/aiohttp-3.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata
+[19:21:35+0000] Downloading aiohttp-3.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (7.4 kB)
+[19:21:35+0000] Collecting aiosignal==1.3.1 (from -r requirements.txt (line 11))
+[19:21:35+0000] Downloading aiosignal-1.3.1-py3-none-any.whl (7.6 kB)
+[19:21:36+0000] Collecting annotated-types==0.6.0 (from -r requirements.txt (line 13))
+[19:21:36+0000] Obtaining dependency information for annotated-types==0.6.0 from https://files.pythonhosted.org/packages/28/78/d31230046e58c207284c6b2c4e8d96e6d3cb4e52354721b944d3e1ee4aa5/annotated_types-0.6.0-py3-none-any.whl.metadata
+[19:21:36+0000] Downloading annotated_types-0.6.0-py3-none-any.whl.metadata (12 kB)
+[19:21:36+0000] Collecting anyio==4.2.0 (from -r requirements.txt (line 15))
+[19:21:36+0000] Obtaining dependency information for anyio==4.2.0 from https://files.pythonhosted.org/packages/bf/cd/d6d9bb1dadf73e7af02d18225cbd2c93f8552e13130484f1c8dcfece292b/anyio-4.2.0-py3-none-any.whl.metadata
+[19:21:36+0000] Downloading anyio-4.2.0-py3-none-any.whl.metadata (4.6 kB)
+[19:21:36+0000] Collecting asgiref==3.7.2 (from -r requirements.txt (line 19))
+[19:21:36+0000] Obtaining dependency information for asgiref==3.7.2 from https://files.pythonhosted.org/packages/9b/80/b9051a4a07ad231558fcd8ffc89232711b4e618c15cb7a392a17384bbeef/asgiref-3.7.2-py3-none-any.whl.metadata
+[19:21:36+0000] Downloading asgiref-3.7.2-py3-none-any.whl.metadata (9.2 kB)
+[19:21:36+0000] Collecting attrs==23.2.0 (from -r requirements.txt (line 21))
+[19:21:36+0000] Obtaining dependency information for attrs==23.2.0 from https://files.pythonhosted.org/packages/e0/44/827b2a91a5816512fcaf3cc4ebc465ccd5d598c45cefa6703fcf4a79018f/attrs-23.2.0-py3-none-any.whl.metadata
+[19:21:36+0000] Downloading attrs-23.2.0-py3-none-any.whl.metadata (9.5 kB)
+[19:21:36+0000] Collecting azure-common==1.1.28 (from -r requirements.txt (line 23))
+[19:21:36+0000] Downloading azure_common-1.1.28-py2.py3-none-any.whl (14 kB)
+[19:21:36+0000] Collecting azure-core==1.29.7 (from -r requirements.txt (line 27))
+[19:21:36+0000] Obtaining dependency information for azure-core==1.29.7 from https://files.pythonhosted.org/packages/ff/29/dbc7182bc207530c7b5858d59f429158465f878845d64a038afc1aa61e35/azure_core-1.29.7-py3-none-any.whl.metadata
+[19:21:36+0000] Downloading azure_core-1.29.7-py3-none-any.whl.metadata (36 kB)
+[19:21:36+0000] Collecting azure-core-tracing-opentelemetry==1.0.0b11 (from -r requirements.txt (line 37))
+[19:21:36+0000] Obtaining dependency information for azure-core-tracing-opentelemetry==1.0.0b11 from https://files.pythonhosted.org/packages/e6/6e/3ef6dfba8e0faa4692caa6d103c721ccba6ac37a24744848a3a10bb3fe89/azure_core_tracing_opentelemetry-1.0.0b11-py3-none-any.whl.metadata
+[19:21:36+0000] Downloading azure_core_tracing_opentelemetry-1.0.0b11-py3-none-any.whl.metadata (8.5 kB)
+[19:21:37+0000] Collecting azure-identity==1.15.0 (from -r requirements.txt (line 39))
+[19:21:37+0000] Obtaining dependency information for azure-identity==1.15.0 from https://files.pythonhosted.org/packages/30/10/5dbf755b368d10a28d55b06ac1f12512a13e88874a23db82defdea9a8cd9/azure_identity-1.15.0-py3-none-any.whl.metadata
+[19:21:37+0000] Downloading azure_identity-1.15.0-py3-none-any.whl.metadata (75 kB)
+[19:21:37+0000] ━━━━━━━━━━━━━━━━��━━━━━━━━━━━━━━━━━━━━━━━ 75.4/75.4 kB 6.2 MB/s eta 0:00:00
+[19:21:37+0000] Collecting azure-keyvault-secrets==4.7.0 (from -r requirements.txt (line 41))
+[19:21:37+0000] Downloading azure_keyvault_secrets-4.7.0-py3-none-any.whl (348 kB)
+[19:21:37+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 348.6/348.6 kB 19.6 MB/s eta 0:00:00
+[19:21:37+0000] Collecting azure-monitor-opentelemetry==1.2.0 (from -r requirements.txt (line 43))
+[19:21:37+0000] Obtaining dependency information for azure-monitor-opentelemetry==1.2.0 from https://files.pythonhosted.org/packages/66/72/5a6bac11b8f3bd60825f19c144c4c770c46951165f8ee5fc10ab3eaadf59/azure_monitor_opentelemetry-1.2.0-py3-none-any.whl.metadata
+[19:21:37+0000] Downloading azure_monitor_opentelemetry-1.2.0-py3-none-any.whl.metadata (19 kB)
+[19:21:37+0000] Collecting azure-monitor-opentelemetry-exporter==1.0.0b21 (from -r requirements.txt (line 45))
+[19:21:37+0000] Obtaining dependency information for azure-monitor-opentelemetry-exporter==1.0.0b21 from https://files.pythonhosted.org/packages/4a/0d/18cb0da98b49c9a6724f6cae46a7e59b8325cda476bde13b64404a428ae8/azure_monitor_opentelemetry_exporter-1.0.0b21-py2.py3-none-any.whl.metadata
+[19:21:37+0000] Downloading azure_monitor_opentelemetry_exporter-1.0.0b21-py2.py3-none-any.whl.metadata (31 kB)
+[19:21:37+0000] Collecting azure-search-documents==11.6.0b1 (from -r requirements.txt (line 47))
+[19:21:37+0000] Obtaining dependency information for azure-search-documents==11.6.0b1 from https://files.pythonhosted.org/packages/7c/f6/b138d9a252f80db69c052c65410bc972dca375e29c71c472e27d0bae327d/azure_search_documents-11.6.0b1-py3-none-any.whl.metadata
+[19:21:37+0000] Downloading azure_search_documents-11.6.0b1-py3-none-any.whl.metadata (23 kB)
+[19:21:37+0000] Collecting azure-storage-blob==12.19.0 (from -r requirements.txt (line 49))
+[19:21:37+0000] Obtaining dependency information for azure-storage-blob==12.19.0 from https://files.pythonhosted.org/packages/f6/82/24b0d7cf67ea63af86f11092756b8fe2adc1d55323241dc4107f5f5748e2/azure_storage_blob-12.19.0-py3-none-any.whl.metadata
+[19:21:37+0000] Downloading azure_storage_blob-12.19.0-py3-none-any.whl.metadata (26 kB)
+[19:21:37+0000] Collecting blinker==1.7.0 (from -r requirements.txt (line 51))
+[19:21:37+0000] Obtaining dependency information for blinker==1.7.0 from https://files.pythonhosted.org/packages/fa/2a/7f3714cbc6356a0efec525ce7a0613d581072ed6eb53eb7b9754f33db807/blinker-1.7.0-py3-none-any.whl.metadata
+[19:21:37+0000] Downloading blinker-1.7.0-py3-none-any.whl.metadata (1.9 kB)
+[19:21:37+0000] Collecting certifi==2023.11.17 (from -r requirements.txt (line 55))
+[19:21:37+0000] Obtaining dependency information for certifi==2023.11.17 from https://files.pythonhosted.org/packages/64/62/428ef076be88fa93716b576e4a01f919d25968913e817077a386fcbe4f42/certifi-2023.11.17-py3-none-any.whl.metadata
+[19:21:37+0000] Downloading certifi-2023.11.17-py3-none-any.whl.metadata (2.2 kB)
+[19:21:39+0000] Collecting cffi==1.16.0 (from -r requirements.txt (line 61))
+[19:21:39+0000] Obtaining dependency information for cffi==1.16.0 from https://files.pythonhosted.org/packages/9b/89/a31c81e36bbb793581d8bba4406a8aac4ba84b2559301c44eef81f4cf5df/cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata
+[19:21:39+0000] Downloading cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (1.5 kB)
+[19:21:40+0000] Collecting charset-normalizer==3.3.2 (from -r requirements.txt (line 63))
+[19:21:40+0000] Obtaining dependency information for charset-normalizer==3.3.2 from https://files.pythonhosted.org/packages/40/26/f35951c45070edc957ba40a5b1db3cf60a9dbb1b350c2d5bef03e01e61de/charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata
+[19:21:40+0000] Downloading charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (33 kB)
+[19:21:40+0000] Collecting click==8.1.7 (from -r requirements.txt (line 65))
+[19:21:40+0000] Obtaining dependency information for click==8.1.7 from https://files.pythonhosted.org/packages/00/2e/d53fa4befbf2cfa713304affc7ca780ce4fc1fd8710527771b58311a3229/click-8.1.7-py3-none-any.whl.metadata
+[19:21:40+0000] Downloading click-8.1.7-py3-none-any.whl.metadata (3.0 kB)
+[19:21:43+0000] Collecting cryptography==42.0.1 (from -r requirements.txt (line 70))
+[19:21:43+0000] Obtaining dependency information for cryptography==42.0.1 from https://files.pythonhosted.org/packages/f8/46/2776ca9b602f79633fdf69824b5e18c94f2e0c5f09a94fc69e5b0887c14d/cryptography-42.0.1-cp39-abi3-manylinux_2_28_x86_64.whl.metadata
+[19:21:43+0000] Downloading cryptography-42.0.1-cp39-abi3-manylinux_2_28_x86_64.whl.metadata (5.3 kB)
+[19:21:43+0000] Collecting deprecated==1.2.14 (from -r requirements.txt (line 78))
+[19:21:43+0000] Obtaining dependency information for deprecated==1.2.14 from https://files.pythonhosted.org/packages/20/8d/778b7d51b981a96554f29136cd59ca7880bf58094338085bcf2a979a0e6a/Deprecated-1.2.14-py2.py3-none-any.whl.metadata
+[19:21:43+0000] Downloading Deprecated-1.2.14-py2.py3-none-any.whl.metadata (5.4 kB)
+[19:21:43+0000] Collecting distro==1.9.0 (from -r requirements.txt (line 80))
+[19:21:43+0000] Obtaining dependency information for distro==1.9.0 from https://files.pythonhosted.org/packages/12/b3/231ffd4ab1fc9d679809f356cebee130ac7daa00d6d6f3206dd4fd137e9e/distro-1.9.0-py3-none-any.whl.metadata
+[19:21:44+0000] Downloading distro-1.9.0-py3-none-any.whl.metadata (6.8 kB)
+[19:21:44+0000] Collecting ecdsa==0.18.0 (from -r requirements.txt (line 82))
+[19:21:44+0000] Downloading ecdsa-0.18.0-py2.py3-none-any.whl (142 kB)
+[19:21:44+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 142.9/142.9 kB 3.7 MB/s eta 0:00:00
+[19:21:44+0000] Collecting fixedint==0.1.6 (from -r requirements.txt (line 84))
+[19:21:44+0000] Downloading fixedint-0.1.6-py3-none-any.whl (12 kB)
+[19:21:45+0000] Collecting flask==3.0.1 (from -r requirements.txt (line 86))
+[19:21:45+0000] Obtaining dependency information for flask==3.0.1 from https://files.pythonhosted.org/packages/bd/0e/63738e88e981ae57c23bad6c499898314a1110a4141f77d7bd929b552fb4/flask-3.0.1-py3-none-any.whl.metadata
+[19:21:45+0000] Downloading flask-3.0.1-py3-none-any.whl.metadata (3.6 kB)
+[19:21:47+0000] Collecting frozenlist==1.4.1 (from -r requirements.txt (line 88))
+[19:21:47+0000] Obtaining dependency information for frozenlist==1.4.1 from https://files.pythonhosted.org/packages/b3/c9/0bc5ee7e1f5cc7358ab67da0b7dfe60fbd05c254cea5c6108e7d1ae28c63/frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata
+[19:21:47+0000] Downloading frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (12 kB)
+[19:21:47+0000] Collecting h11==0.14.0 (from -r requirements.txt (line 92))
+[19:21:47+0000] Downloading h11-0.14.0-py3-none-any.whl (58 kB)
+[19:21:47+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 58.3/58.3 kB 2.5 MB/s eta 0:00:00
+[19:21:48+0000] Collecting h2==4.1.0 (from -r requirements.txt (line 98))
+[19:21:48+0000] Downloading h2-4.1.0-py3-none-any.whl (57 kB)
+[19:21:48+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 57.5/57.5 kB 627.9 kB/s eta 0:00:00
+[19:21:48+0000] Collecting hpack==4.0.0 (from -r requirements.txt (line 100))
+[19:21:48+0000] Downloading hpack-4.0.0-py3-none-any.whl (32 kB)
+[19:21:49+0000] Collecting httpcore==1.0.2 (from -r requirements.txt (line 102))
+[19:21:49+0000] Obtaining dependency information for httpcore==1.0.2 from https://files.pythonhosted.org/packages/56/ba/78b0a99c4da0ff8b0f59defa2f13ca4668189b134bd9840b6202a93d9a0f/httpcore-1.0.2-py3-none-any.whl.metadata
+[19:21:49+0000] Downloading httpcore-1.0.2-py3-none-any.whl.metadata (20 kB)
+[19:21:50+0000] Collecting httpx==0.26.0 (from -r requirements.txt (line 104))
+[19:21:50+0000] Obtaining dependency information for httpx==0.26.0 from https://files.pythonhosted.org/packages/39/9b/4937d841aee9c2c8102d9a4eeb800c7dad25386caabb4a1bf5010df81a57/httpx-0.26.0-py3-none-any.whl.metadata
+[19:21:50+0000] Downloading httpx-0.26.0-py3-none-any.whl.metadata (7.6 kB)
+[19:21:50+0000] Collecting hypercorn==0.16.0 (from -r requirements.txt (line 106))
+[19:21:50+0000] Obtaining dependency information for hypercorn==0.16.0 from https://files.pythonhosted.org/packages/17/9e/700d764316399c20fbe8e98c6fff903b5d3f950043cc2fcbd0831a42c953/hypercorn-0.16.0-py3-none-any.whl.metadata
+[19:21:50+0000] Downloading hypercorn-0.16.0-py3-none-any.whl.metadata (5.4 kB)
+[19:21:50+0000] Collecting hyperframe==6.0.1 (from -r requirements.txt (line 108))
+[19:21:50+0000] Downloading hyperframe-6.0.1-py3-none-any.whl (12 kB)
+[19:21:51+0000] Collecting idna==3.6 (from -r requirements.txt (line 110))
+[19:21:51+0000] Obtaining dependency information for idna==3.6 from https://files.pythonhosted.org/packages/c2/e7/a82b05cf63a603df6e68d59ae6a68bf5064484a0718ea5033660af4b54a9/idna-3.6-py3-none-any.whl.metadata
+[19:21:51+0000] Downloading idna-3.6-py3-none-any.whl.metadata (9.9 kB)
+[19:21:51+0000] Collecting importlib-metadata==6.11.0 (from -r requirements.txt (line 116))
+[19:21:51+0000] Obtaining dependency information for importlib-metadata==6.11.0 from https://files.pythonhosted.org/packages/59/9b/ecce94952ab5ea74c31dcf9ccf78ccd484eebebef06019bf8cb579ab4519/importlib_metadata-6.11.0-py3-none-any.whl.metadata
+[19:21:51+0000] Downloading importlib_metadata-6.11.0-py3-none-any.whl.metadata (4.9 kB)
+[19:21:52+0000] Collecting isodate==0.6.1 (from -r requirements.txt (line 118))
+[19:21:52+0000] Downloading isodate-0.6.1-py2.py3-none-any.whl (41 kB)
+[19:21:52+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 41.7/41.7 kB 1.8 MB/s eta 0:00:00
+[19:21:52+0000] Collecting itsdangerous==2.1.2 (from -r requirements.txt (line 124))
+[19:21:52+0000] Downloading itsdangerous-2.1.2-py3-none-any.whl (15 kB)
+[19:21:52+0000] Collecting jinja2==3.1.3 (from -r requirements.txt (line 128))
+[19:21:52+0000] Obtaining dependency information for jinja2==3.1.3 from https://files.pythonhosted.org/packages/30/6d/6de6be2d02603ab56e72997708809e8a5b0fbfee080735109b40a3564843/Jinja2-3.1.3-py3-none-any.whl.metadata
+[19:21:52+0000] Downloading Jinja2-3.1.3-py3-none-any.whl.metadata (3.3 kB)
+[19:21:53+0000] Collecting markupsafe==2.1.4 (from -r requirements.txt (line 132))
+[19:21:53+0000] Obtaining dependency information for markupsafe==2.1.4 from https://files.pythonhosted.org/packages/d3/0a/c6dfffacc5a9a17c97019cb7cbec67e5abfb65c59a58ecba270fa224f88d/MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata
+[19:21:53+0000] Downloading MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (3.0 kB)
+[19:21:53+0000] Collecting msal==1.26.0 (from -r requirements.txt (line 137))
+[19:21:53+0000] Obtaining dependency information for msal==1.26.0 from https://files.pythonhosted.org/packages/b7/61/2756b963e84db6946e4b93a8e288595106286fc11c7129fcb869267ead67/msal-1.26.0-py2.py3-none-any.whl.metadata
+[19:21:53+0000] Downloading msal-1.26.0-py2.py3-none-any.whl.metadata (11 kB)
+[19:21:54+0000] Collecting msal-extensions==1.1.0 (from -r requirements.txt (line 142))
+[19:21:54+0000] Obtaining dependency information for msal-extensions==1.1.0 from https://files.pythonhosted.org/packages/78/8d/ecd0eb93196f25c722ba1b923fd54d190366feccfa5b159d48dacf2b1fee/msal_extensions-1.1.0-py3-none-any.whl.metadata
+[19:21:54+0000] Downloading msal_extensions-1.1.0-py3-none-any.whl.metadata (7.7 kB)
+[19:21:54+0000] Collecting msrest==0.7.1 (from -r requirements.txt (line 144))
+[19:21:54+0000] Downloading msrest-0.7.1-py3-none-any.whl (85 kB)
+[19:21:54+0000] ━━━━━━━━━━━━━━━━━━━��━━━━━━━━━━━━━━━━━━━━ 85.4/85.4 kB 6.2 MB/s eta 0:00:00
+[19:21:59+0000] Collecting multidict==6.0.4 (from -r requirements.txt (line 146))
+[19:21:59+0000] Downloading multidict-6.0.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (117 kB)
+[19:21:59+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 117.4/117.4 kB 2.2 MB/s eta 0:00:00
+[19:22:05+0000] Collecting numpy==1.26.3 (from -r requirements.txt (line 150))
+[19:22:05+0000] Obtaining dependency information for numpy==1.26.3 from https://files.pythonhosted.org/packages/5a/62/007b63f916aca1d27f5fede933fda3315d931ff9b2c28b9c2cf388cd8edb/numpy-1.26.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata
+[19:22:05+0000] Downloading numpy-1.26.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (61 kB)
+[19:22:05+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━���━━━━━━━━━━━━━━ 61.2/61.2 kB 5.6 MB/s eta 0:00:00
+[19:22:05+0000] Collecting oauthlib==3.2.2 (from -r requirements.txt (line 155))
+[19:22:05+0000] Downloading oauthlib-3.2.2-py3-none-any.whl (151 kB)
+[19:22:05+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 151.7/151.7 kB 7.9 MB/s eta 0:00:00
+[19:22:06+0000] Collecting openai[datalib]==1.10.0 (from -r requirements.txt (line 157))
+[19:22:06+0000] Obtaining dependency information for openai[datalib]==1.10.0 from https://files.pythonhosted.org/packages/46/85/8681046cd9cc13a36ac76e4a1b047338c90dbeab2e9b14fb36de7f314c93/openai-1.10.0-py3-none-any.whl.metadata
+[19:22:06+0000] Downloading openai-1.10.0-py3-none-any.whl.metadata (18 kB)
+[19:22:06+0000] Collecting opentelemetry-api==1.22.0 (from -r requirements.txt (line 159))
+[19:22:06+0000] Obtaining dependency information for opentelemetry-api==1.22.0 from https://files.pythonhosted.org/packages/fc/2e/a8509051aa446783e24ee03d74bd268c07d5d25a8d48686cfcf3429d5d32/opentelemetry_api-1.22.0-py3-none-any.whl.metadata
+[19:22:06+0000] Downloading opentelemetry_api-1.22.0-py3-none-any.whl.metadata (1.4 kB)
+[19:22:07+0000] Collecting opentelemetry-instrumentation==0.43b0 (from -r requirements.txt (line 177))
+[19:22:07+0000] Obtaining dependency information for opentelemetry-instrumentation==0.43b0 from https://files.pythonhosted.org/packages/91/f0/4a9f7cbcc697273d847040a9e4f98ceb07b642e1fe5fed56a0fb6b567665/opentelemetry_instrumentation-0.43b0-py3-none-any.whl.metadata
+[19:22:07+0000] Downloading opentelemetry_instrumentation-0.43b0-py3-none-any.whl.metadata (5.9 kB)
+[19:22:08+0000] Collecting opentelemetry-instrumentation-aiohttp-client==0.43b0 (from -r requirements.txt (line 191))
+[19:22:08+0000] Obtaining dependency information for opentelemetry-instrumentation-aiohttp-client==0.43b0 from https://files.pythonhosted.org/packages/23/75/ce33cd15bc706b1e170e5ce65235a8418e3332ad543419b902a9d24f079f/opentelemetry_instrumentation_aiohttp_client-0.43b0-py3-none-any.whl.metadata
+[19:22:08+0000] Downloading opentelemetry_instrumentation_aiohttp_client-0.43b0-py3-none-any.whl.metadata (2.2 kB)
+[19:22:08+0000] Collecting opentelemetry-instrumentation-asgi==0.43b0 (from -r requirements.txt (line 193))
+[19:22:08+0000] Obtaining dependency information for opentelemetry-instrumentation-asgi==0.43b0 from https://files.pythonhosted.org/packages/71/cd/a0456c8e4441d9ef5b412a3ffdf97629a81adeb331f8bb645df4f9153dd8/opentelemetry_instrumentation_asgi-0.43b0-py3-none-any.whl.metadata
+[19:22:08+0000] Downloading opentelemetry_instrumentation_asgi-0.43b0-py3-none-any.whl.metadata (2.1 kB)
+[19:22:08+0000] Collecting opentelemetry-instrumentation-dbapi==0.43b0 (from -r requirements.txt (line 197))
+[19:22:08+0000] Obtaining dependency information for opentelemetry-instrumentation-dbapi==0.43b0 from https://files.pythonhosted.org/packages/6d/96/f46bffb40e71f0abd82ad24ecfa7a8e29b6abca631f7d049d80afee83ff9/opentelemetry_instrumentation_dbapi-0.43b0-py3-none-any.whl.metadata
+[19:22:08+0000] Downloading opentelemetry_instrumentation_dbapi-0.43b0-py3-none-any.whl.metadata (1.9 kB)
+[19:22:09+0000] Collecting opentelemetry-instrumentation-django==0.43b0 (from -r requirements.txt (line 199))
+[19:22:09+0000] Obtaining dependency information for opentelemetry-instrumentation-django==0.43b0 from https://files.pythonhosted.org/packages/11/66/a6b5aadb04b5daf002fcbe97bb6bc83416c53b81a608de0e9ad886c59643/opentelemetry_instrumentation_django-0.43b0-py3-none-any.whl.metadata
+[19:22:09+0000] Downloading opentelemetry_instrumentation_django-0.43b0-py3-none-any.whl.metadata (2.3 kB)
+[19:22:09+0000] Collecting opentelemetry-instrumentation-fastapi==0.43b0 (from -r requirements.txt (line 201))
+[19:22:09+0000] Obtaining dependency information for opentelemetry-instrumentation-fastapi==0.43b0 from https://files.pythonhosted.org/packages/1d/51/429d04b8694fec2f87184ced4beeab1dd6db194a9444b0a6fca1675338b2/opentelemetry_instrumentation_fastapi-0.43b0-py3-none-any.whl.metadata
+[19:22:09+0000] Downloading opentelemetry_instrumentation_fastapi-0.43b0-py3-none-any.whl.metadata (2.3 kB)
+[19:22:10+0000] Collecting opentelemetry-instrumentation-flask==0.43b0 (from -r requirements.txt (line 203))
+[19:22:10+0000] Obtaining dependency information for opentelemetry-instrumentation-flask==0.43b0 from https://files.pythonhosted.org/packages/21/eb/4b0d6f98d2767c7117ebe497bcc58f00e70cc6b4ce97b99bd3eccf3d6644/opentelemetry_instrumentation_flask-0.43b0-py3-none-any.whl.metadata
+[19:22:10+0000] Downloading opentelemetry_instrumentation_flask-0.43b0-py3-none-any.whl.metadata (2.4 kB)
+[19:22:10+0000] Collecting opentelemetry-instrumentation-httpx==0.43b0 (from -r requirements.txt (line 205))
+[19:22:10+0000] Obtaining dependency information for opentelemetry-instrumentation-httpx==0.43b0 from https://files.pythonhosted.org/packages/7e/ed/a8d3951650145d7d7997c83e35c59c02c8bf632c24ff2e07ab065ad7dd48/opentelemetry_instrumentation_httpx-0.43b0-py3-none-any.whl.metadata
+[19:22:10+0000] Downloading opentelemetry_instrumentation_httpx-0.43b0-py3-none-any.whl.metadata (7.1 kB)
+[19:22:11+0000] Collecting opentelemetry-instrumentation-psycopg2==0.43b0 (from -r requirements.txt (line 207))
+[19:22:11+0000] Obtaining dependency information for opentelemetry-instrumentation-psycopg2==0.43b0 from https://files.pythonhosted.org/packages/0a/4e/f2085da8254b0f019a5dd57f737395c39274a23c25bf3dfe4030a4169325/opentelemetry_instrumentation_psycopg2-0.43b0-py3-none-any.whl.metadata
+[19:22:11+0000] Downloading opentelemetry_instrumentation_psycopg2-0.43b0-py3-none-any.whl.metadata (2.1 kB)
+[19:22:11+0000] Collecting opentelemetry-instrumentation-requests==0.43b0 (from -r requirements.txt (line 209))
+[19:22:11+0000] Obtaining dependency information for opentelemetry-instrumentation-requests==0.43b0 from https://files.pythonhosted.org/packages/3b/a9/98618c6383cad51313f448412cadd0bed43634f0287eaf67a3e71a536f9c/opentelemetry_instrumentation_requests-0.43b0-py3-none-any.whl.metadata
+[19:22:11+0000] Downloading opentelemetry_instrumentation_requests-0.43b0-py3-none-any.whl.metadata (2.7 kB)
+[19:22:12+0000] Collecting opentelemetry-instrumentation-urllib==0.43b0 (from -r requirements.txt (line 213))
+[19:22:12+0000] Obtaining dependency information for opentelemetry-instrumentation-urllib==0.43b0 from https://files.pythonhosted.org/packages/29/8a/c184945b2628ed44b9357e0df84dfc0974efd4e1360b3d89d2180ebfb3c0/opentelemetry_instrumentation_urllib-0.43b0-py3-none-any.whl.metadata
+[19:22:12+0000] Downloading opentelemetry_instrumentation_urllib-0.43b0-py3-none-any.whl.metadata (3.4 kB)
+[19:22:12+0000] Collecting opentelemetry-instrumentation-urllib3==0.43b0 (from -r requirements.txt (line 215))
+[19:22:12+0000] Obtaining dependency information for opentelemetry-instrumentation-urllib3==0.43b0 from https://files.pythonhosted.org/packages/a0/54/3e6fc502e06d6c4cba23f314426951225f950b1af3c2e6decb780cd64ff1/opentelemetry_instrumentation_urllib3-0.43b0-py3-none-any.whl.metadata
+[19:22:12+0000] Downloading opentelemetry_instrumentation_urllib3-0.43b0-py3-none-any.whl.metadata (3.6 kB)
+[19:22:13+0000] Collecting opentelemetry-instrumentation-wsgi==0.43b0 (from -r requirements.txt (line 217))
+[19:22:13+0000] Obtaining dependency information for opentelemetry-instrumentation-wsgi==0.43b0 from https://files.pythonhosted.org/packages/4a/37/6315abd394778d76b9bf206980436a8539cc13ddcd0bced709f4d9c3d1e8/opentelemetry_instrumentation_wsgi-0.43b0-py3-none-any.whl.metadata
+[19:22:13+0000] Downloading opentelemetry_instrumentation_wsgi-0.43b0-py3-none-any.whl.metadata (2.1 kB)
+[19:22:13+0000] Collecting opentelemetry-resource-detector-azure==0.1.3 (from -r requirements.txt (line 221))
+[19:22:13+0000] Obtaining dependency information for opentelemetry-resource-detector-azure==0.1.3 from https://files.pythonhosted.org/packages/99/c4/6790b15d360d0a14c5fb3a754d713470758da8a3635d90502aabb52febe2/opentelemetry_resource_detector_azure-0.1.3-py3-none-any.whl.metadata
+[19:22:13+0000] Downloading opentelemetry_resource_detector_azure-0.1.3-py3-none-any.whl.metadata (4.6 kB)
+[19:22:14+0000] Collecting opentelemetry-sdk==1.22.0 (from -r requirements.txt (line 223))
+[19:22:14+0000] Obtaining dependency information for opentelemetry-sdk==1.22.0 from https://files.pythonhosted.org/packages/ff/94/588f49e0dd9a62ec46102736d2378330032a55e19c79ff7e4febea7ebed1/opentelemetry_sdk-1.22.0-py3-none-any.whl.metadata
+[19:22:14+0000] Downloading opentelemetry_sdk-1.22.0-py3-none-any.whl.metadata (1.5 kB)
+[19:22:14+0000] Collecting opentelemetry-semantic-conventions==0.43b0 (from -r requirements.txt (line 227))
+[19:22:14+0000] Obtaining dependency information for opentelemetry-semantic-conventions==0.43b0 from https://files.pythonhosted.org/packages/e0/26/69be0f1a56a362c68fa0c7632d841b1b8f29d809bc6b1b897387c9f46973/opentelemetry_semantic_conventions-0.43b0-py3-none-any.whl.metadata
+[19:22:14+0000] Downloading opentelemetry_semantic_conventions-0.43b0-py3-none-any.whl.metadata (2.3 kB)
+[19:22:15+0000] Collecting opentelemetry-util-http==0.43b0 (from -r requirements.txt (line 241))
+[19:22:15+0000] Obtaining dependency information for opentelemetry-util-http==0.43b0 from https://files.pythonhosted.org/packages/74/91/a87a59baeeb917a93f2cc86fa670cf533328d18a2d09b0cef4f65e8b83e9/opentelemetry_util_http-0.43b0-py3-none-any.whl.metadata
+[19:22:15+0000] Downloading opentelemetry_util_http-0.43b0-py3-none-any.whl.metadata (2.5 kB)
+[19:22:15+0000] Collecting packaging==23.2 (from -r requirements.txt (line 252))
+[19:22:15+0000] Obtaining dependency information for packaging==23.2 from https://files.pythonhosted.org/packages/ec/1a/610693ac4ee14fcdf2d9bf3c493370e4f2ef7ae2e19217d7a237ff42367d/packaging-23.2-py3-none-any.whl.metadata
+[19:22:15+0000] Downloading packaging-23.2-py3-none-any.whl.metadata (3.2 kB)
+[19:22:20+0000] Collecting pandas==2.2.0 (from -r requirements.txt (line 256))
+[19:22:20+0000] Obtaining dependency information for pandas==2.2.0 from https://files.pythonhosted.org/packages/5b/7e/9fd11ba8e86a8add8f2ff4e11c7111f65ec6fd1b547222160bb969e2bf5e/pandas-2.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata
+[19:22:20+0000] Downloading pandas-2.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (19 kB)
+[19:22:21+0000] Collecting pandas-stubs==2.1.4.231227 (from -r requirements.txt (line 258))
+[19:22:21+0000] Obtaining dependency information for pandas-stubs==2.1.4.231227 from https://files.pythonhosted.org/packages/c0/6d/c5c23926fcc7526a5df32a8f3b3540948be8dd4c25f4a097f9091d40535c/pandas_stubs-2.1.4.231227-py3-none-any.whl.metadata
+[19:22:21+0000] Downloading pandas_stubs-2.1.4.231227-py3-none-any.whl.metadata (9.6 kB)
+[19:22:26+0000] Collecting pillow==10.2.0 (from -r requirements.txt (line 260))
+[19:22:26+0000] Obtaining dependency information for pillow==10.2.0 from https://files.pythonhosted.org/packages/66/9c/2e1877630eb298bbfd23f90deeec0a3f682a4163d5ca9f178937de57346c/pillow-10.2.0-cp311-cp311-manylinux_2_28_x86_64.whl.metadata
+[19:22:26+0000] Downloading pillow-10.2.0-cp311-cp311-manylinux_2_28_x86_64.whl.metadata (9.7 kB)
+[19:22:27+0000] Collecting portalocker==2.8.2 (from -r requirements.txt (line 262))
+[19:22:27+0000] Obtaining dependency information for portalocker==2.8.2 from https://files.pythonhosted.org/packages/17/9e/87671efcca80ba6203811540ed1f9c0462c1609d2281d7b7f53cef05da3d/portalocker-2.8.2-py3-none-any.whl.metadata
+[19:22:27+0000] Downloading portalocker-2.8.2-py3-none-any.whl.metadata (8.5 kB)
+[19:22:28+0000] Collecting priority==2.0.0 (from -r requirements.txt (line 264))
+[19:22:28+0000] Downloading priority-2.0.0-py3-none-any.whl (8.9 kB)
+[19:22:28+0000] Collecting pyasn1==0.5.1 (from -r requirements.txt (line 266))
+[19:22:28+0000] Obtaining dependency information for pyasn1==0.5.1 from https://files.pythonhosted.org/packages/d1/75/4686d2872bf2fc0b37917cbc8bbf0dd3a5cdb0990799be1b9cbf1e1eb733/pyasn1-0.5.1-py2.py3-none-any.whl.metadata
+[19:22:29+0000] Downloading pyasn1-0.5.1-py2.py3-none-any.whl.metadata (8.6 kB)
+[19:22:29+0000] Collecting pycparser==2.21 (from -r requirements.txt (line 270))
+[19:22:29+0000] Downloading pycparser-2.21-py2.py3-none-any.whl (118 kB)
+[19:22:29+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 118.7/118.7 kB 3.6 MB/s eta 0:00:00
+[19:22:31+0000] Collecting pydantic==2.6.0 (from -r requirements.txt (line 272))
+[19:22:31+0000] Obtaining dependency information for pydantic==2.6.0 from https://files.pythonhosted.org/packages/e4/37/3ffe6e7daa1ea1b4bf5228807a92ccbae538cf57c0c50b93564c310c11a8/pydantic-2.6.0-py3-none-any.whl.metadata
+[19:22:31+0000] Downloading pydantic-2.6.0-py3-none-any.whl.metadata (81 kB)
+[19:22:31+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 81.8/81.8 kB 3.6 MB/s eta 0:00:00
+[19:22:39+0000] Collecting pydantic-core==2.16.1 (from -r requirements.txt (line 274))
+[19:22:39+0000] Obtaining dependency information for pydantic-core==2.16.1 from https://files.pythonhosted.org/packages/98/19/955b83b6e33b7ac27914860069a918fe49b29c13bc149dc7bb7c60954812/pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata
+[19:22:39+0000] Downloading pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (6.5 kB)
+[19:22:39+0000] Collecting pyjwt[crypto]==2.8.0 (from -r requirements.txt (line 276))
+[19:22:39+0000] Obtaining dependency information for pyjwt[crypto]==2.8.0 from https://files.pythonhosted.org/packages/2b/4f/e04a8067c7c96c364cef7ef73906504e2f40d690811c021e1a1901473a19/PyJWT-2.8.0-py3-none-any.whl.metadata
+[19:22:39+0000] Downloading PyJWT-2.8.0-py3-none-any.whl.metadata (4.2 kB)
+[19:22:39+0000] Collecting python-dateutil==2.8.2 (from -r requirements.txt (line 278))
+[19:22:39+0000] Downloading python_dateutil-2.8.2-py2.py3-none-any.whl (247 kB)
+[19:22:39+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 247.7/247.7 kB 19.2 MB/s eta 0:00:00
+[19:22:40+0000] Collecting python-jose[cryptography]==3.3.0 (from -r requirements.txt (line 280))
+[19:22:40+0000] Downloading python_jose-3.3.0-py2.py3-none-any.whl (33 kB)
+[19:22:40+0000] Collecting pytz==2023.4 (from -r requirements.txt (line 282))
+[19:22:40+0000] Obtaining dependency information for pytz==2023.4 from https://files.pythonhosted.org/packages/3b/dd/9b84302ba85ac6d3d3042d3e8698374838bde1c386b4adb1223d7a0efd4e/pytz-2023.4-py2.py3-none-any.whl.metadata
+[19:22:40+0000] Downloading pytz-2023.4-py2.py3-none-any.whl.metadata (22 kB)
+[19:22:41+0000] Collecting quart==0.19.4 (from -r requirements.txt (line 284))
+[19:22:41+0000] Obtaining dependency information for quart==0.19.4 from https://files.pythonhosted.org/packages/9a/2c/681b4fcecefd98627a90dd5aecdc6b57ba18c9ce07e173d86a0b1274f20b/quart-0.19.4-py3-none-any.whl.metadata
+[19:22:41+0000] Downloading quart-0.19.4-py3-none-any.whl.metadata (5.7 kB)
+[19:22:42+0000] Collecting quart-cors==0.7.0 (from -r requirements.txt (line 288))
+[19:22:42+0000] Obtaining dependency information for quart-cors==0.7.0 from https://files.pythonhosted.org/packages/60/fc/1ffe9042df05d48f5eaac4116708fee3f7bb18b696380cc4e3797c8fd510/quart_cors-0.7.0-py3-none-any.whl.metadata
+[19:22:42+0000] Downloading quart_cors-0.7.0-py3-none-any.whl.metadata (9.4 kB)
+[19:22:50+0000] Collecting regex==2023.12.25 (from -r requirements.txt (line 290))
+[19:22:50+0000] Obtaining dependency information for regex==2023.12.25 from https://files.pythonhosted.org/packages/8d/6b/2f6478814954c07c04ba60b78d688d3d7bab10d786e0b6c1db607e4f6673/regex-2023.12.25-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata
+[19:22:50+0000] Downloading regex-2023.12.25-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (40 kB)
+[19:22:50+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 40.9/40.9 kB 504.8 kB/s eta 0:00:00
+[19:22:50+0000] Collecting requests==2.31.0 (from -r requirements.txt (line 292))
+[19:22:50+0000] Obtaining dependency information for requests==2.31.0 from https://files.pythonhosted.org/packages/70/8e/0e2d847013cb52cd35b38c009bb167a1a26b2ce6cd6965bf26b47bc0bf44/requests-2.31.0-py3-none-any.whl.metadata
+[19:22:50+0000] Downloading requests-2.31.0-py3-none-any.whl.metadata (4.6 kB)
+[19:22:51+0000] Collecting requests-oauthlib==1.3.1 (from -r requirements.txt (line 299))
+[19:22:51+0000] Downloading requests_oauthlib-1.3.1-py2.py3-none-any.whl (23 kB)
+[19:22:51+0000] Collecting rsa==4.9 (from -r requirements.txt (line 301))
+[19:22:51+0000] Downloading rsa-4.9-py3-none-any.whl (34 kB)
+[19:22:51+0000] Collecting six==1.16.0 (from -r requirements.txt (line 303))
+[19:22:52+0000] Downloading six-1.16.0-py2.py3-none-any.whl (11 kB)
+[19:22:53+0000] Collecting sniffio==1.3.0 (from -r requirements.txt (line 309))
+[19:22:53+0000] Downloading sniffio-1.3.0-py3-none-any.whl (10 kB)
+[19:22:53+0000] Collecting tenacity==8.2.3 (from -r requirements.txt (line 314))
+[19:22:53+0000] Obtaining dependency information for tenacity==8.2.3 from https://files.pythonhosted.org/packages/f4/f1/990741d5bb2487d529d20a433210ffa136a367751e454214013b441c4575/tenacity-8.2.3-py3-none-any.whl.metadata
+[19:22:53+0000] Downloading tenacity-8.2.3-py3-none-any.whl.metadata (1.0 kB)
+[19:22:54+0000] Collecting tiktoken==0.5.2 (from -r requirements.txt (line 316))
+[19:22:54+0000] Obtaining dependency information for tiktoken==0.5.2 from https://files.pythonhosted.org/packages/fb/a9/237dc2db35e6ec0fb7dd63e3d10ebe0377559203bd2a87e12a4adbfc8585/tiktoken-0.5.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata
+[19:22:54+0000] Downloading tiktoken-0.5.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (6.6 kB)
+[19:22:55+0000] Collecting tqdm==4.66.1 (from -r requirements.txt (line 318))
+[19:22:55+0000] Obtaining dependency information for tqdm==4.66.1 from https://files.pythonhosted.org/packages/00/e5/f12a80907d0884e6dff9c16d0c0114d81b8cd07dc3ae54c5e962cc83037e/tqdm-4.66.1-py3-none-any.whl.metadata
+[19:22:55+0000] Downloading tqdm-4.66.1-py3-none-any.whl.metadata (57 kB)
+[19:22:55+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 57.6/57.6 kB 2.4 MB/s eta 0:00:00
+[19:22:55+0000] Collecting types-pillow==10.2.0.20240206 (from -r requirements.txt (line 320))
+[19:22:55+0000] Obtaining dependency information for types-pillow==10.2.0.20240206 from https://files.pythonhosted.org/packages/54/a1/9c24f95c637f5ed77f0a1de9077a06af018acc0d3ffe9bb0843abc13619c/types_Pillow-10.2.0.20240206-py3-none-any.whl.metadata
+[19:22:56+0000] Downloading types_Pillow-10.2.0.20240206-py3-none-any.whl.metadata (1.6 kB)
+[19:22:56+0000] Collecting types-pytz==2023.4.0.20240130 (from -r requirements.txt (line 322))
+[19:22:56+0000] Obtaining dependency information for types-pytz==2023.4.0.20240130 from https://files.pythonhosted.org/packages/83/cd/018e825d60d86c1798c7acccfcb3d7c31227793445e4b87423498e8c486d/types_pytz-2023.4.0.20240130-py3-none-any.whl.metadata
+[19:22:56+0000] Downloading types_pytz-2023.4.0.20240130-py3-none-any.whl.metadata (1.5 kB)
+[19:22:57+0000] Collecting typing-extensions==4.9.0 (from -r requirements.txt (line 324))
+[19:22:57+0000] Obtaining dependency information for typing-extensions==4.9.0 from https://files.pythonhosted.org/packages/b7/f4/6a90020cd2d93349b442bfcb657d0dc91eee65491600b2cb1d388bc98e6b/typing_extensions-4.9.0-py3-none-any.whl.metadata
+[19:22:57+0000] Downloading typing_extensions-4.9.0-py3-none-any.whl.metadata (3.0 kB)
+[19:22:57+0000] Collecting tzdata==2023.4 (from -r requirements.txt (line 333))
+[19:22:57+0000] Obtaining dependency information for tzdata==2023.4 from https://files.pythonhosted.org/packages/a3/fb/52b62131e21b24ee297e4e95ed41eba29647dad0e0051a92bb66b43c70ff/tzdata-2023.4-py2.py3-none-any.whl.metadata
+[19:22:57+0000] Downloading tzdata-2023.4-py2.py3-none-any.whl.metadata (1.4 kB)
+[19:22:57+0000] Collecting urllib3==2.1.0 (from -r requirements.txt (line 335))
+[19:22:57+0000] Obtaining dependency information for urllib3==2.1.0 from https://files.pythonhosted.org/packages/96/94/c31f58c7a7f470d5665935262ebd7455c7e4c7782eb525658d3dbf4b9403/urllib3-2.1.0-py3-none-any.whl.metadata
+[19:22:57+0000] Downloading urllib3-2.1.0-py3-none-any.whl.metadata (6.4 kB)
+[19:22:58+0000] Collecting uvicorn==0.27.0.post1 (from -r requirements.txt (line 337))
+[19:22:58+0000] Obtaining dependency information for uvicorn==0.27.0.post1 from https://files.pythonhosted.org/packages/c7/f3/29caa83f5795b20ed3aca357c648f3ae995ff6ff08e38b22387017abbdc5/uvicorn-0.27.0.post1-py3-none-any.whl.metadata
+[19:22:58+0000] Downloading uvicorn-0.27.0.post1-py3-none-any.whl.metadata (6.4 kB)
+[19:22:59+0000] Collecting werkzeug==3.0.1 (from -r requirements.txt (line 339))
+[19:22:59+0000] Obtaining dependency information for werkzeug==3.0.1 from https://files.pythonhosted.org/packages/c3/fc/254c3e9b5feb89ff5b9076a23218dafbc99c96ac5941e900b71206e6313b/werkzeug-3.0.1-py3-none-any.whl.metadata
+[19:22:59+0000] Downloading werkzeug-3.0.1-py3-none-any.whl.metadata (4.1 kB)
+[19:23:00+0000] Collecting wrapt==1.16.0 (from -r requirements.txt (line 343))
+[19:23:00+0000] Obtaining dependency information for wrapt==1.16.0 from https://files.pythonhosted.org/packages/6e/52/2da48b35193e39ac53cfb141467d9f259851522d0e8c87153f0ba4205fb1/wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata
+[19:23:00+0000] Downloading wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (6.6 kB)
+[19:23:00+0000] Collecting wsproto==1.2.0 (from -r requirements.txt (line 350))
+[19:23:00+0000] Downloading wsproto-1.2.0-py3-none-any.whl (24 kB)
+[19:23:03+0000] Collecting yarl==1.9.4 (from -r requirements.txt (line 352))
+[19:23:03+0000] Obtaining dependency information for yarl==1.9.4 from https://files.pythonhosted.org/packages/9f/ea/94ad7d8299df89844e666e4aa8a0e9b88e02416cd6a7dd97969e9eae5212/yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata
+[19:23:03+0000] Downloading yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (31 kB)
+[19:23:03+0000] Collecting zipp==3.17.0 (from -r requirements.txt (line 354))
+[19:23:03+0000] Obtaining dependency information for zipp==3.17.0 from https://files.pythonhosted.org/packages/d9/66/48866fc6b158c81cc2bfecc04c480f105c6040e8b077bc54c634b4a67926/zipp-3.17.0-py3-none-any.whl.metadata
+[19:23:03+0000] Downloading zipp-3.17.0-py3-none-any.whl.metadata (3.7 kB)
+[19:23:30+0000] Requirement already satisfied: setuptools>=16.0 in ./antenv/lib/python3.11/site-packages (from opentelemetry-instrumentation==0.43b0->-r requirements.txt (line 177)) (65.5.0)
+[19:23:51+0000] Downloading aiofiles-23.2.1-py3-none-any.whl (15 kB)
+[19:23:51+0000] Downloading aiohttp-3.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (1.3 MB)
+[19:23:51+0000] ━━━━━━━━━━━━━━━━━��━━━━━━━━━━━━━━━━━━━━━━ 1.3/1.3 MB 32.0 MB/s eta 0:00:00
+[19:23:51+0000] Downloading annotated_types-0.6.0-py3-none-any.whl (12 kB)
+[19:23:51+0000] Downloading anyio-4.2.0-py3-none-any.whl (85 kB)
+[19:23:51+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 85.5/85.5 kB 6.9 MB/s eta 0:00:00
+[19:23:51+0000] Downloading asgiref-3.7.2-py3-none-any.whl (24 kB)
+[19:23:51+0000] Downloading attrs-23.2.0-py3-none-any.whl (60 kB)
+[19:23:51+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 60.8/60.8 kB 5.9 MB/s eta 0:00:00
+[19:23:51+0000] Downloading azure_core-1.29.7-py3-none-any.whl (192 kB)
+[19:23:51+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 192.9/192.9 kB 6.3 MB/s eta 0:00:00
+[19:23:51+0000] Downloading azure_core_tracing_opentelemetry-1.0.0b11-py3-none-any.whl (10 kB)
+[19:23:51+0000] Downloading azure_identity-1.15.0-py3-none-any.whl (164 kB)
+[19:23:52+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 164.7/164.7 kB 5.7 MB/s eta 0:00:00
+[19:23:52+0000] Downloading azure_monitor_opentelemetry-1.2.0-py3-none-any.whl (20 kB)
+[19:23:52+0000] Downloading azure_monitor_opentelemetry_exporter-1.0.0b21-py2.py3-none-any.whl (78 kB)
+[19:23:52+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 78.5/78.5 kB 787.5 kB/s eta 0:00:00
+[19:23:52+0000] Downloading azure_search_documents-11.6.0b1-py3-none-any.whl (315 kB)
+[19:23:52+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 315.3/315.3 kB 5.6 MB/s eta 0:00:00
+[19:23:52+0000] Downloading azure_storage_blob-12.19.0-py3-none-any.whl (394 kB)
+[19:23:52+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 394.2/394.2 kB 19.4 MB/s eta 0:00:00
+[19:23:52+0000] Downloading blinker-1.7.0-py3-none-any.whl (13 kB)
+[19:23:52+0000] Downloading certifi-2023.11.17-py3-none-any.whl (162 kB)
+[19:23:52+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 162.5/162.5 kB 6.3 MB/s eta 0:00:00
+[19:23:52+0000] Downloading cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (464 kB)
+[19:23:52+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 464.8/464.8 kB 3.1 MB/s eta 0:00:00
+[19:23:52+0000] Downloading charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (140 kB)
+[19:23:52+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 140.3/140.3 kB 5.7 MB/s eta 0:00:00
+[19:23:52+0000] Downloading click-8.1.7-py3-none-any.whl (97 kB)
+[19:23:53+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 97.9/97.9 kB 831.8 kB/s eta 0:00:00
+[19:23:53+0000] Downloading cryptography-42.0.1-cp39-abi3-manylinux_2_28_x86_64.whl (4.6 MB)
+[19:23:53+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.6/4.6 MB 15.8 MB/s eta 0:00:00
+[19:23:53+0000] Downloading Deprecated-1.2.14-py2.py3-none-any.whl (9.6 kB)
+[19:23:53+0000] Downloading distro-1.9.0-py3-none-any.whl (20 kB)
+[19:23:53+0000] Downloading flask-3.0.1-py3-none-any.whl (101 kB)
+[19:23:53+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 101.2/101.2 kB 1.1 MB/s eta 0:00:00
+[19:23:53+0000] Downloading frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (272 kB)
+[19:23:53+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 272.3/272.3 kB 12.9 MB/s eta 0:00:00
+[19:23:53+0000] Downloading httpcore-1.0.2-py3-none-any.whl (76 kB)
+[19:23:53+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 76.9/76.9 kB 7.0 MB/s eta 0:00:00
+[19:23:53+0000] Downloading httpx-0.26.0-py3-none-any.whl (75 kB)
+[19:23:54+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 75.9/75.9 kB 20.7 MB/s eta 0:00:00
+[19:23:54+0000] Downloading hypercorn-0.16.0-py3-none-any.whl (59 kB)
+[19:23:54+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 59.9/59.9 kB 17.8 MB/s eta 0:00:00
+[19:23:54+0000] Downloading idna-3.6-py3-none-any.whl (61 kB)
+[19:23:54+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 61.6/61.6 kB 3.2 MB/s eta 0:00:00
+[19:23:54+0000] Downloading importlib_metadata-6.11.0-py3-none-any.whl (23 kB)
+[19:23:54+0000] Downloading Jinja2-3.1.3-py3-none-any.whl (133 kB)
+[19:23:54+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 133.2/133.2 kB 9.8 MB/s eta 0:00:00
+[19:23:54+0000] Downloading MarkupSafe-2.1.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (28 kB)
+[19:23:54+0000] Downloading msal-1.26.0-py2.py3-none-any.whl (99 kB)
+[19:23:54+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 99.0/99.0 kB 10.0 MB/s eta 0:00:00
+[19:23:54+0000] Downloading msal_extensions-1.1.0-py3-none-any.whl (19 kB)
+[19:23:54+0000] Downloading numpy-1.26.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (18.3 MB)
+[19:23:55+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 18.3/18.3 MB 9.5 MB/s eta 0:00:00
+[19:23:55+0000] Downloading opentelemetry_api-1.22.0-py3-none-any.whl (57 kB)
+[19:23:55+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 57.9/57.9 kB 3.1 MB/s eta 0:00:00
+[19:23:55+0000] Downloading opentelemetry_instrumentation-0.43b0-py3-none-any.whl (28 kB)
+[19:23:55+0000] Downloading opentelemetry_instrumentation_aiohttp_client-0.43b0-py3-none-any.whl (11 kB)
+[19:23:55+0000] Downloading opentelemetry_instrumentation_asgi-0.43b0-py3-none-any.whl (14 kB)
+[19:23:55+0000] Downloading opentelemetry_instrumentation_dbapi-0.43b0-py3-none-any.whl (10 kB)
+[19:23:55+0000] Downloading opentelemetry_instrumentation_django-0.43b0-py3-none-any.whl (18 kB)
+[19:23:55+0000] Downloading opentelemetry_instrumentation_fastapi-0.43b0-py3-none-any.whl (11 kB)
+[19:23:55+0000] Downloading opentelemetry_instrumentation_flask-0.43b0-py3-none-any.whl (14 kB)
+[19:23:55+0000] Downloading opentelemetry_instrumentation_httpx-0.43b0-py3-none-any.whl (12 kB)
+[19:23:55+0000] Downloading opentelemetry_instrumentation_psycopg2-0.43b0-py3-none-any.whl (10 kB)
+[19:23:55+0000] Downloading opentelemetry_instrumentation_requests-0.43b0-py3-none-any.whl (12 kB)
+[19:23:55+0000] Downloading opentelemetry_instrumentation_urllib-0.43b0-py3-none-any.whl (11 kB)
+[19:23:55+0000] Downloading opentelemetry_instrumentation_urllib3-0.43b0-py3-none-any.whl (11 kB)
+[19:23:56+0000] Downloading opentelemetry_instrumentation_wsgi-0.43b0-py3-none-any.whl (13 kB)
+[19:23:56+0000] Downloading opentelemetry_resource_detector_azure-0.1.3-py3-none-any.whl (10 kB)
+[19:23:56+0000] Downloading opentelemetry_sdk-1.22.0-py3-none-any.whl (105 kB)
+[19:23:56+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 105.6/105.6 kB 1.6 MB/s eta 0:00:00
+[19:23:56+0000] Downloading opentelemetry_semantic_conventions-0.43b0-py3-none-any.whl (36 kB)
+[19:23:56+0000] Downloading opentelemetry_util_http-0.43b0-py3-none-any.whl (6.9 kB)
+[19:23:56+0000] Downloading packaging-23.2-py3-none-any.whl (53 kB)
+[19:23:56+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 53.0/53.0 kB 1.9 MB/s eta 0:00:00
+[19:23:56+0000] Downloading pandas-2.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (13.0 MB)
+[19:23:58+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 13.0/13.0 MB 7.7 MB/s eta 0:00:00
+[19:23:58+0000] Downloading pandas_stubs-2.1.4.231227-py3-none-any.whl (153 kB)
+[19:23:58+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 153.6/153.6 kB 8.1 MB/s eta 0:00:00
+[19:23:58+0000] Downloading pillow-10.2.0-cp311-cp311-manylinux_2_28_x86_64.whl (4.5 MB)
+[19:23:58+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 4.5/4.5 MB 16.5 MB/s eta 0:00:00
+[19:23:58+0000] Downloading portalocker-2.8.2-py3-none-any.whl (17 kB)
+[19:23:58+0000] Downloading pyasn1-0.5.1-py2.py3-none-any.whl (84 kB)
+[19:23:58+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 84.9/84.9 kB 6.8 MB/s eta 0:00:00
+[19:23:58+0000] Downloading pydantic-2.6.0-py3-none-any.whl (394 kB)
+[19:23:58+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 394.2/394.2 kB 9.3 MB/s eta 0:00:00
+[19:23:59+0000] Downloading pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.2 MB)
+[19:23:59+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.2/2.2 MB 5.6 MB/s eta 0:00:00
+[19:23:59+0000] Downloading pytz-2023.4-py2.py3-none-any.whl (506 kB)
+[19:23:59+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 506.5/506.5 kB 7.1 MB/s eta 0:00:00
+[19:23:59+0000] Downloading quart-0.19.4-py3-none-any.whl (77 kB)
+[19:23:59+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 77.8/77.8 kB 6.5 MB/s eta 0:00:00
+[19:23:59+0000] Downloading quart_cors-0.7.0-py3-none-any.whl (8.0 kB)
+[19:23:59+0000] Downloading regex-2023.12.25-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (785 kB)
+[19:23:59+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 785.1/785.1 kB 13.7 MB/s eta 0:00:00
+[19:23:59+0000] Downloading requests-2.31.0-py3-none-any.whl (62 kB)
+[19:23:59+0000] ━━━━━━━━━━━━━━━━━━━━━━━���━━━━━━━━━━━━━━━━ 62.6/62.6 kB 1.2 MB/s eta 0:00:00
+[19:23:59+0000] Downloading tenacity-8.2.3-py3-none-any.whl (24 kB)
+[19:23:59+0000] Downloading tiktoken-0.5.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (2.0 MB)
+[19:24:00+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 2.0/2.0 MB 15.3 MB/s eta 0:00:00
+[19:24:00+0000] Downloading tqdm-4.66.1-py3-none-any.whl (78 kB)
+[19:24:00+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 78.3/78.3 kB 6.9 MB/s eta 0:00:00
+[19:24:00+0000] Downloading types_Pillow-10.2.0.20240206-py3-none-any.whl (52 kB)
+[19:24:00+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 52.9/52.9 kB 1.1 MB/s eta 0:00:00
+[19:24:00+0000] Downloading types_pytz-2023.4.0.20240130-py3-none-any.whl (5.1 kB)
+[19:24:00+0000] Downloading typing_extensions-4.9.0-py3-none-any.whl (32 kB)
+[19:24:00+0000] Downloading tzdata-2023.4-py2.py3-none-any.whl (346 kB)
+[19:24:00+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 346.6/346.6 kB 11.7 MB/s eta 0:00:00
+[19:24:00+0000] Downloading urllib3-2.1.0-py3-none-any.whl (104 kB)
+[19:24:00+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 104.6/104.6 kB 1.2 MB/s eta 0:00:00
+[19:24:00+0000] Downloading uvicorn-0.27.0.post1-py3-none-any.whl (60 kB)
+[19:24:00+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 60.7/60.7 kB 5.7 MB/s eta 0:00:00
+[19:24:00+0000] Downloading werkzeug-3.0.1-py3-none-any.whl (226 kB)
+[19:24:00+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 226.7/226.7 kB 12.6 MB/s eta 0:00:00
+[19:24:00+0000] Downloading wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (80 kB)
+[19:24:00+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 80.7/80.7 kB 8.0 MB/s eta 0:00:00
+[19:24:00+0000] Downloading yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (328 kB)
+[19:24:00+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 328.1/328.1 kB 20.1 MB/s eta 0:00:00
+[19:24:00+0000] Downloading zipp-3.17.0-py3-none-any.whl (7.4 kB)
+[19:24:00+0000] Downloading openai-1.10.0-py3-none-any.whl (225 kB)
+[19:24:00+0000] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 225.1/225.1 kB 11.7 MB/s eta 0:00:00
+[19:24:01+0000] Downloading PyJWT-2.8.0-py3-none-any.whl (22 kB)
+[19:24:07+0000] Installing collected packages: pytz, fixedint, azure-common, zipp, wrapt, urllib3, tzdata, typing-extensions, types-pytz, types-pillow, tqdm, tenacity, sniffio, six, regex, pyjwt, pycparser, pyasn1, priority, portalocker, pillow, packaging, opentelemetry-util-http, opentelemetry-semantic-conventions, oauthlib, numpy, multidict, markupsafe, itsdangerous, idna, hyperframe, hpack, h11, frozenlist, distro, click, charset-normalizer, certifi, blinker, attrs, asgiref, annotated-types, aiofiles, yarl, wsproto, werkzeug, uvicorn, rsa, requests, python-dateutil, pydantic-core, pandas-stubs, jinja2, isodate, importlib-metadata, httpcore, h2, ecdsa, deprecated, cffi, anyio, aiosignal, tiktoken, requests-oauthlib, python-jose, pydantic, pandas, opentelemetry-api, hypercorn, httpx, flask, cryptography, azure-core, aiohttp, quart, opentelemetry-sdk, opentelemetry-instrumentation, openai, msrest, azure-storage-blob, azure-search-documents, azure-keyvault-secrets, azure-core-tracing-opentelemetry, quart-cors, opentelemetry-resource-detector-azure, opentelemetry-instrumentation-wsgi, opentelemetry-instrumentation-urllib3, opentelemetry-instrumentation-urllib, opentelemetry-instrumentation-requests, opentelemetry-instrumentation-httpx, opentelemetry-instrumentation-dbapi, opentelemetry-instrumentation-asgi, opentelemetry-instrumentation-aiohttp-client, msal, azure-monitor-opentelemetry-exporter, opentelemetry-instrumentation-psycopg2, opentelemetry-instrumentation-flask, opentelemetry-instrumentation-fastapi, opentelemetry-instrumentation-django, msal-extensions, azure-monitor-opentelemetry, azure-identity
+[19:25:31+0000] Successfully installed aiofiles-23.2.1 aiohttp-3.9.3 aiosignal-1.3.1 annotated-types-0.6.0 anyio-4.2.0 asgiref-3.7.2 attrs-23.2.0 azure-common-1.1.28 azure-core-1.29.7 azure-core-tracing-opentelemetry-1.0.0b11 azure-identity-1.15.0 azure-keyvault-secrets-4.7.0 azure-monitor-opentelemetry-1.2.0 azure-monitor-opentelemetry-exporter-1.0.0b21 azure-search-documents-11.6.0b1 azure-storage-blob-12.19.0 blinker-1.7.0 certifi-2023.11.17 cffi-1.16.0 charset-normalizer-3.3.2 click-8.1.7 cryptography-42.0.1 deprecated-1.2.14 distro-1.9.0 ecdsa-0.18.0 fixedint-0.1.6 flask-3.0.1 frozenlist-1.4.1 h11-0.14.0 h2-4.1.0 hpack-4.0.0 httpcore-1.0.2 httpx-0.26.0 hypercorn-0.16.0 hyperframe-6.0.1 idna-3.6 importlib-metadata-6.11.0 isodate-0.6.1 itsdangerous-2.1.2 jinja2-3.1.3 markupsafe-2.1.4 msal-1.26.0 msal-extensions-1.1.0 msrest-0.7.1 multidict-6.0.4 numpy-1.26.3 oauthlib-3.2.2 openai-1.10.0 opentelemetry-api-1.22.0 opentelemetry-instrumentation-0.43b0 opentelemetry-instrumentation-aiohttp-client-0.43b0 opentelemetry-instrumentation-asgi-0.43b0 opentelemetry-instrumentation-dbapi-0.43b0 opentelemetry-instrumentation-django-0.43b0 opentelemetry-instrumentation-fastapi-0.43b0 opentelemetry-instrumentation-flask-0.43b0 opentelemetry-instrumentation-httpx-0.43b0 opentelemetry-instrumentation-psycopg2-0.43b0 opentelemetry-instrumentation-requests-0.43b0 opentelemetry-instrumentation-urllib-0.43b0 opentelemetry-instrumentation-urllib3-0.43b0 opentelemetry-instrumentation-wsgi-0.43b0 opentelemetry-resource-detector-azure-0.1.3 opentelemetry-sdk-1.22.0 opentelemetry-semantic-conventions-0.43b0 opentelemetry-util-http-0.43b0 packaging-23.2 pandas-2.2.0 pandas-stubs-2.1.4.231227 pillow-10.2.0 portalocker-2.8.2 priority-2.0.0 pyasn1-0.5.1 pycparser-2.21 pydantic-2.6.0 pydantic-core-2.16.1 pyjwt-2.8.0 python-dateutil-2.8.2 python-jose-3.3.0 pytz-2023.4 quart-0.19.4 quart-cors-0.7.0 regex-2023.12.25 requests-2.31.0 requests-oauthlib-1.3.1 rsa-4.9 six-1.16.0 sniffio-1.3.0 tenacity-8.2.3 tiktoken-0.5.2 tqdm-4.66.1 types-pillow-10.2.0.20240206 types-pytz-2023.4.0.20240130 typing-extensions-4.9.0 tzdata-2023.4 urllib3-2.1.0 uvicorn-0.27.0.post1 werkzeug-3.0.1 wrapt-1.16.0 wsproto-1.2.0 yarl-1.9.4 zipp-3.17.0
+
+[notice] A new release of pip is available: 23.2.1 -> 24.0
+[notice] To update, run: pip install --upgrade pip
+Not a vso image, so not writing build commands
+Preparing output...
+
+Copying files to destination directory '/tmp/_preCompressedDestinationDir'...
+Done in 48 sec(s).
+Compressing content of directory '/tmp/_preCompressedDestinationDir'...
+Copied the compressed output to '/home/site/wwwroot'
+
+Removing existing manifest file
+Creating a manifest file...
+Manifest file created.
+Copying .ostype to manifest output directory.
+
+Done in 522 sec(s).
+Look for these important steps in the Oryx build:
+If you see all those steps in the Oryx build, then that’s a good sign +that the build went well, and you can move on to checking the App +Service logs.
+Select Advanced Tools from the side nav:
+Select Go to open the Kudu website.
+When the Kudu website loads, find the Current Docker Logs +link and select Download as zip next to it:
+In the downloaded zip file, find the filename that starts with the +most recent date and ends with “_default_docker.log”:
+Open that file to see the full logs, with the most recent logs at the +bottom.
+
+2024-02-08T19:30:27.900249002Z _____
+2024-02-08T19:30:27.900282702Z / _ \ __________ _________ ____
+2024-02-08T19:30:27.900288002Z / /_\ \\___ / | \_ __ \_/ __ \
+2024-02-08T19:30:27.900291902Z / | \/ /| | /| | \/\ ___/
+2024-02-08T19:30:27.900295502Z \____|__ /_____ \____/ |__| \___ >
+2024-02-08T19:30:27.900299602Z \/ \/ \/
+2024-02-08T19:30:27.900303402Z A P P S E R V I C E O N L I N U X
+2024-02-08T19:30:27.900307003Z
+2024-02-08T19:30:27.900310303Z Documentation: http://aka.ms/webapp-linux
+2024-02-08T19:30:27.900313903Z Python 3.11.4
+2024-02-08T19:30:27.900317303Z Note: Any data outside '/home' is not persisted
+2024-02-08T19:30:32.956710361Z Starting OpenBSD Secure Shell server: sshd.
+2024-02-08T19:30:33.441385332Z Site's appCommandLine: python3 -m gunicorn main:app
+2024-02-08T19:30:33.703536564Z Launching oryx with: create-script -appPath /home/site/wwwroot -output /opt/startup/startup.sh -virtualEnvName antenv -defaultApp /opt/defaultsite -userStartupCommand 'python3 -m gunicorn main:app'
+2024-02-08T19:30:33.703598264Z Found build manifest file at '/home/site/wwwroot/oryx-manifest.toml'. Deserializing it...
+2024-02-08T19:30:33.703605164Z Build Operation ID: 7440a33100749a32
+2024-02-08T19:30:33.703609765Z Oryx Version: 0.2.20230707.1, Commit: 0bd28e69919b5e8beba451e8677e3345f0be8361, ReleaseTagName: 20230707.1
+2024-02-08T19:30:33.712124127Z Output is compressed. Extracting it...
+2024-02-08T19:30:33.712151827Z Extracting '/home/site/wwwroot/output.tar.gz' to directory '/tmp/8dc28dad0e10acb'...
+2024-02-08T19:31:08.047051747Z App path is set to '/tmp/8dc28dad0e10acb'
+2024-02-08T19:31:08.073259604Z Writing output script to '/opt/startup/startup.sh'
+2024-02-08T19:31:08.431803481Z Using packages from virtual environment antenv located at /tmp/8dc28dad0e10acb/antenv.
+2024-02-08T19:31:08.431842281Z Updated PYTHONPATH to '/opt/startup/app_logs:/tmp/8dc28dad0e10acb/antenv/lib/python3.11/site-packages'
+2024-02-08T19:31:11.043306496Z [2024-02-08 19:31:11 +0000] [75] [INFO] Starting gunicorn 20.1.0
+2024-02-08T19:31:11.060556234Z [2024-02-08 19:31:11 +0000] [75] [INFO] Listening at: http://0.0.0.0:8000 (75)
+2024-02-08T19:31:11.060586534Z [2024-02-08 19:31:11 +0000] [75] [INFO] Using worker: uvicorn.workers.UvicornWorker
+2024-02-08T19:31:11.069707155Z [2024-02-08 19:31:11 +0000] [76] [INFO] Booting worker with pid: 76
+2024-02-08T19:31:11.188073718Z [2024-02-08 19:31:11 +0000] [77] [INFO] Booting worker with pid: 77
+2024-02-08T19:31:11.415802023Z [2024-02-08 19:31:11 +0000] [78] [INFO] Booting worker with pid: 78
+2024-02-08T19:32:20.509338341Z [2024-02-08 19:32:20 +0000] [76] [INFO] Started server process [76]
+2024-02-08T19:32:20.521167526Z [2024-02-08 19:32:20 +0000] [77] [INFO] Started server process [77]
+2024-02-08T19:32:20.521189626Z [2024-02-08 19:32:20 +0000] [77] [INFO] Waiting for application startup.
+2024-02-08T19:32:20.521207626Z [2024-02-08 19:32:20 +0000] [78] [INFO] Started server process [78]
+2024-02-08T19:32:20.521212726Z [2024-02-08 19:32:20 +0000] [78] [INFO] Waiting for application startup.
+2024-02-08T19:32:20.521217126Z [2024-02-08 19:32:20 +0000] [76] [INFO] Waiting for application startup.
+2024-02-08T19:32:20.726894213Z [2024-02-08 19:32:20 +0000] [76] [INFO] Application startup complete.
+2024-02-08T19:32:20.726936214Z [2024-02-08 19:32:20 +0000] [78] [INFO] Application startup complete.
+2024-02-08T19:32:20.726942614Z [2024-02-08 19:32:20 +0000] [77] [INFO] Application startup complete.
+A few notable logs:
+2024-02-08T19:30:33.441385332Z Site's appCommandLine: python3 -m gunicorn main:app
+This log indicates that App Service was correctly configured with a
+custom startup command to run the app.[2024-02-08 19:31:11 +0000] [75] [INFO] Starting gunicorn 20.1.0
+That’s the start of the gunicorn server serving the app.2024-02-08T19:32:20.726942614Z [2024-02-08 19:32:20 +0000] [77] [INFO] Application startup complete.
+At this point, the app has started successfully.If you do not see any errors in those logs, then the app should be +running successfully. If you do see errors, then try looking in Azure +Monitor.
+By default, deployed apps use Application Insights to trace and log +errors. (If you explicitly opted out of Application Insights, then you +won’t have this feature.)
+In the Azure Portal, navigate to the Application Insights for your +app.
+To see any exceptions and server errors, navigate to the +Investigate -> Failures blade and browse through the +exceptions.
+By default, the deployed app only logs messages from packages with a
+level of WARNING
or higher, but logs all messages from the
+app with a level of INFO
or higher.
These lines of code in app/backend/app.py
configure the
+logging level:
# Set root level to WARNING to avoid seeing overly verbose logs from SDKS
+=logging.WARNING)
+ logging.basicConfig(level# Set the app logger level to INFO by default
+= "INFO"
+ default_level "APP_LOG_LEVEL", default_level)) app.logger.setLevel(os.getenv(
To change the default level, either change default_level
+or set the APP_LOG_LEVEL
environment variable to one of the
+allowed
+log levels: DEBUG
, INFO
,
+WARNING
, ERROR
, CRITICAL
.
If you need to log in a route handler, use the the global variable
+current_app
’s logger:
async def chat():
+"Received /chat request") current_app.logger.info(
Otherwise, use the logging
module’s root logger:
"System message: %s", system_message) logging.info(
If you’re having troubles finding the logs in App Service, read the +section above on checking +app logs or watch this video about +viewing App Service logs.
diff --git a/data/azd.html b/data/azd.html new file mode 100644 index 0000000000..9a98ed915e --- /dev/null +++ b/data/azd.html @@ -0,0 +1,77 @@ +This guide includes advanced topics that are not necessary for a +basic deployment. If you are new to the project, please consult the main +README for steps on deploying the +project.
+📺 Watch: +Deployment of your chat app
+ +azd up
work?The azd up
command comes from the Azure
+Developer CLI, and takes care of both provisioning the Azure
+resources and deploying code to the selected Azure hosts.
The azd up
command uses the azure.yaml
file
+combined with the infrastructure-as-code .bicep
files in
+the infra/
folder. The azure.yaml
file for
+this project declares several “hooks” for the prepackage step and
+postprovision steps. The up
command first runs the
+prepackage
hook which installs Node dependencies and builds
+the React.JS-based JavaScript files. It then packages all the code (both
+frontend and backend) into a zip file which it will deploy later.
Next, it provisions the resources based on main.bicep
+and main.parameters.json
. At that point, since there is no
+default value for the OpenAI resource location, it asks you to pick a
+location from a short list of available regions. Then it will send
+requests to Azure to provision all the required resources. With
+everything provisioned, it runs the postprovision
hook to
+process the local data and add it to an Azure AI Search index.
Finally, it looks at azure.yaml
to determine the Azure
+host and uploads the zip to Azure App Service. The azd up
+command is now complete, but it may take another 5-10 minutes for the
+App Service app to be fully available and working, especially for the
+initial deploy.
Related commands are azd provision
for just provisioning
+(if infra files change) and azd deploy
for just deploying
+updated app code.
This repository includes both a GitHub Actions workflow and an Azure
+DevOps pipeline for continuous deployment with every push to
+main
. The GitHub Actions workflow is the default, but you
+can switch to Azure DevOps if you prefer.
More details are available in Learn.com: +Configure a pipeline and push updates
+After you have deployed the app once with azd up
, you
+can enable continuous deployment with GitHub Actions.
Run this command to set up a Service Principal account for CI
+deployment and to store your azd
environment variables in
+GitHub Actions secrets:
azd pipeline config
+You can trigger the “Deploy” workflow manually from your GitHub +actions, or wait for the next push to main.
+If you change your azd
environment variables at any time
+(via azd env set
or as a result of provisioning), re-run
+that command in order to update the GitHub Actions secrets.
After you have deployed the app once with azd up
, you
+can enable continuous deployment with Azure DevOps.
Run this command to set up a Service Principal account for CI
+deployment and to store your azd
environment variables in
+GitHub Actions secrets:
azd pipeline config --provider azdo
+If you change your azd
environment variables at any time
+(via azd env set
or as a result of provisioning), re-run
+that command in order to update the GitHub Actions secrets.
Due to a
+limitation of the Azure Developer CLI (azd
), there can
+be only one host option in the azure.yaml
+file. By default, host: containerapp
is used and
+host: appservice
is commented out.
To deploy to Azure App Service, please follow the following +steps:
+Comment out host: containerapp
and uncomment
+host: appservice
in the azure.yaml file.
Login to your Azure account:
+azd auth login
Create a new azd
environment to store the deployment
+parameters:
azd env new
Enter a name that will be used for the resource group. This will
+create a new folder in the .azure
folder, and set it as the
+active environment for any calls to azd
going
+forward.
Set the deployment target to appservice
:
azd env set DEPLOYMENT_TARGET appservice
(Optional) This is the point where you can customize the
+deployment by setting other azd
environment variables, in
+order to use existing resources, enable optional features (such as auth or
+vision), or deploy to free
+tiers.
Provision the resources and deploy the code:
+azd up
This will provision Azure resources and deploy this sample to those
+resources, including building the search index based on the files found
+in the ./data
folder.
Important: Beware that the resources created by this
+command will incur immediate costs, primarily from the AI Search
+resource. These resources may accrue costs even if you interrupt the
+command before it is fully executed. You can run azd down
+or delete the resources manually to avoid unnecessary spending.
Due to a
+limitation of the Azure Developer CLI (azd
), there can
+be only one host option in the azure.yaml
+file. By default, host: containerapp
is used and
+host: appservice
is commented out.
However, if you have an older version of the repo, you may need to +follow these steps to deploy to Container Apps instead, or you can stick +with Azure App Service.
+To deploy to Azure Container Apps, please follow the following +steps:
+Comment out host: appservice
and uncomment
+host: containerapp
in the azure.yaml file.
Login to your Azure account:
+azd auth login
Create a new azd
environment to store the deployment
+parameters:
azd env new
Enter a name that will be used for the resource group. This will
+create a new folder in the .azure
folder, and set it as the
+active environment for any calls to azd
going
+forward.
Set the deployment target to containerapps
:
azd env set DEPLOYMENT_TARGET containerapps
(Optional) This is the point where you can customize the +deployment by setting other `azd1 environment variables, in order to use existing resources, enable optional features (such as auth or +vision), or deploy to free +tiers.
Provision the resources and deploy the code:
+azd up
This will provision Azure resources and deploy this sample to those
+resources, including building the search index based on the files found
+in the ./data
folder.
Important: Beware that the resources created by this
+command will incur immediate costs, primarily from the AI Search
+resource. These resources may accrue costs even if you interrupt the
+command before it is fully executed. You can run azd down
+or delete the resources manually to avoid unnecessary spending.
The default workload profile is Consumption. If you want to use a +dedicated workload profile like D4, please run:
+azd env AZURE_CONTAINER_APPS_WORKLOAD_PROFILE D4
For a full list of workload profiles, please check here. +Please note dedicated workload profiles have a different billing model +than Consumption plan. Please check here +for details.
+Private endpoints is still in private preview for Azure Container +Apps and not supported for now.
diff --git a/data/customization.html b/data/customization.html new file mode 100644 index 0000000000..0bf2389b6b --- /dev/null +++ b/data/customization.html @@ -0,0 +1,309 @@ +📺 Watch: (RAG +Deep Dive series) Customizing the app
+This guide provides more details for customizing the RAG chat +app.
+The Chat App is designed to work with any PDF documents. The sample +data is provided to help you get started quickly, but you can easily +replace it with your own data. You’ll want to first remove all the +existing data, then add your own. See the data ingestion guide for more details.
+The frontend is built using React
+and Fluent UI components. The
+frontend components are stored in the app/frontend/src
+folder. To modify the page title, header text, example questions, and
+other UI elements, you can customize the
+app/frontend/src/locales/{en/es/fr/jp/it}/translation.json
+file for different languages(English is the default). The primary
+strings and labels used throughout the application are defined within
+these files.
The backend is built using Quart, a Python framework
+for asynchronous web applications. The backend code is stored in the
+app/backend
folder. The frontend and backend communicate
+using the AI Chat HTTP
+Protocol.
Typically, the primary backend code you’ll want to customize is the
+app/backend/approaches
folder, which contains the classes
+powering the Chat and Ask tabs. Each class uses a different RAG
+(Retrieval Augmented Generation) approach, which include system messages
+that should be changed to match your data
The chat tab uses the approach programmed in chatreadretrieveread.py.
+The prompts are currently tailored to the sample data since they +start with “Assistant helps the company employees with their healthcare +plan questions, and questions about the employee handbook.” Modify the +chat_query_rewrite.prompty +and chat_answer_question.prompty +prompts to match your data.
+If you followed the instructions in docs/gpt4v.md to enable a GPT Vision model and then
+select “Use GPT vision model”, then the chat tab will use the
+chatreadretrievereadvision.py
approach instead. This
+approach is similar to the chatreadretrieveread.py
+approach, with a few differences:
imageEmbeddings
fields in the
+indexed documents. For each matching document, it downloads the image
+blob and converts it to a base 64 encoding.The prompt for step 2 is currently tailored to the sample data since +it starts with “You are an intelligent assistant helping analyze the +Annual Financial Report of Contoso Ltd.”. Modify the chat_answer_question_vision.prompty +prompt to match your data.
+The ask tab uses the approach programmed in retrievethenread.py.
+The prompt for step 2 is currently tailored to the sample data since +it starts with “You are an intelligent assistant helping Contoso Inc +employees with their healthcare plan questions and employee handbook +questions.” Modify ask_answer_question.prompty +to match your data.
+If you followed the instructions in docs/gpt4v.md to enable the GPT-4 Vision model and
+then select “Use GPT vision model”, then the ask tab will use the
+retrievethenreadvision.py
approach instead. This approach
+is similar to the retrievethenread.py
approach, with a few
+differences:
imageEmbeddings
fields in the
+indexed documents. For each matching document, it downloads the image
+blob and converts it to a base 64 encoding.The prompt for step 2 is currently tailored to the sample data since +it starts with “You are an intelligent assistant helping analyze the +Annual Financial Report of Contoso Ltd”. Modify the ask_answer_question_vision.prompty +prompt to match your data.
+The UI provides a “Developer Settings” menu for customizing the +approaches, like disabling semantic ranker or using vector search. Those +settings are passed in the “context” field of the request to the +backend, and are not saved permanently. However, if you find a setting +that you do want to make permanent, there are two approaches:
+Change the defaults in the frontend. You’ll find the defaults in
+Chat.tsx
and Ask.tsx
. For example, this line
+of code sets the default retrieval mode to Hybrid:
const [retrievalMode, setRetrievalMode] = useState<RetrievalMode>(RetrievalMode.Hybrid);
You can change the default to Text by changing the code to:
+const [retrievalMode, setRetrievalMode] = useState<RetrievalMode>(RetrievalMode.Text);
Change the overrides in the backend. Each of the approaches has a
+run
method that takes a context
parameter, and
+the first line of code extracts the overrides from that
+context
. That’s where you can override any of the settings.
+For example, to change the retrieval mode to text:
= context.get("overrides", {})
+ overrides "retrieval_mode"] = "text" overrides[
By changing the setting on the backend, you can safely remove the +Developer Settings UI from the frontend, if you don’t wish to expose +that to your users.
Once you are running the chat app on your own data and with your own +tailored system prompt, the next step is to test the app with questions +and note the quality of the answers. If you notice any answers that +aren’t as good as you’d like, here’s a process for improving them.
+The first step is to identify where the problem is occurring. For +example, if using the Chat tab, the problem could be:
+You can look at the “Thought process” tab in the chat app to see each +of those steps, and determine which one is the problem.
+If the problem is with the ChatCompletion API calls (steps 1 or 3 +above), you can try changing the relevant prompt.
+Once you’ve changed the prompt, make sure you ask the same question +multiple times to see if the overall quality has improved, and run an evaluation when you’re +satisfied with the changes. The ChatCompletion API can yield different +results every time, even for a temperature of 0.0, but especially for a +higher temperature than that (like our default of 0.7 for step 3).
+You can also try changing the ChatCompletion parameters, like +temperature, to see if that improves results for your domain.
+If the problem is with Azure AI Search (step 2 above), the first step +is to check what search parameters you’re using. Generally, the best +results are found with hybrid search (text + vectors) plus the +additional semantic re-ranking step, and that’s what we’ve enabled by +default. There may be some domains where that combination isn’t optimal, +however. Check out this blog post which evaluates +AI search strategies for a better understanding of the differences, +or watch this RAG +Deep Dive video on AI Search.
+You can change many of the search parameters in the “Developer +settings” in the frontend and see if results improve for your queries. +The most relevant options:
+You may find it easier to experiment with search options with the +index explorer in the Azure Portal. Open up the Azure AI Search +resource, select the Indexes tab, and select the index there.
+Then use the JSON view of the search explorer, and make sure you +specify the same options you’re using in the app. For example, this +query represents a search with semantic ranker configured:
+{
+"search": "eye exams",
+ "queryType": "semantic",
+ "semanticConfiguration": "default",
+ "queryLanguage": "en-us",
+ "speller": "lexicon",
+ "top": 3
+ }
You can also use the highlight
parameter to see what
+text is being matched in the content
field in the search
+results.
{
+"search": "eye exams",
+ "highlight": "content"
+ ...
+ }
The search explorer works well for testing text, but is harder to use +with vectors, since you’d also need to compute the vector embedding and +send it in. It is probably easier to use the app frontend for testing +vectors/hybrid search.
+Here are additional ways for improving the search results:
+content
field.SearchableField
with
+searchable=True
in searchmanager.py.
+A change like that requires re-building
+the index.Once you’ve made changes to the prompts or settings, you’ll want to +rigorously evaluate the results to see if they’ve improved. Follow the +evaluation guide to learn how to run +evaluations, review results, and compare answers across runs.
diff --git a/data/data_ingestion.html b/data/data_ingestion.html new file mode 100644 index 0000000000..b843344202 --- /dev/null +++ b/data/data_ingestion.html @@ -0,0 +1,230 @@ +The azure-search-openai-demo project can set up a +full RAG chat app on Azure AI Search and OpenAI so that you can chat on +custom data, like internal enterprise data or domain-specific knowledge +sets. For full instructions on setting up the project, consult the main README, and then return here for detailed +instructions on the data ingestion component.
+The chat app provides two ways to ingest data: manual indexing and +integrated vectorization. This document explains the differences between +the two approaches and provides an overview of the manual indexing +process.
+In order to ingest a document format, we need a tool that can turn it +into text. By default, the manual indexing uses Azure Document +Intelligence (DI in the table below), but we also have local parsers for +several formats. The local parsers are not as sophisticated as Azure +Document Intelligence, but they can be used to decrease charges.
+Format | +Manual indexing | +Integrated Vectorization | +
---|---|---|
Yes (DI or local with PyPDF) | +Yes | +|
HTML | +Yes (DI or local with BeautifulSoup) | +Yes | +
DOCX, PPTX, XLSX | +Yes (DI) | +Yes | +
Images (JPG, PNG, BPM, TIFF, HEIFF) | +Yes (DI) | +Yes | +
TXT | +Yes (Local) | +Yes | +
JSON | +Yes (Local) | +Yes | +
CSV | +Yes (Local) | +Yes | +
The Blob indexer used by the Integrated Vectorization approach also +supports a few additional +formats.
+The prepdocs.py
+script is responsible for both uploading and indexing documents. The
+typical usage is to call it using scripts/prepdocs.sh
+(Mac/Linux) or scripts/prepdocs.ps1
(Windows), as these
+scripts will set up a Python virtual environment and pass in the
+required parameters based on the current azd
environment.
+You can pass additional arguments directly to the script, for example
+scripts/prepdocs.ps1 --removeall
. Whenever
+azd up
or azd provision
is run, the script is
+called automatically.
The script uses the following steps to index documents:
+We’re often asked why we need to break up the PDFs into chunks when +Azure AI Search supports searching large documents.
+Chunking allows us to limit the amount of information we send to +OpenAI due to token limits. By breaking up the content, it allows us to +easily find potential chunks of text that we can inject into OpenAI. The +method of chunking we use leverages a sliding window of text such that +sentences that end one chunk will start the next. This allows us to +reduce the chance of losing the context of the text.
+If needed, you can modify the chunking algorithm in
+app/backend/prepdocslib/textsplitter.py
.
To enhance search functionality, categorize data during the ingestion
+process with the --category
argument, for example
+scripts/prepdocs.ps1 --category ExampleCategoryName
. This
+argument specifies the category to which the data belongs, enabling you
+to filter search results based on these categories.
After running the script with the desired category, ensure these
+categories are added to the ‘Include Category’ dropdown list. This can
+be found in the developer settings in Settings.tsx
.
+The default option for this dropdown is “All”. By including specific
+categories, you can refine your search results more effectively.
To upload more PDFs, put them in the data/ folder and run
+./scripts/prepdocs.sh
or
+./scripts/prepdocs.ps1
.
A recent +change added checks to see what’s been uploaded before. The prepdocs +script now writes an .md5 file with an MD5 hash of each file that gets +uploaded. Whenever the prepdocs script is re-run, that hash is checked +against the current hash and the file is skipped if it hasn’t +changed.
+You may want to remove documents from the index. For example, if +you’re using the sample data, you may want to remove the documents that +are already in the index before adding your own.
+To remove all documents, use
+./scripts/prepdocs.sh --removeall
or
+./scripts/prepdocs.ps1 --removeall
.
You can also remove individual documents by using the
+--remove
flag. Open either scripts/prepdocs.sh
+or scripts/prepdocs.ps1
and replace /data/*
+with /data/YOUR-DOCUMENT-FILENAME-GOES-HERE.pdf
. Then run
+scripts/prepdocs.sh --remove
or
+scripts/prepdocs.ps1 --remove
.
Azure AI Search includes an integrated +vectorization feature, a cloud-based approach to data ingestion. +Integrated vectorization takes care of document format cracking, data +extraction, chunking, vectorization, and indexing, all with Azure +technologies.
+See this
+notebook to understand the process of setting up integrated
+vectorization. We have integrated that code into our
+prepdocs
script, so you can use it without needing to
+understand the details.
You must first explicitly enable
+integrated vectorization in the azd
environment to use
+this feature.
This feature cannot be used on existing index. You need to create a +new index or drop and recreate an existing index. In the newly created +index schema, a new field ‘parent_id’ is added. This is used internally +by the indexer to manage life cycle of chunks.
+This feature is not supported in the free SKU for Azure AI +Search.
+To add additional documents to the index, first upload them to your +data source (Blob storage, by default). Then navigate to the Azure +portal, find the index, and run it. The Azure AI Search indexer will +identify the new documents and ingest them into the index.
+To remove documents from the index, remove them from your data source +(Blob storage, by default). Then navigate to the Azure portal, find the +index, and run it. The Azure AI Search indexer will take care of +removing those documents from the index.
+If you would like the indexer to run automatically, you can set it up +to run +on a schedule.
+If you are not sure if a file successfully uploaded, you can query +the index from the Azure Portal or from the REST API. Open the index and +paste the queries below into the search bar.
+To see all the filenames uploaded to the index:
+{
+"search": "*",
+ "count": true,
+ "top": 1,
+ "facets": ["sourcefile"]
+ }
To search for specific filenames:
+{
+"search": "*",
+ "count": true,
+ "top": 1,
+ "filter": "sourcefile eq 'employee_handbook.pdf'",
+ "facets": ["sourcefile"]
+ }
If you already have existing Azure resources, or if you want to
+specify the exact name of new Azure Resource, you can do so by setting
+azd
environment values. You should set these values before
+running azd up
. Once you’ve set them, return to the deployment steps.
azd env set AZURE_RESOURCE_GROUP {Name of existing resource group}
azd env set AZURE_LOCATION {Location of existing resource group}
azd env set AZURE_OPENAI_SERVICE {Name of existing OpenAI service}
azd env set AZURE_OPENAI_RESOURCE_GROUP {Name of existing resource group that OpenAI service is provisioned to}
azd env set AZURE_OPENAI_LOCATION {Location of existing OpenAI service}
azd env set AZURE_OPENAI_CHATGPT_DEPLOYMENT {Name of existing chat deployment}
.
+Only needed if your chat deployment is not the default ‘chat’.azd env set AZURE_OPENAI_CHATGPT_MODEL {Model name of existing chat deployment}
.
+Only needed if your chat model is not the default ‘gpt-35-turbo’.azd env set AZURE_OPENAI_CHATGPT_DEPLOYMENT_VERSION {Version string for existing chat deployment}
.
+Only needed if your chat deployment model version is not the default
+‘0125’. You definitely need to change this if you changed the
+model.azd env set AZURE_OPENAI_CHATGPT_DEPLOYMENT_SKU {Name of SKU for existing chat deployment}
.
+Only needed if your chat deployment SKU is not the default ‘Standard’,
+like if it is ‘GlobalStandard’ instead.azd env set AZURE_OPENAI_EMB_DEPLOYMENT {Name of existing embedding deployment}
.
+Only needed if your embeddings deployment is not the default
+‘embedding’.azd env set AZURE_OPENAI_EMB_MODEL_NAME {Model name of existing embedding deployment}
.
+Only needed if your embeddings model is not the default
+‘text-embedding-ada-002’.azd env set AZURE_OPENAI_EMB_DIMENSIONS {Dimensions for existing embedding deployment}
.
+Only needed if your embeddings model is not the default
+‘text-embedding-ada-002’.azd env set AZURE_OPENAI_EMB_DEPLOYMENT_VERSION {Version string for existing embedding deployment}
.
+If your embeddings deployment is one of the ‘text-embedding-3’ models,
+set this to the number 1.azd env set AZURE_OPENAI_DISABLE_KEYS false
. The default
+value is true
so you should only run the command if you
+need key access.When you run azd up
after and are prompted to select a
+value for openAiResourceGroupLocation
, make sure to select
+the same location as the existing OpenAI resource group.
azd env set OPENAI_HOST openai
azd env set OPENAI_ORGANIZATION {Your OpenAI organization}
azd env set OPENAI_API_KEY {Your OpenAI API key}
azd up
You can retrieve your OpenAI key by checking your user page +and your organization by navigating to your +organization page. Learn more about creating an OpenAI free trial at +this link. Do not +check your key into source control.
+When you run azd up
after and are prompted to select a
+value for openAiResourceGroupLocation
, you can select any
+location as it will not be used.
azd env set AZURE_SEARCH_SERVICE {Name of existing Azure AI Search service}
azd env set AZURE_SEARCH_SERVICE_RESOURCE_GROUP {Name of existing resource group with ACS service}
azd up
step, then run
+azd env set AZURE_SEARCH_SERVICE_LOCATION {Location of existing service}
azd env set AZURE_SEARCH_SERVICE_SKU {Name of SKU}
. If you
+specify the free tier, then your app will no longer be able to use
+semantic ranker. Be advised that search
+SKUs cannot be changed. (See
+other possible SKU values)azd env set AZURE_SEARCH_INDEX {Name of existing index}
.
+Otherwise, the azd up
command will create a new index.You can also customize the search service (new or existing) for +non-English searches:
+azd env set AZURE_SEARCH_QUERY_LANGUAGE {Name of query language}
.
+(See
+other possible values)azd env set AZURE_SEARCH_QUERY_SPELLER none
. Consult this
+table to determine if spell checker is supported for your query
+language.azd env set AZURE_SEARCH_ANALYZER_NAME {Name of analyzer name}
.
+(See
+other possible values)azd env set AZURE_APP_SERVICE_PLAN {Name of existing Azure App Service Plan}
azd env set AZURE_APP_SERVICE {Name of existing Azure App Service}
.azd env set AZURE_APP_SERVICE_SKU {SKU of Azure App Service, defaults to B1}
.azd env set AZURE_APPLICATION_INSIGHTS {Name of existing Azure App Insights}
.azd env set AZURE_APPLICATION_INSIGHTS_DASHBOARD {Name of existing Azure App Insights Dashboard}
.azd env set AZURE_LOG_ANALYTICS {Name of existing Azure Log Analytics Workspace Name}
.azd env set AZURE_COMPUTER_VISION_SERVICE {Name of existing Azure Computer Vision Service Name}
azd env set AZURE_COMPUTER_VISION_RESOURCE_GROUP {Name of existing Azure Computer Vision Resource Group Name}
azd env set AZURE_COMPUTER_VISION_LOCATION {Name of existing Azure Computer Vision Location}
azd env set AZURE_COMPUTER_VISION_SKU {SKU of Azure Computer Vision service, defaults to F0}
In order to support analysis of many document formats, this +repository uses a preview version of Azure Document Intelligence +(formerly Form Recognizer) that is only available in limited +regions. If your existing resource is in one of those regions, then +you can re-use it by setting the following environment variables:
+azd env set AZURE_DOCUMENTINTELLIGENCE_SERVICE {Name of existing Azure AI Document Intelligence service}
azd env set AZURE_DOCUMENTINTELLIGENCE_LOCATION {Location of existing service}
azd env set AZURE_DOCUMENTINTELLIGENCE_RESOURCE_GROUP {Name of resource group with existing service, defaults to main resource group}
azd env set AZURE_DOCUMENTINTELLIGENCE_SKU {SKU of existing service, defaults to S0}
azd env set AZURE_SPEECH_SERVICE {Name of existing Azure Speech service}
azd env set AZURE_SPEECH_SERVICE_RESOURCE_GROUP {Name of existing resource group with speech service}
azd up
step, then run
+azd env set AZURE_SPEECH_SERVICE_LOCATION {Location of existing service}
azd env set AZURE_SPEECH_SERVICE_SKU {Name of SKU}
.You can also use existing Azure AI Storage Accounts. See
+./infra/main.parameters.json
for list of environment
+variables to pass to azd env set
to configure those
+existing resources.
This document covers optional features that can be enabled in the
+deployed Azure resources. You should typically enable these features
+before running azd up
. Once you’ve set them, return to the
+deployment steps.
(Instructions for GPT-4, GPT-4o, +and GPT-4o mini models are also included here.)
+We generally find that most developers are able to get high-quality +answers using GPT-3.5. However, if you want to try GPT-4, GPT-4o, or +GPT-4o mini, you can do so by following these steps:
+Execute the following commands inside your terminal:
+To set the name of the deployment, run this command with a unique +name in your Azure OpenAI account. You can use any deployment name, as +long as it’s unique in your Azure OpenAI account.
+azd env set AZURE_OPENAI_CHATGPT_DEPLOYMENT <your-deployment-name>
For example:
+azd env set AZURE_OPENAI_CHATGPT_DEPLOYMENT chat4
To set the GPT model name to a gpt-4, +gpt-4o, or gpt-4o mini version from +the available +models, run this command with the appropriate GPT model name.
+For GPT-4:
+azd env set AZURE_OPENAI_CHATGPT_MODEL gpt-4
For GPT-4o:
+azd env set AZURE_OPENAI_CHATGPT_MODEL gpt-4o
For GPT-4o mini:
+azd env set AZURE_OPENAI_CHATGPT_MODEL gpt-4o-mini
To set the Azure OpenAI deployment SKU name, run this command +with the +desired SKU name.
+azd env set AZURE_OPENAI_CHATGPT_DEPLOYMENT_SKU GlobalStandard
To set the Azure OpenAI deployment capacity, run this command +with the desired capacity.
+azd env set AZURE_OPENAI_CHATGPT_DEPLOYMENT_CAPACITY 10
To set the Azure OpenAI deployment version from the available +versions, run this command with the appropriate version.
+For GPT-4:
+azd env set AZURE_OPENAI_CHATGPT_DEPLOYMENT_VERSION turbo-2024-04-09
For GPT-4o:
+azd env set AZURE_OPENAI_CHATGPT_DEPLOYMENT_VERSION 2024-05-13
For GPT-4o mini:
+azd env set AZURE_OPENAI_CHATGPT_DEPLOYMENT_VERSION 2024-07-18
To update the deployment with the new parameters, run this +command.
+azd up
++[!NOTE] To revert back to GPT 3.5, run the following commands:
++
+- +
azd env set AZURE_OPENAI_CHATGPT_DEPLOYMENT chat
to set +the name of your old GPT 3.5 deployment.- +
azd env set AZURE_OPENAI_CHATGPT_MODEL gpt-35-turbo
to +set the name of your old GPT 3.5 model.- +
azd env set AZURE_OPENAI_CHATGPT_DEPLOYMENT_CAPACITY 30
+to set the capacity of your old GPT 3.5 deployment.- +
azd env set AZURE_OPENAI_CHATGPT_DEPLOYMENT_SKU Standard
+to set the Sku name back to Standard.- +
azd env set AZURE_OPENAI_CHATGPT_DEPLOYMENT_VERSION 0125
+to set the version number of your old GPT 3.5.- +
azd up
to update the provisioned resources.Note that this does not delete your GPT-4 deployment; it just makes +your application create a new or reuse an old GPT 3.5 deployment. If you +want to delete it, you can go to your Azure OpenAI studio and do so.
+
By default, the deployed Azure web app uses the
+text-embedding-ada-002
embedding model. If you want to use
+one of the text-embedding-3 models, you can do so by following these
+steps:
Run one of the following commands to set the desired model:
+azd env set AZURE_OPENAI_EMB_MODEL_NAME text-embedding-3-small
+azd env set AZURE_OPENAI_EMB_MODEL_NAME text-embedding-3-large
Specify the desired dimensions of the model: (from 256-3072, +model dependent)
+azd env set AZURE_OPENAI_EMB_DIMENSIONS 256
Set the model version to “1” (the only version as of March +2024):
+azd env set AZURE_OPENAI_EMB_DEPLOYMENT_VERSION 1
When prompted during azd up
, make sure to select a
+region for the OpenAI resource group location that supports the
+text-embedding-3 models. There are limited
+regions available.
If you have already deployed:
+azd env set AZURE_OPENAI_EMB_DEPLOYMENT <new-deployment-name>
azd env set AZURE_SEARCH_INDEX new-index-name
. When you
+next run azd up
, the new index will be created and the data
+will be re-indexed.openAiResourceGroupLocation
from
+.azure/YOUR-ENV-NAME/config.json
. When running
+azd up
, you will be prompted to select a new region.++![NOTE] The text-embedding-3 models are not currently supported by +the integrated vectorization feature.
+
⚠️ This feature is not currently compatible with integrated +vectorization.
+This section covers the integration of GPT-4 Vision with Azure AI +Search. Learn how to enhance your search capabilities with the power of +image and text indexing, enabling advanced search functionalities over +diverse document types. For a detailed guide on setup and usage, visit +our Enabling GPT-4 Turbo with Vision page.
+⚠️ This feature is not currently compatible with integrated vectorization. +It is compatible with GPT vision integration, +but the features provide similar functionality.
+By default, if your documents contain image-like figures, the data +ingestion process will ignore those figures, so users will not be able +to ask questions about them.
+You can optionably enable the description of media content using +Azure Content Understanding. When enabled, the data ingestion process +will send figures to Azure Content Understanding and replace the figure +with the description in the indexed document.
+To enable media description with Azure Content Understanding, +run:
+azd env set USE_MEDIA_DESCRIBER_AZURE_CU true
+If you have already run azd up
, you will need to run
+azd provision
to create the new Content Understanding
+service. If you have already indexed your documents and want to re-index
+them with the media descriptions, first remove the existing
+documents and then re-ingest the
+data.
⚠️ This feature does not yet support DOCX, PPTX, or XLSX formats. If +you have figures in those formats, they will be ignored. Convert them +first to PDF or image formats to enable media description.
+📺 Watch: (RAG +Deep Dive series) Storing chat history
+This feature allows users to view the chat history of their +conversation, stored in the browser using IndexedDB. +That means the chat history will be available only on the device where +the chat was initiated. To enable browser-stored chat history, run:
+azd env set USE_CHAT_HISTORY_BROWSER true
+📺 Watch: (RAG +Deep Dive series) Storing chat history
+This feature allows authenticated users to view the chat history of +their conversations, stored in the server-side storage using Azure Cosmos +DB.This option requires that authentication be enabled. The chat +history will be persistent and accessible from any device where the user +logs in with the same account. To enable server-stored chat history, +run:
+azd env set USE_CHAT_HISTORY_COSMOS true
+When both the browser-stored and Cosmos DB options are enabled, +Cosmos DB will take precedence over browser-stored chat history.
+You can optionally enable the language picker to allow users to +switch between different languages. Currently, it supports English, +Spanish, French, and Japanese.
+To add support for additional languages, create new locale files and
+update app/frontend/src/i18n/config.ts
accordingly. To
+enable language picker, run:
azd env set ENABLE_LANGUAGE_PICKER true
+📺 Watch a +short video of speech input/output
+You can optionally enable speech input/output by setting the azd +environment variables.
+The speech input feature uses the browser’s built-in Speech +Recognition API. It may not work in all browser/OS combinations. To +enable speech input, run:
+azd env set USE_SPEECH_INPUT_BROWSER true
+The speech output feature uses Azure +Speech Service for speech-to-text. Additional costs will be incurred +for using the Azure Speech Service. See +pricing. To enable speech output, run:
+azd env set USE_SPEECH_OUTPUT_AZURE true
+To set the +voice for the speech output, run:
+azd env set AZURE_SPEECH_SERVICE_VOICE en-US-AndrewMultilingualNeural
+Alternatively you can use the browser’s built-in Speech +Synthesis API. It may not work in all browser/OS combinations. To +enable speech output, run:
+azd env set USE_SPEECH_OUTPUT_BROWSER true
+⚠️ This feature is not currently compatible with the GPT vision integration.
+Azure AI search recently introduced an integrated +vectorization feature in preview mode. This feature is a cloud-based +approach to data ingestion, which takes care of document format +cracking, data extraction, chunking, vectorization, and indexing, all +with Azure technologies.
+To enable integrated vectorization with this sample:
+If you’ve previously deployed, delete the existing search index. +🗑️
To enable the use of integrated vectorization, run:
+azd env set USE_FEATURE_INT_VECTORIZATION true
If you’ve already deployed your app, then you can run just the
+provision
step:
azd provision
+That will set up necessary RBAC roles and configure the integrated +vectorization feature on your search service.
+If you haven’t deployed your app yet, then you should run the full
+azd up
after configuring all optional features.
You can view the resources such as the indexer and skillset in +Azure Portal and monitor the status of the vectorization +process.
By default, the deployed Azure web app will have no authentication or
+access restrictions enabled, meaning anyone with routable network access
+to the web app can chat with your indexed data. If you’d like to
+automatically setup authentication and user login as part of the
+azd up
process, see this
+guide.
Alternatively, you can manually require authentication to your Azure +Active Directory by following the Add +app authentication tutorial and set it up against the deployed web +app.
+To then limit access to a specific set of users or groups, you can
+follow the steps from Restrict
+your Microsoft Entra app to a set of users by changing “Assignment
+Required?” option under the Enterprise Application, and then assigning
+users/groups access. Users not granted explicit access will receive the
+error message -AADSTS50105: Your administrator has configured the
+application
By default, the deployed Azure web app allows users to chat with all +your indexed data. You can enable an optional login system using Azure +Active Directory to restrict access to indexed data based on the logged +in user. Enable the optional login and document level access control +system by following this guide.
+You can enable an optional user document upload system to allow users +to upload their own documents and chat with them. This feature requires +you to first enable login and document +level access control. Then you can enable the optional user document +upload system by setting an azd environment variable:
+azd env set USE_USER_UPLOAD true
Then you’ll need to run azd up
to provision an Azure
+Data Lake Storage Gen2 account for storing the user-uploaded documents.
+When the user uploads a document, it will be stored in a directory in
+that account with the same name as the user’s Entra object id, and will
+have ACLs associated with that directory. When the ingester runs, it
+will also set the oids
of the indexed chunks to the user’s
+Entra object id.
If you are enabling this feature on an existing index, you should
+also update your index to have the new storageUrl
+field:
python ./scripts/manageacl.py -v --acl-action enable_acls
+And then update existing search documents with the storage URL of the +main Blob container:
+python ./scripts/manageacl.py -v --acl-action update_storage_urls --url <https://YOUR-MAIN-STORAGE-ACCOUNT.blob.core.windows.net/content/>
+Going forward, all uploaded documents will have their
+storageUrl
set in the search index. This is necessary to
+disambiguate user-uploaded documents from admin-uploaded documents.
By default, the deployed Azure web app will only allow requests from +the same origin. To enable CORS for a frontend hosted on a different +origin, run:
+azd env set ALLOWED_ORIGIN https://<your-domain.com>
azd up
For the frontend code, change BACKEND_URI
in
+api.ts
to point at the deployed backend URL, so that all
+fetch requests will be sent to the deployed backend.
For an alternate frontend that’s written in Web Components and +deployed to Static Web Apps, check out azure-search-openai-javascript +and its guide on using +a different backend. Both these repositories adhere to the same HTTP protocol for AI chat +apps.
+As discussed in more details in our productionizing guide, you may want to +consider implementing a load balancer between OpenAI instances if you +are consistently going over the TPM limit. Fortunately, this repository +is designed for easy integration with other repositories that create +load balancers for OpenAI instances. For seamless integration +instructions with this sample, please check:
+It is possible to deploy this app with public access disabled, using
+Azure private endpoints and private DNS Zones. For more details, read the private deployment guide. That
+requires a multi-stage provisioning, so you will need to do more than
+just azd up
after setting the environment variables.
If you want to decrease the charges by using local parsers instead of +Azure Document Intelligence, you can set environment variables before +running the data ingestion script. +Note that local parsers will generally be not as sophisticated.
+azd env set USE_LOCAL_PDF_PARSER true
to use the
+local PDF parser.azd env set USE_LOCAL_HTML_PARSER true
to use the
+local HTML parser.The local parsers will be used the next time you run the data
+ingestion script. To use these parsers for the user document upload
+system, you’ll need to run azd provision
to update the web
+app to use the local parsers.
If you have just created an Azure free trial account and are using +the free trial credits, there are several modifications you need to +make, due to restrictions on the free trial account.
+Follow these instructions before you run
+azd up
.
The free trial accounts currently get a max of 1K TPM +(tokens-per-minute), whereas our Bicep templates try to allocate 30K +TPM.
+To reduce the TPM allocation, run these commands:
+azd env set AZURE_OPENAI_CHATGPT_DEPLOYMENT_CAPACITY 1
+azd env set AZURE_OPENAI_EMB_DEPLOYMENT_CAPACITY 1
+Alternatively, if you have an OpenAI.com account, you can use that +instead:
+azd env set OPENAI_HOST openai
+azd env set OPENAI_ORGANIZATION {Your OpenAI organization}
+azd env set OPENAI_API_KEY {Your OpenAI API key}
+By default, this project deploys to Azure Container Apps, using a +remote build process that builds the Docker image in the cloud. +Unfortunately, free trial accounts cannot use that remote build +process.
+You have two options:
+Comment out or delete remoteBuild: true
in
+azure.yaml
, and make sure you have Docker installed in your
+environment.
Deploy using App Service instead:
+Comment out host: containerapp
and uncomment
+host: appservice
in the azure.yaml file.
Set the deployment target to appservice
:
azd env set DEPLOYMENT_TARGET appservice
This AI RAG chat application is designed to be easily deployed using
+the Azure Developer CLI, which provisions the infrastructure according
+to the Bicep files in the infra
folder. Those files
+describe each of the Azure resources needed, and configures their SKU
+(pricing tier) and other parameters. Many Azure services offer a free
+tier, but the infrastructure files in this project do not
+default to the free tier as there are often limitations in that
+tier.
However, if your goal is to minimize costs while prototyping your
+application, follow the steps below before running
+azd up
. Once you’ve gone through these steps, return to the
+deployment steps.
📺 Live stream: +Deploying from a free account
+Log in to your Azure account using the Azure Developer CLI:
+azd auth login
Create a new azd environment for the free resource group:
+azd env new
+Enter a name that will be used for the resource group. This will
+create a new folder in the .azure
folder, and set it as the
+active environment for any calls to azd
going
+forward.
Switch from Azure Container Apps to the free tier of Azure App +Service:
+Azure Container Apps has a consumption-based pricing model that is +very low cost, but it is not free, plus Azure Container Registry costs a +small amount each month.
+To deploy to App Service instead:
+Comment out host: containerapp
and uncomment
+host: appservice
in the azure.yaml file.
Set the deployment target to appservice
:
azd env set DEPLOYMENT_TARGET appservice
Set the App Service SKU to the free tier:
+azd env set AZURE_APP_SERVICE_SKU F1
Limitation: You are only allowed a certain number of free App Service
+instances per region. If you have exceeded your limit in a region, you
+will get an error during the provisioning stage. If that happens, you
+can run azd down
, then azd env new
to create a
+new environment with a new region.
Use the free tier of Azure AI Search:
+azd env set AZURE_SEARCH_SERVICE_SKU free
+Limitations:
+Use the free tier of Azure Document Intelligence (used in +analyzing files):
+azd env set AZURE_DOCUMENTINTELLIGENCE_SKU F0
+Limitation for PDF files:
+The free tier will only scan the first two pages of each PDF. In our +sample documents, those first two pages are just title pages, so you +won’t be able to get answers from the documents. You can either use your +own documents that are only 2-pages long, or you can use a local Python +package for PDF parsing by setting:
+azd env set USE_LOCAL_PDF_PARSER true
+Limitation for HTML files:
+The free tier will only scan the first two pages of each HTML file. +So, you might not get very accurate answers from the files. You can +either use your own files that are only 2-pages long, or you can use a +local Python package for HTML parsing by setting:
+azd env set USE_LOCAL_HTML_PARSER true
Use the free tier of Azure Cosmos DB:
+azd env set AZURE_COSMOSDB_SKU free
+Limitation: You can have only one free Cosmos DB account. To keep +your account free of charge, ensure that you do not exceed the free tier +limits. For more information, see the Azure +Cosmos DB lifetime free tier.
⚠️ This step is currently only possible if you’re deploying to +App Service (see +issue 2281):
+Turn off Azure Monitor (Application Insights):
+azd env set AZURE_USE_APPLICATION_INSIGHTS false
+Application Insights is quite inexpensive already, so turning this +off may not be worth the costs saved, but it is an option for those who +want to minimize costs.
Use OpenAI.com instead of Azure OpenAI: This should not be +necessary, as the costs are same for both services, but you may need +this step if your account does not have access to Azure OpenAI for some +reason.
+azd env set OPENAI_HOST openai
+azd env set OPENAI_ORGANIZATION {Your OpenAI organization}
+azd env set OPENAI_API_KEY {Your OpenAI API key}
+Both Azure OpenAI and openai.com OpenAI accounts will incur costs, +based on tokens used, but the costs are fairly low for the amount of +sample data (less than $10).
Disable vector search:
+azd env set USE_VECTORS false
+By default, the application computes vector embeddings for documents +during the data ingestion phase, and then computes a vector embedding +for user questions asked in the application. Those computations require +an embedding model, which incurs costs per tokens used. The costs are +fairly low, so the benefits of vector search would typically outweigh +the costs, but it is possible to disable vector support. If you do so, +the application will fall back to a keyword search, which is less +accurate.
Once you’ve made the desired customizations, follow the steps in
+the README to run
+azd up
. We recommend using “eastus” as the region, for
+availability reasons.
To save costs for local development, you could use an +OpenAI-compatible model. Follow steps in local development +guide.
diff --git a/data/deploy_private.html b/data/deploy_private.html new file mode 100644 index 0000000000..a22c4d8fb4 --- /dev/null +++ b/data/deploy_private.html @@ -0,0 +1,93 @@ + +📺 Watch: (RAG +Deep Dive series) Private network deployment
+The azure-search-openai-demo project can set up a +full RAG chat app on Azure AI Search and OpenAI so that you can chat on +custom data, like internal enterprise data or domain-specific knowledge +sets. For full instructions on setting up the project, consult the main README, and then return here for detailed +instructions on configuring private endpoints.
+⚠️ This feature is not yet compatible with Azure Container Apps, so +you will need to deploy to Azure App +Service instead.
+If you want to disable public access when deploying the Chat App, you
+can do so by setting azd
environment values.
Deploying with public access disabled adds additional cost to your +deployment. Please see pricing for the following products:
+AZURE_PUBLIC_NETWORK_ACCESS
: Controls the value of
+public network access on supported Azure resources. Valid values are
+‘Enabled’ or ‘Disabled’.
+AZURE_USE_PRIVATE_ENDPOINT
: Controls deployment of private
+endpoints which connect Azure resources to the virtual network.
+AZURE_PUBLIC_NETWORK_ACCESS
is
+‘Disabled’.azd env set AZURE_USE_PRIVATE_ENDPOINT true
+azd env set AZURE_PUBLIC_NETWORK_ACCESS Enabled
+azd up
+azd env set AZURE_PUBLIC_NETWORK_ACCESS Disabled
+azd provision
+If you are experiencing an error when deploying the RAG chat solution +using the deployment steps, this +guide will help you troubleshoot common issues.
+You’re attempting to create resources in regions not enabled for +Azure OpenAI (e.g. East US 2 instead of East US), or where the model +you’re trying to use isn’t enabled. See this matrix of model +availability.
You’ve exceeded a quota, most often number of resources per +region. See this article on quotas +and limits.
You’re getting “same resource name not allowed” conflicts. That’s +likely because you’ve run the sample multiple times and deleted the +resources you’ve been creating each time, but are forgetting to purge +them. Azure keeps resources for 48 hours unless you purge from soft +delete. See this +article on purging resources.
You see CERTIFICATE_VERIFY_FAILED
when the
+prepdocs.py
script runs. That’s typically due to incorrect
+SSL certificates setup on your machine. Try the suggestions in this StackOverflow
+answer.
After running azd up
and visiting the website, you
+see a ‘404 Not Found’ in the browser. Wait 10 minutes and try again, as
+it might be still starting up. Then try running azd deploy
+and wait again. If you still encounter errors with the deployed app and
+are deploying to App Service, consult the guide on debugging App Service
+deployments. Please file an issue if the logs don’t help you resolve
+the error.
📺 Watch: (RAG +Deep Dive series) Evaluating RAG answer quality
+Follow these steps to evaluate the quality of the answers generated +by the RAG flow.
+Run this command to tell azd
to deploy a GPT-4 level
+model for evaluation:
azd env set USE_EVAL true
Set the capacity to the highest possible value to ensure that the +evaluation runs relatively quickly. Even with a high capacity, it can +take a long time to generate ground truth data and run bulk +evaluations.
+azd env set AZURE_OPENAI_EVAL_DEPLOYMENT_CAPACITY 100
+By default, that will provision a gpt-4o
model, version
+2024-08-06
. To change those settings, set the azd
+environment variables AZURE_OPENAI_EVAL_MODEL
and
+AZURE_OPENAI_EVAL_MODEL_VERSION
to the desired
+values.
Then, run the following command to provision the model:
+azd provision
Make a new Python virtual environment and activate it. This is +currently required due to incompatibilities between the dependencies of +the evaluation script and the main project.
+python -m venv .evalenv
source .evalenv/bin/activate
Install all the dependencies for the evaluation script by running the +following command:
+pip install -r evals/requirements.txt
Modify the search terms and tasks in
+evals/generate_config.json
to match your domain.
Generate ground truth data by running the following command:
+python evals/generate_ground_truth.py --numquestions=200 --numsearchdocs=1000
The options are:
+numquestions
: The number of questions to generate. We
+suggest at least 200.numsearchdocs
: The number of documents (chunks) to
+retrieve from your search index. You can leave off the option to fetch
+all documents, but that will significantly increase time it takes to
+generate ground truth data. You may want to at least start with a
+subset.kgfile
: An existing RAGAS knowledge base JSON file,
+which is usually ground_truth_kg.json
. You may want to
+specify this if you already created a knowledge base and just want to
+tweak the question generation steps.groundtruthfile
: The file to write the generated ground
+truth answwers. By default, this is
+evals/ground_truth.jsonl
.🕰️ This may take a long time, possibly several hours, depending on +the size of the search index.
+Review the generated data in evals/ground_truth.jsonl
+after running that script, removing any question/answer pairs that don’t
+seem like realistic user input.
Review the configuration in evals/eval_config.json
to
+ensure that everything is correctly setup. You may want to adjust the
+metrics used. See the
+ai-rag-chat-evaluator README for more information on the available
+metrics.
By default, the evaluation script will evaluate every question in the +ground truth data. Run the evaluation script by running the following +command:
+python evals/evaluate.py
The options are:
+numquestions
: The number of questions to evaluate. By
+default, this is all questions in the ground truth data.resultsdir
: The directory to write the evaluation
+results. By default, this is a timestamped folder in
+evals/results
. This option can also be specified in
+eval_config.json
.targeturl
: The URL of the running application to
+evaluate. By default, this is http://localhost:50505
. This
+option can also be specified in eval_config.json
.🕰️ This may take a long time, possibly several hours, depending on +the number of ground truth questions, and the TPM capacity of the +evaluation model, and the number of GPT metrics requested.
+The evaluation script will output a summary of the evaluation
+results, inside the evals/results
directory.
You can see a summary of results across all evaluation runs by +running the following command:
+python -m evaltools summary evals/results
Compare answers to the ground truth by running the following +command:
+python -m evaltools diff evals/results/baseline/
Compare answers across two runs by running the following command:
+python -m evaltools diff evals/results/baseline/ evals/results/SECONDRUNHERE
This repository includes a GitHub Action workflow
+evaluate.yaml
that can be used to run the evaluation on the
+changes in a PR.
In order for the workflow to run successfully, you must first set up +continuous integration for the +repository.
+To run the evaluation on the changes in a PR, a repository member can
+post a /evaluate
comment to the PR. This will trigger the
+evaluation workflow to run the evaluation on the PR changes and will
+post the results to the PR.
📺 Watch: (RAG +Deep Dive series) Multimedia data ingestion
+This repository includes an optional feature that uses the GPT vision +model to generate responses based on retrieved content. This feature is +useful for answering questions based on the visual content of documents, +such as photos and charts.
+When this feature is enabled, the following changes are made to the +application:
+For more details on how this feature works, read this +blog post or watch this +video.
+Enable GPT vision approach:
+First, make sure you do not have integrated vectorization +enabled, since that is currently incompatible:
+azd env set USE_FEATURE_INT_VECTORIZATION false
+Then set the environment variable for enabling vision support:
+azd env set USE_GPT4V true
+When set, that flag will provision a Azure AI Vision resource and
+gpt-4o model, upload image versions of PDFs to Blob storage, upload
+embeddings of images in a new imageEmbedding
field, and
+enable the vision approach in the UI.
Clean old deployments (optional): Run
+azd down --purge
for a fresh setup.
Start the application: Execute
+azd up
to build, provision, deploy, and initiate document
+preparation.
Try out the feature:
+After deploying the app to Azure, you may want to continue +development locally. This guide explains how to run the app locally, +including hot reloading and debugging.
+You can only run locally after having successfully
+run the azd up
command. If you haven’t yet, follow the
+steps in Azure deployment
+above.
azd auth login
Windows:
+./app/start.ps1
+Linux/Mac:
+./app/start.sh
+VS Code: Run the “VS Code Task: Start App” task.
+When you run ./start.ps1
or ./start.sh
, the
+backend files will be watched and reloaded automatically. However, the
+frontend files will not be watched and reloaded automatically.
To enable hot reloading of frontend files, open a new terminal and +navigate to the frontend directory:
+cd app/frontend
+Then run:
+npm run dev
+You should see:
+> frontend@0.0.0 dev
+> vite
+
+
+ VITE v4.5.1 ready in 957 ms
+
+ ➜ Local: http://localhost:5173/
+ ➜ Network: use --host to expose
+ ➜ press h to show help
+Navigate to the URL shown in the terminal (in this case,
+http://localhost:5173/
). This local server will watch and
+reload frontend files. All backend requests will be routed to the Python
+server according to vite.config.ts
.
Then, whenever you make changes to frontend files, the changes will +be automatically reloaded, without any browser refresh needed.
+This project includes configurations defined in
+.vscode/launch.json
that allow you to run and debug the app
+directly from VS Code:
When you run these configurations, you can set breakpoints in your +code and debug as you would in a normal VS Code debugging session.
+You may want to save costs by developing against a local LLM server, +such as llamafile. Note +that a local LLM will generally be slower and not as sophisticated.
+Once the local LLM server is running and serving an OpenAI-compatible +endpoint, set these environment variables:
+azd env set USE_VECTORS false
+azd env set OPENAI_HOST local
+azd env set OPENAI_BASE_URL <your local endpoint>
+azd env set AZURE_OPENAI_CHATGPT_MODEL local-model-name
+Then restart the local development server. You should now be able to +use the “Ask” tab.
+⚠️ Limitations:
+++[!NOTE] You must set
+OPENAI_HOST
back to a non-local +value (“azure”, “azure_custom”, or “openai”) before running +azd up
orazd provision
, since the deployed +backend can’t access your local server.
For example, to point at a local Ollama server running the
+llama3.1:8b
model:
azd env set OPENAI_HOST local
+azd env set OPENAI_BASE_URL http://localhost:11434/v1
+azd env set AZURE_OPENAI_CHATGPT_MODEL llama3.1:8b
+azd env set USE_VECTORS false
+If you’re running the app inside a VS Code Dev Container, use this +local URL instead:
+azd env set OPENAI_BASE_URL http://host.docker.internal:11434/v1
+To point at a local llamafile server running on its default port:
+azd env set OPENAI_HOST local
+azd env set OPENAI_BASE_URL http://localhost:8080/v1
+azd env set USE_VECTORS false
+Llamafile does not require a model name to be specified.
+If you’re running the app inside a VS Code Dev Container, use this +local URL instead:
+azd env set OPENAI_BASE_URL http://host.docker.internal:8080/v1
diff --git a/data/login_and_acl.html b/data/login_and_acl.html
new file mode 100644
index 0000000000..35cda45c12
--- /dev/null
+++ b/data/login_and_acl.html
@@ -0,0 +1,689 @@
+
+📺 Watch: (RAG +Deep Dive series) Login and access control
+The azure-search-openai-demo project can set up a +full RAG chat app on Azure AI Search and OpenAI so that you can chat on +custom data, like internal enterprise data or domain-specific knowledge +sets. For full instructions on setting up the project, consult the main README, and then return here for detailed +instructions on configuring login and access control.
+This guide demonstrates how to add an optional login and document +level access control system to the sample. This system can be used to +restrict access to indexed data to specific users based on what Microsoft +Entra groups they are a part of, or their user +object id.
+IMPORTANT: In order to add optional login and +document level access control, you’ll need the following in addition to +the normal sample requirements
+Two Microsoft Entra applications must be registered in order to make +the optional login and document level access control system work +correctly. One app is for the client UI. The client UI is implemented as +a single +page application. The other app is for the API server. The API +server uses a confidential +client to call the Microsoft Graph +API.
+The easiest way to setup the two apps is to use the azd
+CLI. We’ve written scripts that will automatically create the two apps
+and configure them for use with the sample. To trigger the automatic
+setup, run the following commands:
azd env set AZURE_USE_AUTHENTICATION true
to enable
+the login UI and use App Service authentication by default.AZURE_USE_AUTHENTICATION
set to true
. If your
+index already exists, run
+python ./scripts/manageacl.py --acl-action enable_acls
.azd env set AZURE_ENFORCE_ACCESS_CONTROL true
.
+Authentication is always required to search on documents with access
+control assigned, regardless of if unauthenticated access is enabled or
+not.azd env set AZURE_ENABLE_GLOBAL_DOCUMENT_ACCESS true
.azd env set AZURE_ENABLE_UNAUTHENTICATED_ACCESS true
.
+AZURE_ENABLE_GLOBAL_DOCUMENT_ACCESS
should also be set to
+true if you want unauthenticated users to be able to search on documents
+with no access control.azd env set AZURE_AUTH_TENANT_ID <YOUR-TENANT-ID>
to
+set the tenant ID associated with authentication.azd auth login --tenant-id <YOUR-TENANT-ID>
to login
+to the authentication tenant simultaneously.azd up
to deploy the app.The following instructions explain how to setup the two apps using +the Azure Portal.
+Sign in to the Azure +portal.
Select the Microsoft Entra ID service.
In the left hand menu, select Application +Registrations.
Select New Registration.
+Azure Search OpenAI Chat API
.Select Register to create the +application
In the app’s registration screen, find the Application +(client) ID.
+azd
command to save this ID:
+azd env set AZURE_SERVER_APP_ID <Application (client) ID>
.Microsoft Entra supports three types of credentials to +authenticate an app using the client +credentials: passwords (app secrets), certificates, and federated +identity credentials. For a higher level of security, either certificates +or federated identity credentials are recommended. This sample currently +uses an app secret for ease of provisioning.
Select Certificates & secrets in the left +hand menu.
In the Client secrets section, select +New client secret.
+Azure Search OpenAI Chat Key
.azd
+command to save this ID:
+azd env set AZURE_SERVER_APP_SECRET <generated key value>
.Select API Permissions in the left hand menu. By
+default, the delegated
+User.Read
permission should be present. This permission
+is required to read the signed-in user’s profile to get the security
+information used for document level access control. If this permission
+is not present, it needs to be added to the application.
User.Read
.Select Expose an API in the left hand menu. The +server app works by using the On +Behalf Of Flow, which requires the server app to expose at least 1 +API.
+api://<application client id>
. Accept the default by
+selecting Save.(Optional) Enable group claims. Include which Microsoft Entra +groups the user is part of as part of the login in the optional +claims. The groups are used for optional +security filtering in the search results.
+Azure Search OpenAI Chat Web App
.Redirect URI (optional)
section, select
+Single-page application (SPA)
in the combo-box and enter
+the following redirect URI:
+http://localhost:50505/redirect
and
+http://localhost:5173/redirect
azd up
:
+https://<your-endpoint>.azurewebsites.net/redirect
.https://<your-codespace>-50505.app.github.dev/redirect
azd
command to save this ID:
+azd env set AZURE_CLIENT_APP_ID <Application (client) ID>
.azd up
:
+https://<your-endpoint>.azurewebsites.net/.auth/login/aad/callback
.User.Read
.Consent from the user must be obtained for use of the client and +server app. The client app can prompt the user for consent through a +dialog when they log in. The server app has no ability to show a dialog +for consent. Client apps can be added +to the list of known clients to access the server app, so a consent +dialog is shown for the server app.
+"knownClientApplications": []
with
+"knownClientApplications": ["<client application id>"]
If you are running setup for the first time, ensure you have run
+azd env set AZURE_ADLS_GEN2_STORAGE_ACCOUNT <YOUR-STORAGE_ACCOUNT>
+before running azd up
. If you do not set this environment
+variable, your index will not be initialized with access control support
+when prepdocs
is run for the first time. To manually enable
+access control in your index, use the manual setup script.
Ensure you run azd env set AZURE_USE_AUTHENTICATION
to
+enable the login UI once you have setup the two Microsoft Entra apps
+before you deploy or run the application. The login UI will not appear
+unless all required
+environment variables have been setup.
In both the chat and ask a question modes, under Developer
+settings optional Use oid security filter and
+Use groups security filter checkboxes will appear. The
+oid (User ID) filter maps to the oids
field in the search
+index and the groups (Group ID) filter maps to the groups
+field in the search index. If AZURE_ENFORCE_ACCESS_CONTROL
+has been set, then both the Use oid security filter and
+Use groups security filter options are always enabled
+and cannot be disabled.
If you want to use the chat endpoint without the UI and still use
+authentication, you must disable App
+Service built-in authentication and use only the app’s MSAL-based
+authentication flow. Ensure the
+AZURE_DISABLE_APP_SERVICES_AUTHENTICATION
environment
+variable is set before deploying.
Get an access token that can be used for calling the chat API using +the following code:
+from azure.identity import DefaultAzureCredential
+import os
+
+= DefaultAzureCredential().get_token(f"api://{os.environ['AZURE_SERVER_APP_ID']}/access_as_user", tenant_id=os.getenv('AZURE_AUTH_TENANT_ID', os.getenv('AZURE_TENANT_ID')))
+ token
+print(token.token)
azd env set AZURE_AUTH_TENANT_ID <YOUR-AUTH-TENANT-ID>
+before running azd up
."prompt": "consent"
to the loginRequest
+property in authentication.py
The sample supports 2 main strategies for adding data with document +level access control.
+Manually enable document level access control on a search index and +manually set access control values using the manageacl.py script.
+Prior to running the script:
+azd up
or use azd env set
to manually
+set the AZURE_SEARCH_SERVICE
and
+AZURE_SEARCH_INDEX
azd environment variablesThe script supports the following commands. All commands support
+-v
for verbose logging.
python ./scripts/manageacl.py --acl-action enable_acls
:
+Creates the required oids
(User ID) and groups
+(Group IDs) security
+filter fields for document level access control on your index, as
+well as the storageUrl
field for storing the Blob storage
+URL. Does nothing if these fields already exist.
Example usage:
+python ./scripts/manageacl.py -v --acl-action enable_acls
python ./scripts/manageacl.py --acl-type [oids or groups]--acl-action view --url [https://url.pdf]
:
+Prints access control values associated with either User IDs or Group
+IDs for the document at the specified URL.
Example to view all Group IDs:
+python ./scripts/manageacl.py -v --acl-type groups --acl-action view --url https://st12345.blob.core.windows.net/content/Benefit_Options.pdf
python ./scripts/manageacl.py --acl-type [oids or groups]--acl-action add --acl [ID of group or user] --url [https://url.pdf]
:
+Adds an access control value associated with either User IDs or Group
+IDs for the document at the specified URL.
Example to add a Group ID:
+python ./scripts/manageacl.py -v --acl-type groups --acl-action add --acl xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx --url https://st12345.blob.core.windows.net/content/Benefit_Options.pdf
python ./scripts/manageacl.py --acl-type [oids or groups]--acl-action remove_all --url [https://url.pdf]
:
+Removes all access control values associated with either User IDs or
+Group IDs for a specific document.
Example to remove all Group IDs:
+python ./scripts/manageacl.py -v --acl-type groups --acl-action remove_all --url https://st12345.blob.core.windows.net/content/Benefit_Options.pdf
python ./scripts/manageacl.py --url [https://url.pdf] --acl-type [oids or groups]--acl-action remove --acl [ID of group or user]
:
+Removes an access control value associated with either User IDs or Group
+IDs for a specific document.
Example to remove a specific User ID:
+python ./scripts/manageacl.py -v --acl-type oids --acl-action remove --acl xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx --url https://st12345.blob.core.windows.net/content/Benefit_Options.pdf
Azure +Data Lake Storage Gen2 implements an access +control model that can be used for document level access control. +The adlsgen2setup.py script +uploads the sample data included in the data folder +to a Data Lake Storage Gen2 storage account. The Storage +Blob Data Owner role is required to use the script.
+In order to use this script, an existing Data Lake Storage Gen2
+storage account is required. Run
+azd env set AZURE_ADLS_GEN2_STORAGE_ACCOUNT <your-storage-account>
+prior to running the script.
Then run the script inside your Python environment:
+python /scripts/adlsgen2setup.py './data/*' --data-access-control './scripts/sampleacls.json' -v
+The script performs the following steps:
+gptkbcontainer
in the
+storage account.In order to use the sample access control, you need to join these +groups in your Microsoft Entra tenant.
+Note that this optional script may not work in Codespaces if your +administrator has applied a Conditional +Access policy to your tenant.
+Once a Data Lake Storage Gen2 storage account has been setup with +sample data and access control lists, prepdocs.py can be used to +automatically process PDFs in the storage account and store them with +their access +control lists in the search index.
+To run this script with a Data Lake Storage Gen2 account, first set +the following environment variables:
+AZURE_ADLS_GEN2_STORAGE_ACCOUNT
: Name of existing Data
+Lake Storage Gen2 storage account.AZURE_ADLS_GEN2_FILESYSTEM
: Name of existing
+Data Lake Storage Gen2 filesystem / container in the storage account. If
+empty, gptkbcontainer
is used.AZURE_ADLS_GEN2_FILESYSTEM_PATH
: Specific
+path in the Data Lake Storage Gen2 filesystem / container to process.
+Only PDFs contained in this path will be processed.Once the environment variables are set, run the script using the
+following command: /scripts/prepdocs.ps1
or
+/scripts/prepdocs.sh
.
The following environment variables are used to setup the optional +login and document level access control:
+AZURE_USE_AUTHENTICATION
: Enables Entra ID login and
+document level access control. Set to true before running
+azd up
.AZURE_ENFORCE_ACCESS_CONTROL
: Enforces Entra ID based
+login and document level access control on documents with access control
+assigned. Set to true before running azd up
. If
+AZURE_ENFORCE_ACCESS_CONTROL
is enabled and
+AZURE_ENABLE_UNAUTHENTICATED_ACCESS
is not enabled, then
+authentication is required to use the app.AZURE_ENABLE_GLOBAL_DOCUMENT_ACCESS
: Allows users to
+search on documents that have no access controls assignedAZURE_ENABLE_UNAUTHENTICATED_ACCESS
: Allows
+unauthenticated users to access the chat app, even when
+AZURE_ENFORCE_ACCESS_CONTROL
is enabled.
+AZURE_ENABLE_GLOBAL_DOCUMENT_ACCESS
should be set to true
+to allow unauthenticated users to search on documents that have no
+access control assigned. Unauthenticated users cannot search on
+documents with access control assigned.AZURE_DISABLE_APP_SERVICES_AUTHENTICATION
: Disables use
+of built-in authentication for App Services. An authentication flow
+based on the MSAL SDKs is used instead. Useful when you want to provide
+programmatic access to the chat endpoints with authentication.AZURE_SERVER_APP_ID
: (Required) Application ID of the
+Microsoft Entra app for the API server.AZURE_SERVER_APP_SECRET
: Client
+secret used by the API server to authenticate using the Microsoft
+Entra server app.AZURE_CLIENT_APP_ID
: Application ID of the Microsoft
+Entra app for the client UI.AZURE_AUTH_TENANT_ID
: Tenant
+ID associated with the Microsoft Entra tenant used for login and
+document level access control. Defaults to AZURE_TENANT_ID
+if not defined.AZURE_ADLS_GEN2_STORAGE_ACCOUNT
: (Optional) Name of
+existing Data
+Lake Storage Gen2 storage account for storing sample data with access
+control lists. Only used with the optional Data Lake Storage Gen2 setup and prep docs
+scripts.AZURE_ADLS_GEN2_FILESYSTEM
: (Optional) Name of existing
+Data
+Lake Storage Gen2 filesystem for storing sample data with access
+control lists. Only used with the optional Data Lake Storage Gen2 setup and prep docs
+scripts.AZURE_ADLS_GEN2_FILESYSTEM_PATH
: (Optional) Name of
+existing path in a Data
+Lake Storage Gen2 filesystem for storing sample data with access
+control lists. Only used with the optional Data Lake Storage Gen2 prep docs
+script.This application uses an in-memory token cache. User sessions are +only available in memory while the application is running. When the +application server is restarted, all users will need to log-in +again.
+The following table describes the impact of the
+AZURE_USE_AUTHENTICATION
and
+AZURE_ENFORCE_ACCESS_CONTROL
variables depending on the
+environment you are deploying the application in:
AZURE_USE_AUTHENTICATION | +AZURE_ENFORCE_ACCESS_CONTROL | +Environment | +Default Behavior | +
---|---|---|---|
True | +False | +App Services | +Use integrated auth Login page blocks access to app +User can opt-into access control in developer settings Allows +unrestricted access to sources |
+
True | +True | +App Services | +Use integrated auth Login page blocks access to app +User must use access control |
+
True | +False | +Local or Codespaces | +Do not use integrated auth Can use app without login +User can opt-into access control in developer settings Allows +unrestricted access to sources |
+
True | +True | +Local or Codespaces | +Do not use integrated auth Cannot use app without login + Behavior is chat box is greyed out with default “Please login +message” User must use login button to make chat box usable + User must use access control when logged in |
+
False | +False | +All | +No login or access control | +
False | +True | +All | +Invalid setting | +
By default, deployed apps use Application Insights for the tracing of +each request, along with the logging of errors.
+ +To see the performance data, go to the Application Insights resource +in your resource group, click on the “Investigate -> Performance” +blade and navigate to any HTTP request to see the timing data. To +inspect the performance of chat requests, use the “Drill into Samples” +button to see end-to-end traces of all the API calls made for any chat +request:
+To see any exceptions and server errors, navigate to the “Investigate +-> Failures” blade and use the filtering tools to locate a specific +exception. You can see Python stack traces on the right-hand side.
+You can see chart summaries on a dashboard by running the following +command:
+azd monitor
+You can modify the contents of that dashboard by updating
+infra/backend-dashboard.bicep
, which is a Bicep file that
+defines the dashboard contents and layout.
The tracing is done using these OpenTelemetry Python packages:
+Those packages are configured in the app.py
file:
if os.getenv("APPLICATIONINSIGHTS_CONNECTION_STRING"):
+
+ configure_azure_monitor()# This tracks HTTP requests made by aiohttp:
+
+ AioHttpClientInstrumentor().instrument()# This tracks HTTP requests made by httpx:
+
+ HTTPXClientInstrumentor().instrument()# This tracks OpenAI SDK requests:
+
+ OpenAIInstrumentor().instrument()# This middleware tracks app route requests:
+ = OpenTelemetryMiddleware(app.asgi_app) app.asgi_app
You can pass in parameters to configure_azure_monitor()
+to customize the tracing, like to add custom span processors. You can
+also set OpenTelemetry
+environment variables to customize the tracing, like to set the
+sampling rate. See the azure-monitor-opentelemetry
+documentation for more details.
By default, opentelemetry-instrumentation-openai
+traces all requests made to the OpenAI API, including the messages and
+responses. To disable that for privacy reasons, set the
+TRACELOOP_TRACE_CONTENT=false
environment variable.
To set environment variables, update appEnvVariables
in
+infra/main.bicep
and re-run azd up
.
There are an increasingly large number of ways to build RAG chat +apps.
+Inspired by this repo, there are similar apps for other +languages:
+They do not all support the same features as this repo, but they +provide a good starting point for building a RAG chat app in your +preferred language.
+Another popular repository for this use case is here: https://github.com/Microsoft/sample-app-aoai-chatGPT/
+That repository is designed for use by customers using Azure OpenAI
+studio and Azure Portal for setup. It also includes azd
+support for folks who want to deploy it completely from scratch.
The primary differences:
+Feature comparison:
+Feature | +azure-search-openai-demo | +sample-app-aoai-chatGPT | +
---|---|---|
RAG approach | +Multiple approaches | +Only via ChatCompletion API data_sources | +
Vector support | +✅ Yes | +✅ Yes | +
Data ingestion | +✅ Yes (Many +formats) | +✅ Yes (Many +formats) | +
Persistent chat history | +✅ Yes | +✅ Yes | +
User feedback | +❌ No | +✅ Yes | +
GPT-4-vision | +✅ Yes | +❌ No | +
Auth + ACL | +✅ Yes | +✅ Yes | +
User upload | +✅ Yes | +❌ No | +
Speech I/O | +✅ Yes | +❌ No | +
Technology comparison:
+Tech | +azure-search-openai-demo | +sample-app-aoai-chatGPT | +
---|---|---|
Frontend | +React | +React | +
Backend | +Python (Quart) | +Python (Quart) | +
Vector DB | +Azure AI Search | +Azure AI Search, CosmosDB Mongo vCore, ElasticSearch, Pinecone, +AzureML | +
Deployment | +Azure Developer CLI (azd) | +Azure Portal, az, azd | +
This sample is designed to be a starting point for your own +production application, but you should do a thorough review of the +security and performance before deploying to production. Here are some +things to consider:
+ +The default TPM (tokens per minute) is set to 30K. That is equivalent
+to approximately 30 conversations per minute (assuming 1K per user
+message/response). You can increase the capacity by changing the
+chatGptDeploymentCapacity
and
+embeddingDeploymentCapacity
parameters in
+infra/main.bicep
to your account’s maximum capacity. You
+can also view the Quotas tab in Azure
+OpenAI studio to understand how much capacity you have.
If the maximum TPM isn’t enough for your expected load, you have a +few options:
+Use a backoff mechanism to retry the request. This is helpful if +you’re running into a short-term quota due to bursts of activity but +aren’t over long-term quota. The tenacity library +is a good option for this, and this pull +request shows how to apply it to this app.
If you are consistently going over the TPM, then consider +implementing a load balancer between OpenAI instances. Most developers +implement that using Azure API Management or container-based load +balancers. A native Python approach that integrates with the OpenAI +Python API Library is also possible. For integration instructions with +this sample, please check:
+The default storage account uses the Standard_LRS
SKU.
+To improve your resiliency, we recommend using Standard_ZRS
+for production deployments, which you can specify using the
+sku
property under the storage
module in
+infra/main.bicep
.
The default search service uses the “Basic” SKU with the free +semantic ranker option, which gives you 1000 free queries a month. After +1000 queries, you will get an error message about exceeding the semantic +ranker free capacity.
+Assuming your app will experience more than 1000 questions per +month, you should upgrade the semantic ranker SKU from “free” to +“standard” SKU:
+azd env set AZURE_SEARCH_SEMANTIC_RANKER standard
+Or disable semantic search entirely:
+azd env set AZURE_SEARCH_SEMANTIC_RANKER disabled
The search service can handle fairly large indexes, but it does
+have per-SKU limits on storage sizes, maximum vector dimensions, etc.
+You may want to upgrade the SKU to either a Standard or Storage
+Optimized SKU, depending on your expected load. However, you cannot
+change the SKU of an existing search service, so you will need to
+re-index the data or manually copy it over. You can change the SKU by
+setting the AZURE_SEARCH_SERVICE_SKU
azd environment
+variable to an
+allowed SKU.
azd env set AZURE_SEARCH_SERVICE_SKU standard
+See the Azure +AI Search service limits documentation for more details.
If you see errors about search service capacity being exceeded,
+you may find it helpful to increase the number of replicas by changing
+replicaCount
in
+infra/core/search/search-services.bicep
or manually scaling
+it from the Azure Portal.
The default app service plan uses the Basic
SKU with 1
+CPU core and 1.75 GB RAM. We recommend using a Premium level SKU,
+starting with 1 CPU core. You can use auto-scaling rules or scheduled
+scaling rules, and scale up the maximum/minimum based on load.
We recommend running a loadtest for your expected number of users.
+You can use the locust tool with
+the locustfile.py
in this sample or set up a loadtest with
+Azure Load Testing.
To use locust, first install the dev requirements that includes +locust:
+python -m pip install -r requirements-dev.txt
+Or manually install locust:
+python -m pip install locust
+Then run the locust command, specifying the name of the User class to
+use from locustfile.py
. We’ve provided a
+ChatUser
class that simulates a user asking questions and
+receiving answers, as well as a ChatVisionUser
to simulate
+a user asking questions with the GPT-4 vision
+mode enabled.
locust ChatUser
+Open the locust UI at http://localhost:8089/, the URI +displayed in the terminal.
+Start a new test with the URI of your website,
+e.g. https://my-chat-app.azurewebsites.net
. Do not
+end the URI with a slash. You can start by pointing at your localhost if
+you’re concerned more about load on OpenAI/AI Search than the host
+platform.
For the number of users and spawn rate, we recommend starting with 20 +users and 1 users/second. From there, you can keep increasing the number +of users to simulate your expected load.
+Here’s an example loadtest for 50 users and a spawn rate of 1 per +second:
+After each test, check the local or App Service logs to see if there +are any errors.
+Before you make your chat app available to users, you’ll want to +rigorously evaluate the answer quality. You can use tools in the AI RAG +Chat evaluator repository to run evaluations, review results, and +compare answers across runs.
diff --git a/data/sharing_environments.html b/data/sharing_environments.html new file mode 100644 index 0000000000..d3a08fd66d --- /dev/null +++ b/data/sharing_environments.html @@ -0,0 +1,27 @@ +If you’ve deployed the RAG chat solution already following the steps +in the deployment guide, you may +want to share the environment with a colleague. Either you or they can +follow these steps:
+azd init -t azure-search-openai-demo
or clone this
+repository.azd env refresh -e {environment name}
They will
+need the azd environment name, subscription ID, and location to run this
+command. You can find those values in your
+.azure/{env name}/.env
file. This will populate their azd
+environment’s .env
file with all the settings needed to run
+the app locally.AZURE_PRINCIPAL_ID
either
+in that .env
file or in the active shell to their Azure ID,
+which they can get with az ad signed-in-user show
../scripts/roles.ps1
or
+.scripts/roles.sh
to assign all of the necessary roles to
+the user. If they do not have the necessary permission to create roles
+in the subscription, then you may need to run this script for them. Once
+the script runs, they should be able to run the app locally.