Merge pull request #21 from invariantlabs-ai/ghcr

Publish GitHub Container Images + Self-Hosted Setup Instructions
This commit is contained in:
Hemang Sarkar
2025-03-12 22:04:30 +01:00
committed by GitHub
8 changed files with 105 additions and 8 deletions
+54
View File
@@ -0,0 +1,54 @@
name: Build Docker Images
on:
workflow_run:
workflows:
- Invariant gateway testing CI
types:
- completed
branches:
- main
- v*
jobs:
build-explorer-local-images:
runs-on: ubuntu-latest
# check that this is a tag or the 'main' branch but not a pull request
# if: ${{ github.event.workflow_run.conclusion == 'success' && (github.ref_type == 'tag' || github.ref == 'refs/heads/main') }}
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to GitHub Container Registry
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
# based on whether this is a version tag or a branch, we will set the tags for the pushed images
- name: Set Docker Tags
id: set-tags
run: |
if [[ "${{ github.event.workflow_run.head_branch }}" == "main" ]]; then
echo "tags=ghcr.io/${{ github.repository }}/gateway:latest" >> $GITHUB_ENV
# starts with v* and only contains numbers and dots
elif [[ "${{ github.event.workflow_run.head_branch }}" =~ ^v[0-9.]+$ ]]; then
# get tag name from the event
version=${{ github.event.workflow_run.head_branch }}
# remove the refs/tags/ prefix
echo "version string is $version"
echo "tags=ghcr.io/${{ github.repository }}/gateway:latest,ghcr.io/${{ github.repository }}/gateway:${version}" >> $GITHUB_ENV
else
echo "Failed to determine the tags for the Docker images for branch ${{ github.event.workflow_run.head_branch }}"
exit 1
fi
- name: build gateway image
uses: docker/build-push-action@v5
with:
context: ./gateway
file: ./gateway/Dockerfile.gateway
platforms: linux/amd64
push: true
tags: ${{ env.tags }}
+26 -3
View File
@@ -2,7 +2,7 @@
**LLM proxy to observe and debug what your AI agents are doing.**
[Documentation](https://explorer.invariantlabs.ai/docs/gateway) | [Quickstart for Users](#quickstart-for-users) | [Quickstart for Developers](#quickstart-for-developers)
[Documentation](https://explorer.invariantlabs.ai/docs/gateway) | [Quickstart for Users](#quickstart-for-users) | [Quickstart for Developers](#quickstart-for-developers) | [Run Locally](#run-the-gateway-locally)
<a href="https://discord.gg/dZuZfhKnJ4"><img src="https://img.shields.io/discord/1265409784409231483?style=plastic&logo=discord&color=blueviolet&logoColor=white" height=18/></a>
@@ -38,6 +38,7 @@ To add Gateway to your agentic system, follow one of the integration guides belo
## **Integration Guides**
### **🔹 OpenAI Integration**
Gateway supports the OpenAI Chat Completions API (`/v1/chat/completions` endpoint).
1. Follow [these steps](https://platform.openai.com/docs/quickstart#create-and-export-an-api-key) to obtain an OpenAI API key.
@@ -62,6 +63,7 @@ Gateway supports the OpenAI Chat Completions API (`/v1/chat/completions` endpoin
> **Note:** Do not include the curly braces `{}`. If the dataset does not exist in Invariant Explorer, it will be created before adding traces.
### **🔹 Anthropic Integration**
Gateway supports the Anthropic Messages API (`/v1/messages` endpoint).
1. Follow [these steps](https://docs.anthropic.com/en/docs/initial-setup#set-your-api-key) to obtain an Anthropic API key.
@@ -84,6 +86,7 @@ Gateway supports the Anthropic Messages API (`/v1/messages` endpoint).
> **Note:** Do not include the curly braces `{}`. If the dataset does not exist in Invariant Explorer, it will be created before adding traces.
### **🔹 Gemini Integration**
Gateway supports the Gemini `generateContent` and `streamGenerateContent` methods.
1. Follow [these steps](https://ai.google.dev/gemini-api/docs/api-key) to obtain a Gemini API key.
@@ -91,7 +94,7 @@ Gateway supports the Gemini `generateContent` and `streamGenerateContent` method
```python
import os
from google import genai
client = genai.Client(
@@ -249,14 +252,34 @@ This setup ensures that SWE-agent works seamlessly with Invariant Gateway, maint
You can also operate your own instance of the Gateway, to ensure privacy and security.
First, clone this repository. To start the Invariant Gateway, run:
To run Gateway locally, you have two options:
### 1. Run gateway from the repository
1. Clone this repository.
2. To start the Invariant Gateway, then run the following commands. Note that you need to have Docker installed.
```bash
cd invariant-gateway
bash run.sh build && bash run.sh up
```
This will launch Gateway at [http://localhost:8005/api/v1/gateway/](http://localhost:8005/api/v1/gateway/docs/).
### 2. Run the Gateway using the published Docker image
You can also run the Gateway using the published Docker image. This is a good option if you want to run the Gateway in a cloud environment.
```bash
# pull the latest image
docker pull --platform linux/amd64 ghcr.io/invariantlabs-ai/invariant-gateway/gateway:latest
# run Gateway on localhost:8005
docker run -p 8005:8005 -e PORT=8005 --platform linux/amd64 ghcr.io/invariantlabs-ai/invariant-gateway/gateway:latest
```
This will launch Gateway at [http://localhost:8002/api/v1/gateway/](http://localhost:8002/api/v1/gateway/docs/).
### **Set Up an Invariant API Key**
1. Follow the instructions [here](https://explorer.invariantlabs.ai/docs/explorer/api/client-setup/) to obtain an API key. This allows the gateway to push traces to [Invariant Explorer](https://explorer.invariantlabs.ai).
+4
View File
@@ -0,0 +1,4 @@
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_DB=invariantmonitor
POSTGRES_HOST=database
+3 -1
View File
@@ -8,5 +8,7 @@ COPY . /srv/gateway
# Ensure run.sh is executable
RUN chmod +x /srv/gateway/run.sh
WORKDIR /srv/gateway
ENTRYPOINT ./run.sh
# Run the application
CMD ["./run.sh"]
+1 -1
View File
@@ -20,7 +20,7 @@ class GatewayConfig:
policies_file = os.getenv("POLICIES_FILE_PATH", "")
if not policies_file:
print("Warning: POLICIES_FILE_PATH is not set. Using empty policies.")
print("[warning: POLICIES_FILE_PATH is not set. Using empty policies]")
return ""
try:
+7 -2
View File
@@ -9,8 +9,13 @@ if [ $? -ne 0 ]; then
exit 1
fi
# check if PORT environment variable is set
UVICORN_PORT=${PORT:-8000}
# using 'exec' belows ensures that signals like SIGTERM are passed to the child process
# and not the shell script itself (important when running in a container)
if [ "$DEV_MODE" = "true" ]; then
uvicorn serve:app --host 0.0.0.0 --port 8000 --reload
exec uvicorn serve:app --host 0.0.0.0 --port $UVICORN_PORT --reload
else
uvicorn serve:app --host 0.0.0.0 --port 8000
exec uvicorn serve:app --host 0.0.0.0 --port $UVICORN_PORT
fi
+9
View File
@@ -32,5 +32,14 @@ router.include_router(gemini_gateway, prefix="/gateway", tags=["gemini_gateway"]
app.include_router(router)
# on / redirect to https://explorer.invariantlabs.ai/docs/gateway
@app.get("/", include_in_schema=False)
async def redirect_to_explorer():
"""Redirect to the explorer"""
return fastapi.responses.RedirectResponse(
url="https://explorer.invariantlabs.ai/docs/gateway"
)
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
+1 -1
View File
@@ -6,7 +6,7 @@ from common.config_manager import GatewayConfigManager
try:
_ = GatewayConfigManager.get_config()
print("GatewayConfig validated successfully.")
print("[gateway config validated successfully]")
sys.exit(0)
except Exception as e:
print(f"Error loading GatewayConfig error: {e}")