diff --git a/.circleci/config.yml b/.circleci/config.yml index fc73e456ba..5f3ed20ac1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -31,6 +31,7 @@ jobs: pip install "google-generativeai>=0.3.2" pip install "google-cloud-aiplatform>=1.38.0" pip install "boto3>=1.28.57" + pip install "aioboto3>=12.3.0" pip install langchain pip install "langfuse>=2.0.0" pip install numpydoc @@ -42,7 +43,10 @@ jobs: pip install "anyio==3.7.1" pip install "aiodynamo==23.10.1" pip install "asyncio==3.4.3" + pip install "apscheduler==3.10.4" pip install "PyGithub==1.59.1" + pip install argon2-cffi + pip install python-multipart - save_cache: paths: - ./venv @@ -86,6 +90,32 @@ jobs: - store_test_results: path: test-results + installing_litellm_on_python: + docker: + - image: circleci/python:3.8 + working_directory: ~/project + + steps: + - checkout + - run: + name: Install Dependencies + command: | + python -m pip install --upgrade pip + pip install python-dotenv + pip install pytest + pip install tiktoken + pip install aiohttp + pip install click + pip install jinja2 + pip install tokenizers + pip install openai + - run: + name: Run tests + command: | + pwd + ls + python -m pytest -vv litellm/tests/test_python_38.py + build_and_test: machine: image: ubuntu-2204:2023.10.1 @@ -114,6 +144,27 @@ jobs: pip install "pytest==7.3.1" pip install "pytest-asyncio==0.21.1" pip install aiohttp + pip install openai + python -m pip install --upgrade pip + python -m pip install -r .circleci/requirements.txt + pip install "pytest==7.3.1" + pip install "pytest-asyncio==0.21.1" + pip install mypy + pip install "google-generativeai>=0.3.2" + pip install "google-cloud-aiplatform>=1.38.0" + pip install "boto3>=1.28.57" + pip install "aioboto3>=12.3.0" + pip install langchain + pip install "langfuse>=2.0.0" + pip install numpydoc + pip install prisma + pip install fastapi + pip install "httpx==0.24.1" + pip install "gunicorn==21.2.0" + pip install "anyio==3.7.1" + pip install "aiodynamo==23.10.1" + pip install "asyncio==3.4.3" + pip install "PyGithub==1.59.1" # Run pytest and generate JUnit XML report - run: name: Build Docker image @@ -124,15 +175,21 @@ jobs: docker run -d \ -p 4000:4000 \ -e DATABASE_URL=$PROXY_DOCKER_DB_URL \ - -e AZURE_API_KEY=$AZURE_FRANCE_API_KEY \ + -e AZURE_API_KEY=$AZURE_API_KEY \ -e AZURE_FRANCE_API_KEY=$AZURE_FRANCE_API_KEY \ -e AZURE_EUROPE_API_KEY=$AZURE_EUROPE_API_KEY \ + -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID \ + -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY \ + -e AWS_REGION_NAME=$AWS_REGION_NAME \ + -e OPENAI_API_KEY=$OPENAI_API_KEY \ --name my-app \ -v $(pwd)/proxy_server_config.yaml:/app/config.yaml \ my-app:latest \ --config /app/config.yaml \ --port 4000 \ - --num_workers 8 + --num_workers 8 \ + --detailed_debug \ + --run_gunicorn \ - run: name: Install curl and dockerize command: | @@ -143,11 +200,7 @@ jobs: sudo rm dockerize-linux-amd64-v0.6.1.tar.gz - run: name: Start outputting logs - command: | - while true; do - docker logs my-app - sleep 10 - done + command: docker logs -f my-app background: true - run: name: Wait for app to be ready @@ -251,6 +304,12 @@ workflows: only: - main - /litellm_.*/ + - installing_litellm_on_python: + filters: + branches: + only: + - main + - /litellm_.*/ - publish_to_pypi: requires: - local_testing diff --git a/.circleci/requirements.txt b/.circleci/requirements.txt index 85b576bff2..4730fc28b1 100644 --- a/.circleci/requirements.txt +++ b/.circleci/requirements.txt @@ -10,4 +10,5 @@ anthropic boto3 orjson pydantic -google-cloud-aiplatform \ No newline at end of file +google-cloud-aiplatform +redisvl==0.0.7 # semantic caching \ No newline at end of file diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..efff383d43 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +/docs +/cookbook +/.circleci +/.github +/tests \ No newline at end of file diff --git a/.github/actions/helm-oci-chart-releaser/action.yml b/.github/actions/helm-oci-chart-releaser/action.yml new file mode 100644 index 0000000000..059277ed88 --- /dev/null +++ b/.github/actions/helm-oci-chart-releaser/action.yml @@ -0,0 +1,77 @@ +name: Helm OCI Chart Releaser +description: Push Helm charts to OCI-based (Docker) registries +author: sergeyshaykhullin +branding: + color: yellow + icon: upload-cloud +inputs: + name: + required: true + description: Chart name + repository: + required: true + description: Chart repository name + tag: + required: true + description: Chart version + app_version: + required: true + description: App version + path: + required: false + description: Chart path (Default 'charts/{name}') + registry: + required: true + description: OCI registry + registry_username: + required: true + description: OCI registry username + registry_password: + required: true + description: OCI registry password + update_dependencies: + required: false + default: 'false' + description: Update chart dependencies before packaging (Default 'false') +outputs: + image: + value: ${{ steps.output.outputs.image }} + description: Chart image (Default '{registry}/{repository}/{image}:{tag}') +runs: + using: composite + steps: + - name: Helm | Login + shell: bash + run: echo ${{ inputs.registry_password }} | helm registry login -u ${{ inputs.registry_username }} --password-stdin ${{ inputs.registry }} + env: + HELM_EXPERIMENTAL_OCI: '1' + + - name: Helm | Dependency + if: inputs.update_dependencies == 'true' + shell: bash + run: helm dependency update ${{ inputs.path == null && format('{0}/{1}', 'charts', inputs.name) || inputs.path }} + env: + HELM_EXPERIMENTAL_OCI: '1' + + - name: Helm | Package + shell: bash + run: helm package ${{ inputs.path == null && format('{0}/{1}', 'charts', inputs.name) || inputs.path }} --version ${{ inputs.tag }} --app-version ${{ inputs.app_version }} + env: + HELM_EXPERIMENTAL_OCI: '1' + + - name: Helm | Push + shell: bash + run: helm push ${{ inputs.name }}-${{ inputs.tag }}.tgz oci://${{ inputs.registry }}/${{ inputs.repository }} + env: + HELM_EXPERIMENTAL_OCI: '1' + + - name: Helm | Logout + shell: bash + run: helm registry logout ${{ inputs.registry }} + env: + HELM_EXPERIMENTAL_OCI: '1' + + - name: Helm | Output + id: output + shell: bash + run: echo "image=${{ inputs.registry }}/${{ inputs.repository }}/${{ inputs.name }}:${{ inputs.tag }}" >> $GITHUB_OUTPUT \ No newline at end of file diff --git a/template.yaml b/.github/template.yaml similarity index 100% rename from template.yaml rename to .github/template.yaml diff --git a/.github/workflows/ghcr_deploy.yml b/.github/workflows/ghcr_deploy.yml index 7b4122c69a..b863fc3fa2 100644 --- a/.github/workflows/ghcr_deploy.yml +++ b/.github/workflows/ghcr_deploy.yml @@ -10,10 +10,12 @@ on: env: REGISTRY: ghcr.io IMAGE_NAME: ${{ github.repository }} + CHART_NAME: litellm-helm # There is a single job in this workflow. It's configured to run on the latest available version of Ubuntu. jobs: docker-hub-deploy: + if: github.repository == 'BerriAI/litellm' runs-on: ubuntu-latest steps: - @@ -41,6 +43,7 @@ jobs: push: true file: Dockerfile.database tags: litellm/litellm-database:${{ github.event.inputs.tag || 'latest' }} + build-and-push-image: runs-on: ubuntu-latest # Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job. @@ -64,46 +67,23 @@ jobs: uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + # Configure multi platform Docker builds + - name: Set up QEMU + uses: docker/setup-qemu-action@e0e4588fad221d38ee467c0bffd91115366dc0c5 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@edfb0fe6204400c56fbfd3feba3fe9ad1adfa345 # This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages. # It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see "[Usage](https://github.com/docker/build-push-action#usage)" in the README of the `docker/build-push-action` repository. # It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step. - name: Build and push Docker image - uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4 + uses: docker/build-push-action@4976231911ebf5f32aad765192d35f942aa48cb8 with: context: . push: true tags: ${{ steps.meta.outputs.tags }}-${{ github.event.inputs.tag || 'latest' }}, ${{ steps.meta.outputs.tags }}-latest # if a tag is provided, use that, otherwise use the release tag, and if neither is available, use 'latest' labels: ${{ steps.meta.outputs.labels }} - build-and-push-image-alpine: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Log in to the Container registry - uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract metadata (tags, labels) for Alpine Dockerfile - id: meta-alpine - uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-alpine - - - name: Build and push Alpine Docker image - uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4 - with: - context: . - file: Dockerfile.alpine - push: true - tags: ${{ steps.meta-alpine.outputs.tags }}-${{ github.event.inputs.tag || 'latest' }}, ${{ steps.meta-alpine.outputs.tags }}-latest - labels: ${{ steps.meta-alpine.outputs.labels }} + platforms: local,linux/amd64,linux/arm64,linux/arm64/v8 + build-and-push-image-database: runs-on: ubuntu-latest permissions: @@ -125,6 +105,11 @@ jobs: uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 with: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-database + # Configure multi platform Docker builds + - name: Set up QEMU + uses: docker/setup-qemu-action@e0e4588fad221d38ee467c0bffd91115366dc0c5 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@edfb0fe6204400c56fbfd3feba3fe9ad1adfa345 - name: Build and push Database Docker image uses: docker/build-push-action@f2a1d5e99d037542a71f64918e516c093c6f3fc4 @@ -134,6 +119,60 @@ jobs: push: true tags: ${{ steps.meta-database.outputs.tags }}-${{ github.event.inputs.tag || 'latest' }}, ${{ steps.meta-database.outputs.tags }}-latest labels: ${{ steps.meta-database.outputs.labels }} + platforms: local,linux/amd64,linux/arm64,linux/arm64/v8 + build-and-push-helm-chart: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the Container registry + uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: lowercase github.repository_owner + run: | + echo "REPO_OWNER=`echo ${{github.repository_owner}} | tr '[:upper:]' '[:lower:]'`" >>${GITHUB_ENV} + - name: Get LiteLLM Latest Tag + id: current_app_tag + uses: WyriHaximus/github-action-get-previous-tag@v1.3.0 + + - name: Get last published chart version + id: current_version + shell: bash + run: | + CHART_LIST=$(helm show chart oci://${{ env.REGISTRY }}/${{ env.REPO_OWNER }}/${{ env.CHART_NAME }} 2>/dev/null || true) + if [ -z "${CHART_LIST}" ]; then + echo "current-version=0.1.0" | tee -a $GITHUB_OUTPUT + else + printf '%s' "${CHART_LIST}" | grep '^version:' | awk 'BEGIN{FS=":"}{print "current-version="$2}' | tr -d " " | tee -a $GITHUB_OUTPUT + fi + env: + HELM_EXPERIMENTAL_OCI: '1' + + # Automatically update the helm chart version one "patch" level + - name: Bump release version + id: bump_version + uses: christian-draeger/increment-semantic-version@1.1.0 + with: + current-version: ${{ steps.current_version.outputs.current-version || '0.1.0' }} + version-fragment: 'bug' + + - uses: ./.github/actions/helm-oci-chart-releaser + with: + name: ${{ env.CHART_NAME }} + repository: ${{ env.REPO_OWNER }} + tag: ${{ github.event.inputs.chartVersion || steps.bump_version.outputs.next-version || '0.1.0' }} + app_version: ${{ steps.current_app_tag.outputs.tag || 'latest' }} + path: deploy/charts/${{ env.CHART_NAME }} + registry: ${{ env.REGISTRY }} + registry_username: ${{ github.actor }} + registry_password: ${{ secrets.GITHUB_TOKEN }} + update_dependencies: true + release: name: "New LiteLLM Release" needs: [docker-hub-deploy, build-and-push-image, build-and-push-image-database] @@ -168,3 +207,40 @@ jobs: } catch (error) { core.setFailed(error.message); } + - name: Fetch Release Notes + id: release-notes + uses: actions/github-script@v6 + with: + github-token: "${{ secrets.GITHUB_TOKEN }}" + script: | + try { + const response = await github.rest.repos.getRelease({ + owner: context.repo.owner, + repo: context.repo.repo, + release_id: process.env.RELEASE_ID, + }); + return response.data.body; + } catch (error) { + core.setFailed(error.message); + } + env: + RELEASE_ID: ${{ env.RELEASE_ID }} + - name: Github Releases To Discord + env: + WEBHOOK_URL: ${{ secrets.WEBHOOK_URL }} + REALEASE_TAG: ${{ env.RELEASE_TAG }} + RELEASE_NOTES: ${{ steps.release-notes.outputs.result }} + run: | + curl -H "Content-Type: application/json" -X POST -d '{ + "content": "New LiteLLM release ${{ env.RELEASE_TAG }}", + "username": "Release Changelog", + "avatar_url": "https://cdn.discordapp.com/avatars/487431320314576937/bd64361e4ba6313d561d54e78c9e7171.png", + "embeds": [ + { + "title": "Changelog for LiteLLM ${{ env.RELEASE_TAG }}", + "description": "${{ env.RELEASE_NOTES }}", + "color": 2105893 + } + ] + }' $WEBHOOK_URL + diff --git a/.github/workflows/ghcr_helm_deploy.yml b/.github/workflows/ghcr_helm_deploy.yml new file mode 100644 index 0000000000..35ea96bfb8 --- /dev/null +++ b/.github/workflows/ghcr_helm_deploy.yml @@ -0,0 +1,64 @@ +# this workflow is triggered by an API call when there is a new PyPI release of LiteLLM +name: Build, Publish LiteLLM Helm Chart. New Release +on: + workflow_dispatch: + inputs: + chartVersion: + description: "Update the helm chart's version to this" + +# Defines two custom environment variables for the workflow. Used for the Container registry domain, and a name for the Docker image that this workflow builds. +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + REPO_OWNER: ${{github.repository_owner}} + +# There is a single job in this workflow. It's configured to run on the latest available version of Ubuntu. +jobs: + build-and-push-helm-chart: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to the Container registry + uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: lowercase github.repository_owner + run: | + echo "REPO_OWNER=`echo ${{github.repository_owner}} | tr '[:upper:]' '[:lower:]'`" >>${GITHUB_ENV} + + - name: Get LiteLLM Latest Tag + id: current_app_tag + uses: WyriHaximus/github-action-get-previous-tag@v1.3.0 + + - name: Get last published chart version + id: current_version + shell: bash + run: helm show chart oci://${{ env.REGISTRY }}/${{ env.REPO_OWNER }}/litellm-helm | grep '^version:' | awk 'BEGIN{FS=":"}{print "current-version="$2}' | tr -d " " | tee -a $GITHUB_OUTPUT + env: + HELM_EXPERIMENTAL_OCI: '1' + + # Automatically update the helm chart version one "patch" level + - name: Bump release version + id: bump_version + uses: christian-draeger/increment-semantic-version@1.1.0 + with: + current-version: ${{ steps.current_version.outputs.current-version || '0.1.0' }} + version-fragment: 'bug' + + - uses: ./.github/actions/helm-oci-chart-releaser + with: + name: litellm-helm + repository: ${{ env.REPO_OWNER }} + tag: ${{ github.event.inputs.chartVersion || steps.bump_version.outputs.next-version || '0.1.0' }} + app_version: ${{ steps.current_app_tag.outputs.tag || 'latest' }} + path: deploy/charts/litellm-helm + registry: ${{ env.REGISTRY }} + registry_username: ${{ github.actor }} + registry_password: ${{ secrets.GITHUB_TOKEN }} + update_dependencies: true + \ No newline at end of file diff --git a/.github/workflows/interpret_load_test.py b/.github/workflows/interpret_load_test.py new file mode 100644 index 0000000000..b52d4d2b39 --- /dev/null +++ b/.github/workflows/interpret_load_test.py @@ -0,0 +1,91 @@ +import csv +import os +from github import Github + + +def interpret_results(csv_file): + with open(csv_file, newline="") as csvfile: + csvreader = csv.DictReader(csvfile) + rows = list(csvreader) + """ + in this csv reader + - Create 1 new column "Status" + - if a row has a median response time < 300 and an average response time < 300, Status = "Passed ✅" + - if a row has a median response time >= 300 or an average response time >= 300, Status = "Failed ❌" + - Order the table in this order Name, Status, Median Response Time, Average Response Time, Requests/s,Failures/s, Min Response Time, Max Response Time, all other columns + """ + + # Add a new column "Status" + for row in rows: + median_response_time = float( + row["Median Response Time"].strip().rstrip("ms") + ) + average_response_time = float( + row["Average Response Time"].strip().rstrip("s") + ) + + request_count = int(row["Request Count"]) + failure_count = int(row["Failure Count"]) + + failure_percent = round((failure_count / request_count) * 100, 2) + + # Determine status based on conditions + if ( + median_response_time < 300 + and average_response_time < 300 + and failure_percent < 5 + ): + row["Status"] = "Passed ✅" + else: + row["Status"] = "Failed ❌" + + # Construct Markdown table header + markdown_table = "| Name | Status | Median Response Time (ms) | Average Response Time (ms) | Requests/s | Failures/s | Request Count | Failure Count | Min Response Time (ms) | Max Response Time (ms) |" + markdown_table += ( + "\n| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |" + ) + + # Construct Markdown table rows + for row in rows: + markdown_table += f"\n| {row['Name']} | {row['Status']} | {row['Median Response Time']} | {row['Average Response Time']} | {row['Requests/s']} | {row['Failures/s']} | {row['Request Count']} | {row['Failure Count']} | {row['Min Response Time']} | {row['Max Response Time']} |" + print("markdown table: ", markdown_table) + return markdown_table + + +if __name__ == "__main__": + csv_file = "load_test_stats.csv" # Change this to the path of your CSV file + markdown_table = interpret_results(csv_file) + + # Update release body with interpreted results + github_token = os.getenv("GITHUB_TOKEN") + g = Github(github_token) + repo = g.get_repo( + "BerriAI/litellm" + ) # Replace with your repository's username and name + latest_release = repo.get_latest_release() + print("got latest release: ", latest_release) + print("latest release body: ", latest_release.body) + print("markdown table: ", markdown_table) + + # check if "Load Test LiteLLM Proxy Results" exists + existing_release_body = latest_release.body + if "Load Test LiteLLM Proxy Results" in latest_release.body: + # find the "Load Test LiteLLM Proxy Results" section and delete it + start_index = latest_release.body.find("Load Test LiteLLM Proxy Results") + existing_release_body = latest_release.body[:start_index] + + new_release_body = ( + existing_release_body + + "\n\n" + + "## Load Test LiteLLM Proxy Results" + + "\n\n" + + markdown_table + ) + print("new release body: ", new_release_body) + try: + latest_release.update_release( + name=latest_release.tag_name, + message=new_release_body, + ) + except Exception as e: + print(e) diff --git a/.github/workflows/load_test.yml b/.github/workflows/load_test.yml new file mode 100644 index 0000000000..ddf613fa66 --- /dev/null +++ b/.github/workflows/load_test.yml @@ -0,0 +1,50 @@ +name: Test Locust Load Test + +on: + workflow_run: + workflows: ["Build, Publish LiteLLM Docker Image. New Release"] + types: + - completed + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v1 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: '3.x' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install PyGithub + - name: Run Load Test + id: locust_run + uses: BerriAI/locust-github-action@master + with: + LOCUSTFILE: ".github/workflows/locustfile.py" + URL: "https://litellm-database-docker-build-production.up.railway.app/" + USERS: "100" + RATE: "10" + RUNTIME: "300s" + - name: Process Load Test Stats + run: | + echo "Current working directory: $PWD" + ls + python ".github/workflows/interpret_load_test.py" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + working-directory: ${{ github.workspace }} + - name: Upload CSV as Asset to Latest Release + uses: xresloader/upload-to-github-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + file: "load_test_stats.csv;load_test.html" + update_latest_release: true + tag_name: "load-test" + overwrite: true \ No newline at end of file diff --git a/.github/workflows/locustfile.py b/.github/workflows/locustfile.py new file mode 100644 index 0000000000..9e5b62ff09 --- /dev/null +++ b/.github/workflows/locustfile.py @@ -0,0 +1,42 @@ +from locust import HttpUser, task, between, events +import json +import time + + +class MyUser(HttpUser): + wait_time = between(1, 5) + + @task + def chat_completion(self): + headers = { + "Content-Type": "application/json", + "Authorization": f"Bearer sk-gUvTeN9g0sgHBMf9HeCaqA", + # Include any additional headers you may need for authentication, etc. + } + + # Customize the payload with "model" and "messages" keys + payload = { + "model": "fake-openai-endpoint", + "messages": [ + {"role": "system", "content": "You are a chat bot."}, + {"role": "user", "content": "Hello, how are you?"}, + ], + # Add more data as necessary + } + + # Make a POST request to the "chat/completions" endpoint + response = self.client.post("chat/completions", json=payload, headers=headers) + + # Print or log the response if needed + + @task(10) + def health_readiness(self): + start_time = time.time() + response = self.client.get("health/readiness") + response_time = time.time() - start_time + + @task(10) + def health_liveliness(self): + start_time = time.time() + response = self.client.get("health/liveliness") + response_time = time.time() - start_time diff --git a/.github/workflows/results_stats.csv b/.github/workflows/results_stats.csv new file mode 100644 index 0000000000..bcef047b0f --- /dev/null +++ b/.github/workflows/results_stats.csv @@ -0,0 +1,27 @@ +Date,"Ben +Ashley",Tom Brooks,Jimmy Cooney,"Sue +Daniels",Berlinda Fong,Terry Jones,Angelina Little,Linda Smith +10/1,FALSE,TRUE,TRUE,TRUE,TRUE,TRUE,FALSE,TRUE +10/2,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE +10/3,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE +10/4,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE +10/5,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE +10/6,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE +10/7,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE +10/8,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE +10/9,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE +10/10,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE +10/11,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE +10/12,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE +10/13,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE +10/14,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE +10/15,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE +10/16,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE +10/17,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE +10/18,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE +10/19,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE +10/20,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE +10/21,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE +10/22,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE +10/23,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE,FALSE +Total,0,1,1,1,1,1,0,1 \ No newline at end of file diff --git a/.github/workflows/update_release.py b/.github/workflows/update_release.py new file mode 100644 index 0000000000..f70509e8e7 --- /dev/null +++ b/.github/workflows/update_release.py @@ -0,0 +1,54 @@ +import os +import requests +from datetime import datetime + +# GitHub API endpoints +GITHUB_API_URL = "https://api.github.com" +REPO_OWNER = "BerriAI" +REPO_NAME = "litellm" + +# GitHub personal access token (required for uploading release assets) +GITHUB_ACCESS_TOKEN = os.environ.get("GITHUB_ACCESS_TOKEN") + +# Headers for GitHub API requests +headers = { + "Accept": "application/vnd.github+json", + "Authorization": f"Bearer {GITHUB_ACCESS_TOKEN}", + "X-GitHub-Api-Version": "2022-11-28", +} + +# Get the latest release +releases_url = f"{GITHUB_API_URL}/repos/{REPO_OWNER}/{REPO_NAME}/releases/latest" +response = requests.get(releases_url, headers=headers) +latest_release = response.json() +print("Latest release:", latest_release) + +# Upload an asset to the latest release +upload_url = latest_release["upload_url"].split("{?")[0] +asset_name = "results_stats.csv" +asset_path = os.path.join(os.getcwd(), asset_name) +print("upload_url:", upload_url) + +with open(asset_path, "rb") as asset_file: + asset_data = asset_file.read() + +upload_payload = { + "name": asset_name, + "label": "Load test results", + "created_at": datetime.utcnow().isoformat() + "Z", +} + +upload_headers = headers.copy() +upload_headers["Content-Type"] = "application/octet-stream" + +upload_response = requests.post( + upload_url, + headers=upload_headers, + data=asset_data, + params=upload_payload, +) + +if upload_response.status_code == 201: + print(f"Asset '{asset_name}' uploaded successfully to the latest release.") +else: + print(f"Failed to upload asset. Response: {upload_response.text}") diff --git a/.gitignore b/.gitignore index 77ee0fbefa..b03bc895bf 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,13 @@ hosted_config.yaml litellm/proxy/tests/node_modules litellm/proxy/tests/package.json litellm/proxy/tests/package-lock.json +ui/litellm-dashboard/.next +ui/litellm-dashboard/node_modules +ui/litellm-dashboard/next-env.d.ts +ui/litellm-dashboard/package.json +ui/litellm-dashboard/package-lock.json +deploy/charts/litellm/*.tgz +deploy/charts/litellm/charts/* +deploy/charts/*.tgz +litellm/proxy/vertex_key.json +**/.vim/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8ab4e3e924..2a84048e0b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,15 +1,21 @@ repos: - repo: https://github.com/psf/black - rev: stable + rev: 24.2.0 hooks: - id: black - repo: https://github.com/pycqa/flake8 - rev: 3.8.4 # The version of flake8 to use + rev: 7.0.0 # The version of flake8 to use hooks: - id: flake8 exclude: ^litellm/tests/|^litellm/proxy/proxy_cli.py|^litellm/integrations/|^litellm/proxy/tests/ additional_dependencies: [flake8-print] files: litellm/.*\.py +- repo: local + hooks: + - id: check-files-match + name: Check if files match + entry: python3 ci_cd/check_files_match.py + language: system - repo: local hooks: - id: mypy diff --git a/Dockerfile b/Dockerfile index 183f062058..4408afb3d6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,6 +20,9 @@ RUN pip install --upgrade pip && \ # Copy the current directory contents into the container at /app COPY . . +# Build Admin UI +RUN chmod +x build_admin_ui.sh && ./build_admin_ui.sh + # Build the package RUN rm -rf dist/* && python -m build @@ -32,6 +35,12 @@ RUN pip install dist/*.whl # install dependencies as wheels RUN pip wheel --no-cache-dir --wheel-dir=/wheels/ -r requirements.txt +# install semantic-cache [Experimental]- we need this here and not in requirements.txt because redisvl pins to pydantic 1.0 +RUN pip install redisvl==0.0.7 --no-deps + +# Build Admin UI +RUN chmod +x build_admin_ui.sh && ./build_admin_ui.sh + # Runtime stage FROM $LITELLM_RUNTIME_IMAGE as runtime @@ -47,9 +56,14 @@ COPY --from=builder /wheels/ /wheels/ # Install the built wheel using pip; again using a wildcard if it's the only file RUN pip install *.whl /wheels/* --no-index --find-links=/wheels/ && rm -f *.whl && rm -rf /wheels +# Generate prisma client +RUN prisma generate RUN chmod +x entrypoint.sh EXPOSE 4000/tcp ENTRYPOINT ["litellm"] -CMD ["--port", "4000"] \ No newline at end of file + +# Append "--detailed_debug" to the end of CMD to view detailed debug logs +# CMD ["--port", "4000", "--config", "./proxy_server_config.yaml", "--run_gunicorn", "--detailed_debug"] +CMD ["--port", "4000", "--config", "./proxy_server_config.yaml", "--run_gunicorn", "--num_workers", "4"] diff --git a/Dockerfile.database b/Dockerfile.database index 5c0c4ad36d..9e2d1637b0 100644 --- a/Dockerfile.database +++ b/Dockerfile.database @@ -20,6 +20,9 @@ RUN pip install --upgrade pip && \ # Copy the current directory contents into the container at /app COPY . . +# Build Admin UI +RUN chmod +x build_admin_ui.sh && ./build_admin_ui.sh + # Build the package RUN rm -rf dist/* && python -m build @@ -47,6 +50,12 @@ COPY --from=builder /wheels/ /wheels/ # Install the built wheel using pip; again using a wildcard if it's the only file RUN pip install *.whl /wheels/* --no-index --find-links=/wheels/ && rm -f *.whl && rm -rf /wheels +# install semantic-cache [Experimental]- we need this here and not in requirements.txt because redisvl pins to pydantic 1.0 +RUN pip install redisvl==0.0.7 --no-deps + +# Build Admin UI +RUN chmod +x build_admin_ui.sh && ./build_admin_ui.sh + # Generate prisma client RUN prisma generate RUN chmod +x entrypoint.sh @@ -56,4 +65,7 @@ EXPOSE 4000/tcp # # Set your entrypoint and command ENTRYPOINT ["litellm"] -CMD ["--port", "4000"] + +# Append "--detailed_debug" to the end of CMD to view detailed debug logs +# CMD ["--port", "4000","--run_gunicorn", "--detailed_debug"] +CMD ["--port", "4000", "--run_gunicorn"] diff --git a/LICENSE b/LICENSE index dd11dc5235..3bfef5bae9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,3 +1,8 @@ +Portions of this software are licensed as follows: + +* All content that resides under the "enterprise/" directory of this repository, if that directory exists, is licensed under the license defined in "enterprise/LICENSE". +* Content outside of the above mentioned directories or restrictions above is available under the MIT license as defined below. +--- MIT License Copyright (c) 2023 Berri AI diff --git a/README.md b/README.md index c4221e031c..6bdaa9d375 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@
Call all LLM APIs using the OpenAI format [Bedrock, Huggingface, VertexAI, TogetherAI, Azure, OpenAI, etc.]