Mark Duppenthaler commited on
Commit
9a03fcf
·
1 Parent(s): 7e43835

Test docker

Browse files
.dockerignore ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.so
6
+ .Python
7
+ env/
8
+ build/
9
+ develop-eggs/
10
+ dist/
11
+ downloads/
12
+ eggs/
13
+ .eggs/
14
+ lib64/
15
+ parts/
16
+ sdist/
17
+ var/
18
+ *.egg-info/
19
+ .installed.cfg
20
+ *.egg
21
+
22
+ # Node
23
+ node_modules/
24
+ npm-debug.log
25
+ yarn-debug.log
26
+ yarn-error.log
27
+ frontend/dist/
28
+ frontend/node_modules/
29
+
30
+ # Virtual environments
31
+ venv/
32
+ ENV/
33
+ env/
34
+
35
+ # Version control
36
+ .git
37
+ .gitignore
38
+
39
+ # Docker
40
+ .dockerignore
41
+ docker-compose.yml
42
+
43
+ # IDE specific files
44
+ .idea/
45
+ .vscode/
46
+ *.swp
47
+ *.swo
48
+
49
+ # Test files
50
+ test/
51
+ tests/
52
+
53
+ # Development files
54
+ .env.local
55
+ .env.development
56
+ .env.test
57
+ .env.production
58
+
59
+ # Logs
60
+ logs/
61
+ *.log
62
+
63
+ # Documentation
64
+ docs/
65
+ *.md
66
+ !README.md
.env.example ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Environment variables for OmniSealBench
2
+ # Copy this file to .env and modify as needed
3
+
4
+ # Flask settings
5
+ FLASK_APP=app.py
6
+ FLASK_ENV=development # Change to 'production' for deployment
7
+
8
+ # Server settings
9
+ PORT=5000
10
+ HOST=0.0.0.0
11
+
12
+ # Data paths (relative to the backend directory)
13
+ DATA_PATH=../data
14
+ EXAMPLES_PATH=../examples
DEPLOYMENT.md ADDED
@@ -0,0 +1,165 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Deployment Guide for OmniSealBench
2
+
3
+ This guide provides detailed instructions for deploying OmniSealBench to different environments.
4
+
5
+ ## 1. HuggingFace Spaces Deployment
6
+
7
+ ### Prerequisites
8
+ - A HuggingFace account
9
+ - Git installed on your local machine
10
+
11
+ ### Steps
12
+
13
+ 1. **Prepare your application for deployment**
14
+ ```bash
15
+ ./prepare_deploy.sh
16
+ ```
17
+ This script will:
18
+ - Set the environment to production
19
+ - Test the frontend build
20
+ - Validate the Docker configuration
21
+
22
+ 2. **Create a new Space on HuggingFace**
23
+ - Go to https://huggingface.co/spaces
24
+ - Click "Create new Space"
25
+ - Set a name for your Space (e.g., "omnisealbench")
26
+ - Select "Docker" as the SDK
27
+ - Set the hardware tier according to your needs (CPU recommended)
28
+ - Click "Create Space"
29
+
30
+ 3. **Clone the HuggingFace repository**
31
+ ```bash
32
+ git clone https://huggingface.co/spaces/YOUR_USERNAME/omnisealbench
33
+ cd omnisealbench
34
+ ```
35
+
36
+ 4. **Copy your project files to the HuggingFace repository**
37
+ ```bash
38
+ # From your project directory
39
+ cp -R * /path/to/huggingface/repo/
40
+ ```
41
+
42
+ 5. **Commit and push to HuggingFace**
43
+ ```bash
44
+ cd /path/to/huggingface/repo
45
+ git add .
46
+ git commit -m "Initial deployment of OmniSealBench"
47
+ git push
48
+ ```
49
+
50
+ 6. **Monitor the deployment**
51
+ - Go to your Space on HuggingFace
52
+ - Click on the "Settings" tab
53
+ - Check the "Container logs" to see the build and deployment progress
54
+
55
+ ## 2. Manual Docker Deployment
56
+
57
+ ### Prerequisites
58
+ - Docker installed on the host machine
59
+
60
+ ### Steps
61
+
62
+ 1. **Build the Docker image**
63
+ ```bash
64
+ docker build -t omnisealbench:latest .
65
+ ```
66
+
67
+ 2. **Run the container**
68
+ ```bash
69
+ docker run -d --name omnisealbench -p 5000:5000 omnisealbench:latest
70
+ ```
71
+
72
+ 3. **Access the application**
73
+ - Open a web browser and navigate to http://localhost:5000
74
+ - If deploying on a remote server, replace "localhost" with the server's IP or domain
75
+
76
+ 4. **Monitor the application**
77
+ ```bash
78
+ # View logs
79
+ docker logs -f omnisealbench
80
+
81
+ # Check container status
82
+ docker ps -a | grep omnisealbench
83
+ ```
84
+
85
+ ## 3. Cloud Deployment Options
86
+
87
+ ### AWS Elastic Container Service (ECS)
88
+
89
+ 1. **Push your Docker image to Amazon ECR**
90
+ ```bash
91
+ # Authenticate Docker to your ECR registry
92
+ aws ecr get-login-password --region your-region | docker login --username AWS --password-stdin your-account-id.dkr.ecr.your-region.amazonaws.com
93
+
94
+ # Create a repository
95
+ aws ecr create-repository --repository-name omnisealbench
96
+
97
+ # Tag your image
98
+ docker tag omnisealbench:latest your-account-id.dkr.ecr.your-region.amazonaws.com/omnisealbench:latest
99
+
100
+ # Push the image
101
+ docker push your-account-id.dkr.ecr.your-region.amazonaws.com/omnisealbench:latest
102
+ ```
103
+
104
+ 2. **Create an ECS cluster and service** using the AWS Management Console or AWS CLI
105
+
106
+ ### Google Cloud Run
107
+
108
+ 1. **Push your Docker image to Google Container Registry**
109
+ ```bash
110
+ # Tag your image for GCR
111
+ docker tag omnisealbench:latest gcr.io/your-project-id/omnisealbench:latest
112
+
113
+ # Push the image
114
+ docker push gcr.io/your-project-id/omnisealbench:latest
115
+ ```
116
+
117
+ 2. **Deploy to Cloud Run** using the Google Cloud Console or gcloud CLI
118
+ ```bash
119
+ gcloud run deploy omnisealbench --image gcr.io/your-project-id/omnisealbench:latest --platform managed
120
+ ```
121
+
122
+ ### Azure Container Instances
123
+
124
+ 1. **Push your Docker image to Azure Container Registry**
125
+ ```bash
126
+ # Log in to Azure
127
+ az login
128
+
129
+ # Create a resource group if needed
130
+ az group create --name myResourceGroup --location eastus
131
+
132
+ # Create a container registry
133
+ az acr create --resource-group myResourceGroup --name myacr --sku Basic
134
+
135
+ # Log in to the registry
136
+ az acr login --name myacr
137
+
138
+ # Tag your image
139
+ docker tag omnisealbench:latest myacr.azurecr.io/omnisealbench:latest
140
+
141
+ # Push the image
142
+ docker push myacr.azurecr.io/omnisealbench:latest
143
+ ```
144
+
145
+ 2. **Deploy to Azure Container Instances** using the Azure Portal or Azure CLI
146
+
147
+ ## Troubleshooting
148
+
149
+ ### Common Issues
150
+
151
+ 1. **Container fails to start**
152
+ - Check logs: `docker logs omnisealbench`
153
+ - Verify port mappings: `docker port omnisealbench`
154
+ - Check if the healthcheck is passing: `docker inspect --format='{{.State.Health.Status}}' omnisealbench`
155
+
156
+ 2. **Application is not accessible**
157
+ - Check if the container is running: `docker ps | grep omnisealbench`
158
+ - Verify the host's firewall settings to ensure port 5000 is open
159
+ - Try accessing the application from inside the container: `docker exec -it omnisealbench curl localhost:5000`
160
+
161
+ 3. **Data or examples not showing up**
162
+ - Verify the data and examples directories were properly copied into the container
163
+ - Check file permissions: `docker exec -it omnisealbench ls -la /app/data /app/examples`
164
+
165
+ For more help, please open an issue on the GitHub repository.
Dockerfile ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Multi-stage Dockerfile for OmniSealBench
2
+
3
+ # Stage 1: Build the React frontend
4
+ FROM node:18-alpine AS frontend-builder
5
+
6
+ WORKDIR /app/frontend
7
+
8
+ # Copy package.json
9
+ COPY frontend/package.json ./
10
+
11
+ # Install dependencies
12
+ RUN npm install
13
+
14
+ # Copy frontend source code
15
+ COPY frontend/ ./
16
+
17
+ # Build the React app
18
+ RUN npm run build
19
+
20
+ # Stage 2: Set up the Flask backend and serve the app
21
+ FROM python:3.10-slim
22
+
23
+ WORKDIR /app
24
+
25
+ # Copy backend requirements and install dependencies
26
+ COPY backend/requirements.txt ./
27
+ RUN pip install --no-cache-dir -r requirements.txt
28
+
29
+ # Copy backend code
30
+ COPY backend/ ./
31
+
32
+ # Copy built frontend from the frontend-builder stage
33
+ COPY --from=frontend-builder /app/frontend/dist ./static
34
+
35
+ # Copy data and examples directories
36
+ COPY data/ ./data/
37
+ COPY examples/ ./examples/
38
+
39
+ # Set environment variables
40
+ ENV FLASK_APP=app.py
41
+ ENV FLASK_ENV=production
42
+
43
+ # Expose the port the app runs on
44
+ EXPOSE 5000
45
+
46
+ # Copy startup and healthcheck scripts and make them executable
47
+ COPY backend/start.sh backend/healthcheck.sh ./
48
+ RUN chmod +x ./start.sh ./healthcheck.sh
49
+
50
+ # Install curl for healthcheck
51
+ RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
52
+
53
+ # Add healthcheck
54
+ HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 CMD ["./healthcheck.sh"]
55
+
56
+ # Command to run the app using the startup script
57
+ CMD ["./start.sh"]
README.md CHANGED
@@ -1,107 +1,167 @@
1
  ---
