Spaces:
Sleeping
Sleeping
Upload 15 files
Browse files- .dockerignore +16 -0
- .env.template +59 -0
- .gitignore +136 -0
- Dockerfile_gaia +47 -0
- Dockerfile_ray +43 -0
- LICENSE +21 -0
- README-docker.md +62 -0
- README-local.md +61 -0
- docker-compose-gaia.yml +40 -0
- docker-compose-ray.yml +78 -0
- run-gaia.sh +19 -0
- run-ray.sh +6 -0
- run-server.sh +8 -0
- run-web.sh +8 -0
- setup.py +253 -0
.dockerignore
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
*.egg-info
|
2 |
+
*.pyc
|
3 |
+
*.pyo
|
4 |
+
*.pyd
|
5 |
+
*.pyw
|
6 |
+
*.pyz
|
7 |
+
.github
|
8 |
+
.vscode
|
9 |
+
.gitignore
|
10 |
+
.git
|
11 |
+
.env
|
12 |
+
__pycache__
|
13 |
+
*.log*
|
14 |
+
.data
|
15 |
+
build
|
16 |
+
trace_data
|
.env.template
ADDED
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# LLM Model Config
|
2 |
+
LLM_PROVIDER_GAIA = {YOUR_CONFIG:ant/openai}
|
3 |
+
LLM_MODEL_NAME_GAIA = {YOUR_CONFIG}
|
4 |
+
LLM_API_KEY_GAIA = {YOUR_CONFIG}
|
5 |
+
LLM_BASE_URL_GAIA = {YOUR_CONFIG}
|
6 |
+
LLM_TEMPERATURE_GAIA = 0.0
|
7 |
+
|
8 |
+
LLM_PROVIDER_WEATHER = {YOUR_CONFIG:ant/openai}
|
9 |
+
LLM_MODEL_NAME_WEATHER = {YOUR_CONFIG}
|
10 |
+
LLM_API_KEY_WEATHER = {YOUR_CONFIG}
|
11 |
+
LLM_BASE_URL_WEATHER = {YOUR_CONFIG}
|
12 |
+
LLM_TEMPERATURE_WEATHER = 0.0
|
13 |
+
|
14 |
+
# ===============MCP Server Configurations=================
|
15 |
+
# Google Search
|
16 |
+
GOOGLE_API_KEY={YOUR_CONFIG}
|
17 |
+
GOOGLE_CSE_ID={YOUR_CONFIG}
|
18 |
+
GOOGLE_SEARCH_ENGINE_ID={YOUR_CONFIG}
|
19 |
+
|
20 |
+
# Google Map Server
|
21 |
+
GOOGLE_MAPS_SECRET={YOUR_CONFIG}
|
22 |
+
|
23 |
+
# GitHub Server
|
24 |
+
GITHUB_ACCESS_TOKEN={YOUR_CONFIG}
|
25 |
+
|
26 |
+
# E2B Server
|
27 |
+
E2B_API_KEY={YOUR_CONFIG}
|
28 |
+
|
29 |
+
# Filesystem Server
|
30 |
+
FILESYSTEM_SERVER_WORKDIR=/app/aworld/gaia-benchmark/fs
|
31 |
+
|
32 |
+
# Browser Use: skip verfication
|
33 |
+
SKIP_LLM_API_KEY_VERIFICATION=true
|
34 |
+
OPENAI_API_KEY={YOUR_CONFIG}
|
35 |
+
|
36 |
+
# Apify Server
|
37 |
+
APIFY_API_TOKEN={YOUR_CONFIG}
|
38 |
+
|
39 |
+
# Audio Server
|
40 |
+
AUDIO_LLM_API_KEY={YOUR_CONFIG}
|
41 |
+
AUDIO_LLM_BASE_URL=https://api.zhizengzeng.com/v1
|
42 |
+
AUDIO_LLM_MODEL_NAME=gpt-4o-transcribe
|
43 |
+
|
44 |
+
# Image Server
|
45 |
+
IMAGE_LLM_API_KEY={YOUR_CONFIG}
|
46 |
+
IMAGE_LLM_BASE_URL=https://openrouter.ai/api/v1
|
47 |
+
IMAGE_LLM_MODEL_NAME=anthropic/claude-3.7-sonnet
|
48 |
+
|
49 |
+
# Video Server
|
50 |
+
VIDEO_LLM_API_KEY={YOUR_CONFIG}
|
51 |
+
VIDEO_LLM_BASE_URL=https://openrouter.ai/api/v1
|
52 |
+
VIDEO_LLM_MODEL_NAME=anthropic/claude-3.7-sonnet
|
53 |
+
|
54 |
+
# - Zhizengzeng LLM ModelConfig
|
55 |
+
LLM_API_KEY_ZZZ={YOUR_CONFIG}
|
56 |
+
LLM_BASE_URL_ZZZ=https://api.zhizengzeng.com/v1
|
57 |
+
|
58 |
+
# Weather Agent MCP API KEY, https://dev.qweather.com/
|
59 |
+
OPENWEATHER_API_KEY={YOUR_CONFIG}
|
.gitignore
ADDED
@@ -0,0 +1,136 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Python specific
|
2 |
+
__pycache__/
|
3 |
+
*.py[cod]
|
4 |
+
*$py.class
|
5 |
+
*.so
|
6 |
+
.Python
|
7 |
+
build/
|
8 |
+
develop-eggs/
|
9 |
+
downloads/
|
10 |
+
eggs/
|
11 |
+
.eggs/
|
12 |
+
lib/
|
13 |
+
lib64/
|
14 |
+
parts/
|
15 |
+
sdist/
|
16 |
+
var/
|
17 |
+
wheels/
|
18 |
+
*.egg-info/
|
19 |
+
.installed.cfg
|
20 |
+
*.egg
|
21 |
+
MANIFEST
|
22 |
+
.pytest_cache/
|
23 |
+
htmlcov/
|
24 |
+
.coverage
|
25 |
+
.coverage.*
|
26 |
+
.cache
|
27 |
+
coverage.xml
|
28 |
+
*.cover
|
29 |
+
|
30 |
+
# Log files
|
31 |
+
*.log
|
32 |
+
git-pre-push-hook-warn.log
|
33 |
+
logs/
|
34 |
+
logging/
|
35 |
+
|
36 |
+
# Environment and virtual environments
|
37 |
+
.venv
|
38 |
+
env/
|
39 |
+
venv/
|
40 |
+
ENV/
|
41 |
+
env.bak/
|
42 |
+
venv.bak/
|
43 |
+
.env
|
44 |
+
.env.docker
|
45 |
+
|
46 |
+
# IDE specific files
|
47 |
+
.idea/
|
48 |
+
.vscode/
|
49 |
+
.cursor/
|
50 |
+
*.swp
|
51 |
+
*.swo
|
52 |
+
.DS_Store
|
53 |
+
.ipynb_checkpoints
|
54 |
+
|
55 |
+
# OS generated files
|
56 |
+
.DS_Store
|
57 |
+
.DS_Store?
|
58 |
+
._*
|
59 |
+
.Spotlight-V100
|
60 |
+
.Trashes
|
61 |
+
ehthumbs.db
|
62 |
+
Thumbs.db
|
63 |
+
|
64 |
+
# Build artifacts
|
65 |
+
*.pyc
|
66 |
+
*.pyo
|
67 |
+
*.pyd
|
68 |
+
|
69 |
+
# Documentation
|
70 |
+
docs/_build/
|
71 |
+
docs/api/
|
72 |
+
|
73 |
+
# Data files (customize as needed)
|
74 |
+
*.csv
|
75 |
+
*.dat
|
76 |
+
*.out
|
77 |
+
data/raw/
|
78 |
+
data/processed/
|
79 |
+
data/workspaces/
|
80 |
+
gaia-benchmark/
|
81 |
+
examples/gaia/gaia_dataset/
|
82 |
+
|
83 |
+
# Compiled files
|
84 |
+
*.com
|
85 |
+
*.class
|
86 |
+
*.dll
|
87 |
+
*.exe
|
88 |
+
*.o
|
89 |
+
*.a
|
90 |
+
|
91 |
+
# Package files
|
92 |
+
*.7z
|
93 |
+
*.dmg
|
94 |
+
*.gz
|
95 |
+
*.iso
|
96 |
+
*.jar
|
97 |
+
*.rar
|
98 |
+
*.tar
|
99 |
+
*.zip
|
100 |
+
|
101 |
+
# Temporary files
|
102 |
+
*.tmp
|
103 |
+
*.temp
|
104 |
+
.*.swp
|
105 |
+
.*.swo
|
106 |
+
*.webm
|
107 |
+
*.mp4
|
108 |
+
*.mp3
|
109 |
+
*.avi
|
110 |
+
*.mov
|
111 |
+
*.flv
|
112 |
+
*.wmv
|
113 |
+
*.mkv
|
114 |
+
*.npy
|
115 |
+
*.xlsx
|
116 |
+
*.xls
|
117 |
+
*.docx
|
118 |
+
*.doc
|
119 |
+
*.pptx
|
120 |
+
*.ppt
|
121 |
+
*.pdf
|
122 |
+
*.wav
|
123 |
+
|
124 |
+
# Node Modules
|
125 |
+
node_modules/
|
126 |
+
|
127 |
+
aworld.db
|
128 |
+
.data/
|
129 |
+
examples/gaia/GAIA
|
130 |
+
traces*.json
|
131 |
+
trace_data
|
132 |
+
|
133 |
+
examples/gaia/output
|
134 |
+
examples/**/workspaces
|
135 |
+
examples/**/data
|
136 |
+
aworld/cmd/web/webui/dist
|
Dockerfile_gaia
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM python:3.11-slim AS base
|
2 |
+
|
3 |
+
RUN mkdir -p /app/aworld
|
4 |
+
|
5 |
+
WORKDIR /app/aworld
|
6 |
+
|
7 |
+
RUN pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/
|
8 |
+
|
9 |
+
RUN rm -fv /etc/apt/sources.list.d/* && \
|
10 |
+
echo "deb [trusted=yes] https://mirrors.aliyun.com/debian bookworm main contrib non-free non-free-firmware" > /etc/apt/sources.list && \
|
11 |
+
echo "deb [trusted=yes] https://mirrors.aliyun.com/debian bookworm-updates main contrib non-free non-free-firmware" >> /etc/apt/sources.list && \
|
12 |
+
echo "deb [trusted=yes] https://mirrors.aliyun.com/debian bookworm-backports main contrib non-free non-free-firmware" >> /etc/apt/sources.list && \
|
13 |
+
echo "deb [trusted=yes] https://mirrors.aliyun.com/debian-security bookworm-security main contrib non-free non-free-firmware" >> /etc/apt/sources.list && \
|
14 |
+
apt-get clean && \
|
15 |
+
rm -rf /var/lib/apt/lists/*
|
16 |
+
|
17 |
+
RUN apt-get update && apt-get install -y wget unzip openssh-client procps nodejs npm
|
18 |
+
|
19 |
+
# Config Env
|
20 |
+
RUN npx playwright install chrome
|
21 |
+
RUN npm install @playwright/mcp @negokaz/excel-mcp-server
|
22 |
+
|
23 |
+
# GAIA Docker Image
|
24 |
+
FROM base AS gaia_env
|
25 |
+
|
26 |
+
# Install aworld
|
27 |
+
COPY ./aworld aworld
|
28 |
+
COPY ./setup.py setup.py
|
29 |
+
RUN python setup.py install
|
30 |
+
|
31 |
+
# Install mcp servers requirements
|
32 |
+
COPY ./mcp_servers mcp_servers
|
33 |
+
RUN pip install -r mcp_servers/requirements.txt
|
34 |
+
|
35 |
+
# Copy examples
|
36 |
+
COPY ./examples/ examples
|
37 |
+
|
38 |
+
# Create GAIA benchmark directory
|
39 |
+
RUN mkdir -p gaia-benchmark/fs && \
|
40 |
+
mkdir -p gaia-benchmark/logs && \
|
41 |
+
mkdir -p static
|
42 |
+
|
43 |
+
ENV PYTHONPATH=/app/aworld${PYTHONPATH:+:${PYTHONPATH}}
|
44 |
+
ENV GAIA_DATASET_PATH=/app/aworld/examples/gaia/GAIA/2023
|
45 |
+
ENV LOG_FILE_PATH=/app/aworld/gaia-benchmark/logs
|
46 |
+
|
47 |
+
CMD [ "python", "examples/gaia/gaia_agent_server.py" ]
|
Dockerfile_ray
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
FROM rayproject/ray:latest-py312-cpu as base
|
2 |
+
|
3 |
+
USER root
|
4 |
+
|
5 |
+
RUN pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/
|
6 |
+
|
7 |
+
RUN wget --quiet https://deb.nodesource.com/setup_lts.x && \
|
8 |
+
bash setup_lts.x && \
|
9 |
+
rm -fv setup_lts.x && \
|
10 |
+
apt-get update
|
11 |
+
|
12 |
+
RUN apt-get install -y procps iputils-ping nodejs build-essential
|
13 |
+
|
14 |
+
RUN apt-get clean
|
15 |
+
|
16 |
+
FROM base as ray
|
17 |
+
|
18 |
+
USER ray
|
19 |
+
|
20 |
+
RUN mkdir -p /home/ray/aworld
|
21 |
+
|
22 |
+
WORKDIR /home/ray/aworld
|
23 |
+
|
24 |
+
COPY ./aworld aworld
|
25 |
+
COPY ./mcp_servers mcp_servers
|
26 |
+
COPY ./setup.py setup.py
|
27 |
+
|
28 |
+
RUN pip install -e .
|
29 |
+
|
30 |
+
RUN pip install -r ./mcp_servers/requirements.txt
|
31 |
+
|
32 |
+
FROM ray
|
33 |
+
|
34 |
+
COPY ./examples/ examples
|
35 |
+
|
36 |
+
RUN pip install -r ./examples/web/agent_deploy/ray_agent/requirements.txt
|
37 |
+
|
38 |
+
# Fix numpy ray compatibility issues
|
39 |
+
RUN pip install -U "numpy>=1.19,<2.0" "pyarrow>=14.0.2" "grpcio==1.66.2"
|
40 |
+
|
41 |
+
ENV RAY_memory_monitor_refresh_ms=0
|
42 |
+
ENV RAY_object_store_memory=1000000000
|
43 |
+
ENV RAY_memory_usage_threshold=0.9
|
LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
MIT License
|
2 |
+
|
3 |
+
Copyright (c) 2024 [Ant Group]
|
4 |
+
|
5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6 |
+
of this software and associated documentation files (the "Software"), to deal
|
7 |
+
in the Software without restriction, including without limitation the rights
|
8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9 |
+
copies of the Software, and to permit persons to whom the Software is
|
10 |
+
furnished to do so, subject to the following conditions:
|
11 |
+
|
12 |
+
The above copyright notice and this permission notice shall be included in all
|
13 |
+
copies or substantial portions of the Software.
|
14 |
+
|
15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21 |
+
SOFTWARE.
|
README-docker.md
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Running GAIA Benchmark in Docker
|
2 |
+
|
3 |
+
## Prerequisites
|
4 |
+
|
5 |
+
1. **Docker Installation**
|
6 |
+
- Ensure Docker is installed and running on your machine
|
7 |
+
- Verify installation:
|
8 |
+
```bash
|
9 |
+
docker ps
|
10 |
+
```
|
11 |
+
|
12 |
+
2. **Repository Setup**
|
13 |
+
- Clone the repository:
|
14 |
+
```bash
|
15 |
+
git clone https://github.com/inclusionAI/AWorld
|
16 |
+
cd AWorld
|
17 |
+
```
|
18 |
+
|
19 |
+
3. **Dataset Preparation**
|
20 |
+
- Download the GAIA dataset from [Hugging Face](https://huggingface.co/datasets/gaia-benchmark/GAIA)
|
21 |
+
- Place it in the correct directory:
|
22 |
+
```bash
|
23 |
+
git clone [email protected]:datasets/gaia-benchmark/GAIA examples/gaia/GAIA
|
24 |
+
```
|
25 |
+
- ⚠️ **Note**: You need to configure [Hugging Face SSH Keys](https://huggingface.co/settings/keys) to access the GAIA repository
|
26 |
+
|
27 |
+
4. **API Configuration**
|
28 |
+
- Set up your environment variables:
|
29 |
+
```bash
|
30 |
+
cp .env.template .env
|
31 |
+
```
|
32 |
+
- Edit `.env` file and replace all `{YOUR_CONFIG}` placeholders with your actual values
|
33 |
+
|
34 |
+
## Running the Benchmark
|
35 |
+
|
36 |
+
1. **Start the Container**
|
37 |
+
- Build and run the GAIA container:
|
38 |
+
```bash
|
39 |
+
sh run-gaia.sh
|
40 |
+
```
|
41 |
+
- Wait for the build to complete and the container to start
|
42 |
+
|
43 |
+
2. **Access the Interface**
|
44 |
+
- Open your browser and navigate to [http://127.0.0.1:8080](http://127.0.0.1:8080)
|
45 |
+
- Register a new account
|
46 |
+
- Log in to your account
|
47 |
+
|
48 |
+
3. **Run the Benchmark**
|
49 |
+
- Select `gaia_agent` from the top menu
|
50 |
+
- Choose a GAIA question from the list
|
51 |
+
- Click the send button to start the benchmark
|
52 |
+
|
53 |
+
## Troubleshooting
|
54 |
+
|
55 |
+
- If you encounter any issues with Docker, ensure it's running properly
|
56 |
+
- For dataset access problems, verify your Hugging Face SSH keys are correctly configured
|
57 |
+
- Check the container logs if the interface is not accessible
|
58 |
+
|
59 |
+
## Additional Resources
|
60 |
+
|
61 |
+
- [GAIA Benchmark Documentation](https://huggingface.co/datasets/gaia-benchmark/GAIA)
|
62 |
+
- [Hugging Face SSH Keys Setup Guide](https://huggingface.co/settings/keys)
|
README-local.md
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Running AWorld Agent
|
2 |
+
|
3 |
+
## Prerequisites
|
4 |
+
|
5 |
+
1. **Conda Environment**
|
6 |
+
- Ensure Conda is installed and configured on your machine
|
7 |
+
- Create a Conda environment:
|
8 |
+
```bash
|
9 |
+
conda create -n aworld python=3.11
|
10 |
+
conda activate aworld
|
11 |
+
```
|
12 |
+
|
13 |
+
2. **Clone Repository**
|
14 |
+
- Clone the repository and switch to the main branch:
|
15 |
+
```bash
|
16 |
+
git clone https://github.com/inclusionAI/AWorld
|
17 |
+
cd AWorld
|
18 |
+
```
|
19 |
+
|
20 |
+
3. **Dataset Preparation**
|
21 |
+
- Download the GAIA dataset from [Hugging Face](https://huggingface.co/datasets/gaia-benchmark/GAIA)
|
22 |
+
- Place it in the correct directory:
|
23 |
+
```bash
|
24 |
+
git clone [email protected]:datasets/gaia-benchmark/GAIA examples/gaia/GAIA
|
25 |
+
```
|
26 |
+
- ⚠️ **Note**: You need to configure [Hugging Face SSH Keys](https://huggingface.co/settings/keys) to access the GAIA repository
|
27 |
+
|
28 |
+
4. **API Configuration**
|
29 |
+
- Set up your environment variables:
|
30 |
+
```bash
|
31 |
+
cp .env.template .env
|
32 |
+
```
|
33 |
+
- Edit the `.env` file and replace all `{YOUR_CONFIG}` placeholders with your actual values
|
34 |
+
|
35 |
+
## Running the Agent
|
36 |
+
|
37 |
+
1. **Start the Web Server**
|
38 |
+
- Build and run the AWorld Agent:
|
39 |
+
```bash
|
40 |
+
sh run-server.sh
|
41 |
+
```
|
42 |
+
- Wait for the installation to complete
|
43 |
+
|
44 |
+
2. **Access the Interface**
|
45 |
+
- Open your browser and navigate to [http://127.0.0.1:8000](http://127.0.0.1:8000)
|
46 |
+
|
47 |
+
## Troubleshooting
|
48 |
+
|
49 |
+
- For dataset access problems, verify that your Hugging Face SSH keys are correctly configured
|
50 |
+
- Set up a pip mirror if necessary
|
51 |
+
|
52 |
+
## Develop Your Own Agent
|
53 |
+
|
54 |
+
- Copy `examples/web/agent_deploy/weather_agent` to `examples/web/agent_deploy/{YOUR_AGENT_NAME}`
|
55 |
+
- Write your code in `examples/web/agent_deploy/{YOUR_AGENT_NAME}/agent.py`
|
56 |
+
- Configure the MCP Server in `examples/web/agent_deploy/{YOUR_AGENT_NAME}/mcp.json`
|
57 |
+
|
58 |
+
## Additional Resources
|
59 |
+
|
60 |
+
- [GAIA Benchmark Documentation](https://huggingface.co/datasets/gaia-benchmark/GAIA)
|
61 |
+
- [Hugging Face SSH Keys Setup Guide](https://huggingface.co/settings/keys)
|
docker-compose-gaia.yml
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
services:
|
2 |
+
openwebui:
|
3 |
+
image: ghcr.io/open-webui/open-webui:main
|
4 |
+
ports:
|
5 |
+
- 127.0.0.1:8080:8080
|
6 |
+
restart: on-failure:3
|
7 |
+
volumes:
|
8 |
+
- ./examples/gaia/openwebui-patch/ca81bd47c050_add_config_table.py:/app/backend/open_webui/migrations/versions/ca81bd47c050_add_config_table.py
|
9 |
+
- ./examples/gaia/GAIA:/app/aworld/examples/gaia/GAIA
|
10 |
+
environment:
|
11 |
+
- HF_HUB_OFFLINE=1
|
12 |
+
- ENABLE_OLLAMA_API=False
|
13 |
+
- ENABLE_DIRECT_CONNECTIONS=False
|
14 |
+
- ENABLE_EVALUATION_ARENA_MODELS=False
|
15 |
+
- ENABLE_TITLE_GENERATION=False
|
16 |
+
- ENABLE_AUTOCOMPLETE_GENERATION=False
|
17 |
+
- ENABLE_TAGS_GENERATION=False
|
18 |
+
- OPENAI_API_BASE_URL=http://gaia_agent:8888/v1
|
19 |
+
- OPENAI_API_KEY=xxx
|
20 |
+
deploy:
|
21 |
+
resources:
|
22 |
+
limits:
|
23 |
+
memory: 2G
|
24 |
+
|
25 |
+
gaia_agent:
|
26 |
+
build:
|
27 |
+
context: .
|
28 |
+
dockerfile: Dockerfile_gaia
|
29 |
+
platform: linux/amd64
|
30 |
+
ports:
|
31 |
+
- 127.0.0.1:8888:8888
|
32 |
+
volumes:
|
33 |
+
- ./.env:/app/aworld/.env
|
34 |
+
env_file:
|
35 |
+
- ./.env
|
36 |
+
restart: on-failure:3
|
37 |
+
deploy:
|
38 |
+
resources:
|
39 |
+
limits:
|
40 |
+
memory: 4G
|
docker-compose-ray.yml
ADDED
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
services:
|
2 |
+
ray-head:
|
3 |
+
build:
|
4 |
+
context: .
|
5 |
+
dockerfile: Dockerfile_ray
|
6 |
+
platform: linux/amd64
|
7 |
+
container_name: ray-head
|
8 |
+
hostname: ray-head
|
9 |
+
ports:
|
10 |
+
- "8265:8265" # Ray Dashboard
|
11 |
+
- "10001:10001" # Ray Client Server
|
12 |
+
- "6379:6379" # Redis port for Ray GCS
|
13 |
+
command: >
|
14 |
+
ray start --head --dashboard-host=0.0.0.0 --dashboard-port=8265 --port=6379 --redis-password="" --object-manager-port=8076 --node-manager-port=8077 --min-worker-port=10002 --max-worker-port=19999 --block
|
15 |
+
env_file:
|
16 |
+
- .env
|
17 |
+
networks:
|
18 |
+
- ray-network
|
19 |
+
restart: unless-stopped
|
20 |
+
deploy:
|
21 |
+
resources:
|
22 |
+
limits:
|
23 |
+
memory: 8G
|
24 |
+
reservations:
|
25 |
+
memory: 8G
|
26 |
+
|
27 |
+
ray-worker:
|
28 |
+
build:
|
29 |
+
context: .
|
30 |
+
dockerfile: Dockerfile_ray
|
31 |
+
platform: linux/amd64
|
32 |
+
container_name: ray-worker
|
33 |
+
hostname: ray-worker
|
34 |
+
depends_on:
|
35 |
+
- ray-head
|
36 |
+
command: >
|
37 |
+
ray start --address=ray-head:6379 --object-manager-port=8076 --node-manager-port=8077 --min-worker-port=10002 --max-worker-port=19999 --block
|
38 |
+
env_file:
|
39 |
+
- .env
|
40 |
+
networks:
|
41 |
+
- ray-network
|
42 |
+
restart: unless-stopped
|
43 |
+
deploy:
|
44 |
+
resources:
|
45 |
+
limits:
|
46 |
+
memory: 4G
|
47 |
+
reservations:
|
48 |
+
memory: 4G
|
49 |
+
|
50 |
+
aworld-web-server:
|
51 |
+
build:
|
52 |
+
context: .
|
53 |
+
dockerfile: Dockerfile_ray
|
54 |
+
platform: linux/amd64
|
55 |
+
container_name: aworld-api-server
|
56 |
+
hostname: aworld-api-server
|
57 |
+
depends_on:
|
58 |
+
- ray-head
|
59 |
+
command: aworld web --port 8000
|
60 |
+
env_file:
|
61 |
+
- .env
|
62 |
+
networks:
|
63 |
+
- ray-network
|
64 |
+
restart: unless-stopped
|
65 |
+
deploy:
|
66 |
+
resources:
|
67 |
+
limits:
|
68 |
+
memory: 4G
|
69 |
+
reservations:
|
70 |
+
memory: 4G
|
71 |
+
|
72 |
+
networks:
|
73 |
+
ray-network:
|
74 |
+
driver: bridge
|
75 |
+
|
76 |
+
volumes:
|
77 |
+
workspace:
|
78 |
+
driver: local
|
run-gaia.sh
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/bin/bash
|
2 |
+
cd "$(dirname "$0")"
|
3 |
+
|
4 |
+
# Check your env file
|
5 |
+
if [ ! -f ".env" ]; then
|
6 |
+
echo "Please add your own .env config file from template .env.template before running gaia test!"
|
7 |
+
exit 1
|
8 |
+
fi
|
9 |
+
|
10 |
+
# Check GAIA dataset
|
11 |
+
if [ ! -d "examples/gaia/GAIA" ]; then
|
12 |
+
echo "Please download GAIA dataset from https://huggingface.co/datasets/gaia-benchmark/GAIA and put it in examples/gaia/GAIA"
|
13 |
+
exit 1
|
14 |
+
fi
|
15 |
+
|
16 |
+
# Build docker image
|
17 |
+
docker compose -f docker-compose-gaia.yml build && \
|
18 |
+
docker compose -f docker-compose-gaia.yml up -d && \
|
19 |
+
docker compose -f docker-compose-gaia.yml logs -f
|
run-ray.sh
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/bin/bash
|
2 |
+
cd "$(dirname "$0")"
|
3 |
+
|
4 |
+
docker compose -f docker-compose-ray.yml build && \
|
5 |
+
docker compose -f docker-compose-ray.yml up -d && \
|
6 |
+
docker compose -f docker-compose-ray.yml logs -f
|
run-server.sh
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/bin/bash
|
2 |
+
cd "$(dirname "$0")"
|
3 |
+
|
4 |
+
pip install -e . && \
|
5 |
+
|
6 |
+
pip install -r mcp_servers/requirements.txt && \
|
7 |
+
|
8 |
+
cd examples/cmd && aworld web_legacy
|
run-web.sh
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/bin/bash
|
2 |
+
cd "$(dirname "$0")"
|
3 |
+
|
4 |
+
pip install -e . && \
|
5 |
+
|
6 |
+
pip install -r mcp_servers/requirements.txt && \
|
7 |
+
|
8 |
+
cd examples/cmd && aworld web
|
setup.py
ADDED
@@ -0,0 +1,253 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# coding: utf-8
|
2 |
+
# Copyright (c) 2025 inclusionAI.
|
3 |
+
import logging
|
4 |
+
import os
|
5 |
+
import sys
|
6 |
+
import re
|
7 |
+
import subprocess
|
8 |
+
|
9 |
+
from setuptools import setup, find_packages
|
10 |
+
from setuptools.command.sdist import sdist
|
11 |
+
from setuptools.command.install import install
|
12 |
+
from setuptools.dist import Distribution
|
13 |
+
|
14 |
+
from aworld.version_gen import __version__
|
15 |
+
|
16 |
+
logger = logging.getLogger("setup")
|
17 |
+
|
18 |
+
version_template = """
|
19 |
+
# auto generated
|
20 |
+
class VersionInfo(object):
|
21 |
+
@property
|
22 |
+
def build_date(self):
|
23 |
+
return "{BUILD_DATE}"
|
24 |
+
|
25 |
+
@property
|
26 |
+
def version(self):
|
27 |
+
return "{BUILD_VERSION}"
|
28 |
+
|
29 |
+
@property
|
30 |
+
def build_user(self):
|
31 |
+
return "{BUILD_USER}"
|
32 |
+
"""
|
33 |
+
|
34 |
+
|
35 |
+
def check_output(cmd):
|
36 |
+
import subprocess
|
37 |
+
|
38 |
+
output = subprocess.check_output(cmd)
|
39 |
+
return output.decode("utf-8")
|
40 |
+
|
41 |
+
|
42 |
+
def get_build_date():
|
43 |
+
import datetime
|
44 |
+
import time
|
45 |
+
|
46 |
+
ts = time.time()
|
47 |
+
return datetime.datetime.fromtimestamp(ts).strftime("%Y-%m-%d %H:%M:%S")
|
48 |
+
|
49 |
+
|
50 |
+
def build_version_template():
|
51 |
+
import getpass
|
52 |
+
|
53 |
+
return version_template.format(
|
54 |
+
BUILD_USER=getpass.getuser(),
|
55 |
+
BUILD_VERSION=__version__,
|
56 |
+
BUILD_DATE=get_build_date(),
|
57 |
+
)
|
58 |
+
|
59 |
+
|
60 |
+
def call_process(cmd, raise_on_error=True, logging=True):
|
61 |
+
if isinstance(cmd, str):
|
62 |
+
shell = True
|
63 |
+
else:
|
64 |
+
shell = False # cmd should be list of args
|
65 |
+
|
66 |
+
try:
|
67 |
+
subprocess.check_call(cmd, shell=shell, timeout=60)
|
68 |
+
except subprocess.CalledProcessError as e:
|
69 |
+
if raise_on_error:
|
70 |
+
raise e
|
71 |
+
logger.error(f"Fail to execute {cmd}, {e}")
|
72 |
+
return e.returncode
|
73 |
+
|
74 |
+
if logging:
|
75 |
+
logger.info(f"Successfully execute: {cmd}")
|
76 |
+
return 0
|
77 |
+
|
78 |
+
|
79 |
+
class AWorldPackage(sdist):
|
80 |
+
def run(self):
|
81 |
+
from aworld.version_gen import generate_version_info
|
82 |
+
|
83 |
+
home = os.path.join(os.path.dirname(__file__), "aworld")
|
84 |
+
with open(os.path.join(home, "version.py"), "w") as f:
|
85 |
+
version_info = build_version_template()
|
86 |
+
f.write(version_info)
|
87 |
+
|
88 |
+
generate_version_info(scenario="AWORLD_SDIST")
|
89 |
+
sdist.run(self)
|
90 |
+
|
91 |
+
|
92 |
+
class AWorldInstaller(install):
|
93 |
+
EXTRA_ENV = "AWORLD_EXTRA"
|
94 |
+
BASE = "framework"
|
95 |
+
BASE_OPT = "optional"
|
96 |
+
|
97 |
+
def __init__(self, *args, **kwargs):
|
98 |
+
super(AWorldInstaller, self).__init__(*args, **kwargs)
|
99 |
+
self._requirements = parse_requirements("aworld/requirements.txt")
|
100 |
+
self._extra = os.getenv(self.EXTRA_ENV)
|
101 |
+
logger.info(f"{os.getcwd()}: Install AWORLD using extra: {self._extra}")
|
102 |
+
|
103 |
+
def run(self):
|
104 |
+
# 1. build wheel using this setup.py, thus using the right install_requires according to ALPS_EXTRA
|
105 |
+
# 2. install this wheel into pip
|
106 |
+
install.run(self)
|
107 |
+
|
108 |
+
reqs = self._requirements.get(self.BASE, [])
|
109 |
+
self._install_reqs(reqs, ignore_error=True)
|
110 |
+
|
111 |
+
# install optional requirements here since pip install doesn't ignore requirement error
|
112 |
+
reqs = self._requirements.get(self.BASE_OPT, [])
|
113 |
+
self._install_reqs(reqs, ignore_error=True)
|
114 |
+
|
115 |
+
def _contains_module(self, module):
|
116 |
+
if self._extra is None:
|
117 |
+
return False
|
118 |
+
|
119 |
+
modules = [mod.strip() for mod in self._extra.split(",")]
|
120 |
+
try:
|
121 |
+
modules.index(module)
|
122 |
+
return True
|
123 |
+
except ValueError:
|
124 |
+
return False
|
125 |
+
|
126 |
+
@staticmethod
|
127 |
+
def _install_reqs(reqs, ignore_error=False, no_deps=False):
|
128 |
+
info = "--no-deps" if no_deps else ""
|
129 |
+
if ignore_error:
|
130 |
+
# install requirements one by one
|
131 |
+
for req in reqs:
|
132 |
+
try:
|
133 |
+
cmd = f"{sys.executable} -m pip install {info} {req}"
|
134 |
+
call_process(cmd)
|
135 |
+
logger.info(f"Installing optional package {req} have succeeded.")
|
136 |
+
except:
|
137 |
+
logger.warning(
|
138 |
+
f"Installing optional package {req} is failed, Ignored."
|
139 |
+
) # ignore
|
140 |
+
elif reqs:
|
141 |
+
cmd = f"{sys.executable} -m pip install {info} {' '.join(reqs)}"
|
142 |
+
call_process(cmd)
|
143 |
+
logger.info(f"Packages {str(reqs)} have been installed.")
|
144 |
+
|
145 |
+
|
146 |
+
def parse_requirements(req_fname):
|
147 |
+
requirements = {}
|
148 |
+
module_name = "unknown"
|
149 |
+
for line in open(req_fname, "r"):
|
150 |
+
match = re.match(r"#+\s+\[(\w+)\]\s+#+", line.strip())
|
151 |
+
if match:
|
152 |
+
# the beginning of a module
|
153 |
+
module_name = match.group(1)
|
154 |
+
else:
|
155 |
+
req = line.strip()
|
156 |
+
if not req or req.startswith("#"):
|
157 |
+
continue
|
158 |
+
|
159 |
+
# it's a requirement, strip trailing comments
|
160 |
+
pos = req.find("#")
|
161 |
+
if pos > 0:
|
162 |
+
req = req[:pos]
|
163 |
+
req = req.strip()
|
164 |
+
|
165 |
+
if module_name not in requirements:
|
166 |
+
requirements[module_name] = []
|
167 |
+
requirements[module_name].append(req)
|
168 |
+
|
169 |
+
return requirements
|
170 |
+
|
171 |
+
|
172 |
+
def get_install_requires(extra, requirements):
|
173 |
+
modules = [AWorldInstaller.BASE]
|
174 |
+
if extra is None:
|
175 |
+
# old style of `pip install alps`, install all requirements for compatibility
|
176 |
+
for mod in requirements:
|
177 |
+
if mod in [AWorldInstaller.BASE, AWorldInstaller.BASE_OPT]:
|
178 |
+
continue
|
179 |
+
modules.append(mod)
|
180 |
+
else:
|
181 |
+
for mod in extra.split(","):
|
182 |
+
mod = mod.strip()
|
183 |
+
if mod != AWorldInstaller.BASE:
|
184 |
+
modules.append(mod)
|
185 |
+
|
186 |
+
install_reqs = []
|
187 |
+
for mod in modules:
|
188 |
+
install_reqs.extend(requirements.get(mod, []))
|
189 |
+
return install_reqs
|
190 |
+
|
191 |
+
|
192 |
+
def get_python_requires():
|
193 |
+
return ">=3.11"
|
194 |
+
|
195 |
+
|
196 |
+
class BinaryDistribution(Distribution):
|
197 |
+
"""This class is needed in order to create OS specific wheels."""
|
198 |
+
|
199 |
+
@staticmethod
|
200 |
+
def has_ext_modules():
|
201 |
+
return True
|
202 |
+
|
203 |
+
|
204 |
+
requirements = parse_requirements("aworld/requirements.txt")
|
205 |
+
extra = os.getenv(AWorldInstaller.EXTRA_ENV, None)
|
206 |
+
|
207 |
+
setup(
|
208 |
+
name="aworld",
|
209 |
+
version=__version__,
|
210 |
+
description="Ant Agent Package",
|
211 |
+
url="https://github.com/inclusionAI/AWorld",
|
212 |
+
author="Ant AI",
|
213 |
+
author_email="",
|
214 |
+
long_description="",
|
215 |
+
long_description_content_type="text/markdown",
|
216 |
+
packages=find_packages(
|
217 |
+
where=".",
|
218 |
+
exclude=["tests", "tests.*", "*.tests", "*.tests.*", "*.test", "*.test.*"],
|
219 |
+
),
|
220 |
+
package_data={
|
221 |
+
"aworld": [
|
222 |
+
"virtual_environments/browsers/script/*.js",
|
223 |
+
"dataset/gaia/gaia.npy",
|
224 |
+
"requirements.txt",
|
225 |
+
"config/*.yaml",
|
226 |
+
"config/*.json",
|
227 |
+
"config/*.tiktoken",
|
228 |
+
"web/templates/*.html",
|
229 |
+
]
|
230 |
+
},
|
231 |
+
license="MIT",
|
232 |
+
platforms=["any"],
|
233 |
+
keywords=["multi-agent", "agent", "environment", "tool", "sandbox"],
|
234 |
+
cmdclass={
|
235 |
+
"sdist": AWorldPackage,
|
236 |
+
"install": AWorldInstaller,
|
237 |
+
},
|
238 |
+
install_requires=get_install_requires(extra, requirements),
|
239 |
+
python_requires=get_python_requires(),
|
240 |
+
classifiers=[
|
241 |
+
"Development Status :: 5 - Production/Stable",
|
242 |
+
# Indicate who your project is intended for
|
243 |
+
"Intended Audience :: Developers",
|
244 |
+
"Topic :: Software Development :: Build Tools",
|
245 |
+
"Programming Language :: Python :: 3.11",
|
246 |
+
"Programming Language :: Python :: 3.12",
|
247 |
+
],
|
248 |
+
entry_points={
|
249 |
+
"console_scripts": [
|
250 |
+
"aworld = aworld.__main__:main",
|
251 |
+
]
|
252 |
+
},
|
253 |
+
)
|