Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/build_and_push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: Build and Push
on:
push:
# tags: ["v*.*.*"] # Match semantic versioning tags like v1.0.0
branches: ["main"]
branches: ["main", "ui"]
workflow_dispatch: # Allow manual triggering of the workflow

env:
Expand Down Expand Up @@ -40,7 +40,7 @@ jobs:

# Cache Docker layers to speed up builds
- name: Cache Docker layers
uses: actions/cache@v3 # Pin to a specific version
uses: actions/cache@v3
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-docker-${{ github.ref }}
Expand All @@ -49,7 +49,7 @@ jobs:

# Build and push Docker image
- name: Build and push Docker image
uses: docker/build-push-action@v6 # Pin to a specific version
uses: docker/build-push-action@v6
id: push
with:
context: .
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ jobs:
runs-on: "ubuntu-latest"
steps:
- name: Checkout repository
uses: actions/checkout@v4 # Pin to specific version
uses: actions/checkout@v4

# Set up Python environment
- name: Set up uv
uses: astral-sh/setup-uv@v5 # Pin to specific version
uses: astral-sh/setup-uv@v5
with:
version: "0.5.24"

Expand All @@ -30,7 +30,7 @@ jobs:
# Lint code with ruff
- name: Lint with ruff
run: uv run ruff check --diff
continue-on-error: false # Fail if issues are found
continue-on-error: false

# Type check with pyright
- name: Type check with pyright
Expand Down
39 changes: 29 additions & 10 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,24 +1,43 @@
# Stage 1: Build Stage
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim AS builder
# Stage 1: Build Frontend
FROM node:18-alpine AS frontend-builder
WORKDIR /frontend
COPY chat-ui/ .
RUN npm install
RUN npm run build

# Stage 2: Build Backend
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim AS backend-builder
ADD . /flare-ai-core
WORKDIR /flare-ai-core
RUN uv sync --frozen

# Stage 2: Production Stage (Final Image)
# Stage 3: Final Image
FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim

# Install nginx
RUN apt-get update && apt-get install -y nginx supervisor curl && \
rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY --from=backend-builder /flare-ai-core/.venv ./.venv
COPY --from=backend-builder /flare-ai-core/src ./src
COPY --from=backend-builder /flare-ai-core/pyproject.toml .
COPY --from=backend-builder /flare-ai-core/README.md .

# Copy frontend files
COPY --from=frontend-builder /frontend/build /usr/share/nginx/html

# Copy nginx configuration
COPY nginx.conf /etc/nginx/sites-enabled/default

COPY --from=builder /flare-ai-core/.venv ./.venv
COPY --from=builder /flare-ai-core/src ./src
COPY --from=builder /flare-ai-core/pyproject.toml .
COPY --from=builder /flare-ai-core/README.md .
# Setup supervisor configuration
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf

# Allow workload operator to override environment variables
LABEL "tee.launch_policy.allow_env_override"="GEMINI_API_KEY,GEMINI_MODEL,WEB3_PROVIDER_URL,WEB3_EXPLORER_URL,SIMULATE_ATTESTATION"
LABEL "tee.launch_policy.log_redirect"="always"

EXPOSE 8080
EXPOSE 80

# Define the entrypoint
ENTRYPOINT ["uv", "run", "start-backend"]
# Start supervisor (which will start both nginx and the backend)
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
107 changes: 106 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,106 @@
# AI Core
# Flare AI Core

A confidential computing solution for AI workloads on Google Cloud Platform, supporting both AMD SEV and Intel TDX architectures.

## Prerequisites

- Google Cloud Platform account with access to the `google-hackathon-project`
- Gemini API key
- `gcloud` CLI installed and configured

## Environment Setup

1. Add the following environment variables to your shell configuration file (`~/.bashrc` or `~/.zshrc`):

```bash
export TEE_IMAGE_REFERENCE=ghcr.io/flare-foundation/flare-ai-core:main
export GEMINI_API_KEY=<your-gemini-api-key>
export WEB3_PROVIDER_URL=https://coston2-api.flare.network/ext/C/rpc
export WEB3_EXPLORER_URL=https://coston2-explorer.flare.network/
export TEAM_NAME=<your-team-name>
```

2. Reload your shell configuration:

```bash
source ~/.bashrc # or ~/.zshrc
```

3. Verify the configuration:

```bash
echo $TEE_IMAGE_REFERENCE # Should output: ghcr.io/flare-foundation/flare-ai-core:main
```

## Deployment

### AMD SEV Deployment

Deploy an AMD SEV-enabled instance with the following configuration:

```bash
gcloud compute instances create ai-core-$TEAM_NAME \
--project=google-hackathon-project \
--zone=us-central1-c \
--machine-type=n2d-standard-2 \
--network-interface=network-tier=PREMIUM,nic-type=GVNIC,stack-type=IPV4_ONLY,subnet=default \
--metadata=tee-image-reference=$TEE_IMAGE_REFERENCE,\
tee-container-log-redirect=true,\
tee-env-GEMINI_API_KEY=$GEMINI_API_KEY,\
tee-env-GEMINI_MODEL=gemini-1.5-flash,\
tee-env-WEB3_PROVIDER_URL=$WEB3_PROVIDER_URL,\
tee-env-SIMULATE_ATTESTATION=false \
--maintenance-policy=MIGRATE \
--provisioning-model=STANDARD \
--service-account=confidential-sa@flare-network-sandbox.iam.gserviceaccount.com \
--scopes=https://www.googleapis.com/auth/cloud-platform \
--min-cpu-platform="AMD Milan" \
--tags=flare-ai-core,http-server,https-server \
--create-disk=auto-delete=yes,\
boot=yes,\
device-name=ai-core-$TEAM_NAME,\
image=projects/confidential-space-images/global/images/confidential-space-debug-250100,\
mode=rw,\
size=11,\
type=pd-standard \
--shielded-secure-boot \
--shielded-vtpm \
--shielded-integrity-monitoring \
--labels=goog-ec-src=vm_add-gcloud \
--reservation-affinity=any \
--confidential-compute-type=SEV
```

