diff --git a/.circleci/config.yml b/.circleci/config.yml index 56f9a15fe3a..daa4d59ec4b 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 @@ -38,8 +39,12 @@ jobs: pip install openai pip install prisma 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 "apscheduler==3.10.4" + pip install "PyGithub==1.59.1" - save_cache: paths: - ./venv @@ -83,6 +88,107 @@ jobs: - store_test_results: path: test-results + build_and_test: + machine: + image: ubuntu-2204:2023.10.1 + working_directory: ~/project + steps: + - checkout + - run: + name: Install Docker CLI (In case it's not already installed) + command: | + sudo apt-get update + sudo apt-get install -y docker-ce docker-ce-cli containerd.io + - run: + name: Install Python 3.9 + command: | + curl https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh --output miniconda.sh + bash miniconda.sh -b -p $HOME/miniconda + export PATH="$HOME/miniconda/bin:$PATH" + conda init bash + source ~/.bashrc + conda create -n myenv python=3.9 -y + conda activate myenv + python --version + - run: + name: Install Dependencies + command: | + 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 + command: docker build -t my-app:latest -f Dockerfile.database . + - run: + name: Run Docker container + command: | + docker run -d \ + -p 4000:4000 \ + -e DATABASE_URL=$PROXY_DOCKER_DB_URL \ + -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 \ + --detailed_debug \ + --run_gunicorn \ + - run: + name: Install curl and dockerize + command: | + sudo apt-get update + sudo apt-get install -y curl + sudo wget https://github.com/jwilder/dockerize/releases/download/v0.6.1/dockerize-linux-amd64-v0.6.1.tar.gz + sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-v0.6.1.tar.gz + sudo rm dockerize-linux-amd64-v0.6.1.tar.gz + - run: + name: Start outputting logs + command: docker logs -f my-app + background: true + - run: + name: Wait for app to be ready + command: dockerize -wait http://localhost:4000 -timeout 1m + - run: + name: Run tests + command: | + pwd + ls + python -m pytest -vv tests/ -x --junitxml=test-results/junit.xml --durations=5 + no_output_timeout: 120m + + # Store test results + - store_test_results: + path: test-results + publish_to_pypi: docker: - image: cimg/python:3.8 @@ -164,9 +270,16 @@ workflows: only: - main - /litellm_.*/ + - build_and_test: + filters: + branches: + only: + - main + - /litellm_.*/ - publish_to_pypi: requires: - local_testing + - build_and_test filters: branches: only: diff --git a/.circleci/requirements.txt b/.circleci/requirements.txt index 85b576bff24..4730fc28b15 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/.github/actions/helm-oci-chart-releaser/action.yml b/.github/actions/helm-oci-chart-releaser/action.yml new file mode 100644 index 00000000000..059277ed882 --- /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 b66432419f0..a367ae2b8bb 100644 --- a/.github/workflows/ghcr_deploy.yml +++ b/.github/workflows/ghcr_deploy.yml @@ -13,6 +13,35 @@ env: # There is a single job in this workflow. It's configured to run on the latest available version of Ubuntu. jobs: + docker-hub-deploy: + runs-on: ubuntu-latest + steps: + - + name: Set up QEMU + uses: docker/setup-qemu-action@v3 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - + name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - + name: Build and push + uses: docker/build-push-action@v5 + with: + push: true + tags: litellm/litellm:${{ github.event.inputs.tag || 'latest' }} + - + name: Build and push litellm-database image + uses: docker/build-push-action@v5 + with: + 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. @@ -36,46 +65,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' }} # if a tag is provided, use that, otherwise use the release tag, and if neither is available, use 'latest' + 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: . - dockerfile: Dockerfile.alpine - push: true - tags: ${{ steps.meta-alpine.outputs.tags }}-${{ github.event.inputs.tag || '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: @@ -104,10 +110,11 @@ jobs: context: . file: Dockerfile.database push: true - tags: ${{ steps.meta-database.outputs.tags }}-${{ github.event.inputs.tag || 'latest' }}, latest + tags: ${{ steps.meta-database.outputs.tags }}-${{ github.event.inputs.tag || 'latest' }}, ${{ steps.meta-database.outputs.tags }}-latest labels: ${{ steps.meta-database.outputs.labels }} release: name: "New LiteLLM Release" + needs: [docker-hub-deploy, build-and-push-image, build-and-push-image-database] runs-on: "ubuntu-latest" @@ -139,3 +146,20 @@ jobs: } catch (error) { core.setFailed(error.message); } + - name: Github Releases To Discord + env: + WEBHOOK_URL: ${{ secrets.WEBHOOK_URL }} + run: | + curl -H "Content-Type: application/json" -X POST -d '{ + "content": "||@everyone||", + "username": "Release Changelog", + "avatar_url": "https://cdn.discordapp.com/avatars/487431320314576937/bd64361e4ba6313d561d54e78c9e7171.png", + "embeds": [ + { + "title": "Changelog", + "description": "This is the changelog for the latest release.", + "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 00000000000..35ea96bfb84 --- /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/.gitignore b/.gitignore index 618e3d87469..de1c7598f60 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,15 @@ proxy_server_config_@.yaml proxy_server_config_2.yaml litellm/proxy/secret_managers/credentials.json 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 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8ab4e3e9248..2a84048e0b1 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 2564e128c36..bcb4ee69255 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 @@ -51,8 +60,5 @@ RUN chmod +x entrypoint.sh EXPOSE 4000/tcp -# Set your entrypoint and command -CMD [ \ - "sh", "-c", \ - "if [ -n \"$DATABASE_URL\" ]; then ./entrypoint.sh; else litellm --port 4000 --num_workers 8; fi" \ -] \ No newline at end of file +ENTRYPOINT ["litellm"] +CMD ["--port", "4000", "--config", "./proxy_server_config.yaml", "--detailed_debug", "--run_gunicorn"] \ No newline at end of file diff --git a/Dockerfile.alpine b/Dockerfile.alpine index 050d4bb3205..287cc439cb9 100644 --- a/Dockerfile.alpine +++ b/Dockerfile.alpine @@ -1,8 +1,8 @@ # Base image for building -ARG LITELLM_BUILD_IMAGE=python:3.9-alpine +ARG LITELLM_BUILD_IMAGE=python:3.11-alpine # Runtime image -ARG LITELLM_RUNTIME_IMAGE=python:3.9-alpine +ARG LITELLM_RUNTIME_IMAGE=python:3.11-alpine # Builder stage FROM $LITELLM_BUILD_IMAGE as builder diff --git a/Dockerfile.database b/Dockerfile.database index 6791dc1a253..1206aba882b 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,11 +50,19 @@ 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 EXPOSE 4000/tcp -# Set your entrypoint and command -CMD ["./entrypoint.sh"] \ No newline at end of file +# # Set your entrypoint and command + +ENTRYPOINT ["litellm"] +CMD ["--port", "4000", "--run_gunicorn"] diff --git a/LICENSE b/LICENSE index dd11dc52350..3bfef5bae9b 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 dd72814ed3d..bc8c1bae25f 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.]