2
- tags:
3
- - gradio-custom-component
4
- title: Gradio Leaderboard Component
5
- emoji: 💻
6
- colorFrom: pink
7
- colorTo: pink
8
- sdk: gradio
9
- sdk_version: 5.8.0
10
- app_file: space.py
11
  pinned: false
12
  license: mit
13
  ---
14
 
 
 
15
 
16
- # gradio_leaderboard
17
- <img alt="Static Badge" src="https://img.shields.io/badge/version%20-%200.0.1%20-%20orange">
18
 
19
- 🔋⚡️🥇 Super fast, batteries included Leaderboards with minimal code.
20
 
21
- The `gradio_leaderboard` package helps you build fully functional and performant leaderboard demos with `gradio`.
22
 
23
- Place the `gradio_leaderboard.Leaderboard` component anywhere in your Gradio application (and optionally pass in some configuration). That's it!
 
 
24
 
25
- For example usage, please see the [Usage](#usage) section.
26
 
27
- For details on configuration, please see the [Configuration](#configuration) section.
 
 
 
28
 
29
- For the API reference, see the [Initialization](#initialization) section.
30
 
31
- ## Installation
32
-
33
- ```bash
34
- pip install gradio_leaderboard
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  ```
36
 
37
- or add `gradio_leaderboard` to your `requirements.txt`.
38
 
39
- ## Usage
 
 
40
 
41
- ```python
42
 
43
- import gradio as gr
44
- from gradio_leaderboard import Leaderboard
45
- from pathlib import Path
46
- import pandas as pd
47
 
48
- abs_path = Path(__file__).parent
 
 
 
 
49
 
50
- # Any pandas-compatible data
51
- df = pd.read_json(str(abs_path / "leaderboard_data.json"))
 
 
52
 
53
- with gr.Blocks() as demo:
54
- gr.Markdown("""
55
- # 🥇 Leaderboard Component
56
- """)
57
- Leaderboard(
58
- value=df,
59
- select_columns=["T", "Model", "Average ⬆️", "ARC",
60
- "HellaSwag", "MMLU", "TruthfulQA",
61
- "Winogrande", "GSM8K"],
62
- search_columns=["model_name_for_query", "Type"],
63
- hide_columns=["model_name_for_query", "Model Size"],
64
- filter_columns=["T", "Precision", "Model Size"],
65
- )
66
 
67
- if __name__ == "__main__":
68
- demo.launch()
69
- ```
70
 
71
- ## Configuration
 
 
 
72
 
73
- ### Selecting
 
 
 
74
 
75
- When column selection is enabled, a checkboxgroup will be displayed in the top left corner of the leaderboard that lets users
76
- select which columns are displayed.
77
 
78
- You can disable/configure the column selection behavior of the `Leaderboard` with the `select_columns` parameter.
79
- It's value can be:
 
 
80
 
81
- * `None`: Column selection is not allowed and all of the columns are displayed when the leaderboard loads.
82
- * `list of column names`: All columns can be selected and the elements of this list correspond to the initial set of selected columns.
83
- * `SelectColumns instance`: You can import `SelectColumns` from `gradio_leaderboard` for full control of the column selection behavior as well as the checkboxgroup appearance. See an example below.
84
 
85
- #### Demo
86
 
87
- ```python
88
- import pandas as pd
89
- import gradio as gr
90
- from gradio_leaderboard import Leaderboard, SelectColumns
91
 
92
- with gr.Blocks() as demo:
93
- Leaderboard(
94
- value=pd.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6], "c": [7, 8, 9]}),
95
- select_columns=SelectColumns(default_selection=["a", "b"],
96
- cant_deselect="a",
97
- label="Select The Columns",
98
- info="Helpful information")
99
- )
100
 
101
- demo.launch()
102
- ```
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103
 
104
- ![](https://github.com/freddyaboulton/gradio-leaderboard/assets/41651716/ea073681-c01e-4d40-814c-1f3cd56ef292)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
 
106
 
107
  ### Searching
@@ -155,7 +215,7 @@ This parameter must be a `list` but it's elements must be:
155
  If the `type` of the `ColumnFilter` is not specified, a heuristic will be used to choose the most appropriate type. If the data in the column is numeric, a slider will be used. If not, a `checkboxgroup` will be used.
156
 
157
 
158
- #### Demo
159
 
160
  ```python
161
  import pandas as pd
 
1
  ---
2
+ title: OmniSealBench
3
+ emoji: 🔐
4
+ colorFrom: blue
5
+ colorTo: indigo
6
+ sdk: docker
7
+ app_port: 5000
 
 
 
8
  pinned: false
9
  license: mit
10
  ---
11
 
12
+ # OmniSealBench
13
+ <img alt="Static Badge" src="https://img.shields.io/badge/version%20-%201.0.0%20-%20blue">
14
 
15
+ 🔐 A comprehensive benchmark for watermarking techniques with a modern web interface.
 
16
 
17
+ ## Overview
18
 
19
+ OmniSealBench is a benchmarking tool for evaluating various watermarking techniques across different media types including images and audio. This application provides:
20
 
21
+ - Interactive leaderboards for comparing watermarking methods
22
+ - Visual examples of various watermark attacks and their effects
23
+ - Easy-to-use interface for exploring benchmark results
24
 
25
+ ## Features
26
 
27
+ - **Responsive UI**: Clean, modern interface that works across devices
28
+ - **Sortable Leaderboard**: View and filter benchmark results with advanced sorting
29
+ - **Example Browser**: Visualize examples of watermarking techniques and attacks
30
+ - **Multi-Modal Support**: Support for both image and audio watermarks
31
 
32
+ ## Project Structure
33
 
34
+ ```
35
+ omnisealbench/
36
+ ├── backend/ # Flask backend
37
+ │ ├── app.py # Main Flask application
38
+ │ ├── api.py # API endpoints
39
+ │ ├── data_processor.py # Data handling utilities
40
+ │ ├── requirements.txt # Python dependencies
41
+ │ ├── Dockerfile.dev # Development Dockerfile
42
+ │ └── start.sh # Production startup script
43
+ ├── frontend/ # React frontend
44
+ │ ├── src/ # Source code
45
+ │ │ ├── components/ # React components
46
+ │ │ ├── api.js # API client
47
+ │ │ ├── App.jsx # Main application component
48
+ │ │ ├── index.jsx # Entry point
49
+ │ │ └── styles.css # Application styles
50
+ │ ├── public/ # Static assets
51
+ │ ├── package.json # Node.js dependencies
52
+ │ ├── vite.config.js # Vite configuration
53
+ │ └── Dockerfile.dev # Development Dockerfile
54
+ ├── data/ # Benchmark data files
55
+ │ ├── image_benchmark.csv
56
+ │ ├── audio_benchmark.csv
57
+ │ └── ...
58
+ ├── examples/ # Example files for visualization
59
+ │ ├── image/
60
+ │ └── audio/
61
+ ├── Dockerfile # Multi-stage build for production
62
+ ├── README.md # Project documentation
63
+ ├── setup_dev.sh # Development setup script
64
+ ├── start_dev.sh # Start development environment
65
+ ├── stop_dev.sh # Stop development environment
66
+ ├── test_build.sh # Test production build
67
+ ├── clean_docker.sh # Clean up Docker resources
68
+ └── prepare_deploy.sh # Prepare for deployment
69
  ```
70
 
71
+ ## Prerequisites
72
 
73
+ - Docker (for containerized deployment and development)
74
+ - Node.js 18+ and npm (for local frontend development)
75
+ - Python 3.10+ (for local backend development)
76
 
77
+ ## Getting Started
78
 
79
+ ### Using Docker (Recommended)
 
 
 
80
 
81
+ 1. Clone the repository:
82
+ ```bash
83
+ git clone https://github.com/yourusername/omnisealbench.git
84
+ cd omnisealbench
85
+ ```
86
 
87
+ 2. Build and run the application with Docker:
88
+ ```bash
89
+ ./test_build.sh
90
+ ```
91
 
92
+ 3. Access the application at http://localhost:5000
 
 
 
 
 
 
 
 
 
 
 
 
93
 
94
+ ### Development Environment
 
 
95
 
96
+ 1. Set up the development environment:
97
+ ```bash
98
+ ./setup_dev.sh
99
+ ```
100
 
101
+ 2. Start the development environment:
102
+ ```bash
103
+ ./start_dev.sh
104
+ ```
105
 
106
+ 3. Access the frontend at http://localhost:3000 and backend at http://localhost:5000
 
107
 
108
+ 4. When finished, stop the development environment:
109
+ ```bash
110
+ ./stop_dev.sh
111
+ ```
112
 
113
+ ## Deployment
 
 
114
 
115
+ This application is designed to be deployed using Docker to various platforms.
116
 
117
+ ### Quick Deployment
 
 
 
118
 
119
+ 1. Prepare for deployment:
120
+ ```bash
121
+ ./prepare_deploy.sh
122
+ ```
 
 
 
 
123
 
124
+ 2. Test the build locally:
125
+ ```bash
126
+ ./test_build.sh
127
+ ```
128
+
129
+ 3. Follow platform-specific deployment instructions in [DEPLOYMENT.md](DEPLOYMENT.md)
130
+
131
+ For detailed deployment instructions for HuggingFace Spaces, AWS, Google Cloud, and Azure, please refer to the [Deployment Guide](DEPLOYMENT.md).
132
+
133
+ ## API Documentation
134
+
135
+ The backend provides the following API endpoints:
136
+
137
+ - `GET /api/benchmarks` - List available benchmarks
138
+ - `GET /api/leaderboard` - Get leaderboard data
139
+ - `GET /api/columns` - Get available columns for a benchmark
140
+ - `GET /api/examples` - Get examples for a specific model
141
+ - `GET /api/attacks` - Get available attacks for a benchmark
142
+
143
+ ## Converting from Gradio
144
 
145
+ This project was converted from a Gradio-based HuggingFace space to a Flask/React application. The key changes included:
146
+
147
+ - Replacing Gradio UI components with React components
148
+ - Creating a Flask API to serve data previously handled by Gradio backend
149
+ - Implementing a containerized deployment with Docker
150
+
151
+ ## Contributing
152
+
153
+ Contributions are welcome! Please feel free to submit a Pull Request.
154
+
155
+ ## License
156
+
157
+ This project is licensed under the MIT License - see the LICENSE file for details.
158
+
159
+ ## Acknowledgements
160
+
161
+ - [Flask](https://flask.palletsprojects.com/) - Backend framework
162
+ - [React](https://reactjs.org/) - Frontend library
163
+ - [Vite](https://vitejs.dev/) - Frontend build tool
164
+ - [Pandas](https://pandas.pydata.org/) - Data processing
165
 
166
 
167
  ### Searching
 
215
  If the `type` of the `ColumnFilter` is not specified, a heuristic will be used to choose the most appropriate type. If the data in the column is numeric, a slider will be used. If not, a `checkboxgroup` will be used.
216
 
217
 
218
+ #### Demo
219
 
220
  ```python
221
  import pandas as pd
backend/Dockerfile.dev ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Development Dockerfile for the backend
2
+ FROM python:3.10-slim@sha256:b85dcf7840589a82e90a07d4cb9e85305eac7a583a28ae629a3718ba05b7bdca
3
+
4
+ WORKDIR /app
5
+
6
+ # Install dependencies
7
+ COPY requirements.txt ./
8
+ RUN pip install --no-cache-dir -r requirements.txt
9
+
10
+ # Copy the rest of the application
11
+ COPY . .
12
+
13
+ # Create symbolic links for data and examples directories
14
+ # These will be overridden by volume mounts when running the container
15
+ RUN mkdir -p /app/data /app/examples
16
+
17
+ # Expose the Flask port
18
+ EXPOSE 5000
19
+
20
+ # Environment variables
21
+ ENV FLASK_APP=app.py
22
+ ENV FLASK_ENV=development
23
+ ENV PYTHONUNBUFFERED=1
24
+
25
+ # Command to run the Flask development server
26
+ CMD ["flask", "run", "--host=0.0.0.0"]
backend/api.py ADDED
@@ -0,0 +1,155 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Blueprint, jsonify, request
2
+ import data_processor
3
+ import os
4
+
5
+ # Create a Blueprint for API routes
6
+ bp = Blueprint("api", __name__, url_prefix="/api")
7
+
8
+
9
+ @bp.route("/leaderboard", methods=["GET"])
10
+ def get_leaderboard():
11
+ """
12
+ API endpoint to get leaderboard data with optional filtering and searching
13
+
14
+ Query parameters:
15
+ - benchmark: Which benchmark to use (image, audio)
16
+ - filter_columns: Columns to filter by
17
+ - search_query: Text to search for
18
+ - core_columns: Core columns that should always be included
19
+ """
20
+ # Get parameters from query string
21
+ benchmark = request.args.get("benchmark", "image")
22
+ filter_columns = (
23
+ request.args.get("filter_columns", "").split(",")
24
+ if request.args.get("filter_columns")
25
+ else []
26
+ )
27
+ search_query = request.args.get("search_query", "")
28
+ core_columns = (
29
+ request.args.get("core_columns", "").split(",")
30
+ if request.args.get("core_columns")
31
+ else []
32
+ )
33
+
34
+ # Determine file path based on benchmark
35
+ file_path = f"./data/{benchmark}_benchmark.csv"
36
+
37
+ # Load data
38
+ try:
39
+ data = data_processor.load_data(file_path)
40
+
41
+ # Apply filters if provided
42
+ if filter_columns:
43
+ data = data_processor.filter_data(data, filter_columns)
44
+
45
+ # Apply search if provided
46
+ if search_query:
47
+ data = data_processor.search_data(data, search_query)
48
+
49
+ return jsonify({"success": True, "data": data})
50
+ except Exception as e:
51
+ return jsonify({"success": False, "error": str(e)}), 500
52
+
53
+
54
+ @bp.route("/columns", methods=["GET"])
55
+ def get_columns():
56
+ """
57
+ API endpoint to get all available columns for a benchmark
58
+
59
+ Query parameters:
60
+ - benchmark: Which benchmark to use (image, audio)
61
+ """
62
+ benchmark = request.args.get("benchmark", "image")
63
+ file_path = f"./data/{benchmark}_benchmark.csv"
64
+
65
+ try:
66
+ columns = data_processor.get_columns(file_path)
67
+ return jsonify({"success": True, "columns": columns})
68
+ except Exception as e:
69
+ return jsonify({"success": False, "error": str(e)}), 500
70
+
71
+
72
+ @bp.route("/benchmarks", methods=["GET"])
73
+ def get_benchmarks():
74
+ """API endpoint to get available benchmarks"""
75
+ try:
76
+ benchmarks = data_processor.get_available_benchmarks()
77
+ return jsonify({"success": True, "benchmarks": benchmarks})
78
+ except Exception as e:
79
+ return jsonify({"success": False, "error": str(e)}), 500
80
+
81
+
82
+ @bp.route("/examples", methods=["GET"])
83
+ def get_examples():
84
+ """
85
+ API endpoint to get examples for a specific benchmark and model
86
+
87
+ Query parameters:
88
+ - benchmark: Which benchmark to use (image, audio)
89
+ - model: Which model to get examples for
90
+ - attack: Which attack to get examples for (optional)
91
+ """
92
+ benchmark = request.args.get("benchmark", "image")
93
+ model = request.args.get("model", "")
94
+ attack = request.args.get("attack", "")
95
+
96
+ if not model:
97
+ return jsonify({"success": False, "error": "Model parameter is required"}), 400
98
+
99
+ # Construct path to examples
100
+ base_path = f"./examples/{benchmark}/{model}"
101
+
102
+ if attack:
103
+ base_path = os.path.join(base_path, attack)
104
+
105
+ try:
106
+ # Check if directory exists
107
+ if not os.path.exists(base_path):
108
+ return (
109
+ jsonify({"success": False, "error": f"No examples found for {model}"}),
110
+ 404,
111
+ )
112
+
113
+ # Get list of examples
114
+ examples = []
115
+ for root, _, files in os.walk(base_path):
116
+ for file in files:
117
+ if file.endswith((".png", ".jpg", ".jpeg", ".wav", ".mp3")):
118
+ rel_path = os.path.relpath(os.path.join(root, file), "./examples")
119
+ examples.append(
120
+ {
121
+ "path": f"/examples/{rel_path}",
122
+ "name": file,
123
+ "attack": attack or os.path.basename(root),
124
+ }
125
+ )
126
+
127
+ return jsonify({"success": True, "examples": examples})
128
+ except Exception as e:
129
+ return jsonify({"success": False, "error": str(e)}), 500
130
+
131
+
132
+ @bp.route("/attacks", methods=["GET"])
133
+ def get_attacks():
134
+ """
135
+ API endpoint to get available attacks for a benchmark
136
+
137
+ Query parameters:
138
+ - benchmark: Which benchmark to use (image, audio)
139
+ """
140
+ benchmark = request.args.get("benchmark", "image")
141
+ file_path = f"./data/{benchmark}_attacks_variations.csv"
142
+
143
+ try:
144
+ if os.path.exists(file_path):
145
+ data = data_processor.load_data(file_path)
146
+ return jsonify({"success": True, "attacks": data})
147
+ else:
148
+ return (
149
+ jsonify(
150
+ {"success": False, "error": f"No attack data found for {benchmark}"}
151
+ ),
152
+ 404,
153
+ )
154
+ except Exception as e:
155
+ return jsonify({"success": False, "error": str(e)}), 500
backend/app.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, send_from_directory, jsonify, request
2
+ from flask_cors import CORS
3
+ import api
4
+
5
+ # Initialize the Flask app
6
+ app = Flask(__name__, static_folder="static")
7
+ CORS(app) # Enable CORS for all routes
8
+
9
+ # Register API routes
10
+ app.register_blueprint(api.bp)
11
+
12
+
13
+ # Serve the React app
14
+ @app.route("/", defaults={"path": ""})
15
+ @app.route("/<path:path>")
16
+ def serve(path):
17
+ if path and path.split("/")[-1].find(".") != -1:
18
+ return send_from_directory("static", path)
19
+ return send_from_directory("static", "index.html")
20
+
21
+
22
+ if __name__ == "__main__":
23
+ app.run(host="0.0.0.0", port=5000, debug=True)
backend/data_processor.py ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import os
3
+ import json
4
+ from typing import List, Dict, Any, Optional
5
+
6
+
7
+ def load_data(csv_file_path: str) -> List[Dict[str, Any]]:
8
+ """
9
+ Load data from a CSV file and convert to a list of dictionaries
10
+
11
+ Args:
12
+ csv_file_path: Path to the CSV file
13
+
14
+ Returns:
15
+ List of dictionaries representing the data
16
+ """
17
+ # Check if file exists
18
+ if not os.path.exists(csv_file_path):
19
+ raise FileNotFoundError(f"File not found: {csv_file_path}")
20
+
21
+ # Read the CSV file
22
+ df = pd.read_csv(csv_file_path)
23
+
24
+ # Convert to list of dictionaries for JSON serialization
25
+ return df.to_dict(orient="records")
26
+
27
+
28
+ def get_columns(csv_file_path: str) -> List[str]:
29
+ """
30
+ Get all column names from the CSV file
31
+
32
+ Args:
33
+ csv_file_path: Path to the CSV file
34
+
35
+ Returns:
36
+ List of column names
37
+ """
38
+ # Check if file exists
39
+ if not os.path.exists(csv_file_path):
40
+ raise FileNotFoundError(f"File not found: {csv_file_path}")
41
+
42
+ # Read the CSV file
43
+ df = pd.read_csv(csv_file_path)
44
+
45
+ # Return column names
46
+ return df.columns.tolist()
47
+
48
+
49
+ def filter_data(
50
+ data: List[Dict[str, Any]], filter_columns: List[str]
51
+ ) -> List[Dict[str, Any]]:
52
+ """
53
+ Filter the data to include only specific columns
54
+
55
+ Args:
56
+ data: List of dictionaries representing the data
57
+ filter_columns: List of column names to include
58
+
59
+ Returns:
60
+ Filtered data
61
+ """
62
+ if not filter_columns:
63
+ return data
64
+
65
+ filtered_data = []
66
+ for item in data:
67
+ filtered_item = {k: v for k, v in item.items() if k in filter_columns}
68
+ filtered_data.append(filtered_item)
69
+
70
+ return filtered_data
71
+
72
+
73
+ def search_data(data: List[Dict[str, Any]], search_query: str) -> List[Dict[str, Any]]:
74
+ """
75
+ Search the data for items matching the search query
76
+
77
+ Args:
78
+ data: List of dictionaries representing the data
79
+ search_query: Search query string
80
+
81
+ Returns:
82
+ Filtered data containing only matching items
83
+ """
84
+ if not search_query:
85
+ return data
86
+
87
+ # Convert to lowercase for case-insensitive search
88
+ search_query = search_query.lower()
89
+
90
+ filtered_data = []
91
+ for item in data:
92
+ # Check if any value contains the search query
93
+ for value in item.values():
94
+ if (
95
+ isinstance(value, (str, int, float))
96
+ and str(value).lower().find(search_query) != -1
97
+ ):
98
+ filtered_data.append(item)
99
+ break
100
+
101
+ return filtered_data
102
+
103
+
104
+ def load_json_data(json_file_path: str) -> Dict[str, Any]:
105
+ """
106
+ Load data from a JSON file
107
+
108
+ Args:
109
+ json_file_path: Path to the JSON file
110
+
111
+ Returns:
112
+ Dictionary representing the JSON data
113
+ """
114
+ # Check if file exists
115
+ if not os.path.exists(json_file_path):
116
+ raise FileNotFoundError(f"File not found: {json_file_path}")
117
+
118
+ # Read the JSON file
119
+ with open(json_file_path, "r") as f:
120
+ data = json.load(f)
121
+
122
+ return data
123
+
124
+
125
+ def get_available_benchmarks() -> List[Dict[str, str]]:
126
+ """
127
+ Get a list of available benchmarks in the data folder
128
+
129
+ Returns:
130
+ List of dictionaries with benchmark information
131
+ """
132
+ benchmarks = []
133
+
134
+ # Look for image benchmarks
135
+ if os.path.exists("./data/image_benchmark.csv"):
136
+ benchmarks.append(
137
+ {
138
+ "id": "image",
139
+ "name": "Image Watermarks",
140
+ "description": "Evaluation of image watermarking techniques",
141
+ "file": "image_benchmark.csv",
142
+ }
143
+ )
144
+
145
+ # Look for audio benchmarks
146
+ if os.path.exists("./data/audio_benchmark.csv"):
147
+ benchmarks.append(
148
+ {
149
+ "id": "audio",
150
+ "name": "Audio Watermarks",
151
+ "description": "Evaluation of audio watermarking techniques",
152
+ "file": "audio_benchmark.csv",
153
+ }
154
+ )
155
+
156
+ return benchmarks
backend/healthcheck.sh ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Healthcheck script for the OmniSealBench container
3
+ # Returns 0 if the app is running, 1 if there's an issue
4
+
5
+ # Try to access the application's root endpoint
6
+ response=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:5000/)
7
+
8
+ if [ "$response" = "200" ]; then
9
+ # Application is responding correctly
10
+ exit 0
11
+ else
12
+ # Application is not responding correctly
13
+ exit 1
14
+ fi
backend/requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ flask==2.3.3
2
+ flask-cors==4.0.0
3
+ pandas==2.1.0
4
+ numpy==1.25.2
5
+ gunicorn==21.2.0
backend/start.sh ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # This script runs when the HuggingFace space starts
3
+
4
+ # Set environment variables
5
+ export FLASK_APP=app.py
6
+ export FLASK_ENV=production
7
+
8
+ # Start the Flask application with gunicorn
9
+ # - workers: Number of worker processes (2-4 x CPU cores is recommended)
10
+ # - timeout: Worker timeout in seconds
11
+ # - access-logfile: Log access to stdout
12
+ # - error-logfile: Log errors to stderr
13
+ exec gunicorn --workers=2 --timeout=120 --bind 0.0.0.0:5000 --access-logfile=- --error-logfile=- app:app
clean_docker.sh ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Script to clean up Docker resources
3
+
4
+ echo "Stopping and removing containers..."
5
+ docker stop omnisealbench-test omnisealbench-frontend-dev omnisealbench-backend-dev 2>/dev/null || true
6
+ docker rm omnisealbench-test omnisealbench-frontend-dev omnisealbench-backend-dev 2>/dev/null || true
7
+
8
+ echo "Removing Docker images..."
9
+ docker rmi omnisealbench-local omnisealbench-frontend-dev omnisealbench-backend-dev 2>/dev/null || true
10
+
11
+ echo "Cleanup complete!"
frontend/Dockerfile.dev ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Development Dockerfile for the frontend
2
+ FROM node:18-alpine@sha256:a020d42b54c53929a8177d8e7aab30de0f4044fa6e25a3e15ecdad32c9075e18
3
+
4
+ WORKDIR /app
5
+
6
+ # Copy package.json and install dependencies
7
+ COPY package*.json ./
8
+ RUN npm install
9
+
10
+ # Copy the rest of the application
11
+ COPY . .
12
+
13
+ # Set the API URL for development
14
+ ENV VITE_API_URL=http://localhost:5000
15
+
16
+ # Expose the development server port
17
+ EXPOSE 3000
18
+
19
+ # Command to run the development server with host set to 0.0.0.0 to allow external connections
20
+ CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"]
frontend/index.html ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
6
+ <link rel="manifest" href="/manifest.json" />
7
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
8
+ <meta name="theme-color" content="#2c3e50" />
9
+ <meta name="description" content="A comprehensive benchmark for watermarking techniques" />
10
+ <title>OmniSealBench Leaderboard</title>
11
+ </head>
12
+ <body>
13
+ <div id="root"></div>
14
+ <script type="module" src="/src/index.jsx"></script>
15
+ </body>
16
+ </html>
frontend/package.json ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "omnisealbench-frontend",
3
+ "version": "0.1.0",
4
+ "description": "Frontend for OmniSealBench",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "keywords": [],
12
+ "author": "",
13
+ "license": "ISC",
14
+ "dependencies": {
15
+ "axios": "^1.4.0",
16
+ "react": "^18.2.0",
17
+ "react-dom": "^18.2.0",
18
+ "react-table": "^7.8.0",
19
+ "react-router-dom": "^6.16.0"
20
+ },
21
+ "devDependencies": {
22
+ "@types/react": "^18.2.15",
23
+ "@types/react-dom": "^18.2.7",
24
+ "@vitejs/plugin-react": "^4.0.3",
25
+ "vite": "^4.4.5"
26
+ }
27
+ }
frontend/public/favicon.svg ADDED
frontend/public/manifest.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "short_name": "OmniSealBench",
3
+ "name": "OmniSealBench - Watermarking Benchmark",
4
+ "icons": [
5
+ {
6
+ "src": "favicon.ico",
7
+ "sizes": "64x64 32x32 24x24 16x16",
8
+ "type": "image/x-icon"
9
+ }
10
+ ],
11
+ "start_url": ".",
12
+ "display": "standalone",
13
+ "theme_color": "#2c3e50",
14
+ "background_color": "#f8f9fa"
15
+ }
frontend/src/App.jsx ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState, useEffect } from 'react';
2
+ import { Routes, Route, Link } from 'react-router-dom';
3
+ import Leaderboard from './components/Leaderboard';
4
+ import ExampleViewer from './components/ExampleViewer';
5
+ import api from './api';
6
+
7
+ function App() {
8
+ const [benchmarks, setBenchmarks] = useState([]);
9
+ const [selectedBenchmark, setSelectedBenchmark] = useState('image');
10
+ const [loading, setLoading] = useState(true);
11
+ const [error, setError] = useState(null);
12
+
13
+ useEffect(() => {
14
+ // Fetch available benchmarks
15
+ async function fetchBenchmarks() {
16
+ try {
17
+ setLoading(true);
18
+ const response = await api.getBenchmarks();
19
+ if (response.success) {
20
+ setBenchmarks(response.benchmarks);
21
+ // Set default benchmark if available
22
+ if (response.benchmarks.length > 0) {
23
+ setSelectedBenchmark(response.benchmarks[0].id);
24
+ }
25
+ } else {
26
+ setError(response.error || 'Failed to fetch benchmarks');
27
+ }
28
+ } catch (err) {
29
+ setError(err.message || 'An error occurred');
30
+ } finally {
31
+ setLoading(false);
32
+ }
33
+ }
34
+
35
+ fetchBenchmarks();
36
+ }, []);
37
+
38
+ const handleBenchmarkChange = (benchmarkId) => {
39
+ setSelectedBenchmark(benchmarkId);
40
+ };
41
+
42
+ return (
43
+ <div className="app">
44
+ <header className="header">
45
+ <div className="container">
46
+ <h1>OmniSealBench</h1>
47
+ <p>A Comprehensive Benchmark for Watermarking Techniques</p>
48
+ </div>
49
+ </header>
50
+
51
+ <nav className="nav">
52
+ <div className="container">
53
+ <ul className="nav-links">
54
+ <li>
55
+ <Link to="/" className="nav-link">Leaderboard</Link>
56
+ </li>
57
+ <li>
58
+ <Link to="/examples" className="nav-link">Examples</Link>
59
+ </li>
60
+ </ul>
61
+
62
+ {benchmarks.length > 0 && (
63
+ <div className="benchmark-selector">
64
+ <label>Benchmark:</label>
65
+ <select
66
+ value={selectedBenchmark}
67
+ onChange={(e) => handleBenchmarkChange(e.target.value)}
68
+ >
69
+ {benchmarks.map(benchmark => (
70
+ <option key={benchmark.id} value={benchmark.id}>
71
+ {benchmark.name}
72
+ </option>
73
+ ))}
74
+ </select>
75
+ </div>
76
+ )}
77
+ </div>
78
+ </nav>
79
+
80
+ <main className="main">
81
+ <div className="container">
82
+ {loading ? (
83
+ <div className="loading">Loading...</div>
84
+ ) : error ? (
85
+ <div className="error">{error}</div>
86
+ ) : (
87
+ <Routes>
88
+ <Route
89
+ path="/"
90
+ element={<Leaderboard benchmark={selectedBenchmark} />}
91
+ />
92
+ <Route
93
+ path="/examples"
94
+ element={<ExampleViewer benchmark={selectedBenchmark} />}
95
+ />
96
+ <Route
97
+ path="*"
98
+ element={<div>Page not found</div>}
99
+ />
100
+ </Routes>
101
+ )}
102
+ </div>
103
+ </main>
104
+
105
+ <footer className="footer">
106
+ <div className="container">
107
+ <p>&copy; {new Date().getFullYear()} OmniSealBench. All rights reserved.</p>
108
+ </div>
109
+ </footer>
110
+ </div>
111
+ );
112
+ }
113
+
114
+ export default App;
frontend/src/api.js ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import axios from 'axios';
2
+
3
+ const API_URL = '/api';
4
+
5
+ const api = {
6
+ // Get available benchmarks
7
+ getBenchmarks: async () => {
8
+ try {
9
+ const response = await axios.get(`${API_URL}/benchmarks`);
10
+ return response.data;
11
+ } catch (error) {
12
+ console.error('Error fetching benchmarks:', error);
13
+ return { success: false, error: error.message };
14
+ }
15
+ },
16
+
17
+ // Get leaderboard data
18
+ getLeaderboard: async (benchmark, filters = {}) => {
19
+ try {
20
+ const { filterColumns, searchQuery, coreColumns } = filters;
21
+
22
+ const params = new URLSearchParams();
23
+ params.append('benchmark', benchmark);
24
+
25
+ if (filterColumns && filterColumns.length) {
26
+ params.append('filter_columns', filterColumns.join(','));
27
+ }
28
+
29
+ if (searchQuery) {
30
+ params.append('search_query', searchQuery);
31
+ }
32
+
33
+ if (coreColumns && coreColumns.length) {
34
+ params.append('core_columns', coreColumns.join(','));
35
+ }
36
+
37
+ const response = await axios.get(`${API_URL}/leaderboard?${params.toString()}`);
38
+ return response.data;
39
+ } catch (error) {
40
+ console.error('Error fetching leaderboard:', error);
41
+ return { success: false, error: error.message };
42
+ }
43
+ },
44
+
45
+ // Get available columns for a benchmark
46
+ getColumns: async (benchmark) => {
47
+ try {
48
+ const response = await axios.get(`${API_URL}/columns?benchmark=${benchmark}`);
49
+ return response.data;
50
+ } catch (error) {
51
+ console.error('Error fetching columns:', error);
52
+ return { success: false, error: error.message };
53
+ }
54
+ },
55
+
56
+ // Get examples for a specific model and benchmark
57
+ getExamples: async (benchmark, model, attack = '') => {
58
+ try {
59
+ const params = new URLSearchParams();
60
+ params.append('benchmark', benchmark);
61
+ params.append('model', model);
62
+
63
+ if (attack) {
64
+ params.append('attack', attack);
65
+ }
66
+
67
+ const response = await axios.get(`${API_URL}/examples?${params.toString()}`);
68
+ return response.data;
69
+ } catch (error) {
70
+ console.error('Error fetching examples:', error);
71
+ return { success: false, error: error.message };
72
+ }
73
+ },
74
+
75
+ // Get available attacks for a benchmark
76
+ getAttacks: async (benchmark) => {
77
+ try {
78
+ const response = await axios.get(`${API_URL}/attacks?benchmark=${benchmark}`);
79
+ return response.data;
80
+ } catch (error) {
81
+ console.error('Error fetching attacks:', error);
82
+ return { success: false, error: error.message };
83
+ }
84
+ }
85
+ };
86
+
87
+ export default api;
frontend/src/components/ExampleViewer.jsx ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState, useEffect } from 'react';
2
+ import api from '../api';
3
+
4
+ function ExampleViewer({ benchmark }) {
5
+ const [models, setModels] = useState([]);
6
+ const [selectedModel, setSelectedModel] = useState('');
7
+ const [attacks, setAttacks] = useState([]);
8
+ const [selectedAttack, setSelectedAttack] = useState('');
9
+ const [examples, setExamples] = useState([]);
10
+ const [loading, setLoading] = useState(true);
11
+ const [error, setError] = useState(null);
12
+
13
+ // Fetch models when benchmark changes
14
+ useEffect(() => {
15
+ async function fetchModels() {
16
+ try {
17
+ setLoading(true);
18
+ const response = await api.getLeaderboard(benchmark);
19
+
20
+ if (response.success) {
21
+ // Extract unique model names
22
+ const modelNames = [...new Set(response.data.map(item => item.model))];
23
+ setModels(modelNames);
24
+
25
+ // Set first model as default if available
26
+ if (modelNames.length > 0) {
27
+ setSelectedModel(modelNames[0]);
28
+ }
29
+ } else {
30
+ setError(response.error || 'Failed to fetch models');
31
+ }
32
+ } catch (err) {
33
+ setError(err.message || 'An error occurred');
34
+ } finally {
35
+ setLoading(false);
36
+ }
37
+ }
38
+
39
+ fetchModels();
40
+ }, [benchmark]);
41
+
42
+ // Fetch attacks when benchmark changes
43
+ useEffect(() => {
44
+ async function fetchAttacks() {
45
+ try {
46
+ const response = await api.getAttacks(benchmark);
47
+ if (response.success) {
48
+ setAttacks(response.attacks);
49
+ }
50
+ } catch (err) {
51
+ console.error('Error fetching attacks:', err);
52
+ // Not setting error state here as it's not critical
53
+ }
54
+ }
55
+
56
+ fetchAttacks();
57
+ }, [benchmark]);
58
+
59
+ // Fetch examples when model or attack changes
60
+ useEffect(() => {
61
+ if (!selectedModel) return;
62
+
63
+ async function fetchExamples() {
64
+ try {
65
+ setLoading(true);
66
+ const response = await api.getExamples(benchmark, selectedModel, selectedAttack);
67
+
68
+ if (response.success) {
69
+ setExamples(response.examples);
70
+ setError(null);
71
+ } else {
72
+ setError(response.error || 'Failed to fetch examples');
73
+ setExamples([]);
74
+ }
75
+ } catch (err) {
76
+ setError(err.message || 'An error occurred');
77
+ setExamples([]);
78
+ } finally {
79
+ setLoading(false);
80
+ }
81
+ }
82
+
83
+ fetchExamples();
84
+ }, [benchmark, selectedModel, selectedAttack]);
85
+
86
+ const handleModelChange = (e) => {
87
+ setSelectedModel(e.target.value);
88
+ };
89
+
90
+ const handleAttackChange = (e) => {
91
+ setSelectedAttack(e.target.value);
92
+ };
93
+
94
+ const renderExampleContent = (example) => {
95
+ // Check if it's an image or audio based on file extension
96
+ const isImage = /\.(jpg|jpeg|png|gif|webp)$/i.test(example.path);
97
+ const isAudio = /\.(mp3|wav|ogg)$/i.test(example.path);
98
+
99
+ if (isImage) {
100
+ return <img src={example.path} alt={example.name} className="example-image" />;
101
+ } else if (isAudio) {
102
+ return <audio controls src={example.path} className="example-audio">Your browser does not support audio.</audio>;
103
+ } else {
104
+ return <div className="example-unsupported">Unsupported file type</div>;
105
+ }
106
+ };
107
+
108
+ return (
109
+ <div className="example-viewer">
110
+ <h2>Example Viewer</h2>
111
+
112
+ <div className="filters">
113
+ <div className="filter-group">
114
+ <label htmlFor="model-select">Model:</label>
115
+ <select
116
+ id="model-select"
117
+ value={selectedModel}
118
+ onChange={handleModelChange}
119
+ disabled={loading || models.length === 0}
120
+ >
121
+ {models.map(model => (
122
+ <option key={model} value={model}>{model}</option>
123
+ ))}
124
+ </select>
125
+ </div>
126
+
127
+ <div className="filter-group">
128
+ <label htmlFor="attack-select">Attack:</label>
129
+ <select
130
+ id="attack-select"
131
+ value={selectedAttack}
132
+ onChange={handleAttackChange}
133
+ disabled={loading}
134
+ >
135
+ <option value="">All Attacks</option>
136
+ {attacks.map(attack => (
137
+ <option key={attack.name} value={attack.name}>{attack.name}</option>
138
+ ))}
139
+ </select>
140
+ </div>
141
+ </div>
142
+
143
+ {loading ? (
144
+ <div className="loading">Loading examples...</div>
145
+ ) : error ? (
146
+ <div className="error">Error: {error}</div>
147
+ ) : examples.length === 0 ? (
148
+ <div className="no-examples">No examples found for the selected options.</div>
149
+ ) : (
150
+ <div className="examples-grid">
151
+ {examples.map((example, index) => (
152
+ <div key={index} className="example-card">
153
+ <h3>{example.name}</h3>
154
+ {example.attack && <p className="attack-label">Attack: {example.attack}</p>}
155
+ <div className="example-content">
156
+ {renderExampleContent(example)}
157
+ </div>
158
+ </div>
159
+ ))}
160
+ </div>
161
+ )}
162
+ </div>
163
+ );
164
+ }
165
+
166
+ export default ExampleViewer;
frontend/src/components/Leaderboard.jsx ADDED
@@ -0,0 +1,213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState, useEffect, useMemo } from 'react';
2
+ import { useTable, useSortBy, useFilters, usePagination } from 'react-table';
3
+ import api from '../api';
4
+
5
+ // Default filter UI for columns
6
+ function DefaultColumnFilter({
7
+ column: { filterValue, preFilteredRows, setFilter },
8
+ }) {
9
+ const count = preFilteredRows.length;
10
+
11
+ return (
12
+ <input
13
+ value={filterValue || ''}
14
+ onChange={e => {
15
+ setFilter(e.target.value || undefined);
16
+ }}
17
+ placeholder={`Search ${count} records...`}
18
+ className="table-filter"
19
+ />
20
+ );
21
+ }
22
+
23
+ function Leaderboard({ benchmark }) {
24
+ const [data, setData] = useState([]);
25
+ const [columns, setColumns] = useState([]);
26
+ const [loading, setLoading] = useState(true);
27
+ const [error, setError] = useState(null);
28
+ const [searchQuery, setSearchQuery] = useState('');
29
+
30
+ useEffect(() => {
31
+ // Fetch leaderboard data and columns when the benchmark changes
32
+ async function fetchData() {
33
+ try {
34
+ setLoading(true);
35
+
36
+ // Fetch available columns first
37
+ const columnsResponse = await api.getColumns(benchmark);
38
+ if (!columnsResponse.success) {
39
+ throw new Error(columnsResponse.error || 'Failed to fetch columns');
40
+ }
41
+
42
+ // Format columns for react-table
43
+ const tableColumns = columnsResponse.columns.map(column => ({
44
+ Header: column,
45
+ accessor: column,
46
+ Filter: DefaultColumnFilter,
47
+ }));
48
+
49
+ setColumns(tableColumns);
50
+
51
+ // Fetch leaderboard data
52
+ const dataResponse = await api.getLeaderboard(benchmark);
53
+ if (!dataResponse.success) {
54
+ throw new Error(dataResponse.error || 'Failed to fetch leaderboard data');
55
+ }
56
+
57
+ setData(dataResponse.data);
58
+ setError(null);
59
+ } catch (err) {
60
+ setError(err.message || 'An error occurred');
61
+ } finally {
62
+ setLoading(false);
63
+ }
64
+ }
65
+
66
+ fetchData();
67
+ }, [benchmark]);
68
+
69
+ // Filter data based on search query
70
+ const filteredData = useMemo(() => {
71
+ if (!searchQuery) return data;
72
+
73
+ return data.filter(row => {
74
+ return Object.values(row).some(value =>
75
+ String(value).toLowerCase().includes(searchQuery.toLowerCase())
76
+ );
77
+ });
78
+ }, [data, searchQuery]);
79
+
80
+ // Set up react-table
81
+ const {
82
+ getTableProps,
83
+ getTableBodyProps,
84
+ headerGroups,
85
+ page,
86
+ prepareRow,
87
+ canPreviousPage,
88
+ canNextPage,
89
+ pageOptions,
90
+ pageCount,
91
+ gotoPage,
92
+ nextPage,
93
+ previousPage,
94
+ setPageSize,
95
+ state: { pageIndex, pageSize },
96
+ } = useTable(
97
+ {
98
+ columns,
99
+ data: filteredData,
100
+ initialState: { pageIndex: 0, pageSize: 10 },
101
+ },
102
+ useFilters,
103
+ useSortBy,
104
+ usePagination
105
+ );
106
+
107
+ if (loading) {
108
+ return <div className="loading">Loading leaderboard data...</div>;
109
+ }
110
+
111
+ if (error) {
112
+ return <div className="error">Error: {error}</div>;
113
+ }
114
+
115
+ return (
116
+ <div className="leaderboard">
117
+ <h2>{benchmark.charAt(0).toUpperCase() + benchmark.slice(1)} Watermark Benchmark</h2>
118
+
119
+ <div className="search-container">
120
+ <input
121
+ type="text"
122
+ placeholder="Search across all columns..."
123
+ value={searchQuery}
124
+ onChange={e => setSearchQuery(e.target.value)}
125
+ className="search-input"
126
+ />
127
+ </div>
128
+
129
+ <div className="table-container">
130
+ <table {...getTableProps()} className="leaderboard-table">
131
+ <thead>
132
+ {headerGroups.map(headerGroup => (
133
+ <tr {...headerGroup.getHeaderGroupProps()}>
134
+ {headerGroup.headers.map(column => (
135
+ <th {...column.getHeaderProps(column.getSortByToggleProps())}>
136
+ {column.render('Header')}
137
+ <span>
138
+ {column.isSorted
139
+ ? column.isSortedDesc
140
+ ? ' 🔽'
141
+ : ' 🔼'
142
+ : ''}
143
+ </span>
144
+ <div>{column.canFilter ? column.render('Filter') : null}</div>
145
+ </th>
146
+ ))}
147
+ </tr>
148
+ ))}
149
+ </thead>
150
+ <tbody {...getTableBodyProps()}>
151
+ {page.map(row => {
152
+ prepareRow(row);
153
+ return (
154
+ <tr {...row.getRowProps()}>
155
+ {row.cells.map(cell => (
156
+ <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
157
+ ))}
158
+ </tr>
159
+ );
160
+ })}
161
+ </tbody>
162
+ </table>
163
+ </div>
164
+
165
+ <div className="pagination">
166
+ <button onClick={() => gotoPage(0)} disabled={!canPreviousPage}>
167
+ {'<<'}
168
+ </button>{' '}
169
+ <button onClick={() => previousPage()} disabled={!canPreviousPage}>
170
+ {'<'}
171
+ </button>{' '}
172
+ <button onClick={() => nextPage()} disabled={!canNextPage}>
173
+ {'>'}
174
+ </button>{' '}
175
+ <button onClick={() => gotoPage(pageCount - 1)} disabled={!canNextPage}>
176
+ {'>>'}
177
+ </button>{' '}
178
+ <span>
179
+ Page{' '}
180
+ <strong>
181
+ {pageIndex + 1} of {pageOptions.length}
182
+ </strong>{' '}
183
+ </span>
184
+ <span>
185
+ | Go to page:{' '}
186
+ <input
187
+ type="number"
188
+ defaultValue={pageIndex + 1}
189
+ onChange={e => {
190
+ const page = e.target.value ? Number(e.target.value) - 1 : 0;
191
+ gotoPage(page);
192
+ }}
193
+ style={{ width: '100px' }}
194
+ />
195
+ </span>{' '}
196
+ <select
197
+ value={pageSize}
198
+ onChange={e => {
199
+ setPageSize(Number(e.target.value));
200
+ }}
201
+ >
202
+ {[10, 20, 30, 40, 50].map(pageSize => (
203
+ <option key={pageSize} value={pageSize}>
204
+ Show {pageSize}
205
+ </option>
206
+ ))}
207
+ </select>
208
+ </div>
209
+ </div>
210
+ );
211
+ }
212
+
213
+ export default Leaderboard;
frontend/src/index.jsx ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react';
2
+ import ReactDOM from 'react-dom/client';
3
+ import { BrowserRouter } from 'react-router-dom';
4
+ import App from './App';
5
+ import './styles.css';
6
+
7
+ ReactDOM.createRoot(document.getElementById('root')).render(
8
+ <React.StrictMode>
9
+ <BrowserRouter>
10
+ <App />
11
+ </BrowserRouter>
12
+ </React.StrictMode>
13
+ );
frontend/src/styles.css ADDED
@@ -0,0 +1,302 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* OmniSealBench Styles */
2
+
3
+ :root {
4
+ --primary-color: #2c3e50;
5
+ --secondary-color: #3498db;
6
+ --accent-color: #e74c3c;
7
+ --background-color: #f8f9fa;
8
+ --text-color: #333;
9
+ --border-color: #ddd;
10
+ --success-color: #2ecc71;
11
+ --warning-color: #f39c12;
12
+ --error-color: #e74c3c;
13
+ }
14
+
15
+ * {
16
+ box-sizing: border-box;
17
+ margin: 0;
18
+ padding: 0;
19
+ }
20
+
21
+ body {
22
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
23
+ line-height: 1.6;
24
+ color: var(--text-color);
25
+ background-color: var(--background-color);
26
+ }
27
+
28
+ .container {
29
+ width: 100%;
30
+ max-width: 1200px;
31
+ margin: 0 auto;
32
+ padding: 0 15px;
33
+ }
34
+
35
+ /* Header Styles */
36
+ .header {
37
+ background-color: var(--primary-color);
38
+ color: white;
39
+ padding: 1rem 0;
40
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
41
+ }
42
+
43
+ .header h1 {
44
+ margin-bottom: 0.5rem;
45
+ }
46
+
47
+ /* Navigation */
48
+ .nav {
49
+ background-color: white;
50
+ border-bottom: 1px solid var(--border-color);
51
+ padding: 0.5rem 0;
52
+ }
53
+
54
+ .nav-links {
55
+ display: flex;
56
+ list-style: none;
57
+ }
58
+
59
+ .nav-link {
60
+ color: var(--primary-color);
61
+ text-decoration: none;
62
+ padding: 0.5rem 1rem;
63
+ font-weight: 500;
64
+ transition: color 0.3s;
65
+ }
66
+
67
+ .nav-link:hover {
68
+ color: var(--secondary-color);
69
+ }
70
+
71
+ .benchmark-selector {
72
+ margin-top: 1rem;
73
+ }
74
+
75
+ .benchmark-selector select {
76
+ padding: 0.5rem;
77
+ border: 1px solid var(--border-color);
78
+ border-radius: 4px;
79
+ }
80
+
81
+ /* Main Content */
82
+ .main {
83
+ padding: 2rem 0;
84
+ min-height: calc(100vh - 180px);
85
+ }
86
+
87
+ /* Leaderboard */
88
+ .leaderboard h2 {
89
+ margin-bottom: 1.5rem;
90
+ color: var(--primary-color);
91
+ }
92
+
93
+ .search-container {
94
+ margin-bottom: 1rem;
95
+ }
96
+
97
+ .search-input {
98
+ width: 100%;
99
+ padding: 0.75rem;
100
+ border: 1px solid var(--border-color);
101
+ border-radius: 4px;
102
+ }
103
+
104
+ .table-container {
105
+ overflow-x: auto;
106
+ }
107
+
108
+ .leaderboard-table {
109
+ width: 100%;
110
+ border-collapse: collapse;
111
+ margin-bottom: 1rem;
112
+ background-color: white;
113
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
114
+ }
115
+
116
+ .leaderboard-table th,
117
+ .leaderboard-table td {
118
+ padding: 0.75rem;
119
+ text-align: left;
120
+ border-bottom: 1px solid var(--border-color);
121
+ }
122
+
123
+ .leaderboard-table th {
124
+ background-color: var(--primary-color);
125
+ color: white;
126
+ position: sticky;
127
+ top: 0;
128
+ }
129
+
130
+ .leaderboard-table tr:hover {
131
+ background-color: rgba(0, 0, 0, 0.02);
132
+ }
133
+
134
+ .table-filter {
135
+ width: 100%;
136
+ padding: 0.3rem;
137
+ margin-top: 0.5rem;
138
+ border: 1px solid var(--border-color);
139
+ border-radius: 4px;
140
+ font-size: 0.8rem;
141
+ }
142
+
143
+ /* Pagination */
144
+ .pagination {
145
+ display: flex;
146
+ align-items: center;
147
+ margin-top: 1rem;
148
+ flex-wrap: wrap;
149
+ gap: 0.5rem;
150
+ }
151
+
152
+ .pagination button {
153
+ padding: 0.5rem 0.75rem;
154
+ background-color: white;
155
+ border: 1px solid var(--border-color);
156
+ border-radius: 4px;
157
+ cursor: pointer;
158
+ transition: background-color 0.2s;
159
+ }
160
+
161
+ .pagination button:hover:not(:disabled) {
162
+ background-color: var(--secondary-color);
163
+ color: white;
164
+ }
165
+
166
+ .pagination button:disabled {
167
+ opacity: 0.5;
168
+ cursor: not-allowed;
169
+ }
170
+
171
+ .pagination input,
172
+ .pagination select {
173
+ padding: 0.5rem;
174
+ border: 1px solid var(--border-color);
175
+ border-radius: 4px;
176
+ }
177
+
178
+ /* Example Viewer */
179
+ .example-viewer h2 {
180
+ margin-bottom: 1.5rem;
181
+ color: var(--primary-color);
182
+ }
183
+
184
+ .filters {
185
+ display: flex;
186
+ gap: 1rem;
187
+ margin-bottom: 1.5rem;
188
+ flex-wrap: wrap;
189
+ }
190
+
191
+ .filter-group {
192
+ display: flex;
193
+ flex-direction: column;
194
+ gap: 0.5rem;
195
+ }
196
+
197
+ .filter-group label {
198
+ font-weight: 500;
199
+ }
200
+
201
+ .filter-group select {
202
+ padding: 0.5rem;
203
+ min-width: 200px;
204
+ border: 1px solid var(--border-color);
205
+ border-radius: 4px;
206
+ }
207
+
208
+ .examples-grid {
209
+ display: grid;
210
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
211
+ gap: 1.5rem;
212
+ }
213
+
214
+ .example-card {
215
+ background-color: white;
216
+ border-radius: 8px;
217
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
218
+ overflow: hidden;
219
+ transition: transform 0.3s;
220
+ }
221
+
222
+ .example-card:hover {
223
+ transform: translateY(-5px);
224
+ }
225
+
226
+ .example-card h3 {
227
+ padding: 1rem;
228
+ background-color: var(--primary-color);
229
+ color: white;
230
+ font-size: 1rem;
231
+ }
232
+
233
+ .attack-label {
234
+ padding: 0.5rem 1rem;
235
+ background-color: rgba(0, 0, 0, 0.05);
236
+ font-size: 0.9rem;
237
+ border-bottom: 1px solid var(--border-color);
238
+ }
239
+
240
+ .example-content {
241
+ padding: 1rem;
242
+ }
243
+
244
+ .example-image {
245
+ width: 100%;
246
+ height: auto;
247
+ display: block;
248
+ }
249
+
250
+ .example-audio {
251
+ width: 100%;
252
+ }
253
+
254
+ /* Status messages */
255
+ .loading, .error, .no-examples {
256
+ padding: 1rem;
257
+ border-radius: 4px;
258
+ text-align: center;
259
+ margin: 1rem 0;
260
+ }
261
+
262
+ .loading {
263
+ background-color: rgba(52, 152, 219, 0.1);
264
+ color: var(--secondary-color);
265
+ border: 1px solid rgba(52, 152, 219, 0.3);
266
+ }
267
+
268
+ .error {
269
+ background-color: rgba(231, 76, 60, 0.1);
270
+ color: var(--error-color);
271
+ border: 1px solid rgba(231, 76, 60, 0.3);
272
+ }
273
+
274
+ .no-examples {
275
+ background-color: rgba(241, 196, 15, 0.1);
276
+ color: var(--warning-color);
277
+ border: 1px solid rgba(241, 196, 15, 0.3);
278
+ }
279
+
280
+ /* Footer */
281
+ .footer {
282
+ background-color: var(--primary-color);
283
+ color: white;
284
+ padding: 1rem 0;
285
+ text-align: center;
286
+ }
287
+
288
+ /* Responsive adjustments */
289
+ @media (max-width: 768px) {
290
+ .examples-grid {
291
+ grid-template-columns: 1fr;
292
+ }
293
+
294
+ .pagination {
295
+ flex-direction: column;
296
+ align-items: flex-start;
297
+ }
298
+
299
+ .filters {
300
+ flex-direction: column;
301
+ }
302
+ }
frontend/vite.config.js ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { defineConfig } from 'vite'
2
+ import react from '@vitejs/plugin-react'
3
+
4
+ // https://vitejs.dev/config/
5
+ export default defineConfig({
6
+ plugins: [react()],
7
+ build: {
8
+ outDir: 'dist',
9
+ emptyOutDir: true,
10
+ sourcemap: false
11
+ },
12
+ server: {
13
+ proxy: {
14
+ '/api': {
15
+ target: 'http://localhost:5000',
16
+ changeOrigin: true
17
+ },
18
+ '/examples': {
19
+ target: 'http://localhost:5000',
20
+ changeOrigin: true
21
+ }
22
+ }
23
+ }
24
+ })
prepare_deploy.sh ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Script to prepare for HuggingFace deployment
3
+
4
+ # Ensure we're in production mode
5
+ sed -i '' 's/ENV FLASK_ENV=development/ENV FLASK_ENV=production/g' Dockerfile
6
+
7
+ # Build frontend to verify it works
8
+ echo "Testing frontend build..."
9
+ cd frontend
10
+ npm install
11
+ npm run build
12
+
13
+ if [ $? -ne 0 ]; then
14
+ echo "Frontend build failed! Please fix errors before deploying."
15
+ exit 1
16
+ fi
17
+ cd ..
18
+
19
+ # Test the Docker build
20
+ echo "Testing Docker build..."
21
+ docker build -t omnisealbench-deploy-test .
22
+
23
+ if [ $? -ne 0 ]; then
24
+ echo "Docker build failed! Please fix errors before deploying."
25
+ exit 1
26
+ fi
27
+
28
+ echo "=== DEPLOYMENT PREPARATION COMPLETE ==="
29
+ echo "Push your code to HuggingFace with:"
30
+ echo "git add ."
31
+ echo "git commit -m 'Ready for deployment'"
32
+ echo "git push huggingface main"
setup_dev.sh ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Setup script for development environment
3
+
4
+ # Check for Docker
5
+ if ! command -v docker &> /dev/null; then
6
+ echo "Docker is not installed. Please install Docker first."
7
+ exit 1
8
+ fi
9
+
10
+ # Create virtual environment for backend
11
+ echo "Setting up backend..."
12
+ cd backend
13
+ python -m venv venv
14
+ source venv/bin/activate
15
+ pip install -r requirements.txt
16
+ cd ..
17
+
18
+ # Install frontend dependencies
19
+ echo "Setting up frontend..."
20
+ cd frontend
21
+ npm install
22
+ cd ..
23
+
24
+ echo "Setup complete! You can now run the application with:"
25
+ echo "./start_dev.sh"
26
+ echo ""
27
+ echo "This will start both the frontend and backend in Docker containers."
28
+ echo "To stop the development environment, run:"
29
+ echo "./stop_dev.sh"
start_dev.sh ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Script to start the development environment with Docker (no docker-compose)
3
+
4
+ echo "Starting development environment..."
5
+
6
+ # Create a Docker network for the containers to communicate
7
+ docker network create omnisealbench-network 2>/dev/null || true
8
+
9
+ # Clean up any existing containers
10
+ ./stop_dev.sh > /dev/null 2>&1
11
+
12
+ # Build and start the backend container
13
+ echo "Building and starting backend..."
14
+ docker build -t omnisealbench-backend-dev -f backend/Dockerfile.dev ./backend
15
+ docker run -d --name omnisealbench-backend-dev \
16
+ --network omnisealbench-network \
17
+ -v "$(pwd)/backend:/app" \
18
+ -v "$(pwd)/data:/app/data" \
19
+ -v "$(pwd)/examples:/app/examples" \
20
+ -p 5000:5000 \
21
+ -e FLASK_APP=app.py \
22
+ -e FLASK_ENV=development \
23
+ -e PYTHONUNBUFFERED=1 \
24
+ omnisealbench-backend-dev
25
+
26
+ # Build and start the frontend container
27
+ echo "Building and starting frontend..."
28
+ docker build -t omnisealbench-frontend-dev -f frontend/Dockerfile.dev ./frontend
29
+ docker run -d --name omnisealbench-frontend-dev \
30
+ --network omnisealbench-network \
31
+ -v "$(pwd)/frontend:/app" \
32
+ -v "$(pwd)/frontend/node_modules:/app/node_modules" \
33
+ -p 3000:3000 \
34
+ -e NODE_ENV=development \
35
+ -e VITE_API_URL=http://omnisealbench-backend-dev:5000 \
36
+ omnisealbench-frontend-dev
37
+
38
+ echo "Development environment started!"
39
+ echo "Frontend: http://localhost:3000"
40
+ echo "Backend API: http://localhost:5000/api"
41
+ echo ""
42
+ echo "To stop the development environment, run:"
43
+ echo "./stop_dev.sh"
stop_dev.sh ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Script to stop the development environment
3
+
4
+ echo "Stopping development containers..."
5
+ docker stop omnisealbench-frontend-dev omnisealbench-backend-dev 2>/dev/null || true
6
+
7
+ echo "Removing development containers..."
8
+ docker rm omnisealbench-frontend-dev omnisealbench-backend-dev 2>/dev/null || true
9
+
10
+ # Remove the network
11
+ docker network rm omnisealbench-network 2>/dev/null || true
12
+
13
+ echo "Development environment stopped."
test_build.sh ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Test the complete build process locally
3
+
4
+ # Clean up any existing test containers
5
+ echo "Cleaning up any existing test containers..."
6
+ docker stop omnisealbench-test 2>/dev/null || true
7
+ docker rm omnisealbench-test 2>/dev/null || true
8
+
9
+ echo "Building Docker image..."
10
+ docker build -t omnisealbench-local .
11
+
12
+ if [ $? -ne 0 ]; then
13
+ echo "Docker build failed!"
14
+ exit 1
15
+ fi
16
+
17
+ echo "Running container..."
18
+ docker run -d -p 5002:5000 --name omnisealbench-test omnisealbench-local
19
+
20
+ if [ $? -ne 0 ]; then
21
+ echo "Docker run failed!"
22
+ exit 1
23
+ fi
24
+
25
+ echo "Container started! Application should be available at:"
26
+ echo "http://localhost:5002"
27
+ echo ""
28
+ echo "To see container logs, run:"
29
+ echo "docker logs -f omnisealbench-test"
30
+ echo ""
31
+ echo "To stop and remove the container, run:"
32
+ echo "docker stop omnisealbench-test && docker rm omnisealbench-test"