### Intel TDX Deployment

For Intel TDX machines, use this alternative configuration:

```bash
gcloud compute instances create ai-core-$TEAM_NAME \
--project=google-hackathon-project \
--machine-type=c3-standard-4 \
--maintenance-policy=TERMINATE \
--zone=us-central1-c \
--network-interface=network-tier=PREMIUM,nic-type=GVNIC,stack-type=IPV4_ONLY,subnet=default \
--metadata=tee-image-reference=$TEE_IMAGE_REFERENCE,\
tee-container-log-redirect=true,\
tee-env-GEMINI_API_KEY=$GEMINI_API_KEY,\
tee-env-GEMINI_MODEL=gemini-1.5-flash,\
tee-env-WEB3_PROVIDER_URL=$WEB3_PROVIDER_URL,\
tee-env-SIMULATE_ATTESTATION=false \
--provisioning-model=STANDARD \
--service-account=confidential-sa@flare-network-sandbox.iam.gserviceaccount.com \
--scopes=https://www.googleapis.com/auth/cloud-platform \
--tags=flare-ai-core,http-server,https-server \
--create-disk=auto-delete=yes,\
boot=yes,\
device-name=ai-core-$TEAM_NAME,\
image=projects/confidential-space-images/global/images/confidential-space-debug-0-tdxpreview-c38b622,\
mode=rw,\
size=11,\
type=pd-balanced \
--shielded-secure-boot \
--shielded-vtpm \
--shielded-integrity-monitoring \
--confidential-compute-type=TDX
```
2 changes: 1 addition & 1 deletion chat-ui/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const ChatInterface = () => {

const handleSendMessage = async (text) => {
try {
const response = await fetch(`${process.env.REACT_APP_API_URL}/api/routes/chat/`, {
const response = await fetch('api/routes/chat/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Expand Down
67 changes: 67 additions & 0 deletions nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
server {
listen 80 default_server;
listen [::]:80 default_server;

root /usr/share/nginx/html;
index index.html;

# Enable gzip compression
gzip on;
gzip_vary on;
gzip_min_length 10240;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml;
gzip_disable "MSIE [1-6]\.";

# Security headers
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header X-Content-Type-Options "nosniff";
add_header Referrer-Policy "strict-origin-when-cross-origin";

# Cache static assets
location /static/ {
expires 1y;
add_header Cache-Control "public, no-transform";
}

# Handle React routing
location / {
try_files $uri $uri/ /index.html;

# Don't cache index.html
add_header Cache-Control "no-store, no-cache, must-revalidate";
}

# API proxy configuration
location /api/ {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $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_cache_bypass $http_upgrade;

# CORS settings
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';

# Handle preflight requests
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
}

# Handle 404 errors
error_page 404 /index.html;
}
43 changes: 7 additions & 36 deletions src/flare_ai_core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,50 +18,21 @@
class Settings(BaseSettings):
"""
Application settings model that provides configuration for all components.

Attributes:
simulate_attestation (bool): Flag to enable/disable attestation simulation.
Defaults to False.

cors_origins (list[str]): List of allowed CORS origins.
Defaults to ["*"] allowing all origins.

gemini_api_key (str): API key for accessing Google's Gemini AI service.
Must be provided via environment variable or .env file.
Defaults to empty string.

gemini_model (str): The Gemini model identifier to use.
Defaults to "gemini-1.5-flash".

api_version (str): Version string for the API.
Defaults to "v1".

web3_provider_url (str): URL for the Flare Network Web3 provider.
Defaults to Coston2 testnet RPC endpoint:
"https://coston2-api.flare.network/ext/C/rpc"

web3_explorer_url (str): URL for the Flare Network block explorer.
Defaults to Coston2 testnet explorer:
"https://coston2-explorer.flare.network/"

Environment Variables:
All settings can be overridden by environment variables with the same name
in uppercase, e.g.:
- SIMULATE_ATTESTATION
- CORS_ORIGINS
- GEMINI_API_KEY
- GEMINI_MODEL
- API_VERSION
- WEB3_PROVIDER_URL
- WEB3_EXPLORER_URL
"""

# Flag to enable/disable attestation simulation
simulate_attestation: bool = False
# Restrict backend listener to specific IPs
cors_origins: list[str] = ["*"]
# API key for accessing Google's Gemini AI service
gemini_api_key: str = ""
# The Gemini model identifier to use
gemini_model: str = "gemini-1.5-flash"
# API version to use at the backend
api_version: str = "v1"
# URL for the Flare Network RPC provider
web3_provider_url: str = "https://coston2-api.flare.network/ext/C/rpc"
# URL for the Flare Network block explorer
web3_explorer_url: str = "https://coston2-explorer.flare.network/"

model_config = SettingsConfigDict(
Expand Down
22 changes: 22 additions & 0 deletions supervisord.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[supervisord]
nodaemon=true
user=root

[program:nginx]
command=nginx -g 'daemon off;'
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0

[program:backend]
command=uv run start-backend
directory=/app
autostart=true
autorestart=true
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
Loading
Loading