Improve Docker Build and Deployment for Arcade Worker (#205)
This PR enhances the Docker build and deployment process for the Arcade Worker by: - **Modularizing Docker Builds:** - Introduces a new `INSTALL_TOOLKITS` build argument in the `Dockerfile` to conditionally include toolkits. this enables the creation of a `arcadeai/worker-base` which can be used to build custom containers in a multi-stage build. an example of this is included in the example dir. - Adds `docker-base` Makefile target to build a lightweight base image without toolkits. - **Publishing to GitHub Container Registry (GHCR):** - Adds Makefile targets `publish-ghcr` and `gh-login` for pushing images to GHCR. - Supports publishing both base and full images with toolkits to GHCR. - **Docker Compose:** - Add Docker compose file and setup - Renames the `actor` service to `worker` - Adds an `nginx` service in `docker-compose.yml` to proxy requests to the Arcade Engine. - Introduces an `nginx.conf` file for the Nginx service. - **Streamlining Toolkit Installation:** - Moves toolkit installation from the `start.sh` script to the Docker build process. - Creates a `toolkits.txt` file to manage toolkit dependencies which can be edited easily when we want to add a new toolkit. Developers can also use this approach as shown in the example. - **Improving Configuration Files:** - Updates `docker.engine.yaml` and `env.example` to align with the new setup. TODO: - CI/CD needs to be adjusted so that images are pushed to ghcr on release. - AWS resources need to be renamed actor -> worker @EricGustin This can go in after the above two items are resolved. --------- Co-authored-by: Wils Dawson <wils@arcade-ai.com> Co-authored-by: Eric Gustin <34000337+EricGustin@users.noreply.github.com> Co-authored-by: sdreyer <sterling@arcade-ai.com> Co-authored-by: Sterling Dreyer <sdreyer21@gmail.com> Co-authored-by: Nate Barbettini <nathanaelb@gmail.com>
This commit is contained in:
parent
6d8e943c96
commit
09a0784cd5
16 changed files with 637 additions and 54 deletions
10
.github/workflows/release-containers.yml
vendored
10
.github/workflows/release-containers.yml
vendored
|
|
@ -100,11 +100,11 @@ jobs:
|
|||
id: build
|
||||
run: |
|
||||
make docker VERSION=${{ needs.set-version.outputs.version }} ARCH=${{ matrix.arch }}
|
||||
make docker-base VERSION=${{ needs.set-version.outputs.version }} ARCH=${{ matrix.arch }}
|
||||
make publish-ecr VERSION=${{ needs.set-version.outputs.version }} ARCH=${{ matrix.arch }}
|
||||
|
||||
- name: Push GHCR
|
||||
if: github.event_name != 'push' || startsWith(github.ref, 'refs/tags/')
|
||||
working-directory: ./docker
|
||||
run: |
|
||||
make publish-ghcr VERSION=${{ needs.set-version.outputs.version }} ARCH=${{ matrix.arch }}
|
||||
|
||||
|
|
@ -144,12 +144,16 @@ jobs:
|
|||
|
||||
- name: Push manifest to ECR
|
||||
working-directory: ./docker
|
||||
run: make ecr-manifest VERSION=${{ needs.set-version.outputs.version }}
|
||||
run: |
|
||||
make ecr-manifest VERSION=${{ needs.set-version.outputs.version }}
|
||||
make ecr-manifest INSTALL_TOOLKITS=false VERSION=${{ needs.set-version.outputs.version }}
|
||||
|
||||
- name: Push manifest to GHCR
|
||||
if: github.event_name != 'push' || startsWith(github.ref, 'refs/tags/')
|
||||
working-directory: ./docker
|
||||
run: make ghcr-manifest VERSION=${{ needs.set-version.outputs.version }}
|
||||
run: |
|
||||
make ghcr-manifest VERSION=${{ needs.set-version.outputs.version }}
|
||||
make ghcr-manifest INSTALL_TOOLKITS=false VERSION=${{ needs.set-version.outputs.version }}
|
||||
|
||||
# Currently broken: workflow deploy requires all versions
|
||||
# deploy:
|
||||
|
|
|
|||
39
Makefile
39
Makefile
|
|
@ -91,15 +91,35 @@ build-and-publish: build publish ## Build and publish.
|
|||
docker: ## Build and run the Docker container
|
||||
@echo "🚀 Building arcade and toolkit wheels..."
|
||||
@make full-dist
|
||||
@echo "Writing extras [fastapi, evals] to requirements.txt"
|
||||
@cd arcade && poetry export --extras "fastapi evals" --output ../dist/requirements.txt
|
||||
@echo "Writing extras requirements.txt"
|
||||
@cd arcade && poetry export --extras "fastapi" --output ../dist/requirements.txt
|
||||
@echo "🚀 Building Docker image"
|
||||
@cd docker && make docker-build
|
||||
@cd docker && make docker-run
|
||||
|
||||
.PHONY: docker-base
|
||||
docker-base: ## Build and run the Docker container
|
||||
@echo "🚀 Building arcade and toolkit wheels..."
|
||||
@make full-dist
|
||||
@echo "Writing extras requirements.txt"
|
||||
@cd arcade && poetry export --extras "fastapi" --output ../dist/requirements.txt
|
||||
@echo "🚀 Building Docker image"
|
||||
@cd docker && INSTALL_TOOLKITS=false make docker-build
|
||||
@cd docker && INSTALL_TOOLKITS=false make docker-run
|
||||
|
||||
.PHONY: publish-ecr
|
||||
publish-ecr: ## Publish to the ECR
|
||||
@cd docker && make publish-ecr
|
||||
# Publish the base image - <ECR_ENDPOINT>/arcadeai/worker-base
|
||||
@cd docker && INSTALL_TOOLKITS=false make publish-ecr
|
||||
# Publish the image with toolkits - <ECR_ENDPOINT>/arcadeai/worker
|
||||
@cd docker && INSTALL_TOOLKITS=true make publish-ecr
|
||||
|
||||
.PHONY: publish-ghcr
|
||||
publish-ghcr: ## Publish to the GHCR
|
||||
# Publish the base image - ghcr.io/arcadeai/worker-base
|
||||
@cd docker && INSTALL_TOOLKITS=false make publish-ghcr
|
||||
# Publish the image with toolkits - ghcr.io/arcadeai/worker
|
||||
@cd docker && INSTALL_TOOLKITS=true make publish-ghcr
|
||||
|
||||
.PHONY: full-dist
|
||||
full-dist: clean-dist ## Build all projects and copy wheels to ./dist
|
||||
|
|
@ -121,19 +141,6 @@ full-dist: clean-dist ## Build all projects and copy wheels to ./dist
|
|||
@echo "Reset version to default (0.1.0)"
|
||||
@make unset-version
|
||||
|
||||
@echo "🛠️ Building all projects and copying wheels to ./dist"
|
||||
# Build and copy wheels for each toolkit
|
||||
# @for toolkit_dir in toolkits/*; do \
|
||||
# if [ -d "$$toolkit_dir" ]; then \
|
||||
# toolkit_name=$$(basename "$$toolkit_dir"); \
|
||||
# echo "Building $$toolkit_name project..."; \
|
||||
# poetry build; \
|
||||
# cp dist/*.whl ../../dist/toolkits; \
|
||||
# cd -; \
|
||||
# fi; \
|
||||
# done
|
||||
|
||||
@echo "✅ All toolkits built and wheels copied to ./dist"
|
||||
|
||||
.PHONY: clean-dist
|
||||
clean-dist: ## Clean all built distributions
|
||||
|
|
|
|||
|
|
@ -1,25 +1,20 @@
|
|||
# Use a lightweight Python image
|
||||
FROM python:3.10-slim
|
||||
|
||||
# Define build arguments with default values
|
||||
ARG PORT=8001
|
||||
ARG HOST=0.0.0.0
|
||||
ARG VERSION=0.1.0
|
||||
ARG VERSION=${VERSION:-0.1.0}
|
||||
ARG INSTALL_TOOLKITS=true
|
||||
|
||||
# Set environment variables using the build arguments
|
||||
ENV PORT=${PORT}
|
||||
ENV HOST=${HOST}
|
||||
ENV VERSION=${VERSION}
|
||||
ENV OTEL_ENABLE=false
|
||||
ENV ARCADE_WORK_DIR=/app
|
||||
ENV TOOLKITS="arcade-code-sandbox,arcade-github,arcade-google,arcade-linkedin,arcade-math,arcade-search,arcade-slack,arcade-spotify,arcade-web,arcade-x,arcade-zoom"
|
||||
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
apt-utils \
|
||||
build-essential \
|
||||
libssl-dev \
|
||||
libffi-dev \
|
||||
python3-dev \
|
||||
curl \
|
||||
&& apt-get clean \
|
||||
|
|
@ -30,6 +25,12 @@ WORKDIR /app/arcade
|
|||
# Copy the parent directory contents into the container
|
||||
COPY ./dist ./arcade /app/arcade/
|
||||
|
||||
# Copy the toolkits.txt file into the container
|
||||
COPY ./docker/toolkits.txt /app/arcade/
|
||||
|
||||
# Expose the port
|
||||
EXPOSE $PORT
|
||||
|
||||
# List files for debugging purposes
|
||||
RUN ls -la /app/arcade/
|
||||
|
||||
|
|
@ -49,8 +50,10 @@ RUN if [ ! "$(echo ${VERSION} | grep -E '\.dev0$')" ]; then \
|
|||
pip install .; \
|
||||
fi
|
||||
|
||||
# Expose the port
|
||||
EXPOSE $PORT
|
||||
# Conditionally install toolkits.txt dependencies
|
||||
RUN if [ "$INSTALL_TOOLKITS" = "true" ] ; then \
|
||||
python -m pip install -r ./toolkits.txt ; \
|
||||
fi
|
||||
|
||||
# Run the arcade workerup (hidden cli command)
|
||||
COPY docker/start.sh /app/start.sh
|
||||
|
|
|
|||
|
|
@ -2,24 +2,35 @@ VENDOR ?= ArcadeAI
|
|||
PROJECT ?= ArcadeAI
|
||||
SOURCE ?= https://github.com/ArcadeAI/arcade-ai
|
||||
LICENSE ?= MIT
|
||||
DESCRIPTION ?= "Arcade AI for LLM Tool Serving"
|
||||
DESCRIPTION ?= "Arcade Worker for LLM Tool Serving"
|
||||
REPOSITORY ?= arcadeai/worker
|
||||
ECR_ENDPOINT ?= 471112909428.dkr.ecr.us-east-1.amazonaws.com
|
||||
ARCH ?= arm64
|
||||
ARCH ?= $(shell uname -m)
|
||||
|
||||
VERSION ?= 0.1.0.dev0
|
||||
COMMIT ?= $(shell git describe --dirty --always --abbrev=15)
|
||||
BUILD_DATE ?= $(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||
IMAGE_NAME ?= worker
|
||||
PORT ?= 8002
|
||||
INSTALL_TOOLKITS ?= true
|
||||
|
||||
# If INSTALL_TOOLKITS is true, we are building the worker image with all toolkits
|
||||
# Otherwise, we are building the worker image with no toolkits
|
||||
ifeq ($(INSTALL_TOOLKITS), true)
|
||||
REPOSITORY := $(REPOSITORY)
|
||||
else
|
||||
REPOSITORY := $(REPOSITORY)-base
|
||||
endif
|
||||
|
||||
VERSION_TAG := $(VERSION)-$(ARCH)
|
||||
|
||||
.PHONY: docker-build
|
||||
docker-build: ## Build the Docker container
|
||||
@echo "🛠️ Building Docker image ($(VERSION)).."
|
||||
@echo "🛠️ Building Docker image ($(VERSION_TAG)).."
|
||||
@echo "- Commit: $(COMMIT)"
|
||||
@echo "- Build Date: $(BUILD_DATE)"
|
||||
@docker build --build-arg PORT=$(PORT) -f Dockerfile -t $(REPOSITORY):$(VERSION) .. \
|
||||
@docker build --build-arg PORT=$(PORT) -f Dockerfile -t $(REPOSITORY):$(VERSION_TAG) \
|
||||
--build-arg INSTALL_TOOLKITS=$(INSTALL_TOOLKITS) \
|
||||
--build-arg PORT=$(PORT) \
|
||||
--build-arg VERSION="$(VERSION)" \
|
||||
--build-arg COMMIT="$(COMMIT)" \
|
||||
|
|
@ -27,14 +38,16 @@ docker-build: ## Build the Docker container
|
|||
--label=org.opencontainers.image.vendor="$(VENDOR)" \
|
||||
--label=org.opencontainers.image.title="$(PROJECT)" \
|
||||
--label=org.opencontainers.image.revision="$(COMMIT)" \
|
||||
--label=org.opencontainers.image.version="$(VERSION)" \
|
||||
--label=org.opencontainers.image.version="$(VERSION_TAG)" \
|
||||
--label=org.opencontainers.image.created="$(BUILD_DATE)" \
|
||||
--label=org.opencontainers.image.source="$(SOURCE)" \
|
||||
--label=org.opencontainers.image.licenses="$(LICENSE)" \
|
||||
--label=org.opencontainers.image.description=$(DESCRIPTION)
|
||||
--label=org.opencontainers.image.description=$(DESCRIPTION) \
|
||||
..
|
||||
|
||||
|
||||
ecr-manifest: ## Make a manifest file for the image
|
||||
@echo "🛠️ Build manifest file ($(VERSION)).."
|
||||
@echo "🛠️ Build manifest file for $(REPOSITORY):$(VERSION).."
|
||||
@echo "- Commit: $(COMMIT)"
|
||||
@echo "- Build Date: $(BUILD_DATE)"
|
||||
@export DOCKER_CLI_EXPERIMENTAL=enabled
|
||||
|
|
@ -56,7 +69,7 @@ ecr-manifest: ## Make a manifest file for the image
|
|||
@docker manifest push $(ECR_ENDPOINT)/$(REPOSITORY):latest
|
||||
|
||||
ghcr-manifest: ## Make a manifest file for the image
|
||||
@echo "🛠️ Build manifest file ($(VERSION)).."
|
||||
@echo "🛠️ Build manifest file for $(REPOSITORY):$(VERSION).."
|
||||
@echo "- Commit: $(COMMIT)"
|
||||
@echo "- Build Date: $(BUILD_DATE)"
|
||||
@export DOCKER_CLI_EXPERIMENTAL=enabled
|
||||
|
|
@ -80,17 +93,16 @@ ghcr-manifest: ## Make a manifest file for the image
|
|||
.PHONY: docker-run
|
||||
docker-run: ## Run the Docker container
|
||||
@echo "\n🚀 Run the container with the following ..."
|
||||
@echo ">>> docker run -d -p $(PORT):$(PORT) $(REPOSITORY):$(VERSION)"
|
||||
|
||||
@echo ">>> docker run -d -p $(PORT):$(PORT) $(REPOSITORY):$(VERSION_TAG)"
|
||||
|
||||
.PHONY: publish-ecr
|
||||
publish-ecr:
|
||||
@echo "🚚 Pushing the Agent image to ECR.."
|
||||
@docker tag $(REPOSITORY):$(VERSION) $(ECR_ENDPOINT)/$(REPOSITORY):$(VERSION)-$(ARCH)
|
||||
@echo "- pushing $(ECR_ENDPOINT)/$(REPOSITORY):$(VERSION)-$(ARCH)"
|
||||
@docker push $(ECR_ENDPOINT)/$(REPOSITORY):$(VERSION)-$(ARCH)
|
||||
@echo $(VERSION) | grep -q $(RC_PART) || { \
|
||||
docker tag $(REPOSITORY):$(VERSION) $(ECR_ENDPOINT)/$(REPOSITORY):latest-$(ARCH); \
|
||||
@docker tag $(REPOSITORY):$(VERSION_TAG) $(ECR_ENDPOINT)/$(REPOSITORY):$(VERSION_TAG)
|
||||
@echo "- pushing $(ECR_ENDPOINT)/$(REPOSITORY):$(VERSION_TAG)"
|
||||
@docker push $(ECR_ENDPOINT)/$(REPOSITORY):$(VERSION_TAG)
|
||||
@echo $(VERSION_TAG) | grep -q $(RC_PART) || { \
|
||||
docker tag $(REPOSITORY):$(VERSION_TAG) $(ECR_ENDPOINT)/$(REPOSITORY):latest-$(ARCH); \
|
||||
echo "- pushing $(ECR_ENDPOINT)/$(REPOSITORY):latest-$(ARCH)"; \
|
||||
docker push $(ECR_ENDPOINT)/$(REPOSITORY):latest-$(ARCH); \
|
||||
}
|
||||
|
|
@ -98,20 +110,30 @@ publish-ecr:
|
|||
.PHONY: publish-ghcr
|
||||
publish-ghcr:
|
||||
@echo "🚚 Pushing the Agent image to GHCR.."
|
||||
@docker tag $(REPOSITORY):$(VERSION) ghcr.io/$(REPOSITORY):$(VERSION)-$(ARCH)
|
||||
@echo "- pushing ghcr.io/$(REPOSITORY):$(VERSION)-$(ARCH)"
|
||||
@docker push ghcr.io/$(REPOSITORY):$(VERSION)-$(ARCH)
|
||||
@docker tag $(REPOSITORY):$(VERSION_TAG) ghcr.io/$(REPOSITORY):$(VERSION_TAG)
|
||||
@echo "- pushing ghcr.io/$(REPOSITORY):$(VERSION_TAG)"
|
||||
@docker push ghcr.io/$(REPOSITORY):$(VERSION_TAG)
|
||||
@echo $(VERSION) | grep -q $(RC_PART) || { \
|
||||
docker tag $(REPOSITORY):$(VERSION) ghcr.io/$(REPOSITORY):latest-$(ARCH); \
|
||||
docker tag $(REPOSITORY):$(VERSION_TAG) ghcr.io/$(REPOSITORY):latest-$(ARCH); \
|
||||
echo "- pushing ghcr.io/$(REPOSITORY):latest-$(ARCH)"; \
|
||||
docker push ghcr.io/$(REPOSITORY):latest-$(ARCH); \
|
||||
}
|
||||
|
||||
|
||||
.PHONY: ecr-login
|
||||
ecr-login: # Login to ECR
|
||||
ecr-login: ## Login to ECR
|
||||
@aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin $(ECR_ENDPOINT)
|
||||
|
||||
|
||||
.PHONY: gh-login
|
||||
gh-login: ## Login to GHCR
|
||||
@echo "🚚 Logging in to GHCR..."
|
||||
@if [ -z "$(GHCR_PAT)" ]; then \
|
||||
echo "Error: GHCR_PAT environment variable is not set"; \
|
||||
exit 1; \
|
||||
fi
|
||||
@echo $(GHCR_PAT) | docker login ghcr.io -u arcadeai --password-stdin
|
||||
|
||||
.PHONY: help
|
||||
help:
|
||||
@echo "🛠️ Worker Docker Commands:\n"
|
||||
|
|
|
|||
115
docker/README.md
Normal file
115
docker/README.md
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
# Arcade Docker Compose Guide
|
||||
|
||||
This guide provides detailed instructions on how to set up and run Arcade using Docker Compose.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- **Docker** installed on your system. [Install Docker](https://docs.docker.com/get-docker/)
|
||||
- **Docker Compose** installed. It comes bundled with Docker Desktop on Windows and macOS. For Linux, follow the [Docker Compose installation guide](https://docs.docker.com/compose/install/).
|
||||
|
||||
## Getting Started
|
||||
|
||||
### 1. Clone the Repository
|
||||
|
||||
Begin by cloning the Arcade repository:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/ArcadeAI/arcade-ai.git
|
||||
```
|
||||
|
||||
Change to the `docker` directory:
|
||||
|
||||
```bash
|
||||
cd arcade-ai/docker
|
||||
```
|
||||
|
||||
### 2. Copy and Configure Environment Variables
|
||||
|
||||
Copy the example environment file to `.env`:
|
||||
|
||||
```bash
|
||||
cp env.example .env
|
||||
```
|
||||
|
||||
Open the `.env` file in your preferred text editor and fill in the required values. At a minimum, you **must** provide the `OPENAI_API_KEY`:
|
||||
|
||||
```env:.env
|
||||
### LLM ###
|
||||
|
||||
OPENAI_API_KEY=your_openai_api_key_here
|
||||
```
|
||||
|
||||
If you plan to use other Large Language Model (LLM) providers, add their API keys as well:
|
||||
|
||||
```env:.env
|
||||
ANTHROPIC_API_KEY=your_anthropic_api_key_here
|
||||
```
|
||||
|
||||
### 3. Run Docker Compose
|
||||
|
||||
Start the Arcade services using Docker Compose:
|
||||
|
||||
```bash
|
||||
docker compose up -p arcade
|
||||
```
|
||||
|
||||
This command will build and start all the services defined in the `docker-compose.yml` file and make their ports available to your host machine.
|
||||
|
||||
### 4. Verify the Engine is Running
|
||||
|
||||
In a separate terminal window, check if the engine is running:
|
||||
|
||||
```bash
|
||||
curl http://localhost:9099/v1/health
|
||||
```
|
||||
|
||||
You should receive a response indicating that the engine is healthy:
|
||||
|
||||
```json
|
||||
{ "status": "healthy" }
|
||||
```
|
||||
|
||||
You can also view the swagger docs at `http://localhost:9099/swagger/index.html`
|
||||
|
||||
## Adding Authentication Providers
|
||||
|
||||
Arcade supports various authentication providers. To add an auth provider, follow these steps:
|
||||
|
||||
### 1. Enable the Auth Provider in the Configuration
|
||||
|
||||
Edit the `docker.engine.yaml` file to enable the desired auth provider. For example, to enable Google authentication, modify the file as follows:
|
||||
|
||||
```yaml:docker.engine.yaml
|
||||
auth:
|
||||
providers:
|
||||
- id: google
|
||||
enabled: true # Change from false to true
|
||||
```
|
||||
|
||||
### 2. Add Client ID and Secret to the `.env` File
|
||||
|
||||
Obtain the client ID and client secret from your auth provider and add them to the `.env` file:
|
||||
|
||||
```env:.env
|
||||
GOOGLE_CLIENT_ID="your_google_client_id"
|
||||
GOOGLE_CLIENT_SECRET="your_google_client_secret"
|
||||
```
|
||||
|
||||
Repeat this step for any other auth providers you wish to enable.
|
||||
|
||||
### 3. Restart the Docker Compose Services
|
||||
|
||||
After making changes to the configuration, restart the services:
|
||||
|
||||
```bash
|
||||
docker compose down
|
||||
docker compose up -p arcade
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **Engine Health Check Fails**: Ensure that all environment variables are correctly set in the `.env` file and that the services have started without errors.
|
||||
- **Port Conflicts**: If the default ports are already in use, modify the ports in the `docker-compose.yml` file.
|
||||
- **Authentication Errors**: Double-check the client IDs and secrets provided for auth providers.
|
||||
|
||||
NOTE: `arcade login` will not work within a docker container, you must copy your credentials into the container if you would like to use it.
|
||||
62
docker/docker-compose.yml
Normal file
62
docker/docker-compose.yml
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
version: '3.8'
|
||||
|
||||
services:
|
||||
engine:
|
||||
image: ghcr.io/arcadeai/engine:latest
|
||||
container_name: arcade-engine
|
||||
volumes:
|
||||
- ./docker.engine.yaml:/bin/engine.yaml
|
||||
- ./.env:/bin/.env
|
||||
- ./arcade-engine.sqlite3:/app/arcade-engine.sqlite3
|
||||
ports:
|
||||
- "9099:9099"
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
worker:
|
||||
condition: service_started
|
||||
networks:
|
||||
arcade-network:
|
||||
command: /bin/arcade-engine --config /bin/engine.yaml --env /bin/.env --migrate
|
||||
|
||||
worker:
|
||||
# image: ghcr.io/arcadeai/arcade-ai:latest
|
||||
image: arcade-worker:0.1.0
|
||||
container_name: arcade-worker
|
||||
ports:
|
||||
- "8002:8002"
|
||||
networks:
|
||||
arcade-network:
|
||||
|
||||
redis:
|
||||
image: redis/redis-stack:latest
|
||||
container_name: arcade-redis
|
||||
ports:
|
||||
- "6379:6379"
|
||||
- "8004:8002"
|
||||
depends_on:
|
||||
worker:
|
||||
condition: service_started
|
||||
healthcheck:
|
||||
test: [ "CMD", "redis-cli", "ping" ]
|
||||
interval: 3s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
networks:
|
||||
arcade-network:
|
||||
|
||||
nginx:
|
||||
image: nginx:stable-alpine
|
||||
container_name: arcade-nginx
|
||||
ports:
|
||||
- "80:80"
|
||||
volumes:
|
||||
- ./nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
depends_on:
|
||||
- engine
|
||||
networks:
|
||||
- arcade-network
|
||||
|
||||
networks:
|
||||
arcade-network:
|
||||
driver: bridge
|
||||
173
docker/docker.engine.yaml
Normal file
173
docker/docker.engine.yaml
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
telemetry:
|
||||
environment: local
|
||||
version: ${env:VERSION}
|
||||
logging:
|
||||
level: debug # debug, info, warn, error
|
||||
encoding: console
|
||||
|
||||
api:
|
||||
development: true
|
||||
host: 0.0.0.0
|
||||
port: 9099
|
||||
analytics:
|
||||
enabled: false
|
||||
# Uncomment to enable rate limiter
|
||||
# rate_limit:
|
||||
# redis:
|
||||
# addr: "localhost:6379"
|
||||
# password: ""
|
||||
# db: 2
|
||||
# time_unit: m
|
||||
# limit: 2
|
||||
# write_timeout: 10
|
||||
# read_timeout: 10
|
||||
|
||||
llm:
|
||||
models:
|
||||
- id: oai
|
||||
openai:
|
||||
api_key: ${env:OPENAI_API_KEY}
|
||||
# - id: anthropic
|
||||
# anthropic:
|
||||
# api_key: ${env:ANTHROPIC_API_KEY}
|
||||
# # model: "claude-3-5-sonnet-20240620"
|
||||
#- id: ollama
|
||||
# openai:
|
||||
# base_url: "http://localhost:11434/v1"
|
||||
# api_key: "ollama"
|
||||
|
||||
tools:
|
||||
directors:
|
||||
- id: default
|
||||
enabled: true
|
||||
workers:
|
||||
- id: "localworker"
|
||||
enabled: true
|
||||
http:
|
||||
uri: ${env:WORKER_URL}
|
||||
timeout: 30
|
||||
retry: 3
|
||||
secret: ${env:ARCADE_WORKER_SECRET} # If not set, defaults to "dev" in development mode only
|
||||
# Uncomment mock and comment http to start engine without live worker
|
||||
# mock:
|
||||
# enabled: true
|
||||
|
||||
security:
|
||||
root_keys:
|
||||
- id: key1
|
||||
default: true
|
||||
value: ${env:ROOT_KEY_1}
|
||||
# - id: key2
|
||||
# value: ${env:ROOT_KEY_2}
|
||||
|
||||
storage:
|
||||
# postgres:
|
||||
# user: postgres
|
||||
# password: 123456
|
||||
# host: localhost
|
||||
# port: 5432
|
||||
# db: arcade_engine
|
||||
# sslmode: disable
|
||||
sqlite: # Default for local development
|
||||
connection_string: "/app/arcade-engine.sqlite3"
|
||||
|
||||
cache:
|
||||
api_key_ttl: "10s"
|
||||
redis:
|
||||
addr: ${env:REDIS_HOST}:${env:REDIS_PORT}
|
||||
password: ${env:REDIS_PASSWORD}
|
||||
|
||||
auth:
|
||||
token_store:
|
||||
redis:
|
||||
addr: ${env:REDIS_HOST}:${env:REDIS_PORT}
|
||||
password: ${env:REDIS_PASSWORD}
|
||||
|
||||
providers:
|
||||
- id: default-atlassian
|
||||
description: "The default Atlassian provider"
|
||||
enabled: false
|
||||
type: oauth2
|
||||
provider_id: atlassian
|
||||
client_id: ${env:ATLASSIAN_CLIENT_ID}
|
||||
client_secret: ${env:ATLASSIAN_CLIENT_SECRET}
|
||||
|
||||
- id: default-discord
|
||||
description: "The default Discord provider"
|
||||
enabled: false
|
||||
type: oauth2
|
||||
provider_id: discord
|
||||
client_id: ${env:DISCORD_CLIENT_ID}
|
||||
client_secret: ${env:DISCORD_CLIENT_SECRET}
|
||||
|
||||
- id: default-dropbox
|
||||
description: "The default Dropbox provider"
|
||||
enabled: false
|
||||
type: oauth2
|
||||
provider_id: dropbox
|
||||
client_id: ${env:DROPBOX_CLIENT_ID}
|
||||
client_secret: ${env:DROPBOX_CLIENT_SECRET}
|
||||
|
||||
- id: default-github
|
||||
description: "The default GitHub provider"
|
||||
enabled: false
|
||||
type: oauth2
|
||||
provider_id: github
|
||||
client_id: ${env:GITHUB_CLIENT_ID}
|
||||
client_secret: ${env:GITHUB_CLIENT_SECRET}
|
||||
|
||||
- id: default-google
|
||||
description: "The default Google provider"
|
||||
enabled: false
|
||||
type: oauth2
|
||||
provider_id: google
|
||||
client_id: ${env:GOOGLE_CLIENT_ID}
|
||||
client_secret: ${env:GOOGLE_CLIENT_SECRET}
|
||||
|
||||
- id: default-linkedin
|
||||
description: "The default LinkedIn provider"
|
||||
enabled: false
|
||||
type: oauth2
|
||||
provider_id: linkedin
|
||||
client_id: ${env:LINKEDIN_CLIENT_ID}
|
||||
client_secret: ${env:LINKEDIN_CLIENT_SECRET}
|
||||
|
||||
- id: default-microsoft
|
||||
description: "The default Microsoft provider"
|
||||
enabled: false
|
||||
type: oauth2
|
||||
provider_id: microsoft
|
||||
client_id: ${env:MICROSOFT_CLIENT_ID}
|
||||
client_secret: ${env:MICROSOFT_CLIENT_SECRET}
|
||||
|
||||
- id: default-slack
|
||||
description: "The default Slack provider"
|
||||
enabled: false
|
||||
type: oauth2
|
||||
provider_id: slack
|
||||
client_id: ${env:SLACK_CLIENT_ID}
|
||||
client_secret: ${env:SLACK_CLIENT_SECRET}
|
||||
|
||||
- id: default-spotify
|
||||
description: "The default Spotify provider"
|
||||
enabled: false
|
||||
type: oauth2
|
||||
provider_id: spotify
|
||||
client_id: ${env:SPOTIFY_CLIENT_ID}
|
||||
client_secret: ${env:SPOTIFY_CLIENT_SECRET}
|
||||
|
||||
- id: default-x
|
||||
description: "The default X provider"
|
||||
enabled: false
|
||||
type: oauth2
|
||||
provider_id: x
|
||||
client_id: ${env:X_CLIENT_ID}
|
||||
client_secret: ${env:X_CLIENT_SECRET}
|
||||
|
||||
- id: default-zoom
|
||||
description: "The default Zoom provider"
|
||||
enabled: false
|
||||
type: oauth2
|
||||
provider_id: zoom
|
||||
client_id: ${env:ZOOM_CLIENT_ID}
|
||||
client_secret: ${env:ZOOM_CLIENT_SECRET}
|
||||
48
docker/env.example
Normal file
48
docker/env.example
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
|
||||
### LLM ###
|
||||
|
||||
OPENAI_API_KEY=
|
||||
ANTHROPIC_API_KEY=
|
||||
|
||||
### Auth providers ###
|
||||
|
||||
GITHUB_CLIENT_ID=
|
||||
GITHUB_CLIENT_SECRET=
|
||||
|
||||
GOOGLE_CLIENT_ID=""
|
||||
GOOGLE_CLIENT_SECRET=
|
||||
|
||||
LINKEDIN_CLIENT_ID=
|
||||
LINKEDIN_CLIENT_SECRET=""
|
||||
|
||||
MICROSOFT_CLIENT_ID=
|
||||
MICROSOFT_CLIENT_SECRET=""
|
||||
|
||||
SLACK_CLIENT_ID=""
|
||||
SLACK_CLIENT_SECRET=
|
||||
|
||||
SPOTIFY_CLIENT_ID=
|
||||
SPOTIFY_CLIENT_SECRET=
|
||||
|
||||
X_CLIENT_ID=
|
||||
X_CLIENT_SECRET=
|
||||
|
||||
ZOOM_CLIENT_ID=
|
||||
ZOOM_CLIENT_SECRET=
|
||||
|
||||
### WORKER ###
|
||||
|
||||
WORKER_URL="http://arcade-worker:8002"
|
||||
|
||||
### Redis ###
|
||||
|
||||
REDIS_HOST=arcade-redis
|
||||
REDIS_PORT=6379
|
||||
REDIS_PASSWORD=""
|
||||
RATE_LIMIT_REQS_PER_MIN=100
|
||||
|
||||
### ARCADE ###
|
||||
|
||||
ARCADE_HOME=/app
|
||||
ARCADE_WORKER_SECRET=dev
|
||||
ROOT_KEY_1=supersecret
|
||||
47
docker/nginx.conf
Normal file
47
docker/nginx.conf
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
user nginx;
|
||||
worker_processes auto;
|
||||
error_log /var/log/nginx/error.log;
|
||||
pid /run/nginx.pid;
|
||||
|
||||
include /usr/share/nginx/modules/*.conf;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
sendfile on;
|
||||
client_max_body_size 5M;
|
||||
client_body_buffer_size 5M;
|
||||
|
||||
gzip on;
|
||||
gzip_comp_level 2;
|
||||
gzip_types text/plain text/css text/javascript application/javascript application/xml image/jpeg image/gif image/png;
|
||||
gzip_vary on;
|
||||
|
||||
keepalive_timeout 300;
|
||||
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
server_name arcade-engine:9099;
|
||||
|
||||
root /app;
|
||||
client_max_body_size 10m;
|
||||
|
||||
location / {
|
||||
proxy_pass http://arcade-engine:9099;
|
||||
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
|
||||
proxy_connect_timeout 300s;
|
||||
proxy_send_timeout 300s;
|
||||
proxy_read_timeout 300s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,4 @@
|
|||
#!/bin/bash
|
||||
|
||||
echo "Installing toolkits..."
|
||||
for toolkit in $(echo $TOOLKITS | tr "," " "); do
|
||||
echo "Installing $toolkit..."
|
||||
pip install $toolkit
|
||||
done
|
||||
|
||||
echo "Starting arcade..."
|
||||
arcade workerup --host $HOST --port $PORT $([ "$OTEL_ENABLE" = "true" ] && echo "--otel-enable")
|
||||
|
|
|
|||
11
docker/toolkits.txt
Normal file
11
docker/toolkits.txt
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
arcade-code-sandbox
|
||||
arcade-github
|
||||
arcade-google
|
||||
arcade-linkedin
|
||||
arcade-math
|
||||
arcade-search
|
||||
arcade-slack
|
||||
arcade-spotify
|
||||
arcade-web
|
||||
arcade-x
|
||||
arcade-zoom
|
||||
10
examples/serving-tools/docker/Dockerfile
Normal file
10
examples/serving-tools/docker/Dockerfile
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
ARG VERSION=latest
|
||||
|
||||
# Base worker image
|
||||
FROM ghcr.io/arcadeai/worker-base:${VERSION}
|
||||
|
||||
# Copy requirements and constraints
|
||||
COPY toolkits.txt ./
|
||||
|
||||
# Install toolkits from file
|
||||
RUN pip install -r toolkits.txt
|
||||
30
examples/serving-tools/docker/README.md
Normal file
30
examples/serving-tools/docker/README.md
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
## Custom Worker Image
|
||||
|
||||
This example shows how to build a custom worker image with toolkits.
|
||||
|
||||
### Requirements
|
||||
|
||||
- Docker
|
||||
|
||||
### Build
|
||||
|
||||
```
|
||||
docker build -t custom-worker:0.1.0 .
|
||||
```
|
||||
|
||||
### Run
|
||||
|
||||
```
|
||||
docker run -p 8002:8002 custom-worker:0.1.0
|
||||
```
|
||||
|
||||
### Change the Toolkits
|
||||
|
||||
To change the toolkits, edit the `toolkits.txt` file.
|
||||
|
||||
```
|
||||
arcade-google==0.1.0
|
||||
arcade-web==0.1.0
|
||||
arcade-zoom==0.1.2
|
||||
...
|
||||
```
|
||||
1
examples/serving-tools/docker/toolkits.txt
Normal file
1
examples/serving-tools/docker/toolkits.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
arcade-google
|
||||
17
examples/serving-tools/modal/README.md
Normal file
17
examples/serving-tools/modal/README.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
## Deploy a Custom Arcade Worker on Modal
|
||||
|
||||
### Requirements
|
||||
|
||||
- Python 3.10+
|
||||
- Modal CLI
|
||||
|
||||
### Deploy
|
||||
|
||||
```bash
|
||||
cd examples/serving-tools
|
||||
modal deploy run-arcade-worker.py
|
||||
```
|
||||
|
||||
### Changing the Toolkits
|
||||
|
||||
To change the toolkits, edit the `toolkits` list in the `run-arcade-worker.py` file.
|
||||
39
examples/serving-tools/modal/run-arcade-worker.py
Normal file
39
examples/serving-tools/modal/run-arcade-worker.py
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import os
|
||||
|
||||
from modal import App, Image, asgi_app
|
||||
|
||||
# Define the FastAPI app
|
||||
app = App("arcade-worker")
|
||||
|
||||
toolkits = ["arcade-google", "arcade-slack"]
|
||||
|
||||
image = (
|
||||
Image.debian_slim()
|
||||
.pip_install("arcade-ai[fastapi]")
|
||||
.pip_install(toolkits)
|
||||
.pip_install("fastapi>=0.115.3")
|
||||
.pip_install("uvicorn>=0.24.0")
|
||||
)
|
||||
|
||||
|
||||
@app.function(image=image)
|
||||
@asgi_app()
|
||||
def fastapi_app():
|
||||
from fastapi import FastAPI
|
||||
|
||||
from arcade.sdk import Toolkit
|
||||
from arcade.worker.fastapi.worker import FastAPIWorker
|
||||
|
||||
web_app = FastAPI()
|
||||
|
||||
# Initialize app and Arcade FastAPIWorker
|
||||
worker_secret = os.environ.get("ARCADE_WORKER_SECRET", "dev")
|
||||
worker = FastAPIWorker(web_app, secret=worker_secret)
|
||||
|
||||
# Register toolkits we've installed
|
||||
installed_toolkits = Toolkit.find_all_arcade_toolkits()
|
||||
for toolkit in toolkits:
|
||||
if toolkit in installed_toolkits:
|
||||
worker.register_toolkit(toolkit)
|
||||
|
||||
return web_app
|
||||
Loading…
Reference in a new issue