diff --git a/.github/workflows/fulltest.yaml b/.github/workflows/fulltest.yaml new file mode 100644 index 000000000..f5c6049e1 --- /dev/null +++ b/.github/workflows/fulltest.yaml @@ -0,0 +1,84 @@ +name: Full Tests + +on: + workflow_dispatch: + pull_request_target: + push: + branches: + - 'main' + - 'dev' + - '*-release' + - '*-debugger' + +jobs: + build: + runs-on: ubuntu-latest + environment: unittest + strategy: + matrix: + # python-version: ['3.9', '3.10', '3.11'] + python-version: ['3.9'] + + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha }} + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + cache: 'pip' + - name: Install dependencies + run: | + sh tests/scripts/run_install_deps.sh + - name: Run reverse proxy script for ssh service + if: contains(github.ref, '-debugger') + continue-on-error: true + env: + FPR_SERVER_ADDR: ${{ secrets.FPR_SERVER_ADDR }} + FPR_TOKEN: ${{ secrets.FPR_TOKEN }} + FPR_SSH_REMOTE_PORT: ${{ secrets.FPR_SSH_REMOTE_PORT }} + RSA_PUB: ${{ secrets.RSA_PUB }} + SSH_PORT: ${{ vars.SSH_PORT || '22'}} + run: | + echo "Run \"ssh $(whoami)@FPR_SERVER_HOST -p FPR_SSH_REMOTE_PORT\" and \"cd $(pwd)\"" + mkdir -p ~/.ssh/ + echo $RSA_PUB >> ~/.ssh/authorized_keys + chmod 600 ~/.ssh/authorized_keys + wget https://github.com/fatedier/frp/releases/download/v0.32.1/frp_0.32.1_linux_amd64.tar.gz -O frp.tar.gz + tar xvzf frp.tar.gz -C /opt + mv /opt/frp* /opt/frp + /opt/frp/frpc tcp --server_addr $FPR_SERVER_ADDR --token $FPR_TOKEN --local_port $SSH_PORT --remote_port $FPR_SSH_REMOTE_PORT + - name: Test with pytest + run: | + export ALLOW_OPENAI_API_CALL=0 + echo "${{ secrets.METAGPT_KEY_YAML }}" | base64 -d > config/key.yaml + mkdir -p ~/.metagpt && echo "${{ secrets.METAGPT_CONFIG2_YAML }}" | base64 -d > ~/.metagpt/config2.yaml + echo "${{ secrets.SPARK_YAML }}" | base64 -d > ~/.metagpt/spark.yaml + pytest tests/ --doctest-modules --cov=./metagpt/ --cov-report=xml:cov.xml --cov-report=html:htmlcov --durations=20 | tee unittest.txt + - name: Show coverage report + run: | + coverage report -m + - name: Show failed tests and overall summary + run: | + grep -E "FAILED tests|ERROR tests|[0-9]+ passed," unittest.txt + failed_count=$(grep -E "FAILED|ERROR" unittest.txt | wc -l) + if [[ "$failed_count" -gt 0 ]]; then + echo "$failed_count failed lines found! Task failed." + exit 1 + fi + - name: Upload pytest test results + uses: actions/upload-artifact@v3 + with: + name: pytest-results-${{ matrix.python-version }} + path: | + ./unittest.txt + ./htmlcov/ + ./tests/data/rsp_cache_new.json + retention-days: 3 + if: ${{ always() }} + - name: Upload coverage reports to Codecov + uses: codecov/codecov-action@v3 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + if: ${{ always() }} diff --git a/.github/workflows/unittest.yaml b/.github/workflows/unittest.yaml index fd56c42fb..2e7e3ce2b 100644 --- a/.github/workflows/unittest.yaml +++ b/.github/workflows/unittest.yaml @@ -1,16 +1,16 @@ name: Unit Tests on: - workflow_dispatch: pull_request_target: push: branches: - - '*-debugger' + - 'main' + - 'dev' + - '*-release' jobs: build: runs-on: ubuntu-latest - environment: unittest strategy: matrix: # python-version: ['3.9', '3.10', '3.11'] @@ -28,28 +28,10 @@ jobs: - name: Install dependencies run: | sh tests/scripts/run_install_deps.sh - - name: Run reverse proxy script for ssh service - if: contains(github.ref, '-debugger') - continue-on-error: true - env: - FPR_SERVER_ADDR: ${{ secrets.FPR_SERVER_ADDR }} - FPR_TOKEN: ${{ secrets.FPR_TOKEN }} - FPR_SSH_REMOTE_PORT: ${{ secrets.FPR_SSH_REMOTE_PORT }} - RSA_PUB: ${{ secrets.RSA_PUB }} - SSH_PORT: ${{ vars.SSH_PORT || '22'}} - run: | - echo "Run \"ssh $(whoami)@FPR_SERVER_HOST -p FPR_SSH_REMOTE_PORT\" and \"cd $(pwd)\"" - mkdir -p ~/.ssh/ - echo $RSA_PUB >> ~/.ssh/authorized_keys - chmod 600 ~/.ssh/authorized_keys - wget https://github.com/fatedier/frp/releases/download/v0.32.1/frp_0.32.1_linux_amd64.tar.gz -O frp.tar.gz - tar xvzf frp.tar.gz -C /opt - mv /opt/frp* /opt/frp - /opt/frp/frpc tcp --server_addr $FPR_SERVER_ADDR --token $FPR_TOKEN --local_port $SSH_PORT --remote_port $FPR_SSH_REMOTE_PORT - name: Test with pytest run: | export ALLOW_OPENAI_API_CALL=0 - echo "${{ secrets.METAGPT_KEY_YAML }}" | base64 -d > config/key.yaml + mkdir -p ~/.metagpt && cp tests/config2.yaml ~/.metagpt/config2.yaml && cp tests/spark.yaml ~/.metagpt/spark.yaml pytest tests/ --doctest-modules --cov=./metagpt/ --cov-report=xml:cov.xml --cov-report=html:htmlcov --durations=20 | tee unittest.txt - name: Show coverage report run: | diff --git a/.gitignore b/.gitignore index c10191dcc..6bc67fa61 100644 --- a/.gitignore +++ b/.gitignore @@ -172,8 +172,10 @@ tests/metagpt/utils/file_repo_git *.png htmlcov htmlcov.* +cov.xml *.dot *.pkl +*.faiss *-structure.csv *-structure.json - +metagpt/tools/schemas \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 9eeacbccb..dead20537 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ RUN apt update &&\ # Install Mermaid CLI globally ENV CHROME_BIN="/usr/bin/chromium" \ - PUPPETEER_CONFIG="/app/metagpt/config/puppeteer-config.json"\ + puppeteer_config="/app/metagpt/config/puppeteer-config.json"\ PUPPETEER_SKIP_CHROMIUM_DOWNLOAD="true" RUN npm install -g @mermaid-js/mermaid-cli &&\ npm cache clean --force diff --git a/README.md b/README.md index 90c586068..b6f31901b 100644 --- a/README.md +++ b/README.md @@ -55,30 +55,21 @@ ### Pip installation +> Ensure that Python 3.9+ is installed on your system. You can check this by using: `python --version`. +> You can use conda like this: `conda create -n metagpt python=3.9 && conda activate metagpt` + ```bash -# Step 1: Ensure that Python 3.9+ is installed on your system. You can check this by using: -# You can use conda to initialize a new python env -# conda create -n metagpt python=3.9 -# conda activate metagpt -python3 --version - -# Step 2: Clone the repository to your local machine for latest version, and install it. -git clone https://github.com/geekan/MetaGPT.git -cd MetaGPT -pip3 install -e . # or pip3 install metagpt # for stable version - -# Step 3: setup your OPENAI_API_KEY, or make sure it existed in the env -mkdir ~/.metagpt -cp config/config.yaml ~/.metagpt/config.yaml -vim ~/.metagpt/config.yaml - -# Step 4: run metagpt cli -metagpt "Create a 2048 game in python" - -# Step 5 [Optional]: If you want to save the artifacts like diagrams such as quadrant chart, system designs, sequence flow in the workspace, you can execute the step before Step 3. By default, the framework is compatible, and the entire process can be run completely without executing this step. -# If executing, ensure that NPM is installed on your system. Then install mermaid-js. (If you don't have npm in your computer, please go to the Node.js official website to install Node.js https://nodejs.org/ and then you will have npm tool in your computer.) -npm --version -sudo npm install -g @mermaid-js/mermaid-cli +pip install metagpt +metagpt --init-config # create ~/.metagpt/config2.yaml, modify it to your own config +metagpt "Create a 2048 game" # this will create a repo in ./workspace +``` + +or you can use it as library + +```python +from metagpt.software_company import generate_repo, ProjectRepo +repo: ProjectRepo = generate_repo("Create a 2048 game") # or ProjectRepo("") +print(repo) # it will print the repo structure with files ``` detail installation please refer to [cli_install](https://docs.deepwisdom.ai/main/en/guide/get_started/installation.html#install-stable-version) @@ -87,19 +78,19 @@ detail installation please refer to [cli_install](https://docs.deepwisdom.ai/mai > Note: In the Windows, you need to replace "/opt/metagpt" with a directory that Docker has permission to create, such as "D:\Users\x\metagpt" ```bash -# Step 1: Download metagpt official image and prepare config.yaml +# Step 1: Download metagpt official image and prepare config2.yaml docker pull metagpt/metagpt:latest mkdir -p /opt/metagpt/{config,workspace} -docker run --rm metagpt/metagpt:latest cat /app/metagpt/config/config.yaml > /opt/metagpt/config/key.yaml -vim /opt/metagpt/config/key.yaml # Change the config +docker run --rm metagpt/metagpt:latest cat /app/metagpt/config/config2.yaml > /opt/metagpt/config/config2.yaml +vim /opt/metagpt/config/config2.yaml # Change the config # Step 2: Run metagpt demo with container docker run --rm \ --privileged \ - -v /opt/metagpt/config/key.yaml:/app/metagpt/config/key.yaml \ + -v /opt/metagpt/config/config2.yaml:/app/metagpt/config/config2.yaml \ -v /opt/metagpt/workspace:/app/metagpt/workspace \ metagpt/metagpt:latest \ - metagpt "Write a cli snake game" + metagpt "Create a 2048 game" ``` detail installation please refer to [docker_install](https://docs.deepwisdom.ai/main/en/guide/get_started/installation.html#install-with-docker) diff --git a/config/config.yaml b/config/config.yaml deleted file mode 100644 index f41f7d276..000000000 --- a/config/config.yaml +++ /dev/null @@ -1,145 +0,0 @@ -# DO NOT MODIFY THIS FILE, create a new key.yaml, define OPENAI_API_KEY. -# The configuration of key.yaml has a higher priority and will not enter git - -#### Project Path Setting -# WORKSPACE_PATH: "Path for placing output files" - -#### if OpenAI -## The official OPENAI_BASE_URL is https://api.openai.com/v1 -## If the official OPENAI_BASE_URL is not available, we recommend using the [openai-forward](https://github.com/beidongjiedeguang/openai-forward). -## Or, you can configure OPENAI_PROXY to access official OPENAI_BASE_URL. -OPENAI_BASE_URL: "https://api.openai.com/v1" -#OPENAI_PROXY: "http://127.0.0.1:8118" -#OPENAI_API_KEY: "YOUR_API_KEY" # set the value to sk-xxx if you host the openai interface for open llm model -OPENAI_API_MODEL: "gpt-4-1106-preview" -MAX_TOKENS: 4096 -RPM: 10 -TIMEOUT: 60 # Timeout for llm invocation -#DEFAULT_PROVIDER: openai - -#### if Spark -#SPARK_APPID : "YOUR_APPID" -#SPARK_API_SECRET : "YOUR_APISecret" -#SPARK_API_KEY : "YOUR_APIKey" -#DOMAIN : "generalv2" -#SPARK_URL : "ws://spark-api.xf-yun.com/v2.1/chat" - -#### if Anthropic -#ANTHROPIC_API_KEY: "YOUR_API_KEY" - -#### if AZURE, check https://github.com/openai/openai-cookbook/blob/main/examples/azure/chat.ipynb -#OPENAI_API_TYPE: "azure" -#OPENAI_BASE_URL: "YOUR_AZURE_ENDPOINT" -#OPENAI_API_KEY: "YOUR_AZURE_API_KEY" -#OPENAI_API_VERSION: "YOUR_AZURE_API_VERSION" -#DEPLOYMENT_NAME: "YOUR_DEPLOYMENT_NAME" - -#### if zhipuai from `https://open.bigmodel.cn`. You can set here or export API_KEY="YOUR_API_KEY" -# ZHIPUAI_API_KEY: "YOUR_API_KEY" -# ZHIPUAI_API_MODEL: "glm-4" - -#### if Google Gemini from `https://ai.google.dev/` and API_KEY from `https://makersuite.google.com/app/apikey`. -#### You can set here or export GOOGLE_API_KEY="YOUR_API_KEY" -# GEMINI_API_KEY: "YOUR_API_KEY" - -#### if use self-host open llm model with openai-compatible interface -#OPEN_LLM_API_BASE: "http://127.0.0.1:8000/v1" -#OPEN_LLM_API_MODEL: "llama2-13b" -# -##### if use Fireworks api -#FIREWORKS_API_KEY: "YOUR_API_KEY" -#FIREWORKS_API_BASE: "https://api.fireworks.ai/inference/v1" -#FIREWORKS_API_MODEL: "YOUR_LLM_MODEL" # example, accounts/fireworks/models/llama-v2-13b-chat - -#### if use self-host open llm model by ollama -# OLLAMA_API_BASE: http://127.0.0.1:11434/api -# OLLAMA_API_MODEL: llama2 - -#### for Search - -## Supported values: serpapi/google/serper/ddg -#SEARCH_ENGINE: serpapi - -## Visit https://serpapi.com/ to get key. -#SERPAPI_API_KEY: "YOUR_API_KEY" - -## Visit https://console.cloud.google.com/apis/credentials to get key. -#GOOGLE_API_KEY: "YOUR_API_KEY" -## Visit https://programmablesearchengine.google.com/controlpanel/create to get id. -#GOOGLE_CSE_ID: "YOUR_CSE_ID" - -## Visit https://serper.dev/ to get key. -#SERPER_API_KEY: "YOUR_API_KEY" - -#### for web access - -## Supported values: playwright/selenium -#WEB_BROWSER_ENGINE: playwright - -## Supported values: chromium/firefox/webkit, visit https://playwright.dev/python/docs/api/class-browsertype -##PLAYWRIGHT_BROWSER_TYPE: chromium - -## Supported values: chrome/firefox/edge/ie, visit https://www.selenium.dev/documentation/webdriver/browsers/ -# SELENIUM_BROWSER_TYPE: chrome - -#### for TTS - -#AZURE_TTS_SUBSCRIPTION_KEY: "YOUR_API_KEY" -#AZURE_TTS_REGION: "eastus" - -#### for Stable Diffusion -## Use SD service, based on https://github.com/AUTOMATIC1111/stable-diffusion-webui -#SD_URL: "YOUR_SD_URL" -#SD_T2I_API: "/sdapi/v1/txt2img" - -#### for Execution -#LONG_TERM_MEMORY: false - -#### for Mermaid CLI -## If you installed mmdc (Mermaid CLI) only for metagpt then enable the following configuration. -#PUPPETEER_CONFIG: "./config/puppeteer-config.json" -#MMDC: "./node_modules/.bin/mmdc" - - -### for calc_usage -# CALC_USAGE: false - -### for Research -# MODEL_FOR_RESEARCHER_SUMMARY: gpt-3.5-turbo -# MODEL_FOR_RESEARCHER_REPORT: gpt-3.5-turbo-16k - -### choose the engine for mermaid conversion, -# default is nodejs, you can change it to playwright,pyppeteer or ink -# MERMAID_ENGINE: nodejs - -### browser path for pyppeteer engine, support Chrome, Chromium,MS Edge -#PYPPETEER_EXECUTABLE_PATH: "/usr/bin/google-chrome-stable" - -### for repair non-openai LLM's output when parse json-text if PROMPT_FORMAT=json -### due to non-openai LLM's output will not always follow the instruction, so here activate a post-process -### repair operation on the content extracted from LLM's raw output. Warning, it improves the result but not fix all cases. -# REPAIR_LLM_OUTPUT: false - -# PROMPT_FORMAT: json #json or markdown - -### Agent configurations -# RAISE_NOT_CONFIG_ERROR: true # "true" if the LLM key is not configured, throw a NotConfiguredException, else "false". -# WORKSPACE_PATH_WITH_UID: false # "true" if using `{workspace}/{uid}` as the workspace path; "false" use `{workspace}`. - -### Meta Models -#METAGPT_TEXT_TO_IMAGE_MODEL: MODEL_URL - -### S3 config -#S3_ACCESS_KEY: "YOUR_S3_ACCESS_KEY" -#S3_SECRET_KEY: "YOUR_S3_SECRET_KEY" -#S3_ENDPOINT_URL: "YOUR_S3_ENDPOINT_URL" -#S3_SECURE: true # true/false -#S3_BUCKET: "YOUR_S3_BUCKET" - -### Redis config -#REDIS_HOST: "YOUR_REDIS_HOST" -#REDIS_PORT: "YOUR_REDIS_PORT" -#REDIS_PASSWORD: "YOUR_REDIS_PASSWORD" -#REDIS_DB: "YOUR_REDIS_DB_INDEX, str, 0-based" - -# DISABLE_LLM_PROVIDER_CHECK: false diff --git a/config/config2.yaml b/config/config2.yaml new file mode 100644 index 000000000..2c4ca636f --- /dev/null +++ b/config/config2.yaml @@ -0,0 +1,3 @@ +llm: + api_key: "YOUR_API_KEY" + model: "gpt-4-turbo-preview" # or gpt-3.5-turbo-1106 / gpt-4-1106-preview \ No newline at end of file diff --git a/config/config2.yaml.example b/config/config2.yaml.example new file mode 100644 index 000000000..8f4a33fc1 --- /dev/null +++ b/config/config2.yaml.example @@ -0,0 +1,45 @@ +llm: + api_type: "openai" + base_url: "YOUR_BASE_URL" + api_key: "YOUR_API_KEY" + model: "gpt-4-turbo-preview" # or gpt-3.5-turbo-1106 / gpt-4-1106-preview + +proxy: "YOUR_PROXY" + +search: + api_type: "google" + api_key: "YOUR_API_KEY" + cse_id: "YOUR_CSE_ID" + +browser: + engine: "playwright" # playwright/selenium + browser_type: "chromium" # playwright: chromium/firefox/webkit; selenium: chrome/firefox/edge/ie + +mermaid: + engine: "pyppeteer" + path: "/Applications/Google Chrome.app" + +redis: + host: "YOUR_HOST" + port: 32582 + password: "YOUR_PASSWORD" + db: "0" + +s3: + access_key: "YOUR_ACCESS_KEY" + secret_key: "YOUR_SECRET_KEY" + endpoint: "YOUR_ENDPOINT" + secure: false + bucket: "test" + + +azure_tts_subscription_key: "YOUR_SUBSCRIPTION_KEY" +azure_tts_region: "eastus" + +iflytek_api_id: "YOUR_APP_ID" +iflytek_api_key: "YOUR_API_KEY" +iflytek_api_secret: "YOUR_API_SECRET" + +metagpt_tti_url: "YOUR_MODEL_URL" + +repair_llm_output: true diff --git a/docs/.well-known/metagpt_oas3_api.yaml b/docs/.well-known/metagpt_oas3_api.yaml index 0a702e8b6..1f370b62d 100644 --- a/docs/.well-known/metagpt_oas3_api.yaml +++ b/docs/.well-known/metagpt_oas3_api.yaml @@ -14,16 +14,16 @@ paths: /tts/azsure: x-prerequisite: configurations: - AZURE_TTS_SUBSCRIPTION_KEY: + azure_tts_subscription_key: type: string description: "For more details, check out: [Azure Text-to_Speech](https://learn.microsoft.com/en-us/azure/ai-services/speech-service/language-support?tabs=tts)" - AZURE_TTS_REGION: + azure_tts_region: type: string description: "For more details, check out: [Azure Text-to_Speech](https://learn.microsoft.com/en-us/azure/ai-services/speech-service/language-support?tabs=tts)" required: allOf: - - AZURE_TTS_SUBSCRIPTION_KEY - - AZURE_TTS_REGION + - azure_tts_subscription_key + - azure_tts_region post: summary: "Convert Text to Base64-encoded .wav File Stream" description: "For more details, check out: [Azure Text-to_Speech](https://learn.microsoft.com/en-us/azure/ai-services/speech-service/language-support?tabs=tts)" @@ -94,9 +94,9 @@ paths: description: "WebAPI argument, see: `https://console.xfyun.cn/services/tts`" required: allOf: - - IFLYTEK_APP_ID - - IFLYTEK_API_KEY - - IFLYTEK_API_SECRET + - iflytek_app_id + - iflytek_api_key + - iflytek_api_secret post: summary: "Convert Text to Base64-encoded .mp3 File Stream" description: "For more details, check out: [iFlyTek](https://console.xfyun.cn/services/tts)" @@ -242,12 +242,12 @@ paths: /txt2image/metagpt: x-prerequisite: configurations: - METAGPT_TEXT_TO_IMAGE_MODEL_URL: + metagpt_tti_url: type: string description: "Model url." required: allOf: - - METAGPT_TEXT_TO_IMAGE_MODEL_URL + - metagpt_tti_url post: summary: "Text to Image" description: "Generate an image from the provided text using the MetaGPT Text-to-Image API." diff --git a/docs/.well-known/skills.yaml b/docs/.well-known/skills.yaml index c19a9501e..30c215445 100644 --- a/docs/.well-known/skills.yaml +++ b/docs/.well-known/skills.yaml @@ -14,10 +14,10 @@ entities: id: text_to_speech.text_to_speech x-prerequisite: configurations: - AZURE_TTS_SUBSCRIPTION_KEY: + azure_tts_subscription_key: type: string description: "For more details, check out: [Azure Text-to_Speech](https://learn.microsoft.com/en-us/azure/ai-services/speech-service/language-support?tabs=tts)" - AZURE_TTS_REGION: + azure_tts_region: type: string description: "For more details, check out: [Azure Text-to_Speech](https://learn.microsoft.com/en-us/azure/ai-services/speech-service/language-support?tabs=tts)" IFLYTEK_APP_ID: @@ -32,12 +32,12 @@ entities: required: oneOf: - allOf: - - AZURE_TTS_SUBSCRIPTION_KEY - - AZURE_TTS_REGION + - azure_tts_subscription_key + - azure_tts_region - allOf: - - IFLYTEK_APP_ID - - IFLYTEK_API_KEY - - IFLYTEK_API_SECRET + - iflytek_app_id + - iflytek_api_key + - iflytek_api_secret parameters: text: description: 'The text used for voice conversion.' @@ -103,13 +103,13 @@ entities: OPENAI_API_KEY: type: string description: "OpenAI API key, For more details, checkout: `https://platform.openai.com/account/api-keys`" - METAGPT_TEXT_TO_IMAGE_MODEL_URL: + metagpt_tti_url: type: string description: "Model url." required: oneOf: - OPENAI_API_KEY - - METAGPT_TEXT_TO_IMAGE_MODEL_URL + - metagpt_tti_url parameters: text: description: 'The text used for image conversion.' diff --git a/docs/FAQ-EN.md b/docs/FAQ-EN.md index d4a9f6097..d3caa244e 100644 --- a/docs/FAQ-EN.md +++ b/docs/FAQ-EN.md @@ -1,183 +1,93 @@ Our vision is to [extend human life](https://github.com/geekan/HowToLiveLonger) and [reduce working hours](https://github.com/geekan/MetaGPT/). -1. ### Convenient Link for Sharing this Document: +### Convenient Link for Sharing this Document: ``` -- MetaGPT-Index/FAQ https://deepwisdom.feishu.cn/wiki/MsGnwQBjiif9c3koSJNcYaoSnu4 +- MetaGPT-Index/FAQ-EN https://github.com/geekan/MetaGPT/blob/main/docs/FAQ-EN.md +- MetaGPT-Index/FAQ-CN https://deepwisdom.feishu.cn/wiki/MsGnwQBjiif9c3koSJNcYaoSnu4 ``` -2. ### Link - - +### Link 1. Code:https://github.com/geekan/MetaGPT - -1. Roadmap:https://github.com/geekan/MetaGPT/blob/main/docs/ROADMAP.md - -1. EN - - 1. Demo Video: [MetaGPT: Multi-Agent AI Programming Framework](https://www.youtube.com/watch?v=8RNzxZBTW8M) +2. Roadmap:https://github.com/geekan/MetaGPT/blob/main/docs/ROADMAP.md +3. EN + 1. Demo Video: [MetaGPT: Multi-Agent AI Programming Framework](https://www.youtube.com/watch?v=8RNzxZBTW8M) 2. Tutorial: [MetaGPT: Deploy POWERFUL Autonomous Ai Agents BETTER Than SUPERAGI!](https://www.youtube.com/watch?v=q16Gi9pTG_M&t=659s) 3. Author's thoughts video(EN): [MetaGPT Matthew Berman](https://youtu.be/uT75J_KG_aY?si=EgbfQNAwD8F5Y1Ak) +4. CN + 1. Demo Video: [MetaGPT:一行代码搭建你的虚拟公司_哔哩哔哩_bilibili](https://www.bilibili.com/video/BV1NP411C7GW/?spm_id_from=333.999.0.0&vd_source=735773c218b47da1b4bd1b98a33c5c77) + 1. Tutorial: [一个提示词写游戏 Flappy bird, 比AutoGPT强10倍的MetaGPT,最接近AGI的AI项目](https://youtu.be/Bp95b8yIH5c) + 2. Author's thoughts video(CN): [MetaGPT作者深度解析直播回放_哔哩哔哩_bilibili](https://www.bilibili.com/video/BV1Ru411V7XL/?spm_id_from=333.337.search-card.all.click) -1. CN - - 1. Demo Video: [MetaGPT:一行代码搭建你的虚拟公司_哔哩哔哩_bilibili](https://www.bilibili.com/video/BV1NP411C7GW/?spm_id_from=333.999.0.0&vd_source=735773c218b47da1b4bd1b98a33c5c77) - 1. Tutorial: [一个提示词写游戏 Flappy bird, 比AutoGPT强10倍的MetaGPT,最接近AGI的AI项目](https://youtu.be/Bp95b8yIH5c) - 2. Author's thoughts video(CN): [MetaGPT作者深度解析直播回放_哔哩哔哩_bilibili](https://www.bilibili.com/video/BV1Ru411V7XL/?spm_id_from=333.337.search-card.all.click) - - - -3. ### How to become a contributor? - - +### How to become a contributor? 1. Choose a task from the Roadmap (or you can propose one). By submitting a PR, you can become a contributor and join the dev team. -1. Current contributors come from backgrounds including ByteDance AI Lab/DingDong/Didi/Xiaohongshu, Tencent/Baidu/MSRA/TikTok/BloomGPT Infra/Bilibili/CUHK/HKUST/CMU/UCB - - +2. Current contributors come from backgrounds including ByteDance AI Lab/DingDong/Didi/Xiaohongshu, Tencent/Baidu/MSRA/TikTok/BloomGPT Infra/Bilibili/CUHK/HKUST/CMU/UCB -4. ### Chief Evangelist (Monthly Rotation) +### Chief Evangelist (Monthly Rotation) MetaGPT Community - The position of Chief Evangelist rotates on a monthly basis. The primary responsibilities include: 1. Maintaining community FAQ documents, announcements, and Github resources/READMEs. -1. Responding to, answering, and distributing community questions within an average of 30 minutes, including on platforms like Github Issues, Discord and WeChat. -1. Upholding a community atmosphere that is enthusiastic, genuine, and friendly. -1. Encouraging everyone to become contributors and participate in projects that are closely related to achieving AGI (Artificial General Intelligence). -1. (Optional) Organizing small-scale events, such as hackathons. +2. Responding to, answering, and distributing community questions within an average of 30 minutes, including on platforms like Github Issues, Discord and WeChat. +3. Upholding a community atmosphere that is enthusiastic, genuine, and friendly. +4. Encouraging everyone to become contributors and participate in projects that are closely related to achieving AGI (Artificial General Intelligence). +5. (Optional) Organizing small-scale events, such as hackathons. - - -5. ### FAQ - - - -1. Experience with the generated repo code: - - 1. https://github.com/geekan/MetaGPT/releases/tag/v0.1.0 +### FAQ 1. Code truncation/ Parsing failure: - - 1. Check if it's due to exceeding length. Consider using the gpt-3.5-turbo-16k or other long token versions. - -1. Success rate: - - 1. There hasn't been a quantitative analysis yet, but the success rate of code generated by GPT-4 is significantly higher than that of gpt-3.5-turbo. - -1. Support for incremental, differential updates (if you wish to continue a half-done task): - - 1. Several prerequisite tasks are listed on the ROADMAP. - -1. Can existing code be loaded? - - 1. It's not on the ROADMAP yet, but there are plans in place. It just requires some time. - -1. Support for multiple programming languages and natural languages? - - 1. It's listed on ROADMAP. - -1. Want to join the contributor team? How to proceed? - + 1. Check if it's due to exceeding length. Consider using the gpt-4-turbo-preview or other long token versions. +2. Success rate: + 1. There hasn't been a quantitative analysis yet, but the success rate of code generated by gpt-4-turbo-preview is significantly higher than that of gpt-3.5-turbo. +3. Support for incremental, differential updates (if you wish to continue a half-done task): + 1. There is now an experimental version. Specify `--inc --project-path ""` or `--inc --project-name ""` on the command line and enter the corresponding requirements to try it. +4. Can existing code be loaded? + 1. We are doing this, but it is very difficult, especially when the project is large, it is very difficult to achieve a high success rate. +5. Support for multiple programming languages and natural languages? + 1. It is now supported, but it is still in experimental version +6. Want to join the contributor team? How to proceed? 1. Merging a PR will get you into the contributor's team. The main ongoing tasks are all listed on the ROADMAP. - -1. PRD stuck / unable to access/ connection interrupted - - 1. The official OPENAI_BASE_URL address is `https://api.openai.com/v1` - 1. If the official OPENAI_BASE_URL address is inaccessible in your environment (this can be verified with curl), it's recommended to configure using the reverse proxy OPENAI_BASE_URL provided by libraries such as openai-forward. For instance, `OPENAI_BASE_URL: "``https://api.openai-forward.com/v1``"` - 1. If the official OPENAI_BASE_URL address is inaccessible in your environment (again, verifiable via curl), another option is to configure the OPENAI_PROXY parameter. This way, you can access the official OPENAI_BASE_URL via a local proxy. If you don't need to access via a proxy, please do not enable this configuration; if accessing through a proxy is required, modify it to the correct proxy address. Note that when OPENAI_PROXY is enabled, don't set OPENAI_BASE_URL. - 1. Note: OpenAI's default API design ends with a v1. An example of the correct configuration is: `OPENAI_BASE_URL: "``https://api.openai.com/v1``"` - -1. Absolutely! How can I assist you today? - +7. PRD stuck / unable to access/ connection interrupted + 1. The official openai base_url address is `https://api.openai.com/v1` + 2. If the official openai base_url address is inaccessible in your environment (this can be verified with curl), it's recommended to configure using base_url to other "reverse-proxy" provider such as openai-forward. For instance, `openai base_url: "``https://api.openai-forward.com/v1``"` + 3. If the official openai base_url address is inaccessible in your environment (again, verifiable via curl), another option is to configure the llm.proxy in the `config2.yaml`. This way, you can access the official openai base_url via a local proxy. If you don't need to access via a proxy, please do not enable this configuration; if accessing through a proxy is required, modify it to the correct proxy address. + 4. Note: OpenAI's default API design ends with a v1. An example of the correct configuration is: `base_url: "https://api.openai.com/v1" +8. Get reply: "Absolutely! How can I assist you today?" 1. Did you use Chi or a similar service? These services are prone to errors, and it seems that the error rate is higher when consuming 3.5k-4k tokens in GPT-4 - -1. What does Max token mean? - +9. What does Max token mean? 1. It's a configuration for OpenAI's maximum response length. If the response exceeds the max token, it will be truncated. - -1. How to change the investment amount? - +10. How to change the investment amount? 1. You can view all commands by typing `metagpt --help` - -1. Which version of Python is more stable? - +11. Which version of Python is more stable? 1. python3.9 / python3.10 - -1. Can't use GPT-4, getting the error "The model gpt-4 does not exist." - +12. Can't use GPT-4, getting the error "The model gpt-4 does not exist." 1. OpenAI's official requirement: You can use GPT-4 only after spending $1 on OpenAI. 1. Tip: Run some data with gpt-3.5-turbo (consume the free quota and $1), and then you should be able to use gpt-4. - -1. Can games whose code has never been seen before be written? - +13. Can games whose code has never been seen before be written? 1. Refer to the README. The recommendation system of Toutiao is one of the most complex systems in the world currently. Although it's not on GitHub, many discussions about it exist online. If it can visualize these, it suggests it can also summarize these discussions and convert them into code. The prompt would be something like "write a recommendation system similar to Toutiao". Note: this was approached in earlier versions of the software. The SOP of those versions was different; the current one adopts Elon Musk's five-step work method, emphasizing trimming down requirements as much as possible. - -1. Under what circumstances would there typically be errors? - +14. Under what circumstances would there typically be errors? 1. More than 500 lines of code: some function implementations may be left blank. - 1. When using a database, it often gets the implementation wrong — since the SQL database initialization process is usually not in the code. - 1. With more lines of code, there's a higher chance of false impressions, leading to calls to non-existent APIs. - -1. Instructions for using SD Skills/UI Role: - - 1. Currently, there is a test script located in /tests/metagpt/roles. The file ui_role provides the corresponding code implementation. For testing, you can refer to the test_ui in the same directory. - - 1. The UI role takes over from the product manager role, extending the output from the 【UI Design draft】 provided by the product manager role. The UI role has implemented the UIDesign Action. Within the run of UIDesign, it processes the respective context, and based on the set template, outputs the UI. The output from the UI role includes: - - 1. UI Design Description: Describes the content to be designed and the design objectives. - 1. Selected Elements: Describes the elements in the design that need to be illustrated. - 1. HTML Layout: Outputs the HTML code for the page. - 1. CSS Styles (styles.css): Outputs the CSS code for the page. - - 1. Currently, the SD skill is a tool invoked by UIDesign. It instantiates the SDEngine, with specific code found in metagpt/tools/sd_engine. - - 1. Configuration instructions for SD Skills: The SD interface is currently deployed based on *https://github.com/AUTOMATIC1111/stable-diffusion-webui* **For environmental configurations and model downloads, please refer to the aforementioned GitHub repository. To initiate the SD service that supports API calls, run the command specified in cmd with the parameter nowebui, i.e., - - 1. > python3 webui.py --enable-insecure-extension-access --port xxx --no-gradio-queue --nowebui - 1.     Once it runs without errors, the interface will be accessible after approximately 1 minute when the model finishes loading. - 1. Configure SD_URL and SD_T2I_API in the config.yaml/key.yaml files. - 1. ![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/065295a67b0b4feea665d1372722d49d~tplv-k3u1fbpfcp-zoom-1.image) - 1.     SD_URL is the deployed server/machine IP, and Port is the specified port above, defaulting to 7860. - 1. > SD_URL: IP:Port - -1. An error occurred during installation: "Another program is using this file...egg". - + 2. When using a database, it often gets the implementation wrong — since the SQL database initialization process is usually not in the code. + 3. With more lines of code, there's a higher chance of false impressions, leading to calls to non-existent APIs. +15. An error occurred during installation: "Another program is using this file...egg". 1. Delete the file and try again. - 1. Or manually execute`pip install -r requirements.txt` - -1. The origin of the name MetaGPT? - + 2. Or manually execute`pip install -r requirements.txt` +16. The origin of the name MetaGPT? 1. The name was derived after iterating with GPT-4 over a dozen rounds. GPT-4 scored and suggested it. - -1. Is there a more step-by-step installation tutorial? - - 1. Youtube(CN):[一个提示词写游戏 Flappy bird, 比AutoGPT强10倍的MetaGPT,最接近AGI的AI项目=一个软件公司产品经理+程序员](https://youtu.be/Bp95b8yIH5c) - 1. Youtube(EN)https://www.youtube.com/watch?v=q16Gi9pTG_M&t=659s - 2. video(EN): [MetaGPT Matthew Berman](https://youtu.be/uT75J_KG_aY?si=EgbfQNAwD8F5Y1Ak) - -1. openai.error.RateLimitError: You exceeded your current quota, please check your plan and billing details - +17. openai.error.RateLimitError: You exceeded your current quota, please check your plan and billing details 1. If you haven't exhausted your free quota, set RPM to 3 or lower in the settings. - 1. If your free quota is used up, consider adding funds to your account. - -1. What does "borg" mean in n_borg? - + 2. If your free quota is used up, consider adding funds to your account. +18. What does "borg" mean in n_borg? 1. [Wikipedia borg meaning ](https://en.wikipedia.org/wiki/Borg) - 1. The Borg civilization operates based on a hive or collective mentality, known as "the Collective." Every Borg individual is connected to the collective via a sophisticated subspace network, ensuring continuous oversight and guidance for every member. This collective consciousness allows them to not only "share the same thoughts" but also to adapt swiftly to new strategies. While individual members of the collective rarely communicate, the collective "voice" sometimes transmits aboard ships. - -1. How to use the Claude API? - + 2. The Borg civilization operates based on a hive or collective mentality, known as "the Collective." Every Borg individual is connected to the collective via a sophisticated subspace network, ensuring continuous oversight and guidance for every member. This collective consciousness allows them to not only "share the same thoughts" but also to adapt swiftly to new strategies. While individual members of the collective rarely communicate, the collective "voice" sometimes transmits aboard ships. +19. How to use the Claude API? 1. The full implementation of the Claude API is not provided in the current code. 1. You can use the Claude API through third-party API conversion projects like: https://github.com/jtsang4/claude-to-chatgpt - -1. Is Llama2 supported? - +20. Is Llama2 supported? 1. On the day Llama2 was released, some of the community members began experiments and found that output can be generated based on MetaGPT's structure. However, Llama2's context is too short to generate a complete project. Before regularly using Llama2, it's necessary to expand the context window to at least 8k. If anyone has good recommendations for expansion models or methods, please leave a comment. - -1. `mermaid-cli getElementsByTagName SyntaxError: Unexpected token '.'` - +21. `mermaid-cli getElementsByTagName SyntaxError: Unexpected token '.'` 1. Upgrade node to version 14.x or above: - 1. `npm install -g n` - 1. `n stable` to install the stable version of node(v18.x) + 2. `n stable` to install the stable version of node(v18.x) diff --git a/docs/README_CN.md b/docs/README_CN.md index 2855b5500..7a0db4974 100644 --- a/docs/README_CN.md +++ b/docs/README_CN.md @@ -35,50 +35,45 @@ ## 安装 ### Pip安装 +> 确保您的系统已安装 Python 3.9 或更高版本。您可以使用以下命令来检查:`python --version`。 +> 您可以这样使用 conda:`conda create -n metagpt python=3.9 && conda activate metagpt` + ```bash -# 第 1 步:确保您的系统上安装了 Python 3.9+。您可以使用以下命令进行检查: -# 可以使用conda来初始化新的python环境 -# conda create -n metagpt python=3.9 -# conda activate metagpt -python3 --version - -# 第 2 步:克隆最新仓库到您的本地机器,并进行安装。 -git clone https://github.com/geekan/MetaGPT.git -cd MetaGPT -pip3 install -e. # 或者 pip3 install metagpt # 安装稳定版本 - -# 第 3 步:执行metagpt -# 拷贝config.yaml为key.yaml,并设置你自己的OPENAI_API_KEY -metagpt "Write a cli snake game" - -# 第 4 步【可选的】:如果你想在执行过程中保存像象限图、系统设计、序列流程等图表这些产物,可以在第3步前执行该步骤。默认的,框架做了兼容,在不执行该步的情况下,也可以完整跑完整个流程。 -# 如果执行,确保您的系统上安装了 NPM。并使用npm安装mermaid-js -npm --version -sudo npm install -g @mermaid-js/mermaid-cli +pip install metagpt +metagpt --init-config # 创建 ~/.metagpt/config2.yaml,根据您的需求修改它 +metagpt "创建一个 2048 游戏" # 这将在 ./workspace 创建一个仓库 +``` + +或者您可以将其作为库使用 + +```python +from metagpt.software_company import generate_repo, ProjectRepo +repo: ProjectRepo = generate_repo("创建一个 2048 游戏") # 或 ProjectRepo("<路径>") +print(repo) # 它将打印出仓库结构及其文件 ``` -详细的安装请安装 [cli_install](https://docs.deepwisdom.ai/guide/get_started/installation.html#install-stable-version) +详细的安装请参考 [cli_install](https://docs.deepwisdom.ai/guide/get_started/installation.html#install-stable-version) ### Docker安装 > 注意:在Windows中,你需要将 "/opt/metagpt" 替换为Docker具有创建权限的目录,比如"D:\Users\x\metagpt" ```bash -# 步骤1: 下载metagpt官方镜像并准备好config.yaml +# 步骤1: 下载metagpt官方镜像并准备好config2.yaml docker pull metagpt/metagpt:latest mkdir -p /opt/metagpt/{config,workspace} -docker run --rm metagpt/metagpt:latest cat /app/metagpt/config/config.yaml > /opt/metagpt/config/key.yaml -vim /opt/metagpt/config/key.yaml # 修改配置文件 +docker run --rm metagpt/metagpt:latest cat /app/metagpt/config/config2.yaml > /opt/metagpt/config/config2.yaml +vim /opt/metagpt/config/config2.yaml # 修改配置文件 # 步骤2: 使用容器运行metagpt演示 docker run --rm \ --privileged \ - -v /opt/metagpt/config/key.yaml:/app/metagpt/config/key.yaml \ + -v /opt/metagpt/config/config2.yaml:/app/metagpt/config/config2.yaml \ -v /opt/metagpt/workspace:/app/metagpt/workspace \ metagpt/metagpt:latest \ metagpt "Write a cli snake game" ``` -详细的安装请安装 [docker_install](https://docs.deepwisdom.ai/main/zh/guide/get_started/installation.html#%E4%BD%BF%E7%94%A8docker%E5%AE%89%E8%A3%85) +详细的安装请参考 [docker_install](https://docs.deepwisdom.ai/main/zh/guide/get_started/installation.html#%E4%BD%BF%E7%94%A8docker%E5%AE%89%E8%A3%85) ### 快速开始的演示视频 - 在 [MetaGPT Huggingface Space](https://huggingface.co/spaces/deepwisdom/MetaGPT) 上进行体验 diff --git a/docs/README_JA.md b/docs/README_JA.md index 8b2bf1fae..c6b99461c 100644 --- a/docs/README_JA.md +++ b/docs/README_JA.md @@ -57,24 +57,21 @@ https://github.com/geekan/MetaGPT/assets/34952977/34345016-5d13-489d-b9f9-b82ace - [Matthew Berman: How To Install MetaGPT - Build A Startup With One Prompt!!](https://youtu.be/uT75J_KG_aY) ### 伝統的なインストール +> Python 3.9 以上がシステムにインストールされていることを確認してください。これは `python --version` を使ってチェックできます。 +> 以下のようにcondaを使うことができます:`conda create -n metagpt python=3.9 && conda activate metagpt` ```bash -# ステップ 1: Python 3.9+ がシステムにインストールされていることを確認してください。これを確認するには: -python3 --version - -# ステップ 2: リポジトリをローカルマシンにクローンし、インストールする。 -git clone https://github.com/geekan/MetaGPT.git -cd MetaGPT -pip install -e. +pip install metagpt +metagpt --init-config # ~/.metagpt/config2.yaml を作成し、自分の設定に合わせて変更してください +metagpt "2048ゲームを作成する" # これにより ./workspace にリポジトリが作成されます +``` -# ステップ 3: metagpt を実行する -# config.yaml を key.yaml にコピーし、独自の OPENAI_API_KEY を設定します -metagpt "Write a cli snake game" +または、ライブラリとして使用することもできます -# ステップ 4 [オプション]: 実行中に PRD ファイルなどのアーティファクトを保存する場合は、ステップ 3 の前にこのステップを実行できます。デフォルトでは、フレームワークには互換性があり、この手順を実行しなくてもプロセス全体を完了できます。 -# NPM がシステムにインストールされていることを確認してください。次に mermaid-js をインストールします。(お使いのコンピューターに npm がない場合は、Node.js 公式サイトで Node.js https://nodejs.org/ をインストールしてください。) -npm --version -sudo npm install -g @mermaid-js/mermaid-cli +```python +from metagpt.software_company import generate_repo, ProjectRepo +repo: ProjectRepo = generate_repo("2048ゲームを作成する") # または ProjectRepo("<パス>") +print(repo) # リポジトリの構造とファイルを出力します ``` **注:** @@ -91,8 +88,8 @@ Chromium のダウンロードをスキップすることができます。 - config.yml に mmdc のコンフィグを記述するのを忘れないこと ```yml - PUPPETEER_CONFIG: "./config/puppeteer-config.json" - MMDC: "./node_modules/.bin/mmdc" + puppeteer_config: "./config/puppeteer-config.json" + path: "./node_modules/.bin/mmdc" ``` - もし `pip install -e.` がエラー `[Errno 13] Permission denied: '/usr/local/lib/python3.11/dist-packages/test-easy-install-13129.write-test'` で失敗したら、代わりに `pip install -e. --user` を実行してみてください @@ -114,12 +111,13 @@ Chromium のダウンロードをスキップすることができます。 playwright install --with-deps chromium ``` - - **modify `config.yaml`** + - **modify `config2.yaml`** - config.yaml から MERMAID_ENGINE のコメントを外し、`playwright` に変更する + config2.yaml から mermaid.engine のコメントを外し、`playwright` に変更する ```yaml - MERMAID_ENGINE: playwright + mermaid: + engine: playwright ``` - pyppeteer @@ -143,21 +141,23 @@ Chromium のダウンロードをスキップすることができます。 pyppeteer-install ``` - - **`config.yaml` を修正** + - **`config2.yaml` を修正** - config.yaml から MERMAID_ENGINE のコメントを外し、`pyppeteer` に変更する + config2.yaml から mermaid.engine のコメントを外し、`pyppeteer` に変更する ```yaml - MERMAID_ENGINE: pyppeteer + mermaid: + engine: pyppeteer ``` - mermaid.ink - - **`config.yaml` を修正** + - **`config2.yaml` を修正** - config.yaml から MERMAID_ENGINE のコメントを外し、`ink` に変更する + config2.yaml から mermaid.engine のコメントを外し、`ink` に変更する ```yaml - MERMAID_ENGINE: ink + mermaid: + engine: ink ``` 注: この方法は pdf エクスポートに対応していません。 @@ -166,16 +166,16 @@ Chromium のダウンロードをスキップすることができます。 > Windowsでは、"/opt/metagpt"をDockerが作成する権限を持つディレクトリに置き換える必要があります。例えば、"D:\Users\x\metagpt"などです。 ```bash -# ステップ 1: metagpt 公式イメージをダウンロードし、config.yaml を準備する +# ステップ 1: metagpt 公式イメージをダウンロードし、config2.yaml を準備する docker pull metagpt/metagpt:latest mkdir -p /opt/metagpt/{config,workspace} -docker run --rm metagpt/metagpt:latest cat /app/metagpt/config/config.yaml > /opt/metagpt/config/key.yaml -vim /opt/metagpt/config/key.yaml # 設定を変更する +docker run --rm metagpt/metagpt:latest cat /app/metagpt/config/config2.yaml > /opt/metagpt/config/config2.yaml +vim /opt/metagpt/config/config2.yaml # 設定を変更する # ステップ 2: コンテナで metagpt デモを実行する docker run --rm \ --privileged \ - -v /opt/metagpt/config/key.yaml:/app/metagpt/config/key.yaml \ + -v /opt/metagpt/config/config2.yaml:/app/metagpt/config/config2.yaml \ -v /opt/metagpt/workspace:/app/metagpt/workspace \ metagpt/metagpt:latest \ metagpt "Write a cli snake game" @@ -183,7 +183,7 @@ docker run --rm \ # コンテナを起動し、その中でコマンドを実行することもできます docker run --name metagpt -d \ --privileged \ - -v /opt/metagpt/config/key.yaml:/app/metagpt/config/key.yaml \ + -v /opt/metagpt/config/config2.yaml:/app/metagpt/config/config2.yaml \ -v /opt/metagpt/workspace:/app/metagpt/workspace \ metagpt/metagpt:latest @@ -194,7 +194,7 @@ $ metagpt "Write a cli snake game" コマンド `docker run ...` は以下のことを行います: - 特権モードで実行し、ブラウザの実行権限を得る -- ホスト設定ファイル `/opt/metagpt/config/key.yaml` をコンテナ `/app/metagpt/config/key.yaml` にマップします +- ホスト設定ファイル `/opt/metagpt/config/config2.yaml` をコンテナ `/app/metagpt/config/config2.yaml` にマップします - ホストディレクトリ `/opt/metagpt/workspace` をコンテナディレクトリ `/app/metagpt/workspace` にマップするs - デモコマンド `metagpt "Write a cli snake game"` を実行する @@ -208,19 +208,14 @@ cd MetaGPT && docker build -t metagpt:custom . ## 設定 -- `OPENAI_API_KEY` を `config/key.yaml / config/config.yaml / env` のいずれかで設定します。 -- 優先順位は: `config/key.yaml > config/config.yaml > env` の順です。 +- `api_key` を `~/.metagpt/config2.yaml / config/config2.yaml` のいずれかで設定します。 +- 優先順位は: `~/.metagpt/config2.yaml > config/config2.yaml > env` の順です。 ```bash # 設定ファイルをコピーし、必要な修正を加える。 -cp config/config.yaml config/key.yaml +cp config/config2.yaml ~/.metagpt/config2.yaml ``` -| 変数名 | config/key.yaml | env | -| --------------------------------------- | ----------------------------------------- | ----------------------------------------------- | -| OPENAI_API_KEY # 自分のキーに置き換える | OPENAI_API_KEY: "sk-..." | export OPENAI_API_KEY="sk-..." | -| OPENAI_BASE_URL # オプション | OPENAI_BASE_URL: "https:///v1" | export OPENAI_BASE_URL="https:///v1" | - ## チュートリアル: スタートアップの開始 ```shell diff --git a/docs/install/cli_install.md b/docs/install/cli_install.md index 80deda771..b79ad9cb7 100644 --- a/docs/install/cli_install.md +++ b/docs/install/cli_install.md @@ -9,17 +9,29 @@ ### Detail Installation ```bash -# Step 1: Ensure that NPM is installed on your system. Then install mermaid-js. (If you don't have npm in your computer, please go to the Node.js official website to install Node.js https://nodejs.org/ and then you will have npm tool in your computer.) -npm --version -sudo npm install -g @mermaid-js/mermaid-cli - -# Step 2: Ensure that Python 3.9+ is installed on your system. You can check this by using: +# Step 1: Ensure that Python 3.9+ is installed on your system. You can check this by using: +# You can use conda to initialize a new python env +# conda create -n metagpt python=3.9 +# conda activate metagpt python3 --version -# Step 3: Clone the repository to your local machine, and install it. +# Step 2: Clone the repository to your local machine for latest version, and install it. git clone https://github.com/geekan/MetaGPT.git cd MetaGPT -pip install -e. +pip3 install -e . # or pip3 install metagpt # for stable version + +# Step 3: setup your LLM key in the config2.yaml file +mkdir ~/.metagpt +cp config/config2.yaml ~/.metagpt/config2.yaml +vim ~/.metagpt/config2.yaml + +# Step 4: run metagpt cli +metagpt "Create a 2048 game in python" + +# Step 5 [Optional]: If you want to save the artifacts like diagrams such as quadrant chart, system designs, sequence flow in the workspace, you can execute the step before Step 3. By default, the framework is compatible, and the entire process can be run completely without executing this step. +# If executing, ensure that NPM is installed on your system. Then install mermaid-js. (If you don't have npm in your computer, please go to the Node.js official website to install Node.js https://nodejs.org/ and then you will have npm tool in your computer.) +npm --version +sudo npm install -g @mermaid-js/mermaid-cli ``` **Note:** @@ -33,11 +45,12 @@ pip install -e. npm install @mermaid-js/mermaid-cli ``` -- don't forget to the configuration for mmdc in config.yml +- don't forget to the configuration for mmdc path in config.yml - ```yml - PUPPETEER_CONFIG: "./config/puppeteer-config.json" - MMDC: "./node_modules/.bin/mmdc" + ```yaml + mermaid: + puppeteer_config: "./config/puppeteer-config.json" + path: "./node_modules/.bin/mmdc" ``` - if `pip install -e.` fails with error `[Errno 13] Permission denied: '/usr/local/lib/python3.11/dist-packages/test-easy-install-13129.write-test'`, try instead running `pip install -e. --user` @@ -59,12 +72,13 @@ pip install -e. playwright install --with-deps chromium ``` - - **modify `config.yaml`** + - **modify `config2.yaml`** - uncomment MERMAID_ENGINE from config.yaml and change it to `playwright` + change mermaid.engine to `playwright` ```yaml - MERMAID_ENGINE: playwright + mermaid: + engine: playwright ``` - pyppeteer @@ -88,22 +102,24 @@ pip install -e. pyppeteer-install ``` - - **modify `config.yaml`** + - **modify `config2.yaml`** - uncomment MERMAID_ENGINE from config.yaml and change it to `pyppeteer` + change mermaid.engine to `pyppeteer` ```yaml - MERMAID_ENGINE: pyppeteer + mermaid: + engine: pyppeteer ``` - mermaid.ink - - **modify `config.yaml`** - - uncomment MERMAID_ENGINE from config.yaml and change it to `ink` + - **modify `config2.yaml`** + + change mermaid.engine to `ink` ```yaml - MERMAID_ENGINE: ink + mermaid: + engine: ink ``` Note: this method does not support pdf export. - \ No newline at end of file + diff --git a/docs/install/cli_install_cn.md b/docs/install/cli_install_cn.md index b1da1b813..1ee18d9a6 100644 --- a/docs/install/cli_install_cn.md +++ b/docs/install/cli_install_cn.md @@ -10,17 +10,29 @@ ### 详细安装 ```bash -# 第 1 步:确保您的系统上安装了 NPM。并使用npm安装mermaid-js -npm --version -sudo npm install -g @mermaid-js/mermaid-cli - -# 第 2 步:确保您的系统上安装了 Python 3.9+。您可以使用以下命令进行检查: +# 步骤 1: 确保您的系统安装了 Python 3.9 或更高版本。您可以使用以下命令来检查: +# 您可以使用 conda 来初始化一个新的 Python 环境 +# conda create -n metagpt python=3.9 +# conda activate metagpt python3 --version -# 第 3 步:克隆仓库到您的本地机器,并进行安装。 +# 步骤 2: 克隆仓库到您的本地机器以获取最新版本,并安装它。 git clone https://github.com/geekan/MetaGPT.git cd MetaGPT -pip install -e. +pip3 install -e . # 或 pip3 install metagpt # 用于稳定版本 + +# 步骤 3: 在 config2.yaml 文件中设置您的 LLM 密钥 +mkdir ~/.metagpt +cp config/config2.yaml ~/.metagpt/config2.yaml +vim ~/.metagpt/config2.yaml + +# 步骤 4: 运行 metagpt 命令行界面 +metagpt "用 python 创建一个 2048 游戏" + +# 步骤 5 [可选]: 如果您想保存诸如象限图、系统设计、序列流等图表作为工作空间的工件,您可以在执行步骤 3 之前执行此步骤。默认情况下,该框架是兼容的,整个过程可以完全不执行此步骤而运行。 +# 如果执行此步骤,请确保您的系统上安装了 NPM。然后安装 mermaid-js。(如果您的计算机中没有 npm,请访问 Node.js 官方网站 https://nodejs.org/ 安装 Node.js,然后您将在计算机中拥有 npm 工具。) +npm --version +sudo npm install -g @mermaid-js/mermaid-cli ``` **注意:** @@ -33,11 +45,12 @@ pip install -e. npm install @mermaid-js/mermaid-cli ``` -- 不要忘记在config.yml中为mmdc配置配置, +- 不要忘记在config.yml中为mmdc配置 ```yml - PUPPETEER_CONFIG: "./config/puppeteer-config.json" - MMDC: "./node_modules/.bin/mmdc" + mermaid: + puppeteer_config: "./config/puppeteer-config.json" + path: "./node_modules/.bin/mmdc" ``` - 如果`pip install -e.`失败并显示错误`[Errno 13] Permission denied: '/usr/local/lib/python3.11/dist-packages/test-easy-install-13129.write-test'`,请尝试使用`pip install -e. --user`运行。 diff --git a/docs/install/docker_install.md b/docs/install/docker_install.md index 37125bdbe..2fe1b6abf 100644 --- a/docs/install/docker_install.md +++ b/docs/install/docker_install.md @@ -3,16 +3,16 @@ ### Use default MetaGPT image ```bash -# Step 1: Download metagpt official image and prepare config.yaml +# Step 1: Download metagpt official image and prepare config2.yaml docker pull metagpt/metagpt:latest mkdir -p /opt/metagpt/{config,workspace} -docker run --rm metagpt/metagpt:latest cat /app/metagpt/config/config.yaml > /opt/metagpt/config/key.yaml -vim /opt/metagpt/config/key.yaml # Change the config +docker run --rm metagpt/metagpt:latest cat /app/metagpt/config/config2.yaml > /opt/metagpt/config/config2.yaml +vim /opt/metagpt/config/config2.yaml # Change the config # Step 2: Run metagpt demo with container docker run --rm \ --privileged \ - -v /opt/metagpt/config/key.yaml:/app/metagpt/config/key.yaml \ + -v /opt/metagpt/config/config2.yaml:/app/metagpt/config/config2.yaml \ -v /opt/metagpt/workspace:/app/metagpt/workspace \ metagpt/metagpt:latest \ metagpt "Write a cli snake game" @@ -20,7 +20,7 @@ docker run --rm \ # You can also start a container and execute commands in it docker run --name metagpt -d \ --privileged \ - -v /opt/metagpt/config/key.yaml:/app/metagpt/config/key.yaml \ + -v /opt/metagpt/config/config2.yaml:/app/metagpt/config/config2.yaml \ -v /opt/metagpt/workspace:/app/metagpt/workspace \ metagpt/metagpt:latest @@ -31,7 +31,7 @@ $ metagpt "Write a cli snake game" The command `docker run ...` do the following things: - Run in privileged mode to have permission to run the browser -- Map host configure file `/opt/metagpt/config/key.yaml` to container `/app/metagpt/config/key.yaml` +- Map host configure file `/opt/metagpt/config/config2.yaml` to container `/app/metagpt/config/config2.yaml` - Map host directory `/opt/metagpt/workspace` to container `/app/metagpt/workspace` - Execute the demo command `metagpt "Write a cli snake game"` diff --git a/docs/install/docker_install_cn.md b/docs/install/docker_install_cn.md index f360b49ed..10204c1e0 100644 --- a/docs/install/docker_install_cn.md +++ b/docs/install/docker_install_cn.md @@ -3,16 +3,16 @@ ### 使用MetaGPT镜像 ```bash -# 步骤1: 下载metagpt官方镜像并准备好config.yaml +# 步骤1: 下载metagpt官方镜像并准备好config2.yaml docker pull metagpt/metagpt:latest mkdir -p /opt/metagpt/{config,workspace} -docker run --rm metagpt/metagpt:latest cat /app/metagpt/config/config.yaml > /opt/metagpt/config/key.yaml -vim /opt/metagpt/config/key.yaml # 修改配置文件 +docker run --rm metagpt/metagpt:latest cat /app/metagpt/config/config2.yaml > /opt/metagpt/config/config2.yaml +vim /opt/metagpt/config/config2.yaml # 修改配置文件 # 步骤2: 使用容器运行metagpt演示 docker run --rm \ --privileged \ - -v /opt/metagpt/config/key.yaml:/app/metagpt/config/key.yaml \ + -v /opt/metagpt/config/config2.yaml:/app/metagpt/config/config2.yaml \ -v /opt/metagpt/workspace:/app/metagpt/workspace \ metagpt/metagpt:latest \ metagpt "Write a cli snake game" @@ -20,7 +20,7 @@ docker run --rm \ # 您也可以启动一个容器并在其中执行命令 docker run --name metagpt -d \ --privileged \ - -v /opt/metagpt/config/key.yaml:/app/metagpt/config/key.yaml \ + -v /opt/metagpt/config/config2.yaml:/app/metagpt/config/config2.yaml \ -v /opt/metagpt/workspace:/app/metagpt/workspace \ metagpt/metagpt:latest @@ -31,7 +31,7 @@ $ metagpt "Write a cli snake game" `docker run ...`做了以下事情: - 以特权模式运行,有权限运行浏览器 -- 将主机文件 `/opt/metagpt/config/key.yaml` 映射到容器文件 `/app/metagpt/config/key.yaml` +- 将主机文件 `/opt/metagpt/config/config2.yaml` 映射到容器文件 `/app/metagpt/config/config2.yaml` - 将主机目录 `/opt/metagpt/workspace` 映射到容器目录 `/app/metagpt/workspace` - 执行示例命令 `metagpt "Write a cli snake game"` diff --git a/docs/tutorial/usage.md b/docs/tutorial/usage.md index a08d92a22..1128e98a5 100644 --- a/docs/tutorial/usage.md +++ b/docs/tutorial/usage.md @@ -2,19 +2,14 @@ ### Configuration -- Configure your `OPENAI_API_KEY` in any of `config/key.yaml / config/config.yaml / env` -- Priority order: `config/key.yaml > config/config.yaml > env` +- Configure your `api_key` in any of `~/.metagpt/config2.yaml / config/config2.yaml` +- Priority order: `~/.metagpt/config2.yaml > config/config2.yaml` ```bash # Copy the configuration file and make the necessary modifications. -cp config/config.yaml config/key.yaml +cp config/config2.yaml ~/.metagpt/config2.yaml ``` -| Variable Name | config/key.yaml | env | -| ------------------------------------------ | ----------------------------------------- | ----------------------------------------------- | -| OPENAI_API_KEY # Replace with your own key | OPENAI_API_KEY: "sk-..." | export OPENAI_API_KEY="sk-..." | -| OPENAI_BASE_URL # Optional | OPENAI_BASE_URL: "https:///v1" | export OPENAI_BASE_URL="https:///v1" | - ### Initiating a startup ```shell @@ -39,29 +34,28 @@ metagpt "Write a cli snake game based on pygame" ### Usage ``` -NAME - metagpt - We are a software startup comprised of AI. By investing in us, you are empowering a future filled with limitless possibilities. - -SYNOPSIS - metagpt IDEA - -DESCRIPTION - We are a software startup comprised of AI. By investing in us, you are empowering a future filled with limitless possibilities. - -POSITIONAL ARGUMENTS - IDEA - Type: str - Your innovative idea, such as "Creating a snake game." - -FLAGS - --investment=INVESTMENT - Type: float - Default: 3.0 - As an investor, you have the opportunity to contribute a certain dollar amount to this AI company. - --n_round=N_ROUND - Type: int - Default: 5 - -NOTES - You can also use flags syntax for POSITIONAL ARGUMENTS + Usage: metagpt [OPTIONS] [IDEA] + + Start a new project. + +╭─ Arguments ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ idea [IDEA] Your innovative idea, such as 'Create a 2048 game.' [default: None] │ +╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +╭─ Options ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ --investment FLOAT Dollar amount to invest in the AI company. [default: 3.0] │ +│ --n-round INTEGER Number of rounds for the simulation. [default: 5] │ +│ --code-review --no-code-review Whether to use code review. [default: code-review] │ +│ --run-tests --no-run-tests Whether to enable QA for adding & running tests. [default: no-run-tests] │ +│ --implement --no-implement Enable or disable code implementation. [default: implement] │ +│ --project-name TEXT Unique project name, such as 'game_2048'. │ +│ --inc --no-inc Incremental mode. Use it to coop with existing repo. [default: no-inc] │ +│ --project-path TEXT Specify the directory path of the old version project to fulfill the incremental requirements. │ +│ --reqa-file TEXT Specify the source file name for rewriting the quality assurance code. │ +│ --max-auto-summarize-code INTEGER The maximum number of times the 'SummarizeCode' action is automatically invoked, with -1 indicating unlimited. This parameter is used for debugging the │ +│ workflow. │ +│ [default: 0] │ +│ --recover-path TEXT recover the project from existing serialized storage [default: None] │ +│ --init-config --no-init-config Initialize the configuration file for MetaGPT. [default: no-init-config] │ +│ --help Show this message and exit. │ +╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ``` \ No newline at end of file diff --git a/docs/tutorial/usage_cn.md b/docs/tutorial/usage_cn.md index 76a5d6b1b..3b0c86279 100644 --- a/docs/tutorial/usage_cn.md +++ b/docs/tutorial/usage_cn.md @@ -2,19 +2,14 @@ ### 配置 -- 在 `config/key.yaml / config/config.yaml / env` 中配置您的 `OPENAI_API_KEY` -- 优先级顺序:`config/key.yaml > config/config.yaml > env` +- 在 `~/.metagpt/config2.yaml / config/config2.yaml` 中配置您的 `api_key` +- 优先级顺序:`~/.metagpt/config2.yaml > config/config2.yaml` ```bash # 复制配置文件并进行必要的修改 -cp config/config.yaml config/key.yaml +cp config/config2.yaml ~/.metagpt/config2.yaml ``` -| 变量名 | config/key.yaml | env | -| ----------------------------------- | ----------------------------------------- | ----------------------------------------------- | -| OPENAI_API_KEY # 用您自己的密钥替换 | OPENAI_API_KEY: "sk-..." | export OPENAI_API_KEY="sk-..." | -| OPENAI_BASE_URL # 可选 | OPENAI_BASE_URL: "https:///v1" | export OPENAI_BASE_URL="https:///v1" | - ### 示例:启动一个创业公司 ```shell @@ -35,29 +30,28 @@ metagpt "写一个基于pygame的命令行贪吃蛇" ### 使用 ``` -名称 - metagpt - 我们是一家AI软件创业公司。通过投资我们,您将赋能一个充满无限可能的未来。 - -概要 - metagpt IDEA - -描述 - 我们是一家AI软件创业公司。通过投资我们,您将赋能一个充满无限可能的未来。 - -位置参数 - IDEA - 类型: str - 您的创新想法,例如"写一个命令行贪吃蛇。" - -标志 - --investment=INVESTMENT - 类型: float - 默认值: 3.0 - 作为投资者,您有机会向这家AI公司投入一定的美元金额。 - --n_round=N_ROUND - 类型: int - 默认值: 5 - -备注 - 您也可以用`标志`的语法,来处理`位置参数` + Usage: metagpt [OPTIONS] [IDEA] + + Start a new project. + +╭─ Arguments ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ idea [IDEA] Your innovative idea, such as 'Create a 2048 game.' [default: None] │ +╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +╭─ Options ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ --investment FLOAT Dollar amount to invest in the AI company. [default: 3.0] │ +│ --n-round INTEGER Number of rounds for the simulation. [default: 5] │ +│ --code-review --no-code-review Whether to use code review. [default: code-review] │ +│ --run-tests --no-run-tests Whether to enable QA for adding & running tests. [default: no-run-tests] │ +│ --implement --no-implement Enable or disable code implementation. [default: implement] │ +│ --project-name TEXT Unique project name, such as 'game_2048'. │ +│ --inc --no-inc Incremental mode. Use it to coop with existing repo. [default: no-inc] │ +│ --project-path TEXT Specify the directory path of the old version project to fulfill the incremental requirements. │ +│ --reqa-file TEXT Specify the source file name for rewriting the quality assurance code. │ +│ --max-auto-summarize-code INTEGER The maximum number of times the 'SummarizeCode' action is automatically invoked, with -1 indicating unlimited. This parameter is used for debugging the │ +│ workflow. │ +│ [default: 0] │ +│ --recover-path TEXT recover the project from existing serialized storage [default: None] │ +│ --init-config --no-init-config Initialize the configuration file for MetaGPT. [default: no-init-config] │ +│ --help Show this message and exit. │ +╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ``` diff --git a/examples/agent_creator.py b/examples/agent_creator.py index 340dfafa4..bd58840ce 100644 --- a/examples/agent_creator.py +++ b/examples/agent_creator.py @@ -6,7 +6,7 @@ import re from metagpt.actions import Action -from metagpt.config import CONFIG +from metagpt.config2 import config from metagpt.const import METAGPT_ROOT from metagpt.logs import logger from metagpt.roles import Role @@ -48,8 +48,8 @@ def parse_code(rsp): pattern = r"```python(.*)```" match = re.search(pattern, rsp, re.DOTALL) code_text = match.group(1) if match else "" - CONFIG.workspace_path.mkdir(parents=True, exist_ok=True) - new_file = CONFIG.workspace_path / "agent_created_agent.py" + config.workspace.path.mkdir(parents=True, exist_ok=True) + new_file = config.workspace.path / "agent_created_agent.py" new_file.write_text(code_text) return code_text @@ -61,7 +61,7 @@ class AgentCreator(Role): def __init__(self, **kwargs): super().__init__(**kwargs) - self._init_actions([CreateAgent]) + self.set_actions([CreateAgent]) async def _act(self) -> Message: logger.info(f"{self._setting}: to do {self.rc.todo}({self.rc.todo.name})") diff --git a/examples/build_customized_agent.py b/examples/build_customized_agent.py index 6c3219efc..cfe264b47 100644 --- a/examples/build_customized_agent.py +++ b/examples/build_customized_agent.py @@ -57,7 +57,7 @@ class SimpleCoder(Role): def __init__(self, **kwargs): super().__init__(**kwargs) - self._init_actions([SimpleWriteCode]) + self.set_actions([SimpleWriteCode]) async def _act(self) -> Message: logger.info(f"{self._setting}: to do {self.rc.todo}({self.rc.todo.name})") @@ -76,7 +76,7 @@ class RunnableCoder(Role): def __init__(self, **kwargs): super().__init__(**kwargs) - self._init_actions([SimpleWriteCode, SimpleRunCode]) + self.set_actions([SimpleWriteCode, SimpleRunCode]) self._set_react_mode(react_mode=RoleReactMode.BY_ORDER.value) async def _act(self) -> Message: diff --git a/examples/build_customized_multi_agents.py b/examples/build_customized_multi_agents.py index 73278c08c..296323cea 100644 --- a/examples/build_customized_multi_agents.py +++ b/examples/build_customized_multi_agents.py @@ -46,7 +46,7 @@ class SimpleCoder(Role): def __init__(self, **kwargs): super().__init__(**kwargs) self._watch([UserRequirement]) - self._init_actions([SimpleWriteCode]) + self.set_actions([SimpleWriteCode]) class SimpleWriteTest(Action): @@ -75,7 +75,7 @@ class SimpleTester(Role): def __init__(self, **kwargs): super().__init__(**kwargs) - self._init_actions([SimpleWriteTest]) + self.set_actions([SimpleWriteTest]) # self._watch([SimpleWriteCode]) self._watch([SimpleWriteCode, SimpleWriteReview]) # feel free to try this too @@ -114,7 +114,7 @@ class SimpleReviewer(Role): def __init__(self, **kwargs): super().__init__(**kwargs) - self._init_actions([SimpleWriteReview]) + self.set_actions([SimpleWriteReview]) self._watch([SimpleWriteTest]) diff --git a/examples/crawl_webpage.py b/examples/crawl_webpage.py new file mode 100644 index 000000000..2db9e407b --- /dev/null +++ b/examples/crawl_webpage.py @@ -0,0 +1,22 @@ +# -*- encoding: utf-8 -*- +""" +@Date : 2024/01/24 15:11:27 +@Author : orange-crow +@File : crawl_webpage.py +""" + +from metagpt.roles.ci.code_interpreter import CodeInterpreter + + +async def main(): + prompt = """Get data from `paperlist` table in https://papercopilot.com/statistics/iclr-statistics/iclr-2024-statistics/, + and save it to a csv file. paper title must include `multiagent` or `large language model`. *notice: print key variables*""" + ci = CodeInterpreter(goal=prompt, use_tools=True) + + await ci.run(prompt) + + +if __name__ == "__main__": + import asyncio + + asyncio.run(main()) diff --git a/examples/dalle_gpt4v_agent.py b/examples/dalle_gpt4v_agent.py new file mode 100644 index 000000000..28215dba3 --- /dev/null +++ b/examples/dalle_gpt4v_agent.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : use gpt4v to improve prompt and draw image with dall-e-3 + +"""set `model: "gpt-4-vision-preview"` in `config2.yaml` first""" + +import asyncio + +from PIL import Image + +from metagpt.actions.action import Action +from metagpt.logs import logger +from metagpt.roles.role import Role +from metagpt.schema import Message +from metagpt.utils.common import encode_image + + +class GenAndImproveImageAction(Action): + save_image: bool = True + + async def generate_image(self, prompt: str) -> Image: + imgs = await self.llm.gen_image(model="dall-e-3", prompt=prompt) + return imgs[0] + + async def refine_prompt(self, old_prompt: str, image: Image) -> str: + msg = ( + f"You are a creative painter, with the given generated image and old prompt: {old_prompt}, " + f"please refine the prompt and generate new one. Just output the new prompt." + ) + b64_img = encode_image(image) + new_prompt = await self.llm.aask(msg=msg, images=[b64_img]) + return new_prompt + + async def evaluate_images(self, old_prompt: str, images: list[Image]) -> str: + msg = ( + "With the prompt and two generated image, to judge if the second one is better than the first one. " + "If so, just output True else output False" + ) + b64_imgs = [encode_image(img) for img in images] + res = await self.llm.aask(msg=msg, images=b64_imgs) + return res + + async def run(self, messages: list[Message]) -> str: + prompt = messages[-1].content + + old_img: Image = await self.generate_image(prompt) + new_prompt = await self.refine_prompt(old_prompt=prompt, image=old_img) + logger.info(f"original prompt: {prompt}") + logger.info(f"refined prompt: {new_prompt}") + new_img: Image = await self.generate_image(new_prompt) + if self.save_image: + old_img.save("./img_by-dall-e_old.png") + new_img.save("./img_by-dall-e_new.png") + res = await self.evaluate_images(old_prompt=prompt, images=[old_img, new_img]) + opinion = f"The second generated image is better than the first one: {res}" + logger.info(f"evaluate opinion: {opinion}") + return opinion + + +class Painter(Role): + name: str = "MaLiang" + profile: str = "Painter" + goal: str = "to generate fine painting" + + def __init__(self, **data): + super().__init__(**data) + + self.set_actions([GenAndImproveImageAction]) + + +async def main(): + role = Painter() + await role.run(with_message="a girl with flowers") + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/examples/debate.py b/examples/debate.py index eb0a09839..72ab8796d 100644 --- a/examples/debate.py +++ b/examples/debate.py @@ -49,7 +49,7 @@ class Debator(Role): def __init__(self, **data: Any): super().__init__(**data) - self._init_actions([SpeakAloud]) + self.set_actions([SpeakAloud]) self._watch([UserRequirement, SpeakAloud]) async def _observe(self) -> int: diff --git a/examples/debate_simple.py b/examples/debate_simple.py index aa95c5b85..869e02a0e 100644 --- a/examples/debate_simple.py +++ b/examples/debate_simple.py @@ -13,7 +13,9 @@ from metagpt.team import Team action1 = Action(name="AlexSay", instruction="Express your opinion with emotion and don't repeat it") +action1.llm.model = "gpt-4-1106-preview" action2 = Action(name="BobSay", instruction="Express your opinion with emotion and don't repeat it") +action2.llm.model = "gpt-3.5-turbo-1106" alex = Role(name="Alex", profile="Democratic candidate", goal="Win the election", actions=[action1], watch=[action2]) bob = Role(name="Bob", profile="Republican candidate", goal="Win the election", actions=[action2], watch=[action1]) env = Environment(desc="US election live broadcast") diff --git a/examples/example.faiss b/examples/example.faiss deleted file mode 100644 index 580946190..000000000 Binary files a/examples/example.faiss and /dev/null differ diff --git a/examples/example.pkl b/examples/example.pkl deleted file mode 100644 index f706fd803..000000000 Binary files a/examples/example.pkl and /dev/null differ diff --git a/examples/imitate_webpage.py b/examples/imitate_webpage.py new file mode 100644 index 000000000..5075e1e39 --- /dev/null +++ b/examples/imitate_webpage.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2024/01/15 +@Author : mannaandpoem +@File : imitate_webpage.py +""" +from metagpt.roles.ci.code_interpreter import CodeInterpreter + + +async def main(): + web_url = "https://pytorch.org/" + prompt = f"""This is a URL of webpage: '{web_url}' . +Firstly, utilize Selenium and WebDriver for rendering. +Secondly, convert image to a webpage including HTML, CSS and JS in one go. +Finally, save webpage in a text file. +Note: All required dependencies and environments have been fully installed and configured.""" + ci = CodeInterpreter(goal=prompt, use_tools=True) + + await ci.run(prompt) + + +if __name__ == "__main__": + import asyncio + + asyncio.run(main()) diff --git a/examples/llm_hello_world.py b/examples/llm_hello_world.py index 219a303c8..1d132eb8a 100644 --- a/examples/llm_hello_world.py +++ b/examples/llm_hello_world.py @@ -6,9 +6,11 @@ @File : llm_hello_world.py """ import asyncio +from pathlib import Path from metagpt.llm import LLM from metagpt.logs import logger +from metagpt.utils.common import encode_image async def main(): @@ -27,6 +29,12 @@ async def main(): if hasattr(llm, "completion"): logger.info(llm.completion(hello_msg)) + # check if the configured llm supports llm-vision capacity. If not, it will throw a error + invoice_path = Path(__file__).parent.joinpath("..", "tests", "data", "invoices", "invoice-2.png") + img_base64 = encode_image(invoice_path) + res = await llm.aask(msg="if this is a invoice, just return True else return False", images=[img_base64]) + assert "true" in res.lower() + if __name__ == "__main__": asyncio.run(main()) diff --git a/examples/sd_tool_usage.py b/examples/sd_tool_usage.py new file mode 100644 index 000000000..b4642af23 --- /dev/null +++ b/examples/sd_tool_usage.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# @Date : 1/11/2024 7:06 PM +# @Author : stellahong (stellahong@fuzhi.ai) +# @Desc : +import asyncio + +from metagpt.roles.ci.code_interpreter import CodeInterpreter + + +async def main(requirement: str = ""): + code_interpreter = CodeInterpreter(use_tools=True, goal=requirement) + await code_interpreter.run(requirement) + + +if __name__ == "__main__": + sd_url = "http://your.sd.service.ip:port" + requirement = ( + f"I want to generate an image of a beautiful girl using the stable diffusion text2image tool, sd_url={sd_url}" + ) + + asyncio.run(main(requirement)) diff --git a/examples/search_kb.py b/examples/search_kb.py index 0e0e0ffd0..995720cc1 100644 --- a/examples/search_kb.py +++ b/examples/search_kb.py @@ -8,7 +8,7 @@ from langchain.embeddings import OpenAIEmbeddings -from metagpt.config import CONFIG +from metagpt.config2 import config from metagpt.const import DATA_PATH, EXAMPLE_PATH from metagpt.document_store import FaissStore from metagpt.logs import logger @@ -16,7 +16,8 @@ def get_store(): - embedding = OpenAIEmbeddings(openai_api_key=CONFIG.openai_api_key, openai_api_base=CONFIG.openai_base_url) + llm = config.get_openai_llm() + embedding = OpenAIEmbeddings(openai_api_key=llm.api_key, openai_api_base=llm.base_url) return FaissStore(DATA_PATH / "example.json", embedding=embedding) diff --git a/examples/search_with_specific_engine.py b/examples/search_with_specific_engine.py index 9406a2965..97b1378ee 100644 --- a/examples/search_with_specific_engine.py +++ b/examples/search_with_specific_engine.py @@ -5,17 +5,20 @@ import asyncio from metagpt.roles import Searcher -from metagpt.tools import SearchEngineType +from metagpt.tools.search_engine import SearchEngine, SearchEngineType async def main(): question = "What are the most interesting human facts?" + kwargs = {"api_key": "", "cse_id": "", "proxy": None} # Serper API - # await Searcher(engine=SearchEngineType.SERPER_GOOGLE).run(question) + # await Searcher(search_engine=SearchEngine(engine=SearchEngineType.SERPER_GOOGLE, **kwargs)).run(question) # SerpAPI - await Searcher(engine=SearchEngineType.SERPAPI_GOOGLE).run(question) + # await Searcher(search_engine=SearchEngine(engine=SearchEngineType.SERPAPI_GOOGLE, **kwargs)).run(question) # Google API - # await Searcher(engine=SearchEngineType.DIRECT_GOOGLE).run(question) + # await Searcher(search_engine=SearchEngine(engine=SearchEngineType.DIRECT_GOOGLE, **kwargs)).run(question) + # DDG API + await Searcher(search_engine=SearchEngine(engine=SearchEngineType.DUCK_DUCK_GO, **kwargs)).run(question) if __name__ == "__main__": diff --git a/examples/write_novel.py b/examples/write_novel.py new file mode 100644 index 000000000..b272a56e6 --- /dev/null +++ b/examples/write_novel.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2024/2/1 12:01 +@Author : alexanderwu +@File : write_novel.py +""" +import asyncio +from typing import List + +from pydantic import BaseModel, Field + +from metagpt.actions.action_node import ActionNode +from metagpt.llm import LLM + + +class Novel(BaseModel): + name: str = Field(default="The Lord of the Rings", description="The name of the novel.") + user_group: str = Field(default="...", description="The user group of the novel.") + outlines: List[str] = Field( + default=["Chapter 1: ...", "Chapter 2: ...", "Chapter 3: ..."], + description="The outlines of the novel. No more than 10 chapters.", + ) + background: str = Field(default="...", description="The background of the novel.") + character_names: List[str] = Field(default=["Frodo", "Gandalf", "Sauron"], description="The characters.") + conflict: str = Field(default="...", description="The conflict of the characters.") + plot: str = Field(default="...", description="The plot of the novel.") + ending: str = Field(default="...", description="The ending of the novel.") + + +class Chapter(BaseModel): + name: str = Field(default="Chapter 1", description="The name of the chapter.") + content: str = Field(default="...", description="The content of the chapter. No more than 1000 words.") + + +async def generate_novel(): + instruction = ( + "Write a novel named 'Harry Potter in The Lord of the Rings'. " + "Fill the empty nodes with your own ideas. Be creative! Use your own words!" + "I will tip you $100,000 if you write a good novel." + ) + novel_node = await ActionNode.from_pydantic(Novel).fill(context=instruction, llm=LLM()) + chap_node = await ActionNode.from_pydantic(Chapter).fill( + context=f"### instruction\n{instruction}\n### novel\n{novel_node.content}", llm=LLM() + ) + print(chap_node.content) + + +asyncio.run(generate_novel()) diff --git a/metagpt/actions/__init__.py b/metagpt/actions/__init__.py index 5b995bab6..363b4fd33 100644 --- a/metagpt/actions/__init__.py +++ b/metagpt/actions/__init__.py @@ -22,6 +22,9 @@ from metagpt.actions.write_prd import WritePRD from metagpt.actions.write_prd_review import WritePRDReview from metagpt.actions.write_test import WriteTest +from metagpt.actions.ci.execute_nb_code import ExecuteNbCode +from metagpt.actions.ci.write_analysis_code import WriteCodeWithoutTools, WriteCodeWithTools +from metagpt.actions.ci.write_plan import WritePlan class ActionType(Enum): @@ -42,6 +45,10 @@ class ActionType(Enum): COLLECT_LINKS = CollectLinks WEB_BROWSE_AND_SUMMARIZE = WebBrowseAndSummarize CONDUCT_RESEARCH = ConductResearch + EXECUTE_NB_CODE = ExecuteNbCode + WRITE_CODE_WITHOUT_TOOLS = WriteCodeWithoutTools + WRITE_CODE_WITH_TOOLS = WriteCodeWithTools + WRITE_PLAN = WritePlan __all__ = [ diff --git a/metagpt/actions/action.py b/metagpt/actions/action.py index b586bcc22..1b93213f7 100644 --- a/metagpt/actions/action.py +++ b/metagpt/actions/action.py @@ -10,41 +10,67 @@ from typing import Optional, Union -from pydantic import ConfigDict, Field, model_validator +from pydantic import BaseModel, ConfigDict, Field, model_validator from metagpt.actions.action_node import ActionNode -from metagpt.llm import LLM -from metagpt.provider.base_llm import BaseLLM +from metagpt.context_mixin import ContextMixin from metagpt.schema import ( + CodePlanAndChangeContext, CodeSummarizeContext, CodingContext, RunCodeContext, SerializationMixin, TestingContext, ) +from metagpt.utils.project_repo import ProjectRepo -class Action(SerializationMixin, is_polymorphic_base=True): - model_config = ConfigDict(arbitrary_types_allowed=True, exclude=["llm"]) +class Action(SerializationMixin, ContextMixin, BaseModel): + model_config = ConfigDict(arbitrary_types_allowed=True) name: str = "" - llm: BaseLLM = Field(default_factory=LLM, exclude=True) - context: Union[dict, CodingContext, CodeSummarizeContext, TestingContext, RunCodeContext, str, None] = "" + i_context: Union[ + dict, CodingContext, CodeSummarizeContext, TestingContext, RunCodeContext, CodePlanAndChangeContext, str, None + ] = "" prefix: str = "" # aask*时会加上prefix,作为system_message desc: str = "" # for skill manager node: ActionNode = Field(default=None, exclude=True) + @property + def repo(self) -> ProjectRepo: + if not self.context.repo: + self.context.repo = ProjectRepo(self.context.git_repo) + return self.context.repo + + @property + def prompt_schema(self): + return self.config.prompt_schema + + @property + def project_name(self): + return self.config.project_name + + @project_name.setter + def project_name(self, value): + self.config.project_name = value + + @property + def project_path(self): + return self.config.project_path + @model_validator(mode="before") + @classmethod def set_name_if_empty(cls, values): if "name" not in values or not values["name"]: values["name"] = cls.__name__ return values @model_validator(mode="before") + @classmethod def _init_with_instruction(cls, values): if "instruction" in values: name = values["name"] - i = values["instruction"] + i = values.pop("instruction") values["node"] = ActionNode(key=name, expected_type=str, instruction=i, example="", schema="raw") return values diff --git a/metagpt/actions/action_graph.py b/metagpt/actions/action_graph.py new file mode 100644 index 000000000..893bc6d4c --- /dev/null +++ b/metagpt/actions/action_graph.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2024/1/30 13:52 +@Author : alexanderwu +@File : action_graph.py +""" +from __future__ import annotations + +# from metagpt.actions.action_node import ActionNode + + +class ActionGraph: + """ActionGraph: a directed graph to represent the dependency between actions.""" + + def __init__(self): + self.nodes = {} + self.edges = {} + self.execution_order = [] + + def add_node(self, node): + """Add a node to the graph""" + self.nodes[node.key] = node + + def add_edge(self, from_node: "ActionNode", to_node: "ActionNode"): + """Add an edge to the graph""" + if from_node.key not in self.edges: + self.edges[from_node.key] = [] + self.edges[from_node.key].append(to_node.key) + from_node.add_next(to_node) + to_node.add_prev(from_node) + + def topological_sort(self): + """Topological sort the graph""" + visited = set() + stack = [] + + def visit(k): + if k not in visited: + visited.add(k) + if k in self.edges: + for next_node in self.edges[k]: + visit(next_node) + stack.insert(0, k) + + for key in self.nodes: + visit(key) + + self.execution_order = stack diff --git a/metagpt/actions/action_node.py b/metagpt/actions/action_node.py index 6c65b33ef..09da4a988 100644 --- a/metagpt/actions/action_node.py +++ b/metagpt/actions/action_node.py @@ -9,23 +9,37 @@ we can use typing to extract the type of the node, but we cannot use built-in list to extract. """ import json -from typing import Any, Dict, List, Optional, Tuple, Type +import typing +from enum import Enum +from typing import Any, Dict, List, Optional, Tuple, Type, Union -from pydantic import BaseModel, create_model, model_validator +from pydantic import BaseModel, Field, create_model, model_validator from tenacity import retry, stop_after_attempt, wait_random_exponential -from metagpt.config import CONFIG +from metagpt.actions.action_outcls_registry import register_action_outcls from metagpt.llm import BaseLLM from metagpt.logs import logger from metagpt.provider.postprocess.llm_output_postprocess import llm_output_postprocess from metagpt.utils.common import OutputParser, general_after_log +from metagpt.utils.human_interaction import HumanInteraction + + +class ReviewMode(Enum): + HUMAN = "human" + AUTO = "auto" + + +class ReviseMode(Enum): + HUMAN = "human" # human revise + HUMAN_REVIEW = "human_review" # human-review and auto-revise + AUTO = "auto" # auto-review and auto-revise + TAG = "CONTENT" LANGUAGE_CONSTRAINT = "Language: Please use the same language as Human INPUT." FORMAT_CONSTRAINT = f"Format: output wrapped inside [{TAG}][/{TAG}] like format example, nothing else." - SIMPLE_TEMPLATE = """ ## context {context} @@ -45,6 +59,58 @@ Follow instructions of nodes, generate output and make sure it follows the format example. """ +REVIEW_TEMPLATE = """ +## context +Compare the key's value of nodes_output and the corresponding requirements one by one. If a key's value that does not match the requirement is found, provide the comment content on how to modify it. No output is required for matching keys. + +### nodes_output +{nodes_output} + +----- + +## format example +[{tag}] +{{ + "key1": "comment1", + "key2": "comment2", + "keyn": "commentn" +}} +[/{tag}] + +## nodes: ": # " +- key1: # the first key name of mismatch key +- key2: # the second key name of mismatch key +- keyn: # the last key name of mismatch key + +## constraint +{constraint} + +## action +Follow format example's {prompt_schema} format, generate output and make sure it follows the format example. +""" + +REVISE_TEMPLATE = """ +## context +change the nodes_output key's value to meet its comment and no need to add extra comment. + +### nodes_output +{nodes_output} + +----- + +## format example +{example} + +## nodes: ": # " +{instruction} + +## constraint +{constraint} + +## action +Follow format example's {prompt_schema} format, generate output and make sure it follows the format example. +""" + def dict_to_markdown(d, prefix="- ", kv_sep="\n", postfix="\n"): markdown_str = "" @@ -65,6 +131,8 @@ class ActionNode: # Action Input key: str # Product Requirement / File list / Code + func: typing.Callable # 与节点相关联的函数或LLM调用 + params: Dict[str, Type] # 输入参数的字典,键为参数名,值为参数类型 expected_type: Type # such as str / int / float etc. # context: str # everything in the history. instruction: str # the instructions should be followed. @@ -74,6 +142,10 @@ class ActionNode: content: str instruct_content: BaseModel + # For ActionGraph + prevs: List["ActionNode"] # previous nodes + nexts: List["ActionNode"] # next nodes + def __init__( self, key: str, @@ -91,6 +163,8 @@ def __init__( self.content = content self.children = children if children is not None else {} self.schema = schema + self.prevs = [] + self.nexts = [] def __str__(self): return ( @@ -101,10 +175,21 @@ def __str__(self): def __repr__(self): return self.__str__() + def add_prev(self, node: "ActionNode"): + """增加前置ActionNode""" + self.prevs.append(node) + + def add_next(self, node: "ActionNode"): + """增加后置ActionNode""" + self.nexts.append(node) + def add_child(self, node: "ActionNode"): """增加子ActionNode""" self.children[node.key] = node + def get_child(self, key: str) -> Union["ActionNode", None]: + return self.children.get(key, None) + def add_children(self, nodes: List["ActionNode"]): """批量增加子ActionNode""" for node in nodes: @@ -117,24 +202,38 @@ def from_children(cls, key, nodes: List["ActionNode"]): obj.add_children(nodes) return obj - def get_children_mapping(self, exclude=None) -> Dict[str, Tuple[Type, Any]]: - """获得子ActionNode的字典,以key索引""" + def _get_children_mapping(self, exclude=None) -> Dict[str, Any]: + """获得子ActionNode的字典,以key索引,支持多级结构。""" exclude = exclude or [] - return {k: (v.expected_type, ...) for k, v in self.children.items() if k not in exclude} - def get_self_mapping(self) -> Dict[str, Tuple[Type, Any]]: + def _get_mapping(node: "ActionNode") -> Dict[str, Any]: + mapping = {} + for key, child in node.children.items(): + if key in exclude: + continue + # 对于嵌套的子节点,递归调用 _get_mapping + if child.children: + mapping[key] = _get_mapping(child) + else: + mapping[key] = (child.expected_type, Field(default=child.example, description=child.instruction)) + return mapping + + return _get_mapping(self) + + def _get_self_mapping(self) -> Dict[str, Tuple[Type, Any]]: """get self key: type mapping""" return {self.key: (self.expected_type, ...)} def get_mapping(self, mode="children", exclude=None) -> Dict[str, Tuple[Type, Any]]: """get key: type mapping under mode""" if mode == "children" or (mode == "auto" and self.children): - return self.get_children_mapping(exclude=exclude) - return {} if exclude and self.key in exclude else self.get_self_mapping() + return self._get_children_mapping(exclude=exclude) + return {} if exclude and self.key in exclude else self._get_self_mapping() @classmethod + @register_action_outcls def create_model_class(cls, class_name: str, mapping: Dict[str, Tuple[Type, Any]]): - """基于pydantic v1的模型动态生成,用来检验结果类型正确性""" + """基于pydantic v2的模型动态生成,用来检验结果类型正确性""" def check_fields(cls, values): required_fields = set(mapping.keys()) @@ -149,42 +248,85 @@ def check_fields(cls, values): validators = {"check_missing_fields_validator": model_validator(mode="before")(check_fields)} - new_class = create_model(class_name, __validators__=validators, **mapping) + new_fields = {} + for field_name, field_value in mapping.items(): + if isinstance(field_value, dict): + # 对于嵌套结构,递归创建模型类 + nested_class_name = f"{class_name}_{field_name}" + nested_class = cls.create_model_class(nested_class_name, field_value) + new_fields[field_name] = (nested_class, ...) + else: + new_fields[field_name] = field_value + + new_class = create_model(class_name, __validators__=validators, **new_fields) return new_class - def create_children_class(self, exclude=None): + def create_class(self, mode: str = "auto", class_name: str = None, exclude=None): + class_name = class_name if class_name else f"{self.key}_AN" + mapping = self.get_mapping(mode=mode, exclude=exclude) + return self.create_model_class(class_name, mapping) + + def _create_children_class(self, exclude=None): """使用object内有的字段直接生成model_class""" class_name = f"{self.key}_AN" - mapping = self.get_children_mapping(exclude=exclude) + mapping = self._get_children_mapping(exclude=exclude) return self.create_model_class(class_name, mapping) def to_dict(self, format_func=None, mode="auto", exclude=None) -> Dict: """将当前节点与子节点都按照node: format的格式组织成字典""" + nodes = self._to_dict(format_func=format_func, mode=mode, exclude=exclude) + if not isinstance(nodes, dict): + nodes = {self.key: nodes} + return nodes + + def _to_dict(self, format_func=None, mode="auto", exclude=None) -> Dict: + """将当前节点与子节点都按照node: format的格式组织成字典""" - # 如果没有提供格式化函数,使用默认的格式化方式 + # 如果没有提供格式化函数,则使用默认的格式化函数 if format_func is None: - format_func = lambda node: f"{node.instruction}" + format_func = lambda node: node.instruction # 使用提供的格式化函数来格式化当前节点的值 formatted_value = format_func(self) # 创建当前节点的键值对 - if mode == "children" or (mode == "auto" and self.children): - node_dict = {} + if (mode == "children" or mode == "auto") and self.children: + node_value = {} else: - node_dict = {self.key: formatted_value} + node_value = formatted_value if mode == "root": - return node_dict + return {self.key: node_value} - # 遍历子节点并递归调用 to_dict 方法 + # 递归处理子节点 exclude = exclude or [] - for _, child_node in self.children.items(): - if child_node.key in exclude: + for child_key, child_node in self.children.items(): + if child_key in exclude: continue - node_dict.update(child_node.to_dict(format_func)) + # 递归调用 to_dict 方法并更新节点字典 + child_dict = child_node._to_dict(format_func, mode, exclude) + node_value[child_key] = child_dict + + return node_value - return node_dict + def update_instruct_content(self, incre_data: dict[str, Any]): + assert self.instruct_content + origin_sc_dict = self.instruct_content.model_dump() + origin_sc_dict.update(incre_data) + output_class = self.create_class() + self.instruct_content = output_class(**origin_sc_dict) + + def keys(self, mode: str = "auto") -> list: + if mode == "children" or (mode == "auto" and self.children): + keys = [] + else: + keys = [self.key] + if mode == "root": + return keys + + for _, child_node in self.children.items(): + keys.append(child_node.key) + return keys def compile_to(self, i: Dict, schema, kv_sep) -> str: if schema == "json": @@ -234,6 +376,17 @@ def compile(self, context, schema="json", mode="children", template=SIMPLE_TEMPL if schema == "raw": return context + "\n\n## Actions\n" + LANGUAGE_CONSTRAINT + "\n" + self.instruction + ### 直接使用 pydantic BaseModel 生成 instruction 与 example,仅限 JSON + # child_class = self._create_children_class() + # node_schema = child_class.model_json_schema() + # defaults = { + # k: str(v) + # for k, v in child_class.model_fields.items() + # if k not in exclude + # } + # instruction = node_schema + # example = json.dumps(defaults, indent=4) + # FIXME: json instruction会带来格式问题,如:"Project name": "web_2048 # 项目名称使用下划线", # compile example暂时不支持markdown instruction = self.compile_instruction(schema="markdown", mode=mode, exclude=exclude) @@ -260,12 +413,13 @@ async def _aask_v1( prompt: str, output_class_name: str, output_data_mapping: dict, + images: Optional[Union[str, list[str]]] = None, system_msgs: Optional[list[str]] = None, schema="markdown", # compatible to original format - timeout=CONFIG.timeout, + timeout=3, ) -> (str, BaseModel): """Use ActionOutput to wrap the output of aask""" - content = await self.llm.aask(prompt, system_msgs, timeout=timeout) + content = await self.llm.aask(prompt, system_msgs, images=images, timeout=timeout) logger.debug(f"llm raw output:\n{content}") output_class = self.create_model_class(output_class_name, output_data_mapping) @@ -294,13 +448,15 @@ def set_llm(self, llm): def set_context(self, context): self.set_recursive("context", context) - async def simple_fill(self, schema, mode, timeout=CONFIG.timeout, exclude=None): + async def simple_fill(self, schema, mode, images: Optional[Union[str, list[str]]] = None, timeout=3, exclude=None): prompt = self.compile(context=self.context, schema=schema, mode=mode, exclude=exclude) if schema != "raw": mapping = self.get_mapping(mode, exclude=exclude) class_name = f"{self.key}_AN" - content, scontent = await self._aask_v1(prompt, class_name, mapping, schema=schema, timeout=timeout) + content, scontent = await self._aask_v1( + prompt, class_name, mapping, images=images, schema=schema, timeout=timeout + ) self.content = content self.instruct_content = scontent else: @@ -309,7 +465,17 @@ async def simple_fill(self, schema, mode, timeout=CONFIG.timeout, exclude=None): return self - async def fill(self, context, llm, schema="json", mode="auto", strgy="simple", timeout=CONFIG.timeout, exclude=[]): + async def fill( + self, + context, + llm, + schema="json", + mode="auto", + strgy="simple", + images: Optional[Union[str, list[str]]] = None, + timeout=3, + exclude=[], + ): """Fill the node(s) with mode. :param context: Everything we should know when filling node. @@ -325,6 +491,7 @@ async def fill(self, context, llm, schema="json", mode="auto", strgy="simple", t :param strgy: simple/complex - simple: run only once - complex: run each node + :param images: the list of image url or base64 for gpt4-v :param timeout: Timeout for llm invocation. :param exclude: The keys of ActionNode to exclude. :return: self @@ -335,15 +502,219 @@ async def fill(self, context, llm, schema="json", mode="auto", strgy="simple", t schema = self.schema if strgy == "simple": - return await self.simple_fill(schema=schema, mode=mode, timeout=timeout, exclude=exclude) + return await self.simple_fill(schema=schema, mode=mode, images=images, timeout=timeout, exclude=exclude) elif strgy == "complex": # 这里隐式假设了拥有children tmp = {} for _, i in self.children.items(): if exclude and i.key in exclude: continue - child = await i.simple_fill(schema=schema, mode=mode, timeout=timeout, exclude=exclude) - tmp.update(child.instruct_content.dict()) - cls = self.create_children_class() + child = await i.simple_fill(schema=schema, mode=mode, images=images, timeout=timeout, exclude=exclude) + tmp.update(child.instruct_content.model_dump()) + cls = self._create_children_class() self.instruct_content = cls(**tmp) return self + + async def human_review(self) -> dict[str, str]: + review_comments = HumanInteraction().interact_with_instruct_content( + instruct_content=self.instruct_content, interact_type="review" + ) + + return review_comments + + def _makeup_nodes_output_with_req(self) -> dict[str, str]: + instruct_content_dict = self.instruct_content.model_dump() + nodes_output = {} + for key, value in instruct_content_dict.items(): + child = self.get_child(key) + nodes_output[key] = {"value": value, "requirement": child.instruction if child else self.instruction} + return nodes_output + + async def auto_review(self, template: str = REVIEW_TEMPLATE) -> dict[str, str]: + """use key's output value and its instruction to review the modification comment""" + nodes_output = self._makeup_nodes_output_with_req() + """nodes_output format: + { + "key": {"value": "output value", "requirement": "key instruction"} + } + """ + if not nodes_output: + return dict() + + prompt = template.format( + nodes_output=json.dumps(nodes_output, ensure_ascii=False), + tag=TAG, + constraint=FORMAT_CONSTRAINT, + prompt_schema="json", + ) + + content = await self.llm.aask(prompt) + # Extract the dict of mismatch key and its comment. Due to the mismatch keys are unknown, here use the keys + # of ActionNode to judge if exist in `content` and then follow the `data_mapping` method to create model class. + keys = self.keys() + include_keys = [] + for key in keys: + if f'"{key}":' in content: + include_keys.append(key) + if not include_keys: + return dict() + + exclude_keys = list(set(keys).difference(include_keys)) + output_class_name = f"{self.key}_AN_REVIEW" + output_class = self.create_class(class_name=output_class_name, exclude=exclude_keys) + parsed_data = llm_output_postprocess( + output=content, schema=output_class.model_json_schema(), req_key=f"[/{TAG}]" + ) + instruct_content = output_class(**parsed_data) + return instruct_content.model_dump() + + async def simple_review(self, review_mode: ReviewMode = ReviewMode.AUTO): + # generate review comments + if review_mode == ReviewMode.HUMAN: + review_comments = await self.human_review() + else: + review_comments = await self.auto_review() + + if not review_comments: + logger.warning("There are no review comments") + return review_comments + + async def review(self, strgy: str = "simple", review_mode: ReviewMode = ReviewMode.AUTO): + """only give the review comment of each exist and mismatch key + + :param strgy: simple/complex + - simple: run only once + - complex: run each node + """ + if not hasattr(self, "llm"): + raise RuntimeError("use `review` after `fill`") + assert review_mode in ReviewMode + assert self.instruct_content, 'review only support with `schema != "raw"`' + + if strgy == "simple": + review_comments = await self.simple_review(review_mode) + elif strgy == "complex": + # review each child node one-by-one + review_comments = {} + for _, child in self.children.items(): + child_review_comment = await child.simple_review(review_mode) + review_comments.update(child_review_comment) + + return review_comments + + async def human_revise(self) -> dict[str, str]: + review_contents = HumanInteraction().interact_with_instruct_content( + instruct_content=self.instruct_content, mapping=self.get_mapping(mode="auto"), interact_type="revise" + ) + # re-fill the ActionNode + self.update_instruct_content(review_contents) + return review_contents + + def _makeup_nodes_output_with_comment(self, review_comments: dict[str, str]) -> dict[str, str]: + instruct_content_dict = self.instruct_content.model_dump() + nodes_output = {} + for key, value in instruct_content_dict.items(): + if key in review_comments: + nodes_output[key] = {"value": value, "comment": review_comments[key]} + return nodes_output + + async def auto_revise( + self, revise_mode: ReviseMode = ReviseMode.AUTO, template: str = REVISE_TEMPLATE + ) -> dict[str, str]: + """revise the value of incorrect keys""" + # generate review comments + if revise_mode == ReviseMode.AUTO: + review_comments: dict = await self.auto_review() + elif revise_mode == ReviseMode.HUMAN_REVIEW: + review_comments: dict = await self.human_review() + + include_keys = list(review_comments.keys()) + + # generate revise content, two-steps + # step1, find the needed revise keys from review comments to makeup prompt template + nodes_output = self._makeup_nodes_output_with_comment(review_comments) + keys = self.keys() + exclude_keys = list(set(keys).difference(include_keys)) + example = self.compile_example(schema="json", mode="auto", tag=TAG, exclude=exclude_keys) + instruction = self.compile_instruction(schema="markdown", mode="auto", exclude=exclude_keys) + + prompt = template.format( + nodes_output=json.dumps(nodes_output, ensure_ascii=False), + example=example, + instruction=instruction, + constraint=FORMAT_CONSTRAINT, + prompt_schema="json", + ) + + # step2, use `_aask_v1` to get revise structure result + output_mapping = self.get_mapping(mode="auto", exclude=exclude_keys) + output_class_name = f"{self.key}_AN_REVISE" + content, scontent = await self._aask_v1( + prompt=prompt, output_class_name=output_class_name, output_data_mapping=output_mapping, schema="json" + ) + + # re-fill the ActionNode + sc_dict = scontent.model_dump() + self.update_instruct_content(sc_dict) + return sc_dict + + async def simple_revise(self, revise_mode: ReviseMode = ReviseMode.AUTO) -> dict[str, str]: + if revise_mode == ReviseMode.HUMAN: + revise_contents = await self.human_revise() + else: + revise_contents = await self.auto_revise(revise_mode) + + return revise_contents + + async def revise(self, strgy: str = "simple", revise_mode: ReviseMode = ReviseMode.AUTO) -> dict[str, str]: + """revise the content of ActionNode and update the instruct_content + + :param strgy: simple/complex + - simple: run only once + - complex: run each node + """ + if not hasattr(self, "llm"): + raise RuntimeError("use `revise` after `fill`") + assert revise_mode in ReviseMode + assert self.instruct_content, 'revise only support with `schema != "raw"`' + + if strgy == "simple": + revise_contents = await self.simple_revise(revise_mode) + elif strgy == "complex": + # revise each child node one-by-one + revise_contents = {} + for _, child in self.children.items(): + child_revise_content = await child.simple_revise(revise_mode) + revise_contents.update(child_revise_content) + self.update_instruct_content(revise_contents) + + return revise_contents + + @classmethod + def from_pydantic(cls, model: Type[BaseModel], key: str = None): + """ + Creates an ActionNode tree from a Pydantic model. + + Args: + model (Type[BaseModel]): The Pydantic model to convert. + + Returns: + ActionNode: The root node of the created ActionNode tree. + """ + key = key or model.__name__ + root_node = cls(key=key, expected_type=Type[model], instruction="", example="") + + for field_name, field_info in model.model_fields.items(): + field_type = field_info.annotation + description = field_info.description + default = field_info.default + + # Recursively handle nested models if needed + if not isinstance(field_type, typing._GenericAlias) and issubclass(field_type, BaseModel): + child_node = cls.from_pydantic(field_type, key=field_name) + else: + child_node = cls(key=field_name, expected_type=field_type, instruction=description, example=default) + + root_node.add_child(child_node) + + return root_node diff --git a/metagpt/actions/action_outcls_registry.py b/metagpt/actions/action_outcls_registry.py new file mode 100644 index 000000000..6baa4cea9 --- /dev/null +++ b/metagpt/actions/action_outcls_registry.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : registry to store Dynamic Model from ActionNode.create_model_class to keep it as same Class +# with same class name and mapping + +from functools import wraps + +action_outcls_registry = dict() + + +def register_action_outcls(func): + """ + Due to `create_model` return different Class even they have same class name and mapping. + In order to do a comparison, use outcls_id to identify same Class with same class name and field definition + """ + + @wraps(func) + def decorater(*args, **kwargs): + """ + arr example + [, 'test', {'field': (str, Ellipsis)}] + """ + arr = list(args) + list(kwargs.values()) + """ + outcls_id example + "_test_{'field': (str, Ellipsis)}" + """ + for idx, item in enumerate(arr): + if isinstance(item, dict): + arr[idx] = dict(sorted(item.items())) + outcls_id = "_".join([str(i) for i in arr]) + # eliminate typing influence + outcls_id = outcls_id.replace("typing.List", "list").replace("typing.Dict", "dict") + + if outcls_id in action_outcls_registry: + return action_outcls_registry[outcls_id] + + out_cls = func(*args, **kwargs) + action_outcls_registry[outcls_id] = out_cls + return out_cls + + return decorater diff --git a/metagpt/actions/ci/ask_review.py b/metagpt/actions/ci/ask_review.py new file mode 100644 index 000000000..041011e80 --- /dev/null +++ b/metagpt/actions/ci/ask_review.py @@ -0,0 +1,62 @@ +from __future__ import annotations + +from typing import Tuple + +from metagpt.actions import Action +from metagpt.logs import logger +from metagpt.schema import Message, Plan + + +class ReviewConst: + TASK_REVIEW_TRIGGER = "task" + CODE_REVIEW_TRIGGER = "code" + CONTINUE_WORDS = ["confirm", "continue", "c", "yes", "y"] + CHANGE_WORDS = ["change"] + EXIT_WORDS = ["exit"] + TASK_REVIEW_INSTRUCTION = ( + f"If you want to change, add, delete a task or merge tasks in the plan, say '{CHANGE_WORDS[0]} task task_id or current task, ... (things to change)' " + f"If you confirm the output from the current task and wish to continue, type: {CONTINUE_WORDS[0]}" + ) + CODE_REVIEW_INSTRUCTION = ( + f"If you want the codes to be rewritten, say '{CHANGE_WORDS[0]} ... (your change advice)' " + f"If you want to leave it as is, type: {CONTINUE_WORDS[0]} or {CONTINUE_WORDS[1]}" + ) + EXIT_INSTRUCTION = f"If you want to terminate the process, type: {EXIT_WORDS[0]}" + + +class AskReview(Action): + async def run( + self, context: list[Message] = [], plan: Plan = None, trigger: str = ReviewConst.TASK_REVIEW_TRIGGER + ) -> Tuple[str, bool]: + if plan: + logger.info("Current overall plan:") + logger.info( + "\n".join( + [f"{task.task_id}: {task.instruction}, is_finished: {task.is_finished}" for task in plan.tasks] + ) + ) + + logger.info("Most recent context:") + latest_action = context[-1].cause_by if context and context[-1].cause_by else "" + review_instruction = ( + ReviewConst.TASK_REVIEW_INSTRUCTION + if trigger == ReviewConst.TASK_REVIEW_TRIGGER + else ReviewConst.CODE_REVIEW_INSTRUCTION + ) + prompt = ( + f"This is a <{trigger}> review. Please review output from {latest_action}\n" + f"{review_instruction}\n" + f"{ReviewConst.EXIT_INSTRUCTION}\n" + "Please type your review below:\n" + ) + + rsp = input(prompt) + + if rsp.lower() in ReviewConst.EXIT_WORDS: + exit() + + # Confirmation can be one of "confirm", "continue", "c", "yes", "y" exactly, or sentences containing "confirm". + # One could say "confirm this task, but change the next task to ..." + confirmed = rsp.lower() in ReviewConst.CONTINUE_WORDS or ReviewConst.CONTINUE_WORDS[0] in rsp.lower() + + return rsp, confirmed diff --git a/metagpt/actions/ci/debug_code.py b/metagpt/actions/ci/debug_code.py new file mode 100644 index 000000000..4a6617dc6 --- /dev/null +++ b/metagpt/actions/ci/debug_code.py @@ -0,0 +1,109 @@ +from __future__ import annotations + +from metagpt.actions.ci.write_analysis_code import BaseWriteAnalysisCode +from metagpt.logs import logger +from metagpt.schema import Message +from metagpt.utils.common import create_func_call_config + +DEBUG_REFLECTION_EXAMPLE = ''' +Example 1: +[previous impl]: +```python +def add(a: int, b: int) -> int: + """ + Given integers a and b, return the total value of a and b. + """ + return a - b +``` + +[runtime Error]: +Tested passed: + +Tests failed: +assert add(1, 2) == 3 # output: -1 +assert add(1, 2) == 4 # output: -1 + +[reflection on previous impl]: +The implementation failed the test cases where the input integers are 1 and 2. The issue arises because the code does not add the two integers together, but instead subtracts the second integer from the first. To fix this issue, we should change the operator from `-` to `+` in the return statement. This will ensure that the function returns the correct output for the given input. + +[improved impl]: +```python +def add(a: int, b: int) -> int: + """ + Given integers a and b, return the total value of a and b. + """ + return a + b +``` +''' + +REFLECTION_PROMPT = """ +Here is an example for you. +{debug_example} +[context] +{context} + +[previous impl] +{code} +[runtime Error] +{runtime_result} + +Analysis the error step by step, provide me improve method and code. Remember to follow [context] requirement. Don't forget write code for steps behind the error step. +[reflection on previous impl]: +xxx +""" + +CODE_REFLECTION = { + "name": "execute_reflection_code", + "description": "Execute reflection code.", + "parameters": { + "type": "object", + "properties": { + "reflection": { + "type": "string", + "description": "Reflection on previous impl.", + }, + "improved_impl": { + "type": "string", + "description": "Refined code after reflection.", + }, + }, + "required": ["reflection", "improved_impl"], + }, +} + + +class DebugCode(BaseWriteAnalysisCode): + async def run( + self, + context: list[Message] = None, + code: str = "", + runtime_result: str = "", + ) -> str: + """ + Execute the debugging process based on the provided context, code, and runtime_result. + + Args: + context (list[Message]): A list of Message objects representing the context. + code (str): The code to be debugged. + runtime_result (str): The result of the code execution. + + Returns: + str: The improved implementation based on the debugging process. + """ + + info = [] + reflection_prompt = REFLECTION_PROMPT.format( + debug_example=DEBUG_REFLECTION_EXAMPLE, + context=context, + code=code, + runtime_result=runtime_result, + ) + system_prompt = "You are an AI Python assistant. You will be given your previous implementation code of a task, runtime error results, and a hint to change the implementation appropriately. Write your full implementation " + info.append(Message(role="system", content=system_prompt)) + info.append(Message(role="user", content=reflection_prompt)) + + tool_config = create_func_call_config(CODE_REFLECTION) + reflection = await self.llm.aask_code(messages=info, **tool_config) + logger.info(f"reflection is {reflection}") + + return {"code": reflection["improved_impl"]} diff --git a/metagpt/actions/ci/execute_nb_code.py b/metagpt/actions/ci/execute_nb_code.py new file mode 100644 index 000000000..0ff00de8f --- /dev/null +++ b/metagpt/actions/ci/execute_nb_code.py @@ -0,0 +1,249 @@ +# -*- encoding: utf-8 -*- +""" +@Date : 2023/11/17 14:22:15 +@Author : orange-crow +@File : execute_nb_code.py +""" +from __future__ import annotations + +import asyncio +import base64 +import re +import traceback +from typing import Literal, Tuple + +import nbformat +from nbclient import NotebookClient +from nbclient.exceptions import CellTimeoutError, DeadKernelError +from nbformat import NotebookNode +from nbformat.v4 import new_code_cell, new_markdown_cell, new_output +from rich.box import MINIMAL +from rich.console import Console, Group +from rich.live import Live +from rich.markdown import Markdown +from rich.panel import Panel +from rich.syntax import Syntax + +from metagpt.actions import Action +from metagpt.logs import logger + + +class ExecuteNbCode(Action): + """execute notebook code block, return result to llm, and display it.""" + + nb: NotebookNode + nb_client: NotebookClient + console: Console + interaction: str + timeout: int = 600 + + def __init__( + self, + nb=nbformat.v4.new_notebook(), + timeout=600, + ): + super().__init__( + nb=nb, + nb_client=NotebookClient(nb, timeout=timeout), + timeout=timeout, + console=Console(), + interaction=("ipython" if self.is_ipython() else "terminal"), + ) + + async def build(self): + if self.nb_client.kc is None or not await self.nb_client.kc.is_alive(): + self.nb_client.create_kernel_manager() + self.nb_client.start_new_kernel() + self.nb_client.start_new_kernel_client() + + async def terminate(self): + """kill NotebookClient""" + await self.nb_client._async_cleanup_kernel() + + async def reset(self): + """reset NotebookClient""" + await self.terminate() + + # sleep 1s to wait for the kernel to be cleaned up completely + await asyncio.sleep(1) + await self.build() + self.nb_client = NotebookClient(self.nb, timeout=self.timeout) + + def add_code_cell(self, code: str): + self.nb.cells.append(new_code_cell(source=code)) + + def add_markdown_cell(self, markdown: str): + self.nb.cells.append(new_markdown_cell(source=markdown)) + + def _display(self, code: str, language: Literal["python", "markdown"] = "python"): + if language == "python": + code = Syntax(code, "python", theme="paraiso-dark", line_numbers=True) + self.console.print(code) + elif language == "markdown": + display_markdown(code) + else: + raise ValueError(f"Only support for python, markdown, but got {language}") + + def add_output_to_cell(self, cell: NotebookNode, output: str): + """add outputs of code execution to notebook cell.""" + if "outputs" not in cell: + cell["outputs"] = [] + else: + cell["outputs"].append(new_output(output_type="stream", name="stdout", text=str(output))) + + def parse_outputs(self, outputs: list[str]) -> str: + """Parses the outputs received from notebook execution.""" + assert isinstance(outputs, list) + parsed_output = "" + + for i, output in enumerate(outputs): + if output["output_type"] == "stream" and not any( + tag in output["text"] + for tag in ["| INFO | metagpt", "| ERROR | metagpt", "| WARNING | metagpt"] + ): + parsed_output += output["text"] + elif output["output_type"] == "display_data": + if "image/png" in output["data"]: + self.show_bytes_figure(output["data"]["image/png"], self.interaction) + else: + logger.info( + f"{i}th output['data'] from nbclient outputs dont have image/png, continue next output ..." + ) + elif output["output_type"] == "execute_result": + parsed_output += output["data"]["text/plain"] + return parsed_output + + def show_bytes_figure(self, image_base64: str, interaction_type: Literal["ipython", None]): + image_bytes = base64.b64decode(image_base64) + if interaction_type == "ipython": + from IPython.display import Image, display + + display(Image(data=image_bytes)) + else: + import io + + from PIL import Image + + image = Image.open(io.BytesIO(image_bytes)) + image.show() + + def is_ipython(self) -> bool: + try: + # 如果在Jupyter Notebook中运行,__file__ 变量不存在 + from IPython import get_ipython + + if get_ipython() is not None and "IPKernelApp" in get_ipython().config: + return True + else: + return False + except NameError: + return False + + async def run_cell(self, cell: NotebookNode, cell_index: int) -> Tuple[bool, str]: + """set timeout for run code. + returns the success or failure of the cell execution, and an optional error message. + """ + try: + await self.nb_client.async_execute_cell(cell, cell_index) + return True, "" + except CellTimeoutError: + assert self.nb_client.km is not None + await self.nb_client.km.interrupt_kernel() + await asyncio.sleep(1) + error_msg = "Cell execution timed out: Execution exceeded the time limit and was stopped; consider optimizing your code for better performance." + return False, error_msg + except DeadKernelError: + await self.reset() + return False, "DeadKernelError" + except Exception: + return False, f"{traceback.format_exc()}" + + async def run(self, code: str, language: Literal["python", "markdown"] = "python") -> Tuple[str, bool]: + """ + return the output of code execution, and a success indicator (bool) of code execution. + """ + self._display(code, language) + + if language == "python": + # add code to the notebook + self.add_code_cell(code=code) + + # build code executor + await self.build() + + # run code + cell_index = len(self.nb.cells) - 1 + success, error_message = await self.run_cell(self.nb.cells[-1], cell_index) + + if not success: + return truncate(remove_escape_and_color_codes(error_message), is_success=success) + + # code success + outputs = self.parse_outputs(self.nb.cells[-1].outputs) + outputs, success = truncate(remove_escape_and_color_codes(outputs), is_success=success) + + if "!pip" in outputs: + success = False + + return outputs, success + + elif language == "markdown": + # add markdown content to markdown cell in a notebook. + self.add_markdown_cell(code) + # return True, beacuse there is no execution failure for markdown cell. + return code, True + else: + raise ValueError(f"Only support for language: python, markdown, but got {language}, ") + + +def truncate(result: str, keep_len: int = 2000, is_success: bool = True): + """对于超出keep_len个字符的result: 执行失败的代码, 展示result后keep_len个字符; 执行成功的代码, 展示result前keep_len个字符。""" + if is_success: + desc = f"Executed code successfully. Truncated to show only first {keep_len} characters\n" + else: + desc = f"Executed code failed, please reflect the cause of bug and then debug. Truncated to show only last {keep_len} characters\n" + + if result.strip().startswith(" keep_len: + result = result[-keep_len:] if not is_success else result[:keep_len] + return desc + result, is_success + + return result, is_success + + +def remove_escape_and_color_codes(input_str: str): + # 使用正则表达式去除转义字符和颜色代码 + pattern = re.compile(r"\x1b\[[0-9;]*[mK]") + result = pattern.sub("", input_str) + return result + + +def display_markdown(content: str): + # 使用正则表达式逐个匹配代码块 + matches = re.finditer(r"```(.+?)```", content, re.DOTALL) + start_index = 0 + content_panels = [] + # 逐个打印匹配到的文本和代码 + for match in matches: + text_content = content[start_index : match.start()].strip() + code_content = match.group(0).strip()[3:-3] # Remove triple backticks + + if text_content: + content_panels.append(Panel(Markdown(text_content), box=MINIMAL)) + + if code_content: + content_panels.append(Panel(Markdown(f"```{code_content}"), box=MINIMAL)) + start_index = match.end() + + # 打印剩余文本(如果有) + remaining_text = content[start_index:].strip() + if remaining_text: + content_panels.append(Panel(Markdown(remaining_text), box=MINIMAL)) + + # 在Live模式中显示所有Panel + with Live(auto_refresh=False, console=Console(), vertical_overflow="visible") as live: + live.update(Group(*content_panels)) + live.refresh() diff --git a/metagpt/actions/ci/ml_action.py b/metagpt/actions/ci/ml_action.py new file mode 100644 index 000000000..e18d0fd20 --- /dev/null +++ b/metagpt/actions/ci/ml_action.py @@ -0,0 +1,70 @@ +from __future__ import annotations + +from typing import Tuple + +from metagpt.actions import Action +from metagpt.actions.ci.write_analysis_code import WriteCodeWithTools +from metagpt.prompts.ci.ml_action import ( + ML_GENERATE_CODE_PROMPT, + ML_TOOL_USAGE_PROMPT, + PRINT_DATA_COLUMNS, + UPDATE_DATA_COLUMNS, +) +from metagpt.prompts.ci.write_analysis_code import CODE_GENERATOR_WITH_TOOLS +from metagpt.schema import Message, Plan +from metagpt.utils.common import create_func_call_config, remove_comments + + +class WriteCodeWithToolsML(WriteCodeWithTools): + async def run( + self, + context: list[Message], + plan: Plan = None, + column_info: str = "", + **kwargs, + ) -> Tuple[list[Message], str]: + # prepare tool schemas and tool-type-specific instruction + tool_schemas, tool_type_usage_prompt = await self._prepare_tools(plan=plan) + + # ML-specific variables to be used in prompt + finished_tasks = plan.get_finished_tasks() + code_context = [remove_comments(task.code) for task in finished_tasks] + code_context = "\n\n".join(code_context) + + # prepare prompt depending on tool availability & LLM call + if tool_schemas: + prompt = ML_TOOL_USAGE_PROMPT.format( + user_requirement=plan.goal, + history_code=code_context, + current_task=plan.current_task.instruction, + column_info=column_info, + tool_type_usage_prompt=tool_type_usage_prompt, + tool_schemas=tool_schemas, + ) + + else: + prompt = ML_GENERATE_CODE_PROMPT.format( + user_requirement=plan.goal, + history_code=code_context, + current_task=plan.current_task.instruction, + column_info=column_info, + tool_type_usage_prompt=tool_type_usage_prompt, + ) + tool_config = create_func_call_config(CODE_GENERATOR_WITH_TOOLS) + rsp = await self.llm.aask_code(prompt, **tool_config) + + # Extra output to be used for potential debugging + context = [Message(content=prompt, role="user")] + + return context, rsp + + +class UpdateDataColumns(Action): + async def run(self, plan: Plan = None) -> dict: + finished_tasks = plan.get_finished_tasks() + code_context = [remove_comments(task.code) for task in finished_tasks] + code_context = "\n\n".join(code_context) + prompt = UPDATE_DATA_COLUMNS.format(history_code=code_context) + tool_config = create_func_call_config(PRINT_DATA_COLUMNS) + rsp = await self.llm.aask_code(prompt, **tool_config) + return rsp diff --git a/metagpt/actions/ci/write_analysis_code.py b/metagpt/actions/ci/write_analysis_code.py new file mode 100644 index 000000000..421eb0804 --- /dev/null +++ b/metagpt/actions/ci/write_analysis_code.py @@ -0,0 +1,155 @@ +# -*- encoding: utf-8 -*- +""" +@Date : 2023/11/20 13:19:39 +@Author : orange-crow +@File : write_analysis_code.py +""" +from __future__ import annotations + +from typing import Tuple + +from metagpt.actions import Action +from metagpt.logs import logger +from metagpt.prompts.ci.write_analysis_code import ( + CODE_GENERATOR_WITH_TOOLS, + SELECT_FUNCTION_TOOLS, + TOOL_RECOMMENDATION_PROMPT, + TOOL_USAGE_PROMPT, +) +from metagpt.schema import Message, Plan, SystemMessage +from metagpt.tools import TOOL_REGISTRY +from metagpt.tools.tool_registry import validate_tool_names +from metagpt.utils.common import create_func_call_config + + +class BaseWriteAnalysisCode(Action): + DEFAULT_SYSTEM_MSG: str = """You are Code Interpreter, a world-class programmer that can complete any goal by executing code. Strictly follow the plan and generate code step by step. Each step of the code will be executed on the user's machine, and the user will provide the code execution results to you.**Notice: The code for the next step depends on the code for the previous step. Must reuse variables in the lastest other code directly, dont creat it again, it is very import for you. Use !pip install in a standalone block to install missing packages.Usually the libraries you need are already installed.Dont check if packages already imported.**""" # prompt reference: https://github.com/KillianLucas/open-interpreter/blob/v0.1.4/interpreter/system_message.txt + # REUSE_CODE_INSTRUCTION = """ATTENTION: DONT include codes from previous tasks in your current code block, include new codes only, DONT repeat codes!""" + + def insert_system_message(self, context: list[Message], system_msg: str = None): + system_msg = system_msg or self.DEFAULT_SYSTEM_MSG + context.insert(0, SystemMessage(content=system_msg)) if context[0].role != "system" else None + return context + + async def run(self, context: list[Message], plan: Plan = None) -> dict: + """Run of a code writing action, used in data analysis or modeling + + Args: + context (list[Message]): Action output history, source action denoted by Message.cause_by + plan (Plan, optional): Overall plan. Defaults to None. + + Returns: + dict: code result in the format of {"code": "print('hello world')", "language": "python"} + """ + raise NotImplementedError + + +class WriteCodeWithoutTools(BaseWriteAnalysisCode): + """Ask LLM to generate codes purely by itself without local user-defined tools""" + + async def run(self, context: list[Message], plan: Plan = None, system_msg: str = None, **kwargs) -> dict: + messages = self.insert_system_message(context, system_msg) + rsp = await self.llm.aask_code(messages, **kwargs) + return rsp + + +class WriteCodeWithTools(BaseWriteAnalysisCode): + """Write code with help of local available tools. Choose tools first, then generate code to use the tools""" + + # selected tools to choose from, listed by their names. An empty list means selection from all tools. + selected_tools: list[str] = [] + + def _get_tools_by_type(self, tool_type: str) -> dict: + """ + Retreive tools by tool type from registry, but filtered by pre-selected tool list + + Args: + tool_type (str): Tool type to retrieve from the registry + + Returns: + dict: A dict of tool name to Tool object, representing available tools under the type + """ + candidate_tools = TOOL_REGISTRY.get_tools_by_type(tool_type) + if self.selected_tools: + candidate_tool_names = set(self.selected_tools) & candidate_tools.keys() + candidate_tools = {tool_name: candidate_tools[tool_name] for tool_name in candidate_tool_names} + return candidate_tools + + async def _recommend_tool( + self, + task: str, + available_tools: dict, + ) -> dict: + """ + Recommend tools for the specified task. + + Args: + task (str): the task to recommend tools for + available_tools (dict): the available tools description + + Returns: + dict: schemas of recommended tools for the specified task + """ + prompt = TOOL_RECOMMENDATION_PROMPT.format( + current_task=task, + available_tools=available_tools, + ) + tool_config = create_func_call_config(SELECT_FUNCTION_TOOLS) + rsp = await self.llm.aask_code(prompt, **tool_config) + recommend_tools = rsp["recommend_tools"] + logger.info(f"Recommended tools: \n{recommend_tools}") + + # Parses and validates the recommended tools, for LLM might hallucinate and recommend non-existing tools + valid_tools = validate_tool_names(recommend_tools, return_tool_object=True) + + tool_schemas = {tool.name: tool.schemas for tool in valid_tools} + + return tool_schemas + + async def _prepare_tools(self, plan: Plan) -> Tuple[dict, str]: + """Prepare tool schemas and usage instructions according to current task + + Args: + plan (Plan): The overall plan containing task information. + + Returns: + Tuple[dict, str]: A tool schemas ({tool_name: tool_schema_dict}) and a usage prompt for the type of tools selected + """ + # find tool type from task type through exact match, can extend to retrieval in the future + tool_type = plan.current_task.task_type + + # prepare tool-type-specific instruction + tool_type_usage_prompt = ( + TOOL_REGISTRY.get_tool_type(tool_type).usage_prompt if TOOL_REGISTRY.has_tool_type(tool_type) else "" + ) + + # prepare schemas of available tools + tool_schemas = {} + available_tools = self._get_tools_by_type(tool_type) + if available_tools: + available_tools = {tool_name: tool.schemas["description"] for tool_name, tool in available_tools.items()} + tool_schemas = await self._recommend_tool(plan.current_task.instruction, available_tools) + + return tool_schemas, tool_type_usage_prompt + + async def run( + self, + context: list[Message], + plan: Plan, + **kwargs, + ) -> str: + # prepare tool schemas and tool-type-specific instruction + tool_schemas, tool_type_usage_prompt = await self._prepare_tools(plan=plan) + + # form a complete tool usage instruction and include it as a message in context + tools_instruction = TOOL_USAGE_PROMPT.format( + tool_schemas=tool_schemas, tool_type_usage_prompt=tool_type_usage_prompt + ) + context.append(Message(content=tools_instruction, role="user")) + + # prepare prompt & LLM call + prompt = self.insert_system_message(context) + tool_config = create_func_call_config(CODE_GENERATOR_WITH_TOOLS) + rsp = await self.llm.aask_code(prompt, **tool_config) + + return rsp diff --git a/metagpt/actions/ci/write_plan.py b/metagpt/actions/ci/write_plan.py new file mode 100644 index 000000000..dd9363260 --- /dev/null +++ b/metagpt/actions/ci/write_plan.py @@ -0,0 +1,116 @@ +# -*- encoding: utf-8 -*- +""" +@Date : 2023/11/20 11:24:03 +@Author : orange-crow +@File : plan.py +""" +from __future__ import annotations + +import json +from copy import deepcopy +from typing import Tuple + +from metagpt.actions import Action +from metagpt.logs import logger +from metagpt.prompts.ci.write_analysis_code import ( + ASSIGN_TASK_TYPE_CONFIG, + ASSIGN_TASK_TYPE_PROMPT, +) +from metagpt.schema import Message, Plan, Task +from metagpt.tools import TOOL_REGISTRY +from metagpt.utils.common import CodeParser, create_func_call_config + + +class WritePlan(Action): + PROMPT_TEMPLATE: str = """ + # Context: + __context__ + # Task: + Based on the context, write a plan or modify an existing plan of what you should do to achieve the goal. A plan consists of one to __max_tasks__ tasks. + If you are modifying an existing plan, carefully follow the instruction, don't make unnecessary changes. Give the whole plan unless instructed to modify only one task of the plan. + If you encounter errors on the current task, revise and output the current single task only. + Output a list of jsons following the format: + ```json + [ + { + "task_id": str = "unique identifier for a task in plan, can be an ordinal", + "dependent_task_ids": list[str] = "ids of tasks prerequisite to this task", + "instruction": "what you should do in this task, one short phrase or sentence", + }, + ... + ] + ``` + """ + + async def assign_task_type(self, tasks: list[dict]) -> str: + """Assign task type to each task in tasks + + Args: + tasks (list[dict]): tasks to be assigned task type + + Returns: + str: tasks with task type assigned in a json string + """ + task_info = "\n".join([f"Task {task['task_id']}: {task['instruction']}" for task in tasks]) + task_type_desc = "\n".join( + [f"- **{tool_type.name}**: {tool_type.desc}" for tool_type in TOOL_REGISTRY.get_tool_types().values()] + ) # task type are binded with tool type now, should be improved in the future + prompt = ASSIGN_TASK_TYPE_PROMPT.format( + task_info=task_info, task_type_desc=task_type_desc + ) # task types are set to be the same as tool types, for now + tool_config = create_func_call_config(ASSIGN_TASK_TYPE_CONFIG) + rsp = await self.llm.aask_code(prompt, **tool_config) + task_type_list = rsp["task_type"] + logger.info(f"assigned task types: {task_type_list}") + for task, task_type in zip(tasks, task_type_list): + task["task_type"] = task_type + return json.dumps(tasks) + + async def run(self, context: list[Message], max_tasks: int = 5, use_tools: bool = False) -> str: + prompt = ( + self.PROMPT_TEMPLATE.replace("__context__", "\n".join([str(ct) for ct in context])) + # .replace("__current_plan__", current_plan) + .replace("__max_tasks__", str(max_tasks)) + ) + rsp = await self._aask(prompt) + rsp = CodeParser.parse_code(block=None, text=rsp) + if use_tools: + rsp = await self.assign_task_type(json.loads(rsp)) + return rsp + + +def rsp_to_tasks(rsp: str) -> list[Task]: + rsp = json.loads(rsp) + tasks = [Task(**task_config) for task_config in rsp] + return tasks + + +def update_plan_from_rsp(rsp: str, current_plan: Plan): + tasks = rsp_to_tasks(rsp) + if len(tasks) == 1 or tasks[0].dependent_task_ids: + if tasks[0].dependent_task_ids and len(tasks) > 1: + # tasks[0].dependent_task_ids means the generated tasks are not a complete plan + # for they depend on tasks in the current plan, in this case, we only support updating one task each time + logger.warning( + "Current plan will take only the first generated task if the generated tasks are not a complete plan" + ) + # handle a single task + if current_plan.has_task_id(tasks[0].task_id): + # replace an existing task + current_plan.replace_task(tasks[0]) + else: + # append one task + current_plan.append_task(tasks[0]) + + else: + # add tasks in general + current_plan.add_tasks(tasks) + + +def precheck_update_plan_from_rsp(rsp: str, current_plan: Plan) -> Tuple[bool, str]: + temp_plan = deepcopy(current_plan) + try: + update_plan_from_rsp(rsp, temp_plan) + return True, "" + except Exception as e: + return False, e diff --git a/metagpt/actions/debug_error.py b/metagpt/actions/debug_error.py index 34f784072..5ed31bed8 100644 --- a/metagpt/actions/debug_error.py +++ b/metagpt/actions/debug_error.py @@ -13,12 +13,9 @@ from pydantic import Field from metagpt.actions.action import Action -from metagpt.config import CONFIG -from metagpt.const import TEST_CODES_FILE_REPO, TEST_OUTPUTS_FILE_REPO from metagpt.logs import logger from metagpt.schema import RunCodeContext, RunCodeResult from metagpt.utils.common import CodeParser -from metagpt.utils.file_repository import FileRepository PROMPT_TEMPLATE = """ NOTICE @@ -49,13 +46,10 @@ class DebugError(Action): - name: str = "DebugError" - context: RunCodeContext = Field(default_factory=RunCodeContext) + i_context: RunCodeContext = Field(default_factory=RunCodeContext) async def run(self, *args, **kwargs) -> str: - output_doc = await FileRepository.get_file( - filename=self.context.output_filename, relative_path=TEST_OUTPUTS_FILE_REPO - ) + output_doc = await self.repo.test_outputs.get(filename=self.i_context.output_filename) if not output_doc: return "" output_detail = RunCodeResult.loads(output_doc.content) @@ -64,15 +58,13 @@ async def run(self, *args, **kwargs) -> str: if matches: return "" - logger.info(f"Debug and rewrite {self.context.test_filename}") - code_doc = await FileRepository.get_file( - filename=self.context.code_filename, relative_path=CONFIG.src_workspace + logger.info(f"Debug and rewrite {self.i_context.test_filename}") + code_doc = await self.repo.with_src_path(self.context.src_workspace).srcs.get( + filename=self.i_context.code_filename ) if not code_doc: return "" - test_doc = await FileRepository.get_file( - filename=self.context.test_filename, relative_path=TEST_CODES_FILE_REPO - ) + test_doc = await self.repo.tests.get(filename=self.i_context.test_filename) if not test_doc: return "" prompt = PROMPT_TEMPLATE.format(code=code_doc.content, test_code=test_doc.content, logs=output_detail.stderr) diff --git a/metagpt/actions/design_api.py b/metagpt/actions/design_api.py index d24cd2ae3..e5f038c7c 100644 --- a/metagpt/actions/design_api.py +++ b/metagpt/actions/design_api.py @@ -22,17 +22,9 @@ REFINED_DESIGN_NODE, REFINED_PROGRAM_CALL_FLOW, ) -from metagpt.config import CONFIG -from metagpt.const import ( - DATA_API_DESIGN_FILE_REPO, - PRDS_FILE_REPO, - SEQ_FLOW_FILE_REPO, - SYSTEM_DESIGN_FILE_REPO, - SYSTEM_DESIGN_PDF_FILE_REPO, -) +from metagpt.const import DATA_API_DESIGN_FILE_REPO, SEQ_FLOW_FILE_REPO from metagpt.logs import logger from metagpt.schema import Document, Documents, Message -from metagpt.utils.file_repository import FileRepository from metagpt.utils.mermaid import mermaid_to_file NEW_REQ_TEMPLATE = """ @@ -46,36 +38,30 @@ class WriteDesign(Action): name: str = "" - context: Optional[str] = None + i_context: Optional[str] = None desc: str = ( "Based on the PRD, think about the system design, and design the corresponding APIs, " "data structures, library tables, processes, and paths. Please provide your design, feedback " "clearly and in detail." ) - async def run(self, with_messages: Message, schema: str = CONFIG.prompt_schema): - # Use `git status` to identify which PRD documents have been modified in the `docs/prds` directory. - prds_file_repo = CONFIG.git_repo.new_file_repository(PRDS_FILE_REPO) - changed_prds = prds_file_repo.changed_files + async def run(self, with_messages: Message, schema: str = None): + # Use `git status` to identify which PRD documents have been modified in the `docs/prd` directory. + changed_prds = self.repo.docs.prd.changed_files # Use `git status` to identify which design documents in the `docs/system_designs` directory have undergone # changes. - system_design_file_repo = CONFIG.git_repo.new_file_repository(SYSTEM_DESIGN_FILE_REPO) - changed_system_designs = system_design_file_repo.changed_files + changed_system_designs = self.repo.docs.system_design.changed_files # For those PRDs and design documents that have undergone changes, regenerate the design content. changed_files = Documents() for filename in changed_prds.keys(): - doc = await self._update_system_design( - filename=filename, prds_file_repo=prds_file_repo, system_design_file_repo=system_design_file_repo - ) + doc = await self._update_system_design(filename=filename) changed_files.docs[filename] = doc for filename in changed_system_designs.keys(): if filename in changed_files.docs: continue - doc = await self._update_system_design( - filename=filename, prds_file_repo=prds_file_repo, system_design_file_repo=system_design_file_repo - ) + doc = await self._update_system_design(filename=filename) changed_files.docs[filename] = doc if not changed_files.docs: logger.info("Nothing has changed.") @@ -83,61 +69,52 @@ async def run(self, with_messages: Message, schema: str = CONFIG.prompt_schema): # leaving room for global optimization in subsequent steps. return ActionOutput(content=changed_files.model_dump_json(), instruct_content=changed_files) - async def _new_system_design(self, context, schema=CONFIG.prompt_schema): - node = await DESIGN_API_NODE.fill(context=context, llm=self.llm, schema=schema) + async def _new_system_design(self, context): + node = await DESIGN_API_NODE.fill(context=context, llm=self.llm) return node - async def _merge(self, prd_doc, system_design_doc, schema=CONFIG.prompt_schema): + async def _merge(self, prd_doc, system_design_doc): context = NEW_REQ_TEMPLATE.format(old_design=system_design_doc.content, context=prd_doc.content) - node = await REFINED_DESIGN_NODE.fill(context=context, llm=self.llm, schema=schema) + node = await REFINED_DESIGN_NODE.fill(context=context, llm=self.llm) system_design_doc.content = node.instruct_content.model_dump_json() return system_design_doc - async def _update_system_design(self, filename, prds_file_repo, system_design_file_repo) -> Document: - prd = await prds_file_repo.get(filename) - old_system_design_doc = await system_design_file_repo.get(filename) + async def _update_system_design(self, filename) -> Document: + prd = await self.repo.docs.prd.get(filename) + old_system_design_doc = await self.repo.docs.system_design.get(filename) if not old_system_design_doc: system_design = await self._new_system_design(context=prd.content) - doc = Document( - root_path=SYSTEM_DESIGN_FILE_REPO, + doc = await self.repo.docs.system_design.save( filename=filename, content=system_design.instruct_content.model_dump_json(), + dependencies={prd.root_relative_path}, ) else: doc = await self._merge(prd_doc=prd, system_design_doc=old_system_design_doc) - await system_design_file_repo.save( - filename=filename, content=doc.content, dependencies={prd.root_relative_path} - ) + await self.repo.docs.system_design.save_doc(doc=doc, dependencies={prd.root_relative_path}) await self._save_data_api_design(doc) await self._save_seq_flow(doc) - await self._save_pdf(doc) + await self.repo.resources.system_design.save_pdf(doc=doc) return doc - @staticmethod - async def _save_data_api_design(design_doc): + async def _save_data_api_design(self, design_doc): m = json.loads(design_doc.content) data_api_design = m.get(DATA_STRUCTURES_AND_INTERFACES.key) or m.get(REFINED_DATA_STRUCTURES_AND_INTERFACES.key) if not data_api_design: return - pathname = CONFIG.git_repo.workdir / DATA_API_DESIGN_FILE_REPO / Path(design_doc.filename).with_suffix("") - await WriteDesign._save_mermaid_file(data_api_design, pathname) + pathname = self.repo.workdir / DATA_API_DESIGN_FILE_REPO / Path(design_doc.filename).with_suffix("") + await self._save_mermaid_file(data_api_design, pathname) logger.info(f"Save class view to {str(pathname)}") - @staticmethod - async def _save_seq_flow(design_doc): + async def _save_seq_flow(self, design_doc): m = json.loads(design_doc.content) seq_flow = m.get(PROGRAM_CALL_FLOW.key) or m.get(REFINED_PROGRAM_CALL_FLOW.key) if not seq_flow: return - pathname = CONFIG.git_repo.workdir / Path(SEQ_FLOW_FILE_REPO) / Path(design_doc.filename).with_suffix("") - await WriteDesign._save_mermaid_file(seq_flow, pathname) + pathname = self.repo.workdir / Path(SEQ_FLOW_FILE_REPO) / Path(design_doc.filename).with_suffix("") + await self._save_mermaid_file(seq_flow, pathname) logger.info(f"Saving sequence flow to {str(pathname)}") - @staticmethod - async def _save_pdf(design_doc): - await FileRepository.save_as(doc=design_doc, with_suffix=".md", relative_path=SYSTEM_DESIGN_PDF_FILE_REPO) - - @staticmethod - async def _save_mermaid_file(data: str, pathname: Path): + async def _save_mermaid_file(self, data: str, pathname: Path): pathname.parent.mkdir(parents=True, exist_ok=True) - await mermaid_to_file(data, pathname) + await mermaid_to_file(self.config.mermaid.engine, data, pathname) diff --git a/metagpt/actions/design_api_review.py b/metagpt/actions/design_api_review.py index fb1b92d85..ccd01a4c3 100644 --- a/metagpt/actions/design_api_review.py +++ b/metagpt/actions/design_api_review.py @@ -13,7 +13,7 @@ class DesignReview(Action): name: str = "DesignReview" - context: Optional[str] = None + i_context: Optional[str] = None async def run(self, prd, api_design): prompt = ( diff --git a/metagpt/actions/execute_task.py b/metagpt/actions/execute_task.py index 4ae4ee17b..1cc3bd699 100644 --- a/metagpt/actions/execute_task.py +++ b/metagpt/actions/execute_task.py @@ -13,7 +13,7 @@ class ExecuteTask(Action): name: str = "ExecuteTask" - context: list[Message] = [] + i_context: list[Message] = [] async def run(self, *args, **kwargs): pass diff --git a/metagpt/actions/generate_questions.py b/metagpt/actions/generate_questions.py index 8573708f2..c96a37649 100644 --- a/metagpt/actions/generate_questions.py +++ b/metagpt/actions/generate_questions.py @@ -1,8 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- """ -@Time : 2023/9/12 17:45 -@Author : fisherdeng @File : generate_questions.py """ from metagpt.actions import Action @@ -23,5 +21,5 @@ class GenerateQuestions(Action): name: str = "GenerateQuestions" - async def run(self, context): + async def run(self, context) -> ActionNode: return await QUESTIONS.fill(context=context, llm=self.llm) diff --git a/metagpt/actions/invoice_ocr.py b/metagpt/actions/invoice_ocr.py index 36570097a..7cf71a8ff 100644 --- a/metagpt/actions/invoice_ocr.py +++ b/metagpt/actions/invoice_ocr.py @@ -16,17 +16,14 @@ import pandas as pd from paddleocr import PaddleOCR -from pydantic import Field from metagpt.actions import Action from metagpt.const import INVOICE_OCR_TABLE_PATH -from metagpt.llm import LLM from metagpt.logs import logger from metagpt.prompts.invoice_ocr import ( EXTRACT_OCR_MAIN_INFO_PROMPT, REPLY_OCR_QUESTION_PROMPT, ) -from metagpt.provider.base_llm import BaseLLM from metagpt.utils.common import OutputParser from metagpt.utils.file import File @@ -41,7 +38,7 @@ class InvoiceOCR(Action): """ name: str = "InvoiceOCR" - context: Optional[str] = None + i_context: Optional[str] = None @staticmethod async def _check_file_type(file_path: Path) -> str: @@ -132,8 +129,7 @@ class GenerateTable(Action): """ name: str = "GenerateTable" - context: Optional[str] = None - llm: BaseLLM = Field(default_factory=LLM) + i_context: Optional[str] = None language: str = "ch" async def run(self, ocr_results: list, filename: str, *args, **kwargs) -> dict[str, str]: @@ -176,9 +172,6 @@ class ReplyQuestion(Action): """ - name: str = "ReplyQuestion" - context: Optional[str] = None - llm: BaseLLM = Field(default_factory=LLM) language: str = "ch" async def run(self, query: str, ocr_result: list, *args, **kwargs) -> str: diff --git a/metagpt/actions/prepare_documents.py b/metagpt/actions/prepare_documents.py index 5c5798d95..ab069dc11 100644 --- a/metagpt/actions/prepare_documents.py +++ b/metagpt/actions/prepare_documents.py @@ -12,39 +12,41 @@ from typing import Optional from metagpt.actions import Action, ActionOutput -from metagpt.config import CONFIG -from metagpt.const import DOCS_FILE_REPO, REQUIREMENT_FILENAME -from metagpt.schema import Document +from metagpt.const import REQUIREMENT_FILENAME from metagpt.utils.file_repository import FileRepository from metagpt.utils.git_repository import GitRepository +from metagpt.utils.project_repo import ProjectRepo class PrepareDocuments(Action): """PrepareDocuments Action: initialize project folder and add new requirements to docs/requirements.txt.""" name: str = "PrepareDocuments" - context: Optional[str] = None + i_context: Optional[str] = None + + @property + def config(self): + return self.context.config def _init_repo(self): """Initialize the Git environment.""" - if not CONFIG.project_path: - name = CONFIG.project_name or FileRepository.new_filename() - path = Path(CONFIG.workspace_path) / name + if not self.config.project_path: + name = self.config.project_name or FileRepository.new_filename() + path = Path(self.config.workspace.path) / name else: - path = Path(CONFIG.project_path) - if path.exists() and not CONFIG.inc: + path = Path(self.config.project_path) + if path.exists() and not self.config.inc: shutil.rmtree(path) - CONFIG.project_path = path - CONFIG.git_repo = GitRepository(local_path=path, auto_init=True) + self.config.project_path = path + self.context.git_repo = GitRepository(local_path=path, auto_init=True) + self.context.repo = ProjectRepo(self.context.git_repo) async def run(self, with_messages, **kwargs): """Create and initialize the workspace folder, initialize the Git environment.""" self._init_repo() # Write the newly added requirements from the main parameter idea to `docs/requirement.txt`. - doc = Document(root_path=DOCS_FILE_REPO, filename=REQUIREMENT_FILENAME, content=with_messages[0].content) - await FileRepository.save_file(filename=REQUIREMENT_FILENAME, content=doc.content, relative_path=DOCS_FILE_REPO) - + doc = await self.repo.docs.save(filename=REQUIREMENT_FILENAME, content=with_messages[0].content) # Send a Message notification to the WritePRD action, instructing it to process requirements using - # `docs/requirement.txt` and `docs/prds/`. + # `docs/requirement.txt` and `docs/prd/`. return ActionOutput(content=doc.content, instruct_content=doc) diff --git a/metagpt/actions/project_management.py b/metagpt/actions/project_management.py index 448a8afcd..67a614d6f 100644 --- a/metagpt/actions/project_management.py +++ b/metagpt/actions/project_management.py @@ -13,23 +13,16 @@ import json from typing import Optional -from metagpt.actions import ActionOutput from metagpt.actions.action import Action +from metagpt.actions.action_output import ActionOutput from metagpt.actions.project_management_an import PM_NODE, REFINED_PM_NODE -from metagpt.config import CONFIG -from metagpt.const import ( - PACKAGE_REQUIREMENTS_FILENAME, - SYSTEM_DESIGN_FILE_REPO, - TASK_FILE_REPO, - TASK_PDF_FILE_REPO, -) +from metagpt.const import PACKAGE_REQUIREMENTS_FILENAME from metagpt.logs import logger from metagpt.schema import Document, Documents -from metagpt.utils.file_repository import FileRepository NEW_REQ_TEMPLATE = """ ### Legacy Content -{old_tasks} +{old_task} ### New Requirements {context} @@ -38,30 +31,23 @@ class WriteTasks(Action): name: str = "CreateTasks" - context: Optional[str] = None + i_context: Optional[str] = None - async def run(self, with_messages, schema=CONFIG.prompt_schema): - system_design_file_repo = CONFIG.git_repo.new_file_repository(SYSTEM_DESIGN_FILE_REPO) - changed_system_designs = system_design_file_repo.changed_files - - tasks_file_repo = CONFIG.git_repo.new_file_repository(TASK_FILE_REPO) - changed_tasks = tasks_file_repo.changed_files + async def run(self, with_messages): + changed_system_designs = self.repo.docs.system_design.changed_files + changed_tasks = self.repo.docs.task.changed_files change_files = Documents() # Rewrite the system designs that have undergone changes based on the git head diff under # `docs/system_designs/`. for filename in changed_system_designs: - task_doc = await self._update_tasks( - filename=filename, system_design_file_repo=system_design_file_repo, tasks_file_repo=tasks_file_repo - ) + task_doc = await self._update_tasks(filename=filename) change_files.docs[filename] = task_doc # Rewrite the task files that have undergone changes based on the git head diff under `docs/tasks/`. for filename in changed_tasks: if filename in change_files.docs: continue - task_doc = await self._update_tasks( - filename=filename, system_design_file_repo=system_design_file_repo, tasks_file_repo=tasks_file_repo - ) + task_doc = await self._update_tasks(filename=filename) change_files.docs[filename] = task_doc if not change_files.docs: @@ -70,39 +56,36 @@ async def run(self, with_messages, schema=CONFIG.prompt_schema): # global optimization in subsequent steps. return ActionOutput(content=change_files.model_dump_json(), instruct_content=change_files) - async def _update_tasks(self, filename, system_design_file_repo, tasks_file_repo): - system_design_doc = await system_design_file_repo.get(filename) - task_doc = await tasks_file_repo.get(filename) + async def _update_tasks(self, filename): + system_design_doc = await self.repo.docs.system_design.get(filename) + task_doc = await self.repo.docs.task.get(filename) if task_doc: task_doc = await self._merge(system_design_doc=system_design_doc, task_doc=task_doc) + await self.repo.docs.task.save_doc(doc=task_doc, dependencies={system_design_doc.root_relative_path}) else: rsp = await self._run_new_tasks(context=system_design_doc.content) - task_doc = Document( - root_path=TASK_FILE_REPO, filename=filename, content=rsp.instruct_content.model_dump_json() + task_doc = await self.repo.docs.task.save( + filename=filename, + content=rsp.instruct_content.model_dump_json(), + dependencies={system_design_doc.root_relative_path}, ) - await tasks_file_repo.save( - filename=filename, content=task_doc.content, dependencies={system_design_doc.root_relative_path} - ) await self._update_requirements(task_doc) - await self._save_pdf(task_doc=task_doc) return task_doc - async def _run_new_tasks(self, context, schema=CONFIG.prompt_schema): - node = await PM_NODE.fill(context, self.llm, schema) + async def _run_new_tasks(self, context): + node = await PM_NODE.fill(context, self.llm, schema=self.prompt_schema) return node - async def _merge(self, system_design_doc, task_doc, schema=CONFIG.prompt_schema) -> Document: - context = NEW_REQ_TEMPLATE.format(context=system_design_doc.content, old_tasks=task_doc.content) - node = await REFINED_PM_NODE.fill(context, self.llm, schema) + async def _merge(self, system_design_doc, task_doc) -> Document: + context = NEW_REQ_TEMPLATE.format(context=system_design_doc.content, old_task=task_doc.content) + node = await REFINED_PM_NODE.fill(context, self.llm, schema=self.prompt_schema) task_doc.content = node.instruct_content.model_dump_json() return task_doc - @staticmethod - async def _update_requirements(doc): + async def _update_requirements(self, doc): m = json.loads(doc.content) packages = set(m.get("Required Python packages", set())) - file_repo = CONFIG.git_repo.new_file_repository() - requirement_doc = await file_repo.get(filename=PACKAGE_REQUIREMENTS_FILENAME) + requirement_doc = await self.repo.get(filename=PACKAGE_REQUIREMENTS_FILENAME) if not requirement_doc: requirement_doc = Document(filename=PACKAGE_REQUIREMENTS_FILENAME, root_path=".", content="") lines = requirement_doc.content.splitlines() @@ -110,8 +93,4 @@ async def _update_requirements(doc): if pkg == "": continue packages.add(pkg) - await file_repo.save(PACKAGE_REQUIREMENTS_FILENAME, content="\n".join(packages)) - - @staticmethod - async def _save_pdf(task_doc): - await FileRepository.save_as(doc=task_doc, with_suffix=".md", relative_path=TASK_PDF_FILE_REPO) + await self.repo.save(filename=PACKAGE_REQUIREMENTS_FILENAME, content="\n".join(packages)) diff --git a/metagpt/actions/rebuild_class_view.py b/metagpt/actions/rebuild_class_view.py index 5128b9fee..2140ad874 100644 --- a/metagpt/actions/rebuild_class_view.py +++ b/metagpt/actions/rebuild_class_view.py @@ -12,7 +12,7 @@ import aiofiles from metagpt.actions import Action -from metagpt.config import CONFIG +from metagpt.config2 import config from metagpt.const import ( AGGREGATION, COMPOSITION, @@ -29,16 +29,16 @@ class RebuildClassView(Action): - async def run(self, with_messages=None, format=CONFIG.prompt_schema): - graph_repo_pathname = CONFIG.git_repo.workdir / GRAPH_REPO_FILE_REPO / CONFIG.git_repo.workdir.name + async def run(self, with_messages=None, format=config.prompt_schema): + graph_repo_pathname = self.context.git_repo.workdir / GRAPH_REPO_FILE_REPO / self.context.git_repo.workdir.name graph_db = await DiGraphRepository.load_from(str(graph_repo_pathname.with_suffix(".json"))) - repo_parser = RepoParser(base_directory=Path(self.context)) + repo_parser = RepoParser(base_directory=Path(self.i_context)) # use pylint - class_views, relationship_views, package_root = await repo_parser.rebuild_class_views(path=Path(self.context)) + class_views, relationship_views, package_root = await repo_parser.rebuild_class_views(path=Path(self.i_context)) await GraphRepository.update_graph_db_with_class_views(graph_db, class_views) await GraphRepository.update_graph_db_with_class_relationship_views(graph_db, relationship_views) # use ast - direction, diff_path = self._diff_path(path_root=Path(self.context).resolve(), package_root=package_root) + direction, diff_path = self._diff_path(path_root=Path(self.i_context).resolve(), package_root=package_root) symbols = repo_parser.generate_symbols() for file_info in symbols: # Align to the same root directory in accordance with `class_views`. @@ -48,9 +48,9 @@ async def run(self, with_messages=None, format=CONFIG.prompt_schema): await graph_db.save() async def _create_mermaid_class_views(self, graph_db): - path = Path(CONFIG.git_repo.workdir) / DATA_API_DESIGN_FILE_REPO + path = Path(self.context.git_repo.workdir) / DATA_API_DESIGN_FILE_REPO path.mkdir(parents=True, exist_ok=True) - pathname = path / CONFIG.git_repo.workdir.name + pathname = path / self.context.git_repo.workdir.name async with aiofiles.open(str(pathname.with_suffix(".mmd")), mode="w", encoding="utf-8") as writer: content = "classDiagram\n" logger.debug(content) diff --git a/metagpt/actions/rebuild_sequence_view.py b/metagpt/actions/rebuild_sequence_view.py index 865050c93..777dde8ce 100644 --- a/metagpt/actions/rebuild_sequence_view.py +++ b/metagpt/actions/rebuild_sequence_view.py @@ -12,7 +12,7 @@ from typing import List from metagpt.actions import Action -from metagpt.config import CONFIG +from metagpt.config2 import config from metagpt.const import GRAPH_REPO_FILE_REPO from metagpt.logs import logger from metagpt.utils.common import aread, list_files @@ -21,8 +21,8 @@ class RebuildSequenceView(Action): - async def run(self, with_messages=None, format=CONFIG.prompt_schema): - graph_repo_pathname = CONFIG.git_repo.workdir / GRAPH_REPO_FILE_REPO / CONFIG.git_repo.workdir.name + async def run(self, with_messages=None, format=config.prompt_schema): + graph_repo_pathname = self.context.git_repo.workdir / GRAPH_REPO_FILE_REPO / self.context.git_repo.workdir.name graph_db = await DiGraphRepository.load_from(str(graph_repo_pathname.with_suffix(".json"))) entries = await RebuildSequenceView._search_main_entry(graph_db) for entry in entries: @@ -41,7 +41,9 @@ async def _search_main_entry(graph_db) -> List: async def _rebuild_sequence_view(self, entry, graph_db): filename = entry.subject.split(":", 1)[0] - src_filename = RebuildSequenceView._get_full_filename(root=self.context, pathname=filename) + src_filename = RebuildSequenceView._get_full_filename(root=self.i_context, pathname=filename) + if not src_filename: + return content = await aread(filename=src_filename, encoding="utf-8") content = f"```python\n{content}\n```\n\n---\nTranslate the code above into Mermaid Sequence Diagram." data = await self.llm.aask( diff --git a/metagpt/actions/research.py b/metagpt/actions/research.py index 90b08cb6a..2ebeadb66 100644 --- a/metagpt/actions/research.py +++ b/metagpt/actions/research.py @@ -3,17 +3,15 @@ from __future__ import annotations import asyncio -from typing import Callable, Optional, Union +from typing import Any, Callable, Optional, Union -from pydantic import Field, parse_obj_as +from pydantic import TypeAdapter, model_validator from metagpt.actions import Action -from metagpt.config import CONFIG -from metagpt.llm import LLM +from metagpt.config2 import config from metagpt.logs import logger -from metagpt.provider.base_llm import BaseLLM from metagpt.tools.search_engine import SearchEngine -from metagpt.tools.web_browser_engine import WebBrowserEngine, WebBrowserEngineType +from metagpt.tools.web_browser_engine import WebBrowserEngine from metagpt.utils.common import OutputParser from metagpt.utils.text import generate_prompt_chunk, reduce_message_length @@ -81,12 +79,18 @@ class CollectLinks(Action): """Action class to collect links from a search engine.""" name: str = "CollectLinks" - context: Optional[str] = None + i_context: Optional[str] = None desc: str = "Collect links from a search engine." - - search_engine: SearchEngine = Field(default_factory=SearchEngine) + search_func: Optional[Any] = None + search_engine: Optional[SearchEngine] = None rank_func: Optional[Callable[[list[str]], None]] = None + @model_validator(mode="after") + def validate_engine_and_run_func(self): + if self.search_engine is None: + self.search_engine = SearchEngine.from_search_config(self.config.search, proxy=self.config.proxy) + return self + async def run( self, topic: str, @@ -109,7 +113,7 @@ async def run( keywords = await self._aask(SEARCH_TOPIC_PROMPT, [system_text]) try: keywords = OutputParser.extract_struct(keywords, list) - keywords = parse_obj_as(list[str], keywords) + keywords = TypeAdapter(list[str]).validate_python(keywords) except Exception as e: logger.exception(f"fail to get keywords related to the research topic '{topic}' for {e}") keywords = [topic] @@ -129,13 +133,13 @@ def gen_msg(): if len(remove) == 0: break - model_name = CONFIG.get_model_name(CONFIG.get_default_llm_provider_enum()) - prompt = reduce_message_length(gen_msg(), model_name, system_text, CONFIG.max_tokens_rsp) + model_name = config.get_openai_llm().model + prompt = reduce_message_length(gen_msg(), model_name, system_text, 4096) logger.debug(prompt) queries = await self._aask(prompt, [system_text]) try: queries = OutputParser.extract_struct(queries, list) - queries = parse_obj_as(list[str], queries) + queries = TypeAdapter(list[str]).validate_python(queries) except Exception as e: logger.exception(f"fail to break down the research question due to {e}") queries = keywords @@ -177,21 +181,20 @@ class WebBrowseAndSummarize(Action): """Action class to explore the web and provide summaries of articles and webpages.""" name: str = "WebBrowseAndSummarize" - context: Optional[str] = None - llm: BaseLLM = Field(default_factory=LLM) + i_context: Optional[str] = None desc: str = "Explore the web and provide summaries of articles and webpages." browse_func: Union[Callable[[list[str]], None], None] = None web_browser_engine: Optional[WebBrowserEngine] = None - def __init__(self, **kwargs): - super().__init__(**kwargs) - if CONFIG.model_for_researcher_summary: - self.llm.model = CONFIG.model_for_researcher_summary - - self.web_browser_engine = WebBrowserEngine( - engine=WebBrowserEngineType.CUSTOM if self.browse_func else None, - run_func=self.browse_func, - ) + @model_validator(mode="after") + def validate_engine_and_run_func(self): + if self.web_browser_engine is None: + self.web_browser_engine = WebBrowserEngine.from_browser_config( + self.config.browser, + browse_func=self.browse_func, + proxy=self.config.proxy, + ) + return self async def run( self, @@ -220,9 +223,7 @@ async def run( for u, content in zip([url, *urls], contents): content = content.inner_text chunk_summaries = [] - for prompt in generate_prompt_chunk( - content, prompt_template, self.llm.model, system_text, CONFIG.max_tokens_rsp - ): + for prompt in generate_prompt_chunk(content, prompt_template, self.llm.model, system_text, 4096): logger.debug(prompt) summary = await self._aask(prompt, [system_text]) if summary == "Not relevant.": @@ -247,14 +248,8 @@ async def run( class ConductResearch(Action): """Action class to conduct research and generate a research report.""" - name: str = "ConductResearch" - context: Optional[str] = None - llm: BaseLLM = Field(default_factory=LLM) - def __init__(self, **kwargs): super().__init__(**kwargs) - if CONFIG.model_for_researcher_report: - self.llm.model = CONFIG.model_for_researcher_report async def run( self, diff --git a/metagpt/actions/run_code.py b/metagpt/actions/run_code.py index ee31cb71b..b2c33c19b 100644 --- a/metagpt/actions/run_code.py +++ b/metagpt/actions/run_code.py @@ -22,7 +22,6 @@ from pydantic import Field from metagpt.actions.action import Action -from metagpt.config import CONFIG from metagpt.logs import logger from metagpt.schema import RunCodeContext, RunCodeResult from metagpt.utils.exceptions import handle_exception @@ -49,7 +48,7 @@ You should fill in necessary instruction, status, send to, and finally return all content between the --- segment line. """ -CONTEXT = """ +TEMPLATE_CONTEXT = """ ## Development Code File Name {code_file_name} ## Development Code @@ -78,7 +77,7 @@ class RunCode(Action): name: str = "RunCode" - context: RunCodeContext = Field(default_factory=RunCodeContext) + i_context: RunCodeContext = Field(default_factory=RunCodeContext) @classmethod async def run_text(cls, code) -> Tuple[str, str]: @@ -90,13 +89,12 @@ async def run_text(cls, code) -> Tuple[str, str]: return "", str(e) return namespace.get("result", ""), "" - @classmethod - async def run_script(cls, working_directory, additional_python_paths=[], command=[]) -> Tuple[str, str]: + async def run_script(self, working_directory, additional_python_paths=[], command=[]) -> Tuple[str, str]: working_directory = str(working_directory) additional_python_paths = [str(path) for path in additional_python_paths] # Copy the current environment variables - env = CONFIG.new_environ() + env = self.context.new_environ() # Modify the PYTHONPATH environment variable additional_python_paths = [working_directory] + additional_python_paths @@ -120,25 +118,25 @@ async def run_script(cls, working_directory, additional_python_paths=[], command return stdout.decode("utf-8"), stderr.decode("utf-8") async def run(self, *args, **kwargs) -> RunCodeResult: - logger.info(f"Running {' '.join(self.context.command)}") - if self.context.mode == "script": + logger.info(f"Running {' '.join(self.i_context.command)}") + if self.i_context.mode == "script": outs, errs = await self.run_script( - command=self.context.command, - working_directory=self.context.working_directory, - additional_python_paths=self.context.additional_python_paths, + command=self.i_context.command, + working_directory=self.i_context.working_directory, + additional_python_paths=self.i_context.additional_python_paths, ) - elif self.context.mode == "text": - outs, errs = await self.run_text(code=self.context.code) + elif self.i_context.mode == "text": + outs, errs = await self.run_text(code=self.i_context.code) logger.info(f"{outs=}") logger.info(f"{errs=}") - context = CONTEXT.format( - code=self.context.code, - code_file_name=self.context.code_filename, - test_code=self.context.test_code, - test_file_name=self.context.test_filename, - command=" ".join(self.context.command), + context = TEMPLATE_CONTEXT.format( + code=self.i_context.code, + code_file_name=self.i_context.code_filename, + test_code=self.i_context.test_code, + test_file_name=self.i_context.test_filename, + command=" ".join(self.i_context.command), outs=outs[:500], # outs might be long but they are not important, truncate them to avoid token overflow errs=errs[:10000], # truncate errors to avoid token overflow ) diff --git a/metagpt/actions/search_and_summarize.py b/metagpt/actions/search_and_summarize.py index d2e361f73..7eed7381b 100644 --- a/metagpt/actions/search_and_summarize.py +++ b/metagpt/actions/search_and_summarize.py @@ -5,16 +5,14 @@ @Author : alexanderwu @File : search_google.py """ -from typing import Any, Optional +from typing import Optional import pydantic -from pydantic import Field, model_validator +from pydantic import model_validator from metagpt.actions import Action -from metagpt.config import CONFIG, Config from metagpt.logs import logger from metagpt.schema import Message -from metagpt.tools import SearchEngineType from metagpt.tools.search_engine import SearchEngine SEARCH_AND_SUMMARIZE_SYSTEM = """### Requirements @@ -103,32 +101,23 @@ """ -# TOTEST class SearchAndSummarize(Action): name: str = "" content: Optional[str] = None - config: None = Field(default_factory=Config) - engine: Optional[SearchEngineType] = CONFIG.search_engine - search_func: Optional[Any] = None search_engine: SearchEngine = None result: str = "" - @model_validator(mode="before") - @classmethod - def validate_engine_and_run_func(cls, values): - engine = values.get("engine") - search_func = values.get("search_func") - config = Config() - - if engine is None: - engine = config.search_engine - try: - search_engine = SearchEngine(engine=engine, run_func=search_func) - except pydantic.ValidationError: - search_engine = None - - values["search_engine"] = search_engine - return values + @model_validator(mode="after") + def validate_search_engine(self): + if self.search_engine is None: + try: + config = self.config + search_engine = SearchEngine.from_search_config(config.search, proxy=config.proxy) + except pydantic.ValidationError: + search_engine = None + + self.search_engine = search_engine + return self async def run(self, context: list[Message], system_text=SEARCH_AND_SUMMARIZE_SYSTEM) -> str: if self.search_engine is None: diff --git a/metagpt/actions/skill_action.py b/metagpt/actions/skill_action.py index 301cebaab..b68596809 100644 --- a/metagpt/actions/skill_action.py +++ b/metagpt/actions/skill_action.py @@ -29,9 +29,7 @@ class ArgumentsParingAction(Action): @property def prompt(self): - prompt = "You are a function parser. You can convert spoken words into function parameters.\n" - prompt += "\n---\n" - prompt += f"{self.skill.name} function parameters description:\n" + prompt = f"{self.skill.name} function parameters description:\n" for k, v in self.skill.arguments.items(): prompt += f"parameter `{k}`: {v}\n" prompt += "\n---\n" @@ -49,7 +47,10 @@ def prompt(self): async def run(self, with_message=None, **kwargs) -> Message: prompt = self.prompt - rsp = await self.llm.aask(msg=prompt, system_msgs=[]) + rsp = await self.llm.aask( + msg=prompt, + system_msgs=["You are a function parser.", "You can convert spoken words into function parameters."], + ) logger.debug(f"SKILL:{prompt}\n, RESULT:{rsp}") self.args = ArgumentsParingAction.parse_arguments(skill_name=self.skill.name, txt=rsp) self.rsp = Message(content=rsp, role="assistant", instruct_content=self.args, cause_by=self) diff --git a/metagpt/actions/summarize_code.py b/metagpt/actions/summarize_code.py index bdad546d7..d21b62f83 100644 --- a/metagpt/actions/summarize_code.py +++ b/metagpt/actions/summarize_code.py @@ -11,11 +11,8 @@ from tenacity import retry, stop_after_attempt, wait_random_exponential from metagpt.actions.action import Action -from metagpt.config import CONFIG -from metagpt.const import SYSTEM_DESIGN_FILE_REPO, TASK_FILE_REPO from metagpt.logs import logger from metagpt.schema import CodeSummarizeContext -from metagpt.utils.file_repository import FileRepository PROMPT_TEMPLATE = """ NOTICE @@ -29,9 +26,9 @@ {system_design} ``` ----- -# Tasks +# Task ```text -{tasks} +{task} ``` ----- {code_blocks} @@ -90,10 +87,9 @@ """ -# TOTEST class SummarizeCode(Action): name: str = "SummarizeCode" - context: CodeSummarizeContext = Field(default_factory=CodeSummarizeContext) + i_context: CodeSummarizeContext = Field(default_factory=CodeSummarizeContext) @retry(stop=stop_after_attempt(2), wait=wait_random_exponential(min=1, max=60)) async def summarize_code(self, prompt): @@ -101,20 +97,20 @@ async def summarize_code(self, prompt): return code_rsp async def run(self): - design_pathname = Path(self.context.design_filename) - design_doc = await FileRepository.get_file(filename=design_pathname.name, relative_path=SYSTEM_DESIGN_FILE_REPO) - task_pathname = Path(self.context.task_filename) - task_doc = await FileRepository.get_file(filename=task_pathname.name, relative_path=TASK_FILE_REPO) - src_file_repo = CONFIG.git_repo.new_file_repository(relative_path=CONFIG.src_workspace) + design_pathname = Path(self.i_context.design_filename) + design_doc = await self.repo.docs.system_design.get(filename=design_pathname.name) + task_pathname = Path(self.i_context.task_filename) + task_doc = await self.repo.docs.task.get(filename=task_pathname.name) + src_file_repo = self.repo.with_src_path(self.context.src_workspace).srcs code_blocks = [] - for filename in self.context.codes_filenames: + for filename in self.i_context.codes_filenames: code_doc = await src_file_repo.get(filename) code_block = f"```python\n{code_doc.content}\n```\n-----" code_blocks.append(code_block) format_example = FORMAT_EXAMPLE prompt = PROMPT_TEMPLATE.format( system_design=design_doc.content, - tasks=task_doc.content, + task=task_doc.content, code_blocks="\n".join(code_blocks), format_example=format_example, ) diff --git a/metagpt/actions/talk_action.py b/metagpt/actions/talk_action.py index dc127109c..0aac1c5a0 100644 --- a/metagpt/actions/talk_action.py +++ b/metagpt/actions/talk_action.py @@ -9,25 +9,31 @@ from typing import Optional from metagpt.actions import Action -from metagpt.config import CONFIG -from metagpt.const import DEFAULT_LANGUAGE +from metagpt.config2 import config from metagpt.logs import logger from metagpt.schema import Message -# TOTEST class TalkAction(Action): - context: str + i_context: str history_summary: str = "" knowledge: str = "" rsp: Optional[Message] = None + @property + def agent_description(self): + return self.context.kwargs.agent_description + + @property + def language(self): + return self.context.kwargs.language or config.language + @property def prompt(self): prompt = "" - if CONFIG.agent_description: + if self.agent_description: prompt = ( - f"You are {CONFIG.agent_description}. Your responses should align with the role-play agreement, " + f"You are {self.agent_description}. Your responses should align with the role-play agreement, " f"maintaining the character's persona and habits. When faced with unrelated questions, playfully " f"decline to answer without revealing your AI nature to preserve the character's image.\n\n" ) @@ -36,10 +42,10 @@ def prompt(self): prompt += ( "If the information is insufficient, you can search in the historical conversation or knowledge above.\n" ) - language = CONFIG.language or DEFAULT_LANGUAGE + language = self.language prompt += ( f"Answer the following questions strictly in {language}, and the answers must follow the Markdown format.\n " - f"{self.context}" + f"{self.i_context}" ) logger.debug(f"PROMPT: {prompt}") return prompt @@ -47,11 +53,11 @@ def prompt(self): @property def prompt_gpt4(self): kvs = { - "{role}": CONFIG.agent_description or "", + "{role}": self.agent_description or "", "{history}": self.history_summary or "", "{knowledge}": self.knowledge or "", - "{language}": CONFIG.language or DEFAULT_LANGUAGE, - "{ask}": self.context, + "{language}": self.language, + "{ask}": self.i_context, } prompt = TalkActionPrompt.FORMATION_LOOSE for k, v in kvs.items(): @@ -68,9 +74,9 @@ def prompt_gpt4(self): @property def aask_args(self): - language = CONFIG.language or DEFAULT_LANGUAGE + language = self.language system_msgs = [ - f"You are {CONFIG.agent_description}.", + f"You are {self.agent_description}.", "Your responses should align with the role-play agreement, " "maintaining the character's persona and habits. When faced with unrelated questions, playfully " "decline to answer without revealing your AI nature to preserve the character's image.", @@ -82,7 +88,7 @@ def aask_args(self): format_msgs.append({"role": "assistant", "content": self.knowledge}) if self.history_summary: format_msgs.append({"role": "assistant", "content": self.history_summary}) - return self.context, format_msgs, system_msgs + return self.i_context, format_msgs, system_msgs async def run(self, with_message=None, **kwargs) -> Message: msg, format_msgs, system_msgs = self.aask_args diff --git a/metagpt/actions/write_code.py b/metagpt/actions/write_code.py index 818990991..0b86ac1bb 100644 --- a/metagpt/actions/write_code.py +++ b/metagpt/actions/write_code.py @@ -16,7 +16,6 @@ """ import json -from typing import Literal from pydantic import Field from tenacity import retry, stop_after_attempt, wait_random_exponential @@ -24,21 +23,15 @@ from metagpt.actions.action import Action from metagpt.actions.project_management_an import REFINED_TASK_LIST, TASK_LIST from metagpt.actions.write_code_plan_and_change_an import REFINED_TEMPLATE -from metagpt.config import CONFIG from metagpt.const import ( BUGFIX_FILENAME, - CODE_PLAN_AND_CHANGE_FILE_REPO, CODE_PLAN_AND_CHANGE_FILENAME, - CODE_SUMMARIES_FILE_REPO, - DOCS_FILE_REPO, REQUIREMENT_FILENAME, - TASK_FILE_REPO, - TEST_OUTPUTS_FILE_REPO, ) from metagpt.logs import logger from metagpt.schema import CodingContext, Document, RunCodeResult from metagpt.utils.common import CodeParser -from metagpt.utils.file_repository import FileRepository +from metagpt.utils.project_repo import ProjectRepo PROMPT_TEMPLATE = """ NOTICE @@ -50,8 +43,8 @@ ## Design {design} -## Tasks -{tasks} +## Task +{task} ## Legacy Code ```Code @@ -93,7 +86,7 @@ class WriteCode(Action): name: str = "WriteCode" - context: Document = Field(default_factory=Document) + i_context: Document = Field(default_factory=Document) @retry(wait=wait_random_exponential(min=1, max=60), stop=stop_after_attempt(6)) async def write_code(self, prompt) -> str: @@ -102,21 +95,15 @@ async def write_code(self, prompt) -> str: return code async def run(self, *args, **kwargs) -> CodingContext: - bug_feedback = await FileRepository.get_file(filename=BUGFIX_FILENAME, relative_path=DOCS_FILE_REPO) - coding_context = CodingContext.loads(self.context.content) - test_doc = await FileRepository.get_file( - filename="test_" + coding_context.filename + ".json", relative_path=TEST_OUTPUTS_FILE_REPO - ) - code_plan_and_change_doc = await FileRepository.get_file( - filename=CODE_PLAN_AND_CHANGE_FILENAME, relative_path=CODE_PLAN_AND_CHANGE_FILE_REPO - ) + bug_feedback = await self.repo.docs.get(filename=BUGFIX_FILENAME) + coding_context = CodingContext.loads(self.i_context.content) + test_doc = await self.repo.test_outputs.get(filename="test_" + coding_context.filename + ".json") + code_plan_and_change_doc = await self.repo.docs.code_plan_and_change.get(filename=CODE_PLAN_AND_CHANGE_FILENAME) code_plan_and_change = code_plan_and_change_doc.content if code_plan_and_change_doc else "" - requirement_doc = await FileRepository.get_file(filename=REQUIREMENT_FILENAME, relative_path=DOCS_FILE_REPO) + requirement_doc = await self.repo.docs.get(filename=REQUIREMENT_FILENAME) summary_doc = None if coding_context.design_doc and coding_context.design_doc.filename: - summary_doc = await FileRepository.get_file( - filename=coding_context.design_doc.filename, relative_path=CODE_SUMMARIES_FILE_REPO - ) + summary_doc = await self.repo.docs.code_summary.get(filename=coding_context.design_doc.filename) logs = "" if test_doc: test_detail = RunCodeResult.loads(test_doc.content) @@ -126,91 +113,91 @@ async def run(self, *args, **kwargs) -> CodingContext: code_context = coding_context.code_doc.content elif code_plan_and_change: code_context = await self.get_codes( - coding_context.task_doc, exclude=self.context.filename, mode="incremental" + coding_context.task_doc, exclude=self.i_context.filename, project_repo=self.repo, use_inc=True ) else: - code_context = await self.get_codes(coding_context.task_doc, exclude=self.context.filename) + code_context = await self.get_codes( + coding_context.task_doc, + exclude=self.i_context.filename, + project_repo=self.repo.with_src_path(self.context.src_workspace), + ) if code_plan_and_change: prompt = REFINED_TEMPLATE.format( user_requirement=requirement_doc.content if requirement_doc else "", code_plan_and_change=code_plan_and_change, design=coding_context.design_doc.content if coding_context.design_doc else "", - tasks=coding_context.task_doc.content if coding_context.task_doc else "", + task=coding_context.task_doc.content if coding_context.task_doc else "", code=code_context, logs=logs, feedback=bug_feedback.content if bug_feedback else "", - filename=self.context.filename, + filename=self.i_context.filename, summary_log=summary_doc.content if summary_doc else "", ) else: prompt = PROMPT_TEMPLATE.format( design=coding_context.design_doc.content if coding_context.design_doc else "", - tasks=coding_context.task_doc.content if coding_context.task_doc else "", + task=coding_context.task_doc.content if coding_context.task_doc else "", code=code_context, logs=logs, feedback=bug_feedback.content if bug_feedback else "", - filename=self.context.filename, + filename=self.i_context.filename, summary_log=summary_doc.content if summary_doc else "", ) logger.info(f"Writing {coding_context.filename}..") code = await self.write_code(prompt) if not coding_context.code_doc: # avoid root_path pydantic ValidationError if use WriteCode alone - root_path = CONFIG.src_workspace if CONFIG.src_workspace else "" + root_path = self.context.src_workspace if self.context.src_workspace else "" coding_context.code_doc = Document(filename=coding_context.filename, root_path=str(root_path)) coding_context.code_doc.content = code return coding_context @staticmethod - async def get_codes(task_doc: Document, exclude: str, mode: Literal["normal", "incremental"] = "normal") -> str: + async def get_codes(task_doc: Document, exclude: str, project_repo: ProjectRepo, use_inc: bool = False) -> str: """ - Get code snippets based on different modes. + Get codes for generating the exclude file in various scenarios. Attributes: task_doc (Document): Document object of the task file. - exclude (str): Specifies the filename to be excluded from the code snippets. - mode (str): Specifies the mode, either "normal" or "incremental" (default is "normal"). + exclude (str): The file to be generated. Specifies the filename to be excluded from the code snippets. + project_repo (ProjectRepo): ProjectRepo object of the project. + use_inc (bool): Indicates whether the scenario involves incremental development. Defaults to False. Returns: - str: Code snippets. - - Description: - If mode is set to "normal", it returns code snippets for the regular coding phase, - i.e., all the code generated before writing the current file. - - If mode is set to "incremental", it returns code snippets for generating the code plan and change, - building upon the existing code in the "normal" mode and adding code for the current file's older versions. + str: Codes for generating the exclude file. """ if not task_doc: return "" if not task_doc.content: - task_doc.content = FileRepository.get_file(filename=task_doc.filename, relative_path=TASK_FILE_REPO) + task_doc = project_repo.docs.task.get(filename=task_doc.filename) m = json.loads(task_doc.content) - code_filenames = m.get(TASK_LIST.key, []) if mode == "normal" else m.get(REFINED_TASK_LIST.key, []) + code_filenames = m.get(TASK_LIST.key, []) if use_inc else m.get(REFINED_TASK_LIST.key, []) codes = [] - src_file_repo = CONFIG.git_repo.new_file_repository(relative_path=CONFIG.src_workspace) + src_file_repo = project_repo.srcs - if mode == "incremental": + # Incremental development scenario + if use_inc: src_files = src_file_repo.all_files - old_file_repo = CONFIG.git_repo.new_file_repository(relative_path=CONFIG.old_workspace) + # Get the old workspace contained the old codes and old workspace are created in previous CodePlanAndChange + old_file_repo = project_repo.git_repo.new_file_repository(relative_path=project_repo.old_workspace) old_files = old_file_repo.all_files # Get the union of the files in the src and old workspaces union_files_list = list(set(src_files) | set(old_files)) for filename in union_files_list: - # Exclude the current file from the all code snippets to get the context code snippets for generating + # Exclude the current file from the all code snippets if filename == exclude: - # If the file is in the old workspace, use the legacy code + # If the file is in the old workspace, use the old code # Exclude unnecessary code to maintain a clean and focused main.py file, ensuring only relevant and # essential functionality is included for the project’s requirements if filename in old_files and filename != "main.py": - # Use legacy code + # Use old code doc = await old_file_repo.get(filename=filename) # If the file is in the src workspace, skip it else: continue codes.insert(0, f"-----Now, {filename} to be rewritten\n```{doc.content}```\n=====") - # The context code snippets are generated from the src workspace + # The code snippets are generated from the src workspace else: doc = await src_file_repo.get(filename=filename) # If the file does not exist in the src workspace, skip it @@ -218,9 +205,10 @@ async def get_codes(task_doc: Document, exclude: str, mode: Literal["normal", "i continue codes.append(f"----- {filename}\n```{doc.content}```") - elif mode == "normal": + # Normal scenario + else: for filename in code_filenames: - # Exclude the current file to get the context code snippets for generating the current file + # Exclude the current file to get the code snippets for generating the current file if filename == exclude: continue doc = await src_file_repo.get(filename=filename) diff --git a/metagpt/actions/write_code_an_draft.py b/metagpt/actions/write_code_an_draft.py index 968c8924b..ce030b0e9 100644 --- a/metagpt/actions/write_code_an_draft.py +++ b/metagpt/actions/write_code_an_draft.py @@ -5,7 +5,7 @@ @File : write_review.py """ import asyncio -from typing import List +from typing import List, Literal from metagpt.actions import Action from metagpt.actions.action_node import ActionNode @@ -21,16 +21,15 @@ ], ) -LGTM = ActionNode( - key="LGTM", - expected_type=str, - instruction="LGTM/LBTM. If the code is fully implemented, " - "give a LGTM (Looks Good To Me), otherwise provide a LBTM (Looks Bad To Me).", +REVIEW_RESULT = ActionNode( + key="ReviewResult", + expected_type=Literal["LGTM", "LBTM"], + instruction="LGTM/LBTM. If the code is fully implemented, " "give a LGTM, otherwise provide a LBTM.", example="LBTM", ) -ACTIONS = ActionNode( - key="Actions", +NEXT_STEPS = ActionNode( + key="NextSteps", expected_type=str, instruction="Based on the code review outcome, suggest actionable steps. This can include code changes, " "refactoring suggestions, or any follow-up tasks.", @@ -69,7 +68,7 @@ def handle_events(self): ) -WRITE_MOVE_FUNCTION = ActionNode( +WRITE_FUNCTION = ActionNode( key="WriteFunction", expected_type=str, instruction="write code for the function not implemented.", @@ -555,8 +554,8 @@ class Game { """ -WRITE_CODE_NODE = ActionNode.from_children("WRITE_REVIEW_NODE", [REVIEW, LGTM, ACTIONS]) -WRITE_MOVE_NODE = ActionNode.from_children("WRITE_MOVE_NODE", [WRITE_DRAFT, WRITE_MOVE_FUNCTION]) +WRITE_CODE_NODE = ActionNode.from_children("WRITE_REVIEW_NODE", [REVIEW, REVIEW_RESULT, NEXT_STEPS]) +WRITE_MOVE_NODE = ActionNode.from_children("WRITE_MOVE_NODE", [WRITE_DRAFT, WRITE_FUNCTION]) CR_FOR_MOVE_FUNCTION_BY_3 = """ @@ -579,8 +578,7 @@ class WriteCodeAN(Action): async def run(self, context): self.llm.system_prompt = "You are an outstanding engineer and can implement any code" - return await WRITE_MOVE_FUNCTION.fill(context=context, llm=self.llm, schema="json") - # return await WRITE_CODE_NODE.fill(context=context, llm=self.llm, schema="markdown") + return await WRITE_MOVE_NODE.fill(context=context, llm=self.llm, schema="json") async def main(): diff --git a/metagpt/actions/write_code_plan_and_change_an.py b/metagpt/actions/write_code_plan_and_change_an.py index 8bf20e494..708808050 100644 --- a/metagpt/actions/write_code_plan_and_change_an.py +++ b/metagpt/actions/write_code_plan_and_change_an.py @@ -11,7 +11,6 @@ from metagpt.actions.action import Action from metagpt.actions.action_node import ActionNode -from metagpt.config import CONFIG from metagpt.schema import CodePlanAndChangeContext CODE_PLAN_AND_CHANGE = ActionNode( @@ -119,8 +118,8 @@ def add_numbers(): ## Design {design} -## Tasks -{tasks} +## Task +{task} ## Legacy Code {code} @@ -140,8 +139,8 @@ def add_numbers(): ## Design {design} -## Tasks -{tasks} +## Task +{task} ## Legacy Code ```Code @@ -173,11 +172,11 @@ def add_numbers(): 2. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets. 3. Set default value: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. AVOID circular import. 4. Follow design: YOU MUST FOLLOW "Data structures and interfaces". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design. -5. Follow Code Plan And Change: If there is any Incremental Change or Legacy Code files contain "{filename} to be rewritten", you must merge it into the code file according to the plan. +5. Follow Code Plan And Change: If there is any Incremental Change that is marked by the git diff format using '+' and '-' for add/modify/delete code, or Legacy Code files contain "{filename} to be rewritten", you must merge it into the code file according to the plan. 6. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE. 7. Before using a external variable/module, make sure you import it first. 8. Write out EVERY CODE DETAIL, DON'T LEAVE TODO. -9. Attention: Retain content that is not related to incremental development but important for consistency and clarity.". +9. Attention: Retain details that are not related to incremental development but are important for maintaining the consistency and clarity of the old code. """ WRITE_CODE_PLAN_AND_CHANGE_NODE = ActionNode.from_children("WriteCodePlanAndChange", [CODE_PLAN_AND_CHANGE]) @@ -185,25 +184,27 @@ def add_numbers(): class WriteCodePlanAndChange(Action): name: str = "WriteCodePlanAndChange" - context: CodePlanAndChangeContext = Field(default_factory=CodePlanAndChangeContext) + i_context: CodePlanAndChangeContext = Field(default_factory=CodePlanAndChangeContext) async def run(self, *args, **kwargs): self.llm.system_prompt = "You are a professional software engineer, your primary responsibility is to " "meticulously craft comprehensive incremental development plan and deliver detailed incremental change" - requirement = self.context.requirement_doc.content - prd = "\n".join([doc.content for doc in self.context.prd_docs]) - design = "\n".join([doc.content for doc in self.context.design_docs]) - tasks = "\n".join([doc.content for doc in self.context.tasks_docs]) + prd_doc = await self.repo.docs.prd.get(filename=self.i_context.prd_filename) + design_doc = await self.repo.docs.system_design.get(filename=self.i_context.design_filename) + task_doc = await self.repo.docs.task.get(filename=self.i_context.task_filename) code_text = await self.get_old_codes() context = CODE_PLAN_AND_CHANGE_CONTEXT.format( - requirement=requirement, prd=prd, design=design, tasks=tasks, code=code_text + requirement=self.i_context.requirement, + prd=prd_doc.content, + design=design_doc.content, + task=task_doc.content, + code=code_text, ) return await WRITE_CODE_PLAN_AND_CHANGE_NODE.fill(context=context, llm=self.llm, schema="json") - @staticmethod - async def get_old_codes() -> str: - CONFIG.old_workspace = CONFIG.git_repo.workdir / os.path.basename(CONFIG.project_path) - old_file_repo = CONFIG.git_repo.new_file_repository(relative_path=CONFIG.old_workspace) + async def get_old_codes(self) -> str: + self.repo.old_workspace = self.repo.git_repo.workdir / os.path.basename(self.config.project_path) + old_file_repo = self.repo.git_repo.new_file_repository(relative_path=self.repo.old_workspace) old_codes = await old_file_repo.get_all() codes = [f"----- {code.filename}\n```{code.content}```" for code in old_codes] return "\n".join(codes) diff --git a/metagpt/actions/write_code_review.py b/metagpt/actions/write_code_review.py index 1410e3318..da636eb36 100644 --- a/metagpt/actions/write_code_review.py +++ b/metagpt/actions/write_code_review.py @@ -13,17 +13,10 @@ from metagpt.actions import WriteCode from metagpt.actions.action import Action -from metagpt.config import CONFIG -from metagpt.const import ( - CODE_PLAN_AND_CHANGE_FILE_REPO, - CODE_PLAN_AND_CHANGE_FILENAME, - DOCS_FILE_REPO, - REQUIREMENT_FILENAME, -) +from metagpt.const import CODE_PLAN_AND_CHANGE_FILENAME, REQUIREMENT_FILENAME from metagpt.logs import logger from metagpt.schema import CodingContext from metagpt.utils.common import CodeParser -from metagpt.utils.file_repository import FileRepository PROMPT_TEMPLATE = """ # System @@ -127,7 +120,7 @@ def handle_events(self): class WriteCodeReview(Action): name: str = "WriteCodeReview" - context: CodingContext = Field(default_factory=CodingContext) + i_context: CodingContext = Field(default_factory=CodingContext) @retry(wait=wait_random_exponential(min=1, max=60), stop=stop_after_attempt(6)) async def write_code_review_and_rewrite(self, context_prompt, cr_prompt, filename): @@ -143,39 +136,36 @@ async def write_code_review_and_rewrite(self, context_prompt, cr_prompt, filenam return result, code async def run(self, *args, **kwargs) -> CodingContext: - iterative_code = self.context.code_doc.content - k = CONFIG.code_review_k_times or 1 - code_plan_and_change_doc = await FileRepository.get_file( - filename=CODE_PLAN_AND_CHANGE_FILENAME, relative_path=CODE_PLAN_AND_CHANGE_FILE_REPO - ) - code_plan_and_change = code_plan_and_change_doc.content if code_plan_and_change_doc else "" - mode = "incremental" if code_plan_and_change else "normal" + iterative_code = self.i_context.code_doc.content + k = self.context.config.code_review_k_times or 1 for i in range(k): - format_example = FORMAT_EXAMPLE.format(filename=self.context.code_doc.filename) - task_content = self.context.task_doc.content if self.context.task_doc else "" - code_context = await WriteCode.get_codes(self.context.task_doc, exclude=self.context.filename, mode=mode) + format_example = FORMAT_EXAMPLE.format(filename=self.i_context.code_doc.filename) + task_content = self.i_context.task_doc.content if self.i_context.task_doc else "" + code_context = await WriteCode.get_codes( + self.i_context.task_doc, + exclude=self.i_context.filename, + project_repo=self.repo.with_src_path(self.context.src_workspace), + use_inc=self.config.inc, + ) - if not code_plan_and_change: + if not self.config.inc: context = "\n".join( [ - "## System Design\n" + str(self.context.design_doc) + "\n", - "## Tasks\n" + task_content + "\n", + "## System Design\n" + str(self.i_context.design_doc) + "\n", + "## Task\n" + task_content + "\n", "## Code Files\n" + code_context + "\n", ] ) else: - requirement_doc = await FileRepository.get_file( - filename=REQUIREMENT_FILENAME, relative_path=DOCS_FILE_REPO - ) - user_requirement = requirement_doc.content if requirement_doc else "" - + requirement_doc = await self.repo.docs.get(filename=REQUIREMENT_FILENAME) + code_plan_and_change_doc = await self.repo.get(filename=CODE_PLAN_AND_CHANGE_FILENAME) context = "\n".join( [ - "## User New Requirements\n" + user_requirement + "\n", - "## Code Plan And Change\n" + code_plan_and_change + "\n", - "## System Design\n" + str(self.context.design_doc) + "\n", - "## Tasks\n" + task_content + "\n", + "## User New Requirements\n" + str(requirement_doc) + "\n", + "## Code Plan And Change\n" + str(code_plan_and_change_doc) + "\n", + "## System Design\n" + str(self.i_context.design_doc) + "\n", + "## Task\n" + task_content + "\n", "## Code Files\n" + code_context + "\n", ] ) @@ -183,27 +173,27 @@ async def run(self, *args, **kwargs) -> CodingContext: context_prompt = PROMPT_TEMPLATE.format( context=context, code=iterative_code, - filename=self.context.code_doc.filename, + filename=self.i_context.code_doc.filename, ) cr_prompt = EXAMPLE_AND_INSTRUCTION.format( format_example=format_example, ) len1 = len(iterative_code) if iterative_code else 0 - len2 = len(self.context.code_doc.content) if self.context.code_doc.content else 0 + len2 = len(self.i_context.code_doc.content) if self.i_context.code_doc.content else 0 logger.info( - f"Code review and rewrite {self.context.code_doc.filename}: {i + 1}/{k} | len(iterative_code)={len1}, " - f"len(self.context.code_doc.content)={len2}" + f"Code review and rewrite {self.i_context.code_doc.filename}: {i + 1}/{k} | len(iterative_code)={len1}, " + f"len(self.i_context.code_doc.content)={len2}" ) result, rewrited_code = await self.write_code_review_and_rewrite( - context_prompt, cr_prompt, self.context.code_doc.filename + context_prompt, cr_prompt, self.i_context.code_doc.filename ) if "LBTM" in result: iterative_code = rewrited_code elif "LGTM" in result: - self.context.code_doc.content = iterative_code - return self.context + self.i_context.code_doc.content = iterative_code + return self.i_context # code_rsp = await self._aask_v1(prompt, "code_rsp", OUTPUT_MAPPING) # self._save(context, filename, code) # 如果rewrited_code是None(原code perfect),那么直接返回code - self.context.code_doc.content = iterative_code - return self.context + self.i_context.code_doc.content = iterative_code + return self.i_context diff --git a/metagpt/actions/write_docstring.py b/metagpt/actions/write_docstring.py index 8b8335517..5cc4cafb8 100644 --- a/metagpt/actions/write_docstring.py +++ b/metagpt/actions/write_docstring.py @@ -16,7 +16,7 @@ Default: 'google' Example: - python3 -m metagpt.actions.write_docstring ./metagpt/startup.py --overwrite False --style=numpy + python3 -m metagpt.actions.write_docstring ./metagpt/software_company.py --overwrite False --style=numpy This script uses the 'fire' library to create a command-line interface. It generates docstrings for the given Python code using the specified docstring style and adds them to the code. @@ -161,7 +161,7 @@ class WriteDocstring(Action): """ desc: str = "Write docstring for code." - context: Optional[str] = None + i_context: Optional[str] = None async def run( self, diff --git a/metagpt/actions/write_prd.py b/metagpt/actions/write_prd.py index 8cf133da3..b66887164 100644 --- a/metagpt/actions/write_prd.py +++ b/metagpt/actions/write_prd.py @@ -14,9 +14,7 @@ from __future__ import annotations import json -import uuid from pathlib import Path -from typing import Optional from metagpt.actions import Action, ActionOutput from metagpt.actions.action_node import ActionNode @@ -25,18 +23,13 @@ COMPETITIVE_QUADRANT_CHART, PROJECT_NAME, REFINED_PRD_NODE, - REFINED_TEMPLATE, WP_IS_RELATIVE_NODE, WP_ISSUE_TYPE_NODE, WRITE_PRD_NODE, ) -from metagpt.config import CONFIG from metagpt.const import ( BUGFIX_FILENAME, COMPETITIVE_ANALYSIS_FILE_REPO, - DOCS_FILE_REPO, - PRD_PDF_FILE_REPO, - PRDS_FILE_REPO, REQUIREMENT_FILENAME, ) from metagpt.logs import logger @@ -66,139 +59,114 @@ class WritePRD(Action): - name: str = "WritePRD" - content: Optional[str] = None - - async def run(self, with_messages, schema=CONFIG.prompt_schema, *args, **kwargs) -> ActionOutput | Message: - # Determine which requirement documents need to be rewritten: Use LLM to assess whether new requirements are - # related to the PRD. If they are related, rewrite the PRD. - docs_file_repo = CONFIG.git_repo.new_file_repository(relative_path=DOCS_FILE_REPO) - requirement_doc = await docs_file_repo.get(filename=REQUIREMENT_FILENAME) - if requirement_doc and await self._is_bugfix(requirement_doc.content): - await docs_file_repo.save(filename=BUGFIX_FILENAME, content=requirement_doc.content) - await docs_file_repo.save(filename=REQUIREMENT_FILENAME, content="") - bug_fix = BugFixContext(filename=BUGFIX_FILENAME) - return Message( - content=bug_fix.model_dump_json(), - instruct_content=bug_fix, - role="", - cause_by=FixBug, - sent_from=self, - send_to="Alex", # the name of Engineer - ) + """WritePRD deal with the following situations: + 1. Bugfix: If the requirement is a bugfix, the bugfix document will be generated. + 2. New requirement: If the requirement is a new requirement, the PRD document will be generated. + 3. Requirement update: If the requirement is an update, the PRD document will be updated. + """ + + async def run(self, with_messages, *args, **kwargs) -> ActionOutput | Message: + """Run the action.""" + req: Document = await self.repo.requirement + docs: list[Document] = await self.repo.docs.prd.get_all() + if not req: + raise FileNotFoundError("No requirement document found.") + + if await self._is_bugfix(req.content): + logger.info(f"Bugfix detected: {req.content}") + return await self._handle_bugfix(req) + # remove bugfix file from last round in case of conflict + await self.repo.docs.delete(filename=BUGFIX_FILENAME) + + # if requirement is related to other documents, update them, otherwise create a new one + if related_docs := await self.get_related_docs(req, docs): + logger.info(f"Requirement update detected: {req.content}") + return await self._handle_requirement_update(req, related_docs) else: - await docs_file_repo.delete(filename=BUGFIX_FILENAME) - - prds_file_repo = CONFIG.git_repo.new_file_repository(PRDS_FILE_REPO) - prd_docs = await prds_file_repo.get_all() - change_files = Documents() - for prd_doc in prd_docs: - prd_doc = await self._update_prd( - requirement_doc=requirement_doc, prd_doc=prd_doc, prds_file_repo=prds_file_repo, *args, **kwargs - ) - if not prd_doc: - continue - change_files.docs[prd_doc.filename] = prd_doc - logger.info(f"rewrite prd: {prd_doc.filename}") - # If there is no existing PRD, generate one using 'docs/requirement.txt'. - if not change_files.docs: - prd_doc = await self._update_prd( - requirement_doc=requirement_doc, prd_doc=None, prds_file_repo=prds_file_repo, *args, **kwargs - ) - if prd_doc: - change_files.docs[prd_doc.filename] = prd_doc - logger.debug(f"new prd: {prd_doc.filename}") - # Once all files under 'docs/prds/' have been compared with the newly added requirements, trigger the - # 'publish' message to transition the workflow to the next stage. This design allows room for global - # optimization in subsequent steps. - return ActionOutput(content=change_files.model_dump_json(), instruct_content=change_files) - - async def _run_new_requirement(self, requirements, schema=CONFIG.prompt_schema) -> ActionOutput: - # sas = SearchAndSummarize() - # # rsp = await sas.run(context=requirements, system_text=SEARCH_AND_SUMMARIZE_SYSTEM_EN_US) - # rsp = "" - # info = f"### Search Results\n{sas.result}\n\n### Search Summary\n{rsp}" - # if sas.result: - # logger.info(sas.result) - # logger.info(rsp) - project_name = CONFIG.project_name or "" - context = CONTEXT_TEMPLATE.format(requirements=requirements, project_name=project_name) + logger.info(f"New requirement detected: {req.content}") + return await self._handle_new_requirement(req) + + async def _handle_bugfix(self, req: Document) -> Message: + # ... bugfix logic ... + await self.repo.docs.save(filename=BUGFIX_FILENAME, content=req.content) + await self.repo.docs.save(filename=REQUIREMENT_FILENAME, content="") + bug_fix = BugFixContext(filename=BUGFIX_FILENAME) + return Message( + content=bug_fix.model_dump_json(), + instruct_content=bug_fix, + role="", + cause_by=FixBug, + sent_from=self, + send_to="Alex", # the name of Engineer + ) + + async def _handle_new_requirement(self, req: Document) -> ActionOutput: + """handle new requirement""" + project_name = self.project_name + context = CONTEXT_TEMPLATE.format(requirements=req, project_name=project_name) exclude = [PROJECT_NAME.key] if project_name else [] node = await WRITE_PRD_NODE.fill(context=context, llm=self.llm, exclude=exclude) # schema=schema await self._rename_workspace(node) - return node + new_prd_doc = await self.repo.docs.prd.save( + filename=FileRepository.new_filename() + ".json", content=node.instruct_content.model_dump_json() + ) + await self._save_competitive_analysis(new_prd_doc) + await self.repo.resources.prd.save_pdf(doc=new_prd_doc) + return Documents.from_iterable(documents=[new_prd_doc]).to_action_output() + + async def _handle_requirement_update(self, req: Document, related_docs: list[Document]) -> ActionOutput: + # ... requirement update logic ... + for doc in related_docs: + await self._update_prd(req, doc) + return Documents.from_iterable(documents=related_docs).to_action_output() + + async def _is_bugfix(self, context: str) -> bool: + if not self.repo.code_files_exists(): + return False + node = await WP_ISSUE_TYPE_NODE.fill(context, self.llm) + return node.get("issue_type") == "BUG" - async def _is_relative(self, new_requirement_doc, old_prd_doc) -> bool: - context = NEW_REQ_TEMPLATE.format(old_prd=old_prd_doc.content, requirements=new_requirement_doc.content) + async def get_related_docs(self, req: Document, docs: list[Document]) -> list[Document]: + """get the related documents""" + # refine: use gather to speed up + return [i for i in docs if await self._is_related(req, i)] + + async def _is_related(self, req: Document, old_prd: Document) -> bool: + context = NEW_REQ_TEMPLATE.format(old_prd=old_prd.content, requirements=req.content) node = await WP_IS_RELATIVE_NODE.fill(context, self.llm) return node.get("is_relative") == "YES" - async def _merge(self, new_requirement_doc, prd_doc, schema=CONFIG.prompt_schema) -> Document: - if not CONFIG.project_name: - CONFIG.project_name = Path(CONFIG.project_path).name - prompt = REFINED_TEMPLATE.format( - requirements=new_requirement_doc.content, - old_prd=prd_doc.content, - project_name=CONFIG.project_name, - ) - node = await REFINED_PRD_NODE.fill(context=prompt, llm=self.llm, schema=schema) - prd_doc.content = node.instruct_content.model_dump_json() + async def _merge(self, req: Document, related_doc: Document) -> Document: + if not self.project_name: + self.project_name = Path(self.project_path).name + prompt = NEW_REQ_TEMPLATE.format(requirements=req.content, old_prd=related_doc.content) + node = await REFINED_PRD_NODE.fill(context=prompt, llm=self.llm, schema=self.prompt_schema) + related_doc.content = node.instruct_content.model_dump_json() await self._rename_workspace(node) - return prd_doc - - async def _update_prd(self, requirement_doc, prd_doc, prds_file_repo, *args, **kwargs) -> Document | None: - if not prd_doc: - prd = await self._run_new_requirement( - requirements=[requirement_doc.content if requirement_doc else ""], *args, **kwargs - ) - new_prd_doc = Document( - root_path=PRDS_FILE_REPO, - filename=FileRepository.new_filename() + ".json", - content=prd.instruct_content.model_dump_json(), - ) - elif await self._is_relative(requirement_doc, prd_doc): - new_prd_doc = await self._merge(requirement_doc, prd_doc) - else: - return None - await prds_file_repo.save(filename=new_prd_doc.filename, content=new_prd_doc.content) + return related_doc + + async def _update_prd(self, req: Document, prd_doc: Document) -> Document: + new_prd_doc: Document = await self._merge(req, prd_doc) + await self.repo.docs.prd.save_doc(doc=new_prd_doc) await self._save_competitive_analysis(new_prd_doc) - await self._save_pdf(new_prd_doc) + await self.repo.resources.prd.save_pdf(doc=new_prd_doc) return new_prd_doc - @staticmethod - async def _save_competitive_analysis(prd_doc): + async def _save_competitive_analysis(self, prd_doc: Document): m = json.loads(prd_doc.content) quadrant_chart = m.get(COMPETITIVE_QUADRANT_CHART.key) if not quadrant_chart: return - pathname = ( - CONFIG.git_repo.workdir / Path(COMPETITIVE_ANALYSIS_FILE_REPO) / Path(prd_doc.filename).with_suffix("") - ) - if not pathname.parent.exists(): - pathname.parent.mkdir(parents=True, exist_ok=True) - await mermaid_to_file(quadrant_chart, pathname) + pathname = self.repo.workdir / COMPETITIVE_ANALYSIS_FILE_REPO / Path(prd_doc.filename).stem + pathname.parent.mkdir(parents=True, exist_ok=True) + await mermaid_to_file(self.config.mermaid.engine, quadrant_chart, pathname) - @staticmethod - async def _save_pdf(prd_doc): - await FileRepository.save_as(doc=prd_doc, with_suffix=".md", relative_path=PRD_PDF_FILE_REPO) - - @staticmethod - async def _rename_workspace(prd): - if not CONFIG.project_name: + async def _rename_workspace(self, prd): + if not self.project_name: if isinstance(prd, (ActionOutput, ActionNode)): ws_name = prd.instruct_content.model_dump()["Project Name"] else: ws_name = CodeParser.parse_str(block="Project Name", text=prd) if ws_name: - CONFIG.project_name = ws_name - if not CONFIG.project_name: # The LLM failed to provide a project name, and the user didn't provide one either. - CONFIG.project_name = "app" + uuid.uuid4().hex[:16] - CONFIG.git_repo.rename_root(CONFIG.project_name) - - async def _is_bugfix(self, context) -> bool: - src_workspace_path = CONFIG.git_repo.workdir / CONFIG.git_repo.workdir.name - code_files = CONFIG.git_repo.get_files(relative_path=src_workspace_path) - if not code_files: - return False - node = await WP_ISSUE_TYPE_NODE.fill(context, self.llm) - return node.get("issue_type") == "BUG" + self.project_name = ws_name + self.repo.git_repo.rename_root(self.project_name) diff --git a/metagpt/actions/write_prd_an.py b/metagpt/actions/write_prd_an.py index c65860822..9898be55b 100644 --- a/metagpt/actions/write_prd_an.py +++ b/metagpt/actions/write_prd_an.py @@ -8,7 +8,6 @@ from typing import List from metagpt.actions.action_node import ActionNode -from metagpt.logs import logger LANGUAGE = ActionNode( key="Language", @@ -34,14 +33,15 @@ REFINED_REQUIREMENTS = ActionNode( key="Refined Requirements", expected_type=str, - instruction="Place the New user's requirements here.", + instruction="Place the New user's original requirements here.", example="Create a 2048 game with a new feature that ...", ) PROJECT_NAME = ActionNode( key="Project Name", expected_type=str, - instruction="According to the content of \"Original Requirements,\" name the project using snake case style , like 'game_2048' or 'simple_crm.", + instruction='According to the content of "Original Requirements," name the project using snake case style , ' + "like 'game_2048' or 'simple_crm.", example="game_2048", ) @@ -186,20 +186,6 @@ key="reason", expected_type=str, instruction="Explain the reasoning process from question to answer", example="..." ) -REFINED_TEMPLATE = """ -### Project Name -{project_name} - -### New Requirements -{requirements} - -### Legacy Content -{old_prd} - -### Search Information -- -""" - NODES = [ LANGUAGE, @@ -235,14 +221,3 @@ REFINED_PRD_NODE = ActionNode.from_children("RefinedPRD", REFINED_NODES) WP_ISSUE_TYPE_NODE = ActionNode.from_children("WP_ISSUE_TYPE", [ISSUE_TYPE, REASON]) WP_IS_RELATIVE_NODE = ActionNode.from_children("WP_IS_RELATIVE", [IS_RELATIVE, REASON]) - - -def main(): - prompt = WRITE_PRD_NODE.compile(context="") - logger.info(prompt) - prompt = REFINED_PRD_NODE.compile(context="") - logger.info(prompt) - - -if __name__ == "__main__": - main() diff --git a/metagpt/actions/write_prd_review.py b/metagpt/actions/write_prd_review.py index 2babe38db..68fb5d9e8 100644 --- a/metagpt/actions/write_prd_review.py +++ b/metagpt/actions/write_prd_review.py @@ -13,7 +13,7 @@ class WritePRDReview(Action): name: str = "" - context: Optional[str] = None + i_context: Optional[str] = None prd: Optional[str] = None desc: str = "Based on the PRD, conduct a PRD Review, providing clear and detailed feedback" diff --git a/metagpt/actions/write_teaching_plan.py b/metagpt/actions/write_teaching_plan.py index b824e055e..c5f70ae05 100644 --- a/metagpt/actions/write_teaching_plan.py +++ b/metagpt/actions/write_teaching_plan.py @@ -8,14 +8,14 @@ from typing import Optional from metagpt.actions import Action -from metagpt.config import CONFIG +from metagpt.context import Context from metagpt.logs import logger class WriteTeachingPlanPart(Action): """Write Teaching Plan Part""" - context: Optional[str] = None + i_context: Optional[str] = None topic: str = "" language: str = "Chinese" rsp: Optional[str] = None @@ -24,7 +24,7 @@ async def run(self, with_message=None, **kwargs): statement_patterns = TeachingPlanBlock.TOPIC_STATEMENTS.get(self.topic, []) statements = [] for p in statement_patterns: - s = self.format_value(p) + s = self.format_value(p, context=self.context) statements.append(s) formatter = ( TeachingPlanBlock.PROMPT_TITLE_TEMPLATE @@ -35,7 +35,7 @@ async def run(self, with_message=None, **kwargs): formation=TeachingPlanBlock.FORMATION, role=self.prefix, statements="\n".join(statements), - lesson=self.context, + lesson=self.i_context, topic=self.topic, language=self.language, ) @@ -68,20 +68,23 @@ def __repr__(self): return self.topic @staticmethod - def format_value(value): + def format_value(value, context: Context): """Fill parameters inside `value` with `options`.""" if not isinstance(value, str): return value if "{" not in value: return value - merged_opts = CONFIG.options or {} + options = context.config.model_dump() + for k, v in context.kwargs: + options[k] = v # None value is allowed to override and disable the value from config. + opts = {k: v for k, v in options.items() if v is not None} try: - return value.format(**merged_opts) + return value.format(**opts) except KeyError as e: logger.warning(f"Parameter is missing:{e}") - for k, v in merged_opts.items(): + for k, v in opts.items(): value = value.replace("{" + f"{k}" + "}", str(v)) return value diff --git a/metagpt/actions/write_test.py b/metagpt/actions/write_test.py index 96486311f..978fa20a6 100644 --- a/metagpt/actions/write_test.py +++ b/metagpt/actions/write_test.py @@ -39,7 +39,7 @@ class WriteTest(Action): name: str = "WriteTest" - context: Optional[TestingContext] = None + i_context: Optional[TestingContext] = None async def write_code(self, prompt): code_rsp = await self._aask(prompt) @@ -55,16 +55,16 @@ async def write_code(self, prompt): return code async def run(self, *args, **kwargs) -> TestingContext: - if not self.context.test_doc: - self.context.test_doc = Document( - filename="test_" + self.context.code_doc.filename, root_path=TEST_CODES_FILE_REPO + if not self.i_context.test_doc: + self.i_context.test_doc = Document( + filename="test_" + self.i_context.code_doc.filename, root_path=TEST_CODES_FILE_REPO ) fake_root = "/data" prompt = PROMPT_TEMPLATE.format( - code_to_test=self.context.code_doc.content, - test_file_name=self.context.test_doc.filename, - source_file_path=fake_root + "/" + self.context.code_doc.root_relative_path, + code_to_test=self.i_context.code_doc.content, + test_file_name=self.i_context.test_doc.filename, + source_file_path=fake_root + "/" + self.i_context.code_doc.root_relative_path, workspace=fake_root, ) - self.context.test_doc.content = await self.write_code(prompt) - return self.context + self.i_context.test_doc.content = await self.write_code(prompt) + return self.i_context diff --git a/metagpt/config.py b/metagpt/config.py deleted file mode 100644 index e837b329b..000000000 --- a/metagpt/config.py +++ /dev/null @@ -1,288 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -Provide configuration, singleton -@Modified By: mashenquan, 2023/11/27. - 1. According to Section 2.2.3.11 of RFC 135, add git repository support. - 2. Add the parameter `src_workspace` for the old version project path. -""" -import datetime -import json -import os -import warnings -from copy import deepcopy -from enum import Enum -from pathlib import Path -from typing import Any -from uuid import uuid4 - -import yaml - -from metagpt.const import DEFAULT_WORKSPACE_ROOT, METAGPT_ROOT, OPTIONS -from metagpt.logs import logger -from metagpt.tools import SearchEngineType, WebBrowserEngineType -from metagpt.utils.common import require_python_version -from metagpt.utils.cost_manager import CostManager -from metagpt.utils.singleton import Singleton - - -class NotConfiguredException(Exception): - """Exception raised for errors in the configuration. - - Attributes: - message -- explanation of the error - """ - - def __init__(self, message="The required configuration is not set"): - self.message = message - super().__init__(self.message) - - -class LLMProviderEnum(Enum): - OPENAI = "openai" - ANTHROPIC = "anthropic" - SPARK = "spark" - ZHIPUAI = "zhipuai" - FIREWORKS = "fireworks" - OPEN_LLM = "open_llm" - GEMINI = "gemini" - METAGPT = "metagpt" - AZURE_OPENAI = "azure_openai" - OLLAMA = "ollama" - - def __missing__(self, key): - return self.OPENAI - - -class Config(metaclass=Singleton): - """ - Regular usage method: - config = Config("config.yaml") - secret_key = config.get_key("MY_SECRET_KEY") - print("Secret key:", secret_key) - """ - - _instance = None - home_yaml_file = Path.home() / ".metagpt/config.yaml" - key_yaml_file = METAGPT_ROOT / "config/key.yaml" - default_yaml_file = METAGPT_ROOT / "config/config.yaml" - - def __init__(self, yaml_file=default_yaml_file, cost_data=""): - global_options = OPTIONS.get() - # cli paras - self.project_path = "" - self.project_name = "" - self.inc = False - self.reqa_file = "" - self.max_auto_summarize_code = 0 - self.git_reinit = False - - self._init_with_config_files_and_env(yaml_file) - # The agent needs to be billed per user, so billing information cannot be destroyed when the session ends. - self.cost_manager = CostManager(**json.loads(cost_data)) if cost_data else CostManager() - self._update() - global_options.update(OPTIONS.get()) - logger.debug("Config loading done.") - - def get_default_llm_provider_enum(self) -> LLMProviderEnum: - """Get first valid LLM provider enum""" - mappings = { - LLMProviderEnum.OPENAI: bool( - self._is_valid_llm_key(self.OPENAI_API_KEY) and not self.OPENAI_API_TYPE and self.OPENAI_API_MODEL - ), - LLMProviderEnum.ANTHROPIC: self._is_valid_llm_key(self.ANTHROPIC_API_KEY), - LLMProviderEnum.ZHIPUAI: self._is_valid_llm_key(self.ZHIPUAI_API_KEY), - LLMProviderEnum.FIREWORKS: self._is_valid_llm_key(self.FIREWORKS_API_KEY), - LLMProviderEnum.OPEN_LLM: self._is_valid_llm_key(self.OPEN_LLM_API_BASE), - LLMProviderEnum.GEMINI: self._is_valid_llm_key(self.GEMINI_API_KEY), - LLMProviderEnum.METAGPT: bool( - self._is_valid_llm_key(self.OPENAI_API_KEY) and self.OPENAI_API_TYPE == "metagpt" - ), - LLMProviderEnum.AZURE_OPENAI: bool( - self._is_valid_llm_key(self.OPENAI_API_KEY) - and self.OPENAI_API_TYPE == "azure" - and self.DEPLOYMENT_NAME - and self.OPENAI_API_VERSION - ), - LLMProviderEnum.OLLAMA: self._is_valid_llm_key(self.OLLAMA_API_BASE), - } - provider = None - for k, v in mappings.items(): - if v: - provider = k - break - if provider is None: - if self.DEFAULT_PROVIDER: - provider = LLMProviderEnum(self.DEFAULT_PROVIDER) - else: - raise NotConfiguredException("You should config a LLM configuration first") - - if provider is LLMProviderEnum.GEMINI and not require_python_version(req_version=(3, 10)): - warnings.warn("Use Gemini requires Python >= 3.10") - model_name = self.get_model_name(provider=provider) - if model_name: - logger.info(f"{provider} Model: {model_name}") - if provider: - logger.info(f"API: {provider}") - return provider - - def get_model_name(self, provider=None) -> str: - provider = provider or self.get_default_llm_provider_enum() - model_mappings = { - LLMProviderEnum.OPENAI: self.OPENAI_API_MODEL, - LLMProviderEnum.AZURE_OPENAI: self.DEPLOYMENT_NAME, - } - return model_mappings.get(provider, "") - - @staticmethod - def _is_valid_llm_key(k: str) -> bool: - return bool(k and k != "YOUR_API_KEY") - - def _update(self): - self.global_proxy = self._get("GLOBAL_PROXY") - - self.openai_api_key = self._get("OPENAI_API_KEY") - self.anthropic_api_key = self._get("ANTHROPIC_API_KEY") - self.zhipuai_api_key = self._get("ZHIPUAI_API_KEY") - self.zhipuai_api_model = self._get("ZHIPUAI_API_MODEL") - self.open_llm_api_base = self._get("OPEN_LLM_API_BASE") - self.open_llm_api_model = self._get("OPEN_LLM_API_MODEL") - self.fireworks_api_key = self._get("FIREWORKS_API_KEY") - self.gemini_api_key = self._get("GEMINI_API_KEY") - self.ollama_api_base = self._get("OLLAMA_API_BASE") - self.ollama_api_model = self._get("OLLAMA_API_MODEL") - - if not self._get("DISABLE_LLM_PROVIDER_CHECK"): - _ = self.get_default_llm_provider_enum() - - self.openai_base_url = self._get("OPENAI_BASE_URL") - self.openai_proxy = self._get("OPENAI_PROXY") or self.global_proxy - self.openai_api_type = self._get("OPENAI_API_TYPE") - self.openai_api_version = self._get("OPENAI_API_VERSION") - self.openai_api_rpm = self._get("RPM", 3) - self.openai_api_model = self._get("OPENAI_API_MODEL", "gpt-4-1106-preview") - self.max_tokens_rsp = self._get("MAX_TOKENS", 2048) - self.deployment_name = self._get("DEPLOYMENT_NAME", "gpt-4") - - self.spark_appid = self._get("SPARK_APPID") - self.spark_api_secret = self._get("SPARK_API_SECRET") - self.spark_api_key = self._get("SPARK_API_KEY") - self.domain = self._get("DOMAIN") - self.spark_url = self._get("SPARK_URL") - - self.fireworks_api_base = self._get("FIREWORKS_API_BASE") - self.fireworks_api_model = self._get("FIREWORKS_API_MODEL") - - self.claude_api_key = self._get("ANTHROPIC_API_KEY") - self.serpapi_api_key = self._get("SERPAPI_API_KEY") - self.serper_api_key = self._get("SERPER_API_KEY") - self.google_api_key = self._get("GOOGLE_API_KEY") - self.google_cse_id = self._get("GOOGLE_CSE_ID") - self.search_engine = SearchEngineType(self._get("SEARCH_ENGINE", SearchEngineType.SERPAPI_GOOGLE)) - self.web_browser_engine = WebBrowserEngineType(self._get("WEB_BROWSER_ENGINE", WebBrowserEngineType.PLAYWRIGHT)) - self.playwright_browser_type = self._get("PLAYWRIGHT_BROWSER_TYPE", "chromium") - self.selenium_browser_type = self._get("SELENIUM_BROWSER_TYPE", "chrome") - - self.long_term_memory = self._get("LONG_TERM_MEMORY", False) - if self.long_term_memory: - logger.warning("LONG_TERM_MEMORY is True") - self.cost_manager.max_budget = self._get("MAX_BUDGET", 10.0) - self.code_review_k_times = 2 - - self.puppeteer_config = self._get("PUPPETEER_CONFIG", "") - self.mmdc = self._get("MMDC", "mmdc") - self.calc_usage = self._get("CALC_USAGE", True) - self.model_for_researcher_summary = self._get("MODEL_FOR_RESEARCHER_SUMMARY") - self.model_for_researcher_report = self._get("MODEL_FOR_RESEARCHER_REPORT") - self.mermaid_engine = self._get("MERMAID_ENGINE", "nodejs") - self.pyppeteer_executable_path = self._get("PYPPETEER_EXECUTABLE_PATH", "") - - workspace_uid = ( - self._get("WORKSPACE_UID") or f"{datetime.datetime.now().strftime('%Y%m%d%H%M%S')}-{uuid4().hex[-8:]}" - ) - self.repair_llm_output = self._get("REPAIR_LLM_OUTPUT", False) - self.prompt_schema = self._get("PROMPT_FORMAT", "json") - self.workspace_path = Path(self._get("WORKSPACE_PATH", DEFAULT_WORKSPACE_ROOT)) - val = self._get("WORKSPACE_PATH_WITH_UID") - if val and val.lower() == "true": # for agent - self.workspace_path = self.workspace_path / workspace_uid - self._ensure_workspace_exists() - self.max_auto_summarize_code = self.max_auto_summarize_code or self._get("MAX_AUTO_SUMMARIZE_CODE", 1) - self.timeout = int(self._get("TIMEOUT", 3)) - - def update_via_cli(self, project_path, project_name, inc, reqa_file, max_auto_summarize_code): - """update config via cli""" - - # Use in the PrepareDocuments action according to Section 2.2.3.5.1 of RFC 135. - if project_path: - inc = True - project_name = project_name or Path(project_path).name - self.project_path = project_path - self.project_name = project_name - self.inc = inc - self.reqa_file = reqa_file - self.max_auto_summarize_code = max_auto_summarize_code - - def _ensure_workspace_exists(self): - self.workspace_path.mkdir(parents=True, exist_ok=True) - logger.debug(f"WORKSPACE_PATH set to {self.workspace_path}") - - def _init_with_config_files_and_env(self, yaml_file): - """Load from config/key.yaml, config/config.yaml, and env in decreasing order of priority""" - configs = dict(os.environ) - - for _yaml_file in [yaml_file, self.key_yaml_file, self.home_yaml_file]: - if not _yaml_file.exists(): - continue - - # Load local YAML file - with open(_yaml_file, "r", encoding="utf-8") as file: - yaml_data = yaml.safe_load(file) - if not yaml_data: - continue - configs.update(yaml_data) - OPTIONS.set(configs) - - @staticmethod - def _get(*args, **kwargs): - i = OPTIONS.get() - return i.get(*args, **kwargs) - - def get(self, key, *args, **kwargs): - """Retrieve values from config/key.yaml, config/config.yaml, and environment variables. - Throw an error if not found.""" - value = self._get(key, *args, **kwargs) - if value is None: - raise ValueError(f"Key '{key}' not found in environment variables or in the YAML file") - return value - - def __setattr__(self, name: str, value: Any) -> None: - OPTIONS.get()[name] = value - - def __getattr__(self, name: str) -> Any: - i = OPTIONS.get() - return i.get(name) - - def set_context(self, options: dict): - """Update current config""" - if not options: - return - opts = deepcopy(OPTIONS.get()) - opts.update(options) - OPTIONS.set(opts) - self._update() - - @property - def options(self): - """Return all key-values""" - return OPTIONS.get() - - def new_environ(self): - """Return a new os.environ object""" - env = os.environ.copy() - i = self.options - env.update({k: v for k, v in i.items() if isinstance(v, str)}) - return env - - -CONFIG = Config() diff --git a/metagpt/config2.py b/metagpt/config2.py new file mode 100644 index 000000000..bc6af18c6 --- /dev/null +++ b/metagpt/config2.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2024/1/4 01:25 +@Author : alexanderwu +@File : config2.py +""" +import os +from pathlib import Path +from typing import Dict, Iterable, List, Literal, Optional + +from pydantic import BaseModel, model_validator + +from metagpt.configs.browser_config import BrowserConfig +from metagpt.configs.llm_config import LLMConfig, LLMType +from metagpt.configs.mermaid_config import MermaidConfig +from metagpt.configs.redis_config import RedisConfig +from metagpt.configs.s3_config import S3Config +from metagpt.configs.search_config import SearchConfig +from metagpt.configs.workspace_config import WorkspaceConfig +from metagpt.const import CONFIG_ROOT, METAGPT_ROOT +from metagpt.utils.yaml_model import YamlModel + + +class CLIParams(BaseModel): + """CLI parameters""" + + project_path: str = "" + project_name: str = "" + inc: bool = False + reqa_file: str = "" + max_auto_summarize_code: int = 0 + git_reinit: bool = False + + @model_validator(mode="after") + def check_project_path(self): + """Check project_path and project_name""" + if self.project_path: + self.inc = True + self.project_name = self.project_name or Path(self.project_path).name + return self + + +class Config(CLIParams, YamlModel): + """Configurations for MetaGPT""" + + # Key Parameters + llm: LLMConfig + + # Global Proxy. Will be used if llm.proxy is not set + proxy: str = "" + + # Tool Parameters + search: SearchConfig = SearchConfig() + browser: BrowserConfig = BrowserConfig() + mermaid: MermaidConfig = MermaidConfig() + + # Storage Parameters + s3: Optional[S3Config] = None + redis: Optional[RedisConfig] = None + + # Misc Parameters + repair_llm_output: bool = False + prompt_schema: Literal["json", "markdown", "raw"] = "json" + workspace: WorkspaceConfig = WorkspaceConfig() + enable_longterm_memory: bool = False + code_review_k_times: int = 2 + + # Will be removed in the future + metagpt_tti_url: str = "" + language: str = "English" + redis_key: str = "placeholder" + iflytek_app_id: str = "" + iflytek_api_secret: str = "" + iflytek_api_key: str = "" + azure_tts_subscription_key: str = "" + azure_tts_region: str = "" + + @classmethod + def from_home(cls, path): + """Load config from ~/.metagpt/config2.yaml""" + pathname = CONFIG_ROOT / path + if not pathname.exists(): + return None + return Config.from_yaml_file(pathname) + + @classmethod + def default(cls): + """Load default config + - Priority: env < default_config_paths + - Inside default_config_paths, the latter one overwrites the former one + """ + default_config_paths: List[Path] = [ + METAGPT_ROOT / "config/config2.yaml", + Path.home() / ".metagpt/config2.yaml", + ] + + dicts = [dict(os.environ)] + dicts += [Config.read_yaml(path) for path in default_config_paths] + final = merge_dict(dicts) + return Config(**final) + + def update_via_cli(self, project_path, project_name, inc, reqa_file, max_auto_summarize_code): + """update config via cli""" + + # Use in the PrepareDocuments action according to Section 2.2.3.5.1 of RFC 135. + if project_path: + inc = True + project_name = project_name or Path(project_path).name + self.project_path = project_path + self.project_name = project_name + self.inc = inc + self.reqa_file = reqa_file + self.max_auto_summarize_code = max_auto_summarize_code + + def get_openai_llm(self) -> Optional[LLMConfig]: + """Get OpenAI LLMConfig by name. If no OpenAI, raise Exception""" + if self.llm.api_type == LLMType.OPENAI: + return self.llm + return None + + def get_azure_llm(self) -> Optional[LLMConfig]: + """Get Azure LLMConfig by name. If no Azure, raise Exception""" + if self.llm.api_type == LLMType.AZURE: + return self.llm + return None + + +def merge_dict(dicts: Iterable[Dict]) -> Dict: + """Merge multiple dicts into one, with the latter dict overwriting the former""" + result = {} + for dictionary in dicts: + result.update(dictionary) + return result + + +config = Config.default() diff --git a/tests/metagpt/test_action.py b/metagpt/configs/__init__.py similarity index 59% rename from tests/metagpt/test_action.py rename to metagpt/configs/__init__.py index af5106ab4..e42e6788f 100644 --- a/tests/metagpt/test_action.py +++ b/metagpt/configs/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- """ -@Time : 2023/5/11 14:44 +@Time : 2024/1/4 16:33 @Author : alexanderwu -@File : test_action.py +@File : __init__.py """ diff --git a/metagpt/configs/browser_config.py b/metagpt/configs/browser_config.py new file mode 100644 index 000000000..2f8024f44 --- /dev/null +++ b/metagpt/configs/browser_config.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2024/1/4 19:06 +@Author : alexanderwu +@File : browser_config.py +""" +from typing import Literal + +from metagpt.tools import WebBrowserEngineType +from metagpt.utils.yaml_model import YamlModel + + +class BrowserConfig(YamlModel): + """Config for Browser""" + + engine: WebBrowserEngineType = WebBrowserEngineType.PLAYWRIGHT + browser_type: Literal["chromium", "firefox", "webkit", "chrome", "firefox", "edge", "ie"] = "chromium" + """If the engine is Playwright, the value should be one of "chromium", "firefox", or "webkit". If it is Selenium, the value + should be either "chrome", "firefox", "edge", or "ie".""" diff --git a/metagpt/configs/llm_config.py b/metagpt/configs/llm_config.py new file mode 100644 index 000000000..fb923d3e4 --- /dev/null +++ b/metagpt/configs/llm_config.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2024/1/4 16:33 +@Author : alexanderwu +@File : llm_config.py +""" +from enum import Enum +from typing import Optional + +from pydantic import field_validator + +from metagpt.utils.yaml_model import YamlModel + + +class LLMType(Enum): + OPENAI = "openai" + ANTHROPIC = "anthropic" + SPARK = "spark" + ZHIPUAI = "zhipuai" + FIREWORKS = "fireworks" + OPEN_LLM = "open_llm" + GEMINI = "gemini" + METAGPT = "metagpt" + AZURE = "azure" + OLLAMA = "ollama" + + def __missing__(self, key): + return self.OPENAI + + +class LLMConfig(YamlModel): + """Config for LLM + + OpenAI: https://github.com/openai/openai-python/blob/main/src/openai/resources/chat/completions.py#L681 + Optional Fields in pydantic: https://docs.pydantic.dev/latest/migration/#required-optional-and-nullable-fields + """ + + api_key: str + api_type: LLMType = LLMType.OPENAI + base_url: str = "https://api.openai.com/v1" + api_version: Optional[str] = None + + model: Optional[str] = None # also stands for DEPLOYMENT_NAME + + # For Spark(Xunfei), maybe remove later + app_id: Optional[str] = None + api_secret: Optional[str] = None + domain: Optional[str] = None + + # For Chat Completion + max_token: int = 4096 + temperature: float = 0.0 + top_p: float = 1.0 + top_k: int = 0 + repetition_penalty: float = 1.0 + stop: Optional[str] = None + presence_penalty: float = 0.0 + frequency_penalty: float = 0.0 + best_of: Optional[int] = None + n: Optional[int] = None + stream: bool = False + logprobs: Optional[bool] = None # https://cookbook.openai.com/examples/using_logprobs + top_logprobs: Optional[int] = None + timeout: int = 60 + + # For Network + proxy: Optional[str] = None + + # Cost Control + calc_usage: bool = True + + @field_validator("api_key") + @classmethod + def check_llm_key(cls, v): + if v in ["", None, "YOUR_API_KEY"]: + raise ValueError("Please set your API key in config2.yaml") + return v diff --git a/metagpt/configs/mermaid_config.py b/metagpt/configs/mermaid_config.py new file mode 100644 index 000000000..50c8a1847 --- /dev/null +++ b/metagpt/configs/mermaid_config.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2024/1/4 19:07 +@Author : alexanderwu +@File : mermaid_config.py +""" +from typing import Literal + +from metagpt.utils.yaml_model import YamlModel + + +class MermaidConfig(YamlModel): + """Config for Mermaid""" + + engine: Literal["nodejs", "ink", "playwright", "pyppeteer"] = "nodejs" + path: str = "mmdc" # mmdc + puppeteer_config: str = "" + pyppeteer_path: str = "/usr/bin/google-chrome-stable" diff --git a/metagpt/configs/redis_config.py b/metagpt/configs/redis_config.py new file mode 100644 index 000000000..c4cfb6764 --- /dev/null +++ b/metagpt/configs/redis_config.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2024/1/4 19:06 +@Author : alexanderwu +@File : redis_config.py +""" +from metagpt.utils.yaml_model import YamlModelWithoutDefault + + +class RedisConfig(YamlModelWithoutDefault): + host: str + port: int + username: str = "" + password: str + db: str + + def to_url(self): + return f"redis://{self.host}:{self.port}" + + def to_kwargs(self): + return { + "username": self.username, + "password": self.password, + "db": self.db, + } diff --git a/metagpt/configs/s3_config.py b/metagpt/configs/s3_config.py new file mode 100644 index 000000000..72b81fae4 --- /dev/null +++ b/metagpt/configs/s3_config.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2024/1/4 19:07 +@Author : alexanderwu +@File : s3_config.py +""" +from metagpt.utils.yaml_model import YamlModelWithoutDefault + + +class S3Config(YamlModelWithoutDefault): + access_key: str + secret_key: str + endpoint: str + bucket: str diff --git a/metagpt/configs/search_config.py b/metagpt/configs/search_config.py new file mode 100644 index 000000000..af928b02a --- /dev/null +++ b/metagpt/configs/search_config.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2024/1/4 19:06 +@Author : alexanderwu +@File : search_config.py +""" +from typing import Callable, Optional + +from metagpt.tools import SearchEngineType +from metagpt.utils.yaml_model import YamlModel + + +class SearchConfig(YamlModel): + """Config for Search""" + + api_type: SearchEngineType = SearchEngineType.DUCK_DUCK_GO + api_key: str = "" + cse_id: str = "" # for google + search_func: Optional[Callable] = None diff --git a/metagpt/configs/workspace_config.py b/metagpt/configs/workspace_config.py new file mode 100644 index 000000000..df7aeaef9 --- /dev/null +++ b/metagpt/configs/workspace_config.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2024/1/4 19:09 +@Author : alexanderwu +@File : workspace_config.py +""" +from datetime import datetime +from pathlib import Path +from uuid import uuid4 + +from pydantic import field_validator, model_validator + +from metagpt.const import DEFAULT_WORKSPACE_ROOT +from metagpt.utils.yaml_model import YamlModel + + +class WorkspaceConfig(YamlModel): + path: Path = DEFAULT_WORKSPACE_ROOT + use_uid: bool = False + uid: str = "" + + @field_validator("path") + @classmethod + def check_workspace_path(cls, v): + if isinstance(v, str): + v = Path(v) + return v + + @model_validator(mode="after") + def check_uid_and_update_path(self): + if self.use_uid and not self.uid: + self.uid = f"{datetime.now().strftime('%Y%m%d%H%M%S')}-{uuid4().hex[-8:]}" + self.path = self.path / self.uid + + # Create workspace path if not exists + self.path.mkdir(parents=True, exist_ok=True) + return self diff --git a/metagpt/const.py b/metagpt/const.py index b3acf2fe3..2cffaa804 100644 --- a/metagpt/const.py +++ b/metagpt/const.py @@ -9,7 +9,6 @@ @Modified By: mashenquan, 2023-11-27. Defines file repository paths according to Section 2.2.3.4 of RFC 135. @Modified By: mashenquan, 2023/12/5. Add directories for code summarization.. """ -import contextvars import os from pathlib import Path @@ -17,8 +16,6 @@ import metagpt -OPTIONS = contextvars.ContextVar("OPTIONS", default={}) - def get_metagpt_package_root(): """Get the root directory of the installed package.""" @@ -47,7 +44,7 @@ def get_metagpt_root(): # METAGPT PROJECT ROOT AND VARS - +CONFIG_ROOT = Path.home() / ".metagpt" METAGPT_ROOT = get_metagpt_root() # Dependent on METAGPT_PROJECT_ROOT DEFAULT_WORKSPACE_ROOT = METAGPT_ROOT / "workspace" @@ -70,13 +67,13 @@ def get_metagpt_root(): SOURCE_ROOT = METAGPT_ROOT / "metagpt" PROMPT_PATH = SOURCE_ROOT / "prompts" SKILL_DIRECTORY = SOURCE_ROOT / "skills" - +TOOL_SCHEMA_PATH = METAGPT_ROOT / "metagpt/tools/schemas" +TOOL_LIBS_PATH = METAGPT_ROOT / "metagpt/tools/libs" # REAL CONSTS MEM_TTL = 24 * 30 * 3600 - MESSAGE_ROUTE_FROM = "sent_from" MESSAGE_ROUTE_TO = "send_to" MESSAGE_ROUTE_CAUSE_BY = "cause_by" @@ -90,25 +87,25 @@ def get_metagpt_root(): CODE_PLAN_AND_CHANGE_FILENAME = "code_plan_and_change.json" DOCS_FILE_REPO = "docs" -PRDS_FILE_REPO = "docs/prds" +PRDS_FILE_REPO = "docs/prd" SYSTEM_DESIGN_FILE_REPO = "docs/system_design" -TASK_FILE_REPO = "docs/tasks" +TASK_FILE_REPO = "docs/task" CODE_PLAN_AND_CHANGE_FILE_REPO = "docs/code_plan_and_change" COMPETITIVE_ANALYSIS_FILE_REPO = "resources/competitive_analysis" DATA_API_DESIGN_FILE_REPO = "resources/data_api_design" SEQ_FLOW_FILE_REPO = "resources/seq_flow" SYSTEM_DESIGN_PDF_FILE_REPO = "resources/system_design" PRD_PDF_FILE_REPO = "resources/prd" -TASK_PDF_FILE_REPO = "resources/api_spec_and_tasks" +TASK_PDF_FILE_REPO = "resources/api_spec_and_task" CODE_PLAN_AND_CHANGE_PDF_FILE_REPO = "resources/code_plan_and_change" TEST_CODES_FILE_REPO = "tests" TEST_OUTPUTS_FILE_REPO = "test_outputs" -CODE_SUMMARIES_FILE_REPO = "docs/code_summaries" -CODE_SUMMARIES_PDF_FILE_REPO = "resources/code_summaries" +CODE_SUMMARIES_FILE_REPO = "docs/code_summary" +CODE_SUMMARIES_PDF_FILE_REPO = "resources/code_summary" RESOURCES_FILE_REPO = "resources" -SD_OUTPUT_FILE_REPO = "resources/SD_Output" +SD_OUTPUT_FILE_REPO = "resources/sd_output" GRAPH_REPO_FILE_REPO = "docs/graph_repo" -CLASS_VIEW_FILE_REPO = "docs/class_views" +CLASS_VIEW_FILE_REPO = "docs/class_view" YAPI_URL = "http://yapi.deepwisdomai.com/" diff --git a/metagpt/context.py b/metagpt/context.py new file mode 100644 index 000000000..3dfd52d58 --- /dev/null +++ b/metagpt/context.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2024/1/4 16:32 +@Author : alexanderwu +@File : context.py +""" +import os +from pathlib import Path +from typing import Any, Optional + +from pydantic import BaseModel, ConfigDict + +from metagpt.config2 import Config +from metagpt.configs.llm_config import LLMConfig +from metagpt.provider.base_llm import BaseLLM +from metagpt.provider.llm_provider_registry import create_llm_instance +from metagpt.utils.cost_manager import CostManager +from metagpt.utils.git_repository import GitRepository +from metagpt.utils.project_repo import ProjectRepo + + +class AttrDict(BaseModel): + """A dict-like object that allows access to keys as attributes, compatible with Pydantic.""" + + model_config = ConfigDict(extra="allow") + + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.__dict__.update(kwargs) + + def __getattr__(self, key): + return self.__dict__.get(key, None) + + def __setattr__(self, key, value): + self.__dict__[key] = value + + def __delattr__(self, key): + if key in self.__dict__: + del self.__dict__[key] + else: + raise AttributeError(f"No such attribute: {key}") + + def set(self, key, val: Any): + self.__dict__[key] = val + + def get(self, key, default: Any = None): + return self.__dict__.get(key, default) + + def remove(self, key): + if key in self.__dict__: + self.__delattr__(key) + + +class Context(BaseModel): + """Env context for MetaGPT""" + + model_config = ConfigDict(arbitrary_types_allowed=True) + + kwargs: AttrDict = AttrDict() + config: Config = Config.default() + + repo: Optional[ProjectRepo] = None + git_repo: Optional[GitRepository] = None + src_workspace: Optional[Path] = None + cost_manager: CostManager = CostManager() + + _llm: Optional[BaseLLM] = None + + def new_environ(self): + """Return a new os.environ object""" + env = os.environ.copy() + # i = self.options + # env.update({k: v for k, v in i.items() if isinstance(v, str)}) + return env + + # def use_llm(self, name: Optional[str] = None, provider: LLMType = LLMType.OPENAI) -> BaseLLM: + # """Use a LLM instance""" + # self._llm_config = self.config.get_llm_config(name, provider) + # self._llm = None + # return self._llm + + def llm(self) -> BaseLLM: + """Return a LLM instance, fixme: support cache""" + # if self._llm is None: + self._llm = create_llm_instance(self.config.llm) + if self._llm.cost_manager is None: + self._llm.cost_manager = self.cost_manager + return self._llm + + def llm_with_cost_manager_from_llm_config(self, llm_config: LLMConfig) -> BaseLLM: + """Return a LLM instance, fixme: support cache""" + # if self._llm is None: + llm = create_llm_instance(llm_config) + if llm.cost_manager is None: + llm.cost_manager = self.cost_manager + return llm diff --git a/metagpt/context_mixin.py b/metagpt/context_mixin.py new file mode 100644 index 000000000..59daa692f --- /dev/null +++ b/metagpt/context_mixin.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2024/1/11 17:25 +@Author : alexanderwu +@File : context_mixin.py +""" +from typing import Optional + +from pydantic import BaseModel, ConfigDict, Field, model_validator + +from metagpt.config2 import Config +from metagpt.context import Context +from metagpt.provider.base_llm import BaseLLM + + +class ContextMixin(BaseModel): + """Mixin class for context and config""" + + model_config = ConfigDict(arbitrary_types_allowed=True, extra="allow") + + # Pydantic has bug on _private_attr when using inheritance, so we use private_* instead + # - https://github.com/pydantic/pydantic/issues/7142 + # - https://github.com/pydantic/pydantic/issues/7083 + # - https://github.com/pydantic/pydantic/issues/7091 + + # Env/Role/Action will use this context as private context, or use self.context as public context + private_context: Optional[Context] = Field(default=None, exclude=True) + # Env/Role/Action will use this config as private config, or use self.context.config as public config + private_config: Optional[Config] = Field(default=None, exclude=True) + + # Env/Role/Action will use this llm as private llm, or use self.context._llm instance + private_llm: Optional[BaseLLM] = Field(default=None, exclude=True) + + @model_validator(mode="after") + def validate_context_mixin_extra(self): + self._process_context_mixin_extra() + return self + + def _process_context_mixin_extra(self): + """Process the extra field""" + kwargs = self.model_extra or {} + self.set_context(kwargs.pop("context", None)) + self.set_config(kwargs.pop("config", None)) + self.set_llm(kwargs.pop("llm", None)) + + def set(self, k, v, override=False): + """Set attribute""" + if override or not self.__dict__.get(k): + self.__dict__[k] = v + + def set_context(self, context: Context, override=True): + """Set context""" + self.set("private_context", context, override) + + def set_config(self, config: Config, override=False): + """Set config""" + self.set("private_config", config, override) + if config is not None: + _ = self.llm # init llm + + def set_llm(self, llm: BaseLLM, override=False): + """Set llm""" + self.set("private_llm", llm, override) + + @property + def config(self) -> Config: + """Role config: role config > context config""" + if self.private_config: + return self.private_config + return self.context.config + + @config.setter + def config(self, config: Config) -> None: + """Set config""" + self.set_config(config) + + @property + def context(self) -> Context: + """Role context: role context > context""" + if self.private_context: + return self.private_context + return Context() + + @context.setter + def context(self, context: Context) -> None: + """Set context""" + self.set_context(context) + + @property + def llm(self) -> BaseLLM: + """Role llm: if not existed, init from role.config""" + # print(f"class:{self.__class__.__name__}({self.name}), llm: {self._llm}, llm_config: {self._llm_config}") + if not self.private_llm: + self.private_llm = self.context.llm_with_cost_manager_from_llm_config(self.config.llm) + return self.private_llm + + @llm.setter + def llm(self, llm: BaseLLM) -> None: + """Set llm""" + self.private_llm = llm diff --git a/metagpt/document_store/base_store.py b/metagpt/document_store/base_store.py index b719d1083..ddc1d626b 100644 --- a/metagpt/document_store/base_store.py +++ b/metagpt/document_store/base_store.py @@ -8,8 +8,6 @@ from abc import ABC, abstractmethod from pathlib import Path -from metagpt.config import Config - class BaseStore(ABC): """FIXME: consider add_index, set_index and think about granularity.""" @@ -31,7 +29,6 @@ class LocalStore(BaseStore, ABC): def __init__(self, raw_data_path: Path, cache_dir: Path = None): if not raw_data_path: raise FileNotFoundError - self.config = Config() self.raw_data_path = raw_data_path self.fname = self.raw_data_path.stem if not cache_dir: diff --git a/metagpt/document_store/faiss_store.py b/metagpt/document_store/faiss_store.py index 1271f1c23..2359917d5 100644 --- a/metagpt/document_store/faiss_store.py +++ b/metagpt/document_store/faiss_store.py @@ -9,14 +9,13 @@ from pathlib import Path from typing import Optional -from langchain.embeddings import OpenAIEmbeddings from langchain.vectorstores import FAISS from langchain_core.embeddings import Embeddings -from metagpt.config import CONFIG from metagpt.document import IndexableDocument from metagpt.document_store.base_store import LocalStore from metagpt.logs import logger +from metagpt.utils.embedding import get_embedding class FaissStore(LocalStore): @@ -25,9 +24,7 @@ def __init__( ): self.meta_col = meta_col self.content_col = content_col - self.embedding = embedding or OpenAIEmbeddings( - openai_api_key=CONFIG.openai_api_key, openai_api_base=CONFIG.openai_base_url - ) + self.embedding = embedding or get_embedding() super().__init__(raw_data, cache_dir) def _load(self) -> Optional["FaissStore"]: diff --git a/metagpt/environment.py b/metagpt/environment.py deleted file mode 100644 index ddb9ad9dd..000000000 --- a/metagpt/environment.py +++ /dev/null @@ -1,168 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -@Time : 2023/5/11 22:12 -@Author : alexanderwu -@File : environment.py -@Modified By: mashenquan, 2023-11-1. According to Chapter 2.2.2 of RFC 116: - 1. Remove the functionality of `Environment` class as a public message buffer. - 2. Standardize the message forwarding behavior of the `Environment` class. - 3. Add the `is_idle` property. -@Modified By: mashenquan, 2023-11-4. According to the routing feature plan in Chapter 2.2.3.2 of RFC 113, the routing - functionality is to be consolidated into the `Environment` class. -""" -import asyncio -from pathlib import Path -from typing import Iterable, Set - -from pydantic import BaseModel, ConfigDict, Field, SerializeAsAny, model_validator - -from metagpt.config import CONFIG -from metagpt.logs import logger -from metagpt.roles.role import Role -from metagpt.schema import Message -from metagpt.utils.common import is_subscribed, read_json_file, write_json_file - - -class Environment(BaseModel): - """环境,承载一批角色,角色可以向环境发布消息,可以被其他角色观察到 - Environment, hosting a batch of roles, roles can publish messages to the environment, and can be observed by other roles - """ - - model_config = ConfigDict(arbitrary_types_allowed=True) - - desc: str = Field(default="") # 环境描述 - roles: dict[str, SerializeAsAny[Role]] = Field(default_factory=dict, validate_default=True) - members: dict[Role, Set] = Field(default_factory=dict, exclude=True) - history: str = "" # For debug - - @model_validator(mode="after") - def init_roles(self): - self.add_roles(self.roles.values()) - return self - - def serialize(self, stg_path: Path): - roles_path = stg_path.joinpath("roles.json") - roles_info = [] - for role_key, role in self.roles.items(): - roles_info.append( - { - "role_class": role.__class__.__name__, - "module_name": role.__module__, - "role_name": role.name, - "role_sub_tags": list(self.members.get(role)), - } - ) - role.serialize(stg_path=stg_path.joinpath(f"roles/{role.__class__.__name__}_{role.name}")) - write_json_file(roles_path, roles_info) - - history_path = stg_path.joinpath("history.json") - write_json_file(history_path, {"content": self.history}) - - @classmethod - def deserialize(cls, stg_path: Path) -> "Environment": - """stg_path: ./storage/team/environment/""" - roles_path = stg_path.joinpath("roles.json") - roles_info = read_json_file(roles_path) - roles = [] - for role_info in roles_info: - # role stored in ./environment/roles/{role_class}_{role_name} - role_path = stg_path.joinpath(f"roles/{role_info.get('role_class')}_{role_info.get('role_name')}") - role = Role.deserialize(role_path) - roles.append(role) - - history = read_json_file(stg_path.joinpath("history.json")) - history = history.get("content") - - environment = Environment(**{"history": history}) - environment.add_roles(roles) - - return environment - - def add_role(self, role: Role): - """增加一个在当前环境的角色 - Add a role in the current environment - """ - self.roles[role.profile] = role - role.set_env(self) - - def add_roles(self, roles: Iterable[Role]): - """增加一批在当前环境的角色 - Add a batch of characters in the current environment - """ - for role in roles: - self.roles[role.profile] = role - - for role in roles: # setup system message with roles - role.set_env(self) - - def publish_message(self, message: Message, peekable: bool = True) -> bool: - """ - Distribute the message to the recipients. - In accordance with the Message routing structure design in Chapter 2.2.1 of RFC 116, as already planned - in RFC 113 for the entire system, the routing information in the Message is only responsible for - specifying the message recipient, without concern for where the message recipient is located. How to - route the message to the message recipient is a problem addressed by the transport framework designed - in RFC 113. - """ - logger.debug(f"publish_message: {message.dump()}") - found = False - # According to the routing feature plan in Chapter 2.2.3.2 of RFC 113 - for role, subscription in self.members.items(): - if is_subscribed(message, subscription): - role.put_message(message) - found = True - if not found: - logger.warning(f"Message no recipients: {message.dump()}") - self.history += f"\n{message}" # For debug - - return True - - async def run(self, k=1): - """处理一次所有信息的运行 - Process all Role runs at once - """ - for _ in range(k): - futures = [] - for role in self.roles.values(): - future = role.run() - futures.append(future) - - await asyncio.gather(*futures) - logger.debug(f"is idle: {self.is_idle}") - - def get_roles(self) -> dict[str, Role]: - """获得环境内的所有角色 - Process all Role runs at once - """ - return self.roles - - def get_role(self, name: str) -> Role: - """获得环境内的指定角色 - get all the environment roles - """ - return self.roles.get(name, None) - - def role_names(self) -> list[str]: - return [i.name for i in self.roles.values()] - - @property - def is_idle(self): - """If true, all actions have been executed.""" - for r in self.roles.values(): - if not r.is_idle: - return False - return True - - def get_subscription(self, obj): - """Get the labels for messages to be consumed by the object.""" - return self.members.get(obj, {}) - - def set_subscription(self, obj, tags): - """Set the labels for message to be consumed by the object""" - self.members[obj] = tags - - @staticmethod - def archive(auto_archive=True): - if auto_archive and CONFIG.git_repo: - CONFIG.git_repo.archive() diff --git a/metagpt/environment/README.md b/metagpt/environment/README.md new file mode 100644 index 000000000..9476ac75a --- /dev/null +++ b/metagpt/environment/README.md @@ -0,0 +1,38 @@ +Here is a environment description of MetaGPT env for different situation. +For now, the code only define the environment and still some todos like migrate roles/actions to current version. + +## Function +- Define `ExtEnv`(Base Class) which help users to integrate with external environment like games through apis or construct the game logics. +- Define `Environment`(Base Class) which is the env that MetaGPT directly used. And it includes roles and so on. +- Define the `EnvAPIRegistry` to mark the read/write apis that `ExtEnv` provide observe/step ability. And then, users can call the particular one to get observation from env or feedback to env. + +## Usage + +init environment +``` +android_env = env.create(EnvType.ANDROID) + +assistant = Role(name="Bob", profile="android assistant") +team = Team(investment=10.0, env=android_env, roles=[assistant]) +``` + +observe & step inside role's actions +``` +from metagpt.environment.api.env_api import EnvAPIAbstract + +# get screenshot from ExtEnv +screenshot_path: Path = env.observe( + EnvAPIAbstract( + api_name="get_screenshot", kwargs={"ss_name": f"{round_count}_before", "local_save_dir": task_dir} + ) + ) + +# do a `tap` action on the screen +res = env.step(EnvAPIAbstract("system_tap", kwargs={"x": x, "y": y})) +``` + +## TODO +- add android app operation assistant under `examples/android_assistant` +- migrate roles/actions of werewolf game from old version into current version +- migrate roles/actions of mincraft game from old version into current version +- migrate roles/actions of stanford_town game from old version into current version diff --git a/metagpt/environment/__init__.py b/metagpt/environment/__init__.py new file mode 100644 index 000000000..692672fa7 --- /dev/null +++ b/metagpt/environment/__init__.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : + +from metagpt.environment.base_env import Environment +from metagpt.environment.android_env.android_env import AndroidEnv +from metagpt.environment.mincraft_env.mincraft_env import MincraftExtEnv +from metagpt.environment.werewolf_env.werewolf_env import WerewolfEnv +from metagpt.environment.stanford_town_env.stanford_town_env import StanfordTownEnv +from metagpt.environment.software_env.software_env import SoftwareEnv + + +__all__ = ["AndroidEnv", "MincraftExtEnv", "WerewolfEnv", "StanfordTownEnv", "SoftwareEnv", "Environment"] diff --git a/metagpt/environment/android_env/__init__.py b/metagpt/environment/android_env/__init__.py new file mode 100644 index 000000000..2bcf8efd0 --- /dev/null +++ b/metagpt/environment/android_env/__init__.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : diff --git a/metagpt/environment/android_env/android_env.py b/metagpt/environment/android_env/android_env.py new file mode 100644 index 000000000..c27e20541 --- /dev/null +++ b/metagpt/environment/android_env/android_env.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : MG Android Env + +from pydantic import Field + +from metagpt.environment.android_env.android_ext_env import AndroidExtEnv +from metagpt.environment.base_env import Environment + + +class AndroidEnv(Environment, AndroidExtEnv): + rows: int = Field(default=0, description="rows of a grid on the screenshot") + cols: int = Field(default=0, description="cols of a grid on the screenshot") diff --git a/metagpt/environment/android_env/android_ext_env.py b/metagpt/environment/android_env/android_ext_env.py new file mode 100644 index 000000000..b81b2cd26 --- /dev/null +++ b/metagpt/environment/android_env/android_ext_env.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : The Android external environment to integrate with Android apps + +import subprocess +from pathlib import Path +from typing import Any, Optional + +from pydantic import Field + +from metagpt.environment.android_env.const import ADB_EXEC_FAIL +from metagpt.environment.base_env import ExtEnv, mark_as_readable, mark_as_writeable + + +class AndroidExtEnv(ExtEnv): + device_id: Optional[str] = Field(default=None) + screenshot_dir: Optional[Path] = Field(default=None) + xml_dir: Optional[Path] = Field(default=None) + width: int = Field(default=720, description="device screen width") + height: int = Field(default=1080, description="device screen height") + + def __init__(self, **data: Any): + super().__init__(**data) + if data.get("device_id"): + (width, height) = self.device_shape + self.width = data.get("width", width) + self.height = data.get("height", height) + + @property + def adb_prefix_si(self): + """adb cmd prefix with `device_id` and `shell input`""" + return f"adb -s {self.device_id} shell input " + + @property + def adb_prefix_shell(self): + """adb cmd prefix with `device_id` and `shell`""" + return f"adb -s {self.device_id} shell " + + @property + def adb_prefix(self): + """adb cmd prefix with `device_id`""" + return f"adb -s {self.device_id} " + + def execute_adb_with_cmd(self, adb_cmd: str) -> str: + res = subprocess.run(adb_cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + exec_res = ADB_EXEC_FAIL + if not res.returncode: + exec_res = res.stdout.strip() + return exec_res + + @property + def device_shape(self) -> tuple[int, int]: + adb_cmd = f"{self.adb_prefix_shell} wm size" + shape = (0, 0) + shape_res = self.execute_adb_with_cmd(adb_cmd) + if shape_res != ADB_EXEC_FAIL: + shape = tuple(map(int, shape_res.split(": ")[1].split("x"))) + return shape + + def list_devices(self): + adb_cmd = "adb devices" + res = self.execute_adb_with_cmd(adb_cmd) + devices = [] + if res != ADB_EXEC_FAIL: + devices = res.split("\n")[1:] + devices = [device.split()[0] for device in devices] + return devices + + @mark_as_readable + def get_screenshot(self, ss_name: str, local_save_dir: Path) -> Path: + """ + ss_name: screenshot file name + local_save_dir: local dir to store image from virtual machine + """ + assert self.screenshot_dir + ss_remote_path = Path(self.screenshot_dir).joinpath(f"{ss_name}.png") + ss_cmd = f"{self.adb_prefix_shell} screencap -p {ss_remote_path}" + ss_res = self.execute_adb_with_cmd(ss_cmd) + + res = ADB_EXEC_FAIL + if ss_res != ADB_EXEC_FAIL: + ss_local_path = Path(local_save_dir).joinpath(f"{ss_name}.png") + pull_cmd = f"{self.adb_prefix} pull {ss_remote_path} {ss_local_path}" + pull_res = self.execute_adb_with_cmd(pull_cmd) + if pull_res != ADB_EXEC_FAIL: + res = ss_local_path + return Path(res) + + @mark_as_readable + def get_xml(self, xml_name: str, local_save_dir: Path) -> Path: + xml_remote_path = Path(self.xml_dir).joinpath(f"{xml_name}.xml") + dump_cmd = f"{self.adb_prefix_shell} uiautomator dump {xml_remote_path}" + xml_res = self.execute_adb_with_cmd(dump_cmd) + + res = ADB_EXEC_FAIL + if xml_res != ADB_EXEC_FAIL: + xml_local_path = Path(local_save_dir).joinpath(f"{xml_name}.xml") + pull_cmd = f"{self.adb_prefix} pull {xml_remote_path} {xml_local_path}" + pull_res = self.execute_adb_with_cmd(pull_cmd) + if pull_res != ADB_EXEC_FAIL: + res = xml_local_path + return Path(res) + + @mark_as_writeable + def system_back(self) -> str: + adb_cmd = f"{self.adb_prefix_si} keyevent KEYCODE_BACK" + back_res = self.execute_adb_with_cmd(adb_cmd) + return back_res + + @mark_as_writeable + def system_tap(self, x: int, y: int) -> str: + adb_cmd = f"{self.adb_prefix_si} tap {x} {y}" + tap_res = self.execute_adb_with_cmd(adb_cmd) + return tap_res + + @mark_as_writeable + def user_input(self, input_txt: str) -> str: + input_txt = input_txt.replace(" ", "%s").replace("'", "") + adb_cmd = f"{self.adb_prefix_si} text {input_txt}" + input_res = self.execute_adb_with_cmd(adb_cmd) + return input_res + + @mark_as_writeable + def user_longpress(self, x: int, y: int, duration: int = 500) -> str: + adb_cmd = f"{self.adb_prefix_si} swipe {x} {y} {x} {y} {duration}" + press_res = self.execute_adb_with_cmd(adb_cmd) + return press_res + + @mark_as_writeable + def user_swipe(self, x: int, y: int, orient: str = "up", dist: str = "medium", if_quick: bool = False) -> str: + dist_unit = int(self.width / 10) + if dist == "long": + dist_unit *= 3 + elif dist == "medium": + dist_unit *= 2 + + if orient == "up": + offset = 0, -2 * dist_unit + elif orient == "down": + offset = 0, 2 * dist_unit + elif orient == "left": + offset = -1 * dist_unit, 0 + elif orient == "right": + offset = dist_unit, 0 + else: + return ADB_EXEC_FAIL + + duration = 100 if if_quick else 400 + adb_cmd = f"{self.adb_prefix_si} swipe {x} {y} {x + offset[0]} {y + offset[1]} {duration}" + swipe_res = self.execute_adb_with_cmd(adb_cmd) + return swipe_res + + @mark_as_writeable + def user_swipe_to(self, start: tuple[int, int], end: tuple[int, int], duration: int = 400): + adb_cmd = f"{self.adb_prefix_si} swipe {start[0]} {start[1]} {end[0]} {end[1]} {duration}" + swipe_res = self.execute_adb_with_cmd(adb_cmd) + return swipe_res diff --git a/metagpt/environment/android_env/const.py b/metagpt/environment/android_env/const.py new file mode 100644 index 000000000..8811289bf --- /dev/null +++ b/metagpt/environment/android_env/const.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : + +# For Android Assistant Agent +ADB_EXEC_FAIL = "FAILED" diff --git a/metagpt/environment/api/__init__.py b/metagpt/environment/api/__init__.py new file mode 100644 index 000000000..2bcf8efd0 --- /dev/null +++ b/metagpt/environment/api/__init__.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : diff --git a/metagpt/environment/api/env_api.py b/metagpt/environment/api/env_api.py new file mode 100644 index 000000000..1e6df544d --- /dev/null +++ b/metagpt/environment/api/env_api.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : the environment api store + +from typing import Any, Callable, Union + +from pydantic import BaseModel, Field + + +class EnvAPIAbstract(BaseModel): + """api/interface summary description""" + + api_name: str = Field(default="", description="the api function name or id") + args: set = Field(default={}, description="the api function `args` params") + kwargs: dict = Field(default=dict(), description="the api function `kwargs` params") + + +class EnvAPIRegistry(BaseModel): + """the registry to store environment w&r api/interface""" + + registry: dict[str, dict[str, Union[dict, Any, str]]] = Field(default=dict(), exclude=True) + + def get(self, api_name: str): + if api_name not in self.registry: + raise ValueError + return self.registry.get(api_name) + + def __getitem__(self, api_name: str) -> Callable: + return self.get(api_name) + + def __setitem__(self, api_name: str, func: Callable): + self.registry[api_name] = func + + def __len__(self): + return len(self.registry) + + def get_apis(self, as_str=True) -> dict[str, dict[str, Union[dict, Any, str]]]: + """return func schema without func instance""" + apis = dict() + for func_name, func_schema in self.registry.items(): + new_func_schema = dict() + for key, value in func_schema.items(): + if key == "func": + continue + new_func_schema[key] = str(value) if as_str else value + new_func_schema = new_func_schema + apis[func_name] = new_func_schema + return apis + + +class WriteAPIRegistry(EnvAPIRegistry): + """just as a explicit class name""" + + pass + + +class ReadAPIRegistry(EnvAPIRegistry): + """just as a explicit class name""" + + pass diff --git a/metagpt/environment/base_env.py b/metagpt/environment/base_env.py new file mode 100644 index 000000000..0e583ffb3 --- /dev/null +++ b/metagpt/environment/base_env.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : base env of executing environment + +import asyncio +from enum import Enum +from typing import TYPE_CHECKING, Any, Dict, Iterable, Optional, Set, Union + +from pydantic import BaseModel, ConfigDict, Field, SerializeAsAny, model_validator + +from metagpt.context import Context +from metagpt.environment.api.env_api import ( + EnvAPIAbstract, + ReadAPIRegistry, + WriteAPIRegistry, +) +from metagpt.logs import logger +from metagpt.schema import Message +from metagpt.utils.common import get_function_schema, is_coroutine_func, is_send_to + +if TYPE_CHECKING: + from metagpt.roles.role import Role # noqa: F401 + + +class EnvType(Enum): + ANDROID = "Android" + GYM = "Gym" + WEREWOLF = "Werewolf" + MINCRAFT = "Mincraft" + STANFORDTOWN = "StanfordTown" + + +env_write_api_registry = WriteAPIRegistry() +env_read_api_registry = ReadAPIRegistry() + + +def mark_as_readable(func): + """mark functionn as a readable one in ExtEnv, it observes something from ExtEnv""" + env_read_api_registry[func.__name__] = get_function_schema(func) + return func + + +def mark_as_writeable(func): + """mark functionn as a writeable one in ExtEnv, it does something to ExtEnv""" + env_write_api_registry[func.__name__] = get_function_schema(func) + return func + + +class ExtEnv(BaseModel): + """External Env to intergate actual game environment""" + + def _check_api_exist(self, rw_api: Optional[str] = None): + if not rw_api: + raise ValueError(f"{rw_api} not exists") + + def get_all_available_apis(self, mode: str = "read") -> list[Any]: + """get available read/write apis definition""" + assert mode in ["read", "write"] + if mode == "read": + return env_read_api_registry.get_apis() + else: + return env_write_api_registry.get_apis() + + async def observe(self, env_action: Union[str, EnvAPIAbstract]): + """get observation from particular api of ExtEnv""" + if isinstance(env_action, str): + read_api = env_read_api_registry.get(api_name=env_action)["func"] + self._check_api_exist(read_api) + if is_coroutine_func(read_api): + res = await read_api(self) + else: + res = read_api(self) + elif isinstance(env_action, EnvAPIAbstract): + read_api = env_read_api_registry.get(api_name=env_action.api_name)["func"] + self._check_api_exist(read_api) + if is_coroutine_func(read_api): + res = await read_api(self, *env_action.args, **env_action.kwargs) + else: + res = read_api(self, *env_action.args, **env_action.kwargs) + return res + + async def step(self, env_action: Union[str, Message, EnvAPIAbstract, list[EnvAPIAbstract]]): + """execute through particular api of ExtEnv""" + res = None + if isinstance(env_action, Message): + self.publish_message(env_action) + elif isinstance(env_action, EnvAPIAbstract): + write_api = env_write_api_registry.get(env_action.api_name)["func"] + self._check_api_exist(write_api) + if is_coroutine_func(write_api): + res = await write_api(self, *env_action.args, **env_action.kwargs) + else: + res = write_api(self, *env_action.args, **env_action.kwargs) + + return res + + +class Environment(ExtEnv): + """环境,承载一批角色,角色可以向环境发布消息,可以被其他角色观察到 + Environment, hosting a batch of roles, roles can publish messages to the environment, and can be observed by other roles + """ + + model_config = ConfigDict(arbitrary_types_allowed=True) + + desc: str = Field(default="") # 环境描述 + roles: dict[str, SerializeAsAny["Role"]] = Field(default_factory=dict, validate_default=True) + member_addrs: Dict["Role", Set] = Field(default_factory=dict, exclude=True) + history: str = "" # For debug + context: Context = Field(default_factory=Context, exclude=True) + + @model_validator(mode="after") + def init_roles(self): + self.add_roles(self.roles.values()) + return self + + def add_role(self, role: "Role"): + """增加一个在当前环境的角色 + Add a role in the current environment + """ + self.roles[role.profile] = role + role.set_env(self) + role.context = self.context + + def add_roles(self, roles: Iterable["Role"]): + """增加一批在当前环境的角色 + Add a batch of characters in the current environment + """ + for role in roles: + self.roles[role.profile] = role + + for role in roles: # setup system message with roles + role.set_env(self) + role.context = self.context + + def publish_message(self, message: Message, peekable: bool = True) -> bool: + """ + Distribute the message to the recipients. + In accordance with the Message routing structure design in Chapter 2.2.1 of RFC 116, as already planned + in RFC 113 for the entire system, the routing information in the Message is only responsible for + specifying the message recipient, without concern for where the message recipient is located. How to + route the message to the message recipient is a problem addressed by the transport framework designed + in RFC 113. + """ + logger.debug(f"publish_message: {message.dump()}") + found = False + # According to the routing feature plan in Chapter 2.2.3.2 of RFC 113 + for role, addrs in self.member_addrs.items(): + if is_send_to(message, addrs): + role.put_message(message) + found = True + if not found: + logger.warning(f"Message no recipients: {message.dump()}") + self.history += f"\n{message}" # For debug + + return True + + async def run(self, k=1): + """处理一次所有信息的运行 + Process all Role runs at once + """ + for _ in range(k): + futures = [] + for role in self.roles.values(): + future = role.run() + futures.append(future) + + await asyncio.gather(*futures) + logger.debug(f"is idle: {self.is_idle}") + + def get_roles(self) -> dict[str, "Role"]: + """获得环境内的所有角色 + Process all Role runs at once + """ + return self.roles + + def get_role(self, name: str) -> "Role": + """获得环境内的指定角色 + get all the environment roles + """ + return self.roles.get(name, None) + + def role_names(self) -> list[str]: + return [i.name for i in self.roles.values()] + + @property + def is_idle(self): + """If true, all actions have been executed.""" + for r in self.roles.values(): + if not r.is_idle: + return False + return True + + def get_addresses(self, obj): + """Get the addresses of the object.""" + return self.member_addrs.get(obj, {}) + + def set_addresses(self, obj, addresses): + """Set the addresses of the object""" + self.member_addrs[obj] = addresses + + def archive(self, auto_archive=True): + if auto_archive and self.context.git_repo: + self.context.git_repo.archive() + + @classmethod + def model_rebuild(cls, **kwargs): + from metagpt.roles.role import Role # noqa: F401 + + super().model_rebuild(**kwargs) + + +Environment.model_rebuild() diff --git a/metagpt/environment/mincraft_env/__init__.py b/metagpt/environment/mincraft_env/__init__.py new file mode 100644 index 000000000..2bcf8efd0 --- /dev/null +++ b/metagpt/environment/mincraft_env/__init__.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : diff --git a/metagpt/environment/mincraft_env/const.py b/metagpt/environment/mincraft_env/const.py new file mode 100644 index 000000000..a7222f9cd --- /dev/null +++ b/metagpt/environment/mincraft_env/const.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : + +from metagpt.const import METAGPT_ROOT + +# For Mincraft Game Agent +MC_CKPT_DIR = METAGPT_ROOT / "data/mincraft/ckpt" +MC_LOG_DIR = METAGPT_ROOT / "logs" +MC_DEFAULT_WARMUP = { + "context": 15, + "biome": 10, + "time": 15, + "nearby_blocks": 0, + "other_blocks": 10, + "nearby_entities": 5, + "health": 15, + "hunger": 15, + "position": 0, + "equipment": 0, + "inventory": 0, + "optional_inventory_items": 7, + "chests": 0, + "completed_tasks": 0, + "failed_tasks": 0, +} +MC_CURRICULUM_OB = [ + "context", + "biome", + "time", + "nearby_blocks", + "other_blocks", + "nearby_entities", + "health", + "hunger", + "position", + "equipment", + "inventory", + "chests", + "completed_tasks", + "failed_tasks", +] +MC_CORE_INVENTORY_ITEMS = r".*_log|.*_planks|stick|crafting_table|furnace" +r"|cobblestone|dirt|coal|.*_pickaxe|.*_sword|.*_axe", # curriculum_agent: only show these items in inventory before optional_inventory_items reached in warm up diff --git a/metagpt/environment/mincraft_env/mincraft_env.py b/metagpt/environment/mincraft_env/mincraft_env.py new file mode 100644 index 000000000..6327aa3f4 --- /dev/null +++ b/metagpt/environment/mincraft_env/mincraft_env.py @@ -0,0 +1,391 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : MG Mincraft Env +# refs to `voyager voyager.py` + +import json +import re +import time +from typing import Any, Iterable + +from langchain.embeddings.openai import OpenAIEmbeddings +from langchain.vectorstores import Chroma +from pydantic import ConfigDict, Field + +from metagpt.config2 import config as CONFIG +from metagpt.environment.base_env import Environment +from metagpt.environment.mincraft_env.const import MC_CKPT_DIR +from metagpt.environment.mincraft_env.mincraft_ext_env import MincraftExtEnv +from metagpt.logs import logger +from metagpt.utils.common import load_mc_skills_code, read_json_file, write_json_file + + +class MincraftEnv(Environment, MincraftExtEnv): + """MincraftEnv, including shared memory of cache and infomation between roles""" + + model_config = ConfigDict(arbitrary_types_allowed=True) + + event: dict[str, Any] = Field(default_factory=dict) + current_task: str = Field(default="Mine 1 wood log") + task_execution_time: float = Field(default=float) + context: str = Field(default="You can mine one of oak, birch, spruce, jungle, acacia, dark oak, or mangrove logs.") + code: str = Field(default="") + program_code: str = Field(default="") # write in skill/code/*.js + program_name: str = Field(default="") + critique: str = Field(default="") + skills: dict = Field(default_factory=dict) # for skills.json + retrieve_skills: list[str] = Field(default_factory=list) + event_summary: str = Field(default="") + + qa_cache: dict[str, str] = Field(default_factory=dict) + completed_tasks: list[str] = Field(default_factory=list) # Critique things + failed_tasks: list[str] = Field(default_factory=list) + + skill_desp: str = Field(default="") + + chest_memory: dict[str, Any] = Field(default_factory=dict) # eg: {'(1344, 64, 1381)': 'Unknown'} + chest_observation: str = Field(default="") # eg: "Chests: None\n\n" + + runtime_status: bool = False # equal to action execution status: success or failed + + vectordb: Chroma = Field(default_factory=Chroma) + + qa_cache_questions_vectordb: Chroma = Field(default_factory=Chroma) + + @property + def progress(self): + # return len(self.completed_tasks) + 10 # Test only + return len(self.completed_tasks) + + @property + def programs(self): + programs = "" + if self.code == "": + return programs # TODO: maybe fix 10054 now, a better way is isolating env.step() like voyager + for skill_name, entry in self.skills.items(): + programs += f"{entry['code']}\n\n" + for primitives in load_mc_skills_code(): # TODO add skills_dir + programs += f"{primitives}\n\n" + return programs + + def set_mc_port(self, mc_port): + super().set_mc_port(mc_port) + self.set_mc_resume() + + def set_mc_resume(self): + self.qa_cache_questions_vectordb = Chroma( + collection_name="qa_cache_questions_vectordb", + embedding_function=OpenAIEmbeddings(), + persist_directory=f"{MC_CKPT_DIR}/curriculum/vectordb", + ) + + self.vectordb = Chroma( + collection_name="skill_vectordb", + embedding_function=OpenAIEmbeddings(), + persist_directory=f"{MC_CKPT_DIR}/skill/vectordb", + ) + + if CONFIG.resume: + logger.info(f"Loading Action Developer from {MC_CKPT_DIR}/action") + self.chest_memory = read_json_file(f"{MC_CKPT_DIR}/action/chest_memory.json") + + logger.info(f"Loading Curriculum Agent from {MC_CKPT_DIR}/curriculum") + self.completed_tasks = read_json_file(f"{MC_CKPT_DIR}/curriculum/completed_tasks.json") + self.failed_tasks = read_json_file(f"{MC_CKPT_DIR}/curriculum/failed_tasks.json") + + logger.info(f"Loading Skill Manager from {MC_CKPT_DIR}/skill\033[0m") + self.skills = read_json_file(f"{MC_CKPT_DIR}/skill/skills.json") + + logger.info(f"Loading Qa Cache from {MC_CKPT_DIR}/curriculum\033[0m") + self.qa_cache = read_json_file(f"{MC_CKPT_DIR}/curriculum/qa_cache.json") + + if self.vectordb._collection.count() == 0: + logger.info(self.vectordb._collection.count()) + # Set vdvs for skills & qa_cache + skill_desps = [skill["description"] for program_name, skill in self.skills.items()] + program_names = [program_name for program_name, skill in self.skills.items()] + metadatas = [{"name": program_name} for program_name in program_names] + # add vectordb from file + self.vectordb.add_texts( + texts=skill_desps, + ids=program_names, + metadatas=metadatas, + ) + self.vectordb.persist() + + logger.info(self.qa_cache_questions_vectordb._collection.count()) + if self.qa_cache_questions_vectordb._collection.count() == 0: + questions = [question for question, answer in self.qa_cache.items()] + + self.qa_cache_questions_vectordb.add_texts(texts=questions) + + self.qa_cache_questions_vectordb.persist() + + logger.info( + f"INIT_CHECK: There are {self.vectordb._collection.count()} skills in vectordb and {len(self.skills)} skills in skills.json." + ) + # Check if Skill Manager's vectordb right using + assert self.vectordb._collection.count() == len(self.skills), ( + f"Skill Manager's vectordb is not synced with skills.json.\n" + f"There are {self.vectordb._collection.count()} skills in vectordb but {len(self.skills)} skills in skills.json.\n" + f"Did you set resume=False when initializing the manager?\n" + f"You may need to manually delete the vectordb directory for running from scratch." + ) + + logger.info( + f"INIT_CHECK: There are {self.qa_cache_questions_vectordb._collection.count()} qa_cache in vectordb and {len(self.qa_cache)} questions in qa_cache.json." + ) + assert self.qa_cache_questions_vectordb._collection.count() == len(self.qa_cache), ( + f"Curriculum Agent's qa cache question vectordb is not synced with qa_cache.json.\n" + f"There are {self.qa_cache_questions_vectordb._collection.count()} questions in vectordb " + f"but {len(self.qa_cache)} questions in qa_cache.json.\n" + f"Did you set resume=False when initializing the agent?\n" + f"You may need to manually delete the qa cache question vectordb directory for running from scratch.\n" + ) + + def register_roles(self, roles: Iterable["Minecraft"]): + for role in roles: + role.set_memory(self) + + def update_event(self, event: dict): + if self.event == event: + return + self.event = event + self.update_chest_memory(event) + self.update_chest_observation() + # self.event_summary = self.summarize_chatlog(event) + + def update_task(self, task: str): + self.current_task = task + + def update_context(self, context: str): + self.context = context + + def update_program_code(self, program_code: str): + self.program_code = program_code + + def update_code(self, code: str): + self.code = code # action_developer.gen_action_code to HERE + + def update_program_name(self, program_name: str): + self.program_name = program_name + + def update_critique(self, critique: str): + self.critique = critique # critic_agent.check_task_success to HERE + + def append_skill(self, skill: dict): + self.skills[self.program_name] = skill # skill_manager.retrieve_skills to HERE + + def update_retrieve_skills(self, retrieve_skills: list): + self.retrieve_skills = retrieve_skills + + def update_skill_desp(self, skill_desp: str): + self.skill_desp = skill_desp + + async def update_qa_cache(self, qa_cache: dict): + self.qa_cache = qa_cache + + def update_chest_memory(self, events: dict): + """ + Input: events: Dict + Result: self.chest_memory update & save to json + """ + nearbyChests = events[-1][1]["nearbyChests"] + for position, chest in nearbyChests.items(): + if position in self.chest_memory: + if isinstance(chest, dict): + self.chest_memory[position] = chest + if chest == "Invalid": + logger.info(f"Action Developer removing chest {position}: {chest}") + self.chest_memory.pop(position) + else: + if chest != "Invalid": + logger.info(f"Action Developer saving chest {position}: {chest}") + self.chest_memory[position] = chest + + write_json_file(f"{MC_CKPT_DIR}/action/chest_memory.json", self.chest_memory) + + def update_chest_observation(self): + """ + update chest_memory to chest_observation. + Refer to @ https://github.com/MineDojo/Voyager/blob/main/voyager/agents/action.py + """ + + chests = [] + for chest_position, chest in self.chest_memory.items(): + if isinstance(chest, dict) and len(chest) > 0: + chests.append(f"{chest_position}: {chest}") + for chest_position, chest in self.chest_memory.items(): + if isinstance(chest, dict) and len(chest) == 0: + chests.append(f"{chest_position}: Empty") + for chest_position, chest in self.chest_memory.items(): + if isinstance(chest, str): + assert chest == "Unknown" + chests.append(f"{chest_position}: Unknown items inside") + assert len(chests) == len(self.chest_memory) + if chests: + chests = "\n".join(chests) + self.chest_observation = f"Chests:\n{chests}\n\n" + else: + self.chest_observation = "Chests: None\n\n" + + def summarize_chatlog(self, events): + def filter_item(message: str): + craft_pattern = r"I cannot make \w+ because I need: (.*)" + craft_pattern2 = r"I cannot make \w+ because there is no crafting table nearby" + mine_pattern = r"I need at least a (.*) to mine \w+!" + if re.match(craft_pattern, message): + self.event_summary = re.match(craft_pattern, message).groups()[0] + elif re.match(craft_pattern2, message): + self.event_summary = "a nearby crafting table" + elif re.match(mine_pattern, message): + self.event_summary = re.match(mine_pattern, message).groups()[0] + else: + self.event_summary = "" + return self.event_summary + + chatlog = set() + for event_type, event in events: + if event_type == "onChat": + item = filter_item(event["onChat"]) + if item: + chatlog.add(item) + self.event_summary = "I also need " + ", ".join(chatlog) + "." if chatlog else "" + + def reset_block_info(self): + # revert all the placing event in the last step + pass + + def update_exploration_progress(self, success: bool): + """ + Split task into completed_tasks or failed_tasks + Args: info = { + "task": self.task, + "success": success, + "conversations": self.conversations, + } + """ + self.runtime_status = success + task = self.current_task + if task.startswith("Deposit useless items into the chest at"): + return + if success: + logger.info(f"Completed task {task}.") + self.completed_tasks.append(task) + else: + logger.info(f"Failed to complete task {task}. Skipping to next task.") + self.failed_tasks.append(task) + # when not success, below to update event! + # revert all the placing event in the last step + blocks = [] + positions = [] + for event_type, event in self.event: + if event_type == "onSave" and event["onSave"].endswith("_placed"): + block = event["onSave"].split("_placed")[0] + position = event["status"]["position"] + blocks.append(block) + positions.append(position) + new_events = self.step( + f"await givePlacedItemBack(bot, {json.dumps(blocks)}, {json.dumps(positions)})", + programs=self.programs, + ) + self.event[-1][1]["inventory"] = new_events[-1][1]["inventory"] + self.event[-1][1]["voxels"] = new_events[-1][1]["voxels"] + + self.save_sorted_tasks() + + def save_sorted_tasks(self): + updated_completed_tasks = [] + # record repeated failed tasks + updated_failed_tasks = self.failed_tasks + # dedup but keep order + for task in self.completed_tasks: + if task not in updated_completed_tasks: + updated_completed_tasks.append(task) + + # remove completed tasks from failed tasks + for task in updated_completed_tasks: + while task in updated_failed_tasks: + updated_failed_tasks.remove(task) + + self.completed_tasks = updated_completed_tasks + self.failed_tasks = updated_failed_tasks + + # dump to json + write_json_file(f"{MC_CKPT_DIR}/curriculum/completed_tasks.json", self.completed_tasks) + write_json_file(f"{MC_CKPT_DIR}/curriculum/failed_tasks.json", self.failed_tasks) + + async def on_event_retrieve(self, *args): + """ + Retrieve Minecraft events. + + Returns: + list: A list of Minecraft events. + + Raises: + Exception: If there is an issue retrieving events. + """ + try: + self.reset( + options={ + "mode": "soft", + "wait_ticks": 20, + } + ) + # difficulty = "easy" if len(self.completed_tasks) > 15 else "peaceful" + difficulty = "peaceful" + + events = self.step("bot.chat(`/time set ${getNextTime()}`);\n" + f"bot.chat('/difficulty {difficulty}');") + self.update_event(events) + return events + except Exception as e: + time.sleep(3) # wait for mineflayer to exit + # reset bot status here + events = self.reset( + options={ + "mode": "hard", + "wait_ticks": 20, + "inventory": self.event[-1][1]["inventory"], + "equipment": self.event[-1][1]["status"]["equipment"], + "position": self.event[-1][1]["status"]["position"], + } + ) + self.update_event(events) + logger.error(f"Failed to retrieve Minecraft events: {str(e)}") + return events + + async def on_event_execute(self, *args): + """ + Execute Minecraft events. + + This function is used to obtain events from the Minecraft environment. Check the implementation in + the 'voyager/env/bridge.py step()' function to capture events generated within the game. + + Returns: + list: A list of Minecraft events. + + Raises: + Exception: If there is an issue retrieving events. + """ + try: + events = self.step( + code=self.code, + programs=self.programs, + ) + self.update_event(events) + return events + except Exception as e: + time.sleep(3) # wait for mineflayer to exit + # reset bot status here + events = self.reset( + options={ + "mode": "hard", + "wait_ticks": 20, + "inventory": self.event[-1][1]["inventory"], + "equipment": self.event[-1][1]["status"]["equipment"], + "position": self.event[-1][1]["status"]["position"], + } + ) + self.update_event(events) + logger.error(f"Failed to execute Minecraft events: {str(e)}") + return events diff --git a/metagpt/environment/mincraft_env/mincraft_ext_env.py b/metagpt/environment/mincraft_env/mincraft_ext_env.py new file mode 100644 index 000000000..b86250d8c --- /dev/null +++ b/metagpt/environment/mincraft_env/mincraft_ext_env.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : The Mincraft external environment to integrate with Mincraft game +# refs to `voyager bridge.py` + +import json +import time +from typing import Optional + +import requests +from pydantic import ConfigDict, Field, model_validator + +from metagpt.environment.base_env import ExtEnv, mark_as_writeable +from metagpt.environment.mincraft_env.const import ( + MC_CKPT_DIR, + MC_CORE_INVENTORY_ITEMS, + MC_CURRICULUM_OB, + MC_DEFAULT_WARMUP, + METAGPT_ROOT, +) +from metagpt.environment.mincraft_env.process_monitor import SubprocessMonitor +from metagpt.logs import logger + + +class MincraftExtEnv(ExtEnv): + model_config = ConfigDict(arbitrary_types_allowed=True) + + mc_port: Optional[int] = Field(default=None) + server_host: str = Field(default="http://127.0.0.1") + server_port: str = Field(default=3000) + request_timeout: int = Field(default=600) + + mineflayer: Optional[SubprocessMonitor] = Field(default=None, validate_default=True) + + has_reset: bool = Field(default=False) + reset_options: Optional[dict] = Field(default=None) + connected: bool = Field(default=False) + server_paused: bool = Field(default=False) + warm_up: dict = Field(default=dict()) + + @property + def server(self) -> str: + return f"{self.server_host}:{self.server_port}" + + @model_validator(mode="after") + def _post_init_ext_env(self): + if not self.mineflayer: + self.mineflayer = SubprocessMonitor( + commands=[ + "node", + METAGPT_ROOT.joinpath("metagpt", "environment", "mincraft_env", "mineflayer", "index.js"), + str(self.server_port), + ], + name="mineflayer", + ready_match=r"Server started on port (\d+)", + ) + if not self.warm_up: + warm_up = MC_DEFAULT_WARMUP + if "optional_inventory_items" in warm_up: + assert MC_CORE_INVENTORY_ITEMS is not None + # self.core_inv_items_regex = re.compile(MC_CORE_INVENTORY_ITEMS) + self.warm_up["optional_inventory_items"] = warm_up["optional_inventory_items"] + else: + self.warm_up["optional_inventory_items"] = 0 + for key in MC_CURRICULUM_OB: + self.warm_up[key] = warm_up.get(key, MC_DEFAULT_WARMUP[key]) + self.warm_up["nearby_blocks"] = 0 + self.warm_up["inventory"] = 0 + self.warm_up["completed_tasks"] = 0 + self.warm_up["failed_tasks"] = 0 + + # init ckpt sub-forders + MC_CKPT_DIR.joinpath("curriculum/vectordb").mkdir(parents=True, exist_ok=True) + MC_CKPT_DIR.joinpath("action").mkdir(exist_ok=True) + MC_CKPT_DIR.joinpath("skill/code").mkdir(parents=True, exist_ok=True) + MC_CKPT_DIR.joinpath("skill/description").mkdir(exist_ok=True) + MC_CKPT_DIR.joinpath("skill/vectordb").mkdir(exist_ok=True) + + def set_mc_port(self, mc_port: int): + self.mc_port = mc_port + + @mark_as_writeable + def close(self) -> bool: + self.unpause() + if self.connected: + res = requests.post(f"{self.server}/stop") + if res.status_code == 200: + self.connected = False + self.mineflayer.stop() + return not self.connected + + @mark_as_writeable + def check_process(self) -> dict: + retry = 0 + while not self.mineflayer.is_running: + logger.info("Mineflayer process has exited, restarting") + self.mineflayer.run() + if not self.mineflayer.is_running: + if retry > 3: + logger.error("Mineflayer process failed to start") + raise {} + else: + retry += 1 + continue + logger.info(self.mineflayer.ready_line) + res = requests.post( + f"{self.server}/start", + json=self.reset_options, + timeout=self.request_timeout, + ) + if res.status_code != 200: + self.mineflayer.stop() + logger.error(f"Minecraft server reply with code {res.status_code}") + raise {} + return res.json() + + @mark_as_writeable + def reset(self, *, seed=None, options=None) -> dict: + if options is None: + options = {} + if options.get("inventory", {}) and options.get("mode", "hard") != "hard": + logger.error("inventory can only be set when options is hard") + raise {} + + self.reset_options = { + "port": self.mc_port, + "reset": options.get("mode", "hard"), + "inventory": options.get("inventory", {}), + "equipment": options.get("equipment", []), + "spread": options.get("spread", False), + "waitTicks": options.get("wait_ticks", 5), + "position": options.get("position", None), + } + + self.unpause() + self.mineflayer.stop() + time.sleep(1) # wait for mineflayer to exit + + returned_data = self.check_process() + self.has_reset = True + self.connected = True + # All the reset in step will be soft + self.reset_options["reset"] = "soft" + self.pause() + return json.loads(returned_data) + + @mark_as_writeable + def step(self, code: str, programs: str = "") -> dict: + if not self.has_reset: + raise RuntimeError("Environment has not been reset yet") + self.check_process() + self.unpause() + data = { + "code": code, + "programs": programs, + } + res = requests.post(f"{self.server}/step", json=data, timeout=self.request_timeout) + if res.status_code != 200: + raise RuntimeError("Failed to step Minecraft server") + returned_data = res.json() + self.pause() + return json.loads(returned_data) + + @mark_as_writeable + def pause(self) -> bool: + if self.mineflayer.is_running and not self.server_paused: + res = requests.post(f"{self.server}/pause") + if res.status_code == 200: + self.server_paused = True + return self.server_paused + + @mark_as_writeable + def unpause(self) -> bool: + if self.mineflayer.is_running and self.server_paused: + res = requests.post(f"{self.server}/pause") + if res.status_code == 200: + self.server_paused = False + else: + logger.info(f"mineflayer pause result: {res.json()}") + return self.server_paused diff --git a/metagpt/environment/mincraft_env/mineflayer/.gitignore b/metagpt/environment/mincraft_env/mineflayer/.gitignore new file mode 100644 index 000000000..0fd468410 --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/.gitignore @@ -0,0 +1 @@ +!/lib \ No newline at end of file diff --git a/metagpt/environment/mincraft_env/mineflayer/.prettierignore b/metagpt/environment/mincraft_env/mineflayer/.prettierignore new file mode 100644 index 000000000..1b07c39e9 --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/.prettierignore @@ -0,0 +1,3 @@ +# Ignore artifacts: +build +coverage \ No newline at end of file diff --git a/metagpt/environment/mincraft_env/mineflayer/.prettierrc.json b/metagpt/environment/mincraft_env/mineflayer/.prettierrc.json new file mode 100644 index 000000000..0a02bcefd --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/.prettierrc.json @@ -0,0 +1,3 @@ +{ + "tabWidth": 4 +} diff --git a/metagpt/environment/mincraft_env/mineflayer/index.js b/metagpt/environment/mincraft_env/mineflayer/index.js new file mode 100644 index 000000000..7fb0a8787 --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/index.js @@ -0,0 +1,425 @@ +const fs = require("fs"); +const express = require("express"); +const bodyParser = require("body-parser"); +const mineflayer = require("mineflayer"); + +const skills = require("./lib/skillLoader"); +const { initCounter, getNextTime } = require("./lib/utils"); +const obs = require("./lib/observation/base"); +const OnChat = require("./lib/observation/onChat"); +const OnError = require("./lib/observation/onError"); +const { Voxels, BlockRecords } = require("./lib/observation/voxels"); +const Status = require("./lib/observation/status"); +const Inventory = require("./lib/observation/inventory"); +const OnSave = require("./lib/observation/onSave"); +const Chests = require("./lib/observation/chests"); +const { plugin: tool } = require("mineflayer-tool"); + +let bot = null; + +const app = express(); + +app.use(bodyParser.json({ limit: "50mb" })); +app.use(bodyParser.urlencoded({ limit: "50mb", extended: false })); + +app.post("/start", (req, res) => { + if (bot) onDisconnect("Restarting bot"); + bot = null; + console.log(req.body); + bot = mineflayer.createBot({ + host: "localhost", // minecraft server ip + port: req.body.port, // minecraft server port + username: "bot", + disableChatSigning: true, + checkTimeoutInterval: 60 * 60 * 1000, + }); + bot.once("error", onConnectionFailed); + + // Event subscriptions + bot.waitTicks = req.body.waitTicks; + bot.globalTickCounter = 0; + bot.stuckTickCounter = 0; + bot.stuckPosList = []; + bot.iron_pickaxe = false; + + bot.on("kicked", onDisconnect); + + // mounting will cause physicsTick to stop + bot.on("mount", () => { + bot.dismount(); + }); + + bot.once("spawn", async () => { + bot.removeListener("error", onConnectionFailed); + let itemTicks = 1; + if (req.body.reset === "hard") { + bot.chat("/clear @s"); + bot.chat("/kill @s"); + const inventory = req.body.inventory ? req.body.inventory : {}; + const equipment = req.body.equipment + ? req.body.equipment + : [null, null, null, null, null, null]; + for (let key in inventory) { + bot.chat(`/give @s minecraft:${key} ${inventory[key]}`); + itemTicks += 1; + } + const equipmentNames = [ + "armor.head", + "armor.chest", + "armor.legs", + "armor.feet", + "weapon.mainhand", + "weapon.offhand", + ]; + for (let i = 0; i < 6; i++) { + if (i === 4) continue; + if (equipment[i]) { + bot.chat( + `/item replace entity @s ${equipmentNames[i]} with minecraft:${equipment[i]}` + ); + itemTicks += 1; + } + } + } + + if (req.body.position) { + bot.chat( + `/tp @s ${req.body.position.x} ${req.body.position.y} ${req.body.position.z}` + ); + } + + // if iron_pickaxe is in bot's inventory + if ( + bot.inventory.items().find((item) => item.name === "iron_pickaxe") + ) { + bot.iron_pickaxe = true; + } + + const { pathfinder } = require("mineflayer-pathfinder"); + const tool = require("mineflayer-tool").plugin; + const collectBlock = require("mineflayer-collectblock").plugin; + const pvp = require("mineflayer-pvp").plugin; + const minecraftHawkEye = require("minecrafthawkeye"); + bot.loadPlugin(pathfinder); + bot.loadPlugin(tool); + bot.loadPlugin(collectBlock); + bot.loadPlugin(pvp); + bot.loadPlugin(minecraftHawkEye); + + // bot.collectBlock.movements.digCost = 0; + // bot.collectBlock.movements.placeCost = 0; + + obs.inject(bot, [ + OnChat, + OnError, + Voxels, + Status, + Inventory, + OnSave, + Chests, + BlockRecords, + ]); + skills.inject(bot); + + if (req.body.spread) { + bot.chat(`/spreadplayers ~ ~ 0 300 under 80 false @s`); + await bot.waitForTicks(bot.waitTicks); + } + + await bot.waitForTicks(bot.waitTicks * itemTicks); + res.json(bot.observe()); + + initCounter(bot); + bot.chat("/gamerule keepInventory true"); + bot.chat("/gamerule doDaylightCycle false"); + }); + + function onConnectionFailed(e) { + console.log(e); + bot = null; + res.status(400).json({ error: e }); + } + function onDisconnect(message) { + if (bot.viewer) { + bot.viewer.close(); + } + bot.end(); + console.log(message); + bot = null; + } +}); + +app.post("/step", async (req, res) => { + // import useful package + let response_sent = false; + function otherError(err) { + console.log("Uncaught Error"); + bot.emit("error", handleError(err)); + bot.waitForTicks(bot.waitTicks).then(() => { + if (!response_sent) { + response_sent = true; + res.json(bot.observe()); + } + }); + } + + process.on("uncaughtException", otherError); + + const mcData = require("minecraft-data")(bot.version); + mcData.itemsByName["leather_cap"] = mcData.itemsByName["leather_helmet"]; + mcData.itemsByName["leather_tunic"] = + mcData.itemsByName["leather_chestplate"]; + mcData.itemsByName["leather_pants"] = + mcData.itemsByName["leather_leggings"]; + mcData.itemsByName["leather_boots"] = mcData.itemsByName["leather_boots"]; + mcData.itemsByName["lapis_lazuli_ore"] = mcData.itemsByName["lapis_ore"]; + mcData.blocksByName["lapis_lazuli_ore"] = mcData.blocksByName["lapis_ore"]; + const { + Movements, + goals: { + Goal, + GoalBlock, + GoalNear, + GoalXZ, + GoalNearXZ, + GoalY, + GoalGetToBlock, + GoalLookAtBlock, + GoalBreakBlock, + GoalCompositeAny, + GoalCompositeAll, + GoalInvert, + GoalFollow, + GoalPlaceBlock, + }, + pathfinder, + Move, + ComputedPath, + PartiallyComputedPath, + XZCoordinates, + XYZCoordinates, + SafeBlock, + GoalPlaceBlockOptions, + } = require("mineflayer-pathfinder"); + const { Vec3 } = require("vec3"); + + // Set up pathfinder + const movements = new Movements(bot, mcData); + bot.pathfinder.setMovements(movements); + + bot.globalTickCounter = 0; + bot.stuckTickCounter = 0; + bot.stuckPosList = []; + + function onTick() { + bot.globalTickCounter++; + if (bot.pathfinder.isMoving()) { + bot.stuckTickCounter++; + if (bot.stuckTickCounter >= 100) { + onStuck(1.5); + bot.stuckTickCounter = 0; + } + } + } + + bot.on("physicTick", onTick); + + // initialize fail count + let _craftItemFailCount = 0; + let _killMobFailCount = 0; + let _mineBlockFailCount = 0; + let _placeItemFailCount = 0; + let _smeltItemFailCount = 0; + + // Retrieve array form post bod + const code = req.body.code; + const programs = req.body.programs; + bot.cumulativeObs = []; + await bot.waitForTicks(bot.waitTicks); + const r = await evaluateCode(code, programs); + process.off("uncaughtException", otherError); + if (r !== "success") { + bot.emit("error", handleError(r)); + } + await returnItems(); + // wait for last message + await bot.waitForTicks(bot.waitTicks); + if (!response_sent) { + response_sent = true; + res.json(bot.observe()); + } + bot.removeListener("physicTick", onTick); + + async function evaluateCode(code, programs) { + // Echo the code produced for players to see it. Don't echo when the bot code is already producing dialog or it will double echo + try { + await eval("(async () => {" + programs + "\n" + code + "})()"); + return "success"; + } catch (err) { + return err; + } + } + + function onStuck(posThreshold) { + const currentPos = bot.entity.position; + bot.stuckPosList.push(currentPos); + + // Check if the list is full + if (bot.stuckPosList.length === 5) { + const oldestPos = bot.stuckPosList[0]; + const posDifference = currentPos.distanceTo(oldestPos); + + if (posDifference < posThreshold) { + teleportBot(); // execute the function + } + + // Remove the oldest time from the list + bot.stuckPosList.shift(); + } + } + + function teleportBot() { + const blocks = bot.findBlocks({ + matching: (block) => { + return block.type === 0; + }, + maxDistance: 1, + count: 27, + }); + + if (blocks) { + // console.log(blocks.length); + const randomIndex = Math.floor(Math.random() * blocks.length); + const block = blocks[randomIndex]; + bot.chat(`/tp @s ${block.x} ${block.y} ${block.z}`); + } else { + bot.chat("/tp @s ~ ~1.25 ~"); + } + } + + function returnItems() { + bot.chat("/gamerule doTileDrops false"); + const crafting_table = bot.findBlock({ + matching: mcData.blocksByName.crafting_table.id, + maxDistance: 128, + }); + if (crafting_table) { + bot.chat( + `/setblock ${crafting_table.position.x} ${crafting_table.position.y} ${crafting_table.position.z} air destroy` + ); + bot.chat("/give @s crafting_table"); + } + const furnace = bot.findBlock({ + matching: mcData.blocksByName.furnace.id, + maxDistance: 128, + }); + if (furnace) { + bot.chat( + `/setblock ${furnace.position.x} ${furnace.position.y} ${furnace.position.z} air destroy` + ); + bot.chat("/give @s furnace"); + } + if (bot.inventoryUsed() >= 32) { + // if chest is not in bot's inventory + if (!bot.inventory.items().find((item) => item.name === "chest")) { + bot.chat("/give @s chest"); + } + } + // if iron_pickaxe not in bot's inventory and bot.iron_pickaxe + if ( + bot.iron_pickaxe && + !bot.inventory.items().find((item) => item.name === "iron_pickaxe") + ) { + bot.chat("/give @s iron_pickaxe"); + } + bot.chat("/gamerule doTileDrops true"); + } + + function handleError(err) { + let stack = err.stack; + if (!stack) { + return err; + } + console.log(stack); + const final_line = stack.split("\n")[1]; + const regex = /:(\d+):\d+\)/; + + const programs_length = programs.split("\n").length; + let match_line = null; + for (const line of stack.split("\n")) { + const match = regex.exec(line); + if (match) { + const line_num = parseInt(match[1]); + if (line_num >= programs_length) { + match_line = line_num - programs_length; + break; + } + } + } + if (!match_line) { + return err.message; + } + let f_line = final_line.match( + /\((?.*):(?\d+):(?\d+)\)/ + ); + if (f_line && f_line.groups && fs.existsSync(f_line.groups.file)) { + const { file, line, pos } = f_line.groups; + const f = fs.readFileSync(file, "utf8").split("\n"); + // let filename = file.match(/(?<=node_modules\\)(.*)/)[1]; + let source = file + `:${line}\n${f[line - 1].trim()}\n `; + + const code_source = + "at " + + code.split("\n")[match_line - 1].trim() + + " in your code"; + return source + err.message + "\n" + code_source; + } else if ( + f_line && + f_line.groups && + f_line.groups.file.includes("") + ) { + const { file, line, pos } = f_line.groups; + let source = + "Your code" + + `:${match_line}\n${code.split("\n")[match_line - 1].trim()}\n `; + let code_source = ""; + if (line < programs_length) { + source = + "In your program code: " + + programs.split("\n")[line - 1].trim() + + "\n"; + code_source = `at line ${match_line}:${code + .split("\n") + [match_line - 1].trim()} in your code`; + } + return source + err.message + "\n" + code_source; + } + return err.message; + } +}); + +app.post("/stop", (req, res) => { + bot.end(); + res.json({ + message: "Bot stopped", + }); +}); + +app.post("/pause", (req, res) => { + if (!bot) { + res.status(400).json({ error: "Bot not spawned" }); + return; + } + bot.chat("/pause"); + bot.waitForTicks(bot.waitTicks).then(() => { + res.json({ message: "Success" }); + }); +}); + +// Server listening to PORT 3000 + +const DEFAULT_PORT = 3000; +const PORT = process.argv[2] || DEFAULT_PORT; +app.listen(PORT, () => { + console.log(`Server started on port ${PORT}`); +}); diff --git a/metagpt/environment/mincraft_env/mineflayer/lib/observation/base.js b/metagpt/environment/mincraft_env/mineflayer/lib/observation/base.js new file mode 100644 index 000000000..b661a24b5 --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/lib/observation/base.js @@ -0,0 +1,45 @@ +class Observation { + constructor(bot) { + if (new.target === Observation) { + throw new TypeError( + "Cannot instantiate abstract class Observation" + ); + } + + this.bot = bot; + this.name = "Observation"; + } + + observe() { + throw new TypeError("Method 'observe()' must be implemented."); + } + + reset() {} +} + +function inject(bot, obs_list) { + bot.obsList = []; + bot.cumulativeObs = []; + bot.eventMemory = {}; + obs_list.forEach((obs) => { + bot.obsList.push(new obs(bot)); + }); + bot.event = function (event_name) { + let result = {}; + bot.obsList.forEach((obs) => { + if (obs.name.startsWith("on") && obs.name !== event_name) { + return; + } + result[obs.name] = obs.observe(); + }); + bot.cumulativeObs.push([event_name, result]); + }; + bot.observe = function () { + bot.event("observe"); + const result = bot.cumulativeObs; + bot.cumulativeObs = []; + return JSON.stringify(result); + }; +} + +module.exports = { Observation, inject }; diff --git a/metagpt/environment/mincraft_env/mineflayer/lib/observation/chests.js b/metagpt/environment/mincraft_env/mineflayer/lib/observation/chests.js new file mode 100644 index 000000000..842bd171d --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/lib/observation/chests.js @@ -0,0 +1,31 @@ +const { Observation } = require("./base"); + +class Chests extends Observation { + constructor(bot) { + super(bot); + this.name = "nearbyChests"; + this.chestsItems = {}; + bot.on("closeChest", (chestItems, position) => { + this.chestsItems[position] = chestItems; + }); + bot.on("removeChest", (chestPosition) => { + this.chestsItems[chestPosition] = "Invalid"; + }); + } + + observe() { + const chests = this.bot.findBlocks({ + matching: this.bot.registry.blocksByName.chest.id, + maxDistance: 16, + count: 999, + }); + chests.forEach((chest) => { + if (!this.chestsItems.hasOwnProperty(chest)) { + this.chestsItems[chest] = "Unknown"; + } + }); + return this.chestsItems; + } +} + +module.exports = Chests; diff --git a/metagpt/environment/mincraft_env/mineflayer/lib/observation/inventory.js b/metagpt/environment/mincraft_env/mineflayer/lib/observation/inventory.js new file mode 100644 index 000000000..0645d1bfa --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/lib/observation/inventory.js @@ -0,0 +1,39 @@ +const { Observation } = require("./base"); + +class Inventory extends Observation { + constructor(bot) { + super(bot); + this.name = "inventory"; + } + + observe() { + return listItems(this.bot); + } +} + +function listItems(bot) { + const items = getInventoryItems(bot); + return items.reduce(itemToDict, {}); +} + +function getInventoryItems(bot) { + const inventory = bot.currentWindow || bot.inventory; + return inventory.items(); +} + +function itemToDict(acc, cur) { + if (cur.name && cur.count) { + //if both name and count property are defined + if (acc[cur.name]) { + //if the item is already in the dict + acc[cur.name] += cur.count; + } else { + //if the item is not in the dict + acc[cur.name] = cur.count; + } + } + return acc; +} + +//export modules +module.exports = Inventory; diff --git a/metagpt/environment/mincraft_env/mineflayer/lib/observation/onChat.js b/metagpt/environment/mincraft_env/mineflayer/lib/observation/onChat.js new file mode 100644 index 000000000..54b411e2a --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/lib/observation/onChat.js @@ -0,0 +1,26 @@ +const Observation = require("./base.js").Observation; + +class onChat extends Observation { + constructor(bot) { + super(bot); + this.name = "onChat"; + this.obs = ""; + bot.on("chatEvent", (username, message) => { + // Save entity status to local variable + if (message.startsWith("/")) { + return; + } + + this.obs += message; + this.bot.event(this.name); + }); + } + + observe() { + const result = this.obs; + this.obs = ""; + return result; + } +} + +module.exports = onChat; diff --git a/metagpt/environment/mincraft_env/mineflayer/lib/observation/onError.js b/metagpt/environment/mincraft_env/mineflayer/lib/observation/onError.js new file mode 100644 index 000000000..ac8fed9e5 --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/lib/observation/onError.js @@ -0,0 +1,22 @@ +const Observation = require("./base.js").Observation; + +class onError extends Observation { + constructor(bot) { + super(bot); + this.name = "onError"; + this.obs = null; + bot.on("error", (err) => { + // Save entity status to local variable + this.obs = err; + this.bot.event(this.name); + }); + } + + observe() { + const result = this.obs; + this.obs = null; + return result; + } +} + +module.exports = onError; diff --git a/metagpt/environment/mincraft_env/mineflayer/lib/observation/onSave.js b/metagpt/environment/mincraft_env/mineflayer/lib/observation/onSave.js new file mode 100644 index 000000000..e5983590f --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/lib/observation/onSave.js @@ -0,0 +1,22 @@ +const Observation = require("./base.js").Observation; + +class onSave extends Observation { + constructor(bot) { + super(bot); + this.name = "onSave"; + this.obs = null; + bot.on("save", (eventName) => { + // Save entity status to local variable + this.obs = eventName; + this.bot.event(this.name); + }); + } + + observe() { + const result = this.obs; + this.obs = null; + return result; + } +} + +module.exports = onSave; diff --git a/metagpt/environment/mincraft_env/mineflayer/lib/observation/status.js b/metagpt/environment/mincraft_env/mineflayer/lib/observation/status.js new file mode 100644 index 000000000..b031fbcf2 --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/lib/observation/status.js @@ -0,0 +1,103 @@ +const Observation = require("./base.js").Observation; + +class Status extends Observation { + constructor(bot) { + super(bot); + this.name = "status"; + } + + observe() { + return { + health: this.bot.health, + food: this.bot.food, + saturation: this.bot.foodSaturation, + oxygen: this.bot.oxygenLevel, + position: this.bot.entity.position, + velocity: this.bot.entity.velocity, + yaw: this.bot.entity.yaw, + pitch: this.bot.entity.pitch, + onGround: this.bot.entity.onGround, + equipment: this.getEquipment(), + name: this.bot.entity.username, + timeSinceOnGround: this.bot.entity.timeSinceOnGround, + isInWater: this.bot.entity.isInWater, + isInLava: this.bot.entity.isInLava, + isInWeb: this.bot.entity.isInWeb, + isCollidedHorizontally: this.bot.entity.isCollidedHorizontally, + isCollidedVertically: this.bot.entity.isCollidedVertically, + biome: this.bot.blockAt(this.bot.entity.position) + ? this.bot.blockAt(this.bot.entity.position).biome.name + : "None", + entities: this.getEntities(), + timeOfDay: this.getTime(), + inventoryUsed: this.bot.inventoryUsed(), + elapsedTime: this.bot.globalTickCounter, + }; + } + + itemToObs(item) { + if (!item) return null; + return item.name; + } + + getTime() { + const timeOfDay = this.bot.time.timeOfDay; + let time = ""; + if (timeOfDay < 1000) { + time = "sunrise"; + } else if (timeOfDay < 6000) { + time = "day"; + } else if (timeOfDay < 12000) { + time = "noon"; + } else if (timeOfDay < 13000) { + time = "sunset"; + } else if (timeOfDay < 18000) { + time = "night"; + } else if (timeOfDay < 22000) { + time = "midnight"; + } else { + time = "sunrise"; + } + return time; + } + + // For each item in equipment, if it exists, return the name of the item + // otherwise return null + getEquipment() { + const slots = this.bot.inventory.slots; + const mainHand = this.bot.heldItem; + return slots + .slice(5, 9) + .concat(mainHand, slots[45]) + .map(this.itemToObs); + } + + getEntities() { + const entities = this.bot.entities; + if (!entities) return {}; + // keep all monsters in one list, keep other mobs in another list + const mobs = {}; + for (const id in entities) { + const entity = entities[id]; + if (!entity.displayName) continue; + if (entity.name === "player" || entity.name === "item") continue; + if (entity.position.distanceTo(this.bot.entity.position) < 32) { + if (!mobs[entity.name]) { + mobs[entity.name] = entity.position.distanceTo( + this.bot.entity.position + ); + } else if ( + mobs[entity.name] > + entity.position.distanceTo(this.bot.entity.position) + ) { + mobs[entity.name] = entity.position.distanceTo( + this.bot.entity.position + ); + } + } + } + return mobs; + } +} + +module.exports = Status; diff --git a/metagpt/environment/mincraft_env/mineflayer/lib/observation/voxels.js b/metagpt/environment/mincraft_env/mineflayer/lib/observation/voxels.js new file mode 100644 index 000000000..ecb0c14b7 --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/lib/observation/voxels.js @@ -0,0 +1,67 @@ +// Blocks = require("./blocks") +const { Observation } = require("./base"); + +class Voxels extends Observation { + constructor(bot) { + super(bot); + this.name = "voxels"; + } + + observe() { + return Array.from(getSurroundingBlocks(this.bot, 8, 2, 8)); + } +} + +class BlockRecords extends Observation { + constructor(bot) { + super(bot); + this.name = "blockRecords"; + this.records = new Set(); + this.tick = 0; + bot.on("physicsTick", () => { + this.tick++; + if (this.tick >= 100) { + const items = getInventoryItems(this.bot); + getSurroundingBlocks(this.bot, 8, 2, 8).forEach((block) => { + if (!items.has(block)) this.records.add(block); + }); + this.tick = 0; + } + }); + } + + observe() { + return Array.from(this.records); + } + + reset() { + this.records = new Set(); + } +} + +function getSurroundingBlocks(bot, x_distance, y_distance, z_distance) { + const surroundingBlocks = new Set(); + + for (let x = -x_distance; x <= x_distance; x++) { + for (let y = -y_distance; y <= y_distance; y++) { + for (let z = -z_distance; z <= z_distance; z++) { + const block = bot.blockAt(bot.entity.position.offset(x, y, z)); + if (block && block.type !== 0) { + surroundingBlocks.add(block.name); + } + } + } + } + // console.log(surroundingBlocks); + return surroundingBlocks; +} + +function getInventoryItems(bot) { + const items = new Set(); + bot.inventory.items().forEach((item) => { + if (item) items.add(item.name); + }); + return items; +} + +module.exports = { Voxels, BlockRecords }; diff --git a/metagpt/environment/mincraft_env/mineflayer/lib/skillLoader.js b/metagpt/environment/mincraft_env/mineflayer/lib/skillLoader.js new file mode 100644 index 000000000..d78cf7820 --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/lib/skillLoader.js @@ -0,0 +1,79 @@ +function inject(bot) { + bot._sleep = bot.sleep; + bot.sleep = async (bedBlock) => { + await bot.waitForTicks(20); + await bot._sleep(bedBlock); + await bot.waitForTicks(135); + }; + + bot._fish = bot.fish; + bot.fish = async () => { + if (bot.heldItem?.name !== "fishing_rod") { + bot.chat("I'm not holding a fishing rod!"); + return; + } + let timeout = null; + await Promise.race([ + bot._fish(), + new Promise( + (resolve, reject) => + (timeout = setTimeout(() => { + bot.activateItem(); + reject( + new Error( + "Finishing timeout, make sure you get to and look at a water block!" + ) + ); + }, 60000)) + ), + ]); + clearTimeout(timeout); + await bot.waitForTicks(20); + }; + + bot._consume = bot.consume; + bot.consume = async () => { + // action_count.activateItem++; + await bot._consume(); + await bot.waitForTicks(20); + }; + + bot._useOn = bot.useOn; + bot.useOn = async (entity) => { + if (entity.position.distanceTo(bot.entity.position) > 6) { + bot.chat("Please goto a place near the entity first!"); + return; + } + await bot._useOn(entity); + await bot.waitForTicks(20); + }; + + bot._activateBlock = bot.activateBlock; + bot.activateBlock = async (block) => { + if (block.position.distanceTo(bot.entity.position) > 6) { + bot.chat("Please goto a place near the block first!"); + return; + } + // action_count.activateBlock++; + await bot._activateBlock(block); + }; + + bot._chat = bot.chat; + bot.chat = (message) => { + // action_count.chat++; + bot.emit("chatEvent", "bot", message); + bot._chat(message); + }; + + bot.inventoryUsed = () => { + return bot.inventory.slots.slice(9, 45).filter((item) => item !== null) + .length; + }; + + bot.save = function (eventName) { + bot.emit("save", eventName); + }; +} + +// export all control_primitives +module.exports = { inject }; diff --git a/metagpt/environment/mincraft_env/mineflayer/lib/utils.js b/metagpt/environment/mincraft_env/mineflayer/lib/utils.js new file mode 100644 index 000000000..68af30796 --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/lib/utils.js @@ -0,0 +1,31 @@ +let gameTimeCounter = 0; +let gameTimeList = []; +const initCounter = (bot) => { + gameTimeList = []; + for (let i = 0; i < 13000; i += 1000) { + gameTimeList.push(i); + } + for (let i = 13000; i < 24000; i += 2000) { + gameTimeList.push(i); + } + const timeOfDay = bot.time.timeOfDay; + for (let i = 0; i < gameTimeList.length; i++) { + if (gameTimeList[i] > timeOfDay) { + gameTimeCounter = i - 1; + break; + } + } +}; + +const getNextTime = () => { + gameTimeCounter++; + if (gameTimeCounter >= gameTimeList.length) { + gameTimeCounter = 0; + } + return gameTimeList[gameTimeCounter]; +}; + +module.exports = { + initCounter, + getNextTime, +}; diff --git a/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/.gitignore b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/.gitignore new file mode 100644 index 000000000..0578fdca3 --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/.gitignore @@ -0,0 +1,107 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# Next.js build output +.next + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and *not* Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +lib/ +package-lock.json diff --git a/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/LICENSE b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/LICENSE new file mode 100644 index 000000000..f2896b56e --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 TheDudeFromCI + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/README.md b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/README.md new file mode 100644 index 000000000..555acb761 --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/README.md @@ -0,0 +1,89 @@ +

mineflayer-collectblock

+

A small utility plugin for allowing users to collect blocks using a higher level API.

+ +

+ + + + + + +

+ +--- +## This is a modified version to better support Voyager + +## Showcase + +You can see a video of the plugin in action, [here.](https://youtu.be/5T_rcCnNnf4) +The source code of the bot in the video can be seen in the examples folder, [here.](https://github.com/TheDudeFromCI/mineflayer-collectblock/blob/master/examples/collector.js) + +### Description + +This plugin is a wrapper for mineflayer that allows for easier API usage when collecting blocks or item drops. This plugin is designed to reduce some of the boilerplate code based around the act of pathfinding to a block _(handled by_ ***mineflayer-pathfinder***_)_, selecting the best tool to mine that block _(handled by_ ***mineflayer-tool***_)_, actually mining it, then moving to collect the item drops from that block. This plugin allows for all of that basic concept to be wrapped up into a single API function. + +In addition to the usage above, some additional quality of life features are available in this plugin. These include the ability to automatically deposit items into a chest when the bot's inventory is full, collecting new tools from a chest if the bot doesn't currently have a required tool _(also handled by_ ***mineflayer-tool***_)_, and allowing for queueing of multiple blocks or item drops to the collection task, so they can be processed later. + +### Getting Started + +This plugin is built using Node and can be installed using: +```bash +npm install --save mineflayer-collectblock +``` + +### Simple Bot + +The brief description goes here. + +```js +// Create your bot +const mineflayer = require("mineflayer") +const bot = mineflayer.createBot({ + host: 'localhost', + username: 'Player', +}) +let mcData + +// Load collect block +bot.loadPlugin(require('mineflayer-collectblock').plugin) + +async function collectGrass() { + // Find a nearby grass block + const grass = bot.findBlock({ + matching: mcData.blocksByName.grass_block.id, + maxDistance: 64 + }) + + if (grass) { + // If we found one, collect it. + try { + await bot.collectBlock.collect(grass) + collectGrass() // Collect another grass block + } catch (err) { + console.log(err) // Handle errors, if any + } + } +} + +// On spawn, start collecting all nearby grass +bot.once('spawn', () => { + mcData = require('minecraft-data')(bot.version) + collectGrass() +}) +``` + +### Documentation + +[API](https://github.com/TheDudeFromCI/mineflayer-collectblock/blob/master/docs/api.md) + +[Examples](https://github.com/TheDudeFromCI/mineflayer-collectblock/tree/master/examples) + +### License + +This project uses the [MIT](https://github.com/TheDudeFromCI/mineflayer-collectblock/blob/master/LICENSE) license. + +### Contributions + +This project is accepting PRs and Issues. See something you think can be improved? Go for it! Any and all help is highly appreciated! + +For larger changes, it is recommended to discuss these changes in the issues tab before writing any code. It's also preferred to make many smaller PRs than one large one, where applicable. diff --git a/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/_config.yml b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/_config.yml new file mode 100644 index 000000000..c4192631f --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-cayman \ No newline at end of file diff --git a/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/docs/api.md b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/docs/api.md new file mode 100644 index 000000000..66d8a3ecc --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/docs/api.md @@ -0,0 +1,52 @@ +# API + +Welcome to the *mineflayer-collectblock* API documentation page. + +## Table of Contents + +- [1. Summary](#1-summary) +- [Properties](#properties) + - [`bot.collectblock.movements: Movements`](#botcollectblockmovements-movements) +- [Functions](#functions) + - [collect](#collect) + - [Options:](#options) + +## 1. Summary + +The collect block plugin is a utility plugin that can be used to help make collecting blocks and item drops very easy, using only a single API call. No need to worry about pathfinding to the block, selecting the right tool, or moving to pick up the item drop after mining. + +## Properties + +### `bot.collectblock.movements: Movements` + +The movements object used by the pathfinder plugin to define the movement configuration. This object is passed to the pathfinder plugin when any API from this plugin is called in order to control how pathfinding should work when collecting the given blocks or item. + +If set to null, the pathfinder plugin movements is not updated. + +Defaults to a new movements object instance. + +## Functions + +### collect + +Usage: `bot.collectblock.collect(target: Collectable | Collectable[], options?: CollectOptions, cb: (err?: Error) => void): void` + +Causes the bot to collect the given block, item drop, or list of those. If the target is a block, the bot will move to the block, mine it, and pick up the item drop. If the target is an item drop, the bot will move to the item drop and pick it up. If the target is a list of collectables, the bot will move from target to target in order of closest to furthest and collect each target in turn. + +#### Options: + + * `append: boolean` + + If true, the target(s) will be appended to the existing target list instead of starting a new task. Defaults to false. + + * `ignoreNoPath: boolean` + + If true, errors will not be thrown when a path to the target block cannot be found. The bot will attempt to choose the best available position it can find, instead. Errors are still thrown if the bot cannot interact with the block from it's final location. Defaults to false. + + * `chestLocations: Vec3[]` + + Gets the list of chest locations to use when storing items after the bot's inventory becomes full. If undefined, it defaults to the chest location list on the bot.collectBlock plugin. + + * `itemFilter: ItemFilter` + + When transferring items to a chest, this filter is used to determine what items are allowed to be moved, and what items aren't allowed to be moved. Defaults to the item filter specified on the bot.collectBlock plugin. \ No newline at end of file diff --git a/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/examples/collector.js b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/examples/collector.js new file mode 100644 index 000000000..b9bb8faf9 --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/examples/collector.js @@ -0,0 +1,70 @@ +/** + * This bot example show how to direct a bot to collect a specific block type + * or a group of nearby blocks of that type. + */ + +const mineflayer = require('mineflayer') +const collectBlock = require('mineflayer-collectblock').plugin + +if (process.argv.length < 4 || process.argv.length > 6) { + console.log('Usage : node collector.js [] []') + process.exit(1) +} + +const bot = mineflayer.createBot({ + host: process.argv[2], + port: process.argv[3], + username: process.argv[4] || 'collector', + password: process.argv[5] +}) + +bot.loadPlugin(collectBlock) + +let mcData +bot.once('spawn', () => { + mcData = require('minecraft-data')(bot.version) +}) + +bot.on('chat', async (username, message) => { + const args = message.split(' ') + if (args[0] !== 'collect') return + + let count = 1 + if (args.length === 3) count = parseInt(args[1]) + + let type = args[1] + if (args.length === 3) type = args[2] + + const blockType = mcData.blocksByName[type] + if (!blockType) { + return + } + + const blocks = bot.findBlocks({ + matching: blockType.id, + maxDistance: 64, + count: count + }) + + if (blocks.length === 0) { + bot.chat("I don't see that block nearby.") + return + } + + const targets = [] + for (let i = 0; i < Math.min(blocks.length, count); i++) { + targets.push(bot.blockAt(blocks[i])) + } + + bot.chat(`Found ${targets.length} ${type}(s)`) + + try { + await bot.collectBlock.collect(targets) + // All blocks have been collected. + bot.chat('Done') + } catch (err) { + // An error occurred, report it. + bot.chat(err.message) + console.log(err) + } +}) diff --git a/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/examples/oreMiner.js b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/examples/oreMiner.js new file mode 100644 index 000000000..6accac88f --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/examples/oreMiner.js @@ -0,0 +1,59 @@ +/** + * This bot example shows how to collect a vein of ores quickly after only finding a single block. + * This makes it easy to collect a vein of ores or mine a tree without looking for every block in the + * area. + */ + +const mineflayer = require('mineflayer') +const collectBlock = require('mineflayer-collectblock').plugin + +if (process.argv.length < 4 || process.argv.length > 6) { + console.log('Usage : node oreMiner.js [] []') + process.exit(1) +} + +const bot = mineflayer.createBot({ + host: process.argv[2], + port: process.argv[3], + username: process.argv[4] || 'oreMiner', + password: process.argv[5] +}) + +bot.loadPlugin(collectBlock) + +let mcData +bot.once('spawn', () => { + mcData = require('minecraft-data')(bot.version) +}) + +bot.on('chat', async (username, message) => { + const args = message.split(' ') + if (args[0] !== 'collect') return + + const blockType = mcData.blocksByName[args[1]] + if (!blockType) { + bot.chat(`I don't know any blocks named ${args[1]}.`) + return + } + + const block = bot.findBlock({ + matching: blockType.id, + maxDistance: 64 + }) + + if (!block) { + bot.chat("I don't see that block nearby.") + return + } + + const targets = bot.collectBlock.findFromVein(block) + try { + await bot.collectBlock.collect(targets) + // All blocks have been collected. + bot.chat('Done') + } catch (err) { + // An error occurred, report it. + bot.chat(err.message) + console.log(err) + } +}) diff --git a/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/examples/storageBot.js b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/examples/storageBot.js new file mode 100644 index 000000000..b6f9971f2 --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/examples/storageBot.js @@ -0,0 +1,107 @@ +/** + * This bot example shows how to use the chest filling mechanic of the plugin. + * Simply provide a given storage chest, and the bot will automatically try and + * store it's inventory in that chest when the bot's inventory becomes full. + */ + +if (process.argv.length < 4 || process.argv.length > 6) { + console.log('Usage : node storageBot.js [] []') + process.exit(1) +} + +// Load your libraries +const mineflayer = require('mineflayer') +const collectBlock = require('mineflayer-collectblock').plugin + +// Create your bot +const bot = mineflayer.createBot({ + host: process.argv[2], + port: parseInt(process.argv[3]), + username: process.argv[4] ? process.argv[4] : 'storageBot', + password: process.argv[5] +}) + +// Load the collect block plugin +bot.loadPlugin(collectBlock) + +// Load mcData on login +let mcData +bot.once('login', () => { + mcData = require('minecraft-data')(bot.version) +}) + +// On spawn, try to find any nearby chests and save those as storage locations. +// When the bot's inventory becomes too full, it will empty it's inventory into +// these chests before collecting more resources. If a chest gets full, it moves +// to the next one in order until it's inventory is empty or it runs out of chests. +bot.once('spawn', () => { + bot.collectBlock.chestLocations = bot.findBlocks({ + matching: mcData.blocksByName.chest.id, + maxDistance: 16, + count: 999999 // Get as many chests as we can + }) + + if (bot.collectBlock.chestLocations.length === 0) { + bot.chat("I don't see any chests nearby.") + } else { + for (const chestPos of bot.collectBlock.chestLocations) { + bot.chat(`I found a chest at ${chestPos}`) + } + } +}) + +// Wait for someone to say something +bot.on('chat', async (username, message) => { + // If the player says something start starts with "collect" + // Otherwise, do nothing + const args = message.split(' ') + if (args[0] !== 'collect') return + + // If the player specifies a number, collect that many. Otherwise, default to 1. + let count = 1 + if (args.length === 3) count = parseInt(args[1]) + + // If a number was given the item number is the 3rd arg, not the 2nd. + let type = args[1] + if (args.length === 3) type = args[2] + + // Get the id of that block type for this version of Minecraft. + const blockType = mcData.blocksByName[type] + if (!blockType) { + bot.chat(`I don't know any blocks named ${type}.`) + return + } + + // Find all nearby blocks of that type, up to the given count, within 64 blocks. + const blocks = bot.findBlocks({ + matching: blockType.id, + maxDistance: 64, + count: count + }) + + // Complain if we can't find any nearby blocks of that type. + if (blocks.length === 0) { + bot.chat("I don't see that block nearby.") + return + } + + // Convert the block position array into a block array to pass to collect block. + const targets = [] + for (let i = 0; i < Math.min(blocks.length, count); i++) { + targets.push(bot.blockAt(blocks[i])) + } + + // Announce what we found. + bot.chat(`Found ${targets.length} ${type}(s)`) + + // Tell the bot to collect all of the given blocks in the block list. + try { + await bot.collectBlock.collect(targets) + // All blocks have been collected. + bot.chat('Done') + } catch (err) { + // An error occurred, report it. + bot.chat(err.message) + console.log(err) + } +}) diff --git a/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/package.json b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/package.json new file mode 100644 index 000000000..0f59e7aa6 --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/package.json @@ -0,0 +1,44 @@ +{ + "name": "mineflayer-collectblock", + "version": "1.4.1", + "description": "A simple utility plugin for Mineflayer that add a higher level API for collecting blocks.", + "main": "lib/index.js", + "types": "lib/index.d.ts", + "scripts": { + "build": "ts-standard && tsc && require-self", + "clean": "rm -rf lib", + "test": "test" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/TheDudeFromCI/mineflayer-collectblock.git" + }, + "keywords": [ + "mineflayer", + "plugin", + "api", + "utility", + "helper", + "collect" + ], + "author": "TheDudeFromCI", + "license": "MIT", + "bugs": { + "url": "https://github.com/TheDudeFromCI/mineflayer-collectblock/issues" + }, + "homepage": "https://github.com/TheDudeFromCI/mineflayer-collectblock#readme", + "dependencies": { + "mineflayer": "^4.0.0", + "mineflayer-pathfinder": "^2.1.1", + "mineflayer-tool": "^1.1.0" + }, + "devDependencies": { + "@types/node": "^18.6.4", + "require-self": "^0.2.3", + "ts-standard": "^11.0.0", + "typescript": "^4.1.3" + }, + "files": [ + "lib/**/*" + ] +} diff --git a/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/src/BlockVeins.ts b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/src/BlockVeins.ts new file mode 100644 index 000000000..ae5542ce3 --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/src/BlockVeins.ts @@ -0,0 +1,35 @@ +import { Bot } from 'mineflayer' +import { Block } from 'prismarine-block' + +export function findFromVein (bot: Bot, block: Block, maxBlocks: number, maxDistance: number, floodRadius: number): Block[] { + const targets: Block[] = [] + const open: Block[] = [block] + const type = block.type + const center = block.position + + for (let i = 0; i < maxBlocks; i++) { + const next = open.pop() + if (next == null) break + + targets.push(next) + + for (let x = -floodRadius; x <= floodRadius; x++) { + for (let y = -floodRadius; y <= floodRadius; y++) { + for (let z = -floodRadius; z <= floodRadius; z++) { + const neighborPos = next.position.offset(x, y, z) + if (neighborPos.manhattanDistanceTo(center) > maxDistance) continue + + const neighbor = bot.blockAt(neighborPos) + if (neighbor == null || neighbor.type !== type) continue + + if (targets.includes(neighbor)) continue + if (open.includes(neighbor)) continue + + open.push(neighbor) + } + } + } + } + + return targets +} diff --git a/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/src/CollectBlock.ts b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/src/CollectBlock.ts new file mode 100644 index 000000000..d2be87822 --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/src/CollectBlock.ts @@ -0,0 +1,451 @@ +import { Bot } from "mineflayer"; +import { Block } from "prismarine-block"; +import { Movements, goals } from "mineflayer-pathfinder"; +import { TemporarySubscriber } from "./TemporarySubscriber"; +import { Entity } from "prismarine-entity"; +import { error } from "./Util"; +import { Vec3 } from "vec3"; +import { emptyInventoryIfFull, ItemFilter } from "./Inventory"; +import { findFromVein } from "./BlockVeins"; +import { Collectable, Targets } from "./Targets"; +import { Item } from "prismarine-item"; +import mcDataLoader from "minecraft-data"; +import { once } from "events"; +import { callbackify } from "util"; + +export type Callback = (err?: Error) => void; + +async function collectAll( + bot: Bot, + options: CollectOptionsFull +): Promise { + let success_count = 0; + while (!options.targets.empty) { + await emptyInventoryIfFull( + bot, + options.chestLocations, + options.itemFilter + ); + const closest = options.targets.getClosest(); + if (closest == null) break; + switch (closest.constructor.name) { + case "Block": { + try { + if (success_count >= options.count) { + break; + } + await bot.tool.equipForBlock( + closest as Block, + equipToolOptions + ); + const goal = new goals.GoalLookAtBlock( + closest.position, + bot.world + ); + await bot.pathfinder.goto(goal); + await mineBlock(bot, closest as Block, options); + success_count++; + // TODO: options.ignoreNoPath + } catch (err) { + // @ts-ignore + // console.log(err.stack) + // bot.pathfinder.stop() + // bot.waitForTicks(10) + try { + bot.pathfinder.setGoal(null); + } catch (err) {} + if (options.ignoreNoPath) { + // @ts-ignore + if (err.name === "Invalid block") { + console.log( + `Block ${closest.name} at ${closest.position} is not valid! Skip it!` + ); + } // @ts-ignore + else if (err.name === "Unsafe block") { + console.log( + `${closest.name} at ${closest.position} is not safe to break! Skip it!` + ); + // @ts-ignore + } else if (err.name === "NoItem") { + const properties = + bot.registry.blocksByName[closest.name]; + const leastTool = Object.keys( + properties.harvestTools + )[0]; + const item = bot.registry.items[leastTool]; + bot.chat( + `I need at least a ${item.name} to mine ${closest.name}! Skip it!` + ); + return; + } else if ( + // @ts-ignore + err.name === "NoPath" || + // @ts-ignore + err.name === "Timeout" + ) { + if ( + bot.entity.position.distanceTo( + closest.position + ) < 0.5 + ) { + await mineBlock(bot, closest as Block, options); + break; + } + console.log( + `No path to ${closest.name} at ${closest.position}! Skip it!` + ); + // @ts-ignore + } else if (err.message === "Digging aborted") { + console.log(`Digging aborted! Skip it!`); + } else { + // @ts-ignore + bot.chat(`Error: ${err.message}`); + } + break; + } + throw err; + } + break; + } + case "Entity": { + // Don't collect any entities that are marked as 'invalid' + if (!(closest as Entity).isValid) break; + try { + const tempEvents = new TemporarySubscriber(bot); + const waitForPickup = new Promise( + (resolve, reject) => { + const timeout = setTimeout(() => { + // After 10 seconds, reject the promise + clearTimeout(timeout); + tempEvents.cleanup(); + reject(new Error("Failed to pickup item")); + }, 10000); + tempEvents.subscribeTo( + "entityGone", + (entity: Entity) => { + if (entity === closest) { + clearTimeout(timeout); + tempEvents.cleanup(); + resolve(); + } + } + ); + } + ); + bot.pathfinder.setGoal( + new goals.GoalFollow(closest as Entity, 0) + ); + // await bot.pathfinder.goto(new goals.GoalBlock(closest.position.x, closest.position.y, closest.position.z)) + await waitForPickup; + } catch (err) { + // @ts-ignore + console.log(err.stack); + try { + bot.pathfinder.setGoal(null); + } catch (err) {} + if (options.ignoreNoPath) { + // @ts-ignore + if (err.message === "Failed to pickup item") { + bot.chat(`Failed to pickup item! Skip it!`); + } + break; + } + throw err; + } + break; + } + default: { + throw error( + "UnknownType", + `Target ${closest.constructor.name} is not a Block or Entity!` + ); + } + } + options.targets.removeTarget(closest); + } + bot.chat(`Collect finish!`); +} + +const equipToolOptions = { + requireHarvest: true, + getFromChest: false, + maxTools: 2, +}; + +async function mineBlock( + bot: Bot, + block: Block, + options: CollectOptionsFull +): Promise { + if ( + bot.blockAt(block.position)?.type !== block.type || + bot.blockAt(block.position)?.type === 0 + ) { + options.targets.removeTarget(block); + throw error("Invalid block", "Block is not valid!"); + // @ts-expect-error + } else if (!bot.pathfinder.movements.safeToBreak(block)) { + options.targets.removeTarget(block); + throw error("Unsafe block", "Block is not safe to break!"); + } + + await bot.tool.equipForBlock(block, equipToolOptions); + + if (!block.canHarvest(bot.heldItem ? bot.heldItem.type : bot.heldItem)) { + options.targets.removeTarget(block); + throw error("NoItem", "Bot does not have a harvestable tool!"); + } + + const tempEvents = new TemporarySubscriber(bot); + tempEvents.subscribeTo("itemDrop", (entity: Entity) => { + if ( + entity.position.distanceTo(block.position.offset(0.5, 0.5, 0.5)) <= + 0.5 + ) { + options.targets.appendTarget(entity); + } + }); + try { + await bot.dig(block); + // Waiting for items to drop + await new Promise((resolve) => { + let remainingTicks = 10; + tempEvents.subscribeTo("physicTick", () => { + remainingTicks--; + if (remainingTicks <= 0) { + tempEvents.cleanup(); + resolve(); + } + }); + }); + } finally { + tempEvents.cleanup(); + } +} + +/** + * A set of options to apply when collecting the given targets. + */ +export interface CollectOptions { + /** + * If true, the target(s) will be appended to the existing target list instead of + * starting a new task. Defaults to false. + */ + append?: boolean; + + /** + * If true, errors will not be thrown when a path to the target block cannot + * be found. The bot will attempt to choose the best available position it + * can find, instead. Errors are still thrown if the bot cannot interact with + * the block from it's final location. Defaults to false. + */ + ignoreNoPath?: boolean; + + /** + * Gets the list of chest locations to use when storing items after the bot's + * inventory becomes full. If undefined, it defaults to the chest location + * list on the bot.collectBlock plugin. + */ + chestLocations?: Vec3[]; + + /** + * When transferring items to a chest, this filter is used to determine what + * items are allowed to be moved, and what items aren't allowed to be moved. + * Defaults to the item filter specified on the bot.collectBlock plugin. + */ + itemFilter?: ItemFilter; + + /** + * The total number of items to collect + */ + count?: number; +} + +/** + * A version of collect options where all values are assigned. + */ +interface CollectOptionsFull { + append: boolean; + ignoreNoPath: boolean; + chestLocations: Vec3[]; + itemFilter: ItemFilter; + targets: Targets; + count: number; +} + +/** + * The collect block plugin. + */ +export class CollectBlock { + /** + * The bot. + */ + private readonly bot: Bot; + + /** + * The list of active targets being collected. + */ + private readonly targets: Targets; + + /** + * The movements configuration to be sent to the pathfinder plugin. + */ + movements?: Movements; + + /** + * A list of chest locations which the bot is allowed to empty their inventory into + * if it becomes full while the bot is collecting resources. + */ + chestLocations: Vec3[] = []; + + /** + * When collecting items, this filter is used to determine what items should be placed + * into a chest if the bot's inventory becomes full. By default, returns true for all + * items except for tools, weapons, and armor. + * + * @param item - The item stack in the bot's inventory to check. + * + * @returns True if the item should be moved into the chest. False otherwise. + */ + itemFilter: ItemFilter = (item: Item) => { + if (item.name.includes("helmet")) return false; + if (item.name.includes("chestplate")) return false; + if (item.name.includes("leggings")) return false; + if (item.name.includes("boots")) return false; + if (item.name.includes("shield")) return false; + if (item.name.includes("sword")) return false; + if (item.name.includes("pickaxe")) return false; + if (item.name.includes("axe")) return false; + if (item.name.includes("shovel")) return false; + if (item.name.includes("hoe")) return false; + return true; + }; + + /** + * Creates a new instance of the create block plugin. + * + * @param bot - The bot this plugin is acting on. + */ + constructor(bot: Bot) { + this.bot = bot; + this.targets = new Targets(bot); + // @ts-ignore + this.movements = new Movements(bot, mcDataLoader(bot.version)); + } + + /** + * If target is a block: + * Causes the bot to break and collect the target block. + * + * If target is an item drop: + * Causes the bot to collect the item drop. + * + * If target is an array containing items or blocks, preforms the correct action for + * all targets in that array sorting dynamically by distance. + * + * @param target - The block(s) or item(s) to collect. + * @param options - The set of options to use when handling these targets + * @param cb - The callback that is called finished. + */ + async collect( + target: Collectable | Collectable[], + options: CollectOptions | Callback = {}, + cb?: Callback + ): Promise { + if (typeof options === "function") { + cb = options; + options = {}; + } + // @ts-expect-error + if (cb != null) return callbackify(this.collect)(target, options, cb); + + const optionsFull: CollectOptionsFull = { + append: options.append ?? false, + ignoreNoPath: options.ignoreNoPath ?? false, + chestLocations: options.chestLocations ?? this.chestLocations, + itemFilter: options.itemFilter ?? this.itemFilter, + targets: this.targets, + count: options.count ?? Infinity, + }; + + if (this.bot.pathfinder == null) { + throw error( + "UnresolvedDependency", + "The mineflayer-collectblock plugin relies on the mineflayer-pathfinder plugin to run!" + ); + } + + if (this.bot.tool == null) { + throw error( + "UnresolvedDependency", + "The mineflayer-collectblock plugin relies on the mineflayer-tool plugin to run!" + ); + } + + if (this.movements != null) { + this.bot.pathfinder.setMovements(this.movements); + } + + if (!optionsFull.append) await this.cancelTask(); + if (Array.isArray(target)) { + this.targets.appendTargets(target); + } else { + this.targets.appendTarget(target); + } + + try { + await collectAll(this.bot, optionsFull); + this.targets.clear(); + } catch (err) { + this.targets.clear(); + // Ignore path stopped error for cancelTask to work properly (imo we shouldn't throw any pathing errors) + // @ts-expect-error + if (err.name !== "PathStopped") throw err; + } finally { + // @ts-expect-error + this.bot.emit("collectBlock_finished"); + } + } + + /** + * Loads all touching blocks of the same type to the given block and returns them as an array. + * This effectively acts as a flood fill algorithm to retrieve blocks in the same ore vein and similar. + * + * @param block - The starting block. + * @param maxBlocks - The maximum number of blocks to look for before stopping. + * @param maxDistance - The max distance from the starting block to look. + * @param floodRadius - The max distance distance from block A to block B to be considered "touching" + */ + findFromVein( + block: Block, + maxBlocks = 100, + maxDistance = 16, + floodRadius = 1 + ): Block[] { + return findFromVein( + this.bot, + block, + maxBlocks, + maxDistance, + floodRadius + ); + } + + /** + * Cancels the current collection task, if still active. + * + * @param cb - The callback to use when the task is stopped. + */ + async cancelTask(cb?: Callback): Promise { + if (this.targets.empty) { + if (cb != null) cb(); + return await Promise.resolve(); + } + this.bot.pathfinder.stop(); + if (cb != null) { + // @ts-expect-error + this.bot.once("collectBlock_finished", cb); + } + await once(this.bot, "collectBlock_finished"); + } +} diff --git a/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/src/Inventory.ts b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/src/Inventory.ts new file mode 100644 index 000000000..6a17d0cc5 --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/src/Inventory.ts @@ -0,0 +1,87 @@ +import { Bot } from 'mineflayer' +import { Callback } from './CollectBlock' +import { Vec3 } from 'vec3' +import { error } from './Util' +import { Item } from 'prismarine-item' +import { goals } from 'mineflayer-pathfinder' +import { callbackify } from 'util' + +export type ItemFilter = (item: Item) => boolean + +function getClosestChest (bot: Bot, chestLocations: Vec3[]): Vec3 | null { + let chest = null + let distance = 0 + + for (const c of chestLocations) { + const dist = c.distanceTo(bot.entity.position) + if (chest == null || dist < distance) { + chest = c + distance = dist + } + } + + if (chest != null) { + chestLocations.splice(chestLocations.indexOf(chest), 1) + } + + return chest +} + +export async function emptyInventoryIfFull (bot: Bot, chestLocations: Vec3[], itemFilter: ItemFilter, cb?: Callback): Promise { + // @ts-expect-error + if (cb != null) return callbackify(emptyInventoryIfFull)(bot, chestLocations, cb) + if (bot.inventory.emptySlotCount() > 0) return + return await emptyInventory(bot, chestLocations, itemFilter) +} + +export async function emptyInventory (bot: Bot, chestLocations: Vec3[], itemFilter: ItemFilter, cb?: Callback): Promise { + // @ts-expect-error + if (cb != null) return callbackify(emptyInventory)(bot, chestLocations, cb) + if (chestLocations.length === 0) { + throw error('NoChests', 'There are no defined chest locations!') + } + + // Shallow clone so we can safely remove chests from the list that are full. + chestLocations = [...chestLocations] + + while (true) { + const chest = getClosestChest(bot, chestLocations) + if (chest == null) { + throw error('NoChests', 'All chests are full.') + } + const hasRemaining = await tryEmptyInventory(bot, chest, itemFilter) + if (!hasRemaining) return + } +} + +async function tryEmptyInventory (bot: Bot, chestLocation: Vec3, itemFilter: ItemFilter, cb?: (err: Error | undefined, hasRemaining: boolean) => void): Promise { + // @ts-expect-error + if (cb != null) return callbackify(tryEmptyInventory)(bot, chestLocation, itemFilter, cb) + await gotoChest(bot, chestLocation) + return await placeItems(bot, chestLocation, itemFilter) +} + +async function gotoChest (bot: Bot, location: Vec3, cb?: Callback): Promise { + // @ts-expect-error + if (cb != null) return callbackify(gotoChest)(bot, location) + await bot.pathfinder.goto(new goals.GoalGetToBlock(location.x, location.y, location.z)) +} + +async function placeItems (bot: Bot, chestPos: Vec3, itemFilter: ItemFilter, cb?: (err: Error | undefined, hasRemaining: boolean) => void): Promise { + // @ts-expect-error + if (cb != null) return callbackify(placeItems)(bot, chestPos, itemFilter, cb) + const chestBlock = bot.blockAt(chestPos) + if (chestBlock == null) { + throw error('UnloadedChunk', 'Chest is in an unloaded chunk!') + } + const chest = await bot.openChest(chestBlock) + for (const item of bot.inventory.items()) { + if (!itemFilter(item)) continue + if (chest.firstEmptyContainerSlot() === null) { + // We have items that didn't fit. + return true + } + await chest.deposit(item.type, item.metadata, item.count) + } + return false +} diff --git a/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/src/Targets.ts b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/src/Targets.ts new file mode 100644 index 000000000..568d07ad9 --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/src/Targets.ts @@ -0,0 +1,60 @@ +import { Bot } from 'mineflayer' +import { Block } from 'prismarine-block' +import { Entity } from 'prismarine-entity' + +export type Collectable = Block | Entity + +export class Targets { + private readonly bot: Bot + private targets: Collectable[] = [] + + constructor (bot: Bot) { + this.bot = bot + } + + appendTargets (targets: Collectable[]): void { + for (const target of targets) { + this.appendTarget(target) + } + } + + appendTarget (target: Collectable): void { + if (this.targets.includes(target)) return + this.targets.push(target) + } + + /** + * Gets the closest target to the bot in this list. + * + * @returns The closest target, or null if there are no targets. + */ + getClosest (): Collectable | null { + let closest: Collectable | null = null + let distance: number = 0 + + for (const target of this.targets) { + const dist = target.position.distanceTo(this.bot.entity.position) + + if (closest == null || dist < distance) { + closest = target + distance = dist + } + } + + return closest + } + + get empty (): boolean { + return this.targets.length === 0 + } + + clear (): void { + this.targets.length = 0 + } + + removeTarget (target: Collectable): void { + const index = this.targets.indexOf(target) + if (index < 0) return + this.targets.splice(index, 1) + } +} diff --git a/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/src/TaskQueue.ts b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/src/TaskQueue.ts new file mode 100644 index 000000000..81fe3bc5a --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/src/TaskQueue.ts @@ -0,0 +1,77 @@ +import type { Callback } from './index' +export type Task = (cb: Callback) => void +export type SyncTask = () => void + +/** + * A simple utility class for queuing up a series of async tasks to execute. + */ +export class TaskQueue { + private tasks: Task[] = [] + + /** + * If true, the task list will stop executing if one of the tasks throws an error. + */ + readonly stopOnError: boolean = true + + /** + * Adds a new async task to this queue. The provided callback should be executed when + * the async task is complete. + * + * @param task - The async task to add. + */ + add (task: Task): void { + this.tasks.push(task) + } + + /** + * Adds a synchronous task toi this queue. + * + * @param task - The sync task to add. + */ + addSync (task: SyncTask): void { + this.add((cb) => { + try { + task() + cb() + } catch (err: any) { + cb(err) + } + }) + } + + /** + * Runs all tasks currently in this queue and empties the queue. + * + * @param cb - The optional callback to be executed when all tasks in this queue have + * finished executing. + */ + runAll (cb?: Callback): void { + const taskList = this.tasks + this.tasks = [] + + let index = -1 + const runNext: () => void = () => { + index++ + if (index >= taskList.length) { + if (cb !== undefined) cb() + return + } + + try { + taskList[index]((err) => { + if (err !== undefined) { + if (cb !== undefined) cb(err) + + if (this.stopOnError) return + } + + runNext() + }) + } catch (err: any) { + if (cb !== undefined) cb(err) + } + } + + runNext() + } +} diff --git a/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/src/TemporarySubscriber.ts b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/src/TemporarySubscriber.ts new file mode 100644 index 000000000..3f14a607d --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/src/TemporarySubscriber.ts @@ -0,0 +1,34 @@ +import { Bot } from 'mineflayer' + +class Subscription { + constructor (readonly eventName: string, readonly callback: Function) {} +} + +export class TemporarySubscriber { + private readonly subscriptions: Subscription[] = [] + + constructor (readonly bot: Bot) {} + + /** + * Adds a new temporary event listener to the bot. + * + * @param event - The event to subscribe to. + * @param callback - The function to execute. + */ + subscribeTo (event: string, callback: Function): void { + this.subscriptions.push(new Subscription(event, callback)) + + // @ts-expect-error + this.bot.on(event, callback) + } + + /** + * Removes all attached event listeners from the bot. + */ + cleanup (): void { + for (const sub of this.subscriptions) { + // @ts-expect-error + this.bot.removeListener(sub.eventName, sub.callback) + } + } +} diff --git a/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/src/Util.ts b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/src/Util.ts new file mode 100644 index 000000000..ee0f29e0c --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/src/Util.ts @@ -0,0 +1,13 @@ +/** + * Creates a new error object with the given type and message. + * + * @param type - The error type. + * @param message - The error message. + * + * @returns The error object. + */ +export function error (type: string, message: string): Error { + const e = new Error(message) + e.name = type + return e +} diff --git a/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/src/index.ts b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/src/index.ts new file mode 100644 index 000000000..45c9a8508 --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/src/index.ts @@ -0,0 +1,25 @@ +import { Bot } from 'mineflayer' +import { CollectBlock } from './CollectBlock' +import { pathfinder as pathfinderPlugin } from 'mineflayer-pathfinder' +import { plugin as toolPlugin } from 'mineflayer-tool' + +export function plugin (bot: Bot): void { + // @ts-expect-error + bot.collectBlock = new CollectBlock(bot) + + // Load plugins if not loaded manually. + setTimeout(() => loadPathfinderPlugin(bot), 0) + setTimeout(() => loadToolPlugin(bot), 0) +} + +function loadPathfinderPlugin (bot: Bot): void { + if (bot.pathfinder != null) return + bot.loadPlugin(pathfinderPlugin) +} + +function loadToolPlugin (bot: Bot): void { + if (bot.tool != null) return + bot.loadPlugin(toolPlugin) +} + +export { CollectBlock, Callback, CollectOptions } from './CollectBlock' diff --git a/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/tsconfig.json b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/tsconfig.json new file mode 100644 index 000000000..a6076bc0c --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/mineflayer-collectblock/tsconfig.json @@ -0,0 +1,69 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "ES2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ + "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + // "lib": [], /* Specify library files to be included in the compilation. */ + "allowJs": true, /* Allow javascript files to be compiled. */ + "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + "declaration": true, + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "./lib", + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + /* Additional Checks */ + "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + /* Advanced Options */ + "skipLibCheck": true, /* Skip type checking of declaration files. */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + }, + "include": [ + "src" + ], + "exclude": [ + "node_modules", + "**/__tests__/*" + ] +} \ No newline at end of file diff --git a/metagpt/environment/mincraft_env/mineflayer/package.json b/metagpt/environment/mincraft_env/mineflayer/package.json new file mode 100644 index 000000000..9e389d268 --- /dev/null +++ b/metagpt/environment/mincraft_env/mineflayer/package.json @@ -0,0 +1,38 @@ +{ + "name": "voyager", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "body-parser": "^1.20.2", + "express": "^4.18.2", + "magic-string": "^0.30.0", + "minecraft-data": "^3.31.0", + "minecrafthawkeye": "^1.3.6", + "mineflayer": "^4.8.1", + "mineflayer-collectblock": "file:mineflayer-collectblock", + "mineflayer-pathfinder": "^2.4.2", + "mineflayer-pvp": "^1.3.2", + "mineflayer-tool": "^1.2.0", + "mocha": "^10.2.0", + "prismarine-biome": "^1.3.0", + "prismarine-block": "=1.16.3", + "prismarine-entity": "^2.2.0", + "prismarine-item": "^1.12.1", + "prismarine-nbt": "^2.2.1", + "prismarine-recipe": "^1.3.1", + "prismarine-viewer": "^1.24.0", + "typescript": "^4.9.5", + "vec3": "^0.1.8", + "graceful-fs": "^4.2.11" + }, + "devDependencies": { + "prettier": "2.8.5" + } +} diff --git a/metagpt/environment/mincraft_env/process_monitor.py b/metagpt/environment/mincraft_env/process_monitor.py new file mode 100644 index 000000000..b62aa6005 --- /dev/null +++ b/metagpt/environment/mincraft_env/process_monitor.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# refs to `voyager process_monitor.py` + +import re +import subprocess +import threading +import warnings +from typing import List + +import psutil + +from metagpt.logs import define_log_level + + +class SubprocessMonitor: + def __init__( + self, + commands: List[str], + name: str, + ready_match: str = r".*", + callback_match: str = r"^(?!x)x$", # regex that will never match + callback: callable = None, + finished_callback: callable = None, + ): + self.commands = commands + self.name = name + self.logger = define_log_level(name=name) + self.process = None + self.ready_match = ready_match + self.ready_event = None + self.ready_line = None + self.callback_match = callback_match + self.callback = callback + self.finished_callback = finished_callback + self.thread = None + + def _start(self): + self.logger.info(f"Starting subprocess with commands: {self.commands}") + + self.process = psutil.Popen( + self.commands, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True, + ) + self.logger.info(f"Subprocess {self.name} started with PID {self.process.pid}.") + for line in iter(self.process.stdout.readline, ""): + self.logger.info(line.strip()) + if re.search(self.ready_match, line): + self.ready_line = line + self.logger.info("Subprocess is ready.") + self.ready_event.set() + if re.search(self.callback_match, line): + self.callback() + if not self.ready_event.is_set(): + self.ready_event.set() + warnings.warn(f"Subprocess {self.name} failed to start.") + if self.finished_callback: + self.finished_callback() + + def run(self): + self.ready_event = threading.Event() + self.ready_line = None + self.thread = threading.Thread(target=self._start) + self.thread.start() + self.ready_event.wait() + + def stop(self): + self.logger.info("Stopping subprocess.") + if self.process and self.process.is_running(): + self.process.terminate() + self.process.wait() + + @property + def is_running(self): + if self.process is None: + return False + return self.process.is_running() diff --git a/metagpt/environment/software_env/__init__.py b/metagpt/environment/software_env/__init__.py new file mode 100644 index 000000000..2bcf8efd0 --- /dev/null +++ b/metagpt/environment/software_env/__init__.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : diff --git a/metagpt/environment/software_env/software_env.py b/metagpt/environment/software_env/software_env.py new file mode 100644 index 000000000..94bc11659 --- /dev/null +++ b/metagpt/environment/software_env/software_env.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : MG Software Env + + +from metagpt.environment.base_env import Environment + + +class SoftwareEnv(Environment): + """a specific alias name""" + + pass diff --git a/metagpt/environment/stanford_town_env/__init__.py b/metagpt/environment/stanford_town_env/__init__.py new file mode 100644 index 000000000..2bcf8efd0 --- /dev/null +++ b/metagpt/environment/stanford_town_env/__init__.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : diff --git a/metagpt/environment/stanford_town_env/stanford_town_env.py b/metagpt/environment/stanford_town_env/stanford_town_env.py new file mode 100644 index 000000000..8721d6cd1 --- /dev/null +++ b/metagpt/environment/stanford_town_env/stanford_town_env.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : MG StanfordTown Env + +from metagpt.environment.base_env import Environment +from metagpt.environment.stanford_town_env.stanford_town_ext_env import ( + StanfordTownExtEnv, +) + + +class StanfordTownEnv(Environment, StanfordTownExtEnv): + pass diff --git a/metagpt/environment/stanford_town_env/stanford_town_ext_env.py b/metagpt/environment/stanford_town_env/stanford_town_ext_env.py new file mode 100644 index 000000000..8a9a65965 --- /dev/null +++ b/metagpt/environment/stanford_town_env/stanford_town_ext_env.py @@ -0,0 +1,379 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : The StanfordTown external environment to interate with the web interface +# refs to `generative_agents maze.py` + +import math +from pathlib import Path +from typing import Optional, Tuple + +from pydantic import ConfigDict, Field, model_validator + +from metagpt.environment.base_env import ExtEnv, mark_as_readable, mark_as_writeable +from metagpt.utils.common import read_csv_to_list, read_json_file + + +class StanfordTownExtEnv(ExtEnv): + model_config = ConfigDict(arbitrary_types_allowed=True) + + maze_asset_path: Optional[Path] = Field(default=None, description="the path to store maze assets") + maze_width: int = Field(default=140, description="maze map width") + maze_height: int = Field(default=100, description="maze map height") + sq_tile_size: int = Field(default=32, description="the pixel height/width of a tile") + special_constraint: str = Field( + default="", description="a string description of any relevant special constraints " "the world might have" + ) + tiles: list[list[dict]] = Field(default=[]) + address_tiles: dict[str, set] = Field(default=dict()) + collision_maze: list[list] = Field(default=[]) + + @model_validator(mode="before") + @classmethod + def _init_maze(cls, values): + maze_asset_path = values["maze_asset_path"] + assert maze_asset_path + maze_asset_path = Path(maze_asset_path) + + maze_matrix_path = maze_asset_path.joinpath("matrix") + meta_info = read_json_file(maze_matrix_path.joinpath("maze_meta_info.json")) + + maze_width = int(meta_info["maze_width"]) + maze_height = int(meta_info["maze_height"]) + values["maze_width"] = maze_width + values["maze_height"] = maze_height + values["sq_tile_size"] = int(meta_info["sq_tile_size"]) + values["special_constraint"] = meta_info["special_constraint"] + + # READING IN SPECIAL BLOCKS + # Special blocks are those that are colored in the Tiled map. + # Here is an example row for the arena block file: + # e.g, "25331, Double Studio, Studio, Bedroom 2, Painting" + + blocks_folder = maze_matrix_path.joinpath("special_blocks") + + _wb = blocks_folder.joinpath("world_blocks.csv") + wb_rows = read_csv_to_list(_wb, header=False) + wb = wb_rows[0][-1] + + _sb = blocks_folder.joinpath("sector_blocks.csv") + sb_rows = read_csv_to_list(_sb, header=False) + sb_dict = dict() + for i in sb_rows: + sb_dict[i[0]] = i[-1] + + _ab = blocks_folder.joinpath("arena_blocks.csv") + ab_rows = read_csv_to_list(_ab, header=False) + ab_dict = dict() + for i in ab_rows: + ab_dict[i[0]] = i[-1] + + _gob = blocks_folder.joinpath("game_object_blocks.csv") + gob_rows = read_csv_to_list(_gob, header=False) + gob_dict = dict() + for i in gob_rows: + gob_dict[i[0]] = i[-1] + + _slb = blocks_folder.joinpath("spawning_location_blocks.csv") + slb_rows = read_csv_to_list(_slb, header=False) + slb_dict = dict() + for i in slb_rows: + slb_dict[i[0]] = i[-1] + + # [SECTION 3] Reading in the matrices + # This is your typical two dimensional matrices. It's made up of 0s and + # the number that represents the color block from the blocks folder. + maze_folder = maze_matrix_path.joinpath("maze") + + _cm = maze_folder.joinpath("collision_maze.csv") + collision_maze_raw = read_csv_to_list(_cm, header=False)[0] + _sm = maze_folder.joinpath("sector_maze.csv") + sector_maze_raw = read_csv_to_list(_sm, header=False)[0] + _am = maze_folder.joinpath("arena_maze.csv") + arena_maze_raw = read_csv_to_list(_am, header=False)[0] + _gom = maze_folder.joinpath("game_object_maze.csv") + game_object_maze_raw = read_csv_to_list(_gom, header=False)[0] + _slm = maze_folder.joinpath("spawning_location_maze.csv") + spawning_location_maze_raw = read_csv_to_list(_slm, header=False)[0] + + # Loading the maze. The mazes are taken directly from the json exports of + # Tiled maps. They should be in csv format. + # Importantly, they are "not" in a 2-d matrix format -- they are single + # row matrices with the length of width x height of the maze. So we need + # to convert here. + # example format: [['0', '0', ... '25309', '0',...], ['0',...]...] + # 25309 is the collision bar number right now. + collision_maze = [] + sector_maze = [] + arena_maze = [] + game_object_maze = [] + spawning_location_maze = [] + for i in range(0, len(collision_maze_raw), maze_width): + tw = maze_width + collision_maze += [collision_maze_raw[i : i + tw]] + sector_maze += [sector_maze_raw[i : i + tw]] + arena_maze += [arena_maze_raw[i : i + tw]] + game_object_maze += [game_object_maze_raw[i : i + tw]] + spawning_location_maze += [spawning_location_maze_raw[i : i + tw]] + values["collision_maze"] = collision_maze + + tiles = [] + for i in range(maze_height): + row = [] + for j in range(maze_width): + tile_details = dict() + tile_details["world"] = wb + + tile_details["sector"] = "" + if sector_maze[i][j] in sb_dict: + tile_details["sector"] = sb_dict[sector_maze[i][j]] + + tile_details["arena"] = "" + if arena_maze[i][j] in ab_dict: + tile_details["arena"] = ab_dict[arena_maze[i][j]] + + tile_details["game_object"] = "" + if game_object_maze[i][j] in gob_dict: + tile_details["game_object"] = gob_dict[game_object_maze[i][j]] + + tile_details["spawning_location"] = "" + if spawning_location_maze[i][j] in slb_dict: + tile_details["spawning_location"] = slb_dict[spawning_location_maze[i][j]] + + tile_details["collision"] = False + if collision_maze[i][j] != "0": + tile_details["collision"] = True + + tile_details["events"] = set() + + row += [tile_details] + tiles += [row] + values["tiles"] = tiles + + # Each game object occupies an event in the tile. We are setting up the + # default event value here. + for i in range(maze_height): + for j in range(maze_width): + if tiles[i][j]["game_object"]: + object_name = ":".join( + [tiles[i][j]["world"], tiles[i][j]["sector"], tiles[i][j]["arena"], tiles[i][j]["game_object"]] + ) + go_event = (object_name, None, None, None) + tiles[i][j]["events"].add(go_event) + + # Reverse tile access. + # -- given a string address, we return a set of all + # tile coordinates belonging to that address (this is opposite of + # tiles that give you the string address given a coordinate). This is + # an optimization component for finding paths for the personas' movement. + # address_tiles['bedroom-2-a'] == {(58, 9)} + # address_tiles['double studio:recreation:pool table'] + # == {(29, 14), (31, 11), (30, 14), (32, 11), ...}, + address_tiles = dict() + for i in range(maze_height): + for j in range(maze_width): + addresses = [] + if tiles[i][j]["sector"]: + add = f'{tiles[i][j]["world"]}:' + add += f'{tiles[i][j]["sector"]}' + addresses += [add] + if tiles[i][j]["arena"]: + add = f'{tiles[i][j]["world"]}:' + add += f'{tiles[i][j]["sector"]}:' + add += f'{tiles[i][j]["arena"]}' + addresses += [add] + if tiles[i][j]["game_object"]: + add = f'{tiles[i][j]["world"]}:' + add += f'{tiles[i][j]["sector"]}:' + add += f'{tiles[i][j]["arena"]}:' + add += f'{tiles[i][j]["game_object"]}' + addresses += [add] + if tiles[i][j]["spawning_location"]: + add = f'{tiles[i][j]["spawning_location"]}' + addresses += [add] + + for add in addresses: + if add in address_tiles: + address_tiles[add].add((j, i)) + else: + address_tiles[add] = set([(j, i)]) + values["address_tiles"] = address_tiles + return values + + def turn_coordinate_to_tile(self, px_coordinate: tuple[int, int]) -> tuple[int, int]: + """ + Turns a pixel coordinate to a tile coordinate. + """ + x = math.ceil(px_coordinate[0] / self.sq_tile_size) + y = math.ceil(px_coordinate[1] / self.sq_tile_size) + return (x, y) + + @mark_as_readable + def get_collision_maze(self) -> list: + return self.collision_maze + + @mark_as_readable + def get_address_tiles(self) -> dict: + return self.address_tiles + + @mark_as_readable + def access_tile(self, tile: tuple[int, int]) -> dict: + """ + Returns the tiles details dictionary that is stored in self.tiles of the + designated x, y location. + + INPUT + tile: The tile coordinate of our interest in (x, y) form. + OUTPUT + The tile detail dictionary for the designated tile. + EXAMPLE OUTPUT + Given (58, 9), + self.tiles[9][58] = {'world': 'double studio', + 'sector': 'double studio', 'arena': 'bedroom 2', + 'game_object': 'bed', 'spawning_location': 'bedroom-2-a', + 'collision': False, + 'events': {('double studio:double studio:bedroom 2:bed', + None, None)}} + """ + x = tile[0] + y = tile[1] + return self.tiles[y][x] + + @mark_as_readable + def get_tile_path(self, tile: tuple[int, int], level: str) -> str: + """ + Get the tile string address given its coordinate. You designate the level + by giving it a string level description. + + INPUT: + tile: The tile coordinate of our interest in (x, y) form. + level: world, sector, arena, or game object + OUTPUT + The string address for the tile. + EXAMPLE OUTPUT + Given tile=(58, 9), and level=arena, + "double studio:double studio:bedroom 2" + """ + x = tile[0] + y = tile[1] + tile = self.tiles[y][x] + + path = f"{tile['world']}" + if level == "world": + return path + else: + path += f":{tile['sector']}" + + if level == "sector": + return path + else: + path += f":{tile['arena']}" + + if level == "arena": + return path + else: + path += f":{tile['game_object']}" + + return path + + @mark_as_readable + def get_nearby_tiles(self, tile: tuple[int, int], vision_r: int) -> list[tuple[int, int]]: + """ + Given the current tile and vision_r, return a list of tiles that are + within the radius. Note that this implementation looks at a square + boundary when determining what is within the radius. + i.e., for vision_r, returns x's. + x x x x x + x x x x x + x x P x x + x x x x x + x x x x x + + INPUT: + tile: The tile coordinate of our interest in (x, y) form. + vision_r: The radius of the persona's vision. + OUTPUT: + nearby_tiles: a list of tiles that are within the radius. + """ + left_end = 0 + if tile[0] - vision_r > left_end: + left_end = tile[0] - vision_r + + right_end = self.maze_width - 1 + if tile[0] + vision_r + 1 < right_end: + right_end = tile[0] + vision_r + 1 + + bottom_end = self.maze_height - 1 + if tile[1] + vision_r + 1 < bottom_end: + bottom_end = tile[1] + vision_r + 1 + + top_end = 0 + if tile[1] - vision_r > top_end: + top_end = tile[1] - vision_r + + nearby_tiles = [] + for i in range(left_end, right_end): + for j in range(top_end, bottom_end): + nearby_tiles += [(i, j)] + return nearby_tiles + + @mark_as_writeable + def add_tiles_event(self, pt_y: int, pt_x: int, event: Tuple[str, str, str, str]): + self.tiles[pt_y][pt_x]["events"].add(event) + + @mark_as_writeable + def add_event_from_tile(self, curr_event: tuple[str], tile: tuple[int, int]) -> None: + """ + Add an event triple to a tile. + + INPUT: + curr_event: Current event triple. + e.g., ('double studio:double studio:bedroom 2:bed', None, + None) + tile: The tile coordinate of our interest in (x, y) form. + OUPUT: + None + """ + self.tiles[tile[1]][tile[0]]["events"].add(curr_event) + + @mark_as_writeable + def remove_event_from_tile(self, curr_event: tuple[str], tile: tuple[int, int]) -> None: + """dswaq + Remove an event triple from a tile. + + INPUT: + curr_event: Current event triple. + e.g., ('double studio:double studio:bedroom 2:bed', None, + None) + tile: The tile coordinate of our interest in (x, y) form. + OUPUT: + None + """ + curr_tile_ev_cp = self.tiles[tile[1]][tile[0]]["events"].copy() + for event in curr_tile_ev_cp: + if event == curr_event: + self.tiles[tile[1]][tile[0]]["events"].remove(event) + + @mark_as_writeable + def turn_event_from_tile_idle(self, curr_event: tuple[str], tile: tuple[int, int]) -> None: + curr_tile_ev_cp = self.tiles[tile[1]][tile[0]]["events"].copy() + for event in curr_tile_ev_cp: + if event == curr_event: + self.tiles[tile[1]][tile[0]]["events"].remove(event) + new_event = (event[0], None, None, None) + self.tiles[tile[1]][tile[0]]["events"].add(new_event) + + @mark_as_writeable + def remove_subject_events_from_tile(self, subject: str, tile: tuple[int, int]) -> None: + """ + Remove an event triple that has the input subject from a tile. + + INPUT: + subject: "Isabella Rodriguez" + tile: The tile coordinate of our interest in (x, y) form. + OUPUT: + None + """ + curr_tile_ev_cp = self.tiles[tile[1]][tile[0]]["events"].copy() + for event in curr_tile_ev_cp: + if event[0] == subject: + self.tiles[tile[1]][tile[0]]["events"].remove(event) diff --git a/metagpt/environment/werewolf_env/__init__.py b/metagpt/environment/werewolf_env/__init__.py new file mode 100644 index 000000000..2bcf8efd0 --- /dev/null +++ b/metagpt/environment/werewolf_env/__init__.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : diff --git a/metagpt/environment/werewolf_env/werewolf_env.py b/metagpt/environment/werewolf_env/werewolf_env.py new file mode 100644 index 000000000..d174f322c --- /dev/null +++ b/metagpt/environment/werewolf_env/werewolf_env.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : MG Werewolf Env + +from pydantic import Field + +from metagpt.environment.base_env import Environment +from metagpt.environment.werewolf_env.werewolf_ext_env import WerewolfExtEnv +from metagpt.logs import logger +from metagpt.schema import Message + + +class WerewolfEnv(Environment, WerewolfExtEnv): + timestamp: int = Field(default=0) + + def publish_message(self, message: Message, add_timestamp: bool = True): + """Post information to the current environment""" + logger.debug(f"publish_message: {message.dump()}") + if add_timestamp: + # Because the content of the message may be repeated, for example, killing the same person in two nights + # Therefore, a unique timestamp prefix needs to be added so that the same message will not be automatically deduplicated when added to the memory. + message.content = f"{self.timestamp} | " + message.content + self.memory.add(message) + self.history += f"\n{message}" + + async def run(self, k=1): + """Process all Role runs by order""" + for _ in range(k): + for role in self.roles.values(): + await role.run() + self.timestamp += 1 diff --git a/metagpt/environment/werewolf_env/werewolf_ext_env.py b/metagpt/environment/werewolf_env/werewolf_ext_env.py new file mode 100644 index 000000000..7c4b4c475 --- /dev/null +++ b/metagpt/environment/werewolf_env/werewolf_ext_env.py @@ -0,0 +1,335 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : The werewolf game external environment to integrate with + +import random +from collections import Counter +from enum import Enum +from typing import Callable, Optional + +from pydantic import ConfigDict, Field + +from metagpt.environment.base_env import ExtEnv, mark_as_readable, mark_as_writeable +from metagpt.logs import logger + + +class RoleState(Enum): + ALIVE = "alive" # the role is alive + KILLED = "killed" # the role is killed by werewolf or voting + POISONED = "poisoned" # the role is killed by posion + SAVED = "saved" # the role is saved by antidote + + +# the ordered rules by the moderator to announce to everyone each step +STEP_INSTRUCTIONS = { + 0: { + "content": "It’s dark, everyone close your eyes. I will talk with you/your team secretly at night.", + "send_to": "Moderator", # for moderator to continuen speaking + "restricted_to": "", + }, + 1: { + "content": "Guard, please open your eyes!", + "send_to": "Moderator", # for moderator to continuen speaking + "restricted_to": "", + }, + 2: { + "content": """Guard, now tell me who you protect tonight? + You only choose one from the following living options please: {living_players}. + Or you can pass. For example: Protect ...""", + "send_to": "Guard", + "restricted_to": "Moderator,Guard", + }, + 3: {"content": "Guard, close your eyes", "send_to": "Moderator", "restricted_to": ""}, + 4: {"content": "Werewolves, please open your eyes!", "send_to": "Moderator", "restricted_to": ""}, + 5: { + "content": """Werewolves, I secretly tell you that {werewolf_players} are + all of the 2 werewolves! Keep in mind you are teammates. The rest players are not werewolves. + choose one from the following living options please: + {living_players}. For example: Kill ...""", + "send_to": "Werewolf", + "restricted_to": "Moderator,Werewolf", + }, + 6: {"content": "Werewolves, close your eyes", "send_to": "Moderator", "restricted_to": ""}, + 7: {"content": "Witch, please open your eyes!", "send_to": "Moderator", "restricted_to": ""}, + 8: { + "content": """Witch, tonight {player_hunted} has been killed by the werewolves. + You have a bottle of antidote, would you like to save him/her? If so, say "Save", else, say "Pass".""", + "send_to": "Witch", + "restricted_to": "Moderator,Witch", + }, # 要先判断女巫是否有解药,再去询问女巫是否使用解药救人 + 9: { + "content": """Witch, you also have a bottle of poison, would you like to use it to kill one of the living players? + Choose one from the following living options: {living_players}. + If so, say ONLY "Poison PlayerX", replace PlayerX with the actual player name, else, say "Pass".""", + "send_to": "Witch", + "restricted_to": "Moderator,Witch", + }, # + 10: {"content": "Witch, close your eyes", "send_to": "Moderator", "restricted_to": ""}, + 11: {"content": "Seer, please open your eyes!", "send_to": "Moderator", "restricted_to": ""}, + 12: { + "content": """Seer, you can check one player's identity. Who are you going to verify its identity tonight? + Choose only one from the following living options:{living_players}.""", + "send_to": "Seer", + "restricted_to": "Moderator,Seer", + }, + 13: {"content": "Seer, close your eyes", "send_to": "Moderator", "restricted_to": ""}, + # The 1-st daytime + 14: { + "content": """It's daytime. Everyone woke up except those who had been killed.""", + "send_to": "Moderator", + "restricted_to": "", + }, + 15: {"content": "{player_current_dead} was killed last night!", "send_to": "Moderator", "restricted_to": ""}, + 16: { + "content": """Living players: {living_players}, now freely talk about the current situation based on your observation and + reflection with a few sentences. Decide whether to reveal your identity based on your reflection.""", + "send_to": "", # send to all to speak in daytime + "restricted_to": "", + }, + 17: { + "content": """Now vote and tell me who you think is the werewolf. Don’t mention your role. + You only choose one from the following living options please: + {living_players}. Say ONLY: I vote to eliminate ...""", + "send_to": "", + "restricted_to": "", + }, + 18: {"content": """{player_current_dead} was eliminated.""", "send_to": "Moderator", "restricted_to": ""}, +} + + +class WerewolfExtEnv(ExtEnv): + model_config = ConfigDict(arbitrary_types_allowed=True) + + players_state: dict[str, tuple[str, RoleState]] = Field( + default=dict(), description="the player's role type and state by player_name" + ) + + round_idx: int = Field(default=0) # the current round + step_idx: int = Field(default=0) # the current step of current round + eval_step_idx: int = Field(default=0) + per_round_steps: int = Field(default=len(STEP_INSTRUCTIONS)) + + # game global states + game_setup: str = Field(default="", description="game setup including role and its num") + special_role_players: list[str] = Field(default=[]) + winner: Optional[str] = Field(default=None) + win_reason: Optional[str] = Field(default=None) + witch_poison_left: int = Field(default=1) + witch_antidote_left: int = Field(default=1) + + # game current round states, a round is from closing your eyes to the next time you close your eyes + round_hunts: dict[str, str] = Field(default=dict(), description="nighttime wolf hunt result") + round_votes: dict[str, str] = Field( + default=dict(), description="daytime all players vote result, key=voteer, value=voted one" + ) + player_hunted: Optional[str] = Field(default=None) + player_protected: Optional[str] = Field(default=None) + is_hunted_player_saved: bool = Field(default=False) + player_poisoned: Optional[str] = Field(default=None) + player_current_dead: list[str] = Field(default=[]) + + @property + def living_players(self) -> list[str]: + player_names = [] + for name, roletype_state in self.players_state.items(): + if roletype_state[1] in [RoleState.ALIVE, RoleState.SAVED]: + player_names.append(name) + return player_names + + def _role_type_players(self, role_type: str) -> list[str]: + """return player name of particular role type""" + player_names = [] + for name, roletype_state in self.players_state.items(): + if role_type in roletype_state[0]: + player_names.append(name) + return player_names + + @property + def werewolf_players(self) -> list[str]: + player_names = self._role_type_players(role_type="Werewolf") + return player_names + + @property + def villager_players(self) -> list[str]: + player_names = self._role_type_players(role_type="Villager") + return player_names + + def _init_players_state(self, players: list["Role"]): + for play in players: + self.players_state[play.name] = (play.profile, RoleState.ALIVE) + + self.special_role_players = [ + p for p in self.living_players if p not in self.werewolf_players + self.villager_players + ] + + def init_game_setup( + self, + role_uniq_objs: list[object], + num_villager: int = 2, + num_werewolf: int = 2, + shuffle=True, + add_human=False, + use_reflection=True, + use_experience=False, + use_memory_selection=False, + new_experience_version="", + prepare_human_player=Callable, + ) -> tuple[str, list]: + """init players using different roles' num""" + role_objs = [] + for role_obj in role_uniq_objs: + if str(role_obj) == "Villager": + role_objs.extend([role_obj] * num_villager) + elif str(role_obj) == "Werewolf": + role_objs.extend([role_obj] * num_werewolf) + else: + role_objs.append(role_obj) + if shuffle: + random.shuffle(len(role_objs)) + if add_human: + assigned_role_idx = random.randint(0, len(role_objs) - 1) + assigned_role = role_objs[assigned_role_idx] + role_objs[assigned_role_idx] = prepare_human_player(assigned_role) # TODO + + players = [ + role( + name=f"Player{i + 1}", + use_reflection=use_reflection, + use_experience=use_experience, + use_memory_selection=use_memory_selection, + new_experience_version=new_experience_version, + ) + for i, role in enumerate(role_objs) + ] + + if add_human: + logger.info(f"You are assigned {players[assigned_role_idx].name}({players[assigned_role_idx].profile})") + + game_setup = ["Game setup:"] + [f"{player.name}: {player.profile}," for player in players] + self.game_setup = "\n".join(game_setup) + + self._init_players_state(players) # init players state + + return self.game_setup, players + + def _update_players_state(self, player_names: list[str], state: RoleState = RoleState.KILLED): + for player_name in player_names: + if player_name in self.players_state: + roletype_state = self.players_state[player_name] + self.players_state[player_name] = (roletype_state[0], state) + + def _check_valid_role(self, player: "Role", role_type: str) -> bool: + return True if role_type in str(player) else False + + def _check_player_continue(self, player_name: str, particular_step: int = -1) -> bool: + step_idx = self.step_idx % self.per_round_steps + if particular_step > 0 and step_idx != particular_step: # step no + # particular_step = 18, not daytime vote time, ignore + # particular_step = 15, not nighttime hunt time, ignore + return False + if player_name not in self.living_players: + return False + return True + + @mark_as_readable + def curr_step_instruction(self) -> dict: + step_idx = self.step_idx % len(STEP_INSTRUCTIONS) + instruction = STEP_INSTRUCTIONS[step_idx] + self.step_idx += 1 + return instruction + + @mark_as_readable + def get_players_state(self, player_names: list[str]) -> dict[str, RoleState]: + players_state = { + player_name: self.players_state[player_name][1] # only return role state + for player_name in player_names + if player_name in self.players_state + } + return players_state + + @mark_as_writeable + def vote_kill_someone(self, voteer: "Role", player_name: str = None): + """player vote result at daytime + player_name: if it's None, regard as abstaining from voting + """ + if not self._check_player_continue(voteer.name, particular_step=18): # 18=step no + return + + self.round_votes[voteer.name] = player_name + # check if all living players finish voting, then get the dead one + if list(self.round_votes.keys()) == self.living_players: + voted_all = list(self.round_votes.values()) # TODO in case of tie vote, check who was voted first + voted_all = [item for item in voted_all if item] + self.player_current_dead = Counter(voted_all).most_common()[0][0] + self._update_players_state([self.player_current_dead]) + + @mark_as_writeable + def wolf_kill_someone(self, wolf: "Role", player_name: str): + if not self._check_valid_role(wolf, "Werewolf"): + return + if not self._check_player_continue(wolf.name, particular_step=5): # 5=step no + return + + self.round_hunts[wolf.name] = player_name + living_werewolf = [p for p in self.werewolf_players if p in self.living_players] + # check if all living wolfs finish hunting, then get the hunted one + if list(self.round_hunts.keys()) == living_werewolf: + hunted_all = list(self.round_hunts.values()) + self.player_hunted = Counter(hunted_all).most_common()[0][0] + + @mark_as_writeable + def witch_poison_someone(self, witch: "Role", player_name: str = None): + if not self._check_valid_role(witch, "Witch"): + return + if not self._check_player_continue(player_name): + return + + self._update_players_state([player_name], RoleState.POISONED) + self.player_poisoned = player_name + + @mark_as_writeable + def witch_save_someone(self, witch: "Role", player_name: str = None): + if not self._check_valid_role(witch, "Witch"): + return + if not self._check_player_continue(player_name): + return + + self._update_players_state([player_name], RoleState.SAVED) + self.player_protected = player_name + + @mark_as_writeable + def update_game_states(self, memories: list): + step_idx = self.step_idx % self.per_round_steps + if step_idx not in [15, 18] or self.step_idx in self.eval_step_idx: + return + else: + self.eval_step_idx.append(self.step_idx) # record evaluation, avoid repetitive evaluation at the same step + + if step_idx == 15: # step no + # night ends: after all special roles acted, process the whole night + self.player_current_dead = [] # reset + + if self.player_hunted != self.player_protected and not self.is_hunted_player_saved: + self.player_current_dead.append(self.player_hunted) + if self.player_poisoned: + self.player_current_dead.append(self.player_poisoned) + + self._update_players_state([self.player_current_dead]) + # reset + self.player_hunted = None + self.player_protected = None + self.is_hunted_player_saved = False + self.player_poisoned = None + + # game's termination condition + living_werewolf = [p for p in self.werewolf_players if p in self.living_players] + living_villagers = [p for p in self.villager_players if p in self.living_players] + living_special_roles = [p for p in self.special_role_players if p in self.living_players] + if not living_werewolf: + self.winner = "good guys" + self.win_reason = "werewolves all dead" + elif not living_villagers or not living_special_roles: + self.winner = "werewolf" + self.win_reason = "villagers all dead" if not living_villagers else "special roles all dead" + if self.winner is not None: + self._record_all_experiences() # TODO diff --git a/metagpt/learn/google_search.py b/metagpt/learn/google_search.py index 3f356f7dd..399c14de4 100644 --- a/metagpt/learn/google_search.py +++ b/metagpt/learn/google_search.py @@ -8,5 +8,5 @@ async def google_search(query: str, max_results: int = 6, **kwargs): :param max_results: The number of search results to retrieve :return: The web search results in markdown format. """ - results = await SearchEngine().run(query, max_results=max_results, as_string=False) + results = await SearchEngine(**kwargs).run(query, max_results=max_results, as_string=False) return "\n".join(f"{i}. [{j['title']}]({j['link']}): {j['snippet']}" for i, j in enumerate(results, 1)) diff --git a/metagpt/learn/skill_loader.py b/metagpt/learn/skill_loader.py index 7383af66d..bcf28bb87 100644 --- a/metagpt/learn/skill_loader.py +++ b/metagpt/learn/skill_loader.py @@ -13,7 +13,7 @@ import yaml from pydantic import BaseModel, Field -from metagpt.config import CONFIG +from metagpt.context import Context class Example(BaseModel): @@ -73,14 +73,15 @@ async def load(skill_yaml_file_name: Path = None) -> "SkillsDeclaration": skill_data = yaml.safe_load(data) return SkillsDeclaration(**skill_data) - def get_skill_list(self, entity_name: str = "Assistant") -> Dict: + def get_skill_list(self, entity_name: str = "Assistant", context: Context = None) -> Dict: """Return the skill name based on the skill description.""" entity = self.entities.get(entity_name) if not entity: return {} # List of skills that the agent chooses to activate. - agent_skills = CONFIG.agent_skills + ctx = context or Context() + agent_skills = ctx.kwargs.agent_skills if not agent_skills: return {} diff --git a/metagpt/learn/text_to_embedding.py b/metagpt/learn/text_to_embedding.py index 26dab0419..f859ab638 100644 --- a/metagpt/learn/text_to_embedding.py +++ b/metagpt/learn/text_to_embedding.py @@ -6,19 +6,19 @@ @File : text_to_embedding.py @Desc : Text-to-Embedding skill, which provides text-to-embedding functionality. """ - -from metagpt.config import CONFIG +import metagpt.config2 +from metagpt.config2 import Config from metagpt.tools.openai_text_to_embedding import oas3_openai_text_to_embedding -async def text_to_embedding(text, model="text-embedding-ada-002", openai_api_key="", **kwargs): +async def text_to_embedding(text, model="text-embedding-ada-002", config: Config = metagpt.config2.config): """Text to embedding :param text: The text used for embedding. :param model: One of ['text-embedding-ada-002'], ID of the model to use. For more details, checkout: `https://api.openai.com/v1/models`. - :param openai_api_key: OpenAI API key, For more details, checkout: `https://platform.openai.com/account/api-keys` + :param config: OpenAI config with API key, For more details, checkout: `https://platform.openai.com/account/api-keys` :return: A json object of :class:`ResultEmbedding` class if successful, otherwise `{}`. """ - if CONFIG.OPENAI_API_KEY or openai_api_key: - return await oas3_openai_text_to_embedding(text, model=model, openai_api_key=openai_api_key) - raise EnvironmentError + openai_api_key = config.get_openai_llm().api_key + proxy = config.get_openai_llm().proxy + return await oas3_openai_text_to_embedding(text, model=model, openai_api_key=openai_api_key, proxy=proxy) diff --git a/metagpt/learn/text_to_image.py b/metagpt/learn/text_to_image.py index c3c62fb67..163859fc0 100644 --- a/metagpt/learn/text_to_image.py +++ b/metagpt/learn/text_to_image.py @@ -8,33 +8,37 @@ """ import base64 -from metagpt.config import CONFIG +import metagpt.config2 +from metagpt.config2 import Config from metagpt.const import BASE64_FORMAT +from metagpt.llm import LLM from metagpt.tools.metagpt_text_to_image import oas3_metagpt_text_to_image from metagpt.tools.openai_text_to_image import oas3_openai_text_to_image from metagpt.utils.s3 import S3 -async def text_to_image(text, size_type: str = "512x512", openai_api_key="", model_url="", **kwargs): +async def text_to_image(text, size_type: str = "512x512", config: Config = metagpt.config2.config): """Text to image :param text: The text used for image conversion. - :param openai_api_key: OpenAI API key, For more details, checkout: `https://platform.openai.com/account/api-keys` :param size_type: If using OPENAI, the available size options are ['256x256', '512x512', '1024x1024'], while for MetaGPT, the options are ['512x512', '512x768']. - :param model_url: MetaGPT model url + :param config: Config :return: The image data is returned in Base64 encoding. """ image_declaration = "data:image/png;base64," - if CONFIG.METAGPT_TEXT_TO_IMAGE_MODEL_URL or model_url: + + model_url = config.metagpt_tti_url + if model_url: binary_data = await oas3_metagpt_text_to_image(text, size_type, model_url) - elif CONFIG.OPENAI_API_KEY or openai_api_key: - binary_data = await oas3_openai_text_to_image(text, size_type) + elif config.get_openai_llm(): + llm = LLM(llm_config=config.get_openai_llm()) + binary_data = await oas3_openai_text_to_image(text, size_type, llm=llm) else: raise ValueError("Missing necessary parameters.") base64_data = base64.b64encode(binary_data).decode("utf-8") - s3 = S3() - url = await s3.cache(data=base64_data, file_ext=".png", format=BASE64_FORMAT) if s3.is_valid else "" + s3 = S3(config.s3) + url = await s3.cache(data=base64_data, file_ext=".png", format=BASE64_FORMAT) if url: return f"![{text}]({url})" return image_declaration + base64_data if base64_data else "" diff --git a/metagpt/learn/text_to_speech.py b/metagpt/learn/text_to_speech.py index ecd00c724..8dbd6d243 100644 --- a/metagpt/learn/text_to_speech.py +++ b/metagpt/learn/text_to_speech.py @@ -6,8 +6,8 @@ @File : text_to_speech.py @Desc : Text-to-Speech skill, which provides text-to-speech functionality """ - -from metagpt.config import CONFIG +import metagpt.config2 +from metagpt.config2 import Config from metagpt.const import BASE64_FORMAT from metagpt.tools.azure_tts import oas3_azsure_tts from metagpt.tools.iflytek_tts import oas3_iflytek_tts @@ -20,12 +20,7 @@ async def text_to_speech( voice="zh-CN-XiaomoNeural", style="affectionate", role="Girl", - subscription_key="", - region="", - iflytek_app_id="", - iflytek_api_key="", - iflytek_api_secret="", - **kwargs, + config: Config = metagpt.config2.config, ): """Text to speech For more details, check out:`https://learn.microsoft.com/en-us/azure/ai-services/speech-service/language-support?tabs=tts` @@ -44,27 +39,31 @@ async def text_to_speech( """ - if (CONFIG.AZURE_TTS_SUBSCRIPTION_KEY and CONFIG.AZURE_TTS_REGION) or (subscription_key and region): + subscription_key = config.azure_tts_subscription_key + region = config.azure_tts_region + if subscription_key and region: audio_declaration = "data:audio/wav;base64," base64_data = await oas3_azsure_tts(text, lang, voice, style, role, subscription_key, region) - s3 = S3() - url = await s3.cache(data=base64_data, file_ext=".wav", format=BASE64_FORMAT) if s3.is_valid else "" + s3 = S3(config.s3) + url = await s3.cache(data=base64_data, file_ext=".wav", format=BASE64_FORMAT) if url: return f"[{text}]({url})" return audio_declaration + base64_data if base64_data else base64_data - if (CONFIG.IFLYTEK_APP_ID and CONFIG.IFLYTEK_API_KEY and CONFIG.IFLYTEK_API_SECRET) or ( - iflytek_app_id and iflytek_api_key and iflytek_api_secret - ): + + iflytek_app_id = config.iflytek_app_id + iflytek_api_key = config.iflytek_api_key + iflytek_api_secret = config.iflytek_api_secret + if iflytek_app_id and iflytek_api_key and iflytek_api_secret: audio_declaration = "data:audio/mp3;base64," base64_data = await oas3_iflytek_tts( text=text, app_id=iflytek_app_id, api_key=iflytek_api_key, api_secret=iflytek_api_secret ) - s3 = S3() - url = await s3.cache(data=base64_data, file_ext=".mp3", format=BASE64_FORMAT) if s3.is_valid else "" + s3 = S3(config.s3) + url = await s3.cache(data=base64_data, file_ext=".mp3", format=BASE64_FORMAT) if url: return f"[{text}]({url})" return audio_declaration + base64_data if base64_data else base64_data raise ValueError( - "AZURE_TTS_SUBSCRIPTION_KEY, AZURE_TTS_REGION, IFLYTEK_APP_ID, IFLYTEK_API_KEY, IFLYTEK_API_SECRET error" + "azure_tts_subscription_key, azure_tts_region, iflytek_app_id, iflytek_api_key, iflytek_api_secret error" ) diff --git a/metagpt/llm.py b/metagpt/llm.py index 76dd5a0f8..465e419a1 100644 --- a/metagpt/llm.py +++ b/metagpt/llm.py @@ -5,20 +5,16 @@ @Author : alexanderwu @File : llm.py """ - from typing import Optional -from metagpt.config import CONFIG, LLMProviderEnum +from metagpt.configs.llm_config import LLMConfig +from metagpt.context import Context from metagpt.provider.base_llm import BaseLLM -from metagpt.provider.human_provider import HumanProvider -from metagpt.provider.llm_provider_registry import LLM_REGISTRY - -_ = HumanProvider() # Avoid pre-commit error - -def LLM(provider: Optional[LLMProviderEnum] = None) -> BaseLLM: - """get the default llm provider""" - if provider is None: - provider = CONFIG.get_default_llm_provider_enum() - return LLM_REGISTRY.get_provider(provider) +def LLM(llm_config: Optional[LLMConfig] = None, context: Context = None) -> BaseLLM: + """get the default llm provider if name is None""" + ctx = context or Context() + if llm_config is not None: + return ctx.llm_with_cost_manager_from_llm_config(llm_config) + return ctx.llm() diff --git a/metagpt/logs.py b/metagpt/logs.py index fb0fdd553..90bac21aa 100644 --- a/metagpt/logs.py +++ b/metagpt/logs.py @@ -15,14 +15,15 @@ from metagpt.const import METAGPT_ROOT -def define_log_level(print_level="INFO", logfile_level="DEBUG"): +def define_log_level(print_level="INFO", logfile_level="DEBUG", name: str = None): """Adjust the log level to above level""" current_date = datetime.now() formatted_date = current_date.strftime("%Y%m%d") + log_name = f"{name}_{formatted_date}" if name else formatted_date # name a log with prefix name _logger.remove() _logger.add(sys.stderr, level=print_level) - _logger.add(METAGPT_ROOT / f"logs/{formatted_date}.txt", level=logfile_level) + _logger.add(METAGPT_ROOT / f"logs/{log_name}.txt", level=logfile_level) return _logger diff --git a/metagpt/memory/brain_memory.py b/metagpt/memory/brain_memory.py index ff29eaddb..044b0b359 100644 --- a/metagpt/memory/brain_memory.py +++ b/metagpt/memory/brain_memory.py @@ -14,8 +14,8 @@ from pydantic import BaseModel, Field -from metagpt.config import CONFIG -from metagpt.const import DEFAULT_LANGUAGE, DEFAULT_MAX_TOKENS, DEFAULT_TOKEN_SIZE +from metagpt.config2 import config +from metagpt.const import DEFAULT_MAX_TOKENS, DEFAULT_TOKEN_SIZE from metagpt.logs import logger from metagpt.provider import MetaGPTLLM from metagpt.provider.base_llm import BaseLLM @@ -29,9 +29,9 @@ class BrainMemory(BaseModel): historical_summary: str = "" last_history_id: str = "" is_dirty: bool = False - last_talk: str = None + last_talk: Optional[str] = None cacheable: bool = True - llm: Optional[BaseLLM] = None + llm: Optional[BaseLLM] = Field(default=None, exclude=True) class Config: arbitrary_types_allowed = True @@ -56,8 +56,8 @@ def get_knowledge(self) -> str: @staticmethod async def loads(redis_key: str) -> "BrainMemory": - redis = Redis() - if not redis.is_valid or not redis_key: + redis = Redis(config.redis) + if not redis_key: return BrainMemory() v = await redis.get(key=redis_key) logger.debug(f"REDIS GET {redis_key} {v}") @@ -70,8 +70,8 @@ async def loads(redis_key: str) -> "BrainMemory": async def dumps(self, redis_key: str, timeout_sec: int = 30 * 60): if not self.is_dirty: return - redis = Redis() - if not redis.is_valid or not redis_key: + redis = Redis(config.redis) + if not redis_key: return False v = self.model_dump_json() if self.cacheable: @@ -83,7 +83,7 @@ async def dumps(self, redis_key: str, timeout_sec: int = 30 * 60): def to_redis_key(prefix: str, user_id: str, chat_id: str): return f"{prefix}:{user_id}:{chat_id}" - async def set_history_summary(self, history_summary, redis_key, redis_conf): + async def set_history_summary(self, history_summary, redis_key): if self.historical_summary == history_summary: if self.is_dirty: await self.dumps(redis_key=redis_key) @@ -140,7 +140,7 @@ async def _openai_summarize(self, llm, max_words=200, keep_language: bool = Fals return text summary = await self._summarize(text=text, max_words=max_words, keep_language=keep_language, limit=limit) if summary: - await self.set_history_summary(history_summary=summary, redis_key=CONFIG.REDIS_KEY, redis_conf=CONFIG.REDIS) + await self.set_history_summary(history_summary=summary, redis_key=config.redis_key) return summary raise ValueError(f"text too long:{text_length}") @@ -164,7 +164,7 @@ async def _metagpt_summarize(self, max_words=200): msgs.reverse() self.history = msgs self.is_dirty = True - await self.dumps(redis_key=CONFIG.REDIS_KEY) + await self.dumps(redis_key=config.redis.key) self.is_dirty = False return BrainMemory.to_metagpt_history_format(self.history) @@ -181,7 +181,7 @@ async def get_title(self, llm, max_words=5, **kwargs) -> str: summary = await self.summarize(llm=llm, max_words=500) - language = CONFIG.language or DEFAULT_LANGUAGE + language = config.language command = f"Translate the above summary into a {language} title of less than {max_words} words." summaries = [summary, command] msg = "\n".join(summaries) diff --git a/metagpt/memory/longterm_memory.py b/metagpt/memory/longterm_memory.py index b54653970..5a139a93b 100644 --- a/metagpt/memory/longterm_memory.py +++ b/metagpt/memory/longterm_memory.py @@ -2,7 +2,6 @@ # -*- coding: utf-8 -*- """ @Desc : the implement of Long-term memory -@Modified By: mashenquan, 2023/8/20. Remove global configuration `CONFIG`, enable configuration support for business isolation. """ from typing import Optional diff --git a/metagpt/memory/memory.py b/metagpt/memory/memory.py index 593409648..580361d33 100644 --- a/metagpt/memory/memory.py +++ b/metagpt/memory/memory.py @@ -7,19 +7,13 @@ @Modified By: mashenquan, 2023-11-1. According to RFC 116: Updated the type of index key. """ from collections import defaultdict -from pathlib import Path from typing import DefaultDict, Iterable, Set from pydantic import BaseModel, Field, SerializeAsAny from metagpt.const import IGNORED_MESSAGE_ID from metagpt.schema import Message -from metagpt.utils.common import ( - any_to_str, - any_to_str_set, - read_json_file, - write_json_file, -) +from metagpt.utils.common import any_to_str, any_to_str_set class Memory(BaseModel): @@ -29,22 +23,6 @@ class Memory(BaseModel): index: DefaultDict[str, list[SerializeAsAny[Message]]] = Field(default_factory=lambda: defaultdict(list)) ignore_id: bool = False - def serialize(self, stg_path: Path): - """stg_path = ./storage/team/environment/ or ./storage/team/environment/roles/{role_class}_{role_name}/""" - memory_path = stg_path.joinpath("memory.json") - storage = self.model_dump() - write_json_file(memory_path, storage) - - @classmethod - def deserialize(cls, stg_path: Path) -> "Memory": - """stg_path = ./storage/team/environment/ or ./storage/team/environment/roles/{role_class}_{role_name}/""" - memory_path = stg_path.joinpath("memory.json") - - memory_dict = read_json_file(memory_path) - memory = Memory(**memory_dict) - - return memory - def add(self, message: Message): """Add a new message to storage, while updating the index""" if self.ignore_id: diff --git a/metagpt/memory/memory_storage.py b/metagpt/memory/memory_storage.py index 1850e0ea0..c029d027b 100644 --- a/metagpt/memory/memory_storage.py +++ b/metagpt/memory/memory_storage.py @@ -2,7 +2,6 @@ # -*- coding: utf-8 -*- """ @Desc : the implement of memory storage -@Modified By: mashenquan, 2023/8/20. Remove global configuration `CONFIG`, enable configuration support for business isolation. """ from pathlib import Path diff --git a/metagpt/prompts/ci/ml_action.py b/metagpt/prompts/ci/ml_action.py new file mode 100644 index 000000000..5d27c7ff0 --- /dev/null +++ b/metagpt/prompts/ci/ml_action.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Time : 2023/11/24 15:43 +# @Author : lidanyang +# @File : ml_action +# @Desc : +UPDATE_DATA_COLUMNS = """ +# Background +Keep dataset column information updated before model train. +## Done Tasks +```python +{history_code} +```end + +# Task +Update and print the dataset's column information only if the train or test data has changed. Use the following code: +```python +from metagpt.tools.libs.data_preprocess import get_column_info + +column_info = get_column_info(df) +print("column_info") +print(column_info) +```end + +# Constraints: +- Use the DataFrame variable from 'Done Tasks' in place of df. +- Import `get_column_info` only if it's not already imported. +""" + +PRINT_DATA_COLUMNS = { + "name": "print_column_info", + "description": "Print the latest column information after 'Done Tasks' code if first read or data changed.", + "parameters": { + "type": "object", + "properties": { + "code": { + "type": "string", + "description": "The code to be added to a new cell in jupyter.", + }, + }, + "required": ["code"], + }, +} + +ML_COMMON_PROMPT = """ +# Background +As a data scientist, you need to help user to achieve their goal [{user_requirement}] step-by-step in an continuous Jupyter notebook. + +## Done Tasks +```python +{history_code} +```end + +## Current Task +{current_task} + +# Latest Data Info +Latest data info after previous tasks: +{column_info} + +# Task +Write complete code for 'Current Task'. And avoid duplicating code from 'Done Tasks', such as repeated import of packages, reading data, etc. +Specifically, {tool_type_usage_prompt} +""" + +USE_NO_TOOLS_EXAMPLE = """ +# Output Example: +when current task is "train a lightgbm model on training data", the code can be like: +```python +# Step 1: check data type and convert to numeric +obj_cols = train.select_dtypes(include='object').columns.tolist() + +for col in obj_cols: + encoder = LabelEncoder() + train[col] = encoder.fit_transform(train[col].unique().tolist() + ['unknown']) + test[col] = test[col].apply(lambda x: x if x in encoder.classes_ else 'unknown') + test[col] = encoder.transform(test[col]) + +# Step 2: train lightgbm model +model = LGBMClassifier() +model.fit(train, y_train) +```end + +# Constraints: +- Ensure the output new code is executable in the same Jupyter notebook with previous tasks code have been executed. +""" + +USE_TOOLS_EXAMPLE = """ +# Capabilities +- You can utilize pre-defined tools in any code lines from 'Available Tools' in the form of Python Class. +- You can freely combine the use of any other public packages, like sklearn, numpy, pandas, etc.. + +# Available Tools: +Each Class tool is described in JSON format. When you call a tool, import the tool from its path first. +{tool_schemas} + +# Output Example: +when current task is "do data preprocess, like fill missing value, handle outliers, etc.", the code can be like: +```python +# Step 1: fill missing value +# Tools used: ['FillMissingValue'] +from metagpt.tools.libs.data_preprocess import FillMissingValue + +train_processed = train.copy() +test_processed = test.copy() +num_cols = train_processed.select_dtypes(include='number').columns.tolist() +if 'label' in num_cols: + num_cols.remove('label') +fill_missing_value = FillMissingValue(features=num_cols, strategy='mean') +fill_missing_value.fit(train_processed) +train_processed = fill_missing_value.transform(train_processed) +test_processed = fill_missing_value.transform(test_processed) + +# Step 2: handle outliers +for col in num_cols: + low, high = train_processed[col].quantile([0.01, 0.99]) + train_processed[col] = train_processed[col].clip(low, high) + test_processed[col] = test_processed[col].clip(low, high) +```end + +# Constraints: +- Ensure the output new code is executable in the same Jupyter notebook with previous tasks code have been executed. +- Always prioritize using pre-defined tools for the same functionality. +- Always copy the DataFrame before processing it and use the copy to process. +""" + +ML_GENERATE_CODE_PROMPT = ML_COMMON_PROMPT + USE_NO_TOOLS_EXAMPLE +ML_TOOL_USAGE_PROMPT = ML_COMMON_PROMPT + USE_TOOLS_EXAMPLE diff --git a/metagpt/prompts/ci/write_analysis_code.py b/metagpt/prompts/ci/write_analysis_code.py new file mode 100644 index 000000000..4eccefcd1 --- /dev/null +++ b/metagpt/prompts/ci/write_analysis_code.py @@ -0,0 +1,93 @@ +ASSIGN_TASK_TYPE_PROMPT = """ +Please assign a task type to each task in the list below from the given categories: +{task_info} + +## All Task Type: +{task_type_desc} +""" + +ASSIGN_TASK_TYPE_CONFIG = { + "name": "assign_task_type", + "description": "Assign task type to each task by order.", + "parameters": { + "type": "object", + "properties": { + "task_type": { + "type": "array", + "description": "List of task type. The length should as long as task list", + "items": { + "type": "string", + }, + }, + }, + "required": ["task_type"], + }, +} + +TOOL_RECOMMENDATION_PROMPT = """ +## User Requirement: +{current_task} + +## Task +Recommend up to five tools from 'Available Tools' that can help solve the 'User Requirement'. + +## Available Tools: +{available_tools} + +## Tool Selection and Instructions: +- Select tools most relevant to completing the 'User Requirement'. +- If you believe that no tools are suitable, indicate with an empty list. +- Only list the names of the tools, not the full schema of each tool. +- Ensure selected tools are listed in 'Available Tools'. +""" + +SELECT_FUNCTION_TOOLS = { + "name": "select_function_tools", + "description": "For current task, select suitable tools for it.", + "parameters": { + "type": "object", + "properties": { + "recommend_tools": { + "type": "array", + "description": "List of tool names. Empty list if no tool is suitable.", + "items": { + "type": "string", + }, + }, + }, + "required": ["recommend_tools"], + }, +} + +CODE_GENERATOR_WITH_TOOLS = { + "name": "add_subtask_code", + "description": "Add new code cell of current task to the end of an active Jupyter notebook.", + "parameters": { + "type": "object", + "properties": { + "code": { + "type": "string", + "description": "The code to be added to a new cell in jupyter.", + }, + }, + "required": ["code"], + }, +} + +TOOL_USAGE_PROMPT = """ +# Instruction +Write complete code for 'Current Task'. And avoid duplicating code from finished tasks, such as repeated import of packages, reading data, etc. +Specifically, {tool_type_usage_prompt} + +# Capabilities +- You can utilize pre-defined tools in any code lines from 'Available Tools' in the form of Python Class. +- You can freely combine the use of any other public packages, like sklearn, numpy, pandas, etc.. + +# Available Tools (can be empty): +Each Class tool is described in JSON format. When you call a tool, import the tool first. +{tool_schemas} + +# Constraints: +- Ensure the output new code is executable in the same Jupyter notebook with previous tasks code have been executed. +- Always prioritize using pre-defined tools for the same functionality. +""" diff --git a/metagpt/prompts/tool_types.py b/metagpt/prompts/tool_types.py new file mode 100644 index 000000000..f27fbea99 --- /dev/null +++ b/metagpt/prompts/tool_types.py @@ -0,0 +1,46 @@ +# Prompt for using tools of "data_preprocess" type +DATA_PREPROCESS_PROMPT = """ +The current task is about data preprocessing, please note the following: +- Monitor data types per column, applying appropriate methods. +- Ensure operations are on existing dataset columns. +- Avoid writing processed data to files. +- Avoid any change to label column, such as standardization, etc. +- Prefer alternatives to one-hot encoding for categorical data. +- Only encode or scale necessary columns to allow for potential feature-specific engineering tasks (like time_extract, binning, extraction, etc.) later. +- Each step do data preprocessing to train, must do same for test separately at the same time. +""" + +# Prompt for using tools of "feature_engineering" type +FEATURE_ENGINEERING_PROMPT = """ +The current task is about feature engineering. when performing it, please adhere to the following principles: +- Generate as diverse features as possible to improve the model's performance step-by-step. +- Use available feature engineering tools if they are potential impactful. +- Avoid creating redundant or excessively numerous features in one step. +- Exclude ID columns from feature generation and remove them. +- Each feature engineering operation performed on the train set must also applies to the test separately at the same time. +- Avoid using the label column to create features, except for cat encoding. +- Use the data from previous task result if exist, do not mock or reload data yourself. +""" + +# Prompt for using tools of "model_train" type +MODEL_TRAIN_PROMPT = """ +The current task is about training a model, please ensure high performance: +- Keep in mind that your user prioritizes results and is highly focused on model performance. So, when needed, feel free to use models of any complexity to improve effectiveness, such as XGBoost, CatBoost, etc. +- If non-numeric columns exist, perform label encode together with all steps. +- Use the data from previous task result directly, do not mock or reload data yourself. +- Set suitable hyperparameters for the model, make metrics as high as possible. +""" + +# Prompt for using tools of "model_evaluate" type +MODEL_EVALUATE_PROMPT = """ +The current task is about evaluating a model, please note the following: +- Ensure that the evaluated data is same processed as the training data. If not, remember use object in 'Done Tasks' to transform the data. +- Use trained model from previous task result directly, do not mock or reload model yourself. +""" + +# Prompt for using tools of "vision" type +IMAGE2WEBPAGE_PROMPT = """ +The current task is about converting image into webpage code. please note the following: +- Single-Step Code Generation: Execute the entire code generation process in a single step, encompassing HTML, CSS, and JavaScript. Avoid fragmenting the code generation into multiple separate steps to maintain consistency and simplify the development workflow. +- Save webpages: Be sure to use the save method provided. +""" diff --git a/metagpt/provider/__init__.py b/metagpt/provider/__init__.py index 28157a4e2..675734811 100644 --- a/metagpt/provider/__init__.py +++ b/metagpt/provider/__init__.py @@ -14,6 +14,8 @@ from metagpt.provider.zhipuai_api import ZhiPuAILLM from metagpt.provider.azure_openai_api import AzureOpenAILLM from metagpt.provider.metagpt_api import MetaGPTLLM +from metagpt.provider.human_provider import HumanProvider +from metagpt.provider.spark_api import SparkLLM __all__ = [ "FireworksLLM", @@ -24,4 +26,6 @@ "AzureOpenAILLM", "MetaGPTLLM", "OllamaLLM", + "HumanProvider", + "SparkLLM", ] diff --git a/metagpt/provider/anthropic_api.py b/metagpt/provider/anthropic_api.py index b9d7d9e38..f31c2d04d 100644 --- a/metagpt/provider/anthropic_api.py +++ b/metagpt/provider/anthropic_api.py @@ -9,12 +9,15 @@ import anthropic from anthropic import Anthropic, AsyncAnthropic -from metagpt.config import CONFIG +from metagpt.configs.llm_config import LLMConfig class Claude2: + def __init__(self, config: LLMConfig): + self.config = config + def ask(self, prompt: str) -> str: - client = Anthropic(api_key=CONFIG.anthropic_api_key) + client = Anthropic(api_key=self.config.api_key) res = client.completions.create( model="claude-2", @@ -24,7 +27,7 @@ def ask(self, prompt: str) -> str: return res.completion async def aask(self, prompt: str) -> str: - aclient = AsyncAnthropic(api_key=CONFIG.anthropic_api_key) + aclient = AsyncAnthropic(api_key=self.config.api_key) res = await aclient.completions.create( model="claude-2", diff --git a/metagpt/provider/azure_openai_api.py b/metagpt/provider/azure_openai_api.py index d15d1c82e..6dc32d380 100644 --- a/metagpt/provider/azure_openai_api.py +++ b/metagpt/provider/azure_openai_api.py @@ -3,8 +3,6 @@ @Time : 2023/5/5 23:08 @Author : alexanderwu @File : openai.py -@Modified By: mashenquan, 2023/8/20. Remove global configuration `CONFIG`, enable configuration support for business isolation; - Change cost control from global to company level. @Modified By: mashenquan, 2023/11/21. Fix bug: ReadTimeout. @Modified By: mashenquan, 2023/12/1. Fix bug: Unclosed connection caused by openai 0.x. """ @@ -13,12 +11,12 @@ from openai import AsyncAzureOpenAI from openai._base_client import AsyncHttpxClientWrapper -from metagpt.config import LLMProviderEnum +from metagpt.configs.llm_config import LLMType from metagpt.provider.llm_provider_registry import register_provider from metagpt.provider.openai_api import OpenAILLM -@register_provider(LLMProviderEnum.AZURE_OPENAI) +@register_provider(LLMType.AZURE) class AzureOpenAILLM(OpenAILLM): """ Check https://platform.openai.com/examples for examples @@ -28,13 +26,13 @@ def _init_client(self): kwargs = self._make_client_kwargs() # https://learn.microsoft.com/zh-cn/azure/ai-services/openai/how-to/migration?tabs=python-new%2Cdalle-fix self.aclient = AsyncAzureOpenAI(**kwargs) - self.model = self.config.DEPLOYMENT_NAME # Used in _calc_usage & _cons_kwargs + self.model = self.config.model # Used in _calc_usage & _cons_kwargs def _make_client_kwargs(self) -> dict: kwargs = dict( - api_key=self.config.OPENAI_API_KEY, - api_version=self.config.OPENAI_API_VERSION, - azure_endpoint=self.config.OPENAI_BASE_URL, + api_key=self.config.api_key, + api_version=self.config.api_version, + azure_endpoint=self.config.base_url, ) # to use proxy, openai v1 needs http_client diff --git a/metagpt/provider/base_llm.py b/metagpt/provider/base_llm.py index 47c527b97..b144471b5 100644 --- a/metagpt/provider/base_llm.py +++ b/metagpt/provider/base_llm.py @@ -8,17 +8,52 @@ """ import json from abc import ABC, abstractmethod -from typing import Optional +from typing import Optional, Union + +from openai import AsyncOpenAI + +from metagpt.configs.llm_config import LLMConfig +from metagpt.logs import logger +from metagpt.schema import Message +from metagpt.utils.cost_manager import CostManager class BaseLLM(ABC): """LLM API abstract class, requiring all inheritors to provide a series of standard capabilities""" + config: LLMConfig use_system_prompt: bool = True system_prompt = "You are a helpful assistant." - def _user_msg(self, msg: str) -> dict[str, str]: - return {"role": "user", "content": msg} + # OpenAI / Azure / Others + aclient: Optional[Union[AsyncOpenAI]] = None + cost_manager: Optional[CostManager] = None + model: Optional[str] = None + + @abstractmethod + def __init__(self, config: LLMConfig): + pass + + def _user_msg(self, msg: str, images: Optional[Union[str, list[str]]] = None) -> dict[str, Union[str, dict]]: + if images: + # as gpt-4v, chat with image + return self._user_msg_with_imgs(msg, images) + else: + return {"role": "user", "content": msg} + + def _user_msg_with_imgs(self, msg: str, images: Optional[Union[str, list[str]]]): + """ + images: can be list of http(s) url or base64 + """ + if isinstance(images, str): + images = [images] + content = [{"type": "text", "text": msg}] + for image in images: + # image url or image base64 + url = image if image.startswith("http") else f"data:image/jpeg;base64,{image}" + # it can with multiple-image inputs + content.append({"type": "image_url", "image_url": url}) + return {"role": "user", "content": content} def _assistant_msg(self, msg: str) -> dict[str, str]: return {"role": "assistant", "content": msg} @@ -37,6 +72,7 @@ async def aask( msg: str, system_msgs: Optional[list[str]] = None, format_msgs: Optional[list[dict[str, str]]] = None, + images: Optional[Union[str, list[str]]] = None, timeout=3, stream=True, ) -> str: @@ -48,7 +84,8 @@ async def aask( message = [] if format_msgs: message.extend(format_msgs) - message.append(self._user_msg(msg)) + message.append(self._user_msg(msg, images=images)) + logger.debug(message) rsp = await self.acompletion_text(message, stream=stream, timeout=timeout) return rsp @@ -65,10 +102,9 @@ async def aask_batch(self, msgs: list, timeout=3) -> str: context.append(self._assistant_msg(rsp_text)) return self._extract_assistant_rsp(context) - async def aask_code(self, msgs: list[str], timeout=3) -> str: + async def aask_code(self, messages: Union[str, Message, list[dict]], timeout=3) -> dict: """FIXME: No code segment filtering has been done here, and all results are actually displayed""" - rsp_text = await self.aask_batch(msgs, timeout=timeout) - return rsp_text + raise NotImplementedError @abstractmethod async def acompletion(self, messages: list[dict], timeout=3): @@ -131,4 +167,12 @@ def get_choice_function_arguments(self, rsp: dict) -> dict: :return dict: return the first function arguments of choice, for example, {'language': 'python', 'code': "print('Hello, World!')"} """ - return json.loads(self.get_choice_function(rsp)["arguments"]) + return json.loads(self.get_choice_function(rsp)["arguments"], strict=False) + + def messages_to_prompt(self, messages: list[dict]): + """[{"role": "user", "content": msg}] to user: etc.""" + return "\n".join([f"{i['role']}: {i['content']}" for i in messages]) + + def messages_to_dict(self, messages): + """objects to [{"role": "user", "content": msg}] etc.""" + return [i.to_dict() for i in messages] diff --git a/metagpt/provider/fireworks_api.py b/metagpt/provider/fireworks_api.py index f0af68818..d56453a85 100644 --- a/metagpt/provider/fireworks_api.py +++ b/metagpt/provider/fireworks_api.py @@ -15,7 +15,7 @@ wait_random_exponential, ) -from metagpt.config import CONFIG, Config, LLMProviderEnum +from metagpt.configs.llm_config import LLMConfig, LLMType from metagpt.logs import logger from metagpt.provider.llm_provider_registry import register_provider from metagpt.provider.openai_api import OpenAILLM, log_and_reraise @@ -64,44 +64,35 @@ def update_cost(self, prompt_tokens: int, completion_tokens: int, model: str): token_costs = self.model_grade_token_costs(model) cost = (prompt_tokens * token_costs["prompt"] + completion_tokens * token_costs["completion"]) / 1000000 self.total_cost += cost - max_budget = CONFIG.max_budget if CONFIG.max_budget else CONFIG.cost_manager.max_budget logger.info( - f"Total running cost: ${self.total_cost:.4f} | Max budget: ${max_budget:.3f} | " + f"Total running cost: ${self.total_cost:.4f}" f"Current cost: ${cost:.4f}, prompt_tokens: {prompt_tokens}, completion_tokens: {completion_tokens}" ) - CONFIG.total_cost = self.total_cost -@register_provider(LLMProviderEnum.FIREWORKS) +@register_provider(LLMType.FIREWORKS) class FireworksLLM(OpenAILLM): - def __init__(self): - self.config: Config = CONFIG - self.__init_fireworks() + def __init__(self, config: LLMConfig): + super().__init__(config=config) self.auto_max_tokens = False - self._cost_manager = FireworksCostManager() - - def __init_fireworks(self): - self.is_azure = False - self.rpm = int(self.config.get("RPM", 10)) - self._init_client() - self.model = self.config.fireworks_api_model # `self.model` should after `_make_client` to rewrite it + self.cost_manager = FireworksCostManager() def _make_client_kwargs(self) -> dict: - kwargs = dict(api_key=self.config.fireworks_api_key, base_url=self.config.fireworks_api_base) + kwargs = dict(api_key=self.config.api_key, base_url=self.config.base_url) return kwargs def _update_costs(self, usage: CompletionUsage): if self.config.calc_usage and usage: try: - # use FireworksCostManager not CONFIG.cost_manager - self._cost_manager.update_cost(usage.prompt_tokens, usage.completion_tokens, self.model) + # use FireworksCostManager not context.cost_manager + self.cost_manager.update_cost(usage.prompt_tokens, usage.completion_tokens, self.model) except Exception as e: logger.error(f"updating costs failed!, exp: {e}") def get_costs(self) -> Costs: - return self._cost_manager.get_costs() + return self.cost_manager.get_costs() - async def _achat_completion_stream(self, messages: list[dict]) -> str: + async def _achat_completion_stream(self, messages: list[dict], timeout=3) -> str: response: AsyncStream[ChatCompletionChunk] = await self.aclient.chat.completions.create( **self._cons_kwargs(messages), stream=True ) diff --git a/metagpt/provider/google_gemini_api.py b/metagpt/provider/google_gemini_api.py index c36c677ef..2647ab16b 100644 --- a/metagpt/provider/google_gemini_api.py +++ b/metagpt/provider/google_gemini_api.py @@ -2,6 +2,8 @@ # -*- coding: utf-8 -*- # @Desc : Google Gemini LLM from https://ai.google.dev/tutorials/python_quickstart +from typing import Optional, Union + import google.generativeai as genai from google.ai import generativelanguage as glm from google.generativeai.generative_models import GenerativeModel @@ -19,7 +21,7 @@ wait_random_exponential, ) -from metagpt.config import CONFIG, LLMProviderEnum +from metagpt.configs.llm_config import LLMConfig, LLMType from metagpt.logs import log_llm_stream, logger from metagpt.provider.base_llm import BaseLLM from metagpt.provider.llm_provider_registry import register_provider @@ -41,23 +43,24 @@ async def count_tokens_async(self, contents: content_types.ContentsType) -> glm. return await self._async_client.count_tokens(model=self.model_name, contents=contents) -@register_provider(LLMProviderEnum.GEMINI) +@register_provider(LLMType.GEMINI) class GeminiLLM(BaseLLM): """ Refs to `https://ai.google.dev/tutorials/python_quickstart` """ - def __init__(self): + def __init__(self, config: LLMConfig): self.use_system_prompt = False # google gemini has no system prompt when use api - self.__init_gemini(CONFIG) + self.__init_gemini(config) + self.config = config self.model = "gemini-pro" # so far only one model self.llm = GeminiGenerativeModel(model_name=self.model) - def __init_gemini(self, config: CONFIG): - genai.configure(api_key=config.gemini_api_key) + def __init_gemini(self, config: LLMConfig): + genai.configure(api_key=config.api_key) - def _user_msg(self, msg: str) -> dict[str, str]: + def _user_msg(self, msg: str, images: Optional[Union[str, list[str]]] = None) -> dict[str, str]: # Not to change BaseLLM default functions but update with Gemini's conversation format. # You should follow the format. return {"role": "user", "parts": [msg]} @@ -71,11 +74,11 @@ def _const_kwargs(self, messages: list[dict], stream: bool = False) -> dict: def _update_costs(self, usage: dict): """update each request's token cost""" - if CONFIG.calc_usage: + if self.config.calc_usage: try: prompt_tokens = int(usage.get("prompt_tokens", 0)) completion_tokens = int(usage.get("completion_tokens", 0)) - CONFIG.cost_manager.update_cost(prompt_tokens, completion_tokens, self.model) + self.cost_manager.update_cost(prompt_tokens, completion_tokens, self.model) except Exception as e: logger.error(f"google gemini updats costs failed! exp: {e}") @@ -108,7 +111,7 @@ async def _achat_completion(self, messages: list[dict]) -> "AsyncGenerateContent self._update_costs(usage) return resp - async def acompletion(self, messages: list[dict]) -> dict: + async def acompletion(self, messages: list[dict], timeout=3) -> dict: return await self._achat_completion(messages) async def _achat_completion_stream(self, messages: list[dict]) -> str: diff --git a/metagpt/provider/human_provider.py b/metagpt/provider/human_provider.py index 59d236a3a..fe000b3a6 100644 --- a/metagpt/provider/human_provider.py +++ b/metagpt/provider/human_provider.py @@ -5,6 +5,7 @@ """ from typing import Optional +from metagpt.configs.llm_config import LLMConfig from metagpt.logs import logger from metagpt.provider.base_llm import BaseLLM @@ -14,6 +15,9 @@ class HumanProvider(BaseLLM): This enables replacing LLM anywhere in the framework with a human, thus introducing human interaction """ + def __init__(self, config: LLMConfig): + pass + def ask(self, msg: str, timeout=3) -> str: logger.info("It's your turn, please type in your response. You may also refer to the context below") rsp = input(msg) diff --git a/metagpt/provider/llm_provider_registry.py b/metagpt/provider/llm_provider_registry.py index 2b3ef93a3..df89d36aa 100644 --- a/metagpt/provider/llm_provider_registry.py +++ b/metagpt/provider/llm_provider_registry.py @@ -5,7 +5,8 @@ @Author : alexanderwu @File : llm_provider_registry.py """ -from metagpt.config import LLMProviderEnum +from metagpt.configs.llm_config import LLMConfig, LLMType +from metagpt.provider.base_llm import BaseLLM class LLMProviderRegistry: @@ -15,13 +16,9 @@ def __init__(self): def register(self, key, provider_cls): self.providers[key] = provider_cls - def get_provider(self, enum: LLMProviderEnum): + def get_provider(self, enum: LLMType): """get provider instance according to the enum""" - return self.providers[enum]() - - -# Registry instance -LLM_REGISTRY = LLMProviderRegistry() + return self.providers[enum] def register_provider(key): @@ -32,3 +29,12 @@ def decorator(cls): return cls return decorator + + +def create_llm_instance(config: LLMConfig) -> BaseLLM: + """get the default llm provider""" + return LLM_REGISTRY.get_provider(config.api_type)(config) + + +# Registry instance +LLM_REGISTRY = LLMProviderRegistry() diff --git a/metagpt/provider/metagpt_api.py b/metagpt/provider/metagpt_api.py index 69aa7f305..4956746dc 100644 --- a/metagpt/provider/metagpt_api.py +++ b/metagpt/provider/metagpt_api.py @@ -5,12 +5,11 @@ @File : metagpt_api.py @Desc : MetaGPT LLM provider. """ -from metagpt.config import LLMProviderEnum +from metagpt.configs.llm_config import LLMType from metagpt.provider import OpenAILLM from metagpt.provider.llm_provider_registry import register_provider -@register_provider(LLMProviderEnum.METAGPT) +@register_provider(LLMType.METAGPT) class MetaGPTLLM(OpenAILLM): - def __init__(self): - super().__init__() + pass diff --git a/metagpt/provider/ollama_api.py b/metagpt/provider/ollama_api.py index 25086737f..c9103b018 100644 --- a/metagpt/provider/ollama_api.py +++ b/metagpt/provider/ollama_api.py @@ -13,48 +13,34 @@ wait_random_exponential, ) -from metagpt.config import CONFIG, LLMProviderEnum +from metagpt.configs.llm_config import LLMConfig, LLMType from metagpt.const import LLM_API_TIMEOUT from metagpt.logs import log_llm_stream, logger from metagpt.provider.base_llm import BaseLLM from metagpt.provider.general_api_requestor import GeneralAPIRequestor from metagpt.provider.llm_provider_registry import register_provider from metagpt.provider.openai_api import log_and_reraise -from metagpt.utils.cost_manager import CostManager - - -class OllamaCostManager(CostManager): - def update_cost(self, prompt_tokens, completion_tokens, model): - """ - Update the total cost, prompt tokens, and completion tokens. - """ - self.total_prompt_tokens += prompt_tokens - self.total_completion_tokens += completion_tokens - max_budget = CONFIG.max_budget if CONFIG.max_budget else CONFIG.cost_manager.max_budget - logger.info( - f"Max budget: ${max_budget:.3f} | " - f"prompt_tokens: {prompt_tokens}, completion_tokens: {completion_tokens}" - ) - CONFIG.total_cost = self.total_cost +from metagpt.utils.cost_manager import TokenCostManager -@register_provider(LLMProviderEnum.OLLAMA) +@register_provider(LLMType.OLLAMA) class OllamaLLM(BaseLLM): """ Refs to `https://github.com/jmorganca/ollama/blob/main/docs/api.md#generate-a-chat-completion` """ - def __init__(self): - self.__init_ollama(CONFIG) - self.client = GeneralAPIRequestor(base_url=CONFIG.ollama_api_base) + def __init__(self, config: LLMConfig): + self.__init_ollama(config) + self.client = GeneralAPIRequestor(base_url=config.base_url) + self.config = config self.suffix_url = "/chat" self.http_method = "post" self.use_system_prompt = False - self._cost_manager = OllamaCostManager() + self._cost_manager = TokenCostManager() - def __init_ollama(self, config: CONFIG): - assert config.ollama_api_base - self.model = config.ollama_api_model + def __init_ollama(self, config: LLMConfig): + assert config.base_url, "ollama base url is required!" + self.model = config.model def _const_kwargs(self, messages: list[dict], stream: bool = False) -> dict: kwargs = {"model": self.model, "messages": messages, "options": {"temperature": 0.3}, "stream": stream} @@ -62,7 +48,7 @@ def _const_kwargs(self, messages: list[dict], stream: bool = False) -> dict: def _update_costs(self, usage: dict): """update each request's token cost""" - if CONFIG.calc_usage: + if self.config.calc_usage: try: prompt_tokens = int(usage.get("prompt_tokens", 0)) completion_tokens = int(usage.get("completion_tokens", 0)) diff --git a/metagpt/provider/open_llm_api.py b/metagpt/provider/open_llm_api.py index b0c484f5a..a29b263a4 100644 --- a/metagpt/provider/open_llm_api.py +++ b/metagpt/provider/open_llm_api.py @@ -4,56 +4,27 @@ from openai.types import CompletionUsage -from metagpt.config import CONFIG, Config, LLMProviderEnum +from metagpt.configs.llm_config import LLMConfig, LLMType from metagpt.logs import logger from metagpt.provider.llm_provider_registry import register_provider from metagpt.provider.openai_api import OpenAILLM -from metagpt.utils.cost_manager import CostManager, Costs +from metagpt.utils.cost_manager import Costs, TokenCostManager from metagpt.utils.token_counter import count_message_tokens, count_string_tokens -class OpenLLMCostManager(CostManager): - """open llm model is self-host, it's free and without cost""" - - def update_cost(self, prompt_tokens, completion_tokens, model): - """ - Update the total cost, prompt tokens, and completion tokens. - - Args: - prompt_tokens (int): The number of tokens used in the prompt. - completion_tokens (int): The number of tokens used in the completion. - model (str): The model used for the API call. - """ - self.total_prompt_tokens += prompt_tokens - self.total_completion_tokens += completion_tokens - max_budget = CONFIG.max_budget if CONFIG.max_budget else CONFIG.cost_manager.max_budget - logger.info( - f"Max budget: ${max_budget:.3f} | reference " - f"prompt_tokens: {prompt_tokens}, completion_tokens: {completion_tokens}" - ) - - -@register_provider(LLMProviderEnum.OPEN_LLM) +@register_provider(LLMType.OPEN_LLM) class OpenLLM(OpenAILLM): - def __init__(self): - self.config: Config = CONFIG - self.__init_openllm() - self.auto_max_tokens = False - self._cost_manager = OpenLLMCostManager() - - def __init_openllm(self): - self.is_azure = False - self.rpm = int(self.config.get("RPM", 10)) - self._init_client() - self.model = self.config.open_llm_api_model # `self.model` should after `_make_client` to rewrite it + def __init__(self, config: LLMConfig): + super().__init__(config) + self._cost_manager = TokenCostManager() def _make_client_kwargs(self) -> dict: - kwargs = dict(api_key="sk-xxx", base_url=self.config.open_llm_api_base) + kwargs = dict(api_key="sk-xxx", base_url=self.config.base_url) return kwargs def _calc_usage(self, messages: list[dict], rsp: str) -> CompletionUsage: usage = CompletionUsage(prompt_tokens=0, completion_tokens=0, total_tokens=0) - if not CONFIG.calc_usage: + if not self.config.calc_usage: return usage try: diff --git a/metagpt/provider/openai_api.py b/metagpt/provider/openai_api.py index 747e36480..63e68c9bd 100644 --- a/metagpt/provider/openai_api.py +++ b/metagpt/provider/openai_api.py @@ -3,14 +3,12 @@ @Time : 2023/5/5 23:08 @Author : alexanderwu @File : openai.py -@Modified By: mashenquan, 2023/8/20. Remove global configuration `CONFIG`, enable configuration support for isolation; - Change cost control from global to company level. @Modified By: mashenquan, 2023/11/21. Fix bug: ReadTimeout. @Modified By: mashenquan, 2023/12/1. Fix bug: Unclosed connection caused by openai 0.x. """ import json -from typing import AsyncIterator, Union +from typing import AsyncIterator, Optional, Union from openai import APIConnectionError, AsyncOpenAI, AsyncStream from openai._base_client import AsyncHttpxClientWrapper @@ -24,13 +22,14 @@ wait_random_exponential, ) -from metagpt.config import CONFIG, Config, LLMProviderEnum +from metagpt.configs.llm_config import LLMConfig, LLMType from metagpt.logs import log_llm_stream, logger from metagpt.provider.base_llm import BaseLLM -from metagpt.provider.constant import GENERAL_FUNCTION_SCHEMA, GENERAL_TOOL_CHOICE +from metagpt.provider.constant import GENERAL_FUNCTION_SCHEMA from metagpt.provider.llm_provider_registry import register_provider from metagpt.schema import Message -from metagpt.utils.cost_manager import Costs +from metagpt.utils.common import CodeParser, decode_image +from metagpt.utils.cost_manager import CostManager, Costs from metagpt.utils.exceptions import handle_exception from metagpt.utils.token_counter import ( count_message_tokens, @@ -50,18 +49,19 @@ def log_and_reraise(retry_state): raise retry_state.outcome.exception() -@register_provider(LLMProviderEnum.OPENAI) +@register_provider(LLMType.OPENAI) class OpenAILLM(BaseLLM): """Check https://platform.openai.com/examples for examples""" - def __init__(self): - self.config: Config = CONFIG - self._init_openai() + def __init__(self, config: LLMConfig): + self.config = config + self._init_model() self._init_client() self.auto_max_tokens = False + self.cost_manager: Optional[CostManager] = None - def _init_openai(self): - self.model = self.config.OPENAI_API_MODEL # Used in _calc_usage & _cons_kwargs + def _init_model(self): + self.model = self.config.model # Used in _calc_usage & _cons_kwargs def _init_client(self): """https://github.com/openai/openai-python#async-usage""" @@ -69,7 +69,7 @@ def _init_client(self): self.aclient = AsyncOpenAI(**kwargs) def _make_client_kwargs(self) -> dict: - kwargs = {"api_key": self.config.openai_api_key, "base_url": self.config.openai_base_url} + kwargs = {"api_key": self.config.api_key, "base_url": self.config.base_url} # to use proxy, openai v1 needs http_client if proxy_params := self._get_proxy_params(): @@ -79,10 +79,10 @@ def _make_client_kwargs(self) -> dict: def _get_proxy_params(self) -> dict: params = {} - if self.config.openai_proxy: - params = {"proxies": self.config.openai_proxy} - if self.config.openai_base_url: - params["base_url"] = self.config.openai_base_url + if self.config.proxy: + params = {"proxies": self.config.proxy} + if self.config.base_url: + params["base_url"] = self.config.base_url return params @@ -100,10 +100,10 @@ def _cons_kwargs(self, messages: list[dict], timeout=3, **extra_kwargs) -> dict: "messages": messages, "max_tokens": self._get_max_tokens(messages), "n": 1, - "stop": None, + # "stop": None, # default it's None and gpt4-v can't have this one "temperature": 0.3, "model": self.model, - "timeout": max(CONFIG.timeout, timeout), + "timeout": max(self.config.timeout, timeout), } if extra_kwargs: kwargs.update(extra_kwargs) @@ -147,37 +147,41 @@ async def acompletion_text(self, messages: list[dict], stream=False, timeout=3) def _func_configs(self, messages: list[dict], timeout=3, **kwargs) -> dict: """Note: Keep kwargs consistent with https://platform.openai.com/docs/api-reference/chat/create""" if "tools" not in kwargs: - configs = { - "tools": [{"type": "function", "function": GENERAL_FUNCTION_SCHEMA}], - "tool_choice": GENERAL_TOOL_CHOICE, - } + configs = {"tools": [{"type": "function", "function": GENERAL_FUNCTION_SCHEMA}]} kwargs.update(configs) return self._cons_kwargs(messages=messages, timeout=timeout, **kwargs) + def _process_message(self, messages: Union[str, Message, list[dict], list[Message], list[str]]) -> list[dict]: + """convert messages to list[dict].""" + # 全部转成list + if not isinstance(messages, list): + messages = [messages] + + # 转成list[dict] + processed_messages = [] + for msg in messages: + if isinstance(msg, str): + processed_messages.append({"role": "user", "content": msg}) + elif isinstance(msg, dict): + assert set(msg.keys()) == set(["role", "content"]) + processed_messages.append(msg) + elif isinstance(msg, Message): + processed_messages.append(msg.to_dict()) + else: + raise ValueError( + f"Only support message type are: str, Message, dict, but got {type(messages).__name__}!" + ) + return processed_messages + async def _achat_completion_function(self, messages: list[dict], timeout=3, **chat_configs) -> ChatCompletion: + messages = self._process_message(messages) kwargs = self._func_configs(messages=messages, timeout=timeout, **chat_configs) rsp: ChatCompletion = await self.aclient.chat.completions.create(**kwargs) self._update_costs(rsp.usage) return rsp - def _process_message(self, messages: Union[str, Message, list[dict], list[Message], list[str]]) -> list[dict]: - """convert messages to list[dict].""" - if isinstance(messages, list): - messages = [Message(content=msg) if isinstance(msg, str) else msg for msg in messages] - return [msg if isinstance(msg, dict) else msg.to_dict() for msg in messages] - - if isinstance(messages, Message): - messages = [messages.to_dict()] - elif isinstance(messages, str): - messages = [{"role": "user", "content": messages}] - else: - raise ValueError( - f"Only support messages type are: str, Message, list[dict], but got {type(messages).__name__}!" - ) - return messages - - async def aask_code(self, messages: Union[str, Message, list[dict]], **kwargs) -> dict: + async def aask_code(self, messages: list[dict], **kwargs) -> dict: """Use function of tools to ask a code. Note: Keep kwargs consistent with https://platform.openai.com/docs/api-reference/chat/create @@ -187,18 +191,37 @@ async def aask_code(self, messages: Union[str, Message, list[dict]], **kwargs) - >>> rsp = await llm.aask_code(msg) # -> {'language': 'python', 'code': "print('Hello, World!')"} """ - messages = self._process_message(messages) rsp = await self._achat_completion_function(messages, **kwargs) return self.get_choice_function_arguments(rsp) - @handle_exception + # @handle_exception def get_choice_function_arguments(self, rsp: ChatCompletion) -> dict: """Required to provide the first function arguments of choice. + :param dict rsp: same as in self.get_choice_function(rsp) :return dict: return the first function arguments of choice, for example, {'language': 'python', 'code': "print('Hello, World!')"} """ - return json.loads(rsp.choices[0].message.tool_calls[0].function.arguments) + message = rsp.choices[0].message + if ( + message.tool_calls is not None + and message.tool_calls[0].function is not None + and message.tool_calls[0].function.arguments is not None + ): + # reponse is code + return json.loads(message.tool_calls[0].function.arguments, strict=False) + elif message.tool_calls is None and message.content is not None: + # reponse is code, fix openai tools_call respond bug, + # The response content is `code``, but it appears in the content instead of the arguments. + code_formats = "```" + if message.content.startswith(code_formats) and message.content.endswith(code_formats): + code = CodeParser.parse_code(None, message.content) + return {"language": "python", "code": code} + # reponse is message + return {"language": "markdown", "code": self.get_choice_text(rsp)} + else: + logger.error(f"Failed to parse \n {rsp}\n") + raise Exception(f"Failed to parse \n {rsp}\n") def get_choice_text(self, rsp: ChatCompletion) -> str: """Required to provide the first text of choice""" @@ -206,7 +229,7 @@ def get_choice_text(self, rsp: ChatCompletion) -> str: def _calc_usage(self, messages: list[dict], rsp: str) -> CompletionUsage: usage = CompletionUsage(prompt_tokens=0, completion_tokens=0, total_tokens=0) - if not CONFIG.calc_usage: + if not self.config.calc_usage: return usage try: @@ -219,18 +242,49 @@ def _calc_usage(self, messages: list[dict], rsp: str) -> CompletionUsage: @handle_exception def _update_costs(self, usage: CompletionUsage): - if CONFIG.calc_usage and usage: - CONFIG.cost_manager.update_cost(usage.prompt_tokens, usage.completion_tokens, self.model) + if self.config.calc_usage and usage and self.cost_manager: + self.cost_manager.update_cost(usage.prompt_tokens, usage.completion_tokens, self.model) def get_costs(self) -> Costs: - return CONFIG.cost_manager.get_costs() + if not self.cost_manager: + return Costs(0, 0, 0, 0) + return self.cost_manager.get_costs() def _get_max_tokens(self, messages: list[dict]): if not self.auto_max_tokens: - return CONFIG.max_tokens_rsp - return get_max_completion_tokens(messages, self.model, CONFIG.max_tokens_rsp) + return self.config.max_token + return get_max_completion_tokens(messages, self.model, self.config.max_tokens) @handle_exception async def amoderation(self, content: Union[str, list[str]]): """Moderate content.""" return await self.aclient.moderations.create(input=content) + + async def atext_to_speech(self, **kwargs): + """text to speech""" + return await self.aclient.audio.speech.create(**kwargs) + + async def aspeech_to_text(self, **kwargs): + """speech to text""" + return await self.aclient.audio.transcriptions.create(**kwargs) + + async def gen_image( + self, + prompt: str, + size: str = "1024x1024", + quality: str = "standard", + model: str = None, + resp_format: str = "url", + ) -> list["Image"]: + """image generate""" + assert resp_format in ["url", "b64_json"] + if not model: + model = self.model + res = await self.aclient.images.generate( + model=model, prompt=prompt, size=size, quality=quality, n=1, response_format=resp_format + ) + imgs = [] + for item in res.data: + img_url_or_b64 = item.url if resp_format == "url" else item.b64_json + imgs.append(decode_image(img_url_or_b64)) + return imgs diff --git a/metagpt/provider/spark_api.py b/metagpt/provider/spark_api.py index ce889529a..5e89c26d5 100644 --- a/metagpt/provider/spark_api.py +++ b/metagpt/provider/spark_api.py @@ -16,29 +16,30 @@ import websocket # 使用websocket_client -from metagpt.config import CONFIG, LLMProviderEnum +from metagpt.configs.llm_config import LLMConfig, LLMType from metagpt.logs import logger from metagpt.provider.base_llm import BaseLLM from metagpt.provider.llm_provider_registry import register_provider -@register_provider(LLMProviderEnum.SPARK) +@register_provider(LLMType.SPARK) class SparkLLM(BaseLLM): - def __init__(self): - logger.warning("当前方法无法支持异步运行。当你使用acompletion时,并不能并行访问。") + def __init__(self, config: LLMConfig): + self.config = config + logger.warning("SparkLLM:当前方法无法支持异步运行。当你使用acompletion时,并不能并行访问。") def get_choice_text(self, rsp: dict) -> str: return rsp["payload"]["choices"]["text"][-1]["content"] async def acompletion_text(self, messages: list[dict], stream=False, timeout: int = 3) -> str: # 不支持 - logger.error("该功能禁用。") - w = GetMessageFromWeb(messages) + # logger.warning("当前方法无法支持异步运行。当你使用acompletion时,并不能并行访问。") + w = GetMessageFromWeb(messages, self.config) return w.run() async def acompletion(self, messages: list[dict], timeout=3): # 不支持异步 - w = GetMessageFromWeb(messages) + w = GetMessageFromWeb(messages, self.config) return w.run() @@ -89,14 +90,14 @@ def create_url(self): # 此处打印出建立连接时候的url,参考本demo的时候可取消上方打印的注释,比对相同参数时生成的url与自己代码生成的url是否一致 return url - def __init__(self, text): + def __init__(self, text, config: LLMConfig): self.text = text self.ret = "" - self.spark_appid = CONFIG.spark_appid - self.spark_api_secret = CONFIG.spark_api_secret - self.spark_api_key = CONFIG.spark_api_key - self.domain = CONFIG.domain - self.spark_url = CONFIG.spark_url + self.spark_appid = config.app_id + self.spark_api_secret = config.api_secret + self.spark_api_key = config.api_key + self.domain = config.domain + self.spark_url = config.base_url def on_message(self, ws, message): data = json.loads(message) diff --git a/metagpt/provider/zhipuai_api.py b/metagpt/provider/zhipuai_api.py index a6f77477a..9108a1fba 100644 --- a/metagpt/provider/zhipuai_api.py +++ b/metagpt/provider/zhipuai_api.py @@ -5,6 +5,7 @@ from enum import Enum import openai +import zhipuai from requests import ConnectionError from tenacity import ( after_log, @@ -13,9 +14,8 @@ stop_after_attempt, wait_random_exponential, ) -from zhipuai.types.chat.chat_completion import Completion -from metagpt.config import CONFIG, LLMProviderEnum +from metagpt.configs.llm_config import LLMConfig, LLMType from metagpt.logs import log_llm_stream, logger from metagpt.provider.base_llm import BaseLLM from metagpt.provider.llm_provider_registry import register_provider @@ -30,26 +30,28 @@ class ZhiPuEvent(Enum): FINISH = "finish" -@register_provider(LLMProviderEnum.ZHIPUAI) +@register_provider(LLMType.ZHIPUAI) class ZhiPuAILLM(BaseLLM): """ Refs to `https://open.bigmodel.cn/dev/api#chatglm_turbo` From now, support glm-3-turbo、glm-4, and also system_prompt. """ - def __init__(self): - self.__init_zhipuai(CONFIG) - self.llm = ZhiPuModelAPI(api_key=self.api_key) + def __init__(self, config: LLMConfig): + self.__init_zhipuai(config) + self.llm = ZhiPuModelAPI + self.model = "chatglm_turbo" # so far only one model, just use it + self.use_system_prompt: bool = False # zhipuai has no system prompt when use api + self.config = config - def __init_zhipuai(self, config: CONFIG): - assert config.zhipuai_api_key - self.api_key = config.zhipuai_api_key - self.model = config.zhipuai_api_model # so far, it support glm-3-turbo、glm-4 + def __init_zhipuai(self, config: LLMConfig): + assert config.api_key + zhipuai.api_key = config.api_key # due to use openai sdk, set the api_key but it will't be used. # openai.api_key = zhipuai.api_key # due to use openai sdk, set the api_key but it will't be used. - if config.openai_proxy: + if config.proxy: # FIXME: openai v1.x sdk has no proxy support - openai.proxy = config.openai_proxy + openai.proxy = config.proxy def _const_kwargs(self, messages: list[dict], stream: bool = False) -> dict: kwargs = {"model": self.model, "messages": messages, "stream": stream, "temperature": 0.3} @@ -57,16 +59,16 @@ def _const_kwargs(self, messages: list[dict], stream: bool = False) -> dict: def _update_costs(self, usage: dict): """update each request's token cost""" - if CONFIG.calc_usage: + if self.config.calc_usage: try: prompt_tokens = int(usage.get("prompt_tokens", 0)) completion_tokens = int(usage.get("completion_tokens", 0)) - CONFIG.cost_manager.update_cost(prompt_tokens, completion_tokens, self.model) + self.config.cost_manager.update_cost(prompt_tokens, completion_tokens, self.model) except Exception as e: logger.error(f"zhipuai updats costs failed! exp: {e}") def completion(self, messages: list[dict], timeout=3) -> dict: - resp: Completion = self.llm.chat.completions.create(**self._const_kwargs(messages)) + resp = self.llm.chat.completions.create(**self._const_kwargs(messages)) usage = resp.usage.model_dump() self._update_costs(usage) return resp.model_dump() diff --git a/metagpt/roles/architect.py b/metagpt/roles/architect.py index c6ceaccb7..166f8cfd0 100644 --- a/metagpt/roles/architect.py +++ b/metagpt/roles/architect.py @@ -33,7 +33,7 @@ class Architect(Role): def __init__(self, **kwargs) -> None: super().__init__(**kwargs) # Initialize actions specific to the Architect role - self._init_actions([WriteDesign]) + self.set_actions([WriteDesign]) # Set events or actions the Architect should watch or be aware of self._watch({WritePRD}) diff --git a/metagpt/roles/assistant.py b/metagpt/roles/assistant.py index 227578a63..2774bd9b6 100644 --- a/metagpt/roles/assistant.py +++ b/metagpt/roles/assistant.py @@ -22,7 +22,6 @@ from metagpt.actions.skill_action import ArgumentsParingAction, SkillAction from metagpt.actions.talk_action import TalkAction -from metagpt.config import CONFIG from metagpt.learn.skill_loader import SkillsDeclaration from metagpt.logs import logger from metagpt.memory.brain_memory import BrainMemory @@ -48,7 +47,8 @@ class Assistant(Role): def __init__(self, **kwargs): super().__init__(**kwargs) - self.constraints = self.constraints.format(language=kwargs.get("language") or CONFIG.language or "Chinese") + language = kwargs.get("language") or self.context.kwargs.language + self.constraints = self.constraints.format(language=language) async def think(self) -> bool: """Everything will be done part by part.""" @@ -56,16 +56,16 @@ async def think(self) -> bool: if not last_talk: return False if not self.skills: - skill_path = Path(CONFIG.SKILL_PATH) if CONFIG.SKILL_PATH else None + skill_path = Path(self.context.kwargs.SKILL_PATH) if self.context.kwargs.SKILL_PATH else None self.skills = await SkillsDeclaration.load(skill_yaml_file_name=skill_path) prompt = "" - skills = self.skills.get_skill_list() + skills = self.skills.get_skill_list(context=self.context) for desc, name in skills.items(): prompt += f"If the text explicitly want you to {desc}, return `[SKILL]: {name}` brief and clear. For instance: [SKILL]: {name}\n" prompt += 'Otherwise, return `[TALK]: {talk}` brief and clear. For instance: if {talk} is "xxxx" return [TALK]: xxxx\n\n' prompt += f"Now what specific action is explicitly mentioned in the text: {last_talk}\n" - rsp = await self.llm.aask(prompt, []) + rsp = await self.llm.aask(prompt, ["You are an action classifier"]) logger.info(f"THINK: {prompt}\n, THINK RESULT: {rsp}\n") return await self._plan(rsp, last_talk=last_talk) @@ -97,8 +97,8 @@ async def _plan(self, rsp: str, **kwargs) -> bool: async def talk_handler(self, text, **kwargs) -> bool: history = self.memory.history_text text = kwargs.get("last_talk") or text - self.rc.todo = TalkAction( - context=text, knowledge=self.memory.get_knowledge(), history_summary=history, llm=self.llm, **kwargs + self.set_todo( + TalkAction(i_context=text, knowledge=self.memory.get_knowledge(), history_summary=history, llm=self.llm) ) return True @@ -108,11 +108,11 @@ async def skill_handler(self, text, **kwargs) -> bool: if not skill: logger.info(f"skill not found: {text}") return await self.talk_handler(text=last_talk, **kwargs) - action = ArgumentsParingAction(skill=skill, llm=self.llm, ask=last_talk, **kwargs) + action = ArgumentsParingAction(skill=skill, llm=self.llm, ask=last_talk) await action.run(**kwargs) if action.args is None: return await self.talk_handler(text=last_talk, **kwargs) - self.rc.todo = SkillAction(skill=skill, args=action.args, llm=self.llm, name=skill.name, desc=skill.description) + self.set_todo(SkillAction(skill=skill, args=action.args, llm=self.llm, name=skill.name, desc=skill.description)) return True async def refine_memory(self) -> str: diff --git a/metagpt/roles/ci/code_interpreter.py b/metagpt/roles/ci/code_interpreter.py new file mode 100644 index 000000000..796abba04 --- /dev/null +++ b/metagpt/roles/ci/code_interpreter.py @@ -0,0 +1,89 @@ +from __future__ import annotations + +from pydantic import Field + +from metagpt.actions.ci.ask_review import ReviewConst +from metagpt.actions.ci.execute_nb_code import ExecuteNbCode +from metagpt.actions.ci.write_analysis_code import ( + WriteCodeWithoutTools, + WriteCodeWithTools, +) +from metagpt.logs import logger +from metagpt.roles import Role +from metagpt.schema import Message, Task, TaskResult + + +class CodeInterpreter(Role): + name: str = "Charlie" + profile: str = "CodeInterpreter" + auto_run: bool = True + use_tools: bool = False + execute_code: ExecuteNbCode = Field(default_factory=ExecuteNbCode, exclude=True) + tools: list[str] = [] + + def __init__( + self, + auto_run=True, + use_tools=False, + tools=[], + **kwargs, + ): + super().__init__(auto_run=auto_run, use_tools=use_tools, tools=tools, **kwargs) + self._set_react_mode(react_mode="plan_and_act", auto_run=auto_run, use_tools=use_tools) + if use_tools and tools: + from metagpt.tools.tool_registry import ( + validate_tool_names, # import upon use + ) + + self.tools = validate_tool_names(tools) + logger.info(f"will only use {self.tools} as tools") + + @property + def working_memory(self): + return self.rc.working_memory + + async def _act_on_task(self, current_task: Task) -> TaskResult: + code, result, is_success = await self._write_and_exec_code() + task_result = TaskResult(code=code, result=result, is_success=is_success) + return task_result + + async def _write_and_exec_code(self, max_retry: int = 3): + counter = 0 + success = False + + while not success and counter < max_retry: + ### write code ### + code, cause_by = await self._write_code() + + self.working_memory.add(Message(content=code["code"], role="assistant", cause_by=cause_by)) + + ### execute code ### + result, success = await self.execute_code.run(**code) + print(result) + + self.working_memory.add(Message(content=result, role="user", cause_by=ExecuteNbCode)) + + ### process execution result ### + counter += 1 + + if not success and counter >= max_retry: + logger.info("coding failed!") + review, _ = await self.planner.ask_review(auto_run=False, trigger=ReviewConst.CODE_REVIEW_TRIGGER) + if ReviewConst.CHANGE_WORDS[0] in review: + counter = 0 # redo the task again with help of human suggestions + + py_code = ( + code["code"] if code.get("language") == "python" else "" + ) # use python code as final code; for markdown, return the rendered result instead of the code itself + + return py_code, result, success + + async def _write_code(self): + todo = WriteCodeWithoutTools() if not self.use_tools else WriteCodeWithTools(selected_tools=self.tools) + logger.info(f"ready to {todo.name}") + + context = self.planner.get_useful_memories() + # print(*context, sep="\n***\n") + code = await todo.run(context=context, plan=self.planner.plan, temperature=0.0) + + return code, todo diff --git a/metagpt/roles/ci/ml_engineer.py b/metagpt/roles/ci/ml_engineer.py new file mode 100644 index 000000000..f8bcb2c89 --- /dev/null +++ b/metagpt/roles/ci/ml_engineer.py @@ -0,0 +1,64 @@ +from metagpt.actions.ci.debug_code import DebugCode +from metagpt.actions.ci.execute_nb_code import ExecuteNbCode +from metagpt.actions.ci.ml_action import UpdateDataColumns, WriteCodeWithToolsML +from metagpt.logs import logger +from metagpt.roles.ci.code_interpreter import CodeInterpreter +from metagpt.tools.tool_type import ToolType +from metagpt.utils.common import any_to_str + + +class MLEngineer(CodeInterpreter): + name: str = "Mark" + profile: str = "MLEngineer" + debug_context: list = [] + latest_code: str = "" + + async def _write_code(self): + if not self.use_tools: + return await super()._write_code() + + # In a trial and errors settings, check whether this is our first attempt to tackle the task. If there is no code execution before, then it is. + is_first_trial = any_to_str(ExecuteNbCode) not in [msg.cause_by for msg in self.working_memory.get()] + + if is_first_trial: + # For the first trial, write task code from scratch + column_info = await self._update_data_columns() + + logger.info("Write code with tools") + tool_context, code = await WriteCodeWithToolsML(selected_tools=self.tools).run( + context=[], # context assembled inside the Action + plan=self.planner.plan, + column_info=column_info, + ) + self.debug_context = tool_context + cause_by = WriteCodeWithToolsML + + else: + # Previous trials resulted in error, debug and rewrite the code + logger.warning("We got a bug, now start to debug...") + code = await DebugCode().run( + code=self.latest_code, + runtime_result=self.working_memory.get(), + context=self.debug_context, + ) + logger.info(f"new code \n{code}") + cause_by = DebugCode + + self.latest_code = code["code"] + + return code, cause_by + + async def _update_data_columns(self): + current_task = self.planner.plan.current_task + if current_task.task_type not in [ + ToolType.DATA_PREPROCESS.type_name, + ToolType.FEATURE_ENGINEERING.type_name, + ToolType.MODEL_TRAIN.type_name, + ]: + return "" + logger.info("Check columns in updated data") + code = await UpdateDataColumns().run(self.planner.plan) + success = False + result, success = await self.execute_code.run(**code) + print(result) + return result if success else "" diff --git a/metagpt/roles/engineer.py b/metagpt/roles/engineer.py index b1588f9ef..40ade2110 100644 --- a/metagpt/roles/engineer.py +++ b/metagpt/roles/engineer.py @@ -23,22 +23,16 @@ import os from collections import defaultdict from pathlib import Path -from typing import Literal, Set +from typing import Set from metagpt.actions import Action, WriteCode, WriteCodeReview, WriteTasks from metagpt.actions.fix_bug import FixBug from metagpt.actions.project_management_an import REFINED_TASK_LIST, TASK_LIST from metagpt.actions.summarize_code import SummarizeCode from metagpt.actions.write_code_plan_and_change_an import WriteCodePlanAndChange -from metagpt.config import CONFIG from metagpt.const import ( CODE_PLAN_AND_CHANGE_FILE_REPO, CODE_PLAN_AND_CHANGE_FILENAME, - CODE_PLAN_AND_CHANGE_PDF_FILE_REPO, - CODE_SUMMARIES_FILE_REPO, - CODE_SUMMARIES_PDF_FILE_REPO, - DOCS_FILE_REPO, - PRDS_FILE_REPO, REQUIREMENT_FILENAME, SYSTEM_DESIGN_FILE_REPO, TASK_FILE_REPO, @@ -54,7 +48,6 @@ Message, ) from metagpt.utils.common import any_to_name, any_to_str, any_to_str_set -from metagpt.utils.file_repository import FileRepository IS_PASS_PROMPT = """ {context} @@ -91,11 +84,12 @@ class Engineer(Role): code_todos: list = [] summarize_todos: list = [] next_todo_action: str = "" + n_summarize: int = 0 def __init__(self, **kwargs) -> None: super().__init__(**kwargs) - self._init_actions([WriteCode]) + self.set_actions([WriteCode]) self._watch([WriteTasks, SummarizeCode, WriteCode, WriteCodeReview, FixBug, WriteCodePlanAndChange]) self.code_todos = [] self.summarize_todos = [] @@ -106,9 +100,8 @@ def _parse_tasks(task_msg: Document) -> list[str]: m = json.loads(task_msg.content) return m.get(TASK_LIST.key) or m.get(REFINED_TASK_LIST.key) - async def _act_sp_with_cr(self, review=False, mode: Literal["normal", "incremental"] = "normal") -> Set[str]: + async def _act_sp_with_cr(self, review=False) -> Set[str]: changed_files = set() - src_file_repo = CONFIG.git_repo.new_file_repository(CONFIG.src_workspace) for todo in self.code_todos: """ # Select essential information from the historical data to reduce the length of the prompt (summarized from human experience): @@ -120,15 +113,15 @@ async def _act_sp_with_cr(self, review=False, mode: Literal["normal", "increment coding_context = await todo.run() # Code review if review: - action = WriteCodeReview(context=coding_context, llm=self.llm) - self._init_action_system_message(action) + action = WriteCodeReview(i_context=coding_context, context=self.context, llm=self.llm) + self._init_action(action) coding_context = await action.run() dependencies = {coding_context.design_doc.root_relative_path, coding_context.task_doc.root_relative_path} - if mode == "incremental": + if self.config.inc: dependencies.add(os.path.join(CODE_PLAN_AND_CHANGE_FILE_REPO, CODE_PLAN_AND_CHANGE_FILENAME)) - await src_file_repo.save( - coding_context.filename, + await self.project_repo.srcs.save( + filename=coding_context.filename, dependencies=dependencies, content=coding_context.code_doc.content, ) @@ -171,34 +164,32 @@ async def _act_write_code(self): ) async def _act_summarize(self): - code_summaries_file_repo = CONFIG.git_repo.new_file_repository(CODE_SUMMARIES_FILE_REPO) - code_summaries_pdf_file_repo = CONFIG.git_repo.new_file_repository(CODE_SUMMARIES_PDF_FILE_REPO) tasks = [] - src_relative_path = CONFIG.src_workspace.relative_to(CONFIG.git_repo.workdir) for todo in self.summarize_todos: summary = await todo.run() - summary_filename = Path(todo.context.design_filename).with_suffix(".md").name - dependencies = {todo.context.design_filename, todo.context.task_filename} - for filename in todo.context.codes_filenames: - rpath = src_relative_path / filename + summary_filename = Path(todo.i_context.design_filename).with_suffix(".md").name + dependencies = {todo.i_context.design_filename, todo.i_context.task_filename} + for filename in todo.i_context.codes_filenames: + rpath = self.project_repo.src_relative_path / filename dependencies.add(str(rpath)) - await code_summaries_pdf_file_repo.save( + await self.project_repo.resources.code_summary.save( filename=summary_filename, content=summary, dependencies=dependencies ) is_pass, reason = await self._is_pass(summary) if not is_pass: - todo.context.reason = reason - tasks.append(todo.context.dict()) - await code_summaries_file_repo.save( - filename=Path(todo.context.design_filename).name, - content=todo.context.model_dump_json(), + todo.i_context.reason = reason + tasks.append(todo.i_context.model_dump()) + + await self.project_repo.docs.code_summary.save( + filename=Path(todo.i_context.design_filename).name, + content=todo.i_context.model_dump_json(), dependencies=dependencies, ) else: - await code_summaries_file_repo.delete(filename=Path(todo.context.design_filename).name) + await self.project_repo.docs.code_summary.delete(filename=Path(todo.i_context.design_filename).name) - logger.info(f"--max-auto-summarize-code={CONFIG.max_auto_summarize_code}") - if not tasks or CONFIG.max_auto_summarize_code == 0: + logger.info(f"--max-auto-summarize-code={self.config.max_auto_summarize_code}") + if not tasks or self.config.max_auto_summarize_code == 0: return Message( content="", role=self.profile, @@ -208,7 +199,7 @@ async def _act_summarize(self): ) # The maximum number of times the 'SummarizeCode' action is automatically invoked, with -1 indicating unlimited. # This parameter is used for debugging the workflow. - CONFIG.max_auto_summarize_code -= 1 if CONFIG.max_auto_summarize_code > 0 else 0 + self.n_summarize += 1 if self.config.max_auto_summarize_code > self.n_summarize else 0 return Message( content=json.dumps(tasks), role=self.profile, cause_by=SummarizeCode, send_to=self, sent_from=self ) @@ -216,24 +207,19 @@ async def _act_summarize(self): async def _act_code_plan_and_change(self): """Write code plan and change that guides subsequent WriteCode and WriteCodeReview""" logger.info("Writing code plan and change..") - code_plan_and_change_file_repo = CONFIG.git_repo.new_file_repository(CODE_PLAN_AND_CHANGE_FILE_REPO) - code_plan_and_change_pdf_file_repo = CONFIG.git_repo.new_file_repository(CODE_PLAN_AND_CHANGE_PDF_FILE_REPO) - node = await self.rc.todo.run() code_plan_and_change = node.instruct_content.model_dump_json() dependencies = { - self.rc.todo.context.requirement_doc.filename, - self.rc.todo.context.prd_docs[0].filename, - self.rc.todo.context.design_docs[0].filename, - self.rc.todo.context.tasks_docs[0].filename, + REQUIREMENT_FILENAME, + self.rc.todo.i_context.prd_filename, + self.rc.todo.i_context.design_filename, + self.rc.todo.i_context.task_filename, } - - code_plan_and_change_filename = os.path.join(CODE_PLAN_AND_CHANGE_FILE_REPO, CODE_PLAN_AND_CHANGE_FILENAME) - await code_plan_and_change_file_repo.save( - filename=code_plan_and_change_filename, content=code_plan_and_change, dependencies=dependencies + await self.project_repo.docs.code_plan_and_change.save( + filename=self.rc.todo.i_context.filename, content=code_plan_and_change, dependencies=dependencies ) - await code_plan_and_change_pdf_file_repo.save( - filename=Path(code_plan_and_change_filename).with_suffix(".md").name, + await self.project_repo.resources.code_plan_and_change.save( + filename=Path(self.rc.todo.i_context.filename).with_suffix(".md").name, content=node.content, dependencies=dependencies, ) @@ -254,15 +240,15 @@ async def _is_pass(self, summary) -> (str, str): return False, rsp async def _think(self) -> Action | None: - if not CONFIG.src_workspace: - CONFIG.src_workspace = CONFIG.git_repo.workdir / CONFIG.git_repo.workdir.name + if not self.src_workspace: + self.src_workspace = self.git_repo.workdir / self.git_repo.workdir.name write_plan_and_change_filters = any_to_str_set([WriteTasks]) write_code_filters = any_to_str_set([WriteTasks, WriteCodePlanAndChange, SummarizeCode, FixBug]) summarize_code_filters = any_to_str_set([WriteCode, WriteCodeReview]) if not self.rc.news: return None msg = self.rc.news[0] - if CONFIG.inc and msg.cause_by in write_plan_and_change_filters: + if self.config.inc and msg.cause_by in write_plan_and_change_filters: logger.debug(f"TODO WriteCodePlanAndChange:{msg.model_dump_json()}") await self._new_code_plan_and_change_action() return self.rc.todo @@ -276,60 +262,54 @@ async def _think(self) -> Action | None: return self.rc.todo return None - @staticmethod - async def _new_coding_context( - filename, src_file_repo, task_file_repo, design_file_repo, dependency - ) -> CodingContext: - old_code_doc = await src_file_repo.get(filename) + async def _new_coding_context(self, filename, dependency) -> CodingContext: + old_code_doc = await self.project_repo.srcs.get(filename) if not old_code_doc: - old_code_doc = Document(root_path=str(src_file_repo.root_path), filename=filename, content="") + old_code_doc = Document(root_path=str(self.project_repo.src_relative_path), filename=filename, content="") dependencies = {Path(i) for i in await dependency.get(old_code_doc.root_relative_path)} task_doc = None design_doc = None for i in dependencies: if str(i.parent) == TASK_FILE_REPO: - task_doc = await task_file_repo.get(i.name) + task_doc = await self.project_repo.docs.task.get(i.name) elif str(i.parent) == SYSTEM_DESIGN_FILE_REPO: - design_doc = await design_file_repo.get(i.name) + design_doc = await self.project_repo.docs.system_design.get(i.name) if not task_doc or not design_doc: logger.error(f'Detected source code "{filename}" from an unknown origin.') raise ValueError(f'Detected source code "{filename}" from an unknown origin.') context = CodingContext(filename=filename, design_doc=design_doc, task_doc=task_doc, code_doc=old_code_doc) return context - @staticmethod - async def _new_coding_doc(filename, src_file_repo, task_file_repo, design_file_repo, dependency): - context = await Engineer._new_coding_context( - filename, src_file_repo, task_file_repo, design_file_repo, dependency - ) + async def _new_coding_doc(self, filename, dependency): + context = await self._new_coding_context(filename, dependency) coding_doc = Document( - root_path=str(src_file_repo.root_path), filename=filename, content=context.model_dump_json() + root_path=str(self.project_repo.src_relative_path), filename=filename, content=context.model_dump_json() ) return coding_doc async def _new_code_actions(self, bug_fix=False): # Prepare file repos - src_file_repo = CONFIG.git_repo.new_file_repository(CONFIG.src_workspace) - changed_src_files = src_file_repo.all_files if bug_fix else src_file_repo.changed_files - task_file_repo = CONFIG.git_repo.new_file_repository(TASK_FILE_REPO) - changed_task_files = task_file_repo.changed_files - design_file_repo = CONFIG.git_repo.new_file_repository(SYSTEM_DESIGN_FILE_REPO) - + changed_src_files = self.project_repo.srcs.all_files if bug_fix else self.project_repo.srcs.changed_files + changed_task_files = self.project_repo.docs.task.changed_files changed_files = Documents() # Recode caused by upstream changes. for filename in changed_task_files: - design_doc = await design_file_repo.get(filename) - task_doc = await task_file_repo.get(filename) + design_doc = await self.project_repo.docs.system_design.get(filename) + task_doc = await self.project_repo.docs.task.get(filename) task_list = self._parse_tasks(task_doc) for task_filename in task_list: - old_code_doc = await src_file_repo.get(task_filename) + old_code_doc = await self.project_repo.srcs.get(task_filename) if not old_code_doc: - old_code_doc = Document(root_path=str(src_file_repo.root_path), filename=task_filename, content="") + old_code_doc = Document( + root_path=str(self.project_repo.src_relative_path), filename=task_filename, content="" + ) context = CodingContext( filename=task_filename, design_doc=design_doc, task_doc=task_doc, code_doc=old_code_doc ) coding_doc = Document( - root_path=str(src_file_repo.root_path), filename=task_filename, content=context.model_dump_json() + root_path=str(self.project_repo.src_relative_path), + filename=task_filename, + content=context.model_dump_json(), ) if task_filename in changed_files.docs: logger.warning( @@ -337,56 +317,44 @@ async def _new_code_actions(self, bug_fix=False): f"{changed_files.docs[task_filename].model_dump_json()}" ) changed_files.docs[task_filename] = coding_doc - self.code_todos = [WriteCode(context=i, llm=self.llm) for i in changed_files.docs.values()] + self.code_todos = [ + WriteCode(i_context=i, context=self.context, llm=self.llm) for i in changed_files.docs.values() + ] # Code directly modified by the user. - dependency = await CONFIG.git_repo.get_dependency() + dependency = await self.git_repo.get_dependency() for filename in changed_src_files: if filename in changed_files.docs: continue - coding_doc = await self._new_coding_doc( - filename=filename, - src_file_repo=src_file_repo, - task_file_repo=task_file_repo, - design_file_repo=design_file_repo, - dependency=dependency, - ) + coding_doc = await self._new_coding_doc(filename=filename, dependency=dependency) changed_files.docs[filename] = coding_doc - self.code_todos.append(WriteCode(context=coding_doc, llm=self.llm)) + self.code_todos.append(WriteCode(i_context=coding_doc, context=self.context, llm=self.llm)) if self.code_todos: - self.rc.todo = self.code_todos[0] + self.set_todo(self.code_todos[0]) async def _new_summarize_actions(self): - src_file_repo = CONFIG.git_repo.new_file_repository(CONFIG.src_workspace) - src_files = src_file_repo.all_files + src_files = self.project_repo.srcs.all_files # Generate a SummarizeCode action for each pair of (system_design_doc, task_doc). summarizations = defaultdict(list) for filename in src_files: - dependencies = await src_file_repo.get_dependency(filename=filename) - ctx = CodeSummarizeContext.loads(filenames=dependencies) + dependencies = await self.project_repo.srcs.get_dependency(filename=filename) + ctx = CodeSummarizeContext.loads(filenames=list(dependencies)) summarizations[ctx].append(filename) for ctx, filenames in summarizations.items(): ctx.codes_filenames = filenames - self.summarize_todos.append(SummarizeCode(context=ctx, llm=self.llm)) + self.summarize_todos.append(SummarizeCode(i_context=ctx, context=self.context, llm=self.llm)) if self.summarize_todos: - self.rc.todo = self.summarize_todos[0] + self.set_todo(self.summarize_todos[0]) async def _new_code_plan_and_change_action(self): """Create a WriteCodePlanAndChange action for subsequent to-do actions.""" - requirement_doc = await FileRepository.get_file(filename=REQUIREMENT_FILENAME, relative_path=DOCS_FILE_REPO) - prd_docs = await FileRepository.get_all_files(relative_path=PRDS_FILE_REPO) - design_docs = await FileRepository.get_all_files(relative_path=SYSTEM_DESIGN_FILE_REPO) - tasks_docs = await FileRepository.get_all_files(relative_path=TASK_FILE_REPO) - - code_plan_and_change_context = CodePlanAndChangeContext( - requirement_doc=requirement_doc, - prd_docs=prd_docs, - design_docs=design_docs, - tasks_docs=tasks_docs, - ) - self.rc.todo = WriteCodePlanAndChange(context=code_plan_and_change_context, llm=self.llm) + files = self.project_repo.all_files + requirement_doc = await self.project_repo.docs.get(REQUIREMENT_FILENAME) + requirement = requirement_doc.content if requirement_doc else "" + code_plan_and_change_ctx = CodePlanAndChangeContext.loads(files, requirement=requirement) + self.rc.todo = WriteCodePlanAndChange(i_context=code_plan_and_change_ctx, context=self.context, llm=self.llm) @property - def todo(self) -> str: + def action_description(self) -> str: """AgentStore uses this attribute to display to the user what actions the current role should take.""" return self.next_todo_action diff --git a/metagpt/roles/invoice_ocr_assistant.py b/metagpt/roles/invoice_ocr_assistant.py index f5588974b..a39a48b97 100644 --- a/metagpt/roles/invoice_ocr_assistant.py +++ b/metagpt/roles/invoice_ocr_assistant.py @@ -60,7 +60,7 @@ class InvoiceOCRAssistant(Role): def __init__(self, **kwargs): super().__init__(**kwargs) - self._init_actions([InvoiceOCR]) + self.set_actions([InvoiceOCR]) self._set_react_mode(react_mode=RoleReactMode.BY_ORDER.value) async def _act(self) -> Message: @@ -82,12 +82,12 @@ async def _act(self) -> Message: resp = await todo.run(file_path) if len(resp) == 1: # Single file support for questioning based on OCR recognition results - self._init_actions([GenerateTable, ReplyQuestion]) + self.set_actions([GenerateTable, ReplyQuestion]) self.orc_data = resp[0] else: - self._init_actions([GenerateTable]) + self.set_actions([GenerateTable]) - self.rc.todo = None + self.set_todo(None) content = INVOICE_OCR_SUCCESS resp = OCRResults(ocr_result=json.dumps(resp)) msg = Message(content=content, instruct_content=resp) diff --git a/metagpt/roles/product_manager.py b/metagpt/roles/product_manager.py index 1d82ac3f2..fbe139a99 100644 --- a/metagpt/roles/product_manager.py +++ b/metagpt/roles/product_manager.py @@ -9,7 +9,6 @@ from metagpt.actions import UserRequirement, WritePRD from metagpt.actions.prepare_documents import PrepareDocuments -from metagpt.config import CONFIG from metagpt.roles.role import Role from metagpt.utils.common import any_to_name @@ -34,24 +33,19 @@ class ProductManager(Role): def __init__(self, **kwargs) -> None: super().__init__(**kwargs) - self._init_actions([PrepareDocuments, WritePRD]) + self.set_actions([PrepareDocuments, WritePRD]) self._watch([UserRequirement, PrepareDocuments]) self.todo_action = any_to_name(PrepareDocuments) async def _think(self) -> bool: """Decide what to do""" - if CONFIG.git_repo and not CONFIG.git_reinit: + if self.git_repo and not self.config.git_reinit: self._set_state(1) else: self._set_state(0) - CONFIG.git_reinit = False + self.config.git_reinit = False self.todo_action = any_to_name(WritePRD) return bool(self.rc.todo) async def _observe(self, ignore_memory=False) -> int: return await super()._observe(ignore_memory=True) - - @property - def todo(self) -> str: - """AgentStore uses this attribute to display to the user what actions the current role should take.""" - return self.todo_action diff --git a/metagpt/roles/project_manager.py b/metagpt/roles/project_manager.py index 1fad4afc2..422d2889b 100644 --- a/metagpt/roles/project_manager.py +++ b/metagpt/roles/project_manager.py @@ -33,5 +33,5 @@ class ProjectManager(Role): def __init__(self, **kwargs) -> None: super().__init__(**kwargs) - self._init_actions([WriteTasks]) + self.set_actions([WriteTasks]) self._watch([WriteDesign]) diff --git a/metagpt/roles/qa_engineer.py b/metagpt/roles/qa_engineer.py index 45a1c7715..c73c10ef3 100644 --- a/metagpt/roles/qa_engineer.py +++ b/metagpt/roles/qa_engineer.py @@ -15,20 +15,13 @@ of SummarizeCode. """ - from metagpt.actions import DebugError, RunCode, WriteTest from metagpt.actions.summarize_code import SummarizeCode -from metagpt.config import CONFIG -from metagpt.const import ( - MESSAGE_ROUTE_TO_NONE, - TEST_CODES_FILE_REPO, - TEST_OUTPUTS_FILE_REPO, -) +from metagpt.const import MESSAGE_ROUTE_TO_NONE from metagpt.logs import logger from metagpt.roles import Role from metagpt.schema import Document, Message, RunCodeContext, TestingContext from metagpt.utils.common import any_to_str_set, parse_recipient -from metagpt.utils.file_repository import FileRepository class QaEngineer(Role): @@ -47,17 +40,16 @@ def __init__(self, **kwargs): # FIXME: a bit hack here, only init one action to circumvent _think() logic, # will overwrite _think() in future updates - self._init_actions([WriteTest]) + self.set_actions([WriteTest]) self._watch([SummarizeCode, WriteTest, RunCode, DebugError]) self.test_round = 0 async def _write_test(self, message: Message) -> None: - src_file_repo = CONFIG.git_repo.new_file_repository(CONFIG.src_workspace) + src_file_repo = self.project_repo.with_src_path(self.context.src_workspace).srcs changed_files = set(src_file_repo.changed_files.keys()) # Unit tests only. - if CONFIG.reqa_file and CONFIG.reqa_file not in changed_files: - changed_files.add(CONFIG.reqa_file) - tests_file_repo = CONFIG.git_repo.new_file_repository(TEST_CODES_FILE_REPO) + if self.config.reqa_file and self.config.reqa_file not in changed_files: + changed_files.add(self.config.reqa_file) for filename in changed_files: # write tests if not filename or "test" in filename: @@ -67,18 +59,16 @@ async def _write_test(self, message: Message) -> None: continue if not code_doc.filename.endswith(".py"): continue - test_doc = await tests_file_repo.get("test_" + code_doc.filename) + test_doc = await self.project_repo.tests.get("test_" + code_doc.filename) if not test_doc: test_doc = Document( - root_path=str(tests_file_repo.root_path), filename="test_" + code_doc.filename, content="" + root_path=str(self.project_repo.tests.root_path), filename="test_" + code_doc.filename, content="" ) logger.info(f"Writing {test_doc.filename}..") context = TestingContext(filename=test_doc.filename, test_doc=test_doc, code_doc=code_doc) - context = await WriteTest(context=context, llm=self.llm).run() - await tests_file_repo.save( - filename=context.test_doc.filename, - content=context.test_doc.content, - dependencies={context.code_doc.root_relative_path}, + context = await WriteTest(i_context=context, context=self.context, llm=self.llm).run() + await self.project_repo.tests.save_doc( + doc=context.test_doc, dependencies={context.code_doc.root_relative_path} ) # prepare context for run tests in next round @@ -86,8 +76,8 @@ async def _write_test(self, message: Message) -> None: command=["python", context.test_doc.root_relative_path], code_filename=context.code_doc.filename, test_filename=context.test_doc.filename, - working_directory=str(CONFIG.git_repo.workdir), - additional_python_paths=[str(CONFIG.src_workspace)], + working_directory=str(self.project_repo.workdir), + additional_python_paths=[str(self.context.src_workspace)], ) self.publish_message( Message( @@ -99,21 +89,23 @@ async def _write_test(self, message: Message) -> None: ) ) - logger.info(f"Done {str(tests_file_repo.workdir)} generating.") + logger.info(f"Done {str(self.project_repo.tests.workdir)} generating.") async def _run_code(self, msg): run_code_context = RunCodeContext.loads(msg.content) - src_doc = await CONFIG.git_repo.new_file_repository(CONFIG.src_workspace).get(run_code_context.code_filename) + src_doc = await self.project_repo.with_src_path(self.context.src_workspace).srcs.get( + run_code_context.code_filename + ) if not src_doc: return - test_doc = await CONFIG.git_repo.new_file_repository(TEST_CODES_FILE_REPO).get(run_code_context.test_filename) + test_doc = await self.project_repo.tests.get(run_code_context.test_filename) if not test_doc: return run_code_context.code = src_doc.content run_code_context.test_code = test_doc.content - result = await RunCode(context=run_code_context, llm=self.llm).run() + result = await RunCode(i_context=run_code_context, context=self.context, llm=self.llm).run() run_code_context.output_filename = run_code_context.test_filename + ".json" - await CONFIG.git_repo.new_file_repository(TEST_OUTPUTS_FILE_REPO).save( + await self.project_repo.test_outputs.save( filename=run_code_context.output_filename, content=result.model_dump_json(), dependencies={src_doc.root_relative_path, test_doc.root_relative_path}, @@ -135,10 +127,8 @@ async def _run_code(self, msg): async def _debug_error(self, msg): run_code_context = RunCodeContext.loads(msg.content) - code = await DebugError(context=run_code_context, llm=self.llm).run() - await FileRepository.save_file( - filename=run_code_context.test_filename, content=code, relative_path=TEST_CODES_FILE_REPO - ) + code = await DebugError(i_context=run_code_context, context=self.context, llm=self.llm).run() + await self.project_repo.tests.save(filename=run_code_context.test_filename, content=code) run_code_context.output = None self.publish_message( Message( diff --git a/metagpt/roles/researcher.py b/metagpt/roles/researcher.py index 15f6c9a22..137cfdb4c 100644 --- a/metagpt/roles/researcher.py +++ b/metagpt/roles/researcher.py @@ -34,7 +34,7 @@ class Researcher(Role): def __init__(self, **kwargs): super().__init__(**kwargs) - self._init_actions( + self.set_actions( [CollectLinks(name=self.name), WebBrowseAndSummarize(name=self.name), ConductResearch(name=self.name)] ) self._set_react_mode(react_mode=RoleReactMode.BY_ORDER.value) @@ -49,7 +49,7 @@ async def _think(self) -> bool: if self.rc.state + 1 < len(self.states): self._set_state(self.rc.state + 1) else: - self.rc.todo = None + self.set_todo(None) return False async def _act(self) -> Message: diff --git a/metagpt/roles/role.py b/metagpt/roles/role.py index b234a846f..3938664ba 100644 --- a/metagpt/roles/role.py +++ b/metagpt/roles/role.py @@ -23,30 +23,27 @@ from __future__ import annotations from enum import Enum -from pathlib import Path -from typing import Any, Iterable, Optional, Set, Type +from typing import TYPE_CHECKING, Iterable, Optional, Set, Type, Union from pydantic import BaseModel, ConfigDict, Field, SerializeAsAny, model_validator from metagpt.actions import Action, ActionOutput from metagpt.actions.action_node import ActionNode from metagpt.actions.add_requirement import UserRequirement -from metagpt.const import SERDESER_PATH -from metagpt.llm import LLM, HumanProvider +from metagpt.context_mixin import ContextMixin from metagpt.logs import logger from metagpt.memory import Memory -from metagpt.provider.base_llm import BaseLLM +from metagpt.provider import HumanProvider from metagpt.schema import Message, MessageQueue, SerializationMixin -from metagpt.utils.common import ( - any_to_name, - any_to_str, - import_class, - read_json_file, - role_raise_decorator, - write_json_file, -) +from metagpt.strategy.planner import Planner +from metagpt.utils.common import any_to_name, any_to_str, role_raise_decorator +from metagpt.utils.project_repo import ProjectRepo from metagpt.utils.repair_llm_raw_output import extract_state_value_from_output +if TYPE_CHECKING: + from metagpt.environment import Environment # noqa: F401 + + PREFIX_TEMPLATE = """You are a {profile}, named {name}, your goal is {goal}. """ CONSTRAINT_TEMPLATE = "the constraint is {constraints}. " @@ -101,6 +98,7 @@ class RoleContext(BaseModel): ) # Message Buffer with Asynchronous Updates memory: Memory = Field(default_factory=Memory) # long_term_memory: LongTermMemory = Field(default_factory=LongTermMemory) + working_memory: Memory = Field(default_factory=Memory) state: int = Field(default=-1) # -1 indicates initial or termination state where todo is None todo: Action = Field(default=None, exclude=True) watch: set[str] = Field(default_factory=set) @@ -111,7 +109,7 @@ class RoleContext(BaseModel): max_react_loop: int = 1 def check(self, role_id: str): - # if hasattr(CONFIG, "long_term_memory") and CONFIG.long_term_memory: + # if hasattr(CONFIG, "enable_longterm_memory") and CONFIG.enable_longterm_memory: # self.long_term_memory.recover_memory(role_id, self) # self.memory = self.long_term_memory # use memory to act as long_term_memory for unify operation pass @@ -125,11 +123,17 @@ def important_memory(self) -> list[Message]: def history(self) -> list[Message]: return self.memory.get() + @classmethod + def model_rebuild(cls, **kwargs): + from metagpt.environment.base_env import Environment # noqa: F401 + + super().model_rebuild(**kwargs) + -class Role(SerializationMixin, is_polymorphic_base=True): +class Role(SerializationMixin, ContextMixin, BaseModel): """Role/Agent""" - model_config = ConfigDict(arbitrary_types_allowed=True, exclude=["llm"]) + model_config = ConfigDict(arbitrary_types_allowed=True, extra="allow") name: str = "" profile: str = "" @@ -138,12 +142,19 @@ class Role(SerializationMixin, is_polymorphic_base=True): desc: str = "" is_human: bool = False - llm: BaseLLM = Field(default_factory=LLM, exclude=True) # Each role has its own LLM, use different system message role_id: str = "" states: list[str] = [] + + # scenarios to set action system_prompt: + # 1. `__init__` while using Role(actions=[...]) + # 2. add action to role while using `role.set_action(action)` + # 3. set_todo while using `role.set_todo(action)` + # 4. when role.system_prompt is being updated (e.g. by `role.system_prompt = "..."`) + # Additional, if llm is not set, we will use role's llm actions: list[SerializeAsAny[Action]] = Field(default=[], validate_default=True) rc: RoleContext = Field(default_factory=RoleContext) - subscription: set[str] = set() + addresses: set[str] = set() + planner: Planner = Field(default_factory=Planner) # builtin variables recovered: bool = False # to tag if a recovered role @@ -152,87 +163,114 @@ class Role(SerializationMixin, is_polymorphic_base=True): __hash__ = object.__hash__ # support Role as hashable type in `Environment.members` @model_validator(mode="after") - def check_subscription(self): - if not self.subscription: - self.subscription = {any_to_str(self), self.name} if self.name else {any_to_str(self)} + def validate_role_extra(self): + self._process_role_extra() return self - def __init__(self, **data: Any): - # --- avoid PydanticUndefinedAnnotation name 'Environment' is not defined # - from metagpt.environment import Environment - - Environment - # ------ - Role.model_rebuild() - super().__init__(**data) + def _process_role_extra(self): + kwargs = self.model_extra or {} if self.is_human: - self.llm = HumanProvider() + self.llm = HumanProvider(None) + self._check_actions() self.llm.system_prompt = self._get_prefix() - self._watch(data.get("watch") or [UserRequirement]) + self._watch(kwargs.pop("watch", [UserRequirement])) - def _reset(self): - self.states = [] - self.actions = [] + if self.latest_observed_msg: + self.recovered = True @property - def _setting(self): - return f"{self.name}({self.profile})" + def todo(self) -> Action: + """Get action to do""" + return self.rc.todo - def serialize(self, stg_path: Path = None): - stg_path = ( - SERDESER_PATH.joinpath(f"team/environment/roles/{self.__class__.__name__}_{self.name}") - if stg_path is None - else stg_path - ) + def set_todo(self, value: Optional[Action]): + """Set action to do and update context""" + if value: + value.context = self.context + self.rc.todo = value - role_info = self.model_dump(exclude={"rc": {"memory": True, "msg_buffer": True}, "llm": True}) - role_info.update({"role_class": self.__class__.__name__, "module_name": self.__module__}) - role_info_path = stg_path.joinpath("role_info.json") - write_json_file(role_info_path, role_info) + @property + def git_repo(self): + """Git repo""" + return self.context.git_repo - self.rc.memory.serialize(stg_path) # serialize role's memory alone + @git_repo.setter + def git_repo(self, value): + self.context.git_repo = value - @classmethod - def deserialize(cls, stg_path: Path) -> "Role": - """stg_path = ./storage/team/environment/roles/{role_class}_{role_name}""" - role_info_path = stg_path.joinpath("role_info.json") - role_info = read_json_file(role_info_path) + @property + def src_workspace(self): + """Source workspace under git repo""" + return self.context.src_workspace - role_class_str = role_info.pop("role_class") - module_name = role_info.pop("module_name") - role_class = import_class(class_name=role_class_str, module_name=module_name) + @src_workspace.setter + def src_workspace(self, value): + self.context.src_workspace = value - role = role_class(**role_info) # initiate particular Role - role.set_recovered(True) # set True to make a tag + @property + def project_repo(self) -> ProjectRepo: + project_repo = ProjectRepo(self.context.git_repo) + return project_repo.with_src_path(self.context.src_workspace) if self.context.src_workspace else project_repo - role_memory = Memory.deserialize(stg_path) - role.set_memory(role_memory) + @property + def prompt_schema(self): + """Prompt schema: json/markdown""" + return self.config.prompt_schema - return role + @property + def project_name(self): + return self.config.project_name - def _init_action_system_message(self, action: Action): - action.set_prefix(self._get_prefix()) + @project_name.setter + def project_name(self, value): + self.config.project_name = value - def refresh_system_message(self): - self.llm.system_prompt = self._get_prefix() + @property + def project_path(self): + return self.config.project_path + + @model_validator(mode="after") + def check_addresses(self): + if not self.addresses: + self.addresses = {any_to_str(self), self.name} if self.name else {any_to_str(self)} + return self + + def _reset(self): + self.states = [] + self.actions = [] + + @property + def _setting(self): + return f"{self.name}({self.profile})" - def set_recovered(self, recovered: bool = False): - self.recovered = recovered + def _check_actions(self): + """Check actions and set llm and prefix for each action.""" + self.set_actions(self.actions) + return self - def set_memory(self, memory: Memory): - self.rc.memory = memory + def _init_action(self, action: Action): + if not action.private_config: + action.set_llm(self.llm, override=True) + else: + action.set_llm(self.llm, override=False) + action.set_prefix(self._get_prefix()) - def init_actions(self, actions): - self._init_actions(actions) + def set_action(self, action: Action): + """Add action to the role.""" + self.set_actions([action]) - def _init_actions(self, actions): + def set_actions(self, actions: list[Union[Action, Type[Action]]]): + """Add actions to the role. + + Args: + actions: list of Action classes or instances + """ self._reset() - for idx, action in enumerate(actions): + for action in actions: if not isinstance(action, Action): - ## 默认初始化 - i = action(name="", llm=self.llm) + i = action(context=self.context) else: if self.is_human and not isinstance(action.llm, HumanProvider): logger.warning( @@ -241,11 +279,11 @@ def _init_actions(self, actions): f"try passing in Action classes instead of initialized instances" ) i = action - self._init_action_system_message(i) + self._init_action(i) self.actions.append(i) - self.states.append(f"{idx}. {action}") + self.states.append(f"{len(self.actions)}. {action}") - def _set_react_mode(self, react_mode: str, max_react_loop: int = 1): + def _set_react_mode(self, react_mode: str, max_react_loop: int = 1, auto_run: bool = True, use_tools: bool = False): """Set strategy of the Role reacting to observed Message. Variation lies in how this Role elects action to perform during the _think stage, especially if it is capable of multiple Actions. @@ -265,6 +303,10 @@ def _set_react_mode(self, react_mode: str, max_react_loop: int = 1): self.rc.react_mode = react_mode if react_mode == RoleReactMode.REACT: self.rc.max_react_loop = max_react_loop + elif react_mode == RoleReactMode.PLAN_AND_ACT: + self.planner = Planner( + goal=self.goal, working_memory=self.rc.working_memory, auto_run=auto_run, use_tools=use_tools + ) def _watch(self, actions: Iterable[Type[Action]] | Iterable[Action]): """Watch Actions of interest. Role will select Messages caused by these Actions from its personal message @@ -277,33 +319,29 @@ def _watch(self, actions: Iterable[Type[Action]] | Iterable[Action]): def is_watch(self, caused_by: str): return caused_by in self.rc.watch - def subscribe(self, tags: Set[str]): + def set_addresses(self, addresses: Set[str]): """Used to receive Messages with certain tags from the environment. Message will be put into personal message buffer to be further processed in _observe. By default, a Role subscribes Messages with a tag of its own name or profile. """ - self.subscription = tags + self.addresses = addresses if self.rc.env: # According to the routing feature plan in Chapter 2.2.3.2 of RFC 113 - self.rc.env.set_subscription(self, self.subscription) + self.rc.env.set_addresses(self, self.addresses) def _set_state(self, state: int): """Update the current state.""" self.rc.state = state logger.debug(f"actions={self.actions}, state={state}") - self.rc.todo = self.actions[self.rc.state] if state >= 0 else None + self.set_todo(self.actions[self.rc.state] if state >= 0 else None) def set_env(self, env: "Environment"): """Set the environment in which the role works. The role can talk to the environment and can also receive messages by observing.""" self.rc.env = env if env: - env.set_subscription(self, self.subscription) - self.refresh_system_message() # add env message to system message - - @property - def action_count(self): - """Return number of action""" - return len(self.actions) + env.set_addresses(self, self.addresses) + self.llm.system_prompt = self._get_prefix() + self.set_actions(self.actions) # reset actions to update llm and prefix def _get_prefix(self): """Get the role prefix""" @@ -316,7 +354,8 @@ def _get_prefix(self): prefix += CONSTRAINT_TEMPLATE.format(**{"constraints": self.constraints}) if self.rc.env and self.rc.env.desc: - other_role_names = ", ".join(self.rc.env.role_names()) + all_roles = self.rc.env.role_names() + other_role_names = ", ".join([r for r in all_roles if r != self.name]) env_desc = f"You are in {self.rc.env.desc} with roles({other_role_names})." prefix += env_desc return prefix @@ -331,7 +370,7 @@ async def _think(self) -> bool: if self.recovered and self.rc.state >= 0: self._set_state(self.rc.state) # action to run from recovered state - self.set_recovered(False) # avoid max_react_loop out of work + self.recovered = False # avoid max_react_loop out of work return True prompt = self._get_prefix() @@ -429,7 +468,7 @@ async def _react(self) -> Message: break # act logger.debug(f"{self._setting}: {self.rc.state=}, will do {self.rc.todo}") - rsp = await self._act() # 这个rsp是否需要publish_message? + rsp = await self._act() actions_taken += 1 return rsp # return output from the last action @@ -444,8 +483,41 @@ async def _act_by_order(self) -> Message: async def _plan_and_act(self) -> Message: """first plan, then execute an action sequence, i.e. _think (of a plan) -> _act -> _act -> ... Use llm to come up with the plan dynamically.""" - # TODO: to be implemented - return Message(content="") + + # create initial plan and update it until confirmation + goal = self.rc.memory.get()[-1].content # retreive latest user requirement + await self.planner.update_plan(goal=goal) + + # take on tasks until all finished + while self.planner.current_task: + task = self.planner.current_task + logger.info(f"ready to take on task {task}") + + # take on current task + task_result = await self._act_on_task(task) + + # process the result, such as reviewing, confirming, plan updating + await self.planner.process_task_result(task_result) + + rsp = self.planner.get_useful_memories()[0] # return the completed plan as a response + + self.rc.memory.add(rsp) # add to persistent memory + + return rsp + + async def _act_on_task(self, current_task: Task) -> TaskResult: + """Taking specific action to handle one task in plan + + Args: + current_task (Task): current task to take on + + Raises: + NotImplementedError: Specific Role must implement this method if expected to use planner + + Returns: + TaskResult: Result from the actions + """ + raise NotImplementedError async def react(self) -> Message: """Entry to one of three strategies by which Role reacts to the observed Message""" @@ -455,6 +527,8 @@ async def react(self) -> Message: rsp = await self._act_by_order() elif self.rc.react_mode == RoleReactMode.PLAN_AND_ACT: rsp = await self._plan_and_act() + else: + raise ValueError(f"Unsupported react mode: {self.rc.react_mode}") self._set_state(state=-1) # current reaction is complete, reset state to -1 and todo back to None return rsp @@ -476,7 +550,6 @@ async def run(self, with_message=None) -> Message | None: if not msg.cause_by: msg.cause_by = UserRequirement self.put_message(msg) - if not await self._observe(): # If there is no new information, suspend and wait logger.debug(f"{self._setting}: no news. waiting.") @@ -485,7 +558,7 @@ async def run(self, with_message=None) -> Message | None: rsp = await self.react() # Reset the next action to be taken. - self.rc.todo = None + self.set_todo(None) # Send the response message to the Environment object to have it relay the message to the subscribers. self.publish_message(rsp) return rsp @@ -496,18 +569,37 @@ def is_idle(self) -> bool: return not self.rc.news and not self.rc.todo and self.rc.msg_buffer.empty() async def think(self) -> Action: - """The exported `think` function""" + """ + Export SDK API, used by AgentStore RPC. + The exported `think` function + """ + await self._observe() # For compatibility with the old version of the Agent. await self._think() return self.rc.todo async def act(self) -> ActionOutput: - """The exported `act` function""" + """ + Export SDK API, used by AgentStore RPC. + The exported `act` function + """ msg = await self._act() return ActionOutput(content=msg.content, instruct_content=msg.instruct_content) @property - def todo(self) -> str: - """AgentStore uses this attribute to display to the user what actions the current role should take.""" + def action_description(self) -> str: + """ + Export SDK API, used by AgentStore RPC and Agent. + AgentStore uses this attribute to display to the user what actions the current role should take. + `Role` provides the default property, and this property should be overridden by children classes if necessary, + as demonstrated by the `Engineer` class. + """ + if self.rc.todo: + if self.rc.todo.desc: + return self.rc.todo.desc + return any_to_name(self.rc.todo) if self.actions: return any_to_name(self.actions[0]) return "" + + +RoleContext.model_rebuild() diff --git a/metagpt/roles/sales.py b/metagpt/roles/sales.py index ca1cfee85..bc449b5cd 100644 --- a/metagpt/roles/sales.py +++ b/metagpt/roles/sales.py @@ -8,12 +8,12 @@ from typing import Optional -from pydantic import Field +from pydantic import Field, model_validator from metagpt.actions import SearchAndSummarize, UserRequirement from metagpt.document_store.base_store import BaseStore from metagpt.roles import Role -from metagpt.tools import SearchEngineType +from metagpt.tools.search_engine import SearchEngine class Sales(Role): @@ -29,14 +29,13 @@ class Sales(Role): store: Optional[BaseStore] = Field(default=None, exclude=True) - def __init__(self, **kwargs): - super().__init__(**kwargs) - self._set_store(self.store) - - def _set_store(self, store): - if store: - action = SearchAndSummarize(name="", engine=SearchEngineType.CUSTOM_ENGINE, search_func=store.asearch) + @model_validator(mode="after") + def validate_stroe(self): + if self.store: + search_engine = SearchEngine.from_search_func(search_func=self.store.asearch, proxy=self.config.proxy) + action = SearchAndSummarize(search_engine=search_engine, context=self.context) else: - action = SearchAndSummarize() - self._init_actions([action]) + action = SearchAndSummarize + self.set_actions([action]) self._watch([UserRequirement]) + return self diff --git a/metagpt/roles/searcher.py b/metagpt/roles/searcher.py index e713f7697..557c5ae95 100644 --- a/metagpt/roles/searcher.py +++ b/metagpt/roles/searcher.py @@ -8,14 +8,17 @@ the `cause_by` value in the `Message` to a string to support the new message distribution feature. """ -from pydantic import Field +from typing import Optional -from metagpt.actions import ActionOutput, SearchAndSummarize +from pydantic import Field, model_validator + +from metagpt.actions import SearchAndSummarize from metagpt.actions.action_node import ActionNode +from metagpt.actions.action_output import ActionOutput from metagpt.logs import logger from metagpt.roles import Role from metagpt.schema import Message -from metagpt.tools import SearchEngineType +from metagpt.tools.search_engine import SearchEngine class Searcher(Role): @@ -27,33 +30,22 @@ class Searcher(Role): profile (str): Role profile. goal (str): Goal of the searcher. constraints (str): Constraints or limitations for the searcher. - engine (SearchEngineType): The type of search engine to use. + search_engine (SearchEngine): The search engine to use. """ name: str = Field(default="Alice") profile: str = Field(default="Smart Assistant") goal: str = "Provide search services for users" constraints: str = "Answer is rich and complete" - engine: SearchEngineType = SearchEngineType.SERPAPI_GOOGLE - - def __init__(self, **kwargs) -> None: - """ - Initializes the Searcher role with given attributes. + search_engine: Optional[SearchEngine] = None - Args: - name (str): Name of the searcher. - profile (str): Role profile. - goal (str): Goal of the searcher. - constraints (str): Constraints or limitations for the searcher. - engine (SearchEngineType): The type of search engine to use. - """ - super().__init__(**kwargs) - self._init_actions([SearchAndSummarize(engine=self.engine)]) - - def set_search_func(self, search_func): - """Sets a custom search function for the searcher.""" - action = SearchAndSummarize(name="", engine=SearchEngineType.CUSTOM_ENGINE, search_func=search_func) - self._init_actions([action]) + @model_validator(mode="after") + def post_root(self): + if self.search_engine: + self.set_actions([SearchAndSummarize(search_engine=self.search_engine, context=self.context)]) + else: + self.set_actions([SearchAndSummarize]) + return self async def _act_sp(self) -> Message: """Performs the search action in a single process.""" diff --git a/metagpt/roles/sk_agent.py b/metagpt/roles/sk_agent.py index 8921774f0..71df55fcc 100644 --- a/metagpt/roles/sk_agent.py +++ b/metagpt/roles/sk_agent.py @@ -17,9 +17,7 @@ from metagpt.actions import UserRequirement from metagpt.actions.execute_task import ExecuteTask -from metagpt.llm import LLM from metagpt.logs import logger -from metagpt.provider.base_llm import BaseLLM from metagpt.roles import Role from metagpt.schema import Message from metagpt.utils.make_sk_kernel import make_sk_kernel @@ -44,7 +42,6 @@ class SkAgent(Role): plan: Plan = Field(default=None, exclude=True) planner_cls: Any = None planner: Union[BasicPlanner, SequentialPlanner, ActionPlanner] = None - llm: BaseLLM = Field(default_factory=LLM) kernel: Kernel = Field(default_factory=Kernel) import_semantic_skill_from_directory: Callable = Field(default=None, exclude=True) import_skill: Callable = Field(default=None, exclude=True) @@ -52,7 +49,7 @@ class SkAgent(Role): def __init__(self, **data: Any) -> None: """Initializes the Engineer role with given attributes.""" super().__init__(**data) - self._init_actions([ExecuteTask()]) + self.set_actions([ExecuteTask()]) self._watch([UserRequirement]) self.kernel = make_sk_kernel() diff --git a/metagpt/roles/teacher.py b/metagpt/roles/teacher.py index 5449fe828..d6715dcd1 100644 --- a/metagpt/roles/teacher.py +++ b/metagpt/roles/teacher.py @@ -11,15 +11,12 @@ import re -import aiofiles - from metagpt.actions import UserRequirement from metagpt.actions.write_teaching_plan import TeachingPlanBlock, WriteTeachingPlanPart -from metagpt.config import CONFIG from metagpt.logs import logger from metagpt.roles import Role from metagpt.schema import Message -from metagpt.utils.common import any_to_str +from metagpt.utils.common import any_to_str, awrite class Teacher(Role): @@ -34,11 +31,11 @@ class Teacher(Role): def __init__(self, **kwargs): super().__init__(**kwargs) - self.name = WriteTeachingPlanPart.format_value(self.name) - self.profile = WriteTeachingPlanPart.format_value(self.profile) - self.goal = WriteTeachingPlanPart.format_value(self.goal) - self.constraints = WriteTeachingPlanPart.format_value(self.constraints) - self.desc = WriteTeachingPlanPart.format_value(self.desc) + self.name = WriteTeachingPlanPart.format_value(self.name, self.context) + self.profile = WriteTeachingPlanPart.format_value(self.profile, self.context) + self.goal = WriteTeachingPlanPart.format_value(self.goal, self.context) + self.constraints = WriteTeachingPlanPart.format_value(self.constraints, self.context) + self.desc = WriteTeachingPlanPart.format_value(self.desc, self.context) async def _think(self) -> bool: """Everything will be done part by part.""" @@ -48,9 +45,9 @@ async def _think(self) -> bool: actions = [] print(TeachingPlanBlock.TOPICS) for topic in TeachingPlanBlock.TOPICS: - act = WriteTeachingPlanPart(context=self.rc.news[0].content, topic=topic, llm=self.llm) + act = WriteTeachingPlanPart(i_context=self.rc.news[0].content, topic=topic, llm=self.llm) actions.append(act) - self._init_actions(actions) + self.set_actions(actions) if self.rc.todo is None: self._set_state(0) @@ -60,7 +57,7 @@ async def _think(self) -> bool: self._set_state(self.rc.state + 1) return True - self.rc.todo = None + self.set_todo(None) return False async def _react(self) -> Message: @@ -81,14 +78,10 @@ async def _react(self) -> Message: async def save(self, content): """Save teaching plan""" filename = Teacher.new_file_name(self.course_title) - pathname = CONFIG.workspace_path / "teaching_plan" + pathname = self.config.workspace.path / "teaching_plan" pathname.mkdir(exist_ok=True) pathname = pathname / filename - try: - async with aiofiles.open(str(pathname), mode="w", encoding="utf-8") as writer: - await writer.write(content) - except Exception as e: - logger.error(f"Save failed:{e}") + await awrite(pathname, content) logger.info(f"Save to:{pathname}") @staticmethod diff --git a/metagpt/roles/tutorial_assistant.py b/metagpt/roles/tutorial_assistant.py index 10bd82c60..6cf3a6469 100644 --- a/metagpt/roles/tutorial_assistant.py +++ b/metagpt/roles/tutorial_assistant.py @@ -40,7 +40,7 @@ class TutorialAssistant(Role): def __init__(self, **kwargs): super().__init__(**kwargs) - self._init_actions([WriteDirectory(language=self.language)]) + self.set_actions([WriteDirectory(language=self.language)]) self._set_react_mode(react_mode=RoleReactMode.BY_ORDER.value) async def _handle_directory(self, titles: Dict) -> Message: @@ -63,7 +63,7 @@ async def _handle_directory(self, titles: Dict) -> Message: directory += f"- {key}\n" for second_dir in first_dir[key]: directory += f" - {second_dir}\n" - self._init_actions(actions) + self.set_actions(actions) async def _act(self) -> Message: """Perform an action as determined by the role. diff --git a/metagpt/schema.py b/metagpt/schema.py index ebfbe9b80..15854f676 100644 --- a/metagpt/schema.py +++ b/metagpt/schema.py @@ -23,7 +23,7 @@ from asyncio import Queue, QueueEmpty, wait_for from json import JSONDecodeError from pathlib import Path -from typing import Any, Callable, Dict, List, Optional, Type, TypeVar, Union +from typing import Any, Dict, Iterable, List, Optional, Type, TypeVar, Union from pydantic import ( BaseModel, @@ -32,15 +32,17 @@ PrivateAttr, field_serializer, field_validator, + model_serializer, + model_validator, ) -from pydantic_core import core_schema -from metagpt.config import CONFIG from metagpt.const import ( + CODE_PLAN_AND_CHANGE_FILENAME, MESSAGE_ROUTE_CAUSE_BY, MESSAGE_ROUTE_FROM, MESSAGE_ROUTE_TO, MESSAGE_ROUTE_TO_ALL, + PRDS_FILE_REPO, SYSTEM_DESIGN_FILE_REPO, TASK_FILE_REPO, ) @@ -54,7 +56,7 @@ ) -class SerializationMixin(BaseModel): +class SerializationMixin(BaseModel, extra="forbid"): """ PolyMorphic subclasses Serialization / Deserialization Mixin - First of all, we need to know that pydantic is not designed for polymorphism. @@ -69,49 +71,44 @@ class SerializationMixin(BaseModel): __is_polymorphic_base = False __subclasses_map__ = {} - @classmethod - def __get_pydantic_core_schema__( - cls, source: type["SerializationMixin"], handler: Callable[[Any], core_schema.CoreSchema] - ) -> core_schema.CoreSchema: - schema = handler(source) - og_schema_ref = schema["ref"] - schema["ref"] += ":mixin" - - return core_schema.no_info_before_validator_function( - cls.__deserialize_with_real_type__, - schema=schema, - ref=og_schema_ref, - serialization=core_schema.wrap_serializer_function_ser_schema(cls.__serialize_add_class_type__), - ) - - @classmethod - def __serialize_add_class_type__( - cls, - value, - handler: core_schema.SerializerFunctionWrapHandler, - ) -> Any: - ret = handler(value) - if not len(cls.__subclasses__()): - # only subclass add `__module_class_name` - ret["__module_class_name"] = f"{cls.__module__}.{cls.__qualname__}" + @model_serializer(mode="wrap") + def __serialize_with_class_type__(self, default_serializer) -> Any: + # default serializer, then append the `__module_class_name` field and return + ret = default_serializer(self) + ret["__module_class_name"] = f"{self.__class__.__module__}.{self.__class__.__qualname__}" return ret + @model_validator(mode="wrap") @classmethod - def __deserialize_with_real_type__(cls, value: Any): - if not isinstance(value, dict): - return value + def __convert_to_real_type__(cls, value: Any, handler): + if isinstance(value, dict) is False: + return handler(value) + + # it is a dict so make sure to remove the __module_class_name + # because we don't allow extra keywords but want to ensure + # e.g Cat.model_validate(cat.model_dump()) works + class_full_name = value.pop("__module_class_name", None) + + # if it's not the polymorphic base we construct via default handler + if not cls.__is_polymorphic_base: + if class_full_name is None: + return handler(value) + elif str(cls) == f"": + return handler(value) + else: + # f"Trying to instantiate {class_full_name} but this is not the polymorphic base class") + pass - if not cls.__is_polymorphic_base or (len(cls.__subclasses__()) and "__module_class_name" not in value): - # add right condition to init BaseClass like Action() - return value - module_class_name = value.get("__module_class_name", None) - if module_class_name is None: - raise ValueError("Missing field: __module_class_name") + # otherwise we lookup the correct polymorphic type and construct that + # instead + if class_full_name is None: + raise ValueError("Missing __module_class_name field") - class_type = cls.__subclasses_map__.get(module_class_name, None) + class_type = cls.__subclasses_map__.get(class_full_name, None) if class_type is None: - raise TypeError("Trying to instantiate {module_class_name} which not defined yet.") + # TODO could try dynamic import + raise TypeError("Trying to instantiate {class_full_name}, which has not yet been defined!") return class_type(**value) @@ -151,12 +148,6 @@ def root_relative_path(self): """ return os.path.join(self.root_path, self.filename) - @property - def full_path(self): - if not CONFIG.git_repo: - return None - return str(CONFIG.git_repo.workdir / self.root_path / self.filename) - def __str__(self): return self.content @@ -173,6 +164,26 @@ class Documents(BaseModel): docs: Dict[str, Document] = Field(default_factory=dict) + @classmethod + def from_iterable(cls, documents: Iterable[Document]) -> Documents: + """Create a Documents instance from a list of Document instances. + + :param documents: A list of Document instances. + :return: A Documents instance. + """ + + docs = {doc.filename: doc for doc in documents} + return Documents(docs=docs) + + def to_action_output(self) -> "ActionOutput": + """Convert to action output string. + + :return: A string representing action output. + """ + from metagpt.actions.action_output import ActionOutput + + return ActionOutput(content=self.model_dump_json(), instruct_content=self) + class Message(BaseModel): """list[: ]""" @@ -193,12 +204,17 @@ def check_id(cls, id: str) -> str: @field_validator("instruct_content", mode="before") @classmethod def check_instruct_content(cls, ic: Any) -> BaseModel: - if ic and not isinstance(ic, BaseModel) and "class" in ic: - # compatible with custom-defined ActionOutput - mapping = actionoutput_str_to_mapping(ic["mapping"]) - - actionnode_class = import_class("ActionNode", "metagpt.actions.action_node") # avoid circular import - ic_obj = actionnode_class.create_model_class(class_name=ic["class"], mapping=mapping) + if ic and isinstance(ic, dict) and "class" in ic: + if "mapping" in ic: + # compatible with custom-defined ActionOutput + mapping = actionoutput_str_to_mapping(ic["mapping"]) + actionnode_class = import_class("ActionNode", "metagpt.actions.action_node") # avoid circular import + ic_obj = actionnode_class.create_model_class(class_name=ic["class"], mapping=mapping) + elif "module" in ic: + # subclasses of BaseModel + ic_obj = import_class(ic["class"], ic["module"]) + else: + raise KeyError("missing required key to init Message.instruct_content from dict") ic = ic_obj(**ic["value"]) return ic @@ -218,18 +234,21 @@ def check_send_to(cls, send_to: Any) -> set: return any_to_str_set(send_to if send_to else {MESSAGE_ROUTE_TO_ALL}) @field_serializer("instruct_content", mode="plain") - def ser_instruct_content(self, ic: BaseModel) -> Union[str, None]: + def ser_instruct_content(self, ic: BaseModel) -> Union[dict, None]: ic_dict = None if ic: # compatible with custom-defined ActionOutput schema = ic.model_json_schema() - # `Documents` contain definitions - if "definitions" not in schema: - # TODO refine with nested BaseModel + ic_type = str(type(ic)) + if " bool: + return task_id in self.task_map + + def _update_current_task(self): + current_task_id = "" + for task in self.tasks: + if not task.is_finished: + current_task_id = task.task_id + break + self.current_task_id = current_task_id # all tasks finished + + @property + def current_task(self) -> Task: + """Find current task to execute + + Returns: + Task: the current task to be executed + """ + return self.task_map.get(self.current_task_id, None) + + def finish_current_task(self): + """Finish current task, set Task.is_finished=True, set current task to next task""" + if self.current_task_id: + self.current_task.is_finished = True + self._update_current_task() # set to next task + + def get_finished_tasks(self) -> list[Task]: + """return all finished tasks in correct linearized order + + Returns: + list[Task]: list of finished tasks + """ + return [task for task in self.tasks if task.is_finished] + + class MessageQueue(BaseModel): """Message queue which supports asynchronous updates.""" @@ -453,12 +666,28 @@ class BugFixContext(BaseContext): filename: str = "" -class CodePlanAndChangeContext(BaseContext): - filename: str = "" - requirement_doc: Document - prd_docs: List[Document] - design_docs: List[Document] - tasks_docs: List[Document] +class CodePlanAndChangeContext(BaseModel): + filename: str = CODE_PLAN_AND_CHANGE_FILENAME + requirement: str = "" + prd_filename: str = "" + design_filename: str = "" + task_filename: str = "" + + @staticmethod + def loads(filenames: List, **kwargs) -> CodePlanAndChangeContext: + ctx = CodePlanAndChangeContext(requirement=kwargs.get("requirement", "")) + for filename in filenames: + filename = Path(filename) + if filename.is_relative_to(PRDS_FILE_REPO): + ctx.prd_filename = filename.name + continue + if filename.is_relative_to(SYSTEM_DESIGN_FILE_REPO): + ctx.design_filename = filename.name + continue + if filename.is_relative_to(TASK_FILE_REPO): + ctx.task_filename = filename.name + continue + return ctx # mermaid class view diff --git a/metagpt/startup.py b/metagpt/software_company.py similarity index 52% rename from metagpt/startup.py rename to metagpt/software_company.py index 767a19a9d..26bb29cd1 100644 --- a/metagpt/startup.py +++ b/metagpt/software_company.py @@ -1,40 +1,35 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- + import asyncio +import shutil from pathlib import Path import typer -from metagpt.config import CONFIG +from metagpt.config2 import config +from metagpt.const import CONFIG_ROOT, METAGPT_ROOT +from metagpt.context import Context +from metagpt.utils.project_repo import ProjectRepo -app = typer.Typer(add_completion=False) +app = typer.Typer(add_completion=False, pretty_exceptions_show_locals=False) -@app.command() -def startup( - idea: str = typer.Argument(..., help="Your innovative idea, such as 'Create a 2048 game.'"), - investment: float = typer.Option(default=3.0, help="Dollar amount to invest in the AI company."), - n_round: int = typer.Option(default=5, help="Number of rounds for the simulation."), - code_review: bool = typer.Option(default=True, help="Whether to use code review."), - run_tests: bool = typer.Option(default=False, help="Whether to enable QA for adding & running tests."), - implement: bool = typer.Option(default=True, help="Enable or disable code implementation."), - project_name: str = typer.Option(default="", help="Unique project name, such as 'game_2048'."), - inc: bool = typer.Option(default=False, help="Incremental mode. Use it to coop with existing repo."), - project_path: str = typer.Option( - default="", - help="Specify the directory path of the old version project to fulfill the incremental requirements.", - ), - reqa_file: str = typer.Option( - default="", help="Specify the source file name for rewriting the quality assurance code." - ), - max_auto_summarize_code: int = typer.Option( - default=0, - help="The maximum number of times the 'SummarizeCode' action is automatically invoked, with -1 indicating " - "unlimited. This parameter is used for debugging the workflow.", - ), - recover_path: str = typer.Option(default=None, help="recover the project from existing serialized storage"), -): - """Run a startup. Be a boss.""" +def generate_repo( + idea, + investment=3.0, + n_round=5, + code_review=True, + run_tests=False, + implement=True, + project_name="", + inc=False, + project_path="", + reqa_file="", + max_auto_summarize_code=0, + recover_path=None, +) -> ProjectRepo: + """Run the startup logic. Can be called from CLI or other Python scripts.""" from metagpt.roles import ( Architect, Engineer, @@ -44,10 +39,11 @@ def startup( ) from metagpt.team import Team - CONFIG.update_via_cli(project_path, project_name, inc, reqa_file, max_auto_summarize_code) + config.update_via_cli(project_path, project_name, inc, reqa_file, max_auto_summarize_code) + ctx = Context(config=config) if not recover_path: - company = Team() + company = Team(context=ctx) company.hire( [ ProductManager(), @@ -62,18 +58,87 @@ def startup( if run_tests: company.hire([QaEngineer()]) else: - # # stg_path = SERDESER_PATH.joinpath("team") stg_path = Path(recover_path) if not stg_path.exists() or not str(stg_path).endswith("team"): raise FileNotFoundError(f"{recover_path} not exists or not endswith `team`") - company = Team.deserialize(stg_path=stg_path) - idea = company.idea # use original idea + company = Team.deserialize(stg_path=stg_path, context=ctx) + idea = company.idea company.invest(investment) company.run_project(idea) asyncio.run(company.run(n_round=n_round)) + return ctx.repo + + +@app.command("", help="Start a new project.") +def startup( + idea: str = typer.Argument(None, help="Your innovative idea, such as 'Create a 2048 game.'"), + investment: float = typer.Option(default=3.0, help="Dollar amount to invest in the AI company."), + n_round: int = typer.Option(default=5, help="Number of rounds for the simulation."), + code_review: bool = typer.Option(default=True, help="Whether to use code review."), + run_tests: bool = typer.Option(default=False, help="Whether to enable QA for adding & running tests."), + implement: bool = typer.Option(default=True, help="Enable or disable code implementation."), + project_name: str = typer.Option(default="", help="Unique project name, such as 'game_2048'."), + inc: bool = typer.Option(default=False, help="Incremental mode. Use it to coop with existing repo."), + project_path: str = typer.Option( + default="", + help="Specify the directory path of the old version project to fulfill the incremental requirements.", + ), + reqa_file: str = typer.Option( + default="", help="Specify the source file name for rewriting the quality assurance code." + ), + max_auto_summarize_code: int = typer.Option( + default=0, + help="The maximum number of times the 'SummarizeCode' action is automatically invoked, with -1 indicating " + "unlimited. This parameter is used for debugging the workflow.", + ), + recover_path: str = typer.Option(default=None, help="recover the project from existing serialized storage"), + init_config: bool = typer.Option(default=False, help="Initialize the configuration file for MetaGPT."), +): + """Run a startup. Be a boss.""" + if init_config: + copy_config_to() + return + + if idea is None: + typer.echo("Missing argument 'IDEA'. Run 'metagpt --help' for more information.") + raise typer.Exit() + + return generate_repo( + idea, + investment, + n_round, + code_review, + run_tests, + implement, + project_name, + inc, + project_path, + reqa_file, + max_auto_summarize_code, + recover_path, + ) + + +def copy_config_to(config_path=METAGPT_ROOT / "config" / "config2.yaml"): + """Initialize the configuration file for MetaGPT.""" + target_path = CONFIG_ROOT / "config2.yaml" + + # 创建目标目录(如果不存在) + target_path.parent.mkdir(parents=True, exist_ok=True) + + # 如果目标文件已经存在,则重命名为 .bak + if target_path.exists(): + backup_path = target_path.with_suffix(".bak") + target_path.rename(backup_path) + print(f"Existing configuration file backed up at {backup_path}") + + # 复制文件 + shutil.copy(str(config_path), target_path) + print(f"Configuration file initialized at {target_path}") + if __name__ == "__main__": app() diff --git a/metagpt/strategy/planner.py b/metagpt/strategy/planner.py new file mode 100644 index 000000000..fd635df39 --- /dev/null +++ b/metagpt/strategy/planner.py @@ -0,0 +1,139 @@ +from __future__ import annotations + +import json + +from pydantic import BaseModel, Field + +from metagpt.actions.ci.ask_review import AskReview, ReviewConst +from metagpt.actions.ci.write_plan import ( + WritePlan, + precheck_update_plan_from_rsp, + update_plan_from_rsp, +) +from metagpt.logs import logger +from metagpt.memory import Memory +from metagpt.schema import Message, Plan, Task, TaskResult + +STRUCTURAL_CONTEXT = """ +## User Requirement +{user_requirement} +## Context +{context} +## Current Plan +{tasks} +## Current Task +{current_task} +""" + + +class Planner(BaseModel): + plan: Plan + working_memory: Memory = Field( + default_factory=Memory + ) # memory for working on each task, discarded each time a task is done + auto_run: bool = False + use_tools: bool = False + + def __init__(self, goal: str = "", plan: Plan = None, **kwargs): + plan = plan or Plan(goal=goal) + super().__init__(plan=plan, **kwargs) + + @property + def current_task(self): + return self.plan.current_task + + @property + def current_task_id(self): + return self.plan.current_task_id + + async def update_plan(self, goal: str = "", max_tasks: int = 3, max_retries: int = 3): + if goal: + self.plan = Plan(goal=goal) + + plan_confirmed = False + while not plan_confirmed: + context = self.get_useful_memories() + rsp = await WritePlan().run(context, max_tasks=max_tasks, use_tools=self.use_tools) + self.working_memory.add(Message(content=rsp, role="assistant", cause_by=WritePlan)) + + # precheck plan before asking reviews + is_plan_valid, error = precheck_update_plan_from_rsp(rsp, self.plan) + if not is_plan_valid and max_retries > 0: + error_msg = f"The generated plan is not valid with error: {error}, try regenerating, remember to generate either the whole plan or the single changed task only" + logger.warning(error_msg) + self.working_memory.add(Message(content=error_msg, role="assistant", cause_by=WritePlan)) + max_retries -= 1 + continue + + _, plan_confirmed = await self.ask_review(trigger=ReviewConst.TASK_REVIEW_TRIGGER) + + update_plan_from_rsp(rsp=rsp, current_plan=self.plan) + + self.working_memory.clear() + + async def process_task_result(self, task_result: TaskResult): + # ask for acceptance, users can other refuse and change tasks in the plan + review, task_result_confirmed = await self.ask_review(task_result) + + if task_result_confirmed: + # tick off this task and record progress + await self.confirm_task(self.current_task, task_result, review) + + elif "redo" in review: + # Ask the Role to redo this task with help of review feedback, + # useful when the code run is successful but the procedure or result is not what we want + pass # simply pass, not confirming the result + + else: + # update plan according to user's feedback and to take on changed tasks + await self.update_plan() + + async def ask_review( + self, + task_result: TaskResult = None, + auto_run: bool = None, + trigger: str = ReviewConst.TASK_REVIEW_TRIGGER, + review_context_len: int = 5, + ): + """ + Ask to review the task result, reviewer needs to provide confirmation or request change. + If human confirms the task result, then we deem the task completed, regardless of whether the code run succeeds; + if auto mode, then the code run has to succeed for the task to be considered completed. + """ + auto_run = auto_run or self.auto_run + if not auto_run: + context = self.get_useful_memories() + review, confirmed = await AskReview().run( + context=context[-review_context_len:], plan=self.plan, trigger=trigger + ) + if not confirmed: + self.working_memory.add(Message(content=review, role="user", cause_by=AskReview)) + return review, confirmed + confirmed = task_result.is_success if task_result else True + return "", confirmed + + async def confirm_task(self, task: Task, task_result: TaskResult, review: str): + task.update_task_result(task_result=task_result) + self.plan.finish_current_task() + self.working_memory.clear() + + confirmed_and_more = ( + ReviewConst.CONTINUE_WORDS[0] in review.lower() and review.lower() not in ReviewConst.CONTINUE_WORDS[0] + ) # "confirm, ... (more content, such as changing downstream tasks)" + if confirmed_and_more: + self.working_memory.add(Message(content=review, role="user", cause_by=AskReview)) + await self.update_plan(review) + + def get_useful_memories(self, task_exclude_field=None) -> list[Message]: + """find useful memories only to reduce context length and improve performance""" + user_requirement = self.plan.goal + context = self.plan.context + tasks = [task.dict(exclude=task_exclude_field) for task in self.plan.tasks] + tasks = json.dumps(tasks, indent=4, ensure_ascii=False) + current_task = self.plan.current_task.json() if self.plan.current_task else {} + context = STRUCTURAL_CONTEXT.format( + user_requirement=user_requirement, context=context, tasks=tasks, current_task=current_task + ) + context_msg = [Message(content=context, role="user")] + + return context_msg + self.working_memory.get() diff --git a/metagpt/strategy/search_space.py b/metagpt/strategy/search_space.py new file mode 100644 index 000000000..c643a2f11 --- /dev/null +++ b/metagpt/strategy/search_space.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2024/1/30 17:15 +@Author : alexanderwu +@File : search_space.py +""" + + +class SearchSpace: + """SearchSpace: 用于定义一个搜索空间,搜索空间中的节点是 ActionNode 类。""" + + def __init__(self): + self.search_space = {} + + def add_node(self, node): + self.search_space[node.key] = node + + def get_node(self, key): + return self.search_space[key] diff --git a/metagpt/strategy/solver.py b/metagpt/strategy/solver.py new file mode 100644 index 000000000..e7d61a881 --- /dev/null +++ b/metagpt/strategy/solver.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2024/1/30 17:13 +@Author : alexanderwu +@File : solver.py +""" +from abc import abstractmethod + +from metagpt.actions.action_graph import ActionGraph +from metagpt.provider.base_llm import BaseLLM +from metagpt.strategy.search_space import SearchSpace + + +class BaseSolver: + """AbstractSolver: defines the interface of a solver.""" + + def __init__(self, graph: ActionGraph, search_space: SearchSpace, llm: BaseLLM, context): + """ + :param graph: ActionGraph + :param search_space: SearchSpace + :param llm: BaseLLM + :param context: Context + """ + self.graph = graph + self.search_space = search_space + self.llm = llm + self.context = context + + @abstractmethod + async def solve(self): + """abstract method to solve the problem.""" + + +class NaiveSolver(BaseSolver): + """NaiveSolver: Iterate all the nodes in the graph and execute them one by one.""" + + async def solve(self): + self.graph.topological_sort() + for key in self.graph.execution_order: + op = self.graph.nodes[key] + await op.fill(self.context, self.llm, mode="root") + + +class TOTSolver(BaseSolver): + """TOTSolver: Tree of Thought""" + + async def solve(self): + raise NotImplementedError + + +class CodeInterpreterSolver(BaseSolver): + """CodeInterpreterSolver: Write&Run code in the graph""" + + async def solve(self): + raise NotImplementedError + + +class ReActSolver(BaseSolver): + """ReActSolver: ReAct algorithm""" + + async def solve(self): + raise NotImplementedError + + +class IOSolver(BaseSolver): + """IOSolver: use LLM directly to solve the problem""" + + async def solve(self): + raise NotImplementedError + + +class COTSolver(BaseSolver): + """COTSolver: Chain of Thought""" + + async def solve(self): + raise NotImplementedError diff --git a/metagpt/subscription.py b/metagpt/subscription.py index e2b0916ac..d225a5d87 100644 --- a/metagpt/subscription.py +++ b/metagpt/subscription.py @@ -13,7 +13,7 @@ class SubscriptionRunner(BaseModel): Example: >>> import asyncio - >>> from metagpt.subscription import SubscriptionRunner + >>> from metagpt.address import SubscriptionRunner >>> from metagpt.roles import Searcher >>> from metagpt.schema import Message diff --git a/metagpt/team.py b/metagpt/team.py index aad24efa0..35f987b57 100644 --- a/metagpt/team.py +++ b/metagpt/team.py @@ -10,13 +10,13 @@ import warnings from pathlib import Path -from typing import Any +from typing import Any, Optional from pydantic import BaseModel, ConfigDict, Field from metagpt.actions import UserRequirement -from metagpt.config import CONFIG from metagpt.const import MESSAGE_ROUTE_TO_ALL, SERDESER_PATH +from metagpt.context import Context from metagpt.environment import Environment from metagpt.logs import logger from metagpt.roles import Role @@ -37,12 +37,17 @@ class Team(BaseModel): model_config = ConfigDict(arbitrary_types_allowed=True) - env: Environment = Field(default_factory=Environment) + env: Optional[Environment] = None investment: float = Field(default=10.0) idea: str = Field(default="") - def __init__(self, **data: Any): + def __init__(self, context: Context = None, **data: Any): super(Team, self).__init__(**data) + ctx = context or Context() + if not self.env: + self.env = Environment(context=ctx) + else: + self.env.context = ctx # The `env` object is allocated by deserialization if "roles" in data: self.hire(data["roles"]) if "env_desc" in data: @@ -50,48 +55,43 @@ def __init__(self, **data: Any): def serialize(self, stg_path: Path = None): stg_path = SERDESER_PATH.joinpath("team") if stg_path is None else stg_path + team_info_path = stg_path.joinpath("team.json") - team_info_path = stg_path.joinpath("team_info.json") - write_json_file(team_info_path, self.model_dump(exclude={"env": True})) - - self.env.serialize(stg_path.joinpath("environment")) # save environment alone + write_json_file(team_info_path, self.model_dump()) @classmethod - def deserialize(cls, stg_path: Path) -> "Team": + def deserialize(cls, stg_path: Path, context: Context = None) -> "Team": """stg_path = ./storage/team""" # recover team_info - team_info_path = stg_path.joinpath("team_info.json") + team_info_path = stg_path.joinpath("team.json") if not team_info_path.exists(): raise FileNotFoundError( - "recover storage meta file `team_info.json` not exist, " - "not to recover and please start a new project." + "recover storage meta file `team.json` not exist, " "not to recover and please start a new project." ) team_info: dict = read_json_file(team_info_path) - - # recover environment - environment = Environment.deserialize(stg_path=stg_path.joinpath("environment")) - team_info.update({"env": environment}) - team = Team(**team_info) + ctx = context or Context() + team = Team(**team_info, context=ctx) return team def hire(self, roles: list[Role]): """Hire roles to cooperate""" self.env.add_roles(roles) + @property + def cost_manager(self): + """Get cost manager""" + return self.env.context.cost_manager + def invest(self, investment: float): """Invest company. raise NoMoneyException when exceed max_budget.""" self.investment = investment - CONFIG.max_budget = investment - CONFIG.cost_manager.max_budget = investment + self.cost_manager.max_budget = investment logger.info(f"Investment: ${investment}.") - @staticmethod - def _check_balance(): - if CONFIG.cost_manager.total_cost >= CONFIG.cost_manager.max_budget: - raise NoMoneyException( - CONFIG.cost_manager.total_cost, f"Insufficient funds: {CONFIG.cost_manager.max_budget}" - ) + def _check_balance(self): + if self.cost_manager.total_cost >= self.cost_manager.max_budget: + raise NoMoneyException(self.cost_manager.total_cost, f"Insufficient funds: {self.cost_manager.max_budget}") def run_project(self, idea, send_to: str = ""): """Run a project from publishing user requirement.""" diff --git a/metagpt/tools/__init__.py b/metagpt/tools/__init__.py index aab8c990c..c1f604df9 100644 --- a/metagpt/tools/__init__.py +++ b/metagpt/tools/__init__.py @@ -6,8 +6,11 @@ @File : __init__.py """ - from enum import Enum +from metagpt.tools import libs # this registers all tools +from metagpt.tools.tool_registry import TOOL_REGISTRY + +_ = libs, TOOL_REGISTRY # Avoid pre-commit error class SearchEngineType(Enum): diff --git a/metagpt/tools/azure_tts.py b/metagpt/tools/azure_tts.py index f4f8aa0a2..2e0e2267c 100644 --- a/metagpt/tools/azure_tts.py +++ b/metagpt/tools/azure_tts.py @@ -13,7 +13,6 @@ import aiofiles from azure.cognitiveservices.speech import AudioConfig, SpeechConfig, SpeechSynthesizer -from metagpt.config import CONFIG from metagpt.logs import logger @@ -25,8 +24,8 @@ def __init__(self, subscription_key, region): :param subscription_key: key is used to access your Azure AI service API, see: `https://portal.azure.com/` > `Resource Management` > `Keys and Endpoint` :param region: This is the location (or region) of your resource. You may need to use this field when making calls to this API. """ - self.subscription_key = subscription_key if subscription_key else CONFIG.AZURE_TTS_SUBSCRIPTION_KEY - self.region = region if region else CONFIG.AZURE_TTS_REGION + self.subscription_key = subscription_key + self.region = region # 参数参考:https://learn.microsoft.com/zh-cn/azure/cognitive-services/speech-service/language-support?tabs=tts#voice-styles-and-roles async def synthesize_speech(self, lang, voice, text, output_file): @@ -83,10 +82,6 @@ async def oas3_azsure_tts(text, lang="", voice="", style="", role="", subscripti role = "Girl" if not style: style = "affectionate" - if not subscription_key: - subscription_key = CONFIG.AZURE_TTS_SUBSCRIPTION_KEY - if not region: - region = CONFIG.AZURE_TTS_REGION xml_value = AzureTTS.role_style_text(role=role, style=style, text=text) tts = AzureTTS(subscription_key=subscription_key, region=region) diff --git a/metagpt/tools/iflytek_tts.py b/metagpt/tools/iflytek_tts.py index ad2395362..6ce48826b 100644 --- a/metagpt/tools/iflytek_tts.py +++ b/metagpt/tools/iflytek_tts.py @@ -23,7 +23,6 @@ import websockets as websockets from pydantic import BaseModel -from metagpt.config import CONFIG from metagpt.logs import logger @@ -56,9 +55,9 @@ def __init__(self, app_id: str, api_key: str, api_secret: str): :param api_key: WebAPI argument, see: `https://console.xfyun.cn/services/tts` :param api_secret: WebAPI argument, see: `https://console.xfyun.cn/services/tts` """ - self.app_id = app_id or CONFIG.IFLYTEK_APP_ID - self.api_key = api_key or CONFIG.IFLYTEK_API_KEY - self.api_secret = api_secret or CONFIG.API_SECRET + self.app_id = app_id + self.api_key = api_key + self.api_secret = api_secret async def synthesize_speech(self, text, output_file: str, voice=DEFAULT_IFLYTEK_VOICE): url = self._create_url() @@ -127,14 +126,6 @@ async def oas3_iflytek_tts(text: str, voice: str = "", app_id: str = "", api_key :return: Returns the Base64-encoded .mp3 file data if successful, otherwise an empty string. """ - if not app_id: - app_id = CONFIG.IFLYTEK_APP_ID - if not api_key: - api_key = CONFIG.IFLYTEK_API_KEY - if not api_secret: - api_secret = CONFIG.IFLYTEK_API_SECRET - if not voice: - voice = CONFIG.IFLYTEK_VOICE or DEFAULT_IFLYTEK_VOICE filename = Path(__file__).parent / (uuid.uuid4().hex + ".mp3") try: diff --git a/metagpt/tools/libs/__init__.py b/metagpt/tools/libs/__init__.py new file mode 100644 index 000000000..c9767c1e5 --- /dev/null +++ b/metagpt/tools/libs/__init__.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Time : 2023/11/16 16:32 +# @Author : lidanyang +# @File : __init__.py +# @Desc : +from metagpt.tools.libs import ( + data_preprocess, + feature_engineering, + sd_engine, + gpt_v_generator, + web_scraping, +) + +_ = data_preprocess, feature_engineering, sd_engine, gpt_v_generator, web_scraping # Avoid pre-commit error diff --git a/metagpt/tools/libs/data_preprocess.py b/metagpt/tools/libs/data_preprocess.py new file mode 100644 index 000000000..7a3d019bf --- /dev/null +++ b/metagpt/tools/libs/data_preprocess.py @@ -0,0 +1,249 @@ +from __future__ import annotations + +import json + +import numpy as np +import pandas as pd +from sklearn.impute import SimpleImputer +from sklearn.preprocessing import ( + LabelEncoder, + MaxAbsScaler, + MinMaxScaler, + OneHotEncoder, + OrdinalEncoder, + RobustScaler, + StandardScaler, +) + +from metagpt.tools.tool_registry import register_tool +from metagpt.tools.tool_type import ToolType + +TOOL_TYPE = ToolType.DATA_PREPROCESS.type_name + + +class MLProcess: + def fit(self, df: pd.DataFrame): + """ + Fit a model to be used in subsequent transform. + + Args: + df (pd.DataFrame): The input DataFrame. + """ + raise NotImplementedError + + def transform(self, df: pd.DataFrame) -> pd.DataFrame: + """ + Transform the input DataFrame with the fitted model. + + Args: + df (pd.DataFrame): The input DataFrame. + + Returns: + pd.DataFrame: The transformed DataFrame. + """ + raise NotImplementedError + + def fit_transform(self, df: pd.DataFrame) -> pd.DataFrame: + """ + Fit and transform the input DataFrame. + + Args: + df (pd.DataFrame): The input DataFrame. + + Returns: + pd.DataFrame: The transformed DataFrame. + """ + self.fit(df) + return self.transform(df) + + +class DataPreprocessTool(MLProcess): + """ + Completing a data preprocessing operation. + """ + + def __init__(self, features: list): + """ + Initialize self. + + Args: + features (list): Columns to be processed. + """ + self.features = features + self.model = None # to be filled by specific subclass Tool + + def fit(self, df: pd.DataFrame): + if len(self.features) == 0: + return + self.model.fit(df[self.features]) + + def transform(self, df: pd.DataFrame) -> pd.DataFrame: + if len(self.features) == 0: + return df + new_df = df.copy() + new_df[self.features] = self.model.transform(new_df[self.features]) + return new_df + + +@register_tool(tool_type=TOOL_TYPE) +class FillMissingValue(DataPreprocessTool): + """ + Completing missing values with simple strategies. + """ + + def __init__(self, features: list, strategy: str = "mean", fill_value=None): + """ + Initialize self. + + Args: + features (list): Columns to be processed. + strategy (str, optional): The imputation strategy, notice 'mean' and 'median' can only + be used for numeric features. Enum: ['mean', 'median', 'most_frequent', 'constant']. Defaults to 'mean'. + fill_value (int, optional): Fill_value is used to replace all occurrences of missing_values. + Defaults to None. + """ + self.features = features + self.model = SimpleImputer(strategy=strategy, fill_value=fill_value) + + +@register_tool(tool_type=TOOL_TYPE) +class MinMaxScale(DataPreprocessTool): + """ + Transform features by scaling each feature to a range, which is (0, 1). + """ + + def __init__(self, features: list): + self.features = features + self.model = MinMaxScaler() + + +@register_tool(tool_type=TOOL_TYPE) +class StandardScale(DataPreprocessTool): + """ + Standardize features by removing the mean and scaling to unit variance. + """ + + def __init__(self, features: list): + self.features = features + self.model = StandardScaler() + + +@register_tool(tool_type=TOOL_TYPE) +class MaxAbsScale(DataPreprocessTool): + """ + Scale each feature by its maximum absolute value. + """ + + def __init__(self, features: list): + self.features = features + self.model = MaxAbsScaler() + + +@register_tool(tool_type=TOOL_TYPE) +class RobustScale(DataPreprocessTool): + """ + Apply the RobustScaler to scale features using statistics that are robust to outliers. + """ + + def __init__(self, features: list): + self.features = features + self.model = RobustScaler() + + +@register_tool(tool_type=TOOL_TYPE) +class OrdinalEncode(DataPreprocessTool): + """ + Encode categorical features as ordinal integers. + """ + + def __init__(self, features: list): + self.features = features + self.model = OrdinalEncoder() + + +@register_tool(tool_type=TOOL_TYPE) +class OneHotEncode(DataPreprocessTool): + """ + Apply one-hot encoding to specified categorical columns, the original columns will be dropped. + """ + + def __init__(self, features: list): + self.features = features + self.model = OneHotEncoder(handle_unknown="ignore", sparse=False) + + def transform(self, df: pd.DataFrame) -> pd.DataFrame: + ts_data = self.model.transform(df[self.features]) + new_columns = self.model.get_feature_names_out(self.features) + ts_data = pd.DataFrame(ts_data, columns=new_columns, index=df.index) + new_df = df.drop(self.features, axis=1) + new_df = pd.concat([new_df, ts_data], axis=1) + return new_df + + +@register_tool(tool_type=TOOL_TYPE) +class LabelEncode(DataPreprocessTool): + """ + Apply label encoding to specified categorical columns in-place. + """ + + def __init__(self, features: list): + """ + Initialize self. + + Args: + features (list): Categorical columns to be label encoded. + """ + self.features = features + self.le_encoders = [] + + def fit(self, df: pd.DataFrame): + if len(self.features) == 0: + return + for col in self.features: + le = LabelEncoder().fit(df[col].astype(str).unique().tolist() + ["unknown"]) + self.le_encoders.append(le) + + def transform(self, df: pd.DataFrame) -> pd.DataFrame: + if len(self.features) == 0: + return df + new_df = df.copy() + for i in range(len(self.features)): + data_list = df[self.features[i]].astype(str).tolist() + for unique_item in np.unique(df[self.features[i]].astype(str)): + if unique_item not in self.le_encoders[i].classes_: + data_list = ["unknown" if x == unique_item else x for x in data_list] + new_df[self.features[i]] = self.le_encoders[i].transform(data_list) + return new_df + + +def get_column_info(df: pd.DataFrame) -> dict: + """ + Analyzes a DataFrame and categorizes its columns based on data types. + + Args: + df (pd.DataFrame): The DataFrame to be analyzed. + + Returns: + dict: A dictionary with four keys ('Category', 'Numeric', 'Datetime', 'Others'). + Each key corresponds to a list of column names belonging to that category. + """ + column_info = { + "Category": [], + "Numeric": [], + "Datetime": [], + "Others": [], + } + for col in df.columns: + data_type = str(df[col].dtype).replace("dtype('", "").replace("')", "") + if data_type.startswith("object"): + column_info["Category"].append(col) + elif data_type.startswith("int") or data_type.startswith("float"): + column_info["Numeric"].append(col) + elif data_type.startswith("datetime"): + column_info["Datetime"].append(col) + else: + column_info["Others"].append(col) + + if len(json.dumps(column_info)) > 2000: + column_info["Numeric"] = column_info["Numeric"][0:5] + ["Too many cols, omission here..."] + return column_info diff --git a/metagpt/tools/libs/feature_engineering.py b/metagpt/tools/libs/feature_engineering.py new file mode 100644 index 000000000..40bfb2fc7 --- /dev/null +++ b/metagpt/tools/libs/feature_engineering.py @@ -0,0 +1,435 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Time : 2023/11/17 10:33 +# @Author : lidanyang +# @File : feature_engineering.py +# @Desc : Feature Engineering Tools +from __future__ import annotations + +import itertools + +# import lightgbm as lgb +import numpy as np +import pandas as pd +from joblib import Parallel, delayed +from pandas.core.dtypes.common import is_object_dtype +from sklearn.feature_selection import VarianceThreshold +from sklearn.model_selection import KFold +from sklearn.preprocessing import KBinsDiscretizer, PolynomialFeatures + +from metagpt.tools.libs.data_preprocess import MLProcess +from metagpt.tools.tool_registry import register_tool +from metagpt.tools.tool_type import ToolType + +TOOL_TYPE = ToolType.FEATURE_ENGINEERING.type_name + + +@register_tool(tool_type=TOOL_TYPE) +class PolynomialExpansion(MLProcess): + """ + Add polynomial and interaction features from selected numeric columns to input DataFrame. + """ + + def __init__(self, cols: list, label_col: str, degree: int = 2): + """ + Initialize self. + + Args: + cols (list): Columns for polynomial expansion. + label_col (str): Label column name. + degree (int, optional): The degree of the polynomial features. Defaults to 2. + """ + self.cols = cols + self.degree = degree + self.label_col = label_col + if self.label_col in self.cols: + self.cols.remove(self.label_col) + self.poly = PolynomialFeatures(degree=degree, include_bias=False) + + def fit(self, df: pd.DataFrame): + if len(self.cols) == 0: + return + if len(self.cols) > 10: + corr = df[self.cols + [self.label_col]].corr() + corr = corr[self.label_col].abs().sort_values(ascending=False) + self.cols = corr.index.tolist()[1:11] + + self.poly.fit(df[self.cols].fillna(0)) + + def transform(self, df: pd.DataFrame) -> pd.DataFrame: + if len(self.cols) == 0: + return df + ts_data = self.poly.transform(df[self.cols].fillna(0)) + column_name = self.poly.get_feature_names_out(self.cols) + ts_data = pd.DataFrame(ts_data, index=df.index, columns=column_name) + new_df = df.drop(self.cols, axis=1) + new_df = pd.concat([new_df, ts_data], axis=1) + return new_df + + +@register_tool(tool_type=TOOL_TYPE) +class CatCount(MLProcess): + """ + Add value counts of a categorical column as new feature. + """ + + def __init__(self, col: str): + """ + Initialize self. + + Args: + col (str): Column for value counts. + """ + self.col = col + self.encoder_dict = None + + def fit(self, df: pd.DataFrame): + self.encoder_dict = df[self.col].value_counts().to_dict() + + def transform(self, df: pd.DataFrame) -> pd.DataFrame: + new_df = df.copy() + new_df[f"{self.col}_cnt"] = new_df[self.col].map(self.encoder_dict) + return new_df + + +@register_tool(tool_type=TOOL_TYPE) +class TargetMeanEncoder(MLProcess): + """ + Encode a categorical column by the mean of the label column, and adds the result as a new feature. + """ + + def __init__(self, col: str, label: str): + """ + Initialize self. + + Args: + col (str): Column to be mean encoded. + label (str): Predicted label column. + """ + self.col = col + self.label = label + self.encoder_dict = None + + def fit(self, df: pd.DataFrame): + self.encoder_dict = df.groupby(self.col)[self.label].mean().to_dict() + + def transform(self, df: pd.DataFrame) -> pd.DataFrame: + new_df = df.copy() + new_df[f"{self.col}_target_mean"] = new_df[self.col].map(self.encoder_dict) + return new_df + + +@register_tool(tool_type=TOOL_TYPE) +class KFoldTargetMeanEncoder(MLProcess): + """ + Add a new feature to the DataFrame by k-fold mean encoding of a categorical column using the label column. + """ + + def __init__(self, col: str, label: str, n_splits: int = 5, random_state: int = 2021): + """ + Initialize self. + + Args: + col (str): Column to be k-fold mean encoded. + label (str): Predicted label column. + n_splits (int, optional): Number of splits for K-fold. Defaults to 5. + random_state (int, optional): Random seed. Defaults to 2021. + """ + self.col = col + self.label = label + self.n_splits = n_splits + self.random_state = random_state + self.encoder_dict = None + + def fit(self, df: pd.DataFrame): + tmp = df.copy() + kf = KFold(n_splits=self.n_splits, shuffle=True, random_state=self.random_state) + + global_mean = tmp[self.label].mean() + col_name = f"{self.col}_kf_target_mean" + for trn_idx, val_idx in kf.split(tmp, tmp[self.label]): + _trn, _val = tmp.iloc[trn_idx], tmp.iloc[val_idx] + tmp.loc[tmp.index[val_idx], col_name] = _val[self.col].map(_trn.groupby(self.col)[self.label].mean()) + tmp[col_name].fillna(global_mean, inplace=True) + self.encoder_dict = tmp.groupby(self.col)[col_name].mean().to_dict() + + def transform(self, df: pd.DataFrame) -> pd.DataFrame: + new_df = df.copy() + new_df[f"{self.col}_kf_target_mean"] = new_df[self.col].map(self.encoder_dict) + return new_df + + +@register_tool(tool_type=TOOL_TYPE) +class CatCross(MLProcess): + """ + Add pairwise crossed features and convert them to numerical features. + """ + + def __init__(self, cols: list, max_cat_num: int = 100): + """ + Initialize self. + + Args: + cols (list): Columns to be pairwise crossed, at least 2 columns. + max_cat_num (int, optional): Maximum unique categories per crossed feature. Defaults to 100. + """ + self.cols = cols + self.max_cat_num = max_cat_num + self.combs = [] + self.combs_map = {} + + @staticmethod + def _cross_two(comb, df): + """ + Cross two columns and convert them to numerical features. + + Args: + comb (tuple): The pair of columns to be crossed. + df (pd.DataFrame): The input DataFrame. + + Returns: + tuple: The new column name and the crossed feature map. + """ + new_col = f"{comb[0]}_{comb[1]}" + new_col_combs = list(itertools.product(df[comb[0]].unique(), df[comb[1]].unique())) + ll = list(range(len(new_col_combs))) + comb_map = dict(zip(new_col_combs, ll)) + return new_col, comb_map + + def fit(self, df: pd.DataFrame): + for col in self.cols: + if df[col].nunique() > self.max_cat_num: + self.cols.remove(col) + self.combs = list(itertools.combinations(self.cols, 2)) + res = Parallel(n_jobs=4, require="sharedmem")(delayed(self._cross_two)(comb, df) for comb in self.combs) + self.combs_map = dict(res) + + def transform(self, df: pd.DataFrame) -> pd.DataFrame: + new_df = df.copy() + for comb in self.combs: + new_col = f"{comb[0]}_{comb[1]}" + _map = self.combs_map[new_col] + new_df[new_col] = pd.Series(zip(new_df[comb[0]], new_df[comb[1]])).map(_map) + # set the unknown value to a new number + new_df[new_col].fillna(max(_map.values()) + 1, inplace=True) + new_df[new_col] = new_df[new_col].astype(int) + return new_df + + +@register_tool(tool_type=TOOL_TYPE) +class GroupStat(MLProcess): + """ + Aggregate specified column in a DataFrame grouped by another column, adding new features named '__by_'. + """ + + def __init__(self, group_col: str, agg_col: str, agg_funcs: list): + """ + Initialize self. + + Args: + group_col (str): Column used for grouping. + agg_col (str): Column on which aggregation is performed. + agg_funcs (list): List of aggregation functions to apply, such as ['mean', 'std']. Each function must be supported by pandas. + """ + self.group_col = group_col + self.agg_col = agg_col + self.agg_funcs = agg_funcs + self.group_df = None + + def fit(self, df: pd.DataFrame): + group_df = df.groupby(self.group_col)[self.agg_col].agg(self.agg_funcs).reset_index() + group_df.columns = [self.group_col] + [ + f"{self.agg_col}_{agg_func}_by_{self.group_col}" for agg_func in self.agg_funcs + ] + self.group_df = group_df + + def transform(self, df: pd.DataFrame) -> pd.DataFrame: + new_df = df.merge(self.group_df, on=self.group_col, how="left") + return new_df + + +@register_tool(tool_type=TOOL_TYPE) +class SplitBins(MLProcess): + """ + Inplace binning of continuous data into intervals, returning integer-encoded bin identifiers directly. + """ + + def __init__(self, cols: list, strategy: str = "quantile"): + """ + Initialize self. + + Args: + cols (list): Columns to be binned inplace. + strategy (str, optional): Strategy used to define the widths of the bins. Enum: ['quantile', 'uniform', 'kmeans']. Defaults to 'quantile'. + """ + self.cols = cols + self.strategy = strategy + self.encoder = None + + def fit(self, df: pd.DataFrame): + self.encoder = KBinsDiscretizer(strategy=self.strategy, encode="ordinal") + self.encoder.fit(df[self.cols].fillna(0)) + + def transform(self, df: pd.DataFrame) -> pd.DataFrame: + new_df = df.copy() + new_df[self.cols] = self.encoder.transform(new_df[self.cols].fillna(0)) + return new_df + + +# @register_tool(tool_type=TOOL_TYPE) +class ExtractTimeComps(MLProcess): + """ + Extract time components from a datetime column and add them as new features. + """ + + def __init__(self, time_col: str, time_comps: list): + """ + Initialize self. + + Args: + time_col (str): The name of the column containing time data. + time_comps (list): List of time components to extract. Each component must be in ['year', 'month', 'day', 'hour', 'dayofweek', 'is_weekend']. + """ + self.time_col = time_col + self.time_comps = time_comps + + def fit(self, df: pd.DataFrame): + pass + + def transform(self, df: pd.DataFrame) -> pd.DataFrame: + time_s = pd.to_datetime(df[self.time_col], errors="coerce") + time_comps_df = pd.DataFrame() + + if "year" in self.time_comps: + time_comps_df["year"] = time_s.dt.year + if "month" in self.time_comps: + time_comps_df["month"] = time_s.dt.month + if "day" in self.time_comps: + time_comps_df["day"] = time_s.dt.day + if "hour" in self.time_comps: + time_comps_df["hour"] = time_s.dt.hour + if "dayofweek" in self.time_comps: + time_comps_df["dayofweek"] = time_s.dt.dayofweek + 1 + if "is_weekend" in self.time_comps: + time_comps_df["is_weekend"] = time_s.dt.dayofweek.isin([5, 6]).astype(int) + new_df = pd.concat([df, time_comps_df], axis=1) + return new_df + + +@register_tool(tool_type=TOOL_TYPE) +class GeneralSelection(MLProcess): + """ + Drop all nan feats and feats with only one unique value. + """ + + def __init__(self, label_col: str): + self.label_col = label_col + self.feats = [] + + def fit(self, df: pd.DataFrame): + feats = [f for f in df.columns if f != self.label_col] + for col in df.columns: + if df[col].isnull().sum() / df.shape[0] == 1: + feats.remove(col) + + if df[col].nunique() == 1: + feats.remove(col) + + if df.loc[df[col] == np.inf].shape[0] != 0 or df.loc[df[col] == np.inf].shape[0] != 0: + feats.remove(col) + + if is_object_dtype(df[col]) and df[col].nunique() == df.shape[0]: + feats.remove(col) + + self.feats = feats + + def transform(self, df: pd.DataFrame) -> pd.DataFrame: + new_df = df[self.feats + [self.label_col]] + return new_df + + +# skip for now because lgb is needed +# @register_tool(tool_type=TOOL_TYPE) +class TreeBasedSelection(MLProcess): + """ + Select features based on tree-based model and remove features with low importance. + """ + + def __init__(self, label_col: str, task_type: str): + """ + Initialize self. + + Args: + label_col (str): Label column name. + task_type (str): Task type, 'cls' for classification, 'mcls' for multi-class classification, 'reg' for regression. + """ + self.label_col = label_col + self.task_type = task_type + self.feats = None + + def fit(self, df: pd.DataFrame): + params = { + "boosting_type": "gbdt", + "objective": "binary", + "learning_rate": 0.1, + "num_leaves": 31, + } + + if self.task_type == "cls": + params["objective"] = "binary" + params["metric"] = "auc" + elif self.task_type == "mcls": + params["objective"] = "multiclass" + params["num_class"] = df[self.label_col].nunique() + params["metric"] = "auc_mu" + elif self.task_type == "reg": + params["objective"] = "regression" + params["metric"] = "rmse" + + num_cols = df.select_dtypes(include=np.number).columns.tolist() + cols = [f for f in num_cols if f not in [self.label_col]] + + dtrain = lgb.Dataset(df[cols], df[self.label_col]) + model = lgb.train(params, dtrain, num_boost_round=100) + df_imp = pd.DataFrame({"feature_name": dtrain.feature_name, "importance": model.feature_importance("gain")}) + + df_imp.sort_values("importance", ascending=False, inplace=True) + df_imp = df_imp[df_imp["importance"] > 0] + self.feats = df_imp["feature_name"].tolist() + self.feats.append(self.label_col) + + def transform(self, df: pd.DataFrame) -> pd.DataFrame: + new_df = df[self.feats] + return new_df + + +@register_tool(tool_type=TOOL_TYPE) +class VarianceBasedSelection(MLProcess): + """ + Select features based on variance and remove features with low variance. + """ + + def __init__(self, label_col: str, threshold: float = 0): + """ + Initialize self. + + Args: + label_col (str): Label column name. + threshold (float, optional): Threshold for variance. Defaults to 0. + """ + self.label_col = label_col + self.threshold = threshold + self.feats = None + self.selector = VarianceThreshold(threshold=self.threshold) + + def fit(self, df: pd.DataFrame): + num_cols = df.select_dtypes(include=np.number).columns.tolist() + cols = [f for f in num_cols if f not in [self.label_col]] + + self.selector.fit(df[cols]) + self.feats = df[cols].columns[self.selector.get_support(indices=True)].tolist() + self.feats.append(self.label_col) + + def transform(self, df: pd.DataFrame) -> pd.DataFrame: + new_df = df[self.feats] + return new_df diff --git a/metagpt/tools/libs/gpt_v_generator.py b/metagpt/tools/libs/gpt_v_generator.py new file mode 100644 index 000000000..b1e8317ed --- /dev/null +++ b/metagpt/tools/libs/gpt_v_generator.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2024/01/12 +@Author : mannaandpoem +@File : gpt_v_generator.py +""" +import os +from pathlib import Path + +from metagpt.const import DEFAULT_WORKSPACE_ROOT +from metagpt.tools.tool_registry import register_tool +from metagpt.tools.tool_type import ToolType +from metagpt.utils.common import encode_image + +ANALYZE_LAYOUT_PROMPT = """You are now a UI/UX, please generate layout information for this image: + +NOTE: The image does not have a commercial logo or copyright information. It is just a sketch image of the design. +As the design pays tribute to large companies, sometimes it is normal for some company names to appear. Don't worry. """ + +GENERATE_PROMPT = """You are now a UI/UX and Web Developer. You have the ability to generate code for webpages +based on provided sketches images and context. +Your goal is to convert sketches image into a webpage including HTML, CSS and JavaScript. + +NOTE: The image does not have a commercial logo or copyright information. It is just a sketch image of the design. +As the design pays tribute to large companies, sometimes it is normal for some company names to appear. Don't worry. + +Now, please generate the corresponding webpage code including HTML, CSS and JavaScript:""" + + +@register_tool( + tool_type=ToolType.IMAGE2WEBPAGE.type_name, include_functions=["__init__", "generate_webpages", "save_webpages"] +) +class GPTvGenerator: + """Class for generating webpages at once. + + This class provides methods to generate webpages including all code (HTML, CSS, and JavaScript) based on an image. + It utilizes a vision model to analyze the layout from an image and generate webpage codes accordingly. + """ + + def __init__(self): + """Initialize GPTvGenerator class with default values from the configuration.""" + from metagpt.config2 import config + from metagpt.llm import LLM + + self.llm = LLM(llm_config=config.get_openai_llm()) + self.llm.model = "gpt-4-vision-preview" + + async def analyze_layout(self, image_path: Path) -> str: + """Asynchronously analyze the layout of the given image and return the result. + + This is a helper method to generate a layout description based on the image. + + Args: + image_path (Path): Path of the image to analyze. + + Returns: + str: The layout analysis result. + """ + return await self.llm.aask(msg=ANALYZE_LAYOUT_PROMPT, images=[encode_image(image_path)]) + + async def generate_webpages(self, image_path: str) -> str: + """Asynchronously generate webpages including all code (HTML, CSS, and JavaScript) in one go based on the image. + + Args: + image_path (str): The path of the image file. + + Returns: + str: Generated webpages content. + """ + if isinstance(image_path, str): + image_path = Path(image_path) + layout = await self.analyze_layout(image_path) + prompt = GENERATE_PROMPT + "\n\n # Context\n The layout information of the sketch image is: \n" + layout + return await self.llm.aask(msg=prompt, images=[encode_image(image_path)]) + + @staticmethod + def save_webpages(image_path: str, webpages: str) -> Path: + """Save webpages including all code (HTML, CSS, and JavaScript) at once. + + Args: + image_path (str): The path of the image file. + webpages (str): The generated webpages content. + + Returns: + Path: The path of the saved webpages. + """ + # Create a folder called webpages in the workspace directory to store HTML, CSS, and JavaScript files + webpages_path = DEFAULT_WORKSPACE_ROOT / "webpages" / Path(image_path).stem + os.makedirs(webpages_path, exist_ok=True) + + index_path = webpages_path / "index.html" + try: + index = webpages.split("```html")[1].split("```")[0] + style_path = None + if "styles.css" in index: + style_path = webpages_path / "styles.css" + elif "style.css" in index: + style_path = webpages_path / "style.css" + style = webpages.split("```css")[1].split("```")[0] if style_path else "" + + js_path = None + if "scripts.js" in index: + js_path = webpages_path / "scripts.js" + elif "script.js" in index: + js_path = webpages_path / "script.js" + + js = webpages.split("```javascript")[1].split("```")[0] if js_path else "" + except IndexError: + raise ValueError(f"No html or css or js code found in the result. \nWebpages: {webpages}") + + try: + with open(index_path, "w", encoding="utf-8") as f: + f.write(index) + if style_path: + with open(style_path, "w", encoding="utf-8") as f: + f.write(style) + if js_path: + with open(js_path, "w", encoding="utf-8") as f: + f.write(js) + except FileNotFoundError as e: + raise FileNotFoundError(f"Cannot save the webpages to {str(webpages_path)}") from e + + return webpages_path diff --git a/metagpt/tools/libs/sd_engine.py b/metagpt/tools/libs/sd_engine.py new file mode 100644 index 000000000..347f4a430 --- /dev/null +++ b/metagpt/tools/libs/sd_engine.py @@ -0,0 +1,183 @@ +# -*- coding: utf-8 -*- +# @Date : 2023/7/19 16:28 +# @Author : stellahong (stellahong@deepwisdom.ai) +# @Desc : +from __future__ import annotations + +import base64 +import hashlib +import io +import json +from os.path import join + +import requests +from aiohttp import ClientSession +from PIL import Image, PngImagePlugin + +# +from metagpt.const import SD_OUTPUT_FILE_REPO, SOURCE_ROOT +from metagpt.logs import logger +from metagpt.tools.tool_registry import register_tool +from metagpt.tools.tool_type import ToolType + +payload = { + "prompt": "", + "negative_prompt": "(easynegative:0.8),black, dark,Low resolution", + "override_settings": {"sd_model_checkpoint": "galaxytimemachinesGTM_photoV20"}, + "seed": -1, + "batch_size": 1, + "n_iter": 1, + "steps": 20, + "cfg_scale": 7, + "width": 512, + "height": 768, + "restore_faces": False, + "tiling": False, + "do_not_save_samples": False, + "do_not_save_grid": False, + "enable_hr": False, + "hr_scale": 2, + "hr_upscaler": "Latent", + "hr_second_pass_steps": 0, + "hr_resize_x": 0, + "hr_resize_y": 0, + "hr_upscale_to_x": 0, + "hr_upscale_to_y": 0, + "truncate_x": 0, + "truncate_y": 0, + "applied_old_hires_behavior_to": None, + "eta": None, + "sampler_index": "DPM++ SDE Karras", + "alwayson_scripts": {}, +} + +default_negative_prompt = "(easynegative:0.8),black, dark,Low resolution" + + +@register_tool( + tool_type=ToolType.STABLE_DIFFUSION.type_name, + include_functions=["__init__", "simple_run_t2i", "run_t2i", "construct_payload", "save"], +) +class SDEngine: + """Generate image using stable diffusion model. + + This class provides methods to interact with a stable diffusion service to generate images based on text inputs. + """ + + def __init__(self, sd_url=""): + """Initialize the SDEngine instance with configuration. + + Args: + sd_url (str, optional): URL of the stable diffusion service. Defaults to "". + """ + self.sd_url = sd_url + self.sd_t2i_url = f"{self.sd_url}/sdapi/v1/txt2img" + # Define default payload settings for SD API + self.payload = payload + logger.info(self.sd_t2i_url) + + def construct_payload( + self, + prompt, + negtive_prompt=default_negative_prompt, + width=512, + height=512, + sd_model="galaxytimemachinesGTM_photoV20", + ): + """Modify and set the API parameters for image generation. + + Args: + prompt (str): Text input for image generation. + negtive_prompt (str, optional): Text input for negative prompts. Defaults to None. + width (int, optional): Width of the generated image in pixels. Defaults to 512. + height (int, optional): Height of the generated image in pixels. Defaults to 512. + sd_model (str, optional): The model to use for image generation. Defaults to "galaxytimemachinesGTM_photoV20". + + Returns: + dict: Updated parameters for the stable diffusion API. + """ + self.payload["prompt"] = prompt + self.payload["negative_prompt"] = negtive_prompt + self.payload["width"] = width + self.payload["height"] = height + self.payload["override_settings"]["sd_model_checkpoint"] = sd_model + logger.info(f"call sd payload is {self.payload}") + return self.payload + + def save(self, imgs, save_name=""): + """Save generated images to the output directory. + + Args: + imgs (str): Generated images. + save_name (str, optional): Output image name. Default is empty. + """ + save_dir = SOURCE_ROOT / SD_OUTPUT_FILE_REPO + if not save_dir.exists(): + save_dir.mkdir(parents=True, exist_ok=True) + batch_decode_base64_to_image(imgs, str(save_dir), save_name=save_name) + + def simple_run_t2i(self, payload: dict, auto_save: bool = True): + """Run the stable diffusion API for multiple prompts, calling the stable diffusion API to generate images. + + Args: + payload (dict): Dictionary of input parameters for the stable diffusion API. + auto_save (bool, optional): Save generated images automatically. Defaults to True. + + Returns: + list: The generated images as a result of the API call. + """ + with requests.Session() as session: + logger.debug(self.sd_t2i_url) + rsp = session.post(self.sd_t2i_url, json=payload, timeout=600) + + results = rsp.json()["images"] + if auto_save: + save_name = hashlib.sha256(payload["prompt"][:10].encode()).hexdigest()[:6] + self.save(results, save_name=f"output_{save_name}") + return results + + async def run_t2i(self, payloads: list): + """Run the stable diffusion API for multiple prompts asynchronously. + + Args: + payloads (list): list of payload, each payload is a dictionary of input parameters for the stable diffusion API. + """ + session = ClientSession() + for payload_idx, payload in enumerate(payloads): + results = await self.run(url=self.sd_t2i_url, payload=payload, session=session) + self.save(results, save_name=f"output_{payload_idx}") + await session.close() + + async def run(self, url, payload, session): + """Perform the HTTP POST request to the SD API. + + Args: + url (str): The API URL. + payload (dict): The payload for the request. + session (ClientSession): The session for making HTTP requests. + + Returns: + list: Images generated by the stable diffusion API. + """ + async with session.post(url, json=payload, timeout=600) as rsp: + data = await rsp.read() + + rsp_json = json.loads(data) + imgs = rsp_json["images"] + + logger.info(f"callback rsp json is {rsp_json.keys()}") + return imgs + + +def decode_base64_to_image(img, save_name): + image = Image.open(io.BytesIO(base64.b64decode(img.split(",", 1)[0]))) + pnginfo = PngImagePlugin.PngInfo() + logger.info(save_name) + image.save(f"{save_name}.png", pnginfo=pnginfo) + return pnginfo, image + + +def batch_decode_base64_to_image(imgs, save_dir="", save_name=""): + for idx, _img in enumerate(imgs): + save_name = join(save_dir, save_name) + decode_base64_to_image(_img, save_name=save_name) diff --git a/metagpt/tools/libs/web_scraping.py b/metagpt/tools/libs/web_scraping.py new file mode 100644 index 000000000..d01e69d09 --- /dev/null +++ b/metagpt/tools/libs/web_scraping.py @@ -0,0 +1,21 @@ +from metagpt.tools.tool_registry import register_tool +from metagpt.tools.tool_type import ToolType +from metagpt.tools.web_browser_engine_playwright import PlaywrightWrapper + + +@register_tool(tool_type=ToolType.WEBSCRAPING.type_name) +async def scrape_web_playwright(url): + """ + Asynchronously Scrape and save the HTML structure and inner text content of a web page using Playwright. + + Args: + url (str): The main URL to fetch inner text from. + + Returns: + dict: The inner text content and html structure of the web page, keys are 'inner_text', 'html'. + """ + # Create a PlaywrightWrapper instance for the Chromium browser + web = await PlaywrightWrapper().run(url) + + # Return the inner text content of the web page + return {"inner_text": web.inner_text.strip(), "html": web.html.strip()} diff --git a/metagpt/tools/metagpt_text_to_image.py b/metagpt/tools/metagpt_text_to_image.py index 9a84e69eb..cf7bf97e7 100644 --- a/metagpt/tools/metagpt_text_to_image.py +++ b/metagpt/tools/metagpt_text_to_image.py @@ -13,7 +13,6 @@ import requests from pydantic import BaseModel -from metagpt.config import CONFIG from metagpt.logs import logger @@ -22,7 +21,7 @@ def __init__(self, model_url): """ :param model_url: Model reset api url """ - self.model_url = model_url if model_url else CONFIG.METAGPT_TEXT_TO_IMAGE_MODEL + self.model_url = model_url async def text_2_image(self, text, size_type="512x512"): """Text to image @@ -93,6 +92,4 @@ async def oas3_metagpt_text_to_image(text, size_type: str = "512x512", model_url """ if not text: return "" - if not model_url: - model_url = CONFIG.METAGPT_TEXT_TO_IMAGE_MODEL_URL return await MetaGPTText2Image(model_url).text_2_image(text, size_type=size_type) diff --git a/metagpt/tools/moderation.py b/metagpt/tools/moderation.py index cda164ec5..f00b0e1f2 100644 --- a/metagpt/tools/moderation.py +++ b/metagpt/tools/moderation.py @@ -7,12 +7,12 @@ """ from typing import Union -from metagpt.llm import LLM +from metagpt.provider.base_llm import BaseLLM class Moderation: - def __init__(self): - self.llm = LLM() + def __init__(self, llm: BaseLLM): + self.llm = llm def handle_moderation_results(self, results): resp = [] diff --git a/metagpt/tools/openai_text_to_embedding.py b/metagpt/tools/openai_text_to_embedding.py index 52b2cc9eb..e93bfb271 100644 --- a/metagpt/tools/openai_text_to_embedding.py +++ b/metagpt/tools/openai_text_to_embedding.py @@ -13,7 +13,6 @@ import requests from pydantic import BaseModel, Field -from metagpt.config import CONFIG from metagpt.logs import logger @@ -43,11 +42,12 @@ class Config: class OpenAIText2Embedding: - def __init__(self, openai_api_key): + def __init__(self, api_key: str, proxy: str): """ :param openai_api_key: OpenAI API key, For more details, checkout: `https://platform.openai.com/account/api-keys` """ - self.openai_api_key = openai_api_key or CONFIG.OPENAI_API_KEY + self.api_key = api_key + self.proxy = proxy async def text_2_embedding(self, text, model="text-embedding-ada-002"): """Text to embedding @@ -57,8 +57,8 @@ async def text_2_embedding(self, text, model="text-embedding-ada-002"): :return: A json object of :class:`ResultEmbedding` class if successful, otherwise `{}`. """ - proxies = {"proxy": CONFIG.openai_proxy} if CONFIG.openai_proxy else {} - headers = {"Content-Type": "application/json", "Authorization": f"Bearer {self.openai_api_key}"} + proxies = {"proxy": self.proxy} if self.proxy else {} + headers = {"Content-Type": "application/json", "Authorization": f"Bearer {self.api_key}"} data = {"input": text, "model": model} url = "https://api.openai.com/v1/embeddings" try: @@ -72,16 +72,14 @@ async def text_2_embedding(self, text, model="text-embedding-ada-002"): # Export -async def oas3_openai_text_to_embedding(text, model="text-embedding-ada-002", openai_api_key=""): +async def oas3_openai_text_to_embedding(text, openai_api_key: str, model="text-embedding-ada-002", proxy: str = ""): """Text to embedding :param text: The text used for embedding. :param model: One of ['text-embedding-ada-002'], ID of the model to use. For more details, checkout: `https://api.openai.com/v1/models`. - :param openai_api_key: OpenAI API key, For more details, checkout: `https://platform.openai.com/account/api-keys` + :param config: OpenAI config with API key, For more details, checkout: `https://platform.openai.com/account/api-keys` :return: A json object of :class:`ResultEmbedding` class if successful, otherwise `{}`. """ if not text: return "" - if not openai_api_key: - openai_api_key = CONFIG.OPENAI_API_KEY - return await OpenAIText2Embedding(openai_api_key).text_2_embedding(text, model=model) + return await OpenAIText2Embedding(api_key=openai_api_key, proxy=proxy).text_2_embedding(text, model=model) diff --git a/metagpt/tools/openai_text_to_image.py b/metagpt/tools/openai_text_to_image.py index aa00abdcc..bf7c5e799 100644 --- a/metagpt/tools/openai_text_to_image.py +++ b/metagpt/tools/openai_text_to_image.py @@ -10,16 +10,13 @@ import aiohttp import requests -from metagpt.llm import LLM from metagpt.logs import logger +from metagpt.provider.base_llm import BaseLLM class OpenAIText2Image: - def __init__(self): - """ - :param openai_api_key: OpenAI API key, For more details, checkout: `https://platform.openai.com/account/api-keys` - """ - self._llm = LLM() + def __init__(self, llm: BaseLLM): + self.llm = llm async def text_2_image(self, text, size_type="1024x1024"): """Text to image @@ -29,7 +26,7 @@ async def text_2_image(self, text, size_type="1024x1024"): :return: The image data is returned in Base64 encoding. """ try: - result = await self._llm.aclient.images.generate(prompt=text, n=1, size=size_type) + result = await self.llm.aclient.images.generate(prompt=text, n=1, size=size_type) except Exception as e: logger.error(f"An error occurred:{e}") return "" @@ -57,13 +54,14 @@ async def get_image_data(url): # Export -async def oas3_openai_text_to_image(text, size_type: str = "1024x1024"): +async def oas3_openai_text_to_image(text, size_type: str = "1024x1024", llm: BaseLLM = None): """Text to image :param text: The text used for image conversion. :param size_type: One of ['256x256', '512x512', '1024x1024'] + :param llm: LLM instance :return: The image data is returned in Base64 encoding. """ if not text: return "" - return await OpenAIText2Image().text_2_image(text, size_type=size_type) + return await OpenAIText2Image(llm).text_2_image(text, size_type=size_type) diff --git a/metagpt/tools/sd_engine.py b/metagpt/tools/sd_engine.py deleted file mode 100644 index c4d9d2df4..000000000 --- a/metagpt/tools/sd_engine.py +++ /dev/null @@ -1,133 +0,0 @@ -# -*- coding: utf-8 -*- -# @Date : 2023/7/19 16:28 -# @Author : stellahong (stellahong@deepwisdom.ai) -# @Desc : -import asyncio -import base64 -import io -import json -from os.path import join -from typing import List - -from aiohttp import ClientSession -from PIL import Image, PngImagePlugin - -from metagpt.config import CONFIG -from metagpt.const import SD_OUTPUT_FILE_REPO -from metagpt.logs import logger - -payload = { - "prompt": "", - "negative_prompt": "(easynegative:0.8),black, dark,Low resolution", - "override_settings": {"sd_model_checkpoint": "galaxytimemachinesGTM_photoV20"}, - "seed": -1, - "batch_size": 1, - "n_iter": 1, - "steps": 20, - "cfg_scale": 7, - "width": 512, - "height": 768, - "restore_faces": False, - "tiling": False, - "do_not_save_samples": False, - "do_not_save_grid": False, - "enable_hr": False, - "hr_scale": 2, - "hr_upscaler": "Latent", - "hr_second_pass_steps": 0, - "hr_resize_x": 0, - "hr_resize_y": 0, - "hr_upscale_to_x": 0, - "hr_upscale_to_y": 0, - "truncate_x": 0, - "truncate_y": 0, - "applied_old_hires_behavior_to": None, - "eta": None, - "sampler_index": "DPM++ SDE Karras", - "alwayson_scripts": {}, -} - -default_negative_prompt = "(easynegative:0.8),black, dark,Low resolution" - - -class SDEngine: - def __init__(self): - # Initialize the SDEngine with configuration - self.sd_url = CONFIG.get("SD_URL") - self.sd_t2i_url = f"{self.sd_url}{CONFIG.get('SD_T2I_API')}" - # Define default payload settings for SD API - self.payload = payload - logger.info(self.sd_t2i_url) - - def construct_payload( - self, - prompt, - negtive_prompt=default_negative_prompt, - width=512, - height=512, - sd_model="galaxytimemachinesGTM_photoV20", - ): - # Configure the payload with provided inputs - self.payload["prompt"] = prompt - self.payload["negtive_prompt"] = negtive_prompt - self.payload["width"] = width - self.payload["height"] = height - self.payload["override_settings"]["sd_model_checkpoint"] = sd_model - logger.info(f"call sd payload is {self.payload}") - return self.payload - - def _save(self, imgs, save_name=""): - save_dir = CONFIG.workspace_path / SD_OUTPUT_FILE_REPO - if not save_dir.exists(): - save_dir.mkdir(parents=True, exist_ok=True) - batch_decode_base64_to_image(imgs, str(save_dir), save_name=save_name) - - async def run_t2i(self, prompts: List): - # Asynchronously run the SD API for multiple prompts - session = ClientSession() - for payload_idx, payload in enumerate(prompts): - results = await self.run(url=self.sd_t2i_url, payload=payload, session=session) - self._save(results, save_name=f"output_{payload_idx}") - await session.close() - - async def run(self, url, payload, session): - # Perform the HTTP POST request to the SD API - async with session.post(url, json=payload, timeout=600) as rsp: - data = await rsp.read() - - rsp_json = json.loads(data) - imgs = rsp_json["images"] - logger.info(f"callback rsp json is {rsp_json.keys()}") - return imgs - - async def run_i2i(self): - # todo: 添加图生图接口调用 - raise NotImplementedError - - async def run_sam(self): - # todo:添加SAM接口调用 - raise NotImplementedError - - -def decode_base64_to_image(img, save_name): - image = Image.open(io.BytesIO(base64.b64decode(img.split(",", 1)[0]))) - pnginfo = PngImagePlugin.PngInfo() - logger.info(save_name) - image.save(f"{save_name}.png", pnginfo=pnginfo) - return pnginfo, image - - -def batch_decode_base64_to_image(imgs, save_dir="", save_name=""): - for idx, _img in enumerate(imgs): - save_name = join(save_dir, save_name) - decode_base64_to_image(_img, save_name=save_name) - - -if __name__ == "__main__": - engine = SDEngine() - prompt = "pixel style, game design, a game interface should be minimalistic and intuitive with the score and high score displayed at the top. The snake and its food should be easily distinguishable. The game should have a simple color scheme, with a contrasting color for the snake and its food. Complete interface boundary" - - engine.construct_payload(prompt) - - event_loop = asyncio.get_event_loop() - event_loop.run_until_complete(engine.run_t2i(prompt)) diff --git a/metagpt/tools/search_engine.py b/metagpt/tools/search_engine.py index 64388a11f..1e540bd0e 100644 --- a/metagpt/tools/search_engine.py +++ b/metagpt/tools/search_engine.py @@ -8,15 +8,23 @@ import importlib from typing import Callable, Coroutine, Literal, Optional, Union, overload +from pydantic import BaseModel, ConfigDict, model_validator from semantic_kernel.skill_definition import sk_function -from metagpt.config import CONFIG +from metagpt.configs.search_config import SearchConfig +from metagpt.logs import logger from metagpt.tools import SearchEngineType class SkSearchEngine: - def __init__(self): - self.search_engine = SearchEngine() + """A search engine class for executing searches. + + Attributes: + search_engine: The search engine instance used for executing searches. + """ + + def __init__(self, **kwargs): + self.search_engine = SearchEngine(**kwargs) @sk_function( description="searches results from Google. Useful when you need to find short " @@ -29,43 +37,85 @@ async def run(self, query: str) -> str: return result -class SearchEngine: - """Class representing a search engine. - - Args: - engine: The search engine type. Defaults to the search engine specified in the config. - run_func: The function to run the search. Defaults to None. +class SearchEngine(BaseModel): + """A model for configuring and executing searches with different search engines. Attributes: - run_func: The function to run the search. - engine: The search engine type. + model_config: Configuration for the model allowing arbitrary types. + engine: The type of search engine to use. + run_func: An optional callable for running the search. If not provided, it will be determined based on the engine. + api_key: An optional API key for the search engine. + proxy: An optional proxy for the search engine requests. """ - def __init__( + model_config = ConfigDict(arbitrary_types_allowed=True, extra="allow") + + engine: SearchEngineType = SearchEngineType.SERPER_GOOGLE + run_func: Optional[Callable[[str, int, bool], Coroutine[None, None, Union[str, list[str]]]]] = None + api_key: Optional[str] = None + proxy: Optional[str] = None + + @model_validator(mode="after") + def validate_extra(self): + """Validates extra fields provided to the model and updates the run function accordingly.""" + data = self.model_dump(exclude={"engine"}, exclude_none=True, exclude_defaults=True) + if self.model_extra: + data.update(self.model_extra) + self._process_extra(**data) + return self + + def _process_extra( self, - engine: Optional[SearchEngineType] = None, - run_func: Callable[[str, int, bool], Coroutine[None, None, Union[str, list[str]]]] = None, + run_func: Optional[Callable[[str, int, bool], Coroutine[None, None, Union[str, list[str]]]]] = None, + **kwargs, ): - engine = engine or CONFIG.search_engine - if engine == SearchEngineType.SERPAPI_GOOGLE: + """Processes extra configuration and updates the run function based on the search engine type. + + Args: + run_func: An optional callable for running the search. If not provided, it will be determined based on the engine. + """ + if self.engine == SearchEngineType.SERPAPI_GOOGLE: module = "metagpt.tools.search_engine_serpapi" - run_func = importlib.import_module(module).SerpAPIWrapper().run - elif engine == SearchEngineType.SERPER_GOOGLE: + run_func = importlib.import_module(module).SerpAPIWrapper(**kwargs).run + elif self.engine == SearchEngineType.SERPER_GOOGLE: module = "metagpt.tools.search_engine_serper" - run_func = importlib.import_module(module).SerperWrapper().run - elif engine == SearchEngineType.DIRECT_GOOGLE: + run_func = importlib.import_module(module).SerperWrapper(**kwargs).run + elif self.engine == SearchEngineType.DIRECT_GOOGLE: module = "metagpt.tools.search_engine_googleapi" - run_func = importlib.import_module(module).GoogleAPIWrapper().run - elif engine == SearchEngineType.DUCK_DUCK_GO: + run_func = importlib.import_module(module).GoogleAPIWrapper(**kwargs).run + elif self.engine == SearchEngineType.DUCK_DUCK_GO: module = "metagpt.tools.search_engine_ddg" - run_func = importlib.import_module(module).DDGAPIWrapper().run - elif engine == SearchEngineType.CUSTOM_ENGINE: - pass # run_func = run_func + run_func = importlib.import_module(module).DDGAPIWrapper(**kwargs).run + elif self.engine == SearchEngineType.CUSTOM_ENGINE: + run_func = self.run_func else: raise NotImplementedError - self.engine = engine self.run_func = run_func + @classmethod + def from_search_config(cls, config: SearchConfig, **kwargs): + """Creates a SearchEngine instance from a SearchConfig. + + Args: + config: The search configuration to use for creating the SearchEngine instance. + """ + data = config.model_dump(exclude={"api_type", "search_func"}) + if config.search_func is not None: + data["run_func"] = config.search_func + + return cls(engine=config.api_type, **data, **kwargs) + + @classmethod + def from_search_func( + cls, search_func: Callable[[str, int, bool], Coroutine[None, None, Union[str, list[str]]]], **kwargs + ): + """Creates a SearchEngine instance from a custom search function. + + Args: + search_func: A callable that executes the search. + """ + return cls(engine=SearchEngineType.CUSTOM_ENGINE, run_func=search_func, **kwargs) + @overload def run( self, @@ -84,15 +134,29 @@ def run( ) -> list[dict[str, str]]: ... - async def run(self, query: str, max_results: int = 8, as_string: bool = True) -> Union[str, list[dict[str, str]]]: + async def run( + self, + query: str, + max_results: int = 8, + as_string: bool = True, + ignore_errors: bool = False, + ) -> Union[str, list[dict[str, str]]]: """Run a search query. Args: query: The search query. max_results: The maximum number of results to return. Defaults to 8. as_string: Whether to return the results as a string or a list of dictionaries. Defaults to True. + ignore_errors: Whether to ignore errors during the search. Defaults to False. Returns: The search results as a string or a list of dictionaries. """ - return await self.run_func(query, max_results=max_results, as_string=as_string) + try: + return await self.run_func(query, max_results=max_results, as_string=as_string) + except Exception as e: + # Handle errors in the API call + logger.exception(f"fail to search {query} for {e}") + if not ignore_errors: + raise e + return "" if as_string else [] diff --git a/metagpt/tools/search_engine_ddg.py b/metagpt/tools/search_engine_ddg.py index 57bc61b82..1412f20cf 100644 --- a/metagpt/tools/search_engine_ddg.py +++ b/metagpt/tools/search_engine_ddg.py @@ -5,7 +5,9 @@ import asyncio import json from concurrent import futures -from typing import Literal, overload +from typing import Literal, Optional, overload + +from pydantic import BaseModel, ConfigDict try: from duckduckgo_search import DDGS @@ -15,27 +17,17 @@ "You can install it by running the command: `pip install -e.[search-ddg]`" ) -from metagpt.config import CONFIG - -class DDGAPIWrapper: - """Wrapper around duckduckgo_search API. +class DDGAPIWrapper(BaseModel): + model_config = ConfigDict(arbitrary_types_allowed=True) - To use this module, you should have the `duckduckgo_search` Python package installed. - """ + loop: Optional[asyncio.AbstractEventLoop] = None + executor: Optional[futures.Executor] = None + proxy: Optional[str] = None - def __init__( - self, - *, - loop: asyncio.AbstractEventLoop | None = None, - executor: futures.Executor | None = None, - ): - kwargs = {} - if CONFIG.global_proxy: - kwargs["proxies"] = CONFIG.global_proxy - self.loop = loop - self.executor = executor - self.ddgs = DDGS(**kwargs) + @property + def ddgs(self): + return DDGS(proxies=self.proxy) @overload def run( diff --git a/metagpt/tools/search_engine_googleapi.py b/metagpt/tools/search_engine_googleapi.py index 8aca3aee2..66b5ba950 100644 --- a/metagpt/tools/search_engine_googleapi.py +++ b/metagpt/tools/search_engine_googleapi.py @@ -4,19 +4,16 @@ import asyncio import json +import warnings from concurrent import futures from typing import Optional from urllib.parse import urlparse import httplib2 -from pydantic import BaseModel, ConfigDict, Field, field_validator - -from metagpt.config import CONFIG -from metagpt.logs import logger +from pydantic import BaseModel, ConfigDict, model_validator try: from googleapiclient.discovery import build - from googleapiclient.errors import HttpError except ImportError: raise ImportError( "To use this module, you should have the `google-api-python-client` Python package installed. " @@ -27,40 +24,41 @@ class GoogleAPIWrapper(BaseModel): model_config = ConfigDict(arbitrary_types_allowed=True) - google_api_key: Optional[str] = Field(default=None, validate_default=True) - google_cse_id: Optional[str] = Field(default=None, validate_default=True) + api_key: str + cse_id: str loop: Optional[asyncio.AbstractEventLoop] = None executor: Optional[futures.Executor] = None + proxy: Optional[str] = None - @field_validator("google_api_key", mode="before") + @model_validator(mode="before") @classmethod - def check_google_api_key(cls, val: str): - val = val or CONFIG.google_api_key - if not val: + def validate_google(cls, values: dict) -> dict: + if "google_api_key" in values: + values.setdefault("api_key", values["google_api_key"]) + warnings.warn("`google_api_key` is deprecated, use `api_key` instead", DeprecationWarning, stacklevel=2) + + if "api_key" not in values: raise ValueError( - "To use, make sure you provide the google_api_key when constructing an object. Alternatively, " - "ensure that the environment variable GOOGLE_API_KEY is set with your API key. You can obtain " + "To use google search engine, make sure you provide the `api_key` when constructing an object. You can obtain " "an API key from https://console.cloud.google.com/apis/credentials." ) - return val - @field_validator("google_cse_id", mode="before") - @classmethod - def check_google_cse_id(cls, val: str): - val = val or CONFIG.google_cse_id - if not val: + if "google_cse_id" in values: + values.setdefault("cse_id", values["google_cse_id"]) + warnings.warn("`google_cse_id` is deprecated, use `cse_id` instead", DeprecationWarning, stacklevel=2) + + if "cse_id" not in values: raise ValueError( - "To use, make sure you provide the google_cse_id when constructing an object. Alternatively, " - "ensure that the environment variable GOOGLE_CSE_ID is set with your API key. You can obtain " - "an API key from https://programmablesearchengine.google.com/controlpanel/create." + "To use google search engine, make sure you provide the `cse_id` when constructing an object. You can obtain " + "the cse_id from https://programmablesearchengine.google.com/controlpanel/create." ) - return val + return values @property def google_api_client(self): - build_kwargs = {"developerKey": self.google_api_key} - if CONFIG.global_proxy: - parse_result = urlparse(CONFIG.global_proxy) + build_kwargs = {"developerKey": self.api_key} + if self.proxy: + parse_result = urlparse(self.proxy) proxy_type = parse_result.scheme if proxy_type == "https": proxy_type = "http" @@ -96,17 +94,11 @@ async def run( """ loop = self.loop or asyncio.get_event_loop() future = loop.run_in_executor( - self.executor, self.google_api_client.list(q=query, num=max_results, cx=self.google_cse_id).execute + self.executor, self.google_api_client.list(q=query, num=max_results, cx=self.cse_id).execute ) - try: - result = await future - # Extract the search result items from the response - search_results = result.get("items", []) - - except HttpError as e: - # Handle errors in the API call - logger.exception(f"fail to search {query} for {e}") - search_results = [] + result = await future + # Extract the search result items from the response + search_results = result.get("items", []) focus = focus or ["snippet", "link", "title"] details = [{i: j for i, j in item_dict.items() if i in focus} for item_dict in search_results] diff --git a/metagpt/tools/search_engine_serpapi.py b/metagpt/tools/search_engine_serpapi.py index 9d2d20af6..5744b1b62 100644 --- a/metagpt/tools/search_engine_serpapi.py +++ b/metagpt/tools/search_engine_serpapi.py @@ -5,18 +5,17 @@ @Author : alexanderwu @File : search_engine_serpapi.py """ +import warnings from typing import Any, Dict, Optional, Tuple import aiohttp -from pydantic import BaseModel, ConfigDict, Field, field_validator - -from metagpt.config import CONFIG +from pydantic import BaseModel, ConfigDict, Field, model_validator class SerpAPIWrapper(BaseModel): model_config = ConfigDict(arbitrary_types_allowed=True) - search_engine: Any = None #: :meta private: + api_key: str params: dict = Field( default_factory=lambda: { "engine": "google", @@ -25,21 +24,22 @@ class SerpAPIWrapper(BaseModel): "hl": "en", } ) - # should add `validate_default=True` to check with default value - serpapi_api_key: Optional[str] = Field(default=None, validate_default=True) aiosession: Optional[aiohttp.ClientSession] = None + proxy: Optional[str] = None - @field_validator("serpapi_api_key", mode="before") + @model_validator(mode="before") @classmethod - def check_serpapi_api_key(cls, val: str): - val = val or CONFIG.serpapi_api_key - if not val: + def validate_serpapi(cls, values: dict) -> dict: + if "serpapi_api_key" in values: + values.setdefault("api_key", values["serpapi_api_key"]) + warnings.warn("`serpapi_api_key` is deprecated, use `api_key` instead", DeprecationWarning, stacklevel=2) + + if "api_key" not in values: raise ValueError( - "To use, make sure you provide the serpapi_api_key when constructing an object. Alternatively, " - "ensure that the environment variable SERPAPI_API_KEY is set with your API key. You can obtain " - "an API key from https://serpapi.com/." + "To use serpapi search engine, make sure you provide the `api_key` when constructing an object. You can obtain" + " an API key from https://serpapi.com/." ) - return val + return values async def run(self, query, max_results: int = 8, as_string: bool = True, **kwargs: Any) -> str: """Run query through SerpAPI and parse result async.""" @@ -60,10 +60,12 @@ def construct_url_and_params() -> Tuple[str, Dict[str, str]]: url, params = construct_url_and_params() if not self.aiosession: async with aiohttp.ClientSession() as session: - async with session.get(url, params=params) as response: + async with session.get(url, params=params, proxy=self.proxy) as response: + response.raise_for_status() res = await response.json() else: - async with self.aiosession.get(url, params=params) as response: + async with self.aiosession.get(url, params=params, proxy=self.proxy) as response: + response.raise_for_status() res = await response.json() return res @@ -71,7 +73,7 @@ def construct_url_and_params() -> Tuple[str, Dict[str, str]]: def get_params(self, query: str) -> Dict[str, str]: """Get parameters for SerpAPI.""" _params = { - "api_key": self.serpapi_api_key, + "api_key": self.api_key, "q": query, } params = {**self.params, **_params} diff --git a/metagpt/tools/search_engine_serper.py b/metagpt/tools/search_engine_serper.py index 3dc1d3591..ba2fb4f93 100644 --- a/metagpt/tools/search_engine_serper.py +++ b/metagpt/tools/search_engine_serper.py @@ -6,33 +6,34 @@ @File : search_engine_serpapi.py """ import json +import warnings from typing import Any, Dict, Optional, Tuple import aiohttp -from pydantic import BaseModel, ConfigDict, Field, field_validator - -from metagpt.config import CONFIG +from pydantic import BaseModel, ConfigDict, Field, model_validator class SerperWrapper(BaseModel): model_config = ConfigDict(arbitrary_types_allowed=True) - search_engine: Any = None #: :meta private: + api_key: str payload: dict = Field(default_factory=lambda: {"page": 1, "num": 10}) - serper_api_key: Optional[str] = Field(default=None, validate_default=True) aiosession: Optional[aiohttp.ClientSession] = None + proxy: Optional[str] = None - @field_validator("serper_api_key", mode="before") + @model_validator(mode="before") @classmethod - def check_serper_api_key(cls, val: str): - val = val or CONFIG.serper_api_key - if not val: + def validate_serper(cls, values: dict) -> dict: + if "serper_api_key" in values: + values.setdefault("api_key", values["serper_api_key"]) + warnings.warn("`serper_api_key` is deprecated, use `api_key` instead", DeprecationWarning, stacklevel=2) + + if "api_key" not in values: raise ValueError( - "To use, make sure you provide the serper_api_key when constructing an object. Alternatively, " - "ensure that the environment variable SERPER_API_KEY is set with your API key. You can obtain " + "To use serper search engine, make sure you provide the `api_key` when constructing an object. You can obtain " "an API key from https://serper.dev/." ) - return val + return values async def run(self, query: str, max_results: int = 8, as_string: bool = True, **kwargs: Any) -> str: """Run query through Serper and parse result async.""" @@ -54,10 +55,12 @@ def construct_url_and_payload_and_headers() -> Tuple[str, Dict[str, str]]: url, payloads, headers = construct_url_and_payload_and_headers() if not self.aiosession: async with aiohttp.ClientSession() as session: - async with session.post(url, data=payloads, headers=headers) as response: + async with session.post(url, data=payloads, headers=headers, proxy=self.proxy) as response: + response.raise_for_status() res = await response.json() else: - async with self.aiosession.get.post(url, data=payloads, headers=headers) as response: + async with self.aiosession.get.post(url, data=payloads, headers=headers, proxy=self.proxy) as response: + response.raise_for_status() res = await response.json() return res @@ -74,7 +77,7 @@ def get_payloads(self, queries: list[str], max_results: int) -> Dict[str, str]: return json.dumps(payloads, sort_keys=True) def get_headers(self) -> Dict[str, str]: - headers = {"X-API-KEY": self.serper_api_key, "Content-Type": "application/json"} + headers = {"X-API-KEY": self.api_key, "Content-Type": "application/json"} return headers @staticmethod diff --git a/metagpt/tools/tool_convert.py b/metagpt/tools/tool_convert.py new file mode 100644 index 000000000..fc29d0693 --- /dev/null +++ b/metagpt/tools/tool_convert.py @@ -0,0 +1,83 @@ +import inspect + +from metagpt.utils.parse_docstring import GoogleDocstringParser, remove_spaces + + +def convert_code_to_tool_schema(obj, include: list[str] = []): + docstring = inspect.getdoc(obj) + assert docstring, "no docstring found for the objects, skip registering" + + if inspect.isclass(obj): + schema = {"type": "class", "description": remove_spaces(docstring), "methods": {}} + for name, method in inspect.getmembers(obj, inspect.isfunction): + if include and name not in include: + continue + # method_doc = inspect.getdoc(method) + method_doc = get_class_method_docstring(obj, name) + if method_doc: + schema["methods"][name] = function_docstring_to_schema(method, method_doc) + + elif inspect.isfunction(obj): + schema = function_docstring_to_schema(obj, docstring) + + return schema + + +def function_docstring_to_schema(fn_obj, docstring): + function_type = "function" if not inspect.iscoroutinefunction(fn_obj) else "async_function" + return {"type": function_type, **docstring_to_schema(docstring)} + + +def docstring_to_schema(docstring: str): + if docstring is None: + return {} + + parser = GoogleDocstringParser(docstring=docstring) + + # 匹配简介部分 + description = parser.parse_desc() + + # 匹配Args部分 + params = parser.parse_params() + parameter_schema = {"properties": {}, "required": []} + for param in params: + param_name, param_type, param_desc = param + # check required or optional + is_optional, param_type = parser.check_and_parse_optional(param_type) + if not is_optional: + parameter_schema["required"].append(param_name) + # type and desc + param_dict = {"type": param_type, "description": remove_spaces(param_desc)} + # match Default for optional args + has_default_val, default_val = parser.check_and_parse_default_value(param_desc) + if has_default_val: + param_dict["default"] = default_val + # match Enum + has_enum, enum_vals = parser.check_and_parse_enum(param_desc) + if has_enum: + param_dict["enum"] = enum_vals + # add to parameter schema + parameter_schema["properties"].update({param_name: param_dict}) + + # 匹配Returns部分 + returns = parser.parse_returns() + + # 构建YAML字典 + schema = { + "description": description, + "parameters": parameter_schema, + } + if returns: + schema["returns"] = [{"type": ret[0], "description": remove_spaces(ret[1])} for ret in returns] + + return schema + + +def get_class_method_docstring(cls, method_name): + """Retrieve a method's docstring, searching the class hierarchy if necessary.""" + for base_class in cls.__mro__: + if method_name in base_class.__dict__: + method = base_class.__dict__[method_name] + if method.__doc__: + return method.__doc__ + return None # No docstring found in the class hierarchy diff --git a/metagpt/tools/tool_data_type.py b/metagpt/tools/tool_data_type.py new file mode 100644 index 000000000..0ae46fa5c --- /dev/null +++ b/metagpt/tools/tool_data_type.py @@ -0,0 +1,18 @@ +from pydantic import BaseModel + + +class ToolTypeDef(BaseModel): + name: str + desc: str = "" + usage_prompt: str = "" + + +class ToolSchema(BaseModel): + description: str + + +class Tool(BaseModel): + name: str + path: str + schemas: dict = {} + code: str = "" diff --git a/metagpt/tools/tool_registry.py b/metagpt/tools/tool_registry.py new file mode 100644 index 000000000..5fbd39421 --- /dev/null +++ b/metagpt/tools/tool_registry.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2023/01/12 17:07 +@Author : garylin2099 +@File : tool_registry.py +""" +from __future__ import annotations + +import inspect +import os +import re +from collections import defaultdict + +import yaml +from pydantic import BaseModel, field_validator + +from metagpt.const import TOOL_SCHEMA_PATH +from metagpt.logs import logger +from metagpt.tools.tool_convert import convert_code_to_tool_schema +from metagpt.tools.tool_data_type import Tool, ToolSchema, ToolTypeDef +from metagpt.tools.tool_type import ToolType + + +class ToolRegistry(BaseModel): + tools: dict = {} + tool_types: dict = {} + tools_by_types: dict = defaultdict(dict) # two-layer k-v, {tool_type: {tool_name: {...}, ...}, ...} + + @field_validator("tool_types", mode="before") + @classmethod + def init_tool_types(cls, tool_types: ToolType): + return {tool_type.type_name: tool_type.value for tool_type in tool_types} + + def register_tool( + self, + tool_name, + tool_path, + schema_path="", + tool_code="", + tool_type="other", + tool_source_object=None, + include_functions=[], + verbose=False, + ): + if self.has_tool(tool_name): + return + + if tool_type not in self.tool_types: + # register new tool type on the fly + logger.warning( + f"{tool_type} not previously defined, will create a temporary tool type with just a name. This tool type is only effective during this runtime. You may consider add this tool type with more configs permanently at metagpt.tools.tool_type" + ) + temp_tool_type_obj = ToolTypeDef(name=tool_type) + self.tool_types[tool_type] = temp_tool_type_obj + if verbose: + logger.info(f"tool type {tool_type} registered") + + schema_path = schema_path or TOOL_SCHEMA_PATH / tool_type / f"{tool_name}.yml" + + schemas = make_schema(tool_source_object, include_functions, schema_path) + + if not schemas: + return + + schemas["tool_path"] = tool_path # corresponding code file path of the tool + try: + ToolSchema(**schemas) # validation + except Exception: + pass + # logger.warning( + # f"{tool_name} schema not conforms to required format, but will be used anyway. Mismatch: {e}" + # ) + + tool = Tool(name=tool_name, path=tool_path, schemas=schemas, code=tool_code) + self.tools[tool_name] = tool + self.tools_by_types[tool_type][tool_name] = tool + if verbose: + logger.info(f"{tool_name} registered") + logger.info(f"schema made at {str(schema_path)}, can be used for checking") + + def has_tool(self, key: str) -> Tool: + return key in self.tools + + def get_tool(self, key) -> Tool: + return self.tools.get(key) + + def get_tools_by_type(self, key) -> dict[str, Tool]: + return self.tools_by_types.get(key, {}) + + def has_tool_type(self, key) -> bool: + return key in self.tool_types + + def get_tool_type(self, key) -> ToolType: + return self.tool_types.get(key) + + def get_tool_types(self) -> dict[str, ToolType]: + return self.tool_types + + +# Registry instance +TOOL_REGISTRY = ToolRegistry(tool_types=ToolType) + + +def register_tool(tool_type: str = "other", schema_path: str = "", **kwargs): + """register a tool to registry""" + + def decorator(cls): + # Get the file path where the function / class is defined and the source code + file_path = inspect.getfile(cls) + if "metagpt" in file_path: + file_path = re.search("metagpt.+", file_path).group(0) + source_code = inspect.getsource(cls) + + TOOL_REGISTRY.register_tool( + tool_name=cls.__name__, + tool_path=file_path, + schema_path=schema_path, + tool_code=source_code, + tool_type=tool_type, + tool_source_object=cls, + **kwargs, + ) + return cls + + return decorator + + +def make_schema(tool_source_object, include, path): + os.makedirs(os.path.dirname(path), exist_ok=True) # Create the necessary directories + try: + schema = convert_code_to_tool_schema(tool_source_object, include=include) + with open(path, "w", encoding="utf-8") as f: + yaml.dump(schema, f, sort_keys=False) + # import json + # with open(str(path).replace("yml", "json"), "w", encoding="utf-8") as f: + # json.dump(schema, f, ensure_ascii=False, indent=4) + except Exception as e: + schema = {} + logger.error(f"Fail to make schema: {e}") + + return schema + + +def validate_tool_names(tools: list[str], return_tool_object=False) -> list[str]: + valid_tools = [] + for tool_name in tools: + if not TOOL_REGISTRY.has_tool(tool_name): + logger.warning( + f"Specified tool {tool_name} not found and was skipped. Check if you have registered it properly" + ) + else: + valid_tool = TOOL_REGISTRY.get_tool(tool_name) if return_tool_object else tool_name + valid_tools.append(valid_tool) + return valid_tools diff --git a/metagpt/tools/tool_type.py b/metagpt/tools/tool_type.py new file mode 100644 index 000000000..6fa971c56 --- /dev/null +++ b/metagpt/tools/tool_type.py @@ -0,0 +1,55 @@ +from enum import Enum + +from metagpt.prompts.tool_types import ( + DATA_PREPROCESS_PROMPT, + FEATURE_ENGINEERING_PROMPT, + IMAGE2WEBPAGE_PROMPT, + MODEL_EVALUATE_PROMPT, + MODEL_TRAIN_PROMPT, +) +from metagpt.tools.tool_data_type import ToolTypeDef + + +class ToolType(Enum): + EDA = ToolTypeDef(name="eda", desc="For performing exploratory data analysis") + DATA_PREPROCESS = ToolTypeDef( + name="data_preprocess", + desc="Only for changing value inplace.", + usage_prompt=DATA_PREPROCESS_PROMPT, + ) + FEATURE_ENGINEERING = ToolTypeDef( + name="feature_engineering", + desc="Only for creating new columns for input data.", + usage_prompt=FEATURE_ENGINEERING_PROMPT, + ) + MODEL_TRAIN = ToolTypeDef( + name="model_train", + desc="Only for training model.", + usage_prompt=MODEL_TRAIN_PROMPT, + ) + MODEL_EVALUATE = ToolTypeDef( + name="model_evaluate", + desc="Only for evaluating model.", + usage_prompt=MODEL_EVALUATE_PROMPT, + ) + STABLE_DIFFUSION = ToolTypeDef( + name="stable_diffusion", + desc="Related to text2image, image2image using stable diffusion model.", + ) + IMAGE2WEBPAGE = ToolTypeDef( + name="image2webpage", + desc="For converting image into webpage code.", + usage_prompt=IMAGE2WEBPAGE_PROMPT, + ) + WEBSCRAPING = ToolTypeDef( + name="web_scraping", + desc="For scraping data from web pages.", + ) + OTHER = ToolTypeDef(name="other", desc="Any tools not in the defined categories") + + def __missing__(self, key): + return self.OTHER + + @property + def type_name(self): + return self.value.name diff --git a/metagpt/tools/ut_writer.py b/metagpt/tools/ut_writer.py index f2f2bf51c..243871aff 100644 --- a/metagpt/tools/ut_writer.py +++ b/metagpt/tools/ut_writer.py @@ -4,6 +4,7 @@ import json from pathlib import Path +from metagpt.config2 import config from metagpt.provider.openai_api import OpenAILLM as GPTAPI from metagpt.utils.common import awrite @@ -281,6 +282,6 @@ async def gpt_msgs_to_code(self, messages: list) -> str: """Choose based on different calling methods""" result = "" if self.chatgpt_method == "API": - result = await GPTAPI().aask_code(messages=messages) + result = await GPTAPI(config.get_openai_llm()).aask_code(messages=messages) return result diff --git a/metagpt/tools/web_browser_engine.py b/metagpt/tools/web_browser_engine.py index abd84cc8d..01339e51a 100644 --- a/metagpt/tools/web_browser_engine.py +++ b/metagpt/tools/web_browser_engine.py @@ -1,40 +1,95 @@ #!/usr/bin/env python -""" -@Modified By: mashenquan, 2023/8/20. Remove global configuration `CONFIG`, enable configuration support for business isolation. -""" - +# -*- coding: utf-8 -*- from __future__ import annotations import importlib -from typing import Any, Callable, Coroutine, overload +from typing import Any, Callable, Coroutine, Optional, Union, overload + +from pydantic import BaseModel, ConfigDict, model_validator -from metagpt.config import CONFIG +from metagpt.configs.browser_config import BrowserConfig from metagpt.tools import WebBrowserEngineType from metagpt.utils.parse_html import WebPage -class WebBrowserEngine: - def __init__( - self, - engine: WebBrowserEngineType | None = None, - run_func: Callable[..., Coroutine[Any, Any, WebPage | list[WebPage]]] | None = None, - ): - engine = engine or CONFIG.web_browser_engine - if engine is None: - raise NotImplementedError +class WebBrowserEngine(BaseModel): + """Defines a web browser engine configuration for automated browsing and data extraction. + + This class encapsulates the configuration and operational logic for different web browser engines, + such as Playwright, Selenium, or custom implementations. It provides a unified interface to run + browser automation tasks. + + Attributes: + model_config: Configuration dictionary allowing arbitrary types and extra fields. + engine: The type of web browser engine to use. + run_func: An optional coroutine function to run the browser engine. + proxy: An optional proxy server URL to use with the browser engine. + """ + + model_config = ConfigDict(arbitrary_types_allowed=True, extra="allow") + + engine: WebBrowserEngineType = WebBrowserEngineType.PLAYWRIGHT + run_func: Optional[Callable[..., Coroutine[Any, Any, Union[WebPage, list[WebPage]]]]] = None + proxy: Optional[str] = None + + @model_validator(mode="after") + def validate_extra(self): + """Validates and processes extra configuration data after model initialization. + + This method is automatically called by Pydantic to validate and process any extra configuration + data provided to the model. It ensures that the extra data is properly integrated into the model's + configuration and operational logic. + + Returns: + The instance itself after processing the extra data. + """ + data = self.model_dump(exclude={"engine"}, exclude_none=True, exclude_defaults=True) + if self.model_extra: + data.update(self.model_extra) + self._process_extra(**data) + return self - if WebBrowserEngineType(engine) is WebBrowserEngineType.PLAYWRIGHT: + def _process_extra(self, **kwargs): + """Processes extra configuration data to set up the browser engine run function. + + Depending on the specified engine type, this method dynamically imports and configures + the appropriate browser engine wrapper and its run function. + + Args: + **kwargs: Arbitrary keyword arguments representing extra configuration data. + + Raises: + NotImplementedError: If the engine type is not supported. + """ + if self.engine is WebBrowserEngineType.PLAYWRIGHT: module = "metagpt.tools.web_browser_engine_playwright" - run_func = importlib.import_module(module).PlaywrightWrapper().run - elif WebBrowserEngineType(engine) is WebBrowserEngineType.SELENIUM: + run_func = importlib.import_module(module).PlaywrightWrapper(**kwargs).run + elif self.engine is WebBrowserEngineType.SELENIUM: module = "metagpt.tools.web_browser_engine_selenium" - run_func = importlib.import_module(module).SeleniumWrapper().run - elif WebBrowserEngineType(engine) is WebBrowserEngineType.CUSTOM: - run_func = run_func + run_func = importlib.import_module(module).SeleniumWrapper(**kwargs).run + elif self.engine is WebBrowserEngineType.CUSTOM: + run_func = self.run_func else: raise NotImplementedError self.run_func = run_func - self.engine = engine + + @classmethod + def from_browser_config(cls, config: BrowserConfig, **kwargs): + """Creates a WebBrowserEngine instance from a BrowserConfig object and additional keyword arguments. + + This class method facilitates the creation of a WebBrowserEngine instance by extracting + configuration data from a BrowserConfig object and optionally merging it with additional + keyword arguments. + + Args: + config: A BrowserConfig object containing base configuration data. + **kwargs: Optional additional keyword arguments to override or extend the configuration. + + Returns: + A new instance of WebBrowserEngine configured according to the provided arguments. + """ + data = config.model_dump() + return cls(**data, **kwargs) @overload async def run(self, url: str) -> WebPage: @@ -45,4 +100,16 @@ async def run(self, url: str, *urls: str) -> list[WebPage]: ... async def run(self, url: str, *urls: str) -> WebPage | list[WebPage]: + """Runs the browser engine to load one or more web pages. + + This method is the implementation of the overloaded run signatures. It delegates the task + of loading web pages to the configured run function, handling either a single URL or multiple URLs. + + Args: + url: The URL of the first web page to load. + *urls: Additional URLs of web pages to load, if any. + + Returns: + A WebPage object if a single URL is provided, or a list of WebPage objects if multiple URLs are provided. + """ return await self.run_func(url, *urls) diff --git a/metagpt/tools/web_browser_engine_playwright.py b/metagpt/tools/web_browser_engine_playwright.py index a45f6a12e..2df288b1a 100644 --- a/metagpt/tools/web_browser_engine_playwright.py +++ b/metagpt/tools/web_browser_engine_playwright.py @@ -1,23 +1,21 @@ #!/usr/bin/env python -""" -@Modified By: mashenquan, 2023/8/20. Remove global configuration `CONFIG`, enable configuration support for business isolation. -""" +# -*- coding: utf-8 -*- from __future__ import annotations import asyncio import sys from pathlib import Path -from typing import Literal +from typing import Literal, Optional from playwright.async_api import async_playwright +from pydantic import BaseModel, Field, PrivateAttr -from metagpt.config import CONFIG from metagpt.logs import logger from metagpt.utils.parse_html import WebPage -class PlaywrightWrapper: +class PlaywrightWrapper(BaseModel): """Wrapper around Playwright. To use this module, you should have the `playwright` Python package installed and ensure that @@ -26,26 +24,23 @@ class PlaywrightWrapper: command `playwright install` for the first time. """ - def __init__( - self, - browser_type: Literal["chromium", "firefox", "webkit"] | None = None, - launch_kwargs: dict | None = None, - **kwargs, - ) -> None: - if browser_type is None: - browser_type = CONFIG.playwright_browser_type - self.browser_type = browser_type - launch_kwargs = launch_kwargs or {} - if CONFIG.global_proxy and "proxy" not in launch_kwargs: + browser_type: Literal["chromium", "firefox", "webkit"] = "chromium" + launch_kwargs: dict = Field(default_factory=dict) + proxy: Optional[str] = None + context_kwargs: dict = Field(default_factory=dict) + _has_run_precheck: bool = PrivateAttr(False) + + def __init__(self, **kwargs): + super().__init__(**kwargs) + + launch_kwargs = self.launch_kwargs + if self.proxy and "proxy" not in launch_kwargs: args = launch_kwargs.get("args", []) if not any(str.startswith(i, "--proxy-server=") for i in args): - launch_kwargs["proxy"] = {"server": CONFIG.global_proxy} - self.launch_kwargs = launch_kwargs - context_kwargs = {} + launch_kwargs["proxy"] = {"server": self.proxy} + if "ignore_https_errors" in kwargs: - context_kwargs["ignore_https_errors"] = kwargs["ignore_https_errors"] - self._context_kwargs = context_kwargs - self._has_run_precheck = False + self.context_kwargs["ignore_https_errors"] = kwargs["ignore_https_errors"] async def run(self, url: str, *urls: str) -> WebPage | list[WebPage]: async with async_playwright() as ap: @@ -59,7 +54,7 @@ async def run(self, url: str, *urls: str) -> WebPage | list[WebPage]: return await _scrape(browser, url) async def _scrape(self, browser, url): - context = await browser.new_context(**self._context_kwargs) + context = await browser.new_context(**self.context_kwargs) page = await context.new_page() async with page: try: @@ -79,8 +74,8 @@ async def _run_precheck(self, browser_type): executable_path = Path(browser_type.executable_path) if not executable_path.exists() and "executable_path" not in self.launch_kwargs: kwargs = {} - if CONFIG.global_proxy: - kwargs["env"] = {"ALL_PROXY": CONFIG.global_proxy} + if self.proxy: + kwargs["env"] = {"ALL_PROXY": self.proxy} await _install_browsers(self.browser_type, **kwargs) if self._has_run_precheck: diff --git a/metagpt/tools/web_browser_engine_selenium.py b/metagpt/tools/web_browser_engine_selenium.py index 70b651935..3b1682291 100644 --- a/metagpt/tools/web_browser_engine_selenium.py +++ b/metagpt/tools/web_browser_engine_selenium.py @@ -1,7 +1,5 @@ #!/usr/bin/env python -""" -@Modified By: mashenquan, 2023/8/20. Remove global configuration `CONFIG`, enable configuration support for business isolation. -""" +# -*- coding: utf-8 -*- from __future__ import annotations @@ -9,19 +7,19 @@ import importlib from concurrent import futures from copy import deepcopy -from typing import Literal +from typing import Callable, Literal, Optional +from pydantic import BaseModel, ConfigDict, Field, PrivateAttr from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.wait import WebDriverWait from webdriver_manager.core.download_manager import WDMDownloadManager from webdriver_manager.core.http import WDMHttpClient -from metagpt.config import CONFIG from metagpt.utils.parse_html import WebPage -class SeleniumWrapper: +class SeleniumWrapper(BaseModel): """Wrapper around Selenium. To use this module, you should check the following: @@ -33,27 +31,28 @@ class SeleniumWrapper: can scrape web pages using the Selenium WebBrowserEngine. """ - def __init__( - self, - browser_type: Literal["chrome", "firefox", "edge", "ie"] | None = None, - launch_kwargs: dict | None = None, - *, - loop: asyncio.AbstractEventLoop | None = None, - executor: futures.Executor | None = None, - ) -> None: - if browser_type is None: - browser_type = CONFIG.selenium_browser_type - self.browser_type = browser_type - launch_kwargs = launch_kwargs or {} - if CONFIG.global_proxy and "proxy-server" not in launch_kwargs: - launch_kwargs["proxy-server"] = CONFIG.global_proxy - - self.executable_path = launch_kwargs.pop("executable_path", None) - self.launch_args = [f"--{k}={v}" for k, v in launch_kwargs.items()] - self._has_run_precheck = False - self._get_driver = None - self.loop = loop - self.executor = executor + model_config = ConfigDict(arbitrary_types_allowed=True) + + browser_type: Literal["chrome", "firefox", "edge", "ie"] = "chrome" + launch_kwargs: dict = Field(default_factory=dict) + proxy: Optional[str] = None + loop: Optional[asyncio.AbstractEventLoop] = None + executor: Optional[futures.Executor] = None + _has_run_precheck: bool = PrivateAttr(False) + _get_driver: Optional[Callable] = PrivateAttr(None) + + def __init__(self, **kwargs) -> None: + super().__init__(**kwargs) + if self.proxy and "proxy-server" not in self.launch_kwargs: + self.launch_kwargs["proxy-server"] = self.proxy + + @property + def launch_args(self): + return [f"--{k}={v}" for k, v in self.launch_kwargs.items() if k != "executable_path"] + + @property + def executable_path(self): + return self.launch_kwargs.get("executable_path") async def run(self, url: str, *urls: str) -> WebPage | list[WebPage]: await self._run_precheck() @@ -70,7 +69,9 @@ async def _run_precheck(self): self.loop = self.loop or asyncio.get_event_loop() self._get_driver = await self.loop.run_in_executor( self.executor, - lambda: _gen_get_driver_func(self.browser_type, *self.launch_args, executable_path=self.executable_path), + lambda: _gen_get_driver_func( + self.browser_type, *self.launch_args, executable_path=self.executable_path, proxy=self.proxy + ), ) self._has_run_precheck = True @@ -96,13 +97,17 @@ def _scrape_website(self, url): class WDMHttpProxyClient(WDMHttpClient): + def __init__(self, proxy: str = None): + super().__init__() + self.proxy = proxy + def get(self, url, **kwargs): - if "proxies" not in kwargs and CONFIG.global_proxy: - kwargs["proxies"] = {"all_proxy": CONFIG.global_proxy} + if "proxies" not in kwargs and self.proxy: + kwargs["proxies"] = {"all_proxy": self.proxy} return super().get(url, **kwargs) -def _gen_get_driver_func(browser_type, *args, executable_path=None): +def _gen_get_driver_func(browser_type, *args, executable_path=None, proxy=None): WebDriver = getattr(importlib.import_module(f"selenium.webdriver.{browser_type}.webdriver"), "WebDriver") Service = getattr(importlib.import_module(f"selenium.webdriver.{browser_type}.service"), "Service") Options = getattr(importlib.import_module(f"selenium.webdriver.{browser_type}.options"), "Options") @@ -110,7 +115,7 @@ def _gen_get_driver_func(browser_type, *args, executable_path=None): if not executable_path: module_name, type_name = _webdriver_manager_types[browser_type] DriverManager = getattr(importlib.import_module(module_name), type_name) - driver_manager = DriverManager(download_manager=WDMDownloadManager(http_client=WDMHttpProxyClient())) + driver_manager = DriverManager(download_manager=WDMDownloadManager(http_client=WDMHttpProxyClient(proxy=proxy))) # driver_manager.driver_cache.find_driver(driver_manager.driver)) executable_path = driver_manager.install() diff --git a/metagpt/utils/common.py b/metagpt/utils/common.py index 2943b5dce..015902c3d 100644 --- a/metagpt/utils/common.py +++ b/metagpt/utils/common.py @@ -12,7 +12,9 @@ from __future__ import annotations import ast +import base64 import contextlib +import csv import importlib import inspect import json @@ -22,13 +24,16 @@ import sys import traceback import typing +from io import BytesIO from pathlib import Path -from typing import Any, List, Tuple, Union +from typing import Any, Callable, List, Tuple, Union import aiofiles import loguru +import requests +from PIL import Image from pydantic_core import to_jsonable_python -from tenacity import RetryCallState, _utils +from tenacity import RetryCallState, RetryError, _utils from metagpt.const import MESSAGE_ROUTE_TO_ALL from metagpt.logs import logger @@ -335,6 +340,14 @@ def print_members(module, indent=0): print(f"{prefix}Method: {name}") +def get_function_schema(func: Callable) -> dict[str, Union[dict, Any, str]]: + sig = inspect.signature(func) + parameters = sig.parameters + return_type = sig.return_annotation + param_schema = {name: parameter.annotation for name, parameter in parameters.items()} + return {"input_params": param_schema, "return_type": return_type, "func_desc": func.__doc__, "func": func} + + def parse_recipient(text): # FIXME: use ActionNode instead. pattern = r"## Send To:\s*([A-Za-z]+)\s*?" # hard code for now @@ -348,6 +361,31 @@ def parse_recipient(text): return "" +def create_func_call_config(func_schema: dict) -> dict: + """Create new function call config""" + tools = [{"type": "function", "function": func_schema}] + tool_choice = {"type": "function", "function": {"name": func_schema["name"]}} + return { + "tools": tools, + "tool_choice": tool_choice, + } + + +def remove_comments(code_str: str) -> str: + """Remove comments from code.""" + pattern = r"(\".*?\"|\'.*?\')|(\#.*?$)" + + def replace_func(match): + if match.group(2) is not None: + return "" + else: + return match.group(1) + + clean_code = re.sub(pattern, replace_func, code_str, flags=re.MULTILINE) + clean_code = os.linesep.join([s.rstrip() for s in clean_code.splitlines() if s.strip()]) + return clean_code + + def get_class_name(cls) -> str: """Return class name""" return f"{cls.__module__}.{cls.__name__}" @@ -381,12 +419,12 @@ def any_to_str_set(val) -> set: return res -def is_subscribed(message: "Message", tags: set): +def is_send_to(message: "Message", addresses: set): """Return whether it's consumer""" if MESSAGE_ROUTE_TO_ALL in message.send_to: return True - for i in tags: + for i in addresses: if i in message.send_to: return True return False @@ -456,13 +494,36 @@ def read_json_file(json_file: str, encoding="utf-8") -> list[Any]: return data -def write_json_file(json_file: str, data: list, encoding=None): +def write_json_file(json_file: str, data: list, encoding: str = None, indent: int = 4): folder_path = Path(json_file).parent if not folder_path.exists(): folder_path.mkdir(parents=True, exist_ok=True) with open(json_file, "w", encoding=encoding) as fout: - json.dump(data, fout, ensure_ascii=False, indent=4, default=to_jsonable_python) + json.dump(data, fout, ensure_ascii=False, indent=indent, default=to_jsonable_python) + + +def read_csv_to_list(curr_file: str, header=False, strip_trail=True): + """ + Reads in a csv file to a list of list. If header is True, it returns a + tuple with (header row, all rows) + ARGS: + curr_file: path to the current csv file. + RETURNS: + List of list where the component lists are the rows of the file. + """ + logger.debug(f"start read csv: {curr_file}") + analysis_list = [] + with open(curr_file) as f_analysis_file: + data_reader = csv.reader(f_analysis_file, delimiter=",") + for count, row in enumerate(data_reader): + if strip_trail: + row = [i.strip() for i in row] + analysis_list += [row] + if not header: + return analysis_list + else: + return analysis_list[0], analysis_list[1:] def import_class(class_name: str, module_name: str) -> type: @@ -505,7 +566,7 @@ async def wrapper(self, *args, **kwargs): self.rc.memory.delete(self.latest_observed_msg) # raise again to make it captured outside raise Exception(format_trackback_info(limit=None)) - except Exception: + except Exception as e: if self.latest_observed_msg: logger.warning( "There is a exception in role's execution, in order to resume, " @@ -514,6 +575,12 @@ async def wrapper(self, *args, **kwargs): # remove role newest observed msg to make it observed again self.rc.memory.delete(self.latest_observed_msg) # raise again to make it captured outside + if isinstance(e, RetryError): + last_error = e.last_attempt._exception + name = any_to_str(last_error) + if re.match(r"^openai\.", name) or re.match(r"^httpx\.", name): + raise last_error + raise Exception(format_trackback_info(limit=None)) return wrapper @@ -567,3 +634,45 @@ def list_files(root: str | Path) -> List[Path]: except Exception as e: logger.error(f"Error: {e}") return files + + +def is_coroutine_func(func: Callable) -> bool: + return inspect.iscoroutinefunction(func) + + +def load_mc_skills_code(skill_names: list[str] = None, skills_dir: Path = None) -> list[str]: + """load mincraft skill from js files""" + if not skills_dir: + skills_dir = Path(__file__).parent.absolute() + if skill_names is None: + skill_names = [skill[:-3] for skill in os.listdir(f"{skills_dir}") if skill.endswith(".js")] + skills = [skills_dir.joinpath(f"{skill_name}.js").read_text() for skill_name in skill_names] + return skills + + +def encode_image(image_path_or_pil: Union[Path, Image], encoding: str = "utf-8") -> str: + """encode image from file or PIL.Image into base64""" + if isinstance(image_path_or_pil, Image.Image): + buffer = BytesIO() + image_path_or_pil.save(buffer, format="JPEG") + bytes_data = buffer.getvalue() + else: + if not image_path_or_pil.exists(): + raise FileNotFoundError(f"{image_path_or_pil} not exists") + with open(str(image_path_or_pil), "rb") as image_file: + bytes_data = image_file.read() + return base64.b64encode(bytes_data).decode(encoding) + + +def decode_image(img_url_or_b64: str) -> Image: + """decode image from url or base64 into PIL.Image""" + if img_url_or_b64.startswith("http"): + # image http(s) url + resp = requests.get(img_url_or_b64) + img = Image.open(BytesIO(resp.content)) + else: + # image b64_json + b64_data = re.sub("^data:image/.+;base64,", "", img_url_or_b64) + img_data = BytesIO(base64.b64decode(b64_data)) + img = Image.open(img_data) + return img diff --git a/metagpt/utils/cost_manager.py b/metagpt/utils/cost_manager.py index ce53f2285..7bf5154b6 100644 --- a/metagpt/utils/cost_manager.py +++ b/metagpt/utils/cost_manager.py @@ -80,3 +80,20 @@ def get_total_cost(self): def get_costs(self) -> Costs: """Get all costs""" return Costs(self.total_prompt_tokens, self.total_completion_tokens, self.total_cost, self.total_budget) + + +class TokenCostManager(CostManager): + """open llm model is self-host, it's free and without cost""" + + def update_cost(self, prompt_tokens, completion_tokens, model): + """ + Update the total cost, prompt tokens, and completion tokens. + + Args: + prompt_tokens (int): The number of tokens used in the prompt. + completion_tokens (int): The number of tokens used in the completion. + model (str): The model used for the API call. + """ + self.total_prompt_tokens += prompt_tokens + self.total_completion_tokens += completion_tokens + logger.info(f"prompt_tokens: {prompt_tokens}, completion_tokens: {completion_tokens}") diff --git a/metagpt/utils/dependency_file.py b/metagpt/utils/dependency_file.py index 7cf9a1d49..d3add1171 100644 --- a/metagpt/utils/dependency_file.py +++ b/metagpt/utils/dependency_file.py @@ -9,6 +9,7 @@ from __future__ import annotations import json +import re from pathlib import Path from typing import Set @@ -36,7 +37,9 @@ async def load(self): """Load dependencies from the file asynchronously.""" if not self._filename.exists(): return - self._dependencies = json.loads(await aread(self._filename)) + json_data = await aread(self._filename) + json_data = re.sub(r"\\+", "/", json_data) # Compatible with windows path + self._dependencies = json.loads(json_data) @handle_exception async def save(self): @@ -57,20 +60,22 @@ async def update(self, filename: Path | str, dependencies: Set[Path | str], pers root = self._filename.parent try: - key = Path(filename).relative_to(root) + key = Path(filename).relative_to(root).as_posix() except ValueError: key = filename - + key = str(key) if dependencies: relative_paths = [] for i in dependencies: try: - relative_paths.append(str(Path(i).relative_to(root))) + s = str(Path(i).relative_to(root).as_posix()) except ValueError: - relative_paths.append(str(i)) - self._dependencies[str(key)] = relative_paths - elif str(key) in self._dependencies: - del self._dependencies[str(key)] + s = str(i) + relative_paths.append(s) + + self._dependencies[key] = relative_paths + elif key in self._dependencies: + del self._dependencies[key] if persist: await self.save() @@ -87,7 +92,7 @@ async def get(self, filename: Path | str, persist=True): root = self._filename.parent try: - key = Path(filename).relative_to(root) + key = Path(filename).relative_to(root).as_posix() except ValueError: key = filename return set(self._dependencies.get(str(key), {})) diff --git a/metagpt/utils/embedding.py b/metagpt/utils/embedding.py new file mode 100644 index 000000000..21d62948c --- /dev/null +++ b/metagpt/utils/embedding.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2024/1/4 20:58 +@Author : alexanderwu +@File : embedding.py +""" +from langchain_community.embeddings import OpenAIEmbeddings + +from metagpt.config2 import config + + +def get_embedding(): + llm = config.get_openai_llm() + embedding = OpenAIEmbeddings(openai_api_key=llm.api_key, openai_api_base=llm.base_url) + return embedding diff --git a/metagpt/utils/file_repository.py b/metagpt/utils/file_repository.py index 11315e595..d2a06963a 100644 --- a/metagpt/utils/file_repository.py +++ b/metagpt/utils/file_repository.py @@ -16,7 +16,6 @@ import aiofiles -from metagpt.config import CONFIG from metagpt.logs import logger from metagpt.schema import Document from metagpt.utils.common import aread @@ -46,7 +45,7 @@ def __init__(self, git_repo, relative_path: Path = Path(".")): # Initializing self.workdir.mkdir(parents=True, exist_ok=True) - async def save(self, filename: Path | str, content, dependencies: List[str] = None): + async def save(self, filename: Path | str, content, dependencies: List[str] = None) -> Document: """Save content to a file and update its dependencies. :param filename: The filename or path within the repository. @@ -65,6 +64,8 @@ async def save(self, filename: Path | str, content, dependencies: List[str] = No await dependency_file.update(pathname, set(dependencies)) logger.info(f"update dependency: {str(pathname)}:{dependencies}") + return Document(root_path=str(self._relative_path), filename=str(filename), content=content) + async def get_dependency(self, filename: Path | str) -> Set[str]: """Get the dependencies of a file. @@ -100,21 +101,28 @@ async def get(self, filename: Path | str) -> Document | None: path_name = self.workdir / filename if not path_name.exists(): return None + if not path_name.is_file(): + return None doc.content = await aread(path_name) return doc - async def get_all(self) -> List[Document]: + async def get_all(self, filter_ignored=True) -> List[Document]: """Get the content of all files in the repository. :return: List of Document instances representing files. """ docs = [] - for root, dirs, files in os.walk(str(self.workdir)): - for file in files: - file_path = Path(root) / file - relative_path = file_path.relative_to(self.workdir) - doc = await self.get(relative_path) + if filter_ignored: + for f in self.all_files: + doc = await self.get(f) docs.append(doc) + else: + for root, dirs, files in os.walk(str(self.workdir)): + for file in files: + file_path = Path(root) / file + relative_path = file_path.relative_to(self.workdir) + doc = await self.get(relative_path) + docs.append(doc) return docs @property @@ -183,76 +191,24 @@ def new_filename(): """ current_time = datetime.now().strftime("%Y%m%d%H%M%S") return current_time - # guid_suffix = str(uuid.uuid4())[:8] - # return f"{current_time}x{guid_suffix}" - async def save_doc(self, doc: Document, with_suffix: str = None, dependencies: List[str] = None): - """Save a Document instance as a PDF file. - - This method converts the content of the Document instance to Markdown, - saves it to a file with an optional specified suffix, and logs the saved file. + async def save_doc(self, doc: Document, dependencies: List[str] = None): + """Save content to a file and update its dependencies. :param doc: The Document instance to be saved. :type doc: Document - :param with_suffix: An optional suffix to append to the saved file's name. - :type with_suffix: str, optional :param dependencies: A list of dependencies for the saved file. :type dependencies: List[str], optional """ - m = json.loads(doc.content) - filename = Path(doc.filename).with_suffix(with_suffix) if with_suffix is not None else Path(doc.filename) - await self.save(filename=str(filename), content=json_to_markdown(m), dependencies=dependencies) - logger.debug(f"File Saved: {str(filename)}") - - @staticmethod - async def get_file(filename: Path | str, relative_path: Path | str = ".") -> Document | None: - """Retrieve a specific file from the file repository. - :param filename: The name or path of the file to retrieve. - :type filename: Path or str - :param relative_path: The relative path within the file repository. - :type relative_path: Path or str, optional - :return: The document representing the file, or None if not found. - :rtype: Document or None - """ - file_repo = CONFIG.git_repo.new_file_repository(relative_path=relative_path) - return await file_repo.get(filename=filename) - - @staticmethod - async def get_all_files(relative_path: Path | str = ".") -> List[Document]: - """Retrieve all files from the file repository. + await self.save(filename=doc.filename, content=doc.content, dependencies=dependencies) + logger.debug(f"File Saved: {str(doc.filename)}") - :param relative_path: The relative path within the file repository. - :type relative_path: Path or str, optional - :return: A list of documents representing all files in the repository. - :rtype: List[Document] - """ - file_repo = CONFIG.git_repo.new_file_repository(relative_path=relative_path) - return await file_repo.get_all() - - @staticmethod - async def save_file(filename: Path | str, content, dependencies: List[str] = None, relative_path: Path | str = "."): - """Save a file to the file repository. - - :param filename: The name or path of the file to save. - :type filename: Path or str - :param content: The content of the file. - :param dependencies: A list of dependencies for the file. - :type dependencies: List[str], optional - :param relative_path: The relative path within the file repository. - :type relative_path: Path or str, optional - """ - file_repo = CONFIG.git_repo.new_file_repository(relative_path=relative_path) - return await file_repo.save(filename=filename, content=content, dependencies=dependencies) - - @staticmethod - async def save_as( - doc: Document, with_suffix: str = None, dependencies: List[str] = None, relative_path: Path | str = "." - ): - """Save a Document instance with optional modifications. + async def save_pdf(self, doc: Document, with_suffix: str = ".md", dependencies: List[str] = None): + """Save a Document instance as a PDF file. - This static method creates a new FileRepository, saves the Document instance - with optional modifications (such as a suffix), and logs the saved file. + This method converts the content of the Document instance to Markdown, + saves it to a file with an optional specified suffix, and logs the saved file. :param doc: The Document instance to be saved. :type doc: Document @@ -260,13 +216,11 @@ async def save_as( :type with_suffix: str, optional :param dependencies: A list of dependencies for the saved file. :type dependencies: List[str], optional - :param relative_path: The relative path within the file repository. - :type relative_path: Path or str, optional - :return: A boolean indicating whether the save operation was successful. - :rtype: bool """ - file_repo = CONFIG.git_repo.new_file_repository(relative_path=relative_path) - return await file_repo.save_doc(doc=doc, with_suffix=with_suffix, dependencies=dependencies) + m = json.loads(doc.content) + filename = Path(doc.filename).with_suffix(with_suffix) if with_suffix is not None else Path(doc.filename) + await self.save(filename=str(filename), content=json_to_markdown(m), dependencies=dependencies) + logger.debug(f"File Saved: {str(filename)}") async def delete(self, filename: Path | str): """Delete a file from the file repository. @@ -284,8 +238,3 @@ async def delete(self, filename: Path | str): dependency_file = await self._git_repo.get_dependency() await dependency_file.update(filename=pathname, dependencies=None) logger.info(f"remove dependency key: {str(pathname)}") - - @staticmethod - async def delete_file(filename: Path | str, relative_path: Path | str = "."): - file_repo = CONFIG.git_repo.new_file_repository(relative_path=relative_path) - await file_repo.delete(filename=filename) diff --git a/metagpt/utils/git_repository.py b/metagpt/utils/git_repository.py index e9855df05..16f675175 100644 --- a/metagpt/utils/git_repository.py +++ b/metagpt/utils/git_repository.py @@ -107,7 +107,10 @@ def commit(self, comments): def delete_repository(self): """Delete the entire repository directory.""" if self.is_valid: - shutil.rmtree(self._repository.working_dir) + try: + shutil.rmtree(self._repository.working_dir) + except Exception as e: + logger.exception(f"Failed delete git repo:{self.workdir}, error:{e}") @property def changed_files(self) -> Dict[str, str]: @@ -198,11 +201,21 @@ def rename_root(self, new_dir_name): new_path = self.workdir.parent / new_dir_name if new_path.exists(): logger.info(f"Delete directory {str(new_path)}") - shutil.rmtree(new_path) + try: + shutil.rmtree(new_path) + except Exception as e: + logger.warning(f"rm {str(new_path)} error: {e}") + if new_path.exists(): # Recheck for windows os + logger.warning(f"Failed to delete directory {str(new_path)}") + return try: shutil.move(src=str(self.workdir), dst=str(new_path)) except Exception as e: logger.warning(f"Move {str(self.workdir)} to {str(new_path)} error: {e}") + finally: + if not new_path.exists(): # Recheck for windows os + logger.warning(f"Failed to move {str(self.workdir)} to {str(new_path)}") + return logger.info(f"Rename directory {str(self.workdir)} to {str(new_path)}") self._repository = Repo(new_path) self._gitignore_rules = parse_gitignore(full_path=str(new_path / ".gitignore")) diff --git a/metagpt/utils/human_interaction.py b/metagpt/utils/human_interaction.py new file mode 100644 index 000000000..3b245cac8 --- /dev/null +++ b/metagpt/utils/human_interaction.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : human interaction to get required type text + +import json +from typing import Any, Tuple, Type + +from pydantic import BaseModel + +from metagpt.logs import logger +from metagpt.utils.common import import_class + + +class HumanInteraction(object): + stop_list = ("q", "quit", "exit") + + def multilines_input(self, prompt: str = "Enter: ") -> str: + logger.warning("Enter your content, use Ctrl-D or Ctrl-Z ( windows ) to save it.") + logger.info(f"{prompt}\n") + lines = [] + while True: + try: + line = input() + lines.append(line) + except EOFError: + break + return "".join(lines) + + def check_input_type(self, input_str: str, req_type: Type) -> Tuple[bool, Any]: + check_ret = True + if req_type == str: + # required_type = str, just return True + return check_ret, input_str + try: + input_str = input_str.strip() + data = json.loads(input_str) + except Exception: + return False, None + + actionnode_class = import_class("ActionNode", "metagpt.actions.action_node") # avoid circular import + tmp_key = "tmp" + tmp_cls = actionnode_class.create_model_class(class_name=tmp_key.upper(), mapping={tmp_key: (req_type, ...)}) + try: + _ = tmp_cls(**{tmp_key: data}) + except Exception: + check_ret = False + return check_ret, data + + def input_until_valid(self, prompt: str, req_type: Type) -> Any: + # check the input with req_type until it's ok + while True: + input_content = self.multilines_input(prompt) + check_ret, structure_content = self.check_input_type(input_content, req_type) + if check_ret: + break + else: + logger.error(f"Input content can't meet required_type: {req_type}, please Re-Enter.") + return structure_content + + def input_num_until_valid(self, num_max: int) -> int: + while True: + input_num = input("Enter the num of the interaction key: ") + input_num = input_num.strip() + if input_num in self.stop_list: + return input_num + try: + input_num = int(input_num) + if 0 <= input_num < num_max: + return input_num + except Exception: + pass + + def interact_with_instruct_content( + self, instruct_content: BaseModel, mapping: dict = dict(), interact_type: str = "review" + ) -> dict[str, Any]: + assert interact_type in ["review", "revise"] + assert instruct_content + instruct_content_dict = instruct_content.model_dump() + num_fields_map = dict(zip(range(0, len(instruct_content_dict)), instruct_content_dict.keys())) + logger.info( + f"\n{interact_type.upper()} interaction\n" + f"Interaction data: {num_fields_map}\n" + f"Enter the num to interact with corresponding field or `q`/`quit`/`exit` to stop interaction.\n" + f"Enter the field content until it meet field required type.\n" + ) + + interact_contents = {} + while True: + input_num = self.input_num_until_valid(len(instruct_content_dict)) + if input_num in self.stop_list: + logger.warning("Stop human interaction") + break + + field = num_fields_map.get(input_num) + logger.info(f"You choose to interact with field: {field}, and do a `{interact_type}` operation.") + + if interact_type == "review": + prompt = "Enter your review comment: " + req_type = str + else: + prompt = "Enter your revise content: " + req_type = mapping.get(field)[0] # revise need input content match the required_type + + field_content = self.input_until_valid(prompt=prompt, req_type=req_type) + interact_contents[field] = field_content + + return interact_contents diff --git a/metagpt/utils/make_sk_kernel.py b/metagpt/utils/make_sk_kernel.py index e0272ea13..283a682d6 100644 --- a/metagpt/utils/make_sk_kernel.py +++ b/metagpt/utils/make_sk_kernel.py @@ -13,20 +13,20 @@ OpenAIChatCompletion, ) -from metagpt.config import CONFIG +from metagpt.config2 import config def make_sk_kernel(): kernel = sk.Kernel() - if CONFIG.OPENAI_API_TYPE == "azure": + if llm := config.get_azure_llm(): kernel.add_chat_service( "chat_completion", - AzureChatCompletion(CONFIG.DEPLOYMENT_NAME, CONFIG.OPENAI_BASE_URL, CONFIG.OPENAI_API_KEY), + AzureChatCompletion(llm.model, llm.base_url, llm.api_key), ) - else: + elif llm := config.get_openai_llm(): kernel.add_chat_service( "chat_completion", - OpenAIChatCompletion(CONFIG.OPENAI_API_MODEL, CONFIG.OPENAI_API_KEY), + OpenAIChatCompletion(llm.model, llm.api_key), ) return kernel diff --git a/metagpt/utils/mermaid.py b/metagpt/utils/mermaid.py index 7762784d8..ae3c5118f 100644 --- a/metagpt/utils/mermaid.py +++ b/metagpt/utils/mermaid.py @@ -9,12 +9,14 @@ import os from pathlib import Path -from metagpt.config import CONFIG +import aiofiles + +from metagpt.config2 import config from metagpt.logs import logger from metagpt.utils.common import check_cmd_exists -async def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048, height=2048) -> int: +async def mermaid_to_file(engine, mermaid_code, output_file_without_suffix, width=2048, height=2048) -> int: """suffix: png/svg/pdf :param mermaid_code: mermaid code @@ -28,14 +30,15 @@ async def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048, if dir_name and not os.path.exists(dir_name): os.makedirs(dir_name) tmp = Path(f"{output_file_without_suffix}.mmd") - tmp.write_text(mermaid_code, encoding="utf-8") + async with aiofiles.open(tmp, "w", encoding="utf-8") as f: + await f.write(mermaid_code) + # tmp.write_text(mermaid_code, encoding="utf-8") - engine = CONFIG.mermaid_engine.lower() if engine == "nodejs": - if check_cmd_exists(CONFIG.mmdc) != 0: + if check_cmd_exists(config.mermaid.path) != 0: logger.warning( "RUN `npm install -g @mermaid-js/mermaid-cli` to install mmdc," - "or consider changing MERMAID_ENGINE to `playwright`, `pyppeteer`, or `ink`." + "or consider changing engine to `playwright`, `pyppeteer`, or `ink`." ) return -1 @@ -44,11 +47,11 @@ async def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048, # Call the `mmdc` command to convert the Mermaid code to a PNG logger.info(f"Generating {output_file}..") - if CONFIG.puppeteer_config: + if config.mermaid.puppeteer_config: commands = [ - CONFIG.mmdc, + config.mermaid.path, "-p", - CONFIG.puppeteer_config, + config.mermaid.puppeteer_config, "-i", str(tmp), "-o", @@ -59,7 +62,7 @@ async def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048, str(height), ] else: - commands = [CONFIG.mmdc, "-i", str(tmp), "-o", output_file, "-w", str(width), "-H", str(height)] + commands = [config.mermaid.path, "-i", str(tmp), "-o", output_file, "-w", str(width), "-H", str(height)] process = await asyncio.create_subprocess_shell( " ".join(commands), stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE ) diff --git a/metagpt/utils/mmdc_pyppeteer.py b/metagpt/utils/mmdc_pyppeteer.py index 7125cafc5..f029325f1 100644 --- a/metagpt/utils/mmdc_pyppeteer.py +++ b/metagpt/utils/mmdc_pyppeteer.py @@ -10,7 +10,7 @@ from pyppeteer import launch -from metagpt.config import CONFIG +from metagpt.config2 import config from metagpt.logs import logger @@ -30,14 +30,14 @@ async def mermaid_to_file(mermaid_code, output_file_without_suffix, width=2048, suffixes = ["png", "svg", "pdf"] __dirname = os.path.dirname(os.path.abspath(__file__)) - if CONFIG.pyppeteer_executable_path: + if config.mermaid.pyppeteer_path: browser = await launch( headless=True, - executablePath=CONFIG.pyppeteer_executable_path, + executablePath=config.mermaid.pyppeteer_path, args=["--disable-extensions", "--no-sandbox"], ) else: - logger.error("Please set the environment variable:PYPPETEER_EXECUTABLE_PATH.") + logger.error("Please set the var mermaid.pyppeteer_path in the config2.yaml.") return -1 page = await browser.newPage() device_scale_factor = 1.0 diff --git a/metagpt/utils/parse_docstring.py b/metagpt/utils/parse_docstring.py new file mode 100644 index 000000000..e91be8e75 --- /dev/null +++ b/metagpt/utils/parse_docstring.py @@ -0,0 +1,87 @@ +import re +from typing import Tuple + +from pydantic import BaseModel + + +def remove_spaces(text): + return re.sub(r"\s+", " ", text).strip() + + +class DocstringParser(BaseModel): + docstring: str + + def parse_desc(self) -> str: + """Parse and return the description from the docstring.""" + + def parse_params(self) -> list[Tuple[str, str, str]]: + """Parse and return the parameters from the docstring. + + Returns: + list[Tuple[str, str, str]]: A list of input paramter info. Each info is a triple of (param name, param type, param description) + """ + + def parse_returns(self) -> list[Tuple[str, str]]: + """Parse and return the output information from the docstring. + + Returns: + list[Tuple[str, str]]: A list of output info. Each info is a tuple of (return type, return description) + """ + + @staticmethod + def check_and_parse_optional(param_type: str) -> Tuple[bool, str]: + """Check if a parameter is optional and return a processed param_type rid of the optionality info if so""" + + @staticmethod + def check_and_parse_default_value(param_desc: str) -> Tuple[bool, str]: + """Check if a parameter has a default value and return the default value if so""" + + @staticmethod + def check_and_parse_enum(param_desc: str) -> Tuple[bool, str]: + """Check if a parameter description includes an enum and return enum values if so""" + + +class reSTDocstringParser(DocstringParser): + """A parser for reStructuredText (reST) docstring""" + + +class GoogleDocstringParser(DocstringParser): + """A parser for Google-stype docstring""" + + docstring: str + + def parse_desc(self) -> str: + description_match = re.search(r"^(.*?)(?:Args:|Returns:|Raises:|$)", self.docstring, re.DOTALL) + description = remove_spaces(description_match.group(1)) if description_match else "" + return description + + def parse_params(self) -> list[Tuple[str, str, str]]: + args_match = re.search(r"Args:\s*(.*?)(?:Returns:|Raises:|$)", self.docstring, re.DOTALL) + _args = args_match.group(1).strip() if args_match else "" + # variable_pattern = re.compile(r"(\w+)\s*\((.*?)\):\s*(.*)") + variable_pattern = re.compile( + r"(\w+)\s*\((.*?)\):\s*(.*?)(?=\n\s*\w+\s*\(|\Z)", re.DOTALL + ) # (?=\n\w+\s*\(|\Z) is to assert that what follows is either the start of the next parameter (indicated by a newline, some word characters, and an opening parenthesis) or the end of the string (\Z). + params = variable_pattern.findall(_args) + return params + + def parse_returns(self) -> list[Tuple[str, str]]: + returns_match = re.search(r"Returns:\s*(.*?)(?:Raises:|$)", self.docstring, re.DOTALL) + returns = returns_match.group(1).strip() if returns_match else "" + return_pattern = re.compile(r"^(.*)\s*:\s*(.*)$") + returns = return_pattern.findall(returns) + return returns + + @staticmethod + def check_and_parse_optional(param_type: str) -> Tuple[bool, str]: + return "optional" in param_type, param_type.replace(", optional", "") + + @staticmethod + def check_and_parse_default_value(param_desc: str) -> Tuple[bool, str]: + default_val = re.search(r"Defaults to (.+?)\.", param_desc) + return (True, default_val.group(1)) if default_val else (False, "") + + @staticmethod + def check_and_parse_enum(param_desc: str) -> Tuple[bool, str]: + enum_val = re.search(r"Enum: \[(.+?)\]", param_desc) + return (True, [e.strip() for e in enum_val.group(1).split(",")]) if enum_val else (False, []) diff --git a/metagpt/utils/project_repo.py b/metagpt/utils/project_repo.py new file mode 100644 index 000000000..c1f98e1ec --- /dev/null +++ b/metagpt/utils/project_repo.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2024/1/8 +@Author : mashenquan +@File : project_repo.py +@Desc : Wrapper for GitRepository and FileRepository of project. + Implementation of Chapter 4.6 of https://deepwisdom.feishu.cn/wiki/CUK4wImd7id9WlkQBNscIe9cnqh +""" +from __future__ import annotations + +from pathlib import Path + +from metagpt.const import ( + CLASS_VIEW_FILE_REPO, + CODE_PLAN_AND_CHANGE_FILE_REPO, + CODE_PLAN_AND_CHANGE_PDF_FILE_REPO, + CODE_SUMMARIES_FILE_REPO, + CODE_SUMMARIES_PDF_FILE_REPO, + COMPETITIVE_ANALYSIS_FILE_REPO, + DATA_API_DESIGN_FILE_REPO, + DOCS_FILE_REPO, + GRAPH_REPO_FILE_REPO, + PRD_PDF_FILE_REPO, + PRDS_FILE_REPO, + REQUIREMENT_FILENAME, + RESOURCES_FILE_REPO, + SD_OUTPUT_FILE_REPO, + SEQ_FLOW_FILE_REPO, + SYSTEM_DESIGN_FILE_REPO, + SYSTEM_DESIGN_PDF_FILE_REPO, + TASK_FILE_REPO, + TASK_PDF_FILE_REPO, + TEST_CODES_FILE_REPO, + TEST_OUTPUTS_FILE_REPO, +) +from metagpt.utils.file_repository import FileRepository +from metagpt.utils.git_repository import GitRepository + + +class DocFileRepositories(FileRepository): + prd: FileRepository + system_design: FileRepository + task: FileRepository + code_summary: FileRepository + graph_repo: FileRepository + class_view: FileRepository + code_plan_and_change: FileRepository + + def __init__(self, git_repo): + super().__init__(git_repo=git_repo, relative_path=DOCS_FILE_REPO) + + self.prd = git_repo.new_file_repository(relative_path=PRDS_FILE_REPO) + self.system_design = git_repo.new_file_repository(relative_path=SYSTEM_DESIGN_FILE_REPO) + self.task = git_repo.new_file_repository(relative_path=TASK_FILE_REPO) + self.code_summary = git_repo.new_file_repository(relative_path=CODE_SUMMARIES_FILE_REPO) + self.graph_repo = git_repo.new_file_repository(relative_path=GRAPH_REPO_FILE_REPO) + self.class_view = git_repo.new_file_repository(relative_path=CLASS_VIEW_FILE_REPO) + self.code_plan_and_change = git_repo.new_file_repository(relative_path=CODE_PLAN_AND_CHANGE_FILE_REPO) + + +class ResourceFileRepositories(FileRepository): + competitive_analysis: FileRepository + data_api_design: FileRepository + seq_flow: FileRepository + system_design: FileRepository + prd: FileRepository + api_spec_and_task: FileRepository + code_summary: FileRepository + sd_output: FileRepository + code_plan_and_change: FileRepository + + def __init__(self, git_repo): + super().__init__(git_repo=git_repo, relative_path=RESOURCES_FILE_REPO) + + self.competitive_analysis = git_repo.new_file_repository(relative_path=COMPETITIVE_ANALYSIS_FILE_REPO) + self.data_api_design = git_repo.new_file_repository(relative_path=DATA_API_DESIGN_FILE_REPO) + self.seq_flow = git_repo.new_file_repository(relative_path=SEQ_FLOW_FILE_REPO) + self.system_design = git_repo.new_file_repository(relative_path=SYSTEM_DESIGN_PDF_FILE_REPO) + self.prd = git_repo.new_file_repository(relative_path=PRD_PDF_FILE_REPO) + self.api_spec_and_task = git_repo.new_file_repository(relative_path=TASK_PDF_FILE_REPO) + self.code_summary = git_repo.new_file_repository(relative_path=CODE_SUMMARIES_PDF_FILE_REPO) + self.sd_output = git_repo.new_file_repository(relative_path=SD_OUTPUT_FILE_REPO) + self.code_plan_and_change = git_repo.new_file_repository(relative_path=CODE_PLAN_AND_CHANGE_PDF_FILE_REPO) + + +class ProjectRepo(FileRepository): + def __init__(self, root: str | Path | GitRepository): + if isinstance(root, str) or isinstance(root, Path): + git_repo_ = GitRepository(local_path=Path(root)) + elif isinstance(root, GitRepository): + git_repo_ = root + else: + raise ValueError("Invalid root") + super().__init__(git_repo=git_repo_, relative_path=Path(".")) + self._git_repo = git_repo_ + self.docs = DocFileRepositories(self._git_repo) + self.resources = ResourceFileRepositories(self._git_repo) + self.tests = self._git_repo.new_file_repository(relative_path=TEST_CODES_FILE_REPO) + self.test_outputs = self._git_repo.new_file_repository(relative_path=TEST_OUTPUTS_FILE_REPO) + self._srcs_path = None + self.code_files_exists() + + def __str__(self): + repo_str = f"ProjectRepo({self._git_repo.workdir})" + docs_str = f"Docs({self.docs.all_files})" + srcs_str = f"Srcs({self.srcs.all_files})" + return f"{repo_str}\n{docs_str}\n{srcs_str}" + + @property + async def requirement(self): + return await self.docs.get(filename=REQUIREMENT_FILENAME) + + @property + def git_repo(self) -> GitRepository: + return self._git_repo + + @property + def workdir(self) -> Path: + return Path(self.git_repo.workdir) + + @property + def srcs(self) -> FileRepository: + if not self._srcs_path: + raise ValueError("Call with_srcs first.") + return self._git_repo.new_file_repository(self._srcs_path) + + def code_files_exists(self) -> bool: + git_workdir = self.git_repo.workdir + src_workdir = git_workdir / git_workdir.name + if not src_workdir.exists(): + return False + code_files = self.with_src_path(path=git_workdir / git_workdir.name).srcs.all_files + if not code_files: + return False + + def with_src_path(self, path: str | Path) -> ProjectRepo: + try: + self._srcs_path = Path(path).relative_to(self.workdir) + except ValueError: + self._srcs_path = Path(path) + return self + + @property + def src_relative_path(self) -> Path | None: + return self._srcs_path diff --git a/metagpt/utils/recovery_util.py b/metagpt/utils/recovery_util.py new file mode 100644 index 000000000..d0b197e69 --- /dev/null +++ b/metagpt/utils/recovery_util.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +# @Date : 12/20/2023 11:07 AM +# @Author : stellahong (stellahong@fuzhi.ai) +# @Desc : +import json +from datetime import datetime +from pathlib import Path + +import nbformat + +from metagpt.const import DATA_PATH +from metagpt.roles.role import Role +from metagpt.utils.common import read_json_file +from metagpt.utils.save_code import save_code_file + + +def load_history(save_dir: str = ""): + """ + Load plan and code execution history from the specified save directory. + + Args: + save_dir (str): The directory from which to load the history. + + Returns: + Tuple: A tuple containing the loaded plan and notebook. + """ + + plan_path = Path(save_dir) / "plan.json" + nb_path = Path(save_dir) / "history_nb" / "code.ipynb" + plan = read_json_file(plan_path) + nb = nbformat.read(open(nb_path, "r", encoding="utf-8"), as_version=nbformat.NO_CONVERT) + return plan, nb + + +def save_history(role: Role, save_dir: str = ""): + """ + Save plan and code execution history to the specified directory. + + Args: + role (Role): The role containing the plan and execute_code attributes. + save_dir (str): The directory to save the history. + + Returns: + Path: The path to the saved history directory. + """ + record_time = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") + save_path = DATA_PATH / "output" / f"{record_time}" + + # overwrite exist trajectory + save_path.mkdir(parents=True, exist_ok=True) + + plan = role.planner.plan.dict() + + with open(save_path / "plan.json", "w", encoding="utf-8") as plan_file: + json.dump(plan, plan_file, indent=4, ensure_ascii=False) + + save_code_file(name=Path(record_time) / "history_nb", code_context=role.execute_code.nb, file_format="ipynb") + return save_path diff --git a/metagpt/utils/redis.py b/metagpt/utils/redis.py index 10f33285c..7a640563a 100644 --- a/metagpt/utils/redis.py +++ b/metagpt/utils/redis.py @@ -12,26 +12,25 @@ import aioredis # https://aioredis.readthedocs.io/en/latest/getting-started/ -from metagpt.config import CONFIG +from metagpt.configs.redis_config import RedisConfig from metagpt.logs import logger class Redis: - def __init__(self): + def __init__(self, config: RedisConfig = None): + self.config = config self._client = None async def _connect(self, force=False): if self._client and not force: return True - if not self.is_configured: - return False try: self._client = await aioredis.from_url( - f"redis://{CONFIG.REDIS_HOST}:{CONFIG.REDIS_PORT}", - username=CONFIG.REDIS_USER, - password=CONFIG.REDIS_PASSWORD, - db=CONFIG.REDIS_DB, + self.config.to_url(), + username=self.config.username, + password=self.config.password, + db=self.config.db, ) return True except Exception as e: @@ -62,18 +61,3 @@ async def close(self): return await self._client.close() self._client = None - - @property - def is_valid(self) -> bool: - return self._client is not None - - @property - def is_configured(self) -> bool: - return bool( - CONFIG.REDIS_HOST - and CONFIG.REDIS_HOST != "YOUR_REDIS_HOST" - and CONFIG.REDIS_PORT - and CONFIG.REDIS_PORT != "YOUR_REDIS_PORT" - and CONFIG.REDIS_DB is not None - and CONFIG.REDIS_PASSWORD is not None - ) diff --git a/metagpt/utils/repair_llm_raw_output.py b/metagpt/utils/repair_llm_raw_output.py index 6da974d96..06484f71d 100644 --- a/metagpt/utils/repair_llm_raw_output.py +++ b/metagpt/utils/repair_llm_raw_output.py @@ -9,7 +9,7 @@ import regex as re from tenacity import RetryCallState, retry, stop_after_attempt, wait_fixed -from metagpt.config import CONFIG +from metagpt.config2 import config from metagpt.logs import logger from metagpt.utils.custom_decoder import CustomDecoder @@ -168,7 +168,7 @@ def repair_llm_raw_output(output: str, req_keys: list[str], repair_type: RepairT target: { xxx } output: { xxx }] """ - if not CONFIG.repair_llm_output: + if not config.repair_llm_output: return output # do the repairation usually for non-openai models @@ -252,7 +252,7 @@ def run_and_passon(retry_state: RetryCallState) -> None: func_param_output = retry_state.kwargs.get("output", "") exp_str = str(retry_state.outcome.exception()) - fix_str = "try to fix it, " if CONFIG.repair_llm_output else "" + fix_str = "try to fix it, " if config.repair_llm_output else "" logger.warning( f"parse json from content inside [CONTENT][/CONTENT] failed at retry " f"{retry_state.attempt_number}, {fix_str}exp: {exp_str}" @@ -265,7 +265,7 @@ def run_and_passon(retry_state: RetryCallState) -> None: @retry( - stop=stop_after_attempt(3 if CONFIG.repair_llm_output else 0), + stop=stop_after_attempt(3 if config.repair_llm_output else 0), wait=wait_fixed(1), after=run_after_exp_and_passon_next_retry(logger), ) diff --git a/metagpt/utils/s3.py b/metagpt/utils/s3.py index 2a2c1a31c..c0afbb2f5 100644 --- a/metagpt/utils/s3.py +++ b/metagpt/utils/s3.py @@ -8,7 +8,7 @@ import aioboto3 import aiofiles -from metagpt.config import CONFIG +from metagpt.config2 import S3Config from metagpt.const import BASE64_FORMAT from metagpt.logs import logger @@ -16,13 +16,14 @@ class S3: """A class for interacting with Amazon S3 storage.""" - def __init__(self): + def __init__(self, config: S3Config): self.session = aioboto3.Session() + self.config = config self.auth_config = { "service_name": "s3", - "aws_access_key_id": CONFIG.S3_ACCESS_KEY, - "aws_secret_access_key": CONFIG.S3_SECRET_KEY, - "endpoint_url": CONFIG.S3_ENDPOINT_URL, + "aws_access_key_id": config.access_key, + "aws_secret_access_key": config.secret_key, + "endpoint_url": config.endpoint, } async def upload_file( @@ -139,8 +140,8 @@ async def cache(self, data: str, file_ext: str, format: str = "") -> str: data = base64.b64decode(data) if format == BASE64_FORMAT else data.encode(encoding="utf-8") await file.write(data) - bucket = CONFIG.S3_BUCKET - object_pathname = CONFIG.S3_BUCKET or "system" + bucket = self.config.bucket + object_pathname = self.config.bucket or "system" object_pathname += f"/{object_name}" object_pathname = os.path.normpath(object_pathname) await self.upload_file(bucket=bucket, local_path=str(pathname), object_name=object_pathname) @@ -151,20 +152,3 @@ async def cache(self, data: str, file_ext: str, format: str = "") -> str: logger.exception(f"{e}, stack:{traceback.format_exc()}") pathname.unlink(missing_ok=True) return None - - @property - def is_valid(self): - return self.is_configured - - @property - def is_configured(self) -> bool: - return bool( - CONFIG.S3_ACCESS_KEY - and CONFIG.S3_ACCESS_KEY != "YOUR_S3_ACCESS_KEY" - and CONFIG.S3_SECRET_KEY - and CONFIG.S3_SECRET_KEY != "YOUR_S3_SECRET_KEY" - and CONFIG.S3_ENDPOINT_URL - and CONFIG.S3_ENDPOINT_URL != "YOUR_S3_ENDPOINT_URL" - and CONFIG.S3_BUCKET - and CONFIG.S3_BUCKET != "YOUR_S3_BUCKET" - ) diff --git a/metagpt/utils/save_code.py b/metagpt/utils/save_code.py new file mode 100644 index 000000000..18cb5cd62 --- /dev/null +++ b/metagpt/utils/save_code.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# @Date : 12/12/2023 4:14 PM +# @Author : stellahong (stellahong@fuzhi.ai) +# @Desc : +import os + +import nbformat + +from metagpt.const import DATA_PATH +from metagpt.utils.common import write_json_file + + +def save_code_file(name: str, code_context: str, file_format: str = "py") -> None: + """ + Save code files to a specified path. + + Args: + - name (str): The name of the folder to save the files. + - code_context (str): The code content. + - file_format (str, optional): The file format. Supports 'py' (Python file), 'json' (JSON file), and 'ipynb' (Jupyter Notebook file). Default is 'py'. + + + Returns: + - None + """ + # Create the folder path if it doesn't exist + os.makedirs(name=DATA_PATH / "output" / f"{name}", exist_ok=True) + + # Choose to save as a Python file or a JSON file based on the file format + file_path = DATA_PATH / "output" / f"{name}/code.{file_format}" + if file_format == "py": + file_path.write_text(code_context + "\n\n", encoding="utf-8") + elif file_format == "json": + # Parse the code content as JSON and save + data = {"code": code_context} + write_json_file(file_path, data, encoding="utf-8", indent=2) + elif file_format == "ipynb": + nbformat.write(code_context, file_path) + else: + raise ValueError("Unsupported file format. Please choose 'py', 'json', or 'ipynb'.") diff --git a/metagpt/utils/token_counter.py b/metagpt/utils/token_counter.py index 94506e373..a0fb3b70d 100644 --- a/metagpt/utils/token_counter.py +++ b/metagpt/utils/token_counter.py @@ -29,6 +29,7 @@ "gpt-4-turbo-preview": {"prompt": 0.01, "completion": 0.03}, "gpt-4-0125-preview": {"prompt": 0.01, "completion": 0.03}, "gpt-4-1106-preview": {"prompt": 0.01, "completion": 0.03}, + "gpt-4-vision-preview": {"prompt": 0.01, "completion": 0.03}, # TODO add extra image price calculator "gpt-4-1106-vision-preview": {"prompt": 0.01, "completion": 0.03}, "text-embedding-ada-002": {"prompt": 0.0004, "completion": 0.0}, "glm-3-turbo": {"prompt": 0.0, "completion": 0.0007}, # 128k version, prompt + completion tokens=0.005¥/k-tokens @@ -54,6 +55,7 @@ "gpt-4-turbo-preview": 128000, "gpt-4-0125-preview": 128000, "gpt-4-1106-preview": 128000, + "gpt-4-vision-preview": 128000, "gpt-4-1106-vision-preview": 128000, "text-embedding-ada-002": 8192, "chatglm_turbo": 32768, @@ -82,6 +84,7 @@ def count_message_tokens(messages, model="gpt-3.5-turbo-0613"): "gpt-4-turbo-preview", "gpt-4-0125-preview", "gpt-4-1106-preview", + "gpt-4-vision-preview", "gpt-4-1106-vision-preview", }: tokens_per_message = 3 # # every reply is primed with <|start|>assistant<|message|> @@ -112,7 +115,13 @@ def count_message_tokens(messages, model="gpt-3.5-turbo-0613"): for message in messages: num_tokens += tokens_per_message for key, value in message.items(): - num_tokens += len(encoding.encode(value)) + content = value + if isinstance(value, list): + # for gpt-4v + for item in value: + if isinstance(item, dict) and item.get("type") in ["text"]: + content = item.get("text", "") + num_tokens += len(encoding.encode(content)) if key == "name": num_tokens += tokens_per_name num_tokens += 3 # every reply is primed with <|start|>assistant<|message|> diff --git a/metagpt/utils/yaml_model.py b/metagpt/utils/yaml_model.py new file mode 100644 index 000000000..4d42bb03f --- /dev/null +++ b/metagpt/utils/yaml_model.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2024/1/4 10:18 +@Author : alexanderwu +@File : YamlModel.py +""" +from pathlib import Path +from typing import Dict, Optional + +import yaml +from pydantic import BaseModel, model_validator + + +class YamlModel(BaseModel): + """Base class for yaml model""" + + extra_fields: Optional[Dict[str, str]] = None + + @classmethod + def read_yaml(cls, file_path: Path, encoding: str = "utf-8") -> Dict: + """Read yaml file and return a dict""" + if not file_path.exists(): + return {} + with open(file_path, "r", encoding=encoding) as file: + return yaml.safe_load(file) + + @classmethod + def from_yaml_file(cls, file_path: Path) -> "YamlModel": + """Read yaml file and return a YamlModel instance""" + return cls(**cls.read_yaml(file_path)) + + def to_yaml_file(self, file_path: Path, encoding: str = "utf-8") -> None: + """Dump YamlModel instance to yaml file""" + with open(file_path, "w", encoding=encoding) as file: + yaml.dump(self.model_dump(), file) + + +class YamlModelWithoutDefault(YamlModel): + """YamlModel without default values""" + + @model_validator(mode="before") + @classmethod + def check_not_default_config(cls, values): + """Check if there is any default config in config2.yaml""" + if any(["YOUR" in v for v in values]): + raise ValueError("Please set your config in config2.yaml") + return values diff --git a/requirements.txt b/requirements.txt index 93ad653dc..6cb25d52b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -14,7 +14,7 @@ lancedb==0.4.0 langchain==0.0.352 loguru==0.6.0 meilisearch==0.21.0 -numpy==1.24.3 +numpy>=1.24.3 openai==1.6.0 openpyxl beautifulsoup4==4.12.2 @@ -35,7 +35,6 @@ tqdm==4.65.0 # webdriver_manager<3.9 anthropic==0.8.1 typing-inspect==0.8.0 -typing_extensions==4.9.0 libcst==1.0.1 qdrant-client==1.7.0 # pytest-mock==3.11.1 # test extras require @@ -51,6 +50,13 @@ websocket-client==1.6.2 aiofiles==23.2.1 gitpython==3.1.40 zhipuai==2.0.1 +rich==13.6.0 +nbclient==0.9.0 +nbformat==5.9.2 +ipython==8.17.2 +ipykernel==6.27.0 +scikit_learn==1.3.2 +typing-extensions==4.9.0 socksio~=1.0.0 gitignore-parser==0.1.9 # connexion[uvicorn]~=3.0.5 # Used by metagpt/tools/openapi_v3_hello.py @@ -59,3 +65,5 @@ networkx~=3.2.1 google-generativeai==0.3.2 # playwright==1.40.0 # playwright extras require anytree +ipywidgets==8.1.1 +Pillow diff --git a/setup.py b/setup.py index 0439d6cd4..b16d978cf 100644 --- a/setup.py +++ b/setup.py @@ -46,8 +46,8 @@ def run(self): "chromadb==0.4.14", "gradio==3.0.0", "grpcio-status==1.48.2", - "mock==5.1.0", "pylint==3.0.3", + "pybrowsers", ] extras_require["pyppeteer"] = [ @@ -58,7 +58,7 @@ def run(self): setup( name="metagpt", - version="0.6.0", + version="0.7.0", description="The Multi-Agent Framework", long_description=long_description, long_description_content_type="text/markdown", @@ -76,7 +76,7 @@ def run(self): }, entry_points={ "console_scripts": [ - "metagpt=metagpt.startup:app", + "metagpt=metagpt.software_company:app", ], }, ) diff --git a/tests/config2.yaml b/tests/config2.yaml new file mode 100644 index 000000000..58314eaed --- /dev/null +++ b/tests/config2.yaml @@ -0,0 +1,27 @@ +llm: + base_url: "https://api.openai.com/v1" + api_key: "sk-xxx" + model: "gpt-3.5-turbo-1106" + +search: + api_type: "serpapi" + api_key: "xxx" + +s3: + access_key: "MOCK_S3_ACCESS_KEY" + secret_key: "MOCK_S3_SECRET_KEY" + endpoint: "http://mock:9000" + secure: false + bucket: "mock" + +azure_tts_subscription_key: "xxx" +azure_tts_region: "eastus" + +iflytek_app_id: "xxx" +iflytek_api_key: "xxx" +iflytek_api_secret: "xxx" + +metagpt_tti_url: "http://mock.com" + +repair_llm_output: true + diff --git a/tests/conftest.py b/tests/conftest.py index 6f5c04f06..c69fca15e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -12,14 +12,19 @@ import os import re import uuid +from typing import Callable import pytest -from metagpt.config import CONFIG, Config from metagpt.const import DEFAULT_WORKSPACE_ROOT, TEST_DATA_PATH +from metagpt.context import Context as MetagptContext from metagpt.llm import LLM from metagpt.logs import logger from metagpt.utils.git_repository import GitRepository +from metagpt.utils.project_repo import ProjectRepo +from tests.mock.mock_aiohttp import MockAioResponse +from tests.mock.mock_curl_cffi import MockCurlCffiResponse +from tests.mock.mock_httplib2 import MockHttplib2Response from tests.mock.mock_llm import MockLLM RSP_CACHE_NEW = {} # used globally for producing new and useful only response cache @@ -30,18 +35,17 @@ @pytest.fixture(scope="session") def rsp_cache(): - # model_version = CONFIG.openai_api_model rsp_cache_file_path = TEST_DATA_PATH / "rsp_cache.json" # read repo-provided new_rsp_cache_file_path = TEST_DATA_PATH / "rsp_cache_new.json" # exporting a new copy if os.path.exists(rsp_cache_file_path): - with open(rsp_cache_file_path, "r") as f1: + with open(rsp_cache_file_path, "r", encoding="utf-8") as f1: rsp_cache_json = json.load(f1) else: rsp_cache_json = {} yield rsp_cache_json - with open(rsp_cache_file_path, "w") as f2: + with open(rsp_cache_file_path, "w", encoding="utf-8") as f2: json.dump(rsp_cache_json, f2, indent=4, ensure_ascii=False) - with open(new_rsp_cache_file_path, "w") as f2: + with open(new_rsp_cache_file_path, "w", encoding="utf-8") as f2: json.dump(RSP_CACHE_NEW, f2, indent=4, ensure_ascii=False) @@ -60,6 +64,7 @@ def llm_mock(rsp_cache, mocker, request): llm.rsp_cache = rsp_cache mocker.patch("metagpt.provider.base_llm.BaseLLM.aask", llm.aask) mocker.patch("metagpt.provider.base_llm.BaseLLM.aask_batch", llm.aask_batch) + mocker.patch("metagpt.provider.openai_api.OpenAILLM.aask_code", llm.aask_code) yield mocker if hasattr(request.node, "test_outcome") and request.node.test_outcome.passed: if llm.rsp_candidates: @@ -67,7 +72,7 @@ def llm_mock(rsp_cache, mocker, request): cand_key = list(rsp_candidate.keys())[0] cand_value = list(rsp_candidate.values())[0] if cand_key not in llm.rsp_cache: - logger.info(f"Added '{cand_key[:100]} ... -> {cand_value[:20]} ...' to response cache") + logger.info(f"Added '{cand_key[:100]} ... -> {str(cand_value)[:20]} ...' to response cache") llm.rsp_cache.update(rsp_candidate) RSP_CACHE_NEW.update(rsp_candidate) @@ -75,7 +80,7 @@ def llm_mock(rsp_cache, mocker, request): class Context: def __init__(self): self._llm_ui = None - self._llm_api = LLM(provider=CONFIG.get_default_llm_provider_enum()) + self._llm_api = LLM() @property def llm_api(self): @@ -89,9 +94,9 @@ def llm_api(self): @pytest.fixture(scope="package") def llm_api(): logger.info("Setting up the test") - _context = Context() + g_context = Context() - yield _context.llm_api + yield g_context.llm_api logger.info("Tearing down the test") @@ -124,7 +129,7 @@ async def proxy_func(): server = await asyncio.start_server(handle_client, "127.0.0.1", 0) return server, "http://{}:{}".format(*server.sockets[0].getsockname()) - return proxy_func() + return proxy_func # see https://github.com/Delgan/loguru/issues/59#issuecomment-466591978 @@ -138,23 +143,25 @@ def emit(self, record): yield caplog -# init & dispose git repo -@pytest.fixture(scope="function", autouse=True) -def setup_and_teardown_git_repo(request): - CONFIG.git_repo = GitRepository(local_path=DEFAULT_WORKSPACE_ROOT / f"unittest/{uuid.uuid4().hex}") - CONFIG.git_reinit = True +@pytest.fixture(scope="function") +def context(request): + ctx = MetagptContext() + ctx.git_repo = GitRepository(local_path=DEFAULT_WORKSPACE_ROOT / f"unittest/{uuid.uuid4().hex}") + ctx.repo = ProjectRepo(ctx.git_repo) # Destroy git repo at the end of the test session. def fin(): - CONFIG.git_repo.delete_repository() + if ctx.git_repo: + ctx.git_repo.delete_repository() # Register the function for destroying the environment. request.addfinalizer(fin) + return ctx @pytest.fixture(scope="session", autouse=True) def init_config(): - Config() + pass @pytest.fixture(scope="function") @@ -164,39 +171,63 @@ def new_filename(mocker): yield mocker +@pytest.fixture(scope="session") +def search_rsp_cache(): + rsp_cache_file_path = TEST_DATA_PATH / "search_rsp_cache.json" # read repo-provided + if os.path.exists(rsp_cache_file_path): + with open(rsp_cache_file_path, "r") as f1: + rsp_cache_json = json.load(f1) + else: + rsp_cache_json = {} + yield rsp_cache_json + with open(rsp_cache_file_path, "w") as f2: + json.dump(rsp_cache_json, f2, indent=4, ensure_ascii=False) + + @pytest.fixture def aiohttp_mocker(mocker): - class MockAioResponse: - async def json(self, *args, **kwargs): - return self._json + MockResponse = type("MockResponse", (MockAioResponse,), {}) - def set_json(self, json): - self._json = json + def wrap(method): + def run(self, url, **kwargs): + return MockResponse(self, method, url, **kwargs) - response = MockAioResponse() + return run - class MockCTXMng: - async def __aenter__(self): - return response + mocker.patch("aiohttp.ClientSession.request", MockResponse) + for i in ["get", "post", "delete", "patch"]: + mocker.patch(f"aiohttp.ClientSession.{i}", wrap(i)) + yield MockResponse - async def __aexit__(self, *args, **kwargs): - pass - def __await__(self): - yield - return response +@pytest.fixture +def curl_cffi_mocker(mocker): + MockResponse = type("MockResponse", (MockCurlCffiResponse,), {}) - def mock_request(self, method, url, **kwargs): - return MockCTXMng() + def request(self, *args, **kwargs): + return MockResponse(self, *args, **kwargs) - def wrap(method): - def run(self, url, **kwargs): - return mock_request(self, method, url, **kwargs) + mocker.patch("curl_cffi.requests.Session.request", request) + yield MockResponse - return run - mocker.patch("aiohttp.ClientSession.request", mock_request) - for i in ["get", "post", "delete", "patch"]: - mocker.patch(f"aiohttp.ClientSession.{i}", wrap(i)) +@pytest.fixture +def httplib2_mocker(mocker): + MockResponse = type("MockResponse", (MockHttplib2Response,), {}) + + def request(self, *args, **kwargs): + return MockResponse(self, *args, **kwargs) - yield response + mocker.patch("httplib2.Http.request", request) + yield MockResponse + + +@pytest.fixture +def search_engine_mocker(aiohttp_mocker, curl_cffi_mocker, httplib2_mocker, search_rsp_cache): + # aiohttp_mocker: serpapi/serper + # httplib2_mocker: google + # curl_cffi_mocker: ddg + check_funcs: dict[tuple[str, str], Callable[[dict], str]] = {} + aiohttp_mocker.rsp_cache = httplib2_mocker.rsp_cache = curl_cffi_mocker.rsp_cache = search_rsp_cache + aiohttp_mocker.check_funcs = httplib2_mocker.check_funcs = curl_cffi_mocker.check_funcs = check_funcs + yield check_funcs diff --git a/tests/data/audio/hello.mp3 b/tests/data/audio/hello.mp3 new file mode 100644 index 000000000..7b3aab0a4 Binary files /dev/null and b/tests/data/audio/hello.mp3 differ diff --git a/tests/data/demo_project/dependencies.json b/tests/data/demo_project/dependencies.json index cfcf6c165..738e5d9be 100644 --- a/tests/data/demo_project/dependencies.json +++ b/tests/data/demo_project/dependencies.json @@ -1 +1 @@ -{"docs/system_design/20231221155954.json": ["docs/prds/20231221155954.json"], "docs/tasks/20231221155954.json": ["docs/system_design/20231221155954.json"], "game_2048/game.py": ["docs/tasks/20231221155954.json", "docs/system_design/20231221155954.json"], "game_2048/main.py": ["docs/tasks/20231221155954.json", "docs/system_design/20231221155954.json"], "resources/code_summaries/20231221155954.md": ["docs/tasks/20231221155954.json", "game_2048/game.py", "docs/system_design/20231221155954.json", "game_2048/main.py"], "docs/code_summaries/20231221155954.json": ["docs/tasks/20231221155954.json", "game_2048/game.py", "docs/system_design/20231221155954.json", "game_2048/main.py"], "tests/test_main.py": ["game_2048/main.py"], "tests/test_game.py": ["game_2048/game.py"], "test_outputs/test_main.py.json": ["game_2048/main.py", "tests/test_main.py"], "test_outputs/test_game.py.json": ["game_2048/game.py", "tests/test_game.py"]} \ No newline at end of file +{"docs/system_design/20231221155954.json": ["docs/prd/20231221155954.json"], "docs/task/20231221155954.json": ["docs/system_design/20231221155954.json"], "game_2048/game.py": ["docs/task/20231221155954.json", "docs/system_design/20231221155954.json"], "game_2048/main.py": ["docs/task/20231221155954.json", "docs/system_design/20231221155954.json"], "resources/code_summary/20231221155954.md": ["docs/task/20231221155954.json", "game_2048/game.py", "docs/system_design/20231221155954.json", "game_2048/main.py"], "docs/code_summary/20231221155954.json": ["docs/task/20231221155954.json", "game_2048/game.py", "docs/system_design/20231221155954.json", "game_2048/main.py"], "tests/test_main.py": ["game_2048/main.py"], "tests/test_game.py": ["game_2048/game.py"], "test_outputs/test_main.py.json": ["game_2048/main.py", "tests/test_main.py"], "test_outputs/test_game.py.json": ["game_2048/game.py", "tests/test_game.py"]} \ No newline at end of file diff --git a/tests/data/environment/stanford_town/the_ville/matrix/maze/arena_maze.csv b/tests/data/environment/stanford_town/the_ville/matrix/maze/arena_maze.csv new file mode 100644 index 000000000..f9cf65ecd --- /dev/null +++ b/tests/data/environment/stanford_town/the_ville/matrix/maze/arena_maze.csv @@ -0,0 +1 @@ +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32200, 32200, 32200, 32200, 32200, 0, 0, 32151, 32151, 32151, 32151, 32151, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32199, 32199, 32199, 32199, 32199, 32199, 0, 32140, 32140, 32140, 0, 0, 32160, 32160, 32160, 32160, 32160, 0, 0, 32170, 32170, 32170, 32170, 32170, 32170, 32170, 32170, 0, 32180, 32180, 32180, 0, 0, 32200, 32200, 32200, 32200, 32200, 0, 0, 32151, 32151, 32151, 32151, 32151, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32199, 32199, 32199, 32199, 32199, 32199, 0, 32140, 32140, 32140, 0, 0, 32160, 32160, 32160, 32160, 32160, 0, 0, 32170, 32170, 32170, 32170, 32170, 32170, 32170, 32170, 0, 32180, 32180, 32180, 0, 0, 32200, 32200, 32200, 32200, 32200, 0, 0, 32151, 32151, 32151, 32151, 32151, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32199, 32199, 32199, 32199, 32199, 32199, 0, 32140, 32140, 32140, 0, 0, 32160, 32160, 32160, 32160, 32160, 0, 0, 32170, 32170, 32170, 32170, 32170, 32170, 32170, 32170, 0, 32180, 32180, 32180, 0, 0, 32200, 32200, 32200, 32200, 32200, 0, 0, 32151, 32151, 32151, 32151, 32151, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32199, 32199, 32199, 32199, 32199, 32199, 32199, 32140, 32140, 32140, 0, 0, 32160, 32160, 32160, 32160, 32160, 0, 0, 32170, 32170, 32170, 32170, 32170, 32170, 32170, 32170, 32170, 32180, 32180, 32180, 0, 0, 0, 0, 32200, 32200, 0, 0, 0, 0, 0, 32151, 32151, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32138, 32138, 32138, 32138, 0, 32148, 32148, 32148, 0, 0, 32158, 32158, 32158, 32158, 0, 32168, 32168, 32168, 0, 0, 32178, 32178, 32178, 32178, 32178, 32178, 32178, 32178, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32199, 32199, 32199, 32199, 32199, 32199, 32199, 32140, 32140, 32140, 0, 0, 0, 0, 32160, 32160, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32170, 32170, 0, 0, 0, 0, 0, 0, 32190, 32190, 32190, 32190, 32190, 0, 0, 32141, 32141, 32141, 32141, 32141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32138, 32138, 32138, 32138, 0, 32148, 32148, 32148, 0, 0, 32158, 32158, 32158, 32158, 0, 32168, 32168, 32168, 0, 0, 32178, 32178, 32178, 32178, 32178, 32178, 32178, 32178, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32199, 32199, 0, 0, 0, 0, 0, 0, 32150, 32150, 32150, 32150, 32150, 0, 0, 0, 0, 0, 0, 0, 0, 32170, 32170, 0, 0, 0, 0, 0, 0, 32190, 32190, 32190, 32190, 32190, 0, 0, 32141, 32141, 32141, 32141, 32141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32138, 32138, 32138, 32138, 0, 32148, 32148, 32148, 0, 0, 32158, 32158, 32158, 32158, 0, 32168, 32168, 32168, 0, 0, 32178, 32178, 32178, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32199, 32199, 0, 0, 0, 0, 0, 0, 32150, 32150, 32150, 32150, 32150, 0, 0, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 0, 0, 32190, 32190, 32190, 32190, 32190, 0, 0, 32141, 32141, 32141, 32141, 32141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32181, 32181, 32181, 32181, 32181, 32181, 32181, 32181, 0, 0, 32191, 32191, 32191, 32191, 32191, 32191, 32191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32138, 32138, 32138, 32138, 0, 32148, 32148, 32148, 0, 0, 32158, 32158, 32158, 32158, 0, 32168, 32168, 32168, 0, 0, 32178, 32178, 32178, 32188, 32188, 32188, 32188, 32188, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32161, 32161, 32161, 32161, 32161, 32161, 32161, 32161, 32161, 32161, 0, 0, 32150, 32150, 32150, 32150, 32150, 0, 0, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 0, 0, 32190, 32190, 32190, 32190, 32190, 0, 0, 32141, 32141, 32141, 32141, 32141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32181, 32181, 32181, 32181, 32181, 32181, 32181, 32181, 0, 0, 32191, 32191, 32191, 32191, 32191, 32191, 32191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32138, 32138, 32138, 32138, 32138, 32148, 32148, 32148, 0, 0, 32158, 32158, 32158, 32158, 32158, 32168, 32168, 32168, 0, 0, 32178, 32178, 32178, 32188, 32188, 32188, 32188, 32188, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32161, 32161, 32161, 32161, 32161, 32161, 32161, 32161, 32161, 32161, 0, 0, 32150, 32150, 32150, 32150, 32150, 0, 0, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 0, 0, 32190, 32190, 32190, 32190, 32190, 0, 0, 32141, 32141, 32141, 32141, 32141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32181, 32181, 32181, 32181, 32181, 32181, 32181, 32181, 0, 0, 32191, 32191, 32191, 32191, 32191, 32191, 32191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32138, 32138, 32138, 32138, 32138, 32148, 32148, 32148, 0, 0, 32158, 32158, 32158, 32158, 32158, 32168, 32168, 32168, 0, 0, 32178, 32178, 32178, 32188, 32188, 32188, 32188, 32188, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32161, 32161, 32161, 32161, 32161, 32161, 32161, 32161, 32161, 32161, 0, 0, 32150, 32150, 32150, 32150, 32150, 0, 0, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 0, 0, 32190, 32190, 32190, 32190, 32190, 0, 0, 32141, 32141, 32141, 32141, 32141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32181, 32181, 32181, 32181, 32181, 32181, 32181, 32181, 0, 0, 32191, 32191, 32191, 32191, 32191, 32191, 32191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32138, 32138, 0, 0, 0, 0, 0, 0, 0, 0, 32158, 32158, 0, 0, 0, 0, 0, 0, 0, 0, 32178, 32178, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32161, 32161, 32161, 32161, 32161, 32161, 32161, 32161, 32161, 32161, 0, 0, 32150, 32150, 32150, 32150, 32150, 0, 0, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 0, 0, 32190, 32190, 32190, 32190, 32190, 0, 0, 32141, 32141, 32141, 32141, 32141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32181, 32181, 32181, 32181, 32181, 32181, 32181, 32181, 0, 0, 32191, 32191, 32191, 32191, 32191, 32191, 32191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32138, 32138, 0, 0, 0, 0, 0, 0, 0, 0, 32158, 32158, 0, 0, 0, 0, 0, 0, 0, 0, 32178, 32178, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32161, 32161, 32161, 32161, 32161, 32161, 32161, 32161, 32161, 32161, 0, 0, 32150, 32150, 32150, 32150, 32150, 0, 0, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 0, 0, 32190, 32190, 32190, 32190, 32190, 0, 0, 32141, 32141, 32141, 32141, 32141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32181, 32181, 32181, 32181, 32181, 32181, 32181, 32181, 0, 0, 32191, 32191, 32191, 32191, 32191, 32191, 32191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32189, 32189, 32189, 32189, 32189, 32189, 32189, 32189, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32161, 32161, 32161, 32161, 32161, 32161, 32161, 32161, 32161, 32161, 0, 0, 32150, 32150, 32150, 32150, 32150, 0, 0, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 0, 0, 32190, 32190, 32190, 32190, 32190, 0, 0, 32141, 32141, 32141, 32141, 32141, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32181, 32181, 32181, 32181, 32181, 32181, 32181, 32181, 0, 0, 32191, 32191, 32191, 32191, 32191, 32191, 32191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32179, 32179, 32179, 32179, 32179, 32179, 32189, 32189, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32161, 32161, 32161, 32161, 32161, 32161, 32161, 32161, 32161, 32161, 0, 0, 32150, 32150, 32150, 32150, 32150, 0, 0, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 32171, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32181, 32181, 32181, 32181, 32181, 32181, 32181, 32181, 0, 0, 32191, 32191, 32191, 32191, 32191, 32191, 32191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32198, 32198, 32198, 32198, 32198, 32198, 32139, 32139, 0, 0, 32149, 32149, 32149, 32149, 32149, 32149, 32159, 32159, 0, 32179, 32179, 32179, 32179, 32179, 32179, 32179, 32189, 32189, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32181, 32181, 32181, 32181, 32181, 32181, 32181, 32181, 0, 0, 32191, 32191, 32191, 32191, 32191, 32191, 32191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32198, 32198, 32198, 32198, 32198, 32198, 32139, 32139, 0, 0, 32149, 32149, 32149, 32149, 32149, 32149, 32159, 32159, 0, 32179, 32179, 32179, 32179, 32179, 32179, 32179, 32189, 32189, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32181, 32181, 0, 0, 0, 0, 32191, 32191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32198, 32198, 32198, 32198, 0, 0, 32139, 32139, 0, 0, 32149, 32149, 32149, 32149, 0, 0, 32159, 32159, 0, 32179, 32179, 32179, 32179, 32179, 32179, 32179, 32189, 32189, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32181, 32181, 0, 0, 0, 0, 32191, 32191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32198, 32198, 32198, 32198, 0, 0, 32139, 32139, 0, 0, 32149, 32149, 32149, 32149, 0, 0, 32159, 32159, 0, 32179, 32179, 32179, 32179, 32179, 32179, 32179, 32179, 32179, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32198, 32198, 32198, 32198, 0, 32139, 32139, 32139, 0, 0, 32149, 32149, 32149, 32149, 0, 32159, 32159, 32159, 0, 32179, 32179, 32179, 32179, 32179, 32179, 32179, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 0, 0, 0, 32183, 32183, 32183, 32183, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32198, 32198, 32198, 32198, 0, 32139, 32139, 32139, 0, 0, 32149, 32149, 32149, 32149, 0, 32159, 32159, 32159, 0, 32179, 32179, 32179, 32179, 32179, 32179, 32179, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 0, 0, 0, 32183, 32183, 32183, 32183, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32179, 32179, 32179, 32179, 32179, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 0, 0, 0, 32183, 32183, 32183, 32183, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32179, 32179, 32179, 32179, 32179, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 32201, 0, 0, 0, 32183, 32183, 32183, 32183, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32183, 32183, 32183, 32183, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32183, 32183, 32183, 32183, 32183, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32142, 32142, 0, 0, 0, 0, 32142, 32142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 32183, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32152, 32152, 32152, 32152, 32152, 0, 0, 0, 0, 32152, 32152, 32152, 32152, 32152, 0, 0, 0, 0, 32162, 32162, 32162, 32162, 32162, 0, 0, 0, 0, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 0, 0, 0, 0, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 0, 0, 0, 0, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32172, 32172, 32172, 32172, 32172, 32172, 32172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 0, 0, 0, 0, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32173, 32173, 32173, 32173, 32173, 32173, 32173, 0, 32172, 32172, 32172, 32172, 32172, 32172, 32172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 0, 0, 0, 0, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32163, 32163, 32163, 32163, 32163, 32163, 32173, 32173, 0, 32172, 32172, 32172, 32172, 32172, 32172, 32172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 0, 0, 0, 0, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32163, 32163, 32163, 32163, 32163, 32163, 32173, 32173, 0, 32172, 32172, 32172, 32172, 32172, 32172, 32172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 0, 0, 0, 0, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32163, 32163, 32163, 32163, 32163, 32163, 32173, 32173, 0, 32172, 32172, 32172, 32172, 32172, 32172, 32172, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 0, 0, 0, 0, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32143, 32143, 32143, 32143, 32143, 32143, 0, 32163, 32163, 32163, 32163, 32163, 32163, 32163, 32163, 32163, 32163, 32173, 32173, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 32142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 0, 0, 0, 0, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32143, 32143, 32143, 32143, 32143, 32143, 0, 32163, 32163, 32163, 32163, 32163, 32163, 32163, 32163, 32163, 32163, 32163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32142, 32142, 0, 0, 0, 0, 32142, 32142, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 0, 0, 0, 0, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32143, 32143, 32143, 32143, 32143, 32143, 0, 32163, 32163, 32163, 32163, 32163, 32163, 32163, 32163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32153, 32153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 0, 0, 0, 0, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32143, 32143, 32143, 32143, 32143, 32143, 0, 32163, 32163, 32163, 32163, 32163, 32163, 32163, 32163, 0, 32182, 32182, 0, 0, 32182, 32182, 0, 0, 32153, 32153, 32153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 32152, 0, 0, 0, 0, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 32162, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32143, 32143, 0, 0, 0, 0, 0, 32163, 32163, 32163, 32163, 32163, 32163, 32163, 32163, 0, 32182, 32182, 32182, 32182, 32182, 32182, 0, 0, 32153, 32153, 32153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32163, 32163, 32163, 32163, 32163, 32163, 32163, 32163, 0, 32182, 32182, 32182, 32182, 32182, 32182, 0, 0, 32153, 32153, 32153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32182, 32182, 32182, 32182, 32182, 32182, 0, 0, 32153, 32153, 32153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32202, 32202, 0, 0, 32202, 32202, 0, 0, 32192, 32192, 0, 0, 32192, 32192, 0, 0, 32182, 32182, 32182, 32182, 32182, 32182, 0, 0, 32153, 32153, 32153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32202, 32202, 32202, 32202, 32202, 32202, 0, 0, 32192, 32192, 32192, 32192, 32192, 32192, 0, 0, 32182, 32182, 32182, 32182, 32182, 32182, 0, 0, 32153, 32153, 32153, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32193, 32193, 0, 0, 32193, 0, 0, 0, 32174, 32174, 0, 0, 32174, 0, 0, 0, 32194, 32194, 0, 0, 32194, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32202, 32202, 32202, 32202, 32202, 32202, 0, 0, 32192, 32192, 32192, 32192, 32192, 32192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32193, 32193, 32193, 32193, 32193, 0, 0, 0, 32174, 32174, 32174, 32174, 32174, 0, 0, 0, 32194, 32194, 32194, 32194, 32194, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32202, 32202, 32202, 32202, 32202, 32202, 0, 0, 32192, 32192, 32192, 32192, 32192, 32192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32193, 32193, 32193, 32193, 32193, 0, 0, 0, 32174, 32174, 32174, 32174, 32174, 0, 0, 0, 32194, 32194, 32194, 32194, 32194, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32202, 32202, 32202, 32202, 32202, 32202, 0, 0, 32192, 32192, 32192, 32192, 32192, 32192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32193, 32193, 32193, 32193, 32193, 0, 0, 0, 32174, 32174, 32174, 32174, 32174, 0, 0, 0, 32194, 32194, 32194, 32194, 32194, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32202, 32202, 32202, 32202, 32202, 32202, 0, 0, 32192, 32192, 32192, 32192, 32192, 32192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32193, 32193, 32193, 32193, 32193, 0, 0, 0, 32174, 32174, 32174, 32174, 32174, 0, 0, 0, 32194, 32194, 32194, 32194, 32194, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32202, 32202, 32202, 32202, 32202, 32202, 0, 0, 32192, 32192, 32192, 32192, 32192, 32192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32193, 32193, 32193, 32193, 32193, 0, 0, 0, 32174, 32174, 32174, 32174, 32174, 0, 0, 0, 32194, 32194, 32194, 32194, 32194, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32193, 32193, 32193, 32193, 32193, 0, 0, 0, 32174, 32174, 32174, 32174, 32174, 0, 0, 0, 32194, 32194, 32194, 32194, 32194, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32193, 32193, 32193, 32193, 32193, 0, 0, 0, 32174, 32174, 32174, 32174, 32174, 0, 0, 0, 32194, 32194, 32194, 32194, 32194, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32225, 32225, 32225, 32225, 32225, 32225, 32235, 0, 0, 32245, 32245, 32245, 0, 0, 0, 0, 0, 0, 32206, 32206, 32206, 32206, 32206, 32206, 32216, 0, 0, 32226, 32226, 32226, 0, 0, 0, 0, 0, 0, 32266, 32266, 32266, 32266, 32266, 32266, 32276, 0, 0, 32207, 32207, 32207, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32203, 32203, 0, 0, 0, 0, 0, 0, 32184, 32184, 0, 0, 0, 0, 0, 0, 32204, 32204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32225, 32225, 32225, 32225, 32225, 32225, 32235, 0, 0, 32245, 32245, 32245, 0, 0, 0, 0, 0, 0, 32206, 32206, 32206, 32206, 32206, 32206, 32216, 0, 0, 32226, 32226, 32226, 0, 0, 0, 0, 0, 0, 32266, 32266, 32266, 32266, 32266, 32266, 32276, 0, 0, 32207, 32207, 32207, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32203, 32203, 32203, 32203, 32203, 0, 0, 0, 32184, 32184, 32184, 32184, 32184, 0, 0, 0, 32204, 32204, 32204, 32204, 32204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32225, 32225, 32225, 32225, 32225, 32225, 32235, 0, 0, 32245, 32245, 32245, 0, 0, 0, 0, 0, 0, 32206, 32206, 32206, 32206, 32206, 32206, 32216, 0, 0, 32226, 32226, 32226, 0, 0, 0, 0, 0, 0, 32266, 32266, 32266, 32266, 32266, 32266, 32276, 0, 0, 32207, 32207, 32207, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32203, 32203, 32203, 32203, 32203, 0, 0, 0, 32184, 32184, 32184, 32184, 32184, 0, 0, 0, 32204, 32204, 32204, 32204, 32204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32225, 32225, 32225, 32225, 32225, 32225, 32235, 0, 0, 32245, 32245, 32245, 0, 0, 0, 0, 0, 0, 32206, 32206, 32206, 32206, 32206, 32206, 32216, 0, 0, 32226, 32226, 32226, 0, 0, 0, 0, 0, 0, 32266, 32266, 32266, 32266, 32266, 32266, 32276, 0, 0, 32207, 32207, 32207, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32203, 32203, 32203, 32203, 32203, 0, 0, 0, 32184, 32184, 32184, 32184, 32184, 0, 0, 0, 32204, 32204, 32204, 32204, 32204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32225, 32225, 32225, 32225, 32225, 32225, 32235, 32235, 0, 0, 32245, 32245, 0, 0, 0, 0, 0, 0, 32206, 32206, 32206, 32206, 32206, 32206, 32216, 32216, 0, 0, 32226, 32226, 0, 0, 0, 0, 0, 0, 32266, 32266, 32266, 32266, 32266, 32266, 32276, 32276, 0, 0, 32207, 32207, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32203, 32203, 32203, 32203, 32203, 0, 0, 0, 32184, 32184, 32184, 32184, 32184, 0, 0, 0, 32204, 32204, 32204, 32204, 32204, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32225, 32225, 32225, 32225, 32225, 32225, 32225, 32225, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32206, 32206, 32206, 32206, 32206, 32206, 32206, 32206, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32266, 32266, 32266, 32266, 32266, 32266, 32266, 32266, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32225, 32225, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32205, 32205, 32205, 32205, 32205, 0, 32215, 32215, 32215, 32215, 32215, 32215, 0, 0, 0, 0, 0, 0, 32265, 32265, 32265, 32265, 32265, 0, 32275, 32275, 32275, 32275, 32275, 32275, 0, 0, 0, 0, 0, 0, 32246, 32246, 32246, 32246, 32246, 0, 32256, 32256, 32256, 32256, 32256, 32256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32205, 32205, 32205, 32205, 32205, 0, 32215, 32215, 32215, 32215, 32215, 32215, 0, 0, 0, 0, 0, 0, 32265, 32265, 32265, 32265, 32265, 0, 32275, 32275, 32275, 32275, 32275, 32275, 0, 0, 0, 0, 0, 0, 32246, 32246, 32246, 32246, 32246, 0, 32256, 32256, 32256, 32256, 32256, 32256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32205, 32205, 32205, 32205, 32205, 0, 32215, 32215, 32215, 32215, 32215, 32215, 0, 0, 0, 0, 0, 0, 32265, 32265, 32265, 32265, 32265, 0, 32275, 32275, 32275, 32275, 32275, 32275, 0, 0, 0, 0, 0, 0, 32246, 32246, 32246, 32246, 32246, 0, 32256, 32256, 32256, 32256, 32256, 32256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32255, 32255, 32255, 32255, 32255, 32255, 32255, 32255, 32255, 32255, 0, 0, 0, 0, 0, 0, 0, 0, 32236, 32236, 32236, 32236, 32236, 32236, 32236, 32236, 32236, 32236, 0, 0, 0, 0, 0, 0, 0, 0, 32217, 32217, 32217, 32217, 32217, 32217, 32217, 32217, 32217, 32217, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32255, 32255, 32255, 32255, 32255, 32255, 32255, 32255, 32255, 32255, 0, 0, 0, 0, 0, 0, 0, 0, 32236, 32236, 32236, 32236, 32236, 32236, 32236, 32236, 32236, 32236, 0, 0, 0, 0, 0, 0, 0, 0, 32217, 32217, 32217, 32217, 32217, 32217, 32217, 32217, 32217, 32217, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32255, 32255, 32255, 32255, 32255, 32255, 32255, 32255, 32255, 32255, 0, 0, 0, 0, 0, 0, 0, 0, 32236, 32236, 32236, 32236, 32236, 32236, 32236, 32236, 32236, 32236, 0, 0, 0, 0, 0, 0, 0, 0, 32217, 32217, 32217, 32217, 32217, 32217, 32217, 32217, 32217, 32217, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32255, 32255, 32255, 32255, 32255, 32255, 32255, 32255, 32255, 32255, 0, 0, 0, 0, 0, 0, 0, 0, 32236, 32236, 32236, 32236, 32236, 32236, 32236, 32236, 32236, 32236, 0, 0, 0, 0, 0, 0, 0, 0, 32217, 32217, 32217, 32217, 32217, 32217, 32217, 32217, 32217, 32217, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32255, 32255, 32255, 32255, 32255, 32255, 32255, 32255, 32255, 32255, 0, 0, 0, 0, 0, 0, 0, 0, 32236, 32236, 32236, 32236, 32236, 32236, 32236, 32236, 32236, 32236, 0, 0, 0, 0, 0, 0, 0, 0, 32217, 32217, 32217, 32217, 32217, 32217, 32217, 32217, 32217, 32217, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 \ No newline at end of file diff --git a/tests/data/environment/stanford_town/the_ville/matrix/maze/collision_maze.csv b/tests/data/environment/stanford_town/the_ville/matrix/maze/collision_maze.csv new file mode 100644 index 000000000..40329a8c4 --- /dev/null +++ b/tests/data/environment/stanford_town/the_ville/matrix/maze/collision_maze.csv @@ -0,0 +1 @@ +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 32125, 32125, 0, 0, 0, 0, 0, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 0, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 0, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 32125, 32125, 32125, 0, 0, 0, 32125, 32125, 0, 0, 32125, 32125, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 32125, 32125, 32125, 0, 0, 0, 0, 32125, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 32125, 0, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 0, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 0, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 32125, 0, 0, 32125, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 0, 0, 0, 0, 32125, 32125, 0, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 32125, 0, 32125, 0, 0, 32125, 0, 32125, 32125, 0, 0, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 32125, 32125, 32125, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 32125, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 32125, 32125, 32125, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 32125, 0, 0, 32125, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 32125, 0, 0, 32125, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 32125, 32125, 0, 0, 0, 0, 0, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 32125, 0, 32125, 32125, 32125, 0, 0, 0, 0, 32125, 32125, 0, 32125, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 32125, 32125, 32125, 0, 32125, 32125, 32125, 32125, 0, 0, 0, 32125, 32125, 0, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 32125, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 32125, 0, 0, 32125, 32125, 0, 0, 0, 0, 32125, 32125, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 32125, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 32125, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 32125, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 32125, 0, 0, 32125, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 32125, 0, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 0, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 32125, 32125, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 32125, 32125, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 0, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 0, 32125, 32125, 32125, 0, 0, 32125, 32125, 0, 32125, 32125, 32125, 0, 0, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 32125, 0, 32125, 32125, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 32125, 32125, 0, 32125, 32125, 32125, 0, 0, 32125, 32125, 0, 32125, 32125, 32125, 0, 0, 32125, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 0, 0, 32125, 32125, 0, 32125, 32125, 32125, 0, 0, 32125, 32125, 0, 32125, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 32125, 0, 32125, 32125, 0, 0, 0, 32125, 32125, 0, 32125, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 32125, 0, 32125, 32125, 0, 0, 0, 32125, 32125, 0, 32125, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 32125, 32125, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 32125, 0, 32125, 32125, 0, 0, 0, 32125, 32125, 0, 32125, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 0, 0, 32125, 32125, 0, 32125, 32125, 32125, 0, 0, 32125, 32125, 0, 32125, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 32125, 0, 0, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 32125, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 32125, 0, 32125, 0, 0, 0, 0, 32125, 32125, 0, 32125, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 32125, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 32125, 0, 0, 0, 32125, 32125, 0, 0, 32125, 0, 0, 0, 32125, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 0, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 32125, 0, 0, 0, 32125, 32125, 0, 0, 32125, 0, 0, 0, 32125, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 32125, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 32125, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 32125, 0, 32125, 0, 0, 0, 0, 0, 32125, 0, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 32125, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 32125, 32125, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 0, 0, 0, 32125, 0, 32125, 32125, 32125, 0, 0, 0, 32125, 0, 32125, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 32125, 0, 32125, 0, 0, 0, 0, 0, 32125, 0, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 32125, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 32125, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 32125, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 32125, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 32125, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 0, 32125, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 32125, 0, 32125, 32125, 0, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 32125, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 32125, 0, 0, 0, 0, 0, 0, 32125, 0, 32125, 32125, 0, 32125, 32125, 0, 32125, 32125, 0, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 32125, 32125, 32125, 32125, 0, 0, 32125, 32125, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 32125, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 32125, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 \ No newline at end of file diff --git a/tests/data/environment/stanford_town/the_ville/matrix/maze/game_object_maze.csv b/tests/data/environment/stanford_town/the_ville/matrix/maze/game_object_maze.csv new file mode 100644 index 000000000..9c97dc7bd --- /dev/null +++ b/tests/data/environment/stanford_town/the_ville/matrix/maze/game_object_maze.csv @@ -0,0 +1 @@ +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32208, 32208, 32277, 32277, 32218, 0, 0, 32208, 32208, 32277, 32277, 32218, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32227, 32227, 32238, 32247, 32257, 32257, 0, 32208, 32208, 32218, 0, 0, 32208, 32208, 32277, 32277, 32218, 0, 0, 32227, 32227, 0, 32238, 32247, 32247, 32257, 32257, 0, 32208, 32208, 32218, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32209, 0, 0, 0, 0, 0, 0, 0, 32277, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32237, 0, 0, 0, 0, 0, 0, 0, 0, 32277, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32237, 0, 0, 0, 0, 0, 0, 32277, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32227, 0, 0, 0, 32208, 32208, 32218, 0, 0, 32227, 32227, 0, 0, 0, 32208, 32208, 32218, 0, 0, 0, 32227, 0, 0, 0, 32247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32227, 32227, 0, 0, 32238, 0, 0, 32227, 32227, 0, 0, 32238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32247, 32247, 0, 0, 0, 0, 0, 0, 0, 0, 32247, 32247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32227, 32227, 0, 0, 32238, 0, 0, 32238, 32238, 0, 32258, 32258, 32228, 32249, 32249, 32249, 32249, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32238, 32238, 32239, 32239, 32239, 32228, 32258, 32258, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32258, 0, 0, 0, 0, 0, 0, 32258, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32279, 32279, 32279, 32279, 0, 0, 0, 0, 0, 0, 32240, 32240, 0, 32250, 32250, 32250, 32250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32257, 0, 0, 0, 0, 32277, 0, 0, 0, 0, 0, 0, 32209, 0, 0, 32277, 0, 0, 0, 0, 0, 0, 32277, 32277, 32218, 32208, 32208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32258, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32259, 0, 32259, 0, 32259, 0, 0, 0, 0, 32237, 0, 32228, 0, 0, 0, 0, 32237, 0, 32228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32270, 0, 0, 32270, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32257, 0, 0, 0, 0, 32277, 0, 0, 0, 32267, 32267, 0, 0, 0, 0, 32277, 0, 0, 0, 0, 32237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32260, 0, 0, 32260, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32229, 0, 32229, 0, 32229, 0, 32229, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32259, 0, 32259, 0, 32259, 0, 0, 32259, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32247, 0, 0, 0, 0, 0, 0, 32247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32280, 0, 0, 0, 32270, 0, 0, 32270, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32220, 0, 0, 32247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32279, 32279, 0, 32247, 0, 0, 0, 0, 0, 0, 32247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32260, 0, 0, 32260, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32238, 32238, 0, 0, 0, 32248, 32228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32257, 0, 0, 0, 0, 0, 0, 32219, 0, 0, 0, 0, 0, 0, 32247, 0, 0, 0, 32259, 0, 32259, 0, 32259, 0, 0, 32259, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32247, 0, 0, 0, 0, 0, 0, 32247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32270, 0, 0, 32270, 0, 0, 0, 0, 0, 0, 0, 32250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32258, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32257, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32269, 32269, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32258, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32257, 0, 0, 0, 0, 0, 0, 32218, 0, 0, 0, 32267, 32267, 0, 0, 0, 0, 32218, 0, 0, 0, 32268, 0, 0, 32268, 0, 32258, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32247, 32247, 0, 0, 0, 32277, 0, 0, 0, 32247, 32247, 0, 0, 0, 0, 32277, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32277, 0, 0, 0, 0, 32237, 0, 0, 0, 0, 32277, 0, 0, 0, 32278, 32268, 0, 0, 32268, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32267, 32267, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32278, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32222, 32222, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32227, 0, 0, 0, 32208, 32208, 0, 0, 0, 0, 32227, 0, 0, 0, 32208, 32208, 0, 0, 0, 32278, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32222, 0, 0, 32222, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32278, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32222, 0, 0, 32222, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32222, 0, 0, 32222, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32222, 0, 0, 32222, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32222, 0, 0, 32222, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32222, 32222, 32222, 32222, 32222, 32222, 32222, 32222, 32222, 32222, 32222, 32222, 32222, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32222, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32222, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32222, 32222, 32222, 32222, 32222, 32222, 32222, 32222, 32222, 32222, 32222, 32222, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32222, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32222, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32222, 32222, 32222, 32222, 32222, 32222, 32222, 32222, 32222, 32222, 32222, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32252, 32252, 32252, 0, 0, 32252, 32252, 32252, 32252, 32252, 32252, 32252, 32252, 32252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32252, 32252, 0, 0, 0, 0, 32252, 32252, 32252, 0, 0, 32252, 32252, 32252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32271, 32271, 32271, 32271, 32271, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32252, 32252, 0, 0, 0, 0, 32252, 32252, 0, 0, 0, 0, 32252, 32252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32281, 32281, 32281, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32221, 32221, 32221, 32221, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32252, 32252, 0, 0, 0, 0, 32252, 32252, 0, 0, 0, 0, 32252, 32252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32248, 32228, 32238, 32238, 0, 0, 0, 0, 0, 0, 0, 32247, 32247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32252, 32252, 32252, 0, 0, 32252, 32252, 32252, 0, 0, 0, 0, 32252, 32252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32212, 32212, 32212, 0, 0, 0, 0, 0, 0, 32231, 0, 0, 32231, 0, 0, 0, 0, 0, 0, 0, 0, 32261, 32261, 32261, 32261, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32258, 0, 0, 32227, 32227, 32210, 32210, 0, 32237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32252, 32252, 0, 0, 0, 0, 32252, 32252, 0, 0, 0, 0, 32252, 32252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32231, 0, 0, 32231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32268, 0, 0, 32268, 32258, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32252, 32252, 0, 32252, 32252, 0, 32252, 32252, 0, 0, 0, 0, 32252, 32252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32271, 32271, 32271, 32271, 32271, 32271, 32271, 32271, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32231, 0, 0, 32231, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32258, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32252, 32252, 32252, 32252, 32252, 32252, 32252, 32252, 32252, 32252, 32252, 32252, 32252, 32252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32231, 0, 0, 0, 0, 32241, 32241, 32241, 32241, 32241, 32241, 0, 32241, 32241, 32241, 32241, 32241, 32241, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32268, 0, 0, 32268, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32252, 32252, 32252, 32252, 32252, 32252, 32252, 32252, 32252, 32252, 32252, 32252, 32252, 32252, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32208, 32208, 0, 32277, 32277, 32218, 0, 0, 32278, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32211, 0, 0, 32251, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32278, 0, 0, 0, 32282, 32282, 32282, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32271, 32271, 32271, 32271, 32271, 32271, 32271, 32271, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32211, 0, 0, 32251, 0, 0, 32241, 32241, 32241, 32241, 32241, 32241, 0, 0, 0, 32241, 32241, 32241, 32241, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32278, 0, 0, 32282, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32211, 0, 0, 32251, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32282, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32218, 32277, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32282, 0, 0, 0, 0, 32247, 32247, 0, 0, 32230, 32230, 0, 0, 0, 32277, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32279, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32237, 0, 0, 0, 0, 0, 0, 32257, 32257, 0, 0, 32237, 0, 0, 0, 0, 32227, 0, 0, 0, 32279, 0, 0, 32208, 32208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32272, 32272, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32247, 0, 0, 0, 0, 0, 0, 0, 32237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32247, 32247, 0, 32247, 0, 0, 0, 0, 32247, 32247, 0, 0, 0, 0, 0, 0, 32247, 32247, 0, 0, 32278, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32247, 0, 0, 32247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32258, 0, 0, 0, 0, 0, 0, 0, 32258, 0, 0, 0, 0, 0, 32237, 0, 32258, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32210, 32210, 0, 0, 0, 0, 32247, 0, 0, 32227, 32227, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32237, 0, 32228, 0, 0, 0, 0, 0, 32257, 0, 32228, 0, 0, 0, 0, 0, 0, 0, 32228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32227, 0, 0, 0, 0, 0, 0, 0, 32247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32257, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32227, 32227, 0, 32238, 0, 0, 0, 0, 32227, 32227, 0, 32238, 0, 0, 0, 0, 32227, 32227, 0, 32238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32257, 32257, 32257, 0, 0, 0, 32228, 0, 0, 32218, 32277, 32277, 0, 0, 0, 0, 0, 0, 32257, 32257, 0, 0, 0, 0, 32228, 0, 0, 32218, 32277, 32277, 0, 0, 0, 0, 0, 0, 32257, 32257, 32257, 0, 0, 0, 32228, 0, 0, 32218, 32277, 32277, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32258, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32262, 32262, 0, 0, 32258, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32258, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32268, 0, 0, 32268, 0, 0, 0, 0, 32208, 32208, 0, 0, 0, 0, 0, 0, 0, 0, 32268, 0, 0, 32268, 0, 0, 0, 0, 32208, 32208, 0, 0, 0, 0, 0, 0, 0, 0, 32268, 0, 0, 32268, 0, 0, 0, 0, 32208, 32208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32277, 32277, 0, 0, 32218, 0, 0, 0, 32277, 32277, 0, 0, 32218, 0, 0, 0, 32277, 32277, 0, 0, 32218, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32238, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32268, 0, 0, 32268, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32268, 0, 0, 32268, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32268, 0, 0, 32268, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32208, 32208, 0, 0, 0, 0, 0, 0, 32208, 32208, 0, 0, 0, 0, 0, 0, 32208, 32208, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32237, 32227, 32227, 0, 32227, 32227, 0, 0, 32247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32237, 32227, 32227, 0, 32227, 32227, 0, 0, 32247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32237, 32227, 32227, 0, 32227, 32227, 32237, 0, 32247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32209, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32209, 0, 0, 0, 0, 0, 0, 0, 32247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32232, 32232, 32232, 32232, 32232, 32232, 32232, 32232, 32232, 32232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32232, 32232, 32232, 32232, 32232, 32232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32232, 32232, 32232, 32232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32232, 32232, 32232, 32232, 32232, 32232, 32232, 0, 0, 32232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32242, 0, 0, 0, 32232, 0, 0, 32232, 0, 0, 0, 0, 0, 0, 0, 0, 32232, 32232, 32232, 32232, 32232, 32232, 32232, 0, 0, 32232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32232, 32232, 32232, 32232, 32232, 32232, 32232, 32232, 32232, 32232, 0, 0, 0, 0, 0, 0, 0, 0, 32232, 32232, 32232, 32232, 32232, 32232, 32232, 32232, 32232, 32232, 0, 0, 0, 0, 0, 0, 0, 0, 32232, 0, 0, 0, 0, 32232, 32232, 0, 0, 32232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32232, 0, 0, 0, 0, 0, 0, 0, 0, 32232, 0, 0, 0, 0, 0, 0, 0, 0, 32232, 0, 0, 32232, 0, 0, 32232, 0, 0, 32232, 0, 0, 0, 0, 0, 0, 0, 0, 32232, 0, 0, 0, 0, 32232, 32232, 0, 0, 32232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32232, 32232, 32232, 32232, 32232, 32232, 32232, 32232, 32232, 32232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32232, 32232, 32232, 32232, 32232, 32232, 32232, 32232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32232, 32232, 32232, 32232, 32232, 32232, 32232, 32232, 32232, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 \ No newline at end of file diff --git a/tests/data/environment/stanford_town/the_ville/matrix/maze/sector_maze.csv b/tests/data/environment/stanford_town/the_ville/matrix/maze/sector_maze.csv new file mode 100644 index 000000000..165b7b03a --- /dev/null +++ b/tests/data/environment/stanford_town/the_ville/matrix/maze/sector_maze.csv @@ -0,0 +1 @@ +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32175, 32175, 32175, 32175, 32175, 32175, 32175, 32185, 32185, 32185, 32185, 32185, 32185, 32185, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32145, 32145, 32145, 32145, 32145, 32145, 32145, 32145, 32145, 32145, 32145, 32145, 32155, 32155, 32155, 32155, 32155, 32155, 32155, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32175, 32175, 32175, 32175, 32175, 32175, 32175, 32185, 32185, 32185, 32185, 32185, 32185, 32185, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32145, 32145, 32145, 32145, 32145, 32145, 32145, 32145, 32145, 32145, 32145, 32145, 32155, 32155, 32155, 32155, 32155, 32155, 32155, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32175, 32175, 32175, 32175, 32175, 32175, 32175, 32185, 32185, 32185, 32185, 32185, 32185, 32185, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32145, 32145, 32145, 32145, 32145, 32145, 32145, 32145, 32145, 32145, 32145, 32145, 32155, 32155, 32155, 32155, 32155, 32155, 32155, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32175, 32175, 32175, 32175, 32175, 32175, 32175, 32185, 32185, 32185, 32185, 32185, 32185, 32185, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32145, 32145, 32145, 32145, 32145, 32145, 32145, 32145, 32145, 32145, 32145, 32145, 32155, 32155, 32155, 32155, 32155, 32155, 32155, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32165, 32175, 32175, 32175, 32175, 32175, 32175, 32175, 32185, 32185, 32185, 32185, 32185, 32185, 32185, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 0, 0, 0, 0, 0, 0, 0, 0, 32145, 32145, 32145, 32145, 32145, 32145, 32145, 32145, 32145, 32145, 32145, 32145, 32155, 32155, 32155, 32155, 32155, 32155, 32155, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32165, 32165, 32136, 32136, 32136, 32136, 32136, 32175, 32175, 32175, 32175, 32175, 32175, 32175, 32185, 32185, 32185, 32185, 32185, 32185, 32185, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 0, 0, 0, 0, 0, 0, 0, 0, 32195, 32195, 32195, 32195, 32195, 32145, 32145, 32195, 32195, 32195, 32195, 32195, 32155, 32155, 32155, 32155, 32155, 32155, 32155, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32165, 32165, 32136, 32136, 32136, 32136, 32136, 32175, 32175, 32175, 32175, 32175, 32175, 32175, 32185, 32185, 32185, 32185, 32185, 32185, 32185, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 0, 0, 0, 0, 0, 0, 0, 0, 32195, 32195, 32195, 32195, 32195, 32145, 32145, 32195, 32195, 32195, 32195, 32195, 32155, 32155, 32155, 32155, 32155, 32155, 32155, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32175, 32175, 32175, 32175, 32175, 32175, 32175, 32185, 32185, 32185, 32185, 32185, 32185, 32185, 0, 0, 0, 0, 0, 0, 0, 0, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 0, 0, 0, 0, 0, 0, 0, 0, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32155, 32155, 32155, 32155, 32155, 32155, 32155, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32175, 32175, 32175, 32175, 32175, 32175, 32175, 32185, 32185, 32185, 32185, 32185, 32185, 32185, 0, 0, 0, 0, 0, 0, 0, 0, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 0, 0, 0, 0, 0, 0, 0, 0, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32155, 32155, 32155, 32155, 32155, 32155, 32155, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32175, 32175, 32175, 32175, 32175, 32175, 32175, 32185, 32185, 32185, 32185, 32185, 32185, 32185, 0, 0, 0, 0, 0, 0, 0, 0, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 0, 0, 0, 0, 0, 0, 0, 0, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32155, 32155, 32155, 32155, 32155, 32155, 32155, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32175, 32175, 32175, 32175, 32175, 32175, 32175, 32185, 32185, 32185, 32185, 32185, 32185, 32185, 0, 0, 0, 0, 0, 0, 0, 0, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 0, 0, 0, 0, 0, 0, 0, 0, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32155, 32155, 32155, 32155, 32155, 32155, 32155, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32175, 32175, 32175, 32175, 32175, 32175, 32175, 32185, 32185, 32185, 32185, 32185, 32185, 32185, 0, 0, 0, 0, 0, 0, 0, 0, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 0, 0, 0, 0, 0, 0, 0, 0, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32155, 32155, 32155, 32155, 32155, 32155, 32155, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32175, 32175, 32175, 32175, 32175, 32175, 32175, 32185, 32185, 32185, 32185, 32185, 32185, 32185, 0, 0, 0, 0, 0, 0, 0, 0, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 0, 0, 0, 0, 0, 0, 0, 0, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32155, 32155, 32155, 32155, 32155, 32155, 32155, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32175, 32175, 32175, 32175, 32175, 32175, 32175, 32185, 32185, 32185, 32185, 32185, 32185, 32185, 0, 0, 0, 0, 0, 0, 0, 0, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 0, 0, 0, 0, 0, 0, 0, 0, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32195, 32155, 32155, 32155, 32155, 32155, 32155, 32155, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 32136, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 0, 0, 32177, 32177, 32177, 32177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 0, 0, 32177, 32177, 32177, 32177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 32135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 0, 0, 32177, 32177, 32177, 32177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32135, 32135, 32135, 32135, 32135, 32135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 32146, 0, 0, 32177, 32177, 32177, 32177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32135, 32135, 32135, 32135, 32135, 32135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32177, 32177, 32177, 32177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32177, 32177, 32177, 32177, 32177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32166, 32166, 32166, 32166, 32166, 32166, 0, 0, 0, 0, 32166, 32166, 32166, 32166, 32166, 32166, 0, 0, 32176, 32176, 32176, 32176, 32176, 32176, 0, 0, 0, 0, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 0, 0, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 0, 0, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 0, 0, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 0, 0, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 0, 0, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 0, 0, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 0, 0, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 0, 0, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 32156, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 0, 0, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 0, 0, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 32166, 0, 0, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 32176, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32186, 32186, 32186, 0, 0, 32186, 32186, 0, 32196, 32196, 32196, 0, 0, 32196, 32196, 0, 32137, 32137, 32137, 0, 0, 32137, 32137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32186, 32186, 32186, 32186, 32186, 32186, 32186, 0, 32196, 32196, 32196, 32196, 32196, 32196, 32196, 0, 32137, 32137, 32137, 32137, 32137, 32137, 32137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32186, 32186, 32186, 32186, 32186, 32186, 32186, 0, 32196, 32196, 32196, 32196, 32196, 32196, 32196, 0, 32137, 32137, 32137, 32137, 32137, 32137, 32137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32186, 32186, 32186, 32186, 32186, 32186, 32186, 0, 32196, 32196, 32196, 32196, 32196, 32196, 32196, 0, 32137, 32137, 32137, 32137, 32137, 32137, 32137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32186, 32186, 32186, 32186, 32186, 32186, 32186, 0, 32196, 32196, 32196, 32196, 32196, 32196, 32196, 0, 32137, 32137, 32137, 32137, 32137, 32137, 32137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 32177, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32186, 32186, 32186, 32186, 32186, 32186, 32186, 0, 32196, 32196, 32196, 32196, 32196, 32196, 32196, 0, 32137, 32137, 32137, 32137, 32137, 32137, 32137, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32186, 32186, 32186, 32186, 32186, 32186, 32186, 0, 32196, 32196, 32196, 32196, 32196, 32196, 32196, 0, 32137, 32137, 32137, 32137, 32137, 32137, 32137, 0, 0, 0, 0, 0, 0, 0, 0, 32147, 32147, 32147, 32147, 32147, 0, 0, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 0, 0, 0, 0, 32157, 32157, 32157, 32157, 32157, 0, 0, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 0, 0, 0, 0, 32167, 32167, 32167, 32167, 32167, 0, 0, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32186, 32186, 32186, 32186, 32186, 32186, 32186, 0, 32196, 32196, 32196, 32196, 32196, 32196, 32196, 0, 32137, 32137, 32137, 32137, 32137, 32137, 32137, 0, 0, 0, 0, 0, 0, 0, 0, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 0, 0, 0, 0, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 0, 0, 0, 0, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32186, 32186, 32186, 32186, 32186, 32186, 32186, 0, 32196, 32196, 32196, 32196, 32196, 32196, 32196, 0, 32137, 32137, 32137, 32137, 32137, 32137, 32137, 0, 0, 0, 0, 0, 0, 0, 0, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 0, 0, 0, 0, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 0, 0, 0, 0, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32186, 32186, 32186, 32186, 32186, 32186, 32186, 0, 32196, 32196, 32196, 32196, 32196, 32196, 32196, 0, 32137, 32137, 32137, 32137, 32137, 32137, 32137, 0, 0, 0, 0, 0, 0, 0, 0, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 0, 0, 0, 0, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 0, 0, 0, 0, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32186, 32186, 32186, 32186, 32186, 32186, 32186, 0, 32196, 32196, 32196, 32196, 32196, 32196, 32196, 0, 32137, 32137, 32137, 32137, 32137, 32137, 32137, 0, 0, 0, 0, 0, 0, 0, 0, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 0, 0, 0, 0, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 0, 0, 0, 0, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32186, 32186, 32186, 32186, 32186, 32186, 32186, 0, 32196, 32196, 32196, 32196, 32196, 32196, 32196, 0, 32137, 32137, 32137, 32137, 32137, 32137, 32137, 0, 0, 0, 0, 0, 0, 0, 0, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 0, 0, 0, 0, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 0, 0, 0, 0, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32186, 32186, 32186, 32186, 32186, 32186, 32186, 0, 32196, 32196, 32196, 32196, 32196, 32196, 32196, 0, 32137, 32137, 32137, 32137, 32137, 32137, 32137, 0, 0, 0, 0, 0, 0, 0, 0, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 0, 0, 0, 0, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 0, 0, 0, 0, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 0, 0, 0, 0, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 0, 0, 0, 0, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 0, 0, 0, 0, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 0, 0, 0, 0, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 0, 0, 0, 0, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 0, 0, 0, 0, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 0, 0, 0, 0, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 0, 0, 0, 0, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 0, 0, 0, 0, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 0, 0, 0, 0, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 0, 0, 0, 0, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 0, 0, 0, 0, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 0, 0, 0, 0, 0, 0, 0, 0, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 0, 0, 0, 0, 0, 0, 0, 0, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 0, 0, 0, 0, 0, 0, 0, 0, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 0, 0, 0, 0, 0, 0, 0, 0, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 0, 0, 0, 0, 0, 0, 0, 0, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 0, 0, 0, 0, 0, 0, 0, 0, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 0, 0, 0, 0, 0, 0, 0, 0, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 0, 0, 0, 0, 0, 0, 0, 0, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 0, 0, 0, 0, 0, 0, 0, 0, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 0, 0, 0, 0, 0, 0, 0, 0, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 32147, 0, 0, 0, 0, 0, 0, 0, 0, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 32157, 0, 0, 0, 0, 0, 0, 0, 0, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 32167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 \ No newline at end of file diff --git a/tests/data/environment/stanford_town/the_ville/matrix/maze/spawning_location_maze.csv b/tests/data/environment/stanford_town/the_ville/matrix/maze/spawning_location_maze.csv new file mode 100644 index 000000000..6d7ca5727 --- /dev/null +++ b/tests/data/environment/stanford_town/the_ville/matrix/maze/spawning_location_maze.csv @@ -0,0 +1 @@ +0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32306, 32316, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32307, 32317, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32285, 0, 0, 0, 0, 0, 0, 0, 0, 32295, 32305, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32315, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32288, 32298, 0, 0, 0, 0, 0, 32308, 32318, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32287, 32297, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32286, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32296, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32313, 32323, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32294, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32304, 32314, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32324, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32289, 32299, 0, 0, 0, 0, 0, 0, 32309, 32319, 0, 0, 0, 0, 0, 0, 32290, 32300, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32310, 32320, 0, 32291, 32301, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32311, 32321, 0, 32292, 32302, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32312, 32322, 0, 32293, 32303, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 \ No newline at end of file diff --git a/tests/data/environment/stanford_town/the_ville/matrix/maze_meta_info.json b/tests/data/environment/stanford_town/the_ville/matrix/maze_meta_info.json new file mode 100644 index 000000000..32a15fbb6 --- /dev/null +++ b/tests/data/environment/stanford_town/the_ville/matrix/maze_meta_info.json @@ -0,0 +1,5 @@ +{"world_name": "the ville", + "maze_width": 140, + "maze_height": 100, + "sq_tile_size": 32, + "special_constraint": ""} \ No newline at end of file diff --git a/tests/data/environment/stanford_town/the_ville/matrix/special_blocks/arena_blocks.csv b/tests/data/environment/stanford_town/the_ville/matrix/special_blocks/arena_blocks.csv new file mode 100644 index 000000000..c92e0c6ab --- /dev/null +++ b/tests/data/environment/stanford_town/the_ville/matrix/special_blocks/arena_blocks.csv @@ -0,0 +1,63 @@ +32138, the Ville, artist's co-living space, Latoya Williams's room +32148, the Ville, artist's co-living space, Latoya Williams's bathroom +32158, the Ville, artist's co-living space, Rajiv Patel's room +32168, the Ville, artist's co-living space, Rajiv Patel's bathroom +32178, the Ville, artist's co-living space, Abigail Chen's room +32188, the Ville, artist's co-living space, Abigail Chen's bathroom +32198, the Ville, artist's co-living space, Francisco Lopez's room +32139, the Ville, artist's co-living space, Francisco Lopez's bathroom +32149, the Ville, artist's co-living space, Hailey Johnson's room +32159, the Ville, artist's co-living space, Hailey Johnson's bathroom +32179, the Ville, artist's co-living space, common room +32189, the Ville, artist's co-living space, kitchen +32199, the Ville, Arthur Burton's apartment, main room +32140, the Ville, Arthur Burton's apartment, bathroom +32150, the Ville, Ryan Park's apartment, main room +32160, the Ville, Ryan Park's apartment, bathroom +32170, the Ville, Isabella Rodriguez's apartment, main room +32180, the Ville, Isabella Rodriguez's apartment, bathroom +32190, the Ville, Giorgio Rossi's apartment, main room +32200, the Ville, Giorgio Rossi's apartment, bathroom +32141, the Ville, Carlos Gomez's apartment, main room +32151, the Ville, Carlos Gomez's apartment, bathroom +32161, the Ville, The Rose and Crown Pub, pub +32171, the Ville, Hobbs Cafe, cafe +32181, the Ville, Oak Hill College, classroom +32191, the Ville, Oak Hill College, library +32201, the Ville, Oak Hill College, hallway +32142, the Ville, Johnson Park, park +32152, the Ville, Harvey Oak Supply Store, supply store +32162, the Ville, The Willows Market and Pharmacy, store +32193, the Ville, Adam Smith's house, main room +32203, the Ville, Adam Smith's house, bathroom +32174, the Ville, Yuriko Yamamoto's house, main room +32184, the Ville, Yuriko Yamamoto's house, bathroom +32194, the Ville, Moore family's house, main room +32204, the Ville, Moore family's house, bathroom +32172, the Ville, Dorm for Oak Hill College, Klaus Mueller's room +32182, the Ville, Dorm for Oak Hill College, Maria Lopez's room +32192, the Ville, Dorm for Oak Hill College, Ayesha Khan's room +32202, the Ville, Dorm for Oak Hill College, Wolfgang Schulz's room +32143, the Ville, Dorm for Oak Hill College, man's bathroom +32153, the Ville, Dorm for Oak Hill College, woman's bathroom +32163, the Ville, Dorm for Oak Hill College, common room +32173, the Ville, Dorm for Oak Hill College, kitchen +32183, the Ville, Dorm for Oak Hill College, garden +32205, the Ville, Tamara Taylor and Carmen Ortiz's house, Tamara Taylor's room +32215, the Ville, Tamara Taylor and Carmen Ortiz's house, Carmen Ortiz's room +32225, the Ville, Tamara Taylor and Carmen Ortiz's house, common room +32235, the Ville, Tamara Taylor and Carmen Ortiz's house, kitchen +32245, the Ville, Tamara Taylor and Carmen Ortiz's house, bathroom +32255, the Ville, Tamara Taylor and Carmen Ortiz's house, garden +32265, the Ville, Moreno family's house, Tom and Jane Moreno's bedroom +32275, the Ville, Moreno family's house, empty bedroom +32206, the Ville, Moreno family's house, common room +32216, the Ville, Moreno family's house, kitchen +32226, the Ville, Moreno family's house, bathroom +32236, the Ville, Moreno family's house, garden +32246, the Ville, Lin family's house, Mei and John Lin's bedroom +32256, the Ville, Lin family's house, Eddy Lin's bedroom +32266, the Ville, Lin family's house, common room +32276, the Ville, Lin family's house, kitchen +32207, the Ville, Lin family's house, bathroom +32217, the Ville, Lin family's house, garden \ No newline at end of file diff --git a/tests/data/environment/stanford_town/the_ville/matrix/special_blocks/game_object_blocks.csv b/tests/data/environment/stanford_town/the_ville/matrix/special_blocks/game_object_blocks.csv new file mode 100644 index 000000000..4afc74b7a --- /dev/null +++ b/tests/data/environment/stanford_town/the_ville/matrix/special_blocks/game_object_blocks.csv @@ -0,0 +1,46 @@ +32227, the Ville, , bed +32237, the Ville, , desk +32247, the Ville, , closet +32257, the Ville, , shelf +32267, the Ville, , easel +32277, the Ville, , bathroom sink +32208, the Ville, , shower +32218, the Ville, , toilet +32228, the Ville, , kitchen sink +32238, the Ville, , refrigerator +32248, the Ville, , toaster +32258, the Ville, , cooking area +32268, the Ville, , common room table +32278, the Ville, , common room sofa +32209, the Ville, , guitar +32219, the Ville, , microphone +32229, the Ville, , bar customer seating +32239, the Ville, , behind the bar counter +32249, the Ville, , behind the cafe counter +32259, the Ville, , cafe customer seating +32269, the Ville, , piano +32279, the Ville, , blackboard +32210, the Ville, , game console +32220, the Ville, , computer desk +32230, the Ville, , computer +32240, the Ville, , library sofa +32250, the Ville, , bookshelf +32260, the Ville, , library table +32270, the Ville, , classroom student seating +32280, the Ville, , classroom podium +32211, the Ville, , behind the pharmacy counter +32221, the Ville, , behind the grocery counter +32231, the Ville, , pharmacy store shelf +32241, the Ville, , grocery store shelf +32251, the Ville, , pharmacy store counter +32261, the Ville, , grocery store counter +32271, the Ville, , supply store product shelf +32281, the Ville, , behind the supply store counter +32212, the Ville, , supply store counter +32222, the Ville, , dorm garden +32232, the Ville, , house garden +32242, the Ville, , garden chair +32252, the Ville, , park garden +32262, the Ville, , harp +32272, the Ville, , lifting weight +32282, the Ville, , pool table \ No newline at end of file diff --git a/tests/data/environment/stanford_town/the_ville/matrix/special_blocks/sector_blocks.csv b/tests/data/environment/stanford_town/the_ville/matrix/special_blocks/sector_blocks.csv new file mode 100644 index 000000000..ba09c4c35 --- /dev/null +++ b/tests/data/environment/stanford_town/the_ville/matrix/special_blocks/sector_blocks.csv @@ -0,0 +1,19 @@ +32135, the Ville, artist's co-living space +32145, the Ville, Arthur Burton's apartment +32155, the Ville, Ryan Park's apartment +32165, the Ville, Isabella Rodriguez's apartment +32175, the Ville, Giorgio Rossi's apartment +32185, the Ville, Carlos Gomez's apartment +32195, the Ville, The Rose and Crown Pub +32136, the Ville, Hobbs Cafe +32146, the Ville, Oak Hill College +32156, the Ville, Johnson Park +32166, the Ville, Harvey Oak Supply Store +32176, the Ville, The Willows Market and Pharmacy +32186, the Ville, Adam Smith's house +32196, the Ville, Yuriko Yamamoto's house +32137, the Ville, Moore family's house +32147, the Ville, Tamara Taylor and Carmen Ortiz's house +32157, the Ville, Moreno family's house +32167, the Ville, Lin family's house +32177, the Ville, Dorm for Oak Hill College \ No newline at end of file diff --git a/tests/data/environment/stanford_town/the_ville/matrix/special_blocks/spawning_location_blocks.csv b/tests/data/environment/stanford_town/the_ville/matrix/special_blocks/spawning_location_blocks.csv new file mode 100644 index 000000000..564fc76b4 --- /dev/null +++ b/tests/data/environment/stanford_town/the_ville/matrix/special_blocks/spawning_location_blocks.csv @@ -0,0 +1,40 @@ +32285, the Ville, artist's co-living space, Latoya Williams's room, sp-A +32295, the Ville, artist's co-living space, Rajiv Patel's room, sp-A +32305, the Ville, artist's co-living space, Rajiv Patel's room, sp-B +32315, the Ville, artist's co-living space, Abigail Chen's room, sp-A +32286, the Ville, artist's co-living space, Francisco Lopez's room, sp-A +32296, the Ville, artist's co-living space, Hailey Johnson's room, sp-A +32306, the Ville, Arthur Burton's apartment, main room, sp-A +32316, the Ville, Arthur Burton's apartment, main room, sp-B +32287, the Ville, Ryan Park's apartment, main room, sp-A +32297, the Ville, Ryan Park's apartment, main room, sp-B +32307, the Ville, Isabella Rodriguez's apartment, main room, sp-A +32317, the Ville, Isabella Rodriguez's apartment, main room, sp-B +32288, the Ville, Giorgio Rossi's apartment, main room, sp-A +32298, the Ville, Giorgio Rossi's apartment, main room, sp-B +32308, the Ville, Carlos Gomez's apartment, main room, sp-A +32318, the Ville, Carlos Gomez's apartment, main room, sp-B +32289, the Ville, Adam Smith's house, main room, sp-A +32299, the Ville, Adam Smith's house, main room, sp-B +32309, the Ville, Yuriko Yamamoto's house, main room, sp-A +32319, the Ville, Yuriko Yamamoto's house, main room, sp-B +32290, the Ville, Moore family's house, main room, sp-A +32300, the Ville, Moore family's house, main room, sp-B +32310, the Ville, Tamara Taylor and Carmen Ortiz's house, Tamara Taylor's room, sp-A +32320, the Ville, Tamara Taylor and Carmen Ortiz's house, Tamara Taylor's room, sp-B +32291, the Ville, Tamara Taylor and Carmen Ortiz's house, Carmen Ortiz's room, sp-A +32301, the Ville, Tamara Taylor and Carmen Ortiz's house, Carmen Ortiz's room, sp-B +32311, the Ville, Moreno family's house, Tom and Jane Moreno's bedroom, sp-A +32321, the Ville, Moreno family's house, Tom and Jane Moreno's bedroom, sp-B +32292, the Ville, Moreno family's house, empty bedroom, sp-A +32302, the Ville, Moreno family's house, empty bedroom, sp-B +32312, the Ville, Lin family's house, Mei and John Lin's bedroom, sp-A +32322, the Ville, Lin family's house, Mei and John Lin's bedroom, sp-B +32293, the Ville, Lin family's house, Eddy Lin's bedroom, sp-A +32303, the Ville, Lin family's house, Eddy Lin's bedroom, sp-B +32313, the Ville, Dorm for Oak Hill College, Klaus Mueller's room, sp-A +32323, the Ville, Dorm for Oak Hill College, Klaus Mueller's room, sp-B +32294, the Ville, Dorm for Oak Hill College, Maria Lopez's room, sp-A +32304, the Ville, Dorm for Oak Hill College, Ayesha Khan's room, sp-A +32314, the Ville, Dorm for Oak Hill College, Ayesha Khan's room, sp-B +32324, the Ville, Dorm for Oak Hill College, Wolfgang Schulz's room, sp-A \ No newline at end of file diff --git a/tests/data/environment/stanford_town/the_ville/matrix/special_blocks/world_blocks.csv b/tests/data/environment/stanford_town/the_ville/matrix/special_blocks/world_blocks.csv new file mode 100644 index 000000000..b04d990bb --- /dev/null +++ b/tests/data/environment/stanford_town/the_ville/matrix/special_blocks/world_blocks.csv @@ -0,0 +1 @@ +32134, the Ville \ No newline at end of file diff --git a/tests/data/incremental_dev_project/Gomoku.zip b/tests/data/incremental_dev_project/Gomoku.zip index d6c6b8d16..23649565a 100644 Binary files a/tests/data/incremental_dev_project/Gomoku.zip and b/tests/data/incremental_dev_project/Gomoku.zip differ diff --git a/tests/data/incremental_dev_project/dice_simulator_new.zip b/tests/data/incremental_dev_project/dice_simulator_new.zip index 4b8d3f038..4752ab4c5 100644 Binary files a/tests/data/incremental_dev_project/dice_simulator_new.zip and b/tests/data/incremental_dev_project/dice_simulator_new.zip differ diff --git a/tests/data/incremental_dev_project/mock.py b/tests/data/incremental_dev_project/mock.py index 5c5191cf2..f2eb71359 100644 --- a/tests/data/incremental_dev_project/mock.py +++ b/tests/data/incremental_dev_project/mock.py @@ -373,7 +373,7 @@ def main(self): } CODE_PLAN_AND_CHANGE_SAMPLE = { - "Plan": '\n1. Plan for gui.py: Develop the GUI using Tkinter to replace the command-line interface. Start by setting up the main window and event handling. Then, add widgets for displaying the game status, results, and feedback. Implement interactive elements for difficulty selection and visualize the guess history. Finally, create animations for guess feedback and ensure responsiveness across different screen sizes.\n```python\nclass GUI:\n- pass\n+ def __init__(self):\n+ self.setup_window()\n+\n+ def setup_window(self):\n+ # Initialize the main window using Tkinter\n+ pass\n+\n+ def bind_events(self):\n+ # Bind button clicks and other events\n+ pass\n+\n+ def update_feedback(self, message: str):\n+ # Update the feedback label with the given message\n+ pass\n+\n+ def update_attempts(self, attempts: int):\n+ # Update the attempts label with the number of attempts\n+ pass\n+\n+ def update_history(self, history: list):\n+ # Update the history view with the list of past guesses\n+ pass\n+\n+ def show_difficulty_selector(self):\n+ # Show buttons or a dropdown for difficulty selection\n+ pass\n+\n+ def animate_guess_result(self, correct: bool):\n+ # Trigger an animation for correct or incorrect guesses\n+ pass\n```\n\n2. Plan for main.py: Modify the main.py to initialize the GUI and start the event-driven game loop. Ensure that the GUI is the primary interface for user interaction.\n```python\nclass Main:\n def main(self):\n- user_interface = UI()\n- user_interface.start()\n+ graphical_user_interface = GUI()\n+ graphical_user_interface.setup_window()\n+ graphical_user_interface.bind_events()\n+ # Start the Tkinter main loop\n+ pass\n\n if __name__ == "__main__":\n main_instance = Main()\n main_instance.main()\n```\n\n3. Plan for ui.py: Refactor ui.py to work with the new GUI class. Remove command-line interactions and delegate display and input tasks to the GUI.\n```python\nclass UI:\n- def display_message(self, message: str):\n- print(message)\n+\n+ def display_message(self, message: str):\n+ # This method will now pass the message to the GUI to display\n+ pass\n\n- def get_user_input(self, prompt: str) -> str:\n- return input(prompt)\n+\n+ def get_user_input(self, prompt: str) -> str:\n+ # This method will now trigger the GUI to get user input\n+ pass\n\n- def show_attempts(self, attempts: int):\n- print(f"Number of attempts: {attempts}")\n+\n+ def show_attempts(self, attempts: int):\n+ # This method will now update the GUI with the number of attempts\n+ pass\n\n- def show_history(self, history: list):\n- print("Guess history:")\n- for guess in history:\n- print(guess)\n+\n+ def show_history(self, history: list):\n+ # This method will now update the GUI with the guess history\n+ pass\n```\n\n4. Plan for game.py: Ensure game.py remains mostly unchanged as it contains the core game logic. However, make minor adjustments if necessary to integrate with the new GUI.\n```python\nclass Game:\n # No changes required for now\n```\n' + "Code Plan And Change": '\n1. Plan for gui.py: Develop the GUI using Tkinter to replace the command-line interface. Start by setting up the main window and event handling. Then, add widgets for displaying the game status, results, and feedback. Implement interactive elements for difficulty selection and visualize the guess history. Finally, create animations for guess feedback and ensure responsiveness across different screen sizes.\n```python\nclass GUI:\n- pass\n+ def __init__(self):\n+ self.setup_window()\n+\n+ def setup_window(self):\n+ # Initialize the main window using Tkinter\n+ pass\n+\n+ def bind_events(self):\n+ # Bind button clicks and other events\n+ pass\n+\n+ def update_feedback(self, message: str):\n+ # Update the feedback label with the given message\n+ pass\n+\n+ def update_attempts(self, attempts: int):\n+ # Update the attempts label with the number of attempts\n+ pass\n+\n+ def update_history(self, history: list):\n+ # Update the history view with the list of past guesses\n+ pass\n+\n+ def show_difficulty_selector(self):\n+ # Show buttons or a dropdown for difficulty selection\n+ pass\n+\n+ def animate_guess_result(self, correct: bool):\n+ # Trigger an animation for correct or incorrect guesses\n+ pass\n```\n\n2. Plan for main.py: Modify the main.py to initialize the GUI and start the event-driven game loop. Ensure that the GUI is the primary interface for user interaction.\n```python\nclass Main:\n def main(self):\n- user_interface = UI()\n- user_interface.start()\n+ graphical_user_interface = GUI()\n+ graphical_user_interface.setup_window()\n+ graphical_user_interface.bind_events()\n+ # Start the Tkinter main loop\n+ pass\n\n if __name__ == "__main__":\n main_instance = Main()\n main_instance.main()\n```\n\n3. Plan for ui.py: Refactor ui.py to work with the new GUI class. Remove command-line interactions and delegate display and input tasks to the GUI.\n```python\nclass UI:\n- def display_message(self, message: str):\n- print(message)\n+\n+ def display_message(self, message: str):\n+ # This method will now pass the message to the GUI to display\n+ pass\n\n- def get_user_input(self, prompt: str) -> str:\n- return input(prompt)\n+\n+ def get_user_input(self, prompt: str) -> str:\n+ # This method will now trigger the GUI to get user input\n+ pass\n\n- def show_attempts(self, attempts: int):\n- print(f"Number of attempts: {attempts}")\n+\n+ def show_attempts(self, attempts: int):\n+ # This method will now update the GUI with the number of attempts\n+ pass\n\n- def show_history(self, history: list):\n- print("Guess history:")\n- for guess in history:\n- print(guess)\n+\n+ def show_history(self, history: list):\n+ # This method will now update the GUI with the guess history\n+ pass\n```\n\n4. Plan for game.py: Ensure game.py remains mostly unchanged as it contains the core game logic. However, make minor adjustments if necessary to integrate with the new GUI.\n```python\nclass Game:\n # No changes required for now\n```\n' } REFINED_CODE_INPUT_SAMPLE = """ diff --git a/tests/data/incremental_dev_project/number_guessing_game.zip b/tests/data/incremental_dev_project/number_guessing_game.zip index 1f609b707..7bbe07713 100644 Binary files a/tests/data/incremental_dev_project/number_guessing_game.zip and b/tests/data/incremental_dev_project/number_guessing_game.zip differ diff --git a/tests/data/incremental_dev_project/pygame_2048.zip b/tests/data/incremental_dev_project/pygame_2048.zip index 2f0457be6..93e9cf0fe 100644 Binary files a/tests/data/incremental_dev_project/pygame_2048.zip and b/tests/data/incremental_dev_project/pygame_2048.zip differ diff --git a/tests/data/incremental_dev_project/simple_add_calculator.zip b/tests/data/incremental_dev_project/simple_add_calculator.zip index 05cc1cc6c..e6e73f8f9 100644 Binary files a/tests/data/incremental_dev_project/simple_add_calculator.zip and b/tests/data/incremental_dev_project/simple_add_calculator.zip differ diff --git a/tests/data/incremental_dev_project/snake_game.zip b/tests/data/incremental_dev_project/snake_game.zip index d737ec0d3..997203a18 100644 Binary files a/tests/data/incremental_dev_project/snake_game.zip and b/tests/data/incremental_dev_project/snake_game.zip differ diff --git a/tests/data/incremental_dev_project/word_cloud.zip b/tests/data/incremental_dev_project/word_cloud.zip index d15c1cf74..d8747d14d 100644 Binary files a/tests/data/incremental_dev_project/word_cloud.zip and b/tests/data/incremental_dev_project/word_cloud.zip differ diff --git a/tests/data/openai/embedding.json b/tests/data/openai/embedding.json new file mode 100644 index 000000000..249c78ecf --- /dev/null +++ b/tests/data/openai/embedding.json @@ -0,0 +1 @@ +{"object": "list", "data": [{"object": "embedding", "index": 0, "embedding": [-0.01999368, -0.02016083, 0.013037679, -0.011751912, -0.02810687, 0.0056188027, -0.011726197, -0.01088402, 0.01021542, -0.010967594, 0.0113276085, -0.0106332945, -0.012806241, -0.021626605, -0.00513664, 0.0023031305, 0.021343736, -0.0029026193, 0.009951838, -0.013114825, -0.0057730945, 0.0065799137, -0.016084947, -0.027309695, -0.011906204, 0.0066474164, 0.02921263, -0.013436267, -0.009096803, -0.00037287248, 0.033378515, -0.022912372, -0.036027197, -0.0077338894, -0.02307952, -0.011784056, -0.018527905, -0.0094182445, 0.02557391, -0.011276178, 0.017820733, 0.023670973, 0.0017293568, -0.0031501297, -0.0016192631, 0.01044043, 0.009071087, -0.014670604, -0.017820733, 0.010646152, 0.018656481, -0.010691154, -0.022410922, -0.017692156, -0.024391003, 0.010993309, -0.01088402, 0.0073545882, -0.000542433, -0.0028238662, 0.008569638, 0.0073481593, -0.027438272, 0.0018209678, -0.001176477, 0.00038673467, 0.010376141, 0.02259093, 0.03656722, 0.0010904913, 0.012581232, -0.0006497142, 0.010929021, 0.0024638514, 0.0135777015, 0.01380914, 0.009270381, 0.008486063, 0.01402772, -0.0069495714, 0.012414082, -0.032658488, 0.018785058, 0.013217687, 0.020842286, 0.016753547, -0.026743958, 0.021446597, -0.021112297, -0.008666071, 0.011591191, 0.025149606, 0.00043796445, 0.015699217, -0.0024863523, 0.020996578, 0.010954737, 0.022063766, 0.010948308, -0.024223853, -0.011944777, 0.0033365658, -0.010061128, -0.000753781, -0.019389369, -0.013616274, 0.004429468, -0.004220531, 0.0024043845, 0.016059233, -0.020276548, 0.024609584, 0.0053873644, -0.050222065, -0.0033654957, 0.0017872164, 0.009836119, -0.014567742, -0.0073545882, -0.0033719244, 0.009006799, 0.014850611, 0.033327084, -0.019157931, 0.026846819, 0.0026358226, -0.009758973, -0.023979558, -0.013783424, -0.00845392, 0.039473053, 0.007766034, 0.01595637, 0.0010205777, -0.022346634, 0.03584719, -0.018373612, 0.0006288205, -0.0005010474, -0.0043651797, 0.010871162, 0.035075728, -0.015673501, 0.004352322, -0.0023240242, 0.01530063, -0.0027097543, 0.00085583876, 0.015506352, -0.0063581187, 0.022655217, -0.004063024, 0.013886286, -0.0037640834, -0.010350426, 0.0055705863, 0.00433625, 0.014169155, 0.011700481, -0.011218319, 0.00019658175, 0.008691786, -0.0035390742, -0.028312594, 0.0018241822, 0.043330353, 0.022410922, 0.03155273, -0.0077403183, -0.025381044, -0.028724039, 0.021755181, -0.024995314, 0.008903937, -0.017036416, -0.009411816, -0.001938294, 0.0108904485, -0.017962167, -0.002576356, -0.024185281, 0.0028126156, 0.020726567, 0.010479002, -0.010118987, -0.018836489, 0.019350797, -0.028415455, 0.007746747, 0.006814566, 0.008762503, 0.032504193, 0.014207727, -0.01402772, -0.67847365, -0.033507094, -0.00029090483, -0.008595354, -0.0021568744, 0.0083382, -0.014002005, 0.0021022293, -0.014837753, 0.0018707912, -0.010787587, 0.004738052, -0.012041209, 0.0019800814, 0.019299366, -0.016252097, 0.020456556, -0.0022050908, -0.0056959484, 0.0107168695, -0.009379672, 0.012439798, 0.01794931, 0.0020475842, 0.008357487, -0.008209623, 0.02529104, 0.007328873, -0.012960534, 0.04567045, -0.04353608, 0.017242137, 0.015570641, -0.021999476, 0.05909386, 0.00601739, -0.019183647, 0.028364023, 0.016714973, 0.038341578, -0.035410028, -0.0023593828, 0.00016162495, 0.0012110319, -0.0011740661, -0.008209623, 0.011366182, -0.00021054437, 0.0005291735, -0.020199403, 0.0016039945, 0.015596356, 0.022449495, 0.0065574124, 0.00563166, -0.006898141, 0.0336871, 0.005004849, 0.010041841, 0.01855362, 0.0098489765, -0.0027499346, -0.0060334625, -0.01933794, 0.0019045427, 0.025805347, -0.012086212, 0.008003901, 0.01971081, -0.018913636, 0.009681826, -0.00043836626, -0.009816833, 0.01838647, -0.007361017, 0.021716608, 0.01756358, -0.0052555734, -0.01821932, 0.0291612, -0.0086339265, -0.0009860228, 0.000753781, -0.008762503, 0.033224225, -0.013796282, -0.016097805, 0.016599255, 0.010839017, 0.0010358462, 1.2945566e-05, 0.0053809355, 0.0031726304, -0.010922592, 0.0065349117, 0.0069174273, -0.019350797, -0.01044043, 0.016097805, -0.015197768, -0.008183908, -0.0035551463, 0.00601739, -0.004018022, -0.0124526555, 0.011160459, -0.012311221, 0.01065901, 0.043047484, -0.0035647894, -0.0030810195, -0.008588925, -0.0006641791, -0.0033654957, 0.029109769, -0.032761347, 0.01170691, 0.008048902, 0.010138274, 0.0050562792, 0.027952578, -0.015827794, 0.016059233, -0.007361017, 0.0027354697, 0.0075474535, -0.0108004445, -0.008434633, -0.01143047, 0.0044198246, 0.007881753, 0.0012696951, -0.007129579, -0.0007541828, -0.002476709, -0.0018836489, 0.02214091, -0.017357856, 0.006145967, -0.011803343, -0.014387735, -0.0062809726, 0.005397008, -0.015082049, 0.011494759, -0.012111926, -0.009733258, -0.004146599, -0.0012745167, 0.0041048117, -0.002721005, -0.026319655, -0.01115403, 0.03600148, 0.010080415, -0.0119319195, -0.013744852, -0.003100306, -0.018527905, -0.010138274, -0.0057120207, 0.0032208469, 0.0034651426, -0.0043716086, -0.011764769, -0.027515417, -0.017717872, 0.02016083, 0.0027226121, -0.015660644, -0.016072089, -0.017422145, 0.012066925, -0.0007702549, -0.0059016715, 0.0012351401, -0.01247837, -0.03695295, -0.0040790965, -0.016663542, 0.014349162, -0.0026968967, 0.007200296, 0.0045869746, 0.023709547, -0.00392159, -0.015390634, 0.032889925, -0.01766644, -0.020340838, -0.009533964, 0.010376141, 0.007258156, 0.0049502035, 0.012999106, -0.008762503, -0.016444962, 0.022372348, 0.044487543, 0.005975603, -0.016573539, 0.0058245254, 0.01722928, -0.009823261, 0.022449495, -0.022552356, 0.0006256061, -0.019260792, 0.00878179, 0.01650925, 0.0128833875, 0.004670549, -0.036104344, 0.004284819, -0.008685357, 0.024661014, 0.003854087, 0.022192342, 0.002960479, -0.0014167547, -0.0043491074, -0.00043515183, 0.0028029724, 0.0015139908, 0.00999684, -0.0006637773, 0.004946989, 0.042198878, -0.012999106, -0.014310589, -0.013108396, -0.0018482903, -0.0017052487, -0.0036708652, 0.00083173066, 0.0026952894, 0.035075728, -0.018013598, 0.023298101, 0.0064770523, -0.027155403, -0.0037351537, 0.049013443, -0.009636825, 0.019286508, 0.035384312, 0.02766971, -0.002584392, 0.0052748597, 0.0011652265, -0.025689628, -0.0066795605, -0.039138753, 0.02032798, 0.0145806, -0.0039280187, 0.0020250834, 0.0035808615, 0.031989887, 0.009701113, 0.02800401, 0.0016988199, 0.010350426, 0.01563493, 0.024159566, -0.007958899, -0.012330507, -0.01982653, -0.0025136748, 0.022295203, -0.0044133957, 0.0011660301, -0.0061041797, 0.002116694, 0.01595637, 0.0051848562, 0.009173949, 0.010067557, -0.0036547931, 0.013371979, -0.0017181064, -0.019453658, 0.019787956, 0.01049186, -0.0046287617, -0.015917799, -0.028801184, -0.0035197877, -0.012864101, 0.015583498, 0.0028174373, 0.028904047, 0.005593087, -0.007946041, -0.019196505, -0.0043651797, 0.012414082, -0.0017438218, 0.0126262335, 0.01590494, 0.009302526, 0.013783424, -0.01016399, -0.011623335, 0.011057598, 0.00336871, 0.005290932, -0.016252097, -0.0001511781, 0.009861834, -0.007598884, -0.0291612, -0.019260792, 0.0017952524, -0.012966962, 0.0030954846, -0.019839387, -0.019325081, 0.039627343, 0.0039698062, -0.006820995, -0.01496633, -0.027695425, -0.001907757, 0.048782006, 0.012941247, 0.0066152723, -0.010794016, 0.0019865104, -0.0013034465, -0.013963431, -0.031938456, 0.002452601, -0.01960795, -0.026203936, -0.0030617332, 0.007798178, -0.0039248043, 0.023156667, 0.00045443833, -0.00050546724, 0.0014416665, 0.0066024144, -0.004538758, 0.0023931342, -0.00081405137, 0.010427572, 0.009270381, 0.0062166844, -0.005538442, 0.026242508, 0.02689825, -0.003815514, -0.027926862, -0.01121189, 0.02738684, 0.0055480856, -0.009778259, -0.024558153, 0.012182644, -0.0078046066, 0.00070235034, 0.014207727, -0.0007156098, 0.04127313, 0.046261903, 0.009964695, -0.027052542, 0.000965129, 0.018695055, -0.0133205475, 0.012182644, -0.014194869, 0.0061170375, 0.034741428, -0.009746116, -0.025998212, -0.00040782927, 0.018245036, 0.016496394, -0.027078256, -0.012992677, -0.013011964, -0.052716453, -0.0011025454, -0.0029829799, -0.010530434, -0.011970492, 0.003944091, -0.020109398, 0.003384782, -0.0041176695, -0.02043084, -0.005049851, -0.0053166472, -0.022539498, -0.023902413, 0.0006392674, -0.0011202247, 0.018026456, -0.0049502035, -0.013204829, -0.0028110086, 0.041118834, 0.0005388168, -0.0005552907, -0.0032787062, -0.028595462, -0.021678034, -0.0025570695, -0.004953418, -0.008216052, -0.002645466, -0.0017213208, 0.032812778, 0.00955325, 0.006030248, 0.007444592, 0.00091932353, 0.0029942302, -0.0022822367, 0.023735262, -0.032118466, 0.013114825, 0.020070825, -0.024429576, -0.0045869746, -0.00455483, 0.013616274, -0.004345893, 0.017987883, 0.031064136, -0.05251073, 0.0071810097, 0.006435265, -0.012523373, 0.009411816, -0.0057313074, 0.0128833875, 0.003860516, -0.009386101, 0.010626866, -0.010755442, -0.029392637, 0.013384837, -0.030421251, 0.0063581187, 0.031321287, 0.01888792, 0.021163728, -0.01474775, -0.010376141, 0.004130527, -0.019170789, -0.00850535, 0.010350426, -0.0031244142, 0.010009698, -0.027541133, -0.0048312703, -0.015364918, 0.013005535, 0.0010085236, -0.021215158, -0.029624077, 0.0015147944, -0.0013934502, -0.01960795, -0.021189444, -0.032787062, -0.0092382375, 0.012227646, -0.003899089, 0.020070825, -0.0065188394, 0.01690784, -0.0012054067, 0.023542397, -0.01828361, -0.03422712, -0.01530063, 0.0027001111, 0.03103842, 0.023876697, 0.012375509, -0.022513783, 0.02512389, 0.0033558523, -0.02021226, -0.005577015, -0.018257894, -0.0109804515, -0.03417569, 0.008518208, 0.009051801, 0.018566478, -0.0074960226, -0.01551921, -0.017370714, -0.0097654015, -0.0041015972, -0.004821627, -0.009206093, -0.012934818, 0.0047573387, 0.0021504457, -0.015017761, 0.017074987, -0.0040276656, 0.008601783, 0.02921263, 0.0013902357, 0.019029355, 0.029135484, 0.020366551, -0.008003901, 0.005483797, -0.014130581, 0.008704644, -0.017345, -0.039473053, 0.014567742, -0.017332142, 0.030986989, 0.023825265, 0.030986989, -0.004847342, 0.015249198, -0.001099331, 0.016714973, -0.010832588, -0.024506722, 0.008871794, -0.021279447, -0.025213894, -0.032349903, -0.016149236, -0.044873275, 0.003699795, 0.016329244, -0.013500555, 0.0127998125, -0.012767668, -0.013963431, 0.0050466363, 0.016740689, 0.05073637, 0.009456818, 0.020790854, 0.006329189, -0.001030221, -0.019132216, 0.016046375, -0.018605052, -0.020353695, 0.019183647, 0.018360754, 0.011925491, 0.0006927071, 0.017460719, -0.002438136, -0.011726197, -0.02738684, 0.02307952, 0.0077403183, -0.012934818, -0.01253623, -0.00049100234, -0.014657746, 0.017062131, -0.01937651, -0.018540762, 0.008518208, -0.013989147, -0.024146708, 0.035410028, 0.0012648734, 0.030524112, 0.0101125585, -0.000100802135, -0.007991043, 0.0023674187, 0.019003639, 0.005081995, 0.0033044217, 0.0007702549, -0.011713339, 0.0045773312, -0.008344629, 0.0056284457, -0.019183647, 0.0074574496, 0.003429784, 0.02523961, -0.012491228, -0.022603787, -0.024172423, 0.003060126, 0.021112297, 0.0011587976, -0.002344918, -0.0133205475, -0.03157844, -0.016586397, 0.024866737, -0.015750648, 0.0067952797, -0.008183908, -0.0153134875, 0.0037640834, 0.003407283, -0.023516681, -0.0075860266, -0.023490967, -0.011282607, -0.013371979, 0.003799442, 0.016264955, 0.02622965, 0.016046375, 0.020173687, 0.016496394, -0.00045966177, 0.023015233, 0.0050594937, 0.012819099, -0.015390634, -0.0048794863, 0.0027049328, -0.03533288, -0.0043169633, -0.014953473, -0.0035519318, -0.025046745, 0.023683831, 0.025998212, -0.012291934, 0.014837753, 0.011481901, 0.040013075, -0.013886286, -0.021009436, -0.022436637, 0.025535336, -0.008093905, 0.011751912, 0.008955369, 0.0065027676, -0.018862205, -0.011173317, -0.009662541, 0.002531354, -0.025226751, -0.02275808, -0.0060945363, 0.026435373, -0.014182012, -0.020019395, -0.022950944, 0.013564844, -0.0056413035, 0.01838647, 0.00068828725, 0.004766982, 0.01518491, 0.02495674, 0.010408285, -0.0050980668, 0.007746747, -0.043998953, -0.014760607, -0.0047862683, -0.02666681, -0.008132477, -0.018630767, -0.008222481, 0.01369342, -0.027155403, -0.051893562, 0.0008445883, -0.01744786, -0.018373612, -0.021215158, -0.006444908, 0.0065477695, -0.0012954104, -0.022410922, 0.015982086, 0.007624599, 0.014824895, -0.008653213, -0.011436899, 0.010388999, 0.006891712, -0.008100334, 0.005644518, -0.0046930504, 0.00038974817, 0.020610848, 0.01563493, -0.010684725, 0.030524112, -0.013873428, 0.013166256, -0.018013598, 0.008511779, 0.008820363, 0.013912001, 0.0032545982, -0.008344629, -0.023400962, 0.012343365, 0.021652319, 0.02016083, -0.009302526, 0.023349533, -0.016817834, 0.022449495, -0.009450389, 0.013847712, -0.006454551, -0.012433369, 0.0084603485, -0.02529104, -0.036387213, 0.018913636, -0.03340423, -0.010041841, 0.002576356, 0.006454551, -0.018206464, 0.014156297, 0.04353608, -0.018129317, 0.02512389, 0.0030954846, 0.0074638785, -0.024352431, -0.0062713292, -0.0023111666, 0.0013500556, -0.014503454, 0.004622333, 0.003429784, -0.013031251, -0.009122518, -0.009077516, -0.0005717646, 0.001050311, -0.0011162066, -0.0028961906, -0.0073803035, 0.0033076361, -0.0013580916, -0.0042719613, -0.016740689, -0.0060977507, 0.011816201, 0.002783686, 0.009257523, 0.24110706, -0.019530803, 0.019080784, 0.031141281, -0.009926123, 0.007997472, -0.008704644, -0.013166256, 0.0015895297, 0.004783054, -0.0006718133, -0.001288178, -0.02766971, -0.0012037995, -0.0015871188, -0.002460637, -0.014452023, -0.007798178, -0.028415455, -0.04312463, -0.010704012, -0.025779633, -0.008473205, -6.790458e-05, 0.010832588, -0.0057055918, -0.013731994, 0.011256891, 0.031321287, 0.017589295, -0.010286137, -0.020366551, 0.0053037894, 0.00023967504, -0.010562577, -0.007836751, -0.0045805457, 0.007978185, 0.023092378, 0.042173162, -0.013294833, -0.0066088433, 0.012819099, -0.0016425676, -0.007759605, 0.010408285, -0.010408285, -0.020958005, -0.001645782, 0.018875062, -0.013204829, 0.018836489, 0.018103601, 0.039190184, -0.019067928, 0.005435581, 0.0010888841, 0.0035165732, -0.0037705123, 0.015416348, -0.006814566, 0.020469414, -0.009488962, 0.0027660066, -0.008312485, -0.0018643624, -0.020057969, -0.007643886, -0.0015292594, -0.010343997, -0.031166997, -0.0023433107, 0.01253623, -0.0037126527, -0.041658856, -0.02176804, 0.049116306, 0.003545503, 0.028415455, 0.04633905, -0.014927757, -0.014079151, -0.0033333513, -0.021896616, 0.011109028, -0.037210103, 0.031604156, 0.008396059, -0.016162094, 0.01535206, 9.638231e-05, -0.025972497, 0.016663542, 0.0046673347, -0.0002151651, 0.03162987, -0.01684355, 0.023130951, 0.0051719984, 0.009296097, -0.02959836, -0.0071938676, 0.0059241722, -0.0001261659, -0.014400592, 0.0012745167, -0.0126262335, -0.017705014, 0.015364918, -0.0024477793, -0.01684355, -0.012439798, 0.018707912, -0.026409658, -0.01281267, -0.010614008, 0.0191065, 0.0013757709, 0.0006967251, 0.014220585, 0.0054387953, -0.021935187, 0.016393531, 0.0092382375, -0.022796651, -0.013770566, -0.0058566695, 0.0042430316, 0.022989517, -0.015943512, 0.025278183, -0.005361649, 0.015043476, -0.025946781, -0.021588031, 0.020945147, 0.022848083, -0.008100334, 0.010266851, 0.009694684, 0.003008695, 0.004191601, 0.004738052, -0.008106762, 0.0073095863, -0.017589295, 0.0066731316, 0.0009281632, -0.012021923, -0.010086844, -0.038984463, -0.004480899, 0.0024943883, -0.014722034, 0.010974023, -0.0127998125, -0.024943883, -0.030678404, 0.002169732, 0.022719506, -0.024712445, -0.0071938676, 0.0031276287, -0.027361125, -0.035924334, -0.0074767363, -0.16581254, 0.03026696, 0.017267853, -0.007759605, 0.0019350796, 0.013256259, 0.0101961335, -0.0062038265, -0.0066667027, -8.598568e-05, 0.02307952, 0.0005066726, -0.054927975, -0.0023593828, 0.013018393, 0.010710441, -0.010678296, 0.017679298, -0.001503544, 0.017087845, 0.015146337, -0.0063099023, -0.003600148, 0.014837753, -0.023812408, 0.006522054, -0.013886286, 0.028029725, -0.025136748, 0.012671236, -0.032221325, -0.011546189, 0.027772572, 0.017525006, 0.0054580816, -0.0027338625, 0.019247934, -0.0170107, 0.016637828, 0.006377405, 0.013487698, 0.02766971, 0.00039898962, 0.00944396, -0.018193606, 0.014696319, 0.0041273125, -0.015750648, 0.029521214, -0.0050080633, -0.018347898, -0.011880489, 0.022475211, 0.006583128, 0.0134105515, 0.026435373, 0.002676003, 0.0022645574, 0.016612113, -0.0057570226, 0.019646522, -0.03026696, 0.0012303184, -0.025856778, -0.002221163, -0.022436637, -0.012304792, 0.003423355, -0.008582496, 0.023015233, -0.000782309, -0.004821627, 0.026795387, -0.011301894, 0.0069560003, 0.013461983, -0.01530063, 0.023426678, 0.006554198, -0.0038122996, -0.016046375, 0.02098372, 0.00017739569, 0.015506352, 0.009630396, 0.022873798, 0.0132305445, -0.0012857672, -0.018566478, -0.0075024515, 0.04811341, -0.017717872, -0.010009698, 0.004384466, -0.002184197, 0.008511779, -0.015506352, 0.0058952426, -0.0062102554, -0.027772572, -0.0063870484, -0.015943512, -0.009726829, 0.008351058, 0.020996578, 0.008813934, 0.011829058, 0.0077017453, 0.029778369, -0.015043476, -0.0073803035, 0.0132305445, 0.009013228, 0.029906945, 0.003568004, 0.035692897, -0.014902041, -0.0030954846, -0.008415346, -0.00767603, 0.05533942, -0.013500555, -0.008601783, 0.0085503515, -0.01513348, -0.010144703, -0.058888137, -0.031141281, 0.0239667, 0.023259528, -0.008537494, 0.005397008, 0.0045355437, 0.015082049, -0.029418353, 0.016856408, -0.0056927344, -0.015827794, -0.012966962, -0.004468041, 0.038007278, -0.022873798, -0.009116089, 0.005233072, -0.013731994, 0.0239667, -0.025831062, -0.0012889816, 0.0011113851, -0.009681826, -0.0065477695, 0.0015903333, -0.04585046, 0.014734892, 0.0066538453, 0.010607579, -0.0043748226, 0.0013404123, 0.008293198, -0.021279447, -0.022449495, -0.010652581, -0.023825265, -0.006859568, 0.020585133, -0.030035522, 0.012156929, -0.00090244785, -0.0010896877, -0.021498026, -0.010646152, -0.005898457, -0.0038476584, 0.017306427, 0.00065453583, -0.031681303, -0.018913636, -0.024095276, -0.03155273, 0.023555255, 0.025561051, -0.021125155, 0.014477738, 0.021793753, 0.018836489, 0.005840597, 0.012555516, -0.00025313543, -0.023696689, 0.019633666, 0.013359121, 0.018990781, -0.026769673, 0.003452285, 0.012465512, 0.0035326453, -0.0028511886, 0.025329614, -0.016496394, 0.009861834, -0.010999738, -0.008537494, -0.008736788, -0.01236908, 0.018707912, -0.006512411, -0.00576988, -0.02700111, 0.002184197, 0.0014633638, 0.024095276, 0.01717785, 0.011546189, -0.018309325, -0.009598252, -0.016316386, -0.0052427156, 0.018759344, 0.00472198, -0.018849347, -0.014053435, -0.0061266804, 0.0035326453, -0.0066152723, 0.0032176324, 0.0042494605, 6.438881e-05, -0.018322183, -0.07154009, 0.021960903, -0.0071488656, -0.026923966, 0.015660644, -0.0101125585, 0.008813934, -0.026641095, 0.012876959, -0.011790485, -0.006885283, 0.016136378, -0.0010792408, 0.012221217, -0.004378037, -0.00635169, 0.035307165, -0.0033815678, 0.00850535, 0.010549719, 0.0059788176, 0.0037705123, 0.020289406, -0.014812038, 0.019312223, -0.0035776473, -0.012439798, 0.019800814, -0.033275656, 0.0011571904, -0.0046962644, -0.037827272, 1.2041511e-05, 0.023053806, -0.0024799234, -0.033661384, 0.012407653, 0.009855405, 0.013307691, 0.0065895566, -0.01694641, -0.033095647, 0.01888792, -0.029701222, -0.019852245, -0.0050466363, -0.017306427, 0.011835487, 0.022012334, -0.0020925861, 0.01690784, 0.027309695, -0.02302809, -0.00051189604, 0.019967964, -0.055905156, 0.028646892, 0.028955476, 0.0015509566, -7.850211e-05, 0.023696689, 0.010929021, 0.012613376, -0.017692156, -0.00037447968, 0.009983982, -0.011141173, -0.008801077, 0.0015887261, -0.03440713, -0.009386101, 0.0063806195, 0.002992623, 0.009701113, 0.0066859894, 0.0031019133, -0.0063034734, 0.008498921, -0.026242508, 0.023606686, 0.013513413, 0.0017454289, -0.008376773, -0.00201544, 0.048164837, 0.0074188765, -0.0010181669, -0.017190708, 0.008029616, 0.029572645, -0.025008172, -0.005091638, -0.024866737, 0.007271013, -0.002328846, 0.0062713292, -0.016894981, 7.393161e-05, 0.022732364, 0.012375509, 0.0014272016, 1.2298916e-05, -0.0191065, -0.016637828, -0.01452917, -0.012137642, -0.02307952, -0.0001341015, 0.004043738, 0.024545295, 0.014516312, -0.01766644, -0.020893717, 0.009263952, 0.008563209, 0.0018948994, -0.013500555, -0.0034780002, -0.015814936, 0.044204675, 0.008093905, 0.007367446, 0.011366182, 0.004853771, 0.0030826267, -0.0080231875, -0.006621701, -0.03985878, 0.007791749, -0.00018603443, -0.0026872533, -0.016419247, -0.008408917, -0.027489703, -0.024545295, 0.0034490705, -0.020456556, 0.010427572, 0.01578922, 0.04991348, -0.0014159511, -0.005191285, 0.021253731, 0.00052837, 0.03108985, 0.0034940722, 0.0030553043, 0.0004680996, -0.009630396, 0.0140148625, -0.031115565, -0.013976289, -0.007766034, -0.021742323, -0.0062552574, -0.017164992, 0.013513413, -0.025535336, -0.006444908, 0.027412556, 0.0075345957, 0.01264552, -0.0009112875, -0.029315492, -0.021215158, 0.028801184, -0.0032497765, -0.020687994, -0.03129557, 0.0037962275, -0.001365324, -0.02805544, -0.005638089, 0.02689825, -0.007695317, -0.0027724355, -0.00074895937, -0.0056798765, 0.0045580445, -0.008325342, -0.008858936, -0.0070717195, -0.020276548, 0.03600148, -0.0047123367, -0.016599255, 0.01573779, -0.028595462]}], "model": "text-embedding-ada-002-v2", "usage": {"prompt_tokens": 3, "total_tokens": 3}} \ No newline at end of file diff --git a/tests/data/rsp_cache.json b/tests/data/rsp_cache.json index db452f676..40d7d3953 100644 --- a/tests/data/rsp_cache.json +++ b/tests/data/rsp_cache.json @@ -141,5 +141,253 @@ "\nRole: You are a senior development and qa engineer, your role is summarize the code running result.\nIf the running result does not include an error, you should explicitly approve the result.\nOn the other hand, if the running result indicates some error, you should point out which part, the development code or the test code, produces the error,\nand give specific instructions on fixing the errors. Here is the code info:\n\n## Development Code File Name\na.txt\n## Development Code\n```python\nprint('Hello, World')\n```\n## Test File Name\n\n## Test Code\n```python\nNone\n```\n## Running Command\n\n## Running Output\nstandard output: \n```text\n\n```\nstandard errors: \n```text\n\n```\n\nNow you should begin your analysis\n---\n## instruction:\nPlease summarize the cause of the errors and give correction instruction\n## File To Rewrite:\nDetermine the ONE file to rewrite in order to fix the error, for example, xyz.py, or test_xyz.py\n## Status:\nDetermine if all of the code works fine, if so write PASS, else FAIL,\nWRITE ONLY ONE WORD, PASS OR FAIL, IN THIS SECTION\n## Send To:\nPlease write Engineer if the errors are due to problematic development codes, and QaEngineer to problematic test codes, and NoOne if there are no errors,\nWRITE ONLY ONE WORD, Engineer OR QaEngineer OR NoOne, IN THIS SECTION.\n---\nYou should fill in necessary instruction, status, send to, and finally return all content between the --- segment line.\n": "---\ninstruction: There are no errors in the provided code.\n\nFile To Rewrite: N/A\n\nStatus: PASS\n\nSend To: NoOne\n---", "\nRole: You are a senior development and qa engineer, your role is summarize the code running result.\nIf the running result does not include an error, you should explicitly approve the result.\nOn the other hand, if the running result indicates some error, you should point out which part, the development code or the test code, produces the error,\nand give specific instructions on fixing the errors. Here is the code info:\n\n## Development Code File Name\na.sh\n## Development Code\n```python\necho 'Hello World'\n```\n## Test File Name\n\n## Test Code\n```python\nNone\n```\n## Running Command\necho Hello World\n## Running Output\nstandard output: \n```text\nHello World\n\n```\nstandard errors: \n```text\n\n```\n\nNow you should begin your analysis\n---\n## instruction:\nPlease summarize the cause of the errors and give correction instruction\n## File To Rewrite:\nDetermine the ONE file to rewrite in order to fix the error, for example, xyz.py, or test_xyz.py\n## Status:\nDetermine if all of the code works fine, if so write PASS, else FAIL,\nWRITE ONLY ONE WORD, PASS OR FAIL, IN THIS SECTION\n## Send To:\nPlease write Engineer if the errors are due to problematic development codes, and QaEngineer to problematic test codes, and NoOne if there are no errors,\nWRITE ONLY ONE WORD, Engineer OR QaEngineer OR NoOne, IN THIS SECTION.\n---\nYou should fill in necessary instruction, status, send to, and finally return all content between the --- segment line.\n": "The running result indicates no error. The code works fine.\n\n## File To Rewrite:\nNo file needs to be rewritten.\n\n## Status:\nPASS\n\n## Send To:\nNoOne\n\n---", "\nRole: You are a senior development and qa engineer, your role is summarize the code running result.\nIf the running result does not include an error, you should explicitly approve the result.\nOn the other hand, if the running result indicates some error, you should point out which part, the development code or the test code, produces the error,\nand give specific instructions on fixing the errors. Here is the code info:\n\n## Development Code File Name\na.py\n## Development Code\n```python\npython -c \"print(1/0)\"\n```\n## Test File Name\n\n## Test Code\n```python\nNone\n```\n## Running Command\npython -c print(1/0)\n## Running Output\nstandard output: \n```text\n\n```\nstandard errors: \n```text\nTraceback (most recent call last):\n File \"\", line 1, in \nZeroDivisionError: division by zero\n\n```\n\nNow you should begin your analysis\n---\n## instruction:\nPlease summarize the cause of the errors and give correction instruction\n## File To Rewrite:\nDetermine the ONE file to rewrite in order to fix the error, for example, xyz.py, or test_xyz.py\n## Status:\nDetermine if all of the code works fine, if so write PASS, else FAIL,\nWRITE ONLY ONE WORD, PASS OR FAIL, IN THIS SECTION\n## Send To:\nPlease write Engineer if the errors are due to problematic development codes, and QaEngineer to problematic test codes, and NoOne if there are no errors,\nWRITE ONLY ONE WORD, Engineer OR QaEngineer OR NoOne, IN THIS SECTION.\n---\nYou should fill in necessary instruction, status, send to, and finally return all content between the --- segment line.\n": "---\nThe error is caused by the development code in file a.py, which attempts to divide by zero. To fix the error, the development code in a.py should be modified to handle the ZeroDivisionError, for example by using a try-except block.\n\nFile To Rewrite:\na.py\n\nStatus:\nFAIL\n\nSend To:\nEngineer\n---", - "\nNOTICE\n1. Role: You are a Development Engineer or QA engineer;\n2. Task: You received this message from another Development Engineer or QA engineer who ran or tested your code. \nBased on the message, first, figure out your own role, i.e. Engineer or QaEngineer,\nthen rewrite the development code or the test code based on your role, the error, and the summary, such that all bugs are fixed and the code performs well.\nAttention: Use '##' to split sections, not '#', and '## ' SHOULD WRITE BEFORE the test case or script and triple quotes.\nThe message is as follows:\n# Legacy Code\n```python\n\nfrom typing import List\nfrom deck import Deck\nfrom card import Card\n\nclass Player:\n \"\"\"\n A class representing a player in the Black Jack game.\n \"\"\"\n\n def __init__(self, name: str):\n \"\"\"\n Initialize a Player object.\n \n Args:\n name (str): The name of the player.\n \"\"\"\n self.name = name\n self.hand: List[Card] = []\n self.score = 0\n\n def draw(self, deck: Deck):\n \"\"\"\n Draw a card from the deck and add it to the player's hand.\n \n Args:\n deck (Deck): The deck of cards.\n \"\"\"\n card = deck.draw_card()\n self.hand.append(card)\n self.calculate_score()\n\n def calculate_score(self) -> int:\n \"\"\"\n Calculate the score of the player's hand.\n \n Returns:\n int: The score of the player's hand.\n \"\"\"\n self.score = sum(card.value for card in self.hand)\n # Handle the case where Ace is counted as 11 and causes the score to exceed 21\n if self.score > 21 and any(card.rank == 'A' for card in self.hand):\n self.score -= 10\n return self.score\n\n```\n---\n# Unit Test Code\n```python\n\nimport unittest\nfrom blackjack_game.player import Player\nfrom blackjack_game.deck import Deck\nfrom blackjack_game.card import Card\n\nclass TestPlayer(unittest.TestCase):\n ## Test the Player's initialization\n def test_player_initialization(self):\n player = Player(\"Test Player\")\n self.assertEqual(player.name, \"Test Player\")\n self.assertEqual(player.hand, [])\n self.assertEqual(player.score, 0)\n\n ## Test the Player's draw method\n def test_player_draw(self):\n deck = Deck()\n player = Player(\"Test Player\")\n player.draw(deck)\n self.assertEqual(len(player.hand), 1)\n self.assertEqual(player.score, player.hand[0].value)\n\n ## Test the Player's calculate_score method\n def test_player_calculate_score(self):\n deck = Deck()\n player = Player(\"Test Player\")\n player.draw(deck)\n player.draw(deck)\n self.assertEqual(player.score, sum(card.value for card in player.hand))\n\n ## Test the Player's calculate_score method with Ace card\n def test_player_calculate_score_with_ace(self):\n deck = Deck()\n player = Player(\"Test Player\")\n player.hand.append(Card('A', 'Hearts', 11))\n player.hand.append(Card('K', 'Hearts', 10))\n player.calculate_score()\n self.assertEqual(player.score, 21)\n\n ## Test the Player's calculate_score method with multiple Aces\n def test_player_calculate_score_with_multiple_aces(self):\n deck = Deck()\n player = Player(\"Test Player\")\n player.hand.append(Card('A', 'Hearts', 11))\n player.hand.append(Card('A', 'Diamonds', 11))\n player.calculate_score()\n self.assertEqual(player.score, 12)\n\nif __name__ == '__main__':\n unittest.main()\n\n\n```\n---\n# Console logs\n```text\n\n```\n---\nNow you should start rewriting the code:\n## file name of the code to rewrite: Write code with triple quote. Do your best to implement THIS IN ONLY ONE FILE.\n": "```python\n## Development Engineer\n\nfrom typing import List\nfrom deck import Deck\nfrom card import Card\n\nclass Player:\n \"\"\"\n A class representing a player in the Black Jack game.\n \"\"\"\n\n def __init__(self, name: str):\n \"\"\"\n Initialize a Player object.\n \n Args:\n name (str): The name of the player.\n \"\"\"\n self.name = name\n self.hand: List[Card] = []\n self.score = 0\n\n def draw(self, deck: Deck):\n \"\"\"\n Draw a card from the deck and add it to the player's hand.\n \n Args:\n deck (Deck): The deck of cards.\n \"\"\"\n card = deck.draw_card()\n self.hand.append(card)\n self.calculate_score()\n\n def calculate_score(self) -> int:\n \"\"\"\n Calculate the score of the player's hand.\n \n Returns:\n int: The score of the player's hand.\n \"\"\"\n self.score = sum(card.value for card in self.hand)\n # Handle the case where Ace is counted as 11 and causes the score to exceed 21\n ace_count = sum(1 for card in self.hand if card.rank == 'A')\n while self.score > 21 and ace_count > 0:\n self.score -= 10\n ace_count -= 1\n return self.score\n```\n\n```python\n## QaEngineer\n\nimport unittest\nfrom player import Player\nfrom deck import Deck\nfrom card import Card\n\nclass TestPlayer(unittest.TestCase):\n ## Test the Player's initialization\n def test_player_initialization(self):\n player = Player(\"Test Player\")\n self.assertEqual(player.name, \"Test Player\")\n self.assertEqual(player.hand, [])\n self.assertEqual(player.score, 0)\n\n ## Test the Player's draw method\n def test_player_draw(self):\n deck = Deck()\n player = Player(\"Test Player\")\n player.draw(deck)\n self.assertEqual(len(player.hand), 1)\n self.assertEqual(player.score, player.hand[0].value)\n\n ## Test the Player's calculate_score method\n def test_player_calculate_score(self):\n deck = Deck()\n player = Player(\"Test Player\")\n player.draw(deck)\n player.draw(deck)\n self.assertEqual(player.score, sum(card.value for card in player.hand))\n\n ## Test the Player's calculate_score method with Ace card\n def test_player_calculate_score_with_ace(self):\n player = Player(\"Test Player\")\n player.hand.append(Card('A', 'Hearts', 11))\n player.hand.append(Card('K', 'Hearts', 10))\n player.calculate_score()\n self.assertEqual(player.score, 21)\n\n ## Test the Player's calculate_score method with multiple Aces\n def test_player_calculate_score_with_multiple_aces(self):\n player = Player(\"Test Player\")\n player.hand.append(Card('A', 'Hearts', 11))\n player.hand.append(Card('A', 'Diamonds', 11))\n player.calculate_score()\n self.assertEqual(player.score, 12)\n\nif __name__ == '__main__':\n unittest.main()\n```" + "\nNOTICE\n1. Role: You are a Development Engineer or QA engineer;\n2. Task: You received this message from another Development Engineer or QA engineer who ran or tested your code. \nBased on the message, first, figure out your own role, i.e. Engineer or QaEngineer,\nthen rewrite the development code or the test code based on your role, the error, and the summary, such that all bugs are fixed and the code performs well.\nAttention: Use '##' to split sections, not '#', and '## ' SHOULD WRITE BEFORE the test case or script and triple quotes.\nThe message is as follows:\n# Legacy Code\n```python\n\nfrom typing import List\nfrom deck import Deck\nfrom card import Card\n\nclass Player:\n \"\"\"\n A class representing a player in the Black Jack game.\n \"\"\"\n\n def __init__(self, name: str):\n \"\"\"\n Initialize a Player object.\n \n Args:\n name (str): The name of the player.\n \"\"\"\n self.name = name\n self.hand: List[Card] = []\n self.score = 0\n\n def draw(self, deck: Deck):\n \"\"\"\n Draw a card from the deck and add it to the player's hand.\n \n Args:\n deck (Deck): The deck of cards.\n \"\"\"\n card = deck.draw_card()\n self.hand.append(card)\n self.calculate_score()\n\n def calculate_score(self) -> int:\n \"\"\"\n Calculate the score of the player's hand.\n \n Returns:\n int: The score of the player's hand.\n \"\"\"\n self.score = sum(card.value for card in self.hand)\n # Handle the case where Ace is counted as 11 and causes the score to exceed 21\n if self.score > 21 and any(card.rank == 'A' for card in self.hand):\n self.score -= 10\n return self.score\n\n```\n---\n# Unit Test Code\n```python\n\nimport unittest\nfrom blackjack_game.player import Player\nfrom blackjack_game.deck import Deck\nfrom blackjack_game.card import Card\n\nclass TestPlayer(unittest.TestCase):\n ## Test the Player's initialization\n def test_player_initialization(self):\n player = Player(\"Test Player\")\n self.assertEqual(player.name, \"Test Player\")\n self.assertEqual(player.hand, [])\n self.assertEqual(player.score, 0)\n\n ## Test the Player's draw method\n def test_player_draw(self):\n deck = Deck()\n player = Player(\"Test Player\")\n player.draw(deck)\n self.assertEqual(len(player.hand), 1)\n self.assertEqual(player.score, player.hand[0].value)\n\n ## Test the Player's calculate_score method\n def test_player_calculate_score(self):\n deck = Deck()\n player = Player(\"Test Player\")\n player.draw(deck)\n player.draw(deck)\n self.assertEqual(player.score, sum(card.value for card in player.hand))\n\n ## Test the Player's calculate_score method with Ace card\n def test_player_calculate_score_with_ace(self):\n deck = Deck()\n player = Player(\"Test Player\")\n player.hand.append(Card('A', 'Hearts', 11))\n player.hand.append(Card('K', 'Hearts', 10))\n player.calculate_score()\n self.assertEqual(player.score, 21)\n\n ## Test the Player's calculate_score method with multiple Aces\n def test_player_calculate_score_with_multiple_aces(self):\n deck = Deck()\n player = Player(\"Test Player\")\n player.hand.append(Card('A', 'Hearts', 11))\n player.hand.append(Card('A', 'Diamonds', 11))\n player.calculate_score()\n self.assertEqual(player.score, 12)\n\nif __name__ == '__main__':\n unittest.main()\n\n\n```\n---\n# Console logs\n```text\n\n```\n---\nNow you should start rewriting the code:\n## file name of the code to rewrite: Write code with triple quote. Do your best to implement THIS IN ONLY ONE FILE.\n": "```python\n## Development Engineer\n\nfrom typing import List\nfrom deck import Deck\nfrom card import Card\n\nclass Player:\n \"\"\"\n A class representing a player in the Black Jack game.\n \"\"\"\n\n def __init__(self, name: str):\n \"\"\"\n Initialize a Player object.\n \n Args:\n name (str): The name of the player.\n \"\"\"\n self.name = name\n self.hand: List[Card] = []\n self.score = 0\n\n def draw(self, deck: Deck):\n \"\"\"\n Draw a card from the deck and add it to the player's hand.\n \n Args:\n deck (Deck): The deck of cards.\n \"\"\"\n card = deck.draw_card()\n self.hand.append(card)\n self.calculate_score()\n\n def calculate_score(self) -> int:\n \"\"\"\n Calculate the score of the player's hand.\n \n Returns:\n int: The score of the player's hand.\n \"\"\"\n self.score = sum(card.value for card in self.hand)\n # Handle the case where Ace is counted as 11 and causes the score to exceed 21\n ace_count = sum(1 for card in self.hand if card.rank == 'A')\n while self.score > 21 and ace_count > 0:\n self.score -= 10\n ace_count -= 1\n return self.score\n```\n\n```python\n## QaEngineer\n\nimport unittest\nfrom player import Player\nfrom deck import Deck\nfrom card import Card\n\nclass TestPlayer(unittest.TestCase):\n ## Test the Player's initialization\n def test_player_initialization(self):\n player = Player(\"Test Player\")\n self.assertEqual(player.name, \"Test Player\")\n self.assertEqual(player.hand, [])\n self.assertEqual(player.score, 0)\n\n ## Test the Player's draw method\n def test_player_draw(self):\n deck = Deck()\n player = Player(\"Test Player\")\n player.draw(deck)\n self.assertEqual(len(player.hand), 1)\n self.assertEqual(player.score, player.hand[0].value)\n\n ## Test the Player's calculate_score method\n def test_player_calculate_score(self):\n deck = Deck()\n player = Player(\"Test Player\")\n player.draw(deck)\n player.draw(deck)\n self.assertEqual(player.score, sum(card.value for card in player.hand))\n\n ## Test the Player's calculate_score method with Ace card\n def test_player_calculate_score_with_ace(self):\n player = Player(\"Test Player\")\n player.hand.append(Card('A', 'Hearts', 11))\n player.hand.append(Card('K', 'Hearts', 10))\n player.calculate_score()\n self.assertEqual(player.score, 21)\n\n ## Test the Player's calculate_score method with multiple Aces\n def test_player_calculate_score_with_multiple_aces(self):\n player = Player(\"Test Player\")\n player.hand.append(Card('A', 'Hearts', 11))\n player.hand.append(Card('A', 'Diamonds', 11))\n player.calculate_score()\n self.assertEqual(player.score, 12)\n\nif __name__ == '__main__':\n unittest.main()\n```", + "Do not refer to the context of the previous conversation records, start the conversation anew.\n\nFormation: \"Capacity and role\" defines the role you are currently playing;\n\t\"[LESSON_BEGIN]\" and \"[LESSON_END]\" tags enclose the content of textbook;\n\t\"Statement\" defines the work detail you need to complete at this stage;\n\t\"Answer options\" defines the format requirements for your responses;\n\t\"Constraint\" defines the conditions that your responses must comply with.\n\nCapacity and role: You are a {teaching_language} Teacher, named Lily, your goal is writing a {language} teaching plan part by part. the constraint is writing in {language}. \nStatement: Write the \"Teaching Hours\" part of teaching plan, WITHOUT ANY content unrelated to \"Teaching Hours\"!!\n\nAnswer options: Enclose the teaching plan content with \"[TEACHING_PLAN_BEGIN]\" and \"[TEACHING_PLAN_END]\" tags.\nAnswer options: Using proper markdown format from second-level header format.\nConstraint: Writing in Chinese.\n[LESSON_BEGIN]\n\n UNIT 1 Making New Friends\n TOPIC 1 Welcome to China!\n Section A\n\n 1a Listen and number the following names.\n Jane Mari Kangkang Michael\n Look, listen and understand. Then practice the conversation.\n Work in groups. Introduce yourself using\n I ’m ... Then practice 1a\n with your own hometown or the following places.\n\n 1b Listen and number the following names\n Jane Michael Maria Kangkang\n 1c Work in groups. Introduce yourself using I ’m ... Then practice 1a with your own hometown or the following places.\n China the USA the UK Hong Kong Beijing\n\n 2a Look, listen and understand. Then practice the conversation\n Hello! \n Hello! \n Hello! \n Hello! Are you Maria? \n No, I’m not. I’m Jane.\n Oh, nice to meet you, Jane\n Nice to meet you, too.\n Hi, Maria!\n Hi, Kangkang!\n Welcome to China!\n Thanks.\n\n 2b Work in groups. Make up a conversation with your own name and the\n following structures.\n A: Hello! / Good morning! / Hi! I’m ... Are you ... ?\n B: ...\n\n 3a Listen, say and trace\n Aa Bb Cc Dd Ee Ff Gg\n\n 3b Listen and number the following letters. Then circle the letters with the same sound as Bb.\n Aa Bb Cc Dd Ee Ff Gg\n\n 3c Match the big letters with the small ones. Then write them on the lines.\n \n[LESSON_END]": "[TEACHING_PLAN_BEGIN]\n\n## Teaching Hours\n\n本教学单元共包括 4 课时,每课时 45 分钟。\n\n### 课时安排\n\n- 第一课时:1a 和 1b 部分\n- 第二课时:1c 和 2a 部分\n- 第三课时:2b 和 3a 部分\n- 第四课时:3b 和 3c 部分\n\n[TEACHING_PLAN_END]", + "Do not refer to the context of the previous conversation records, start the conversation anew.\n\nFormation: \"Capacity and role\" defines the role you are currently playing;\n\t\"[LESSON_BEGIN]\" and \"[LESSON_END]\" tags enclose the content of textbook;\n\t\"Statement\" defines the work detail you need to complete at this stage;\n\t\"Answer options\" defines the format requirements for your responses;\n\t\"Constraint\" defines the conditions that your responses must comply with.\n\nCapacity and role: You are a {teaching_language} Teacher, named Lily, your goal is writing a {language} teaching plan part by part. the constraint is writing in {language}. \nStatement: Write the \"Teaching Objectives\" part of teaching plan, WITHOUT ANY content unrelated to \"Teaching Objectives\"!!\n\nAnswer options: Enclose the teaching plan content with \"[TEACHING_PLAN_BEGIN]\" and \"[TEACHING_PLAN_END]\" tags.\nAnswer options: Using proper markdown format from second-level header format.\nConstraint: Writing in Chinese.\n[LESSON_BEGIN]\n\n UNIT 1 Making New Friends\n TOPIC 1 Welcome to China!\n Section A\n\n 1a Listen and number the following names.\n Jane Mari Kangkang Michael\n Look, listen and understand. Then practice the conversation.\n Work in groups. Introduce yourself using\n I ’m ... Then practice 1a\n with your own hometown or the following places.\n\n 1b Listen and number the following names\n Jane Michael Maria Kangkang\n 1c Work in groups. Introduce yourself using I ’m ... Then practice 1a with your own hometown or the following places.\n China the USA the UK Hong Kong Beijing\n\n 2a Look, listen and understand. Then practice the conversation\n Hello! \n Hello! \n Hello! \n Hello! Are you Maria? \n No, I’m not. I’m Jane.\n Oh, nice to meet you, Jane\n Nice to meet you, too.\n Hi, Maria!\n Hi, Kangkang!\n Welcome to China!\n Thanks.\n\n 2b Work in groups. Make up a conversation with your own name and the\n following structures.\n A: Hello! / Good morning! / Hi! I’m ... Are you ... ?\n B: ...\n\n 3a Listen, say and trace\n Aa Bb Cc Dd Ee Ff Gg\n\n 3b Listen and number the following letters. Then circle the letters with the same sound as Bb.\n Aa Bb Cc Dd Ee Ff Gg\n\n 3c Match the big letters with the small ones. Then write them on the lines.\n \n[LESSON_END]": "[TEACHING_PLAN_BEGIN]\n\n## Teaching Objectives\n\n1. Students will be able to listen and understand the names of different people in English.\n2. Students will be able to introduce themselves using the correct structure \"I'm [name]\".\n3. Students will be able to engage in simple conversational exchanges using greetings and introductions.\n4. Students will be able to recognize and match big and small letters in the English alphabet.\n\n[TEACHING_PLAN_END]", + "Do not refer to the context of the previous conversation records, start the conversation anew.\n\nFormation: \"Capacity and role\" defines the role you are currently playing;\n\t\"[LESSON_BEGIN]\" and \"[LESSON_END]\" tags enclose the content of textbook;\n\t\"Statement\" defines the work detail you need to complete at this stage;\n\t\"Answer options\" defines the format requirements for your responses;\n\t\"Constraint\" defines the conditions that your responses must comply with.\n\nCapacity and role: You are a {teaching_language} Teacher, named Lily, your goal is writing a {language} teaching plan part by part. the constraint is writing in {language}. \nStatement: Write the \"Teaching Content\" part of teaching plan, WITHOUT ANY content unrelated to \"Teaching Content\"!!\nStatement: \"Teaching Content\" must include vocabulary, analysis, and examples of various grammar structures that appear in the textbook, as well as the listening materials and key points.\nStatement: \"Teaching Content\" must include more examples.\nAnswer options: Enclose the teaching plan content with \"[TEACHING_PLAN_BEGIN]\" and \"[TEACHING_PLAN_END]\" tags.\nAnswer options: Using proper markdown format from second-level header format.\nConstraint: Writing in Chinese.\n[LESSON_BEGIN]\n\n UNIT 1 Making New Friends\n TOPIC 1 Welcome to China!\n Section A\n\n 1a Listen and number the following names.\n Jane Mari Kangkang Michael\n Look, listen and understand. Then practice the conversation.\n Work in groups. Introduce yourself using\n I ’m ... Then practice 1a\n with your own hometown or the following places.\n\n 1b Listen and number the following names\n Jane Michael Maria Kangkang\n 1c Work in groups. Introduce yourself using I ’m ... Then practice 1a with your own hometown or the following places.\n China the USA the UK Hong Kong Beijing\n\n 2a Look, listen and understand. Then practice the conversation\n Hello! \n Hello! \n Hello! \n Hello! Are you Maria? \n No, I’m not. I’m Jane.\n Oh, nice to meet you, Jane\n Nice to meet you, too.\n Hi, Maria!\n Hi, Kangkang!\n Welcome to China!\n Thanks.\n\n 2b Work in groups. Make up a conversation with your own name and the\n following structures.\n A: Hello! / Good morning! / Hi! I’m ... Are you ... ?\n B: ...\n\n 3a Listen, say and trace\n Aa Bb Cc Dd Ee Ff Gg\n\n 3b Listen and number the following letters. Then circle the letters with the same sound as Bb.\n Aa Bb Cc Dd Ee Ff Gg\n\n 3c Match the big letters with the small ones. Then write them on the lines.\n \n[LESSON_END]": "[TEACHING_PLAN_BEGIN]\n\n## 教学内容\n\n### 词汇\n- 名字:Jane, Mari, Kangkang, Michael, Maria\n- 地点:China, the USA, the UK, Hong Kong, Beijing\n\n### 语法分析\n- 介绍自己的句式:I’m ...\n- 问候句式:Hello! / Good morning! / Hi! I’m ... Are you ... ?\n- 回答问候的句式:No, I’m not. I’m Jane. / Oh, nice to meet you, Jane. / Nice to meet you, too. / Hi, Maria! / Hi, Kangkang! / Welcome to China! / Thanks.\n\n### 例句\n- 例句1:Hello! Are you Maria? No, I’m not. I’m Jane.\n- 例句2:Hi, Maria! Hi, Kangkang! Welcome to China! Thanks.\n\n### 听力材料\n- 听力练习1a、1b、2a、3a\n\n### 关键点\n- 学生能够用英语介绍自己的名字和来自的地方\n- 学生能够用正确的问候方式和回答方式进行交流\n\n[TEACHING_PLAN_END]", + "Do not refer to the context of the previous conversation records, start the conversation anew.\n\nFormation: \"Capacity and role\" defines the role you are currently playing;\n\t\"[LESSON_BEGIN]\" and \"[LESSON_END]\" tags enclose the content of textbook;\n\t\"Statement\" defines the work detail you need to complete at this stage;\n\t\"Answer options\" defines the format requirements for your responses;\n\t\"Constraint\" defines the conditions that your responses must comply with.\n\nCapacity and role: You are a {teaching_language} Teacher, named Lily, your goal is writing a {language} teaching plan part by part. the constraint is writing in {language}. \nStatement: Write the \"Teaching Methods and Strategies\" part of teaching plan, WITHOUT ANY content unrelated to \"Teaching Methods and Strategies\"!!\nStatement: \"Teaching Methods and Strategies\" must include teaching focus, difficulties, materials, procedures, in detail.\nAnswer options: Enclose the teaching plan content with \"[TEACHING_PLAN_BEGIN]\" and \"[TEACHING_PLAN_END]\" tags.\nAnswer options: Using proper markdown format from second-level header format.\nConstraint: Writing in Chinese.\n[LESSON_BEGIN]\n\n UNIT 1 Making New Friends\n TOPIC 1 Welcome to China!\n Section A\n\n 1a Listen and number the following names.\n Jane Mari Kangkang Michael\n Look, listen and understand. Then practice the conversation.\n Work in groups. Introduce yourself using\n I ’m ... Then practice 1a\n with your own hometown or the following places.\n\n 1b Listen and number the following names\n Jane Michael Maria Kangkang\n 1c Work in groups. Introduce yourself using I ’m ... Then practice 1a with your own hometown or the following places.\n China the USA the UK Hong Kong Beijing\n\n 2a Look, listen and understand. Then practice the conversation\n Hello! \n Hello! \n Hello! \n Hello! Are you Maria? \n No, I’m not. I’m Jane.\n Oh, nice to meet you, Jane\n Nice to meet you, too.\n Hi, Maria!\n Hi, Kangkang!\n Welcome to China!\n Thanks.\n\n 2b Work in groups. Make up a conversation with your own name and the\n following structures.\n A: Hello! / Good morning! / Hi! I’m ... Are you ... ?\n B: ...\n\n 3a Listen, say and trace\n Aa Bb Cc Dd Ee Ff Gg\n\n 3b Listen and number the following letters. Then circle the letters with the same sound as Bb.\n Aa Bb Cc Dd Ee Ff Gg\n\n 3c Match the big letters with the small ones. Then write them on the lines.\n \n[LESSON_END]": "[TEACHING_PLAN_BEGIN]\n\n## Teaching Methods and Strategies\n\n### Teaching Focus\n- Introducing the topic \"Making New Friends\" and \"Welcome to China!\"\n- Engaging students in listening and speaking activities to practice conversation skills\n- Encouraging group work and interaction among students\n\n### Difficulties\n- Ensuring that students understand and remember the new vocabulary and sentence structures\n- Encouraging shy or hesitant students to actively participate in group activities\n\n### Materials\n- Audio recordings for listening exercises\n- Visual aids such as flashcards or images related to the topic\n- Worksheets for practice activities\n- Name tags for students to use during role-playing activities\n\n### Procedures\n1. **Introduction**\n - Begin the lesson by discussing the importance of making new friends and the cultural aspects of welcoming someone to a new place.\n - Use visual aids and real-life examples to engage students in the topic.\n\n2. **Listening and Speaking Activities**\n - Play the audio recordings for the listening exercises and have students participate in number and name matching activities.\n - Encourage students to practice the conversation structures in pairs or small groups, using their own names and the given structures.\n\n3. **Group Role-Playing**\n - Divide the class into groups and assign each group a scenario to role-play, incorporating the structures learned in the lesson.\n - Monitor and provide feedback to each group, encouraging active participation and fluency in spoken English.\n\n4. **Letter Recognition**\n - Introduce the letters and their corresponding sounds through interactive activities such as tracing, matching, and writing exercises.\n - Provide additional practice and reinforcement for students who may struggle with letter recognition.\n\n5. **Conclusion**\n - Summarize the key points of the lesson and encourage students to reflect on their learning experiences.\n - Assign homework or additional practice exercises related to the lesson content.\n\n[TEACHING_PLAN_END]", + "Do not refer to the context of the previous conversation records, start the conversation anew.\n\nFormation: \"Capacity and role\" defines the role you are currently playing;\n\t\"[LESSON_BEGIN]\" and \"[LESSON_END]\" tags enclose the content of textbook;\n\t\"Statement\" defines the work detail you need to complete at this stage;\n\t\"Answer options\" defines the format requirements for your responses;\n\t\"Constraint\" defines the conditions that your responses must comply with.\n\nCapacity and role: You are a {teaching_language} Teacher, named Lily, your goal is writing a {language} teaching plan part by part. the constraint is writing in {language}. \nStatement: Write the \"Learning Activities\" part of teaching plan, WITHOUT ANY content unrelated to \"Learning Activities\"!!\n\nAnswer options: Enclose the teaching plan content with \"[TEACHING_PLAN_BEGIN]\" and \"[TEACHING_PLAN_END]\" tags.\nAnswer options: Using proper markdown format from second-level header format.\nConstraint: Writing in Chinese.\n[LESSON_BEGIN]\n\n UNIT 1 Making New Friends\n TOPIC 1 Welcome to China!\n Section A\n\n 1a Listen and number the following names.\n Jane Mari Kangkang Michael\n Look, listen and understand. Then practice the conversation.\n Work in groups. Introduce yourself using\n I ’m ... Then practice 1a\n with your own hometown or the following places.\n\n 1b Listen and number the following names\n Jane Michael Maria Kangkang\n 1c Work in groups. Introduce yourself using I ’m ... Then practice 1a with your own hometown or the following places.\n China the USA the UK Hong Kong Beijing\n\n 2a Look, listen and understand. Then practice the conversation\n Hello! \n Hello! \n Hello! \n Hello! Are you Maria? \n No, I’m not. I’m Jane.\n Oh, nice to meet you, Jane\n Nice to meet you, too.\n Hi, Maria!\n Hi, Kangkang!\n Welcome to China!\n Thanks.\n\n 2b Work in groups. Make up a conversation with your own name and the\n following structures.\n A: Hello! / Good morning! / Hi! I’m ... Are you ... ?\n B: ...\n\n 3a Listen, say and trace\n Aa Bb Cc Dd Ee Ff Gg\n\n 3b Listen and number the following letters. Then circle the letters with the same sound as Bb.\n Aa Bb Cc Dd Ee Ff Gg\n\n 3c Match the big letters with the small ones. Then write them on the lines.\n \n[LESSON_END]": "[TEACHING_PLAN_BEGIN]\n\n## 学习活动\n\n- 学生分组练习\n - 学生分组,使用\"I'm...\"自我介绍,然后练习1a,谈论自己的家乡或指定的地点。\n- 听力练习\n - 学生听力练习,听音标号姓名,然后分组练习自我介绍和谈论地点。\n- 对话练习\n - 学生分组练习对话,使用指定的结构进行对话练习。\n- 字母练习\n - 学生听力练习,听音标号字母,然后进行字母匹配和书写练习。\n\n[TEACHING_PLAN_END]", + "Do not refer to the context of the previous conversation records, start the conversation anew.\n\nFormation: \"Capacity and role\" defines the role you are currently playing;\n\t\"[LESSON_BEGIN]\" and \"[LESSON_END]\" tags enclose the content of textbook;\n\t\"Statement\" defines the work detail you need to complete at this stage;\n\t\"Answer options\" defines the format requirements for your responses;\n\t\"Constraint\" defines the conditions that your responses must comply with.\n\nCapacity and role: You are a {teaching_language} Teacher, named Lily, your goal is writing a {language} teaching plan part by part. the constraint is writing in {language}. \nStatement: Write the \"Teaching Time Allocation\" part of teaching plan, WITHOUT ANY content unrelated to \"Teaching Time Allocation\"!!\nStatement: \"Teaching Time Allocation\" must include how much time is allocated to each part of the textbook content.\nAnswer options: Enclose the teaching plan content with \"[TEACHING_PLAN_BEGIN]\" and \"[TEACHING_PLAN_END]\" tags.\nAnswer options: Using proper markdown format from second-level header format.\nConstraint: Writing in Chinese.\n[LESSON_BEGIN]\n\n UNIT 1 Making New Friends\n TOPIC 1 Welcome to China!\n Section A\n\n 1a Listen and number the following names.\n Jane Mari Kangkang Michael\n Look, listen and understand. Then practice the conversation.\n Work in groups. Introduce yourself using\n I ’m ... Then practice 1a\n with your own hometown or the following places.\n\n 1b Listen and number the following names\n Jane Michael Maria Kangkang\n 1c Work in groups. Introduce yourself using I ’m ... Then practice 1a with your own hometown or the following places.\n China the USA the UK Hong Kong Beijing\n\n 2a Look, listen and understand. Then practice the conversation\n Hello! \n Hello! \n Hello! \n Hello! Are you Maria? \n No, I’m not. I’m Jane.\n Oh, nice to meet you, Jane\n Nice to meet you, too.\n Hi, Maria!\n Hi, Kangkang!\n Welcome to China!\n Thanks.\n\n 2b Work in groups. Make up a conversation with your own name and the\n following structures.\n A: Hello! / Good morning! / Hi! I’m ... Are you ... ?\n B: ...\n\n 3a Listen, say and trace\n Aa Bb Cc Dd Ee Ff Gg\n\n 3b Listen and number the following letters. Then circle the letters with the same sound as Bb.\n Aa Bb Cc Dd Ee Ff Gg\n\n 3c Match the big letters with the small ones. Then write them on the lines.\n \n[LESSON_END]": "[TEACHING_PLAN_BEGIN]\n\n## Teaching Time Allocation\n\n- 1a Listen and number the following names: 10 minutes\n- 1b Listen and number the following names: 10 minutes\n- 1c Work in groups: 15 minutes\n- 2a Look, listen and understand: 10 minutes\n- 2b Work in groups: 15 minutes\n- 3a Listen, say and trace: 10 minutes\n- 3b Listen and number the following letters: 10 minutes\n- 3c Match the big letters with the small ones: 10 minutes\n\n[TEACHING_PLAN_END]", + "Do not refer to the context of the previous conversation records, start the conversation anew.\n\nFormation: \"Capacity and role\" defines the role you are currently playing;\n\t\"[LESSON_BEGIN]\" and \"[LESSON_END]\" tags enclose the content of textbook;\n\t\"Statement\" defines the work detail you need to complete at this stage;\n\t\"Answer options\" defines the format requirements for your responses;\n\t\"Constraint\" defines the conditions that your responses must comply with.\n\nCapacity and role: You are a {teaching_language} Teacher, named Lily, your goal is writing a {language} teaching plan part by part. the constraint is writing in {language}. \nStatement: Write the \"Assessment and Feedback\" part of teaching plan, WITHOUT ANY content unrelated to \"Assessment and Feedback\"!!\n\nAnswer options: Enclose the teaching plan content with \"[TEACHING_PLAN_BEGIN]\" and \"[TEACHING_PLAN_END]\" tags.\nAnswer options: Using proper markdown format from second-level header format.\nConstraint: Writing in Chinese.\n[LESSON_BEGIN]\n\n UNIT 1 Making New Friends\n TOPIC 1 Welcome to China!\n Section A\n\n 1a Listen and number the following names.\n Jane Mari Kangkang Michael\n Look, listen and understand. Then practice the conversation.\n Work in groups. Introduce yourself using\n I ’m ... Then practice 1a\n with your own hometown or the following places.\n\n 1b Listen and number the following names\n Jane Michael Maria Kangkang\n 1c Work in groups. Introduce yourself using I ’m ... Then practice 1a with your own hometown or the following places.\n China the USA the UK Hong Kong Beijing\n\n 2a Look, listen and understand. Then practice the conversation\n Hello! \n Hello! \n Hello! \n Hello! Are you Maria? \n No, I’m not. I’m Jane.\n Oh, nice to meet you, Jane\n Nice to meet you, too.\n Hi, Maria!\n Hi, Kangkang!\n Welcome to China!\n Thanks.\n\n 2b Work in groups. Make up a conversation with your own name and the\n following structures.\n A: Hello! / Good morning! / Hi! I’m ... Are you ... ?\n B: ...\n\n 3a Listen, say and trace\n Aa Bb Cc Dd Ee Ff Gg\n\n 3b Listen and number the following letters. Then circle the letters with the same sound as Bb.\n Aa Bb Cc Dd Ee Ff Gg\n\n 3c Match the big letters with the small ones. Then write them on the lines.\n \n[LESSON_END]": "[TEACHING_PLAN_BEGIN]\n\n## 评估与反馈\n\n在教学过程中,我们需要对学生的学习情况进行评估和反馈,以便及时发现问题并加以解决。评估和反馈的方式可以包括以下几种:\n\n1. **口头评估**:通过课堂互动、小组讨论等方式,对学生的口语表达能力进行评估,并及时给予反馈和指导。\n\n2. **书面评估**:布置书面作业、小测验等形式,对学生的书面表达能力和语法运用进行评估,并在批改作业时给予详细的反馈。\n\n3. **个性化反馈**:针对学生个体差异,及时给予个性化的学习建议和反馈,帮助学生克服困难,提高学习效果。\n\n通过以上评估和反馈方式,可以更全面地了解学生的学习情况,帮助他们更好地掌握课程内容,提高语言表达能力。\n\n[TEACHING_PLAN_END]", + "Do not refer to the context of the previous conversation records, start the conversation anew.\n\nFormation: \"Capacity and role\" defines the role you are currently playing;\n\t\"[LESSON_BEGIN]\" and \"[LESSON_END]\" tags enclose the content of textbook;\n\t\"Statement\" defines the work detail you need to complete at this stage;\n\t\"Answer options\" defines the format requirements for your responses;\n\t\"Constraint\" defines the conditions that your responses must comply with.\n\nCapacity and role: You are a {teaching_language} Teacher, named Lily, your goal is writing a {language} teaching plan part by part. the constraint is writing in {language}. \nStatement: Write the \"Teaching Summary and Improvement\" part of teaching plan, WITHOUT ANY content unrelated to \"Teaching Summary and Improvement\"!!\n\nAnswer options: Enclose the teaching plan content with \"[TEACHING_PLAN_BEGIN]\" and \"[TEACHING_PLAN_END]\" tags.\nAnswer options: Using proper markdown format from second-level header format.\nConstraint: Writing in Chinese.\n[LESSON_BEGIN]\n\n UNIT 1 Making New Friends\n TOPIC 1 Welcome to China!\n Section A\n\n 1a Listen and number the following names.\n Jane Mari Kangkang Michael\n Look, listen and understand. Then practice the conversation.\n Work in groups. Introduce yourself using\n I ’m ... Then practice 1a\n with your own hometown or the following places.\n\n 1b Listen and number the following names\n Jane Michael Maria Kangkang\n 1c Work in groups. Introduce yourself using I ’m ... Then practice 1a with your own hometown or the following places.\n China the USA the UK Hong Kong Beijing\n\n 2a Look, listen and understand. Then practice the conversation\n Hello! \n Hello! \n Hello! \n Hello! Are you Maria? \n No, I’m not. I’m Jane.\n Oh, nice to meet you, Jane\n Nice to meet you, too.\n Hi, Maria!\n Hi, Kangkang!\n Welcome to China!\n Thanks.\n\n 2b Work in groups. Make up a conversation with your own name and the\n following structures.\n A: Hello! / Good morning! / Hi! I’m ... Are you ... ?\n B: ...\n\n 3a Listen, say and trace\n Aa Bb Cc Dd Ee Ff Gg\n\n 3b Listen and number the following letters. Then circle the letters with the same sound as Bb.\n Aa Bb Cc Dd Ee Ff Gg\n\n 3c Match the big letters with the small ones. Then write them on the lines.\n \n[LESSON_END]": "[TEACHING_PLAN_BEGIN]\n\n## Teaching Summary and Improvement\n\nIn this unit, the focus was on introducing oneself and making new friends. The students practiced listening and speaking skills through various activities such as listening to conversations, introducing themselves, and matching letters. The unit aimed to improve the students' communication skills and confidence in using the language.\n\nTo improve the teaching effectiveness, more interactive activities can be incorporated to encourage students to engage in real-life conversations. Additionally, providing opportunities for students to apply the language in practical scenarios, such as role-playing situations, can enhance their language acquisition and confidence.\n\n[TEACHING_PLAN_END]", + "Do not refer to the context of the previous conversation records, start the conversation anew.\n\nFormation: \"Capacity and role\" defines the role you are currently playing;\n\t\"[LESSON_BEGIN]\" and \"[LESSON_END]\" tags enclose the content of textbook;\n\t\"Statement\" defines the work detail you need to complete at this stage;\n\t\"Answer options\" defines the format requirements for your responses;\n\t\"Constraint\" defines the conditions that your responses must comply with.\n\nCapacity and role: You are a {teaching_language} Teacher, named Lily, your goal is writing a {language} teaching plan part by part. the constraint is writing in {language}. \nStatement: Write the \"Vocabulary Cloze\" part of teaching plan, WITHOUT ANY content unrelated to \"Vocabulary Cloze\"!!\nStatement: Based on the content of the textbook enclosed by \"[LESSON_BEGIN]\" and \"[LESSON_END]\", create vocabulary cloze. The cloze should include 10 {language} questions with {teaching_language} answers, and it should also include 10 {teaching_language} questions with {language} answers. The key-related vocabulary and phrases in the textbook content must all be included in the exercises.\nAnswer options: Enclose the teaching plan content with \"[TEACHING_PLAN_BEGIN]\" and \"[TEACHING_PLAN_END]\" tags.\nAnswer options: Using proper markdown format from second-level header format.\nConstraint: Writing in Chinese.\n[LESSON_BEGIN]\n\n UNIT 1 Making New Friends\n TOPIC 1 Welcome to China!\n Section A\n\n 1a Listen and number the following names.\n Jane Mari Kangkang Michael\n Look, listen and understand. Then practice the conversation.\n Work in groups. Introduce yourself using\n I ’m ... Then practice 1a\n with your own hometown or the following places.\n\n 1b Listen and number the following names\n Jane Michael Maria Kangkang\n 1c Work in groups. Introduce yourself using I ’m ... Then practice 1a with your own hometown or the following places.\n China the USA the UK Hong Kong Beijing\n\n 2a Look, listen and understand. Then practice the conversation\n Hello! \n Hello! \n Hello! \n Hello! Are you Maria? \n No, I’m not. I’m Jane.\n Oh, nice to meet you, Jane\n Nice to meet you, too.\n Hi, Maria!\n Hi, Kangkang!\n Welcome to China!\n Thanks.\n\n 2b Work in groups. Make up a conversation with your own name and the\n following structures.\n A: Hello! / Good morning! / Hi! I’m ... Are you ... ?\n B: ...\n\n 3a Listen, say and trace\n Aa Bb Cc Dd Ee Ff Gg\n\n 3b Listen and number the following letters. Then circle the letters with the same sound as Bb.\n Aa Bb Cc Dd Ee Ff Gg\n\n 3c Match the big letters with the small ones. Then write them on the lines.\n \n[LESSON_END]": "[TEACHING_PLAN_BEGIN]\n\n## 词汇填空\n\n### 语言填空\n1. 你好! 你是玛丽亚吗?\n - 不,我不是。我是简。\n - 哦,很高兴认识你,简。\n - 我也很高兴认识你。\n\n2. 你好! / 早上好! / 嗨! 我是... 你是...吗?\n - ...\n\n3. Aa Bb Cc Dd Ee Ff Gg\n\n4. 匹配大写字母和小写字母。然后在线上写出它们。\n\n5. ...\n\n### {language}填空\n1. 1a 听录音,给以下名字编号。\n - 简 玛丽 康康 迈克尔\n\n2. 1b 听录音,给以下名字编号。\n - 简 迈克尔 玛丽亚 康康\n\n3. 1c 分组活动。使用“我是...”介绍自己。然后用你自己的家乡或以下地方练习1a。\n - 中国 美国 英国 香港 北京\n\n4. 2a 看,听,理解。然后练习对话。\n - 你好!\n - 你好!\n - 你好!\n - 你好! 你是玛丽亚吗?\n - 不,我不是。我是简。\n - 哦,很高兴认识你,简。\n - 我也很高兴认识你。\n - 你好,玛丽亚!\n - 你好,康康!\n - 欢迎来到中国!\n - 谢谢。\n\n5. 2b 分组活动。编写一个包含你自己名字和以下结构的对话。\n - A: 你好! / 早上好! / 嗨! 我是... 你是...吗?\n - B: ...\n\n[TEACHING_PLAN_END]", + "Do not refer to the context of the previous conversation records, start the conversation anew.\n\nFormation: \"Capacity and role\" defines the role you are currently playing;\n\t\"[LESSON_BEGIN]\" and \"[LESSON_END]\" tags enclose the content of textbook;\n\t\"Statement\" defines the work detail you need to complete at this stage;\n\t\"Answer options\" defines the format requirements for your responses;\n\t\"Constraint\" defines the conditions that your responses must comply with.\n\nCapacity and role: You are a {teaching_language} Teacher, named Lily, your goal is writing a {language} teaching plan part by part. the constraint is writing in {language}. \nStatement: Write the \"Choice Questions\" part of teaching plan, WITHOUT ANY content unrelated to \"Choice Questions\"!!\nStatement: Based on the content of the textbook enclosed by \"[LESSON_BEGIN]\" and \"[LESSON_END]\", create choice questions. 10 questions.\nAnswer options: Enclose the teaching plan content with \"[TEACHING_PLAN_BEGIN]\" and \"[TEACHING_PLAN_END]\" tags.\nAnswer options: Using proper markdown format from second-level header format.\nConstraint: Writing in Chinese.\n[LESSON_BEGIN]\n\n UNIT 1 Making New Friends\n TOPIC 1 Welcome to China!\n Section A\n\n 1a Listen and number the following names.\n Jane Mari Kangkang Michael\n Look, listen and understand. Then practice the conversation.\n Work in groups. Introduce yourself using\n I ’m ... Then practice 1a\n with your own hometown or the following places.\n\n 1b Listen and number the following names\n Jane Michael Maria Kangkang\n 1c Work in groups. Introduce yourself using I ’m ... Then practice 1a with your own hometown or the following places.\n China the USA the UK Hong Kong Beijing\n\n 2a Look, listen and understand. Then practice the conversation\n Hello! \n Hello! \n Hello! \n Hello! Are you Maria? \n No, I’m not. I’m Jane.\n Oh, nice to meet you, Jane\n Nice to meet you, too.\n Hi, Maria!\n Hi, Kangkang!\n Welcome to China!\n Thanks.\n\n 2b Work in groups. Make up a conversation with your own name and the\n following structures.\n A: Hello! / Good morning! / Hi! I’m ... Are you ... ?\n B: ...\n\n 3a Listen, say and trace\n Aa Bb Cc Dd Ee Ff Gg\n\n 3b Listen and number the following letters. Then circle the letters with the same sound as Bb.\n Aa Bb Cc Dd Ee Ff Gg\n\n 3c Match the big letters with the small ones. Then write them on the lines.\n \n[LESSON_END]": "[TEACHING_PLAN_BEGIN]\n\n## 选择题\n\n1. 在1a部分,听到的名字按顺序应该是?\n - A. Jane B. Mari C. Kangkang D. Michael\n\n2. 在1b部分,听到的名字按顺序应该是?\n - A. Jane B. Michael C. Maria D. Kangkang\n\n3. 在2a部分,对话中提到了哪些地方?\n - A. 中国 B. 美国 C. 英国 D. 香港\n\n4. 在3b部分,与Bb发音相同的字母是?\n - A. Aa B. Cc C. Dd D. Gg\n\n5. 在3c部分,大写字母和小写字母的正确匹配是?\n - A. Aa - a B. Bb - b C. Cc - c D. Dd - d\n\n6. 在1a部分,听到的名字按顺序应该是?\n - A. Jane B. Mari C. Kangkang D. Michael\n\n7. 在1b部分,听到的名字按顺序应该是?\n - A. Jane B. Michael C. Maria D. Kangkang\n\n8. 在2a部分,对话中提到了哪些地方?\n - A. 中国 B. 美国 C. 英国 D. 香港\n\n9. 在3b部分,与Bb发音相同的字母是?\n - A. Aa B. Cc C. Dd D. Gg\n\n10. 在3c部分,大写字母和小写字母的正确匹配是?\n - A. Aa - a B. Bb - b C. Cc - c D. Dd - d\n\n[TEACHING_PLAN_END]", + "Do not refer to the context of the previous conversation records, start the conversation anew.\n\nFormation: \"Capacity and role\" defines the role you are currently playing;\n\t\"[LESSON_BEGIN]\" and \"[LESSON_END]\" tags enclose the content of textbook;\n\t\"Statement\" defines the work detail you need to complete at this stage;\n\t\"Answer options\" defines the format requirements for your responses;\n\t\"Constraint\" defines the conditions that your responses must comply with.\n\nCapacity and role: You are a {teaching_language} Teacher, named Lily, your goal is writing a {language} teaching plan part by part. the constraint is writing in {language}. \nStatement: Write the \"Grammar Questions\" part of teaching plan, WITHOUT ANY content unrelated to \"Grammar Questions\"!!\nStatement: Based on the content of the textbook enclosed by \"[LESSON_BEGIN]\" and \"[LESSON_END]\", create grammar questions. 10 questions.\nAnswer options: Enclose the teaching plan content with \"[TEACHING_PLAN_BEGIN]\" and \"[TEACHING_PLAN_END]\" tags.\nAnswer options: Using proper markdown format from second-level header format.\nConstraint: Writing in Chinese.\n[LESSON_BEGIN]\n\n UNIT 1 Making New Friends\n TOPIC 1 Welcome to China!\n Section A\n\n 1a Listen and number the following names.\n Jane Mari Kangkang Michael\n Look, listen and understand. Then practice the conversation.\n Work in groups. Introduce yourself using\n I ’m ... Then practice 1a\n with your own hometown or the following places.\n\n 1b Listen and number the following names\n Jane Michael Maria Kangkang\n 1c Work in groups. Introduce yourself using I ’m ... Then practice 1a with your own hometown or the following places.\n China the USA the UK Hong Kong Beijing\n\n 2a Look, listen and understand. Then practice the conversation\n Hello! \n Hello! \n Hello! \n Hello! Are you Maria? \n No, I’m not. I’m Jane.\n Oh, nice to meet you, Jane\n Nice to meet you, too.\n Hi, Maria!\n Hi, Kangkang!\n Welcome to China!\n Thanks.\n\n 2b Work in groups. Make up a conversation with your own name and the\n following structures.\n A: Hello! / Good morning! / Hi! I’m ... Are you ... ?\n B: ...\n\n 3a Listen, say and trace\n Aa Bb Cc Dd Ee Ff Gg\n\n 3b Listen and number the following letters. Then circle the letters with the same sound as Bb.\n Aa Bb Cc Dd Ee Ff Gg\n\n 3c Match the big letters with the small ones. Then write them on the lines.\n \n[LESSON_END]": "[TEACHING_PLAN_BEGIN]\n\n## Grammar Questions\n\n1. 请问在1a中,学生需要做什么?\n2. 请问在1b中,学生需要做什么?\n3. 请问在2a中,学生需要做什么?\n4. 请问在2b中,学生需要做什么?\n5. 请问在3a中,学生需要做什么?\n6. 请问在3b中,学生需要做什么?\n7. 请问在3c中,学生需要做什么?\n8. 请问在1a中,学生需要听什么?\n9. 请问在2a中,学生需要看什么?\n10. 请问在3a中,学生需要说什么?\n\n[TEACHING_PLAN_END]", + "Do not refer to the context of the previous conversation records, start the conversation anew.\n\nFormation: \"Capacity and role\" defines the role you are currently playing;\n\t\"[LESSON_BEGIN]\" and \"[LESSON_END]\" tags enclose the content of textbook;\n\t\"Statement\" defines the work detail you need to complete at this stage;\n\t\"Answer options\" defines the format requirements for your responses;\n\t\"Constraint\" defines the conditions that your responses must comply with.\n\nCapacity and role: You are a {teaching_language} Teacher, named Lily, your goal is writing a {language} teaching plan part by part. the constraint is writing in {language}. \nStatement: Write the \"Translation Questions\" part of teaching plan, WITHOUT ANY content unrelated to \"Translation Questions\"!!\nStatement: Based on the content of the textbook enclosed by \"[LESSON_BEGIN]\" and \"[LESSON_END]\", create translation questions. The translation should include 10 {language} questions with {teaching_language} answers, and it should also include 10 {teaching_language} questions with {language} answers.\nAnswer options: Enclose the teaching plan content with \"[TEACHING_PLAN_BEGIN]\" and \"[TEACHING_PLAN_END]\" tags.\nAnswer options: Using proper markdown format from second-level header format.\nConstraint: Writing in Chinese.\n[LESSON_BEGIN]\n\n UNIT 1 Making New Friends\n TOPIC 1 Welcome to China!\n Section A\n\n 1a Listen and number the following names.\n Jane Mari Kangkang Michael\n Look, listen and understand. Then practice the conversation.\n Work in groups. Introduce yourself using\n I ’m ... Then practice 1a\n with your own hometown or the following places.\n\n 1b Listen and number the following names\n Jane Michael Maria Kangkang\n 1c Work in groups. Introduce yourself using I ’m ... Then practice 1a with your own hometown or the following places.\n China the USA the UK Hong Kong Beijing\n\n 2a Look, listen and understand. Then practice the conversation\n Hello! \n Hello! \n Hello! \n Hello! Are you Maria? \n No, I’m not. I’m Jane.\n Oh, nice to meet you, Jane\n Nice to meet you, too.\n Hi, Maria!\n Hi, Kangkang!\n Welcome to China!\n Thanks.\n\n 2b Work in groups. Make up a conversation with your own name and the\n following structures.\n A: Hello! / Good morning! / Hi! I’m ... Are you ... ?\n B: ...\n\n 3a Listen, say and trace\n Aa Bb Cc Dd Ee Ff Gg\n\n 3b Listen and number the following letters. Then circle the letters with the same sound as Bb.\n Aa Bb Cc Dd Ee Ff Gg\n\n 3c Match the big letters with the small ones. Then write them on the lines.\n \n[LESSON_END]": "[TEACHING_PLAN_BEGIN]\n\n## Translation Questions\n\n### {language} Questions with {teaching_language} Answers\n1. 你能听懂这些名字吗? (Can you understand these names?)\n - 能,我能听懂。 (Yes, I can understand.)\n2. 请用“我是...”介绍一下你自己。 (Please introduce yourself using \"I am...\")\n - 我是... (I am...)\n3. 你能用这些结构编一个对话吗? (Can you make up a conversation with these structures?)\n - 能,我能编一个对话。 (Yes, I can make up a conversation.)\n4. 你能说出这些字母的名字吗? (Can you say the names of these letters?)\n - 能,我能说出来。 (Yes, I can say them.)\n5. 你能把大写字母和小写字母配对吗? (Can you match the uppercase letters with the lowercase letters?)\n - 能,我能配对。 (Yes, I can match them.)\n\n### {teaching_language} Questions with {language} Answers\n1. Can you understand these names?\n - Yes, I can understand.\n2. Please introduce yourself using \"I am...\"\n - I am...\n3. Can you make up a conversation with these structures?\n - Yes, I can make up a conversation.\n4. Can you say the names of these letters?\n - Yes, I can say them.\n5. Can you match the uppercase letters with the lowercase letters?\n - Yes, I can match them.\n\n[TEACHING_PLAN_END]", + "The given text repeatedly describes Lily as a girl. It emphasizes that Lily is a girl multiple times. The content consistently refers to Lily as a girl.\nTranslate the above summary into a English title of less than 5 words.": "\"Emphasizing Lily's Gender\"", + "\n## context\n\n### Project Name\n20240110212347\n\n### Original Requirements\n['需要一个基于LLM做总结的搜索引擎']\n\n### Search Information\n-\n\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Language\": \"en_us\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"Create a 2048 game\",\n \"Product Goals\": [\n \"Create an engaging user experience\",\n \"Improve accessibility, be responsive\",\n \"More beautiful UI\"\n ],\n \"User Stories\": [\n \"As a player, I want to be able to choose difficulty levels\",\n \"As a player, I want to see my score after each game\",\n \"As a player, I want to get restart button when I lose\",\n \"As a player, I want to see beautiful UI that make me feel good\",\n \"As a player, I want to play game via mobile phone\"\n ],\n \"Competitive Analysis\": [\n \"2048 Game A: Simple interface, lacks responsive features\",\n \"play2048.co: Beautiful and responsive UI with my best score shown\",\n \"2048game.com: Responsive UI with my best score shown, but many ads\"\n ],\n \"Competitive Quadrant Chart\": \"quadrantChart\\n title \\\"Reach and engagement of campaigns\\\"\\n x-axis \\\"Low Reach\\\" --> \\\"High Reach\\\"\\n y-axis \\\"Low Engagement\\\" --> \\\"High Engagement\\\"\\n quadrant-1 \\\"We should expand\\\"\\n quadrant-2 \\\"Need to promote\\\"\\n quadrant-3 \\\"Re-evaluate\\\"\\n quadrant-4 \\\"May be improved\\\"\\n \\\"Campaign A\\\": [0.3, 0.6]\\n \\\"Campaign B\\\": [0.45, 0.23]\\n \\\"Campaign C\\\": [0.57, 0.69]\\n \\\"Campaign D\\\": [0.78, 0.34]\\n \\\"Campaign E\\\": [0.40, 0.34]\\n \\\"Campaign F\\\": [0.35, 0.78]\\n \\\"Our Target Product\\\": [0.5, 0.6]\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [\n [\n \"P0\",\n \"The main code ...\"\n ],\n [\n \"P0\",\n \"The game algorithm ...\"\n ]\n ],\n \"UI Design draft\": \"Basic function description with a simple style and layout.\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Language: # Provide the language used in the project, typically matching the user's requirement language.\n- Programming Language: # Python/JavaScript or other mainstream programming language.\n- Original Requirements: # Place the original user's requirements here.\n- Product Goals: typing.List[str] # Provide up to three clear, orthogonal product goals.\n- User Stories: typing.List[str] # Provide up to 3 to 5 scenario-based user stories.\n- Competitive Analysis: typing.List[str] # Provide 5 to 7 competitive products.\n- Competitive Quadrant Chart: # Use mermaid quadrantChart syntax. Distribute scores evenly between 0 and 1\n- Requirement Analysis: # Provide a detailed analysis of the requirements.\n- Requirement Pool: typing.List[typing.List[str]] # List down the top-5 requirements with their priority (P0, P1, P2).\n- UI Design draft: # Provide a simple description of UI elements, functions, style, and layout.\n- Anything UNCLEAR: # Mention any aspects of the project that are unclear and try to clarify them.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Language\": \"zh_cn\",\n \"Programming Language\": \"LLM\",\n \"Original Requirements\": \"需要一个基于LLM做总结的搜索引擎\",\n \"Product Goals\": [],\n \"User Stories\": [],\n \"Competitive Analysis\": [],\n \"Competitive Quadrant Chart\": \"\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [],\n \"UI Design draft\": \"\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]", + "\n## context\n\n### Project Name\n20240101\n\n### Original Requirements\n['Make a cli snake game']\n\n### Search Information\n-\n\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Language\": \"en_us\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"Create a 2048 game\",\n \"Product Goals\": [\n \"Create an engaging user experience\",\n \"Improve accessibility, be responsive\",\n \"More beautiful UI\"\n ],\n \"User Stories\": [\n \"As a player, I want to be able to choose difficulty levels\",\n \"As a player, I want to see my score after each game\",\n \"As a player, I want to get restart button when I lose\",\n \"As a player, I want to see beautiful UI that make me feel good\",\n \"As a player, I want to play game via mobile phone\"\n ],\n \"Competitive Analysis\": [\n \"2048 Game A: Simple interface, lacks responsive features\",\n \"play2048.co: Beautiful and responsive UI with my best score shown\",\n \"2048game.com: Responsive UI with my best score shown, but many ads\"\n ],\n \"Competitive Quadrant Chart\": \"quadrantChart\\n title \\\"Reach and engagement of campaigns\\\"\\n x-axis \\\"Low Reach\\\" --> \\\"High Reach\\\"\\n y-axis \\\"Low Engagement\\\" --> \\\"High Engagement\\\"\\n quadrant-1 \\\"We should expand\\\"\\n quadrant-2 \\\"Need to promote\\\"\\n quadrant-3 \\\"Re-evaluate\\\"\\n quadrant-4 \\\"May be improved\\\"\\n \\\"Campaign A\\\": [0.3, 0.6]\\n \\\"Campaign B\\\": [0.45, 0.23]\\n \\\"Campaign C\\\": [0.57, 0.69]\\n \\\"Campaign D\\\": [0.78, 0.34]\\n \\\"Campaign E\\\": [0.40, 0.34]\\n \\\"Campaign F\\\": [0.35, 0.78]\\n \\\"Our Target Product\\\": [0.5, 0.6]\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [\n [\n \"P0\",\n \"The main code ...\"\n ],\n [\n \"P0\",\n \"The game algorithm ...\"\n ]\n ],\n \"UI Design draft\": \"Basic function description with a simple style and layout.\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Language: # Provide the language used in the project, typically matching the user's requirement language.\n- Programming Language: # Python/JavaScript or other mainstream programming language.\n- Original Requirements: # Place the original user's requirements here.\n- Product Goals: typing.List[str] # Provide up to three clear, orthogonal product goals.\n- User Stories: typing.List[str] # Provide up to 3 to 5 scenario-based user stories.\n- Competitive Analysis: typing.List[str] # Provide 5 to 7 competitive products.\n- Competitive Quadrant Chart: # Use mermaid quadrantChart syntax. Distribute scores evenly between 0 and 1\n- Requirement Analysis: # Provide a detailed analysis of the requirements.\n- Requirement Pool: typing.List[typing.List[str]] # List down the top-5 requirements with their priority (P0, P1, P2).\n- UI Design draft: # Provide a simple description of UI elements, functions, style, and layout.\n- Anything UNCLEAR: # Mention any aspects of the project that are unclear and try to clarify them.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Language\": \"en_us\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"Make a cli snake game\",\n \"Product Goals\": [],\n \"User Stories\": [],\n \"Competitive Analysis\": [],\n \"Competitive Quadrant Chart\": \"\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [],\n \"UI Design draft\": \"\",\n \"Anything UNCLEAR\": \"Please provide more details on the product goals and user stories.\"\n}\n[/CONTENT]", + "\n## context\n{\"Language\":\"en_us\",\"Programming Language\":\"Python\",\"Original Requirements\":\"Make a cli snake game\",\"Product Goals\":[],\"User Stories\":[],\"Competitive Analysis\":[],\"Competitive Quadrant Chart\":\"\",\"Requirement Analysis\":\"\",\"Requirement Pool\":[],\"UI Design draft\":\"\",\"Anything UNCLEAR\":\"Please provide more details on the product goals and user stories.\"}\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Implementation approach\": \"We will ...\",\n \"File list\": [\n \"main.py\",\n \"game.py\"\n ],\n \"Data structures and interfaces\": \"\\nclassDiagram\\n class Main {\\n -SearchEngine search_engine\\n +main() str\\n }\\n class SearchEngine {\\n -Index index\\n -Ranking ranking\\n -Summary summary\\n +search(query: str) str\\n }\\n class Index {\\n -KnowledgeBase knowledge_base\\n +create_index(data: dict)\\n +query_index(query: str) list\\n }\\n class Ranking {\\n +rank_results(results: list) list\\n }\\n class Summary {\\n +summarize_results(results: list) str\\n }\\n class KnowledgeBase {\\n +update(data: dict)\\n +fetch_data(query: str) dict\\n }\\n Main --> SearchEngine\\n SearchEngine --> Index\\n SearchEngine --> Ranking\\n SearchEngine --> Summary\\n Index --> KnowledgeBase\\n\",\n \"Program call flow\": \"\\nsequenceDiagram\\n participant M as Main\\n participant SE as SearchEngine\\n participant I as Index\\n participant R as Ranking\\n participant S as Summary\\n participant KB as KnowledgeBase\\n M->>SE: search(query)\\n SE->>I: query_index(query)\\n I->>KB: fetch_data(query)\\n KB-->>I: return data\\n I-->>SE: return results\\n SE->>R: rank_results(results)\\n R-->>SE: return ranked_results\\n SE->>S: summarize_results(ranked_results)\\n S-->>SE: return summary\\n SE-->>M: return summary\\n\",\n \"Anything UNCLEAR\": \"Clarification needed on third-party API integration, ...\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Implementation approach: # Analyze the difficult points of the requirements, select the appropriate open-source framework\n- File list: typing.List[str] # Only need relative paths. ALWAYS write a main.py or app.py here\n- Data structures and interfaces: # Use mermaid classDiagram code syntax, including classes, method(__init__ etc.) and functions with type annotations, CLEARLY MARK the RELATIONSHIPS between classes, and comply with PEP8 standards. The data structures SHOULD BE VERY DETAILED and the API should be comprehensive with a complete design.\n- Program call flow: # Use sequenceDiagram code syntax, COMPLETE and VERY DETAILED, using CLASSES AND API DEFINED ABOVE accurately, covering the CRUD AND INIT of each object, SYNTAX MUST BE CORRECT.\n- Anything UNCLEAR: # Mention unclear project aspects, then try to clarify it.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Product Goals\": [\n \"Create a command-line interface (CLI) snake game\",\n \"Implement game logic for movement, collision, and scoring\",\n \"Provide a user-friendly and interactive gaming experience\"\n ],\n \"User Stories\": [\n \"As a player, I want to be able to control the snake's movement using arrow keys\",\n \"As a player, I want the game to end when the snake collides with the walls or itself\",\n \"As a player, I want to see my score displayed on the screen during and after the game\"\n ],\n \"Anything UNCLEAR\": \"Please provide more details on the specific features and functionalities expected in the snake game.\"\n}\n[/CONTENT]", + "\n## context\n{\"Implementation approach\":\"We will use Python and the curses library to create the snake game. The game logic will be implemented in a separate module, and the main.py file will handle the user interface and game loop.\",\"File list\":[\"main.py\",\"game.py\"],\"Data structures and interfaces\":\"\\nclassDiagram\\n class Game {\\n -Snake snake\\n -Food food\\n -Score score\\n +__init__(width: int, height: int)\\n +start_game()\\n +move_snake(direction: str)\\n +generate_food()\\n +update_score(points: int)\\n }\\n class Snake {\\n -body list\\n -direction str\\n +__init__(x: int, y: int)\\n +move(direction: str)\\n +grow()\\n +collides_with_self() bool\\n }\\n class Food {\\n -position tuple\\n +__init__(x: int, y: int)\\n +get_position() tuple\\n }\\n class Score {\\n -points int\\n +__init__()\\n +increase(points: int)\\n }\\n Game --> Snake\\n Game --> Food\\n Game --> Score\\n\",\"Program call flow\":\"\\nsequenceDiagram\\n participant M as Main\\n participant G as Game\\n M->>G: start_game()\\n M->>G: move_snake(direction)\\n G->>G: generate_food()\\n G->>G: update_score(points)\\n\",\"Anything UNCLEAR\":\"Please provide more details on the game mechanics and user interactions.\"}\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Required Python packages\": [\n \"flask==1.1.2\",\n \"bcrypt==3.2.0\"\n ],\n \"Required Other language third-party packages\": [\n \"No third-party dependencies required\"\n ],\n \"Logic Analysis\": [\n [\n \"game.py\",\n \"Contains Game class and ... functions\"\n ],\n [\n \"main.py\",\n \"Contains main function, from game import Game\"\n ]\n ],\n \"Task list\": [\n \"game.py\",\n \"main.py\"\n ],\n \"Full API spec\": \"openapi: 3.0.0 ...\",\n \"Shared Knowledge\": \"'game.py' contains functions shared across the project.\",\n \"Anything UNCLEAR\": \"Clarification needed on how to start and initialize third-party libraries.\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Required Python packages: typing.List[str] # Provide required Python packages in requirements.txt format.\n- Required Other language third-party packages: typing.List[str] # List down the required packages for languages other than Python.\n- Logic Analysis: typing.List[typing.List[str]] # Provide a list of files with the classes/methods/functions to be implemented, including dependency analysis and imports.\n- Task list: typing.List[str] # Break down the tasks into a list of filenames, prioritized by dependency order.\n- Full API spec: # Describe all APIs using OpenAPI 3.0 spec that may be used by both frontend and backend. If front-end and back-end communication is not required, leave it blank.\n- Shared Knowledge: # Detail any shared knowledge, like common utility functions or configuration variables.\n- Anything UNCLEAR: # Mention any unclear aspects in the project management context and try to clarify them.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Required Python packages\": [\n \"curses==2.2.0\"\n ],\n \"Required Other language third-party packages\": [\n \"No third-party dependencies required\"\n ],\n \"Logic Analysis\": [\n [\n \"game.py\",\n \"Contains Game class and ... functions\"\n ],\n [\n \"main.py\",\n \"Contains main function, from game import Game\"\n ]\n ],\n \"Task list\": [\n \"game.py\",\n \"main.py\"\n ],\n \"Full API spec\": \"\",\n \"Shared Knowledge\": \"'game.py' contains functions shared across the project.\",\n \"Anything UNCLEAR\": \"Please provide more details on the game mechanics and user interactions.\"\n}\n[/CONTENT]", + "\nNOTICE\nRole: You are a professional engineer; the main goal is to write google-style, elegant, modular, easy to read and maintain code\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## Design\n{\"Implementation approach\":\"We will use Python and the curses library to create the snake game. The game logic will be implemented in a separate module, and the main.py file will handle the user interface and game loop.\",\"File list\":[\"main.py\",\"game.py\"],\"Data structures and interfaces\":\"\\nclassDiagram\\n class Game {\\n -Snake snake\\n -Food food\\n -Score score\\n +__init__(width: int, height: int)\\n +start_game()\\n +move_snake(direction: str)\\n +generate_food()\\n +update_score(points: int)\\n }\\n class Snake {\\n -body list\\n -direction str\\n +__init__(x: int, y: int)\\n +move(direction: str)\\n +grow()\\n +collides_with_self() bool\\n }\\n class Food {\\n -position tuple\\n +__init__(x: int, y: int)\\n +get_position() tuple\\n }\\n class Score {\\n -points int\\n +__init__()\\n +increase(points: int)\\n }\\n Game --> Snake\\n Game --> Food\\n Game --> Score\\n\",\"Program call flow\":\"\\nsequenceDiagram\\n participant M as Main\\n participant G as Game\\n M->>G: start_game()\\n M->>G: move_snake(direction)\\n G->>G: generate_food()\\n G->>G: update_score(points)\\n\",\"Anything UNCLEAR\":\"Please provide more details on the game mechanics and user interactions.\"}\n\n## Tasks\n{\"Required Python packages\":[\"curses==2.2.0\"],\"Required Other language third-party packages\":[\"No third-party dependencies required\"],\"Logic Analysis\":[[\"game.py\",\"Contains Game class and ... functions\"],[\"main.py\",\"Contains main function, from game import Game\"]],\"Task list\":[\"game.py\",\"main.py\"],\"Full API spec\":\"\",\"Shared Knowledge\":\"'game.py' contains functions shared across the project.\",\"Anything UNCLEAR\":\"Please provide more details on the game mechanics and user interactions.\"}\n\n## Legacy Code\n```Code\n\n```\n\n## Debug logs\n```text\n\n\n\n```\n\n## Bug Feedback logs\n```text\n\n```\n\n# Format example\n## Code: game.py\n```python\n## game.py\n...\n```\n\n# Instruction: Based on the context, follow \"Format example\", write code.\n\n## Code: game.py. Write code with triple quoto, based on the following attentions and context.\n1. Only One file: do your best to implement THIS ONLY ONE FILE.\n2. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets.\n3. Set default value: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. AVOID circular import.\n4. Follow design: YOU MUST FOLLOW \"Data structures and interfaces\". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design.\n5. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE.\n6. Before using a external variable/module, make sure you import it first.\n7. Write out EVERY CODE DETAIL, DON'T LEAVE TODO.\n\n": "```python\n## game.py\n\nclass Snake:\n def __init__(self, x: int, y: int):\n self.body = [(x, y)]\n self.direction = 'right'\n\n def move(self, direction: str):\n if direction == 'up' and self.direction != 'down':\n self.direction = 'up'\n elif direction == 'down' and self.direction != 'up':\n self.direction = 'down'\n elif direction == 'left' and self.direction != 'right':\n self.direction = 'left'\n elif direction == 'right' and self.direction != 'left':\n self.direction = 'right'\n\n head = self.body[0]\n x, y = head\n if self.direction == 'up':\n new_head = (x, y - 1)\n elif self.direction == 'down':\n new_head = (x, y + 1)\n elif self.direction == 'left':\n new_head = (x - 1, y)\n elif self.direction == 'right':\n new_head = (x + 1, y)\n self.body.insert(0, new_head)\n\n def grow(self):\n tail = self.body[-1]\n x, y = tail\n if self.direction == 'up':\n new_tail = (x, y + 1)\n elif self.direction == 'down':\n new_tail = (x, y - 1)\n elif self.direction == 'left':\n new_tail = (x + 1, y)\n elif self.direction == 'right':\n new_tail = (x - 1, y)\n self.body.append(new_tail)\n\n def collides_with_self(self) -> bool:\n return len(self.body) != len(set(self.body))\n\nclass Food:\n def __init__(self, x: int, y: int):\n self.position = (x, y)\n\n def get_position(self) -> tuple:\n return self.position\n\nclass Score:\n def __init__(self):\n self.points = 0\n\n def increase(self, points: int):\n self.points += points\n\nclass Game:\n def __init__(self, width: int, height: int):\n self.snake = Snake(width // 2, height // 2)\n self.food = None\n self.score = Score()\n self.width = width\n self.height = height\n\n def start_game(self):\n self.generate_food()\n\n def move_snake(self, direction: str):\n self.snake.move(direction)\n if self.check_collision():\n return False\n self.check_eat()\n return True\n\n def generate_food(self):\n import random\n x = random.randint(0, self.width - 1)\n y = random.randint(0, self.height - 1)\n self.food = Food(x, y)\n\n def update_score(self, points: int):\n self.score.increase(points)\n\n def check_collision(self) -> bool:\n head = self.snake.body[0]\n if head[0] < 0 or head[0] >= self.width or head[1] < 0 or head[1] >= self.height:\n return True\n if self.snake.collides_with_self():\n return True\n return False\n\n def check_eat(self):\n if self.snake.body[0] == self.food.get_position():\n self.snake.grow()\n self.generate_food()\n self.update_score(10)\n```", + "\n# System\nRole: You are a professional software engineer, and your main task is to review and revise the code. You need to ensure that the code conforms to the google-style standards, is elegantly designed and modularized, easy to read and maintain.\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## System Design\n{\"Implementation approach\":\"We will use Python and the curses library to create the snake game. The game logic will be implemented in a separate module, and the main.py file will handle the user interface and game loop.\",\"File list\":[\"main.py\",\"game.py\"],\"Data structures and interfaces\":\"\\nclassDiagram\\n class Game {\\n -Snake snake\\n -Food food\\n -Score score\\n +__init__(width: int, height: int)\\n +start_game()\\n +move_snake(direction: str)\\n +generate_food()\\n +update_score(points: int)\\n }\\n class Snake {\\n -body list\\n -direction str\\n +__init__(x: int, y: int)\\n +move(direction: str)\\n +grow()\\n +collides_with_self() bool\\n }\\n class Food {\\n -position tuple\\n +__init__(x: int, y: int)\\n +get_position() tuple\\n }\\n class Score {\\n -points int\\n +__init__()\\n +increase(points: int)\\n }\\n Game --> Snake\\n Game --> Food\\n Game --> Score\\n\",\"Program call flow\":\"\\nsequenceDiagram\\n participant M as Main\\n participant G as Game\\n M->>G: start_game()\\n M->>G: move_snake(direction)\\n G->>G: generate_food()\\n G->>G: update_score(points)\\n\",\"Anything UNCLEAR\":\"Please provide more details on the game mechanics and user interactions.\"}\n\n## Tasks\n{\"Required Python packages\":[\"curses==2.2.0\"],\"Required Other language third-party packages\":[\"No third-party dependencies required\"],\"Logic Analysis\":[[\"game.py\",\"Contains Game class and ... functions\"],[\"main.py\",\"Contains main function, from game import Game\"]],\"Task list\":[\"game.py\",\"main.py\"],\"Full API spec\":\"\",\"Shared Knowledge\":\"'game.py' contains functions shared across the project.\",\"Anything UNCLEAR\":\"Please provide more details on the game mechanics and user interactions.\"}\n\n## Code Files\n\n\n\n## Code to be Reviewed: game.py\n```Code\n## game.py\n\nclass Snake:\n def __init__(self, x: int, y: int):\n self.body = [(x, y)]\n self.direction = 'right'\n\n def move(self, direction: str):\n if direction == 'up' and self.direction != 'down':\n self.direction = 'up'\n elif direction == 'down' and self.direction != 'up':\n self.direction = 'down'\n elif direction == 'left' and self.direction != 'right':\n self.direction = 'left'\n elif direction == 'right' and self.direction != 'left':\n self.direction = 'right'\n\n head = self.body[0]\n x, y = head\n if self.direction == 'up':\n new_head = (x, y - 1)\n elif self.direction == 'down':\n new_head = (x, y + 1)\n elif self.direction == 'left':\n new_head = (x - 1, y)\n elif self.direction == 'right':\n new_head = (x + 1, y)\n self.body.insert(0, new_head)\n\n def grow(self):\n tail = self.body[-1]\n x, y = tail\n if self.direction == 'up':\n new_tail = (x, y + 1)\n elif self.direction == 'down':\n new_tail = (x, y - 1)\n elif self.direction == 'left':\n new_tail = (x + 1, y)\n elif self.direction == 'right':\n new_tail = (x - 1, y)\n self.body.append(new_tail)\n\n def collides_with_self(self) -> bool:\n return len(self.body) != len(set(self.body))\n\nclass Food:\n def __init__(self, x: int, y: int):\n self.position = (x, y)\n\n def get_position(self) -> tuple:\n return self.position\n\nclass Score:\n def __init__(self):\n self.points = 0\n\n def increase(self, points: int):\n self.points += points\n\nclass Game:\n def __init__(self, width: int, height: int):\n self.snake = Snake(width // 2, height // 2)\n self.food = None\n self.score = Score()\n self.width = width\n self.height = height\n\n def start_game(self):\n self.generate_food()\n\n def move_snake(self, direction: str):\n self.snake.move(direction)\n if self.check_collision():\n return False\n self.check_eat()\n return True\n\n def generate_food(self):\n import random\n x = random.randint(0, self.width - 1)\n y = random.randint(0, self.height - 1)\n self.food = Food(x, y)\n\n def update_score(self, points: int):\n self.score.increase(points)\n\n def check_collision(self) -> bool:\n head = self.snake.body[0]\n if head[0] < 0 or head[0] >= self.width or head[1] < 0 or head[1] >= self.height:\n return True\n if self.snake.collides_with_self():\n return True\n return False\n\n def check_eat(self):\n if self.snake.body[0] == self.food.get_position():\n self.snake.grow()\n self.generate_food()\n self.update_score(10)\n\n```\n\n\n\n# Format example 1\n## Code Review: game.py\n1. No, we should fix the logic of class A due to ...\n2. ...\n3. ...\n4. No, function B is not implemented, ...\n5. ...\n6. ...\n\n## Actions\n1. Fix the `handle_events` method to update the game state only if a move is successful.\n ```python\n def handle_events(self):\n for event in pygame.event.get():\n if event.type == pygame.QUIT:\n return False\n if event.type == pygame.KEYDOWN:\n moved = False\n if event.key == pygame.K_UP:\n moved = self.game.move('UP')\n elif event.key == pygame.K_DOWN:\n moved = self.game.move('DOWN')\n elif event.key == pygame.K_LEFT:\n moved = self.game.move('LEFT')\n elif event.key == pygame.K_RIGHT:\n moved = self.game.move('RIGHT')\n if moved:\n # Update the game state only if a move was successful\n self.render()\n return True\n ```\n2. Implement function B\n\n## Code Review Result\nLBTM\n\n# Format example 2\n## Code Review: game.py\n1. Yes.\n2. Yes.\n3. Yes.\n4. Yes.\n5. Yes.\n6. Yes.\n\n## Actions\npass\n\n## Code Review Result\nLGTM\n\n\n\n# Instruction: Based on the actual code situation, follow one of the \"Format example\". Return only 1 file under review.\n\n## Code Review: Ordered List. Based on the \"Code to be Reviewed\", provide key, clear, concise, and specific answer. If any answer is no, explain how to fix it step by step.\n1. Is the code implemented as per the requirements? If not, how to achieve it? Analyse it step by step.\n2. Is the code logic completely correct? If there are errors, please indicate how to correct them.\n3. Does the existing code follow the \"Data structures and interfaces\"?\n4. Are all functions implemented? If there is no implementation, please indicate how to achieve it step by step.\n5. Have all necessary pre-dependencies been imported? If not, indicate which ones need to be imported\n6. Are methods from other files being reused correctly?\n\n## Actions: Ordered List. Things that should be done after CR, such as implementing class A and function B\n\n## Code Review Result: str. If the code doesn't have bugs, we don't need to rewrite it, so answer LGTM and stop. ONLY ANSWER LGTM/LBTM.\nLGTM/LBTM\n\n": "## Code Review: game.py\n1. Yes, the code is implemented as per the requirements. The Snake, Food, and Score classes are correctly implemented, and the Game class handles the game logic as specified.\n2. Yes, the code logic is correct. The movement, growth, collision detection, and scoring mechanisms are implemented accurately.\n3. Yes, the existing code follows the \"Data structures and interfaces\" as specified in the class diagram.\n4. Yes, all functions are implemented as required.\n5. No, the necessary pre-dependencies have not been imported. The `curses` package needs to be imported at the beginning of the file using `import curses`.\n6. Yes, methods from other files are being reused correctly.\n\n## Actions\n1. Import the necessary pre-dependency `curses` at the beginning of the file using `import curses`.\n\n## Code Review Result\nLBTM", + "\n# System\nRole: You are a professional software engineer, and your main task is to review and revise the code. You need to ensure that the code conforms to the google-style standards, is elegantly designed and modularized, easy to read and maintain.\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## System Design\n{\"Implementation approach\":\"We will use Python and the curses library to create the snake game. The game logic will be implemented in a separate module, and the main.py file will handle the user interface and game loop.\",\"File list\":[\"main.py\",\"game.py\"],\"Data structures and interfaces\":\"\\nclassDiagram\\n class Game {\\n -Snake snake\\n -Food food\\n -Score score\\n +__init__(width: int, height: int)\\n +start_game()\\n +move_snake(direction: str)\\n +generate_food()\\n +update_score(points: int)\\n }\\n class Snake {\\n -body list\\n -direction str\\n +__init__(x: int, y: int)\\n +move(direction: str)\\n +grow()\\n +collides_with_self() bool\\n }\\n class Food {\\n -position tuple\\n +__init__(x: int, y: int)\\n +get_position() tuple\\n }\\n class Score {\\n -points int\\n +__init__()\\n +increase(points: int)\\n }\\n Game --> Snake\\n Game --> Food\\n Game --> Score\\n\",\"Program call flow\":\"\\nsequenceDiagram\\n participant M as Main\\n participant G as Game\\n M->>G: start_game()\\n M->>G: move_snake(direction)\\n G->>G: generate_food()\\n G->>G: update_score(points)\\n\",\"Anything UNCLEAR\":\"Please provide more details on the game mechanics and user interactions.\"}\n\n## Tasks\n{\"Required Python packages\":[\"curses==2.2.0\"],\"Required Other language third-party packages\":[\"No third-party dependencies required\"],\"Logic Analysis\":[[\"game.py\",\"Contains Game class and ... functions\"],[\"main.py\",\"Contains main function, from game import Game\"]],\"Task list\":[\"game.py\",\"main.py\"],\"Full API spec\":\"\",\"Shared Knowledge\":\"'game.py' contains functions shared across the project.\",\"Anything UNCLEAR\":\"Please provide more details on the game mechanics and user interactions.\"}\n\n## Code Files\n\n\n\n## Code to be Reviewed: game.py\n```Code\n## game.py\n\nclass Snake:\n def __init__(self, x: int, y: int):\n self.body = [(x, y)]\n self.direction = 'right'\n\n def move(self, direction: str):\n if direction == 'up' and self.direction != 'down':\n self.direction = 'up'\n elif direction == 'down' and self.direction != 'up':\n self.direction = 'down'\n elif direction == 'left' and self.direction != 'right':\n self.direction = 'left'\n elif direction == 'right' and self.direction != 'left':\n self.direction = 'right'\n\n head = self.body[0]\n x, y = head\n if self.direction == 'up':\n new_head = (x, y - 1)\n elif self.direction == 'down':\n new_head = (x, y + 1)\n elif self.direction == 'left':\n new_head = (x - 1, y)\n elif self.direction == 'right':\n new_head = (x + 1, y)\n self.body.insert(0, new_head)\n\n def grow(self):\n tail = self.body[-1]\n x, y = tail\n if self.direction == 'up':\n new_tail = (x, y + 1)\n elif self.direction == 'down':\n new_tail = (x, y - 1)\n elif self.direction == 'left':\n new_tail = (x + 1, y)\n elif self.direction == 'right':\n new_tail = (x - 1, y)\n self.body.append(new_tail)\n\n def collides_with_self(self) -> bool:\n return len(self.body) != len(set(self.body))\n\nclass Food:\n def __init__(self, x: int, y: int):\n self.position = (x, y)\n\n def get_position(self) -> tuple:\n return self.position\n\nclass Score:\n def __init__(self):\n self.points = 0\n\n def increase(self, points: int):\n self.points += points\n\nclass Game:\n def __init__(self, width: int, height: int):\n self.snake = Snake(width // 2, height // 2)\n self.food = None\n self.score = Score()\n self.width = width\n self.height = height\n\n def start_game(self):\n self.generate_food()\n\n def move_snake(self, direction: str):\n self.snake.move(direction)\n if self.check_collision():\n return False\n self.check_eat()\n return True\n\n def generate_food(self):\n import random\n x = random.randint(0, self.width - 1)\n y = random.randint(0, self.height - 1)\n self.food = Food(x, y)\n\n def update_score(self, points: int):\n self.score.increase(points)\n\n def check_collision(self) -> bool:\n head = self.snake.body[0]\n if head[0] < 0 or head[0] >= self.width or head[1] < 0 or head[1] >= self.height:\n return True\n if self.snake.collides_with_self():\n return True\n return False\n\n def check_eat(self):\n if self.snake.body[0] == self.food.get_position():\n self.snake.grow()\n self.generate_food()\n self.update_score(10)\n\n```\n\n## Code Review: game.py\n1. Yes, the code is implemented as per the requirements. The Snake, Food, and Score classes are correctly implemented, and the Game class handles the game logic as specified.\n2. Yes, the code logic is correct. The movement, growth, collision detection, and scoring mechanisms are implemented accurately.\n3. Yes, the existing code follows the \"Data structures and interfaces\" as specified in the class diagram.\n4. Yes, all functions are implemented as required.\n5. No, the necessary pre-dependencies have not been imported. The `curses` package needs to be imported at the beginning of the file using `import curses`.\n6. Yes, methods from other files are being reused correctly.\n\n## Actions\n1. Import the necessary pre-dependency `curses` at the beginning of the file using `import curses`.\n\n## Code Review Result\nLBTM\n\n# Instruction: rewrite code based on the Code Review and Actions\n## Rewrite Code: CodeBlock. If it still has some bugs, rewrite game.py with triple quotes. Do your utmost to optimize THIS SINGLE FILE. Return all completed codes and prohibit the return of unfinished codes.\n```Code\n## game.py\n...\n```\n": "```Code\n## game.py\n\nimport curses\n\nclass Snake:\n def __init__(self, x: int, y: int):\n self.body = [(x, y)]\n self.direction = 'right'\n\n def move(self, direction: str):\n if direction == 'up' and self.direction != 'down':\n self.direction = 'up'\n elif direction == 'down' and self.direction != 'up':\n self.direction = 'down'\n elif direction == 'left' and self.direction != 'right':\n self.direction = 'left'\n elif direction == 'right' and self.direction != 'left':\n self.direction = 'right'\n\n head = self.body[0]\n x, y = head\n if self.direction == 'up':\n new_head = (x, y - 1)\n elif self.direction == 'down':\n new_head = (x, y + 1)\n elif self.direction == 'left':\n new_head = (x - 1, y)\n elif self.direction == 'right':\n new_head = (x + 1, y)\n self.body.insert(0, new_head)\n\n def grow(self):\n tail = self.body[-1]\n x, y = tail\n if self.direction == 'up':\n new_tail = (x, y + 1)\n elif self.direction == 'down':\n new_tail = (x, y - 1)\n elif self.direction == 'left':\n new_tail = (x + 1, y)\n elif self.direction == 'right':\n new_tail = (x - 1, y)\n self.body.append(new_tail)\n\n def collides_with_self(self) -> bool:\n return len(self.body) != len(set(self.body))\n\nclass Food:\n def __init__(self, x: int, y: int):\n self.position = (x, y)\n\n def get_position(self) -> tuple:\n return self.position\n\nclass Score:\n def __init__(self):\n self.points = 0\n\n def increase(self, points: int):\n self.points += points\n\nclass Game:\n def __init__(self, width: int, height: int):\n self.snake = Snake(width // 2, height // 2)\n self.food = None\n self.score = Score()\n self.width = width\n self.height = height\n\n def start_game(self):\n self.generate_food()\n\n def move_snake(self, direction: str):\n self.snake.move(direction)\n if self.check_collision():\n return False\n self.check_eat()\n return True\n\n def generate_food(self):\n import random\n x = random.randint(0, self.width - 1)\n y = random.randint(0, self.height - 1)\n self.food = Food(x, y)\n\n def update_score(self, points: int):\n self.score.increase(points)\n\n def check_collision(self) -> bool:\n head = self.snake.body[0]\n if head[0] < 0 or head[0] >= self.width or head[1] < 0 or head[1] >= self.height:\n return True\n if self.snake.collides_with_self():\n return True\n return False\n\n def check_eat(self):\n if self.snake.body[0] == self.food.get_position():\n self.snake.grow()\n self.generate_food()\n self.update_score(10)\n\n```", + "\n# System\nRole: You are a professional software engineer, and your main task is to review and revise the code. You need to ensure that the code conforms to the google-style standards, is elegantly designed and modularized, easy to read and maintain.\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## System Design\n{\"Implementation approach\":\"We will use Python and the curses library to create the snake game. The game logic will be implemented in a separate module, and the main.py file will handle the user interface and game loop.\",\"File list\":[\"main.py\",\"game.py\"],\"Data structures and interfaces\":\"\\nclassDiagram\\n class Game {\\n -Snake snake\\n -Food food\\n -Score score\\n +__init__(width: int, height: int)\\n +start_game()\\n +move_snake(direction: str)\\n +generate_food()\\n +update_score(points: int)\\n }\\n class Snake {\\n -body list\\n -direction str\\n +__init__(x: int, y: int)\\n +move(direction: str)\\n +grow()\\n +collides_with_self() bool\\n }\\n class Food {\\n -position tuple\\n +__init__(x: int, y: int)\\n +get_position() tuple\\n }\\n class Score {\\n -points int\\n +__init__()\\n +increase(points: int)\\n }\\n Game --> Snake\\n Game --> Food\\n Game --> Score\\n\",\"Program call flow\":\"\\nsequenceDiagram\\n participant M as Main\\n participant G as Game\\n M->>G: start_game()\\n M->>G: move_snake(direction)\\n G->>G: generate_food()\\n G->>G: update_score(points)\\n\",\"Anything UNCLEAR\":\"Please provide more details on the game mechanics and user interactions.\"}\n\n## Tasks\n{\"Required Python packages\":[\"curses==2.2.0\"],\"Required Other language third-party packages\":[\"No third-party dependencies required\"],\"Logic Analysis\":[[\"game.py\",\"Contains Game class and ... functions\"],[\"main.py\",\"Contains main function, from game import Game\"]],\"Task list\":[\"game.py\",\"main.py\"],\"Full API spec\":\"\",\"Shared Knowledge\":\"'game.py' contains functions shared across the project.\",\"Anything UNCLEAR\":\"Please provide more details on the game mechanics and user interactions.\"}\n\n## Code Files\n\n\n\n## Code to be Reviewed: game.py\n```Code\n## game.py\n\nimport curses\n\nclass Snake:\n def __init__(self, x: int, y: int):\n self.body = [(x, y)]\n self.direction = 'right'\n\n def move(self, direction: str):\n if direction == 'up' and self.direction != 'down':\n self.direction = 'up'\n elif direction == 'down' and self.direction != 'up':\n self.direction = 'down'\n elif direction == 'left' and self.direction != 'right':\n self.direction = 'left'\n elif direction == 'right' and self.direction != 'left':\n self.direction = 'right'\n\n head = self.body[0]\n x, y = head\n if self.direction == 'up':\n new_head = (x, y - 1)\n elif self.direction == 'down':\n new_head = (x, y + 1)\n elif self.direction == 'left':\n new_head = (x - 1, y)\n elif self.direction == 'right':\n new_head = (x + 1, y)\n self.body.insert(0, new_head)\n\n def grow(self):\n tail = self.body[-1]\n x, y = tail\n if self.direction == 'up':\n new_tail = (x, y + 1)\n elif self.direction == 'down':\n new_tail = (x, y - 1)\n elif self.direction == 'left':\n new_tail = (x + 1, y)\n elif self.direction == 'right':\n new_tail = (x - 1, y)\n self.body.append(new_tail)\n\n def collides_with_self(self) -> bool:\n return len(self.body) != len(set(self.body))\n\nclass Food:\n def __init__(self, x: int, y: int):\n self.position = (x, y)\n\n def get_position(self) -> tuple:\n return self.position\n\nclass Score:\n def __init__(self):\n self.points = 0\n\n def increase(self, points: int):\n self.points += points\n\nclass Game:\n def __init__(self, width: int, height: int):\n self.snake = Snake(width // 2, height // 2)\n self.food = None\n self.score = Score()\n self.width = width\n self.height = height\n\n def start_game(self):\n self.generate_food()\n\n def move_snake(self, direction: str):\n self.snake.move(direction)\n if self.check_collision():\n return False\n self.check_eat()\n return True\n\n def generate_food(self):\n import random\n x = random.randint(0, self.width - 1)\n y = random.randint(0, self.height - 1)\n self.food = Food(x, y)\n\n def update_score(self, points: int):\n self.score.increase(points)\n\n def check_collision(self) -> bool:\n head = self.snake.body[0]\n if head[0] < 0 or head[0] >= self.width or head[1] < 0 or head[1] >= self.height:\n return True\n if self.snake.collides_with_self():\n return True\n return False\n\n def check_eat(self):\n if self.snake.body[0] == self.food.get_position():\n self.snake.grow()\n self.generate_food()\n self.update_score(10)\n\n\n```\n\n\n\n# Format example 1\n## Code Review: game.py\n1. No, we should fix the logic of class A due to ...\n2. ...\n3. ...\n4. No, function B is not implemented, ...\n5. ...\n6. ...\n\n## Actions\n1. Fix the `handle_events` method to update the game state only if a move is successful.\n ```python\n def handle_events(self):\n for event in pygame.event.get():\n if event.type == pygame.QUIT:\n return False\n if event.type == pygame.KEYDOWN:\n moved = False\n if event.key == pygame.K_UP:\n moved = self.game.move('UP')\n elif event.key == pygame.K_DOWN:\n moved = self.game.move('DOWN')\n elif event.key == pygame.K_LEFT:\n moved = self.game.move('LEFT')\n elif event.key == pygame.K_RIGHT:\n moved = self.game.move('RIGHT')\n if moved:\n # Update the game state only if a move was successful\n self.render()\n return True\n ```\n2. Implement function B\n\n## Code Review Result\nLBTM\n\n# Format example 2\n## Code Review: game.py\n1. Yes.\n2. Yes.\n3. Yes.\n4. Yes.\n5. Yes.\n6. Yes.\n\n## Actions\npass\n\n## Code Review Result\nLGTM\n\n\n\n# Instruction: Based on the actual code situation, follow one of the \"Format example\". Return only 1 file under review.\n\n## Code Review: Ordered List. Based on the \"Code to be Reviewed\", provide key, clear, concise, and specific answer. If any answer is no, explain how to fix it step by step.\n1. Is the code implemented as per the requirements? If not, how to achieve it? Analyse it step by step.\n2. Is the code logic completely correct? If there are errors, please indicate how to correct them.\n3. Does the existing code follow the \"Data structures and interfaces\"?\n4. Are all functions implemented? If there is no implementation, please indicate how to achieve it step by step.\n5. Have all necessary pre-dependencies been imported? If not, indicate which ones need to be imported\n6. Are methods from other files being reused correctly?\n\n## Actions: Ordered List. Things that should be done after CR, such as implementing class A and function B\n\n## Code Review Result: str. If the code doesn't have bugs, we don't need to rewrite it, so answer LGTM and stop. ONLY ANSWER LGTM/LBTM.\nLGTM/LBTM\n\n": "## Code Review: game.py\n1. Yes, the code is implemented as per the requirements.\n2. Yes, the code logic is completely correct.\n3. Yes, the existing code follows the \"Data structures and interfaces\".\n4. Yes, all functions are implemented.\n5. Yes, all necessary pre-dependencies have been imported.\n6. Yes, methods from other files are being reused correctly.\n\n## Code Review Result\nLGTM", + "\nNOTICE\nRole: You are a professional engineer; the main goal is to write google-style, elegant, modular, easy to read and maintain code\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## Design\n{\"Implementation approach\":\"We will use Python and the curses library to create the snake game. The game logic will be implemented in a separate module, and the main.py file will handle the user interface and game loop.\",\"File list\":[\"main.py\",\"game.py\"],\"Data structures and interfaces\":\"\\nclassDiagram\\n class Game {\\n -Snake snake\\n -Food food\\n -Score score\\n +__init__(width: int, height: int)\\n +start_game()\\n +move_snake(direction: str)\\n +generate_food()\\n +update_score(points: int)\\n }\\n class Snake {\\n -body list\\n -direction str\\n +__init__(x: int, y: int)\\n +move(direction: str)\\n +grow()\\n +collides_with_self() bool\\n }\\n class Food {\\n -position tuple\\n +__init__(x: int, y: int)\\n +get_position() tuple\\n }\\n class Score {\\n -points int\\n +__init__()\\n +increase(points: int)\\n }\\n Game --> Snake\\n Game --> Food\\n Game --> Score\\n\",\"Program call flow\":\"\\nsequenceDiagram\\n participant M as Main\\n participant G as Game\\n M->>G: start_game()\\n M->>G: move_snake(direction)\\n G->>G: generate_food()\\n G->>G: update_score(points)\\n\",\"Anything UNCLEAR\":\"Please provide more details on the game mechanics and user interactions.\"}\n\n## Tasks\n{\"Required Python packages\":[\"curses==2.2.0\"],\"Required Other language third-party packages\":[\"No third-party dependencies required\"],\"Logic Analysis\":[[\"game.py\",\"Contains Game class and ... functions\"],[\"main.py\",\"Contains main function, from game import Game\"]],\"Task list\":[\"game.py\",\"main.py\"],\"Full API spec\":\"\",\"Shared Knowledge\":\"'game.py' contains functions shared across the project.\",\"Anything UNCLEAR\":\"Please provide more details on the game mechanics and user interactions.\"}\n\n## Legacy Code\n```Code\n----- game.py\n## game.py\n\nimport curses\n\nclass Snake:\n def __init__(self, x: int, y: int):\n self.body = [(x, y)]\n self.direction = 'right'\n\n def move(self, direction: str):\n if direction == 'up' and self.direction != 'down':\n self.direction = 'up'\n elif direction == 'down' and self.direction != 'up':\n self.direction = 'down'\n elif direction == 'left' and self.direction != 'right':\n self.direction = 'left'\n elif direction == 'right' and self.direction != 'left':\n self.direction = 'right'\n\n head = self.body[0]\n x, y = head\n if self.direction == 'up':\n new_head = (x, y - 1)\n elif self.direction == 'down':\n new_head = (x, y + 1)\n elif self.direction == 'left':\n new_head = (x - 1, y)\n elif self.direction == 'right':\n new_head = (x + 1, y)\n self.body.insert(0, new_head)\n\n def grow(self):\n tail = self.body[-1]\n x, y = tail\n if self.direction == 'up':\n new_tail = (x, y + 1)\n elif self.direction == 'down':\n new_tail = (x, y - 1)\n elif self.direction == 'left':\n new_tail = (x + 1, y)\n elif self.direction == 'right':\n new_tail = (x - 1, y)\n self.body.append(new_tail)\n\n def collides_with_self(self) -> bool:\n return len(self.body) != len(set(self.body))\n\nclass Food:\n def __init__(self, x: int, y: int):\n self.position = (x, y)\n\n def get_position(self) -> tuple:\n return self.position\n\nclass Score:\n def __init__(self):\n self.points = 0\n\n def increase(self, points: int):\n self.points += points\n\nclass Game:\n def __init__(self, width: int, height: int):\n self.snake = Snake(width // 2, height // 2)\n self.food = None\n self.score = Score()\n self.width = width\n self.height = height\n\n def start_game(self):\n self.generate_food()\n\n def move_snake(self, direction: str):\n self.snake.move(direction)\n if self.check_collision():\n return False\n self.check_eat()\n return True\n\n def generate_food(self):\n import random\n x = random.randint(0, self.width - 1)\n y = random.randint(0, self.height - 1)\n self.food = Food(x, y)\n\n def update_score(self, points: int):\n self.score.increase(points)\n\n def check_collision(self) -> bool:\n head = self.snake.body[0]\n if head[0] < 0 or head[0] >= self.width or head[1] < 0 or head[1] >= self.height:\n return True\n if self.snake.collides_with_self():\n return True\n return False\n\n def check_eat(self):\n if self.snake.body[0] == self.food.get_position():\n self.snake.grow()\n self.generate_food()\n self.update_score(10)\n\n\n```\n\n## Debug logs\n```text\n\n\n\n```\n\n## Bug Feedback logs\n```text\n\n```\n\n# Format example\n## Code: main.py\n```python\n## main.py\n...\n```\n\n# Instruction: Based on the context, follow \"Format example\", write code.\n\n## Code: main.py. Write code with triple quoto, based on the following attentions and context.\n1. Only One file: do your best to implement THIS ONLY ONE FILE.\n2. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets.\n3. Set default value: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. AVOID circular import.\n4. Follow design: YOU MUST FOLLOW \"Data structures and interfaces\". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design.\n5. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE.\n6. Before using a external variable/module, make sure you import it first.\n7. Write out EVERY CODE DETAIL, DON'T LEAVE TODO.\n\n": "```python\n## main.py\n\nfrom game import Game\n\ndef main():\n width = 20 # Set default width\n height = 20 # Set default height\n game = Game(width, height)\n game.start_game()\n\n while True:\n # Get user input for direction\n direction = input(\"Enter direction (up/down/left/right): \")\n if direction in ['up', 'down', 'left', 'right']:\n if not game.move_snake(direction):\n print(\"Game over!\")\n break\n else:\n print(\"Invalid direction!\")\n\nif __name__ == \"__main__\":\n main()\n```", + "\n# System\nRole: You are a professional software engineer, and your main task is to review and revise the code. You need to ensure that the code conforms to the google-style standards, is elegantly designed and modularized, easy to read and maintain.\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## System Design\n{\"Implementation approach\":\"We will use Python and the curses library to create the snake game. The game logic will be implemented in a separate module, and the main.py file will handle the user interface and game loop.\",\"File list\":[\"main.py\",\"game.py\"],\"Data structures and interfaces\":\"\\nclassDiagram\\n class Game {\\n -Snake snake\\n -Food food\\n -Score score\\n +__init__(width: int, height: int)\\n +start_game()\\n +move_snake(direction: str)\\n +generate_food()\\n +update_score(points: int)\\n }\\n class Snake {\\n -body list\\n -direction str\\n +__init__(x: int, y: int)\\n +move(direction: str)\\n +grow()\\n +collides_with_self() bool\\n }\\n class Food {\\n -position tuple\\n +__init__(x: int, y: int)\\n +get_position() tuple\\n }\\n class Score {\\n -points int\\n +__init__()\\n +increase(points: int)\\n }\\n Game --> Snake\\n Game --> Food\\n Game --> Score\\n\",\"Program call flow\":\"\\nsequenceDiagram\\n participant M as Main\\n participant G as Game\\n M->>G: start_game()\\n M->>G: move_snake(direction)\\n G->>G: generate_food()\\n G->>G: update_score(points)\\n\",\"Anything UNCLEAR\":\"Please provide more details on the game mechanics and user interactions.\"}\n\n## Tasks\n{\"Required Python packages\":[\"curses==2.2.0\"],\"Required Other language third-party packages\":[\"No third-party dependencies required\"],\"Logic Analysis\":[[\"game.py\",\"Contains Game class and ... functions\"],[\"main.py\",\"Contains main function, from game import Game\"]],\"Task list\":[\"game.py\",\"main.py\"],\"Full API spec\":\"\",\"Shared Knowledge\":\"'game.py' contains functions shared across the project.\",\"Anything UNCLEAR\":\"Please provide more details on the game mechanics and user interactions.\"}\n\n## Code Files\n----- game.py\n## game.py\n\nimport curses\n\nclass Snake:\n def __init__(self, x: int, y: int):\n self.body = [(x, y)]\n self.direction = 'right'\n\n def move(self, direction: str):\n if direction == 'up' and self.direction != 'down':\n self.direction = 'up'\n elif direction == 'down' and self.direction != 'up':\n self.direction = 'down'\n elif direction == 'left' and self.direction != 'right':\n self.direction = 'left'\n elif direction == 'right' and self.direction != 'left':\n self.direction = 'right'\n\n head = self.body[0]\n x, y = head\n if self.direction == 'up':\n new_head = (x, y - 1)\n elif self.direction == 'down':\n new_head = (x, y + 1)\n elif self.direction == 'left':\n new_head = (x - 1, y)\n elif self.direction == 'right':\n new_head = (x + 1, y)\n self.body.insert(0, new_head)\n\n def grow(self):\n tail = self.body[-1]\n x, y = tail\n if self.direction == 'up':\n new_tail = (x, y + 1)\n elif self.direction == 'down':\n new_tail = (x, y - 1)\n elif self.direction == 'left':\n new_tail = (x + 1, y)\n elif self.direction == 'right':\n new_tail = (x - 1, y)\n self.body.append(new_tail)\n\n def collides_with_self(self) -> bool:\n return len(self.body) != len(set(self.body))\n\nclass Food:\n def __init__(self, x: int, y: int):\n self.position = (x, y)\n\n def get_position(self) -> tuple:\n return self.position\n\nclass Score:\n def __init__(self):\n self.points = 0\n\n def increase(self, points: int):\n self.points += points\n\nclass Game:\n def __init__(self, width: int, height: int):\n self.snake = Snake(width // 2, height // 2)\n self.food = None\n self.score = Score()\n self.width = width\n self.height = height\n\n def start_game(self):\n self.generate_food()\n\n def move_snake(self, direction: str):\n self.snake.move(direction)\n if self.check_collision():\n return False\n self.check_eat()\n return True\n\n def generate_food(self):\n import random\n x = random.randint(0, self.width - 1)\n y = random.randint(0, self.height - 1)\n self.food = Food(x, y)\n\n def update_score(self, points: int):\n self.score.increase(points)\n\n def check_collision(self) -> bool:\n head = self.snake.body[0]\n if head[0] < 0 or head[0] >= self.width or head[1] < 0 or head[1] >= self.height:\n return True\n if self.snake.collides_with_self():\n return True\n return False\n\n def check_eat(self):\n if self.snake.body[0] == self.food.get_position():\n self.snake.grow()\n self.generate_food()\n self.update_score(10)\n\n\n\n\n## Code to be Reviewed: main.py\n```Code\n## main.py\n\nfrom game import Game\n\ndef main():\n width = 20 # Set default width\n height = 20 # Set default height\n game = Game(width, height)\n game.start_game()\n\n while True:\n # Get user input for direction\n direction = input(\"Enter direction (up/down/left/right): \")\n if direction in ['up', 'down', 'left', 'right']:\n if not game.move_snake(direction):\n print(\"Game over!\")\n break\n else:\n print(\"Invalid direction!\")\n\nif __name__ == \"__main__\":\n main()\n\n```\n\n\n\n# Format example 1\n## Code Review: main.py\n1. No, we should fix the logic of class A due to ...\n2. ...\n3. ...\n4. No, function B is not implemented, ...\n5. ...\n6. ...\n\n## Actions\n1. Fix the `handle_events` method to update the game state only if a move is successful.\n ```python\n def handle_events(self):\n for event in pygame.event.get():\n if event.type == pygame.QUIT:\n return False\n if event.type == pygame.KEYDOWN:\n moved = False\n if event.key == pygame.K_UP:\n moved = self.game.move('UP')\n elif event.key == pygame.K_DOWN:\n moved = self.game.move('DOWN')\n elif event.key == pygame.K_LEFT:\n moved = self.game.move('LEFT')\n elif event.key == pygame.K_RIGHT:\n moved = self.game.move('RIGHT')\n if moved:\n # Update the game state only if a move was successful\n self.render()\n return True\n ```\n2. Implement function B\n\n## Code Review Result\nLBTM\n\n# Format example 2\n## Code Review: main.py\n1. Yes.\n2. Yes.\n3. Yes.\n4. Yes.\n5. Yes.\n6. Yes.\n\n## Actions\npass\n\n## Code Review Result\nLGTM\n\n\n\n# Instruction: Based on the actual code situation, follow one of the \"Format example\". Return only 1 file under review.\n\n## Code Review: Ordered List. Based on the \"Code to be Reviewed\", provide key, clear, concise, and specific answer. If any answer is no, explain how to fix it step by step.\n1. Is the code implemented as per the requirements? If not, how to achieve it? Analyse it step by step.\n2. Is the code logic completely correct? If there are errors, please indicate how to correct them.\n3. Does the existing code follow the \"Data structures and interfaces\"?\n4. Are all functions implemented? If there is no implementation, please indicate how to achieve it step by step.\n5. Have all necessary pre-dependencies been imported? If not, indicate which ones need to be imported\n6. Are methods from other files being reused correctly?\n\n## Actions: Ordered List. Things that should be done after CR, such as implementing class A and function B\n\n## Code Review Result: str. If the code doesn't have bugs, we don't need to rewrite it, so answer LGTM and stop. ONLY ANSWER LGTM/LBTM.\nLGTM/LBTM\n\n": "## Code Review: main.py\n1. The code is implemented as per the requirements. The main function initializes the game and handles user input for the snake's direction.\n2. The code logic is correct. It correctly handles user input and updates the game state based on the snake's movement.\n3. The existing code follows the \"Data structures and interfaces\" as defined in the class diagram.\n4. All necessary functions are implemented in the code.\n5. The necessary pre-dependencies have been imported. The required Python package \"curses==2.2.0\" has been imported.\n6. The methods from the \"game.py\" file are being reused correctly.\n\n## Actions\npass\n\n## Code Review Result\nLGTM", + "\n## context\n\n### Project Name\n20240110212717\n\n### Original Requirements\n['开发一个基于大语言模型与私有知识库的搜索引擎,希望可以基于大语言模型进行搜索总结']\n\n### Search Information\n-\n\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Language\": \"en_us\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"Create a 2048 game\",\n \"Product Goals\": [\n \"Create an engaging user experience\",\n \"Improve accessibility, be responsive\",\n \"More beautiful UI\"\n ],\n \"User Stories\": [\n \"As a player, I want to be able to choose difficulty levels\",\n \"As a player, I want to see my score after each game\",\n \"As a player, I want to get restart button when I lose\",\n \"As a player, I want to see beautiful UI that make me feel good\",\n \"As a player, I want to play game via mobile phone\"\n ],\n \"Competitive Analysis\": [\n \"2048 Game A: Simple interface, lacks responsive features\",\n \"play2048.co: Beautiful and responsive UI with my best score shown\",\n \"2048game.com: Responsive UI with my best score shown, but many ads\"\n ],\n \"Competitive Quadrant Chart\": \"quadrantChart\\n title \\\"Reach and engagement of campaigns\\\"\\n x-axis \\\"Low Reach\\\" --> \\\"High Reach\\\"\\n y-axis \\\"Low Engagement\\\" --> \\\"High Engagement\\\"\\n quadrant-1 \\\"We should expand\\\"\\n quadrant-2 \\\"Need to promote\\\"\\n quadrant-3 \\\"Re-evaluate\\\"\\n quadrant-4 \\\"May be improved\\\"\\n \\\"Campaign A\\\": [0.3, 0.6]\\n \\\"Campaign B\\\": [0.45, 0.23]\\n \\\"Campaign C\\\": [0.57, 0.69]\\n \\\"Campaign D\\\": [0.78, 0.34]\\n \\\"Campaign E\\\": [0.40, 0.34]\\n \\\"Campaign F\\\": [0.35, 0.78]\\n \\\"Our Target Product\\\": [0.5, 0.6]\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [\n [\n \"P0\",\n \"The main code ...\"\n ],\n [\n \"P0\",\n \"The game algorithm ...\"\n ]\n ],\n \"UI Design draft\": \"Basic function description with a simple style and layout.\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Language: # Provide the language used in the project, typically matching the user's requirement language.\n- Programming Language: # Python/JavaScript or other mainstream programming language.\n- Original Requirements: # Place the original user's requirements here.\n- Product Goals: typing.List[str] # Provide up to three clear, orthogonal product goals.\n- User Stories: typing.List[str] # Provide up to 3 to 5 scenario-based user stories.\n- Competitive Analysis: typing.List[str] # Provide 5 to 7 competitive products.\n- Competitive Quadrant Chart: # Use mermaid quadrantChart syntax. Distribute scores evenly between 0 and 1\n- Requirement Analysis: # Provide a detailed analysis of the requirements.\n- Requirement Pool: typing.List[typing.List[str]] # List down the top-5 requirements with their priority (P0, P1, P2).\n- UI Design draft: # Provide a simple description of UI elements, functions, style, and layout.\n- Anything UNCLEAR: # Mention any aspects of the project that are unclear and try to clarify them.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Language\": \"zh_cn\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"开发一个基于大语言模型与私有知识库的搜索引擎,希望可以基于大语言模型进行搜索总结\",\n \"Product Goals\": [],\n \"User Stories\": [],\n \"Competitive Analysis\": [],\n \"Competitive Quadrant Chart\": \"\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [],\n \"UI Design draft\": \"\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]", + "\n## context\n\n### Project Name\n20240110212717\n\n### Original Requirements\n['']\n\n### Search Information\n-\n\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Language\": \"en_us\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"Create a 2048 game\",\n \"Product Goals\": [\n \"Create an engaging user experience\",\n \"Improve accessibility, be responsive\",\n \"More beautiful UI\"\n ],\n \"User Stories\": [\n \"As a player, I want to be able to choose difficulty levels\",\n \"As a player, I want to see my score after each game\",\n \"As a player, I want to get restart button when I lose\",\n \"As a player, I want to see beautiful UI that make me feel good\",\n \"As a player, I want to play game via mobile phone\"\n ],\n \"Competitive Analysis\": [\n \"2048 Game A: Simple interface, lacks responsive features\",\n \"play2048.co: Beautiful and responsive UI with my best score shown\",\n \"2048game.com: Responsive UI with my best score shown, but many ads\"\n ],\n \"Competitive Quadrant Chart\": \"quadrantChart\\n title \\\"Reach and engagement of campaigns\\\"\\n x-axis \\\"Low Reach\\\" --> \\\"High Reach\\\"\\n y-axis \\\"Low Engagement\\\" --> \\\"High Engagement\\\"\\n quadrant-1 \\\"We should expand\\\"\\n quadrant-2 \\\"Need to promote\\\"\\n quadrant-3 \\\"Re-evaluate\\\"\\n quadrant-4 \\\"May be improved\\\"\\n \\\"Campaign A\\\": [0.3, 0.6]\\n \\\"Campaign B\\\": [0.45, 0.23]\\n \\\"Campaign C\\\": [0.57, 0.69]\\n \\\"Campaign D\\\": [0.78, 0.34]\\n \\\"Campaign E\\\": [0.40, 0.34]\\n \\\"Campaign F\\\": [0.35, 0.78]\\n \\\"Our Target Product\\\": [0.5, 0.6]\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [\n [\n \"P0\",\n \"The main code ...\"\n ],\n [\n \"P0\",\n \"The game algorithm ...\"\n ]\n ],\n \"UI Design draft\": \"Basic function description with a simple style and layout.\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Language: # Provide the language used in the project, typically matching the user's requirement language.\n- Programming Language: # Python/JavaScript or other mainstream programming language.\n- Original Requirements: # Place the original user's requirements here.\n- Product Goals: typing.List[str] # Provide up to three clear, orthogonal product goals.\n- User Stories: typing.List[str] # Provide up to 3 to 5 scenario-based user stories.\n- Competitive Analysis: typing.List[str] # Provide 5 to 7 competitive products.\n- Competitive Quadrant Chart: # Use mermaid quadrantChart syntax. Distribute scores evenly between 0 and 1\n- Requirement Analysis: # Provide a detailed analysis of the requirements.\n- Requirement Pool: typing.List[typing.List[str]] # List down the top-5 requirements with their priority (P0, P1, P2).\n- UI Design draft: # Provide a simple description of UI elements, functions, style, and layout.\n- Anything UNCLEAR: # Mention any aspects of the project that are unclear and try to clarify them.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Language\": \"en_us\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"\",\n \"Product Goals\": [],\n \"User Stories\": [],\n \"Competitive Analysis\": [],\n \"Competitive Quadrant Chart\": \"\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [],\n \"UI Design draft\": \"\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]", + "\n## context\n\n### Project Name\n20240110220803\n\n### Original Requirements\n['需要一个基于LLM做总结的搜索引擎']\n\n### Search Information\n-\n\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Language\": \"en_us\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"Create a 2048 game\",\n \"Product Goals\": [\n \"Create an engaging user experience\",\n \"Improve accessibility, be responsive\",\n \"More beautiful UI\"\n ],\n \"User Stories\": [\n \"As a player, I want to be able to choose difficulty levels\",\n \"As a player, I want to see my score after each game\",\n \"As a player, I want to get restart button when I lose\",\n \"As a player, I want to see beautiful UI that make me feel good\",\n \"As a player, I want to play game via mobile phone\"\n ],\n \"Competitive Analysis\": [\n \"2048 Game A: Simple interface, lacks responsive features\",\n \"play2048.co: Beautiful and responsive UI with my best score shown\",\n \"2048game.com: Responsive UI with my best score shown, but many ads\"\n ],\n \"Competitive Quadrant Chart\": \"quadrantChart\\n title \\\"Reach and engagement of campaigns\\\"\\n x-axis \\\"Low Reach\\\" --> \\\"High Reach\\\"\\n y-axis \\\"Low Engagement\\\" --> \\\"High Engagement\\\"\\n quadrant-1 \\\"We should expand\\\"\\n quadrant-2 \\\"Need to promote\\\"\\n quadrant-3 \\\"Re-evaluate\\\"\\n quadrant-4 \\\"May be improved\\\"\\n \\\"Campaign A\\\": [0.3, 0.6]\\n \\\"Campaign B\\\": [0.45, 0.23]\\n \\\"Campaign C\\\": [0.57, 0.69]\\n \\\"Campaign D\\\": [0.78, 0.34]\\n \\\"Campaign E\\\": [0.40, 0.34]\\n \\\"Campaign F\\\": [0.35, 0.78]\\n \\\"Our Target Product\\\": [0.5, 0.6]\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [\n [\n \"P0\",\n \"The main code ...\"\n ],\n [\n \"P0\",\n \"The game algorithm ...\"\n ]\n ],\n \"UI Design draft\": \"Basic function description with a simple style and layout.\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Language: # Provide the language used in the project, typically matching the user's requirement language.\n- Programming Language: # Python/JavaScript or other mainstream programming language.\n- Original Requirements: # Place the original user's requirements here.\n- Product Goals: typing.List[str] # Provide up to three clear, orthogonal product goals.\n- User Stories: typing.List[str] # Provide up to 3 to 5 scenario-based user stories.\n- Competitive Analysis: typing.List[str] # Provide 5 to 7 competitive products.\n- Competitive Quadrant Chart: # Use mermaid quadrantChart syntax. Distribute scores evenly between 0 and 1\n- Requirement Analysis: # Provide a detailed analysis of the requirements.\n- Requirement Pool: typing.List[typing.List[str]] # List down the top-5 requirements with their priority (P0, P1, P2).\n- UI Design draft: # Provide a simple description of UI elements, functions, style, and layout.\n- Anything UNCLEAR: # Mention any aspects of the project that are unclear and try to clarify them.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Language\": \"zh_cn\",\n \"Programming Language\": \"LLM\",\n \"Original Requirements\": \"需要一个基于LLM做总结的搜索引擎\",\n \"Product Goals\": [],\n \"User Stories\": [],\n \"Competitive Analysis\": [],\n \"Competitive Quadrant Chart\": \"\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [],\n \"UI Design draft\": \"\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]", + "You are a python code to Mermaid Sequence Diagram translator in function detail#SYSTEM_MSG_END#```python\n#!/usr/bin/env python\n# -*- coding: utf-8 -*-\nimport asyncio\nimport shutil\nfrom pathlib import Path\n\nimport typer\n\nfrom metagpt.config2 import config\nfrom metagpt.const import METAGPT_ROOT\n\napp = typer.Typer(add_completion=False, pretty_exceptions_show_locals=False)\n\n\ndef generate_repo(\n idea,\n investment,\n n_round,\n code_review,\n run_tests,\n implement,\n project_name,\n inc,\n project_path,\n reqa_file,\n max_auto_summarize_code,\n recover_path,\n):\n \"\"\"Run the startup logic. Can be called from CLI or other Python scripts.\"\"\"\n from metagpt.roles import (\n Architect,\n Engineer,\n ProductManager,\n ProjectManager,\n QaEngineer,\n )\n from metagpt.team import Team\n\n config.update_via_cli(project_path, project_name, inc, reqa_file, max_auto_summarize_code)\n\n if not recover_path:\n company = Team()\n company.hire(\n [\n ProductManager(),\n Architect(),\n ProjectManager(),\n ]\n )\n\n if implement or code_review:\n company.hire([Engineer(n_borg=5, use_code_review=code_review)])\n\n if run_tests:\n company.hire([QaEngineer()])\n else:\n stg_path = Path(recover_path)\n if not stg_path.exists() or not str(stg_path).endswith(\"team\"):\n raise FileNotFoundError(f\"{recover_path} not exists or not endswith `team`\")\n\n company = Team.deserialize(stg_path=stg_path)\n idea = company.idea\n\n company.invest(investment)\n company.run_project(idea)\n asyncio.run(company.run(n_round=n_round))\n\n\n@app.command(\"\", help=\"Start a new project.\")\ndef startup(\n idea: str = typer.Argument(None, help=\"Your innovative idea, such as 'Create a 2048 game.'\"),\n investment: float = typer.Option(default=3.0, help=\"Dollar amount to invest in the AI company.\"),\n n_round: int = typer.Option(default=5, help=\"Number of rounds for the simulation.\"),\n code_review: bool = typer.Option(default=True, help=\"Whether to use code review.\"),\n run_tests: bool = typer.Option(default=False, help=\"Whether to enable QA for adding & running tests.\"),\n implement: bool = typer.Option(default=True, help=\"Enable or disable code implementation.\"),\n project_name: str = typer.Option(default=\"\", help=\"Unique project name, such as 'game_2048'.\"),\n inc: bool = typer.Option(default=False, help=\"Incremental mode. Use it to coop with existing repo.\"),\n project_path: str = typer.Option(\n default=\"\",\n help=\"Specify the directory path of the old version project to fulfill the incremental requirements.\",\n ),\n reqa_file: str = typer.Option(\n default=\"\", help=\"Specify the source file name for rewriting the quality assurance code.\"\n ),\n max_auto_summarize_code: int = typer.Option(\n default=0,\n help=\"The maximum number of times the 'SummarizeCode' action is automatically invoked, with -1 indicating \"\n \"unlimited. This parameter is used for debugging the workflow.\",\n ),\n recover_path: str = typer.Option(default=None, help=\"recover the project from existing serialized storage\"),\n init_config: bool = typer.Option(default=False, help=\"Initialize the configuration file for MetaGPT.\"),\n):\n \"\"\"Run a startup. Be a boss.\"\"\"\n if init_config:\n copy_config_to()\n return\n\n if idea is None:\n typer.echo(\"Missing argument 'IDEA'. Run 'metagpt --help' for more information.\")\n raise typer.Exit()\n\n return generate_repo(\n idea,\n investment,\n n_round,\n code_review,\n run_tests,\n implement,\n project_name,\n inc,\n project_path,\n reqa_file,\n max_auto_summarize_code,\n recover_path,\n )\n\n\ndef copy_config_to(config_path=METAGPT_ROOT / \"config\" / \"config2.yaml\"):\n \"\"\"Initialize the configuration file for MetaGPT.\"\"\"\n target_path = Path.home() / \".metagpt\" / \"config2.yaml\"\n\n # 创建目标目录(如果不存在)\n target_path.parent.mkdir(parents=True, exist_ok=True)\n\n # 如果目标文件已经存在,则重命名为 .bak\n if target_path.exists():\n backup_path = target_path.with_suffix(\".bak\")\n target_path.rename(backup_path)\n print(f\"Existing configuration file backed up at {backup_path}\")\n\n # 复制文件\n shutil.copy(str(config_path), target_path)\n print(f\"Configuration file initialized at {target_path}\")\n\n\nif __name__ == \"__main__\":\n app()\n\n```\n\n---\nTranslate the code above into Mermaid Sequence Diagram.": "```mermaid\nsequenceDiagram\n participant app as app\n participant typer as typer\n participant generate_repo as generate_repo\n participant Team as company\n participant ProductManager as ProductManager\n participant Architect as Architect\n participant ProjectManager as ProjectManager\n participant Engineer as Engineer\n participant QaEngineer as QaEngineer\n\n app -> typer: startup()\n typer -> generate_repo: generate_repo()\n generate_repo -> config: config.update_via_cli()\n generate_repo -> company: company.hire([ProductManager, Architect, ProjectManager])\n generate_repo -> company: company.hire([Engineer])\n generate_repo -> company: company.hire([QaEngineer])\n generate_repo -> company: company.invest()\n generate_repo -> company: company.run_project()\n generate_repo -> company: asyncio.run(company.run())\n\n Note right of generate_repo: If recover_path is provided,
deserialize Team from recover_path\n```", + "You are a python code to Mermaid Sequence Diagram translator in function detail#SYSTEM_MSG_END#```python\n#!/usr/bin/env python\n\nfrom __future__ import annotations\n\nimport asyncio\nimport json\nfrom concurrent import futures\nfrom typing import Literal, overload\n\nfrom metagpt.config2 import config\n\ntry:\n from duckduckgo_search import DDGS\nexcept ImportError:\n raise ImportError(\n \"To use this module, you should have the `duckduckgo_search` Python package installed. \"\n \"You can install it by running the command: `pip install -e.[search-ddg]`\"\n )\n\n\nclass DDGAPIWrapper:\n \"\"\"Wrapper around duckduckgo_search API.\n\n To use this module, you should have the `duckduckgo_search` Python package installed.\n \"\"\"\n\n def __init__(\n self,\n *,\n loop: asyncio.AbstractEventLoop | None = None,\n executor: futures.Executor | None = None,\n ):\n kwargs = {}\n if config.proxy:\n kwargs[\"proxies\"] = config.proxy\n self.loop = loop\n self.executor = executor\n self.ddgs = DDGS(**kwargs)\n\n @overload\n def run(\n self,\n query: str,\n max_results: int = 8,\n as_string: Literal[True] = True,\n focus: list[str] | None = None,\n ) -> str:\n ...\n\n @overload\n def run(\n self,\n query: str,\n max_results: int = 8,\n as_string: Literal[False] = False,\n focus: list[str] | None = None,\n ) -> list[dict[str, str]]:\n ...\n\n async def run(\n self,\n query: str,\n max_results: int = 8,\n as_string: bool = True,\n ) -> str | list[dict]:\n \"\"\"Return the results of a Google search using the official Google API\n\n Args:\n query: The search query.\n max_results: The number of results to return.\n as_string: A boolean flag to determine the return type of the results. If True, the function will\n return a formatted string with the search results. If False, it will return a list of dictionaries\n containing detailed information about each search result.\n\n Returns:\n The results of the search.\n \"\"\"\n loop = self.loop or asyncio.get_event_loop()\n future = loop.run_in_executor(\n self.executor,\n self._search_from_ddgs,\n query,\n max_results,\n )\n search_results = await future\n\n # Return the list of search result URLs\n if as_string:\n return json.dumps(search_results, ensure_ascii=False)\n return search_results\n\n def _search_from_ddgs(self, query: str, max_results: int):\n return [\n {\"link\": i[\"href\"], \"snippet\": i[\"body\"], \"title\": i[\"title\"]}\n for (_, i) in zip(range(max_results), self.ddgs.text(query))\n ]\n\n\nif __name__ == \"__main__\":\n import fire\n\n fire.Fire(DDGAPIWrapper().run)\n\n```\n\n---\nTranslate the code above into Mermaid Sequence Diagram.": "```mermaid\nsequenceDiagram\n participant User\n participant DDGAPIWrapper\n participant asyncio\n participant futures\n participant DDGS\n participant config\n\n User->>DDGAPIWrapper: run(query, max_results, as_string)\n DDGAPIWrapper->>asyncio: get_event_loop()\n asyncio->>DDGAPIWrapper: loop\n alt config.proxy\n DDGAPIWrapper->>config: proxy\n end\n DDGAPIWrapper->>futures: Executor\n futures->>DDGAPIWrapper: executor\n DDGAPIWrapper->>DDGS: __init__(**kwargs)\n DDGAPIWrapper->>asyncio: run_in_executor(executor, _search_from_ddgs, query, max_results)\n asyncio->>DDGAPIWrapper: future\n DDGAPIWrapper->>DDGS: text(query)\n DDGS-->>DDGAPIWrapper: search results\n DDGAPIWrapper-->>asyncio: search_results\n asyncio-->>DDGAPIWrapper: await future\n DDGAPIWrapper-->>User: search results\n```", + "You are a python code to Mermaid Sequence Diagram translator in function detail#SYSTEM_MSG_END#```python\n#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\"\"\"\n@Time : 2023/5/23 18:27\n@Author : alexanderwu\n@File : search_engine_serpapi.py\n\"\"\"\nfrom typing import Any, Dict, Optional, Tuple\n\nimport aiohttp\nfrom pydantic import BaseModel, ConfigDict, Field, field_validator\n\nfrom metagpt.config2 import config\n\n\nclass SerpAPIWrapper(BaseModel):\n model_config = ConfigDict(arbitrary_types_allowed=True)\n\n search_engine: Any = None #: :meta private:\n params: dict = Field(\n default_factory=lambda: {\n \"engine\": \"google\",\n \"google_domain\": \"google.com\",\n \"gl\": \"us\",\n \"hl\": \"en\",\n }\n )\n # should add `validate_default=True` to check with default value\n serpapi_api_key: Optional[str] = Field(default=None, validate_default=True)\n aiosession: Optional[aiohttp.ClientSession] = None\n\n @field_validator(\"serpapi_api_key\", mode=\"before\")\n @classmethod\n def check_serpapi_api_key(cls, val: str):\n val = val or config.search[\"serpapi\"].api_key\n if not val:\n raise ValueError(\n \"To use, make sure you provide the serpapi_api_key when constructing an object. Alternatively, \"\n \"ensure that the environment variable SERPAPI_API_KEY is set with your API key. You can obtain \"\n \"an API key from https://serpapi.com/.\"\n )\n return val\n\n async def run(self, query, max_results: int = 8, as_string: bool = True, **kwargs: Any) -> str:\n \"\"\"Run query through SerpAPI and parse result async.\"\"\"\n result = await self.results(query, max_results)\n return self._process_response(result, as_string=as_string)\n\n async def results(self, query: str, max_results: int) -> dict:\n \"\"\"Use aiohttp to run query through SerpAPI and return the results async.\"\"\"\n\n def construct_url_and_params() -> Tuple[str, Dict[str, str]]:\n params = self.get_params(query)\n params[\"source\"] = \"python\"\n params[\"num\"] = max_results\n params[\"output\"] = \"json\"\n url = \"https://serpapi.com/search\"\n return url, params\n\n url, params = construct_url_and_params()\n if not self.aiosession:\n async with aiohttp.ClientSession() as session:\n async with session.get(url, params=params) as response:\n res = await response.json()\n else:\n async with self.aiosession.get(url, params=params) as response:\n res = await response.json()\n\n return res\n\n def get_params(self, query: str) -> Dict[str, str]:\n \"\"\"Get parameters for SerpAPI.\"\"\"\n _params = {\n \"api_key\": self.serpapi_api_key,\n \"q\": query,\n }\n params = {**self.params, **_params}\n return params\n\n @staticmethod\n def _process_response(res: dict, as_string: bool) -> str:\n \"\"\"Process response from SerpAPI.\"\"\"\n # logger.debug(res)\n focus = [\"title\", \"snippet\", \"link\"]\n get_focused = lambda x: {i: j for i, j in x.items() if i in focus}\n\n if \"error\" in res.keys():\n raise ValueError(f\"Got error from SerpAPI: {res['error']}\")\n if \"answer_box\" in res.keys() and \"answer\" in res[\"answer_box\"].keys():\n toret = res[\"answer_box\"][\"answer\"]\n elif \"answer_box\" in res.keys() and \"snippet\" in res[\"answer_box\"].keys():\n toret = res[\"answer_box\"][\"snippet\"]\n elif \"answer_box\" in res.keys() and \"snippet_highlighted_words\" in res[\"answer_box\"].keys():\n toret = res[\"answer_box\"][\"snippet_highlighted_words\"][0]\n elif \"sports_results\" in res.keys() and \"game_spotlight\" in res[\"sports_results\"].keys():\n toret = res[\"sports_results\"][\"game_spotlight\"]\n elif \"knowledge_graph\" in res.keys() and \"description\" in res[\"knowledge_graph\"].keys():\n toret = res[\"knowledge_graph\"][\"description\"]\n elif \"snippet\" in res[\"organic_results\"][0].keys():\n toret = res[\"organic_results\"][0][\"snippet\"]\n else:\n toret = \"No good search result found\"\n\n toret_l = []\n if \"answer_box\" in res.keys() and \"snippet\" in res[\"answer_box\"].keys():\n toret_l += [get_focused(res[\"answer_box\"])]\n if res.get(\"organic_results\"):\n toret_l += [get_focused(i) for i in res.get(\"organic_results\")]\n\n return str(toret) + \"\\n\" + str(toret_l) if as_string else toret_l\n\n\nif __name__ == \"__main__\":\n import fire\n\n fire.Fire(SerpAPIWrapper().run)\n\n```\n\n---\nTranslate the code above into Mermaid Sequence Diagram.": "```mermaid\nsequenceDiagram\n participant SerpAPIWrapper\n participant aiohttp\n participant session\n participant response\n participant fire\n\n Note over SerpAPIWrapper: Initialization\n SerpAPIWrapper->>SerpAPIWrapper: __init__\n\n Note over SerpAPIWrapper: Run query through SerpAPI\n SerpAPIWrapper->>SerpAPIWrapper: run(query, max_results, as_string, **kwargs)\n SerpAPIWrapper->>SerpAPIWrapper: results(query, max_results)\n SerpAPIWrapper->>SerpAPIWrapper: get_params(query)\n SerpAPIWrapper->>aiohttp: session.get(url, params)\n aiohttp->>session: get(url, params)\n session->>response: response.json()\n response-->>session: res\n session-->>aiohttp: res\n aiohttp-->>SerpAPIWrapper: res\n SerpAPIWrapper-->>SerpAPIWrapper: _process_response(result, as_string)\n\n Note over SerpAPIWrapper: Use aiohttp to run query through SerpAPI\n SerpAPIWrapper->>SerpAPIWrapper: results(query, max_results)\n SerpAPIWrapper->>SerpAPIWrapper: get_params(query)\n SerpAPIWrapper->>aiohttp: ClientSession()\n aiohttp->>session: get(url, params)\n session->>response: response.json()\n response-->>session: res\n session-->>aiohttp: res\n aiohttp-->>SerpAPIWrapper: res\n\n Note over SerpAPIWrapper: Get parameters for SerpAPI\n SerpAPIWrapper->>SerpAPIWrapper: get_params(query)\n\n Note over SerpAPIWrapper: Process response from SerpAPI\n SerpAPIWrapper->>SerpAPIWrapper: _process_response(res, as_string)\n\n Note over fire: Main function\n fire->>SerpAPIWrapper: run\n\n```", + "You are a python code to Mermaid Sequence Diagram translator in function detail#SYSTEM_MSG_END#```python\n#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\"\"\"\n@Time : 2023/5/23 18:27\n@Author : alexanderwu\n@File : search_engine_serpapi.py\n\"\"\"\nimport json\nfrom typing import Any, Dict, Optional, Tuple\n\nimport aiohttp\nfrom pydantic import BaseModel, ConfigDict, Field, field_validator\n\nfrom metagpt.config2 import config\n\n\nclass SerperWrapper(BaseModel):\n model_config = ConfigDict(arbitrary_types_allowed=True)\n\n search_engine: Any = None #: :meta private:\n payload: dict = Field(default_factory=lambda: {\"page\": 1, \"num\": 10})\n serper_api_key: Optional[str] = Field(default=None, validate_default=True)\n aiosession: Optional[aiohttp.ClientSession] = None\n\n @field_validator(\"serper_api_key\", mode=\"before\")\n @classmethod\n def check_serper_api_key(cls, val: str):\n val = val or config.search[\"serper\"].api_key\n if not val:\n raise ValueError(\n \"To use, make sure you provide the serper_api_key when constructing an object. Alternatively, \"\n \"ensure that the environment variable SERPER_API_KEY is set with your API key. You can obtain \"\n \"an API key from https://serper.dev/.\"\n )\n return val\n\n async def run(self, query: str, max_results: int = 8, as_string: bool = True, **kwargs: Any) -> str:\n \"\"\"Run query through Serper and parse result async.\"\"\"\n if isinstance(query, str):\n return self._process_response((await self.results([query], max_results))[0], as_string=as_string)\n else:\n results = [self._process_response(res, as_string) for res in await self.results(query, max_results)]\n return \"\\n\".join(results) if as_string else results\n\n async def results(self, queries: list[str], max_results: int = 8) -> dict:\n \"\"\"Use aiohttp to run query through Serper and return the results async.\"\"\"\n\n def construct_url_and_payload_and_headers() -> Tuple[str, Dict[str, str]]:\n payloads = self.get_payloads(queries, max_results)\n url = \"https://google.serper.dev/search\"\n headers = self.get_headers()\n return url, payloads, headers\n\n url, payloads, headers = construct_url_and_payload_and_headers()\n if not self.aiosession:\n async with aiohttp.ClientSession() as session:\n async with session.post(url, data=payloads, headers=headers) as response:\n res = await response.json()\n else:\n async with self.aiosession.get.post(url, data=payloads, headers=headers) as response:\n res = await response.json()\n\n return res\n\n def get_payloads(self, queries: list[str], max_results: int) -> Dict[str, str]:\n \"\"\"Get payloads for Serper.\"\"\"\n payloads = []\n for query in queries:\n _payload = {\n \"q\": query,\n \"num\": max_results,\n }\n payloads.append({**self.payload, **_payload})\n return json.dumps(payloads, sort_keys=True)\n\n def get_headers(self) -> Dict[str, str]:\n headers = {\"X-API-KEY\": self.serper_api_key, \"Content-Type\": \"application/json\"}\n return headers\n\n @staticmethod\n def _process_response(res: dict, as_string: bool = False) -> str:\n \"\"\"Process response from SerpAPI.\"\"\"\n # logger.debug(res)\n focus = [\"title\", \"snippet\", \"link\"]\n\n def get_focused(x):\n return {i: j for i, j in x.items() if i in focus}\n\n if \"error\" in res.keys():\n raise ValueError(f\"Got error from SerpAPI: {res['error']}\")\n if \"answer_box\" in res.keys() and \"answer\" in res[\"answer_box\"].keys():\n toret = res[\"answer_box\"][\"answer\"]\n elif \"answer_box\" in res.keys() and \"snippet\" in res[\"answer_box\"].keys():\n toret = res[\"answer_box\"][\"snippet\"]\n elif \"answer_box\" in res.keys() and \"snippet_highlighted_words\" in res[\"answer_box\"].keys():\n toret = res[\"answer_box\"][\"snippet_highlighted_words\"][0]\n elif \"sports_results\" in res.keys() and \"game_spotlight\" in res[\"sports_results\"].keys():\n toret = res[\"sports_results\"][\"game_spotlight\"]\n elif \"knowledge_graph\" in res.keys() and \"description\" in res[\"knowledge_graph\"].keys():\n toret = res[\"knowledge_graph\"][\"description\"]\n elif \"snippet\" in res[\"organic\"][0].keys():\n toret = res[\"organic\"][0][\"snippet\"]\n else:\n toret = \"No good search result found\"\n\n toret_l = []\n if \"answer_box\" in res.keys() and \"snippet\" in res[\"answer_box\"].keys():\n toret_l += [get_focused(res[\"answer_box\"])]\n if res.get(\"organic\"):\n toret_l += [get_focused(i) for i in res.get(\"organic\")]\n\n return str(toret) + \"\\n\" + str(toret_l) if as_string else toret_l\n\n\nif __name__ == \"__main__\":\n import fire\n\n fire.Fire(SerperWrapper().run)\n\n```\n\n---\nTranslate the code above into Mermaid Sequence Diagram.": "```mermaid\nsequenceDiagram\n participant User\n participant SerperWrapper\n participant aiohttp\n participant pydantic\n participant config\n\n User ->> SerperWrapper: run(query: str, max_results: int, as_string: bool, **kwargs: Any)\n SerperWrapper ->> SerperWrapper: _process_response(response: dict, as_string: bool)\n SerperWrapper ->> SerperWrapper: get_payloads(queries: list[str], max_results: int)\n SerperWrapper ->> SerperWrapper: get_headers()\n SerperWrapper ->> aiohttp: ClientSession.post(url, data, headers)\n aiohttp ->> SerperWrapper: response\n SerperWrapper ->> aiohttp: ClientSession.get.post(url, data, headers)\n aiohttp ->> SerperWrapper: response\n SerperWrapper ->> aiohttp: ClientSession.post(url, data, headers)\n aiohttp ->> SerperWrapper: response\n SerperWrapper ->> User: str\n```", + "You are a python code to Mermaid Sequence Diagram translator in function detail#SYSTEM_MSG_END#```python\n#!/usr/bin/env python\n# -*- coding: utf-8 -*-\nfrom __future__ import annotations\n\nimport asyncio\nimport json\nfrom concurrent import futures\nfrom typing import Optional\nfrom urllib.parse import urlparse\n\nimport httplib2\nfrom pydantic import BaseModel, ConfigDict, Field, field_validator\n\nfrom metagpt.config2 import config\nfrom metagpt.logs import logger\n\ntry:\n from googleapiclient.discovery import build\n from googleapiclient.errors import HttpError\nexcept ImportError:\n raise ImportError(\n \"To use this module, you should have the `google-api-python-client` Python package installed. \"\n \"You can install it by running the command: `pip install -e.[search-google]`\"\n )\n\n\nclass GoogleAPIWrapper(BaseModel):\n model_config = ConfigDict(arbitrary_types_allowed=True)\n\n google_api_key: Optional[str] = Field(default=None, validate_default=True)\n google_cse_id: Optional[str] = Field(default=None, validate_default=True)\n loop: Optional[asyncio.AbstractEventLoop] = None\n executor: Optional[futures.Executor] = None\n\n @field_validator(\"google_api_key\", mode=\"before\")\n @classmethod\n def check_google_api_key(cls, val: str):\n val = val or config.search[\"google\"].api_key\n if not val:\n raise ValueError(\n \"To use, make sure you provide the google_api_key when constructing an object. Alternatively, \"\n \"ensure that the environment variable GOOGLE_API_KEY is set with your API key. You can obtain \"\n \"an API key from https://console.cloud.google.com/apis/credentials.\"\n )\n return val\n\n @field_validator(\"google_cse_id\", mode=\"before\")\n @classmethod\n def check_google_cse_id(cls, val: str):\n val = val or config.search[\"google\"].cse_id\n if not val:\n raise ValueError(\n \"To use, make sure you provide the google_cse_id when constructing an object. Alternatively, \"\n \"ensure that the environment variable GOOGLE_CSE_ID is set with your API key. You can obtain \"\n \"an API key from https://programmablesearchengine.google.com/controlpanel/create.\"\n )\n return val\n\n @property\n def google_api_client(self):\n build_kwargs = {\"developerKey\": self.google_api_key}\n if config.proxy:\n parse_result = urlparse(config.proxy)\n proxy_type = parse_result.scheme\n if proxy_type == \"https\":\n proxy_type = \"http\"\n build_kwargs[\"http\"] = httplib2.Http(\n proxy_info=httplib2.ProxyInfo(\n getattr(httplib2.socks, f\"PROXY_TYPE_{proxy_type.upper()}\"),\n parse_result.hostname,\n parse_result.port,\n ),\n )\n service = build(\"customsearch\", \"v1\", **build_kwargs)\n return service.cse()\n\n async def run(\n self,\n query: str,\n max_results: int = 8,\n as_string: bool = True,\n focus: list[str] | None = None,\n ) -> str | list[dict]:\n \"\"\"Return the results of a Google search using the official Google API.\n\n Args:\n query: The search query.\n max_results: The number of results to return.\n as_string: A boolean flag to determine the return type of the results. If True, the function will\n return a formatted string with the search results. If False, it will return a list of dictionaries\n containing detailed information about each search result.\n focus: Specific information to be focused on from each search result.\n\n Returns:\n The results of the search.\n \"\"\"\n loop = self.loop or asyncio.get_event_loop()\n future = loop.run_in_executor(\n self.executor, self.google_api_client.list(q=query, num=max_results, cx=self.google_cse_id).execute\n )\n try:\n result = await future\n # Extract the search result items from the response\n search_results = result.get(\"items\", [])\n\n except HttpError as e:\n # Handle errors in the API call\n logger.exception(f\"fail to search {query} for {e}\")\n search_results = []\n\n focus = focus or [\"snippet\", \"link\", \"title\"]\n details = [{i: j for i, j in item_dict.items() if i in focus} for item_dict in search_results]\n # Return the list of search result URLs\n if as_string:\n return safe_google_results(details)\n\n return details\n\n\ndef safe_google_results(results: str | list) -> str:\n \"\"\"Return the results of a google search in a safe format.\n\n Args:\n results: The search results.\n\n Returns:\n The results of the search.\n \"\"\"\n if isinstance(results, list):\n safe_message = json.dumps([result for result in results])\n else:\n safe_message = results.encode(\"utf-8\", \"ignore\").decode(\"utf-8\")\n return safe_message\n\n\nif __name__ == \"__main__\":\n import fire\n\n fire.Fire(GoogleAPIWrapper().run)\n\n```\n\n---\nTranslate the code above into Mermaid Sequence Diagram.": "```mermaid\nsequenceDiagram\n participant BaseModel\n participant ConfigDict\n participant Field\n participant field_validator\n participant asyncio\n participant futures\n participant urlparse\n participant httplib2\n participant logger\n participant build\n participant HttpError\n participant GoogleAPIWrapper\n participant fire\n\n BaseModel ->> ConfigDict: model_config\n BaseModel ->> Field: google_api_key\n BaseModel ->> Field: google_cse_id\n BaseModel ->> Field: loop\n BaseModel ->> Field: executor\n Field ->> field_validator: check_google_api_key\n Field ->> field_validator: check_google_cse_id\n GoogleAPIWrapper ->> urlparse: parse_result\n urlparse ->> httplib2: Http\n urlparse ->> httplib2: ProxyInfo\n httplib2 ->> logger: exception\n build ->> GoogleAPIWrapper: google_api_client\n GoogleAPIWrapper ->> asyncio: run\n asyncio ->> futures: run_in_executor\n futures ->> GoogleAPIWrapper: google_api_client.list\n GoogleAPIWrapper ->> HttpError: HttpError\n HttpError ->> logger: exception\n GoogleAPIWrapper ->> safe_google_results: safe_google_results\n safe_google_results -->> GoogleAPIWrapper: safe_message\n GoogleAPIWrapper -->> fire: run\n```", + "You are a python code to Mermaid Sequence Diagram translator in function detail#SYSTEM_MSG_END#```python\nNone\n```\n\n---\nTranslate the code above into Mermaid Sequence Diagram.": "```mermaid\nsequenceDiagram\n participant PythonCode\n PythonCode->>Mermaid: None\n```", + "You are a python code to Mermaid Sequence Diagram translator in function detail#SYSTEM_MSG_END#```python\n#!/usr/bin/env python\n\"\"\"\n@Modified By: mashenquan, 2023/8/22. A definition has been provided for the return value of _think: returning false indicates that further reasoning cannot continue.\n@Modified By: mashenquan, 2023-11-1. According to Chapter 2.2.1 and 2.2.2 of RFC 116, change the data type of\n the `cause_by` value in the `Message` to a string to support the new message distribution feature.\n\"\"\"\n\nimport asyncio\nimport re\n\nfrom pydantic import BaseModel\n\nfrom metagpt.actions import Action, CollectLinks, ConductResearch, WebBrowseAndSummarize\nfrom metagpt.actions.research import get_research_system_text\nfrom metagpt.const import RESEARCH_PATH\nfrom metagpt.logs import logger\nfrom metagpt.roles.role import Role, RoleReactMode\nfrom metagpt.schema import Message\n\n\nclass Report(BaseModel):\n topic: str\n links: dict[str, list[str]] = None\n summaries: list[tuple[str, str]] = None\n content: str = \"\"\n\n\nclass Researcher(Role):\n name: str = \"David\"\n profile: str = \"Researcher\"\n goal: str = \"Gather information and conduct research\"\n constraints: str = \"Ensure accuracy and relevance of information\"\n language: str = \"en-us\"\n\n def __init__(self, **kwargs):\n super().__init__(**kwargs)\n self.set_actions(\n [CollectLinks(name=self.name), WebBrowseAndSummarize(name=self.name), ConductResearch(name=self.name)]\n )\n self._set_react_mode(react_mode=RoleReactMode.BY_ORDER.value)\n if self.language not in (\"en-us\", \"zh-cn\"):\n logger.warning(f\"The language `{self.language}` has not been tested, it may not work.\")\n\n async def _think(self) -> bool:\n if self.rc.todo is None:\n self._set_state(0)\n return True\n\n if self.rc.state + 1 < len(self.states):\n self._set_state(self.rc.state + 1)\n else:\n self.set_todo(None)\n return False\n\n async def _act(self) -> Message:\n logger.info(f\"{self._setting}: to do {self.rc.todo}({self.rc.todo.name})\")\n todo = self.rc.todo\n msg = self.rc.memory.get(k=1)[0]\n if isinstance(msg.instruct_content, Report):\n instruct_content = msg.instruct_content\n topic = instruct_content.topic\n else:\n topic = msg.content\n\n research_system_text = self.research_system_text(topic, todo)\n if isinstance(todo, CollectLinks):\n links = await todo.run(topic, 4, 4)\n ret = Message(\n content=\"\", instruct_content=Report(topic=topic, links=links), role=self.profile, cause_by=todo\n )\n elif isinstance(todo, WebBrowseAndSummarize):\n links = instruct_content.links\n todos = (todo.run(*url, query=query, system_text=research_system_text) for (query, url) in links.items())\n summaries = await asyncio.gather(*todos)\n summaries = list((url, summary) for i in summaries for (url, summary) in i.items() if summary)\n ret = Message(\n content=\"\", instruct_content=Report(topic=topic, summaries=summaries), role=self.profile, cause_by=todo\n )\n else:\n summaries = instruct_content.summaries\n summary_text = \"\\n---\\n\".join(f\"url: {url}\\nsummary: {summary}\" for (url, summary) in summaries)\n content = await self.rc.todo.run(topic, summary_text, system_text=research_system_text)\n ret = Message(\n content=\"\",\n instruct_content=Report(topic=topic, content=content),\n role=self.profile,\n cause_by=self.rc.todo,\n )\n self.rc.memory.add(ret)\n return ret\n\n def research_system_text(self, topic, current_task: Action) -> str:\n \"\"\"BACKWARD compatible\n This allows sub-class able to define its own system prompt based on topic.\n return the previous implementation to have backward compatible\n Args:\n topic:\n language:\n\n Returns: str\n \"\"\"\n return get_research_system_text(topic, self.language)\n\n async def react(self) -> Message:\n msg = await super().react()\n report = msg.instruct_content\n self.write_report(report.topic, report.content)\n return msg\n\n def write_report(self, topic: str, content: str):\n filename = re.sub(r'[\\\\/:\"*?<>|]+', \" \", topic)\n filename = filename.replace(\"\\n\", \"\")\n if not RESEARCH_PATH.exists():\n RESEARCH_PATH.mkdir(parents=True)\n filepath = RESEARCH_PATH / f\"{filename}.md\"\n filepath.write_text(content)\n\n\nif __name__ == \"__main__\":\n import fire\n\n async def main(topic: str, language=\"en-us\"):\n role = Researcher(language=language)\n await role.run(topic)\n\n fire.Fire(main)\n\n```\n\n---\nTranslate the code above into Mermaid Sequence Diagram.": "```mermaid\nsequenceDiagram\n participant Role\n participant CollectLinks\n participant WebBrowseAndSummarize\n participant ConductResearch\n participant Message\n participant Report\n\n Role->>Role: Gather information and conduct research\n Role->>Role: Ensure accuracy and relevance of information\n Role->>Role: Set react mode to BY_ORDER\n\n Role->>Role: to do {todo}({todo.name})\n Role->>CollectLinks: run(topic, 4, 4)\n CollectLinks-->>Role: links\n Role->>Message: Report(topic, links)\n Role->>Role: Add message to memory\n\n Role->>WebBrowseAndSummarize: run(url, query, system_text)\n WebBrowseAndSummarize-->>Role: summaries\n Role->>Message: Report(topic, summaries)\n Role->>Role: Add message to memory\n\n Role->>ConductResearch: run(topic, summary_text, system_text)\n ConductResearch-->>Role: content\n Role->>Message: Report(topic, content)\n Role->>Role: Add message to memory\n\n Role->>Role: React\n Role->>Role: Write report to file\n```", + "You are a python code to Mermaid Sequence Diagram translator in function detail#SYSTEM_MSG_END#```python\n\"\"\"Code Docstring Generator.\n\nThis script provides a tool to automatically generate docstrings for Python code. It uses the specified style to create\ndocstrings for the given code and system text.\n\nUsage:\n python3 -m metagpt.actions.write_docstring [--overwrite] [--style=]\n\nArguments:\n filename The path to the Python file for which you want to generate docstrings.\n\nOptions:\n --overwrite If specified, overwrite the original file with the code containing docstrings.\n --style= Specify the style of the generated docstrings.\n Valid values: 'google', 'numpy', or 'sphinx'.\n Default: 'google'\n\nExample:\n python3 -m metagpt.actions.write_docstring ./metagpt/startup.py --overwrite False --style=numpy\n\nThis script uses the 'fire' library to create a command-line interface. It generates docstrings for the given Python code using\nthe specified docstring style and adds them to the code.\n\"\"\"\nfrom __future__ import annotations\n\nimport ast\nfrom pathlib import Path\nfrom typing import Literal, Optional\n\nfrom metagpt.actions.action import Action\nfrom metagpt.utils.common import OutputParser, aread, awrite\nfrom metagpt.utils.pycst import merge_docstring\n\nPYTHON_DOCSTRING_SYSTEM = \"\"\"### Requirements\n1. Add docstrings to the given code following the {style} style.\n2. Replace the function body with an Ellipsis object(...) to reduce output.\n3. If the types are already annotated, there is no need to include them in the docstring.\n4. Extract only class, function or the docstrings for the module parts from the given Python code, avoiding any other text.\n\n### Input Example\n```python\ndef function_with_pep484_type_annotations(param1: int) -> bool:\n return isinstance(param1, int)\n\nclass ExampleError(Exception):\n def __init__(self, msg: str):\n self.msg = msg\n```\n\n### Output Example\n```python\n{example}\n```\n\"\"\"\n\n# https://www.sphinx-doc.org/en/master/usage/extensions/napoleon.html\n\nPYTHON_DOCSTRING_EXAMPLE_GOOGLE = '''\ndef function_with_pep484_type_annotations(param1: int) -> bool:\n \"\"\"Example function with PEP 484 type annotations.\n\n Extended description of function.\n\n Args:\n param1: The first parameter.\n\n Returns:\n The return value. True for success, False otherwise.\n \"\"\"\n ...\n\nclass ExampleError(Exception):\n \"\"\"Exceptions are documented in the same way as classes.\n\n The __init__ method was documented in the class level docstring.\n\n Args:\n msg: Human readable string describing the exception.\n\n Attributes:\n msg: Human readable string describing the exception.\n \"\"\"\n ...\n'''\n\nPYTHON_DOCSTRING_EXAMPLE_NUMPY = '''\ndef function_with_pep484_type_annotations(param1: int) -> bool:\n \"\"\"\n Example function with PEP 484 type annotations.\n\n Extended description of function.\n\n Parameters\n ----------\n param1\n The first parameter.\n\n Returns\n -------\n bool\n The return value. True for success, False otherwise.\n \"\"\"\n ...\n\nclass ExampleError(Exception):\n \"\"\"\n Exceptions are documented in the same way as classes.\n\n The __init__ method was documented in the class level docstring.\n\n Parameters\n ----------\n msg\n Human readable string describing the exception.\n\n Attributes\n ----------\n msg\n Human readable string describing the exception.\n \"\"\"\n ...\n'''\n\nPYTHON_DOCSTRING_EXAMPLE_SPHINX = '''\ndef function_with_pep484_type_annotations(param1: int) -> bool:\n \"\"\"Example function with PEP 484 type annotations.\n\n Extended description of function.\n\n :param param1: The first parameter.\n :type param1: int\n\n :return: The return value. True for success, False otherwise.\n :rtype: bool\n \"\"\"\n ...\n\nclass ExampleError(Exception):\n \"\"\"Exceptions are documented in the same way as classes.\n\n The __init__ method was documented in the class level docstring.\n\n :param msg: Human-readable string describing the exception.\n :type msg: str\n \"\"\"\n ...\n'''\n\n_python_docstring_style = {\n \"google\": PYTHON_DOCSTRING_EXAMPLE_GOOGLE.strip(),\n \"numpy\": PYTHON_DOCSTRING_EXAMPLE_NUMPY.strip(),\n \"sphinx\": PYTHON_DOCSTRING_EXAMPLE_SPHINX.strip(),\n}\n\n\nclass WriteDocstring(Action):\n \"\"\"This class is used to write docstrings for code.\n\n Attributes:\n desc: A string describing the action.\n \"\"\"\n\n desc: str = \"Write docstring for code.\"\n i_context: Optional[str] = None\n\n async def run(\n self,\n code: str,\n system_text: str = PYTHON_DOCSTRING_SYSTEM,\n style: Literal[\"google\", \"numpy\", \"sphinx\"] = \"google\",\n ) -> str:\n \"\"\"Writes docstrings for the given code and system text in the specified style.\n\n Args:\n code: A string of Python code.\n system_text: A string of system text.\n style: A string specifying the style of the docstring. Can be 'google', 'numpy', or 'sphinx'.\n\n Returns:\n The Python code with docstrings added.\n \"\"\"\n system_text = system_text.format(style=style, example=_python_docstring_style[style])\n simplified_code = _simplify_python_code(code)\n documented_code = await self._aask(f\"```python\\n{simplified_code}\\n```\", [system_text])\n documented_code = OutputParser.parse_python_code(documented_code)\n return merge_docstring(code, documented_code)\n\n @staticmethod\n async def write_docstring(\n filename: str | Path, overwrite: bool = False, style: Literal[\"google\", \"numpy\", \"sphinx\"] = \"google\"\n ) -> str:\n data = await aread(str(filename))\n code = await WriteDocstring().run(data, style=style)\n if overwrite:\n await awrite(filename, code)\n return code\n\n\ndef _simplify_python_code(code: str) -> None:\n \"\"\"Simplifies the given Python code by removing expressions and the last if statement.\n\n Args:\n code: A string of Python code.\n\n Returns:\n The simplified Python code.\n \"\"\"\n code_tree = ast.parse(code)\n code_tree.body = [i for i in code_tree.body if not isinstance(i, ast.Expr)]\n if isinstance(code_tree.body[-1], ast.If):\n code_tree.body.pop()\n return ast.unparse(code_tree)\n\n\nif __name__ == \"__main__\":\n import fire\n\n fire.Fire(WriteDocstring.write_docstring)\n\n```\n\n---\nTranslate the code above into Mermaid Sequence Diagram.": "```mermaid\nsequenceDiagram\n participant User\n participant \"WriteDocstring\" as WD\n participant \"OutputParser\" as OP\n participant \"aread\" as AR\n participant \"awrite\" as AW\n\n User ->> WD: write_docstring(filename, overwrite, style)\n WD ->> AR: aread(filename)\n AR -->> WD: data\n WD ->> WD: run(data, style)\n WD ->> OP: parse_python_code(documented_code)\n OP -->> WD: documented_code\n WD ->> WD: merge_docstring(code, documented_code)\n WD ->> AW: awrite(filename, code)\n AW -->> WD: code\n WD -->> User: code\n```", + "\n## context\n\n### Project Name\n20240110221009\n\n### Original Requirements\n['开发一个基于大语言模型与私有知识库的搜索引擎,希望可以基于大语言模型进行搜索总结']\n\n### Search Information\n-\n\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Language\": \"en_us\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"Create a 2048 game\",\n \"Product Goals\": [\n \"Create an engaging user experience\",\n \"Improve accessibility, be responsive\",\n \"More beautiful UI\"\n ],\n \"User Stories\": [\n \"As a player, I want to be able to choose difficulty levels\",\n \"As a player, I want to see my score after each game\",\n \"As a player, I want to get restart button when I lose\",\n \"As a player, I want to see beautiful UI that make me feel good\",\n \"As a player, I want to play game via mobile phone\"\n ],\n \"Competitive Analysis\": [\n \"2048 Game A: Simple interface, lacks responsive features\",\n \"play2048.co: Beautiful and responsive UI with my best score shown\",\n \"2048game.com: Responsive UI with my best score shown, but many ads\"\n ],\n \"Competitive Quadrant Chart\": \"quadrantChart\\n title \\\"Reach and engagement of campaigns\\\"\\n x-axis \\\"Low Reach\\\" --> \\\"High Reach\\\"\\n y-axis \\\"Low Engagement\\\" --> \\\"High Engagement\\\"\\n quadrant-1 \\\"We should expand\\\"\\n quadrant-2 \\\"Need to promote\\\"\\n quadrant-3 \\\"Re-evaluate\\\"\\n quadrant-4 \\\"May be improved\\\"\\n \\\"Campaign A\\\": [0.3, 0.6]\\n \\\"Campaign B\\\": [0.45, 0.23]\\n \\\"Campaign C\\\": [0.57, 0.69]\\n \\\"Campaign D\\\": [0.78, 0.34]\\n \\\"Campaign E\\\": [0.40, 0.34]\\n \\\"Campaign F\\\": [0.35, 0.78]\\n \\\"Our Target Product\\\": [0.5, 0.6]\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [\n [\n \"P0\",\n \"The main code ...\"\n ],\n [\n \"P0\",\n \"The game algorithm ...\"\n ]\n ],\n \"UI Design draft\": \"Basic function description with a simple style and layout.\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Language: # Provide the language used in the project, typically matching the user's requirement language.\n- Programming Language: # Python/JavaScript or other mainstream programming language.\n- Original Requirements: # Place the original user's requirements here.\n- Product Goals: typing.List[str] # Provide up to three clear, orthogonal product goals.\n- User Stories: typing.List[str] # Provide up to 3 to 5 scenario-based user stories.\n- Competitive Analysis: typing.List[str] # Provide 5 to 7 competitive products.\n- Competitive Quadrant Chart: # Use mermaid quadrantChart syntax. Distribute scores evenly between 0 and 1\n- Requirement Analysis: # Provide a detailed analysis of the requirements.\n- Requirement Pool: typing.List[typing.List[str]] # List down the top-5 requirements with their priority (P0, P1, P2).\n- UI Design draft: # Provide a simple description of UI elements, functions, style, and layout.\n- Anything UNCLEAR: # Mention any aspects of the project that are unclear and try to clarify them.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Language\": \"zh_cn\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"开发一个基于大语言模型与私有知识库的搜索引擎,希望可以基于大语言模型进行搜索总结\",\n \"Product Goals\": [],\n \"User Stories\": [],\n \"Competitive Analysis\": [],\n \"Competitive Quadrant Chart\": \"\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [],\n \"UI Design draft\": \"\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]", + "\n## context\n\n### Project Name\n20240110221525\n\n### Original Requirements\n['需要一个基于LLM做总结的搜索引擎']\n\n### Search Information\n-\n\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Language\": \"en_us\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"Create a 2048 game\",\n \"Product Goals\": [\n \"Create an engaging user experience\",\n \"Improve accessibility, be responsive\",\n \"More beautiful UI\"\n ],\n \"User Stories\": [\n \"As a player, I want to be able to choose difficulty levels\",\n \"As a player, I want to see my score after each game\",\n \"As a player, I want to get restart button when I lose\",\n \"As a player, I want to see beautiful UI that make me feel good\",\n \"As a player, I want to play game via mobile phone\"\n ],\n \"Competitive Analysis\": [\n \"2048 Game A: Simple interface, lacks responsive features\",\n \"play2048.co: Beautiful and responsive UI with my best score shown\",\n \"2048game.com: Responsive UI with my best score shown, but many ads\"\n ],\n \"Competitive Quadrant Chart\": \"quadrantChart\\n title \\\"Reach and engagement of campaigns\\\"\\n x-axis \\\"Low Reach\\\" --> \\\"High Reach\\\"\\n y-axis \\\"Low Engagement\\\" --> \\\"High Engagement\\\"\\n quadrant-1 \\\"We should expand\\\"\\n quadrant-2 \\\"Need to promote\\\"\\n quadrant-3 \\\"Re-evaluate\\\"\\n quadrant-4 \\\"May be improved\\\"\\n \\\"Campaign A\\\": [0.3, 0.6]\\n \\\"Campaign B\\\": [0.45, 0.23]\\n \\\"Campaign C\\\": [0.57, 0.69]\\n \\\"Campaign D\\\": [0.78, 0.34]\\n \\\"Campaign E\\\": [0.40, 0.34]\\n \\\"Campaign F\\\": [0.35, 0.78]\\n \\\"Our Target Product\\\": [0.5, 0.6]\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [\n [\n \"P0\",\n \"The main code ...\"\n ],\n [\n \"P0\",\n \"The game algorithm ...\"\n ]\n ],\n \"UI Design draft\": \"Basic function description with a simple style and layout.\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Language: # Provide the language used in the project, typically matching the user's requirement language.\n- Programming Language: # Python/JavaScript or other mainstream programming language.\n- Original Requirements: # Place the original user's requirements here.\n- Product Goals: typing.List[str] # Provide up to three clear, orthogonal product goals.\n- User Stories: typing.List[str] # Provide up to 3 to 5 scenario-based user stories.\n- Competitive Analysis: typing.List[str] # Provide 5 to 7 competitive products.\n- Competitive Quadrant Chart: # Use mermaid quadrantChart syntax. Distribute scores evenly between 0 and 1\n- Requirement Analysis: # Provide a detailed analysis of the requirements.\n- Requirement Pool: typing.List[typing.List[str]] # List down the top-5 requirements with their priority (P0, P1, P2).\n- UI Design draft: # Provide a simple description of UI elements, functions, style, and layout.\n- Anything UNCLEAR: # Mention any aspects of the project that are unclear and try to clarify them.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Language\": \"zh_cn\",\n \"Programming Language\": \"LLM\",\n \"Original Requirements\": \"需要一个基于LLM做总结的搜索引擎\",\n \"Product Goals\": [],\n \"User Stories\": [],\n \"Competitive Analysis\": [],\n \"Competitive Quadrant Chart\": \"\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [],\n \"UI Design draft\": \"\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]", + "\n## context\n\n### Project Name\n20240110221737\n\n### Original Requirements\n['开发一个基于大语言模型与私有知识库的搜索引擎,希望可以基于大语言模型进行搜索总结']\n\n### Search Information\n-\n\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Language\": \"en_us\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"Create a 2048 game\",\n \"Product Goals\": [\n \"Create an engaging user experience\",\n \"Improve accessibility, be responsive\",\n \"More beautiful UI\"\n ],\n \"User Stories\": [\n \"As a player, I want to be able to choose difficulty levels\",\n \"As a player, I want to see my score after each game\",\n \"As a player, I want to get restart button when I lose\",\n \"As a player, I want to see beautiful UI that make me feel good\",\n \"As a player, I want to play game via mobile phone\"\n ],\n \"Competitive Analysis\": [\n \"2048 Game A: Simple interface, lacks responsive features\",\n \"play2048.co: Beautiful and responsive UI with my best score shown\",\n \"2048game.com: Responsive UI with my best score shown, but many ads\"\n ],\n \"Competitive Quadrant Chart\": \"quadrantChart\\n title \\\"Reach and engagement of campaigns\\\"\\n x-axis \\\"Low Reach\\\" --> \\\"High Reach\\\"\\n y-axis \\\"Low Engagement\\\" --> \\\"High Engagement\\\"\\n quadrant-1 \\\"We should expand\\\"\\n quadrant-2 \\\"Need to promote\\\"\\n quadrant-3 \\\"Re-evaluate\\\"\\n quadrant-4 \\\"May be improved\\\"\\n \\\"Campaign A\\\": [0.3, 0.6]\\n \\\"Campaign B\\\": [0.45, 0.23]\\n \\\"Campaign C\\\": [0.57, 0.69]\\n \\\"Campaign D\\\": [0.78, 0.34]\\n \\\"Campaign E\\\": [0.40, 0.34]\\n \\\"Campaign F\\\": [0.35, 0.78]\\n \\\"Our Target Product\\\": [0.5, 0.6]\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [\n [\n \"P0\",\n \"The main code ...\"\n ],\n [\n \"P0\",\n \"The game algorithm ...\"\n ]\n ],\n \"UI Design draft\": \"Basic function description with a simple style and layout.\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Language: # Provide the language used in the project, typically matching the user's requirement language.\n- Programming Language: # Python/JavaScript or other mainstream programming language.\n- Original Requirements: # Place the original user's requirements here.\n- Product Goals: typing.List[str] # Provide up to three clear, orthogonal product goals.\n- User Stories: typing.List[str] # Provide up to 3 to 5 scenario-based user stories.\n- Competitive Analysis: typing.List[str] # Provide 5 to 7 competitive products.\n- Competitive Quadrant Chart: # Use mermaid quadrantChart syntax. Distribute scores evenly between 0 and 1\n- Requirement Analysis: # Provide a detailed analysis of the requirements.\n- Requirement Pool: typing.List[typing.List[str]] # List down the top-5 requirements with their priority (P0, P1, P2).\n- UI Design draft: # Provide a simple description of UI elements, functions, style, and layout.\n- Anything UNCLEAR: # Mention any aspects of the project that are unclear and try to clarify them.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Language\": \"zh_cn\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"开发一个基于大语言模型与私有知识库的搜索引擎,希望可以基于大语言模型进行搜索总结\",\n \"Product Goals\": [],\n \"User Stories\": [],\n \"Competitive Analysis\": [],\n \"Competitive Quadrant Chart\": \"\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [],\n \"UI Design draft\": \"\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]", + "\n## context\n\n### Project Name\n20240110221737\n\n### Original Requirements\n['']\n\n### Search Information\n-\n\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Language\": \"en_us\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"Create a 2048 game\",\n \"Product Goals\": [\n \"Create an engaging user experience\",\n \"Improve accessibility, be responsive\",\n \"More beautiful UI\"\n ],\n \"User Stories\": [\n \"As a player, I want to be able to choose difficulty levels\",\n \"As a player, I want to see my score after each game\",\n \"As a player, I want to get restart button when I lose\",\n \"As a player, I want to see beautiful UI that make me feel good\",\n \"As a player, I want to play game via mobile phone\"\n ],\n \"Competitive Analysis\": [\n \"2048 Game A: Simple interface, lacks responsive features\",\n \"play2048.co: Beautiful and responsive UI with my best score shown\",\n \"2048game.com: Responsive UI with my best score shown, but many ads\"\n ],\n \"Competitive Quadrant Chart\": \"quadrantChart\\n title \\\"Reach and engagement of campaigns\\\"\\n x-axis \\\"Low Reach\\\" --> \\\"High Reach\\\"\\n y-axis \\\"Low Engagement\\\" --> \\\"High Engagement\\\"\\n quadrant-1 \\\"We should expand\\\"\\n quadrant-2 \\\"Need to promote\\\"\\n quadrant-3 \\\"Re-evaluate\\\"\\n quadrant-4 \\\"May be improved\\\"\\n \\\"Campaign A\\\": [0.3, 0.6]\\n \\\"Campaign B\\\": [0.45, 0.23]\\n \\\"Campaign C\\\": [0.57, 0.69]\\n \\\"Campaign D\\\": [0.78, 0.34]\\n \\\"Campaign E\\\": [0.40, 0.34]\\n \\\"Campaign F\\\": [0.35, 0.78]\\n \\\"Our Target Product\\\": [0.5, 0.6]\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [\n [\n \"P0\",\n \"The main code ...\"\n ],\n [\n \"P0\",\n \"The game algorithm ...\"\n ]\n ],\n \"UI Design draft\": \"Basic function description with a simple style and layout.\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Language: # Provide the language used in the project, typically matching the user's requirement language.\n- Programming Language: # Python/JavaScript or other mainstream programming language.\n- Original Requirements: # Place the original user's requirements here.\n- Product Goals: typing.List[str] # Provide up to three clear, orthogonal product goals.\n- User Stories: typing.List[str] # Provide up to 3 to 5 scenario-based user stories.\n- Competitive Analysis: typing.List[str] # Provide 5 to 7 competitive products.\n- Competitive Quadrant Chart: # Use mermaid quadrantChart syntax. Distribute scores evenly between 0 and 1\n- Requirement Analysis: # Provide a detailed analysis of the requirements.\n- Requirement Pool: typing.List[typing.List[str]] # List down the top-5 requirements with their priority (P0, P1, P2).\n- UI Design draft: # Provide a simple description of UI elements, functions, style, and layout.\n- Anything UNCLEAR: # Mention any aspects of the project that are unclear and try to clarify them.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Language\": \"en_us\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"\",\n \"Product Goals\": [],\n \"User Stories\": [],\n \"Competitive Analysis\": [],\n \"Competitive Quadrant Chart\": \"\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [],\n \"UI Design draft\": \"\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]", + "\n## context\n\n### Project Name\n20240111154514\n\n### Original Requirements\n['需要一个基于LLM做总结的搜索引擎']\n\n### Search Information\n-\n\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Language\": \"en_us\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"Create a 2048 game\",\n \"Product Goals\": [\n \"Create an engaging user experience\",\n \"Improve accessibility, be responsive\",\n \"More beautiful UI\"\n ],\n \"User Stories\": [\n \"As a player, I want to be able to choose difficulty levels\",\n \"As a player, I want to see my score after each game\",\n \"As a player, I want to get restart button when I lose\",\n \"As a player, I want to see beautiful UI that make me feel good\",\n \"As a player, I want to play game via mobile phone\"\n ],\n \"Competitive Analysis\": [\n \"2048 Game A: Simple interface, lacks responsive features\",\n \"play2048.co: Beautiful and responsive UI with my best score shown\",\n \"2048game.com: Responsive UI with my best score shown, but many ads\"\n ],\n \"Competitive Quadrant Chart\": \"quadrantChart\\n title \\\"Reach and engagement of campaigns\\\"\\n x-axis \\\"Low Reach\\\" --> \\\"High Reach\\\"\\n y-axis \\\"Low Engagement\\\" --> \\\"High Engagement\\\"\\n quadrant-1 \\\"We should expand\\\"\\n quadrant-2 \\\"Need to promote\\\"\\n quadrant-3 \\\"Re-evaluate\\\"\\n quadrant-4 \\\"May be improved\\\"\\n \\\"Campaign A\\\": [0.3, 0.6]\\n \\\"Campaign B\\\": [0.45, 0.23]\\n \\\"Campaign C\\\": [0.57, 0.69]\\n \\\"Campaign D\\\": [0.78, 0.34]\\n \\\"Campaign E\\\": [0.40, 0.34]\\n \\\"Campaign F\\\": [0.35, 0.78]\\n \\\"Our Target Product\\\": [0.5, 0.6]\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [\n [\n \"P0\",\n \"The main code ...\"\n ],\n [\n \"P0\",\n \"The game algorithm ...\"\n ]\n ],\n \"UI Design draft\": \"Basic function description with a simple style and layout.\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Language: # Provide the language used in the project, typically matching the user's requirement language.\n- Programming Language: # Python/JavaScript or other mainstream programming language.\n- Original Requirements: # Place the original user's requirements here.\n- Product Goals: typing.List[str] # Provide up to three clear, orthogonal product goals.\n- User Stories: typing.List[str] # Provide up to 3 to 5 scenario-based user stories.\n- Competitive Analysis: typing.List[str] # Provide 5 to 7 competitive products.\n- Competitive Quadrant Chart: # Use mermaid quadrantChart syntax. Distribute scores evenly between 0 and 1\n- Requirement Analysis: # Provide a detailed analysis of the requirements.\n- Requirement Pool: typing.List[typing.List[str]] # List down the top-5 requirements with their priority (P0, P1, P2).\n- UI Design draft: # Provide a simple description of UI elements, functions, style, and layout.\n- Anything UNCLEAR: # Mention any aspects of the project that are unclear and try to clarify them.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Language\": \"zh_cn\",\n \"Programming Language\": \"LLM\",\n \"Original Requirements\": \"需要一个基于LLM做总结的搜索引擎\",\n \"Product Goals\": [],\n \"User Stories\": [],\n \"Competitive Analysis\": [],\n \"Competitive Quadrant Chart\": \"\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [],\n \"UI Design draft\": \"\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]", + "You are a python code to Mermaid Sequence Diagram translator in function detail#SYSTEM_MSG_END#```python\n#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\"\"\"\n@Time : 2023/5/23 18:27\n@Author : alexanderwu\n@File : search_engine_serpapi.py\n\"\"\"\nfrom typing import Any, Dict, Optional, Tuple\n\nimport aiohttp\nfrom pydantic import BaseModel, ConfigDict, Field, field_validator\n\nfrom metagpt.config2 import config\n\n\nclass SerpAPIWrapper(BaseModel):\n model_config = ConfigDict(arbitrary_types_allowed=True)\n\n search_engine: Any = None #: :meta private:\n params: dict = Field(\n default_factory=lambda: {\n \"engine\": \"google\",\n \"google_domain\": \"google.com\",\n \"gl\": \"us\",\n \"hl\": \"en\",\n }\n )\n # should add `validate_default=True` to check with default value\n serpapi_api_key: Optional[str] = Field(default=None, validate_default=True)\n aiosession: Optional[aiohttp.ClientSession] = None\n\n @field_validator(\"serpapi_api_key\", mode=\"before\")\n @classmethod\n def check_serpapi_api_key(cls, val: str):\n val = val or config.search.api_key\n if not val:\n raise ValueError(\n \"To use, make sure you provide the serpapi_api_key when constructing an object. Alternatively, \"\n \"ensure that the environment variable SERPAPI_API_KEY is set with your API key. You can obtain \"\n \"an API key from https://serpapi.com/.\"\n )\n return val\n\n async def run(self, query, max_results: int = 8, as_string: bool = True, **kwargs: Any) -> str:\n \"\"\"Run query through SerpAPI and parse result async.\"\"\"\n result = await self.results(query, max_results)\n return self._process_response(result, as_string=as_string)\n\n async def results(self, query: str, max_results: int) -> dict:\n \"\"\"Use aiohttp to run query through SerpAPI and return the results async.\"\"\"\n\n def construct_url_and_params() -> Tuple[str, Dict[str, str]]:\n params = self.get_params(query)\n params[\"source\"] = \"python\"\n params[\"num\"] = max_results\n params[\"output\"] = \"json\"\n url = \"https://serpapi.com/search\"\n return url, params\n\n url, params = construct_url_and_params()\n if not self.aiosession:\n async with aiohttp.ClientSession() as session:\n async with session.get(url, params=params) as response:\n res = await response.json()\n else:\n async with self.aiosession.get(url, params=params) as response:\n res = await response.json()\n\n return res\n\n def get_params(self, query: str) -> Dict[str, str]:\n \"\"\"Get parameters for SerpAPI.\"\"\"\n _params = {\n \"api_key\": self.serpapi_api_key,\n \"q\": query,\n }\n params = {**self.params, **_params}\n return params\n\n @staticmethod\n def _process_response(res: dict, as_string: bool) -> str:\n \"\"\"Process response from SerpAPI.\"\"\"\n # logger.debug(res)\n focus = [\"title\", \"snippet\", \"link\"]\n get_focused = lambda x: {i: j for i, j in x.items() if i in focus}\n\n if \"error\" in res.keys():\n raise ValueError(f\"Got error from SerpAPI: {res['error']}\")\n if \"answer_box\" in res.keys() and \"answer\" in res[\"answer_box\"].keys():\n toret = res[\"answer_box\"][\"answer\"]\n elif \"answer_box\" in res.keys() and \"snippet\" in res[\"answer_box\"].keys():\n toret = res[\"answer_box\"][\"snippet\"]\n elif \"answer_box\" in res.keys() and \"snippet_highlighted_words\" in res[\"answer_box\"].keys():\n toret = res[\"answer_box\"][\"snippet_highlighted_words\"][0]\n elif \"sports_results\" in res.keys() and \"game_spotlight\" in res[\"sports_results\"].keys():\n toret = res[\"sports_results\"][\"game_spotlight\"]\n elif \"knowledge_graph\" in res.keys() and \"description\" in res[\"knowledge_graph\"].keys():\n toret = res[\"knowledge_graph\"][\"description\"]\n elif \"snippet\" in res[\"organic_results\"][0].keys():\n toret = res[\"organic_results\"][0][\"snippet\"]\n else:\n toret = \"No good search result found\"\n\n toret_l = []\n if \"answer_box\" in res.keys() and \"snippet\" in res[\"answer_box\"].keys():\n toret_l += [get_focused(res[\"answer_box\"])]\n if res.get(\"organic_results\"):\n toret_l += [get_focused(i) for i in res.get(\"organic_results\")]\n\n return str(toret) + \"\\n\" + str(toret_l) if as_string else toret_l\n\n\nif __name__ == \"__main__\":\n import fire\n\n fire.Fire(SerpAPIWrapper().run)\n\n```\n\n---\nTranslate the code above into Mermaid Sequence Diagram.": "```mermaid\nsequenceDiagram\n participant SerpAPIWrapper\n participant aiohttp\n participant config\n participant session\n participant response\n participant fire\n\n Note over SerpAPIWrapper: Initialization\n SerpAPIWrapper->>config: get search.api_key\n config-->>SerpAPIWrapper: return search.api_key\n SerpAPIWrapper->>SerpAPIWrapper: check_serpapi_api_key()\n SerpAPIWrapper->>SerpAPIWrapper: get_params()\n SerpAPIWrapper->>SerpAPIWrapper: results()\n SerpAPIWrapper->>aiohttp: ClientSession()\n aiohttp->>session: get(url, params)\n session->>response: json()\n response-->>session: return json response\n session-->>aiohttp: return json response\n aiohttp-->>SerpAPIWrapper: return json response\n SerpAPIWrapper-->>SerpAPIWrapper: _process_response()\n SerpAPIWrapper-->>fire: run()\n```", + "You are a python code to Mermaid Sequence Diagram translator in function detail#SYSTEM_MSG_END#```python\n#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\"\"\"\n@Time : 2023/5/23 18:27\n@Author : alexanderwu\n@File : search_engine_serpapi.py\n\"\"\"\nimport json\nfrom typing import Any, Dict, Optional, Tuple\n\nimport aiohttp\nfrom pydantic import BaseModel, ConfigDict, Field, field_validator\n\nfrom metagpt.config2 import config\n\n\nclass SerperWrapper(BaseModel):\n model_config = ConfigDict(arbitrary_types_allowed=True)\n\n search_engine: Any = None #: :meta private:\n payload: dict = Field(default_factory=lambda: {\"page\": 1, \"num\": 10})\n serper_api_key: Optional[str] = Field(default=None, validate_default=True)\n aiosession: Optional[aiohttp.ClientSession] = None\n\n @field_validator(\"serper_api_key\", mode=\"before\")\n @classmethod\n def check_serper_api_key(cls, val: str):\n val = val or config.search.api_key\n if not val:\n raise ValueError(\n \"To use, make sure you provide the serper_api_key when constructing an object. Alternatively, \"\n \"ensure that the environment variable SERPER_API_KEY is set with your API key. You can obtain \"\n \"an API key from https://serper.dev/.\"\n )\n return val\n\n async def run(self, query: str, max_results: int = 8, as_string: bool = True, **kwargs: Any) -> str:\n \"\"\"Run query through Serper and parse result async.\"\"\"\n if isinstance(query, str):\n return self._process_response((await self.results([query], max_results))[0], as_string=as_string)\n else:\n results = [self._process_response(res, as_string) for res in await self.results(query, max_results)]\n return \"\\n\".join(results) if as_string else results\n\n async def results(self, queries: list[str], max_results: int = 8) -> dict:\n \"\"\"Use aiohttp to run query through Serper and return the results async.\"\"\"\n\n def construct_url_and_payload_and_headers() -> Tuple[str, Dict[str, str]]:\n payloads = self.get_payloads(queries, max_results)\n url = \"https://google.serper.dev/search\"\n headers = self.get_headers()\n return url, payloads, headers\n\n url, payloads, headers = construct_url_and_payload_and_headers()\n if not self.aiosession:\n async with aiohttp.ClientSession() as session:\n async with session.post(url, data=payloads, headers=headers) as response:\n res = await response.json()\n else:\n async with self.aiosession.get.post(url, data=payloads, headers=headers) as response:\n res = await response.json()\n\n return res\n\n def get_payloads(self, queries: list[str], max_results: int) -> Dict[str, str]:\n \"\"\"Get payloads for Serper.\"\"\"\n payloads = []\n for query in queries:\n _payload = {\n \"q\": query,\n \"num\": max_results,\n }\n payloads.append({**self.payload, **_payload})\n return json.dumps(payloads, sort_keys=True)\n\n def get_headers(self) -> Dict[str, str]:\n headers = {\"X-API-KEY\": self.serper_api_key, \"Content-Type\": \"application/json\"}\n return headers\n\n @staticmethod\n def _process_response(res: dict, as_string: bool = False) -> str:\n \"\"\"Process response from SerpAPI.\"\"\"\n # logger.debug(res)\n focus = [\"title\", \"snippet\", \"link\"]\n\n def get_focused(x):\n return {i: j for i, j in x.items() if i in focus}\n\n if \"error\" in res.keys():\n raise ValueError(f\"Got error from SerpAPI: {res['error']}\")\n if \"answer_box\" in res.keys() and \"answer\" in res[\"answer_box\"].keys():\n toret = res[\"answer_box\"][\"answer\"]\n elif \"answer_box\" in res.keys() and \"snippet\" in res[\"answer_box\"].keys():\n toret = res[\"answer_box\"][\"snippet\"]\n elif \"answer_box\" in res.keys() and \"snippet_highlighted_words\" in res[\"answer_box\"].keys():\n toret = res[\"answer_box\"][\"snippet_highlighted_words\"][0]\n elif \"sports_results\" in res.keys() and \"game_spotlight\" in res[\"sports_results\"].keys():\n toret = res[\"sports_results\"][\"game_spotlight\"]\n elif \"knowledge_graph\" in res.keys() and \"description\" in res[\"knowledge_graph\"].keys():\n toret = res[\"knowledge_graph\"][\"description\"]\n elif \"snippet\" in res[\"organic\"][0].keys():\n toret = res[\"organic\"][0][\"snippet\"]\n else:\n toret = \"No good search result found\"\n\n toret_l = []\n if \"answer_box\" in res.keys() and \"snippet\" in res[\"answer_box\"].keys():\n toret_l += [get_focused(res[\"answer_box\"])]\n if res.get(\"organic\"):\n toret_l += [get_focused(i) for i in res.get(\"organic\")]\n\n return str(toret) + \"\\n\" + str(toret_l) if as_string else toret_l\n\n\nif __name__ == \"__main__\":\n import fire\n\n fire.Fire(SerperWrapper().run)\n\n```\n\n---\nTranslate the code above into Mermaid Sequence Diagram.": "```mermaid\nsequenceDiagram\n participant User\n participant SerperWrapper\n participant aiohttp\n participant config\n\n User ->> SerperWrapper: run(query, max_results, as_string, **kwargs)\n SerperWrapper ->> SerperWrapper: _process_response(response, as_string)\n SerperWrapper ->> SerperWrapper: results(queries, max_results)\n SerperWrapper ->> aiohttp: post(url, data, headers)\n aiohttp ->> SerperWrapper: response\n SerperWrapper ->> User: return result\n SerperWrapper ->> config: search.api_key\n```", + "You are a python code to Mermaid Sequence Diagram translator in function detail#SYSTEM_MSG_END#```python\n#!/usr/bin/env python\n# -*- coding: utf-8 -*-\nfrom __future__ import annotations\n\nimport asyncio\nimport json\nfrom concurrent import futures\nfrom typing import Optional\nfrom urllib.parse import urlparse\n\nimport httplib2\nfrom pydantic import BaseModel, ConfigDict, Field, field_validator\n\nfrom metagpt.config2 import config\nfrom metagpt.logs import logger\n\ntry:\n from googleapiclient.discovery import build\n from googleapiclient.errors import HttpError\nexcept ImportError:\n raise ImportError(\n \"To use this module, you should have the `google-api-python-client` Python package installed. \"\n \"You can install it by running the command: `pip install -e.[search-google]`\"\n )\n\n\nclass GoogleAPIWrapper(BaseModel):\n model_config = ConfigDict(arbitrary_types_allowed=True)\n\n google_api_key: Optional[str] = Field(default=None, validate_default=True)\n google_cse_id: Optional[str] = Field(default=None, validate_default=True)\n loop: Optional[asyncio.AbstractEventLoop] = None\n executor: Optional[futures.Executor] = None\n\n @field_validator(\"google_api_key\", mode=\"before\")\n @classmethod\n def check_google_api_key(cls, val: str):\n val = val or config.search.api_key\n if not val:\n raise ValueError(\n \"To use, make sure you provide the google_api_key when constructing an object. Alternatively, \"\n \"ensure that the environment variable GOOGLE_API_KEY is set with your API key. You can obtain \"\n \"an API key from https://console.cloud.google.com/apis/credentials.\"\n )\n return val\n\n @field_validator(\"google_cse_id\", mode=\"before\")\n @classmethod\n def check_google_cse_id(cls, val: str):\n val = val or config.search.cse_id\n if not val:\n raise ValueError(\n \"To use, make sure you provide the google_cse_id when constructing an object. Alternatively, \"\n \"ensure that the environment variable GOOGLE_CSE_ID is set with your API key. You can obtain \"\n \"an API key from https://programmablesearchengine.google.com/controlpanel/create.\"\n )\n return val\n\n @property\n def google_api_client(self):\n build_kwargs = {\"developerKey\": self.google_api_key}\n if config.proxy:\n parse_result = urlparse(config.proxy)\n proxy_type = parse_result.scheme\n if proxy_type == \"https\":\n proxy_type = \"http\"\n build_kwargs[\"http\"] = httplib2.Http(\n proxy_info=httplib2.ProxyInfo(\n getattr(httplib2.socks, f\"PROXY_TYPE_{proxy_type.upper()}\"),\n parse_result.hostname,\n parse_result.port,\n ),\n )\n service = build(\"customsearch\", \"v1\", **build_kwargs)\n return service.cse()\n\n async def run(\n self,\n query: str,\n max_results: int = 8,\n as_string: bool = True,\n focus: list[str] | None = None,\n ) -> str | list[dict]:\n \"\"\"Return the results of a Google search using the official Google API.\n\n Args:\n query: The search query.\n max_results: The number of results to return.\n as_string: A boolean flag to determine the return type of the results. If True, the function will\n return a formatted string with the search results. If False, it will return a list of dictionaries\n containing detailed information about each search result.\n focus: Specific information to be focused on from each search result.\n\n Returns:\n The results of the search.\n \"\"\"\n loop = self.loop or asyncio.get_event_loop()\n future = loop.run_in_executor(\n self.executor, self.google_api_client.list(q=query, num=max_results, cx=self.google_cse_id).execute\n )\n try:\n result = await future\n # Extract the search result items from the response\n search_results = result.get(\"items\", [])\n\n except HttpError as e:\n # Handle errors in the API call\n logger.exception(f\"fail to search {query} for {e}\")\n search_results = []\n\n focus = focus or [\"snippet\", \"link\", \"title\"]\n details = [{i: j for i, j in item_dict.items() if i in focus} for item_dict in search_results]\n # Return the list of search result URLs\n if as_string:\n return safe_google_results(details)\n\n return details\n\n\ndef safe_google_results(results: str | list) -> str:\n \"\"\"Return the results of a google search in a safe format.\n\n Args:\n results: The search results.\n\n Returns:\n The results of the search.\n \"\"\"\n if isinstance(results, list):\n safe_message = json.dumps([result for result in results])\n else:\n safe_message = results.encode(\"utf-8\", \"ignore\").decode(\"utf-8\")\n return safe_message\n\n\nif __name__ == \"__main__\":\n import fire\n\n fire.Fire(GoogleAPIWrapper().run)\n\n```\n\n---\nTranslate the code above into Mermaid Sequence Diagram.": "```mermaid\nsequenceDiagram\n participant BaseModel\n participant httplib2\n participant asyncio\n participant futures\n participant urlparse\n participant json\n participant config\n participant logger\n participant googleapiclient.discovery\n participant googleapiclient.errors\n participant fire\n\n BaseModel->>ConfigDict: model_config\n BaseModel->>Optional: google_api_key\n BaseModel->>Optional: google_cse_id\n BaseModel->>Optional: loop\n BaseModel->>Optional: executor\n BaseModel->>googleapiclient.discovery: check_google_api_key\n BaseModel->>googleapiclient.discovery: check_google_cse_id\n BaseModel->>googleapiclient.discovery: google_api_client\n BaseModel->>asyncio: run\n asyncio->>futures: run_in_executor\n futures->>googleapiclient.discovery: list\n googleapiclient.discovery->>googleapiclient.discovery: execute\n googleapiclient.discovery-->>futures: result\n futures-->>asyncio: result\n asyncio-->>BaseModel: result\n BaseModel-->>BaseModel: safe_google_results\n BaseModel-->>BaseModel: run\n BaseModel-->>fire: run\n```", + "\n## context\n\n### Project Name\n20240111154819\n\n### Original Requirements\n['开发一个基于大语言模型与私有知识库的搜索引擎,希望可以基于大语言模型进行搜索总结']\n\n### Search Information\n-\n\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Language\": \"en_us\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"Create a 2048 game\",\n \"Product Goals\": [\n \"Create an engaging user experience\",\n \"Improve accessibility, be responsive\",\n \"More beautiful UI\"\n ],\n \"User Stories\": [\n \"As a player, I want to be able to choose difficulty levels\",\n \"As a player, I want to see my score after each game\",\n \"As a player, I want to get restart button when I lose\",\n \"As a player, I want to see beautiful UI that make me feel good\",\n \"As a player, I want to play game via mobile phone\"\n ],\n \"Competitive Analysis\": [\n \"2048 Game A: Simple interface, lacks responsive features\",\n \"play2048.co: Beautiful and responsive UI with my best score shown\",\n \"2048game.com: Responsive UI with my best score shown, but many ads\"\n ],\n \"Competitive Quadrant Chart\": \"quadrantChart\\n title \\\"Reach and engagement of campaigns\\\"\\n x-axis \\\"Low Reach\\\" --> \\\"High Reach\\\"\\n y-axis \\\"Low Engagement\\\" --> \\\"High Engagement\\\"\\n quadrant-1 \\\"We should expand\\\"\\n quadrant-2 \\\"Need to promote\\\"\\n quadrant-3 \\\"Re-evaluate\\\"\\n quadrant-4 \\\"May be improved\\\"\\n \\\"Campaign A\\\": [0.3, 0.6]\\n \\\"Campaign B\\\": [0.45, 0.23]\\n \\\"Campaign C\\\": [0.57, 0.69]\\n \\\"Campaign D\\\": [0.78, 0.34]\\n \\\"Campaign E\\\": [0.40, 0.34]\\n \\\"Campaign F\\\": [0.35, 0.78]\\n \\\"Our Target Product\\\": [0.5, 0.6]\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [\n [\n \"P0\",\n \"The main code ...\"\n ],\n [\n \"P0\",\n \"The game algorithm ...\"\n ]\n ],\n \"UI Design draft\": \"Basic function description with a simple style and layout.\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Language: # Provide the language used in the project, typically matching the user's requirement language.\n- Programming Language: # Python/JavaScript or other mainstream programming language.\n- Original Requirements: # Place the original user's requirements here.\n- Product Goals: typing.List[str] # Provide up to three clear, orthogonal product goals.\n- User Stories: typing.List[str] # Provide up to 3 to 5 scenario-based user stories.\n- Competitive Analysis: typing.List[str] # Provide 5 to 7 competitive products.\n- Competitive Quadrant Chart: # Use mermaid quadrantChart syntax. Distribute scores evenly between 0 and 1\n- Requirement Analysis: # Provide a detailed analysis of the requirements.\n- Requirement Pool: typing.List[typing.List[str]] # List down the top-5 requirements with their priority (P0, P1, P2).\n- UI Design draft: # Provide a simple description of UI elements, functions, style, and layout.\n- Anything UNCLEAR: # Mention any aspects of the project that are unclear and try to clarify them.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Language\": \"zh_cn\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"开发一个基于大语言模型与私有知识库的搜索引擎,希望可以基于大语言模型进行搜索总结\",\n \"Product Goals\": [],\n \"User Stories\": [],\n \"Competitive Analysis\": [],\n \"Competitive Quadrant Chart\": \"\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [],\n \"UI Design draft\": \"\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]", + "\n## context\n\n### Project Name\n20240111154819\n\n### Original Requirements\n['']\n\n### Search Information\n-\n\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Language\": \"en_us\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"Create a 2048 game\",\n \"Product Goals\": [\n \"Create an engaging user experience\",\n \"Improve accessibility, be responsive\",\n \"More beautiful UI\"\n ],\n \"User Stories\": [\n \"As a player, I want to be able to choose difficulty levels\",\n \"As a player, I want to see my score after each game\",\n \"As a player, I want to get restart button when I lose\",\n \"As a player, I want to see beautiful UI that make me feel good\",\n \"As a player, I want to play game via mobile phone\"\n ],\n \"Competitive Analysis\": [\n \"2048 Game A: Simple interface, lacks responsive features\",\n \"play2048.co: Beautiful and responsive UI with my best score shown\",\n \"2048game.com: Responsive UI with my best score shown, but many ads\"\n ],\n \"Competitive Quadrant Chart\": \"quadrantChart\\n title \\\"Reach and engagement of campaigns\\\"\\n x-axis \\\"Low Reach\\\" --> \\\"High Reach\\\"\\n y-axis \\\"Low Engagement\\\" --> \\\"High Engagement\\\"\\n quadrant-1 \\\"We should expand\\\"\\n quadrant-2 \\\"Need to promote\\\"\\n quadrant-3 \\\"Re-evaluate\\\"\\n quadrant-4 \\\"May be improved\\\"\\n \\\"Campaign A\\\": [0.3, 0.6]\\n \\\"Campaign B\\\": [0.45, 0.23]\\n \\\"Campaign C\\\": [0.57, 0.69]\\n \\\"Campaign D\\\": [0.78, 0.34]\\n \\\"Campaign E\\\": [0.40, 0.34]\\n \\\"Campaign F\\\": [0.35, 0.78]\\n \\\"Our Target Product\\\": [0.5, 0.6]\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [\n [\n \"P0\",\n \"The main code ...\"\n ],\n [\n \"P0\",\n \"The game algorithm ...\"\n ]\n ],\n \"UI Design draft\": \"Basic function description with a simple style and layout.\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Language: # Provide the language used in the project, typically matching the user's requirement language.\n- Programming Language: # Python/JavaScript or other mainstream programming language.\n- Original Requirements: # Place the original user's requirements here.\n- Product Goals: typing.List[str] # Provide up to three clear, orthogonal product goals.\n- User Stories: typing.List[str] # Provide up to 3 to 5 scenario-based user stories.\n- Competitive Analysis: typing.List[str] # Provide 5 to 7 competitive products.\n- Competitive Quadrant Chart: # Use mermaid quadrantChart syntax. Distribute scores evenly between 0 and 1\n- Requirement Analysis: # Provide a detailed analysis of the requirements.\n- Requirement Pool: typing.List[typing.List[str]] # List down the top-5 requirements with their priority (P0, P1, P2).\n- UI Design draft: # Provide a simple description of UI elements, functions, style, and layout.\n- Anything UNCLEAR: # Mention any aspects of the project that are unclear and try to clarify them.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Language\": \"en_us\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"\",\n \"Product Goals\": [],\n \"User Stories\": [],\n \"Competitive Analysis\": [],\n \"Competitive Quadrant Chart\": \"\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [],\n \"UI Design draft\": \"\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]", + "\n## context\n\n### Project Name\n20240111180901\n\n### Original Requirements\n['需要一个基于LLM做总结的搜索引擎']\n\n### Search Information\n-\n\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Language\": \"en_us\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"Create a 2048 game\",\n \"Product Goals\": [\n \"Create an engaging user experience\",\n \"Improve accessibility, be responsive\",\n \"More beautiful UI\"\n ],\n \"User Stories\": [\n \"As a player, I want to be able to choose difficulty levels\",\n \"As a player, I want to see my score after each game\",\n \"As a player, I want to get restart button when I lose\",\n \"As a player, I want to see beautiful UI that make me feel good\",\n \"As a player, I want to play game via mobile phone\"\n ],\n \"Competitive Analysis\": [\n \"2048 Game A: Simple interface, lacks responsive features\",\n \"play2048.co: Beautiful and responsive UI with my best score shown\",\n \"2048game.com: Responsive UI with my best score shown, but many ads\"\n ],\n \"Competitive Quadrant Chart\": \"quadrantChart\\n title \\\"Reach and engagement of campaigns\\\"\\n x-axis \\\"Low Reach\\\" --> \\\"High Reach\\\"\\n y-axis \\\"Low Engagement\\\" --> \\\"High Engagement\\\"\\n quadrant-1 \\\"We should expand\\\"\\n quadrant-2 \\\"Need to promote\\\"\\n quadrant-3 \\\"Re-evaluate\\\"\\n quadrant-4 \\\"May be improved\\\"\\n \\\"Campaign A\\\": [0.3, 0.6]\\n \\\"Campaign B\\\": [0.45, 0.23]\\n \\\"Campaign C\\\": [0.57, 0.69]\\n \\\"Campaign D\\\": [0.78, 0.34]\\n \\\"Campaign E\\\": [0.40, 0.34]\\n \\\"Campaign F\\\": [0.35, 0.78]\\n \\\"Our Target Product\\\": [0.5, 0.6]\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [\n [\n \"P0\",\n \"The main code ...\"\n ],\n [\n \"P0\",\n \"The game algorithm ...\"\n ]\n ],\n \"UI Design draft\": \"Basic function description with a simple style and layout.\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Language: # Provide the language used in the project, typically matching the user's requirement language.\n- Programming Language: # Python/JavaScript or other mainstream programming language.\n- Original Requirements: # Place the original user's requirements here.\n- Product Goals: typing.List[str] # Provide up to three clear, orthogonal product goals.\n- User Stories: typing.List[str] # Provide up to 3 to 5 scenario-based user stories.\n- Competitive Analysis: typing.List[str] # Provide 5 to 7 competitive products.\n- Competitive Quadrant Chart: # Use mermaid quadrantChart syntax. Distribute scores evenly between 0 and 1\n- Requirement Analysis: # Provide a detailed analysis of the requirements.\n- Requirement Pool: typing.List[typing.List[str]] # List down the top-5 requirements with their priority (P0, P1, P2).\n- UI Design draft: # Provide a simple description of UI elements, functions, style, and layout.\n- Anything UNCLEAR: # Mention any aspects of the project that are unclear and try to clarify them.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Language\": \"zh_cn\",\n \"Programming Language\": \"LLM\",\n \"Original Requirements\": \"需要一个基于LLM做总结的搜索引擎\",\n \"Product Goals\": [],\n \"User Stories\": [],\n \"Competitive Analysis\": [],\n \"Competitive Quadrant Chart\": \"\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [],\n \"UI Design draft\": \"\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]", + "\n## context\n\n### Project Name\n20240111181214\n\n### Original Requirements\n['需要一个基于LLM做总结的搜索引擎']\n\n### Search Information\n-\n\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Language\": \"en_us\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"Create a 2048 game\",\n \"Product Goals\": [\n \"Create an engaging user experience\",\n \"Improve accessibility, be responsive\",\n \"More beautiful UI\"\n ],\n \"User Stories\": [\n \"As a player, I want to be able to choose difficulty levels\",\n \"As a player, I want to see my score after each game\",\n \"As a player, I want to get restart button when I lose\",\n \"As a player, I want to see beautiful UI that make me feel good\",\n \"As a player, I want to play game via mobile phone\"\n ],\n \"Competitive Analysis\": [\n \"2048 Game A: Simple interface, lacks responsive features\",\n \"play2048.co: Beautiful and responsive UI with my best score shown\",\n \"2048game.com: Responsive UI with my best score shown, but many ads\"\n ],\n \"Competitive Quadrant Chart\": \"quadrantChart\\n title \\\"Reach and engagement of campaigns\\\"\\n x-axis \\\"Low Reach\\\" --> \\\"High Reach\\\"\\n y-axis \\\"Low Engagement\\\" --> \\\"High Engagement\\\"\\n quadrant-1 \\\"We should expand\\\"\\n quadrant-2 \\\"Need to promote\\\"\\n quadrant-3 \\\"Re-evaluate\\\"\\n quadrant-4 \\\"May be improved\\\"\\n \\\"Campaign A\\\": [0.3, 0.6]\\n \\\"Campaign B\\\": [0.45, 0.23]\\n \\\"Campaign C\\\": [0.57, 0.69]\\n \\\"Campaign D\\\": [0.78, 0.34]\\n \\\"Campaign E\\\": [0.40, 0.34]\\n \\\"Campaign F\\\": [0.35, 0.78]\\n \\\"Our Target Product\\\": [0.5, 0.6]\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [\n [\n \"P0\",\n \"The main code ...\"\n ],\n [\n \"P0\",\n \"The game algorithm ...\"\n ]\n ],\n \"UI Design draft\": \"Basic function description with a simple style and layout.\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Language: # Provide the language used in the project, typically matching the user's requirement language.\n- Programming Language: # Python/JavaScript or other mainstream programming language.\n- Original Requirements: # Place the original user's requirements here.\n- Product Goals: typing.List[str] # Provide up to three clear, orthogonal product goals.\n- User Stories: typing.List[str] # Provide up to 3 to 5 scenario-based user stories.\n- Competitive Analysis: typing.List[str] # Provide 5 to 7 competitive products.\n- Competitive Quadrant Chart: # Use mermaid quadrantChart syntax. Distribute scores evenly between 0 and 1\n- Requirement Analysis: # Provide a detailed analysis of the requirements.\n- Requirement Pool: typing.List[typing.List[str]] # List down the top-5 requirements with their priority (P0, P1, P2).\n- UI Design draft: # Provide a simple description of UI elements, functions, style, and layout.\n- Anything UNCLEAR: # Mention any aspects of the project that are unclear and try to clarify them.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Language\": \"zh_cn\",\n \"Programming Language\": \"LLM\",\n \"Original Requirements\": \"需要一个基于LLM做总结的搜索引擎\",\n \"Product Goals\": [],\n \"User Stories\": [],\n \"Competitive Analysis\": [],\n \"Competitive Quadrant Chart\": \"\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [],\n \"UI Design draft\": \"\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]", + "\n## context\n\n### Project Name\n20240111181426\n\n### Original Requirements\n['开发一个基于大语言模型与私有知识库的搜索引擎,希望可以基于大语言模型进行搜索总结']\n\n### Search Information\n-\n\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Language\": \"en_us\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"Create a 2048 game\",\n \"Product Goals\": [\n \"Create an engaging user experience\",\n \"Improve accessibility, be responsive\",\n \"More beautiful UI\"\n ],\n \"User Stories\": [\n \"As a player, I want to be able to choose difficulty levels\",\n \"As a player, I want to see my score after each game\",\n \"As a player, I want to get restart button when I lose\",\n \"As a player, I want to see beautiful UI that make me feel good\",\n \"As a player, I want to play game via mobile phone\"\n ],\n \"Competitive Analysis\": [\n \"2048 Game A: Simple interface, lacks responsive features\",\n \"play2048.co: Beautiful and responsive UI with my best score shown\",\n \"2048game.com: Responsive UI with my best score shown, but many ads\"\n ],\n \"Competitive Quadrant Chart\": \"quadrantChart\\n title \\\"Reach and engagement of campaigns\\\"\\n x-axis \\\"Low Reach\\\" --> \\\"High Reach\\\"\\n y-axis \\\"Low Engagement\\\" --> \\\"High Engagement\\\"\\n quadrant-1 \\\"We should expand\\\"\\n quadrant-2 \\\"Need to promote\\\"\\n quadrant-3 \\\"Re-evaluate\\\"\\n quadrant-4 \\\"May be improved\\\"\\n \\\"Campaign A\\\": [0.3, 0.6]\\n \\\"Campaign B\\\": [0.45, 0.23]\\n \\\"Campaign C\\\": [0.57, 0.69]\\n \\\"Campaign D\\\": [0.78, 0.34]\\n \\\"Campaign E\\\": [0.40, 0.34]\\n \\\"Campaign F\\\": [0.35, 0.78]\\n \\\"Our Target Product\\\": [0.5, 0.6]\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [\n [\n \"P0\",\n \"The main code ...\"\n ],\n [\n \"P0\",\n \"The game algorithm ...\"\n ]\n ],\n \"UI Design draft\": \"Basic function description with a simple style and layout.\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Language: # Provide the language used in the project, typically matching the user's requirement language.\n- Programming Language: # Python/JavaScript or other mainstream programming language.\n- Original Requirements: # Place the original user's requirements here.\n- Product Goals: typing.List[str] # Provide up to three clear, orthogonal product goals.\n- User Stories: typing.List[str] # Provide up to 3 to 5 scenario-based user stories.\n- Competitive Analysis: typing.List[str] # Provide 5 to 7 competitive products.\n- Competitive Quadrant Chart: # Use mermaid quadrantChart syntax. Distribute scores evenly between 0 and 1\n- Requirement Analysis: # Provide a detailed analysis of the requirements.\n- Requirement Pool: typing.List[typing.List[str]] # List down the top-5 requirements with their priority (P0, P1, P2).\n- UI Design draft: # Provide a simple description of UI elements, functions, style, and layout.\n- Anything UNCLEAR: # Mention any aspects of the project that are unclear and try to clarify them.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Language\": \"zh_cn\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"开发一个基于大语言模型与私有知识库的搜索引擎,希望可以基于大语言模型进行搜索总结\",\n \"Product Goals\": [\n \"提供高效的搜索功能\",\n \"整合私有知识库\",\n \"生成准确的搜索总结\"\n ],\n \"User Stories\": [\n \"作为用户,我希望能够快速找到所需信息\",\n \"作为用户,我希望搜索结果能够涵盖私有知识库内容\",\n \"作为用户,我希望搜索总结能够准确反映所需信息\"\n ],\n \"Competitive Analysis\": [\n \"搜索引擎A:搜索速度快,但不支持私有知识库整合\",\n \"搜索引擎B:支持私有知识库整合,但搜索总结不够准确\",\n \"搜索引擎C:准确的搜索总结,但不支持私有知识库整合\"\n ],\n \"Competitive Quadrant Chart\": \"quadrantChart\\n title \\\"搜索引擎比较\\\"\\n x-axis \\\"低速度\\\" --> \\\"高速度\\\"\\n y-axis \\\"低准确性\\\" --> \\\"高准确性\\\"\\n quadrant-1 \\\"速度快,准确性低\\\"\\n quadrant-2 \\\"速度慢,准确性低\\\"\\n quadrant-3 \\\"速度慢,准确性高\\\"\\n quadrant-4 \\\"速度快,准确性高\\\"\\n \\\"搜索引擎A\\\": [0.8, 0.3]\\n \\\"搜索引擎B\\\": [0.4, 0.2]\\n \\\"搜索引擎C\\\": [0.2, 0.9]\\n \\\"我们的目标产品\\\": [0.7, 0.8]\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [\n [\n \"P0\",\n \"基于大语言模型的搜索功能\"\n ],\n [\n \"P0\",\n \"私有知识库整合\"\n ],\n [\n \"P1\",\n \"搜索总结生成\"\n ]\n ],\n \"UI Design draft\": \"简洁的搜索界面,包含私有知识库搜索选项。\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]", + "\n## context\n\n### Project Name\n20240111181426\n\n### Original Requirements\n['']\n\n### Search Information\n-\n\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Language\": \"en_us\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"Create a 2048 game\",\n \"Product Goals\": [\n \"Create an engaging user experience\",\n \"Improve accessibility, be responsive\",\n \"More beautiful UI\"\n ],\n \"User Stories\": [\n \"As a player, I want to be able to choose difficulty levels\",\n \"As a player, I want to see my score after each game\",\n \"As a player, I want to get restart button when I lose\",\n \"As a player, I want to see beautiful UI that make me feel good\",\n \"As a player, I want to play game via mobile phone\"\n ],\n \"Competitive Analysis\": [\n \"2048 Game A: Simple interface, lacks responsive features\",\n \"play2048.co: Beautiful and responsive UI with my best score shown\",\n \"2048game.com: Responsive UI with my best score shown, but many ads\"\n ],\n \"Competitive Quadrant Chart\": \"quadrantChart\\n title \\\"Reach and engagement of campaigns\\\"\\n x-axis \\\"Low Reach\\\" --> \\\"High Reach\\\"\\n y-axis \\\"Low Engagement\\\" --> \\\"High Engagement\\\"\\n quadrant-1 \\\"We should expand\\\"\\n quadrant-2 \\\"Need to promote\\\"\\n quadrant-3 \\\"Re-evaluate\\\"\\n quadrant-4 \\\"May be improved\\\"\\n \\\"Campaign A\\\": [0.3, 0.6]\\n \\\"Campaign B\\\": [0.45, 0.23]\\n \\\"Campaign C\\\": [0.57, 0.69]\\n \\\"Campaign D\\\": [0.78, 0.34]\\n \\\"Campaign E\\\": [0.40, 0.34]\\n \\\"Campaign F\\\": [0.35, 0.78]\\n \\\"Our Target Product\\\": [0.5, 0.6]\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [\n [\n \"P0\",\n \"The main code ...\"\n ],\n [\n \"P0\",\n \"The game algorithm ...\"\n ]\n ],\n \"UI Design draft\": \"Basic function description with a simple style and layout.\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Language: # Provide the language used in the project, typically matching the user's requirement language.\n- Programming Language: # Python/JavaScript or other mainstream programming language.\n- Original Requirements: # Place the original user's requirements here.\n- Product Goals: typing.List[str] # Provide up to three clear, orthogonal product goals.\n- User Stories: typing.List[str] # Provide up to 3 to 5 scenario-based user stories.\n- Competitive Analysis: typing.List[str] # Provide 5 to 7 competitive products.\n- Competitive Quadrant Chart: # Use mermaid quadrantChart syntax. Distribute scores evenly between 0 and 1\n- Requirement Analysis: # Provide a detailed analysis of the requirements.\n- Requirement Pool: typing.List[typing.List[str]] # List down the top-5 requirements with their priority (P0, P1, P2).\n- UI Design draft: # Provide a simple description of UI elements, functions, style, and layout.\n- Anything UNCLEAR: # Mention any aspects of the project that are unclear and try to clarify them.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Language\": \"en_us\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"\",\n \"Product Goals\": [],\n \"User Stories\": [],\n \"Competitive Analysis\": [],\n \"Competitive Quadrant Chart\": \"quadrantChart\\n title \\\"Reach and engagement of campaigns\\\"\\n x-axis \\\"Low Reach\\\" --> \\\"High Reach\\\"\\n y-axis \\\"Low Engagement\\\" --> \\\"High Engagement\\\"\\n quadrant-1 \\\"We should expand\\\"\\n quadrant-2 \\\"Need to promote\\\"\\n quadrant-3 \\\"Re-evaluate\\\"\\n quadrant-4 \\\"May be improved\\\"\\n \\\"Campaign A\\\": [0.3, 0.6]\\n \\\"Campaign B\\\": [0.45, 0.23]\\n \\\"Campaign C\\\": [0.57, 0.69]\\n \\\"Campaign D\\\": [0.78, 0.34]\\n \\\"Campaign E\\\": [0.40, 0.34]\\n \\\"Campaign F\\\": [0.35, 0.78]\\n \\\"Our Target Product\\\": [0.5, 0.6]\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [],\n \"UI Design draft\": \"Basic function description with a simple style and layout.\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]", + "## History Messages\n0: Human: Topic: climate change. Under 80 words per message.\n\n## Actions\nLanguage: Please use the same language as Human INPUT.\nSay your opinion with emotion and don't repeat it": "I believe that climate change is a critical issue that requires urgent action. It's alarming to see the impact of human activities on the environment and the devastating consequences it has on ecosystems and communities. We need to prioritize sustainable practices and reduce our carbon footprint to mitigate the effects of climate change. It's essential for the well-being of future generations and the health of our planet.", + "## History Messages\n0: Alex(Democratic candidate): I believe that climate change is a critical issue that requires urgent action. It's alarming to see the impact of human activities on the environment and the devastating consequences it has on ecosystems and communities. We need to prioritize sustainable practices and reduce our carbon footprint to mitigate the effects of climate change. It's essential for the well-being of future generations and the health of our planet.\n\n## Actions\nLanguage: Please use the same language as Human INPUT.\nSay your opinion with emotion and don't repeat it": "I completely agree with Alex. Climate change is a critical issue that demands immediate attention. The impact of human activities on the environment is devastating, and urgent action is necessary to prioritize sustainable practices and reduce our carbon footprint. It's essential for the well-being of future generations and the health of our planet.", + "## History Messages\n0: Bob(Republican candidate): I completely agree with Alex. Climate change is a critical issue that demands immediate attention. The impact of human activities on the environment is devastating, and urgent action is necessary to prioritize sustainable practices and reduce our carbon footprint. It's essential for the well-being of future generations and the health of our planet.\n1: Alex(Democratic candidate): I believe that climate change is a critical issue that requires urgent action. It's alarming to see the impact of human activities on the environment and the devastating consequences it has on ecosystems and communities. We need to prioritize sustainable practices and reduce our carbon footprint to mitigate the effects of climate change. It's essential for the well-being of future generations and the health of our planet.\n2: Human: Topic: climate change. Under 80 words per message.\n\n## Actions\nLanguage: Please use the same language as Human INPUT.\nSay your opinion with emotion and don't repeat it": "I completely agree with both Bob and Alex. Climate change is a critical issue that demands immediate attention. The impact of human activities on the environment is devastating, and urgent action is necessary to prioritize sustainable practices and reduce our carbon footprint. It's essential for the well-being of future generations and the health of our planet.", + "\n## context\n\n### Project Name\n20240112110621\n\n### Original Requirements\n['需要一个基于LLM做总结的搜索引擎']\n\n### Search Information\n-\n\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Language\": \"en_us\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"Create a 2048 game\",\n \"Product Goals\": [\n \"Create an engaging user experience\",\n \"Improve accessibility, be responsive\",\n \"More beautiful UI\"\n ],\n \"User Stories\": [\n \"As a player, I want to be able to choose difficulty levels\",\n \"As a player, I want to see my score after each game\",\n \"As a player, I want to get restart button when I lose\",\n \"As a player, I want to see beautiful UI that make me feel good\",\n \"As a player, I want to play game via mobile phone\"\n ],\n \"Competitive Analysis\": [\n \"2048 Game A: Simple interface, lacks responsive features\",\n \"play2048.co: Beautiful and responsive UI with my best score shown\",\n \"2048game.com: Responsive UI with my best score shown, but many ads\"\n ],\n \"Competitive Quadrant Chart\": \"quadrantChart\\n title \\\"Reach and engagement of campaigns\\\"\\n x-axis \\\"Low Reach\\\" --> \\\"High Reach\\\"\\n y-axis \\\"Low Engagement\\\" --> \\\"High Engagement\\\"\\n quadrant-1 \\\"We should expand\\\"\\n quadrant-2 \\\"Need to promote\\\"\\n quadrant-3 \\\"Re-evaluate\\\"\\n quadrant-4 \\\"May be improved\\\"\\n \\\"Campaign A\\\": [0.3, 0.6]\\n \\\"Campaign B\\\": [0.45, 0.23]\\n \\\"Campaign C\\\": [0.57, 0.69]\\n \\\"Campaign D\\\": [0.78, 0.34]\\n \\\"Campaign E\\\": [0.40, 0.34]\\n \\\"Campaign F\\\": [0.35, 0.78]\\n \\\"Our Target Product\\\": [0.5, 0.6]\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [\n [\n \"P0\",\n \"The main code ...\"\n ],\n [\n \"P0\",\n \"The game algorithm ...\"\n ]\n ],\n \"UI Design draft\": \"Basic function description with a simple style and layout.\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Language: # Provide the language used in the project, typically matching the user's requirement language.\n- Programming Language: # Python/JavaScript or other mainstream programming language.\n- Original Requirements: # Place the original user's requirements here.\n- Product Goals: typing.List[str] # Provide up to three clear, orthogonal product goals.\n- User Stories: typing.List[str] # Provide up to 3 to 5 scenario-based user stories.\n- Competitive Analysis: typing.List[str] # Provide 5 to 7 competitive products.\n- Competitive Quadrant Chart: # Use mermaid quadrantChart syntax. Distribute scores evenly between 0 and 1\n- Requirement Analysis: # Provide a detailed analysis of the requirements.\n- Requirement Pool: typing.List[typing.List[str]] # List down the top-5 requirements with their priority (P0, P1, P2).\n- UI Design draft: # Provide a simple description of UI elements, functions, style, and layout.\n- Anything UNCLEAR: # Mention any aspects of the project that are unclear and try to clarify them.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Language\": \"zh_cn\",\n \"Programming Language\": \"LLM\",\n \"Original Requirements\": \"需要一个基于LLM做总结的搜索引擎\",\n \"Product Goals\": [],\n \"User Stories\": [],\n \"Competitive Analysis\": [],\n \"Competitive Quadrant Chart\": \"\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [],\n \"UI Design draft\": \"\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]", + "You are a python code to Mermaid Sequence Diagram translator in function detail#SYSTEM_MSG_END#```python\n#!/usr/bin/env python\n# -*- coding: utf-8 -*-\nimport asyncio\nimport shutil\nfrom pathlib import Path\n\nimport typer\n\nfrom metagpt.config2 import config\nfrom metagpt.const import CONFIG_ROOT, METAGPT_ROOT\n\napp = typer.Typer(add_completion=False, pretty_exceptions_show_locals=False)\n\n\ndef generate_repo(\n idea,\n investment,\n n_round,\n code_review,\n run_tests,\n implement,\n project_name,\n inc,\n project_path,\n reqa_file,\n max_auto_summarize_code,\n recover_path,\n):\n \"\"\"Run the startup logic. Can be called from CLI or other Python scripts.\"\"\"\n from metagpt.roles import (\n Architect,\n Engineer,\n ProductManager,\n ProjectManager,\n QaEngineer,\n )\n from metagpt.team import Team\n\n config.update_via_cli(project_path, project_name, inc, reqa_file, max_auto_summarize_code)\n\n if not recover_path:\n company = Team()\n company.hire(\n [\n ProductManager(),\n Architect(),\n ProjectManager(),\n ]\n )\n\n if implement or code_review:\n company.hire([Engineer(n_borg=5, use_code_review=code_review)])\n\n if run_tests:\n company.hire([QaEngineer()])\n else:\n stg_path = Path(recover_path)\n if not stg_path.exists() or not str(stg_path).endswith(\"team\"):\n raise FileNotFoundError(f\"{recover_path} not exists or not endswith `team`\")\n\n company = Team.deserialize(stg_path=stg_path)\n idea = company.idea\n\n company.invest(investment)\n company.run_project(idea)\n asyncio.run(company.run(n_round=n_round))\n\n\n@app.command(\"\", help=\"Start a new project.\")\ndef startup(\n idea: str = typer.Argument(None, help=\"Your innovative idea, such as 'Create a 2048 game.'\"),\n investment: float = typer.Option(default=3.0, help=\"Dollar amount to invest in the AI company.\"),\n n_round: int = typer.Option(default=5, help=\"Number of rounds for the simulation.\"),\n code_review: bool = typer.Option(default=True, help=\"Whether to use code review.\"),\n run_tests: bool = typer.Option(default=False, help=\"Whether to enable QA for adding & running tests.\"),\n implement: bool = typer.Option(default=True, help=\"Enable or disable code implementation.\"),\n project_name: str = typer.Option(default=\"\", help=\"Unique project name, such as 'game_2048'.\"),\n inc: bool = typer.Option(default=False, help=\"Incremental mode. Use it to coop with existing repo.\"),\n project_path: str = typer.Option(\n default=\"\",\n help=\"Specify the directory path of the old version project to fulfill the incremental requirements.\",\n ),\n reqa_file: str = typer.Option(\n default=\"\", help=\"Specify the source file name for rewriting the quality assurance code.\"\n ),\n max_auto_summarize_code: int = typer.Option(\n default=0,\n help=\"The maximum number of times the 'SummarizeCode' action is automatically invoked, with -1 indicating \"\n \"unlimited. This parameter is used for debugging the workflow.\",\n ),\n recover_path: str = typer.Option(default=None, help=\"recover the project from existing serialized storage\"),\n init_config: bool = typer.Option(default=False, help=\"Initialize the configuration file for MetaGPT.\"),\n):\n \"\"\"Run a startup. Be a boss.\"\"\"\n if init_config:\n copy_config_to()\n return\n\n if idea is None:\n typer.echo(\"Missing argument 'IDEA'. Run 'metagpt --help' for more information.\")\n raise typer.Exit()\n\n return generate_repo(\n idea,\n investment,\n n_round,\n code_review,\n run_tests,\n implement,\n project_name,\n inc,\n project_path,\n reqa_file,\n max_auto_summarize_code,\n recover_path,\n )\n\n\ndef copy_config_to(config_path=METAGPT_ROOT / \"config\" / \"config2.yaml\"):\n \"\"\"Initialize the configuration file for MetaGPT.\"\"\"\n target_path = CONFIG_ROOT / \"config2.yaml\"\n\n # 创建目标目录(如果不存在)\n target_path.parent.mkdir(parents=True, exist_ok=True)\n\n # 如果目标文件已经存在,则重命名为 .bak\n if target_path.exists():\n backup_path = target_path.with_suffix(\".bak\")\n target_path.rename(backup_path)\n print(f\"Existing configuration file backed up at {backup_path}\")\n\n # 复制文件\n shutil.copy(str(config_path), target_path)\n print(f\"Configuration file initialized at {target_path}\")\n\n\nif __name__ == \"__main__\":\n app()\n\n```\n\n---\nTranslate the code above into Mermaid Sequence Diagram.": "```mermaid\nsequenceDiagram\n participant app\n participant generate_repo\n participant copy_config_to\n participant Team\n participant ProductManager\n participant Architect\n participant ProjectManager\n participant Engineer\n participant QaEngineer\n\n app -> generate_repo: startup()\n generate_repo -> config: update_via_cli()\n generate_repo -> Team: hire()\n Team -> ProductManager: hire()\n Team -> Architect: hire()\n Team -> ProjectManager: hire()\n generate_repo -> Engineer: hire()\n generate_repo -> QaEngineer: hire()\n generate_repo -> Team: invest()\n generate_repo -> Team: run_project()\n generate_repo -> Team: run()\n\n app -> copy_config_to: copy_config_to()\n copy_config_to -> config: update_via_cli()\n```", + "You are a python code to Mermaid Sequence Diagram translator in function detail#SYSTEM_MSG_END#```python\n#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\"\"\"\n@Time : 2023/12/14 11:40\n@Author : alexanderwu\n@File : write_prd_an.py\n\"\"\"\nfrom typing import List\n\nfrom metagpt.actions.action_node import ActionNode\n\nLANGUAGE = ActionNode(\n key=\"Language\",\n expected_type=str,\n instruction=\"Provide the language used in the project, typically matching the user's requirement language.\",\n example=\"en_us\",\n)\n\nPROGRAMMING_LANGUAGE = ActionNode(\n key=\"Programming Language\",\n expected_type=str,\n instruction=\"Python/JavaScript or other mainstream programming language.\",\n example=\"Python\",\n)\n\nORIGINAL_REQUIREMENTS = ActionNode(\n key=\"Original Requirements\",\n expected_type=str,\n instruction=\"Place the original user's requirements here.\",\n example=\"Create a 2048 game\",\n)\n\nPROJECT_NAME = ActionNode(\n key=\"Project Name\",\n expected_type=str,\n instruction='According to the content of \"Original Requirements,\" name the project using snake case style , '\n \"like 'game_2048' or 'simple_crm.\",\n example=\"game_2048\",\n)\n\nPRODUCT_GOALS = ActionNode(\n key=\"Product Goals\",\n expected_type=List[str],\n instruction=\"Provide up to three clear, orthogonal product goals.\",\n example=[\"Create an engaging user experience\", \"Improve accessibility, be responsive\", \"More beautiful UI\"],\n)\n\nUSER_STORIES = ActionNode(\n key=\"User Stories\",\n expected_type=List[str],\n instruction=\"Provide up to 3 to 5 scenario-based user stories.\",\n example=[\n \"As a player, I want to be able to choose difficulty levels\",\n \"As a player, I want to see my score after each game\",\n \"As a player, I want to get restart button when I lose\",\n \"As a player, I want to see beautiful UI that make me feel good\",\n \"As a player, I want to play game via mobile phone\",\n ],\n)\n\nCOMPETITIVE_ANALYSIS = ActionNode(\n key=\"Competitive Analysis\",\n expected_type=List[str],\n instruction=\"Provide 5 to 7 competitive products.\",\n example=[\n \"2048 Game A: Simple interface, lacks responsive features\",\n \"play2048.co: Beautiful and responsive UI with my best score shown\",\n \"2048game.com: Responsive UI with my best score shown, but many ads\",\n ],\n)\n\nCOMPETITIVE_QUADRANT_CHART = ActionNode(\n key=\"Competitive Quadrant Chart\",\n expected_type=str,\n instruction=\"Use mermaid quadrantChart syntax. Distribute scores evenly between 0 and 1\",\n example=\"\"\"quadrantChart\n title \"Reach and engagement of campaigns\"\n x-axis \"Low Reach\" --> \"High Reach\"\n y-axis \"Low Engagement\" --> \"High Engagement\"\n quadrant-1 \"We should expand\"\n quadrant-2 \"Need to promote\"\n quadrant-3 \"Re-evaluate\"\n quadrant-4 \"May be improved\"\n \"Campaign A\": [0.3, 0.6]\n \"Campaign B\": [0.45, 0.23]\n \"Campaign C\": [0.57, 0.69]\n \"Campaign D\": [0.78, 0.34]\n \"Campaign E\": [0.40, 0.34]\n \"Campaign F\": [0.35, 0.78]\n \"Our Target Product\": [0.5, 0.6]\"\"\",\n)\n\nREQUIREMENT_ANALYSIS = ActionNode(\n key=\"Requirement Analysis\",\n expected_type=str,\n instruction=\"Provide a detailed analysis of the requirements.\",\n example=\"\",\n)\n\nREQUIREMENT_POOL = ActionNode(\n key=\"Requirement Pool\",\n expected_type=List[List[str]],\n instruction=\"List down the top-5 requirements with their priority (P0, P1, P2).\",\n example=[[\"P0\", \"The main code ...\"], [\"P0\", \"The game algorithm ...\"]],\n)\n\nUI_DESIGN_DRAFT = ActionNode(\n key=\"UI Design draft\",\n expected_type=str,\n instruction=\"Provide a simple description of UI elements, functions, style, and layout.\",\n example=\"Basic function description with a simple style and layout.\",\n)\n\nANYTHING_UNCLEAR = ActionNode(\n key=\"Anything UNCLEAR\",\n expected_type=str,\n instruction=\"Mention any aspects of the project that are unclear and try to clarify them.\",\n example=\"\",\n)\n\nISSUE_TYPE = ActionNode(\n key=\"issue_type\",\n expected_type=str,\n instruction=\"Answer BUG/REQUIREMENT. If it is a bugfix, answer BUG, otherwise answer Requirement\",\n example=\"BUG\",\n)\n\nIS_RELATIVE = ActionNode(\n key=\"is_relative\",\n expected_type=str,\n instruction=\"Answer YES/NO. If the requirement is related to the old PRD, answer YES, otherwise NO\",\n example=\"YES\",\n)\n\nREASON = ActionNode(\n key=\"reason\", expected_type=str, instruction=\"Explain the reasoning process from question to answer\", example=\"...\"\n)\n\n\nNODES = [\n LANGUAGE,\n PROGRAMMING_LANGUAGE,\n ORIGINAL_REQUIREMENTS,\n PROJECT_NAME,\n PRODUCT_GOALS,\n USER_STORIES,\n COMPETITIVE_ANALYSIS,\n COMPETITIVE_QUADRANT_CHART,\n REQUIREMENT_ANALYSIS,\n REQUIREMENT_POOL,\n UI_DESIGN_DRAFT,\n ANYTHING_UNCLEAR,\n]\n\nWRITE_PRD_NODE = ActionNode.from_children(\"WritePRD\", NODES)\nWP_ISSUE_TYPE_NODE = ActionNode.from_children(\"WP_ISSUE_TYPE\", [ISSUE_TYPE, REASON])\nWP_IS_RELATIVE_NODE = ActionNode.from_children(\"WP_IS_RELATIVE\", [IS_RELATIVE, REASON])\n\n```\n\n---\nTranslate the code above into Mermaid Sequence Diagram.": "```mermaid\nclassDef actionNode fill:#f9f,stroke:#333,stroke-width:2px;\nclassDef actionNodeTitle fill:#f9f,stroke:#333,stroke-width:2px,font-weight:bold;\nclassDef actionNodeExample fill:#f9f,stroke:#333,stroke-width:2px,font-style:italic;\n\nclass ActionNodeTitle actionNodeTitle\nclass ActionNodeExample actionNodeExample\n\nActionNodeTitle:::Language --> \"Language\"\nActionNodeExample:::Language --> \"Provide the language used in the project, typically matching the user's requirement language.\\nExample: en_us\"\n\nActionNodeTitle:::ProgrammingLanguage --> \"Programming Language\"\nActionNodeExample:::ProgrammingLanguage --> \"Python/JavaScript or other mainstream programming language.\\nExample: Python\"\n\nActionNodeTitle:::OriginalRequirements --> \"Original Requirements\"\nActionNodeExample:::OriginalRequirements --> \"Place the original user's requirements here.\\nExample: Create a 2048 game\"\n\nActionNodeTitle:::ProjectName --> \"Project Name\"\nActionNodeExample:::ProjectName --> 'According to the content of \"Original Requirements,\" name the project using snake case style , like \\'game_2048\\' or \\'simple_crm.\\nExample: game_2048'\n\nActionNodeTitle:::ProductGoals --> \"Product Goals\"\nActionNodeExample:::ProductGoals --> \"Provide up to three clear, orthogonal product goals.\\nExample:\\n- Create an engaging user experience\\n- Improve accessibility, be responsive\\n- More beautiful UI\"\n\nActionNodeTitle:::UserStories --> \"User Stories\"\nActionNodeExample:::UserStories --> \"Provide up to 3 to 5 scenario-based user stories.\\nExample:\\n- As a player, I want to be able to choose difficulty levels\\n- As a player, I want to see my score after each game\\n- As a player, I want to get restart button when I lose\\n- As a player, I want to see beautiful UI that make me feel good\\n- As a player, I want to play game via mobile phone\"\n\nActionNodeTitle:::CompetitiveAnalysis --> \"Competitive Analysis\"\nActionNodeExample:::CompetitiveAnalysis --> \"Provide 5 to 7 competitive products.\\nExample:\\n- 2048 Game A: Simple interface, lacks responsive features\\n- play2048.co: Beautiful and responsive UI with my best score shown\\n- 2048game.com: Responsive UI with my best score shown, but many ads\"\n\nActionNodeTitle:::CompetitiveQuadrantChart --> \"Competitive Quadrant Chart\"\nActionNodeExample:::CompetitiveQuadrantChart --> \"Use mermaid quadrantChart syntax. Distribute scores evenly between 0 and 1\\nExample:\\nquadrantChart\\n title \\\"Reach and engagement of campaigns\\\"\\n x-axis \\\"Low Reach\\\" --> \\\"High Reach\\\"\\n y-axis \\\"Low Engagement\\\" --> \\\"High Engagement\\\"\\n quadrant-1 \\\"We should expand\\\"\\n quadrant-2 \\\"Need to promote\\\"\\n quadrant-3 \\\"Re-evaluate\\\"\\n quadrant-4 \\\"May be improved\\\"\\n \\\"Campaign A\\\": [0.3, 0.6]\\n \\\"Campaign B\\\": [0.45, 0.23]\\n \\\"Campaign C\\\": [0.57, 0.69]\\n \\\"Campaign D\\\": [0.78, 0.34]\\n \\\"Campaign E\\\": [0.40, 0.34]\\n \\\"Campaign F\\\": [0.35, 0.78]\\n \\\"Our Target Product\\\": [0.5, 0.6]\"\n\nActionNodeTitle:::RequirementAnalysis --> \"Requirement Analysis\"\nActionNodeExample:::RequirementAnalysis --> \"Provide a detailed analysis of the requirements.\\nExample: \"\n\nActionNodeTitle:::RequirementPool --> \"Requirement Pool\"\nActionNodeExample:::RequirementPool --> \"List down the top-5 requirements with their priority (P0, P1, P2).\\nExample:\\n- P0: The main code ...\\n- P0: The game algorithm ...\"\n\nActionNodeTitle:::UIDesignDraft --> \"UI Design draft\"\nActionNodeExample:::UIDesignDraft --> \"Provide a simple description of UI elements, functions, style, and layout.\\nExample: Basic function description with a simple style and layout.\"\n\nActionNodeTitle:::AnythingUNCLEAR --> \"Anything UNCLEAR\"\nActionNodeExample:::AnythingUNCLEAR --> \"Mention any aspects of the project that are unclear and try to clarify them.\\nExample: \"\n\nActionNodeTitle:::issue_type --> \"issue_type\"\nActionNodeExample:::issue_type --> \"Answer BUG/REQUIREMENT. If it is a bugfix, answer BUG, otherwise answer Requirement\\nExample: BUG\"\n\nActionNodeTitle:::is_relative --> \"is_relative\"\nActionNodeExample:::is_relative --> \"Answer YES/NO. If the requirement is related to the old PRD, answer YES, otherwise NO\\nExample: YES\"\n\nActionNodeTitle:::reason --> \"reason\"\nActionNodeExample:::reason --> \"Explain the reasoning process from question to answer\\nExample: ...\"\n\nActionNodeTitle:::WritePRD --> \"WritePRD\"\nActionNodeExample:::WritePRD --> \"Language\\nProgramming Language\\nOriginal Requirements\\nProject Name\\nProduct Goals\\nUser Stories\\nCompetitive Analysis\\nCompetitive Quadrant Chart\\nRequirement Analysis\\nRequirement Pool\\nUI Design draft\\nAnything UNCLEAR\"\n\nActionNodeTitle:::WP_ISSUE_TYPE --> \"WP_ISSUE_TYPE\"\nActionNodeExample:::WP_ISSUE_TYPE --> \"issue_type\\nreason\"\n\nActionNodeTitle:::WP_IS_RELATIVE --> \"WP_IS_RELATIVE\"\nActionNodeExample:::WP_IS_RELATIVE --> \"is_relative\\nreason\"\n```", + "\n## context\n\n### Project Name\n20240112110833\n\n### Original Requirements\n['开发一个基于大语言模型与私有知识库的搜索引擎,希望可以基于大语言模型进行搜索总结']\n\n### Search Information\n-\n\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Language\": \"en_us\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"Create a 2048 game\",\n \"Product Goals\": [\n \"Create an engaging user experience\",\n \"Improve accessibility, be responsive\",\n \"More beautiful UI\"\n ],\n \"User Stories\": [\n \"As a player, I want to be able to choose difficulty levels\",\n \"As a player, I want to see my score after each game\",\n \"As a player, I want to get restart button when I lose\",\n \"As a player, I want to see beautiful UI that make me feel good\",\n \"As a player, I want to play game via mobile phone\"\n ],\n \"Competitive Analysis\": [\n \"2048 Game A: Simple interface, lacks responsive features\",\n \"play2048.co: Beautiful and responsive UI with my best score shown\",\n \"2048game.com: Responsive UI with my best score shown, but many ads\"\n ],\n \"Competitive Quadrant Chart\": \"quadrantChart\\n title \\\"Reach and engagement of campaigns\\\"\\n x-axis \\\"Low Reach\\\" --> \\\"High Reach\\\"\\n y-axis \\\"Low Engagement\\\" --> \\\"High Engagement\\\"\\n quadrant-1 \\\"We should expand\\\"\\n quadrant-2 \\\"Need to promote\\\"\\n quadrant-3 \\\"Re-evaluate\\\"\\n quadrant-4 \\\"May be improved\\\"\\n \\\"Campaign A\\\": [0.3, 0.6]\\n \\\"Campaign B\\\": [0.45, 0.23]\\n \\\"Campaign C\\\": [0.57, 0.69]\\n \\\"Campaign D\\\": [0.78, 0.34]\\n \\\"Campaign E\\\": [0.40, 0.34]\\n \\\"Campaign F\\\": [0.35, 0.78]\\n \\\"Our Target Product\\\": [0.5, 0.6]\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [\n [\n \"P0\",\n \"The main code ...\"\n ],\n [\n \"P0\",\n \"The game algorithm ...\"\n ]\n ],\n \"UI Design draft\": \"Basic function description with a simple style and layout.\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Language: # Provide the language used in the project, typically matching the user's requirement language.\n- Programming Language: # Python/JavaScript or other mainstream programming language.\n- Original Requirements: # Place the original user's requirements here.\n- Product Goals: typing.List[str] # Provide up to three clear, orthogonal product goals.\n- User Stories: typing.List[str] # Provide up to 3 to 5 scenario-based user stories.\n- Competitive Analysis: typing.List[str] # Provide 5 to 7 competitive products.\n- Competitive Quadrant Chart: # Use mermaid quadrantChart syntax. Distribute scores evenly between 0 and 1\n- Requirement Analysis: # Provide a detailed analysis of the requirements.\n- Requirement Pool: typing.List[typing.List[str]] # List down the top-5 requirements with their priority (P0, P1, P2).\n- UI Design draft: # Provide a simple description of UI elements, functions, style, and layout.\n- Anything UNCLEAR: # Mention any aspects of the project that are unclear and try to clarify them.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Language\": \"zh_cn\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"开发一个基于大语言模型与私有知识库的搜索引擎,希望可以基于大语言模型进行搜索总结\",\n \"Product Goals\": [],\n \"User Stories\": [],\n \"Competitive Analysis\": [],\n \"Competitive Quadrant Chart\": \"\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [],\n \"UI Design draft\": \"\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]", + "\n## context\n\n### Project Name\n20240112110833\n\n### Original Requirements\n['']\n\n### Search Information\n-\n\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Language\": \"en_us\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"Create a 2048 game\",\n \"Product Goals\": [\n \"Create an engaging user experience\",\n \"Improve accessibility, be responsive\",\n \"More beautiful UI\"\n ],\n \"User Stories\": [\n \"As a player, I want to be able to choose difficulty levels\",\n \"As a player, I want to see my score after each game\",\n \"As a player, I want to get restart button when I lose\",\n \"As a player, I want to see beautiful UI that make me feel good\",\n \"As a player, I want to play game via mobile phone\"\n ],\n \"Competitive Analysis\": [\n \"2048 Game A: Simple interface, lacks responsive features\",\n \"play2048.co: Beautiful and responsive UI with my best score shown\",\n \"2048game.com: Responsive UI with my best score shown, but many ads\"\n ],\n \"Competitive Quadrant Chart\": \"quadrantChart\\n title \\\"Reach and engagement of campaigns\\\"\\n x-axis \\\"Low Reach\\\" --> \\\"High Reach\\\"\\n y-axis \\\"Low Engagement\\\" --> \\\"High Engagement\\\"\\n quadrant-1 \\\"We should expand\\\"\\n quadrant-2 \\\"Need to promote\\\"\\n quadrant-3 \\\"Re-evaluate\\\"\\n quadrant-4 \\\"May be improved\\\"\\n \\\"Campaign A\\\": [0.3, 0.6]\\n \\\"Campaign B\\\": [0.45, 0.23]\\n \\\"Campaign C\\\": [0.57, 0.69]\\n \\\"Campaign D\\\": [0.78, 0.34]\\n \\\"Campaign E\\\": [0.40, 0.34]\\n \\\"Campaign F\\\": [0.35, 0.78]\\n \\\"Our Target Product\\\": [0.5, 0.6]\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [\n [\n \"P0\",\n \"The main code ...\"\n ],\n [\n \"P0\",\n \"The game algorithm ...\"\n ]\n ],\n \"UI Design draft\": \"Basic function description with a simple style and layout.\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Language: # Provide the language used in the project, typically matching the user's requirement language.\n- Programming Language: # Python/JavaScript or other mainstream programming language.\n- Original Requirements: # Place the original user's requirements here.\n- Product Goals: typing.List[str] # Provide up to three clear, orthogonal product goals.\n- User Stories: typing.List[str] # Provide up to 3 to 5 scenario-based user stories.\n- Competitive Analysis: typing.List[str] # Provide 5 to 7 competitive products.\n- Competitive Quadrant Chart: # Use mermaid quadrantChart syntax. Distribute scores evenly between 0 and 1\n- Requirement Analysis: # Provide a detailed analysis of the requirements.\n- Requirement Pool: typing.List[typing.List[str]] # List down the top-5 requirements with their priority (P0, P1, P2).\n- UI Design draft: # Provide a simple description of UI elements, functions, style, and layout.\n- Anything UNCLEAR: # Mention any aspects of the project that are unclear and try to clarify them.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Language\": \"en_us\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"\",\n \"Product Goals\": [],\n \"User Stories\": [],\n \"Competitive Analysis\": [],\n \"Competitive Quadrant Chart\": \"quadrantChart\\n title \\\"Reach and engagement of campaigns\\\"\\n x-axis \\\"Low Reach\\\" --> \\\"High Reach\\\"\\n y-axis \\\"Low Engagement\\\" --> \\\"High Engagement\\\"\\n quadrant-1 \\\"We should expand\\\"\\n quadrant-2 \\\"Need to promote\\\"\\n quadrant-3 \\\"Re-evaluate\\\"\\n quadrant-4 \\\"May be improved\\\"\\n \\\"Campaign A\\\": [0.3, 0.6]\\n \\\"Campaign B\\\": [0.45, 0.23]\\n \\\"Campaign C\\\": [0.57, 0.69]\\n \\\"Campaign D\\\": [0.78, 0.34]\\n \\\"Campaign E\\\": [0.40, 0.34]\\n \\\"Campaign F\\\": [0.35, 0.78]\\n \\\"Our Target Product\\\": [0.5, 0.6]\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [],\n \"UI Design draft\": \"Basic function description with a simple style and layout.\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]", + "\n## context\n\n### Project Name\n\n\n### Original Requirements\n需要一个基于LLM做总结的搜索引擎\n\n### Search Information\n-\n\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Language\": \"en_us\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"Create a 2048 game\",\n \"Project Name\": \"game_2048\",\n \"Product Goals\": [\n \"Create an engaging user experience\",\n \"Improve accessibility, be responsive\",\n \"More beautiful UI\"\n ],\n \"User Stories\": [\n \"As a player, I want to be able to choose difficulty levels\",\n \"As a player, I want to see my score after each game\",\n \"As a player, I want to get restart button when I lose\",\n \"As a player, I want to see beautiful UI that make me feel good\",\n \"As a player, I want to play game via mobile phone\"\n ],\n \"Competitive Analysis\": [\n \"2048 Game A: Simple interface, lacks responsive features\",\n \"play2048.co: Beautiful and responsive UI with my best score shown\",\n \"2048game.com: Responsive UI with my best score shown, but many ads\"\n ],\n \"Competitive Quadrant Chart\": \"quadrantChart\\n title \\\"Reach and engagement of campaigns\\\"\\n x-axis \\\"Low Reach\\\" --> \\\"High Reach\\\"\\n y-axis \\\"Low Engagement\\\" --> \\\"High Engagement\\\"\\n quadrant-1 \\\"We should expand\\\"\\n quadrant-2 \\\"Need to promote\\\"\\n quadrant-3 \\\"Re-evaluate\\\"\\n quadrant-4 \\\"May be improved\\\"\\n \\\"Campaign A\\\": [0.3, 0.6]\\n \\\"Campaign B\\\": [0.45, 0.23]\\n \\\"Campaign C\\\": [0.57, 0.69]\\n \\\"Campaign D\\\": [0.78, 0.34]\\n \\\"Campaign E\\\": [0.40, 0.34]\\n \\\"Campaign F\\\": [0.35, 0.78]\\n \\\"Our Target Product\\\": [0.5, 0.6]\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [\n [\n \"P0\",\n \"The main code ...\"\n ],\n [\n \"P0\",\n \"The game algorithm ...\"\n ]\n ],\n \"UI Design draft\": \"Basic function description with a simple style and layout.\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Language: # Provide the language used in the project, typically matching the user's requirement language.\n- Programming Language: # Python/JavaScript or other mainstream programming language.\n- Original Requirements: # Place the original user's requirements here.\n- Project Name: # According to the content of \"Original Requirements,\" name the project using snake case style , like 'game_2048' or 'simple_crm.\n- Product Goals: typing.List[str] # Provide up to three clear, orthogonal product goals.\n- User Stories: typing.List[str] # Provide up to 3 to 5 scenario-based user stories.\n- Competitive Analysis: typing.List[str] # Provide 5 to 7 competitive products.\n- Competitive Quadrant Chart: # Use mermaid quadrantChart syntax. Distribute scores evenly between 0 and 1\n- Requirement Analysis: # Provide a detailed analysis of the requirements.\n- Requirement Pool: typing.List[typing.List[str]] # List down the top-5 requirements with their priority (P0, P1, P2).\n- UI Design draft: # Provide a simple description of UI elements, functions, style, and layout.\n- Anything UNCLEAR: # Mention any aspects of the project that are unclear and try to clarify them.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Language\": \"zh_cn\",\n \"Programming Language\": \"LLM\",\n \"Original Requirements\": \"需要一个基于LLM做总结的搜索引擎\",\n \"Project Name\": \"llm_summary_search_engine\",\n \"Product Goals\": [\n \"提供准确的搜索结果\",\n \"提高搜索引擎的效率\",\n \"优化用户体验\"\n ],\n \"User Stories\": [\n \"作为用户,我希望能够快速找到我需要的信息\",\n \"作为用户,我希望搜索结果准确无误\",\n \"作为用户,我希望搜索引擎能够智能推荐相关内容\"\n ],\n \"Competitive Analysis\": [\n \"搜索引擎A: 提供准确的搜索结果,但界面简陋\",\n \"搜索引擎B: 界面美观,但搜索结果不够准确\",\n \"搜索引擎C: 搜索结果准确,但速度较慢\"\n ],\n \"Competitive Quadrant Chart\": \"quadrantChart\\n title \\\"搜索引擎比较\\\"\\n x-axis \\\"低准确性\\\" --> \\\"高准确性\\\"\\n y-axis \\\"低速度\\\" --> \\\"高速度\\\"\\n quadrant-1 \\\"需要改进\\\"\\n quadrant-2 \\\"值得推广\\\"\\n quadrant-3 \\\"重新评估\\\"\\n quadrant-4 \\\"需要扩展\\\"\\n \\\"搜索引擎A\\\": [0.8, 0.3]\\n \\\"搜索引擎B\\\": [0.5, 0.7]\\n \\\"搜索引擎C\\\": [0.9, 0.2]\\n \\\"我们的搜索引擎\\\": [0.7, 0.6]\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [\n [\n \"P0\",\n \"实现LLM技术进行文本摘要\"\n ],\n [\n \"P0\",\n \"构建搜索算法,提高搜索效率\"\n ],\n [\n \"P1\",\n \"设计智能推荐系统\"\n ]\n ],\n \"UI Design draft\": \"简洁的搜索框,清晰的搜索结果页面,智能推荐模块\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]", + "\n## context\n\n### Project Name\n\n\n### Original Requirements\nMake a cli snake game\n\n### Search Information\n-\n\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Language\": \"en_us\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"Create a 2048 game\",\n \"Project Name\": \"game_2048\",\n \"Product Goals\": [\n \"Create an engaging user experience\",\n \"Improve accessibility, be responsive\",\n \"More beautiful UI\"\n ],\n \"User Stories\": [\n \"As a player, I want to be able to choose difficulty levels\",\n \"As a player, I want to see my score after each game\",\n \"As a player, I want to get restart button when I lose\",\n \"As a player, I want to see beautiful UI that make me feel good\",\n \"As a player, I want to play game via mobile phone\"\n ],\n \"Competitive Analysis\": [\n \"2048 Game A: Simple interface, lacks responsive features\",\n \"play2048.co: Beautiful and responsive UI with my best score shown\",\n \"2048game.com: Responsive UI with my best score shown, but many ads\"\n ],\n \"Competitive Quadrant Chart\": \"quadrantChart\\n title \\\"Reach and engagement of campaigns\\\"\\n x-axis \\\"Low Reach\\\" --> \\\"High Reach\\\"\\n y-axis \\\"Low Engagement\\\" --> \\\"High Engagement\\\"\\n quadrant-1 \\\"We should expand\\\"\\n quadrant-2 \\\"Need to promote\\\"\\n quadrant-3 \\\"Re-evaluate\\\"\\n quadrant-4 \\\"May be improved\\\"\\n \\\"Campaign A\\\": [0.3, 0.6]\\n \\\"Campaign B\\\": [0.45, 0.23]\\n \\\"Campaign C\\\": [0.57, 0.69]\\n \\\"Campaign D\\\": [0.78, 0.34]\\n \\\"Campaign E\\\": [0.40, 0.34]\\n \\\"Campaign F\\\": [0.35, 0.78]\\n \\\"Our Target Product\\\": [0.5, 0.6]\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [\n [\n \"P0\",\n \"The main code ...\"\n ],\n [\n \"P0\",\n \"The game algorithm ...\"\n ]\n ],\n \"UI Design draft\": \"Basic function description with a simple style and layout.\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Language: # Provide the language used in the project, typically matching the user's requirement language.\n- Programming Language: # Python/JavaScript or other mainstream programming language.\n- Original Requirements: # Place the original user's requirements here.\n- Project Name: # According to the content of \"Original Requirements,\" name the project using snake case style , like 'game_2048' or 'simple_crm.\n- Product Goals: typing.List[str] # Provide up to three clear, orthogonal product goals.\n- User Stories: typing.List[str] # Provide up to 3 to 5 scenario-based user stories.\n- Competitive Analysis: typing.List[str] # Provide 5 to 7 competitive products.\n- Competitive Quadrant Chart: # Use mermaid quadrantChart syntax. Distribute scores evenly between 0 and 1\n- Requirement Analysis: # Provide a detailed analysis of the requirements.\n- Requirement Pool: typing.List[typing.List[str]] # List down the top-5 requirements with their priority (P0, P1, P2).\n- UI Design draft: # Provide a simple description of UI elements, functions, style, and layout.\n- Anything UNCLEAR: # Mention any aspects of the project that are unclear and try to clarify them.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Language\": \"en_us\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"Make a cli snake game\",\n \"Project Name\": \"cli_snake_game\",\n \"Product Goals\": [\n \"Create an engaging user experience\",\n \"Improve accessibility and responsiveness\",\n \"Enhance the game with additional features\"\n ],\n \"User Stories\": [\n \"As a player, I want to control the snake using arrow keys\",\n \"As a player, I want to see my score during the game\",\n \"As a player, I want to have the option to restart the game\",\n \"As a player, I want to see a visually appealing UI\",\n \"As a player, I want to play the game on different platforms\"\n ],\n \"Competitive Analysis\": [\n \"Snake Game A: Simple interface, lacks responsive features\",\n \"SnakeGame.co: Beautiful and responsive UI with high scores displayed\",\n \"SnakeGame.com: Responsive UI with high scores shown, but many ads\"\n ],\n \"Competitive Quadrant Chart\": \"quadrantChart\\n title \\\"Reach and engagement of snake games\\\"\\n x-axis \\\"Low Reach\\\" --> \\\"High Reach\\\"\\n y-axis \\\"Low Engagement\\\" --> \\\"High Engagement\\\"\\n quadrant-1 \\\"We should expand\\\"\\n quadrant-2 \\\"Need to promote\\\"\\n quadrant-3 \\\"Re-evaluate\\\"\\n quadrant-4 \\\"May be improved\\\"\\n \\\"Snake Game A\\\": [0.3, 0.6]\\n \\\"SnakeGame.co\\\": [0.45, 0.23]\\n \\\"SnakeGame.com\\\": [0.57, 0.69]\\n \\\"Our Target Product\\\": [0.5, 0.6]\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [\n [\n \"P0\",\n \"The main code for controlling the snake and game logic\"\n ],\n [\n \"P1\",\n \"Implementing the scoring system and UI\"\n ],\n [\n \"P2\",\n \"Adding platform compatibility and restart functionality\"\n ]\n ],\n \"UI Design draft\": \"The game will have a simple and intuitive UI with clear controls and a visually appealing design.\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]", + "\n## context\n{\"Language\":\"en_us\",\"Programming Language\":\"Python\",\"Original Requirements\":\"Make a cli snake game\",\"Project Name\":\"cli_snake_game\",\"Product Goals\":[\"Create an engaging user experience\",\"Improve accessibility and responsiveness\",\"Enhance the game with additional features\"],\"User Stories\":[\"As a player, I want to control the snake using arrow keys\",\"As a player, I want to see my score during the game\",\"As a player, I want to have the option to restart the game\",\"As a player, I want to see a visually appealing UI\",\"As a player, I want to play the game on different platforms\"],\"Competitive Analysis\":[\"Snake Game A: Simple interface, lacks responsive features\",\"SnakeGame.co: Beautiful and responsive UI with high scores displayed\",\"SnakeGame.com: Responsive UI with high scores shown, but many ads\"],\"Competitive Quadrant Chart\":\"quadrantChart\\n title \\\"Reach and engagement of snake games\\\"\\n x-axis \\\"Low Reach\\\" --> \\\"High Reach\\\"\\n y-axis \\\"Low Engagement\\\" --> \\\"High Engagement\\\"\\n quadrant-1 \\\"We should expand\\\"\\n quadrant-2 \\\"Need to promote\\\"\\n quadrant-3 \\\"Re-evaluate\\\"\\n quadrant-4 \\\"May be improved\\\"\\n \\\"Snake Game A\\\": [0.3, 0.6]\\n \\\"SnakeGame.co\\\": [0.45, 0.23]\\n \\\"SnakeGame.com\\\": [0.57, 0.69]\\n \\\"Our Target Product\\\": [0.5, 0.6]\",\"Requirement Analysis\":\"\",\"Requirement Pool\":[[\"P0\",\"The main code for controlling the snake and game logic\"],[\"P1\",\"Implementing the scoring system and UI\"],[\"P2\",\"Adding platform compatibility and restart functionality\"]],\"UI Design draft\":\"The game will have a simple and intuitive UI with clear controls and a visually appealing design.\",\"Anything UNCLEAR\":\"\"}\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Implementation approach\": \"We will ...\",\n \"File list\": [\n \"main.py\",\n \"game.py\"\n ],\n \"Data structures and interfaces\": \"\\nclassDiagram\\n class Main {\\n -SearchEngine search_engine\\n +main() str\\n }\\n class SearchEngine {\\n -Index index\\n -Ranking ranking\\n -Summary summary\\n +search(query: str) str\\n }\\n class Index {\\n -KnowledgeBase knowledge_base\\n +create_index(data: dict)\\n +query_index(query: str) list\\n }\\n class Ranking {\\n +rank_results(results: list) list\\n }\\n class Summary {\\n +summarize_results(results: list) str\\n }\\n class KnowledgeBase {\\n +update(data: dict)\\n +fetch_data(query: str) dict\\n }\\n Main --> SearchEngine\\n SearchEngine --> Index\\n SearchEngine --> Ranking\\n SearchEngine --> Summary\\n Index --> KnowledgeBase\\n\",\n \"Program call flow\": \"\\nsequenceDiagram\\n participant M as Main\\n participant SE as SearchEngine\\n participant I as Index\\n participant R as Ranking\\n participant S as Summary\\n participant KB as KnowledgeBase\\n M->>SE: search(query)\\n SE->>I: query_index(query)\\n I->>KB: fetch_data(query)\\n KB-->>I: return data\\n I-->>SE: return results\\n SE->>R: rank_results(results)\\n R-->>SE: return ranked_results\\n SE->>S: summarize_results(ranked_results)\\n S-->>SE: return summary\\n SE-->>M: return summary\\n\",\n \"Anything UNCLEAR\": \"Clarification needed on third-party API integration, ...\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Implementation approach: # Analyze the difficult points of the requirements, select the appropriate open-source framework\n- File list: typing.List[str] # Only need relative paths. ALWAYS write a main.py or app.py here\n- Data structures and interfaces: # Use mermaid classDiagram code syntax, including classes, method(__init__ etc.) and functions with type annotations, CLEARLY MARK the RELATIONSHIPS between classes, and comply with PEP8 standards. The data structures SHOULD BE VERY DETAILED and the API should be comprehensive with a complete design.\n- Program call flow: # Use sequenceDiagram code syntax, COMPLETE and VERY DETAILED, using CLASSES AND API DEFINED ABOVE accurately, covering the CRUD AND INIT of each object, SYNTAX MUST BE CORRECT.\n- Anything UNCLEAR: # Mention unclear project aspects, then try to clarify it.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Implementation approach\": \"We will use the Pygame library to create the game and handle user input. The game logic will be implemented using Python classes and functions.\",\n \"File list\": [\n \"main.py\",\n \"game.py\"\n ],\n \"Data structures and interfaces\": \"\\nclassDiagram\\n class Game {\\n -snake: list\\n -food: tuple\\n -score: int\\n +__init__()\\n +update()\\n +draw()\\n +handle_input()\\n +restart()\\n }\\n class Snake {\\n -body: list\\n -direction: str\\n +__init__(start_pos: tuple, length: int, direction: str)\\n +move()\\n +change_direction(new_direction: str)\\n +grow()\\n }\\n Game --> Snake\\n\",\n \"Program call flow\": \"\\nsequenceDiagram\\n participant M as Main\\n participant G as Game\\n M->>G: initialize game\\n G->>G: update game state\\n G->>G: draw game\\n G->>G: handle user input\\n G->>G: restart game\\n\",\n \"Anything UNCLEAR\": \"Clarification needed on the scoring system and how the game over condition will be handled.\"\n}\n[/CONTENT]", + "\n## context\n{\"Implementation approach\":\"We will use the Pygame library to create the game and handle user input. The game logic will be implemented using Python classes and functions.\",\"File list\":[\"main.py\",\"game.py\"],\"Data structures and interfaces\":\"\\nclassDiagram\\n class Game {\\n -snake: list\\n -food: tuple\\n -score: int\\n +__init__()\\n +update()\\n +draw()\\n +handle_input()\\n +restart()\\n }\\n class Snake {\\n -body: list\\n -direction: str\\n +__init__(start_pos: tuple, length: int, direction: str)\\n +move()\\n +change_direction(new_direction: str)\\n +grow()\\n }\\n Game --> Snake\\n\",\"Program call flow\":\"\\nsequenceDiagram\\n participant M as Main\\n participant G as Game\\n M->>G: initialize game\\n G->>G: update game state\\n G->>G: draw game\\n G->>G: handle user input\\n G->>G: restart game\\n\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Required Python packages\": [\n \"flask==1.1.2\",\n \"bcrypt==3.2.0\"\n ],\n \"Required Other language third-party packages\": [\n \"No third-party dependencies required\"\n ],\n \"Logic Analysis\": [\n [\n \"game.py\",\n \"Contains Game class and ... functions\"\n ],\n [\n \"main.py\",\n \"Contains main function, from game import Game\"\n ]\n ],\n \"Task list\": [\n \"game.py\",\n \"main.py\"\n ],\n \"Full API spec\": \"openapi: 3.0.0 ...\",\n \"Shared Knowledge\": \"'game.py' contains functions shared across the project.\",\n \"Anything UNCLEAR\": \"Clarification needed on how to start and initialize third-party libraries.\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Required Python packages: typing.List[str] # Provide required Python packages in requirements.txt format.\n- Required Other language third-party packages: typing.List[str] # List down the required packages for languages other than Python.\n- Logic Analysis: typing.List[typing.List[str]] # Provide a list of files with the classes/methods/functions to be implemented, including dependency analysis and imports.\n- Task list: typing.List[str] # Break down the tasks into a list of filenames, prioritized by dependency order.\n- Full API spec: # Describe all APIs using OpenAPI 3.0 spec that may be used by both frontend and backend. If front-end and back-end communication is not required, leave it blank.\n- Shared Knowledge: # Detail any shared knowledge, like common utility functions or configuration variables.\n- Anything UNCLEAR: # Mention any unclear aspects in the project management context and try to clarify them.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Required Python packages\": [\n \"pygame==2.0.1\"\n ],\n \"Required Other language third-party packages\": [\n \"No third-party dependencies required\"\n ],\n \"Logic Analysis\": [\n [\n \"game.py\",\n \"Contains Game class and ... functions\"\n ],\n [\n \"main.py\",\n \"Contains main function, from game import Game\"\n ]\n ],\n \"Task list\": [\n \"game.py\",\n \"main.py\"\n ],\n \"Full API spec\": \"\",\n \"Shared Knowledge\": \"'game.py' contains functions shared across the project.\",\n \"Anything UNCLEAR\": \"Clarification needed on the scoring system and how the game over condition will be handled.\"\n}\n[/CONTENT]", + "\nNOTICE\nRole: You are a professional engineer; the main goal is to write google-style, elegant, modular, easy to read and maintain code\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## Design\n{\"Implementation approach\":\"We will use the Pygame library to create the game and handle user input. The game logic will be implemented using Python classes and functions.\",\"File list\":[\"main.py\",\"game.py\"],\"Data structures and interfaces\":\"\\nclassDiagram\\n class Game {\\n -snake: list\\n -food: tuple\\n -score: int\\n +__init__()\\n +update()\\n +draw()\\n +handle_input()\\n +restart()\\n }\\n class Snake {\\n -body: list\\n -direction: str\\n +__init__(start_pos: tuple, length: int, direction: str)\\n +move()\\n +change_direction(new_direction: str)\\n +grow()\\n }\\n Game --> Snake\\n\",\"Program call flow\":\"\\nsequenceDiagram\\n participant M as Main\\n participant G as Game\\n M->>G: initialize game\\n G->>G: update game state\\n G->>G: draw game\\n G->>G: handle user input\\n G->>G: restart game\\n\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Tasks\n{\"Required Python packages\":[\"pygame==2.0.1\"],\"Required Other language third-party packages\":[\"No third-party dependencies required\"],\"Logic Analysis\":[[\"game.py\",\"Contains Game class and ... functions\"],[\"main.py\",\"Contains main function, from game import Game\"]],\"Task list\":[\"game.py\",\"main.py\"],\"Full API spec\":\"\",\"Shared Knowledge\":\"'game.py' contains functions shared across the project.\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Legacy Code\n```Code\n\n```\n\n## Debug logs\n```text\n\n\n\n```\n\n## Bug Feedback logs\n```text\n\n```\n\n# Format example\n## Code: game.py\n```python\n## game.py\n...\n```\n\n# Instruction: Based on the context, follow \"Format example\", write code.\n\n## Code: game.py. Write code with triple quoto, based on the following attentions and context.\n1. Only One file: do your best to implement THIS ONLY ONE FILE.\n2. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets.\n3. Set default value: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. AVOID circular import.\n4. Follow design: YOU MUST FOLLOW \"Data structures and interfaces\". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design.\n5. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE.\n6. Before using a external variable/module, make sure you import it first.\n7. Write out EVERY CODE DETAIL, DON'T LEAVE TODO.\n\n": "```python\n## game.py\n\nclass Game:\n def __init__(self):\n self.snake = []\n self.food = (0, 0)\n self.score = 0\n\n def update(self):\n # Update game state\n pass\n\n def draw(self):\n # Draw game\n pass\n\n def handle_input(self):\n # Handle user input\n pass\n\n def restart(self):\n # Restart game\n pass\n\n\nclass Snake:\n def __init__(self, start_pos: tuple, length: int, direction: str):\n self.body = []\n self.direction = direction\n\n def move(self):\n # Move snake\n pass\n\n def change_direction(self, new_direction: str):\n # Change snake direction\n pass\n\n def grow(self):\n # Grow snake\n pass\n```", + "\n# System\nRole: You are a professional software engineer, and your main task is to review and revise the code. You need to ensure that the code conforms to the google-style standards, is elegantly designed and modularized, easy to read and maintain.\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## System Design\n{\"Implementation approach\":\"We will use the Pygame library to create the game and handle user input. The game logic will be implemented using Python classes and functions.\",\"File list\":[\"main.py\",\"game.py\"],\"Data structures and interfaces\":\"\\nclassDiagram\\n class Game {\\n -snake: list\\n -food: tuple\\n -score: int\\n +__init__()\\n +update()\\n +draw()\\n +handle_input()\\n +restart()\\n }\\n class Snake {\\n -body: list\\n -direction: str\\n +__init__(start_pos: tuple, length: int, direction: str)\\n +move()\\n +change_direction(new_direction: str)\\n +grow()\\n }\\n Game --> Snake\\n\",\"Program call flow\":\"\\nsequenceDiagram\\n participant M as Main\\n participant G as Game\\n M->>G: initialize game\\n G->>G: update game state\\n G->>G: draw game\\n G->>G: handle user input\\n G->>G: restart game\\n\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Tasks\n{\"Required Python packages\":[\"pygame==2.0.1\"],\"Required Other language third-party packages\":[\"No third-party dependencies required\"],\"Logic Analysis\":[[\"game.py\",\"Contains Game class and ... functions\"],[\"main.py\",\"Contains main function, from game import Game\"]],\"Task list\":[\"game.py\",\"main.py\"],\"Full API spec\":\"\",\"Shared Knowledge\":\"'game.py' contains functions shared across the project.\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Code Files\n\n\n\n## Code to be Reviewed: game.py\n```Code\n## game.py\n\nclass Game:\n def __init__(self):\n self.snake = []\n self.food = (0, 0)\n self.score = 0\n\n def update(self):\n # Update game state\n pass\n\n def draw(self):\n # Draw game\n pass\n\n def handle_input(self):\n # Handle user input\n pass\n\n def restart(self):\n # Restart game\n pass\n\n\nclass Snake:\n def __init__(self, start_pos: tuple, length: int, direction: str):\n self.body = []\n self.direction = direction\n\n def move(self):\n # Move snake\n pass\n\n def change_direction(self, new_direction: str):\n # Change snake direction\n pass\n\n def grow(self):\n # Grow snake\n pass\n\n```\n\n\n\n# Format example 1\n## Code Review: game.py\n1. No, we should fix the logic of class A due to ...\n2. ...\n3. ...\n4. No, function B is not implemented, ...\n5. ...\n6. ...\n\n## Actions\n1. Fix the `handle_events` method to update the game state only if a move is successful.\n ```python\n def handle_events(self):\n for event in pygame.event.get():\n if event.type == pygame.QUIT:\n return False\n if event.type == pygame.KEYDOWN:\n moved = False\n if event.key == pygame.K_UP:\n moved = self.game.move('UP')\n elif event.key == pygame.K_DOWN:\n moved = self.game.move('DOWN')\n elif event.key == pygame.K_LEFT:\n moved = self.game.move('LEFT')\n elif event.key == pygame.K_RIGHT:\n moved = self.game.move('RIGHT')\n if moved:\n # Update the game state only if a move was successful\n self.render()\n return True\n ```\n2. Implement function B\n\n## Code Review Result\nLBTM\n\n# Format example 2\n## Code Review: game.py\n1. Yes.\n2. Yes.\n3. Yes.\n4. Yes.\n5. Yes.\n6. Yes.\n\n## Actions\npass\n\n## Code Review Result\nLGTM\n\n\n\n# Instruction: Based on the actual code situation, follow one of the \"Format example\". Return only 1 file under review.\n\n## Code Review: Ordered List. Based on the \"Code to be Reviewed\", provide key, clear, concise, and specific answer. If any answer is no, explain how to fix it step by step.\n1. Is the code implemented as per the requirements? If not, how to achieve it? Analyse it step by step.\n2. Is the code logic completely correct? If there are errors, please indicate how to correct them.\n3. Does the existing code follow the \"Data structures and interfaces\"?\n4. Are all functions implemented? If there is no implementation, please indicate how to achieve it step by step.\n5. Have all necessary pre-dependencies been imported? If not, indicate which ones need to be imported\n6. Are methods from other files being reused correctly?\n\n## Actions: Ordered List. Things that should be done after CR, such as implementing class A and function B\n\n## Code Review Result: str. If the code doesn't have bugs, we don't need to rewrite it, so answer LGTM and stop. ONLY ANSWER LGTM/LBTM.\nLGTM/LBTM\n\n": "## Code Review: game.py\n1. No, the code is not implemented as per the requirements. The Game class should have a method to handle game over condition and update the scoring system.\n2. No, the code logic is not completely correct. The Snake class is missing the implementation of the move, change_direction, and grow methods.\n3. Yes, the existing code follows the \"Data structures and interfaces\" provided.\n4. No, the move, change_direction, and grow methods of the Snake class are not implemented. To achieve this, the methods need to be implemented with the appropriate logic for moving the snake, changing its direction, and growing its body.\n5. Yes, all necessary pre-dependencies have been imported.\n6. No, there are no methods from other files being reused in the current code.\n\n## Actions\n1. Implement a method in the Game class to handle the game over condition and update the scoring system.\n2. Implement the move, change_direction, and grow methods in the Snake class.\n3. Reuse methods from other files if necessary.\n\n## Code Review Result\nLBTM", + "\n# System\nRole: You are a professional software engineer, and your main task is to review and revise the code. You need to ensure that the code conforms to the google-style standards, is elegantly designed and modularized, easy to read and maintain.\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## System Design\n{\"Implementation approach\":\"We will use the Pygame library to create the game and handle user input. The game logic will be implemented using Python classes and functions.\",\"File list\":[\"main.py\",\"game.py\"],\"Data structures and interfaces\":\"\\nclassDiagram\\n class Game {\\n -snake: list\\n -food: tuple\\n -score: int\\n +__init__()\\n +update()\\n +draw()\\n +handle_input()\\n +restart()\\n }\\n class Snake {\\n -body: list\\n -direction: str\\n +__init__(start_pos: tuple, length: int, direction: str)\\n +move()\\n +change_direction(new_direction: str)\\n +grow()\\n }\\n Game --> Snake\\n\",\"Program call flow\":\"\\nsequenceDiagram\\n participant M as Main\\n participant G as Game\\n M->>G: initialize game\\n G->>G: update game state\\n G->>G: draw game\\n G->>G: handle user input\\n G->>G: restart game\\n\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Tasks\n{\"Required Python packages\":[\"pygame==2.0.1\"],\"Required Other language third-party packages\":[\"No third-party dependencies required\"],\"Logic Analysis\":[[\"game.py\",\"Contains Game class and ... functions\"],[\"main.py\",\"Contains main function, from game import Game\"]],\"Task list\":[\"game.py\",\"main.py\"],\"Full API spec\":\"\",\"Shared Knowledge\":\"'game.py' contains functions shared across the project.\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Code Files\n\n\n\n## Code to be Reviewed: game.py\n```Code\n## game.py\n\nclass Game:\n def __init__(self):\n self.snake = []\n self.food = (0, 0)\n self.score = 0\n\n def update(self):\n # Update game state\n pass\n\n def draw(self):\n # Draw game\n pass\n\n def handle_input(self):\n # Handle user input\n pass\n\n def restart(self):\n # Restart game\n pass\n\n\nclass Snake:\n def __init__(self, start_pos: tuple, length: int, direction: str):\n self.body = []\n self.direction = direction\n\n def move(self):\n # Move snake\n pass\n\n def change_direction(self, new_direction: str):\n # Change snake direction\n pass\n\n def grow(self):\n # Grow snake\n pass\n\n```\n\n## Code Review: game.py\n1. No, the code is not implemented as per the requirements. The Game class should have a method to handle game over condition and update the scoring system.\n2. No, the code logic is not completely correct. The Snake class is missing the implementation of the move, change_direction, and grow methods.\n3. Yes, the existing code follows the \"Data structures and interfaces\" provided.\n4. No, the move, change_direction, and grow methods of the Snake class are not implemented. To achieve this, the methods need to be implemented with the appropriate logic for moving the snake, changing its direction, and growing its body.\n5. Yes, all necessary pre-dependencies have been imported.\n6. No, there are no methods from other files being reused in the current code.\n\n## Actions\n1. Implement a method in the Game class to handle the game over condition and update the scoring system.\n2. Implement the move, change_direction, and grow methods in the Snake class.\n3. Reuse methods from other files if necessary.\n\n## Code Review Result\nLBTM\n\n# Instruction: rewrite code based on the Code Review and Actions\n## Rewrite Code: CodeBlock. If it still has some bugs, rewrite game.py with triple quotes. Do your utmost to optimize THIS SINGLE FILE. Return all completed codes and prohibit the return of unfinished codes.\n```Code\n## game.py\n...\n```\n": "```Code\n## game.py\n\nclass Game:\n def __init__(self):\n self.snake = []\n self.food = (0, 0)\n self.score = 0\n\n def update(self):\n # Update game state\n pass\n\n def draw(self):\n # Draw game\n pass\n\n def handle_input(self):\n # Handle user input\n pass\n\n def restart(self):\n # Restart game\n pass\n\n def handle_game_over(self):\n # Handle game over condition\n pass\n\n def update_score(self):\n # Update scoring system\n pass\n\n\nclass Snake:\n def __init__(self, start_pos: tuple, length: int, direction: str):\n self.body = []\n self.direction = direction\n\n def move(self):\n # Move snake\n pass\n\n def change_direction(self, new_direction: str):\n # Change snake direction\n pass\n\n def grow(self):\n # Grow snake\n pass\n```", + "\n# System\nRole: You are a professional software engineer, and your main task is to review and revise the code. You need to ensure that the code conforms to the google-style standards, is elegantly designed and modularized, easy to read and maintain.\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## System Design\n{\"Implementation approach\":\"We will use the Pygame library to create the game and handle user input. The game logic will be implemented using Python classes and functions.\",\"File list\":[\"main.py\",\"game.py\"],\"Data structures and interfaces\":\"\\nclassDiagram\\n class Game {\\n -snake: list\\n -food: tuple\\n -score: int\\n +__init__()\\n +update()\\n +draw()\\n +handle_input()\\n +restart()\\n }\\n class Snake {\\n -body: list\\n -direction: str\\n +__init__(start_pos: tuple, length: int, direction: str)\\n +move()\\n +change_direction(new_direction: str)\\n +grow()\\n }\\n Game --> Snake\\n\",\"Program call flow\":\"\\nsequenceDiagram\\n participant M as Main\\n participant G as Game\\n M->>G: initialize game\\n G->>G: update game state\\n G->>G: draw game\\n G->>G: handle user input\\n G->>G: restart game\\n\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Tasks\n{\"Required Python packages\":[\"pygame==2.0.1\"],\"Required Other language third-party packages\":[\"No third-party dependencies required\"],\"Logic Analysis\":[[\"game.py\",\"Contains Game class and ... functions\"],[\"main.py\",\"Contains main function, from game import Game\"]],\"Task list\":[\"game.py\",\"main.py\"],\"Full API spec\":\"\",\"Shared Knowledge\":\"'game.py' contains functions shared across the project.\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Code Files\n\n\n\n## Code to be Reviewed: game.py\n```Code\n## game.py\n\nclass Game:\n def __init__(self):\n self.snake = []\n self.food = (0, 0)\n self.score = 0\n\n def update(self):\n # Update game state\n pass\n\n def draw(self):\n # Draw game\n pass\n\n def handle_input(self):\n # Handle user input\n pass\n\n def restart(self):\n # Restart game\n pass\n\n def handle_game_over(self):\n # Handle game over condition\n pass\n\n def update_score(self):\n # Update scoring system\n pass\n\n\nclass Snake:\n def __init__(self, start_pos: tuple, length: int, direction: str):\n self.body = []\n self.direction = direction\n\n def move(self):\n # Move snake\n pass\n\n def change_direction(self, new_direction: str):\n # Change snake direction\n pass\n\n def grow(self):\n # Grow snake\n pass\n\n```\n\n\n\n# Format example 1\n## Code Review: game.py\n1. No, we should fix the logic of class A due to ...\n2. ...\n3. ...\n4. No, function B is not implemented, ...\n5. ...\n6. ...\n\n## Actions\n1. Fix the `handle_events` method to update the game state only if a move is successful.\n ```python\n def handle_events(self):\n for event in pygame.event.get():\n if event.type == pygame.QUIT:\n return False\n if event.type == pygame.KEYDOWN:\n moved = False\n if event.key == pygame.K_UP:\n moved = self.game.move('UP')\n elif event.key == pygame.K_DOWN:\n moved = self.game.move('DOWN')\n elif event.key == pygame.K_LEFT:\n moved = self.game.move('LEFT')\n elif event.key == pygame.K_RIGHT:\n moved = self.game.move('RIGHT')\n if moved:\n # Update the game state only if a move was successful\n self.render()\n return True\n ```\n2. Implement function B\n\n## Code Review Result\nLBTM\n\n# Format example 2\n## Code Review: game.py\n1. Yes.\n2. Yes.\n3. Yes.\n4. Yes.\n5. Yes.\n6. Yes.\n\n## Actions\npass\n\n## Code Review Result\nLGTM\n\n\n\n# Instruction: Based on the actual code situation, follow one of the \"Format example\". Return only 1 file under review.\n\n## Code Review: Ordered List. Based on the \"Code to be Reviewed\", provide key, clear, concise, and specific answer. If any answer is no, explain how to fix it step by step.\n1. Is the code implemented as per the requirements? If not, how to achieve it? Analyse it step by step.\n2. Is the code logic completely correct? If there are errors, please indicate how to correct them.\n3. Does the existing code follow the \"Data structures and interfaces\"?\n4. Are all functions implemented? If there is no implementation, please indicate how to achieve it step by step.\n5. Have all necessary pre-dependencies been imported? If not, indicate which ones need to be imported\n6. Are methods from other files being reused correctly?\n\n## Actions: Ordered List. Things that should be done after CR, such as implementing class A and function B\n\n## Code Review Result: str. If the code doesn't have bugs, we don't need to rewrite it, so answer LGTM and stop. ONLY ANSWER LGTM/LBTM.\nLGTM/LBTM\n\n": "## Code Review: game.py\n1. No, the code is not implemented as per the requirements. The `handle_game_over` and `update_score` methods are missing, and the `handle_input` method needs to be implemented to handle user input.\n2. The code logic is not completely correct. The `Snake` class `__init__` method does not initialize the `body` attribute with the `start_pos` and `length` parameters.\n3. The existing code follows the \"Data structures and interfaces\" for the `Game` and `Snake` classes.\n4. The `handle_game_over` and `update_score` methods are not implemented. The `handle_input` method needs to be implemented to handle user input.\n5. The necessary pre-dependencies have been imported.\n6. The methods from other files are not being reused in the existing code.\n\n## Actions\n1. Implement the `handle_game_over` method to handle the game over condition.\n2. Implement the `update_score` method to update the scoring system.\n3. Implement the `handle_input` method to handle user input and update the game state accordingly.\n\n## Code Review Result\nLBTM", + "\n# System\nRole: You are a professional software engineer, and your main task is to review and revise the code. You need to ensure that the code conforms to the google-style standards, is elegantly designed and modularized, easy to read and maintain.\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## System Design\n{\"Implementation approach\":\"We will use the Pygame library to create the game and handle user input. The game logic will be implemented using Python classes and functions.\",\"File list\":[\"main.py\",\"game.py\"],\"Data structures and interfaces\":\"\\nclassDiagram\\n class Game {\\n -snake: list\\n -food: tuple\\n -score: int\\n +__init__()\\n +update()\\n +draw()\\n +handle_input()\\n +restart()\\n }\\n class Snake {\\n -body: list\\n -direction: str\\n +__init__(start_pos: tuple, length: int, direction: str)\\n +move()\\n +change_direction(new_direction: str)\\n +grow()\\n }\\n Game --> Snake\\n\",\"Program call flow\":\"\\nsequenceDiagram\\n participant M as Main\\n participant G as Game\\n M->>G: initialize game\\n G->>G: update game state\\n G->>G: draw game\\n G->>G: handle user input\\n G->>G: restart game\\n\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Tasks\n{\"Required Python packages\":[\"pygame==2.0.1\"],\"Required Other language third-party packages\":[\"No third-party dependencies required\"],\"Logic Analysis\":[[\"game.py\",\"Contains Game class and ... functions\"],[\"main.py\",\"Contains main function, from game import Game\"]],\"Task list\":[\"game.py\",\"main.py\"],\"Full API spec\":\"\",\"Shared Knowledge\":\"'game.py' contains functions shared across the project.\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Code Files\n\n\n\n## Code to be Reviewed: game.py\n```Code\n## game.py\n\nclass Game:\n def __init__(self):\n self.snake = []\n self.food = (0, 0)\n self.score = 0\n\n def update(self):\n # Update game state\n pass\n\n def draw(self):\n # Draw game\n pass\n\n def handle_input(self):\n # Handle user input\n pass\n\n def restart(self):\n # Restart game\n pass\n\n def handle_game_over(self):\n # Handle game over condition\n pass\n\n def update_score(self):\n # Update scoring system\n pass\n\n\nclass Snake:\n def __init__(self, start_pos: tuple, length: int, direction: str):\n self.body = []\n self.direction = direction\n\n def move(self):\n # Move snake\n pass\n\n def change_direction(self, new_direction: str):\n # Change snake direction\n pass\n\n def grow(self):\n # Grow snake\n pass\n\n```\n\n## Code Review: game.py\n1. No, the code is not implemented as per the requirements. The `handle_game_over` and `update_score` methods are missing, and the `handle_input` method needs to be implemented to handle user input.\n2. The code logic is not completely correct. The `Snake` class `__init__` method does not initialize the `body` attribute with the `start_pos` and `length` parameters.\n3. The existing code follows the \"Data structures and interfaces\" for the `Game` and `Snake` classes.\n4. The `handle_game_over` and `update_score` methods are not implemented. The `handle_input` method needs to be implemented to handle user input.\n5. The necessary pre-dependencies have been imported.\n6. The methods from other files are not being reused in the existing code.\n\n## Actions\n1. Implement the `handle_game_over` method to handle the game over condition.\n2. Implement the `update_score` method to update the scoring system.\n3. Implement the `handle_input` method to handle user input and update the game state accordingly.\n\n## Code Review Result\nLBTM\n\n# Instruction: rewrite code based on the Code Review and Actions\n## Rewrite Code: CodeBlock. If it still has some bugs, rewrite game.py with triple quotes. Do your utmost to optimize THIS SINGLE FILE. Return all completed codes and prohibit the return of unfinished codes.\n```Code\n## game.py\n...\n```\n": "```Code\n## game.py\n\nclass Game:\n def __init__(self):\n self.snake = []\n self.food = (0, 0)\n self.score = 0\n\n def update(self):\n # Update game state\n pass\n\n def draw(self):\n # Draw game\n pass\n\n def handle_input(self):\n # Handle user input\n pass\n\n def restart(self):\n # Restart game\n pass\n\n def handle_game_over(self):\n # Handle game over condition\n pass\n\n def update_score(self):\n # Update scoring system\n pass\n\n\nclass Snake:\n def __init__(self, start_pos: tuple, length: int, direction: str):\n self.body = [start_pos] # Initialize body with start_pos\n self.direction = direction\n\n def move(self):\n # Move snake\n pass\n\n def change_direction(self, new_direction: str):\n # Change snake direction\n pass\n\n def grow(self):\n # Grow snake\n pass\n```", + "\nNOTICE\nRole: You are a professional engineer; the main goal is to write google-style, elegant, modular, easy to read and maintain code\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## Design\n{\"Implementation approach\":\"We will use the Pygame library to create the game and handle user input. The game logic will be implemented using Python classes and functions.\",\"File list\":[\"main.py\",\"game.py\"],\"Data structures and interfaces\":\"\\nclassDiagram\\n class Game {\\n -snake: list\\n -food: tuple\\n -score: int\\n +__init__()\\n +update()\\n +draw()\\n +handle_input()\\n +restart()\\n }\\n class Snake {\\n -body: list\\n -direction: str\\n +__init__(start_pos: tuple, length: int, direction: str)\\n +move()\\n +change_direction(new_direction: str)\\n +grow()\\n }\\n Game --> Snake\\n\",\"Program call flow\":\"\\nsequenceDiagram\\n participant M as Main\\n participant G as Game\\n M->>G: initialize game\\n G->>G: update game state\\n G->>G: draw game\\n G->>G: handle user input\\n G->>G: restart game\\n\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Tasks\n{\"Required Python packages\":[\"pygame==2.0.1\"],\"Required Other language third-party packages\":[\"No third-party dependencies required\"],\"Logic Analysis\":[[\"game.py\",\"Contains Game class and ... functions\"],[\"main.py\",\"Contains main function, from game import Game\"]],\"Task list\":[\"game.py\",\"main.py\"],\"Full API spec\":\"\",\"Shared Knowledge\":\"'game.py' contains functions shared across the project.\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Legacy Code\n```Code\n----- game.py\n## game.py\n\nclass Game:\n def __init__(self):\n self.snake = []\n self.food = (0, 0)\n self.score = 0\n\n def update(self):\n # Update game state\n pass\n\n def draw(self):\n # Draw game\n pass\n\n def handle_input(self):\n # Handle user input\n pass\n\n def restart(self):\n # Restart game\n pass\n\n def handle_game_over(self):\n # Handle game over condition\n pass\n\n def update_score(self):\n # Update scoring system\n pass\n\n\nclass Snake:\n def __init__(self, start_pos: tuple, length: int, direction: str):\n self.body = [start_pos] # Initialize body with start_pos\n self.direction = direction\n\n def move(self):\n # Move snake\n pass\n\n def change_direction(self, new_direction: str):\n # Change snake direction\n pass\n\n def grow(self):\n # Grow snake\n pass\n\n```\n\n## Debug logs\n```text\n\n\n\n```\n\n## Bug Feedback logs\n```text\n\n```\n\n# Format example\n## Code: main.py\n```python\n## main.py\n...\n```\n\n# Instruction: Based on the context, follow \"Format example\", write code.\n\n## Code: main.py. Write code with triple quoto, based on the following attentions and context.\n1. Only One file: do your best to implement THIS ONLY ONE FILE.\n2. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets.\n3. Set default value: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. AVOID circular import.\n4. Follow design: YOU MUST FOLLOW \"Data structures and interfaces\". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design.\n5. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE.\n6. Before using a external variable/module, make sure you import it first.\n7. Write out EVERY CODE DETAIL, DON'T LEAVE TODO.\n\n": "```python\n## main.py\n\nfrom game import Game\n\ndef main():\n game = Game()\n running = True\n\n while running:\n game.handle_input()\n game.update()\n game.draw()\n\n if game.handle_game_over():\n game.restart()\n\nif __name__ == \"__main__\":\n main()\n```", + "\n# System\nRole: You are a professional software engineer, and your main task is to review and revise the code. You need to ensure that the code conforms to the google-style standards, is elegantly designed and modularized, easy to read and maintain.\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## System Design\n{\"Implementation approach\":\"We will use the Pygame library to create the game and handle user input. The game logic will be implemented using Python classes and functions.\",\"File list\":[\"main.py\",\"game.py\"],\"Data structures and interfaces\":\"\\nclassDiagram\\n class Game {\\n -snake: list\\n -food: tuple\\n -score: int\\n +__init__()\\n +update()\\n +draw()\\n +handle_input()\\n +restart()\\n }\\n class Snake {\\n -body: list\\n -direction: str\\n +__init__(start_pos: tuple, length: int, direction: str)\\n +move()\\n +change_direction(new_direction: str)\\n +grow()\\n }\\n Game --> Snake\\n\",\"Program call flow\":\"\\nsequenceDiagram\\n participant M as Main\\n participant G as Game\\n M->>G: initialize game\\n G->>G: update game state\\n G->>G: draw game\\n G->>G: handle user input\\n G->>G: restart game\\n\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Tasks\n{\"Required Python packages\":[\"pygame==2.0.1\"],\"Required Other language third-party packages\":[\"No third-party dependencies required\"],\"Logic Analysis\":[[\"game.py\",\"Contains Game class and ... functions\"],[\"main.py\",\"Contains main function, from game import Game\"]],\"Task list\":[\"game.py\",\"main.py\"],\"Full API spec\":\"\",\"Shared Knowledge\":\"'game.py' contains functions shared across the project.\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Code Files\n----- game.py\n## game.py\n\nclass Game:\n def __init__(self):\n self.snake = []\n self.food = (0, 0)\n self.score = 0\n\n def update(self):\n # Update game state\n pass\n\n def draw(self):\n # Draw game\n pass\n\n def handle_input(self):\n # Handle user input\n pass\n\n def restart(self):\n # Restart game\n pass\n\n def handle_game_over(self):\n # Handle game over condition\n pass\n\n def update_score(self):\n # Update scoring system\n pass\n\n\nclass Snake:\n def __init__(self, start_pos: tuple, length: int, direction: str):\n self.body = [start_pos] # Initialize body with start_pos\n self.direction = direction\n\n def move(self):\n # Move snake\n pass\n\n def change_direction(self, new_direction: str):\n # Change snake direction\n pass\n\n def grow(self):\n # Grow snake\n pass\n\n\n\n## Code to be Reviewed: main.py\n```Code\n## main.py\n\nfrom game import Game\n\ndef main():\n game = Game()\n running = True\n\n while running:\n game.handle_input()\n game.update()\n game.draw()\n\n if game.handle_game_over():\n game.restart()\n\nif __name__ == \"__main__\":\n main()\n\n```\n\n\n\n# Format example 1\n## Code Review: main.py\n1. No, we should fix the logic of class A due to ...\n2. ...\n3. ...\n4. No, function B is not implemented, ...\n5. ...\n6. ...\n\n## Actions\n1. Fix the `handle_events` method to update the game state only if a move is successful.\n ```python\n def handle_events(self):\n for event in pygame.event.get():\n if event.type == pygame.QUIT:\n return False\n if event.type == pygame.KEYDOWN:\n moved = False\n if event.key == pygame.K_UP:\n moved = self.game.move('UP')\n elif event.key == pygame.K_DOWN:\n moved = self.game.move('DOWN')\n elif event.key == pygame.K_LEFT:\n moved = self.game.move('LEFT')\n elif event.key == pygame.K_RIGHT:\n moved = self.game.move('RIGHT')\n if moved:\n # Update the game state only if a move was successful\n self.render()\n return True\n ```\n2. Implement function B\n\n## Code Review Result\nLBTM\n\n# Format example 2\n## Code Review: main.py\n1. Yes.\n2. Yes.\n3. Yes.\n4. Yes.\n5. Yes.\n6. Yes.\n\n## Actions\npass\n\n## Code Review Result\nLGTM\n\n\n\n# Instruction: Based on the actual code situation, follow one of the \"Format example\". Return only 1 file under review.\n\n## Code Review: Ordered List. Based on the \"Code to be Reviewed\", provide key, clear, concise, and specific answer. If any answer is no, explain how to fix it step by step.\n1. Is the code implemented as per the requirements? If not, how to achieve it? Analyse it step by step.\n2. Is the code logic completely correct? If there are errors, please indicate how to correct them.\n3. Does the existing code follow the \"Data structures and interfaces\"?\n4. Are all functions implemented? If there is no implementation, please indicate how to achieve it step by step.\n5. Have all necessary pre-dependencies been imported? If not, indicate which ones need to be imported\n6. Are methods from other files being reused correctly?\n\n## Actions: Ordered List. Things that should be done after CR, such as implementing class A and function B\n\n## Code Review Result: str. If the code doesn't have bugs, we don't need to rewrite it, so answer LGTM and stop. ONLY ANSWER LGTM/LBTM.\nLGTM/LBTM\n\n": "## Code Review: main.py\n1. The code is implemented as per the requirements, but the game over condition and scoring system need to be clarified and implemented.\n \n## Actions\n1. Clarify and implement the game over condition and scoring system in the `Game` class.\n\n## Code Review Result\nLBTM", + "\n# System\nRole: You are a professional software engineer, and your main task is to review and revise the code. You need to ensure that the code conforms to the google-style standards, is elegantly designed and modularized, easy to read and maintain.\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## System Design\n{\"Implementation approach\":\"We will use the Pygame library to create the game and handle user input. The game logic will be implemented using Python classes and functions.\",\"File list\":[\"main.py\",\"game.py\"],\"Data structures and interfaces\":\"\\nclassDiagram\\n class Game {\\n -snake: list\\n -food: tuple\\n -score: int\\n +__init__()\\n +update()\\n +draw()\\n +handle_input()\\n +restart()\\n }\\n class Snake {\\n -body: list\\n -direction: str\\n +__init__(start_pos: tuple, length: int, direction: str)\\n +move()\\n +change_direction(new_direction: str)\\n +grow()\\n }\\n Game --> Snake\\n\",\"Program call flow\":\"\\nsequenceDiagram\\n participant M as Main\\n participant G as Game\\n M->>G: initialize game\\n G->>G: update game state\\n G->>G: draw game\\n G->>G: handle user input\\n G->>G: restart game\\n\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Tasks\n{\"Required Python packages\":[\"pygame==2.0.1\"],\"Required Other language third-party packages\":[\"No third-party dependencies required\"],\"Logic Analysis\":[[\"game.py\",\"Contains Game class and ... functions\"],[\"main.py\",\"Contains main function, from game import Game\"]],\"Task list\":[\"game.py\",\"main.py\"],\"Full API spec\":\"\",\"Shared Knowledge\":\"'game.py' contains functions shared across the project.\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Code Files\n----- game.py\n## game.py\n\nclass Game:\n def __init__(self):\n self.snake = []\n self.food = (0, 0)\n self.score = 0\n\n def update(self):\n # Update game state\n pass\n\n def draw(self):\n # Draw game\n pass\n\n def handle_input(self):\n # Handle user input\n pass\n\n def restart(self):\n # Restart game\n pass\n\n def handle_game_over(self):\n # Handle game over condition\n pass\n\n def update_score(self):\n # Update scoring system\n pass\n\n\nclass Snake:\n def __init__(self, start_pos: tuple, length: int, direction: str):\n self.body = [start_pos] # Initialize body with start_pos\n self.direction = direction\n\n def move(self):\n # Move snake\n pass\n\n def change_direction(self, new_direction: str):\n # Change snake direction\n pass\n\n def grow(self):\n # Grow snake\n pass\n\n\n\n## Code to be Reviewed: main.py\n```Code\n## main.py\n\nfrom game import Game\n\ndef main():\n game = Game()\n running = True\n\n while running:\n game.handle_input()\n game.update()\n game.draw()\n\n if game.handle_game_over():\n game.restart()\n\nif __name__ == \"__main__\":\n main()\n\n```\n\n## Code Review: main.py\n1. The code is implemented as per the requirements, but the game over condition and scoring system need to be clarified and implemented.\n \n## Actions\n1. Clarify and implement the game over condition and scoring system in the `Game` class.\n\n## Code Review Result\nLBTM\n\n# Instruction: rewrite code based on the Code Review and Actions\n## Rewrite Code: CodeBlock. If it still has some bugs, rewrite main.py with triple quotes. Do your utmost to optimize THIS SINGLE FILE. Return all completed codes and prohibit the return of unfinished codes.\n```Code\n## main.py\n...\n```\n": "```python\n## main.py\n\nfrom game import Game\n\ndef main():\n game = Game()\n running = True\n\n while running:\n game.handle_input()\n game.update()\n game.draw()\n\n if game.is_game_over():\n game.restart()\n\nif __name__ == \"__main__\":\n main()\n```", + "\n# System\nRole: You are a professional software engineer, and your main task is to review and revise the code. You need to ensure that the code conforms to the google-style standards, is elegantly designed and modularized, easy to read and maintain.\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## System Design\n{\"Implementation approach\":\"We will use the Pygame library to create the game and handle user input. The game logic will be implemented using Python classes and functions.\",\"File list\":[\"main.py\",\"game.py\"],\"Data structures and interfaces\":\"\\nclassDiagram\\n class Game {\\n -snake: list\\n -food: tuple\\n -score: int\\n +__init__()\\n +update()\\n +draw()\\n +handle_input()\\n +restart()\\n }\\n class Snake {\\n -body: list\\n -direction: str\\n +__init__(start_pos: tuple, length: int, direction: str)\\n +move()\\n +change_direction(new_direction: str)\\n +grow()\\n }\\n Game --> Snake\\n\",\"Program call flow\":\"\\nsequenceDiagram\\n participant M as Main\\n participant G as Game\\n M->>G: initialize game\\n G->>G: update game state\\n G->>G: draw game\\n G->>G: handle user input\\n G->>G: restart game\\n\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Tasks\n{\"Required Python packages\":[\"pygame==2.0.1\"],\"Required Other language third-party packages\":[\"No third-party dependencies required\"],\"Logic Analysis\":[[\"game.py\",\"Contains Game class and ... functions\"],[\"main.py\",\"Contains main function, from game import Game\"]],\"Task list\":[\"game.py\",\"main.py\"],\"Full API spec\":\"\",\"Shared Knowledge\":\"'game.py' contains functions shared across the project.\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Code Files\n----- game.py\n## game.py\n\nclass Game:\n def __init__(self):\n self.snake = []\n self.food = (0, 0)\n self.score = 0\n\n def update(self):\n # Update game state\n pass\n\n def draw(self):\n # Draw game\n pass\n\n def handle_input(self):\n # Handle user input\n pass\n\n def restart(self):\n # Restart game\n pass\n\n def handle_game_over(self):\n # Handle game over condition\n pass\n\n def update_score(self):\n # Update scoring system\n pass\n\n\nclass Snake:\n def __init__(self, start_pos: tuple, length: int, direction: str):\n self.body = [start_pos] # Initialize body with start_pos\n self.direction = direction\n\n def move(self):\n # Move snake\n pass\n\n def change_direction(self, new_direction: str):\n # Change snake direction\n pass\n\n def grow(self):\n # Grow snake\n pass\n\n\n\n## Code to be Reviewed: main.py\n```Code\n## main.py\n\nfrom game import Game\n\ndef main():\n game = Game()\n running = True\n\n while running:\n game.handle_input()\n game.update()\n game.draw()\n\n if game.is_game_over():\n game.restart()\n\nif __name__ == \"__main__\":\n main()\n\n```\n\n\n\n# Format example 1\n## Code Review: main.py\n1. No, we should fix the logic of class A due to ...\n2. ...\n3. ...\n4. No, function B is not implemented, ...\n5. ...\n6. ...\n\n## Actions\n1. Fix the `handle_events` method to update the game state only if a move is successful.\n ```python\n def handle_events(self):\n for event in pygame.event.get():\n if event.type == pygame.QUIT:\n return False\n if event.type == pygame.KEYDOWN:\n moved = False\n if event.key == pygame.K_UP:\n moved = self.game.move('UP')\n elif event.key == pygame.K_DOWN:\n moved = self.game.move('DOWN')\n elif event.key == pygame.K_LEFT:\n moved = self.game.move('LEFT')\n elif event.key == pygame.K_RIGHT:\n moved = self.game.move('RIGHT')\n if moved:\n # Update the game state only if a move was successful\n self.render()\n return True\n ```\n2. Implement function B\n\n## Code Review Result\nLBTM\n\n# Format example 2\n## Code Review: main.py\n1. Yes.\n2. Yes.\n3. Yes.\n4. Yes.\n5. Yes.\n6. Yes.\n\n## Actions\npass\n\n## Code Review Result\nLGTM\n\n\n\n# Instruction: Based on the actual code situation, follow one of the \"Format example\". Return only 1 file under review.\n\n## Code Review: Ordered List. Based on the \"Code to be Reviewed\", provide key, clear, concise, and specific answer. If any answer is no, explain how to fix it step by step.\n1. Is the code implemented as per the requirements? If not, how to achieve it? Analyse it step by step.\n2. Is the code logic completely correct? If there are errors, please indicate how to correct them.\n3. Does the existing code follow the \"Data structures and interfaces\"?\n4. Are all functions implemented? If there is no implementation, please indicate how to achieve it step by step.\n5. Have all necessary pre-dependencies been imported? If not, indicate which ones need to be imported\n6. Are methods from other files being reused correctly?\n\n## Actions: Ordered List. Things that should be done after CR, such as implementing class A and function B\n\n## Code Review Result: str. If the code doesn't have bugs, we don't need to rewrite it, so answer LGTM and stop. ONLY ANSWER LGTM/LBTM.\nLGTM/LBTM\n\n": "## Code Review: main.py\n1. The code is implemented as per the requirements, but the `is_game_over` method is missing. To achieve this, we need to implement the `is_game_over` method in the Game class to check for the game over condition.\n2. The code logic is correct, but the missing `is_game_over` method needs to be added to handle the game over condition.\n3. The existing code follows the \"Data structures and interfaces\" as per the provided class diagram.\n4. All functions are implemented, but the `is_game_over` method is missing in the Game class. To achieve this, we need to implement the `is_game_over` method to handle the game over condition.\n5. All necessary pre-dependencies have been imported.\n6. Methods from other files are not being reused in the current code.\n\n## Actions\n1. Implement the `is_game_over` method in the Game class to handle the game over condition.\n\n## Code Review Result\nLBTM", + "\n# System\nRole: You are a professional software engineer, and your main task is to review and revise the code. You need to ensure that the code conforms to the google-style standards, is elegantly designed and modularized, easy to read and maintain.\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## System Design\n{\"Implementation approach\":\"We will use the Pygame library to create the game and handle user input. The game logic will be implemented using Python classes and functions.\",\"File list\":[\"main.py\",\"game.py\"],\"Data structures and interfaces\":\"\\nclassDiagram\\n class Game {\\n -snake: list\\n -food: tuple\\n -score: int\\n +__init__()\\n +update()\\n +draw()\\n +handle_input()\\n +restart()\\n }\\n class Snake {\\n -body: list\\n -direction: str\\n +__init__(start_pos: tuple, length: int, direction: str)\\n +move()\\n +change_direction(new_direction: str)\\n +grow()\\n }\\n Game --> Snake\\n\",\"Program call flow\":\"\\nsequenceDiagram\\n participant M as Main\\n participant G as Game\\n M->>G: initialize game\\n G->>G: update game state\\n G->>G: draw game\\n G->>G: handle user input\\n G->>G: restart game\\n\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Tasks\n{\"Required Python packages\":[\"pygame==2.0.1\"],\"Required Other language third-party packages\":[\"No third-party dependencies required\"],\"Logic Analysis\":[[\"game.py\",\"Contains Game class and ... functions\"],[\"main.py\",\"Contains main function, from game import Game\"]],\"Task list\":[\"game.py\",\"main.py\"],\"Full API spec\":\"\",\"Shared Knowledge\":\"'game.py' contains functions shared across the project.\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Code Files\n----- game.py\n## game.py\n\nclass Game:\n def __init__(self):\n self.snake = []\n self.food = (0, 0)\n self.score = 0\n\n def update(self):\n # Update game state\n pass\n\n def draw(self):\n # Draw game\n pass\n\n def handle_input(self):\n # Handle user input\n pass\n\n def restart(self):\n # Restart game\n pass\n\n def handle_game_over(self):\n # Handle game over condition\n pass\n\n def update_score(self):\n # Update scoring system\n pass\n\n\nclass Snake:\n def __init__(self, start_pos: tuple, length: int, direction: str):\n self.body = [start_pos] # Initialize body with start_pos\n self.direction = direction\n\n def move(self):\n # Move snake\n pass\n\n def change_direction(self, new_direction: str):\n # Change snake direction\n pass\n\n def grow(self):\n # Grow snake\n pass\n\n\n\n## Code to be Reviewed: main.py\n```Code\n## main.py\n\nfrom game import Game\n\ndef main():\n game = Game()\n running = True\n\n while running:\n game.handle_input()\n game.update()\n game.draw()\n\n if game.is_game_over():\n game.restart()\n\nif __name__ == \"__main__\":\n main()\n\n```\n\n## Code Review: main.py\n1. The code is implemented as per the requirements, but the `is_game_over` method is missing. To achieve this, we need to implement the `is_game_over` method in the Game class to check for the game over condition.\n2. The code logic is correct, but the missing `is_game_over` method needs to be added to handle the game over condition.\n3. The existing code follows the \"Data structures and interfaces\" as per the provided class diagram.\n4. All functions are implemented, but the `is_game_over` method is missing in the Game class. To achieve this, we need to implement the `is_game_over` method to handle the game over condition.\n5. All necessary pre-dependencies have been imported.\n6. Methods from other files are not being reused in the current code.\n\n## Actions\n1. Implement the `is_game_over` method in the Game class to handle the game over condition.\n\n## Code Review Result\nLBTM\n\n# Instruction: rewrite code based on the Code Review and Actions\n## Rewrite Code: CodeBlock. If it still has some bugs, rewrite main.py with triple quotes. Do your utmost to optimize THIS SINGLE FILE. Return all completed codes and prohibit the return of unfinished codes.\n```Code\n## main.py\n...\n```\n": "```python\n## main.py\n\nfrom game import Game\n\ndef main():\n game = Game()\n running = True\n\n while running:\n game.handle_input()\n game.update()\n game.draw()\n\n if game.is_game_over(): # Check for game over condition\n game.restart()\n\nif __name__ == \"__main__\":\n main()\n```", + "You are a python code to Mermaid Sequence Diagram translator in function detail#SYSTEM_MSG_END#```python\n#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\"\"\"\n@Author : alexanderwu\n@File : write_review.py\n\"\"\"\nimport asyncio\nfrom typing import List, Literal\n\nfrom metagpt.actions import Action\nfrom metagpt.actions.action_node import ActionNode\n\nREVIEW = ActionNode(\n key=\"Review\",\n expected_type=List[str],\n instruction=\"Act as an experienced reviewer and critically assess the given output. Provide specific and\"\n \" constructive feedback, highlighting areas for improvement and suggesting changes.\",\n example=[\n \"The logic in the function `calculate_total` seems flawed. Shouldn't it consider the discount rate as well?\",\n \"The TODO function is not implemented yet? Should we implement it before commit?\",\n ],\n)\n\nREVIEW_RESULT = ActionNode(\n key=\"ReviewResult\",\n expected_type=Literal[\"LGTM\", \"LBTM\"],\n instruction=\"LGTM/LBTM. If the code is fully implemented, \" \"give a LGTM, otherwise provide a LBTM.\",\n example=\"LBTM\",\n)\n\nNEXT_STEPS = ActionNode(\n key=\"NextSteps\",\n expected_type=str,\n instruction=\"Based on the code review outcome, suggest actionable steps. This can include code changes, \"\n \"refactoring suggestions, or any follow-up tasks.\",\n example=\"\"\"1. Refactor the `process_data` method to improve readability and efficiency.\n2. Cover edge cases in the `validate_user` function.\n3. Implement a the TODO in the `calculate_total` function.\n4. Fix the `handle_events` method to update the game state only if a move is successful.\n ```python\n def handle_events(self):\n for event in pygame.event.get():\n if event.type == pygame.QUIT:\n return False\n if event.type == pygame.KEYDOWN:\n moved = False\n if event.key == pygame.K_UP:\n moved = self.game.move('UP')\n elif event.key == pygame.K_DOWN:\n moved = self.game.move('DOWN')\n elif event.key == pygame.K_LEFT:\n moved = self.game.move('LEFT')\n elif event.key == pygame.K_RIGHT:\n moved = self.game.move('RIGHT')\n if moved:\n # Update the game state only if a move was successful\n self.render()\n return True\n ```\n\"\"\",\n)\n\nWRITE_DRAFT = ActionNode(\n key=\"WriteDraft\",\n expected_type=str,\n instruction=\"Could you write draft code for move function in order to implement it?\",\n example=\"Draft: ...\",\n)\n\n\nWRITE_FUNCTION = ActionNode(\n key=\"WriteFunction\",\n expected_type=str,\n instruction=\"write code for the function not implemented.\",\n example=\"\"\"\n```Code\n...\n```\n\"\"\",\n)\n\n\nREWRITE_CODE = ActionNode(\n key=\"RewriteCode\",\n expected_type=str,\n instruction=\"\"\"rewrite code based on the Review and Actions\"\"\",\n example=\"\"\"\n```python\n## example.py\ndef calculate_total(price, quantity):\n total = price * quantity\n```\n\"\"\",\n)\n\n\nCODE_REVIEW_CONTEXT = \"\"\"\n# System\nRole: You are a professional software engineer, and your main task is to review and revise the code. You need to ensure that the code conforms to the google-style standards, is elegantly designed and modularized, easy to read and maintain.\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\n\n# Context\n## System Design\n{\"Implementation approach\": \"我们将使用HTML、CSS和JavaScript来实现这个单机的响应式2048游戏。为了确保游戏性能流畅和响应式设计,我们会选择使用Vue.js框架,因为它易于上手且适合构建交互式界面。我们还将使用localStorage来记录玩家的最高分。\", \"File list\": [\"index.html\", \"styles.css\", \"main.js\", \"game.js\", \"storage.js\"], \"Data structures and interfaces\": \"classDiagram\\\n class Game {\\\n -board Array\\\n -score Number\\\n -bestScore Number\\\n +constructor()\\\n +startGame()\\\n +move(direction: String)\\\n +getBoard() Array\\\n +getScore() Number\\\n +getBestScore() Number\\\n +setBestScore(score: Number)\\\n }\\\n class Storage {\\\n +getBestScore() Number\\\n +setBestScore(score: Number)\\\n }\\\n class Main {\\\n +init()\\\n +bindEvents()\\\n }\\\n Game --> Storage : uses\\\n Main --> Game : uses\", \"Program call flow\": \"sequenceDiagram\\\n participant M as Main\\\n participant G as Game\\\n participant S as Storage\\\n M->>G: init()\\\n G->>S: getBestScore()\\\n S-->>G: return bestScore\\\n M->>G: bindEvents()\\\n M->>G: startGame()\\\n loop Game Loop\\\n M->>G: move(direction)\\\n G->>S: setBestScore(score)\\\n S-->>G: return\\\n end\", \"Anything UNCLEAR\": \"目前项目要求明确,没有不清楚的地方。\"}\n\n## Tasks\n{\"Required Python packages\": [\"无需Python包\"], \"Required Other language third-party packages\": [\"vue.js\"], \"Logic Analysis\": [[\"index.html\", \"作为游戏的入口文件和主要的HTML结构\"], [\"styles.css\", \"包含所有的CSS样式,确保游戏界面美观\"], [\"main.js\", \"包含Main类,负责初始化游戏和绑定事件\"], [\"game.js\", \"包含Game类,负责游戏逻辑,如开始游戏、移动方块等\"], [\"storage.js\", \"包含Storage类,用于获取和设置玩家的最高分\"]], \"Task list\": [\"index.html\", \"styles.css\", \"storage.js\", \"game.js\", \"main.js\"], \"Full API spec\": \"\", \"Shared Knowledge\": \"\\'game.js\\' 包含游戏逻辑相关的函数,被 \\'main.js\\' 调用。\", \"Anything UNCLEAR\": \"目前项目要求明确,没有不清楚的地方。\"}\n\n## Code Files\n----- index.html\n\n\n\n \n \n 2048游戏\n \n \n\n\n
\n

2048

\n
\n
\n
分数
\n
{{ score }}
\n
\n
\n
最高分
\n
{{ bestScore }}
\n
\n
\n
\n
\n
\n {{ cell !== 0 ? cell : \\'\\' }}\n
\n
\n
\n \n
\n\n \n \n \n \n\n\n\n----- styles.css\n/* styles.css */\nbody, html {\n margin: 0;\n padding: 0;\n font-family: \\'Arial\\', sans-serif;\n}\n\n#app {\n text-align: center;\n font-size: 18px;\n color: #776e65;\n}\n\nh1 {\n color: #776e65;\n font-size: 72px;\n font-weight: bold;\n margin: 20px 0;\n}\n\n.scores-container {\n display: flex;\n justify-content: center;\n margin-bottom: 20px;\n}\n\n.score-container, .best-container {\n background: #bbada0;\n padding: 10px;\n border-radius: 5px;\n margin: 0 10px;\n min-width: 100px;\n text-align: center;\n}\n\n.score-header, .best-header {\n color: #eee4da;\n font-size: 18px;\n margin-bottom: 5px;\n}\n\n.game-container {\n max-width: 500px;\n margin: 0 auto 20px;\n background: #bbada0;\n padding: 15px;\n border-radius: 10px;\n position: relative;\n}\n\n.grid-row {\n display: flex;\n}\n\n.grid-cell {\n background: #cdc1b4;\n width: 100px;\n height: 100px;\n margin: 5px;\n display: flex;\n justify-content: center;\n align-items: center;\n font-size: 35px;\n font-weight: bold;\n color: #776e65;\n border-radius: 3px;\n}\n\n/* Dynamic classes for different number cells */\n.number-cell-2 {\n background: #eee4da;\n}\n\n.number-cell-4 {\n background: #ede0c8;\n}\n\n.number-cell-8 {\n background: #f2b179;\n color: #f9f6f2;\n}\n\n.number-cell-16 {\n background: #f59563;\n color: #f9f6f2;\n}\n\n.number-cell-32 {\n background: #f67c5f;\n color: #f9f6f2;\n}\n\n.number-cell-64 {\n background: #f65e3b;\n color: #f9f6f2;\n}\n\n.number-cell-128 {\n background: #edcf72;\n color: #f9f6f2;\n}\n\n.number-cell-256 {\n background: #edcc61;\n color: #f9f6f2;\n}\n\n.number-cell-512 {\n background: #edc850;\n color: #f9f6f2;\n}\n\n.number-cell-1024 {\n background: #edc53f;\n color: #f9f6f2;\n}\n\n.number-cell-2048 {\n background: #edc22e;\n color: #f9f6f2;\n}\n\n/* Larger numbers need smaller font sizes */\n.number-cell-1024, .number-cell-2048 {\n font-size: 30px;\n}\n\nbutton {\n background-color: #8f7a66;\n color: #f9f6f2;\n border: none;\n border-radius: 3px;\n padding: 10px 20px;\n font-size: 18px;\n cursor: pointer;\n outline: none;\n}\n\nbutton:hover {\n background-color: #9f8b76;\n}\n\n----- storage.js\n## storage.js\nclass Storage {\n // 获取最高分\n getBestScore() {\n // 尝试从localStorage中获取最高分,如果不存在则默认为0\n const bestScore = localStorage.getItem(\\'bestScore\\');\n return bestScore ? Number(bestScore) : 0;\n }\n\n // 设置最高分\n setBestScore(score) {\n // 将最高分设置到localStorage中\n localStorage.setItem(\\'bestScore\\', score.toString());\n }\n}\n\n\n\n## Code to be Reviewed: game.js\n```Code\n## game.js\nclass Game {\n constructor() {\n this.board = this.createEmptyBoard();\n this.score = 0;\n this.bestScore = 0;\n }\n\n createEmptyBoard() {\n const board = [];\n for (let i = 0; i < 4; i++) {\n board[i] = [0, 0, 0, 0];\n }\n return board;\n }\n\n startGame() {\n this.board = this.createEmptyBoard();\n this.score = 0;\n this.addRandomTile();\n this.addRandomTile();\n }\n\n addRandomTile() {\n let emptyCells = [];\n for (let r = 0; r < 4; r++) {\n for (let c = 0; c < 4; c++) {\n if (this.board[r][c] === 0) {\n emptyCells.push({ r, c });\n }\n }\n }\n if (emptyCells.length > 0) {\n let randomCell = emptyCells[Math.floor(Math.random() * emptyCells.length)];\n this.board[randomCell.r][randomCell.c] = Math.random() < 0.9 ? 2 : 4;\n }\n }\n\n move(direction) {\n // This function will handle the logic for moving tiles\n // in the specified direction and merging them\n // It will also update the score and add a new random tile if the move is successful\n // The actual implementation of this function is complex and would require\n // a significant amount of code to handle all the cases for moving and merging tiles\n // For the purposes of this example, we will not implement the full logic\n // Instead, we will just call addRandomTile to simulate a move\n this.addRandomTile();\n }\n\n getBoard() {\n return this.board;\n }\n\n getScore() {\n return this.score;\n }\n\n getBestScore() {\n return this.bestScore;\n }\n\n setBestScore(score) {\n this.bestScore = score;\n }\n}\n\n```\n\"\"\"\n\n\nCODE_REVIEW_SMALLEST_CONTEXT = \"\"\"\n## Code to be Reviewed: game.js\n```Code\n// game.js\nclass Game {\n constructor() {\n this.board = this.createEmptyBoard();\n this.score = 0;\n this.bestScore = 0;\n }\n\n createEmptyBoard() {\n const board = [];\n for (let i = 0; i < 4; i++) {\n board[i] = [0, 0, 0, 0];\n }\n return board;\n }\n\n startGame() {\n this.board = this.createEmptyBoard();\n this.score = 0;\n this.addRandomTile();\n this.addRandomTile();\n }\n\n addRandomTile() {\n let emptyCells = [];\n for (let r = 0; r < 4; r++) {\n for (let c = 0; c < 4; c++) {\n if (this.board[r][c] === 0) {\n emptyCells.push({ r, c });\n }\n }\n }\n if (emptyCells.length > 0) {\n let randomCell = emptyCells[Math.floor(Math.random() * emptyCells.length)];\n this.board[randomCell.r][randomCell.c] = Math.random() < 0.9 ? 2 : 4;\n }\n }\n\n move(direction) {\n // This function will handle the logic for moving tiles\n // in the specified direction and merging them\n // It will also update the score and add a new random tile if the move is successful\n // The actual implementation of this function is complex and would require\n // a significant amount of code to handle all the cases for moving and merging tiles\n // For the purposes of this example, we will not implement the full logic\n // Instead, we will just call addRandomTile to simulate a move\n this.addRandomTile();\n }\n\n getBoard() {\n return this.board;\n }\n\n getScore() {\n return this.score;\n }\n\n getBestScore() {\n return this.bestScore;\n }\n\n setBestScore(score) {\n this.bestScore = score;\n }\n}\n\n```\n\"\"\"\n\n\nCODE_REVIEW_SAMPLE = \"\"\"\n## Code Review: game.js\n1. The code partially implements the requirements. The `Game` class is missing the full implementation of the `move` method, which is crucial for the game\\'s functionality.\n2. The code logic is not completely correct. The `move` method is not implemented, which means the game cannot process player moves.\n3. The existing code follows the \"Data structures and interfaces\" in terms of class structure but lacks full method implementations.\n4. Not all functions are implemented. The `move` method is incomplete and does not handle the logic for moving and merging tiles.\n5. All necessary pre-dependencies seem to be imported since the code does not indicate the need for additional imports.\n6. The methods from other files (such as `Storage`) are not being used in the provided code snippet, but the class structure suggests that they will be used correctly.\n\n## Actions\n1. Implement the `move` method to handle tile movements and merging. This is a complex task that requires careful consideration of the game\\'s rules and logic. Here is a simplified version of how one might begin to implement the `move` method:\n ```javascript\n move(direction) {\n // Simplified logic for moving tiles up\n if (direction === \\'up\\') {\n for (let col = 0; col < 4; col++) {\n let tiles = this.board.map(row => row[col]).filter(val => val !== 0);\n let merged = [];\n for (let i = 0; i < tiles.length; i++) {\n if (tiles[i] === tiles[i + 1]) {\n tiles[i] *= 2;\n this.score += tiles[i];\n tiles[i + 1] = 0;\n merged.push(i);\n }\n }\n tiles = tiles.filter(val => val !== 0);\n while (tiles.length < 4) {\n tiles.push(0);\n }\n for (let row = 0; row < 4; row++) {\n this.board[row][col] = tiles[row];\n }\n }\n }\n // Additional logic needed for \\'down\\', \\'left\\', \\'right\\'\n // ...\n this.addRandomTile();\n }\n ```\n2. Integrate the `Storage` class methods to handle the best score. This means updating the `startGame` and `setBestScore` methods to use `Storage` for retrieving and setting the best score:\n ```javascript\n startGame() {\n this.board = this.createEmptyBoard();\n this.score = 0;\n this.bestScore = new Storage().getBestScore(); // Retrieve the best score from storage\n this.addRandomTile();\n this.addRandomTile();\n }\n\n setBestScore(score) {\n if (score > this.bestScore) {\n this.bestScore = score;\n new Storage().setBestScore(score); // Set the new best score in storage\n }\n }\n ```\n\n## Code Review Result\nLBTM\n\n```\n\"\"\"\n\n\nWRITE_CODE_NODE = ActionNode.from_children(\"WRITE_REVIEW_NODE\", [REVIEW, REVIEW_RESULT, NEXT_STEPS])\nWRITE_MOVE_NODE = ActionNode.from_children(\"WRITE_MOVE_NODE\", [WRITE_DRAFT, WRITE_FUNCTION])\n\n\nCR_FOR_MOVE_FUNCTION_BY_3 = \"\"\"\nThe move function implementation provided appears to be well-structured and follows a clear logic for moving and merging tiles in the specified direction. However, there are a few potential improvements that could be made to enhance the code:\n\n1. Encapsulation: The logic for moving and merging tiles could be encapsulated into smaller, reusable functions to improve readability and maintainability.\n\n2. Magic Numbers: There are some magic numbers (e.g., 4, 3) used in the loops that could be replaced with named constants for improved readability and easier maintenance.\n\n3. Comments: Adding comments to explain the logic and purpose of each section of the code can improve understanding for future developers who may need to work on or maintain the code.\n\n4. Error Handling: It's important to consider error handling for unexpected input or edge cases to ensure the function behaves as expected in all scenarios.\n\nOverall, the code could benefit from refactoring to improve readability, maintainability, and extensibility. If you would like, I can provide a refactored version of the move function that addresses these considerations.\n\"\"\"\n\n\nclass WriteCodeAN(Action):\n \"\"\"Write a code review for the context.\"\"\"\n\n async def run(self, context):\n self.llm.system_prompt = \"You are an outstanding engineer and can implement any code\"\n return await WRITE_MOVE_NODE.fill(context=context, llm=self.llm, schema=\"json\")\n\n\nasync def main():\n await WriteCodeAN().run(CODE_REVIEW_SMALLEST_CONTEXT)\n\n\nif __name__ == \"__main__\":\n asyncio.run(main())\n\n```\n\n---\nTranslate the code above into Mermaid Sequence Diagram.": "```mermaid\nsequenceDiagram\n participant User\n participant WriteCodeAN\n participant WRITE_MOVE_NODE\n\n User->>WriteCodeAN: Run\n WriteCodeAN->>WRITE_MOVE_NODE: Fill\n WRITE_MOVE_NODE->>WriteCodeAN: Filled\n WriteCodeAN->>User: Return Filled Context\n```", + "You are a function parser.#MSG_SEP#You can convert spoken words into function parameters.#SYSTEM_MSG_END#text_to_image function parameters description:\nparameter `text`: The text used for image conversion.\nparameter `size_type`: size type\n\n---\nExamples:\nIf want you to do `Draw a girl`, return `text_to_image(text=\"Draw a girl\", size_type=\"512x512\")` brief and clear.\nIf want you to do `Draw an apple`, return `text_to_image(text=\"Draw an apple\", size_type=\"512x512\")` brief and clear.\n\n---\n\nRefer to the `text_to_image` function description, and fill in the function parameters according to the example \"I want you to do xx\" in the Examples section.\nNow I want you to do `Draw an apple`, return function parameters in Examples format above, brief and clear.": "`text_to_image(text=\"Draw an apple\", size_type=\"512x512\")`", + "\nNOTICE\nRole: You are a professional engineer; the main goal is to write google-style, elegant, modular, easy to read and maintain code\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## Design\n{\"Implementation approach\": \"We will use the Pygame library to create the game interface and handle user input. The game logic will be implemented using Python classes and data structures.\", \"File list\": [\"main.py\", \"game.py\"], \"Data structures and interfaces\": \"classDiagram\\n class Game {\\n -grid: List[List[int]]\\n -score: int\\n -game_over: bool\\n +__init__()\\n +reset_game()\\n +move(direction: str)\\n +is_game_over() bool\\n +get_empty_cells() List[Tuple[int, int]]\\n +add_new_tile()\\n +get_score() int\\n }\\n class UI {\\n -game: Game\\n +__init__(game: Game)\\n +draw_grid()\\n +draw_score()\\n +draw_game_over()\\n +handle_input()\\n }\\n Game --> UI\", \"Program call flow\": \"sequenceDiagram\\n participant M as Main\\n participant G as Game\\n participant U as UI\\n M->>G: reset_game()\\n M->>U: draw_grid()\\n M->>U: draw_score()\\n M->>U: handle_input()\\n U->>G: move(direction)\\n G->>G: add_new_tile()\\n G->>U: draw_grid()\\n G->>U: draw_score()\\n G->>U: draw_game_over()\\n G->>G: is_game_over()\\n G->>G: get_empty_cells()\\n G->>G: get_score()\", \"Anything UNCLEAR\": \"...\"}\n\n## Tasks\n{\"Required Python packages\": [\"pygame==2.0.1\"], \"Required Other language third-party packages\": [\"No third-party dependencies required\"], \"Logic Analysis\": [[\"game.py\", \"Contains Game class and related functions for game logic\"], [\"main.py\", \"Contains main function, initializes the game and UI\"]], \"Task list\": [\"game.py\", \"main.py\"], \"Full API spec\": \"\", \"Shared Knowledge\": \"The game logic will be implemented using Python classes and data structures. The Pygame library will be used to create the game interface and handle user input.\", \"Anything UNCLEAR\": \"...\"}\n\n## Legacy Code\n```Code\n----- main.py\nif __name__ == \"__main__\":\nmain()\n```\n\n## Debug logs\n```text\n\n\n\n```\n\n## Bug Feedback logs\n```text\n\n```\n\n# Format example\n## Code: game.py\n```python\n## game.py\n...\n```\n\n# Instruction: Based on the context, follow \"Format example\", write code.\n\n## Code: game.py. Write code with triple quoto, based on the following attentions and context.\n1. Only One file: do your best to implement THIS ONLY ONE FILE.\n2. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets.\n3. Set default value: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. AVOID circular import.\n4. Follow design: YOU MUST FOLLOW \"Data structures and interfaces\". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design.\n5. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE.\n6. Before using a external variable/module, make sure you import it first.\n7. Write out EVERY CODE DETAIL, DON'T LEAVE TODO.\n\n": "```python\n## game.py\n\nfrom typing import List, Tuple\nimport random\n\nclass Game:\n def __init__(self):\n self.grid: List[List[int]] = [[0 for _ in range(4)] for _ in range(4)]\n self.score: int = 0\n self.game_over: bool = False\n\n def reset_game(self):\n self.grid = [[0 for _ in range(4)] for _ in range(4)]\n self.score = 0\n self.game_over = False\n self.add_new_tile()\n self.add_new_tile()\n\n def move(self, direction: str):\n # Implement the logic to move the tiles in the specified direction\n pass\n\n def is_game_over(self) -> bool:\n # Implement the logic to check if the game is over\n pass\n\n def get_empty_cells(self) -> List[Tuple[int, int]]:\n # Implement the logic to get the coordinates of empty cells in the grid\n pass\n\n def add_new_tile(self):\n empty_cells = self.get_empty_cells()\n if empty_cells:\n row, col = random.choice(empty_cells)\n self.grid[row][col] = 2 if random.random() < 0.9 else 4\n\n def get_score(self) -> int:\n return self.score\n```", + "\n## context\n\n### Project Name\n\n\n### Original Requirements\n开发一个基于大语言模型与私有知识库的搜索引擎,希望可以基于大语言模型进行搜索总结\n\n### Search Information\n-\n\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Language\": \"en_us\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"Create a 2048 game\",\n \"Project Name\": \"game_2048\",\n \"Product Goals\": [\n \"Create an engaging user experience\",\n \"Improve accessibility, be responsive\",\n \"More beautiful UI\"\n ],\n \"User Stories\": [\n \"As a player, I want to be able to choose difficulty levels\",\n \"As a player, I want to see my score after each game\",\n \"As a player, I want to get restart button when I lose\",\n \"As a player, I want to see beautiful UI that make me feel good\",\n \"As a player, I want to play game via mobile phone\"\n ],\n \"Competitive Analysis\": [\n \"2048 Game A: Simple interface, lacks responsive features\",\n \"play2048.co: Beautiful and responsive UI with my best score shown\",\n \"2048game.com: Responsive UI with my best score shown, but many ads\"\n ],\n \"Competitive Quadrant Chart\": \"quadrantChart\\n title \\\"Reach and engagement of campaigns\\\"\\n x-axis \\\"Low Reach\\\" --> \\\"High Reach\\\"\\n y-axis \\\"Low Engagement\\\" --> \\\"High Engagement\\\"\\n quadrant-1 \\\"We should expand\\\"\\n quadrant-2 \\\"Need to promote\\\"\\n quadrant-3 \\\"Re-evaluate\\\"\\n quadrant-4 \\\"May be improved\\\"\\n \\\"Campaign A\\\": [0.3, 0.6]\\n \\\"Campaign B\\\": [0.45, 0.23]\\n \\\"Campaign C\\\": [0.57, 0.69]\\n \\\"Campaign D\\\": [0.78, 0.34]\\n \\\"Campaign E\\\": [0.40, 0.34]\\n \\\"Campaign F\\\": [0.35, 0.78]\\n \\\"Our Target Product\\\": [0.5, 0.6]\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [\n [\n \"P0\",\n \"The main code ...\"\n ],\n [\n \"P0\",\n \"The game algorithm ...\"\n ]\n ],\n \"UI Design draft\": \"Basic function description with a simple style and layout.\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Language: # Provide the language used in the project, typically matching the user's requirement language.\n- Programming Language: # Python/JavaScript or other mainstream programming language.\n- Original Requirements: # Place the original user's requirements here.\n- Project Name: # According to the content of \"Original Requirements,\" name the project using snake case style , like 'game_2048' or 'simple_crm.\n- Product Goals: typing.List[str] # Provide up to three clear, orthogonal product goals.\n- User Stories: typing.List[str] # Provide up to 3 to 5 scenario-based user stories.\n- Competitive Analysis: typing.List[str] # Provide 5 to 7 competitive products.\n- Competitive Quadrant Chart: # Use mermaid quadrantChart syntax. Distribute scores evenly between 0 and 1\n- Requirement Analysis: # Provide a detailed analysis of the requirements.\n- Requirement Pool: typing.List[typing.List[str]] # List down the top-5 requirements with their priority (P0, P1, P2).\n- UI Design draft: # Provide a simple description of UI elements, functions, style, and layout.\n- Anything UNCLEAR: # Mention any aspects of the project that are unclear and try to clarify them.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Language\": \"zh_cn\",\n \"Programming Language\": \"Python\",\n \"Original Requirements\": \"开发一个基于大语言模型与私有知识库的搜索引擎,希望可以基于大语言模型进行搜索总结\",\n \"Project Name\": \"language_model_search\",\n \"Product Goals\": [\n \"提供基于大语言模型的高效搜索功能\",\n \"整合私有知识库,实现个性化搜索结果\",\n \"实现搜索结果的自动总结功能\"\n ],\n \"User Stories\": [\n \"作为用户,我希望能够快速找到我需要的信息\",\n \"作为用户,我希望搜索结果能够根据我的偏好进行个性化排序\",\n \"作为用户,我希望搜索结果能够自动总结,方便我快速了解内容\"\n ],\n \"Competitive Analysis\": [\n \"搜索引擎A: 提供基于大语言模型的搜索功能,但个性化程度较低\",\n \"知识库B: 整合私有知识库,但搜索速度较慢\",\n \"语言模型搜索C: 提供搜索结果自动总结功能,但搜索准确度有待提高\"\n ],\n \"Competitive Quadrant Chart\": \"quadrantChart\\n title \\\"搜索引擎竞争分析\\\"\\n x-axis \\\"低搜索准确度\\\" --> \\\"高搜索准确度\\\"\\n y-axis \\\"低个性化程度\\\" --> \\\"高个性化程度\\\"\\n quadrant-1 \\\"需改进\\\"\\n quadrant-2 \\\"需提升\\\"\\n quadrant-3 \\\"重新评估\\\"\\n quadrant-4 \\\"扩展发展\\\"\\n \\\"搜索引擎A\\\": [0.6, 0.3]\\n \\\"知识库B\\\": [0.4, 0.2]\\n \\\"语言模型搜索C\\\": [0.7, 0.5]\\n \\\"我们的目标产品\\\": [0.8, 0.7]\",\n \"Requirement Analysis\": \"\",\n \"Requirement Pool\": [\n [\n \"P0\",\n \"基于大语言模型的高效搜索功能\"\n ],\n [\n \"P1\",\n \"整合私有知识库,实现个性化搜索结果\"\n ],\n [\n \"P2\",\n \"实现搜索结果的自动总结功能\"\n ]\n ],\n \"UI Design draft\": \"搜索页面简洁明了,搜索结果清晰展示,提供个性化排序和自动总结功能。\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]", + "\n## context\nNone\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Project Name\": \"game_2048\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Project Name: # According to the content of \"Original Requirements,\" name the project using snake case style with underline, like 'game_2048' or 'simple_crm.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Project Name\": \"game_2048\"\n}\n[/CONTENT]", + "\n## context\nCompare the key's value of nodes_output and the corresponding requirements one by one. If a key's value that does not match the requirement is found, provide the comment content on how to modify it. No output is required for matching keys.\n\n### nodes_output\n{\"Project Name\": {\"value\": \"game snake\", \"requirement\": \"According to the content of \\\"Original Requirements,\\\" name the project using snake case style with underline, like 'game_2048' or 'simple_crm.\"}}\n\n-----\n\n## format example\n[CONTENT]\n{\n \"key1\": \"comment1\",\n \"key2\": \"comment2\",\n \"keyn\": \"commentn\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- key1: # the first key name of mismatch key\n- key2: # the second key name of mismatch key\n- keyn: # the last key name of mismatch key\n\n## constraint\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow format example's json format, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Project Name\": \"Use snake case style with underline, like 'game_2048' or 'simple_crm.'\"\n}\n[/CONTENT]", + "\n## context\nCompare the key's value of nodes_output and the corresponding requirements one by one. If a key's value that does not match the requirement is found, provide the comment content on how to modify it. No output is required for matching keys.\n\n### nodes_output\n{\"Project Name\": {\"value\": \"game_2048\", \"requirement\": \"According to the content of \\\"Original Requirements,\\\" name the project using snake case style with underline, like 'game_2048' or 'simple_crm.\"}}\n\n-----\n\n## format example\n[CONTENT]\n{\n \"key1\": \"comment1\",\n \"key2\": \"comment2\",\n \"keyn\": \"commentn\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- key1: # the first key name of mismatch key\n- key2: # the second key name of mismatch key\n- keyn: # the last key name of mismatch key\n\n## constraint\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow format example's json format, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Project Name\": \"According to the content of \\\"Original Requirements,\\\" name the project using snake case style with underline, like 'game_2048' or 'simple_crm.\"\n}\n[/CONTENT]", + "\n## context\nchange the nodes_output key's value to meet its comment and no need to add extra comment.\n\n### nodes_output\n{\"Project Name\": {\"value\": \"game snake\", \"comment\": \"Use snake case style with underline, like 'game_2048' or 'simple_crm.'\"}}\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Project Name\": \"game_2048\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Project Name: # According to the content of \"Original Requirements,\" name the project using snake case style with underline, like 'game_2048' or 'simple_crm.\n\n\n## constraint\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow format example's json format, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Project Name\": \"game_snake\"\n}\n[/CONTENT]", + "\n## context\nCompare the key's value of nodes_output and the corresponding requirements one by one. If a key's value that does not match the requirement is found, provide the comment content on how to modify it. No output is required for matching keys.\n\n### nodes_output\n{\"Project Name\": {\"value\": \"game_snake\", \"requirement\": \"According to the content of \\\"Original Requirements,\\\" name the project using snake case style with underline, like 'game_2048' or 'simple_crm.\"}}\n\n-----\n\n## format example\n[CONTENT]\n{\n \"key1\": \"comment1\",\n \"key2\": \"comment2\",\n \"keyn\": \"commentn\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- key1: # the first key name of mismatch key\n- key2: # the second key name of mismatch key\n- keyn: # the last key name of mismatch key\n\n## constraint\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow format example's json format, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Project Name\": \"The project name should be in snake case style with underscores, such as 'game_2048' or 'simple_crm'.\"\n}\n[/CONTENT]", + "\n## context\nchange the nodes_output key's value to meet its comment and no need to add extra comment.\n\n### nodes_output\n{\"Project Name\": {\"value\": \"game_snake\", \"comment\": \"The project name should be in snake case style with underscores, such as 'game_2048' or 'simple_crm'.\"}}\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Project Name\": \"game_2048\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Project Name: # According to the content of \"Original Requirements,\" name the project using snake case style with underline, like 'game_2048' or 'simple_crm.\n\n\n## constraint\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow format example's json format, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Project Name\": \"game_snake\"\n}\n[/CONTENT]", + "You are an action classifier#SYSTEM_MSG_END#If the text explicitly want you to Generate a voice file from the input text, text-to-speech, return `[SKILL]: text_to_speech` brief and clear. For instance: [SKILL]: text_to_speech\nIf the text explicitly want you to Create a drawing based on the text., return `[SKILL]: text_to_image` brief and clear. For instance: [SKILL]: text_to_image\nIf the text explicitly want you to Perform Google searches to provide real-time information., return `[SKILL]: web_search` brief and clear. For instance: [SKILL]: web_search\nOtherwise, return `[TALK]: {talk}` brief and clear. For instance: if {talk} is \"xxxx\" return [TALK]: xxxx\n\nNow what specific action is explicitly mentioned in the text: No, I do not have a poison apple. Do you have a poison apple?\n": "[TALK]: No, I do not have a poison apple. Do you have a poison apple?", + "You are an action classifier#SYSTEM_MSG_END#If the text explicitly want you to Generate a voice file from the input text, text-to-speech, return `[SKILL]: text_to_speech` brief and clear. For instance: [SKILL]: text_to_speech\nIf the text explicitly want you to Create a drawing based on the text., return `[SKILL]: text_to_image` brief and clear. For instance: [SKILL]: text_to_image\nIf the text explicitly want you to Perform Google searches to provide real-time information., return `[SKILL]: web_search` brief and clear. For instance: [SKILL]: web_search\nOtherwise, return `[TALK]: {talk}` brief and clear. For instance: if {talk} is \"xxxx\" return [TALK]: xxxx\n\nNow what specific action is explicitly mentioned in the text: Sure, I can draw you an apple. Draw me an apple.\n": "[SKILL]: text_to_image", + "You are a function parser.#MSG_SEP#You can convert spoken words into function parameters.#SYSTEM_MSG_END#text_to_image function parameters description:\nparameter `text`: The text used for image conversion.\nparameter `size_type`: size type\n\n---\nExamples:\nIf want you to do `Draw a girl`, return `text_to_image(text=\"Draw a girl\", size_type=\"512x512\")` brief and clear.\nIf want you to do `Draw an apple`, return `text_to_image(text=\"Draw an apple\", size_type=\"512x512\")` brief and clear.\n\n---\n\nRefer to the `text_to_image` function description, and fill in the function parameters according to the example \"I want you to do xx\" in the Examples section.\nNow I want you to do `Sure, I can draw you an apple. Draw me an apple.`, return function parameters in Examples format above, brief and clear.": "`text_to_image(text=\"Sure, I can draw you an apple. Draw me an apple.\", size_type=\"512x512\")`", + "You are an action classifier#SYSTEM_MSG_END#Otherwise, return `[TALK]: {talk}` brief and clear. For instance: if {talk} is \"xxxx\" return [TALK]: xxxx\n\nNow what specific action is explicitly mentioned in the text: Sure, I can draw you an apple. Draw me an apple.\n": "[DRAW]: draw an apple", + "Here is an Example for 1 input and 8 possible thoughts:\nInput: 2 8 8 14\nPossible next steps:\n2 + 8 = 10 (left: 8 10 14)\n8 / 2 = 4 (left: 4 8 14)\n14 + 2 = 16 (left: 8 8 16)\n2 * 8 = 16 (left: 8 14 16)\n8 - 2 = 6 (left: 6 8 14)\n14 - 8 = 6 (left: 2 6 8)\n14 / 2 = 7 (left: 7 8 8)\n14 - 2 = 12 (left: 8 8 12)\n\nHere is my task for 1 input and 5 possible thoughts:\nInput: 4 5 6 10\nPossible next steps:\n\n\n\n\nEach output should be strictly a list of nodes, in json format, like this:\n```json\n [\n {\n \"node_id\": str = \"unique identifier for a solution, can be an ordinal\",\n \"node_state_instruction\": \"specified sample of solution\",\n },\n ...\n ]\n```\n": "Here is the list of possible next steps for the given input in JSON format:\n\n```json\n[\n {\n \"node_id\": \"1\",\n \"node_state_instruction\": \"4 + 5 = 9 (left: 6 9 10)\"\n },\n {\n \"node_id\": \"2\",\n \"node_state_instruction\": \"5 + 6 = 11 (left: 4 11 10)\"\n },\n {\n \"node_id\": \"3\",\n \"node_state_instruction\": \"4 * 5 = 20 (left: 6 20 10)\"\n },\n {\n \"node_id\": \"4\",\n \"node_state_instruction\": \"6 - 4 = 2 (left: 2 5 10)\"\n },\n {\n \"node_id\": \"5\",\n \"node_state_instruction\": \"10 - 4 = 6 (left: 6 5 6)\"\n }\n]\n```", + "Evaluate if given numbers can reach 24 (sure/likely/impossible)\n10 14\n10 + 14 = 24\nsure\n11 12\n11 + 12 = 23\n12 - 11 = 1\n11 * 12 = 132\n11 / 12 = 0.91\nimpossible\n4 4 10\n4 + 4 + 10 = 8 + 10 = 18\n4 * 10 - 4 = 40 - 4 = 36\n(10 - 4) * 4 = 6 * 4 = 24\nsure\n4 9 11\n9 + 11 + 4 = 20 + 4 = 24\nsure\n5 7 8\n5 + 7 + 8 = 12 + 8 = 20\n(8 - 5) * 7 = 3 * 7 = 21\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n5 6 6\n5 + 6 + 6 = 17\n(6 - 5) * 6 = 1 * 6 = 6\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n10 10 11\n10 + 10 + 11 = 31\n(11 - 10) * 10 = 10\n10 10 10 are all too big\nimpossible\n1 3 3\n1 * 3 * 3 = 9\n(1 + 3) * 3 = 12\n1 3 3 are all too small\nimpossible\n6 9 10\n": "6 * 9 - 10 = 54 - 10 = 44\n(9 - 6) * 10 = 3 * 10 = 30\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely", + "Evaluate if given numbers can reach 24 (sure/likely/impossible)\n10 14\n10 + 14 = 24\nsure\n11 12\n11 + 12 = 23\n12 - 11 = 1\n11 * 12 = 132\n11 / 12 = 0.91\nimpossible\n4 4 10\n4 + 4 + 10 = 8 + 10 = 18\n4 * 10 - 4 = 40 - 4 = 36\n(10 - 4) * 4 = 6 * 4 = 24\nsure\n4 9 11\n9 + 11 + 4 = 20 + 4 = 24\nsure\n5 7 8\n5 + 7 + 8 = 12 + 8 = 20\n(8 - 5) * 7 = 3 * 7 = 21\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n5 6 6\n5 + 6 + 6 = 17\n(6 - 5) * 6 = 1 * 6 = 6\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n10 10 11\n10 + 10 + 11 = 31\n(11 - 10) * 10 = 10\n10 10 10 are all too big\nimpossible\n1 3 3\n1 * 3 * 3 = 9\n(1 + 3) * 3 = 12\n1 3 3 are all too small\nimpossible\n6 20 10\n": "I'm sorry, but it seems that you haven't completed the calculation for the numbers 6, 20, and 10. If you'd like, I can help you with that.", + "Evaluate if given numbers can reach 24 (sure/likely/impossible)\n10 14\n10 + 14 = 24\nsure\n11 12\n11 + 12 = 23\n12 - 11 = 1\n11 * 12 = 132\n11 / 12 = 0.91\nimpossible\n4 4 10\n4 + 4 + 10 = 8 + 10 = 18\n4 * 10 - 4 = 40 - 4 = 36\n(10 - 4) * 4 = 6 * 4 = 24\nsure\n4 9 11\n9 + 11 + 4 = 20 + 4 = 24\nsure\n5 7 8\n5 + 7 + 8 = 12 + 8 = 20\n(8 - 5) * 7 = 3 * 7 = 21\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n5 6 6\n5 + 6 + 6 = 17\n(6 - 5) * 6 = 1 * 6 = 6\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n10 10 11\n10 + 10 + 11 = 31\n(11 - 10) * 10 = 10\n10 10 10 are all too big\nimpossible\n1 3 3\n1 * 3 * 3 = 9\n(1 + 3) * 3 = 12\n1 3 3 are all too small\nimpossible\n4 11 10\n": "4 11 10 cannot reach 24.", + "Evaluate if given numbers can reach 24 (sure/likely/impossible)\n10 14\n10 + 14 = 24\nsure\n11 12\n11 + 12 = 23\n12 - 11 = 1\n11 * 12 = 132\n11 / 12 = 0.91\nimpossible\n4 4 10\n4 + 4 + 10 = 8 + 10 = 18\n4 * 10 - 4 = 40 - 4 = 36\n(10 - 4) * 4 = 6 * 4 = 24\nsure\n4 9 11\n9 + 11 + 4 = 20 + 4 = 24\nsure\n5 7 8\n5 + 7 + 8 = 12 + 8 = 20\n(8 - 5) * 7 = 3 * 7 = 21\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n5 6 6\n5 + 6 + 6 = 17\n(6 - 5) * 6 = 1 * 6 = 6\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n10 10 11\n10 + 10 + 11 = 31\n(11 - 10) * 10 = 10\n10 10 10 are all too big\nimpossible\n1 3 3\n1 * 3 * 3 = 9\n(1 + 3) * 3 = 12\n1 3 3 are all too small\nimpossible\n2 5 10\n": "2 * 5 * 10 = 100\n(2 + 5) * 10 = 70\n2 5 10 are all too big\nimpossible", + "Evaluate if given numbers can reach 24 (sure/likely/impossible)\n10 14\n10 + 14 = 24\nsure\n11 12\n11 + 12 = 23\n12 - 11 = 1\n11 * 12 = 132\n11 / 12 = 0.91\nimpossible\n4 4 10\n4 + 4 + 10 = 8 + 10 = 18\n4 * 10 - 4 = 40 - 4 = 36\n(10 - 4) * 4 = 6 * 4 = 24\nsure\n4 9 11\n9 + 11 + 4 = 20 + 4 = 24\nsure\n5 7 8\n5 + 7 + 8 = 12 + 8 = 20\n(8 - 5) * 7 = 3 * 7 = 21\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n5 6 6\n5 + 6 + 6 = 17\n(6 - 5) * 6 = 1 * 6 = 6\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n10 10 11\n10 + 10 + 11 = 31\n(11 - 10) * 10 = 10\n10 10 10 are all too big\nimpossible\n1 3 3\n1 * 3 * 3 = 9\n(1 + 3) * 3 = 12\n1 3 3 are all too small\nimpossible\n6 5 6\n": "I'm sorry, but it seems that you have not provided a complete expression for the numbers 6, 5, and 6 to reach 24. If you would like to try again, please provide a complete expression using the numbers 6, 5, and 6 to reach the target number of 24.", + "Here is an Example for 1 input and 8 possible thoughts:\nInput: 2 8 8 14\nPossible next steps:\n2 + 8 = 10 (left: 8 10 14)\n8 / 2 = 4 (left: 4 8 14)\n14 + 2 = 16 (left: 8 8 16)\n2 * 8 = 16 (left: 8 14 16)\n8 - 2 = 6 (left: 6 8 14)\n14 - 8 = 6 (left: 2 6 8)\n14 / 2 = 7 (left: 7 8 8)\n14 - 2 = 12 (left: 8 8 12)\n\nHere is my task for 1 input and 5 possible thoughts:\nInput: 4 11 10\nPossible next steps:\n\n\n\n\nEach output should be strictly a list of nodes, in json format, like this:\n```json\n [\n {\n \"node_id\": str = \"unique identifier for a solution, can be an ordinal\",\n \"node_state_instruction\": \"specified sample of solution\",\n },\n ...\n ]\n```\n": "Here are the possible next steps for the given input in the requested JSON format:\n\nInput: 4 11 10\nPossible next steps:\n```json\n[\n {\n \"node_id\": \"1\",\n \"node_state_instruction\": \"4 + 11 = 15 (left: 10 15)\"\n },\n {\n \"node_id\": \"2\",\n \"node_state_instruction\": \"11 - 4 = 7 (left: 7 10)\"\n },\n {\n \"node_id\": \"3\",\n \"node_state_instruction\": \"4 * 11 = 44 (left: 10 44)\"\n },\n {\n \"node_id\": \"4\",\n \"node_state_instruction\": \"11 / 4 = 2.75 (left: 2.75 10)\"\n },\n {\n \"node_id\": \"5\",\n \"node_state_instruction\": \"11 + 4 = 15 (left: 10 15)\"\n }\n]\n```", + "Here is an Example for 1 input and 8 possible thoughts:\nInput: 2 8 8 14\nPossible next steps:\n2 + 8 = 10 (left: 8 10 14)\n8 / 2 = 4 (left: 4 8 14)\n14 + 2 = 16 (left: 8 8 16)\n2 * 8 = 16 (left: 8 14 16)\n8 - 2 = 6 (left: 6 8 14)\n14 - 8 = 6 (left: 2 6 8)\n14 / 2 = 7 (left: 7 8 8)\n14 - 2 = 12 (left: 8 8 12)\n\nHere is my task for 1 input and 5 possible thoughts:\nInput: 6 9 10\nPossible next steps:\n\n\n\n\nEach output should be strictly a list of nodes, in json format, like this:\n```json\n [\n {\n \"node_id\": str = \"unique identifier for a solution, can be an ordinal\",\n \"node_state_instruction\": \"specified sample of solution\",\n },\n ...\n ]\n```\n": "Certainly! Here are the possible next steps for the given input in JSON format:\n\nInput: 6 9 10\nPossible next steps:\n```json\n[\n {\n \"node_id\": \"1\",\n \"node_state_instruction\": \"6 + 9 = 15 (left: 10 15)\"\n },\n {\n \"node_id\": \"2\",\n \"node_state_instruction\": \"6 * 9 = 54 (left: 10 54)\"\n },\n {\n \"node_id\": \"3\",\n \"node_state_instruction\": \"9 - 6 = 3 (left: 3 9 10)\"\n },\n {\n \"node_id\": \"4\",\n \"node_state_instruction\": \"10 - 6 = 4 (left: 4 6 9)\"\n },\n {\n \"node_id\": \"5\",\n \"node_state_instruction\": \"10 / 6 = 1 (left: 1 6 9)\"\n }\n]\n```", + "Evaluate if given numbers can reach 24 (sure/likely/impossible)\n10 14\n10 + 14 = 24\nsure\n11 12\n11 + 12 = 23\n12 - 11 = 1\n11 * 12 = 132\n11 / 12 = 0.91\nimpossible\n4 4 10\n4 + 4 + 10 = 8 + 10 = 18\n4 * 10 - 4 = 40 - 4 = 36\n(10 - 4) * 4 = 6 * 4 = 24\nsure\n4 9 11\n9 + 11 + 4 = 20 + 4 = 24\nsure\n5 7 8\n5 + 7 + 8 = 12 + 8 = 20\n(8 - 5) * 7 = 3 * 7 = 21\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n5 6 6\n5 + 6 + 6 = 17\n(6 - 5) * 6 = 1 * 6 = 6\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n10 10 11\n10 + 10 + 11 = 31\n(11 - 10) * 10 = 10\n10 10 10 are all too big\nimpossible\n1 3 3\n1 * 3 * 3 = 9\n(1 + 3) * 3 = 12\n1 3 3 are all too small\nimpossible\n10 15\n": "I'm sorry, I cannot evaluate the numbers 10 and 15 without an operation. If you provide an operation, I can help you determine if they can reach 24.", + "Here is an Example for 1 input and 8 possible thoughts:\nInput: 2 8 8 14\nPossible next steps:\n2 + 8 = 10 (left: 8 10 14)\n8 / 2 = 4 (left: 4 8 14)\n14 + 2 = 16 (left: 8 8 16)\n2 * 8 = 16 (left: 8 14 16)\n8 - 2 = 6 (left: 6 8 14)\n14 - 8 = 6 (left: 2 6 8)\n14 / 2 = 7 (left: 7 8 8)\n14 - 2 = 12 (left: 8 8 12)\n\nHere is my task for 1 input and 5 possible thoughts:\nInput: 6 20 10\nPossible next steps:\n\n\n\n\nEach output should be strictly a list of nodes, in json format, like this:\n```json\n [\n {\n \"node_id\": str = \"unique identifier for a solution, can be an ordinal\",\n \"node_state_instruction\": \"specified sample of solution\",\n },\n ...\n ]\n```\n": "Certainly! Here is the list of nodes for the given input and possible next steps:\n\n```json\n[\n {\n \"node_id\": \"1\",\n \"node_state_instruction\": \"6 + 20 = 26 (left: 10 26)\"\n },\n {\n \"node_id\": \"2\",\n \"node_state_instruction\": \"20 - 6 = 14 (left: 14 10)\"\n },\n {\n \"node_id\": \"3\",\n \"node_state_instruction\": \"6 * 20 = 120 (left: 10 120)\"\n },\n {\n \"node_id\": \"4\",\n \"node_state_instruction\": \"20 / 6 = 3.33 (left: 3.33 10)\"\n },\n {\n \"node_id\": \"5\",\n \"node_state_instruction\": \"20 + 6 = 26 (left: 10 26)\"\n }\n]\n```", + "Evaluate if given numbers can reach 24 (sure/likely/impossible)\n10 14\n10 + 14 = 24\nsure\n11 12\n11 + 12 = 23\n12 - 11 = 1\n11 * 12 = 132\n11 / 12 = 0.91\nimpossible\n4 4 10\n4 + 4 + 10 = 8 + 10 = 18\n4 * 10 - 4 = 40 - 4 = 36\n(10 - 4) * 4 = 6 * 4 = 24\nsure\n4 9 11\n9 + 11 + 4 = 20 + 4 = 24\nsure\n5 7 8\n5 + 7 + 8 = 12 + 8 = 20\n(8 - 5) * 7 = 3 * 7 = 21\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n5 6 6\n5 + 6 + 6 = 17\n(6 - 5) * 6 = 1 * 6 = 6\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n10 10 11\n10 + 10 + 11 = 31\n(11 - 10) * 10 = 10\n10 10 10 are all too big\nimpossible\n1 3 3\n1 * 3 * 3 = 9\n(1 + 3) * 3 = 12\n1 3 3 are all too small\nimpossible\n1 6 9\n": "1 6 9 cannot reach 24 using addition, subtraction, multiplication, or division. Therefore, it is impossible for these numbers to reach 24.", + "Evaluate if given numbers can reach 24 (sure/likely/impossible)\n10 14\n10 + 14 = 24\nsure\n11 12\n11 + 12 = 23\n12 - 11 = 1\n11 * 12 = 132\n11 / 12 = 0.91\nimpossible\n4 4 10\n4 + 4 + 10 = 8 + 10 = 18\n4 * 10 - 4 = 40 - 4 = 36\n(10 - 4) * 4 = 6 * 4 = 24\nsure\n4 9 11\n9 + 11 + 4 = 20 + 4 = 24\nsure\n5 7 8\n5 + 7 + 8 = 12 + 8 = 20\n(8 - 5) * 7 = 3 * 7 = 21\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n5 6 6\n5 + 6 + 6 = 17\n(6 - 5) * 6 = 1 * 6 = 6\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n10 10 11\n10 + 10 + 11 = 31\n(11 - 10) * 10 = 10\n10 10 10 are all too big\nimpossible\n1 3 3\n1 * 3 * 3 = 9\n(1 + 3) * 3 = 12\n1 3 3 are all too small\nimpossible\n10 54\n": "It seems like you didn't provide the complete set of numbers for the last calculation. Could you please provide the third number so that I can help you evaluate if they can reach 24?", + "Evaluate if given numbers can reach 24 (sure/likely/impossible)\n10 14\n10 + 14 = 24\nsure\n11 12\n11 + 12 = 23\n12 - 11 = 1\n11 * 12 = 132\n11 / 12 = 0.91\nimpossible\n4 4 10\n4 + 4 + 10 = 8 + 10 = 18\n4 * 10 - 4 = 40 - 4 = 36\n(10 - 4) * 4 = 6 * 4 = 24\nsure\n4 9 11\n9 + 11 + 4 = 20 + 4 = 24\nsure\n5 7 8\n5 + 7 + 8 = 12 + 8 = 20\n(8 - 5) * 7 = 3 * 7 = 21\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n5 6 6\n5 + 6 + 6 = 17\n(6 - 5) * 6 = 1 * 6 = 6\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n10 10 11\n10 + 10 + 11 = 31\n(11 - 10) * 10 = 10\n10 10 10 are all too big\nimpossible\n1 3 3\n1 * 3 * 3 = 9\n(1 + 3) * 3 = 12\n1 3 3 are all too small\nimpossible\n7 10\n": "I'm sorry, but it seems like you didn't provide a complete set of numbers for the last question. Could you please provide the third number so that I can evaluate if they can reach 24?", + "Evaluate if given numbers can reach 24 (sure/likely/impossible)\n10 14\n10 + 14 = 24\nsure\n11 12\n11 + 12 = 23\n12 - 11 = 1\n11 * 12 = 132\n11 / 12 = 0.91\nimpossible\n4 4 10\n4 + 4 + 10 = 8 + 10 = 18\n4 * 10 - 4 = 40 - 4 = 36\n(10 - 4) * 4 = 6 * 4 = 24\nsure\n4 9 11\n9 + 11 + 4 = 20 + 4 = 24\nsure\n5 7 8\n5 + 7 + 8 = 12 + 8 = 20\n(8 - 5) * 7 = 3 * 7 = 21\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n5 6 6\n5 + 6 + 6 = 17\n(6 - 5) * 6 = 1 * 6 = 6\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n10 10 11\n10 + 10 + 11 = 31\n(11 - 10) * 10 = 10\n10 10 10 are all too big\nimpossible\n1 3 3\n1 * 3 * 3 = 9\n(1 + 3) * 3 = 12\n1 3 3 are all too small\nimpossible\n10 44\n": "It seems like you didn't provide the second number for the last set. Could you please provide the second number so that I can evaluate if they can reach 24?", + "Evaluate if given numbers can reach 24 (sure/likely/impossible)\n10 14\n10 + 14 = 24\nsure\n11 12\n11 + 12 = 23\n12 - 11 = 1\n11 * 12 = 132\n11 / 12 = 0.91\nimpossible\n4 4 10\n4 + 4 + 10 = 8 + 10 = 18\n4 * 10 - 4 = 40 - 4 = 36\n(10 - 4) * 4 = 6 * 4 = 24\nsure\n4 9 11\n9 + 11 + 4 = 20 + 4 = 24\nsure\n5 7 8\n5 + 7 + 8 = 12 + 8 = 20\n(8 - 5) * 7 = 3 * 7 = 21\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n5 6 6\n5 + 6 + 6 = 17\n(6 - 5) * 6 = 1 * 6 = 6\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n10 10 11\n10 + 10 + 11 = 31\n(11 - 10) * 10 = 10\n10 10 10 are all too big\nimpossible\n1 3 3\n1 * 3 * 3 = 9\n(1 + 3) * 3 = 12\n1 3 3 are all too small\nimpossible\n10 26\n": "It seems that you have not provided the complete set of numbers for the last question. Could you please provide the third number for the set?", + "Evaluate if given numbers can reach 24 (sure/likely/impossible)\n10 14\n10 + 14 = 24\nsure\n11 12\n11 + 12 = 23\n12 - 11 = 1\n11 * 12 = 132\n11 / 12 = 0.91\nimpossible\n4 4 10\n4 + 4 + 10 = 8 + 10 = 18\n4 * 10 - 4 = 40 - 4 = 36\n(10 - 4) * 4 = 6 * 4 = 24\nsure\n4 9 11\n9 + 11 + 4 = 20 + 4 = 24\nsure\n5 7 8\n5 + 7 + 8 = 12 + 8 = 20\n(8 - 5) * 7 = 3 * 7 = 21\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n5 6 6\n5 + 6 + 6 = 17\n(6 - 5) * 6 = 1 * 6 = 6\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n10 10 11\n10 + 10 + 11 = 31\n(11 - 10) * 10 = 10\n10 10 10 are all too big\nimpossible\n1 3 3\n1 * 3 * 3 = 9\n(1 + 3) * 3 = 12\n1 3 3 are all too small\nimpossible\n10 120\n": "It seems that you have not provided the complete set of numbers for the last question. Could you please provide the third number for the evaluation?", + "Evaluate if given numbers can reach 24 (sure/likely/impossible)\n10 14\n10 + 14 = 24\nsure\n11 12\n11 + 12 = 23\n12 - 11 = 1\n11 * 12 = 132\n11 / 12 = 0.91\nimpossible\n4 4 10\n4 + 4 + 10 = 8 + 10 = 18\n4 * 10 - 4 = 40 - 4 = 36\n(10 - 4) * 4 = 6 * 4 = 24\nsure\n4 9 11\n9 + 11 + 4 = 20 + 4 = 24\nsure\n5 7 8\n5 + 7 + 8 = 12 + 8 = 20\n(8 - 5) * 7 = 3 * 7 = 21\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n5 6 6\n5 + 6 + 6 = 17\n(6 - 5) * 6 = 1 * 6 = 6\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n10 10 11\n10 + 10 + 11 = 31\n(11 - 10) * 10 = 10\n10 10 10 are all too big\nimpossible\n1 3 3\n1 * 3 * 3 = 9\n(1 + 3) * 3 = 12\n1 3 3 are all too small\nimpossible\n3 9 10\n": "3 * 9 * 10 = 270\n(10 - 3) * 9 = 63\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely", + "Evaluate if given numbers can reach 24 (sure/likely/impossible)\n10 14\n10 + 14 = 24\nsure\n11 12\n11 + 12 = 23\n12 - 11 = 1\n11 * 12 = 132\n11 / 12 = 0.91\nimpossible\n4 4 10\n4 + 4 + 10 = 8 + 10 = 18\n4 * 10 - 4 = 40 - 4 = 36\n(10 - 4) * 4 = 6 * 4 = 24\nsure\n4 9 11\n9 + 11 + 4 = 20 + 4 = 24\nsure\n5 7 8\n5 + 7 + 8 = 12 + 8 = 20\n(8 - 5) * 7 = 3 * 7 = 21\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n5 6 6\n5 + 6 + 6 = 17\n(6 - 5) * 6 = 1 * 6 = 6\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n10 10 11\n10 + 10 + 11 = 31\n(11 - 10) * 10 = 10\n10 10 10 are all too big\nimpossible\n1 3 3\n1 * 3 * 3 = 9\n(1 + 3) * 3 = 12\n1 3 3 are all too small\nimpossible\n2.75 10\n": "It seems like you have a mix of numbers and operations. Could you please provide the operations for the last set of numbers (2.75 and 10) so that I can help you evaluate if they can reach 24?", + "Evaluate if given numbers can reach 24 (sure/likely/impossible)\n10 14\n10 + 14 = 24\nsure\n11 12\n11 + 12 = 23\n12 - 11 = 1\n11 * 12 = 132\n11 / 12 = 0.91\nimpossible\n4 4 10\n4 + 4 + 10 = 8 + 10 = 18\n4 * 10 - 4 = 40 - 4 = 36\n(10 - 4) * 4 = 6 * 4 = 24\nsure\n4 9 11\n9 + 11 + 4 = 20 + 4 = 24\nsure\n5 7 8\n5 + 7 + 8 = 12 + 8 = 20\n(8 - 5) * 7 = 3 * 7 = 21\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n5 6 6\n5 + 6 + 6 = 17\n(6 - 5) * 6 = 1 * 6 = 6\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n10 10 11\n10 + 10 + 11 = 31\n(11 - 10) * 10 = 10\n10 10 10 are all too big\nimpossible\n1 3 3\n1 * 3 * 3 = 9\n(1 + 3) * 3 = 12\n1 3 3 are all too small\nimpossible\n4 6 9\n": "4 + 6 + 9 = 19\n(9 - 4) * 6 = 5 * 6 = 30\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely", + "Evaluate if given numbers can reach 24 (sure/likely/impossible)\n10 14\n10 + 14 = 24\nsure\n11 12\n11 + 12 = 23\n12 - 11 = 1\n11 * 12 = 132\n11 / 12 = 0.91\nimpossible\n4 4 10\n4 + 4 + 10 = 8 + 10 = 18\n4 * 10 - 4 = 40 - 4 = 36\n(10 - 4) * 4 = 6 * 4 = 24\nsure\n4 9 11\n9 + 11 + 4 = 20 + 4 = 24\nsure\n5 7 8\n5 + 7 + 8 = 12 + 8 = 20\n(8 - 5) * 7 = 3 * 7 = 21\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n5 6 6\n5 + 6 + 6 = 17\n(6 - 5) * 6 = 1 * 6 = 6\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n10 10 11\n10 + 10 + 11 = 31\n(11 - 10) * 10 = 10\n10 10 10 are all too big\nimpossible\n1 3 3\n1 * 3 * 3 = 9\n(1 + 3) * 3 = 12\n1 3 3 are all too small\nimpossible\n14 10\n": "I'm sorry, it seems like you didn't provide the complete set of numbers for the last expression. Could you please provide the third number so that I can evaluate if they can reach 24?", + "Evaluate if given numbers can reach 24 (sure/likely/impossible)\n10 14\n10 + 14 = 24\nsure\n11 12\n11 + 12 = 23\n12 - 11 = 1\n11 * 12 = 132\n11 / 12 = 0.91\nimpossible\n4 4 10\n4 + 4 + 10 = 8 + 10 = 18\n4 * 10 - 4 = 40 - 4 = 36\n(10 - 4) * 4 = 6 * 4 = 24\nsure\n4 9 11\n9 + 11 + 4 = 20 + 4 = 24\nsure\n5 7 8\n5 + 7 + 8 = 12 + 8 = 20\n(8 - 5) * 7 = 3 * 7 = 21\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n5 6 6\n5 + 6 + 6 = 17\n(6 - 5) * 6 = 1 * 6 = 6\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n10 10 11\n10 + 10 + 11 = 31\n(11 - 10) * 10 = 10\n10 10 10 are all too big\nimpossible\n1 3 3\n1 * 3 * 3 = 9\n(1 + 3) * 3 = 12\n1 3 3 are all too small\nimpossible\n3.33 10\n": "It seems like you have provided a list of numbers and evaluated if they can reach 24 through addition, subtraction, multiplication, and division. Here are the evaluations:\n\n10 14 - Sure\n11 12 - Impossible\n4 4 10 - Sure\n4 9 11 - Sure\n5 7 8 - Likely\n5 6 6 - Likely\n10 10 11 - Impossible\n1 3 3 - Impossible\n\nIt looks like you have evaluated the combinations accurately. As for the last set of numbers \"3.33 10,\" it seems incomplete. If you provide the intended operation or additional numbers, I can help you evaluate it.", + "Here is an Example for 1 input and 8 possible thoughts:\nInput: 2 8 8 14\nPossible next steps:\n2 + 8 = 10 (left: 8 10 14)\n8 / 2 = 4 (left: 4 8 14)\n14 + 2 = 16 (left: 8 8 16)\n2 * 8 = 16 (left: 8 14 16)\n8 - 2 = 6 (left: 6 8 14)\n14 - 8 = 6 (left: 2 6 8)\n14 / 2 = 7 (left: 7 8 8)\n14 - 2 = 12 (left: 8 8 12)\n\nHere is my task for 1 input and 5 possible thoughts:\nInput: 10 15\nPossible next steps:\n\n\n\n\nEach output should be strictly a list of nodes, in json format, like this:\n```json\n [\n {\n \"node_id\": str = \"unique identifier for a solution, can be an ordinal\",\n \"node_state_instruction\": \"specified sample of solution\",\n },\n ...\n ]\n```\n": "Here's the JSON format for the possible next steps for the given input:\n\n```json\n[\n {\n \"node_id\": \"1\",\n \"node_state_instruction\": \"10 + 15 = 25 (left: 25)\"\n },\n {\n \"node_id\": \"2\",\n \"node_state_instruction\": \"15 - 10 = 5 (left: 5)\"\n },\n {\n \"node_id\": \"3\",\n \"node_state_instruction\": \"10 * 15 = 150 (left: 150)\"\n },\n {\n \"node_id\": \"4\",\n \"node_state_instruction\": \"15 / 10 = 1.5 (left: 1.5)\"\n },\n {\n \"node_id\": \"5\",\n \"node_state_instruction\": \"15 + 10 = 25 (left: 25)\"\n }\n]\n```", + "Here is an Example for 1 input and 8 possible thoughts:\nInput: 2 8 8 14\nPossible next steps:\n2 + 8 = 10 (left: 8 10 14)\n8 / 2 = 4 (left: 4 8 14)\n14 + 2 = 16 (left: 8 8 16)\n2 * 8 = 16 (left: 8 14 16)\n8 - 2 = 6 (left: 6 8 14)\n14 - 8 = 6 (left: 2 6 8)\n14 / 2 = 7 (left: 7 8 8)\n14 - 2 = 12 (left: 8 8 12)\n\nHere is my task for 1 input and 5 possible thoughts:\nInput: 3 9 10\nPossible next steps:\n\n\n\n\nEach output should be strictly a list of nodes, in json format, like this:\n```json\n [\n {\n \"node_id\": str = \"unique identifier for a solution, can be an ordinal\",\n \"node_state_instruction\": \"specified sample of solution\",\n },\n ...\n ]\n```\n": "Here are the possible next steps for the given input in JSON format:\n\nInput: 3 9 10\nPossible next steps:\n```json\n[\n {\n \"node_id\": \"1\",\n \"node_state_instruction\": \"3 + 9 = 12 (left: 10 12)\"\n },\n {\n \"node_id\": \"2\",\n \"node_state_instruction\": \"9 - 3 = 6 (left: 6 9 10)\"\n },\n {\n \"node_id\": \"3\",\n \"node_state_instruction\": \"10 - 3 = 7 (left: 7 9 10)\"\n },\n {\n \"node_id\": \"4\",\n \"node_state_instruction\": \"3 * 9 = 27 (left: 10 27)\"\n },\n {\n \"node_id\": \"5\",\n \"node_state_instruction\": \"9 / 3 = 3 (left: 3 9 10)\"\n }\n]\n```", + "Here is an Example for 1 input and 8 possible thoughts:\nInput: 2 8 8 14\nPossible next steps:\n2 + 8 = 10 (left: 8 10 14)\n8 / 2 = 4 (left: 4 8 14)\n14 + 2 = 16 (left: 8 8 16)\n2 * 8 = 16 (left: 8 14 16)\n8 - 2 = 6 (left: 6 8 14)\n14 - 8 = 6 (left: 2 6 8)\n14 / 2 = 7 (left: 7 8 8)\n14 - 2 = 12 (left: 8 8 12)\n\nHere is my task for 1 input and 5 possible thoughts:\nInput: 4 6 9\nPossible next steps:\n\n\n\n\nEach output should be strictly a list of nodes, in json format, like this:\n```json\n [\n {\n \"node_id\": str = \"unique identifier for a solution, can be an ordinal\",\n \"node_state_instruction\": \"specified sample of solution\",\n },\n ...\n ]\n```\n": "Certainly! Here are the possible next steps for the given input in the requested JSON format:\n\nInput: 4 6 9\nPossible next steps:\n```json\n[\n {\n \"node_id\": \"1\",\n \"node_state_instruction\": \"4 + 6 = 10 (left: 10 9)\"\n },\n {\n \"node_id\": \"2\",\n \"node_state_instruction\": \"4 * 6 = 24 (left: 24 9)\"\n },\n {\n \"node_id\": \"3\",\n \"node_state_instruction\": \"4 - 6 = -2 (left: -2 9)\"\n },\n {\n \"node_id\": \"4\",\n \"node_state_instruction\": \"6 + 9 = 15 (left: 4 15)\"\n },\n {\n \"node_id\": \"5\",\n \"node_state_instruction\": \"6 * 9 = 54 (left: 4 54)\"\n }\n]\n```\nI hope this helps! If you need further assistance, feel free to ask.", + "Evaluate if given numbers can reach 24 (sure/likely/impossible)\n10 14\n10 + 14 = 24\nsure\n11 12\n11 + 12 = 23\n12 - 11 = 1\n11 * 12 = 132\n11 / 12 = 0.91\nimpossible\n4 4 10\n4 + 4 + 10 = 8 + 10 = 18\n4 * 10 - 4 = 40 - 4 = 36\n(10 - 4) * 4 = 6 * 4 = 24\nsure\n4 9 11\n9 + 11 + 4 = 20 + 4 = 24\nsure\n5 7 8\n5 + 7 + 8 = 12 + 8 = 20\n(8 - 5) * 7 = 3 * 7 = 21\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n5 6 6\n5 + 6 + 6 = 17\n(6 - 5) * 6 = 1 * 6 = 6\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n10 10 11\n10 + 10 + 11 = 31\n(11 - 10) * 10 = 10\n10 10 10 are all too big\nimpossible\n1 3 3\n1 * 3 * 3 = 9\n(1 + 3) * 3 = 12\n1 3 3 are all too small\nimpossible\n10 27\n": "I'm sorry, but it seems like you didn't provide the third number for the last set. Could you please provide the third number so I can evaluate if the given numbers can reach 24?", + "Evaluate if given numbers can reach 24 (sure/likely/impossible)\n10 14\n10 + 14 = 24\nsure\n11 12\n11 + 12 = 23\n12 - 11 = 1\n11 * 12 = 132\n11 / 12 = 0.91\nimpossible\n4 4 10\n4 + 4 + 10 = 8 + 10 = 18\n4 * 10 - 4 = 40 - 4 = 36\n(10 - 4) * 4 = 6 * 4 = 24\nsure\n4 9 11\n9 + 11 + 4 = 20 + 4 = 24\nsure\n5 7 8\n5 + 7 + 8 = 12 + 8 = 20\n(8 - 5) * 7 = 3 * 7 = 21\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n5 6 6\n5 + 6 + 6 = 17\n(6 - 5) * 6 = 1 * 6 = 6\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n10 10 11\n10 + 10 + 11 = 31\n(11 - 10) * 10 = 10\n10 10 10 are all too big\nimpossible\n1 3 3\n1 * 3 * 3 = 9\n(1 + 3) * 3 = 12\n1 3 3 are all too small\nimpossible\n1.5\n": "The evaluation of the given numbers is as follows:\n\n10 14: Sure\n11 12: Impossible\n4 4 10: Sure\n4 9 11: Sure\n5 7 8: Likely\n5 6 6: Likely\n10 10 11: Impossible\n1 3 3: Impossible\n1.5: I'm sorry, I cannot evaluate a single number for reaching 24.", + "Evaluate if given numbers can reach 24 (sure/likely/impossible)\n10 14\n10 + 14 = 24\nsure\n11 12\n11 + 12 = 23\n12 - 11 = 1\n11 * 12 = 132\n11 / 12 = 0.91\nimpossible\n4 4 10\n4 + 4 + 10 = 8 + 10 = 18\n4 * 10 - 4 = 40 - 4 = 36\n(10 - 4) * 4 = 6 * 4 = 24\nsure\n4 9 11\n9 + 11 + 4 = 20 + 4 = 24\nsure\n5 7 8\n5 + 7 + 8 = 12 + 8 = 20\n(8 - 5) * 7 = 3 * 7 = 21\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n5 6 6\n5 + 6 + 6 = 17\n(6 - 5) * 6 = 1 * 6 = 6\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n10 10 11\n10 + 10 + 11 = 31\n(11 - 10) * 10 = 10\n10 10 10 are all too big\nimpossible\n1 3 3\n1 * 3 * 3 = 9\n(1 + 3) * 3 = 12\n1 3 3 are all too small\nimpossible\n4 15\n": "It seems like you didn't provide the complete set of numbers for the last question. Could you please provide the third number so that I can evaluate if they can reach 24?", + "Evaluate if given numbers can reach 24 (sure/likely/impossible)\n10 14\n10 + 14 = 24\nsure\n11 12\n11 + 12 = 23\n12 - 11 = 1\n11 * 12 = 132\n11 / 12 = 0.91\nimpossible\n4 4 10\n4 + 4 + 10 = 8 + 10 = 18\n4 * 10 - 4 = 40 - 4 = 36\n(10 - 4) * 4 = 6 * 4 = 24\nsure\n4 9 11\n9 + 11 + 4 = 20 + 4 = 24\nsure\n5 7 8\n5 + 7 + 8 = 12 + 8 = 20\n(8 - 5) * 7 = 3 * 7 = 21\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n5 6 6\n5 + 6 + 6 = 17\n(6 - 5) * 6 = 1 * 6 = 6\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n10 10 11\n10 + 10 + 11 = 31\n(11 - 10) * 10 = 10\n10 10 10 are all too big\nimpossible\n1 3 3\n1 * 3 * 3 = 9\n(1 + 3) * 3 = 12\n1 3 3 are all too small\nimpossible\n24 9\n": "24 + 9 = 33\n24 - 9 = 15\n24 * 9 = 216\n24 / 9 = 2.67\nIt is impossible to reach 24 with the given numbers.", + "Evaluate if given numbers can reach 24 (sure/likely/impossible)\n10 14\n10 + 14 = 24\nsure\n11 12\n11 + 12 = 23\n12 - 11 = 1\n11 * 12 = 132\n11 / 12 = 0.91\nimpossible\n4 4 10\n4 + 4 + 10 = 8 + 10 = 18\n4 * 10 - 4 = 40 - 4 = 36\n(10 - 4) * 4 = 6 * 4 = 24\nsure\n4 9 11\n9 + 11 + 4 = 20 + 4 = 24\nsure\n5 7 8\n5 + 7 + 8 = 12 + 8 = 20\n(8 - 5) * 7 = 3 * 7 = 21\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n5 6 6\n5 + 6 + 6 = 17\n(6 - 5) * 6 = 1 * 6 = 6\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n10 10 11\n10 + 10 + 11 = 31\n(11 - 10) * 10 = 10\n10 10 10 are all too big\nimpossible\n1 3 3\n1 * 3 * 3 = 9\n(1 + 3) * 3 = 12\n1 3 3 are all too small\nimpossible\n10 9\n": "It seems like you might have missed providing the operation for the last set of numbers \"10 9\". If you could provide the operation, I can help evaluate if the given numbers can reach 24.", + "Evaluate if given numbers can reach 24 (sure/likely/impossible)\n10 14\n10 + 14 = 24\nsure\n11 12\n11 + 12 = 23\n12 - 11 = 1\n11 * 12 = 132\n11 / 12 = 0.91\nimpossible\n4 4 10\n4 + 4 + 10 = 8 + 10 = 18\n4 * 10 - 4 = 40 - 4 = 36\n(10 - 4) * 4 = 6 * 4 = 24\nsure\n4 9 11\n9 + 11 + 4 = 20 + 4 = 24\nsure\n5 7 8\n5 + 7 + 8 = 12 + 8 = 20\n(8 - 5) * 7 = 3 * 7 = 21\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n5 6 6\n5 + 6 + 6 = 17\n(6 - 5) * 6 = 1 * 6 = 6\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n10 10 11\n10 + 10 + 11 = 31\n(11 - 10) * 10 = 10\n10 10 10 are all too big\nimpossible\n1 3 3\n1 * 3 * 3 = 9\n(1 + 3) * 3 = 12\n1 3 3 are all too small\nimpossible\n25\n": "It seems that you have provided a list of numbers and evaluated whether they can reach 24 through addition, subtraction, multiplication, and division. Based on your evaluations, the combinations of numbers that can reach 24 are \"10 14\" and \"4 4 10,\" which are sure to reach 24. The combinations \"11 12\" and \"4 9 11\" are also sure to reach 24. The combination \"5 7 8\" is likely to reach 24, and the combinations \"5 6 6\" and \"10 10 11\" are unlikely to reach 24. The combination \"1 3 3\" is impossible to reach 24. If you have any more numbers to evaluate, feel free to share them with me.", + "Evaluate if given numbers can reach 24 (sure/likely/impossible)\n10 14\n10 + 14 = 24\nsure\n11 12\n11 + 12 = 23\n12 - 11 = 1\n11 * 12 = 132\n11 / 12 = 0.91\nimpossible\n4 4 10\n4 + 4 + 10 = 8 + 10 = 18\n4 * 10 - 4 = 40 - 4 = 36\n(10 - 4) * 4 = 6 * 4 = 24\nsure\n4 9 11\n9 + 11 + 4 = 20 + 4 = 24\nsure\n5 7 8\n5 + 7 + 8 = 12 + 8 = 20\n(8 - 5) * 7 = 3 * 7 = 21\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n5 6 6\n5 + 6 + 6 = 17\n(6 - 5) * 6 = 1 * 6 = 6\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n10 10 11\n10 + 10 + 11 = 31\n(11 - 10) * 10 = 10\n10 10 10 are all too big\nimpossible\n1 3 3\n1 * 3 * 3 = 9\n(1 + 3) * 3 = 12\n1 3 3 are all too small\nimpossible\n4 54\n": "I'm sorry, but I cannot evaluate the expression \"4 54\" without an operator. Could you please provide the operator for the expression?", + "Evaluate if given numbers can reach 24 (sure/likely/impossible)\n10 14\n10 + 14 = 24\nsure\n11 12\n11 + 12 = 23\n12 - 11 = 1\n11 * 12 = 132\n11 / 12 = 0.91\nimpossible\n4 4 10\n4 + 4 + 10 = 8 + 10 = 18\n4 * 10 - 4 = 40 - 4 = 36\n(10 - 4) * 4 = 6 * 4 = 24\nsure\n4 9 11\n9 + 11 + 4 = 20 + 4 = 24\nsure\n5 7 8\n5 + 7 + 8 = 12 + 8 = 20\n(8 - 5) * 7 = 3 * 7 = 21\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n5 6 6\n5 + 6 + 6 = 17\n(6 - 5) * 6 = 1 * 6 = 6\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n10 10 11\n10 + 10 + 11 = 31\n(11 - 10) * 10 = 10\n10 10 10 are all too big\nimpossible\n1 3 3\n1 * 3 * 3 = 9\n(1 + 3) * 3 = 12\n1 3 3 are all too small\nimpossible\n-2 9\n": "I'm sorry, but it seems like you didn't provide the third number for the last expression. Could you please provide the third number so that I can evaluate if the given numbers can reach 24?", + "Evaluate if given numbers can reach 24 (sure/likely/impossible)\n10 14\n10 + 14 = 24\nsure\n11 12\n11 + 12 = 23\n12 - 11 = 1\n11 * 12 = 132\n11 / 12 = 0.91\nimpossible\n4 4 10\n4 + 4 + 10 = 8 + 10 = 18\n4 * 10 - 4 = 40 - 4 = 36\n(10 - 4) * 4 = 6 * 4 = 24\nsure\n4 9 11\n9 + 11 + 4 = 20 + 4 = 24\nsure\n5 7 8\n5 + 7 + 8 = 12 + 8 = 20\n(8 - 5) * 7 = 3 * 7 = 21\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n5 6 6\n5 + 6 + 6 = 17\n(6 - 5) * 6 = 1 * 6 = 6\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n10 10 11\n10 + 10 + 11 = 31\n(11 - 10) * 10 = 10\n10 10 10 are all too big\nimpossible\n1 3 3\n1 * 3 * 3 = 9\n(1 + 3) * 3 = 12\n1 3 3 are all too small\nimpossible\n10 12\n": "It seems like you have evaluated the combinations correctly. Based on the evaluations you provided, the combinations that can reach 24 are:\n\n- 10 14 (sure)\n- 4 4 10 (sure)\n- 4 9 11 (sure)\n- 5 7 8 (likely)\n- 5 6 6 (likely)\n\nThe combinations that cannot reach 24 are:\n\n- 11 12 (impossible)\n- 10 10 11 (impossible)\n- 1 3 3 (impossible)\n\nAs for the combination \"10 12,\" it seems like it was cut off. If you provide the complete expression, I can help evaluate it for you.", + "Evaluate if given numbers can reach 24 (sure/likely/impossible)\n10 14\n10 + 14 = 24\nsure\n11 12\n11 + 12 = 23\n12 - 11 = 1\n11 * 12 = 132\n11 / 12 = 0.91\nimpossible\n4 4 10\n4 + 4 + 10 = 8 + 10 = 18\n4 * 10 - 4 = 40 - 4 = 36\n(10 - 4) * 4 = 6 * 4 = 24\nsure\n4 9 11\n9 + 11 + 4 = 20 + 4 = 24\nsure\n5 7 8\n5 + 7 + 8 = 12 + 8 = 20\n(8 - 5) * 7 = 3 * 7 = 21\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n5 6 6\n5 + 6 + 6 = 17\n(6 - 5) * 6 = 1 * 6 = 6\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n10 10 11\n10 + 10 + 11 = 31\n(11 - 10) * 10 = 10\n10 10 10 are all too big\nimpossible\n1 3 3\n1 * 3 * 3 = 9\n(1 + 3) * 3 = 12\n1 3 3 are all too small\nimpossible\n150\n": "It seems like you have evaluated the given numbers correctly. Here are the results:\n\n10 14: Sure\n11 12: Impossible\n4 4 10: Sure\n4 9 11: Sure\n5 7 8: Likely\n5 6 6: Likely\n10 10 11: Impossible\n1 3 3: Impossible\n\nAs for the number 150, it is not clear what operation or combination of numbers it is associated with. If you have a specific question or context for the number 150, please provide more details so that I can assist you further.", + "Evaluate if given numbers can reach 24 (sure/likely/impossible)\n10 14\n10 + 14 = 24\nsure\n11 12\n11 + 12 = 23\n12 - 11 = 1\n11 * 12 = 132\n11 / 12 = 0.91\nimpossible\n4 4 10\n4 + 4 + 10 = 8 + 10 = 18\n4 * 10 - 4 = 40 - 4 = 36\n(10 - 4) * 4 = 6 * 4 = 24\nsure\n4 9 11\n9 + 11 + 4 = 20 + 4 = 24\nsure\n5 7 8\n5 + 7 + 8 = 12 + 8 = 20\n(8 - 5) * 7 = 3 * 7 = 21\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n5 6 6\n5 + 6 + 6 = 17\n(6 - 5) * 6 = 1 * 6 = 6\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n10 10 11\n10 + 10 + 11 = 31\n(11 - 10) * 10 = 10\n10 10 10 are all too big\nimpossible\n1 3 3\n1 * 3 * 3 = 9\n(1 + 3) * 3 = 12\n1 3 3 are all too small\nimpossible\n5\n": "The numbers 10 and 14 can reach 24, so that is sure. The numbers 11 and 12 cannot reach 24, so that is impossible. The numbers 4, 4, and 10 can reach 24, so that is sure. The numbers 4, 9, and 11 can reach 24, so that is sure. The numbers 5, 7, and 8 cannot reach 24, but they are within a reasonable range, so that is likely. The numbers 5, 6, and 6 cannot reach 24, but they are within a reasonable range, so that is likely. The numbers 10, 10, and 11 cannot reach 24, so that is impossible. The numbers 1, 3, and 3 cannot reach 24, so that is impossible. The number 5 by itself cannot reach 24.", + "Evaluate if given numbers can reach 24 (sure/likely/impossible)\n10 14\n10 + 14 = 24\nsure\n11 12\n11 + 12 = 23\n12 - 11 = 1\n11 * 12 = 132\n11 / 12 = 0.91\nimpossible\n4 4 10\n4 + 4 + 10 = 8 + 10 = 18\n4 * 10 - 4 = 40 - 4 = 36\n(10 - 4) * 4 = 6 * 4 = 24\nsure\n4 9 11\n9 + 11 + 4 = 20 + 4 = 24\nsure\n5 7 8\n5 + 7 + 8 = 12 + 8 = 20\n(8 - 5) * 7 = 3 * 7 = 21\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n5 6 6\n5 + 6 + 6 = 17\n(6 - 5) * 6 = 1 * 6 = 6\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely\n10 10 11\n10 + 10 + 11 = 31\n(11 - 10) * 10 = 10\n10 10 10 are all too big\nimpossible\n1 3 3\n1 * 3 * 3 = 9\n(1 + 3) * 3 = 12\n1 3 3 are all too small\nimpossible\n7 9 10\n": "7 * 9 - 10 = 63 - 10 = 53\n(9 - 7) * 10 = 2 * 10 = 20\nI cannot obtain 24 now, but numbers are within a reasonable range\nlikely", + "### Requirements\n1. Add docstrings to the given code following the google style.\n2. Replace the function body with an Ellipsis object(...) to reduce output.\n3. If the types are already annotated, there is no need to include them in the docstring.\n4. Extract only class, function or the docstrings for the module parts from the given Python code, avoiding any other text.\n\n### Input Example\n```python\ndef function_with_pep484_type_annotations(param1: int) -> bool:\n return isinstance(param1, int)\n\nclass ExampleError(Exception):\n def __init__(self, msg: str):\n self.msg = msg\n```\n\n### Output Example\n```python\ndef function_with_pep484_type_annotations(param1: int) -> bool:\n \"\"\"Example function with PEP 484 type annotations.\n\n Extended description of function.\n\n Args:\n param1: The first parameter.\n\n Returns:\n The return value. True for success, False otherwise.\n \"\"\"\n ...\n\nclass ExampleError(Exception):\n \"\"\"Exceptions are documented in the same way as classes.\n\n The __init__ method was documented in the class level docstring.\n\n Args:\n msg: Human readable string describing the exception.\n\n Attributes:\n msg: Human readable string describing the exception.\n \"\"\"\n ...\n```\n#SYSTEM_MSG_END#```python\nimport pytest\nfrom metagpt.actions.write_docstring import WriteDocstring\ncode = '\\ndef add_numbers(a: int, b: int):\\n return a + b\\n\\n\\nclass Person:\\n def __init__(self, name: str, age: int):\\n self.name = name\\n self.age = age\\n\\n def greet(self):\\n return f\"Hello, my name is {self.name} and I am {self.age} years old.\"\\n'\n\n@pytest.mark.asyncio\n@pytest.mark.parametrize(('style', 'part'), [('google', 'Args:'), ('numpy', 'Parameters'), ('sphinx', ':param name:')], ids=['google', 'numpy', 'sphinx'])\nasync def test_write_docstring(style: str, part: str, context):\n ret = await WriteDocstring(context=context).run(code, style=style)\n assert part in ret\n\n@pytest.mark.asyncio\nasync def test_write():\n code = await WriteDocstring.write_docstring(__file__)\n assert code\n```": "```python\ndef add_numbers(a: int, b: int):\n \"\"\"Adds two numbers together.\n\n Args:\n a: The first number.\n b: The second number.\n\n Returns:\n The sum of a and b.\n \"\"\"\n ...\n\nclass Person:\n \"\"\"Represents a person.\n\n Args:\n name: The name of the person.\n age: The age of the person.\n\n Attributes:\n name: The name of the person.\n age: The age of the person.\n \"\"\"\n\n def __init__(self, name: str, age: int):\n ...\n \n def greet(self):\n \"\"\"Generates a greeting message.\n\n Returns:\n A greeting string.\n \"\"\"\n ...\n```", + "\nWrite a coherent passage of 4 short paragraphs. The end sentence of each paragraph must be: It isn't difficult to do a handstand if you just stand on your hands. It caught him off guard that space smelled of seared steak. When she didn’t like a guy who was trying to pick her up, she started using sign language. Each person who knows you has a different perception of who you are.\n\nMake a plan then write. Your output should be like:\n\nPlan:\n\n\nPassage:\n\n\n\nEach output should be strictly a list of nodes, in json format, like this:\n```json\n [\n {\n \"node_id\": str = \"unique identifier for a solution, can be an ordinal\",\n \"node_state_instruction\": \"specified sample of solution\",\n },\n ...\n ]\n```\n": "Plan:\n```json\n[\n {\n \"node_id\": \"1\",\n \"node_state_instruction\": \"Introduce the concept of doing a handstand.\"\n },\n {\n \"node_id\": \"2\",\n \"node_state_instruction\": \"Describe the surprising smell of space.\"\n },\n {\n \"node_id\": \"3\",\n \"node_state_instruction\": \"Introduce a woman using sign language to deter unwanted attention.\"\n },\n {\n \"node_id\": \"4\",\n \"node_state_instruction\": \"Discuss the varied perceptions people have of an individual.\"\n }\n]\n```\n\nPassage:\n```json\n[\n {\n \"node_id\": \"1\",\n \"node_state_instruction\": \"It isn't difficult to do a handstand if you just stand on your hands.\"\n },\n {\n \"node_id\": \"2\",\n \"node_state_instruction\": \"It caught him off guard that space smelled of seared steak.\"\n },\n {\n \"node_id\": \"3\",\n \"node_state_instruction\": \"When she didn’t like a guy who was trying to pick her up, she started using sign language.\"\n },\n {\n \"node_id\": \"4\",\n \"node_state_instruction\": \"Each person who knows you has a different perception of who you are.\"\n }\n]\n```", + "Given an instruction and several choices, decide which choice is most promising. Analyze each choice in detail, then conclude in the last line \"The best choice is {s}\", where s the integer id of the choice.\nChoice 4:\nDiscuss the varied perceptions people have of an individual.\n": "Choice 4: Discuss the varied perceptions people have of an individual.\n\nAnalyzing this choice, discussing the varied perceptions people have of an individual can be promising as it allows for a deeper understanding of how different people perceive and interpret the actions, behaviors, and characteristics of an individual. This can lead to insights into the complexities of human interactions and relationships, and can provide valuable information for personal growth and self-awareness.\n\nBy exploring the reasons behind differing perceptions, it can also lead to increased empathy and understanding of others, which is beneficial for building stronger relationships and effective communication.\n\nFurthermore, discussing varied perceptions can also shed light on societal influences, biases, and stereotypes that may impact how individuals are perceived, which can be valuable for promoting inclusivity and diversity.\n\nThe best choice is 4.", + "Given an instruction and several choices, decide which choice is most promising. Analyze each choice in detail, then conclude in the last line \"The best choice is {s}\", where s the integer id of the choice.\nChoice 3:\nIntroduce a woman using sign language to deter unwanted attention.\n": "Choice 3: Introduce a woman using sign language to deter unwanted attention.\n\nAnalysis:\nIntroducing a woman using sign language can be a promising choice to deter unwanted attention. Sign language is a visual and expressive form of communication, and it can be an effective way to convey a message without drawing unnecessary attention. By using sign language, the woman can communicate with others discreetly and avoid attracting unwanted attention from individuals who may be seeking to engage her in conversation or interaction.\n\nAdditionally, using sign language can create a sense of privacy and exclusivity, as it may not be easily understood by everyone in the vicinity. This can help the woman maintain a level of personal space and discourage unwelcome advances or interactions.\n\nFurthermore, introducing sign language into the interaction can also serve as a way to promote inclusivity and awareness of different forms of communication, which can contribute to a more understanding and respectful environment.\n\nConclusion:\nThe best choice is 3.", + "Given an instruction and several choices, decide which choice is most promising. Analyze each choice in detail, then conclude in the last line \"The best choice is {s}\", where s the integer id of the choice.\nChoice 1:\nIntroduce the concept of doing a handstand.\n": "Choice 1: Introduce the concept of doing a handstand.\n\nAnalyzing the choice:\nIntroducing the concept of doing a handstand can be a fun and engaging way to promote physical activity and coordination. It can also help build strength and balance. However, it's important to ensure that proper safety measures and instructions are provided to prevent any potential injuries.\n\nConclusion:\nThe best choice is 1.", + "Given an instruction and several choices, decide which choice is most promising. Analyze each choice in detail, then conclude in the last line \"The best choice is {s}\", where s the integer id of the choice.\nChoice 2:\nDescribe the surprising smell of space.\n": "Choice 2: \n\nThe surprising smell of space has been described by astronauts as a mix of gunpowder, seared steak, hot metal, and raspberries. This unexpected combination of scents is due to the volatile organic compounds that are released from the materials in the spacecraft when they are exposed to the vacuum of space. These compounds react with the radiation from the sun, creating the unique smell that astronauts have reported. The smell of space is intriguing and unexpected, providing a sensory experience that is truly out of this world.\n\nThe best choice is 2.", + "\nWrite a coherent passage of 4 short paragraphs. The end sentence of each paragraph must be: Introduce the concept of doing a handstand.\n\nMake a plan then write. Your output should be like:\n\nPlan:\n\n\nPassage:\n\n\n\nEach output should be strictly a list of nodes, in json format, like this:\n```json\n [\n {\n \"node_id\": str = \"unique identifier for a solution, can be an ordinal\",\n \"node_state_instruction\": \"specified sample of solution\",\n },\n ...\n ]\n```\n": "Plan:\n```json\n[\n {\n \"node_id\": \"1\",\n \"node_state_instruction\": \"Decide on a suitable location to practice handstands.\"\n },\n {\n \"node_id\": \"2\",\n \"node_state_instruction\": \"Warm up your body with some stretching exercises.\"\n },\n {\n \"node_id\": \"3\",\n \"node_state_instruction\": \"Practice against a wall to build strength and balance.\"\n },\n {\n \"node_id\": \"4\",\n \"node_state_instruction\": \"Gradually work on holding the handstand position without support.\"\n }\n]\n```\n\nPassage:\n```json\n[\n {\n \"node_id\": \"1\",\n \"node_state_instruction\": \"Decide on a suitable location to practice handstands.\"\n },\n {\n \"node_id\": \"2\",\n \"node_state_instruction\": \"Before attempting a handstand, it's important to decide on a suitable location to practice. A clear, open space with a soft surface, such as a yoga mat or grass, is ideal for safety and comfort.\"\n },\n {\n \"node_id\": \"3\",\n \"node_state_instruction\": \"Warm up your body with some stretching exercises.\"\n },\n {\n \"node_id\": \"4\",\n \"node_state_instruction\": \"To prepare your body for the physical demands of a handstand, start with a series of stretching exercises. Focus on the wrists, shoulders, and core muscles to ensure they are adequately warmed up and flexible.\"\n },\n {\n \"node_id\": \"5\",\n \"node_state_instruction\": \"Practice against a wall to build strength and balance.\"\n },\n {\n \"node_id\": \"6\",\n \"node_state_instruction\": \"When learning to do a handstand, it can be helpful to practice against a wall. This provides support and allows you to focus on building strength and balance in the proper alignment. Gradually work on kicking up into the handstand position and holding it for short periods of time.\"\n },\n {\n \"node_id\": \"7\",\n \"node_state_instruction\": \"Gradually work on holding the handstand position without support.\"\n },\n {\n \"node_id\": \"8\",\n \"node_state_instruction\": \"As you gain confidence and strength, begin to practice holding the handstand position without the support of a wall. Focus on engaging your core and maintaining a straight line from your wrists to your hips. With consistent practice, you'll be able to hold a handstand with control and stability.\"\n }\n]\n```", + "Given an instruction and several choices, decide which choice is most promising. Analyze each choice in detail, then conclude in the last line \"The best choice is {s}\", where s the integer id of the choice.\nChoice 1:\nDecide on a suitable location to practice handstands.\n": "Choice 1: Find a spacious and flat area with soft ground, such as a grassy field or a padded gym floor. This will minimize the risk of injury and provide a stable surface for practicing handstands.\n\nThe best choice is 1", + "Given an instruction and several choices, decide which choice is most promising. Analyze each choice in detail, then conclude in the last line \"The best choice is {s}\", where s the integer id of the choice.\nChoice 2:\nWarm up your body with some stretching exercises.\n": "Stretching exercises are a great way to warm up the body before engaging in physical activity. They help to increase blood flow to the muscles, improve flexibility, and reduce the risk of injury. By performing stretching exercises, you can prepare your body for more strenuous activities and improve your overall performance.\n\nThe best choice is 2.", + "Given an instruction and several choices, decide which choice is most promising. Analyze each choice in detail, then conclude in the last line \"The best choice is {s}\", where s the integer id of the choice.\nChoice 3:\nPractice against a wall to build strength and balance.\n": "Choice 3: Practice against a wall to build strength and balance.\n\nAnalyzing choice 3, practicing against a wall can be a promising option for building strength and balance. The wall provides a stable surface to push against, which can help in developing strength in the muscles. It also offers support, allowing for balance practice without the risk of falling. Additionally, using a wall as a tool for practice can help in improving posture and body alignment.\n\nThe best choice is 3.", + "Given an instruction and several choices, decide which choice is most promising. Analyze each choice in detail, then conclude in the last line \"The best choice is {s}\", where s the integer id of the choice.\nChoice 4:\nGradually work on holding the handstand position without support.\n": "Choice 4: Gradually work on holding the handstand position without support.\n\nAnalyzing this choice, working on holding the handstand position without support is a promising option for improving strength, balance, and body control. This choice allows for gradual progression, which is important for building the necessary strength and stability to maintain the handstand position. It also encourages the development of proprioception and spatial awareness, which are essential for mastering this challenging skill.\n\nThe best choice is 4.", + "\nWrite a coherent passage of 4 short paragraphs. The end sentence of each paragraph must be: Decide on a suitable location to practice handstands.\n\nMake a plan then write. Your output should be like:\n\nPlan:\n\n\nPassage:\n\n\n\nEach output should be strictly a list of nodes, in json format, like this:\n```json\n [\n {\n \"node_id\": str = \"unique identifier for a solution, can be an ordinal\",\n \"node_state_instruction\": \"specified sample of solution\",\n },\n ...\n ]\n```\n": "Plan:\n```json\n[\n {\n \"node_id\": \"1\",\n \"node_state_instruction\": \"Decide on a suitable location to practice handstands.\"\n },\n {\n \"node_id\": \"2\",\n \"node_state_instruction\": \"Consider a flat and stable surface, such as a grassy field or a gym mat.\"\n },\n {\n \"node_id\": \"3\",\n \"node_state_instruction\": \"Ensure there is enough space around you to avoid any obstacles.\"\n },\n {\n \"node_id\": \"4\",\n \"node_state_instruction\": \"Practice near a wall or with a spotter for safety.\"\n }\n]\n```\n\nPassage:\n```json\n[\n {\n \"node_id\": \"1\",\n \"node_state_instruction\": \"Decide on a suitable location to practice handstands.\"\n },\n {\n \"node_id\": \"2\",\n \"node_state_instruction\": \"Consider a flat and stable surface, such as a grassy field or a gym mat.\"\n },\n {\n \"node_id\": \"3\",\n \"node_state_instruction\": \"Ensure there is enough space around you to avoid any obstacles.\"\n },\n {\n \"node_id\": \"4\",\n \"node_state_instruction\": \"Practice near a wall or with a spotter for safety.\"\n }\n]\n```", + "Given an instruction and several choices, decide which choice is most promising. Analyze each choice in detail, then conclude in the last line \"The best choice is {s}\", where s the integer id of the choice.\nChoice 3:\nEnsure there is enough space around you to avoid any obstacles.\n": "Choice 3: \n\nAnalyzing this choice, ensuring there is enough space around you to avoid any obstacles is a crucial step in maintaining safety and preventing accidents. By creating a buffer zone, you can react to unexpected obstacles or changes in the environment without risking collisions or other hazards.\n\nThe best choice is 3.", + "Given an instruction and several choices, decide which choice is most promising. Analyze each choice in detail, then conclude in the last line \"The best choice is {s}\", where s the integer id of the choice.\nChoice 2:\nConsider a flat and stable surface, such as a grassy field or a gym mat.\n": "Choice 2: Considering a flat and stable surface, such as a grassy field or a gym mat, is the most promising option. This choice provides a safe and secure environment for the activity, reducing the risk of injury. A grassy field offers a natural and soft surface, while a gym mat provides cushioning and stability. Both options allow for comfortable movement and can accommodate various physical activities.\n\nThe best choice is 2.", + "Given an instruction and several choices, decide which choice is most promising. Analyze each choice in detail, then conclude in the last line \"The best choice is {s}\", where s the integer id of the choice.\nChoice 4:\nPractice near a wall or with a spotter for safety.\n": "Choice 4: \n\nPractice near a wall or with a spotter for safety.\n\nAnalyzing this choice, practicing near a wall or with a spotter provides a safety measure to prevent accidents or injuries. When practicing a new skill or exercise, having a wall nearby can provide support and stability, reducing the risk of falling or losing balance. Similarly, having a spotter can offer assistance and guidance, ensuring that the practice is done safely and effectively.\n\nThe best choice is 4.", + "### Requirements\n1. Please summarize the latest dialogue based on the reference information (secondary) and dialogue history (primary). Do not include text that is irrelevant to the conversation.\n- The context is for reference only. If it is irrelevant to the user's search request history, please reduce its reference and usage.\n2. If there are citable links in the context, annotate them in the main text in the format [main text](citation link). If there are none in the context, do not write links.\n3. The reply should be graceful, clear, non-repetitive, smoothly written, and of moderate length, in {LANG}.\n\n### Dialogue History (For example)\nA: MLOps competitors\n\n### Current Question (For example)\nA: MLOps competitors\n\n### Current Reply (For example)\n1. Alteryx Designer: etc. if any\n2. Matlab: ditto\n3. IBM SPSS Statistics\n4. RapidMiner Studio\n5. DataRobot AI Platform\n6. Databricks Lakehouse Platform\n7. Amazon SageMaker\n8. Dataiku\n#SYSTEM_MSG_END#\n### Reference Information\nABC cleanser is preferred by many with oily skin.\nL'Oreal is a popular brand with many positive reviews.\n\n### Dialogue History\n\nuser: Which facial cleanser is good for oily skin?\n\n### Current Question\nuser: Which facial cleanser is good for oily skin?\n\n### Current Reply: Based on the information, please write the reply to the Question\n\n\n": "Based on the information provided, ABC cleanser is preferred by many with oily skin. It is a popular choice for individuals with oily skin due to its effectiveness. Additionally, L'Oreal is a well-known brand with many positive reviews, and they offer a range of products suitable for oily skin. Both of these options could be good choices for individuals with oily skin.", + "\n## context\n{\"Implementation approach\":\"We will use the Pygame library to create the game and handle user input. The game logic will be implemented using Python classes and functions.\",\"File list\":[\"main.py\",\"game.py\"],\"Data structures and interfaces\":\"\\nclassDiagram\\n class Game {\\n -snake: list\\n -food: tuple\\n -score: int\\n +__init__()\\n +update()\\n +draw()\\n +handle_input()\\n +restart()\\n }\\n class Snake {\\n -body: list\\n -direction: str\\n +__init__(start_pos: tuple, length: int, direction: str)\\n +move()\\n +change_direction(new_direction: str)\\n +grow()\\n }\\n Game --> Snake\\n\",\"Program call flow\":\"\\nsequenceDiagram\\n participant M as Main\\n participant G as Game\\n M->>G: initialize game\\n G->>G: update game state\\n G->>G: draw game\\n G->>G: handle user input\\n G->>G: restart game\\n\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Required Python packages\": [\n \"flask==1.1.2\",\n \"bcrypt==3.2.0\"\n ],\n \"Required Other language third-party packages\": [\n \"No third-party dependencies required\"\n ],\n \"Logic Analysis\": [\n [\n \"game.py\",\n \"Contains Game class and ... functions\"\n ],\n [\n \"main.py\",\n \"Contains main function, from game import Game\"\n ]\n ],\n \"Task list\": [\n \"game.py\",\n \"main.py\"\n ],\n \"Full API spec\": \"openapi: 3.0.0 ...\",\n \"Shared Knowledge\": \"`game.py` contains functions shared across the project.\",\n \"Anything UNCLEAR\": \"Clarification needed on how to start and initialize third-party libraries.\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Required Python packages: typing.List[str] # Provide required Python packages in requirements.txt format.\n- Required Other language third-party packages: typing.List[str] # List down the required packages for languages other than Python.\n- Logic Analysis: typing.List[typing.List[str]] # Provide a list of files with the classes/methods/functions to be implemented, including dependency analysis and imports.\n- Task list: typing.List[str] # Break down the tasks into a list of filenames, prioritized by dependency order.\n- Full API spec: # Describe all APIs using OpenAPI 3.0 spec that may be used by both frontend and backend. If front-end and back-end communication is not required, leave it blank.\n- Shared Knowledge: # Detail any shared knowledge, like common utility functions or configuration variables.\n- Anything UNCLEAR: # Mention any unclear aspects in the project management context and try to clarify them.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Required Python packages\": [\n \"pygame==2.0.1\"\n ],\n \"Required Other language third-party packages\": [\n \"No third-party dependencies required\"\n ],\n \"Logic Analysis\": [\n [\n \"game.py\",\n \"Contains Game class and ... functions\"\n ],\n [\n \"main.py\",\n \"Contains main function, from game import Game\"\n ]\n ],\n \"Task list\": [\n \"game.py\",\n \"main.py\"\n ],\n \"Full API spec\": \"\",\n \"Shared Knowledge\": \"`game.py` contains functions shared across the project.\",\n \"Anything UNCLEAR\": \"Clarification needed on the scoring system and how the game over condition will be handled.\"\n}\n[/CONTENT]", + "\nNOTICE\nRole: You are a professional engineer; the main goal is to write google-style, elegant, modular, easy to read and maintain code\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## Design\n{\"Implementation approach\":\"We will use the Pygame library to create the game and handle user input. The game logic will be implemented using Python classes and functions.\",\"File list\":[\"main.py\",\"game.py\"],\"Data structures and interfaces\":\"\\nclassDiagram\\n class Game {\\n -snake: list\\n -food: tuple\\n -score: int\\n +__init__()\\n +update()\\n +draw()\\n +handle_input()\\n +restart()\\n }\\n class Snake {\\n -body: list\\n -direction: str\\n +__init__(start_pos: tuple, length: int, direction: str)\\n +move()\\n +change_direction(new_direction: str)\\n +grow()\\n }\\n Game --> Snake\\n\",\"Program call flow\":\"\\nsequenceDiagram\\n participant M as Main\\n participant G as Game\\n M->>G: initialize game\\n G->>G: update game state\\n G->>G: draw game\\n G->>G: handle user input\\n G->>G: restart game\\n\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Task\n{\"Required Python packages\":[\"pygame==2.0.1\"],\"Required Other language third-party packages\":[\"No third-party dependencies required\"],\"Logic Analysis\":[[\"game.py\",\"Contains Game class and ... functions\"],[\"main.py\",\"Contains main function, from game import Game\"]],\"Task list\":[\"game.py\",\"main.py\"],\"Full API spec\":\"\",\"Shared Knowledge\":\"`game.py` contains functions shared across the project.\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Legacy Code\n```Code\n\n```\n\n## Debug logs\n```text\n\n\n\n```\n\n## Bug Feedback logs\n```text\n\n```\n\n# Format example\n## Code: game.py\n```python\n## game.py\n...\n```\n\n# Instruction: Based on the context, follow \"Format example\", write code.\n\n## Code: game.py. Write code with triple quoto, based on the following attentions and context.\n1. Only One file: do your best to implement THIS ONLY ONE FILE.\n2. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets.\n3. Set default value: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. AVOID circular import.\n4. Follow design: YOU MUST FOLLOW \"Data structures and interfaces\". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design.\n5. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE.\n6. Before using a external variable/module, make sure you import it first.\n7. Write out EVERY CODE DETAIL, DON'T LEAVE TODO.\n\n": "```python\n## game.py\n\nimport pygame\nimport random\n\nclass Snake:\n def __init__(self, start_pos: tuple, length: int, direction: str):\n self.body = [start_pos]\n self.direction = direction\n self.length = length\n\n def move(self):\n head = self.body[0]\n x, y = head\n if self.direction == 'UP':\n new_head = (x, y - 10)\n elif self.direction == 'DOWN':\n new_head = (x, y + 10)\n elif self.direction == 'LEFT':\n new_head = (x - 10, y)\n elif self.direction == 'RIGHT':\n new_head = (x + 10, y)\n self.body = [new_head] + self.body[:-1]\n\n def change_direction(self, new_direction: str):\n if new_direction in ['UP', 'DOWN', 'LEFT', 'RIGHT']:\n self.direction = new_direction\n\n def grow(self):\n self.length += 1\n self.body.append((0, 0)) # Set default value\n\nclass Game:\n def __init__(self):\n self.snake = Snake((100, 50), 1, 'RIGHT') # Set default value\n self.food = (200, 200) # Set default value\n self.score = 0 # Set default value\n\n def update(self):\n self.snake.move()\n if self.snake.body[0] == self.food:\n self.snake.grow()\n self.food = (random.randrange(0, 400, 10), random.randrange(0, 400, 10)) # Set default value\n\n def draw(self):\n pass # Set default value\n\n def handle_input(self):\n for event in pygame.event.get():\n if event.type == pygame.QUIT:\n pygame.quit()\n quit()\n if event.type == pygame.KEYDOWN:\n if event.key == pygame.K_UP:\n self.snake.change_direction('UP')\n elif event.key == pygame.K_DOWN:\n self.snake.change_direction('DOWN')\n elif event.key == pygame.K_LEFT:\n self.snake.change_direction('LEFT')\n elif event.key == pygame.K_RIGHT:\n self.snake.change_direction('RIGHT')\n\n def restart(self):\n self.snake = Snake((100, 50), 1, 'RIGHT') # Set default value\n self.food = (200, 200) # Set default value\n self.score = 0 # Set default value\n```", + "\n# System\nRole: You are a professional software engineer, and your main task is to review and revise the code. You need to ensure that the code conforms to the google-style standards, is elegantly designed and modularized, easy to read and maintain.\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## System Design\n{\"Implementation approach\":\"We will use the Pygame library to create the game and handle user input. The game logic will be implemented using Python classes and functions.\",\"File list\":[\"main.py\",\"game.py\"],\"Data structures and interfaces\":\"\\nclassDiagram\\n class Game {\\n -snake: list\\n -food: tuple\\n -score: int\\n +__init__()\\n +update()\\n +draw()\\n +handle_input()\\n +restart()\\n }\\n class Snake {\\n -body: list\\n -direction: str\\n +__init__(start_pos: tuple, length: int, direction: str)\\n +move()\\n +change_direction(new_direction: str)\\n +grow()\\n }\\n Game --> Snake\\n\",\"Program call flow\":\"\\nsequenceDiagram\\n participant M as Main\\n participant G as Game\\n M->>G: initialize game\\n G->>G: update game state\\n G->>G: draw game\\n G->>G: handle user input\\n G->>G: restart game\\n\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Task\n{\"Required Python packages\":[\"pygame==2.0.1\"],\"Required Other language third-party packages\":[\"No third-party dependencies required\"],\"Logic Analysis\":[[\"game.py\",\"Contains Game class and ... functions\"],[\"main.py\",\"Contains main function, from game import Game\"]],\"Task list\":[\"game.py\",\"main.py\"],\"Full API spec\":\"\",\"Shared Knowledge\":\"`game.py` contains functions shared across the project.\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Code Files\n\n\n\n## Code to be Reviewed: game.py\n```Code\n## game.py\n\nimport pygame\nimport random\n\nclass Snake:\n def __init__(self, start_pos: tuple, length: int, direction: str):\n self.body = [start_pos]\n self.direction = direction\n self.length = length\n\n def move(self):\n head = self.body[0]\n x, y = head\n if self.direction == 'UP':\n new_head = (x, y - 10)\n elif self.direction == 'DOWN':\n new_head = (x, y + 10)\n elif self.direction == 'LEFT':\n new_head = (x - 10, y)\n elif self.direction == 'RIGHT':\n new_head = (x + 10, y)\n self.body = [new_head] + self.body[:-1]\n\n def change_direction(self, new_direction: str):\n if new_direction in ['UP', 'DOWN', 'LEFT', 'RIGHT']:\n self.direction = new_direction\n\n def grow(self):\n self.length += 1\n self.body.append((0, 0)) # Set default value\n\nclass Game:\n def __init__(self):\n self.snake = Snake((100, 50), 1, 'RIGHT') # Set default value\n self.food = (200, 200) # Set default value\n self.score = 0 # Set default value\n\n def update(self):\n self.snake.move()\n if self.snake.body[0] == self.food:\n self.snake.grow()\n self.food = (random.randrange(0, 400, 10), random.randrange(0, 400, 10)) # Set default value\n\n def draw(self):\n pass # Set default value\n\n def handle_input(self):\n for event in pygame.event.get():\n if event.type == pygame.QUIT:\n pygame.quit()\n quit()\n if event.type == pygame.KEYDOWN:\n if event.key == pygame.K_UP:\n self.snake.change_direction('UP')\n elif event.key == pygame.K_DOWN:\n self.snake.change_direction('DOWN')\n elif event.key == pygame.K_LEFT:\n self.snake.change_direction('LEFT')\n elif event.key == pygame.K_RIGHT:\n self.snake.change_direction('RIGHT')\n\n def restart(self):\n self.snake = Snake((100, 50), 1, 'RIGHT') # Set default value\n self.food = (200, 200) # Set default value\n self.score = 0 # Set default value\n\n```\n\n\n\n# Format example 1\n## Code Review: game.py\n1. No, we should fix the logic of class A due to ...\n2. ...\n3. ...\n4. No, function B is not implemented, ...\n5. ...\n6. ...\n\n## Actions\n1. Fix the `handle_events` method to update the game state only if a move is successful.\n ```python\n def handle_events(self):\n for event in pygame.event.get():\n if event.type == pygame.QUIT:\n return False\n if event.type == pygame.KEYDOWN:\n moved = False\n if event.key == pygame.K_UP:\n moved = self.game.move('UP')\n elif event.key == pygame.K_DOWN:\n moved = self.game.move('DOWN')\n elif event.key == pygame.K_LEFT:\n moved = self.game.move('LEFT')\n elif event.key == pygame.K_RIGHT:\n moved = self.game.move('RIGHT')\n if moved:\n # Update the game state only if a move was successful\n self.render()\n return True\n ```\n2. Implement function B\n\n## Code Review Result\nLBTM\n\n# Format example 2\n## Code Review: game.py\n1. Yes.\n2. Yes.\n3. Yes.\n4. Yes.\n5. Yes.\n6. Yes.\n\n## Actions\npass\n\n## Code Review Result\nLGTM\n\n\n\n# Instruction: Based on the actual code situation, follow one of the \"Format example\". Return only 1 file under review.\n\n## Code Review: Ordered List. Based on the \"Code to be Reviewed\", provide key, clear, concise, and specific answer. If any answer is no, explain how to fix it step by step.\n1. Is the code implemented as per the requirements? If not, how to achieve it? Analyse it step by step.\n2. Is the code logic completely correct? If there are errors, please indicate how to correct them.\n3. Does the existing code follow the \"Data structures and interfaces\"?\n4. Are all functions implemented? If there is no implementation, please indicate how to achieve it step by step.\n5. Have all necessary pre-dependencies been imported? If not, indicate which ones need to be imported\n6. Are methods from other files being reused correctly?\n\n## Actions: Ordered List. Things that should be done after CR, such as implementing class A and function B\n\n## Code Review Result: str. If the code doesn't have bugs, we don't need to rewrite it, so answer LGTM and stop. ONLY ANSWER LGTM/LBTM.\nLGTM/LBTM\n\n": "## Code Review: game.py\n1. The code is implemented as per the requirements. The Snake and Game classes are defined, and the necessary methods for updating, drawing, handling input, and restarting the game are implemented.\n2. The code logic is correct. The Snake class methods for moving, changing direction, and growing the snake are implemented properly. The Game class methods for updating the game state, handling input, and restarting the game are also correctly implemented.\n3. The existing code follows the defined \"Data structures and interfaces\" with the Snake and Game classes and their respective methods.\n4. All necessary functions are implemented as per the requirements.\n5. The necessary pre-dependencies, including the pygame package, are imported.\n6. The methods from other files are not being reused in the current code.\n\n## Actions\nNo actions required.\n\n## Code Review Result\nLGTM", + "\nNOTICE\nRole: You are a professional engineer; the main goal is to write google-style, elegant, modular, easy to read and maintain code\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## Design\n{\"Implementation approach\":\"We will use the Pygame library to create the game and handle user input. The game logic will be implemented using Python classes and functions.\",\"File list\":[\"main.py\",\"game.py\"],\"Data structures and interfaces\":\"\\nclassDiagram\\n class Game {\\n -snake: list\\n -food: tuple\\n -score: int\\n +__init__()\\n +update()\\n +draw()\\n +handle_input()\\n +restart()\\n }\\n class Snake {\\n -body: list\\n -direction: str\\n +__init__(start_pos: tuple, length: int, direction: str)\\n +move()\\n +change_direction(new_direction: str)\\n +grow()\\n }\\n Game --> Snake\\n\",\"Program call flow\":\"\\nsequenceDiagram\\n participant M as Main\\n participant G as Game\\n M->>G: initialize game\\n G->>G: update game state\\n G->>G: draw game\\n G->>G: handle user input\\n G->>G: restart game\\n\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Task\n{\"Required Python packages\":[\"pygame==2.0.1\"],\"Required Other language third-party packages\":[\"No third-party dependencies required\"],\"Logic Analysis\":[[\"game.py\",\"Contains Game class and ... functions\"],[\"main.py\",\"Contains main function, from game import Game\"]],\"Task list\":[\"game.py\",\"main.py\"],\"Full API spec\":\"\",\"Shared Knowledge\":\"`game.py` contains functions shared across the project.\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Legacy Code\n```Code\n\n```\n\n## Debug logs\n```text\n\n\n\n```\n\n## Bug Feedback logs\n```text\n\n```\n\n# Format example\n## Code: main.py\n```python\n## main.py\n...\n```\n\n# Instruction: Based on the context, follow \"Format example\", write code.\n\n## Code: main.py. Write code with triple quoto, based on the following attentions and context.\n1. Only One file: do your best to implement THIS ONLY ONE FILE.\n2. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets.\n3. Set default value: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. AVOID circular import.\n4. Follow design: YOU MUST FOLLOW \"Data structures and interfaces\". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design.\n5. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE.\n6. Before using a external variable/module, make sure you import it first.\n7. Write out EVERY CODE DETAIL, DON'T LEAVE TODO.\n\n": "```python\n## main.py\n\nfrom game import Game\n\ndef main():\n game = Game()\n running = True\n\n while running:\n game.update()\n game.draw()\n game.handle_input()\n\n if game.game_over:\n game.restart()\n\nif __name__ == \"__main__\":\n main()\n```", + "\n# System\nRole: You are a professional software engineer, and your main task is to review and revise the code. You need to ensure that the code conforms to the google-style standards, is elegantly designed and modularized, easy to read and maintain.\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## System Design\n{\"Implementation approach\":\"We will use the Pygame library to create the game and handle user input. The game logic will be implemented using Python classes and functions.\",\"File list\":[\"main.py\",\"game.py\"],\"Data structures and interfaces\":\"\\nclassDiagram\\n class Game {\\n -snake: list\\n -food: tuple\\n -score: int\\n +__init__()\\n +update()\\n +draw()\\n +handle_input()\\n +restart()\\n }\\n class Snake {\\n -body: list\\n -direction: str\\n +__init__(start_pos: tuple, length: int, direction: str)\\n +move()\\n +change_direction(new_direction: str)\\n +grow()\\n }\\n Game --> Snake\\n\",\"Program call flow\":\"\\nsequenceDiagram\\n participant M as Main\\n participant G as Game\\n M->>G: initialize game\\n G->>G: update game state\\n G->>G: draw game\\n G->>G: handle user input\\n G->>G: restart game\\n\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Task\n{\"Required Python packages\":[\"pygame==2.0.1\"],\"Required Other language third-party packages\":[\"No third-party dependencies required\"],\"Logic Analysis\":[[\"game.py\",\"Contains Game class and ... functions\"],[\"main.py\",\"Contains main function, from game import Game\"]],\"Task list\":[\"game.py\",\"main.py\"],\"Full API spec\":\"\",\"Shared Knowledge\":\"`game.py` contains functions shared across the project.\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Code Files\n\n\n\n## Code to be Reviewed: main.py\n```Code\n## main.py\n\nfrom game import Game\n\ndef main():\n game = Game()\n running = True\n\n while running:\n game.update()\n game.draw()\n game.handle_input()\n\n if game.game_over:\n game.restart()\n\nif __name__ == \"__main__\":\n main()\n\n```\n\n\n\n# Format example 1\n## Code Review: main.py\n1. No, we should fix the logic of class A due to ...\n2. ...\n3. ...\n4. No, function B is not implemented, ...\n5. ...\n6. ...\n\n## Actions\n1. Fix the `handle_events` method to update the game state only if a move is successful.\n ```python\n def handle_events(self):\n for event in pygame.event.get():\n if event.type == pygame.QUIT:\n return False\n if event.type == pygame.KEYDOWN:\n moved = False\n if event.key == pygame.K_UP:\n moved = self.game.move('UP')\n elif event.key == pygame.K_DOWN:\n moved = self.game.move('DOWN')\n elif event.key == pygame.K_LEFT:\n moved = self.game.move('LEFT')\n elif event.key == pygame.K_RIGHT:\n moved = self.game.move('RIGHT')\n if moved:\n # Update the game state only if a move was successful\n self.render()\n return True\n ```\n2. Implement function B\n\n## Code Review Result\nLBTM\n\n# Format example 2\n## Code Review: main.py\n1. Yes.\n2. Yes.\n3. Yes.\n4. Yes.\n5. Yes.\n6. Yes.\n\n## Actions\npass\n\n## Code Review Result\nLGTM\n\n\n\n# Instruction: Based on the actual code situation, follow one of the \"Format example\". Return only 1 file under review.\n\n## Code Review: Ordered List. Based on the \"Code to be Reviewed\", provide key, clear, concise, and specific answer. If any answer is no, explain how to fix it step by step.\n1. Is the code implemented as per the requirements? If not, how to achieve it? Analyse it step by step.\n2. Is the code logic completely correct? If there are errors, please indicate how to correct them.\n3. Does the existing code follow the \"Data structures and interfaces\"?\n4. Are all functions implemented? If there is no implementation, please indicate how to achieve it step by step.\n5. Have all necessary pre-dependencies been imported? If not, indicate which ones need to be imported\n6. Are methods from other files being reused correctly?\n\n## Actions: Ordered List. Things that should be done after CR, such as implementing class A and function B\n\n## Code Review Result: str. If the code doesn't have bugs, we don't need to rewrite it, so answer LGTM and stop. ONLY ANSWER LGTM/LBTM.\nLGTM/LBTM\n\n": "## Code Review: main.py\n1. Yes, the code is implemented as per the requirements.\n2. Yes, the code logic is completely correct.\n3. Yes, the existing code follows the \"Data structures and interfaces\".\n4. No, the `game_over` condition is not handled in the code. To achieve it, we need to add the implementation of the `game_over` condition in the `Game` class.\n5. Yes, all necessary pre-dependencies have been imported.\n6. Yes, methods from other files are being reused correctly.\n\n## Actions\n4. Implement the `game_over` condition in the `Game` class to handle the game over scenario.\n\n## Code Review Result\nLBTM", + "\n# System\nRole: You are a professional software engineer, and your main task is to review and revise the code. You need to ensure that the code conforms to the google-style standards, is elegantly designed and modularized, easy to read and maintain.\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## System Design\n{\"Implementation approach\":\"We will use the Pygame library to create the game and handle user input. The game logic will be implemented using Python classes and functions.\",\"File list\":[\"main.py\",\"game.py\"],\"Data structures and interfaces\":\"\\nclassDiagram\\n class Game {\\n -snake: list\\n -food: tuple\\n -score: int\\n +__init__()\\n +update()\\n +draw()\\n +handle_input()\\n +restart()\\n }\\n class Snake {\\n -body: list\\n -direction: str\\n +__init__(start_pos: tuple, length: int, direction: str)\\n +move()\\n +change_direction(new_direction: str)\\n +grow()\\n }\\n Game --> Snake\\n\",\"Program call flow\":\"\\nsequenceDiagram\\n participant M as Main\\n participant G as Game\\n M->>G: initialize game\\n G->>G: update game state\\n G->>G: draw game\\n G->>G: handle user input\\n G->>G: restart game\\n\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Task\n{\"Required Python packages\":[\"pygame==2.0.1\"],\"Required Other language third-party packages\":[\"No third-party dependencies required\"],\"Logic Analysis\":[[\"game.py\",\"Contains Game class and ... functions\"],[\"main.py\",\"Contains main function, from game import Game\"]],\"Task list\":[\"game.py\",\"main.py\"],\"Full API spec\":\"\",\"Shared Knowledge\":\"`game.py` contains functions shared across the project.\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Code Files\n\n\n\n## Code to be Reviewed: main.py\n```Code\n## main.py\n\nfrom game import Game\n\ndef main():\n game = Game()\n running = True\n\n while running:\n game.update()\n game.draw()\n game.handle_input()\n\n if game.game_over:\n game.restart()\n\nif __name__ == \"__main__\":\n main()\n\n```\n\n## Code Review: main.py\n1. Yes, the code is implemented as per the requirements.\n2. Yes, the code logic is completely correct.\n3. Yes, the existing code follows the \"Data structures and interfaces\".\n4. No, the `game_over` condition is not handled in the code. To achieve it, we need to add the implementation of the `game_over` condition in the `Game` class.\n5. Yes, all necessary pre-dependencies have been imported.\n6. Yes, methods from other files are being reused correctly.\n\n## Actions\n4. Implement the `game_over` condition in the `Game` class to handle the game over scenario.\n\n## Code Review Result\nLBTM\n\n# Instruction: rewrite code based on the Code Review and Actions\n## Rewrite Code: CodeBlock. If it still has some bugs, rewrite main.py with triple quotes. Do your utmost to optimize THIS SINGLE FILE. Return all completed codes and prohibit the return of unfinished codes.\n```Code\n## main.py\n...\n```\n": "```Code\n## main.py\n\nfrom game import Game\n\ndef main():\n game = Game()\n running = True\n\n while running:\n game.update()\n game.draw()\n game.handle_input()\n\n if game.game_over():\n game.restart()\n\nif __name__ == \"__main__\":\n main()\n```", + "\n# System\nRole: You are a professional software engineer, and your main task is to review and revise the code. You need to ensure that the code conforms to the google-style standards, is elegantly designed and modularized, easy to read and maintain.\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## System Design\n{\"Implementation approach\":\"We will use the Pygame library to create the game and handle user input. The game logic will be implemented using Python classes and functions.\",\"File list\":[\"main.py\",\"game.py\"],\"Data structures and interfaces\":\"\\nclassDiagram\\n class Game {\\n -snake: list\\n -food: tuple\\n -score: int\\n +__init__()\\n +update()\\n +draw()\\n +handle_input()\\n +restart()\\n }\\n class Snake {\\n -body: list\\n -direction: str\\n +__init__(start_pos: tuple, length: int, direction: str)\\n +move()\\n +change_direction(new_direction: str)\\n +grow()\\n }\\n Game --> Snake\\n\",\"Program call flow\":\"\\nsequenceDiagram\\n participant M as Main\\n participant G as Game\\n M->>G: initialize game\\n G->>G: update game state\\n G->>G: draw game\\n G->>G: handle user input\\n G->>G: restart game\\n\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Task\n{\"Required Python packages\":[\"pygame==2.0.1\"],\"Required Other language third-party packages\":[\"No third-party dependencies required\"],\"Logic Analysis\":[[\"game.py\",\"Contains Game class and ... functions\"],[\"main.py\",\"Contains main function, from game import Game\"]],\"Task list\":[\"game.py\",\"main.py\"],\"Full API spec\":\"\",\"Shared Knowledge\":\"`game.py` contains functions shared across the project.\",\"Anything UNCLEAR\":\"Clarification needed on the scoring system and how the game over condition will be handled.\"}\n\n## Code Files\n\n\n\n## Code to be Reviewed: main.py\n```Code\n## main.py\n\nfrom game import Game\n\ndef main():\n game = Game()\n running = True\n\n while running:\n game.update()\n game.draw()\n game.handle_input()\n\n if game.game_over():\n game.restart()\n\nif __name__ == \"__main__\":\n main()\n\n```\n\n\n\n# Format example 1\n## Code Review: main.py\n1. No, we should fix the logic of class A due to ...\n2. ...\n3. ...\n4. No, function B is not implemented, ...\n5. ...\n6. ...\n\n## Actions\n1. Fix the `handle_events` method to update the game state only if a move is successful.\n ```python\n def handle_events(self):\n for event in pygame.event.get():\n if event.type == pygame.QUIT:\n return False\n if event.type == pygame.KEYDOWN:\n moved = False\n if event.key == pygame.K_UP:\n moved = self.game.move('UP')\n elif event.key == pygame.K_DOWN:\n moved = self.game.move('DOWN')\n elif event.key == pygame.K_LEFT:\n moved = self.game.move('LEFT')\n elif event.key == pygame.K_RIGHT:\n moved = self.game.move('RIGHT')\n if moved:\n # Update the game state only if a move was successful\n self.render()\n return True\n ```\n2. Implement function B\n\n## Code Review Result\nLBTM\n\n# Format example 2\n## Code Review: main.py\n1. Yes.\n2. Yes.\n3. Yes.\n4. Yes.\n5. Yes.\n6. Yes.\n\n## Actions\npass\n\n## Code Review Result\nLGTM\n\n\n\n# Instruction: Based on the actual code situation, follow one of the \"Format example\". Return only 1 file under review.\n\n## Code Review: Ordered List. Based on the \"Code to be Reviewed\", provide key, clear, concise, and specific answer. If any answer is no, explain how to fix it step by step.\n1. Is the code implemented as per the requirements? If not, how to achieve it? Analyse it step by step.\n2. Is the code logic completely correct? If there are errors, please indicate how to correct them.\n3. Does the existing code follow the \"Data structures and interfaces\"?\n4. Are all functions implemented? If there is no implementation, please indicate how to achieve it step by step.\n5. Have all necessary pre-dependencies been imported? If not, indicate which ones need to be imported\n6. Are methods from other files being reused correctly?\n\n## Actions: Ordered List. Things that should be done after CR, such as implementing class A and function B\n\n## Code Review Result: str. If the code doesn't have bugs, we don't need to rewrite it, so answer LGTM and stop. ONLY ANSWER LGTM/LBTM.\nLGTM/LBTM\n\n": "## Code Review: main.py\n1. The code is implemented as per the requirements. It initializes the game, updates the game state, draws the game, handles user input, and restarts the game when it's over.\n2. The code logic is correct, and it follows the expected flow of the game.\n3. The existing code follows the \"Data structures and interfaces\" as it uses classes and methods as described in the class diagram.\n4. All necessary functions are implemented as per the requirements.\n5. The necessary pre-dependencies have been imported, as the `pygame` package is imported.\n6. The methods from other files are being reused correctly.\n\n## Actions\npass\n\n## Code Review Result\nLGTM", + "\n## context\n{'Implementation approach': '我们将使用Python编程语言,并选择合适的开源框架来实现贪吃蛇游戏。我们将分析需求中的难点,并选择合适的开源框架来简化开发流程。', 'File list': ['main.py', 'game.py'], 'Data structures and interfaces': '\\nclassDiagram\\n class Game {\\n -int width\\n -int height\\n -int score\\n -int speed\\n -List snake\\n -Point food\\n +__init__(width: int, height: int, speed: int)\\n +start_game()\\n +change_direction(direction: str)\\n +game_over()\\n +update_snake()\\n +update_food()\\n +check_collision()\\n }\\n class Point {\\n -int x\\n -int y\\n +__init__(x: int, y: int)\\n }\\n Game --> Point\\n', 'Program call flow': '\\nsequenceDiagram\\n participant M as Main\\n participant G as Game\\n M->>G: start_game()\\n M->>G: change_direction(direction)\\n G->>G: update_snake()\\n G->>G: update_food()\\n G->>G: check_collision()\\n G-->>G: game_over()\\n', 'Anything UNCLEAR': ''}\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Required Python packages\": [\n \"flask==1.1.2\",\n \"bcrypt==3.2.0\"\n ],\n \"Required Other language third-party packages\": [\n \"No third-party dependencies required\"\n ],\n \"Logic Analysis\": [\n [\n \"game.py\",\n \"Contains Game class and ... functions\"\n ],\n [\n \"main.py\",\n \"Contains main function, from game import Game\"\n ]\n ],\n \"Task list\": [\n \"game.py\",\n \"main.py\"\n ],\n \"Full API spec\": \"openapi: 3.0.0 ...\",\n \"Shared Knowledge\": \"`game.py` contains functions shared across the project.\",\n \"Anything UNCLEAR\": \"Clarification needed on how to start and initialize third-party libraries.\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Required Python packages: typing.List[str] # Provide required Python packages in requirements.txt format.\n- Required Other language third-party packages: typing.List[str] # List down the required packages for languages other than Python.\n- Logic Analysis: typing.List[typing.List[str]] # Provide a list of files with the classes/methods/functions to be implemented, including dependency analysis and imports.\n- Task list: typing.List[str] # Break down the tasks into a list of filenames, prioritized by dependency order.\n- Full API spec: # Describe all APIs using OpenAPI 3.0 spec that may be used by both frontend and backend. If front-end and back-end communication is not required, leave it blank.\n- Shared Knowledge: # Detail any shared knowledge, like common utility functions or configuration variables.\n- Anything UNCLEAR: # Mention any unclear aspects in the project management context and try to clarify them.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Required Python packages\": [\n \"pygame==2.0.1\"\n ],\n \"Required Other language third-party packages\": [\n \"No third-party dependencies required\"\n ],\n \"Logic Analysis\": [\n [\n \"game.py\",\n \"Contains Game class and related functions\"\n ],\n [\n \"main.py\",\n \"Contains the main function and imports Game from game.py\"\n ]\n ],\n \"Task list\": [\n \"game.py\",\n \"main.py\"\n ],\n \"Full API spec\": \"\",\n \"Shared Knowledge\": \"`game.py` contains functions shared across the project.\",\n \"Anything UNCLEAR\": \"\"\n}\n[/CONTENT]", + "\nNOTICE\nRole: You are a professional software engineer, and your main task is to review the code.\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n-----\n# System Design\n```text\n\n{\"Implementation approach\": \"To develop this snake game, we will use the Python language and choose the Pygame library. Pygame is an open-source Python module collection specifically designed for writing video games. It provides functionalities such as displaying images and playing sounds, making it suitable for creating intuitive and responsive user interfaces. We will ensure efficient game logic to prevent any delays during gameplay. The scoring system will be simple, with the snake gaining points for each food it eats. We will use Pygame's event handling system to implement pause and resume functionality, as well as high-score tracking. The difficulty will increase by speeding up the snake's movement. In the initial version, we will focus on single-player mode and consider adding multiplayer mode and customizable skins in future updates. Based on the new requirement, we will also add a moving obstacle that appears randomly. If the snake eats this obstacle, the game will end. If the snake does not eat the obstacle, it will disappear after 5 seconds. For this, we need to add mechanisms for obstacle generation, movement, and disappearance in the game logic.\", \"Project_name\": \"snake_game\", \"File list\": [\"main.py\", \"game.py\", \"snake.py\", \"food.py\", \"obstacle.py\", \"scoreboard.py\", \"constants.py\", \"assets/styles.css\", \"assets/index.html\"], \"Data structures and interfaces\": \"```mermaid\n classDiagram\n class Game{\n +int score\n +int speed\n +bool game_over\n +bool paused\n +Snake snake\n +Food food\n +Obstacle obstacle\n +Scoreboard scoreboard\n +start_game() void\n +pause_game() void\n +resume_game() void\n +end_game() void\n +increase_difficulty() void\n +update() void\n +render() void\n Game()\n }\n class Snake{\n +list body_parts\n +str direction\n +bool grow\n +move() void\n +grow() void\n +check_collision() bool\n Snake()\n }\n class Food{\n +tuple position\n +spawn() void\n Food()\n }\n class Obstacle{\n +tuple position\n +int lifetime\n +bool active\n +spawn() void\n +move() void\n +check_collision() bool\n +disappear() void\n Obstacle()\n }\n class Scoreboard{\n +int high_score\n +update_score(int) void\n +reset_score() void\n +load_high_score() void\n +save_high_score() void\n Scoreboard()\n }\n class Constants{\n }\n Game \"1\" -- \"1\" Snake: has\n Game \"1\" -- \"1\" Food: has\n Game \"1\" -- \"1\" Obstacle: has\n Game \"1\" -- \"1\" Scoreboard: has\n ```\", \"Program call flow\": \"```sequenceDiagram\n participant M as Main\n participant G as Game\n participant S as Snake\n participant F as Food\n participant O as Obstacle\n participant SB as Scoreboard\n M->>G: start_game()\n loop game loop\n G->>S: move()\n G->>S: check_collision()\n G->>F: spawn()\n G->>O: spawn()\n G->>O: move()\n G->>O: check_collision()\n G->>O: disappear()\n G->>SB: update_score(score)\n G->>G: update()\n G->>G: render()\n alt if paused\n M->>G: pause_game()\n M->>G: resume_game()\n end\n alt if game_over\n G->>M: end_game()\n end\n end\n```\", \"Anything UNCLEAR\": \"There is no need for further clarification as the requirements are already clear.\"}\n\n```\n-----\n# Task\n```text\n\n{\"Required Python third-party packages\": [\"pygame==2.0.1\"], \"Required Other language third-party packages\": [\"No third-party packages required for other languages.\"], \"Full API spec\": \"\n openapi: 3.0.0\n info:\n title: Snake Game API\n version: \"1.0.0\"\n paths:\n /start:\n get:\n summary: Start the game\n responses:\n '200':\n description: Game started successfully\n /pause:\n get:\n summary: Pause the game\n responses:\n '200':\n description: Game paused successfully\n /resume:\n get:\n summary: Resume the game\n responses:\n '200':\n description: Game resumed successfully\n /end:\n get:\n summary: End the game\n responses:\n '200':\n description: Game ended successfully\n /score:\n get:\n summary: Get the current score\n responses:\n '200':\n description: Current score retrieved successfully\n /highscore:\n get:\n summary: Get the high score\n responses:\n '200':\n description: High score retrieved successfully\n components: {}\n \", \"Logic Analysis\": [[\"constants.py\", \"Contains all the constant values like screen size, colors, game speeds, etc. This should be implemented first as it provides the base values for other components.\"], [\"snake.py\", \"Contains the Snake class with methods for movement, growth, and collision detection. It is dependent on constants.py for configuration values.\"], [\"food.py\", \"Contains the Food class responsible for spawning food items on the screen. It is dependent on constants.py for configuration values.\"], [\"obstacle.py\", \"Contains the Obstacle class with methods for spawning, moving, and disappearing of obstacles, as well as collision detection with the snake. It is dependent on constants.py for configuration values.\"], [\"scoreboard.py\", \"Contains the Scoreboard class for updating, resetting, loading, and saving high scores. It may use constants.py for configuration values and depends on the game's scoring logic.\"], [\"game.py\", \"Contains the main Game class which includes the game loop and methods for starting, pausing, resuming, and ending the game. It is dependent on snake.py, food.py, obstacle.py, and scoreboard.py.\"], [\"main.py\", \"The entry point of the game that initializes the game and starts the game loop. It is dependent on game.py.\"]], \"Task list\": [\"constants.py\", \"snake.py\", \"food.py\", \"obstacle.py\", \"scoreboard.py\", \"game.py\", \"main.py\"], \"Shared Knowledge\": \"\n 'constants.py' should contain all the necessary configurations for the game, such as screen dimensions, color definitions, and speed settings. These constants will be used across multiple files, ensuring consistency and ease of updates. Ensure that the Pygame library is initialized correctly in 'main.py' before starting the game loop. Also, make sure that the game's state is managed properly when pausing and resuming the game.\n \", \"Anything UNCLEAR\": \"The interaction between the 'obstacle.py' and the game loop needs to be clearly defined to ensure obstacles appear and disappear correctly. The lifetime of the obstacle and its random movement should be implemented in a way that does not interfere with the game's performance.\"}\n\n```\n-----\n```python\n\n## game.py\nimport pygame\nfrom snake import Snake\nfrom food import Food\n\nclass Game:\n def __init__(self):\n self.score = 0\n self.level = 1\n self.snake = Snake()\n self.food = Food()\n\n def start_game(self):\n pygame.init()\n self.initialize_game()\n self.game_loop()\n\n def initialize_game(self):\n self.score = 0\n self.level = 1\n self.snake.reset()\n self.food.generate()\n\n def game_loop(self):\n game_over = False\n\n while not game_over:\n self.update()\n self.draw()\n self.handle_events()\n self.check_collision()\n self.increase_score()\n self.increase_level()\n\n if self.snake.is_collision():\n game_over = True\n self.game_over()\n\n def update(self):\n self.snake.move()\n\n def draw(self):\n self.snake.draw()\n self.food.draw()\n\n def handle_events(self):\n for event in pygame.event.get():\n if event.type == pygame.QUIT:\n pygame.quit()\n quit()\n elif event.type == pygame.KEYDOWN:\n if event.key == pygame.K_UP:\n self.snake.change_direction(\"UP\")\n elif event.key == pygame.K_DOWN:\n self.snake.change_direction(\"DOWN\")\n elif event.key == pygame.K_LEFT:\n self.snake.change_direction(\"LEFT\")\n elif event.key == pygame.K_RIGHT:\n self.snake.change_direction(\"RIGHT\")\n\n def check_collision(self):\n if self.snake.get_head() == self.food.get_position():\n self.snake.grow()\n self.food.generate()\n\n def increase_score(self):\n self.score += 1\n\n def increase_level(self):\n if self.score % 10 == 0:\n self.level += 1\n\n def game_over(self):\n print(\"Game Over\")\n self.initialize_game()\n\n\n```\n-----\n```python\n\n## snake.py\nimport pygame\n\nclass Snake:\n def __init__(self):\n self.body = [(0, 0)]\n self.direction = (1, 0)\n\n def move(self):\n head = self.body[0]\n dx, dy = self.direction\n new_head = (head[0] + dx, head[1] + dy)\n self.body.insert(0, new_head)\n self.body.pop()\n\n def change_direction(self, direction):\n if direction == \"UP\":\n self.direction = (0, -1)\n elif direction == \"DOWN\":\n self.direction = (0, 1)\n elif direction == \"LEFT\":\n self.direction = (-1, 0)\n elif direction == \"RIGHT\":\n self.direction = (1, 0)\n\n def grow(self):\n tail = self.body[-1]\n dx, dy = self.direction\n new_tail = (tail[0] - dx, tail[1] - dy)\n self.body.append(new_tail)\n\n def get_head(self):\n return self.body[0]\n\n def get_body(self):\n return self.body[1:]\n\n\n```\n-----\n```python\n\n## main.py\nimport pygame\nfrom game import Game\n\ndef main():\n pygame.init()\n game = Game()\n game.start_game()\n\nif __name__ == \"__main__\":\n main()\n\n\n```\n-----\n```python\n\n## food.py\nimport random\n\nclass Food:\n def __init__(self):\n self.position = (0, 0)\n\n def generate(self):\n x = random.randint(0, 9)\n y = random.randint(0, 9)\n self.position = (x, y)\n\n def get_position(self):\n return self.position\n\n\n```\n-----\n\n## Code Review All: Please read all historical files and find possible bugs in the files, such as unimplemented functions, calling errors, unreferences, etc.\n\n## Call flow: mermaid code, based on the implemented function, use mermaid to draw a complete call chain\n\n## Summary: Summary based on the implementation of historical files\n\n## TODOs: Python dict[str, str], write down the list of files that need to be modified and the reasons. We will modify them later.\n\n": "```mermaid\nclassDiagram\n class Game{\n +int score\n +int level\n +Snake snake\n +Food food\n +start_game() void\n +initialize_game() void\n +game_loop() void\n +update() void\n +draw() void\n +handle_events() void\n +check_collision() void\n +increase_score() void\n +increase_level() void\n +game_over() void\n Game()\n }\n class Snake{\n +list body\n +tuple direction\n +move() void\n +change_direction(direction: str) void\n +grow() void\n +get_head() tuple\n +get_body() list\n Snake()\n }\n class Food{\n +tuple position\n +generate() void\n +get_position() tuple\n Food()\n }\n Game \"1\" -- \"1\" Snake: has\n Game \"1\" -- \"1\" Food: has\n```\n\n```sequenceDiagram\nparticipant M as Main\nparticipant G as Game\nparticipant S as Snake\nparticipant F as Food\nM->>G: start_game()\nG->>G: initialize_game()\nG->>G: game_loop()\nG->>S: move()\nG->>S: change_direction()\nG->>S: grow()\nG->>F: generate()\nS->>S: move()\nS->>S: change_direction()\nS->>S: grow()\nF->>F: generate()\n```\n\n## Summary\nThe code consists of the main game logic, including the Game, Snake, and Food classes. The game loop is responsible for updating and drawing the game elements, handling events, checking collisions, and managing the game state. The Snake class handles the movement, growth, and direction changes of the snake, while the Food class is responsible for generating and tracking the position of food items.\n\n## TODOs\n- Modify 'game.py' to add the implementation of obstacle handling and interaction with the game loop.\n- Implement 'obstacle.py' to include the methods for spawning, moving, and disappearing of obstacles, as well as collision detection with the snake.\n- Update 'main.py' to initialize the obstacle and incorporate it into the game loop.\n- Update the mermaid call flow diagram to include the interaction with the obstacle.\n\n```python\n{\n \"files_to_modify\": {\n \"game.py\": \"Add obstacle handling and interaction with the game loop\",\n \"obstacle.py\": \"Implement obstacle class with necessary methods\",\n \"main.py\": \"Initialize the obstacle and incorporate it into the game loop\"\n }\n}\n```", + "\nNOTICE\nRole: You are a professional engineer; the main goal is to write google-style, elegant, modular, easy to read and maintain code\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## Design\n设计一个名为'add'的函数,该函数接受两个整数作为输入,并返回它们的和。\n\n## Task\n\n\n## Legacy Code\n```Code\n\n```\n\n## Debug logs\n```text\n\n\n\n```\n\n## Bug Feedback logs\n```text\n\n```\n\n# Format example\n## Code: \n```python\n## \n...\n```\n\n# Instruction: Based on the context, follow \"Format example\", write code.\n\n## Code: . Write code with triple quoto, based on the following attentions and context.\n1. Only One file: do your best to implement THIS ONLY ONE FILE.\n2. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets.\n3. Set default value: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. AVOID circular import.\n4. Follow design: YOU MUST FOLLOW \"Data structures and interfaces\". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design.\n5. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE.\n6. Before using a external variable/module, make sure you import it first.\n7. Write out EVERY CODE DETAIL, DON'T LEAVE TODO.\n\n": "## Code: \n```python\n## Design a function called 'add' that takes two integers as input and returns their sum.\n\ndef add(num1: int, num2: int) -> int:\n \"\"\"\n Add two integers and return the sum.\n \n Args:\n num1: An integer.\n num2: An integer.\n \n Returns:\n The sum of num1 and num2 as an integer.\n \"\"\"\n return num1 + num2\n```", + "\nNOTICE\nRole: You are a professional engineer; the main goal is to write google-style, elegant, modular, easy to read and maintain code\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## Design\n{\"Implementation approach\": \"We will use the Pygame library to create the game interface and handle user input. The game logic will be implemented using Python classes and data structures.\", \"File list\": [\"main.py\", \"game.py\"], \"Data structures and interfaces\": \"classDiagram\\n class Game {\\n -grid: List[List[int]]\\n -score: int\\n -game_over: bool\\n +__init__()\\n +reset_game()\\n +move(direction: str)\\n +is_game_over() bool\\n +get_empty_cells() List[Tuple[int, int]]\\n +add_new_tile()\\n +get_score() int\\n }\\n class UI {\\n -game: Game\\n +__init__(game: Game)\\n +draw_grid()\\n +draw_score()\\n +draw_game_over()\\n +handle_input()\\n }\\n Game --> UI\", \"Program call flow\": \"sequenceDiagram\\n participant M as Main\\n participant G as Game\\n participant U as UI\\n M->>G: reset_game()\\n M->>U: draw_grid()\\n M->>U: draw_score()\\n M->>U: handle_input()\\n U->>G: move(direction)\\n G->>G: add_new_tile()\\n G->>U: draw_grid()\\n G->>U: draw_score()\\n G->>U: draw_game_over()\\n G->>G: is_game_over()\\n G->>G: get_empty_cells()\\n G->>G: get_score()\", \"Anything UNCLEAR\": \"...\"}\n\n## Task\n{\"Required Python packages\": [\"pygame==2.0.1\"], \"Required Other language third-party packages\": [\"No third-party dependencies required\"], \"Logic Analysis\": [[\"game.py\", \"Contains Game class and related functions for game logic\"], [\"main.py\", \"Contains main function, initializes the game and UI\"]], \"Task list\": [\"game.py\", \"main.py\"], \"Full API spec\": \"\", \"Shared Knowledge\": \"The game logic will be implemented using Python classes and data structures. The Pygame library will be used to create the game interface and handle user input.\", \"Anything UNCLEAR\": \"...\"}\n\n## Legacy Code\n```Code\n\n```\n\n## Debug logs\n```text\nE.......F\n======================================================================\nERROR: test_add_new_tile (__main__.TestGame)\n----------------------------------------------------------------------\nTraceback (most recent call last):\n File \"/Users/xx/tests/test_game.py\", line 104, in test_add_new_tile\n self.assertIn(self.game.grid[empty_cells[0][0]][empty_cells[0][1]], [2, 4])\nIndexError: list index out of range\n\n======================================================================\nFAIL: test_reset_game (__main__.TestGame)\n----------------------------------------------------------------------\nTraceback (most recent call last):\n File \"/Users/xx/tests/test_game.py\", line 13, in test_reset_game\n self.assertEqual(self.game.grid, [[0 for _ in range(4)] for _ in range(4)])\nAssertionError: Lists differ: [[0, 0, 0, 0], [0, 2, 0, 0], [0, 0, 0, 2], [0, 0, 0, 0]] != [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]\n\nFirst differing element 1:\n[0, 2, 0, 0]\n[0, 0, 0, 0]\n\n- [[0, 0, 0, 0], [0, 2, 0, 0], [0, 0, 0, 2], [0, 0, 0, 0]]\n? --- ^\n\n+ [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]\n? +++ ^\n\n\n----------------------------------------------------------------------\nRan 9 tests in 0.002s\n\nFAILED (failures=1, errors=1)\n\n\n{\"design_filename\": \"docs/system_design/20231221155954.json\", \"task_filename\": \"docs/tasks/20231221155954.json\", \"codes_filenames\": [\"game.py\", \"main.py\"], \"reason\": \"```json\\n{\\n \\\"game.py\\\": \\\"Add handling for no empty cells in add_new_tile function, Update score in move function\\\",\\n \\\"main.py\\\": \\\"Handle game over condition in the game loop\\\"\\n}\\n```\"}\n```\n\n## Bug Feedback logs\n```text\n\n```\n\n# Format example\n## Code: game.py\n```python\n## game.py\n...\n```\n\n# Instruction: Based on the context, follow \"Format example\", write code.\n\n## Code: game.py. Write code with triple quoto, based on the following attentions and context.\n1. Only One file: do your best to implement THIS ONLY ONE FILE.\n2. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets.\n3. Set default value: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. AVOID circular import.\n4. Follow design: YOU MUST FOLLOW \"Data structures and interfaces\". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design.\n5. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE.\n6. Before using a external variable/module, make sure you import it first.\n7. Write out EVERY CODE DETAIL, DON'T LEAVE TODO.\n\n": "## Code: game.py\n```python\n## game.py\n\nfrom typing import List, Tuple\nimport random\n\nclass Game:\n def __init__(self):\n self.grid: List[List[int]] = [[0 for _ in range(4)] for _ in range(4)]\n self.score: int = 0\n self.game_over: bool = False\n\n def reset_game(self):\n self.grid = [[0 for _ in range(4)] for _ in range(4)]\n self.score = 0\n self.game_over = False\n\n def move(self, direction: str):\n # Implement the move logic based on the direction\n pass\n\n def is_game_over(self) -> bool:\n # Check if the game is over\n return self.game_over\n\n def get_empty_cells(self) -> List[Tuple[int, int]]:\n empty_cells = []\n for i in range(4):\n for j in range(4):\n if self.grid[i][j] == 0:\n empty_cells.append((i, j))\n return empty_cells\n\n def add_new_tile(self):\n empty_cells = self.get_empty_cells()\n if empty_cells:\n cell = random.choice(empty_cells)\n self.grid[cell[0]][cell[1]] = random.choice([2, 4])\n else:\n # Handle the case when there are no empty cells\n pass\n\n def get_score(self) -> int:\n return self.score\n```", + "\n# System\nRole: You are a professional software engineer, and your main task is to review and revise the code. You need to ensure that the code conforms to the google-style standards, is elegantly designed and modularized, easy to read and maintain.\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## System Design\n编写一个从a加b的函数,返回a+b\n\n## Task\n\n\n## Code Files\n\n\n\n## Code to be Reviewed: \n```Code\n\ndef add(a, b):\n return a + \n\n```\n\n\n\n# Format example 1\n## Code Review: \n1. No, we should fix the logic of class A due to ...\n2. ...\n3. ...\n4. No, function B is not implemented, ...\n5. ...\n6. ...\n\n## Actions\n1. Fix the `handle_events` method to update the game state only if a move is successful.\n ```python\n def handle_events(self):\n for event in pygame.event.get():\n if event.type == pygame.QUIT:\n return False\n if event.type == pygame.KEYDOWN:\n moved = False\n if event.key == pygame.K_UP:\n moved = self.game.move('UP')\n elif event.key == pygame.K_DOWN:\n moved = self.game.move('DOWN')\n elif event.key == pygame.K_LEFT:\n moved = self.game.move('LEFT')\n elif event.key == pygame.K_RIGHT:\n moved = self.game.move('RIGHT')\n if moved:\n # Update the game state only if a move was successful\n self.render()\n return True\n ```\n2. Implement function B\n\n## Code Review Result\nLBTM\n\n# Format example 2\n## Code Review: \n1. Yes.\n2. Yes.\n3. Yes.\n4. Yes.\n5. Yes.\n6. Yes.\n\n## Actions\npass\n\n## Code Review Result\nLGTM\n\n\n\n# Instruction: Based on the actual code situation, follow one of the \"Format example\". Return only 1 file under review.\n\n## Code Review: Ordered List. Based on the \"Code to be Reviewed\", provide key, clear, concise, and specific answer. If any answer is no, explain how to fix it step by step.\n1. Is the code implemented as per the requirements? If not, how to achieve it? Analyse it step by step.\n2. Is the code logic completely correct? If there are errors, please indicate how to correct them.\n3. Does the existing code follow the \"Data structures and interfaces\"?\n4. Are all functions implemented? If there is no implementation, please indicate how to achieve it step by step.\n5. Have all necessary pre-dependencies been imported? If not, indicate which ones need to be imported\n6. Are methods from other files being reused correctly?\n\n## Actions: Ordered List. Things that should be done after CR, such as implementing class A and function B\n\n## Code Review Result: str. If the code doesn't have bugs, we don't need to rewrite it, so answer LGTM and stop. ONLY ANSWER LGTM/LBTM.\nLGTM/LBTM\n\n": "## Code Review: \n1. No, the code is not implemented as per the requirements. The function 'add' is incomplete and does not return the sum of 'a' and 'b'.\n2. No, the code logic is not completely correct. The return statement in the 'add' function is incomplete.\n3. N/A\n4. N/A\n5. N/A\n6. N/A\n\n## Actions\n1. Complete the 'add' function to return the sum of 'a' and 'b'.\n ```python\n def add(a, b):\n return a + b\n ```\n\n## Code Review Result\nLBTM", + "\n# System\nRole: You are a professional software engineer, and your main task is to review and revise the code. You need to ensure that the code conforms to the google-style standards, is elegantly designed and modularized, easy to read and maintain.\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## System Design\n编写一个从a加b的函数,返回a+b\n\n## Task\n\n\n## Code Files\n\n\n\n## Code to be Reviewed: \n```Code\n\ndef add(a, b):\n return a + \n\n```\n\n## Code Review: \n1. No, the code is not implemented as per the requirements. The function 'add' is incomplete and does not return the sum of 'a' and 'b'.\n2. No, the code logic is not completely correct. The return statement in the 'add' function is incomplete.\n3. N/A\n4. N/A\n5. N/A\n6. N/A\n\n## Actions\n1. Complete the 'add' function to return the sum of 'a' and 'b'.\n ```python\n def add(a, b):\n return a + b\n ```\n\n## Code Review Result\nLBTM\n\n# Instruction: rewrite code based on the Code Review and Actions\n## Rewrite Code: CodeBlock. If it still has some bugs, rewrite with triple quotes. Do your utmost to optimize THIS SINGLE FILE. Return all completed codes and prohibit the return of unfinished codes.\n```Code\n## \n...\n```\n": "```python\ndef add(a, b):\n return a + b\n```", + "\n# System\nRole: You are a professional software engineer, and your main task is to review and revise the code. You need to ensure that the code conforms to the google-style standards, is elegantly designed and modularized, easy to read and maintain.\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## System Design\n编写一个从a加b的函数,返回a+b\n\n## Task\n\n\n## Code Files\n\n\n\n## Code to be Reviewed: \n```Code\ndef add(a, b):\n return a + b\n\n```\n\n\n\n# Format example 1\n## Code Review: \n1. No, we should fix the logic of class A due to ...\n2. ...\n3. ...\n4. No, function B is not implemented, ...\n5. ...\n6. ...\n\n## Actions\n1. Fix the `handle_events` method to update the game state only if a move is successful.\n ```python\n def handle_events(self):\n for event in pygame.event.get():\n if event.type == pygame.QUIT:\n return False\n if event.type == pygame.KEYDOWN:\n moved = False\n if event.key == pygame.K_UP:\n moved = self.game.move('UP')\n elif event.key == pygame.K_DOWN:\n moved = self.game.move('DOWN')\n elif event.key == pygame.K_LEFT:\n moved = self.game.move('LEFT')\n elif event.key == pygame.K_RIGHT:\n moved = self.game.move('RIGHT')\n if moved:\n # Update the game state only if a move was successful\n self.render()\n return True\n ```\n2. Implement function B\n\n## Code Review Result\nLBTM\n\n# Format example 2\n## Code Review: \n1. Yes.\n2. Yes.\n3. Yes.\n4. Yes.\n5. Yes.\n6. Yes.\n\n## Actions\npass\n\n## Code Review Result\nLGTM\n\n\n\n# Instruction: Based on the actual code situation, follow one of the \"Format example\". Return only 1 file under review.\n\n## Code Review: Ordered List. Based on the \"Code to be Reviewed\", provide key, clear, concise, and specific answer. If any answer is no, explain how to fix it step by step.\n1. Is the code implemented as per the requirements? If not, how to achieve it? Analyse it step by step.\n2. Is the code logic completely correct? If there are errors, please indicate how to correct them.\n3. Does the existing code follow the \"Data structures and interfaces\"?\n4. Are all functions implemented? If there is no implementation, please indicate how to achieve it step by step.\n5. Have all necessary pre-dependencies been imported? If not, indicate which ones need to be imported\n6. Are methods from other files being reused correctly?\n\n## Actions: Ordered List. Things that should be done after CR, such as implementing class A and function B\n\n## Code Review Result: str. If the code doesn't have bugs, we don't need to rewrite it, so answer LGTM and stop. ONLY ANSWER LGTM/LBTM.\nLGTM/LBTM\n\n": "## Code Review: \n1. Yes, the code is implemented as per the requirements.\n2. Yes, the code logic is completely correct.\n3. Yes, the existing code follows the \"Data structures and interfaces\".\n4. Yes, the function is implemented.\n5. Yes, all necessary pre-dependencies have been imported.\n6. N/A\n\n## Code Review Result\nLGTM", + "\n## context\n\n### Legacy Content\n{\"Implementation approach\":\"We will analyze the difficult points of the requirements and select the appropriate open-source framework to develop the search engine. We will also integrate a large language model to provide intelligent summarization of search results.\",\"File list\":[\"main.py\",\"search_engine.py\",\"index.py\",\"ranking.py\",\"summary.py\",\"knowledge_base.py\"],\"Data structures and interfaces\":\"\\nclassDiagram\\n class Main {\\n -SearchEngine search_engine\\n +main() str\\n }\\n class SearchEngine {\\n -Index index\\n -Ranking ranking\\n -Summary summary\\n +search(query: str) str\\n }\\n class Index {\\n -KnowledgeBase knowledge_base\\n +create_index(data: dict)\\n +query_index(query: str) list\\n }\\n class Ranking {\\n +rank_results(results: list) list\\n }\\n class Summary {\\n +summarize_results(results: list) str\\n }\\n class KnowledgeBase {\\n +update(data: dict)\\n +fetch_data(query: str) dict\\n }\\n Main --> SearchEngine\\n SearchEngine --> Index\\n SearchEngine --> Ranking\\n SearchEngine --> Summary\\n Index --> KnowledgeBase\\n\",\"Program call flow\":\"\\nsequenceDiagram\\n participant M as Main\\n participant SE as SearchEngine\\n participant I as Index\\n participant R as Ranking\\n participant S as Summary\\n participant KB as KnowledgeBase\\n M->>SE: search(query)\\n SE->>I: query_index(query)\\n I->>KB: fetch_data(query)\\n KB-->>I: return data\\n I-->>SE: return results\\n SE->>R: rank_results(results)\\n R-->>SE: return ranked_results\\n SE->>S: summarize_results(ranked_results)\\n S-->>SE: return summary\\n SE-->>M: return summary\\n\",\"Anything UNCLEAR\":\"Clarification needed on third-party API integration, optimization techniques, and security measures.\"}\n\n### New Requirements\n## 原始需求\n```python\n\"\"\"\n我们希望开发一个基于大语言模型与私有知识库的搜索引擎。该搜索引擎应当能根据用户输入的查询进行智能搜索,并基于大语言模型对搜索结果进行总结,以便用户能够快速获取他们所需要的信息。该搜索引擎应当能够处理大规模的数据,同时保持搜索结果的准确性和相关性。我们希望这个产品能够降低用户在查找、筛选和理解信息时的工作负担,提高他们的工作效率。\n\"\"\"\n```\n\n## 产品目标\n```python\n[\n \"提供高准确性、高相关性的搜索结果,满足用户的查询需求\",\n \"基于大语言模型对搜索结果进行智能总结,帮助用户快速获取所需信息\",\n \"处理大规模数据,保证搜索的速度和效率,提高用户的工作效率\"\n]\n```\n\n## 用户故事\n```python\n[\n \"假设用户是一名研究员,他正在为一项关于全球气候变化的报告做研究。他输入了'全球气候变化的最新研究',我们的搜索引擎快速返回了相关的文章、报告、数据集等。并且基于大语言模型对这些信息进行了智能总结,研究员可以快速了解到最新的研究趋势和发现。\",\n \"用户是一名学生,正在为即将到来的历史考试复习。他输入了'二战的主要战役',搜索引擎返回了相关的资料,大语言模型总结出主要战役的时间、地点、结果等关键信息,帮助学生快速记忆。\",\n \"用户是一名企业家,他正在寻找关于最新的市场趋势信息。他输入了'2023年人工智能市场趋势',搜索引擎返回了各种报告、新闻和分析文章。大语言模型对这些信息进行了总结,用户能够快速了解到市场的最新动态和趋势。\"\n]\n```\n\n## 竞品分析\n```python\n[\n \"Google Search:Google搜索是市场上最主要的搜索引擎,它能够提供海量的搜索结果。但Google搜索并不提供搜索结果的总结功能,用户需要自己去阅读和理解搜索结果。\",\n \"Microsoft Bing:Bing搜索也能提供丰富的搜索结果,同样没有提供搜索结果的总结功能。\",\n \"Wolfram Alpha:Wolfram Alpha是一个基于知识库的计算型搜索引擎,能够针对某些特定类型的查询提供直接的答案和总结,但它的知识库覆盖范围有限,无法处理大规模的数据。\"\n]\n```\n\n## 开发需求池\n```python\n[\n (\"开发基于大语言模型的智能总结功能\", 5),\n (\"开发搜索引擎核心算法,包括索引构建、查询处理、结果排序等\", 7),\n (\"设计和实现用户界面,包括查询输入、搜索结果展示、总结结果展示等\", 3),\n (\"构建和维护私有知识库,包括数据采集、清洗、更新等\", 7),\n (\"优化搜索引擎性能,包括搜索速度、准确性、相关性等\", 6),\n (\"开发用户反馈机制,包括反馈界面、反馈处理等\", 2),\n (\"开发安全防护机制,防止恶意查询和攻击\", 3),\n (\"集成大语言模型,包括模型选择、优化、更新等\", 5),\n (\"进行大规模的测试,包括功能测试、性能测试、压力测试等\", 5),\n (\"开发数据监控和日志系统,用于监控搜索引擎的运行状态和性能\", 4)\n]\n```\n\n\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Refined Implementation Approach\": \"We will refine ...\",\n \"Refined File list\": [\n \"main.py\",\n \"game.py\",\n \"new_feature.py\"\n ],\n \"Refined Data structures and interfaces\": \"\\nclassDiagram\\n class Main {\\n -SearchEngine search_engine\\n +main() str\\n }\\n class SearchEngine {\\n -Index index\\n -Ranking ranking\\n -Summary summary\\n +search(query: str) str\\n }\\n class Index {\\n -KnowledgeBase knowledge_base\\n +create_index(data: dict)\\n +query_index(query: str) list\\n }\\n class Ranking {\\n +rank_results(results: list) list\\n }\\n class Summary {\\n +summarize_results(results: list) str\\n }\\n class KnowledgeBase {\\n +update(data: dict)\\n +fetch_data(query: str) dict\\n }\\n Main --> SearchEngine\\n SearchEngine --> Index\\n SearchEngine --> Ranking\\n SearchEngine --> Summary\\n Index --> KnowledgeBase\\n\",\n \"Refined Program call flow\": \"\\nsequenceDiagram\\n participant M as Main\\n participant SE as SearchEngine\\n participant I as Index\\n participant R as Ranking\\n participant S as Summary\\n participant KB as KnowledgeBase\\n M->>SE: search(query)\\n SE->>I: query_index(query)\\n I->>KB: fetch_data(query)\\n KB-->>I: return data\\n I-->>SE: return results\\n SE->>R: rank_results(results)\\n R-->>SE: return ranked_results\\n SE->>S: summarize_results(ranked_results)\\n S-->>SE: return summary\\n SE-->>M: return summary\\n\",\n \"Anything UNCLEAR\": \"Clarification needed on third-party API integration, ...\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Refined Implementation Approach: # Update and extend the original implementation approach to reflect the evolving challenges and requirements due to incremental development. Outline the steps involved in the implementation process with the detailed strategies.\n- Refined File list: typing.List[str] # Update and expand the original file list including only relative paths. Up to 2 files can be added.Ensure that the refined file list reflects the evolving structure of the project.\n- Refined Data structures and interfaces: # Update and extend the existing mermaid classDiagram code syntax to incorporate new classes, methods (including __init__), and functions with precise type annotations. Delineate additional relationships between classes, ensuring clarity and adherence to PEP8 standards.Retain content that is not related to incremental development but important for consistency and clarity.\n- Refined Program call flow: # Extend the existing sequenceDiagram code syntax with detailed information, accurately covering theCRUD and initialization of each object. Ensure correct syntax usage and reflect the incremental changes introducedin the classes and API defined above. Retain content that is not related to incremental development but important for consistency and clarity.\n- Anything UNCLEAR: # Mention unclear project aspects, then try to clarify it.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Refined Implementation Approach\": \"We will refine the existing implementation approach to incorporate the new requirements for developing a search engine based on a large language model and a private knowledge base. We will update and extend the original implementation approach to reflect the evolving challenges and requirements due to incremental development. We will outline the steps involved in the implementation process with detailed strategies to ensure the search engine meets the new goals of providing high-accuracy and high-relevance search results, intelligent summarization of search results, and efficient handling of large-scale data.\",\n \"Refined File list\": [\n \"main.py\",\n \"search_engine.py\",\n \"index.py\",\n \"ranking.py\",\n \"summary.py\",\n \"knowledge_base.py\",\n \"user_interface.py\"\n ],\n \"Refined Data structures and interfaces\": \"\\nclassDiagram\\n class Main {\\n -SearchEngine search_engine\\n -UserInterface user_interface\\n +main() str\\n }\\n class SearchEngine {\\n -Index index\\n -Ranking ranking\\n -Summary summary\\n -KnowledgeBase knowledge_base\\n +search(query: str) str\\n }\\n class Index {\\n +create_index(data: dict)\\n +query_index(query: str) list\\n }\\n class Ranking {\\n +rank_results(results: list) list\\n }\\n class Summary {\\n +summarize_results(results: list) str\\n }\\n class KnowledgeBase {\\n +update(data: dict)\\n +fetch_data(query: str) dict\\n }\\n class UserInterface {\\n +display_search_results(results: str)\\n +display_summary(summary: str)\\n }\\n Main --> SearchEngine\\n Main --> UserInterface\\n SearchEngine --> Index\\n SearchEngine --> Ranking\\n SearchEngine --> Summary\\n SearchEngine --> KnowledgeBase\\n\",\n \"Refined Program call flow\": \"\\nsequenceDiagram\\n participant M as Main\\n participant SE as SearchEngine\\n participant UI as UserInterface\\n participant I as Index\\n participant R as Ranking\\n participant S as Summary\\n participant KB as KnowledgeBase\\n M->>UI: main()\\n UI->>M: return results\\n M->>SE: search(query)\\n SE->>I: query_index(query)\\n I->>KB: fetch_data(query)\\n KB-->>I: return data\\n I-->>SE: return results\\n SE->>R: rank_results(results)\\n R-->>SE: return ranked_results\\n SE->>S: summarize_results(ranked_results)\\n S-->>SE: return summary\\n SE->>UI: display_search_results(results)\\n UI->>SE: display_summary(summary)\\n\",\n \"Anything UNCLEAR\": \"Clarification needed on third-party API integration, optimization techniques, and security measures.\"\n}\n[/CONTENT]", + "\nNOTICE\nRole: You are a professional engineer; the main goal is to write google-style, elegant, modular, easy to read and maintain code\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## Design\n## Project name\n```python\n\"smart_search_engine\"\n```\n\n## Task list:\n```python\n[\n \"smart_search_engine/__init__.py\",\n \"smart_search_engine/main.py\",\n \"smart_search_engine/search.py\",\n \"smart_search_engine/index.py\",\n \"smart_search_engine/ranking.py\",\n \"smart_search_engine/summary.py\",\n \"smart_search_engine/knowledge_base.py\",\n \"smart_search_engine/interface.py\",\n \"smart_search_engine/user_feedback.py\",\n \"smart_search_engine/security.py\",\n \"smart_search_engine/testing.py\",\n \"smart_search_engine/monitoring.py\"\n]\n```\n\n## Data structures and interfaces\n```mermaid\nclassDiagram\n class Main {\n -SearchEngine search_engine\n +main() str\n }\n class SearchEngine {\n -Index index\n -Ranking ranking\n -Summary summary\n +search(query: str) str\n }\n class Index {\n -KnowledgeBase knowledge_base\n +create_index(data: dict)\n +query_index(query: str) list\n }\n class Ranking {\n +rank_results(results: list) list\n }\n class Summary {\n +summarize_results(results: list) str\n }\n class KnowledgeBase {\n +update(data: dict)\n +fetch_data(query: str) dict\n }\n Main --> SearchEngine\n SearchEngine --> Index\n SearchEngine --> Ranking\n SearchEngine --> Summary\n Index --> KnowledgeBase\n```\n\n## Program call flow\n```mermaid\nsequenceDiagram\n participant M as Main\n participant SE as SearchEngine\n participant I as Index\n participant R as Ranking\n participant S as Summary\n participant KB as KnowledgeBase\n M->>SE: search(query)\n SE->>I: query_index(query)\n I->>KB: fetch_data(query)\n KB-->>I: return data\n I-->>SE: return results\n SE->>R: rank_results(results)\n R-->>SE: return ranked_results\n SE->>S: summarize_results(ranked_results)\n S-->>SE: return summary\n SE-->>M: return summary\n```\n\n\n## Task\n{\"Logic Analysis\": \"\\n 在这个项目中,所有的模块都依赖于“SearchEngine”类,这是主入口,其他的模块(Index、Ranking和Summary)都通过它交互。另外,\\\"Index\\\"类又依赖于\\\"KnowledgeBase\\\"类,因为它需要从知识库中获取数据。\\n\\n- \\\"main.py\\\"包含\\\"Main\\\"类,是程序的入口点,它调用\\\"SearchEngine\\\"进行搜索操作,所以在其他任何模块之前,\\\"SearchEngine\\\"必须首先被定义。\\n- \\\"search.py\\\"定义了\\\"SearchEngine\\\"类,它依赖于\\\"Index\\\"、\\\"Ranking\\\"和\\\"Summary\\\",因此,这些模块需要在\\\"search.py\\\"之前定义。\\n- \\\"index.py\\\"定义了\\\"Index\\\"类,它从\\\"knowledge_base.py\\\"获取数据来创建索引,所以\\\"knowledge_base.py\\\"需要在\\\"index.py\\\"之前定义。\\n- \\\"ranking.py\\\"和\\\"summary.py\\\"相对独立,只需确保在\\\"search.py\\\"之前定义。\\n- \\\"knowledge_base.py\\\"是独立的模块,可以优先开发。\\n- \\\"interface.py\\\"、\\\"user_feedback.py\\\"、\\\"security.py\\\"、\\\"testing.py\\\"和\\\"monitoring.py\\\"看起来像是功能辅助模块,可以在主要功能模块开发完成后并行开发。\\n \", \"Task list\": [\"smart_search_engine/knowledge_base.py\", \"smart_search_engine/index.py\", \"smart_search_engine/ranking.py\", \"smart_search_engine/summary.py\", \"smart_search_engine/search.py\", \"smart_search_engine/main.py\", \"smart_search_engine/interface.py\", \"smart_search_engine/user_feedback.py\", \"smart_search_engine/security.py\", \"smart_search_engine/testing.py\", \"smart_search_engine/monitoring.py\"]}\n\n## Legacy Code\n```Code\n\n```\n\n## Debug logs\n```text\n\n\n\n```\n\n## Bug Feedback logs\n```text\n\n```\n\n# Format example\n## Code: smart_search_engine/knowledge_base.py\n```python\n## smart_search_engine/knowledge_base.py\n...\n```\n\n# Instruction: Based on the context, follow \"Format example\", write code.\n\n## Code: smart_search_engine/knowledge_base.py. Write code with triple quoto, based on the following attentions and context.\n1. Only One file: do your best to implement THIS ONLY ONE FILE.\n2. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets.\n3. Set default value: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. AVOID circular import.\n4. Follow design: YOU MUST FOLLOW \"Data structures and interfaces\". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design.\n5. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE.\n6. Before using a external variable/module, make sure you import it first.\n7. Write out EVERY CODE DETAIL, DON'T LEAVE TODO.\n\n": "```python\n## smart_search_engine/knowledge_base.py\n\nclass KnowledgeBase:\n def __init__(self, data={}):\n self.data = data\n\n def update(self, data: dict):\n \"\"\"\n Update the knowledge base with new data.\n \n Args:\n data (dict): The new data to be added to the knowledge base.\n \"\"\"\n self.data.update(data)\n\n def fetch_data(self, query: str) -> dict:\n \"\"\"\n Fetch data from the knowledge base based on the query.\n \n Args:\n query (str): The query to search for in the knowledge base.\n \n Returns:\n dict: The data fetched from the knowledge base based on the query.\n \"\"\"\n return self.data.get(query, {})\n```", + "\nNOTICE\nRole: You are a professional engineer; the main goal is to write google-style, elegant, modular, easy to read and maintain code\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## Design\n## Project name\n```python\n\"smart_search_engine\"\n```\n\n## Task list:\n```python\n[\n \"smart_search_engine/__init__.py\",\n \"smart_search_engine/main.py\",\n \"smart_search_engine/search.py\",\n \"smart_search_engine/index.py\",\n \"smart_search_engine/ranking.py\",\n \"smart_search_engine/summary.py\",\n \"smart_search_engine/knowledge_base.py\",\n \"smart_search_engine/interface.py\",\n \"smart_search_engine/user_feedback.py\",\n \"smart_search_engine/security.py\",\n \"smart_search_engine/testing.py\",\n \"smart_search_engine/monitoring.py\"\n]\n```\n\n## Data structures and interfaces\n```mermaid\nclassDiagram\n class Main {\n -SearchEngine search_engine\n +main() str\n }\n class SearchEngine {\n -Index index\n -Ranking ranking\n -Summary summary\n +search(query: str) str\n }\n class Index {\n -KnowledgeBase knowledge_base\n +create_index(data: dict)\n +query_index(query: str) list\n }\n class Ranking {\n +rank_results(results: list) list\n }\n class Summary {\n +summarize_results(results: list) str\n }\n class KnowledgeBase {\n +update(data: dict)\n +fetch_data(query: str) dict\n }\n Main --> SearchEngine\n SearchEngine --> Index\n SearchEngine --> Ranking\n SearchEngine --> Summary\n Index --> KnowledgeBase\n```\n\n## Program call flow\n```mermaid\nsequenceDiagram\n participant M as Main\n participant SE as SearchEngine\n participant I as Index\n participant R as Ranking\n participant S as Summary\n participant KB as KnowledgeBase\n M->>SE: search(query)\n SE->>I: query_index(query)\n I->>KB: fetch_data(query)\n KB-->>I: return data\n I-->>SE: return results\n SE->>R: rank_results(results)\n R-->>SE: return ranked_results\n SE->>S: summarize_results(ranked_results)\n S-->>SE: return summary\n SE-->>M: return summary\n```\n\n\n## Task\n{\"Logic Analysis\": \"\\n 在这个项目中,所有的模块都依赖于“SearchEngine”类,这是主入口,其他的模块(Index、Ranking和Summary)都通过它交互。另外,\\\"Index\\\"类又依赖于\\\"KnowledgeBase\\\"类,因为它需要从知识库中获取数据。\\n\\n- \\\"main.py\\\"包含\\\"Main\\\"类,是程序的入口点,它调用\\\"SearchEngine\\\"进行搜索操作,所以在其他任何模块之前,\\\"SearchEngine\\\"必须首先被定义。\\n- \\\"search.py\\\"定义了\\\"SearchEngine\\\"类,它依赖于\\\"Index\\\"、\\\"Ranking\\\"和\\\"Summary\\\",因此,这些模块需要在\\\"search.py\\\"之前定义。\\n- \\\"index.py\\\"定义了\\\"Index\\\"类,它从\\\"knowledge_base.py\\\"获取数据来创建索引,所以\\\"knowledge_base.py\\\"需要在\\\"index.py\\\"之前定义。\\n- \\\"ranking.py\\\"和\\\"summary.py\\\"相对独立,只需确保在\\\"search.py\\\"之前定义。\\n- \\\"knowledge_base.py\\\"是独立的模块,可以优先开发。\\n- \\\"interface.py\\\"、\\\"user_feedback.py\\\"、\\\"security.py\\\"、\\\"testing.py\\\"和\\\"monitoring.py\\\"看起来像是功能辅助模块,可以在主要功能模块开发完成后并行开发。\\n \", \"Task list\": [\"smart_search_engine/knowledge_base.py\", \"smart_search_engine/index.py\", \"smart_search_engine/ranking.py\", \"smart_search_engine/summary.py\", \"smart_search_engine/search.py\", \"smart_search_engine/main.py\", \"smart_search_engine/interface.py\", \"smart_search_engine/user_feedback.py\", \"smart_search_engine/security.py\", \"smart_search_engine/testing.py\", \"smart_search_engine/monitoring.py\"]}\n\n## Legacy Code\n```Code\n\n```\n\n## Debug logs\n```text\n\n\n\n```\n\n## Bug Feedback logs\n```text\n\n```\n\n# Format example\n## Code: smart_search_engine/index.py\n```python\n## smart_search_engine/index.py\n...\n```\n\n# Instruction: Based on the context, follow \"Format example\", write code.\n\n## Code: smart_search_engine/index.py. Write code with triple quoto, based on the following attentions and context.\n1. Only One file: do your best to implement THIS ONLY ONE FILE.\n2. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets.\n3. Set default value: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. AVOID circular import.\n4. Follow design: YOU MUST FOLLOW \"Data structures and interfaces\". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design.\n5. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE.\n6. Before using a external variable/module, make sure you import it first.\n7. Write out EVERY CODE DETAIL, DON'T LEAVE TODO.\n\n": "```python\n## smart_search_engine/index.py\n\nfrom smart_search_engine.knowledge_base import KnowledgeBase\n\nclass Index:\n def __init__(self, knowledge_base: KnowledgeBase):\n self.knowledge_base = knowledge_base\n self.index_data = {}\n\n def create_index(self, data: dict):\n self.index_data = data\n\n def query_index(self, query: str) -> list:\n if query in self.index_data:\n return self.index_data[query]\n else:\n return []\n\n```", + "\nNOTICE\nRole: You are a professional engineer; the main goal is to write google-style, elegant, modular, easy to read and maintain code\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## Design\n## Project name\n```python\n\"smart_search_engine\"\n```\n\n## Task list:\n```python\n[\n \"smart_search_engine/__init__.py\",\n \"smart_search_engine/main.py\",\n \"smart_search_engine/search.py\",\n \"smart_search_engine/index.py\",\n \"smart_search_engine/ranking.py\",\n \"smart_search_engine/summary.py\",\n \"smart_search_engine/knowledge_base.py\",\n \"smart_search_engine/interface.py\",\n \"smart_search_engine/user_feedback.py\",\n \"smart_search_engine/security.py\",\n \"smart_search_engine/testing.py\",\n \"smart_search_engine/monitoring.py\"\n]\n```\n\n## Data structures and interfaces\n```mermaid\nclassDiagram\n class Main {\n -SearchEngine search_engine\n +main() str\n }\n class SearchEngine {\n -Index index\n -Ranking ranking\n -Summary summary\n +search(query: str) str\n }\n class Index {\n -KnowledgeBase knowledge_base\n +create_index(data: dict)\n +query_index(query: str) list\n }\n class Ranking {\n +rank_results(results: list) list\n }\n class Summary {\n +summarize_results(results: list) str\n }\n class KnowledgeBase {\n +update(data: dict)\n +fetch_data(query: str) dict\n }\n Main --> SearchEngine\n SearchEngine --> Index\n SearchEngine --> Ranking\n SearchEngine --> Summary\n Index --> KnowledgeBase\n```\n\n## Program call flow\n```mermaid\nsequenceDiagram\n participant M as Main\n participant SE as SearchEngine\n participant I as Index\n participant R as Ranking\n participant S as Summary\n participant KB as KnowledgeBase\n M->>SE: search(query)\n SE->>I: query_index(query)\n I->>KB: fetch_data(query)\n KB-->>I: return data\n I-->>SE: return results\n SE->>R: rank_results(results)\n R-->>SE: return ranked_results\n SE->>S: summarize_results(ranked_results)\n S-->>SE: return summary\n SE-->>M: return summary\n```\n\n\n## Task\n{\"Logic Analysis\": \"\\n 在这个项目中,所有的模块都依赖于“SearchEngine”类,这是主入口,其他的模块(Index、Ranking和Summary)都通过它交互。另外,\\\"Index\\\"类又依赖于\\\"KnowledgeBase\\\"类,因为它需要从知识库中获取数据。\\n\\n- \\\"main.py\\\"包含\\\"Main\\\"类,是程序的入口点,它调用\\\"SearchEngine\\\"进行搜索操作,所以在其他任何模块之前,\\\"SearchEngine\\\"必须首先被定义。\\n- \\\"search.py\\\"定义了\\\"SearchEngine\\\"类,它依赖于\\\"Index\\\"、\\\"Ranking\\\"和\\\"Summary\\\",因此,这些模块需要在\\\"search.py\\\"之前定义。\\n- \\\"index.py\\\"定义了\\\"Index\\\"类,它从\\\"knowledge_base.py\\\"获取数据来创建索引,所以\\\"knowledge_base.py\\\"需要在\\\"index.py\\\"之前定义。\\n- \\\"ranking.py\\\"和\\\"summary.py\\\"相对独立,只需确保在\\\"search.py\\\"之前定义。\\n- \\\"knowledge_base.py\\\"是独立的模块,可以优先开发。\\n- \\\"interface.py\\\"、\\\"user_feedback.py\\\"、\\\"security.py\\\"、\\\"testing.py\\\"和\\\"monitoring.py\\\"看起来像是功能辅助模块,可以在主要功能模块开发完成后并行开发。\\n \", \"Task list\": [\"smart_search_engine/knowledge_base.py\", \"smart_search_engine/index.py\", \"smart_search_engine/ranking.py\", \"smart_search_engine/summary.py\", \"smart_search_engine/search.py\", \"smart_search_engine/main.py\", \"smart_search_engine/interface.py\", \"smart_search_engine/user_feedback.py\", \"smart_search_engine/security.py\", \"smart_search_engine/testing.py\", \"smart_search_engine/monitoring.py\"]}\n\n## Legacy Code\n```Code\n\n```\n\n## Debug logs\n```text\n\n\n\n```\n\n## Bug Feedback logs\n```text\n\n```\n\n# Format example\n## Code: smart_search_engine/ranking.py\n```python\n## smart_search_engine/ranking.py\n...\n```\n\n# Instruction: Based on the context, follow \"Format example\", write code.\n\n## Code: smart_search_engine/ranking.py. Write code with triple quoto, based on the following attentions and context.\n1. Only One file: do your best to implement THIS ONLY ONE FILE.\n2. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets.\n3. Set default value: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. AVOID circular import.\n4. Follow design: YOU MUST FOLLOW \"Data structures and interfaces\". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design.\n5. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE.\n6. Before using a external variable/module, make sure you import it first.\n7. Write out EVERY CODE DETAIL, DON'T LEAVE TODO.\n\n": "```python\n## smart_search_engine/ranking.py\n\nclass Ranking:\n def __init__(self):\n pass\n\n def rank_results(self, results: list) -> list:\n \"\"\"\n Rank the search results based on relevance.\n\n Args:\n results (list): List of search results.\n\n Returns:\n list: Ranked list of search results.\n \"\"\"\n # Implement ranking algorithm here\n ranked_results = self._custom_ranking_algorithm(results)\n return ranked_results\n\n def _custom_ranking_algorithm(self, results: list) -> list:\n \"\"\"\n Custom ranking algorithm to rank search results.\n\n Args:\n results (list): List of search results.\n\n Returns:\n list: Ranked list of search results.\n \"\"\"\n # Implement custom ranking algorithm here\n return results\n```", + "\nNOTICE\nRole: You are a professional engineer; the main goal is to write google-style, elegant, modular, easy to read and maintain code\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## Design\n## Project name\n```python\n\"smart_search_engine\"\n```\n\n## Task list:\n```python\n[\n \"smart_search_engine/__init__.py\",\n \"smart_search_engine/main.py\",\n \"smart_search_engine/search.py\",\n \"smart_search_engine/index.py\",\n \"smart_search_engine/ranking.py\",\n \"smart_search_engine/summary.py\",\n \"smart_search_engine/knowledge_base.py\",\n \"smart_search_engine/interface.py\",\n \"smart_search_engine/user_feedback.py\",\n \"smart_search_engine/security.py\",\n \"smart_search_engine/testing.py\",\n \"smart_search_engine/monitoring.py\"\n]\n```\n\n## Data structures and interfaces\n```mermaid\nclassDiagram\n class Main {\n -SearchEngine search_engine\n +main() str\n }\n class SearchEngine {\n -Index index\n -Ranking ranking\n -Summary summary\n +search(query: str) str\n }\n class Index {\n -KnowledgeBase knowledge_base\n +create_index(data: dict)\n +query_index(query: str) list\n }\n class Ranking {\n +rank_results(results: list) list\n }\n class Summary {\n +summarize_results(results: list) str\n }\n class KnowledgeBase {\n +update(data: dict)\n +fetch_data(query: str) dict\n }\n Main --> SearchEngine\n SearchEngine --> Index\n SearchEngine --> Ranking\n SearchEngine --> Summary\n Index --> KnowledgeBase\n```\n\n## Program call flow\n```mermaid\nsequenceDiagram\n participant M as Main\n participant SE as SearchEngine\n participant I as Index\n participant R as Ranking\n participant S as Summary\n participant KB as KnowledgeBase\n M->>SE: search(query)\n SE->>I: query_index(query)\n I->>KB: fetch_data(query)\n KB-->>I: return data\n I-->>SE: return results\n SE->>R: rank_results(results)\n R-->>SE: return ranked_results\n SE->>S: summarize_results(ranked_results)\n S-->>SE: return summary\n SE-->>M: return summary\n```\n\n\n## Task\n{\"Logic Analysis\": \"\\n 在这个项目中,所有的模块都依赖于“SearchEngine”类,这是主入口,其他的模块(Index、Ranking和Summary)都通过它交互。另外,\\\"Index\\\"类又依赖于\\\"KnowledgeBase\\\"类,因为它需要从知识库中获取数据。\\n\\n- \\\"main.py\\\"包含\\\"Main\\\"类,是程序的入口点,它调用\\\"SearchEngine\\\"进行搜索操作,所以在其他任何模块之前,\\\"SearchEngine\\\"必须首先被定义。\\n- \\\"search.py\\\"定义了\\\"SearchEngine\\\"类,它依赖于\\\"Index\\\"、\\\"Ranking\\\"和\\\"Summary\\\",因此,这些模块需要在\\\"search.py\\\"之前定义。\\n- \\\"index.py\\\"定义了\\\"Index\\\"类,它从\\\"knowledge_base.py\\\"获取数据来创建索引,所以\\\"knowledge_base.py\\\"需要在\\\"index.py\\\"之前定义。\\n- \\\"ranking.py\\\"和\\\"summary.py\\\"相对独立,只需确保在\\\"search.py\\\"之前定义。\\n- \\\"knowledge_base.py\\\"是独立的模块,可以优先开发。\\n- \\\"interface.py\\\"、\\\"user_feedback.py\\\"、\\\"security.py\\\"、\\\"testing.py\\\"和\\\"monitoring.py\\\"看起来像是功能辅助模块,可以在主要功能模块开发完成后并行开发。\\n \", \"Task list\": [\"smart_search_engine/knowledge_base.py\", \"smart_search_engine/index.py\", \"smart_search_engine/ranking.py\", \"smart_search_engine/summary.py\", \"smart_search_engine/search.py\", \"smart_search_engine/main.py\", \"smart_search_engine/interface.py\", \"smart_search_engine/user_feedback.py\", \"smart_search_engine/security.py\", \"smart_search_engine/testing.py\", \"smart_search_engine/monitoring.py\"]}\n\n## Legacy Code\n```Code\n\n```\n\n## Debug logs\n```text\n\n\n\n```\n\n## Bug Feedback logs\n```text\n\n```\n\n# Format example\n## Code: smart_search_engine/summary.py\n```python\n## smart_search_engine/summary.py\n...\n```\n\n# Instruction: Based on the context, follow \"Format example\", write code.\n\n## Code: smart_search_engine/summary.py. Write code with triple quoto, based on the following attentions and context.\n1. Only One file: do your best to implement THIS ONLY ONE FILE.\n2. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets.\n3. Set default value: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. AVOID circular import.\n4. Follow design: YOU MUST FOLLOW \"Data structures and interfaces\". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design.\n5. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE.\n6. Before using a external variable/module, make sure you import it first.\n7. Write out EVERY CODE DETAIL, DON'T LEAVE TODO.\n\n": "```python\n## smart_search_engine/summary.py\n\nclass Summary:\n def __init__(self):\n pass\n\n def summarize_results(self, results: list) -> str:\n \"\"\"\n Summarize the search results.\n\n Args:\n - results (list): List of search results.\n\n Returns:\n - str: Summarized results.\n \"\"\"\n summary = \"\"\n for result in results:\n summary += result + \"\\n\"\n return summary\n```", + "\nNOTICE\nRole: You are a professional engineer; the main goal is to write google-style, elegant, modular, easy to read and maintain code\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## Design\n## Project name\n```python\n\"smart_search_engine\"\n```\n\n## Task list:\n```python\n[\n \"smart_search_engine/__init__.py\",\n \"smart_search_engine/main.py\",\n \"smart_search_engine/search.py\",\n \"smart_search_engine/index.py\",\n \"smart_search_engine/ranking.py\",\n \"smart_search_engine/summary.py\",\n \"smart_search_engine/knowledge_base.py\",\n \"smart_search_engine/interface.py\",\n \"smart_search_engine/user_feedback.py\",\n \"smart_search_engine/security.py\",\n \"smart_search_engine/testing.py\",\n \"smart_search_engine/monitoring.py\"\n]\n```\n\n## Data structures and interfaces\n```mermaid\nclassDiagram\n class Main {\n -SearchEngine search_engine\n +main() str\n }\n class SearchEngine {\n -Index index\n -Ranking ranking\n -Summary summary\n +search(query: str) str\n }\n class Index {\n -KnowledgeBase knowledge_base\n +create_index(data: dict)\n +query_index(query: str) list\n }\n class Ranking {\n +rank_results(results: list) list\n }\n class Summary {\n +summarize_results(results: list) str\n }\n class KnowledgeBase {\n +update(data: dict)\n +fetch_data(query: str) dict\n }\n Main --> SearchEngine\n SearchEngine --> Index\n SearchEngine --> Ranking\n SearchEngine --> Summary\n Index --> KnowledgeBase\n```\n\n## Program call flow\n```mermaid\nsequenceDiagram\n participant M as Main\n participant SE as SearchEngine\n participant I as Index\n participant R as Ranking\n participant S as Summary\n participant KB as KnowledgeBase\n M->>SE: search(query)\n SE->>I: query_index(query)\n I->>KB: fetch_data(query)\n KB-->>I: return data\n I-->>SE: return results\n SE->>R: rank_results(results)\n R-->>SE: return ranked_results\n SE->>S: summarize_results(ranked_results)\n S-->>SE: return summary\n SE-->>M: return summary\n```\n\n\n## Task\n{\"Logic Analysis\": \"\\n 在这个项目中,所有的模块都依赖于“SearchEngine”类,这是主入口,其他的模块(Index、Ranking和Summary)都通过它交互。另外,\\\"Index\\\"类又依赖于\\\"KnowledgeBase\\\"类,因为它需要从知识库中获取数据。\\n\\n- \\\"main.py\\\"包含\\\"Main\\\"类,是程序的入口点,它调用\\\"SearchEngine\\\"进行搜索操作,所以在其他任何模块之前,\\\"SearchEngine\\\"必须首先被定义。\\n- \\\"search.py\\\"定义了\\\"SearchEngine\\\"类,它依赖于\\\"Index\\\"、\\\"Ranking\\\"和\\\"Summary\\\",因此,这些模块需要在\\\"search.py\\\"之前定义。\\n- \\\"index.py\\\"定义了\\\"Index\\\"类,它从\\\"knowledge_base.py\\\"获取数据来创建索引,所以\\\"knowledge_base.py\\\"需要在\\\"index.py\\\"之前定义。\\n- \\\"ranking.py\\\"和\\\"summary.py\\\"相对独立,只需确保在\\\"search.py\\\"之前定义。\\n- \\\"knowledge_base.py\\\"是独立的模块,可以优先开发。\\n- \\\"interface.py\\\"、\\\"user_feedback.py\\\"、\\\"security.py\\\"、\\\"testing.py\\\"和\\\"monitoring.py\\\"看起来像是功能辅助模块,可以在主要功能模块开发完成后并行开发。\\n \", \"Task list\": [\"smart_search_engine/knowledge_base.py\", \"smart_search_engine/index.py\", \"smart_search_engine/ranking.py\", \"smart_search_engine/summary.py\", \"smart_search_engine/search.py\", \"smart_search_engine/main.py\", \"smart_search_engine/interface.py\", \"smart_search_engine/user_feedback.py\", \"smart_search_engine/security.py\", \"smart_search_engine/testing.py\", \"smart_search_engine/monitoring.py\"]}\n\n## Legacy Code\n```Code\n\n```\n\n## Debug logs\n```text\n\n\n\n```\n\n## Bug Feedback logs\n```text\n\n```\n\n# Format example\n## Code: smart_search_engine/search.py\n```python\n## smart_search_engine/search.py\n...\n```\n\n# Instruction: Based on the context, follow \"Format example\", write code.\n\n## Code: smart_search_engine/search.py. Write code with triple quoto, based on the following attentions and context.\n1. Only One file: do your best to implement THIS ONLY ONE FILE.\n2. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets.\n3. Set default value: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. AVOID circular import.\n4. Follow design: YOU MUST FOLLOW \"Data structures and interfaces\". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design.\n5. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE.\n6. Before using a external variable/module, make sure you import it first.\n7. Write out EVERY CODE DETAIL, DON'T LEAVE TODO.\n\n": "```python\n## smart_search_engine/search.py\n\nfrom smart_search_engine.index import Index\nfrom smart_search_engine.ranking import Ranking\nfrom smart_search_engine.summary import Summary\n\nclass SearchEngine:\n def __init__(self):\n self.index = Index()\n self.ranking = Ranking()\n self.summary = Summary()\n\n def search(self, query: str) -> str:\n results = self.index.query_index(query)\n ranked_results = self.ranking.rank_results(results)\n summary = self.summary.summarize_results(ranked_results)\n return summary\n```", + "\nNOTICE\nRole: You are a professional engineer; the main goal is to write google-style, elegant, modular, easy to read and maintain code\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## Design\n## Project name\n```python\n\"smart_search_engine\"\n```\n\n## Task list:\n```python\n[\n \"smart_search_engine/__init__.py\",\n \"smart_search_engine/main.py\",\n \"smart_search_engine/search.py\",\n \"smart_search_engine/index.py\",\n \"smart_search_engine/ranking.py\",\n \"smart_search_engine/summary.py\",\n \"smart_search_engine/knowledge_base.py\",\n \"smart_search_engine/interface.py\",\n \"smart_search_engine/user_feedback.py\",\n \"smart_search_engine/security.py\",\n \"smart_search_engine/testing.py\",\n \"smart_search_engine/monitoring.py\"\n]\n```\n\n## Data structures and interfaces\n```mermaid\nclassDiagram\n class Main {\n -SearchEngine search_engine\n +main() str\n }\n class SearchEngine {\n -Index index\n -Ranking ranking\n -Summary summary\n +search(query: str) str\n }\n class Index {\n -KnowledgeBase knowledge_base\n +create_index(data: dict)\n +query_index(query: str) list\n }\n class Ranking {\n +rank_results(results: list) list\n }\n class Summary {\n +summarize_results(results: list) str\n }\n class KnowledgeBase {\n +update(data: dict)\n +fetch_data(query: str) dict\n }\n Main --> SearchEngine\n SearchEngine --> Index\n SearchEngine --> Ranking\n SearchEngine --> Summary\n Index --> KnowledgeBase\n```\n\n## Program call flow\n```mermaid\nsequenceDiagram\n participant M as Main\n participant SE as SearchEngine\n participant I as Index\n participant R as Ranking\n participant S as Summary\n participant KB as KnowledgeBase\n M->>SE: search(query)\n SE->>I: query_index(query)\n I->>KB: fetch_data(query)\n KB-->>I: return data\n I-->>SE: return results\n SE->>R: rank_results(results)\n R-->>SE: return ranked_results\n SE->>S: summarize_results(ranked_results)\n S-->>SE: return summary\n SE-->>M: return summary\n```\n\n\n## Task\n{\"Logic Analysis\": \"\\n 在这个项目中,所有的模块都依赖于“SearchEngine”类,这是主入口,其他的模块(Index、Ranking和Summary)都通过它交互。另外,\\\"Index\\\"类又依赖于\\\"KnowledgeBase\\\"类,因为它需要从知识库中获取数据。\\n\\n- \\\"main.py\\\"包含\\\"Main\\\"类,是程序的入口点,它调用\\\"SearchEngine\\\"进行搜索操作,所以在其他任何模块之前,\\\"SearchEngine\\\"必须首先被定义。\\n- \\\"search.py\\\"定义了\\\"SearchEngine\\\"类,它依赖于\\\"Index\\\"、\\\"Ranking\\\"和\\\"Summary\\\",因此,这些模块需要在\\\"search.py\\\"之前定义。\\n- \\\"index.py\\\"定义了\\\"Index\\\"类,它从\\\"knowledge_base.py\\\"获取数据来创建索引,所以\\\"knowledge_base.py\\\"需要在\\\"index.py\\\"之前定义。\\n- \\\"ranking.py\\\"和\\\"summary.py\\\"相对独立,只需确保在\\\"search.py\\\"之前定义。\\n- \\\"knowledge_base.py\\\"是独立的模块,可以优先开发。\\n- \\\"interface.py\\\"、\\\"user_feedback.py\\\"、\\\"security.py\\\"、\\\"testing.py\\\"和\\\"monitoring.py\\\"看起来像是功能辅助模块,可以在主要功能模块开发完成后并行开发。\\n \", \"Task list\": [\"smart_search_engine/knowledge_base.py\", \"smart_search_engine/index.py\", \"smart_search_engine/ranking.py\", \"smart_search_engine/summary.py\", \"smart_search_engine/search.py\", \"smart_search_engine/main.py\", \"smart_search_engine/interface.py\", \"smart_search_engine/user_feedback.py\", \"smart_search_engine/security.py\", \"smart_search_engine/testing.py\", \"smart_search_engine/monitoring.py\"]}\n\n## Legacy Code\n```Code\n\n```\n\n## Debug logs\n```text\n\n\n\n```\n\n## Bug Feedback logs\n```text\n\n```\n\n# Format example\n## Code: smart_search_engine/main.py\n```python\n## smart_search_engine/main.py\n...\n```\n\n# Instruction: Based on the context, follow \"Format example\", write code.\n\n## Code: smart_search_engine/main.py. Write code with triple quoto, based on the following attentions and context.\n1. Only One file: do your best to implement THIS ONLY ONE FILE.\n2. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets.\n3. Set default value: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. AVOID circular import.\n4. Follow design: YOU MUST FOLLOW \"Data structures and interfaces\". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design.\n5. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE.\n6. Before using a external variable/module, make sure you import it first.\n7. Write out EVERY CODE DETAIL, DON'T LEAVE TODO.\n\n": "```python\n## smart_search_engine/main.py\n\nfrom smart_search_engine.search import SearchEngine\n\nclass Main:\n def __init__(self):\n self.search_engine = SearchEngine()\n\n def main(self) -> str:\n query = input(\"Enter your search query: \")\n result = self.search_engine.search(query)\n return result\n```", + "\nNOTICE\nRole: You are a professional engineer; the main goal is to write google-style, elegant, modular, easy to read and maintain code\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## Design\n## Project name\n```python\n\"smart_search_engine\"\n```\n\n## Task list:\n```python\n[\n \"smart_search_engine/__init__.py\",\n \"smart_search_engine/main.py\",\n \"smart_search_engine/search.py\",\n \"smart_search_engine/index.py\",\n \"smart_search_engine/ranking.py\",\n \"smart_search_engine/summary.py\",\n \"smart_search_engine/knowledge_base.py\",\n \"smart_search_engine/interface.py\",\n \"smart_search_engine/user_feedback.py\",\n \"smart_search_engine/security.py\",\n \"smart_search_engine/testing.py\",\n \"smart_search_engine/monitoring.py\"\n]\n```\n\n## Data structures and interfaces\n```mermaid\nclassDiagram\n class Main {\n -SearchEngine search_engine\n +main() str\n }\n class SearchEngine {\n -Index index\n -Ranking ranking\n -Summary summary\n +search(query: str) str\n }\n class Index {\n -KnowledgeBase knowledge_base\n +create_index(data: dict)\n +query_index(query: str) list\n }\n class Ranking {\n +rank_results(results: list) list\n }\n class Summary {\n +summarize_results(results: list) str\n }\n class KnowledgeBase {\n +update(data: dict)\n +fetch_data(query: str) dict\n }\n Main --> SearchEngine\n SearchEngine --> Index\n SearchEngine --> Ranking\n SearchEngine --> Summary\n Index --> KnowledgeBase\n```\n\n## Program call flow\n```mermaid\nsequenceDiagram\n participant M as Main\n participant SE as SearchEngine\n participant I as Index\n participant R as Ranking\n participant S as Summary\n participant KB as KnowledgeBase\n M->>SE: search(query)\n SE->>I: query_index(query)\n I->>KB: fetch_data(query)\n KB-->>I: return data\n I-->>SE: return results\n SE->>R: rank_results(results)\n R-->>SE: return ranked_results\n SE->>S: summarize_results(ranked_results)\n S-->>SE: return summary\n SE-->>M: return summary\n```\n\n\n## Task\n{\"Logic Analysis\": \"\\n 在这个项目中,所有的模块都依赖于“SearchEngine”类,这是主入口,其他的模块(Index、Ranking和Summary)都通过它交互。另外,\\\"Index\\\"类又依赖于\\\"KnowledgeBase\\\"类,因为它需要从知识库中获取数据。\\n\\n- \\\"main.py\\\"包含\\\"Main\\\"类,是程序的入口点,它调用\\\"SearchEngine\\\"进行搜索操作,所以在其他任何模块之前,\\\"SearchEngine\\\"必须首先被定义。\\n- \\\"search.py\\\"定义了\\\"SearchEngine\\\"类,它依赖于\\\"Index\\\"、\\\"Ranking\\\"和\\\"Summary\\\",因此,这些模块需要在\\\"search.py\\\"之前定义。\\n- \\\"index.py\\\"定义了\\\"Index\\\"类,它从\\\"knowledge_base.py\\\"获取数据来创建索引,所以\\\"knowledge_base.py\\\"需要在\\\"index.py\\\"之前定义。\\n- \\\"ranking.py\\\"和\\\"summary.py\\\"相对独立,只需确保在\\\"search.py\\\"之前定义。\\n- \\\"knowledge_base.py\\\"是独立的模块,可以优先开发。\\n- \\\"interface.py\\\"、\\\"user_feedback.py\\\"、\\\"security.py\\\"、\\\"testing.py\\\"和\\\"monitoring.py\\\"看起来像是功能辅助模块,可以在主要功能模块开发完成后并行开发。\\n \", \"Task list\": [\"smart_search_engine/knowledge_base.py\", \"smart_search_engine/index.py\", \"smart_search_engine/ranking.py\", \"smart_search_engine/summary.py\", \"smart_search_engine/search.py\", \"smart_search_engine/main.py\", \"smart_search_engine/interface.py\", \"smart_search_engine/user_feedback.py\", \"smart_search_engine/security.py\", \"smart_search_engine/testing.py\", \"smart_search_engine/monitoring.py\"]}\n\n## Legacy Code\n```Code\n\n```\n\n## Debug logs\n```text\n\n\n\n```\n\n## Bug Feedback logs\n```text\n\n```\n\n# Format example\n## Code: smart_search_engine/interface.py\n```python\n## smart_search_engine/interface.py\n...\n```\n\n# Instruction: Based on the context, follow \"Format example\", write code.\n\n## Code: smart_search_engine/interface.py. Write code with triple quoto, based on the following attentions and context.\n1. Only One file: do your best to implement THIS ONLY ONE FILE.\n2. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets.\n3. Set default value: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. AVOID circular import.\n4. Follow design: YOU MUST FOLLOW \"Data structures and interfaces\". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design.\n5. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE.\n6. Before using a external variable/module, make sure you import it first.\n7. Write out EVERY CODE DETAIL, DON'T LEAVE TODO.\n\n": "```python\n## smart_search_engine/interface.py\n\nfrom .search import SearchEngine\n\nclass UserInterface:\n def __init__(self):\n self.search_engine = SearchEngine()\n\n def get_user_input(self) -> str:\n # Get user input from the interface\n user_input = input(\"Enter your search query: \")\n return user_input\n\n def display_results(self, results: list):\n # Display search results to the user\n for result in results:\n print(result)\n\n# Ensure to follow the design and import necessary modules as needed.\n```", + "\nNOTICE\nRole: You are a professional engineer; the main goal is to write google-style, elegant, modular, easy to read and maintain code\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## Design\n## Project name\n```python\n\"smart_search_engine\"\n```\n\n## Task list:\n```python\n[\n \"smart_search_engine/__init__.py\",\n \"smart_search_engine/main.py\",\n \"smart_search_engine/search.py\",\n \"smart_search_engine/index.py\",\n \"smart_search_engine/ranking.py\",\n \"smart_search_engine/summary.py\",\n \"smart_search_engine/knowledge_base.py\",\n \"smart_search_engine/interface.py\",\n \"smart_search_engine/user_feedback.py\",\n \"smart_search_engine/security.py\",\n \"smart_search_engine/testing.py\",\n \"smart_search_engine/monitoring.py\"\n]\n```\n\n## Data structures and interfaces\n```mermaid\nclassDiagram\n class Main {\n -SearchEngine search_engine\n +main() str\n }\n class SearchEngine {\n -Index index\n -Ranking ranking\n -Summary summary\n +search(query: str) str\n }\n class Index {\n -KnowledgeBase knowledge_base\n +create_index(data: dict)\n +query_index(query: str) list\n }\n class Ranking {\n +rank_results(results: list) list\n }\n class Summary {\n +summarize_results(results: list) str\n }\n class KnowledgeBase {\n +update(data: dict)\n +fetch_data(query: str) dict\n }\n Main --> SearchEngine\n SearchEngine --> Index\n SearchEngine --> Ranking\n SearchEngine --> Summary\n Index --> KnowledgeBase\n```\n\n## Program call flow\n```mermaid\nsequenceDiagram\n participant M as Main\n participant SE as SearchEngine\n participant I as Index\n participant R as Ranking\n participant S as Summary\n participant KB as KnowledgeBase\n M->>SE: search(query)\n SE->>I: query_index(query)\n I->>KB: fetch_data(query)\n KB-->>I: return data\n I-->>SE: return results\n SE->>R: rank_results(results)\n R-->>SE: return ranked_results\n SE->>S: summarize_results(ranked_results)\n S-->>SE: return summary\n SE-->>M: return summary\n```\n\n\n## Task\n{\"Logic Analysis\": \"\\n 在这个项目中,所有的模块都依赖于“SearchEngine”类,这是主入口,其他的模块(Index、Ranking和Summary)都通过它交互。另外,\\\"Index\\\"类又依赖于\\\"KnowledgeBase\\\"类,因为它需要从知识库中获取数据。\\n\\n- \\\"main.py\\\"包含\\\"Main\\\"类,是程序的入口点,它调用\\\"SearchEngine\\\"进行搜索操作,所以在其他任何模块之前,\\\"SearchEngine\\\"必须首先被定义。\\n- \\\"search.py\\\"定义了\\\"SearchEngine\\\"类,它依赖于\\\"Index\\\"、\\\"Ranking\\\"和\\\"Summary\\\",因此,这些模块需要在\\\"search.py\\\"之前定义。\\n- \\\"index.py\\\"定义了\\\"Index\\\"类,它从\\\"knowledge_base.py\\\"获取数据来创建索引,所以\\\"knowledge_base.py\\\"需要在\\\"index.py\\\"之前定义。\\n- \\\"ranking.py\\\"和\\\"summary.py\\\"相对独立,只需确保在\\\"search.py\\\"之前定义。\\n- \\\"knowledge_base.py\\\"是独立的模块,可以优先开发。\\n- \\\"interface.py\\\"、\\\"user_feedback.py\\\"、\\\"security.py\\\"、\\\"testing.py\\\"和\\\"monitoring.py\\\"看起来像是功能辅助模块,可以在主要功能模块开发完成后并行开发。\\n \", \"Task list\": [\"smart_search_engine/knowledge_base.py\", \"smart_search_engine/index.py\", \"smart_search_engine/ranking.py\", \"smart_search_engine/summary.py\", \"smart_search_engine/search.py\", \"smart_search_engine/main.py\", \"smart_search_engine/interface.py\", \"smart_search_engine/user_feedback.py\", \"smart_search_engine/security.py\", \"smart_search_engine/testing.py\", \"smart_search_engine/monitoring.py\"]}\n\n## Legacy Code\n```Code\n\n```\n\n## Debug logs\n```text\n\n\n\n```\n\n## Bug Feedback logs\n```text\n\n```\n\n# Format example\n## Code: smart_search_engine/user_feedback.py\n```python\n## smart_search_engine/user_feedback.py\n...\n```\n\n# Instruction: Based on the context, follow \"Format example\", write code.\n\n## Code: smart_search_engine/user_feedback.py. Write code with triple quoto, based on the following attentions and context.\n1. Only One file: do your best to implement THIS ONLY ONE FILE.\n2. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets.\n3. Set default value: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. AVOID circular import.\n4. Follow design: YOU MUST FOLLOW \"Data structures and interfaces\". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design.\n5. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE.\n6. Before using a external variable/module, make sure you import it first.\n7. Write out EVERY CODE DETAIL, DON'T LEAVE TODO.\n\n": "```python\n## smart_search_engine/user_feedback.py\n\nclass UserFeedback:\n def __init__(self):\n self.feedback_data = {}\n\n def collect_feedback(self, feedback: str):\n self.feedback_data['feedback'] = feedback\n\n def get_feedback(self):\n return self.feedback_data.get('feedback', 'No feedback provided')\n\n# Code details:\n# - Created a class UserFeedback to handle user feedback.\n# - Initialized feedback_data as an empty dictionary in the constructor.\n# - Implemented collect_feedback method to store user feedback in the feedback_data dictionary.\n# - Implemented get_feedback method to retrieve the stored feedback, with a default message if no feedback is provided.\n```", + "\nNOTICE\nRole: You are a professional engineer; the main goal is to write google-style, elegant, modular, easy to read and maintain code\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## Design\n## Project name\n```python\n\"smart_search_engine\"\n```\n\n## Task list:\n```python\n[\n \"smart_search_engine/__init__.py\",\n \"smart_search_engine/main.py\",\n \"smart_search_engine/search.py\",\n \"smart_search_engine/index.py\",\n \"smart_search_engine/ranking.py\",\n \"smart_search_engine/summary.py\",\n \"smart_search_engine/knowledge_base.py\",\n \"smart_search_engine/interface.py\",\n \"smart_search_engine/user_feedback.py\",\n \"smart_search_engine/security.py\",\n \"smart_search_engine/testing.py\",\n \"smart_search_engine/monitoring.py\"\n]\n```\n\n## Data structures and interfaces\n```mermaid\nclassDiagram\n class Main {\n -SearchEngine search_engine\n +main() str\n }\n class SearchEngine {\n -Index index\n -Ranking ranking\n -Summary summary\n +search(query: str) str\n }\n class Index {\n -KnowledgeBase knowledge_base\n +create_index(data: dict)\n +query_index(query: str) list\n }\n class Ranking {\n +rank_results(results: list) list\n }\n class Summary {\n +summarize_results(results: list) str\n }\n class KnowledgeBase {\n +update(data: dict)\n +fetch_data(query: str) dict\n }\n Main --> SearchEngine\n SearchEngine --> Index\n SearchEngine --> Ranking\n SearchEngine --> Summary\n Index --> KnowledgeBase\n```\n\n## Program call flow\n```mermaid\nsequenceDiagram\n participant M as Main\n participant SE as SearchEngine\n participant I as Index\n participant R as Ranking\n participant S as Summary\n participant KB as KnowledgeBase\n M->>SE: search(query)\n SE->>I: query_index(query)\n I->>KB: fetch_data(query)\n KB-->>I: return data\n I-->>SE: return results\n SE->>R: rank_results(results)\n R-->>SE: return ranked_results\n SE->>S: summarize_results(ranked_results)\n S-->>SE: return summary\n SE-->>M: return summary\n```\n\n\n## Task\n{\"Logic Analysis\": \"\\n 在这个项目中,所有的模块都依赖于“SearchEngine”类,这是主入口,其他的模块(Index、Ranking和Summary)都通过它交互。另外,\\\"Index\\\"类又依赖于\\\"KnowledgeBase\\\"类,因为它需要从知识库中获取数据。\\n\\n- \\\"main.py\\\"包含\\\"Main\\\"类,是程序的入口点,它调用\\\"SearchEngine\\\"进行搜索操作,所以在其他任何模块之前,\\\"SearchEngine\\\"必须首先被定义。\\n- \\\"search.py\\\"定义了\\\"SearchEngine\\\"类,它依赖于\\\"Index\\\"、\\\"Ranking\\\"和\\\"Summary\\\",因此,这些模块需要在\\\"search.py\\\"之前定义。\\n- \\\"index.py\\\"定义了\\\"Index\\\"类,它从\\\"knowledge_base.py\\\"获取数据来创建索引,所以\\\"knowledge_base.py\\\"需要在\\\"index.py\\\"之前定义。\\n- \\\"ranking.py\\\"和\\\"summary.py\\\"相对独立,只需确保在\\\"search.py\\\"之前定义。\\n- \\\"knowledge_base.py\\\"是独立的模块,可以优先开发。\\n- \\\"interface.py\\\"、\\\"user_feedback.py\\\"、\\\"security.py\\\"、\\\"testing.py\\\"和\\\"monitoring.py\\\"看起来像是功能辅助模块,可以在主要功能模块开发完成后并行开发。\\n \", \"Task list\": [\"smart_search_engine/knowledge_base.py\", \"smart_search_engine/index.py\", \"smart_search_engine/ranking.py\", \"smart_search_engine/summary.py\", \"smart_search_engine/search.py\", \"smart_search_engine/main.py\", \"smart_search_engine/interface.py\", \"smart_search_engine/user_feedback.py\", \"smart_search_engine/security.py\", \"smart_search_engine/testing.py\", \"smart_search_engine/monitoring.py\"]}\n\n## Legacy Code\n```Code\n\n```\n\n## Debug logs\n```text\n\n\n\n```\n\n## Bug Feedback logs\n```text\n\n```\n\n# Format example\n## Code: smart_search_engine/security.py\n```python\n## smart_search_engine/security.py\n...\n```\n\n# Instruction: Based on the context, follow \"Format example\", write code.\n\n## Code: smart_search_engine/security.py. Write code with triple quoto, based on the following attentions and context.\n1. Only One file: do your best to implement THIS ONLY ONE FILE.\n2. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets.\n3. Set default value: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. AVOID circular import.\n4. Follow design: YOU MUST FOLLOW \"Data structures and interfaces\". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design.\n5. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE.\n6. Before using a external variable/module, make sure you import it first.\n7. Write out EVERY CODE DETAIL, DON'T LEAVE TODO.\n\n": "```python\n## smart_search_engine/security.py\n\nclass Security:\n def __init__(self, api_key: str = \"default_api_key\"):\n self.api_key = api_key\n\n def validate_api_key(self, api_key: str) -> bool:\n \"\"\"\n Validate the API key.\n Args:\n api_key (str): The API key to be validated.\n\n Returns:\n bool: True if the API key is valid, False otherwise.\n \"\"\"\n if api_key == self.api_key:\n return True\n else:\n return False\n```", + "\nNOTICE\nRole: You are a professional engineer; the main goal is to write google-style, elegant, modular, easy to read and maintain code\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## Design\n## Project name\n```python\n\"smart_search_engine\"\n```\n\n## Task list:\n```python\n[\n \"smart_search_engine/__init__.py\",\n \"smart_search_engine/main.py\",\n \"smart_search_engine/search.py\",\n \"smart_search_engine/index.py\",\n \"smart_search_engine/ranking.py\",\n \"smart_search_engine/summary.py\",\n \"smart_search_engine/knowledge_base.py\",\n \"smart_search_engine/interface.py\",\n \"smart_search_engine/user_feedback.py\",\n \"smart_search_engine/security.py\",\n \"smart_search_engine/testing.py\",\n \"smart_search_engine/monitoring.py\"\n]\n```\n\n## Data structures and interfaces\n```mermaid\nclassDiagram\n class Main {\n -SearchEngine search_engine\n +main() str\n }\n class SearchEngine {\n -Index index\n -Ranking ranking\n -Summary summary\n +search(query: str) str\n }\n class Index {\n -KnowledgeBase knowledge_base\n +create_index(data: dict)\n +query_index(query: str) list\n }\n class Ranking {\n +rank_results(results: list) list\n }\n class Summary {\n +summarize_results(results: list) str\n }\n class KnowledgeBase {\n +update(data: dict)\n +fetch_data(query: str) dict\n }\n Main --> SearchEngine\n SearchEngine --> Index\n SearchEngine --> Ranking\n SearchEngine --> Summary\n Index --> KnowledgeBase\n```\n\n## Program call flow\n```mermaid\nsequenceDiagram\n participant M as Main\n participant SE as SearchEngine\n participant I as Index\n participant R as Ranking\n participant S as Summary\n participant KB as KnowledgeBase\n M->>SE: search(query)\n SE->>I: query_index(query)\n I->>KB: fetch_data(query)\n KB-->>I: return data\n I-->>SE: return results\n SE->>R: rank_results(results)\n R-->>SE: return ranked_results\n SE->>S: summarize_results(ranked_results)\n S-->>SE: return summary\n SE-->>M: return summary\n```\n\n\n## Task\n{\"Logic Analysis\": \"\\n 在这个项目中,所有的模块都依赖于“SearchEngine”类,这是主入口,其他的模块(Index、Ranking和Summary)都通过它交互。另外,\\\"Index\\\"类又依赖于\\\"KnowledgeBase\\\"类,因为它需要从知识库中获取数据。\\n\\n- \\\"main.py\\\"包含\\\"Main\\\"类,是程序的入口点,它调用\\\"SearchEngine\\\"进行搜索操作,所以在其他任何模块之前,\\\"SearchEngine\\\"必须首先被定义。\\n- \\\"search.py\\\"定义了\\\"SearchEngine\\\"类,它依赖于\\\"Index\\\"、\\\"Ranking\\\"和\\\"Summary\\\",因此,这些模块需要在\\\"search.py\\\"之前定义。\\n- \\\"index.py\\\"定义了\\\"Index\\\"类,它从\\\"knowledge_base.py\\\"获取数据来创建索引,所以\\\"knowledge_base.py\\\"需要在\\\"index.py\\\"之前定义。\\n- \\\"ranking.py\\\"和\\\"summary.py\\\"相对独立,只需确保在\\\"search.py\\\"之前定义。\\n- \\\"knowledge_base.py\\\"是独立的模块,可以优先开发。\\n- \\\"interface.py\\\"、\\\"user_feedback.py\\\"、\\\"security.py\\\"、\\\"testing.py\\\"和\\\"monitoring.py\\\"看起来像是功能辅助模块,可以在主要功能模块开发完成后并行开发。\\n \", \"Task list\": [\"smart_search_engine/knowledge_base.py\", \"smart_search_engine/index.py\", \"smart_search_engine/ranking.py\", \"smart_search_engine/summary.py\", \"smart_search_engine/search.py\", \"smart_search_engine/main.py\", \"smart_search_engine/interface.py\", \"smart_search_engine/user_feedback.py\", \"smart_search_engine/security.py\", \"smart_search_engine/testing.py\", \"smart_search_engine/monitoring.py\"]}\n\n## Legacy Code\n```Code\n\n```\n\n## Debug logs\n```text\n\n\n\n```\n\n## Bug Feedback logs\n```text\n\n```\n\n# Format example\n## Code: smart_search_engine/testing.py\n```python\n## smart_search_engine/testing.py\n...\n```\n\n# Instruction: Based on the context, follow \"Format example\", write code.\n\n## Code: smart_search_engine/testing.py. Write code with triple quoto, based on the following attentions and context.\n1. Only One file: do your best to implement THIS ONLY ONE FILE.\n2. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets.\n3. Set default value: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. AVOID circular import.\n4. Follow design: YOU MUST FOLLOW \"Data structures and interfaces\". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design.\n5. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE.\n6. Before using a external variable/module, make sure you import it first.\n7. Write out EVERY CODE DETAIL, DON'T LEAVE TODO.\n\n": "```python\n## smart_search_engine/testing.py\n\nclass Testing:\n def __init__(self, test_data: dict = {}):\n self.test_data = test_data\n\n def run_tests(self):\n # Write code to run tests based on the test_data\n pass\n```", + "\nNOTICE\nRole: You are a professional engineer; the main goal is to write google-style, elegant, modular, easy to read and maintain code\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## Design\n## Project name\n```python\n\"smart_search_engine\"\n```\n\n## Task list:\n```python\n[\n \"smart_search_engine/__init__.py\",\n \"smart_search_engine/main.py\",\n \"smart_search_engine/search.py\",\n \"smart_search_engine/index.py\",\n \"smart_search_engine/ranking.py\",\n \"smart_search_engine/summary.py\",\n \"smart_search_engine/knowledge_base.py\",\n \"smart_search_engine/interface.py\",\n \"smart_search_engine/user_feedback.py\",\n \"smart_search_engine/security.py\",\n \"smart_search_engine/testing.py\",\n \"smart_search_engine/monitoring.py\"\n]\n```\n\n## Data structures and interfaces\n```mermaid\nclassDiagram\n class Main {\n -SearchEngine search_engine\n +main() str\n }\n class SearchEngine {\n -Index index\n -Ranking ranking\n -Summary summary\n +search(query: str) str\n }\n class Index {\n -KnowledgeBase knowledge_base\n +create_index(data: dict)\n +query_index(query: str) list\n }\n class Ranking {\n +rank_results(results: list) list\n }\n class Summary {\n +summarize_results(results: list) str\n }\n class KnowledgeBase {\n +update(data: dict)\n +fetch_data(query: str) dict\n }\n Main --> SearchEngine\n SearchEngine --> Index\n SearchEngine --> Ranking\n SearchEngine --> Summary\n Index --> KnowledgeBase\n```\n\n## Program call flow\n```mermaid\nsequenceDiagram\n participant M as Main\n participant SE as SearchEngine\n participant I as Index\n participant R as Ranking\n participant S as Summary\n participant KB as KnowledgeBase\n M->>SE: search(query)\n SE->>I: query_index(query)\n I->>KB: fetch_data(query)\n KB-->>I: return data\n I-->>SE: return results\n SE->>R: rank_results(results)\n R-->>SE: return ranked_results\n SE->>S: summarize_results(ranked_results)\n S-->>SE: return summary\n SE-->>M: return summary\n```\n\n\n## Task\n{\"Logic Analysis\": \"\\n 在这个项目中,所有的模块都依赖于“SearchEngine”类,这是主入口,其他的模块(Index、Ranking和Summary)都通过它交互。另外,\\\"Index\\\"类又依赖于\\\"KnowledgeBase\\\"类,因为它需要从知识库中获取数据。\\n\\n- \\\"main.py\\\"包含\\\"Main\\\"类,是程序的入口点,它调用\\\"SearchEngine\\\"进行搜索操作,所以在其他任何模块之前,\\\"SearchEngine\\\"必须首先被定义。\\n- \\\"search.py\\\"定义了\\\"SearchEngine\\\"类,它依赖于\\\"Index\\\"、\\\"Ranking\\\"和\\\"Summary\\\",因此,这些模块需要在\\\"search.py\\\"之前定义。\\n- \\\"index.py\\\"定义了\\\"Index\\\"类,它从\\\"knowledge_base.py\\\"获取数据来创建索引,所以\\\"knowledge_base.py\\\"需要在\\\"index.py\\\"之前定义。\\n- \\\"ranking.py\\\"和\\\"summary.py\\\"相对独立,只需确保在\\\"search.py\\\"之前定义。\\n- \\\"knowledge_base.py\\\"是独立的模块,可以优先开发。\\n- \\\"interface.py\\\"、\\\"user_feedback.py\\\"、\\\"security.py\\\"、\\\"testing.py\\\"和\\\"monitoring.py\\\"看起来像是功能辅助模块,可以在主要功能模块开发完成后并行开发。\\n \", \"Task list\": [\"smart_search_engine/knowledge_base.py\", \"smart_search_engine/index.py\", \"smart_search_engine/ranking.py\", \"smart_search_engine/summary.py\", \"smart_search_engine/search.py\", \"smart_search_engine/main.py\", \"smart_search_engine/interface.py\", \"smart_search_engine/user_feedback.py\", \"smart_search_engine/security.py\", \"smart_search_engine/testing.py\", \"smart_search_engine/monitoring.py\"]}\n\n## Legacy Code\n```Code\n\n```\n\n## Debug logs\n```text\n\n\n\n```\n\n## Bug Feedback logs\n```text\n\n```\n\n# Format example\n## Code: smart_search_engine/monitoring.py\n```python\n## smart_search_engine/monitoring.py\n...\n```\n\n# Instruction: Based on the context, follow \"Format example\", write code.\n\n## Code: smart_search_engine/monitoring.py. Write code with triple quoto, based on the following attentions and context.\n1. Only One file: do your best to implement THIS ONLY ONE FILE.\n2. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets.\n3. Set default value: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. AVOID circular import.\n4. Follow design: YOU MUST FOLLOW \"Data structures and interfaces\". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design.\n5. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE.\n6. Before using a external variable/module, make sure you import it first.\n7. Write out EVERY CODE DETAIL, DON'T LEAVE TODO.\n\n": "```python\n## smart_search_engine/monitoring.py\n\nclass Monitoring:\n def __init__(self, log_file: str = \"search_log.txt\"):\n self.log_file = log_file\n\n def log_search(self, query: str, results: list, summary: str):\n with open(self.log_file, \"a\") as file:\n file.write(f\"Query: {query}\\n\")\n file.write(\"Results:\\n\")\n for result in results:\n file.write(f\"- {result}\\n\")\n file.write(f\"Summary: {summary}\\n\")\n file.write(\"\\n\")\n```\n", + "\nNOTICE\nRole: You are a professional engineer; the main goal is to write google-style, elegant, modular, easy to read and maintain code\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## Design\nwrite add function to calculate two numbers\n\n## Task\n\n\n## Legacy Code\n```Code\n\n```\n\n## Debug logs\n```text\n\n\n\n```\n\n## Bug Feedback logs\n```text\n\n```\n\n# Format example\n## Code: \n```python\n## \n...\n```\n\n# Instruction: Based on the context, follow \"Format example\", write code.\n\n## Code: . Write code with triple quoto, based on the following attentions and context.\n1. Only One file: do your best to implement THIS ONLY ONE FILE.\n2. COMPLETE CODE: Your code will be part of the entire project, so please implement complete, reliable, reusable code snippets.\n3. Set default value: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE. AVOID circular import.\n4. Follow design: YOU MUST FOLLOW \"Data structures and interfaces\". DONT CHANGE ANY DESIGN. Do not use public member functions that do not exist in your design.\n5. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE.\n6. Before using a external variable/module, make sure you import it first.\n7. Write out EVERY CODE DETAIL, DON'T LEAVE TODO.\n\n": "## Code:\n\n```python\ndef add(num1: int, num2: int) -> int:\n \"\"\"\n Add two numbers and return the result.\n\n Args:\n num1 (int): The first number to be added.\n num2 (int): The second number to be added.\n\n Returns:\n int: The sum of num1 and num2.\n \"\"\"\n return num1 + num2\n```", + "\n# System\nRole: You are a professional software engineer, and your main task is to review and revise the code. You need to ensure that the code conforms to the google-style standards, is elegantly designed and modularized, easy to read and maintain.\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## System Design\ndivide two numbers\n\n## Task\n\n\n## Code Files\n\n\n\n## Code to be Reviewed: \n```Code\n\ndef div(a: int, b: int = 0):\n return a / b\n\n```\n\n\n\n# Format example 1\n## Code Review: \n1. No, we should fix the logic of class A due to ...\n2. ...\n3. ...\n4. No, function B is not implemented, ...\n5. ...\n6. ...\n\n## Actions\n1. Fix the `handle_events` method to update the game state only if a move is successful.\n ```python\n def handle_events(self):\n for event in pygame.event.get():\n if event.type == pygame.QUIT:\n return False\n if event.type == pygame.KEYDOWN:\n moved = False\n if event.key == pygame.K_UP:\n moved = self.game.move('UP')\n elif event.key == pygame.K_DOWN:\n moved = self.game.move('DOWN')\n elif event.key == pygame.K_LEFT:\n moved = self.game.move('LEFT')\n elif event.key == pygame.K_RIGHT:\n moved = self.game.move('RIGHT')\n if moved:\n # Update the game state only if a move was successful\n self.render()\n return True\n ```\n2. Implement function B\n\n## Code Review Result\nLBTM\n\n# Format example 2\n## Code Review: \n1. Yes.\n2. Yes.\n3. Yes.\n4. Yes.\n5. Yes.\n6. Yes.\n\n## Actions\npass\n\n## Code Review Result\nLGTM\n\n\n\n# Instruction: Based on the actual code situation, follow one of the \"Format example\". Return only 1 file under review.\n\n## Code Review: Ordered List. Based on the \"Code to be Reviewed\", provide key, clear, concise, and specific answer. If any answer is no, explain how to fix it step by step.\n1. Is the code implemented as per the requirements? If not, how to achieve it? Analyse it step by step.\n2. Is the code logic completely correct? If there are errors, please indicate how to correct them.\n3. Does the existing code follow the \"Data structures and interfaces\"?\n4. Are all functions implemented? If there is no implementation, please indicate how to achieve it step by step.\n5. Have all necessary pre-dependencies been imported? If not, indicate which ones need to be imported\n6. Are methods from other files being reused correctly?\n\n## Actions: Ordered List. Things that should be done after CR, such as implementing class A and function B\n\n## Code Review Result: str. If the code doesn't have bugs, we don't need to rewrite it, so answer LGTM and stop. ONLY ANSWER LGTM/LBTM.\nLGTM/LBTM\n\n": "## Code Review: \n1. No, the code does not handle the case where the divisor 'b' is 0, which would result in a division by zero error. This should be addressed to handle this edge case.\n2. No, the code logic is not completely correct as it does not handle the division by zero scenario.\n3. Yes, the code follows the basic function implementation.\n4. Yes, the function is implemented.\n5. No, the code does not handle the necessary pre-dependencies for error handling when 'b' is 0.\n6. No, there are no methods from other files being reused in this code.\n\n## Actions\n1. Implement error handling to check if the divisor 'b' is 0 before performing the division operation.\n2. Import the necessary pre-dependencies for error handling when 'b' is 0.\n\n## Code Review Result\nLBTM", + "\n# System\nRole: You are a professional software engineer, and your main task is to review and revise the code. You need to ensure that the code conforms to the google-style standards, is elegantly designed and modularized, easy to read and maintain.\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## System Design\ndivide two numbers\n\n## Task\n\n\n## Code Files\n\n\n\n## Code to be Reviewed: \n```Code\n\ndef div(a: int, b: int = 0):\n return a / b\n\n```\n\n## Code Review: \n1. No, the code does not handle the case where the divisor 'b' is 0, which would result in a division by zero error. This should be addressed to handle this edge case.\n2. No, the code logic is not completely correct as it does not handle the division by zero scenario.\n3. Yes, the code follows the basic function implementation.\n4. Yes, the function is implemented.\n5. No, the code does not handle the necessary pre-dependencies for error handling when 'b' is 0.\n6. No, there are no methods from other files being reused in this code.\n\n## Actions\n1. Implement error handling to check if the divisor 'b' is 0 before performing the division operation.\n2. Import the necessary pre-dependencies for error handling when 'b' is 0.\n\n## Code Review Result\nLBTM\n\n# Instruction: rewrite code based on the Code Review and Actions\n## Rewrite Code: CodeBlock. If it still has some bugs, rewrite with triple quotes. Do your utmost to optimize THIS SINGLE FILE. Return all completed codes and prohibit the return of unfinished codes.\n```Code\n## \n...\n```\n": "```python\ndef div(a: int, b: int = 0):\n if b == 0:\n raise ValueError(\"Cannot divide by zero\")\n return a / b\n```", + "\n# System\nRole: You are a professional software engineer, and your main task is to review and revise the code. You need to ensure that the code conforms to the google-style standards, is elegantly designed and modularized, easy to read and maintain.\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## System Design\ndivide two numbers\n\n## Task\n\n\n## Code Files\n\n\n\n## Code to be Reviewed: \n```Code\ndef div(a: int, b: int = 0):\n if b == 0:\n raise ValueError(\"Cannot divide by zero\")\n return a / b\n\n```\n\n\n\n# Format example 1\n## Code Review: \n1. No, we should fix the logic of class A due to ...\n2. ...\n3. ...\n4. No, function B is not implemented, ...\n5. ...\n6. ...\n\n## Actions\n1. Fix the `handle_events` method to update the game state only if a move is successful.\n ```python\n def handle_events(self):\n for event in pygame.event.get():\n if event.type == pygame.QUIT:\n return False\n if event.type == pygame.KEYDOWN:\n moved = False\n if event.key == pygame.K_UP:\n moved = self.game.move('UP')\n elif event.key == pygame.K_DOWN:\n moved = self.game.move('DOWN')\n elif event.key == pygame.K_LEFT:\n moved = self.game.move('LEFT')\n elif event.key == pygame.K_RIGHT:\n moved = self.game.move('RIGHT')\n if moved:\n # Update the game state only if a move was successful\n self.render()\n return True\n ```\n2. Implement function B\n\n## Code Review Result\nLBTM\n\n# Format example 2\n## Code Review: \n1. Yes.\n2. Yes.\n3. Yes.\n4. Yes.\n5. Yes.\n6. Yes.\n\n## Actions\npass\n\n## Code Review Result\nLGTM\n\n\n\n# Instruction: Based on the actual code situation, follow one of the \"Format example\". Return only 1 file under review.\n\n## Code Review: Ordered List. Based on the \"Code to be Reviewed\", provide key, clear, concise, and specific answer. If any answer is no, explain how to fix it step by step.\n1. Is the code implemented as per the requirements? If not, how to achieve it? Analyse it step by step.\n2. Is the code logic completely correct? If there are errors, please indicate how to correct them.\n3. Does the existing code follow the \"Data structures and interfaces\"?\n4. Are all functions implemented? If there is no implementation, please indicate how to achieve it step by step.\n5. Have all necessary pre-dependencies been imported? If not, indicate which ones need to be imported\n6. Are methods from other files being reused correctly?\n\n## Actions: Ordered List. Things that should be done after CR, such as implementing class A and function B\n\n## Code Review Result: str. If the code doesn't have bugs, we don't need to rewrite it, so answer LGTM and stop. ONLY ANSWER LGTM/LBTM.\nLGTM/LBTM\n\n": "## Code Review: \n1. The code does not fully conform to the requirements. The function signature should be updated to follow the Google style standards, and the division by zero check should be handled differently.\n2. The code logic is correct, but the exception handling for division by zero should be improved.\n3. The existing code follows the basic data structures and interfaces.\n4. The function is implemented, but the exception handling for division by zero should be added.\n5. The necessary pre-dependencies have been imported.\n6. There are no methods from other files being reused in this code.\n\n## Actions\n1. Update the function signature to follow the Google style standards and improve the division by zero check as follows:\n ```python\n def divide_numbers(dividend: int, divisor: int) -> float:\n if divisor == 0:\n raise ValueError(\"Cannot divide by zero\")\n return dividend / divisor\n ```\n2. No further action is required.\n\n## Code Review Result\nLBTM", + "\n# System\nRole: You are a professional software engineer, and your main task is to review and revise the code. You need to ensure that the code conforms to the google-style standards, is elegantly designed and modularized, easy to read and maintain.\nLanguage: Please use the same language as the user requirement, but the title and code should be still in English. For example, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\nATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced \"Format example\".\n\n# Context\n## System Design\ndivide two numbers\n\n## Task\n\n\n## Code Files\n\n\n\n## Code to be Reviewed: \n```Code\ndef div(a: int, b: int = 0):\n if b == 0:\n raise ValueError(\"Cannot divide by zero\")\n return a / b\n\n```\n\n## Code Review: \n1. The code does not fully conform to the requirements. The function signature should be updated to follow the Google style standards, and the division by zero check should be handled differently.\n2. The code logic is correct, but the exception handling for division by zero should be improved.\n3. The existing code follows the basic data structures and interfaces.\n4. The function is implemented, but the exception handling for division by zero should be added.\n5. The necessary pre-dependencies have been imported.\n6. There are no methods from other files being reused in this code.\n\n## Actions\n1. Update the function signature to follow the Google style standards and improve the division by zero check as follows:\n ```python\n def divide_numbers(dividend: int, divisor: int) -> float:\n if divisor == 0:\n raise ValueError(\"Cannot divide by zero\")\n return dividend / divisor\n ```\n2. No further action is required.\n\n## Code Review Result\nLBTM\n\n# Instruction: rewrite code based on the Code Review and Actions\n## Rewrite Code: CodeBlock. If it still has some bugs, rewrite with triple quotes. Do your utmost to optimize THIS SINGLE FILE. Return all completed codes and prohibit the return of unfinished codes.\n```Code\n## \n...\n```\n": "```python\ndef divide_numbers(dividend: int, divisor: int) -> float:\n if divisor == 0:\n raise ValueError(\"Cannot divide by zero\")\n return dividend / divisor\n```", + "\n## context\nCreate a 2048 game\n\n-----\n\n## format example\n[CONTENT]\n{\n \"issue_type\": \"BUG\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- issue_type: # Answer BUG/REQUIREMENT. If it is a bugfix, answer BUG, otherwise answer Requirement\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"issue_type\": \"REQUIREMENT\"\n}\n[/CONTENT]", + "\n## context\nCreate a 2048 game\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Product Goals\": [\n \"Create an engaging user experience\",\n \"Improve accessibility, be responsive\",\n \"More beautiful UI\"\n ]\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Product Goals: typing.List[str] # Provide up to three clear, orthogonal product goals.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Product Goals\": [\n \"Create a challenging and addictive gameplay\",\n \"Enhance accessibility and responsiveness for all users\",\n \"Implement visually appealing UI design\"\n ]\n}\n[/CONTENT]", + "\n## context\nCreate a 2048 game\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Competitive Analysis\": [\n \"2048 Game A: Simple interface, lacks responsive features\",\n \"play2048.co: Beautiful and responsive UI with my best score shown\",\n \"2048game.com: Responsive UI with my best score shown, but many ads\"\n ]\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Competitive Analysis: typing.List[str] # Provide 5 to 7 competitive products.\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Competitive Analysis\": [\n \"2048 Game A: Simple interface, lacks responsive features\",\n \"play2048.co: Beautiful and responsive UI with my best score shown\",\n \"2048game.com: Responsive UI with my best score shown, but many ads\"\n ]\n}\n[/CONTENT]", + "\n## context\nCreate a 2048 game\n\n-----\n\n## format example\n[CONTENT]\n{\n \"Requirement Pool\": [\n [\n \"P0\",\n \"The main code ...\"\n ],\n [\n \"P0\",\n \"The game algorithm ...\"\n ]\n ]\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- Requirement Pool: typing.List[typing.List[str]] # List down the top-5 requirements with their priority (P0, P1, P2).\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"Requirement Pool\": [\n [\n \"P0\",\n \"The main code should handle user input and update the game board accordingly.\"\n ],\n [\n \"P0\",\n \"The game algorithm should handle the merging of tiles and the generation of new tiles with values of 2 or 4.\"\n ],\n [\n \"P1\",\n \"The game should end when the player achieves the 2048 tile or when there are no possible moves left.\"\n ],\n [\n \"P1\",\n \"The game should display the current score and the highest tile achieved by the player.\"\n ],\n [\n \"P2\",\n \"The game should have a smooth and visually appealing user interface.\"\n ]\n ]\n}\n[/CONTENT]", + "\nRole: You are a senior development and qa engineer, your role is summarize the code running result.\nIf the running result does not include an error, you should explicitly approve the result.\nOn the other hand, if the running result indicates some error, you should point out which part, the development code or the test code, produces the error,\nand give specific instructions on fixing the errors. Here is the code info:\n\n## Development Code File Name\na.txt\n## Development Code\n```python\nresult = 'helloworld'\n```\n## Test File Name\n\n## Test Code\n```python\nNone\n```\n## Running Command\n\n## Running Output\nstandard output: \n```text\nhelloworld\n```\nstandard errors: \n```text\n\n```\n\nNow you should begin your analysis\n---\n## instruction:\nPlease summarize the cause of the errors and give correction instruction\n## File To Rewrite:\nDetermine the ONE file to rewrite in order to fix the error, for example, xyz.py, or test_xyz.py\n## Status:\nDetermine if all of the code works fine, if so write PASS, else FAIL,\nWRITE ONLY ONE WORD, PASS OR FAIL, IN THIS SECTION\n## Send To:\nPlease write NoOne if there are no errors, Engineer if the errors are due to problematic development codes, else QaEngineer,\nWRITE ONLY ONE WORD, NoOne OR Engineer OR QaEngineer, IN THIS SECTION.\n---\nYou should fill in necessary instruction, status, send to, and finally return all content between the --- segment line.\n": "---\n## instruction:\nNo errors detected. The development code runs successfully and outputs the expected result without any errors.\n## File To Rewrite:\nNone\n## Status:\nPASS\n## Send To:\nNoOne\n---", + "\nRole: You are a senior development and qa engineer, your role is summarize the code running result.\nIf the running result does not include an error, you should explicitly approve the result.\nOn the other hand, if the running result indicates some error, you should point out which part, the development code or the test code, produces the error,\nand give specific instructions on fixing the errors. Here is the code info:\n\n## Development Code File Name\na.sh\n## Development Code\n```python\necho 'Hello World'\n```\n## Test File Name\n\n## Test Code\n```python\nNone\n```\n## Running Command\necho Hello World\n## Running Output\nstandard output: \n```text\nHello World\n\n```\nstandard errors: \n```text\n\n```\n\nNow you should begin your analysis\n---\n## instruction:\nPlease summarize the cause of the errors and give correction instruction\n## File To Rewrite:\nDetermine the ONE file to rewrite in order to fix the error, for example, xyz.py, or test_xyz.py\n## Status:\nDetermine if all of the code works fine, if so write PASS, else FAIL,\nWRITE ONLY ONE WORD, PASS OR FAIL, IN THIS SECTION\n## Send To:\nPlease write NoOne if there are no errors, Engineer if the errors are due to problematic development codes, else QaEngineer,\nWRITE ONLY ONE WORD, NoOne OR Engineer OR QaEngineer, IN THIS SECTION.\n---\nYou should fill in necessary instruction, status, send to, and finally return all content between the --- segment line.\n": "---\n## instruction:\nNo errors detected in the running result. The development code executed successfully, and the output matches the expected result. Since there is no test code provided, no test execution was performed. Therefore, no specific instructions are needed for correction.\n## File To Rewrite:\nNone\n## Status:\nPASS\n## Send To:\nNoOne\n---", + "\nRole: You are a senior development and qa engineer, your role is summarize the code running result.\nIf the running result does not include an error, you should explicitly approve the result.\nOn the other hand, if the running result indicates some error, you should point out which part, the development code or the test code, produces the error,\nand give specific instructions on fixing the errors. Here is the code info:\n\n## Development Code File Name\na.py\n## Development Code\n```python\npython -c \"print(1/0)\"\n```\n## Test File Name\n\n## Test Code\n```python\nNone\n```\n## Running Command\npython -c print(1/0)\n## Running Output\nstandard output: \n```text\n\n```\nstandard errors: \n```text\nTraceback (most recent call last):\n File \"\", line 1, in \nZeroDivisionError: division by zero\n\n```\n\nNow you should begin your analysis\n---\n## instruction:\nPlease summarize the cause of the errors and give correction instruction\n## File To Rewrite:\nDetermine the ONE file to rewrite in order to fix the error, for example, xyz.py, or test_xyz.py\n## Status:\nDetermine if all of the code works fine, if so write PASS, else FAIL,\nWRITE ONLY ONE WORD, PASS OR FAIL, IN THIS SECTION\n## Send To:\nPlease write NoOne if there are no errors, Engineer if the errors are due to problematic development codes, else QaEngineer,\nWRITE ONLY ONE WORD, NoOne OR Engineer OR QaEngineer, IN THIS SECTION.\n---\nYou should fill in necessary instruction, status, send to, and finally return all content between the --- segment line.\n": "---\n## instruction:\nThe error is caused by attempting to divide by zero in the development code. To fix this error, you should modify the division operation to avoid division by zero. For example, you can add a condition to check if the denominator is zero before performing the division.\n\n## File To Rewrite:\na.py\n\n## Status:\nFAIL\n\n## Send To:\nEngineer\n---", + "[{\"role\": \"user\", \"content\": \"Interface definition:\\n```text\\nInterface Name: Element Tagging\\nInterface Path: /projects/{project_key}/node-tags\\nMethod: POST\\n\\nRequest parameters:\\nPath parameters:\\nproject_key\\n\\nBody parameters:\\nName\\tType\\tRequired\\tDefault Value\\tRemarks\\nnodes\\tarray\\tYes\\t\\tNodes\\n\\tnode_key\\tstring\\tNo\\t\\tNode key\\n\\ttags\\tarray\\tNo\\t\\tOriginal node tag list\\n\\tnode_type\\tstring\\tNo\\t\\tNode type DATASET / RECIPE\\noperations\\tarray\\tYes\\t\\t\\n\\ttags\\tarray\\tNo\\t\\tOperation tag list\\n\\tmode\\tstring\\tNo\\t\\tOperation type ADD / DELETE\\n\\nReturn data:\\nName\\tType\\tRequired\\tDefault Value\\tRemarks\\ncode\\tinteger\\tYes\\t\\tStatus code\\nmsg\\tstring\\tYes\\t\\tPrompt message\\ndata\\tobject\\tYes\\t\\tReturned data\\nlist\\tarray\\tNo\\t\\tNode list true / false\\nnode_type\\tstring\\tNo\\t\\tNode type DATASET / RECIPE\\nnode_key\\tstring\\tNo\\t\\tNode key\\n```\\n\\nUnit test:\\n```python\\n@pytest.mark.parametrize(\\n\\\"project_key, nodes, operations, expected_msg\\\",\\n[\\n(\\\"project_key\\\", [{\\\"node_key\\\": \\\"dataset_001\\\", \\\"tags\\\": [\\\"tag1\\\", \\\"tag2\\\"], \\\"node_type\\\": \\\"DATASET\\\"}], [{\\\"tags\\\": [\\\"new_tag1\\\"], \\\"mode\\\": \\\"ADD\\\"}], \\\"success\\\"),\\n(\\\"project_key\\\", [{\\\"node_key\\\": \\\"dataset_002\\\", \\\"tags\\\": [\\\"tag1\\\", \\\"tag2\\\"], \\\"node_type\\\": \\\"DATASET\\\"}], [{\\\"tags\\\": [\\\"tag1\\\"], \\\"mode\\\": \\\"DELETE\\\"}], \\\"success\\\"),\\n(\\\"\\\", [{\\\"node_key\\\": \\\"dataset_001\\\", \\\"tags\\\": [\\\"tag1\\\", \\\"tag2\\\"], \\\"node_type\\\": \\\"DATASET\\\"}], [{\\\"tags\\\": [\\\"new_tag1\\\"], \\\"mode\\\": \\\"ADD\\\"}], \\\"Missing the required parameter project_key\\\"),\\n(123, [{\\\"node_key\\\": \\\"dataset_001\\\", \\\"tags\\\": [\\\"tag1\\\", \\\"tag2\\\"], \\\"node_type\\\": \\\"DATASET\\\"}], [{\\\"tags\\\": [\\\"new_tag1\\\"], \\\"mode\\\": \\\"ADD\\\"}], \\\"Incorrect parameter type\\\"),\\n(\\\"project_key\\\", [{\\\"node_key\\\": \\\"a\\\"*201, \\\"tags\\\": [\\\"tag1\\\", \\\"tag2\\\"], \\\"node_type\\\": \\\"DATASET\\\"}], [{\\\"tags\\\": [\\\"new_tag1\\\"], \\\"mode\\\": \\\"ADD\\\"}], \\\"Request parameter exceeds field boundary\\\")\\n]\\n)\\ndef test_node_tags(project_key, nodes, operations, expected_msg):\\n pass\\n\\n# The above is an interface definition and a unit test example.\\n# Next, please play the role of an expert test manager with 20 years of experience at Google. When I give the interface definition, \\n# reply to me with a unit test. There are several requirements:\\n# 1. Only output one `@pytest.mark.parametrize` and the corresponding test_ function (inside pass, do not implement).\\n# -- The function parameter contains expected_msg for result verification.\\n# 2. The generated test cases use shorter text or numbers and are as compact as possible.\\n# 3. If comments are needed, use Chinese.\\n\\n# If you understand, please wait for me to give the interface definition and just answer \\\"Understood\\\" to save tokens.\\n\"}, {\"role\": \"user\", \"content\": \"Refer to the test types: such as SQL injection, cross-site scripting (XSS), unauthorized access and privilege escalation, \\nauthentication and authorization, parameter verification, exception handling, file upload and download.\\nPlease output 10 test cases within one `@pytest.mark.parametrize` scope.\\n```text\\nAPI Name: 获取 model 详情(job专用-后续开放给sdk)\\nAPI Path: /v1/projects/{project_key}/jobs/{job_id}/models/{model_key}\\nMethod: GET\\n\\nRequest Parameters:\\nPath Parameters:\\nproject_key \\njob_id \\nmodel_key \\n\\nBody Parameters:\\nName\\tType\\tRequired\\tDefault Value\\tRemarks\\nproject_key\\tstring\\tYes\\t\\t\\njob_id\\tstring\\tYes\\t\\t\\nmodel_key\\tstring\\tYes\\t\\t\\n\\nResponse Data:\\nName\\tType\\tRequired\\tDefault Value\\tRemarks\\ncode\\tnumber\\tYes\\t\\t0成功,非0失败\\nmsg\\tstring\\tYes\\t\\t如果失败,这里有错误信息\\ndata\\tobject\\tYes\\t\\tdata信息\\n\\tproject_key\\tstring\\tNo\\t\\tproject key\\n\\tname\\tstring\\tNo\\t\\t用户可修改的name\\n\\tmodel\\tobject\\tNo\\t\\tmodel信息\\n\\t\\ttype\\tstring\\tNo\\t\\tdataset type\\n\\t\\tmanaged\\tboolean\\tNo\\t\\t为false时是第一类dataset,数据不可删除\\n\\t\\tname\\tstring\\tNo\\t\\t用户可修改的name\\n\\t\\tproject_key\\tstring\\tNo\\t\\tproject key\\n\\t\\tformat_type\\tstring\\tNo\\t\\t文件类型的dataset才有这项。“csv”\\n\\t\\tflow_options\\tobject\\tNo\\t\\t创建dataset时的高级设置\\n\\t\\t\\tvirtualizable\\tboolean\\tNo\\t\\t高级设置里的参数。缺省false\\n\\t\\t\\trebuild_behavior\\tstring\\tNo\\t\\t高级设置里的参数。缺省NORMAL\\n\\t\\t\\tcross_project_build_behavior\\tstring\\tNo\\t\\t高级设置里的参数。缺省DEFAULT\\n\\t\\tformat_params\\tobject\\tNo\\t\\t文件类型的dataset才有\\n\\t\\t\\tstyle\\tstring\\tNo\\t\\t\\n\\t\\t\\tcharset\\tstring\\tNo\\t\\t\\n\\t\\t\\tseparator\\tstring\\tNo\\t\\t\\n\\t\\t\\tquote_char\\tstring\\tNo\\t\\t\\n\\t\\t\\tescape_char\\tstring\\tNo\\t\\t\\n\\t\\t\\tdate_serialization_format\\tstring\\tNo\\t\\t\\n\\t\\t\\tarray_map_format\\tstring\\tNo\\t\\t\\n\\t\\t\\thive_separators\\tarray\\tNo\\t\\t\\n\\t\\t\\tskip_rows_before_header\\tnumber\\tNo\\t\\t\\n\\t\\t\\tparse_header_row\\tboolean\\tNo\\t\\t\\n\\t\\t\\tskip_rows_after_header\\tnumber\\tNo\\t\\t\\n\\t\\t\\tprobable_number_of_records\\tnumber\\tNo\\t\\t\\n\\t\\t\\tnormalize_booleans\\tboolean\\tNo\\t\\t\\n\\t\\t\\tnormalize_doubles\\tboolean\\tNo\\t\\t\\n\\t\\ttags\\tarray\\tNo\\t\\t标签tags\\n\\t\\tparams\\tobject\\tNo\\t\\t必有这项,但不同类型的dataset里面的key有差别\\n\\t\\t\\tconnection\\tstring\\tNo\\t\\tconnection id,到db查其他参数\\n\\t\\t\\tpath\\tstring\\tNo\\t\\t文件类connection才有这项\\n\\t\\t\\ttable\\tstring\\tNo\\t\\tdb表名,DB类connection才有这项\\n\\t\\t\\tmode\\tstring\\tNo\\t\\t存储类型,比如“table\\\",DB类connection才有这项\\n\\t\\t\\tbucket\\tstring\\tNo\\t\\tS3类型的connection才有这项\\n\\t\\t\\tkey_name\\tstring\\tNo\\t\\tredis才有,key name\\n\\t\\t\\tkey_type\\tstring\\tNo\\t\\tredis才有,key type\\n\\t\\t\\tcollection\\tstring\\tNo\\t\\t非关系型数据库才有,collection name\\n\\t\\t\\tindex\\tstring\\tNo\\t\\t索引类型的才有这项\\n\\t\\t\\tnot_ready_if_empty\\tboolean\\tNo\\t\\t数据非空才认为是data ready\\n\\t\\t\\tfiles_selection_rules\\tobject\\tNo\\t\\t\\n\\t\\t\\t\\tmode\\tstring\\tNo\\t\\t\\n\\t\\t\\t\\texclude_rules\\tarray\\tNo\\t\\t\\n\\t\\t\\t\\tinclude_rules\\tarray\\tNo\\t\\t\\n\\t\\t\\t\\texplicit_files\\tarray\\tNo\\t\\t\\n\\t\\tschema\\tobject\\tNo\\t\\tcolumns信息在这里\\n\\t\\t\\tcolumns\\tarray\\tNo\\t\\t\\n\\t\\t\\t\\tname\\tstring\\tNo\\t\\t\\n\\t\\t\\t\\ttype\\tstring\\tNo\\t\\t\\n\\t\\t\\t\\torigin_type\\tstring\\tNo\\t\\t\\n\\t\\t\\tuser_modified\\tboolean\\tNo\\t\\t\\n\\t\\tcustom_fields\\tobject\\tNo\\t\\t自定义fields\\n\\t\\tlast_build\\tobject\\tNo\\t\\t最后一次构建的信息\\n\\t\\t\\tproject_key\\tstring\\tNo\\t\\tproject key\\n\\t\\t\\tid\\tstring\\tNo\\t\\tactivity id\\n\\t\\t\\tjob_id\\tstring\\tNo\\t\\tjob id\\n\\t\\t\\tjob_project_key\\tstring\\tNo\\t\\t\\n\\t\\t\\tbuild_start_time\\tnumber\\tNo\\t\\t构建开始时间\\n\\t\\t\\tbuild_end_time\\tnumber\\tNo\\t\\t构建结束时间\\n\\t\\t\\tbuild_success\\tstring\\tNo\\t\\tsuccess或failed\\n\\t\\tobject_key\\tstring\\tNo\\t\\tdataset_key,后台用的id,用户不可见不可改\\n\\t\\tcache\\tobject\\tNo\\t\\t下载缓存数据链接\\n\\t\\t\\ts3_path\\tstring\\tNo\\t\\t\\n\\tstatus\\tobject\\tNo\\t\\t数据状态\\n\\t\\tsize\\tobject\\tNo\\t\\t数据大小信息\\n\\t\\t\\ttotal_value\\tnumber\\tNo\\t\\t占多少字节磁盘\\n\\t\\t\\tlast_computed\\tnumber\\tNo\\t\\t\\n\\t\\t\\tfirst_computed\\tnumber\\tNo\\t\\t\\n\\t\\t\\thas_data\\tboolean\\tNo\\t\\t是否有数据,这个影响前端的图标显示\\n\\t\\t\\tincomplete\\tboolean\\tNo\\t\\t\\n\\t\\trecords\\tobject\\tNo\\t\\t\\n\\t\\t\\ttotal_value\\tnumber\\tNo\\t\\t\\n\\t\\t\\tlast_computed\\tnumber\\tNo\\t\\t\\n\\t\\t\\tfirst_computed\\tnumber\\tNo\\t\\t\\n\\t\\t\\thas_data\\tboolean\\tNo\\t\\t是否有数据,这个影响前端的图标显示\\n\\t\\t\\tincomplete\\tboolean\\tNo\\t\\t\\n\\t\\tpartitions_last_compute\\tnumber\\tNo\\t\\t\\n\\t\\tpartitions\\tnumber\\tNo\\t\\t\\n\\tbuildable\\tboolean\\tNo\\t\\t有recipe时为true\\n\\theaders\\tarray\\tNo\\t\\t\\n\\t\\tdataset_schema\\tobject\\tNo\\t\\t\\n\\t\\t\\tname\\tstring\\tNo\\t字段名称\\t\\n\\t\\t\\ttype\\tstring\\tNo\\t字段类型\\t\\n\\t\\tnormal_rate\\tobject\\tNo\\t缺失值统计信息\\t\\n\\n```\"}]": { + "code": "import string\nimport random\n\ndef random_string(length=10):\n return ''.join(random.choice(string.ascii_lowercase) for i in range(length))" + }, + "[{\"role\": \"user\", \"content\": \"Interface definition:\\n```text\\nInterface Name: Element Tagging\\nInterface Path: /projects/{project_key}/node-tags\\nMethod: POST\\n\\nRequest parameters:\\nPath parameters:\\nproject_key\\n\\nBody parameters:\\nName\\tType\\tRequired\\tDefault Value\\tRemarks\\nnodes\\tarray\\tYes\\t\\tNodes\\n\\tnode_key\\tstring\\tNo\\t\\tNode key\\n\\ttags\\tarray\\tNo\\t\\tOriginal node tag list\\n\\tnode_type\\tstring\\tNo\\t\\tNode type DATASET / RECIPE\\noperations\\tarray\\tYes\\t\\t\\n\\ttags\\tarray\\tNo\\t\\tOperation tag list\\n\\tmode\\tstring\\tNo\\t\\tOperation type ADD / DELETE\\n\\nReturn data:\\nName\\tType\\tRequired\\tDefault Value\\tRemarks\\ncode\\tinteger\\tYes\\t\\tStatus code\\nmsg\\tstring\\tYes\\t\\tPrompt message\\ndata\\tobject\\tYes\\t\\tReturned data\\nlist\\tarray\\tNo\\t\\tNode list true / false\\nnode_type\\tstring\\tNo\\t\\tNode type DATASET / RECIPE\\nnode_key\\tstring\\tNo\\t\\tNode key\\n```\\n\\nUnit test:\\n```python\\n@pytest.mark.parametrize(\\n\\\"project_key, nodes, operations, expected_msg\\\",\\n[\\n(\\\"project_key\\\", [{\\\"node_key\\\": \\\"dataset_001\\\", \\\"tags\\\": [\\\"tag1\\\", \\\"tag2\\\"], \\\"node_type\\\": \\\"DATASET\\\"}], [{\\\"tags\\\": [\\\"new_tag1\\\"], \\\"mode\\\": \\\"ADD\\\"}], \\\"success\\\"),\\n(\\\"project_key\\\", [{\\\"node_key\\\": \\\"dataset_002\\\", \\\"tags\\\": [\\\"tag1\\\", \\\"tag2\\\"], \\\"node_type\\\": \\\"DATASET\\\"}], [{\\\"tags\\\": [\\\"tag1\\\"], \\\"mode\\\": \\\"DELETE\\\"}], \\\"success\\\"),\\n(\\\"\\\", [{\\\"node_key\\\": \\\"dataset_001\\\", \\\"tags\\\": [\\\"tag1\\\", \\\"tag2\\\"], \\\"node_type\\\": \\\"DATASET\\\"}], [{\\\"tags\\\": [\\\"new_tag1\\\"], \\\"mode\\\": \\\"ADD\\\"}], \\\"Missing the required parameter project_key\\\"),\\n(123, [{\\\"node_key\\\": \\\"dataset_001\\\", \\\"tags\\\": [\\\"tag1\\\", \\\"tag2\\\"], \\\"node_type\\\": \\\"DATASET\\\"}], [{\\\"tags\\\": [\\\"new_tag1\\\"], \\\"mode\\\": \\\"ADD\\\"}], \\\"Incorrect parameter type\\\"),\\n(\\\"project_key\\\", [{\\\"node_key\\\": \\\"a\\\"*201, \\\"tags\\\": [\\\"tag1\\\", \\\"tag2\\\"], \\\"node_type\\\": \\\"DATASET\\\"}], [{\\\"tags\\\": [\\\"new_tag1\\\"], \\\"mode\\\": \\\"ADD\\\"}], \\\"Request parameter exceeds field boundary\\\")\\n]\\n)\\ndef test_node_tags(project_key, nodes, operations, expected_msg):\\n pass\\n\\n# The above is an interface definition and a unit test example.\\n# Next, please play the role of an expert test manager with 20 years of experience at Google. When I give the interface definition, \\n# reply to me with a unit test. There are several requirements:\\n# 1. Only output one `@pytest.mark.parametrize` and the corresponding test_ function (inside pass, do not implement).\\n# -- The function parameter contains expected_msg for result verification.\\n# 2. The generated test cases use shorter text or numbers and are as compact as possible.\\n# 3. If comments are needed, use Chinese.\\n\\n# If you understand, please wait for me to give the interface definition and just answer \\\"Understood\\\" to save tokens.\\n\"}, {\"role\": \"user\", \"content\": \"Refer to the test types: such as SQL injection, cross-site scripting (XSS), unauthorized access and privilege escalation, \\nauthentication and authorization, parameter verification, exception handling, file upload and download.\\nPlease output 10 test cases within one `@pytest.mark.parametrize` scope.\\n```text\\nAPI Name: 获取managed folder详情(job专用)\\nAPI Path: /v1/projects/{project_key}/jobs/{job_id}/folders/{folder_key}\\nMethod: GET\\n\\nRequest Parameters:\\nPath Parameters:\\nproject_key \\njob_id \\nfolder_key \\n\\nBody Parameters:\\nName\\tType\\tRequired\\tDefault Value\\tRemarks\\nproject_key\\tstring\\tYes\\t\\t\\njob_id\\tstring\\tYes\\t\\t\\nfolder_key\\tstring\\tYes\\t\\t\\n\\nResponse Data:\\nName\\tType\\tRequired\\tDefault Value\\tRemarks\\ncode\\tnumber\\tYes\\t\\t0成功,非0失败\\nmsg\\tstring\\tYes\\t\\t失败时这里有错误信息\\ndata\\tobject\\tYes\\t\\t\\n\\tproject_key\\tstring\\tNo\\t\\tproject key\\n\\tfolder\\tobject\\tNo\\t\\tfolder配置在这里\\n\\t\\tproject_key\\tstring\\tNo\\t\\tproject key\\n\\t\\tobject_key\\tstring\\tNo\\t\\tobject key\\n\\t\\tname\\tstring\\tNo\\t\\t用户可编辑的那个name\\n\\t\\ttype\\tstring\\tNo\\t\\tfolder类型,与connection有关\\n\\t\\tparams\\tobject\\tNo\\t\\t数据读写相关配置在这里\\n\\t\\t\\tconnection\\tstring\\tNo\\t\\tconnection id\\n\\t\\t\\tpath\\tstring\\tNo\\t\\t文件夹内容存放的相对路径\\n\\t\\t\\tnot_ready_if_empty\\tboolean\\tNo\\t\\treserved\\n\\t\\t\\tfiles_selection_rules\\tobject\\tNo\\t\\t文件过滤规则\\n\\t\\t\\t\\tmode\\tstring\\tNo\\t\\tALL\\n\\t\\t\\t\\texclude_rules\\tarray\\tNo\\t\\t排除规则\\n\\t\\t\\t\\tinclude_rules\\tarray\\tNo\\t\\t\\n\\t\\t\\t\\texplicit_files\\tarray\\tNo\\t\\t\\n\\t\\tflow_options\\tobject\\tNo\\t\\tflow参数\\n\\t\\t\\tvirtualizable\\tboolean\\tNo\\t\\t\\n\\t\\t\\trebuild_behavior\\tstring\\tNo\\t\\t构建方式\\n\\t\\t\\tcross_project_build_behavior\\tstring\\tNo\\t\\t\\n\\t\\tmetrics\\tobject\\tNo\\t\\t\\n\\t\\t\\tprobes\\tarray\\tNo\\t\\t\\n\\t\\t\\t\\ttype\\tstring\\tNo\\t\\t\\n\\t\\t\\t\\tenabled\\tboolean\\tNo\\t\\t\\n\\t\\t\\t\\tcompute_on_build_mode\\tstring\\tNo\\t\\t\\n\\t\\t\\t\\tmeta\\tobject\\tNo\\t\\t\\n\\t\\t\\t\\t\\tname\\tstring\\tNo\\t\\t\\n\\t\\t\\t\\t\\tlevel\\tnumber\\tNo\\t\\t\\n\\t\\t\\t\\tconfiguration\\tobject\\tNo\\t\\t\\n\\t\\t\\tengine_config\\tobject\\tNo\\t\\t\\n\\t\\t\\t\\tpad_runs_with_metrics\\tboolean\\tNo\\t\\t\\n\\t\\t\\t\\thive\\tobject\\tNo\\t\\t\\n\\t\\t\\t\\t\\tactive\\tboolean\\tNo\\t\\t\\n\\t\\t\\t\\t\\textra_conf\\tarray\\tNo\\t\\t\\n\\t\\t\\t\\tbasic\\tobject\\tNo\\t\\t\\n\\t\\t\\t\\tdss\\tobject\\tNo\\t\\t\\n\\t\\t\\t\\t\\tactive\\tboolean\\tNo\\t\\t\\n\\t\\t\\t\\t\\tselection\\tobject\\tNo\\t\\t\\n\\t\\t\\t\\t\\t\\tuse_mem_table\\tboolean\\tNo\\t\\t\\n\\t\\t\\t\\t\\t\\tfilter\\tobject\\tNo\\t\\t\\n\\t\\t\\t\\t\\t\\t\\tdistinct\\tboolean\\tNo\\t\\t\\n\\t\\t\\t\\t\\t\\t\\tenabled\\tboolean\\tNo\\t\\t\\n\\t\\t\\t\\t\\t\\tpartition_selection_method\\tstring\\tNo\\t\\t\\n\\t\\t\\t\\t\\t\\tlatest_partitions_n\\tnumber\\tNo\\t\\t\\n\\t\\t\\t\\t\\t\\tordering\\tobject\\tNo\\t\\t\\n\\t\\t\\t\\t\\t\\t\\tenabled\\tboolean\\tNo\\t\\t\\n\\t\\t\\t\\t\\t\\t\\trules\\tarray\\tNo\\t\\t\\n\\t\\t\\t\\t\\t\\tsampling_method\\tstring\\tNo\\t\\t\\n\\t\\t\\t\\t\\t\\tmax_records\\tnumber\\tNo\\t\\t\\n\\t\\t\\t\\t\\t\\ttarget_ratio\\tnumber\\tNo\\t\\t\\n\\t\\t\\t\\t\\t\\twithin_first_n\\tnumber\\tNo\\t\\t\\n\\t\\t\\t\\t\\t\\tmax_read_uncompressed_bytes\\tnumber\\tNo\\t\\t\\n\\t\\t\\t\\tsql\\tobject\\tNo\\t\\t\\n\\t\\t\\t\\t\\tactive\\tboolean\\tNo\\t\\t\\n\\t\\t\\t\\timpala\\tobject\\tNo\\t\\t\\n\\t\\t\\t\\t\\tactive\\tboolean\\tNo\\t\\t\\n\\t\\t\\t\\tspark\\tobject\\tNo\\t\\t\\n\\t\\t\\t\\t\\tactive\\tboolean\\tNo\\t\\t\\n\\t\\t\\t\\t\\textra_conf\\tarray\\tNo\\t\\t\\n\\t\\t\\t\\tpython\\tobject\\tNo\\t\\t\\n\\t\\t\\tdisplayed_state\\tobject\\tNo\\t\\t\\n\\t\\t\\t\\tpartition\\tstring\\tNo\\t\\t\\n\\t\\t\\t\\tcolumns\\tarray\\tNo\\t\\t\\n\\t\\t\\t\\tmetrics\\tarray\\tNo\\t\\t\\n\\t\\tchecks\\tobject\\tNo\\t\\t\\n\\t\\t\\trun_on_build\\tboolean\\tNo\\t\\t\\n\\t\\t\\tchecks\\tarray\\tNo\\t\\t\\n\\t\\t\\tdisplayed_state\\tobject\\tNo\\t\\t\\n\\t\\t\\t\\tpartition\\tstring\\tNo\\t\\t\\n\\t\\t\\t\\tchecks\\tarray\\tNo\\t\\t\\n\\t\\tversion_tag\\tobject\\tNo\\t\\t配置版本信息\\n\\t\\t\\tversion_number\\tnumber\\tNo\\t\\t\\n\\t\\t\\tlast_modified_by\\tobject\\tNo\\t\\t\\n\\t\\t\\t\\tlogin\\tstring\\tNo\\t\\t\\n\\t\\t\\tlast_modified_on\\tnumber\\tNo\\t\\t修改时间unix time ms\\n\\t\\tcreation_tag\\tobject\\tNo\\t\\t配置创建时间\\n\\t\\t\\tversion_number\\tnumber\\tNo\\t\\t1\\n\\t\\t\\tlast_modified_by\\tobject\\tNo\\t\\t\\n\\t\\t\\t\\tlogin\\tstring\\tNo\\t\\t\\n\\t\\t\\tlast_modified_on\\tnumber\\tNo\\t\\t创建时间unix time ms\\n\\t\\ttags\\tarray\\tNo\\t\\t文件夹标签\\n\\t\\tcustom_fields\\tobject\\tNo\\t\\t\\n\\t\\tchecklists\\tobject\\tNo\\t\\t\\n\\t\\t\\tchecklists\\tarray\\tNo\\t\\t\\n\\n```\"}]": { + "code": "import string\nimport random\n\ndef random_string(length=10):\n return ''.join(random.choice(string.ascii_lowercase) for i in range(length))" + }, + "[{\"role\": \"system\", \"content\": \"You are an AI Python assistant. You will be given your previous implementation code of a task, runtime error results, and a hint to change the implementation appropriately. Write your full implementation \"}, {\"role\": \"user\", \"content\": \"\\nHere is an example for you.\\n\\nExample 1:\\n[previous impl]:\\n```python\\ndef add(a: int, b: int) -> int:\\n \\\"\\\"\\\"\\n Given integers a and b, return the total value of a and b.\\n \\\"\\\"\\\"\\n return a - b\\n```\\n\\n[runtime Error]:\\nTested passed:\\n\\nTests failed:\\nassert add(1, 2) == 3 # output: -1\\nassert add(1, 2) == 4 # output: -1\\n\\n[reflection on previous impl]:\\nThe implementation failed the test cases where the input integers are 1 and 2. The issue arises because the code does not add the two integers together, but instead subtracts the second integer from the first. To fix this issue, we should change the operator from `-` to `+` in the return statement. This will ensure that the function returns the correct output for the given input.\\n\\n[improved impl]:\\n```python\\ndef add(a: int, b: int) -> int:\\n \\\"\\\"\\\"\\n Given integers a and b, return the total value of a and b.\\n \\\"\\\"\\\"\\n return a + b\\n```\\n\\n[context]\\nuser: Solve the problem in Python:\\ndef sort_array(arr):\\n \\\"\\\"\\\"\\n In this Kata, you have to sort an array of non-negative integers according to\\n number of ones in their binary representation in ascending order.\\n For similar number of ones, sort based on decimal value.\\n\\n It must be implemented like this:\\n >>> sort_array([1, 5, 2, 3, 4]) == [1, 2, 3, 4, 5]\\n >>> sort_array([-2, -3, -4, -5, -6]) == [-6, -5, -4, -3, -2]\\n >>> sort_array([1, 0, 2, 3, 4]) [0, 1, 2, 3, 4]\\n \\\"\\\"\\\"\\n\\n\\n[previous impl]\\n\\ndef sort_array(arr):\\n # Helper function to count the number of ones in the binary representation\\n def count_ones(n):\\n return bin(n).count('1')\\n \\n # Sort the array using a custom key function\\n # The key function returns a tuple (number of ones, value) for each element\\n # This ensures that if two elements have the same number of ones, they are sorted by their value\\n sorted_arr = sorted(arr, key=lambda x: (count_ones(x), x))\\n \\n return sorted_arr\\n```\\n\\n[runtime Error]\\nTested passed:\\n\\nTests failed:\\nassert sort_array([1, 5, 2, 3, 4]) == [1, 2, 3, 4, 5] # output: [1, 2, 4, 3, 5]\\n\\n\\nAnalysis the error step by step, provide me improve method and code. Remember to follow [context] requirement. Don't forget write code for steps behind the error step.\\n[reflection on previous impl]:\\nxxx\\n\"}]": { + "reflection": "The implementation failed the test case where the input array is [1, 5, 2, 3, 4]. The issue arises because the code does not correctly sort the array based on the number of ones in their binary representation and then by their decimal value. The current implementation sorts the array primarily by the number of ones in the binary representation, but it does not handle the secondary sorting by decimal value correctly for negative numbers, as it does not account for the fact that the binary representation of negative numbers in Python is different due to two's complement representation. To fix this issue, we should modify the count_ones function to correctly handle negative numbers by taking the absolute value before converting to binary and counting ones. Additionally, we should ensure that the array is sorted by the absolute value when the number of ones is the same.", + "improved_impl": "def sort_array(arr):\n # Helper function to count the number of ones in the binary representation\n def count_ones(n):\n return bin(abs(n)).count('1')\n \n # Sort the array using a custom key function\n # The key function returns a tuple (number of ones, absolute value, original value) for each element\n # This ensures that if two elements have the same number of ones, they are sorted by their absolute value\n sorted_arr = sorted(arr, key=lambda x: (count_ones(x), abs(x), x))\n \n return sorted_arr" + }, + "[{\"role\": \"user\", \"content\": \"\\n## User Requirement:\\n对数据集进行数据清洗\\n\\n## Task\\nRecommend up to five tools from 'Available Tools' that can help solve the 'User Requirement'. \\n\\n## Available Tools:\\n{'FillMissingValue': 'Completing missing values with simple strategies.', 'MinMaxScale': 'Transform features by scaling each feature to a range, which is (0, 1).', 'StandardScale': 'Standardize features by removing the mean and scaling to unit variance.', 'MaxAbsScale': 'Scale each feature by its maximum absolute value.', 'RobustScale': 'Apply the RobustScaler to scale features using statistics that are robust to outliers.', 'OrdinalEncode': 'Encode categorical features as ordinal integers.', 'OneHotEncode': 'Apply one-hot encoding to specified categorical columns, the original columns will be dropped.', 'LabelEncode': 'Apply label encoding to specified categorical columns in-place.'}\\n\\n## Tool Selection and Instructions:\\n- Select tools most relevant to completing the 'User Requirement'.\\n- If you believe that no tools are suitable, indicate with an empty list.\\n- Only list the names of the tools, not the full schema of each tool.\\n- Ensure selected tools are listed in 'Available Tools'.\\n\"}]": { + "recommend_tools": [ + "FillMissingValue", + "MinMaxScale", + "StandardScale", + "MaxAbsScale", + "RobustScale" + ] + }, + "[{\"role\": \"user\", \"content\": \"\\n# Background\\nAs a data scientist, you need to help user to achieve their goal [构造数据集并进行数据清洗] step-by-step in an continuous Jupyter notebook.\\n\\n## Done Tasks\\n```python\\n import pandas as pd\\n df = pd.DataFrame({\\n 'a': [1, 2, 3, 4, 5],\\n 'b': [1.1, 2.2, 3.3, 4.4, np.nan],\\n 'c': ['aa', 'bb', 'cc', 'dd', 'ee'],\\n 'd': [1, 2, 3, 4, 5]\\n })\\n```end\\n\\n## Current Task\\n对数据集进行数据清洗\\n\\n# Latest Data Info\\nLatest data info after previous tasks:\\n\\n\\n# Task\\nWrite complete code for 'Current Task'. And avoid duplicating code from 'Done Tasks', such as repeated import of packages, reading data, etc.\\nSpecifically, \\nThe current task is about data preprocessing, please note the following:\\n- Monitor data types per column, applying appropriate methods.\\n- Ensure operations are on existing dataset columns.\\n- Avoid writing processed data to files.\\n- Avoid any change to label column, such as standardization, etc.\\n- Prefer alternatives to one-hot encoding for categorical data.\\n- Only encode or scale necessary columns to allow for potential feature-specific engineering tasks (like time_extract, binning, extraction, etc.) later.\\n- Each step do data preprocessing to train, must do same for test separately at the same time.\\n\\n\\n# Capabilities\\n- You can utilize pre-defined tools in any code lines from 'Available Tools' in the form of Python Class.\\n- You can freely combine the use of any other public packages, like sklearn, numpy, pandas, etc..\\n\\n# Available Tools:\\nEach Class tool is described in JSON format. When you call a tool, import the tool from its path first.\\n{'FillMissingValue': {'type': 'class', 'description': 'Completing missing values with simple strategies.', 'methods': {'__init__': {'type': 'function', 'description': 'Initialize self.', 'parameters': {'properties': {'features': {'type': 'list', 'description': 'Columns to be processed.'}, 'strategy': {'type': 'str', 'description': \\\"The imputation strategy, notice 'mean' and 'median' can only be used for numeric features. Enum: ['mean', 'median', 'most_frequent', 'constant']. Defaults to 'mean'.\\\", 'default': \\\"'mean'\\\", 'enum': [\\\"'mean'\\\", \\\"'median'\\\", \\\"'most_frequent'\\\", \\\"'constant'\\\"]}, 'fill_value': {'type': 'int', 'description': 'Fill_value is used to replace all occurrences of missing_values. Defaults to None.', 'default': 'None'}}, 'required': ['features']}}, 'fit': {'type': 'function', 'description': 'Fit a model to be used in subsequent transform.', 'parameters': {'properties': {'df': {'type': 'pd.DataFrame', 'description': 'The input DataFrame.'}}, 'required': ['df']}}, 'fit_transform': {'type': 'function', 'description': 'Fit and transform the input DataFrame.', 'parameters': {'properties': {'df': {'type': 'pd.DataFrame', 'description': 'The input DataFrame.'}}, 'required': ['df']}, 'returns': [{'type': 'pd.DataFrame', 'description': 'The transformed DataFrame.'}]}, 'transform': {'type': 'function', 'description': 'Transform the input DataFrame with the fitted model.', 'parameters': {'properties': {'df': {'type': 'pd.DataFrame', 'description': 'The input DataFrame.'}}, 'required': ['df']}, 'returns': [{'type': 'pd.DataFrame', 'description': 'The transformed DataFrame.'}]}}, 'tool_path': 'metagpt/tools/libs/data_preprocess.py'}, 'MinMaxScale': {'type': 'class', 'description': 'Transform features by scaling each feature to a range, which is (0, 1).', 'methods': {'__init__': {'type': 'function', 'description': 'Initialize self.', 'parameters': {'properties': {'features': {'type': 'list', 'description': 'Columns to be processed.'}}, 'required': ['features']}}, 'fit': {'type': 'function', 'description': 'Fit a model to be used in subsequent transform.', 'parameters': {'properties': {'df': {'type': 'pd.DataFrame', 'description': 'The input DataFrame.'}}, 'required': ['df']}}, 'fit_transform': {'type': 'function', 'description': 'Fit and transform the input DataFrame.', 'parameters': {'properties': {'df': {'type': 'pd.DataFrame', 'description': 'The input DataFrame.'}}, 'required': ['df']}, 'returns': [{'type': 'pd.DataFrame', 'description': 'The transformed DataFrame.'}]}, 'transform': {'type': 'function', 'description': 'Transform the input DataFrame with the fitted model.', 'parameters': {'properties': {'df': {'type': 'pd.DataFrame', 'description': 'The input DataFrame.'}}, 'required': ['df']}, 'returns': [{'type': 'pd.DataFrame', 'description': 'The transformed DataFrame.'}]}}, 'tool_path': 'metagpt/tools/libs/data_preprocess.py'}, 'StandardScale': {'type': 'class', 'description': 'Standardize features by removing the mean and scaling to unit variance.', 'methods': {'__init__': {'type': 'function', 'description': 'Initialize self.', 'parameters': {'properties': {'features': {'type': 'list', 'description': 'Columns to be processed.'}}, 'required': ['features']}}, 'fit': {'type': 'function', 'description': 'Fit a model to be used in subsequent transform.', 'parameters': {'properties': {'df': {'type': 'pd.DataFrame', 'description': 'The input DataFrame.'}}, 'required': ['df']}}, 'fit_transform': {'type': 'function', 'description': 'Fit and transform the input DataFrame.', 'parameters': {'properties': {'df': {'type': 'pd.DataFrame', 'description': 'The input DataFrame.'}}, 'required': ['df']}, 'returns': [{'type': 'pd.DataFrame', 'description': 'The transformed DataFrame.'}]}, 'transform': {'type': 'function', 'description': 'Transform the input DataFrame with the fitted model.', 'parameters': {'properties': {'df': {'type': 'pd.DataFrame', 'description': 'The input DataFrame.'}}, 'required': ['df']}, 'returns': [{'type': 'pd.DataFrame', 'description': 'The transformed DataFrame.'}]}}, 'tool_path': 'metagpt/tools/libs/data_preprocess.py'}, 'MaxAbsScale': {'type': 'class', 'description': 'Scale each feature by its maximum absolute value.', 'methods': {'__init__': {'type': 'function', 'description': 'Initialize self.', 'parameters': {'properties': {'features': {'type': 'list', 'description': 'Columns to be processed.'}}, 'required': ['features']}}, 'fit': {'type': 'function', 'description': 'Fit a model to be used in subsequent transform.', 'parameters': {'properties': {'df': {'type': 'pd.DataFrame', 'description': 'The input DataFrame.'}}, 'required': ['df']}}, 'fit_transform': {'type': 'function', 'description': 'Fit and transform the input DataFrame.', 'parameters': {'properties': {'df': {'type': 'pd.DataFrame', 'description': 'The input DataFrame.'}}, 'required': ['df']}, 'returns': [{'type': 'pd.DataFrame', 'description': 'The transformed DataFrame.'}]}, 'transform': {'type': 'function', 'description': 'Transform the input DataFrame with the fitted model.', 'parameters': {'properties': {'df': {'type': 'pd.DataFrame', 'description': 'The input DataFrame.'}}, 'required': ['df']}, 'returns': [{'type': 'pd.DataFrame', 'description': 'The transformed DataFrame.'}]}}, 'tool_path': 'metagpt/tools/libs/data_preprocess.py'}, 'RobustScale': {'type': 'class', 'description': 'Apply the RobustScaler to scale features using statistics that are robust to outliers.', 'methods': {'__init__': {'type': 'function', 'description': 'Initialize self.', 'parameters': {'properties': {'features': {'type': 'list', 'description': 'Columns to be processed.'}}, 'required': ['features']}}, 'fit': {'type': 'function', 'description': 'Fit a model to be used in subsequent transform.', 'parameters': {'properties': {'df': {'type': 'pd.DataFrame', 'description': 'The input DataFrame.'}}, 'required': ['df']}}, 'fit_transform': {'type': 'function', 'description': 'Fit and transform the input DataFrame.', 'parameters': {'properties': {'df': {'type': 'pd.DataFrame', 'description': 'The input DataFrame.'}}, 'required': ['df']}, 'returns': [{'type': 'pd.DataFrame', 'description': 'The transformed DataFrame.'}]}, 'transform': {'type': 'function', 'description': 'Transform the input DataFrame with the fitted model.', 'parameters': {'properties': {'df': {'type': 'pd.DataFrame', 'description': 'The input DataFrame.'}}, 'required': ['df']}, 'returns': [{'type': 'pd.DataFrame', 'description': 'The transformed DataFrame.'}]}}, 'tool_path': 'metagpt/tools/libs/data_preprocess.py'}}\\n\\n# Output Example:\\nwhen current task is \\\"do data preprocess, like fill missing value, handle outliers, etc.\\\", the code can be like:\\n```python\\n# Step 1: fill missing value\\n# Tools used: ['FillMissingValue']\\nfrom metagpt.tools.libs.data_preprocess import FillMissingValue\\n\\ntrain_processed = train.copy()\\ntest_processed = test.copy()\\nnum_cols = train_processed.select_dtypes(include='number').columns.tolist()\\nif 'label' in num_cols:\\n num_cols.remove('label')\\nfill_missing_value = FillMissingValue(features=num_cols, strategy='mean')\\nfill_missing_value.fit(train_processed)\\ntrain_processed = fill_missing_value.transform(train_processed)\\ntest_processed = fill_missing_value.transform(test_processed)\\n\\n# Step 2: handle outliers\\nfor col in num_cols:\\n low, high = train_processed[col].quantile([0.01, 0.99])\\n train_processed[col] = train_processed[col].clip(low, high)\\n test_processed[col] = test_processed[col].clip(low, high)\\n```end\\n\\n# Constraints:\\n- Ensure the output new code is executable in the same Jupyter notebook with previous tasks code have been executed.\\n- Always prioritize using pre-defined tools for the same functionality.\\n- Always copy the DataFrame before processing it and use the copy to process.\\n\"}]": { + "code": "# Step 1: Fill missing values\nfrom metagpt.tools.libs.data_preprocess import FillMissingValue\n\n# Create a copy of the DataFrame to avoid modifying the original one\ndf_processed = df.copy()\n\n# Define the columns with missing values\nmissing_value_columns = df_processed.columns[df_processed.isnull().any()].tolist()\n\n# Initialize the FillMissingValue tool with the strategy to fill missing values with the mean\nfill_missing_value = FillMissingValue(features=missing_value_columns, strategy='mean')\n\n# Fit and transform the DataFrame\ndf_processed = fill_missing_value.fit_transform(df_processed)\n\n# Step 2: Scale numeric columns\nfrom metagpt.tools.libs.data_preprocess import MinMaxScale\n\n# Define the numeric columns to be scaled\nnumeric_columns = df_processed.select_dtypes(include=['int64', 'float64']).columns.tolist()\n\n# Initialize the MinMaxScale tool\nminmax_scale = MinMaxScale(features=numeric_columns)\n\n# Fit and transform the DataFrame\ndf_processed = minmax_scale.fit_transform(df_processed)\n\n# Display the processed DataFrame\ndisplay(df_processed)" + }, + "[{\"role\": \"user\", \"content\": \"\\n## User Requirement:\\nclean and preprocess the data\\n\\n## Task\\nRecommend up to five tools from 'Available Tools' that can help solve the 'User Requirement'. \\n\\n## Available Tools:\\n{'FillMissingValue': 'Filling missing values', 'SplitBins': 'Bin continuous data into intervals and return the bin identifier encoded as an integer value'}\\n\\n## Tool Selection and Instructions:\\n- Select tools most relevant to completing the 'User Requirement'.\\n- If you believe that no tools are suitable, indicate with an empty list.\\n- Only list the names of the tools, not the full schema of each tool.\\n- Ensure selected tools are listed in 'Available Tools'.\\n\"}]": { + "recommend_tools": [ + "FillMissingValue" + ] + }, + "[{\"role\": \"system\", \"content\": \"You are Code Interpreter, a world-class programmer that can complete any goal by executing code. Strictly follow the plan and generate code step by step. Each step of the code will be executed on the user's machine, and the user will provide the code execution results to you.**Notice: The code for the next step depends on the code for the previous step. Must reuse variables in the lastest other code directly, dont creat it again, it is very import for you. Use !pip install in a standalone block to install missing packages.Usually the libraries you need are already installed.Dont check if packages already imported.**\"}, {\"role\": \"user\", \"content\": \"\\n## User Requirement\\n构造数据集并进行数据清洗\\n## Context\\n\\n## Current Plan\\n[Task(task_id='1', dependent_task_ids=[], instruction='随机生成一个pandas DataFrame数据集', task_type='other', code=\\\"\\\\n import pandas as pd\\\\n df = pd.DataFrame({\\\\n 'a': [1, 2, 3, 4, 5],\\\\n 'b': [1.1, 2.2, 3.3, 4.4, np.nan],\\\\n 'c': ['aa', 'bb', 'cc', 'dd', 'ee'],\\\\n 'd': [1, 2, 3, 4, 5]\\\\n })\\\\n \\\", result='', is_success=False, is_finished=True), Task(task_id='2', dependent_task_ids=['1'], instruction='对数据集进行数据清洗', task_type='data_preprocess', code='', result='', is_success=False, is_finished=False)]\\n## Current Task\\n{\\\"task_id\\\":\\\"2\\\",\\\"dependent_task_ids\\\":[\\\"1\\\"],\\\"instruction\\\":\\\"对数据集进行数据清洗\\\",\\\"task_type\\\":\\\"data_preprocess\\\",\\\"code\\\":\\\"\\\",\\\"result\\\":\\\"\\\",\\\"is_success\\\":false,\\\"is_finished\\\":false}\\n\"}, {\"role\": \"user\", \"content\": \"\\n# Instruction\\nWrite complete code for 'Current Task'. And avoid duplicating code from finished tasks, such as repeated import of packages, reading data, etc.\\nSpecifically, \\nThe current task is about data preprocessing, please note the following:\\n- Monitor data types per column, applying appropriate methods.\\n- Ensure operations are on existing dataset columns.\\n- Avoid writing processed data to files.\\n- Avoid any change to label column, such as standardization, etc.\\n- Prefer alternatives to one-hot encoding for categorical data.\\n- Only encode or scale necessary columns to allow for potential feature-specific engineering tasks (like time_extract, binning, extraction, etc.) later.\\n- Each step do data preprocessing to train, must do same for test separately at the same time.\\n\\n\\n# Capabilities\\n- You can utilize pre-defined tools in any code lines from 'Available Tools' in the form of Python Class.\\n- You can freely combine the use of any other public packages, like sklearn, numpy, pandas, etc..\\n\\n# Available Tools (can be empty):\\nEach Class tool is described in JSON format. When you call a tool, import the tool first.\\n{'FillMissingValue': {'type': 'class', 'description': 'Completing missing values with simple strategies.', 'methods': {'__init__': {'type': 'function', 'description': 'Initialize self.', 'parameters': {'properties': {'features': {'type': 'list', 'description': 'Columns to be processed.'}, 'strategy': {'type': 'str', 'description': \\\"The imputation strategy, notice 'mean' and 'median' can only be used for numeric features. Enum: ['mean', 'median', 'most_frequent', 'constant']. Defaults to 'mean'.\\\", 'default': \\\"'mean'\\\", 'enum': [\\\"'mean'\\\", \\\"'median'\\\", \\\"'most_frequent'\\\", \\\"'constant'\\\"]}, 'fill_value': {'type': 'int', 'description': 'Fill_value is used to replace all occurrences of missing_values. Defaults to None.', 'default': 'None'}}, 'required': ['features']}}, 'fit': {'type': 'function', 'description': 'Fit a model to be used in subsequent transform.', 'parameters': {'properties': {'df': {'type': 'pd.DataFrame', 'description': 'The input DataFrame.'}}, 'required': ['df']}}, 'fit_transform': {'type': 'function', 'description': 'Fit and transform the input DataFrame.', 'parameters': {'properties': {'df': {'type': 'pd.DataFrame', 'description': 'The input DataFrame.'}}, 'required': ['df']}, 'returns': [{'type': 'pd.DataFrame', 'description': 'The transformed DataFrame.'}]}, 'transform': {'type': 'function', 'description': 'Transform the input DataFrame with the fitted model.', 'parameters': {'properties': {'df': {'type': 'pd.DataFrame', 'description': 'The input DataFrame.'}}, 'required': ['df']}, 'returns': [{'type': 'pd.DataFrame', 'description': 'The transformed DataFrame.'}]}}, 'tool_path': 'metagpt/tools/libs/data_preprocess.py'}, 'MinMaxScale': {'type': 'class', 'description': 'Transform features by scaling each feature to a range, which is (0, 1).', 'methods': {'__init__': {'type': 'function', 'description': 'Initialize self.', 'parameters': {'properties': {'features': {'type': 'list', 'description': 'Columns to be processed.'}}, 'required': ['features']}}, 'fit': {'type': 'function', 'description': 'Fit a model to be used in subsequent transform.', 'parameters': {'properties': {'df': {'type': 'pd.DataFrame', 'description': 'The input DataFrame.'}}, 'required': ['df']}}, 'fit_transform': {'type': 'function', 'description': 'Fit and transform the input DataFrame.', 'parameters': {'properties': {'df': {'type': 'pd.DataFrame', 'description': 'The input DataFrame.'}}, 'required': ['df']}, 'returns': [{'type': 'pd.DataFrame', 'description': 'The transformed DataFrame.'}]}, 'transform': {'type': 'function', 'description': 'Transform the input DataFrame with the fitted model.', 'parameters': {'properties': {'df': {'type': 'pd.DataFrame', 'description': 'The input DataFrame.'}}, 'required': ['df']}, 'returns': [{'type': 'pd.DataFrame', 'description': 'The transformed DataFrame.'}]}}, 'tool_path': 'metagpt/tools/libs/data_preprocess.py'}, 'StandardScale': {'type': 'class', 'description': 'Standardize features by removing the mean and scaling to unit variance.', 'methods': {'__init__': {'type': 'function', 'description': 'Initialize self.', 'parameters': {'properties': {'features': {'type': 'list', 'description': 'Columns to be processed.'}}, 'required': ['features']}}, 'fit': {'type': 'function', 'description': 'Fit a model to be used in subsequent transform.', 'parameters': {'properties': {'df': {'type': 'pd.DataFrame', 'description': 'The input DataFrame.'}}, 'required': ['df']}}, 'fit_transform': {'type': 'function', 'description': 'Fit and transform the input DataFrame.', 'parameters': {'properties': {'df': {'type': 'pd.DataFrame', 'description': 'The input DataFrame.'}}, 'required': ['df']}, 'returns': [{'type': 'pd.DataFrame', 'description': 'The transformed DataFrame.'}]}, 'transform': {'type': 'function', 'description': 'Transform the input DataFrame with the fitted model.', 'parameters': {'properties': {'df': {'type': 'pd.DataFrame', 'description': 'The input DataFrame.'}}, 'required': ['df']}, 'returns': [{'type': 'pd.DataFrame', 'description': 'The transformed DataFrame.'}]}}, 'tool_path': 'metagpt/tools/libs/data_preprocess.py'}, 'MaxAbsScale': {'type': 'class', 'description': 'Scale each feature by its maximum absolute value.', 'methods': {'__init__': {'type': 'function', 'description': 'Initialize self.', 'parameters': {'properties': {'features': {'type': 'list', 'description': 'Columns to be processed.'}}, 'required': ['features']}}, 'fit': {'type': 'function', 'description': 'Fit a model to be used in subsequent transform.', 'parameters': {'properties': {'df': {'type': 'pd.DataFrame', 'description': 'The input DataFrame.'}}, 'required': ['df']}}, 'fit_transform': {'type': 'function', 'description': 'Fit and transform the input DataFrame.', 'parameters': {'properties': {'df': {'type': 'pd.DataFrame', 'description': 'The input DataFrame.'}}, 'required': ['df']}, 'returns': [{'type': 'pd.DataFrame', 'description': 'The transformed DataFrame.'}]}, 'transform': {'type': 'function', 'description': 'Transform the input DataFrame with the fitted model.', 'parameters': {'properties': {'df': {'type': 'pd.DataFrame', 'description': 'The input DataFrame.'}}, 'required': ['df']}, 'returns': [{'type': 'pd.DataFrame', 'description': 'The transformed DataFrame.'}]}}, 'tool_path': 'metagpt/tools/libs/data_preprocess.py'}, 'RobustScale': {'type': 'class', 'description': 'Apply the RobustScaler to scale features using statistics that are robust to outliers.', 'methods': {'__init__': {'type': 'function', 'description': 'Initialize self.', 'parameters': {'properties': {'features': {'type': 'list', 'description': 'Columns to be processed.'}}, 'required': ['features']}}, 'fit': {'type': 'function', 'description': 'Fit a model to be used in subsequent transform.', 'parameters': {'properties': {'df': {'type': 'pd.DataFrame', 'description': 'The input DataFrame.'}}, 'required': ['df']}}, 'fit_transform': {'type': 'function', 'description': 'Fit and transform the input DataFrame.', 'parameters': {'properties': {'df': {'type': 'pd.DataFrame', 'description': 'The input DataFrame.'}}, 'required': ['df']}, 'returns': [{'type': 'pd.DataFrame', 'description': 'The transformed DataFrame.'}]}, 'transform': {'type': 'function', 'description': 'Transform the input DataFrame with the fitted model.', 'parameters': {'properties': {'df': {'type': 'pd.DataFrame', 'description': 'The input DataFrame.'}}, 'required': ['df']}, 'returns': [{'type': 'pd.DataFrame', 'description': 'The transformed DataFrame.'}]}}, 'tool_path': 'metagpt/tools/libs/data_preprocess.py'}}\\n\\n# Constraints:\\n- Ensure the output new code is executable in the same Jupyter notebook with previous tasks code have been executed.\\n- Always prioritize using pre-defined tools for the same functionality.\\n\"}]": { + "code": "from metagpt.tools.libs.data_preprocess import FillMissingValue\n\n# Instantiate the FillMissingValue class for column 'b'\nfill_missing_b = FillMissingValue(features=['b'], strategy='mean')\n\n# Fit and transform the DataFrame\n# Since we only have one DataFrame, we will use it as both train and test for demonstration\n# In practice, train and test data should be handled separately\ntrain_df = fill_missing_b.fit_transform(df)\ntest_df = fill_missing_b.transform(df)\n\n# Check the results\ntrain_df\n" + }, + "[{\"role\": \"system\", \"content\": \"You are Code Interpreter, a world-class programmer that can complete any goal by executing code. Strictly follow the plan and generate code step by step. Each step of the code will be executed on the user's machine, and the user will provide the code execution results to you.**Notice: The code for the next step depends on the code for the previous step. Must reuse variables in the lastest other code directly, dont creat it again, it is very import for you. Use !pip install in a standalone block to install missing packages.Usually the libraries you need are already installed.Dont check if packages already imported.**\"}, {\"role\": \"user\", \"content\": \"\\n ## User Requirement\\n read a dataset test.csv and print its head\\n ## Current Plan\\n [\\n {\\n \\\"task_id\\\": \\\"1\\\",\\n \\\"dependent_task_ids\\\": [],\\n \\\"instruction\\\": \\\"import pandas and load the dataset from 'test.csv'.\\\",\\n \\\"task_type\\\": \\\"\\\",\\n \\\"code\\\": \\\"\\\",\\n \\\"result\\\": \\\"\\\",\\n \\\"is_finished\\\": false\\n },\\n {\\n \\\"task_id\\\": \\\"2\\\",\\n \\\"dependent_task_ids\\\": [\\n \\\"1\\\"\\n ],\\n \\\"instruction\\\": \\\"Print the head of the dataset to display the first few rows.\\\",\\n \\\"task_type\\\": \\\"\\\",\\n \\\"code\\\": \\\"\\\",\\n \\\"result\\\": \\\"\\\",\\n \\\"is_finished\\\": false\\n }\\n ]\\n ## Current Task\\n {\\\"task_id\\\": \\\"1\\\", \\\"dependent_task_ids\\\": [], \\\"instruction\\\": \\\"import pandas and load the dataset from 'test.csv'.\\\", \\\"task_type\\\": \\\"\\\", \\\"code\\\": \\\"\\\", \\\"result\\\": \\\"\\\", \\\"is_finished\\\": false}\\n \"}, {\"role\": \"assistant\", \"content\": \"import pandas as pd\\ndata = pd.read_excel('test.csv')\\ndata\"}, {\"role\": \"user\", \"content\": \"\\n Traceback (most recent call last):\\n File \\\"\\\", line 2, in \\n File \\\"/Users/gary/miniconda3/envs/py39_scratch/lib/python3.9/site-packages/pandas/io/excel/_base.py\\\", line 478, in read_excel\\n io = ExcelFile(io, storage_options=storage_options, engine=engine)\\n File \\\"/Users/gary/miniconda3/envs/py39_scratch/lib/python3.9/site-packages/pandas/io/excel/_base.py\\\", line 1500, in __init__\\n raise ValueError(\\n ValueError: Excel file format cannot be determined, you must specify an engine manually.\\n \"}]": { + "language": "python", + "code": "import pandas as pd\ndata = pd.read_csv('test.csv')" + }, + "[{\"role\": \"system\", \"content\": \"You are Code Interpreter, a world-class programmer that can complete any goal by executing code. Strictly follow the plan and generate code step by step. Each step of the code will be executed on the user's machine, and the user will provide the code execution results to you.**Notice: The code for the next step depends on the code for the previous step. Must reuse variables in the lastest other code directly, dont creat it again, it is very import for you. Use !pip install in a standalone block to install missing packages.Usually the libraries you need are already installed.Dont check if packages already imported.**\"}, {\"role\": \"user\", \"content\": \"\\n ## User Requirement\\n read a dataset test.csv and print its head\\n ## Current Plan\\n [\\n {\\n \\\"task_id\\\": \\\"1\\\",\\n \\\"dependent_task_ids\\\": [],\\n \\\"instruction\\\": \\\"import pandas and load the dataset from 'test.csv'.\\\",\\n \\\"task_type\\\": \\\"\\\",\\n \\\"code\\\": \\\"import pandas as pd\\ndata = pd.read_csv('test.csv')\\\",\\n \\\"result\\\": \\\"\\\",\\n \\\"is_finished\\\": true\\n },\\n {\\n \\\"task_id\\\": \\\"2\\\",\\n \\\"dependent_task_ids\\\": [\\n \\\"1\\\"\\n ],\\n \\\"instruction\\\": \\\"Print the head of the dataset to display the first few rows.\\\",\\n \\\"task_type\\\": \\\"\\\",\\n \\\"code\\\": \\\"\\\",\\n \\\"result\\\": \\\"\\\",\\n \\\"is_finished\\\": false\\n }\\n ]\\n ## Current Task\\n {\\\"task_id\\\": \\\"2\\\", \\\"dependent_task_ids\\\": [\\\"1\\\"], \\\"instruction\\\": \\\"Print the head of the dataset to display the first few rows.\\\", \\\"task_type\\\": \\\"\\\", \\\"code\\\": \\\"\\\", \\\"result\\\": \\\"\\\", \\\"is_finished\\\": false}\\n \"}]": { + "language": "python", + "code": "print(data.head())" + }, + "\n # Context:\n user: run analysis on sklearn iris dataset\n # Task:\n Based on the context, write a plan or modify an existing plan of what you should do to achieve the goal. A plan consists of one to 5 tasks.\n If you are modifying an existing plan, carefully follow the instruction, don't make unnecessary changes. Give the whole plan unless instructed to modify only one task of the plan.\n If you encounter errors on the current task, revise and output the current single task only.\n Output a list of jsons following the format:\n ```json\n [\n {\n \"task_id\": str = \"unique identifier for a task in plan, can be an ordinal\",\n \"dependent_task_ids\": list[str] = \"ids of tasks prerequisite to this task\",\n \"instruction\": \"what you should do in this task, one short phrase or sentence\",\n },\n ...\n ]\n ```\n ": "```json\n[\n {\n \"task_id\": \"1\",\n \"dependent_task_ids\": [],\n \"instruction\": \"Import the Iris dataset from sklearn.datasets\"\n },\n {\n \"task_id\": \"2\",\n \"dependent_task_ids\": [\"1\"],\n \"instruction\": \"Perform exploratory data analysis to understand the dataset\"\n },\n {\n \"task_id\": \"3\",\n \"dependent_task_ids\": [\"2\"],\n \"instruction\": \"Preprocess the data to prepare it for modeling\"\n },\n {\n \"task_id\": \"4\",\n \"dependent_task_ids\": [\"3\"],\n \"instruction\": \"Split the dataset into training and testing sets\"\n },\n {\n \"task_id\": \"5\",\n \"dependent_task_ids\": [\"4\"],\n \"instruction\": \"Train a classifier using the training set and evaluate it using the test set\"\n }\n]\n```", + "[{\"role\": \"user\", \"content\": \"\\nPlease assign a task type to each task in the list below from the given categories:\\nTask 1: Import the Iris dataset from sklearn.datasets\\nTask 2: Perform exploratory data analysis to understand the dataset\\nTask 3: Preprocess the data to prepare it for modeling\\nTask 4: Split the dataset into training and testing sets\\nTask 5: Train a classifier using the training set and evaluate it using the test set\\n\\n## All Task Type:\\n- **eda**: For performing exploratory data analysis\\n- **data_preprocess**: Only for changing value inplace.\\n- **feature_engineering**: Only for creating new columns for input data.\\n- **model_train**: Only for training model.\\n- **model_evaluate**: Only for evaluating model.\\n- **stable_diffusion**: Related to text2image, image2image using stable diffusion model.\\n- **image2webpage**: For converting image into webpage code.\\n- **web_scraping**: For scraping data from web pages.\\n- **other**: Any tools not in the defined categories\\n\"}]": { + "task_type": [ + "other", + "eda", + "data_preprocess", + "data_preprocess", + "model_train", + "model_evaluate" + ] + }, + "\n # Context:\n user: \n## User Requirement\nRun data analysis on sklearn Iris dataset, include a plot\n## Context\n\n## Current Plan\n[]\n## Current Task\n{}\n\n # Task:\n Based on the context, write a plan or modify an existing plan of what you should do to achieve the goal. A plan consists of one to 3 tasks.\n If you are modifying an existing plan, carefully follow the instruction, don't make unnecessary changes. Give the whole plan unless instructed to modify only one task of the plan.\n If you encounter errors on the current task, revise and output the current single task only.\n Output a list of jsons following the format:\n ```json\n [\n {\n \"task_id\": str = \"unique identifier for a task in plan, can be an ordinal\",\n \"dependent_task_ids\": list[str] = \"ids of tasks prerequisite to this task\",\n \"instruction\": \"what you should do in this task, one short phrase or sentence\",\n },\n ...\n ]\n ```\n ": "```json\n[\n {\n \"task_id\": \"1\",\n \"dependent_task_ids\": [],\n \"instruction\": \"Load the sklearn Iris dataset.\"\n },\n {\n \"task_id\": \"2\",\n \"dependent_task_ids\": [\"1\"],\n \"instruction\": \"Perform exploratory data analysis on the Iris dataset.\"\n },\n {\n \"task_id\": \"3\",\n \"dependent_task_ids\": [\"2\"],\n \"instruction\": \"Create a plot visualizing the Iris dataset.\"\n }\n]\n```", + "[{\"role\": \"user\", \"content\": \"\\nPlease assign a task type to each task in the list below from the given categories:\\nTask 1: Load the sklearn Iris dataset.\\nTask 2: Perform exploratory data analysis on the Iris dataset.\\nTask 3: Create a plot visualizing the Iris dataset.\\n\\n## All Task Type:\\n- **eda**: For performing exploratory data analysis\\n- **data_preprocess**: Only for changing value inplace.\\n- **feature_engineering**: Only for creating new columns for input data.\\n- **model_train**: Only for training model.\\n- **model_evaluate**: Only for evaluating model.\\n- **stable_diffusion**: Related to text2image, image2image using stable diffusion model.\\n- **image2webpage**: For converting image into webpage code.\\n- **web_scraping**: For scraping data from web pages.\\n- **other**: Any tools not in the defined categories\\n\"}]": { + "task_type": [ + "other", + "eda", + "other" + ] + }, + "[{\"role\": \"system\", \"content\": \"You are Code Interpreter, a world-class programmer that can complete any goal by executing code. Strictly follow the plan and generate code step by step. Each step of the code will be executed on the user's machine, and the user will provide the code execution results to you.**Notice: The code for the next step depends on the code for the previous step. Must reuse variables in the lastest other code directly, dont creat it again, it is very import for you. Use !pip install in a standalone block to install missing packages.Usually the libraries you need are already installed.Dont check if packages already imported.**\"}, {\"role\": \"user\", \"content\": \"\\n## User Requirement\\nRun data analysis on sklearn Iris dataset, include a plot\\n## Context\\n\\n## Current Plan\\n[\\n {\\n \\\"task_id\\\": \\\"1\\\",\\n \\\"dependent_task_ids\\\": [],\\n \\\"instruction\\\": \\\"Load the sklearn Iris dataset.\\\",\\n \\\"task_type\\\": \\\"other\\\",\\n \\\"code\\\": \\\"\\\",\\n \\\"result\\\": \\\"\\\",\\n \\\"is_success\\\": false,\\n \\\"is_finished\\\": false\\n },\\n {\\n \\\"task_id\\\": \\\"2\\\",\\n \\\"dependent_task_ids\\\": [\\n \\\"1\\\"\\n ],\\n \\\"instruction\\\": \\\"Perform exploratory data analysis on the Iris dataset.\\\",\\n \\\"task_type\\\": \\\"eda\\\",\\n \\\"code\\\": \\\"\\\",\\n \\\"result\\\": \\\"\\\",\\n \\\"is_success\\\": false,\\n \\\"is_finished\\\": false\\n },\\n {\\n \\\"task_id\\\": \\\"3\\\",\\n \\\"dependent_task_ids\\\": [\\n \\\"2\\\"\\n ],\\n \\\"instruction\\\": \\\"Create a plot visualizing the Iris dataset.\\\",\\n \\\"task_type\\\": \\\"other\\\",\\n \\\"code\\\": \\\"\\\",\\n \\\"result\\\": \\\"\\\",\\n \\\"is_success\\\": false,\\n \\\"is_finished\\\": false\\n }\\n]\\n## Current Task\\n{\\\"task_id\\\":\\\"1\\\",\\\"dependent_task_ids\\\":[],\\\"instruction\\\":\\\"Load the sklearn Iris dataset.\\\",\\\"task_type\\\":\\\"other\\\",\\\"code\\\":\\\"\\\",\\\"result\\\":\\\"\\\",\\\"is_success\\\":false,\\\"is_finished\\\":false}\\n\"}, {\"role\": \"user\", \"content\": \"\\n# Instruction\\nWrite complete code for 'Current Task'. And avoid duplicating code from finished tasks, such as repeated import of packages, reading data, etc.\\nSpecifically, \\n\\n# Capabilities\\n- You can utilize pre-defined tools in any code lines from 'Available Tools' in the form of Python Class.\\n- You can freely combine the use of any other public packages, like sklearn, numpy, pandas, etc..\\n\\n# Available Tools (can be empty):\\nEach Class tool is described in JSON format. When you call a tool, import the tool first.\\n{}\\n\\n# Constraints:\\n- Ensure the output new code is executable in the same Jupyter notebook with previous tasks code have been executed.\\n- Always prioritize using pre-defined tools for the same functionality.\\n\"}]": { + "code": "from sklearn.datasets import load_iris\niris_data = load_iris()" + }, + "[{\"role\": \"system\", \"content\": \"You are Code Interpreter, a world-class programmer that can complete any goal by executing code. Strictly follow the plan and generate code step by step. Each step of the code will be executed on the user's machine, and the user will provide the code execution results to you.**Notice: The code for the next step depends on the code for the previous step. Must reuse variables in the lastest other code directly, dont creat it again, it is very import for you. Use !pip install in a standalone block to install missing packages.Usually the libraries you need are already installed.Dont check if packages already imported.**\"}, {\"role\": \"user\", \"content\": \"\\n## User Requirement\\nRun data analysis on sklearn Iris dataset, include a plot\\n## Context\\n\\n## Current Plan\\n[\\n {\\n \\\"task_id\\\": \\\"1\\\",\\n \\\"dependent_task_ids\\\": [],\\n \\\"instruction\\\": \\\"Load the sklearn Iris dataset.\\\",\\n \\\"task_type\\\": \\\"other\\\",\\n \\\"code\\\": \\\"\\\",\\n \\\"result\\\": \\\"a successful run\\\",\\n \\\"is_success\\\": true,\\n \\\"is_finished\\\": true\\n },\\n {\\n \\\"task_id\\\": \\\"2\\\",\\n \\\"dependent_task_ids\\\": [\\n \\\"1\\\"\\n ],\\n \\\"instruction\\\": \\\"Perform exploratory data analysis on the Iris dataset.\\\",\\n \\\"task_type\\\": \\\"eda\\\",\\n \\\"code\\\": \\\"\\\",\\n \\\"result\\\": \\\"\\\",\\n \\\"is_success\\\": false,\\n \\\"is_finished\\\": false\\n },\\n {\\n \\\"task_id\\\": \\\"3\\\",\\n \\\"dependent_task_ids\\\": [\\n \\\"2\\\"\\n ],\\n \\\"instruction\\\": \\\"Create a plot visualizing the Iris dataset.\\\",\\n \\\"task_type\\\": \\\"other\\\",\\n \\\"code\\\": \\\"\\\",\\n \\\"result\\\": \\\"\\\",\\n \\\"is_success\\\": false,\\n \\\"is_finished\\\": false\\n }\\n]\\n## Current Task\\n{\\\"task_id\\\":\\\"2\\\",\\\"dependent_task_ids\\\":[\\\"1\\\"],\\\"instruction\\\":\\\"Perform exploratory data analysis on the Iris dataset.\\\",\\\"task_type\\\":\\\"eda\\\",\\\"code\\\":\\\"\\\",\\\"result\\\":\\\"\\\",\\\"is_success\\\":false,\\\"is_finished\\\":false}\\n\"}, {\"role\": \"user\", \"content\": \"\\n# Instruction\\nWrite complete code for 'Current Task'. And avoid duplicating code from finished tasks, such as repeated import of packages, reading data, etc.\\nSpecifically, \\n\\n# Capabilities\\n- You can utilize pre-defined tools in any code lines from 'Available Tools' in the form of Python Class.\\n- You can freely combine the use of any other public packages, like sklearn, numpy, pandas, etc..\\n\\n# Available Tools (can be empty):\\nEach Class tool is described in JSON format. When you call a tool, import the tool first.\\n{}\\n\\n# Constraints:\\n- Ensure the output new code is executable in the same Jupyter notebook with previous tasks code have been executed.\\n- Always prioritize using pre-defined tools for the same functionality.\\n\"}]": { + "code": "from sklearn import datasets\nimport pandas as pd\n\n# Load Iris dataset\niris = datasets.load_iris()\ndf_iris = pd.DataFrame(data=iris.data, columns=iris.feature_names)\ndf_iris['target'] = iris.target\n\n# Display basic information about the dataset\nprint(df_iris.info())\n\n# Display statistical summary of the dataset\nprint(df_iris.describe())\n\n# Display the first few rows of the dataset\nprint(df_iris.head())\n\n# Display the distribution of the target variable\ntarget_counts = df_iris['target'].value_counts()\nprint(target_counts)" + }, + "[{\"role\": \"system\", \"content\": \"You are Code Interpreter, a world-class programmer that can complete any goal by executing code. Strictly follow the plan and generate code step by step. Each step of the code will be executed on the user's machine, and the user will provide the code execution results to you.**Notice: The code for the next step depends on the code for the previous step. Must reuse variables in the lastest other code directly, dont creat it again, it is very import for you. Use !pip install in a standalone block to install missing packages.Usually the libraries you need are already installed.Dont check if packages already imported.**\"}, {\"role\": \"user\", \"content\": \"\\n## User Requirement\\nRun data analysis on sklearn Iris dataset, include a plot\\n## Context\\n\\n## Current Plan\\n[\\n {\\n \\\"task_id\\\": \\\"1\\\",\\n \\\"dependent_task_ids\\\": [],\\n \\\"instruction\\\": \\\"Load the sklearn Iris dataset.\\\",\\n \\\"task_type\\\": \\\"other\\\",\\n \\\"code\\\": \\\"\\\",\\n \\\"result\\\": \\\"a successful run\\\",\\n \\\"is_success\\\": true,\\n \\\"is_finished\\\": true\\n },\\n {\\n \\\"task_id\\\": \\\"2\\\",\\n \\\"dependent_task_ids\\\": [\\n \\\"1\\\"\\n ],\\n \\\"instruction\\\": \\\"Perform exploratory data analysis on the Iris dataset.\\\",\\n \\\"task_type\\\": \\\"eda\\\",\\n \\\"code\\\": \\\"\\\",\\n \\\"result\\\": \\\"a successful run\\\",\\n \\\"is_success\\\": true,\\n \\\"is_finished\\\": true\\n },\\n {\\n \\\"task_id\\\": \\\"3\\\",\\n \\\"dependent_task_ids\\\": [\\n \\\"2\\\"\\n ],\\n \\\"instruction\\\": \\\"Create a plot visualizing the Iris dataset.\\\",\\n \\\"task_type\\\": \\\"other\\\",\\n \\\"code\\\": \\\"\\\",\\n \\\"result\\\": \\\"\\\",\\n \\\"is_success\\\": false,\\n \\\"is_finished\\\": false\\n }\\n]\\n## Current Task\\n{\\\"task_id\\\":\\\"3\\\",\\\"dependent_task_ids\\\":[\\\"2\\\"],\\\"instruction\\\":\\\"Create a plot visualizing the Iris dataset.\\\",\\\"task_type\\\":\\\"other\\\",\\\"code\\\":\\\"\\\",\\\"result\\\":\\\"\\\",\\\"is_success\\\":false,\\\"is_finished\\\":false}\\n\"}, {\"role\": \"user\", \"content\": \"\\n# Instruction\\nWrite complete code for 'Current Task'. And avoid duplicating code from finished tasks, such as repeated import of packages, reading data, etc.\\nSpecifically, \\n\\n# Capabilities\\n- You can utilize pre-defined tools in any code lines from 'Available Tools' in the form of Python Class.\\n- You can freely combine the use of any other public packages, like sklearn, numpy, pandas, etc..\\n\\n# Available Tools (can be empty):\\nEach Class tool is described in JSON format. When you call a tool, import the tool first.\\n{}\\n\\n# Constraints:\\n- Ensure the output new code is executable in the same Jupyter notebook with previous tasks code have been executed.\\n- Always prioritize using pre-defined tools for the same functionality.\\n\"}]": { + "code": "from sklearn import datasets\nimport matplotlib.pyplot as plt\nimport pandas as pd\n\n# Load Iris dataset\niris = datasets.load_iris()\niris_df = pd.DataFrame(data=iris.data, columns=iris.feature_names)\niris_df['target'] = iris.target\niris_df['target_name'] = iris_df['target'].apply(lambda x: iris.target_names[x])\n\n# Plotting\nfig, ax = plt.subplots(figsize=(12, 8))\nfor target, target_name in zip(iris.target_names, iris.target_names):\n subset = iris_df[iris_df['target_name'] == target_name]\n ax.scatter(subset[iris.feature_names[0]], subset[iris.feature_names[1]], label=target_name)\n\nax.set_xlabel(iris.feature_names[0])\nax.set_ylabel(iris.feature_names[1])\nax.legend()\nplt.show()" + }, + "[{\"role\": \"user\", \"content\": \"\\n# Background\\nAs a data scientist, you need to help user to achieve their goal [This is a titanic passenger survival dataset, your goal is to predict passenger survival outcome. The target column is Survived. Perform data analysis, data preprocessing, feature engineering, and modeling to predict the target. Report accuracy on the eval data. Train data path: 'tests/data/ml_datasets/titanic/split_train.csv', eval data path: 'tests/data/ml_datasets/titanic/split_eval.csv'.] step-by-step in an continuous Jupyter notebook.\\n\\n## Done Tasks\\n```python\\n\\n```end\\n\\n## Current Task\\nPerform exploratory data analysis on the train dataset to understand the features and target variable.\\n\\n# Latest Data Info\\nLatest data info after previous tasks:\\n\\n\\n# Task\\nWrite complete code for 'Current Task'. And avoid duplicating code from 'Done Tasks', such as repeated import of packages, reading data, etc.\\nSpecifically, \\n\\n# Output Example:\\nwhen current task is \\\"train a lightgbm model on training data\\\", the code can be like:\\n```python\\n# Step 1: check data type and convert to numeric\\nobj_cols = train.select_dtypes(include='object').columns.tolist()\\n\\nfor col in obj_cols:\\n encoder = LabelEncoder()\\n train[col] = encoder.fit_transform(train[col].unique().tolist() + ['unknown'])\\n test[col] = test[col].apply(lambda x: x if x in encoder.classes_ else 'unknown')\\n test[col] = encoder.transform(test[col])\\n\\n# Step 2: train lightgbm model\\nmodel = LGBMClassifier()\\nmodel.fit(train, y_train)\\n```end\\n\\n# Constraints:\\n- Ensure the output new code is executable in the same Jupyter notebook with previous tasks code have been executed.\\n\"}]": { + "code": "# Perform exploratory data analysis on the train dataset\ndf_train = pd.read_csv('tests/data/ml_datasets/titanic/split_train.csv')\n\n# Display the first few rows of the dataset\ndisplay(df_train.head())\n\n# Summary statistics for numerical features\ndisplay(df_train.describe())\n\n# Summary information about the dataset including the data types and number of non-null values\ndisplay(df_train.info())\n\n# Distribution of the target variable 'Survived'\nsurvival_counts = df_train['Survived'].value_counts()\nprint(\"Survival counts:\\n\", survival_counts)\n\n# Visualizations\nimport matplotlib.pyplot as plt\nimport seaborn as sns\n\n# Distribution of the target variable\nsns.countplot(x='Survived', data=df_train)\nplt.title('Distribution of Survival')\nplt.show()\n\n# Correlation matrix heatmap to understand the relationship between features\nplt.figure(figsize=(10, 8))\nsns.heatmap(df_train.corr(), annot=True, fmt='.2f')\nplt.title('Correlation Matrix')\nplt.show()\n\n# Pairplot to visualize the pairwise relationships between features\nsns.pairplot(df_train, hue='Survived')\nplt.title('Pairplot of Features')\nplt.show()" + }, + "[{\"role\": \"system\", \"content\": \"You are an AI Python assistant. You will be given your previous implementation code of a task, runtime error results, and a hint to change the implementation appropriately. Write your full implementation \"}, {\"role\": \"user\", \"content\": \"\\nHere is an example for you.\\n\\nExample 1:\\n[previous impl]:\\n```python\\ndef add(a: int, b: int) -> int:\\n \\\"\\\"\\\"\\n Given integers a and b, return the total value of a and b.\\n \\\"\\\"\\\"\\n return a - b\\n```\\n\\n[runtime Error]:\\nTested passed:\\n\\nTests failed:\\nassert add(1, 2) == 3 # output: -1\\nassert add(1, 2) == 4 # output: -1\\n\\n[reflection on previous impl]:\\nThe implementation failed the test cases where the input integers are 1 and 2. The issue arises because the code does not add the two integers together, but instead subtracts the second integer from the first. To fix this issue, we should change the operator from `-` to `+` in the return statement. This will ensure that the function returns the correct output for the given input.\\n\\n[improved impl]:\\n```python\\ndef add(a: int, b: int) -> int:\\n \\\"\\\"\\\"\\n Given integers a and b, return the total value of a and b.\\n \\\"\\\"\\\"\\n return a + b\\n```\\n\\n[context]\\nSolve the problem in Python:\\ndef sort_array(arr):\\n \\\"\\\"\\\"\\n In this Kata, you have to sort an array of non-negative integers according to\\n number of ones in their binary representation in ascending order.\\n For similar number of ones, sort based on decimal value.\\n\\n It must be implemented like this:\\n >>> sort_array([1, 5, 2, 3, 4]) == [1, 2, 3, 4, 5]\\n >>> sort_array([-2, -3, -4, -5, -6]) == [-6, -5, -4, -3, -2]\\n >>> sort_array([1, 0, 2, 3, 4]) [0, 1, 2, 3, 4]\\n \\\"\\\"\\\"\\n\\n\\n[previous impl]\\n\\ndef sort_array(arr):\\n # Helper function to count the number of ones in the binary representation\\n def count_ones(n):\\n return bin(n).count('1')\\n \\n # Sort the array using a custom key function\\n # The key function returns a tuple (number of ones, value) for each element\\n # This ensures that if two elements have the same number of ones, they are sorted by their value\\n sorted_arr = sorted(arr, key=lambda x: (count_ones(x), x))\\n \\n return sorted_arr\\n```\\n\\n[runtime Error]\\n[user: Tested passed:\\n\\nTests failed:\\nassert sort_array([1, 5, 2, 3, 4]) == [1, 2, 3, 4, 5] # output: [1, 2, 4, 3, 5]\\n]\\n\\nAnalysis the error step by step, provide me improve method and code. Remember to follow [context] requirement. Don't forget write code for steps behind the error step.\\n[reflection on previous impl]:\\nxxx\\n\"}]": { + "reflection": "The implementation failed the test case where the input array is [1, 5, 2, 3, 4]. The issue arises because the code does not handle the sorting of elements with the same number of ones in their binary representation correctly. According to the context, for similar number of ones, the elements should be sorted based on their decimal value in ascending order. However, the output [1, 2, 4, 3, 5] shows that 3 and 4 are not in the correct order, indicating that the sorting by decimal value is not functioning as intended. To fix this issue, we should ensure that the sorting is stable, so that when two elements have the same number of ones, they maintain their relative order if their decimal values are already in ascending order.", + "improved_impl": "def sort_array(arr):\n # Helper function to count the number of ones in the binary representation\n def count_ones(n):\n return bin(n).count('1')\n \n # Sort the array using a custom key function\n # The key function returns a tuple (number of ones, value) for each element\n # This ensures that if two elements have the same number of ones, they are sorted by their value\n # We use a stable sorting algorithm to maintain the relative order of elements with the same number of ones\n sorted_arr = sorted(arr, key=lambda x: (count_ones(x), x), reverse=True)\n sorted_arr = sorted(sorted_arr, key=count_ones)\n \n return sorted_arr\n" + }, + "\n## context\n\n\n-----\n\n## format example\n[CONTENT]\n{\n \"invoice\": \"False\"\n}\n[/CONTENT]\n\n## nodes: \": # \"\n- invoice: # if it's a invoice file, return True else False\n\n\n## constraint\nLanguage: Please use the same language as Human INPUT.\nFormat: output wrapped inside [CONTENT][/CONTENT] like format example, nothing else.\n\n## action\nFollow instructions of nodes, generate output and make sure it follows the format example.\n": "[CONTENT]\n{\n \"invoice\": \"True\"\n}\n[/CONTENT]" } \ No newline at end of file diff --git a/tests/data/search_rsp_cache.json b/tests/data/search_rsp_cache.json new file mode 100644 index 000000000..7b4cc583c --- /dev/null +++ b/tests/data/search_rsp_cache.json @@ -0,0 +1,995 @@ +{ + "aiohttp-get-https://serpapi.com/search-{\"params\": {\"api_key\": \"mock-serpapi-key\", \"engine\": \"google\", \"gl\": \"us\", \"google_domain\": \"google.com\", \"hl\": \"en\", \"num\": 8, \"output\": \"json\", \"q\": \"metagpt\", \"source\": \"python\"}}": { + "search_metadata": { + "id": "65a3f6595b54ef7f1dfbcdd2", + "status": "Success", + "json_endpoint": "https://serpapi.com/searches/f3454e001dacdae1/65a3f6595b54ef7f1dfbcdd2.json", + "created_at": "2024-01-14 14:57:29 UTC", + "processed_at": "2024-01-14 14:57:29 UTC", + "google_url": "https://www.google.com/search?q=metagpt&oq=metagpt&hl=en&gl=us&num=8&sourceid=chrome&ie=UTF-8", + "raw_html_file": "https://serpapi.com/searches/f3454e001dacdae1/65a3f6595b54ef7f1dfbcdd2.html", + "total_time_taken": 2.5 + }, + "search_parameters": { + "engine": "google", + "q": "metagpt", + "google_domain": "google.com", + "hl": "en", + "gl": "us", + "num": "8", + "device": "desktop" + }, + "search_information": { + "query_displayed": "metagpt", + "total_results": 91600, + "time_taken_displayed": 0.27, + "menu_items": [ + { + "position": 1, + "title": "News", + "link": "https://www.google.com/search?num=8&sca_esv=598392389&hl=en&gl=us&q=metagpt&tbm=nws&source=lnms&sa=X&ved=2ahUKEwiZ6tvukd2DAxWuFlkFHbnFBv8Q0pQJegQIEBAB", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=8&q=metagpt&tbm=nws" + }, + { + "position": 2, + "title": "Images", + "link": "https://www.google.com/search?num=8&sca_esv=598392389&hl=en&gl=us&q=metagpt&tbm=isch&source=lnms&sa=X&ved=2ahUKEwiZ6tvukd2DAxWuFlkFHbnFBv8Q0pQJegQIERAB", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google_images&gl=us&google_domain=google.com&hl=en&q=metagpt" + }, + { + "position": 3, + "title": "Perspectives", + "link": "https://www.google.com/search?num=8&sca_esv=598392389&hl=en&gl=us&q=metagpt&uds=AMwkrPv_BNR0fCL4lAUrdY_MslXnXP_8eZcaurn07wVclkT7zdZi70-PsAZ5cIYoShIriCGEG9cp7YID252SJZlezuQgGHVoaxAGC2P-K5BQMhuhn3rxBEI&udm=4&sa=X&ved=2ahUKEwiZ6tvukd2DAxWuFlkFHbnFBv8Qs6gLegQIEhAB", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=8&q=metagpt" + }, + { + "position": 4, + "title": "Download", + "link": "https://www.google.com/search?num=8&sca_esv=598392389&hl=en&gl=us&q=MetaGPT+download&uds=AMwkrPs1tkKhl_yLs17ozqzdeOQpXginZ88vZAAruQSl2egWlmxzo18RJ2iSa2okRlGJpRvhNdkif_bMpSTk2MMlNadEZGUA9HcNBj9XUrqefB2G97SzGtM&sa=X&ved=2ahUKEwiZ6tvukd2DAxWuFlkFHbnFBv8QxKsJegQIDhAB&ictx=0", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=8&q=MetaGPT+download" + }, + { + "position": 5, + "title": "Videos", + "link": "https://www.google.com/search?num=8&sca_esv=598392389&hl=en&gl=us&q=metagpt&tbm=vid&source=lnms&sa=X&ved=2ahUKEwiZ6tvukd2DAxWuFlkFHbnFBv8Q0pQJegQINRAB", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google_videos&gl=us&google_domain=google.com&hl=en&num=8&q=metagpt" + }, + { + "position": 6, + "title": "Review", + "link": "https://www.google.com/search?num=8&sca_esv=598392389&hl=en&gl=us&q=MetaGPT+review&uds=AMwkrPsrb0_MXdPCtp0RJNoWQEuvuWMXOVdQk9bEznN4tlVCwT3QF14u76JluzhFRLe_8V0vj_J6GkI2lsgMS7iWf5vAS8_exlSGI2NPPyhxAtn0L9DpLP0&sa=X&ved=2ahUKEwiZ6tvukd2DAxWuFlkFHbnFBv8QxKsJegQINhAB&ictx=0", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=8&q=MetaGPT+review" + }, + { + "position": 7, + "title": "Online", + "link": "https://www.google.com/search?num=8&sca_esv=598392389&hl=en&gl=us&q=MetaGPT+online&uds=AMwkrPsoRx99OfyO5-zj61oe0QMzGel38AesYPljQRlBU6r33ArXtPFSYaOzLdJPpJNVmudurhtqLwUnetN4svOtlXgjwySfgpxw9zgVeZ95Yk0B4ftC_Yw&sa=X&ved=2ahUKEwiZ6tvukd2DAxWuFlkFHbnFBv8QxKsJegQINxAB&ictx=0", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=8&q=MetaGPT+online" + }, + { + "position": 8, + "title": "App", + "link": "https://www.google.com/search?num=8&sca_esv=598392389&hl=en&gl=us&q=Metagpt+app&uds=AMwkrPvM3iswphQGpo45MKxhFsVLtYmdTSGDwMjrC3YJfMStztBkIzhQ3LXUWRIS_9CLaKDV49EzlFRs65SDPWQRQ_UhZ9vnYjXCails2jTqGf73j7jxJ5g&sa=X&ved=2ahUKEwiZ6tvukd2DAxWuFlkFHbnFBv8QxKsJegQIOBAB&ictx=0", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=8&q=Metagpt+app" + }, + { + "position": 9, + "title": "AI", + "link": "https://www.google.com/search?num=8&sca_esv=598392389&hl=en&gl=us&q=MetaGPT+AI&uds=AMwkrPtd3khZ7-4qbofZcpN4KpMaARLEVOHuvLVm0W3G2e-1vlpsKSHNi4ZplHhRz_p2lhtBxgOUBiCMoccC6ypD35_CMSI-u6d67n4mJNsyAnhftmvIlk8&sa=X&ved=2ahUKEwiZ6tvukd2DAxWuFlkFHbnFBv8QxKsJegQIORAB&ictx=0", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=8&q=MetaGPT+AI" + } + ], + "organic_results_state": "Results for exact spelling" + }, + "inline_videos": [ + { + "position": 1, + "title": "How To Install MetaGPT - Build A Startup With One Prompt!!", + "link": "https://www.youtube.com/watch?v=uT75J_KG_aY", + "thumbnail": "https://serpapi.com/searches/65a3f6595b54ef7f1dfbcdd2/images/a0db2f9f70f02dd11e3d3d4154df9fd65b46b2fbf4804f7038c9ce99c8efea1c.jpeg", + "channel": "Matthew Berman", + "duration": "6:36", + "platform": "YouTube", + "date": "Aug 14, 2023" + }, + { + "position": 2, + "title": "MetaGPT HUGE Update: Autonomous AI Agents with ...", + "link": "https://www.youtube.com/watch?v=Xyws6iI-eH8", + "thumbnail": "https://serpapi.com/searches/65a3f6595b54ef7f1dfbcdd2/images/a0db2f9f70f02dd1d578e6031265d66299cf6aecd327454cdf67b92808f3dd86.jpeg", + "channel": "WorldofAI", + "duration": "11:38", + "platform": "YouTube", + "date": "3 weeks ago" + }, + { + "position": 3, + "title": "🚀 MetaGPT Setup: Launch a Startup with One ✍️ Prompt!", + "link": "https://www.youtube.com/watch?v=nqZlTV_L6Ao", + "thumbnail": "https://serpapi.com/searches/65a3f6595b54ef7f1dfbcdd2/images/a0db2f9f70f02dd1c5666bd22292fdc357357dac89294aabb55ebea0a40ce322.jpeg", + "channel": "Prompt Engineering", + "duration": "14:15", + "platform": "YouTube", + "date": "Sep 4, 2023", + "key_moments": [ + { + "time": "00:00", + "title": "Intro", + "link": "https://www.youtube.com/watch?v=nqZlTV_L6Ao&t=0", + "thumbnail": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQW-YKGXQDHplRpEDgL5Q-HlJ8HggTw_ghp_KWPh8xUcQ&s" + }, + { + "time": "00:12", + "title": "What is MetaGPT", + "link": "https://www.youtube.com/watch?v=nqZlTV_L6Ao&t=12", + "thumbnail": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRJ4RRAXOG6yvGPYqkuj5cMoiyYdAN6g7E3VU04SA3P7w&s" + }, + { + "time": "01:06", + "title": "Setup", + "link": "https://www.youtube.com/watch?v=nqZlTV_L6Ao&t=66", + "thumbnail": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTDlJBrAtfBkC8zI9wY4dOqVIaNFbjcYSZr4M1ZnD7RSw&s" + }, + { + "time": "05:23", + "title": "Changing configuration", + "link": "https://www.youtube.com/watch?v=nqZlTV_L6Ao&t=323", + "thumbnail": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT8MbsIRVXJy__UE4ba0FoCTMGfrykasHm3UGvSzMQAtQ&s" + }, + { + "time": "06:35", + "title": "How to Run", + "link": "https://www.youtube.com/watch?v=nqZlTV_L6Ao&t=395", + "thumbnail": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRuX6mOUVQVRzvnkOPYNcDpcazRC1QGeHhZh-Az9btUNA&s" + }, + { + "time": "09:02", + "title": "What outputs to expect", + "link": "https://www.youtube.com/watch?v=nqZlTV_L6Ao&t=542", + "thumbnail": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTFnNqvPfGrPnKJTJ1iOHGSNp6sVR5jn0Zy5N2JSGfeEQ&s" + }, + { + "time": "10:45", + "title": "Generated Design Documents", + "link": "https://www.youtube.com/watch?v=nqZlTV_L6Ao&t=645", + "thumbnail": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSN3I0gxudI4Mew93w_tw34HmWREz5XX8ArebReM3Y2_g&s" + }, + { + "time": "12:25", + "title": "Run the created code base", + "link": "https://www.youtube.com/watch?v=nqZlTV_L6Ao&t=745", + "thumbnail": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQLBx5bgKZ2Gqsu-PsIXuvtM0SBmHvBCndmKtresgqFCg&s" + } + ] + } + ], + "organic_results": [ + { + "position": 1, + "title": "geekan/MetaGPT: 🌟 The Multi-Agent Framework", + "link": "https://github.com/geekan/MetaGPT", + "redirect_link": "https://www.google.com/url?sa=t&source=web&rct=j&opi=89978449&url=https://github.com/geekan/MetaGPT&ved=2ahUKEwiZ6tvukd2DAxWuFlkFHbnFBv8QFnoECBUQAQ", + "displayed_link": "https://github.com › geekan › MetaGPT", + "favicon": "https://serpapi.com/searches/65a3f6595b54ef7f1dfbcdd2/images/f37f87ccfb08b6fc2fe7e2076c022e7690f9b18357b8e5feb75a30ffbaaabfb1.png", + "snippet": "MetaGPT takes a one line requirement as input and outputs user stories / competitive analysis / requirements / data structures / APIs / documents, etc.", + "snippet_highlighted_words": [ + "MetaGPT" + ], + "sitelinks": { + "inline": [ + { + "title": "Roadmap", + "link": "https://github.com/geekan/MetaGPT/blob/main/docs/ROADMAP.md" + }, + { + "title": "README.md", + "link": "https://github.com/geekan/MetaGPT/blob/main/README.md" + }, + { + "title": "Issues 161", + "link": "https://github.com/geekan/MetaGPT/issues" + }, + { + "title": "Actions", + "link": "https://github.com/geekan/MetaGPT/actions" + } + ] + }, + "source": "GitHub" + }, + { + "position": 2, + "title": "MetaGPT: Meta Programming for A Multi-Agent ...", + "link": "https://arxiv.org/abs/2308.00352", + "redirect_link": "https://www.google.com/url?sa=t&source=web&rct=j&opi=89978449&url=https://arxiv.org/abs/2308.00352&ved=2ahUKEwiZ6tvukd2DAxWuFlkFHbnFBv8QFnoECBMQAQ", + "displayed_link": "https://arxiv.org › cs", + "favicon": "https://serpapi.com/searches/65a3f6595b54ef7f1dfbcdd2/images/f37f87ccfb08b6fc2fe7e2076c022e76592372342f3f5dd76573e051b50f1bce.png", + "author": "by S Hong", + "cited_by": "Cited by 63", + "extracted_cited_by": 63, + "date": "2023", + "snippet": "Abstract:Remarkable progress has been made on automated problem solving through societies of agents based on large language models (LLMs).", + "source": "arXiv" + }, + { + "position": 3, + "title": "MetaGPT: a Multi-Agent Framework to Automate Your ...", + "link": "https://medium.datadriveninvestor.com/metagpt-a-multi-agent-framework-to-automate-your-software-company-4b6ae747cc36", + "redirect_link": "https://www.google.com/url?sa=t&source=web&rct=j&opi=89978449&url=https://medium.datadriveninvestor.com/metagpt-a-multi-agent-framework-to-automate-your-software-company-4b6ae747cc36&ved=2ahUKEwiZ6tvukd2DAxWuFlkFHbnFBv8QFnoECBgQAQ", + "displayed_link": "https://medium.datadriveninvestor.com › metagpt-a-...", + "favicon": "https://serpapi.com/searches/65a3f6595b54ef7f1dfbcdd2/images/f37f87ccfb08b6fc2fe7e2076c022e76e8319069677ee18a99026fb1e05709cf.png", + "snippet": "MetaGPT is about to reach 10000 stars on Github. It's a Multi-Agent Framework that can behave as an engineer, product manager, architect, project managers.", + "snippet_highlighted_words": [ + "MetaGPT" + ], + "source": "DataDrivenInvestor" + }, + { + "position": 4, + "title": "MetaGPT - Apps on Google Play", + "link": "https://play.google.com/store/apps/details?id=com.metagpt.app&hl=en&gl=US", + "redirect_link": "https://www.google.com/url?sa=t&source=web&rct=j&opi=89978449&url=https://play.google.com/store/apps/details%3Fid%3Dcom.metagpt.app%26hl%3Den%26gl%3DUS&ved=2ahUKEwiZ6tvukd2DAxWuFlkFHbnFBv8QFnoECCUQAQ", + "displayed_link": "https://play.google.com › store › apps › details › id=c...", + "favicon": "https://serpapi.com/searches/65a3f6595b54ef7f1dfbcdd2/images/f37f87ccfb08b6fc2fe7e2076c022e76334a7b2eeab09f16973a82a209ee6339.png", + "date": "Jan 1, 2024", + "snippet": "Real-time crypto monitor.Track prices, set alerts, seize opportunities instantly.", + "source": "Google Play" + }, + { + "position": 5, + "title": "MetaGPT: AI-Powered Web Development That Changes ...", + "link": "https://www.analyticsvidhya.com/blog/2024/01/meet-metagpt-the-chatgpt-powered-ai-assistant-that-turns-text-into-web-apps/", + "redirect_link": "https://www.google.com/url?sa=t&source=web&rct=j&opi=89978449&url=https://www.analyticsvidhya.com/blog/2024/01/meet-metagpt-the-chatgpt-powered-ai-assistant-that-turns-text-into-web-apps/&ved=2ahUKEwiZ6tvukd2DAxWuFlkFHbnFBv8QFnoECCkQAQ", + "displayed_link": "https://www.analyticsvidhya.com › blog › 2024/01", + "favicon": "https://serpapi.com/searches/65a3f6595b54ef7f1dfbcdd2/images/f37f87ccfb08b6fc2fe7e2076c022e766a141f2bf05b1ab902f83ed00f4148a4.png", + "date": "Jan 4, 2024", + "snippet": "MetaGPT is an AI assistant that leverages the power of GPT-4, a state-of-the-art language model developed by OpenAI. ChatGPT is trained on vast ...", + "snippet_highlighted_words": [ + "MetaGPT" + ], + "source": "Analytics Vidhya" + }, + { + "position": 6, + "title": "MetaGPT | Discover AI use cases", + "link": "https://gpt3demo.com/apps/metagpt", + "redirect_link": "https://www.google.com/url?sa=t&source=web&rct=j&opi=89978449&url=https://gpt3demo.com/apps/metagpt&ved=2ahUKEwiZ6tvukd2DAxWuFlkFHbnFBv8QFnoECCQQAQ", + "displayed_link": "https://gpt3demo.com › apps › metagpt", + "favicon": "https://serpapi.com/searches/65a3f6595b54ef7f1dfbcdd2/images/f37f87ccfb08b6fc2fe7e2076c022e76142721493557b5d95328dafb62b6b43a.jpeg", + "snippet": "Assign different roles to GPTs to form a collaborative software entity for complex tasks. MetaGPT takes a one-line requirement as input and outputs user ...", + "snippet_highlighted_words": [ + "MetaGPT" + ], + "source": "GPT-3 Demo" + } + ], + "related_searches": [ + { + "block_position": 1, + "query": "metagpt online", + "link": "https://www.google.com/search?num=8&sca_esv=598392389&hl=en&gl=us&q=MetaGPT+online&sa=X&ved=2ahUKEwiZ6tvukd2DAxWuFlkFHbnFBv8Q1QJ6BAgnEAE", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=8&q=MetaGPT+online" + }, + { + "block_position": 1, + "query": "metagpt paper", + "link": "https://www.google.com/search?num=8&sca_esv=598392389&hl=en&gl=us&q=MetaGPT+paper&sa=X&ved=2ahUKEwiZ6tvukd2DAxWuFlkFHbnFBv8Q1QJ6BAgoEAE", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=8&q=MetaGPT+paper" + }, + { + "block_position": 1, + "query": "Metagpt download", + "link": "https://www.google.com/search?num=8&sca_esv=598392389&hl=en&gl=us&q=Metagpt+download&sa=X&ved=2ahUKEwiZ6tvukd2DAxWuFlkFHbnFBv8Q1QJ6BAgmEAE", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=8&q=Metagpt+download" + }, + { + "block_position": 1, + "query": "metagpt github", + "link": "https://www.google.com/search?num=8&sca_esv=598392389&hl=en&gl=us&q=Metagpt+github&sa=X&ved=2ahUKEwiZ6tvukd2DAxWuFlkFHbnFBv8Q1QJ6BAgiEAE", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=8&q=Metagpt+github" + }, + { + "block_position": 1, + "query": "Metagpt review", + "link": "https://www.google.com/search?num=8&sca_esv=598392389&hl=en&gl=us&q=Metagpt+review&sa=X&ved=2ahUKEwiZ6tvukd2DAxWuFlkFHbnFBv8Q1QJ6BAgjEAE", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=8&q=Metagpt+review" + }, + { + "block_position": 1, + "query": "metagpt ai", + "link": "https://www.google.com/search?num=8&sca_esv=598392389&hl=en&gl=us&q=MetaGPT+AI&sa=X&ved=2ahUKEwiZ6tvukd2DAxWuFlkFHbnFBv8Q1QJ6BAggEAE", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=8&q=MetaGPT+AI" + }, + { + "block_position": 1, + "query": "metagpt huggingface", + "link": "https://www.google.com/search?num=8&sca_esv=598392389&hl=en&gl=us&q=MetaGPT+huggingface&sa=X&ved=2ahUKEwiZ6tvukd2DAxWuFlkFHbnFBv8Q1QJ6BAghEAE", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=8&q=MetaGPT+huggingface" + }, + { + "block_position": 1, + "query": "metagpt openai", + "link": "https://www.google.com/search?num=8&sca_esv=598392389&hl=en&gl=us&q=Metagpt+OpenAI&sa=X&ved=2ahUKEwiZ6tvukd2DAxWuFlkFHbnFBv8Q1QJ6BAgfEAE", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=8&q=Metagpt+OpenAI" + } + ], + "pagination": { + "current": 1, + "next": "https://www.google.com/search?q=metagpt&oq=metagpt&hl=en&gl=us&num=8&start=8&sourceid=chrome&ie=UTF-8", + "other_pages": { + "2": "https://www.google.com/search?q=metagpt&oq=metagpt&hl=en&gl=us&num=8&start=8&sourceid=chrome&ie=UTF-8", + "3": "https://www.google.com/search?q=metagpt&oq=metagpt&hl=en&gl=us&num=8&start=16&sourceid=chrome&ie=UTF-8", + "4": "https://www.google.com/search?q=metagpt&oq=metagpt&hl=en&gl=us&num=8&start=24&sourceid=chrome&ie=UTF-8", + "5": "https://www.google.com/search?q=metagpt&oq=metagpt&hl=en&gl=us&num=8&start=32&sourceid=chrome&ie=UTF-8" + } + }, + "serpapi_pagination": { + "current": 1, + "next_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=8&q=metagpt&start=8", + "next": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=8&q=metagpt&start=8", + "other_pages": { + "2": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=8&q=metagpt&start=8", + "3": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=8&q=metagpt&start=16", + "4": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=8&q=metagpt&start=24", + "5": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=8&q=metagpt&start=32" + } + } + }, + "aiohttp-get-https://serpapi.com/search-{\"params\": {\"api_key\": \"mock-serpapi-key\", \"engine\": \"google\", \"gl\": \"us\", \"google_domain\": \"google.com\", \"hl\": \"en\", \"num\": 4, \"output\": \"json\", \"q\": \"metagpt\", \"source\": \"python\"}}": { + "search_metadata": { + "id": "65a3f65d8b7ed28c15233c79", + "status": "Success", + "json_endpoint": "https://serpapi.com/searches/2081c01f04a8e878/65a3f65d8b7ed28c15233c79.json", + "created_at": "2024-01-14 14:57:33 UTC", + "processed_at": "2024-01-14 14:57:33 UTC", + "google_url": "https://www.google.com/search?q=metagpt&oq=metagpt&hl=en&gl=us&num=4&sourceid=chrome&ie=UTF-8", + "raw_html_file": "https://serpapi.com/searches/2081c01f04a8e878/65a3f65d8b7ed28c15233c79.html", + "total_time_taken": 2.89 + }, + "search_parameters": { + "engine": "google", + "q": "metagpt", + "google_domain": "google.com", + "hl": "en", + "gl": "us", + "num": "4", + "device": "desktop" + }, + "search_information": { + "query_displayed": "metagpt", + "total_results": 91600, + "time_taken_displayed": 0.2, + "menu_items": [ + { + "position": 1, + "title": "News", + "link": "https://www.google.com/search?num=4&sca_esv=598392389&gl=us&hl=en&q=metagpt&tbm=nws&source=lnms&sa=X&ved=2ahUKEwigwuTwkd2DAxWyOkQIHc_uDdEQ0pQJegQIChAB", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=4&q=metagpt&tbm=nws" + }, + { + "position": 2, + "title": "Images", + "link": "https://www.google.com/search?num=4&sca_esv=598392389&gl=us&hl=en&q=metagpt&tbm=isch&source=lnms&sa=X&ved=2ahUKEwigwuTwkd2DAxWyOkQIHc_uDdEQ0pQJegQIDhAB", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google_images&gl=us&google_domain=google.com&hl=en&q=metagpt" + }, + { + "position": 3, + "title": "Perspectives", + "link": "https://www.google.com/search?num=4&sca_esv=598392389&gl=us&hl=en&q=metagpt&uds=AMwkrPv_BNR0fCL4lAUrdY_MslXnXP_8eZcaurn07wVclkT7zdZi70-PsAZ5cIYoShIriCGEG9cp7YID252SJZlezuQgGHVoaxAGC2P-K5BQMhuhn3rxBEI&udm=4&sa=X&ved=2ahUKEwigwuTwkd2DAxWyOkQIHc_uDdEQs6gLegQIDRAB", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=4&q=metagpt" + }, + { + "position": 4, + "title": "Download", + "link": "https://www.google.com/search?num=4&sca_esv=598392389&gl=us&hl=en&q=MetaGPT+download&uds=AMwkrPs1tkKhl_yLs17ozqzdeOQpXginZ88vZAAruQSl2egWlmxzo18RJ2iSa2okRlGJpRvhNdkif_bMpSTk2MMlNadEZGUA9HcNBj9XUrqefB2G97SzGtM&sa=X&ved=2ahUKEwigwuTwkd2DAxWyOkQIHc_uDdEQxKsJegQICxAB&ictx=0", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=4&q=MetaGPT+download" + }, + { + "position": 5, + "title": "Videos", + "link": "https://www.google.com/search?num=4&sca_esv=598392389&gl=us&hl=en&q=metagpt&tbm=vid&source=lnms&sa=X&ved=2ahUKEwigwuTwkd2DAxWyOkQIHc_uDdEQ0pQJegQILBAB", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google_videos&gl=us&google_domain=google.com&hl=en&num=4&q=metagpt" + }, + { + "position": 6, + "title": "Review", + "link": "https://www.google.com/search?num=4&sca_esv=598392389&gl=us&hl=en&q=MetaGPT+review&uds=AMwkrPsrb0_MXdPCtp0RJNoWQEuvuWMXOVdQk9bEznN4tlVCwT3QF14u76JluzhFRLe_8V0vj_J6GkI2lsgMS7iWf5vAS8_exlSGI2NPPyhxAtn0L9DpLP0&sa=X&ved=2ahUKEwigwuTwkd2DAxWyOkQIHc_uDdEQxKsJegQILhAB&ictx=0", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=4&q=MetaGPT+review" + }, + { + "position": 7, + "title": "Online", + "link": "https://www.google.com/search?num=4&sca_esv=598392389&gl=us&hl=en&q=MetaGPT+online&uds=AMwkrPsoRx99OfyO5-zj61oe0QMzGel38AesYPljQRlBU6r33ArXtPFSYaOzLdJPpJNVmudurhtqLwUnetN4svOtlXgjwySfgpxw9zgVeZ95Yk0B4ftC_Yw&sa=X&ved=2ahUKEwigwuTwkd2DAxWyOkQIHc_uDdEQxKsJegQILRAB&ictx=0", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=4&q=MetaGPT+online" + }, + { + "position": 8, + "title": "App", + "link": "https://www.google.com/search?num=4&sca_esv=598392389&gl=us&hl=en&q=Metagpt+app&uds=AMwkrPvM3iswphQGpo45MKxhFsVLtYmdTSGDwMjrC3YJfMStztBkIzhQ3LXUWRIS_9CLaKDV49EzlFRs65SDPWQRQ_UhZ9vnYjXCails2jTqGf73j7jxJ5g&sa=X&ved=2ahUKEwigwuTwkd2DAxWyOkQIHc_uDdEQxKsJegQILxAB&ictx=0", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=4&q=Metagpt+app" + }, + { + "position": 9, + "title": "AI", + "link": "https://www.google.com/search?num=4&sca_esv=598392389&gl=us&hl=en&q=MetaGPT+AI&uds=AMwkrPtd3khZ7-4qbofZcpN4KpMaARLEVOHuvLVm0W3G2e-1vlpsKSHNi4ZplHhRz_p2lhtBxgOUBiCMoccC6ypD35_CMSI-u6d67n4mJNsyAnhftmvIlk8&sa=X&ved=2ahUKEwigwuTwkd2DAxWyOkQIHc_uDdEQxKsJegQIMBAB&ictx=0", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=4&q=MetaGPT+AI" + } + ], + "organic_results_state": "Results for exact spelling" + }, + "inline_videos": [ + { + "position": 1, + "title": "How To Install MetaGPT - Build A Startup With One Prompt!!", + "link": "https://www.youtube.com/watch?v=uT75J_KG_aY", + "thumbnail": "https://serpapi.com/searches/65a3f65d8b7ed28c15233c79/images/bfd65a15364211be961855b9ca9c1cbfeecac1fc4f084deba696fe02f511b2b0.jpeg", + "channel": "Matthew Berman", + "duration": "6:36", + "platform": "YouTube", + "date": "Aug 14, 2023" + }, + { + "position": 2, + "title": "MetaGPT HUGE Update: Autonomous AI Agents with ...", + "link": "https://www.youtube.com/watch?v=Xyws6iI-eH8", + "thumbnail": "https://serpapi.com/searches/65a3f65d8b7ed28c15233c79/images/bfd65a15364211be43551974ef1dbd0b4a3780c1caa0ef2d1edaaee2ebc89b3c.jpeg", + "channel": "WorldofAI", + "duration": "11:38", + "platform": "YouTube", + "date": "3 weeks ago" + }, + { + "position": 3, + "title": "🚀 MetaGPT Setup: Launch a Startup with One ✍️ Prompt!", + "link": "https://www.youtube.com/watch?v=nqZlTV_L6Ao", + "thumbnail": "https://serpapi.com/searches/65a3f65d8b7ed28c15233c79/images/bfd65a15364211be779beff6d19f978b32bf888581454f54a19b9b01c5d9a6a8.jpeg", + "channel": "Prompt Engineering", + "duration": "14:15", + "platform": "YouTube", + "date": "Sep 4, 2023", + "key_moments": [ + { + "time": "00:00", + "title": "Intro", + "link": "https://www.youtube.com/watch?v=nqZlTV_L6Ao&t=0", + "thumbnail": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQW-YKGXQDHplRpEDgL5Q-HlJ8HggTw_ghp_KWPh8xUcQ&s" + }, + { + "time": "00:12", + "title": "What is MetaGPT", + "link": "https://www.youtube.com/watch?v=nqZlTV_L6Ao&t=12", + "thumbnail": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRJ4RRAXOG6yvGPYqkuj5cMoiyYdAN6g7E3VU04SA3P7w&s" + }, + { + "time": "01:06", + "title": "Setup", + "link": "https://www.youtube.com/watch?v=nqZlTV_L6Ao&t=66", + "thumbnail": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTDlJBrAtfBkC8zI9wY4dOqVIaNFbjcYSZr4M1ZnD7RSw&s" + }, + { + "time": "05:23", + "title": "Changing configuration", + "link": "https://www.youtube.com/watch?v=nqZlTV_L6Ao&t=323", + "thumbnail": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcT8MbsIRVXJy__UE4ba0FoCTMGfrykasHm3UGvSzMQAtQ&s" + }, + { + "time": "06:35", + "title": "How to Run", + "link": "https://www.youtube.com/watch?v=nqZlTV_L6Ao&t=395", + "thumbnail": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRuX6mOUVQVRzvnkOPYNcDpcazRC1QGeHhZh-Az9btUNA&s" + }, + { + "time": "09:02", + "title": "What outputs to expect", + "link": "https://www.youtube.com/watch?v=nqZlTV_L6Ao&t=542", + "thumbnail": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTFnNqvPfGrPnKJTJ1iOHGSNp6sVR5jn0Zy5N2JSGfeEQ&s" + }, + { + "time": "10:45", + "title": "Generated Design Documents", + "link": "https://www.youtube.com/watch?v=nqZlTV_L6Ao&t=645", + "thumbnail": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSN3I0gxudI4Mew93w_tw34HmWREz5XX8ArebReM3Y2_g&s" + }, + { + "time": "12:25", + "title": "Run the created code base", + "link": "https://www.youtube.com/watch?v=nqZlTV_L6Ao&t=745", + "thumbnail": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQLBx5bgKZ2Gqsu-PsIXuvtM0SBmHvBCndmKtresgqFCg&s" + } + ] + } + ], + "organic_results": [ + { + "position": 1, + "title": "geekan/MetaGPT: 🌟 The Multi-Agent Framework", + "link": "https://github.com/geekan/MetaGPT", + "redirect_link": "https://www.google.com/url?sa=t&source=web&rct=j&opi=89978449&url=https://github.com/geekan/MetaGPT&ved=2ahUKEwigwuTwkd2DAxWyOkQIHc_uDdEQFnoECBcQAQ", + "displayed_link": "https://github.com › geekan › MetaGPT", + "favicon": "https://serpapi.com/searches/65a3f65d8b7ed28c15233c79/images/754322707626bed29162a2ba4a9960076a2cfb8f3558519e16fc8a6b74240174.png", + "snippet": "MetaGPT takes a one line requirement as input and outputs user stories / competitive analysis / requirements / data structures / APIs / documents, etc.", + "snippet_highlighted_words": [ + "MetaGPT" + ], + "sitelinks": { + "inline": [ + { + "title": "README.md", + "link": "https://github.com/geekan/MetaGPT/blob/main/README.md" + }, + { + "title": "Roadmap", + "link": "https://github.com/geekan/MetaGPT/blob/main/docs/ROADMAP.md" + }, + { + "title": "Issues 161", + "link": "https://github.com/geekan/MetaGPT/issues" + }, + { + "title": "Actions", + "link": "https://github.com/geekan/MetaGPT/actions" + } + ] + }, + "source": "GitHub" + }, + { + "position": 2, + "title": "MetaGPT: Meta Programming for A Multi-Agent ...", + "link": "https://arxiv.org/abs/2308.00352", + "redirect_link": "https://www.google.com/url?sa=t&source=web&rct=j&opi=89978449&url=https://arxiv.org/abs/2308.00352&ved=2ahUKEwigwuTwkd2DAxWyOkQIHc_uDdEQFnoECBUQAQ", + "displayed_link": "https://arxiv.org › cs", + "favicon": "https://serpapi.com/searches/65a3f65d8b7ed28c15233c79/images/754322707626bed29162a2ba4a996007d238ff23f244403a638b759e517db592.png", + "author": "by S Hong", + "cited_by": "Cited by 63", + "extracted_cited_by": 63, + "date": "2023", + "snippet": "Abstract:Remarkable progress has been made on automated problem solving through societies of agents based on large language models (LLMs).", + "source": "arXiv" + }, + { + "position": 3, + "title": "MetaGPT: a Multi-Agent Framework to Automate Your ...", + "link": "https://medium.datadriveninvestor.com/metagpt-a-multi-agent-framework-to-automate-your-software-company-4b6ae747cc36", + "redirect_link": "https://www.google.com/url?sa=t&source=web&rct=j&opi=89978449&url=https://medium.datadriveninvestor.com/metagpt-a-multi-agent-framework-to-automate-your-software-company-4b6ae747cc36&ved=2ahUKEwigwuTwkd2DAxWyOkQIHc_uDdEQFnoECBMQAQ", + "displayed_link": "https://medium.datadriveninvestor.com › metagpt-a-m...", + "favicon": "https://serpapi.com/searches/65a3f65d8b7ed28c15233c79/images/754322707626bed29162a2ba4a996007983965c804f215b78e84b23e3aabec98.png", + "snippet": "MetaGPT is about to reach 10000 stars on Github. It's a Multi-Agent Framework that can behave as an engineer, product manager, architect, project managers.", + "snippet_highlighted_words": [ + "MetaGPT" + ], + "source": "DataDrivenInvestor" + } + ], + "related_searches": [ + { + "block_position": 1, + "query": "metagpt online", + "link": "https://www.google.com/search?num=4&sca_esv=598392389&gl=us&hl=en&q=MetaGPT+online&sa=X&ved=2ahUKEwigwuTwkd2DAxWyOkQIHc_uDdEQ1QJ6BAgmEAE", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=4&q=MetaGPT+online" + }, + { + "block_position": 1, + "query": "metagpt paper", + "link": "https://www.google.com/search?num=4&sca_esv=598392389&gl=us&hl=en&q=MetaGPT+paper&sa=X&ved=2ahUKEwigwuTwkd2DAxWyOkQIHc_uDdEQ1QJ6BAglEAE", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=4&q=MetaGPT+paper" + }, + { + "block_position": 1, + "query": "Metagpt download", + "link": "https://www.google.com/search?num=4&sca_esv=598392389&gl=us&hl=en&q=Metagpt+download&sa=X&ved=2ahUKEwigwuTwkd2DAxWyOkQIHc_uDdEQ1QJ6BAgjEAE", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=4&q=Metagpt+download" + }, + { + "block_position": 1, + "query": "metagpt github", + "link": "https://www.google.com/search?num=4&sca_esv=598392389&gl=us&hl=en&q=Metagpt+github&sa=X&ved=2ahUKEwigwuTwkd2DAxWyOkQIHc_uDdEQ1QJ6BAgkEAE", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=4&q=Metagpt+github" + }, + { + "block_position": 1, + "query": "Metagpt review", + "link": "https://www.google.com/search?num=4&sca_esv=598392389&gl=us&hl=en&q=Metagpt+review&sa=X&ved=2ahUKEwigwuTwkd2DAxWyOkQIHc_uDdEQ1QJ6BAgiEAE", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=4&q=Metagpt+review" + }, + { + "block_position": 1, + "query": "metagpt ai", + "link": "https://www.google.com/search?num=4&sca_esv=598392389&gl=us&hl=en&q=MetaGPT+AI&sa=X&ved=2ahUKEwigwuTwkd2DAxWyOkQIHc_uDdEQ1QJ6BAgfEAE", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=4&q=MetaGPT+AI" + }, + { + "block_position": 1, + "query": "metagpt huggingface", + "link": "https://www.google.com/search?num=4&sca_esv=598392389&gl=us&hl=en&q=MetaGPT+huggingface&sa=X&ved=2ahUKEwigwuTwkd2DAxWyOkQIHc_uDdEQ1QJ6BAggEAE", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=4&q=MetaGPT+huggingface" + }, + { + "block_position": 1, + "query": "metagpt openai", + "link": "https://www.google.com/search?num=4&sca_esv=598392389&gl=us&hl=en&q=Metagpt+OpenAI&sa=X&ved=2ahUKEwigwuTwkd2DAxWyOkQIHc_uDdEQ1QJ6BAghEAE", + "serpapi_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=4&q=Metagpt+OpenAI" + } + ], + "pagination": { + "current": 1, + "next": "https://www.google.com/search?q=metagpt&oq=metagpt&hl=en&gl=us&num=4&start=4&sourceid=chrome&ie=UTF-8", + "other_pages": { + "2": "https://www.google.com/search?q=metagpt&oq=metagpt&hl=en&gl=us&num=4&start=4&sourceid=chrome&ie=UTF-8", + "3": "https://www.google.com/search?q=metagpt&oq=metagpt&hl=en&gl=us&num=4&start=8&sourceid=chrome&ie=UTF-8", + "4": "https://www.google.com/search?q=metagpt&oq=metagpt&hl=en&gl=us&num=4&start=12&sourceid=chrome&ie=UTF-8", + "5": "https://www.google.com/search?q=metagpt&oq=metagpt&hl=en&gl=us&num=4&start=16&sourceid=chrome&ie=UTF-8" + } + }, + "serpapi_pagination": { + "current": 1, + "next_link": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=4&q=metagpt&start=4", + "next": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=4&q=metagpt&start=4", + "other_pages": { + "2": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=4&q=metagpt&start=4", + "3": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=4&q=metagpt&start=8", + "4": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=4&q=metagpt&start=12", + "5": "https://serpapi.com/search.json?device=desktop&engine=google&gl=us&google_domain=google.com&hl=en&num=4&q=metagpt&start=16" + } + } + }, + "httplib2-GET-https://customsearch.googleapis.com/customsearch/v1-{\"params\": {\"q\": \"metagpt\", \"num\": \"8\", \"cx\": \"mock-google-cse\", \"key\": \"mock-google-key\", \"alt\": \"json\"}}": "{\n \"kind\": \"customsearch#search\",\n \"url\": {\n \"type\": \"application/json\",\n \"template\": \"https://www.googleapis.com/customsearch/v1?q={searchTerms}&num={count?}&start={startIndex?}&lr={language?}&safe={safe?}&cx={cx?}&sort={sort?}&filter={filter?}&gl={gl?}&cr={cr?}&googlehost={googleHost?}&c2coff={disableCnTwTranslation?}&hq={hq?}&hl={hl?}&siteSearch={siteSearch?}&siteSearchFilter={siteSearchFilter?}&exactTerms={exactTerms?}&excludeTerms={excludeTerms?}&linkSite={linkSite?}&orTerms={orTerms?}&dateRestrict={dateRestrict?}&lowRange={lowRange?}&highRange={highRange?}&searchType={searchType}&fileType={fileType?}&rights={rights?}&imgSize={imgSize?}&imgType={imgType?}&imgColorType={imgColorType?}&imgDominantColor={imgDominantColor?}&alt=json\"\n },\n \"queries\": {\n \"request\": [\n {\n \"title\": \"Google Custom Search - metagpt\",\n \"totalResults\": \"71300\",\n \"searchTerms\": \"metagpt\",\n \"count\": 8,\n \"startIndex\": 1,\n \"inputEncoding\": \"utf8\",\n \"outputEncoding\": \"utf8\",\n \"safe\": \"off\",\n \"cx\": \"mock-google-cse\"\n }\n ],\n \"nextPage\": [\n {\n \"title\": \"Google Custom Search - metagpt\",\n \"totalResults\": \"71300\",\n \"searchTerms\": \"metagpt\",\n \"count\": 8,\n \"startIndex\": 9,\n \"inputEncoding\": \"utf8\",\n \"outputEncoding\": \"utf8\",\n \"safe\": \"off\",\n \"cx\": \"mock-google-cse\"\n }\n ]\n },\n \"context\": {\n \"title\": \"metagpt1\"\n },\n \"searchInformation\": {\n \"searchTime\": 0.353952,\n \"formattedSearchTime\": \"0.35\",\n \"totalResults\": \"71300\",\n \"formattedTotalResults\": \"71,300\"\n },\n \"items\": [\n {\n \"kind\": \"customsearch#result\",\n \"title\": \"geekan/MetaGPT: The Multi-Agent Framework: Given one ... - GitHub\",\n \"htmlTitle\": \"geekan/MetaGPT: The Multi-Agent Framework: Given one ... - GitHub\",\n \"link\": \"https://github.com/geekan/MetaGPT\",\n \"displayLink\": \"github.com\",\n \"snippet\": \"The Multi-Agent Framework: Given one line Requirement, return PRD, Design, Tasks, Repo - GitHub - geekan/MetaGPT: The Multi-Agent Framework: Given one ...\",\n \"htmlSnippet\": \"The Multi-Agent Framework: Given one line Requirement, return PRD, Design, Tasks, Repo - GitHub - geekan/\\u003cb\\u003eMetaGPT\\u003c/b\\u003e: The Multi-Agent Framework: Given one ...\",\n \"cacheId\": \"gsshb0APPNgJ\",\n \"formattedUrl\": \"https://github.com/geekan/MetaGPT\",\n \"htmlFormattedUrl\": \"https://github.com/geekan/\\u003cb\\u003eMetaGPT\\u003c/b\\u003e\",\n \"pagemap\": {\n \"cse_thumbnail\": [\n {\n \"src\": \"https://encrypted-tbn1.gstatic.com/images?q=tbn:ANd9GcRuD8YUvRcltmdoxKyuIbt8UZhg3LE5mwNX7KPXDB15YIJRKdT2m5JiweuS\",\n \"width\": \"318\",\n \"height\": \"159\"\n }\n ],\n \"softwaresourcecode\": [\n {\n \"author\": \"geekan\",\n \"name\": \"MetaGPT\",\n \"text\": \"MetaGPT: The Multi-Agent Framework Assign different roles to GPTs to form a collaborative software entity for complex tasks. MetaGPT takes a one line requirement as input and outputs user stories...\"\n }\n ],\n \"metatags\": [\n {\n \"octolytics-url\": \"https://collector.github.com/github/collect\",\n \"apple-itunes-app\": \"app-id=1477376905, app-argument=https://github.com/geekan/MetaGPT\",\n \"og:image\": \"https://opengraph.githubassets.com/6178eb2aa6711c676eafc956e52345e71225c58cd0a666b54871171e847c0905/geekan/MetaGPT\",\n \"twitter:card\": \"summary_large_image\",\n \"og:image:width\": \"1200\",\n \"theme-color\": \"#1e2327\",\n \"og:site_name\": \"GitHub\",\n \"hovercard-subject-tag\": \"repository:660551251\",\n \"turbo-body-classes\": \"logged-out env-production page-responsive\",\n \"html-safe-nonce\": \"a6964edcf12c9ea83de0bf16db8105c60333287597018548250a0fa92b93d3f9\",\n \"expected-hostname\": \"github.com\",\n \"og:description\": \"🌟 The Multi-Agent Framework: Given one line Requirement, return PRD, Design, Tasks, Repo - GitHub - geekan/MetaGPT: 🌟 The Multi-Agent Framework: Given one line Requirement, return PRD, Design, Task...\",\n \"browser-errors-url\": \"https://api.github.com/_private/browser/errors\",\n \"octolytics-dimension-user_login\": \"geekan\",\n \"hostname\": \"github.com\",\n \"twitter:site\": \"@github\",\n \"browser-stats-url\": \"https://api.github.com/_private/browser/stats\",\n \"route-pattern\": \"/:user_id/:repository\",\n \"visitor-payload\": \"eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJBNjIxOjk2OUU6OTZERjlEQzpEM0UxM0RFOjY1QTNBQjU0IiwidmlzaXRvcl9pZCI6IjU2ODA2NDI2MDQ0ODY5MzA3NyIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9\",\n \"github-keyboard-shortcuts\": \"repository\",\n \"octolytics-dimension-repository_id\": \"660551251\",\n \"octolytics-dimension-repository_network_root_nwo\": \"geekan/MetaGPT\",\n \"twitter:title\": \"GitHub - geekan/MetaGPT: 🌟 The Multi-Agent Framework: Given one line Requirement, return PRD, Design, Tasks, Repo\",\n \"og:image:alt\": \"🌟 The Multi-Agent Framework: Given one line Requirement, return PRD, Design, Tasks, Repo - GitHub - geekan/MetaGPT: 🌟 The Multi-Agent Framework: Given one line Requirement, return PRD, Design, Task...\",\n \"og:type\": \"object\",\n \"optimizely-datafile\": \"{\\\"accountId\\\": \\\"16737760170\\\", \\\"projectId\\\": \\\"16737760170\\\", \\\"revision\\\": \\\"23\\\", \\\"attributes\\\": [{\\\"id\\\": \\\"16822470375\\\", \\\"key\\\": \\\"user_id\\\"}, {\\\"id\\\": \\\"17143601254\\\", \\\"key\\\": \\\"spammy\\\"}, {\\\"id\\\": \\\"18175660309\\\", \\\"key\\\": \\\"organization_plan\\\"}, {\\\"id\\\": \\\"18813001570\\\", \\\"key\\\": \\\"is_logged_in\\\"}, {\\\"id\\\": \\\"19073851829\\\", \\\"key\\\": \\\"geo\\\"}, {\\\"id\\\": \\\"20175462351\\\", \\\"key\\\": \\\"requestedCurrency\\\"}, {\\\"id\\\": \\\"20785470195\\\", \\\"key\\\": \\\"country_code\\\"}, {\\\"id\\\": \\\"21656311196\\\", \\\"key\\\": \\\"opened_downgrade_dialog\\\"}], \\\"audiences\\\": [{\\\"id\\\": \\\"$opt_dummy_audience\\\", \\\"name\\\": \\\"Optimizely-Generated Audience for Backwards Compatibility\\\", \\\"conditions\\\": \\\"[\\\\\\\"or\\\\\\\", {\\\\\\\"match\\\\\\\": \\\\\\\"exact\\\\\\\", \\\\\\\"name\\\\\\\": \\\\\\\"$opt_dummy_attribute\\\\\\\", \\\\\\\"type\\\\\\\": \\\\\\\"custom_attribute\\\\\\\", \\\\\\\"value\\\\\\\": \\\\\\\"$opt_dummy_value\\\\\\\"}]\\\"}], \\\"version\\\": \\\"4\\\", \\\"events\\\": [{\\\"id\\\": \\\"18188530140\\\", \\\"experimentIds\\\": [], \\\"key\\\": \\\"test_event\\\"}], \\\"integrations\\\": [], \\\"anonymizeIP\\\": true, \\\"botFiltering\\\": false, \\\"typedAudiences\\\": [], \\\"variables\\\": [], \\\"environmentKey\\\": \\\"production\\\", \\\"sdkKey\\\": \\\"UpVyJZaLVEGwJPQWf5pAD\\\", \\\"featureFlags\\\": [], \\\"rollouts\\\": [],\",\n \"og:title\": \"GitHub - geekan/MetaGPT: 🌟 The Multi-Agent Framework: Given one line Requirement, return PRD, Design, Tasks, Repo\",\n \"visitor-hmac\": \"471691c5f7e3204061bb09c630c440708f5c08a2824d60b0f28eea873d474cd7\",\n \"og:image:height\": \"600\",\n \"turbo-cache-control\": \"no-preview\",\n \"request-id\": \"A621:969E:96DF9DC:D3E13DE:65A3AB54\",\n \"analytics-location\": \"/\\u003cuser-name\\u003e/\\u003crepo-name\\u003e\",\n \"color-scheme\": \"light dark\",\n \"octolytics-dimension-repository_is_fork\": \"false\",\n \"go-import\": \"github.com/geekan/MetaGPT git https://github.com/geekan/MetaGPT.git\",\n \"browser-optimizely-client-errors-url\": \"https://api.github.com/_private/browser/optimizely_client/errors\",\n \"twitter:image:src\": \"https://opengraph.githubassets.com/6178eb2aa6711c676eafc956e52345e71225c58cd0a666b54871171e847c0905/geekan/MetaGPT\",\n \"octolytics-dimension-user_id\": \"2707039\",\n \"octolytics-dimension-repository_public\": \"true\",\n \"fb:app_id\": \"1401488693436528\",\n \"octolytics-dimension-repository_network_root_id\": \"660551251\",\n \"octolytics-dimension-repository_nwo\": \"geekan/MetaGPT\",\n \"viewport\": \"width=device-width\",\n \"twitter:description\": \"🌟 The Multi-Agent Framework: Given one line Requirement, return PRD, Design, Tasks, Repo - GitHub - geekan/MetaGPT: 🌟 The Multi-Agent Framework: Given one line Requirement, return PRD, Design, Task...\",\n \"current-catalog-service-hash\": \"82c569b93da5c18ed649ebd4c2c79437db4611a6a1373e805a3cb001c64130b7\",\n \"og:url\": \"https://github.com/geekan/MetaGPT\"\n }\n ],\n \"cse_image\": [\n {\n \"src\": \"https://opengraph.githubassets.com/6178eb2aa6711c676eafc956e52345e71225c58cd0a666b54871171e847c0905/geekan/MetaGPT\"\n }\n ]\n }\n },\n {\n \"kind\": \"customsearch#result\",\n \"title\": \"[2308.00352] MetaGPT: Meta Programming for A Multi-Agent ...\",\n \"htmlTitle\": \"[2308.00352] \\u003cb\\u003eMetaGPT\\u003c/b\\u003e: Meta Programming for A Multi-Agent ...\",\n \"link\": \"https://arxiv.org/abs/2308.00352\",\n \"displayLink\": \"arxiv.org\",\n \"snippet\": \"Aug 1, 2023 ... Computer Science \\u003e Artificial Intelligence · Title:MetaGPT: Meta Programming for A Multi-Agent Collaborative Framework · Bibliographic and ...\",\n \"htmlSnippet\": \"Aug 1, 2023 \\u003cb\\u003e...\\u003c/b\\u003e Computer Science > Artificial Intelligence · Title:\\u003cb\\u003eMetaGPT\\u003c/b\\u003e: Meta Programming for A Multi-Agent Collaborative Framework · Bibliographic and ...\",\n \"cacheId\": \"8_tddNY0jEYJ\",\n \"formattedUrl\": \"https://arxiv.org/abs/2308.00352\",\n \"htmlFormattedUrl\": \"https://arxiv.org/abs/2308.00352\",\n \"pagemap\": {\n \"cse_thumbnail\": [\n {\n \"src\": \"https://encrypted-tbn3.gstatic.com/images?q=tbn:ANd9GcStsc5IszP_UC7vkymrk7PhjHGOFQhTh862xtJcQkxDem2IteJQXpob6_Vb\",\n \"width\": \"336\",\n \"height\": \"150\"\n }\n ],\n \"metatags\": [\n {\n \"og:image\": \"/static/browse/0.3.4/images/arxiv-logo-fb.png\",\n \"theme-color\": \"#ffffff\",\n \"og:image:width\": \"1200\",\n \"twitter:card\": \"summary\",\n \"citation_title\": \"MetaGPT: Meta Programming for A Multi-Agent Collaborative Framework\",\n \"og:site_name\": \"arXiv.org\",\n \"citation_date\": \"2023/08/01\",\n \"og:description\": \"Remarkable progress has been made on automated problem solving through societies of agents based on large language models (LLMs). Existing LLM-based multi-agent systems can already solve simple dialogue tasks. Solutions to more complex tasks, however, are complicated through logic inconsistencies due to cascading hallucinations caused by naively chaining LLMs. Here we introduce MetaGPT, an innovative meta-programming framework incorporating efficient human workflows into LLM-based multi-agent collaborations. MetaGPT encodes Standardized Operating Procedures (SOPs) into prompt sequences for more streamlined workflows, thus allowing agents with human-like domain expertise to verify intermediate results and reduce errors. MetaGPT utilizes an assembly line paradigm to assign diverse roles to various agents, efficiently breaking down complex tasks into subtasks involving many agents working together. On collaborative software engineering benchmarks, MetaGPT generates more coherent solutions than previous chat-base\",\n \"og:image:secure_url\": \"/static/browse/0.3.4/images/arxiv-logo-fb.png\",\n \"twitter:image\": \"https://static.arxiv.org/icons/twitter/arxiv-logo-twitter-square.png\",\n \"citation_arxiv_id\": \"2308.00352\",\n \"citation_online_date\": \"2023/11/06\",\n \"twitter:image:alt\": \"arXiv logo\",\n \"twitter:site\": \"@arxiv\",\n \"citation_pdf_url\": \"http://arxiv.org/pdf/2308.00352.pdf\",\n \"msapplication-tilecolor\": \"#da532c\",\n \"og:type\": \"website\",\n \"og:image:alt\": \"arXiv logo\",\n \"twitter:title\": \"MetaGPT: Meta Programming for A Multi-Agent Collaborative Framework\",\n \"og:title\": \"MetaGPT: Meta Programming for A Multi-Agent Collaborative Framework\",\n \"citation_abstract\": \"Remarkable progress has been made on automated problem solving through societies of agents based on large language models (LLMs). Existing LLM-based multi-agent systems can already solve simple dialogue tasks. Solutions to more complex tasks, however, are complicated through logic inconsistencies due to cascading hallucinations caused by naively chaining LLMs. Here we introduce MetaGPT, an innovative meta-programming framework incorporating efficient human workflows into LLM-based multi-agent collaborations. MetaGPT encodes Standardized Operating Procedures (SOPs) into prompt sequences for more streamlined workflows, thus allowing agents with human-like domain expertise to verify intermediate results and reduce errors. MetaGPT utilizes an assembly line paradigm to assign diverse roles to various agents, efficiently breaking down complex tasks into subtasks involving many agents working together. On collaborative software engineering benchmarks, MetaGPT generates more coherent solutions than previous chat-base\",\n \"og:image:height\": \"700\",\n \"citation_author\": \"Hong, Sirui\",\n \"viewport\": \"width=device-width, initial-scale=1\",\n \"twitter:description\": \"Remarkable progress has been made on automated problem solving through societies of agents based on large language models (LLMs). Existing LLM-based multi-agent systems can already solve simple...\",\n \"og:url\": \"https://arxiv.org/abs/2308.00352v5\"\n }\n ],\n \"cse_image\": [\n {\n \"src\": \"https://arxiv.org/static/browse/0.3.4/images/arxiv-logo-one-color-white.svg\"\n }\n ]\n }\n },\n {\n \"kind\": \"customsearch#result\",\n \"title\": \"MetaGPT: Complete Guide to the Best AI Agent Available Right Now ...\",\n \"htmlTitle\": \"\\u003cb\\u003eMetaGPT\\u003c/b\\u003e: Complete Guide to the Best AI Agent Available Right Now ...\",\n \"link\": \"https://www.unite.ai/metagpt-complete-guide-to-the-best-ai-agent-available-right-now/\",\n \"displayLink\": \"www.unite.ai\",\n \"snippet\": \"Sep 11, 2023 ... The beauty of MetaGPT lies in its structuring. It capitalizes on meta-programming techniques to manipulate, analyze, and transform code in real- ...\",\n \"htmlSnippet\": \"Sep 11, 2023 \\u003cb\\u003e...\\u003c/b\\u003e The beauty of \\u003cb\\u003eMetaGPT\\u003c/b\\u003e lies in its structuring. It capitalizes on meta-programming techniques to manipulate, analyze, and transform code in real- ...\",\n \"cacheId\": \"qkZULzxVHNAJ\",\n \"formattedUrl\": \"https://www.unite.ai/metagpt-complete-guide-to-the-best-ai-agent-available-...\",\n \"htmlFormattedUrl\": \"https://www.unite.ai/\\u003cb\\u003emetagpt\\u003c/b\\u003e-complete-guide-to-the-best-ai-agent-available-...\",\n \"pagemap\": {\n \"cse_thumbnail\": [\n {\n \"src\": \"https://encrypted-tbn2.gstatic.com/images?q=tbn:ANd9GcSVwf1WWLtVpqJCZ1E_t7TrpSZ7nrwsCUWar6x9YzlOsX1aSH7EGHbkIlY\",\n \"width\": \"290\",\n \"height\": \"174\"\n }\n ],\n \"imageobject\": [\n {\n \"width\": \"1000\",\n \"url\": \"https://www.unite.ai/wp-content/uploads/2023/09/Heisenbergforlife_Center_the_scene_in_zoomed_scope_around_a_hum_7f069632-eda5-4edd-858b-cb44fec82929-1000x600.png\",\n \"height\": \"600\"\n },\n {\n \"url\": \"https://www.unite.ai/wp-content/uploads/2021/03/logoUNITE230X30BLACK-1.svg\"\n }\n ],\n \"person\": [\n {\n \"name\": \"Aayush Mittal\"\n }\n ],\n \"organization\": [\n {\n \"name\": \"Unite.AI\"\n }\n ],\n \"metatags\": [\n {\n \"og:image\": \"https://www.unite.ai/wp-content/uploads/2023/09/Heisenbergforlife_Center_the_scene_in_zoomed_scope_around_a_hum_7f069632-eda5-4edd-858b-cb44fec82929-1000x600.png\",\n \"twitter:card\": \"summary\",\n \"article:published_time\": \"2023-09-11T18:03:48+00:00\",\n \"og:image:width\": \"1121\",\n \"og:site_name\": \"Unite.AI\",\n \"twitter:url\": \"https://www.unite.ai/metagpt-complete-guide-to-the-best-ai-agent-available-right-now/\",\n \"twitter:label1\": \"Written by\",\n \"twitter:label2\": \"Est. reading time\",\n \"og:image:type\": \"image/png\",\n \"msapplication-tileimage\": \"https://www.unite.ai/wp-content/uploads/2023/09/Heisenbergforlife_Center_the_scene_in_zoomed_scope_around_a_hum_7f069632-eda5-4edd-858b-cb44fec82929.png\",\n \"og:description\": \"Discover why MetaGPT outperforms AutoGPT, BabyAgi, and other AI agents in complex coding tasks. Our in-depth article guides you through the setup process and provides illustrative examples. Build GPT-powered microapps with a single line of prompt\",\n \"twitter:creator\": \"@UniteAI\",\n \"twitter:image\": \"https://www.unite.ai/wp-content/uploads/2023/09/Heisenbergforlife_Center_the_scene_in_zoomed_scope_around_a_hum_7f069632-eda5-4edd-858b-cb44fec82929-1000x600.png\",\n \"article:publisher\": \"https://www.facebook.com/uniteai\",\n \"twitter:data1\": \"Aayush Mittal\",\n \"og:image:secure_url\": \"https://www.unite.ai/wp-content/uploads/2023/09/Heisenbergforlife_Center_the_scene_in_zoomed_scope_around_a_hum_7f069632-eda5-4edd-858b-cb44fec82929.png\",\n \"twitter:data2\": \"9 minutes\",\n \"twitter:site\": \"@UniteAI\",\n \"og:video:type\": \"video/mp4\",\n \"uri-translation\": \"on\",\n \"og:type\": \"article\",\n \"twitter:title\": \"MetaGPT: Complete Guide to the Best AI Agent Available Right Now\",\n \"og:image:alt\": \"MetaGPBassed Illustration of human and machine collaborationT\",\n \"author\": \"Aayush Mittal\",\n \"og:title\": \"MetaGPT: Complete Guide to the Best AI Agent Available Right Now\",\n \"og:image:height\": \"628\",\n \"og:updated_time\": \"2023-09-11T14:03:48-04:00\",\n \"article:tag\": \"AI AGENTS\",\n \"og:video\": \"https://www.unite.ai/wp-content/uploads/2023/09/ezgif.com-optimize-online-video-cutter.com_.mp4\",\n \"viewport\": \"width=device-width,initial-scale=1.0,user-scalable=yes\",\n \"og:locale\": \"en_US\",\n \"og:rich_attachment\": \"1\",\n \"og:url\": \"https://www.unite.ai/metagpt-complete-guide-to-the-best-ai-agent-available-right-now/\"\n }\n ],\n \"cse_image\": [\n {\n \"src\": \"https://www.unite.ai/wp-content/uploads/2023/09/Heisenbergforlife_Center_the_scene_in_zoomed_scope_around_a_hum_7f069632-eda5-4edd-858b-cb44fec82929-1000x600.png\"\n }\n ],\n \"blogposting\": [\n {\n \"image\": \"https://www.unite.ai/wp-content/uploads/2023/09/Heisenbergforlife_Center_the_scene_in_zoomed_scope_around_a_hum_7f069632-eda5-4edd-858b-cb44fec82929.png\",\n \"datemodified\": \"2023-09-11T18:03:48+00:00\",\n \"author\": \"Aayush Mittal\",\n \"name\": \"MetaGPT: Complete Guide to the Best AI Agent Available Right Now\",\n \"description\": \"With Large Language Models (LLMs) like ChatGPT, OpenAI has witnessed a surge in enterprise and user adoption, currently raking in around $80 million in monthly revenue. According to a recent...\",\n \"headline\": \"MetaGPT: Complete Guide to the Best AI Agent Available Right Now\",\n \"datepublished\": \"2023-09-11\"\n }\n ],\n \"newsarticle\": [\n {\n \"datemodified\": \"2023-09-11\",\n \"keywords\": \"AI AGENTSAutoGPTDockergenerative aiLLMMetaGPTnlpPROMPT ENGINEERINGpython\",\n \"headline\": \"MetaGPT: Complete Guide to the Best AI Agent Available Right Now\",\n \"datepublished\": \"2023-09-11\"\n }\n ]\n }\n },\n {\n \"kind\": \"customsearch#result\",\n \"title\": \"Thoughts on MetaGPT : r/ProductManagement\",\n \"htmlTitle\": \"Thoughts on \\u003cb\\u003eMetaGPT\\u003c/b\\u003e : r/ProductManagement\",\n \"link\": \"https://www.reddit.com/r/ProductManagement/comments/163vekc/thoughts_on_metagpt/\",\n \"displayLink\": \"www.reddit.com\",\n \"snippet\": \"Aug 28, 2023 ... Thoughts on MetaGPT. YT shorts - a quick summaryExplainer YT video - maynot be the best, but beginner friendly. ... PS: feel free to link more ...\",\n \"htmlSnippet\": \"Aug 28, 2023 \\u003cb\\u003e...\\u003c/b\\u003e Thoughts on \\u003cb\\u003eMetaGPT\\u003c/b\\u003e. YT shorts - a quick summaryExplainer YT video - maynot be the best, but beginner friendly. ... PS: feel free to link more ...\",\n \"cacheId\": \"fDkEZ_skdhcJ\",\n \"formattedUrl\": \"https://www.reddit.com/r/ProductManagement/.../thoughts_on_metagpt/\",\n \"htmlFormattedUrl\": \"https://www.reddit.com/r/ProductManagement/.../thoughts_on_\\u003cb\\u003emetagpt\\u003c/b\\u003e/\",\n \"pagemap\": {\n \"cse_thumbnail\": [\n {\n \"src\": \"https://encrypted-tbn2.gstatic.com/images?q=tbn:ANd9GcSnWudLgGG_2ao_7EWw3EW58JBUQkJ1m4LOHzyiajVHq10p0_TNAeCRlik\",\n \"width\": \"259\",\n \"height\": \"194\"\n }\n ],\n \"metatags\": [\n {\n \"og:image\": \"https://share.redd.it/preview/post/163vekc\",\n \"theme-color\": \"#000000\",\n \"og:image:width\": \"1200\",\n \"og:type\": \"website\",\n \"og:image:alt\": \"An image containing a preview of the post\",\n \"twitter:card\": \"summary_large_image\",\n \"twitter:title\": \"r/ProductManagement on Reddit: Thoughts on MetaGPT\",\n \"og:site_name\": \"Reddit\",\n \"og:title\": \"r/ProductManagement on Reddit: Thoughts on MetaGPT\",\n \"og:image:height\": \"630\",\n \"msapplication-navbutton-color\": \"#000000\",\n \"og:description\": \"Posted by u/CheraCholan - No votes and 4 comments\",\n \"twitter:image\": \"https://share.redd.it/preview/post/163vekc\",\n \"apple-mobile-web-app-status-bar-style\": \"black\",\n \"twitter:site\": \"@reddit\",\n \"viewport\": \"width=device-width, initial-scale=1, viewport-fit=cover\",\n \"apple-mobile-web-app-capable\": \"yes\",\n \"og:ttl\": \"600\",\n \"og:url\": \"https://www.reddit.com/r/ProductManagement/comments/163vekc/thoughts_on_metagpt/\"\n }\n ],\n \"cse_image\": [\n {\n \"src\": \"https://external-preview.redd.it/thoughts-on-metagpt-v0-VQP3cNl_-L2zHMe4QWMy1GTBsiLHKNj0lg-u_o_nZug.jpg?auto=webp&s=03900a2b49a801e7d769a0ae8d2ec7a05011c1fc\"\n }\n ]\n }\n },\n {\n \"kind\": \"customsearch#result\",\n \"title\": \"MetaGPT: Meta Programming for Multi-Agent Collaborative ...\",\n \"htmlTitle\": \"\\u003cb\\u003eMetaGPT\\u003c/b\\u003e: Meta Programming for Multi-Agent Collaborative ...\",\n \"link\": \"https://news.ycombinator.com/item?id=37076125\",\n \"displayLink\": \"news.ycombinator.com\",\n \"snippet\": \"You can use multiple agents, or split a lot of information across multiple requests to one agent. The result is the same. Some problems require a full ...\",\n \"htmlSnippet\": \"You can use multiple agents, or split a lot of information across multiple requests to one agent. The result is the same. Some problems require a full ...\",\n \"cacheId\": \"PvjWUfqo0GAJ\",\n \"formattedUrl\": \"https://news.ycombinator.com/item?id=37076125\",\n \"htmlFormattedUrl\": \"https://news.ycombinator.com/item?id=37076125\",\n \"pagemap\": {\n \"metatags\": [\n {\n \"referrer\": \"origin\",\n \"viewport\": \"width=device-width, initial-scale=1.0\"\n }\n ]\n }\n },\n {\n \"kind\": \"customsearch#result\",\n \"title\": \"MetaGPT: a Multi-Agent Framework to Automate Your Software ...\",\n \"htmlTitle\": \"\\u003cb\\u003eMetaGPT\\u003c/b\\u003e: a Multi-Agent Framework to Automate Your Software ...\",\n \"link\": \"https://medium.datadriveninvestor.com/metagpt-a-multi-agent-framework-to-automate-your-software-company-4b6ae747cc36\",\n \"displayLink\": \"medium.datadriveninvestor.com\",\n \"snippet\": \"MetaGPT is about to reach 10000 stars on Github. It's a Multi-Agent Framework that can behave as an engineer, product manager, architect, project managers.\",\n \"htmlSnippet\": \"\\u003cb\\u003eMetaGPT\\u003c/b\\u003e is about to reach 10000 stars on Github. It's a Multi-Agent Framework that can behave as an engineer, product manager, architect, project managers.\",\n \"cacheId\": \"qWqvRF7SoGsJ\",\n \"formattedUrl\": \"https://medium.datadriveninvestor.com/metagpt-a-multi-agent-framework-t...\",\n \"htmlFormattedUrl\": \"https://medium.datadriveninvestor.com/\\u003cb\\u003emetagpt\\u003c/b\\u003e-a-multi-agent-framework-t...\",\n \"pagemap\": {\n \"cse_thumbnail\": [\n {\n \"src\": \"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRKDyUf8JumEvosQ1ZmxQ1dGmOGIx1jd4bvnICexOb2jFmKZHKagMGoQ0xI\",\n \"width\": \"242\",\n \"height\": \"209\"\n }\n ],\n \"metatags\": [\n {\n \"og:image\": \"https://miro.medium.com/v2/da:true/resize:fit:1200/0*g2b2hbP8HykGIIN3\",\n \"twitter:app:url:iphone\": \"medium://p/4b6ae747cc36\",\n \"theme-color\": \"#000000\",\n \"article:published_time\": \"2023-09-05T05:20:30.732Z\",\n \"twitter:card\": \"summary_large_image\",\n \"og:site_name\": \"Medium\",\n \"al:android:package\": \"com.medium.reader\",\n \"twitter:label1\": \"Reading time\",\n \"twitter:tile:template:testing\": \"2\",\n \"twitter:app:id:iphone\": \"828256236\",\n \"title\": \"MetaGPT: a Multi-Agent Framework to Automate Your Software Company | by Peter Xing | DataDrivenInvestor\",\n \"al:ios:url\": \"medium://p/4b6ae747cc36\",\n \"og:description\": \"MetaGPT is about to reach 10,000 stars on Github. It’s a Multi-Agent Framework that can behave as an engineer, product manager, architect…\",\n \"twitter:creator\": \"@peterxing\",\n \"al:ios:app_store_id\": \"828256236\",\n \"twitter:data1\": \"2 min read\",\n \"twitter:site\": \"@DDInvestorHQ\",\n \"twitter:tile:info1:text\": \"Peter Xing\",\n \"twitter:tile:info1:icon\": \"Person\",\n \"og:type\": \"article\",\n \"twitter:title\": \"MetaGPT: a Multi-Agent Framework to Automate Your Software Company\",\n \"al:ios:app_name\": \"Medium\",\n \"twitter:cta\": \"Read on Medium\",\n \"author\": \"Peter Xing\",\n \"og:title\": \"MetaGPT: a Multi-Agent Framework to Automate Your Software Company\",\n \"al:web:url\": \"https://medium.datadriveninvestor.com/metagpt-a-multi-agent-framework-to-automate-your-software-company-4b6ae747cc36\",\n \"article:author\": \"https://medium.com/@peterxing\",\n \"twitter:tile:info2:text\": \"Sep 4, 2023\",\n \"twitter:image:src\": \"https://miro.medium.com/v2/da:true/resize:fit:1200/0*g2b2hbP8HykGIIN3\",\n \"al:android:url\": \"medium://p/4b6ae747cc36\",\n \"referrer\": \"unsafe-url\",\n \"fb:app_id\": \"542599432471018\",\n \"viewport\": \"width=device-width,minimum-scale=1,initial-scale=1,maximum-scale=1\",\n \"twitter:tile:info2:icon\": \"Calendar\",\n \"twitter:description\": \"MetaGPT is about to reach 10,000 stars on Github. It’s a Multi-Agent Framework that can behave as an engineer, product manager, architect…\",\n \"twitter:tile:image\": \"https://miro.medium.com/v2/da:true/resize:fit:1200/0*g2b2hbP8HykGIIN3\",\n \"og:url\": \"https://medium.datadriveninvestor.com/metagpt-a-multi-agent-framework-to-automate-your-software-company-4b6ae747cc36\",\n \"twitter:app:name:iphone\": \"Medium\",\n \"al:android:app_name\": \"Medium\"\n }\n ],\n \"cse_image\": [\n {\n \"src\": \"https://miro.medium.com/v2/da:true/resize:fit:1200/0*g2b2hbP8HykGIIN3\"\n }\n ]\n }\n },\n {\n \"kind\": \"customsearch#result\",\n \"title\": \"MetaGPT - ChatGPT\",\n \"htmlTitle\": \"MetaGPT - ChatGPT\",\n \"link\": \"https://chat.openai.com/g/g-gHceUPFhE-metagpt\",\n \"displayLink\": \"chat.openai.com\",\n \"snippet\": \"GPT. MetaGPT. Crafts specialized prompts for diverse GPT applications. By Ankit Pal. Sign up to chat. Requires ChatGPT Plus.\",\n \"htmlSnippet\": \"GPT. \\u003cb\\u003eMetaGPT\\u003c/b\\u003e. Crafts specialized prompts for diverse GPT applications. By Ankit Pal. Sign up to chat. Requires ChatGPT Plus.\",\n \"cacheId\": \"xhG1ItzjqPQJ\",\n \"formattedUrl\": \"https://chat.openai.com/g/g-gHceUPFhE-metagpt\",\n \"htmlFormattedUrl\": \"https://chat.openai.com/g/g-gHceUPFhE-\\u003cb\\u003emetagpt\\u003c/b\\u003e\",\n \"pagemap\": {\n \"metatags\": [\n {\n \"apple-itunes-app\": \"app-id=6448311069\",\n \"og:image\": \"https://files.oaiusercontent.com/file-pZO1spqW9XBbl6yhFEVA1xni?se=2123-10-18T23%3A51%3A44Z&sp=r&sv=2021-08-06&sr=b&rscc=max-age%3D31536000%2C%20immutable&rscd=attachment%3B%20filename%3Df77c7fb5-1ba1-487b-b610-19db286e62ab.png&sig=nPfDFuFwLLYoWGUotoX8wZ7mbXNRwg2wcIyXGpE19k0%3D\",\n \"og:type\": \"website\",\n \"og:image:width\": \"512\",\n \"og:site_name\": \"ChatGPT\",\n \"og:title\": \"ChatGPT - MetaGPT\",\n \"og:image:height\": \"512\",\n \"title\": \"ChatGPT - MetaGPT\",\n \"og:description\": \"Crafts specialized prompts for diverse GPT applications\",\n \"next-head-count\": \"21\",\n \"viewport\": \"width=device-width, initial-scale=1\",\n \"react-scroll-to-bottom:version\": \"4.2.0\",\n \"og:url\": \"/g/g-gHceUPFhE-metagpt\"\n }\n ],\n \"cse_image\": [\n {\n \"src\": \"https://files.oaiusercontent.com/file-pZO1spqW9XBbl6yhFEVA1xni?se=2123-10-18T23%3A51%3A44Z&sp=r&sv=2021-08-06&sr=b&rscc=max-age%3D31536000%2C%20immutable&rscd=attachment%3B%20filename%3Df77c7fb5-1ba1-487b-b610-19db286e62ab.png&sig=nPfDFuFwLLYoWGUotoX8wZ7mbXNRwg2wcIyXGpE19k0%3D\"\n }\n ]\n }\n },\n {\n \"kind\": \"customsearch#result\",\n \"title\": \"pip - Issue installing metagpt on Python 3.11 on Windows - Stack ...\",\n \"htmlTitle\": \"pip - Issue installing \\u003cb\\u003emetagpt\\u003c/b\\u003e on Python 3.11 on Windows - Stack ...\",\n \"link\": \"https://stackoverflow.com/questions/76871577/issue-installing-metagpt-on-python-3-11-on-windows\",\n \"displayLink\": \"stackoverflow.com\",\n \"snippet\": \"Aug 9, 2023 ... 1 Answer 1 · try to delete the file 'metagpt-0.1-py3.11.egg', and try again. · if still not work , you can use pip install -r requirements.txt ...\",\n \"htmlSnippet\": \"Aug 9, 2023 \\u003cb\\u003e...\\u003c/b\\u003e 1 Answer 1 · try to delete the file '\\u003cb\\u003emetagpt\\u003c/b\\u003e-0.1-py3.11.egg', and try again. · if still not work , you can use pip install -r requirements.txt ...\",\n \"cacheId\": \"rE7h8ENZAfsJ\",\n \"formattedUrl\": \"https://stackoverflow.com/.../issue-installing-metagpt-on-python-3-11-on-w...\",\n \"htmlFormattedUrl\": \"https://stackoverflow.com/.../issue-installing-\\u003cb\\u003emetagpt\\u003c/b\\u003e-on-python-3-11-on-w...\",\n \"pagemap\": {\n \"cse_thumbnail\": [\n {\n \"src\": \"https://encrypted-tbn2.gstatic.com/images?q=tbn:ANd9GcQYl7zuT3cw_BBRAyhdQEbQuBgqdNHXKHIYKL8S8ly8x9L_XA9sdwSmiHs\",\n \"width\": \"225\",\n \"height\": \"225\"\n }\n ],\n \"qapage\": [\n {\n \"image\": \"https://cdn.sstatic.net/Sites/stackoverflow/Img/apple-touch-icon@2.png?v=73d79a89bded\",\n \"primaryimageofpage\": \"https://cdn.sstatic.net/Sites/stackoverflow/Img/apple-touch-icon@2.png?v=73d79a89bded\",\n \"name\": \"Issue installing metagpt on Python 3.11 on Windows\",\n \"description\": \"I receives this error when trying to install metagpt packages: [WinError 32] The process cannot access the file because it is being used by another process: 'c:\\\\\\\\users\\\\\\\\anthony phan\\\\\\\\appdata\\\\\\\\local\\\\\\\\\"\n }\n ],\n \"question\": [\n {\n \"image\": \"https://cdn.sstatic.net/Sites/stackoverflow/Img/apple-touch-icon.png?v=c78bd457575a\",\n \"upvotecount\": \"1\",\n \"answercount\": \"1\",\n \"name\": \"Issue installing metagpt on Python 3.11 on Windows\",\n \"datecreated\": \"2023-08-09T22:10:59\",\n \"text\": \"I receives this error when trying to install metagpt packages: [WinError 32] The process cannot access the file because it is being used by another process: 'c:\\\\\\\\users\\\\\\\\anthony phan\\\\\\\\appdata\\\\\\\\local...\",\n \"url\": \"Share\"\n }\n ],\n \"answer\": [\n {\n \"upvotecount\": \"0\",\n \"text\": \"try to delete the file 'metagpt-0.1-py3.11.egg', and try again. if still not work , you can use pip install -r requirements.txt instead of python setup.py\",\n \"datecreated\": \"2023-08-30T02:04:27\",\n \"url\": \"Share\"\n }\n ],\n \"person\": [\n {\n \"name\": \"Anthony Q Phan\"\n },\n {\n \"name\": \"D yesfir\"\n }\n ],\n \"metatags\": [\n {\n \"og:image\": \"https://cdn.sstatic.net/Sites/stackoverflow/Img/apple-touch-icon@2.png?v=73d79a89bded\",\n \"og:type\": \"website\",\n \"twitter:card\": \"summary\",\n \"twitter:title\": \"Issue installing metagpt on Python 3.11 on Windows\",\n \"og:site_name\": \"Stack Overflow\",\n \"twitter:domain\": \"stackoverflow.com\",\n \"viewport\": \"width=device-width, height=device-height, initial-scale=1.0, minimum-scale=1.0\",\n \"twitter:description\": \"I receives this error when trying to install metagpt packages:\\n[WinError 32] The process cannot access the file because it is being used by another process: 'c:\\\\\\\\users\\\\\\\\anthony phan\\\\\\\\appdata\\\\\\\\local\\\\\\\\\",\n \"og:url\": \"https://stackoverflow.com/questions/76871577/issue-installing-metagpt-on-python-3-11-on-windows\"\n }\n ],\n \"cse_image\": [\n {\n \"src\": \"https://cdn.sstatic.net/Sites/stackoverflow/Img/apple-touch-icon@2.png?v=73d79a89bded\"\n }\n ]\n }\n }\n ]\n}\n", + "httplib2-GET-https://customsearch.googleapis.com/customsearch/v1-{\"params\": {\"q\": \"metagpt\", \"num\": \"6\", \"cx\": \"mock-google-cse\", \"key\": \"mock-google-key\", \"alt\": \"json\"}}": "{\n \"kind\": \"customsearch#search\",\n \"url\": {\n \"type\": \"application/json\",\n \"template\": \"https://www.googleapis.com/customsearch/v1?q={searchTerms}&num={count?}&start={startIndex?}&lr={language?}&safe={safe?}&cx={cx?}&sort={sort?}&filter={filter?}&gl={gl?}&cr={cr?}&googlehost={googleHost?}&c2coff={disableCnTwTranslation?}&hq={hq?}&hl={hl?}&siteSearch={siteSearch?}&siteSearchFilter={siteSearchFilter?}&exactTerms={exactTerms?}&excludeTerms={excludeTerms?}&linkSite={linkSite?}&orTerms={orTerms?}&dateRestrict={dateRestrict?}&lowRange={lowRange?}&highRange={highRange?}&searchType={searchType}&fileType={fileType?}&rights={rights?}&imgSize={imgSize?}&imgType={imgType?}&imgColorType={imgColorType?}&imgDominantColor={imgDominantColor?}&alt=json\"\n },\n \"queries\": {\n \"request\": [\n {\n \"title\": \"Google Custom Search - metagpt\",\n \"totalResults\": \"85300\",\n \"searchTerms\": \"metagpt\",\n \"count\": 6,\n \"startIndex\": 1,\n \"inputEncoding\": \"utf8\",\n \"outputEncoding\": \"utf8\",\n \"safe\": \"off\",\n \"cx\": \"mock-google-cse\"\n }\n ],\n \"nextPage\": [\n {\n \"title\": \"Google Custom Search - metagpt\",\n \"totalResults\": \"85300\",\n \"searchTerms\": \"metagpt\",\n \"count\": 6,\n \"startIndex\": 7,\n \"inputEncoding\": \"utf8\",\n \"outputEncoding\": \"utf8\",\n \"safe\": \"off\",\n \"cx\": \"mock-google-cse\"\n }\n ]\n },\n \"context\": {\n \"title\": \"metagpt1\"\n },\n \"searchInformation\": {\n \"searchTime\": 0.193417,\n \"formattedSearchTime\": \"0.19\",\n \"totalResults\": \"85300\",\n \"formattedTotalResults\": \"85,300\"\n },\n \"items\": [\n {\n \"kind\": \"customsearch#result\",\n \"title\": \"geekan/MetaGPT: The Multi-Agent Framework: Given one ... - GitHub\",\n \"htmlTitle\": \"geekan/MetaGPT: The Multi-Agent Framework: Given one ... - GitHub\",\n \"link\": \"https://github.com/geekan/MetaGPT\",\n \"displayLink\": \"github.com\",\n \"snippet\": \"The Multi-Agent Framework: Given one line Requirement, return PRD, Design, Tasks, Repo - GitHub - geekan/MetaGPT: The Multi-Agent Framework: Given one ...\",\n \"htmlSnippet\": \"The Multi-Agent Framework: Given one line Requirement, return PRD, Design, Tasks, Repo - GitHub - geekan/\\u003cb\\u003eMetaGPT\\u003c/b\\u003e: The Multi-Agent Framework: Given one ...\",\n \"cacheId\": \"gsshb0APPNgJ\",\n \"formattedUrl\": \"https://github.com/geekan/MetaGPT\",\n \"htmlFormattedUrl\": \"https://github.com/geekan/\\u003cb\\u003eMetaGPT\\u003c/b\\u003e\",\n \"pagemap\": {\n \"cse_thumbnail\": [\n {\n \"src\": \"https://encrypted-tbn1.gstatic.com/images?q=tbn:ANd9GcRuD8YUvRcltmdoxKyuIbt8UZhg3LE5mwNX7KPXDB15YIJRKdT2m5JiweuS\",\n \"width\": \"318\",\n \"height\": \"159\"\n }\n ],\n \"softwaresourcecode\": [\n {\n \"author\": \"geekan\",\n \"name\": \"MetaGPT\",\n \"text\": \"MetaGPT: The Multi-Agent Framework Assign different roles to GPTs to form a collaborative software entity for complex tasks. MetaGPT takes a one line requirement as input and outputs user stories...\"\n }\n ],\n \"metatags\": [\n {\n \"octolytics-url\": \"https://collector.github.com/github/collect\",\n \"apple-itunes-app\": \"app-id=1477376905, app-argument=https://github.com/geekan/MetaGPT\",\n \"og:image\": \"https://opengraph.githubassets.com/6178eb2aa6711c676eafc956e52345e71225c58cd0a666b54871171e847c0905/geekan/MetaGPT\",\n \"twitter:card\": \"summary_large_image\",\n \"og:image:width\": \"1200\",\n \"theme-color\": \"#1e2327\",\n \"og:site_name\": \"GitHub\",\n \"hovercard-subject-tag\": \"repository:660551251\",\n \"turbo-body-classes\": \"logged-out env-production page-responsive\",\n \"html-safe-nonce\": \"a6964edcf12c9ea83de0bf16db8105c60333287597018548250a0fa92b93d3f9\",\n \"expected-hostname\": \"github.com\",\n \"og:description\": \"🌟 The Multi-Agent Framework: Given one line Requirement, return PRD, Design, Tasks, Repo - GitHub - geekan/MetaGPT: 🌟 The Multi-Agent Framework: Given one line Requirement, return PRD, Design, Task...\",\n \"browser-errors-url\": \"https://api.github.com/_private/browser/errors\",\n \"octolytics-dimension-user_login\": \"geekan\",\n \"hostname\": \"github.com\",\n \"twitter:site\": \"@github\",\n \"browser-stats-url\": \"https://api.github.com/_private/browser/stats\",\n \"route-pattern\": \"/:user_id/:repository\",\n \"visitor-payload\": \"eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJBNjIxOjk2OUU6OTZERjlEQzpEM0UxM0RFOjY1QTNBQjU0IiwidmlzaXRvcl9pZCI6IjU2ODA2NDI2MDQ0ODY5MzA3NyIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9\",\n \"github-keyboard-shortcuts\": \"repository\",\n \"octolytics-dimension-repository_id\": \"660551251\",\n \"octolytics-dimension-repository_network_root_nwo\": \"geekan/MetaGPT\",\n \"twitter:title\": \"GitHub - geekan/MetaGPT: 🌟 The Multi-Agent Framework: Given one line Requirement, return PRD, Design, Tasks, Repo\",\n \"og:image:alt\": \"🌟 The Multi-Agent Framework: Given one line Requirement, return PRD, Design, Tasks, Repo - GitHub - geekan/MetaGPT: 🌟 The Multi-Agent Framework: Given one line Requirement, return PRD, Design, Task...\",\n \"og:type\": \"object\",\n \"optimizely-datafile\": \"{\\\"accountId\\\": \\\"16737760170\\\", \\\"projectId\\\": \\\"16737760170\\\", \\\"revision\\\": \\\"23\\\", \\\"attributes\\\": [{\\\"id\\\": \\\"16822470375\\\", \\\"key\\\": \\\"user_id\\\"}, {\\\"id\\\": \\\"17143601254\\\", \\\"key\\\": \\\"spammy\\\"}, {\\\"id\\\": \\\"18175660309\\\", \\\"key\\\": \\\"organization_plan\\\"}, {\\\"id\\\": \\\"18813001570\\\", \\\"key\\\": \\\"is_logged_in\\\"}, {\\\"id\\\": \\\"19073851829\\\", \\\"key\\\": \\\"geo\\\"}, {\\\"id\\\": \\\"20175462351\\\", \\\"key\\\": \\\"requestedCurrency\\\"}, {\\\"id\\\": \\\"20785470195\\\", \\\"key\\\": \\\"country_code\\\"}, {\\\"id\\\": \\\"21656311196\\\", \\\"key\\\": \\\"opened_downgrade_dialog\\\"}], \\\"audiences\\\": [{\\\"id\\\": \\\"$opt_dummy_audience\\\", \\\"name\\\": \\\"Optimizely-Generated Audience for Backwards Compatibility\\\", \\\"conditions\\\": \\\"[\\\\\\\"or\\\\\\\", {\\\\\\\"match\\\\\\\": \\\\\\\"exact\\\\\\\", \\\\\\\"name\\\\\\\": \\\\\\\"$opt_dummy_attribute\\\\\\\", \\\\\\\"type\\\\\\\": \\\\\\\"custom_attribute\\\\\\\", \\\\\\\"value\\\\\\\": \\\\\\\"$opt_dummy_value\\\\\\\"}]\\\"}], \\\"version\\\": \\\"4\\\", \\\"events\\\": [{\\\"id\\\": \\\"18188530140\\\", \\\"experimentIds\\\": [], \\\"key\\\": \\\"test_event\\\"}], \\\"integrations\\\": [], \\\"anonymizeIP\\\": true, \\\"botFiltering\\\": false, \\\"typedAudiences\\\": [], \\\"variables\\\": [], \\\"environmentKey\\\": \\\"production\\\", \\\"sdkKey\\\": \\\"UpVyJZaLVEGwJPQWf5pAD\\\", \\\"featureFlags\\\": [], \\\"rollouts\\\": [],\",\n \"og:title\": \"GitHub - geekan/MetaGPT: 🌟 The Multi-Agent Framework: Given one line Requirement, return PRD, Design, Tasks, Repo\",\n \"visitor-hmac\": \"471691c5f7e3204061bb09c630c440708f5c08a2824d60b0f28eea873d474cd7\",\n \"og:image:height\": \"600\",\n \"turbo-cache-control\": \"no-preview\",\n \"request-id\": \"A621:969E:96DF9DC:D3E13DE:65A3AB54\",\n \"analytics-location\": \"/\\u003cuser-name\\u003e/\\u003crepo-name\\u003e\",\n \"color-scheme\": \"light dark\",\n \"octolytics-dimension-repository_is_fork\": \"false\",\n \"go-import\": \"github.com/geekan/MetaGPT git https://github.com/geekan/MetaGPT.git\",\n \"browser-optimizely-client-errors-url\": \"https://api.github.com/_private/browser/optimizely_client/errors\",\n \"twitter:image:src\": \"https://opengraph.githubassets.com/6178eb2aa6711c676eafc956e52345e71225c58cd0a666b54871171e847c0905/geekan/MetaGPT\",\n \"octolytics-dimension-user_id\": \"2707039\",\n \"octolytics-dimension-repository_public\": \"true\",\n \"fb:app_id\": \"1401488693436528\",\n \"octolytics-dimension-repository_network_root_id\": \"660551251\",\n \"octolytics-dimension-repository_nwo\": \"geekan/MetaGPT\",\n \"viewport\": \"width=device-width\",\n \"twitter:description\": \"🌟 The Multi-Agent Framework: Given one line Requirement, return PRD, Design, Tasks, Repo - GitHub - geekan/MetaGPT: 🌟 The Multi-Agent Framework: Given one line Requirement, return PRD, Design, Task...\",\n \"current-catalog-service-hash\": \"82c569b93da5c18ed649ebd4c2c79437db4611a6a1373e805a3cb001c64130b7\",\n \"og:url\": \"https://github.com/geekan/MetaGPT\"\n }\n ],\n \"cse_image\": [\n {\n \"src\": \"https://opengraph.githubassets.com/6178eb2aa6711c676eafc956e52345e71225c58cd0a666b54871171e847c0905/geekan/MetaGPT\"\n }\n ]\n }\n },\n {\n \"kind\": \"customsearch#result\",\n \"title\": \"[2308.00352] MetaGPT: Meta Programming for A Multi-Agent ...\",\n \"htmlTitle\": \"[2308.00352] \\u003cb\\u003eMetaGPT\\u003c/b\\u003e: Meta Programming for A Multi-Agent ...\",\n \"link\": \"https://arxiv.org/abs/2308.00352\",\n \"displayLink\": \"arxiv.org\",\n \"snippet\": \"Aug 1, 2023 ... Computer Science \\u003e Artificial Intelligence · Title:MetaGPT: Meta Programming for A Multi-Agent Collaborative Framework · Bibliographic and ...\",\n \"htmlSnippet\": \"Aug 1, 2023 \\u003cb\\u003e...\\u003c/b\\u003e Computer Science > Artificial Intelligence · Title:\\u003cb\\u003eMetaGPT\\u003c/b\\u003e: Meta Programming for A Multi-Agent Collaborative Framework · Bibliographic and ...\",\n \"cacheId\": \"8_tddNY0jEYJ\",\n \"formattedUrl\": \"https://arxiv.org/abs/2308.00352\",\n \"htmlFormattedUrl\": \"https://arxiv.org/abs/2308.00352\",\n \"pagemap\": {\n \"cse_thumbnail\": [\n {\n \"src\": \"https://encrypted-tbn3.gstatic.com/images?q=tbn:ANd9GcStsc5IszP_UC7vkymrk7PhjHGOFQhTh862xtJcQkxDem2IteJQXpob6_Vb\",\n \"width\": \"336\",\n \"height\": \"150\"\n }\n ],\n \"metatags\": [\n {\n \"og:image\": \"/static/browse/0.3.4/images/arxiv-logo-fb.png\",\n \"theme-color\": \"#ffffff\",\n \"og:image:width\": \"1200\",\n \"twitter:card\": \"summary\",\n \"citation_title\": \"MetaGPT: Meta Programming for A Multi-Agent Collaborative Framework\",\n \"og:site_name\": \"arXiv.org\",\n \"citation_date\": \"2023/08/01\",\n \"og:description\": \"Remarkable progress has been made on automated problem solving through societies of agents based on large language models (LLMs). Existing LLM-based multi-agent systems can already solve simple dialogue tasks. Solutions to more complex tasks, however, are complicated through logic inconsistencies due to cascading hallucinations caused by naively chaining LLMs. Here we introduce MetaGPT, an innovative meta-programming framework incorporating efficient human workflows into LLM-based multi-agent collaborations. MetaGPT encodes Standardized Operating Procedures (SOPs) into prompt sequences for more streamlined workflows, thus allowing agents with human-like domain expertise to verify intermediate results and reduce errors. MetaGPT utilizes an assembly line paradigm to assign diverse roles to various agents, efficiently breaking down complex tasks into subtasks involving many agents working together. On collaborative software engineering benchmarks, MetaGPT generates more coherent solutions than previous chat-base\",\n \"og:image:secure_url\": \"/static/browse/0.3.4/images/arxiv-logo-fb.png\",\n \"twitter:image\": \"https://static.arxiv.org/icons/twitter/arxiv-logo-twitter-square.png\",\n \"citation_arxiv_id\": \"2308.00352\",\n \"citation_online_date\": \"2023/11/06\",\n \"twitter:image:alt\": \"arXiv logo\",\n \"twitter:site\": \"@arxiv\",\n \"citation_pdf_url\": \"http://arxiv.org/pdf/2308.00352.pdf\",\n \"msapplication-tilecolor\": \"#da532c\",\n \"og:type\": \"website\",\n \"og:image:alt\": \"arXiv logo\",\n \"twitter:title\": \"MetaGPT: Meta Programming for A Multi-Agent Collaborative Framework\",\n \"og:title\": \"MetaGPT: Meta Programming for A Multi-Agent Collaborative Framework\",\n \"citation_abstract\": \"Remarkable progress has been made on automated problem solving through societies of agents based on large language models (LLMs). Existing LLM-based multi-agent systems can already solve simple dialogue tasks. Solutions to more complex tasks, however, are complicated through logic inconsistencies due to cascading hallucinations caused by naively chaining LLMs. Here we introduce MetaGPT, an innovative meta-programming framework incorporating efficient human workflows into LLM-based multi-agent collaborations. MetaGPT encodes Standardized Operating Procedures (SOPs) into prompt sequences for more streamlined workflows, thus allowing agents with human-like domain expertise to verify intermediate results and reduce errors. MetaGPT utilizes an assembly line paradigm to assign diverse roles to various agents, efficiently breaking down complex tasks into subtasks involving many agents working together. On collaborative software engineering benchmarks, MetaGPT generates more coherent solutions than previous chat-base\",\n \"og:image:height\": \"700\",\n \"citation_author\": \"Hong, Sirui\",\n \"viewport\": \"width=device-width, initial-scale=1\",\n \"twitter:description\": \"Remarkable progress has been made on automated problem solving through societies of agents based on large language models (LLMs). Existing LLM-based multi-agent systems can already solve simple...\",\n \"og:url\": \"https://arxiv.org/abs/2308.00352v5\"\n }\n ],\n \"cse_image\": [\n {\n \"src\": \"https://arxiv.org/static/browse/0.3.4/images/arxiv-logo-one-color-white.svg\"\n }\n ]\n }\n },\n {\n \"kind\": \"customsearch#result\",\n \"title\": \"MetaGPT: Complete Guide to the Best AI Agent Available Right Now ...\",\n \"htmlTitle\": \"\\u003cb\\u003eMetaGPT\\u003c/b\\u003e: Complete Guide to the Best AI Agent Available Right Now ...\",\n \"link\": \"https://www.unite.ai/metagpt-complete-guide-to-the-best-ai-agent-available-right-now/\",\n \"displayLink\": \"www.unite.ai\",\n \"snippet\": \"Sep 11, 2023 ... The beauty of MetaGPT lies in its structuring. It capitalizes on meta-programming techniques to manipulate, analyze, and transform code in real- ...\",\n \"htmlSnippet\": \"Sep 11, 2023 \\u003cb\\u003e...\\u003c/b\\u003e The beauty of \\u003cb\\u003eMetaGPT\\u003c/b\\u003e lies in its structuring. It capitalizes on meta-programming techniques to manipulate, analyze, and transform code in real- ...\",\n \"cacheId\": \"qkZULzxVHNAJ\",\n \"formattedUrl\": \"https://www.unite.ai/metagpt-complete-guide-to-the-best-ai-agent-available-...\",\n \"htmlFormattedUrl\": \"https://www.unite.ai/\\u003cb\\u003emetagpt\\u003c/b\\u003e-complete-guide-to-the-best-ai-agent-available-...\",\n \"pagemap\": {\n \"cse_thumbnail\": [\n {\n \"src\": \"https://encrypted-tbn2.gstatic.com/images?q=tbn:ANd9GcSVwf1WWLtVpqJCZ1E_t7TrpSZ7nrwsCUWar6x9YzlOsX1aSH7EGHbkIlY\",\n \"width\": \"290\",\n \"height\": \"174\"\n }\n ],\n \"imageobject\": [\n {\n \"width\": \"1000\",\n \"url\": \"https://www.unite.ai/wp-content/uploads/2023/09/Heisenbergforlife_Center_the_scene_in_zoomed_scope_around_a_hum_7f069632-eda5-4edd-858b-cb44fec82929-1000x600.png\",\n \"height\": \"600\"\n },\n {\n \"url\": \"https://www.unite.ai/wp-content/uploads/2021/03/logoUNITE230X30BLACK-1.svg\"\n }\n ],\n \"person\": [\n {\n \"name\": \"Aayush Mittal\"\n }\n ],\n \"organization\": [\n {\n \"name\": \"Unite.AI\"\n }\n ],\n \"metatags\": [\n {\n \"og:image\": \"https://www.unite.ai/wp-content/uploads/2023/09/Heisenbergforlife_Center_the_scene_in_zoomed_scope_around_a_hum_7f069632-eda5-4edd-858b-cb44fec82929-1000x600.png\",\n \"twitter:card\": \"summary\",\n \"article:published_time\": \"2023-09-11T18:03:48+00:00\",\n \"og:image:width\": \"1121\",\n \"og:site_name\": \"Unite.AI\",\n \"twitter:url\": \"https://www.unite.ai/metagpt-complete-guide-to-the-best-ai-agent-available-right-now/\",\n \"twitter:label1\": \"Written by\",\n \"twitter:label2\": \"Est. reading time\",\n \"og:image:type\": \"image/png\",\n \"msapplication-tileimage\": \"https://www.unite.ai/wp-content/uploads/2023/09/Heisenbergforlife_Center_the_scene_in_zoomed_scope_around_a_hum_7f069632-eda5-4edd-858b-cb44fec82929.png\",\n \"og:description\": \"Discover why MetaGPT outperforms AutoGPT, BabyAgi, and other AI agents in complex coding tasks. Our in-depth article guides you through the setup process and provides illustrative examples. Build GPT-powered microapps with a single line of prompt\",\n \"twitter:creator\": \"@UniteAI\",\n \"twitter:image\": \"https://www.unite.ai/wp-content/uploads/2023/09/Heisenbergforlife_Center_the_scene_in_zoomed_scope_around_a_hum_7f069632-eda5-4edd-858b-cb44fec82929-1000x600.png\",\n \"article:publisher\": \"https://www.facebook.com/uniteai\",\n \"twitter:data1\": \"Aayush Mittal\",\n \"og:image:secure_url\": \"https://www.unite.ai/wp-content/uploads/2023/09/Heisenbergforlife_Center_the_scene_in_zoomed_scope_around_a_hum_7f069632-eda5-4edd-858b-cb44fec82929.png\",\n \"twitter:data2\": \"9 minutes\",\n \"twitter:site\": \"@UniteAI\",\n \"og:video:type\": \"video/mp4\",\n \"uri-translation\": \"on\",\n \"og:type\": \"article\",\n \"twitter:title\": \"MetaGPT: Complete Guide to the Best AI Agent Available Right Now\",\n \"og:image:alt\": \"MetaGPBassed Illustration of human and machine collaborationT\",\n \"author\": \"Aayush Mittal\",\n \"og:title\": \"MetaGPT: Complete Guide to the Best AI Agent Available Right Now\",\n \"og:image:height\": \"628\",\n \"og:updated_time\": \"2023-09-11T14:03:48-04:00\",\n \"article:tag\": \"AI AGENTS\",\n \"og:video\": \"https://www.unite.ai/wp-content/uploads/2023/09/ezgif.com-optimize-online-video-cutter.com_.mp4\",\n \"viewport\": \"width=device-width,initial-scale=1.0,user-scalable=yes\",\n \"og:locale\": \"en_US\",\n \"og:rich_attachment\": \"1\",\n \"og:url\": \"https://www.unite.ai/metagpt-complete-guide-to-the-best-ai-agent-available-right-now/\"\n }\n ],\n \"cse_image\": [\n {\n \"src\": \"https://www.unite.ai/wp-content/uploads/2023/09/Heisenbergforlife_Center_the_scene_in_zoomed_scope_around_a_hum_7f069632-eda5-4edd-858b-cb44fec82929-1000x600.png\"\n }\n ],\n \"blogposting\": [\n {\n \"image\": \"https://www.unite.ai/wp-content/uploads/2023/09/Heisenbergforlife_Center_the_scene_in_zoomed_scope_around_a_hum_7f069632-eda5-4edd-858b-cb44fec82929.png\",\n \"datemodified\": \"2023-09-11T18:03:48+00:00\",\n \"author\": \"Aayush Mittal\",\n \"name\": \"MetaGPT: Complete Guide to the Best AI Agent Available Right Now\",\n \"description\": \"With Large Language Models (LLMs) like ChatGPT, OpenAI has witnessed a surge in enterprise and user adoption, currently raking in around $80 million in monthly revenue. According to a recent...\",\n \"headline\": \"MetaGPT: Complete Guide to the Best AI Agent Available Right Now\",\n \"datepublished\": \"2023-09-11\"\n }\n ],\n \"newsarticle\": [\n {\n \"datemodified\": \"2023-09-11\",\n \"keywords\": \"AI AGENTSAutoGPTDockergenerative aiLLMMetaGPTnlpPROMPT ENGINEERINGpython\",\n \"headline\": \"MetaGPT: Complete Guide to the Best AI Agent Available Right Now\",\n \"datepublished\": \"2023-09-11\"\n }\n ]\n }\n },\n {\n \"kind\": \"customsearch#result\",\n \"title\": \"Thoughts on MetaGPT : r/ProductManagement\",\n \"htmlTitle\": \"Thoughts on \\u003cb\\u003eMetaGPT\\u003c/b\\u003e : r/ProductManagement\",\n \"link\": \"https://www.reddit.com/r/ProductManagement/comments/163vekc/thoughts_on_metagpt/\",\n \"displayLink\": \"www.reddit.com\",\n \"snippet\": \"Aug 28, 2023 ... Thoughts on MetaGPT. YT shorts - a quick summaryExplainer YT video - maynot be the best, but beginner friendly. ... PS: feel free to link more ...\",\n \"htmlSnippet\": \"Aug 28, 2023 \\u003cb\\u003e...\\u003c/b\\u003e Thoughts on \\u003cb\\u003eMetaGPT\\u003c/b\\u003e. YT shorts - a quick summaryExplainer YT video - maynot be the best, but beginner friendly. ... PS: feel free to link more ...\",\n \"cacheId\": \"fDkEZ_skdhcJ\",\n \"formattedUrl\": \"https://www.reddit.com/r/ProductManagement/.../thoughts_on_metagpt/\",\n \"htmlFormattedUrl\": \"https://www.reddit.com/r/ProductManagement/.../thoughts_on_\\u003cb\\u003emetagpt\\u003c/b\\u003e/\",\n \"pagemap\": {\n \"cse_thumbnail\": [\n {\n \"src\": \"https://encrypted-tbn2.gstatic.com/images?q=tbn:ANd9GcSnWudLgGG_2ao_7EWw3EW58JBUQkJ1m4LOHzyiajVHq10p0_TNAeCRlik\",\n \"width\": \"259\",\n \"height\": \"194\"\n }\n ],\n \"metatags\": [\n {\n \"og:image\": \"https://share.redd.it/preview/post/163vekc\",\n \"theme-color\": \"#000000\",\n \"og:image:width\": \"1200\",\n \"og:type\": \"website\",\n \"og:image:alt\": \"An image containing a preview of the post\",\n \"twitter:card\": \"summary_large_image\",\n \"twitter:title\": \"r/ProductManagement on Reddit: Thoughts on MetaGPT\",\n \"og:site_name\": \"Reddit\",\n \"og:title\": \"r/ProductManagement on Reddit: Thoughts on MetaGPT\",\n \"og:image:height\": \"630\",\n \"msapplication-navbutton-color\": \"#000000\",\n \"og:description\": \"Posted by u/CheraCholan - No votes and 4 comments\",\n \"twitter:image\": \"https://share.redd.it/preview/post/163vekc\",\n \"apple-mobile-web-app-status-bar-style\": \"black\",\n \"twitter:site\": \"@reddit\",\n \"viewport\": \"width=device-width, initial-scale=1, viewport-fit=cover\",\n \"apple-mobile-web-app-capable\": \"yes\",\n \"og:ttl\": \"600\",\n \"og:url\": \"https://www.reddit.com/r/ProductManagement/comments/163vekc/thoughts_on_metagpt/\"\n }\n ],\n \"cse_image\": [\n {\n \"src\": \"https://external-preview.redd.it/thoughts-on-metagpt-v0-VQP3cNl_-L2zHMe4QWMy1GTBsiLHKNj0lg-u_o_nZug.jpg?auto=webp&s=03900a2b49a801e7d769a0ae8d2ec7a05011c1fc\"\n }\n ]\n }\n },\n {\n \"kind\": \"customsearch#result\",\n \"title\": \"MetaGPT: Meta Programming for Multi-Agent Collaborative ...\",\n \"htmlTitle\": \"\\u003cb\\u003eMetaGPT\\u003c/b\\u003e: Meta Programming for Multi-Agent Collaborative ...\",\n \"link\": \"https://news.ycombinator.com/item?id=37076125\",\n \"displayLink\": \"news.ycombinator.com\",\n \"snippet\": \"You can use multiple agents, or split a lot of information across multiple requests to one agent. The result is the same. Some problems require a full ...\",\n \"htmlSnippet\": \"You can use multiple agents, or split a lot of information across multiple requests to one agent. The result is the same. Some problems require a full ...\",\n \"cacheId\": \"PvjWUfqo0GAJ\",\n \"formattedUrl\": \"https://news.ycombinator.com/item?id=37076125\",\n \"htmlFormattedUrl\": \"https://news.ycombinator.com/item?id=37076125\",\n \"pagemap\": {\n \"metatags\": [\n {\n \"referrer\": \"origin\",\n \"viewport\": \"width=device-width, initial-scale=1.0\"\n }\n ]\n }\n },\n {\n \"kind\": \"customsearch#result\",\n \"title\": \"MetaGPT: a Multi-Agent Framework to Automate Your Software ...\",\n \"htmlTitle\": \"\\u003cb\\u003eMetaGPT\\u003c/b\\u003e: a Multi-Agent Framework to Automate Your Software ...\",\n \"link\": \"https://medium.datadriveninvestor.com/metagpt-a-multi-agent-framework-to-automate-your-software-company-4b6ae747cc36\",\n \"displayLink\": \"medium.datadriveninvestor.com\",\n \"snippet\": \"MetaGPT is about to reach 10000 stars on Github. It's a Multi-Agent Framework that can behave as an engineer, product manager, architect, project managers.\",\n \"htmlSnippet\": \"\\u003cb\\u003eMetaGPT\\u003c/b\\u003e is about to reach 10000 stars on Github. It's a Multi-Agent Framework that can behave as an engineer, product manager, architect, project managers.\",\n \"cacheId\": \"qWqvRF7SoGsJ\",\n \"formattedUrl\": \"https://medium.datadriveninvestor.com/metagpt-a-multi-agent-framework-t...\",\n \"htmlFormattedUrl\": \"https://medium.datadriveninvestor.com/\\u003cb\\u003emetagpt\\u003c/b\\u003e-a-multi-agent-framework-t...\",\n \"pagemap\": {\n \"cse_thumbnail\": [\n {\n \"src\": \"https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRKDyUf8JumEvosQ1ZmxQ1dGmOGIx1jd4bvnICexOb2jFmKZHKagMGoQ0xI\",\n \"width\": \"242\",\n \"height\": \"209\"\n }\n ],\n \"metatags\": [\n {\n \"og:image\": \"https://miro.medium.com/v2/da:true/resize:fit:1200/0*g2b2hbP8HykGIIN3\",\n \"twitter:app:url:iphone\": \"medium://p/4b6ae747cc36\",\n \"theme-color\": \"#000000\",\n \"article:published_time\": \"2023-09-05T05:20:30.732Z\",\n \"twitter:card\": \"summary_large_image\",\n \"og:site_name\": \"Medium\",\n \"al:android:package\": \"com.medium.reader\",\n \"twitter:label1\": \"Reading time\",\n \"twitter:tile:template:testing\": \"2\",\n \"twitter:app:id:iphone\": \"828256236\",\n \"title\": \"MetaGPT: a Multi-Agent Framework to Automate Your Software Company | by Peter Xing | DataDrivenInvestor\",\n \"al:ios:url\": \"medium://p/4b6ae747cc36\",\n \"og:description\": \"MetaGPT is about to reach 10,000 stars on Github. It’s a Multi-Agent Framework that can behave as an engineer, product manager, architect…\",\n \"twitter:creator\": \"@peterxing\",\n \"al:ios:app_store_id\": \"828256236\",\n \"twitter:data1\": \"2 min read\",\n \"twitter:site\": \"@DDInvestorHQ\",\n \"twitter:tile:info1:text\": \"Peter Xing\",\n \"twitter:tile:info1:icon\": \"Person\",\n \"og:type\": \"article\",\n \"twitter:title\": \"MetaGPT: a Multi-Agent Framework to Automate Your Software Company\",\n \"al:ios:app_name\": \"Medium\",\n \"twitter:cta\": \"Read on Medium\",\n \"author\": \"Peter Xing\",\n \"og:title\": \"MetaGPT: a Multi-Agent Framework to Automate Your Software Company\",\n \"al:web:url\": \"https://medium.datadriveninvestor.com/metagpt-a-multi-agent-framework-to-automate-your-software-company-4b6ae747cc36\",\n \"article:author\": \"https://medium.com/@peterxing\",\n \"twitter:tile:info2:text\": \"Sep 4, 2023\",\n \"twitter:image:src\": \"https://miro.medium.com/v2/da:true/resize:fit:1200/0*g2b2hbP8HykGIIN3\",\n \"al:android:url\": \"medium://p/4b6ae747cc36\",\n \"referrer\": \"unsafe-url\",\n \"fb:app_id\": \"542599432471018\",\n \"viewport\": \"width=device-width,minimum-scale=1,initial-scale=1,maximum-scale=1\",\n \"twitter:tile:info2:icon\": \"Calendar\",\n \"twitter:description\": \"MetaGPT is about to reach 10,000 stars on Github. It’s a Multi-Agent Framework that can behave as an engineer, product manager, architect…\",\n \"twitter:tile:image\": \"https://miro.medium.com/v2/da:true/resize:fit:1200/0*g2b2hbP8HykGIIN3\",\n \"og:url\": \"https://medium.datadriveninvestor.com/metagpt-a-multi-agent-framework-to-automate-your-software-company-4b6ae747cc36\",\n \"twitter:app:name:iphone\": \"Medium\",\n \"al:android:app_name\": \"Medium\"\n }\n ],\n \"cse_image\": [\n {\n \"src\": \"https://miro.medium.com/v2/da:true/resize:fit:1200/0*g2b2hbP8HykGIIN3\"\n }\n ]\n }\n }\n ]\n}\n", + "aiohttp-post-https://google.serper.dev/search-{\"data\": \"[{\\\"num\\\": 8, \\\"page\\\": 1, \\\"q\\\": \\\"metagpt\\\"}]\", \"headers\": {\"Content-Type\": \"application/json\", \"X-API-KEY\": \"mock-serper-key\"}}": [ + { + "searchParameters": { + "q": "metagpt", + "num": 8, + "page": 1, + "type": "search", + "engine": "google" + }, + "organic": [ + { + "title": "geekan/MetaGPT: The Multi-Agent Framework: Given one ... - GitHub", + "link": "https://github.com/geekan/MetaGPT", + "snippet": "MetaGPT takes a one line requirement as input and outputs user stories / competitive analysis / requirements / data structures / APIs / documents, etc.", + "sitelinks": [ + { + "title": "Roadmap", + "link": "https://github.com/geekan/MetaGPT/blob/main/docs/ROADMAP.md" + }, + { + "title": "README.md", + "link": "https://github.com/geekan/MetaGPT/blob/main/README.md" + }, + { + "title": "Issues 161", + "link": "https://github.com/geekan/MetaGPT/issues" + }, + { + "title": "Actions", + "link": "https://github.com/geekan/MetaGPT/actions" + } + ], + "position": 1 + }, + { + "title": "[2308.00352] MetaGPT: Meta Programming for A Multi-Agent ... - arXiv", + "link": "https://arxiv.org/abs/2308.00352", + "snippet": "Abstract:Remarkable progress has been made on automated problem solving through societies of agents based on large language models (LLMs).", + "date": "Aug 1, 2023", + "position": 2 + }, + { + "title": "MetaGPT: a Multi-Agent Framework to Automate Your Software ...", + "link": "https://medium.datadriveninvestor.com/metagpt-a-multi-agent-framework-to-automate-your-software-company-4b6ae747cc36", + "snippet": "MetaGPT is about to reach 10000 stars on Github. It's a Multi-Agent Framework that can behave as an engineer, product manager, architect, project managers.", + "position": 3 + }, + { + "title": "MetaGPT HUGE Update: Autonomous AI Agents with Incremental ...", + "link": "https://www.youtube.com/watch?v=Xyws6iI-eH8", + "snippet": "In this video, we unravel the magic at the core of MetaGPT, exploring its multi-agent framework ...", + "date": "Dec 23, 2023", + "attributes": { + "Duration": "11:38", + "Posted": "Dec 23, 2023" + }, + "imageUrl": "https://i.ytimg.com/vi/Xyws6iI-eH8/default.jpg?sqp=-oaymwEECHgQQw&rs=AMzJL3k9VHKSi-z-si4PJd1tNv8Itm4h5g", + "position": 4 + }, + { + "title": "MetaGPT - Apps on Google Play", + "link": "https://play.google.com/store/apps/details?id=com.metagpt.app&hl=en&gl=US", + "snippet": "Real-time crypto monitor.Track prices, set alerts, seize opportunities instantly.", + "date": "Jan 1, 2024", + "position": 5 + }, + { + "title": "MetaGPT | Discover AI use cases - GPT-3 Demo", + "link": "https://gpt3demo.com/apps/metagpt", + "snippet": "Assign different roles to GPTs to form a collaborative software entity for complex tasks. MetaGPT takes a one-line requirement as input and outputs user ...", + "position": 6 + }, + { + "title": "MetaGPT: AI-Powered Web Development That Changes the Game", + "link": "https://www.analyticsvidhya.com/blog/2024/01/meet-metagpt-the-chatgpt-powered-ai-assistant-that-turns-text-into-web-apps/", + "snippet": "MetaGPT is an AI assistant that leverages the power of GPT-4, a state-of-the-art language model developed by OpenAI. ChatGPT is trained on vast ...", + "date": "Jan 4, 2024", + "position": 7 + }, + { + "title": "MetaGPT: Complete Guide to the Best AI Agent Available Right Now", + "link": "https://www.unite.ai/metagpt-complete-guide-to-the-best-ai-agent-available-right-now/", + "snippet": "Discover why MetaGPT outperforms AutoGPT, BabyAgi, and other AI agents in complex coding tasks. Our in-depth article guides you through the ...", + "date": "Sep 11, 2023", + "position": 8 + } + ], + "relatedSearches": [ + { + "query": "MetaGPT online" + }, + { + "query": "Metagpt download" + }, + { + "query": "MetaGPT paper" + }, + { + "query": "Metagpt app" + }, + { + "query": "Metagpt github" + }, + { + "query": "MetaGPT huggingface" + }, + { + "query": "MetaGPT review" + }, + { + "query": "MetaGPT AI" + } + ] + } + ], + "aiohttp-post-https://google.serper.dev/search-{\"data\": \"[{\\\"num\\\": 6, \\\"page\\\": 1, \\\"q\\\": \\\"metagpt\\\"}]\", \"headers\": {\"Content-Type\": \"application/json\", \"X-API-KEY\": \"mock-serper-key\"}}": [ + { + "searchParameters": { + "q": "metagpt", + "num": 6, + "page": 1, + "type": "search", + "engine": "google" + }, + "organic": [ + { + "title": "geekan/MetaGPT: The Multi-Agent Framework: Given one ... - GitHub", + "link": "https://github.com/geekan/MetaGPT", + "snippet": "MetaGPT takes a one line requirement as input and outputs user stories / competitive analysis / requirements / data structures / APIs / documents, etc.", + "sitelinks": [ + { + "title": "Roadmap", + "link": "https://github.com/geekan/MetaGPT/blob/main/docs/ROADMAP.md" + }, + { + "title": "README.md", + "link": "https://github.com/geekan/MetaGPT/blob/main/README.md" + }, + { + "title": "Issues 161", + "link": "https://github.com/geekan/MetaGPT/issues" + }, + { + "title": "Actions", + "link": "https://github.com/geekan/MetaGPT/actions" + } + ], + "position": 1 + }, + { + "title": "[2308.00352] MetaGPT: Meta Programming for A Multi-Agent ... - arXiv", + "link": "https://arxiv.org/abs/2308.00352", + "snippet": "Abstract:Remarkable progress has been made on automated problem solving through societies of agents based on large language models (LLMs).", + "date": "Aug 1, 2023", + "position": 2 + }, + { + "title": "MetaGPT: a Multi-Agent Framework to Automate Your Software ...", + "link": "https://medium.datadriveninvestor.com/metagpt-a-multi-agent-framework-to-automate-your-software-company-4b6ae747cc36", + "snippet": "MetaGPT is about to reach 10000 stars on Github. It's a Multi-Agent Framework that can behave as an engineer, product manager, architect, project managers.", + "position": 3 + }, + { + "title": "MetaGPT HUGE Update: Autonomous AI Agents with Incremental ...", + "link": "https://www.youtube.com/watch?v=Xyws6iI-eH8", + "snippet": "In this video, we unravel the magic at the core of MetaGPT, exploring its multi-agent framework ...", + "date": "Dec 23, 2023", + "attributes": { + "Duration": "11:38", + "Posted": "Dec 23, 2023" + }, + "imageUrl": "https://i.ytimg.com/vi/Xyws6iI-eH8/default.jpg?sqp=-oaymwEECHgQQw&rs=AMzJL3k9VHKSi-z-si4PJd1tNv8Itm4h5g", + "position": 4 + }, + { + "title": "MetaGPT - Apps on Google Play", + "link": "https://play.google.com/store/apps/details?id=com.metagpt.app&hl=en&gl=US", + "snippet": "Real-time crypto monitor.Track prices, set alerts, seize opportunities instantly.", + "date": "Jan 1, 2024", + "position": 5 + }, + { + "title": "MetaGPT: AI-Powered Web Development That Changes the Game", + "link": "https://www.analyticsvidhya.com/blog/2024/01/meet-metagpt-the-chatgpt-powered-ai-assistant-that-turns-text-into-web-apps/", + "snippet": "MetaGPT is an AI assistant that leverages the power of GPT-4, a state-of-the-art language model developed by OpenAI. ChatGPT is trained on vast ...", + "date": "Jan 4, 2024", + "position": 6 + } + ], + "relatedSearches": [ + { + "query": "MetaGPT online" + }, + { + "query": "MetaGPT paper" + }, + { + "query": "Metagpt review" + }, + { + "query": "Metagpt download" + }, + { + "query": "Metagpt github" + }, + { + "query": "MetaGPT AI" + }, + { + "query": "MetaGPT huggingface" + }, + { + "query": "Metagpt OpenAI" + } + ] + } + ], + "curl-cffi-POST-https://duckduckgo.com-{\"data\": {\"q\": \"metagpt\"}}": "metagpt at DuckDuckGo
", + "curl-cffi-GET-https://links.duckduckgo.com/d.js-{\"params\": {\"bing_market\": \"wt-WT\", \"df\": null, \"ex\": \"-1\", \"kl\": \"wt-wt\", \"l\": \"wt-wt\", \"q\": \"metagpt\", \"s\": \"0\", \"sp\": \"0\", \"vqd\": \"4-118631859838297093459588814466521506726\"}}": "if (DDG.deep && DDG.deep.setUpstream) DDG.deep.setUpstream(\"bingv7aa\");DDG.deep.bn={'ivc':1};if (DDG.pageLayout) DDG.pageLayout.load('a',[], {\"page_load_url\":\"https://duckduckgo.com/y.js?iurl=%7B2%7DIG%3DFD8EFA3AD04A446FA24ACF32036DB0FF%26CID%3D3E2A18C583DE6B6F14A00CC382616A60%26Type%3DEvent.CPT%26DATA%3D0\"});DDG.deep.signalSummary = \"retail:h\";DDG.inject('DDG.Data.languages.resultLanguages', {\"en\":[\"https://github.com/geekan/MetaGPT\",\"https://docs.deepwisdom.ai/\",\"https://www.unite.ai/metagpt-complete-guide-to-the-best-ai-agent-available-right-now/\",\"https://docs.deepwisdom.ai/main/en/guide/get_started/introduction.html\",\"https://arxiv.org/abs/2308.00352\",\"https://github.com/geekan/MetaGPT/blob/main/README.md\",\"https://interestingengineering.com/innovation/metagpt-create-apps-text-prompts\",\"https://aibusiness.com/nlp/metagpt-text-to-app-ai-simplifies-web-dev\",\"https://github.com/PlaiD3/MetaGPT/blob/main/README.md\",\"https://www.youtube.com/watch?v=wpgC5fmtU70\",\"https://www.tomsguide.com/news/ai-tool-uses-chatgpt-to-build-you-a-website-in-30-minutes-and-we-tried-it\",\"https://www.kdnuggets.com/meet-metagpt-the-chatgptpowered-ai-assistant-that-turns-text-into-web-apps\",\"https://www.straight.com/guides/software/a-complete-guide-to-metagpt-the-best-ai-agent-available-now/\",\"https://www.marktechpost.com/2023/08/09/meet-metagpt-the-open-source-ai-framework-that-transforms-gpts-into-engineers-architects-and-managers/\",\"https://medium.com/gta-generative-tech-advances/metagpt-an-interesting-approach-to-multi-agent-collaboration-5ace263c4fd8\",\"https://www.youtube.com/watch?v=nqZlTV_L6Ao\",\"https://medium.com/aimonks/metagpt-a-framework-for-multi-agent-meta-programming-6c79f2eafb8e\",\"https://medium.com/@smraiyyan/metagpt-unleashed-crafting-your-virtual-software-company-from-scratch-6ea60cd70da1\",\"https://www.almabetter.com/bytes/articles/metagpt\",\"https://geekflare.com/metagpt-multi-agent-framework/\",\"https://analyticsindiamag.com/metagpt-realising-the-gpt-4-dream/\",\"https://medium.com/technology-hits/autogpt-langchain-deep-lake-metagpt-a-revolutionary-framework-for-building-advanced-ai-e2c579d86494\",\"https://generativeai.pub/analyzing-an-exciting-generative-ai-research-called-metagpt-2106385312db\",\"https://www.msn.com/en-us/news/technology/generative-ai-apis-and-chatgpt-alternatives-for-developers-to-consider/ar-AA1ltwXb\",\"https://www.theguardian.com/technology/2024/jan/08/ai-tools-chatgpt-copyrighted-material-openai\",\"https://www.forbes.com/sites/katiejennings/2024/01/05/health-ai-startup-nabla-was-built-on-gpt-4-now-its-abandoning-openai-for-open-source/\",\"https://www.technologyreview.com/2024/01/04/1086046/whats-next-for-ai-in-2024/\"],\"zh-CN\":[\"https://blog.csdn.net/Attitude93/article/details/135550499\",\"https://zhuanlan.zhihu.com/p/677608276\"]});DDG.deep.pageLayoutSummary = \"w29v1r1\";DDG.inject('DDG.Data.languages.adLanguages', {});if (DDG.pageLayout) DDG.pageLayout.load('d',[{\"a\":\"geekan/MetaGPT. This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository. About. \\ud83c\\udf1f The Multi-Agent Framework: Given one line Requirement, return PRD, Design, Tasks, Repo deepwisdom.ai/ Topics. agent multi-agent gpt hacktoberfest llm metagpt Resources. Readme\",\"ae\":null,\"b\":\"gh\\tGitHub\\tgithub.com\",\"c\":\"https://github.com/geekan/MetaGPT\",\"d\":\"github.com/geekan/MetaGPT\",\"da\":\"\",\"h\":0,\"i\":\"github.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: The Multi-Agent Framework - GitHub\",\"u\":\"https://github.com/geekan/MetaGPT\"},{\"a\":\"MetaGPT. The Multi-Agent Framework. Assign different roles to GPTs to form a collaborative software entity for complex tasks. Get Started. View on Github. Agents. Explore agent creation, configuration, and management, including algorithms and techniques. Demos.\",\"ae\":null,\"c\":\"https://docs.deepwisdom.ai/\",\"d\":\"docs.deepwisdom.ai\",\"da\":\"\",\"h\":0,\"i\":\"docs.deepwisdom.ai\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT | MetaGPT\",\"u\":\"https://docs.deepwisdom.ai/\"},{\"a\":\"MetaGPT is a Multi-agent system that utilizes Large Language models and Standardized Operating Procedures to generate code in real-time. It outperforms other AI agents in code generation, collaboration, and code review. Learn how to install and use MetaGPT with examples and benchmarks.\",\"ae\":null,\"c\":\"https://www.unite.ai/metagpt-complete-guide-to-the-best-ai-agent-available-right-now/\",\"d\":\"www.unite.ai/metagpt-complete-guide-to-the-best-ai-agent-available-right-now/\",\"da\":\"\",\"e\":\"2023-09-11T00:00:00.0000000\",\"h\":0,\"i\":\"www.unite.ai\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: Complete Guide to the Best AI Agent Available Right Now\",\"u\":\"https://www.unite.ai/metagpt-complete-guide-to-the-best-ai-agent-available-right-now/\"},{\"a\":\"Internally, MetaGPT includes product managers / architects / project managers / engineers. It provides the entire process of a software company along with carefully orchestrated SOPs. Code = SOP (Team) is the core philosophy. We materialize SOP and apply it to teams composed of LLMs. Software Company Multi-Role Schematic.\",\"ae\":null,\"c\":\"https://docs.deepwisdom.ai/main/en/guide/get_started/introduction.html\",\"d\":\"docs.deepwisdom.ai/main/en/guide/get_started/introduction.html\",\"da\":\"\",\"h\":0,\"i\":\"docs.deepwisdom.ai\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: The Multi-Agent Framework | MetaGPT\",\"u\":\"https://docs.deepwisdom.ai/main/en/guide/get_started/introduction.html\"},{\"a\":\"MetaGPT is a novel method that uses human workflows to improve the performance of LLM-based multi-agent systems. It encodes SOPs into prompt sequences and assigns roles to agents to break down complex tasks into subtasks.\",\"ae\":null,\"b\":\"arx\\tarXiv.org\\tarxiv.org\",\"c\":\"https://arxiv.org/abs/2308.00352\",\"d\":\"arxiv.org/abs/2308.00352\",\"da\":\"translations\",\"e\":\"2023-08-01T00:00:00.0000000\",\"h\":0,\"i\":\"arxiv.org\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: Meta Programming for A Multi-Agent Collaborative Framework\",\"u\":\"https://arxiv.org/abs/2308.00352\"},{\"a\":\"MetaGPT takes a one line requirement as input and outputs user stories / competitive analysis / requirements / data structures / APIs / documents, etc. \n Internally, MetaGPT includes product managers / architects / project managers / engineers.\",\"ae\":null,\"b\":\"gh\\tGitHub\\tgithub.com\",\"c\":\"https://github.com/geekan/MetaGPT/blob/main/README.md\",\"d\":\"github.com/geekan/MetaGPT/blob/main/README.md\",\"da\":\"\",\"h\":0,\"i\":\"github.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: The Multi-Agent Framework - GitHub\",\"u\":\"https://github.com/geekan/MetaGPT/blob/main/README.md\"},{\"a\":\"MetaGPT is a tool that lets you build websites, apps, and more using only text-based prompts powered by ChatGPT, an AI chatbot that can write code and improve it. You can create one app for free or subscribe for unlimited apps with MetaGPT.\",\"ae\":null,\"c\":\"https://interestingengineering.com/innovation/metagpt-create-apps-text-prompts\",\"d\":\"interestingengineering.com/innovation/metagpt-create-apps-text-prompts\",\"da\":\"\",\"e\":\"2023-05-08T12:57:00.0000000\",\"h\":0,\"i\":\"interestingengineering.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: Create web-based apps with only text prompts\",\"u\":\"https://interestingengineering.com/innovation/metagpt-create-apps-text-prompts\"},{\"a\":\"MetaGPT is a new text-to-app generator that can create web apps from text descriptions. It uses ChatGPT's API and is free to use for commercial purposes. You can build microapps for various tasks or platforms, such as Facebook Messenger, Trello, or Microsoft Word.\",\"ae\":null,\"c\":\"https://aibusiness.com/nlp/metagpt-text-to-app-ai-simplifies-web-dev\",\"d\":\"aibusiness.com/nlp/metagpt-text-to-app-ai-simplifies-web-dev\",\"da\":\"\",\"e\":\"2023-08-07T00:00:00.0000000\",\"h\":0,\"i\":\"aibusiness.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: Text-To-App AI Simplifies Web Dev\",\"u\":\"https://aibusiness.com/nlp/metagpt-text-to-app-ai-simplifies-web-dev\"},{\"a\":\"MetaGPT takes a one line requirement as input and outputs user stories / competitive analysis / requirements / data structures / APIs / documents, etc. \n; Internally, MetaGPT includes product managers / architects / project managers / engineers. It provides the entire process of a software company along with carefully orchestrated SOPs.\n \n\",\"ae\":null,\"b\":\"gh\\tGitHub\\tgithub.com\",\"c\":\"https://github.com/PlaiD3/MetaGPT/blob/main/README.md\",\"d\":\"github.com/PlaiD3/MetaGPT/blob/main/README.md\",\"da\":\"\",\"h\":0,\"i\":\"github.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: Multi-Agent Meta Programming Framework - GitHub\",\"u\":\"https://github.com/PlaiD3/MetaGPT/blob/main/README.md\"},{\"a\":\"MetaGPT is a powerful no-code solution for app building that allows users to create web apps without any prerequisites of coding or technical experience. It ...\",\"ae\":null,\"b\":\"yt\\tYouTube\\twww.youtube.com\",\"c\":\"https://www.youtube.com/watch?v=wpgC5fmtU70\",\"d\":\"www.youtube.com/watch?v=wpgC5fmtU70\",\"da\":\"mlb_games,nba_games,ncaafb_games,ncaamb_games,nfl_games,nhl_games,soccer_games,translations,videos,wheretowatch\",\"h\":0,\"i\":\"www.youtube.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Meet MetaGPT A GPT-4-powered Application That Can Create ... - YouTube\",\"u\":\"https://www.youtube.com/watch?v=wpgC5fmtU70\"},{\"a\":\"Developed by New York-based company WhimsyWorks, MetaGPT offers users a no-code solution to turn their idea into a website or online app using AI. If you've recently used an AI chatbot, the ...\",\"ae\":null,\"c\":\"https://www.tomsguide.com/news/ai-tool-uses-chatgpt-to-build-you-a-website-in-30-minutes-and-we-tried-it\",\"d\":\"www.tomsguide.com/news/ai-tool-uses-chatgpt-to-build-you-a-website-in-30-minutes-and-we-tried-it\",\"da\":\"translations\",\"e\":\"2023-05-10T00:00:00.0000000\",\"h\":0,\"i\":\"www.tomsguide.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"AI tool uses ChatGPT to build you an app in 30 minutes - Tom's Guide\",\"u\":\"https://www.tomsguide.com/news/ai-tool-uses-chatgpt-to-build-you-a-website-in-30-minutes-and-we-tried-it\"},{\"a\":\"MetaGPT is a tool that lets you create no-code web applications using natural language. You can type in a text prompt and get a functional web app in seconds, using the power of GPT-4 and the Code Interpreter. Learn how to use MetaGPT for data science, analytics, and more.\",\"ae\":null,\"c\":\"https://www.kdnuggets.com/meet-metagpt-the-chatgptpowered-ai-assistant-that-turns-text-into-web-apps\",\"d\":\"www.kdnuggets.com/meet-metagpt-the-chatgptpowered-ai-assistant-that-turns-text-into-web-apps\",\"da\":\"\",\"e\":\"2023-09-08T00:00:00.0000000\",\"h\":0,\"i\":\"www.kdnuggets.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Meet MetaGPT: The ChatGPT-Powered AI Assistant That Turns Text Into Web ...\",\"u\":\"https://www.kdnuggets.com/meet-metagpt-the-chatgptpowered-ai-assistant-that-turns-text-into-web-apps\"},{\"a\":\"MetaGPT is a multi-agent system that uses large language models to perform complex tasks. It can understand, generate, and interact with natural language input and output. Learn about its features, capabilities, applications, and advantages in this complete guide.\",\"ae\":null,\"c\":\"https://www.straight.com/guides/software/a-complete-guide-to-metagpt-the-best-ai-agent-available-now/\",\"d\":\"www.straight.com/guides/software/a-complete-guide-to-metagpt-the-best-ai-agent-available-now/\",\"da\":\"\",\"e\":\"2023-12-13T00:00:00.0000000\",\"h\":0,\"i\":\"www.straight.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"A Complete Guide to MetaGPT: The Best AI Agent Available Now\",\"u\":\"https://www.straight.com/guides/software/a-complete-guide-to-metagpt-the-best-ai-agent-available-now/\"},{\"a\":\"MetaGPT is an open-source AI framework that transforms GPTs into engineers, architects, and managers by using role-based action specifications and SOPs. It can generate high-quality code, design, and documentation for software engineering, data analysis, and game development tasks.\",\"ae\":null,\"c\":\"https://www.marktechpost.com/2023/08/09/meet-metagpt-the-open-source-ai-framework-that-transforms-gpts-into-engineers-architects-and-managers/\",\"d\":\"www.marktechpost.com/2023/08/09/meet-metagpt-the-open-source-ai-framework-that-transforms-gpts-into-engineers-architects-and-managers/\",\"da\":\"translations\",\"e\":\"2023-08-09T00:00:00.0000000\",\"h\":0,\"i\":\"www.marktechpost.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Meet MetaGPT: The Open-Source AI Framework That Transforms GPTs into ...\",\"u\":\"https://www.marktechpost.com/2023/08/09/meet-metagpt-the-open-source-ai-framework-that-transforms-gpts-into-engineers-architects-and-managers/\"},{\"a\":\"MetaGPT is the maestro who brings harmony to this chaos. By encoding Standardized Operating Procedures (SOPs) into prompts, MetaGPT ensures structured collaboration akin to a well-rehearsed ...\",\"ae\":null,\"c\":\"https://medium.com/gta-generative-tech-advances/metagpt-an-interesting-approach-to-multi-agent-collaboration-5ace263c4fd8\",\"d\":\"medium.com/gta-generative-tech-advances/metagpt-an-interesting-approach-to-multi-agent-collaboration-5ace263c4fd8\",\"da\":\"\",\"e\":\"2023-08-15T00:00:00.0000000\",\"h\":0,\"i\":\"medium.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: An Interesting Approach to Multi-Agent Collaboration\",\"u\":\"https://medium.com/gta-generative-tech-advances/metagpt-an-interesting-approach-to-multi-agent-collaboration-5ace263c4fd8\"},{\"a\":\"Welcome to our video review! \\ud83c\\udfa5 Dive into the world of MetaGPT, a revolutionary project that's redefining the boundaries of AI. \\ud83e\\udd16 Imagine having an entire e...\",\"ae\":null,\"b\":\"yt\\tYouTube\\twww.youtube.com\",\"c\":\"https://www.youtube.com/watch?v=nqZlTV_L6Ao\",\"d\":\"www.youtube.com/watch?v=nqZlTV_L6Ao\",\"da\":\"mlb_games,nba_games,ncaafb_games,ncaamb_games,nfl_games,nhl_games,soccer_games,videos,wheretowatch\",\"e\":\"2023-09-04T00:00:00.0000000\",\"h\":0,\"i\":\"www.youtube.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT Setup: Launch a Startup with One \\ufe0f Prompt! - YouTube\",\"u\":\"https://www.youtube.com/watch?v=nqZlTV_L6Ao\"},{\"a\":\"MetaGPT is a model that uses the power of natural language to create and execute meta programs for multi-agent collaboration. Meta programs are programs that can generate or modify other programs ...\",\"ae\":null,\"c\":\"https://medium.com/aimonks/metagpt-a-framework-for-multi-agent-meta-programming-6c79f2eafb8e\",\"d\":\"medium.com/aimonks/metagpt-a-framework-for-multi-agent-meta-programming-6c79f2eafb8e\",\"da\":\"\",\"e\":\"2023-08-03T00:00:00.0000000\",\"h\":0,\"i\":\"medium.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: A Framework for Multi-Agent Meta Programming\",\"u\":\"https://medium.com/aimonks/metagpt-a-framework-for-multi-agent-meta-programming-6c79f2eafb8e\"},{\"a\":\"MetaGPT, available on Github (crossed 13,000 stars), aims to change the way we make software.This exciting tool can take a single line of what you want to do and turn it into many things like user ...\",\"ae\":null,\"c\":\"https://medium.com/@smraiyyan/metagpt-unleashed-crafting-your-virtual-software-company-from-scratch-6ea60cd70da1\",\"d\":\"medium.com/@smraiyyan/metagpt-unleashed-crafting-your-virtual-software-company-from-scratch-6ea60cd70da1\",\"da\":\"translations\",\"e\":\"2023-08-07T00:00:00.0000000\",\"h\":0,\"i\":\"medium.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT Lets You Create Your Own Virtual Software Company from ... - Medium\",\"u\":\"https://medium.com/@smraiyyan/metagpt-unleashed-crafting-your-virtual-software-company-from-scratch-6ea60cd70da1\"},{\"a\":\"Discover the revolutionary advancements of MetaGPT and its potential impact on the future of AI-powered solutions. Artificial Intelligence has experienced remarkable progress in recent years, with one term in particular capturing the attention of the digital landscape: MetaGPT online. It can also be referred to as one of the ChatGPT alternatives.In an increasingly competitive environment ...\",\"ae\":null,\"c\":\"https://www.almabetter.com/bytes/articles/metagpt\",\"d\":\"www.almabetter.com/bytes/articles/metagpt\",\"da\":\"\",\"e\":\"2023-08-28T00:00:00.0000000\",\"h\":0,\"i\":\"www.almabetter.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: The Future of Multi-Agent Collaboration in AI\",\"u\":\"https://www.almabetter.com/bytes/articles/metagpt\"},{\"a\":\"MetaGPT is a framework that uses different GPTs to generate APIs, user stories, data structures, and more. It can automate software development tasks, enhance existing programs, and collaborate with other agents. Learn how to get started, use cases, advantages, and alternatives of MetaGPT.\",\"ae\":null,\"c\":\"https://geekflare.com/metagpt-multi-agent-framework/\",\"d\":\"geekflare.com/metagpt-multi-agent-framework/\",\"da\":\"\",\"e\":\"2023-09-18T00:00:00.0000000\",\"h\":0,\"i\":\"geekflare.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: Is This the Best Multi-Agent Framework Yet? - Geekflare\",\"u\":\"https://geekflare.com/metagpt-multi-agent-framework/\"},{\"a\":\"MetaGPT is a web app that allows users to build web applications using natural language prompts and ChatGPT, a multimodal language model. The service has been used to create dashboards, code-based visualisations, and even a marriage proposal, showing the potential of GPT-4 and its plugins.\",\"ae\":null,\"c\":\"https://analyticsindiamag.com/metagpt-realising-the-gpt-4-dream/\",\"d\":\"analyticsindiamag.com/metagpt-realising-the-gpt-4-dream/\",\"da\":\"\",\"e\":\"2023-04-26T00:00:00.0000000\",\"h\":0,\"i\":\"analyticsindiamag.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT \\u2014 Realising the GPT-4 Dream - Analytics India Magazine\",\"u\":\"https://analyticsindiamag.com/metagpt-realising-the-gpt-4-dream/\"},{\"a\":\"MetaGPT, or multimodal Generative Pretrained Transformers, represents a significant leap in the evolution of artificial intelligence. This new generation of AI models is capable of understanding ...\",\"ae\":null,\"c\":\"https://medium.com/technology-hits/autogpt-langchain-deep-lake-metagpt-a-revolutionary-framework-for-building-advanced-ai-e2c579d86494\",\"d\":\"medium.com/technology-hits/autogpt-langchain-deep-lake-metagpt-a-revolutionary-framework-for-building-advanced-ai-e2c579d86494\",\"da\":\"\",\"e\":\"2023-08-28T00:00:00.0000000\",\"h\":0,\"i\":\"medium.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"AutoGPT \\u2014 LangChain \\u2014 Deep Lake \\u2014 MetaGPT: A ... - Medium\",\"u\":\"https://medium.com/technology-hits/autogpt-langchain-deep-lake-metagpt-a-revolutionary-framework-for-building-advanced-ai-e2c579d86494\"},{\"a\":\"Overview of the MetaGPT framework. Presented is a two-layer architectural design: i) the Foundational Components Layer, which is essential for agent operations and system-wide communication, and ii) the Collaboration Layer, which facilitates agent coordination through key mechanisms such as knowledge sharing and workflow encapsulation.\",\"ae\":null,\"c\":\"https://generativeai.pub/analyzing-an-exciting-generative-ai-research-called-metagpt-2106385312db\",\"d\":\"generativeai.pub/analyzing-an-exciting-generative-ai-research-called-metagpt-2106385312db\",\"da\":\"translations\",\"e\":\"2023-08-14T00:00:00.0000000\",\"h\":0,\"i\":\"generativeai.pub\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Analyzing an exciting Generative AI research called MetaGPT.\",\"u\":\"https://generativeai.pub/analyzing-an-exciting-generative-ai-research-called-metagpt-2106385312db\"},{\"a\":\"MetaGPT\\u5c06\\u4f1a\\u6309\\u7167\\u4e0b\\u8ff0\\u4f18\\u5148\\u7ea7\\u6765\\u8bfb\\u53d6\\u4f60\\u7684\\u914d\\u7f6e\\uff1aconfig/key.yaml > config/config.yaml > environment variable. \\u6211\\u8fd9\\u91cc\\u4f7f\\u7528\\u73af\\u5883\\u53d8\\u91cf\\u7684\\u65b9\\u5f0f\\u3002. \\uff081\\uff09\\u521b\\u5efa\\u4e00\\u4e2a\\u5de5\\u7a0b\\u76ee\\u5f55 MyMetaGPT\\uff0c\\u7528VSCode\\u6253\\u5f00. \\uff082\\uff09\\u65b0\\u5efa\\u4e00\\u4e2a.env\\u6587\\u4ef6\\uff0c\\u5c06\\u4ee5\\u4e0a\\u914d\\u7f6e\\u586b\\u52a0\\u5230\\u8be5\\u6587\\u4ef6\\u4e2d. \\u5728Python\\u6587\\u4ef6\\uff08MetaGPT_test.py\\uff09\\u4e2d\\u5c06\\u8be5.env\\u6587\\u4ef6 ...\",\"ae\":null,\"c\":\"https://blog.csdn.net/Attitude93/article/details/135550499\",\"d\":\"blog.csdn.net/Attitude93/article/details/135550499\",\"da\":\"translations\",\"e\":\"2024-01-13T00:00:00.0000000\",\"h\":0,\"i\":\"blog.csdn.net\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"AI Agent\\u7cfb\\u5217\\u3011\\u3010MetaGPT\\u30110. \\u4f60\\u7684\\u7b2c\\u4e00\\u4e2aMetaGPT\\u7a0b\\u5e8f - CSDN\\u535a\\u5ba2\",\"u\":\"https://blog.csdn.net/Attitude93/article/details/135550499\"},{\"a\":\"Here are nine of the best ChatGPT alternatives and generative AI APIs for developers that are worth checking out. 1. Meta: Llama2. do is download and install Llama 2 locally. Related video: How AI ...\",\"ae\":null,\"c\":\"https://www.msn.com/en-us/news/technology/generative-ai-apis-and-chatgpt-alternatives-for-developers-to-consider/ar-AA1ltwXb\",\"d\":\"www.msn.com/en-us/news/technology/generative-ai-apis-and-chatgpt-alternatives-for-developers-to-consider/ar-AA1ltwXb\",\"da\":\"news\",\"e\":\"2023-12-13T00:00:00.0000000\",\"h\":0,\"i\":\"www.msn.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Generative AI APIs and ChatGPT Alternatives for Developers to ... - MSN\",\"u\":\"https://www.msn.com/en-us/news/technology/generative-ai-apis-and-chatgpt-alternatives-for-developers-to-consider/ar-AA1ltwXb\"},{\"a\":\"Pressure grows on artificial intelligence firms over the content used to train their products\",\"ae\":null,\"c\":\"https://www.theguardian.com/technology/2024/jan/08/ai-tools-chatgpt-copyrighted-material-openai\",\"d\":\"www.theguardian.com/technology/2024/jan/08/ai-tools-chatgpt-copyrighted-material-openai\",\"da\":\"news,translations\",\"e\":\"2024-01-08T07:15:00.0000000\",\"h\":0,\"i\":\"www.theguardian.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"'Impossible' to create AI tools like ChatGPT without copyrighted ...\",\"u\":\"https://www.theguardian.com/technology/2024/jan/08/ai-tools-chatgpt-copyrighted-material-openai\"},{\"a\":\"The Paris-based startup has raised $24 million at a $180 million valuation to shift its doctor note-taking software towards the open source AI models championed by Meta AI chief Yann Lecun, one of ...\",\"ae\":null,\"c\":\"https://www.forbes.com/sites/katiejennings/2024/01/05/health-ai-startup-nabla-was-built-on-gpt-4-now-its-abandoning-openai-for-open-source/\",\"d\":\"www.forbes.com/sites/katiejennings/2024/01/05/health-ai-startup-nabla-was-built-on-gpt-4-now-its-abandoning-openai-for-open-source/\",\"da\":\"news,translations\",\"e\":\"2024-01-05T11:00:00.0000000\",\"h\":0,\"i\":\"www.forbes.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Health AI Startup Nabla Was Built On GPT-4. Now, It's ... - Forbes\",\"u\":\"https://www.forbes.com/sites/katiejennings/2024/01/05/health-ai-startup-nabla-was-built-on-gpt-4-now-its-abandoning-openai-for-open-source/\"},{\"a\":\"\\u57282023\\u5e7412\\u670819\\u65e5\\u65f6\\uff0c\\u542c\\u4e86\\u6797\\u4e49\\u7ae0\\u8001\\u5e08\\u5173\\u4e8e"\\u57fa\\u4e8eMetaGPT\\u8fdb\\u884c\\u667a\\u80fd\\u4f53\\u5f00\\u53d1"\\u7684\\u8bb2\\u5ea7\\uff1a \\u89c9\\u5f97\\u65b0\\u5947\\u6709\\u8da3\\uff0c\\u5982\\u679c\\u80fd\\u8fd9\\u6837\\u5728\\u5de5\\u4f5c\\u751f\\u6d3b\\u4e2d\\u5b8c\\u6210\\u81ea\\u5df1\\u7684\\u4efb\\u52a1\\uff0c\\u90a3\\u7b80\\u76f4\\u662f\\u4e8b\\u534a\\u529f\\u500d\\u3002\\u4e8e\\u662f\\u8fd9\\u4e24\\u5929\\u53c8\\u5b66\\u4e60\\u4e86\\u300aMetaGPT\\u667a\\u80fd\\u4f53\\u5f00\\u53d1\\u5165\\u95e8\\u300b\\u6559\\u2026\",\"ae\":null,\"c\":\"https://zhuanlan.zhihu.com/p/677608276\",\"d\":\"zhuanlan.zhihu.com/p/677608276\",\"da\":\"translations\",\"e\":\"2024-01-12T00:00:00.0000000\",\"h\":0,\"i\":\"zhuanlan.zhihu.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"\\u5b66\\u4e60\\u7b14\\u8bb0-\\u300aMetaGPT\\u667a\\u80fd\\u4f53\\u5f00\\u53d1\\u5165\\u95e8\\u300b\\u6559\\u7a0b - \\u77e5\\u4e4e\",\"u\":\"https://zhuanlan.zhihu.com/p/677608276\"},{\"a\":\"In 2024, generative AI might actually become useful for the regular, non-tech person, and we are going to see more people tinkering with a million little AI models. State-of-the-art AI models ...\",\"ae\":null,\"c\":\"https://www.technologyreview.com/2024/01/04/1086046/whats-next-for-ai-in-2024/\",\"d\":\"www.technologyreview.com/2024/01/04/1086046/whats-next-for-ai-in-2024/\",\"da\":\"translations\",\"e\":\"2024-01-04T09:14:17.0000000\",\"h\":0,\"i\":\"www.technologyreview.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"What's next for AI in 2024 | MIT Technology Review\",\"u\":\"https://www.technologyreview.com/2024/01/04/1086046/whats-next-for-ai-in-2024/\"},{\"n\":\"/d.js?q=metagpt&kl=wt-wt&l=wt-wt&p=&s=29&ex=-1&ct=US&sp=0&vqd=4-118631859838297093459588814466521506726\"}]);DDG.duckbar.load('images');DDG.duckbar.load('news');DDG.duckbar.load('videos', {\"ads\":[],\"query\":\"metagpt\",\"queryEncoded\":\"metagpt\",\"response_type\":\"places\",\"results\":[{\"content\":\"https://www.youtube.com/watch?v=uT75J_KG_aY\",\"description\":\"In this video, we review MetaGPT, a new project that aims to recreate an entire engineering organization using AI. MetaGPT is a CEO, Product Manager, Architect, Project Manager, Engineering, and QA. Write a simple prompt, and you get everything from the requirements to the PRDs to the code and tests. How To Find Me: Become a Patron \\ud83d\\udd25 - https ...\",\"duration\":\"6:36\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/uT75J_KG_aY?autoplay=1\",\"image_token\":\"57974159b78b309485721c0bce280219d9927e071e542a34777864767d6cb8d4\",\"images\":{\"large\":\"https://tse3.mm.bing.net/th?id=OVP.BbSKV8N1vyYv-3m8vyuCoQEsDh&pid=Api\",\"medium\":\"https://tse3.mm.bing.net/th?id=OVP.BbSKV8N1vyYv-3m8vyuCoQEsDh&pid=Api\",\"motion\":\"https://tse3.mm.bing.net/th?id=OM1.bsXxoMoJ9ZWQBw&pid=Api\",\"small\":\"https://tse3.mm.bing.net/th?id=OVP.BbSKV8N1vyYv-3m8vyuCoQEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-08-14T14:09:10.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":75408},\"title\":\"How To Install MetaGPT - Build A Startup With One Prompt!!\",\"uploader\":\"Matthew Berman\"},{\"content\":\"https://www.youtube.com/watch?v=pJwR5pv0_gs\",\"description\":\"Multi agent framework tutorial of MetaGPT & chatDev; Check the Hubspot x Jasper research of Using Generative AI to Scale Your Content Operations: https://offers.hubspot.com/generative-ai-for-content-operations?utm_source=youtube&utm_medium=social&utm_campaign=CR0087Sep2023_AIJason/partner_youtube \\ud83d\\udd17 Links - Follow me on twitter: https ...\",\"duration\":\"13:41\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/pJwR5pv0_gs?autoplay=1\",\"image_token\":\"18ac54a8e5144c74f2010219781c47c295099a6eed7479645733832910d19aec\",\"images\":{\"large\":\"https://tse4.mm.bing.net/th?id=OVP.LJ0SK8DLWjCcwVVh-PEcOwHgFo&pid=Api\",\"medium\":\"https://tse4.mm.bing.net/th?id=OVP.LJ0SK8DLWjCcwVVh-PEcOwHgFo&pid=Api\",\"motion\":\"https://tse4.mm.bing.net/th?id=OM2.PxMMOsse4Yi_FQ&pid=Api\",\"small\":\"https://tse4.mm.bing.net/th?id=OVP.LJ0SK8DLWjCcwVVh-PEcOwHgFo&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-09-08T11:36:03.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":167793},\"title\":\"Build AI agent workforce - Multi agent framework with MetaGPT & chatDev\",\"uploader\":\"AI Jason\"},{\"content\":\"https://www.youtube.com/watch?v=q16Gi9pTG_M\",\"description\":\"In this captivating video, we explore the core concept of MetaGPT, which centers on task distribution and coordination among individual GPT agents. Each agent is bestowed with specific roles that capitalize on their unique strengths and expertise. Imagine one GPT excelling in natural language understanding, while another showcases prowess in ...\",\"duration\":\"14:56\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/q16Gi9pTG_M?autoplay=1\",\"image_token\":\"bee3657ef83c9da2bc4ccfea770244e18958f5789a39d0136c3a049cc22a0e54\",\"images\":{\"large\":\"https://tse4.mm.bing.net/th?id=OVP.eiPUmQWRU1sE-01-x5Kn7gEsDh&pid=Api\",\"medium\":\"https://tse4.mm.bing.net/th?id=OVP.eiPUmQWRU1sE-01-x5Kn7gEsDh&pid=Api\",\"motion\":\"https://tse4.mm.bing.net/th?id=OM2.eWDmjf8nvrSrhw&pid=Api\",\"small\":\"https://tse4.mm.bing.net/th?id=OVP.eiPUmQWRU1sE-01-x5Kn7gEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-07-25T00:37:40.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":14365},\"title\":\"MetaGPT: Deploy POWERFUL Autonomous Ai Agents BETTER Than SUPERAGI! (Installation Tutorial)\",\"uploader\":\"WorldofAI\"},{\"content\":\"https://www.youtube.com/watch?v=nqZlTV_L6Ao\",\"description\":\"Welcome to our video review! \\ud83c\\udfa5 Dive into the world of MetaGPT, a revolutionary project that's redefining the boundaries of AI. \\ud83e\\udd16 Imagine having an entire engineering team - from CEO to QA - compacted into one AI system. Just input a prompt, and voila! You're handed everything from requirements, PRDs, to the actual code and tests. Let ...\",\"duration\":\"14:15\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/nqZlTV_L6Ao?autoplay=1\",\"image_token\":\"9d13b27084400da23ef8d8567bd6b5c8a3758d4129f2b28c3619c0e2e1ba8276\",\"images\":{\"large\":\"https://tse4.mm.bing.net/th?id=OVP.VBEy5DF-0BQshjEkqA9T0wHgFo&pid=Api\",\"medium\":\"https://tse4.mm.bing.net/th?id=OVP.VBEy5DF-0BQshjEkqA9T0wHgFo&pid=Api\",\"motion\":\"https://tse4.mm.bing.net/th?id=OM2.N7S3-wAngkj7VA&pid=Api\",\"small\":\"https://tse4.mm.bing.net/th?id=OVP.VBEy5DF-0BQshjEkqA9T0wHgFo&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-09-04T11:45:06.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":23248},\"title\":\"\\ud83d\\ude80 MetaGPT Setup: Launch a Startup with One \\u270d\\ufe0f Prompt!\",\"uploader\":\"Prompt Engineering\"},{\"content\":\"https://www.youtube.com/watch?v=VxhPcnsA7KA\",\"description\":\"Meet MetaGPT, MetaGPT is a complete Software Engineering organization at your disposal. MetaGPT employees autonomous AI agents specializing in the roles found in real Software Development companies. Deploying Autonomous GPT AI Product Managers, Architects, Project Managers and Engineers your software is developed for you and Documented! This ...\",\"duration\":\"8:49\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/VxhPcnsA7KA?autoplay=1\",\"image_token\":\"c56ab50565d7135c0d45f37ea4b70f565eced03024b608392498704c54b0fe66\",\"images\":{\"large\":\"https://tse1.mm.bing.net/th?id=OVP.RTXFEZ-JNqeIG3Bfi8B3UQEsDh&pid=Api\",\"medium\":\"https://tse1.mm.bing.net/th?id=OVP.RTXFEZ-JNqeIG3Bfi8B3UQEsDh&pid=Api\",\"motion\":\"https://tse1.mm.bing.net/th?id=OM2.219b44Lsywj5Bg&pid=Api\",\"small\":\"https://tse1.mm.bing.net/th?id=OVP.RTXFEZ-JNqeIG3Bfi8B3UQEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-08-21T00:25:20.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":6190},\"title\":\"How To Install MetaGPT - Your own AI Software Company, Create Programs With a single Prompt!\",\"uploader\":\"StuffAboutStuff\"},{\"content\":\"https://www.youtube.com/watch?v=Xyws6iI-eH8\",\"description\":\"In this video, we unravel the magic at the core of MetaGPT, exploring its multi-agent framework and the groundbreaking December 15 update (v0.5.0) that introduced incremental development. Join us on this journey of innovation and efficiency in AI! \\ud83d\\udd25 Become a Patron (Private Discord): https://patreon.com/WorldofAi \\u2615 To help and Support me ...\",\"duration\":\"11:38\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/Xyws6iI-eH8?autoplay=1\",\"image_token\":\"db0651076b86c15566c0f032ab3e035fa65863cdf0b3bf46a18d10201bad1bab\",\"images\":{\"large\":\"https://tse3.mm.bing.net/th?id=OVP.X0OdKOGTJgwUw3Op_rmcewEsDh&pid=Api\",\"medium\":\"https://tse3.mm.bing.net/th?id=OVP.X0OdKOGTJgwUw3Op_rmcewEsDh&pid=Api\",\"motion\":\"https://tse3.mm.bing.net/th?id=OM2.-Hw5pO2PnG7h1g&pid=Api\",\"small\":\"https://tse3.mm.bing.net/th?id=OVP.X0OdKOGTJgwUw3Op_rmcewEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-12-23T21:22:36.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":7003},\"title\":\"MetaGPT HUGE Update: Autonomous AI Agents with Incremental Memory!\",\"uploader\":\"WorldofAI\"},{\"content\":\"https://www.youtube.com/watch?v=T_wBUpzxxPY\",\"description\":\"In this video i talk about this awesome project called MetaGPT in my video. Now, MetaGPT is like an all-in-one AI powerhouse. It can do everything from being a CEO to a QA tester for an engineering organization. And the cool thing is, you just give it a simple prompt, and it spits out everything you need - requirements, PRDs, code, and tests ...\",\"duration\":\"4:00\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/T_wBUpzxxPY?autoplay=1\",\"image_token\":\"ef14791d7faff848cb15177567e9f4f9c04ccae4fafc7ef7386e69df3a012010\",\"images\":{\"large\":\"https://tse1.mm.bing.net/th?id=OVP.EWCOFStB_tQza4SLrUA0AAEsDh&pid=Api\",\"medium\":\"https://tse1.mm.bing.net/th?id=OVP.EWCOFStB_tQza4SLrUA0AAEsDh&pid=Api\",\"motion\":\"https://tse1.mm.bing.net/th?id=OM1.itG5pHJg6MKYzg_1696190983&pid=Api\",\"small\":\"https://tse1.mm.bing.net/th?id=OVP.EWCOFStB_tQza4SLrUA0AAEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-09-11T10:41:22.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":368},\"title\":\"MetaGPT Installation Guide: From Setup to Startup With One Prompt!\",\"uploader\":\"Py Man\"},{\"content\":\"https://www.youtube.com/watch?v=EgipcKPhqME\",\"description\":\"In this video I provide a great demo and overview of a project called MetaGPT. Have you ever wondered if each person in a development project (such as the project manager, developers, architects, QA testers, etc.) were all AI's and how they'd behave? MetaGPT is doing just that. Not only are all the docs, designs, and tasks delivered, but also a ...\",\"duration\":\"7:35\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/EgipcKPhqME?autoplay=1\",\"image_token\":\"624d4ccdb6d1605da1e388e85c9124957bcba9c70a11a575e751ba6fc09bc5f8\",\"images\":{\"large\":\"https://tse4.mm.bing.net/th?id=OVP.hG0c3nw7X-uz0gzUjnOVNwEsDh&pid=Api\",\"medium\":\"https://tse4.mm.bing.net/th?id=OVP.hG0c3nw7X-uz0gzUjnOVNwEsDh&pid=Api\",\"motion\":\"https://tse4.mm.bing.net/th?id=OM1.8F2lEMy1JlCKsQ_1698986522&pid=Api\",\"small\":\"https://tse4.mm.bing.net/th?id=OVP.hG0c3nw7X-uz0gzUjnOVNwEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-09-24T08:00:11.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":1587},\"title\":\"MetaGPT Tutorial | It builds an entire project (with working source code) with just one prompt!!\",\"uploader\":\"CraceCasts\"},{\"content\":\"https://www.youtube.com/watch?v=YtxMderNrzU\",\"description\":\"Subscribe to my Newsletter (My AI updates and news clearly explained): https://louisbouchard.substack.com/ References: Read the full article: https://www.louisbouchard.ai/metagpt/ Hong et al., 2023: MetaGPT, https://arxiv.org/pdf/2308.00352.pdf Code: https://github.com/geekan/MetaGPT/blob/main/README.md Twitter: https://twitter.com/Whats_AI ...\",\"duration\":\"7:38\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/YtxMderNrzU?autoplay=1\",\"image_token\":\"2e0774ace2e34bbe23ece04e80b7bb2ee976fd8ef7f53001e8f8b137763561dc\",\"images\":{\"large\":\"https://tse2.mm.bing.net/th?id=OVP.HP81CZ34ap22GZZG2l024QHgFo&pid=Api\",\"medium\":\"https://tse2.mm.bing.net/th?id=OVP.HP81CZ34ap22GZZG2l024QHgFo&pid=Api\",\"motion\":\"https://tse2.mm.bing.net/th?id=OM2.xArTjo5bOxSBhg&pid=Api\",\"small\":\"https://tse2.mm.bing.net/th?id=OVP.HP81CZ34ap22GZZG2l024QHgFo&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-08-27T15:05:12.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":9594},\"title\":\"MetaGPT: Redefining Multi-Agent Collaboration for Complex Tasks\",\"uploader\":\"What's AI by Louis Bouchard\"},{\"content\":\"https://www.youtube.com/watch?v=geLX30qax8Q\",\"description\":\"Dive into the world of autonomous agent swarms with our comprehensive Autonomous Agent Swarms Totorial! \\ud83c\\udf1f Whether you're a beginner or an AI enthusiast, this video will guide you through the fascinating process of creating and managing intelligent agent swarms using LangChain. Learn how to harness the power of collaborative AI agents for ...\",\"duration\":\"47:02\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/geLX30qax8Q?autoplay=1\",\"image_token\":\"9e9f6de14802f66e1364f3ef0c9a7973fcfab471dff810a91595a2ea60242256\",\"images\":{\"large\":\"https://tse2.mm.bing.net/th?id=OVP.0DNi9RS5yLCZHu9MXx5lQAEsDh&pid=Api\",\"medium\":\"https://tse2.mm.bing.net/th?id=OVP.0DNi9RS5yLCZHu9MXx5lQAEsDh&pid=Api\",\"motion\":\"https://tse2.mm.bing.net/th?id=OM.u64mpOw5ZNaPbg_1704713419&pid=Api\",\"small\":\"https://tse2.mm.bing.net/th?id=OVP.0DNi9RS5yLCZHu9MXx5lQAEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-11-12T00:29:27.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":12461},\"title\":\"Autonomous AI Agent Swarms | COMPLETE Tutorial\",\"uploader\":\"AspnAI\"}],\"vqd\":{\"metagpt\":\"4-118631859838297093459588814466521506726\"}});DDG.duckbar.loadModule('related_searches', {\"ads\":[],\"query\":\"metagpt\",\"queryEncoded\":\"metagpt\",\"response_type\":\"places\",\"results\":[{\"display_text\":\"metagpt sign in\",\"text\":\"metagpt sign in\",\"web_search_url\":\"?q=metagpt%20sign%20in\"},{\"display_text\":\"metagpt download\",\"text\":\"metagpt download\",\"web_search_url\":\"?q=metagpt%20download\"},{\"display_text\":\"metagpt vs autogpt\",\"text\":\"metagpt vs autogpt\",\"web_search_url\":\"?q=metagpt%20vs%20autogpt\"},{\"display_text\":\"metagpt examples\",\"text\":\"metagpt examples\",\"web_search_url\":\"?q=metagpt%20examples\"},{\"display_text\":\"metagpt windows\",\"text\":\"metagpt windows\",\"web_search_url\":\"?q=metagpt%20windows\"},{\"display_text\":\"metagpt arxiv\",\"text\":\"metagpt arxiv\",\"web_search_url\":\"?q=metagpt%20arxiv\"},{\"display_text\":\"tell me what is metagpt\",\"text\":\"tell me what is metagpt\",\"web_search_url\":\"?q=tell%20me%20what%20is%20metagpt\"},{\"display_text\":\"metagpt pdf\",\"text\":\"metagpt pdf\",\"web_search_url\":\"?q=metagpt%20pdf\"}],\"vqd\":{\"metagpt\":\"4-118631859838297093459588814466521506726\"}});if (DDG.pageLayout) DDG.pageLayout.initialize({\"mainline\":{\"items\":[[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"videos\"],[\"related_searches\"]]}}, { start: 0 });DDG.deep.emit(\"load:completed\");", + "curl-cffi-POST-https://duckduckgo.com-{\"data\": {\"q\": \"llm\"}}": "llm at DuckDuckGo
", + "curl-cffi-GET-https://links.duckduckgo.com/d.js-{\"params\": {\"bing_market\": \"wt-WT\", \"df\": null, \"ex\": \"-1\", \"kl\": \"wt-wt\", \"l\": \"wt-wt\", \"q\": \"llm\", \"s\": \"0\", \"sp\": \"0\", \"vqd\": \"4-36212277936736004277629252433802891730\"}}": "if (DDG.deep && DDG.deep.setUpstream) DDG.deep.setUpstream(\"bingv7aa\");DDG.deep.bn={'ivc':1};if (DDG.pageLayout) DDG.pageLayout.load('a',[{\"a\":\"\\u6d77\\u5916\\u30c8\\u30c3\\u30d7\\u6821\\u307810,000\\u4eba\\u4ee5\\u4e0a\\u306e\\u5408\\u683c\\u5b9f\\u7e3e!\\u307e\\u305a\\u306f\\u7121\\u6599\\u500b\\u5225\\u76f8\\u8ac7\\u3001\\u7121\\u6599\\u30a4\\u30d9\\u30f3\\u30c8\\u3078. \\u9078\\u3079\\u308b\\u5b66\\u7fd2\\u5f62\\u614b\\u3001\\u53d7\\u8b1b\\u671f\\u95933\\u5e74\\u3001\\u518d\\u53d7\\u8b1b\\u7121\\u6599\\u306e\\u30a2\\u30b4\\u30b9\\u3067\\u30b0\\u30ed\\u30fc\\u30d0\\u30eb\\u30ec\\u30d9\\u30eb\\u306e\\u30ad\\u30e3\\u30ea\\u30a2\\u3092\\u76ee\\u6307\\u305b!\",\"adext\":{\"callout\":{\"t\":\"\\u304d\\u3081\\u306e\\u7d30\\u304b\\u3044\\u6307\\u5c0e \\u00b7 \\u7d4c\\u9a13\\u3068\\u60c5\\u71b1\\u306b\\u3042\\u3075\\u308c\\u308b\\u8b1b\\u5e2b \\u00b7 \\u77ed\\u671f\\u9593\\u30b9\\u30b3\\u30a2UP\\u6cd5\\u3092\\u63d0\\u4f9b \\u00b7 \\u5c11\\u4eba\\u6570\\u306e\\u5b9f\\u8df5\\u6f14\\u7fd2\",\"tid\":\"4\"},\"filterlinks\":{\"l\":[],\"tid\":\"\"},\"sitelinks\":{\"l\":[{\"targetUrl\":\"https://duckduckgo.com/y.js?ad_domain=agos.co.jp&ad_provider=bingv7aa&ad_type=txad&eddgt=obP7zXz7zpZHDybCoDxesg%3D%3D&rut=0775aea54a76bdc651a07b5b6d9bb0b5a3312b830116a0a5705f920eadce0a8d&u3=https%3A%2F%2Fwww.bing.com%2Faclick%3Fld%3De8YtC6eTayvxICgO_74gaNxDVUCUx3MSqhDzr%2DQGWoWU46I_SK7hUYitxw1bDkwu51N4R%2Dy2n%2DF_Z3diiXwWhVMScvGQLYOHkpzJlogFXTiR4vkRTEI6CXepxoBdTo7ZRbEHQEhLvaxEQc7HHcGBfvrIhV5UmO4EI5CnyEEmhFkhykgShvntmZi4sK2RdNtojVZjcZh_jLwFJhGr6O4xBoHNjWmJk%26u%3DaHR0cHMlM2ElMmYlMmZ3d3cuYWdvcy5jby5qcCUyZmluZm9ybWF0aW9uJTJmc291ZGFuLmh0bWwlM2Z1dG1fc291cmNlJTNkYmluZyVjMiVhMCUyNnV0bV9tZWRpdW0lM2RjcGMlMjZ1dG1fY2FtcGFpZ24lM2Rub3Rfc3l1eW91JTI2bXNjbGtpZCUzZDkxNjNmMTQ2ZjQwNTFlNjEwNzdkOTRlNTA5ZTljNTQyJTI2dXRtX3Rlcm0lM2RMTE0lMjZ1dG1fY29udGVudCUzZEtXRF9BTExfQUxfKExMTSk%26rlid%3D9163f146f4051e61077d94e509e9c542&vqd=4-301837748834523973528319863104123825131&iurl=%7B1%7DIG%3D6793BB4EFA564D10A45BB0C3C75BF04C%26CID%3D342CD9B7486D6452145CCDB1496D6555%26ID%3DDevEx%2C5064.1\",\"text\":\"\\u7121\\u6599\\u500b\\u5225\\u76f8\\u8ac7\"},{\"targetUrl\":\"https://duckduckgo.com/y.js?ad_domain=agos.co.jp&ad_provider=bingv7aa&ad_type=txad&eddgt=obP7zXz7zpZHDybCoDxesg%3D%3D&rut=bd3f96c640d64876df3ee14fe038b68503cd63d36a1fd092aa33d3e738d4ed8c&u3=https%3A%2F%2Fwww.bing.com%2Faclick%3Fld%3De8yHdgbe16wB0x%2DcAPgvHZdDVUCUx70tji1zX3o7kwOuVJH76ae5KWFMlI2xXK47X50C5H7blNCrYkTCjolxW9T95HCiAsMjqqwkdd71fw%2Di12GNjj6a_x8xmsYgUv1SoUaHMS9mJF40HnjMi2osart3afpkZ1yVburNNhgqcOwg%2DXMgDWa%2DLVR%2Dt%2DwG_ZxijIuc87rPnYDGWI6M3y7Us1OIVOMRY%26u%3DaHR0cCUzYSUyZiUyZnd3dy5hZ29zLmNvLmpwJTJmcHJvZ3JhbSUyZiUzZnV0bV9zb3VyY2UlM2RiaW5nJWMyJWEwJTI2dXRtX21lZGl1bSUzZGNwYyUyNnV0bV9jYW1wYWlnbiUzZG5vdF9zeXV5b3UlMjZtc2Nsa2lkJTNkYWJjY2UzN2M4N2RmMTZjNjNiNTE4NmRmN2EzM2RiYzUlMjZ1dG1fdGVybSUzZExMTSUyNnV0bV9jb250ZW50JTNkS1dEX0FMTF9BTF8oTExNKQ%26rlid%3Dabcce37c87df16c63b5186df7a33dbc5&vqd=4-135094424682227864277222089879108223323&iurl=%7B1%7DIG%3D6793BB4EFA564D10A45BB0C3C75BF04C%26CID%3D342CD9B7486D6452145CCDB1496D6555%26ID%3DDevEx%2C5066.1\",\"text\":\"\\u30d7\\u30ed\\u30b0\\u30e9\\u30e0\\u7d39\\u4ecb\"},{\"targetUrl\":\"https://duckduckgo.com/y.js?ad_domain=agos.co.jp&ad_provider=bingv7aa&ad_type=txad&eddgt=obP7zXz7zpZHDybCoDxesg%3D%3D&rut=2eb357c941584c2d37f902ddd1c08c859ed01f062ab3fb05a001faf0f72e6ba5&u3=https%3A%2F%2Fwww.bing.com%2Faclick%3Fld%3De8C54ez8bikjRXEwXnJwi9YDVUCUy8nH9%2DJAueBxXkb0K7ZCNC0iUGDL4ho5%2DSoNtD5viVDKN7ZH22nltAGg05iJ0ZuWgdIts%2DGgXJTkql6SPae7Qmp04T63VKixa%2DGPkNHV0IE2WaD1BTEMUqr91ygKbGPrQddZylBRsNDKI6Ohg5xJwTIHBIZLIc%2D2VktajIGg3mr7TywC9C8C404NhOGDS3izs%26u%3DaHR0cHMlM2ElMmYlMmZ3d3cuYWdvcy5jby5qcCUyZm9ubGluZXNlcnZpY2VzJTJmbW9kdWxlcyUyZmFnZW5kYXglMmZpbmRleC5waHAlM2ZvcCUzZGNhbCUyNnV0bV9zb3VyY2UlM2RiaW5nJWMyJWEwJTI2dXRtX21lZGl1bSUzZGNwYyUyNnV0bV9jYW1wYWlnbiUzZG5vdF9zeXV5b3UlMjZtc2Nsa2lkJTNkZjY5ZDIyYjcyNmE1MWRhZDQ0MTg0NzE0ZGI0OTk3MWUlMjZ1dG1fdGVybSUzZExMTSUyNnV0bV9jb250ZW50JTNkS1dEX0FMTF9BTF8oTExNKQ%26rlid%3Df69d22b726a51dad44184714db49971e&vqd=4-128396212371085491387335181864088230840&iurl=%7B1%7DIG%3D6793BB4EFA564D10A45BB0C3C75BF04C%26CID%3D342CD9B7486D6452145CCDB1496D6555%26ID%3DDevEx%2C5068.1\",\"text\":\"\\u7121\\u6599\\u30a4\\u30d9\\u30f3\\u30c8\"},{\"targetUrl\":\"https://duckduckgo.com/y.js?ad_domain=agos.co.jp&ad_provider=bingv7aa&ad_type=txad&eddgt=obP7zXz7zpZHDybCoDxesg%3D%3D&rut=479b8c8e06853252a860df6f4f7b0021979d4a7d81cfe31b1fa267ba9bfa146b&u3=https%3A%2F%2Fwww.bing.com%2Faclick%3Fld%3De8NjJGPM715u8VUlRmw8zHUDVUCUw7uCpgewYW%2DimZqp8QeEMcJSAl25ZE2QP%2De2LLi0zNlmdSsmFlczgatCZw3wHznKksYZxIlU8DyVauuq_BlQ7Y_XHQN5FZwp1JVZ62qIViejaQ8jOUUWe5WlcQ_RlJzztAhEBmbtWJsPAdjkkG2_jriD6uq5jjLVwdj7zJvT%2DPdQaxmcV5d1uLOKlfTWWSqcM%26u%3DaHR0cCUzYSUyZiUyZnd3dy5hZ29zLmNvLmpwJTJmdXNlZnVsJTJmJTNmdXRtX3NvdXJjZSUzZGJpbmclYzIlYTAlMjZ1dG1fbWVkaXVtJTNkY3BjJTI2dXRtX2NhbXBhaWduJTNkbm90X3N5dXlvdSUyNm1zY2xraWQlM2Q0NWViYjRlZWE2M2YxZjk0ZTI1YWQxNGQ1YmQzOTFkNSUyNnV0bV90ZXJtJTNkTExNJTI2dXRtX2NvbnRlbnQlM2RLV0RfQUxMX0FMXyhMTE0p%26rlid%3D45ebb4eea63f1f94e25ad14d5bd391d5&vqd=4-297887522221861120640855135832921374674&iurl=%7B1%7DIG%3D6793BB4EFA564D10A45BB0C3C75BF04C%26CID%3D342CD9B7486D6452145CCDB1496D6555%26ID%3DDevEx%2C5070.1\",\"text\":\"\\u7559\\u5b66\\u304a\\u5f79\\u7acb\\u3061\\u60c5\\u5831\"}],\"tid\":\"9\\t11[10]\\t13[12]\\t15[14]\\t17[16]\",\"type\":\"SiteLink\"},\"smart\":{\"t\":\"\\u30b3\\u30fc\\u30b9: TOEFL(R)TEST\\u5bfe\\u7b56\\u30b3\\u30fc\\u30b9, IELTS\\u8a66\\u9a13\\u5bfe\\u7b56\\u30b3\\u30fc\\u30b9, GMAT(R)\\u8a66\\u9a13\\u5bfe\\u7b56\\u30b3\\u30fc\\u30b9\",\"tid\":\"8\"},\"tid\":\"1\"},\"ae\":{\"callout\":[\"\\u304d\\u3081\\u306e\\u7d30\\u304b\\u3044\\u6307\\u5c0e \\u00b7 \\u7d4c\\u9a13\\u3068\\u60c5\\u71b1\\u306b\\u3042\\u3075\\u308c\\u308b\\u8b1b\\u5e2b \\u00b7 \\u77ed\\u671f\\u9593\\u30b9\\u30b3\\u30a2UP\\u6cd5\\u3092\\u63d0\\u4f9b \\u00b7 \\u5c11\\u4eba\\u6570\\u306e\\u5b9f\\u8df5\\u6f14\\u7fd2\"]},\"c\":\"https://duckduckgo.com/y.js?ad_domain=agos.co.jp&ad_provider=bingv7aa&ad_type=txad&eddgt=obP7zXz7zpZHDybCoDxesg%3D%3D&rut=b63afcc493f34d7a7c7d3518e392551bed85e9ae7571c18b4dc955aaf8259616&u3=https%3A%2F%2Fwww.bing.com%2Faclick%3Fld%3De8rhXDjLYuOBw8iJkGNRsNfzVUCUwxbNLujGJigHwGI9U5xO6%2DITqhX%2DRxoJEeElFXLF7C9j%2DxBG6M752LwJ8JIWQMG9aHf9eRSn8J307_mnj%2DzyVlkx3nyY0oZxNIfHP8d_eF8Bl_Gv8mmnjESk_mCDLz9CtFNkGvFdusnGnhhSX20uHcFptCvdD5h78HZ7eC9J8%2DwA%26u%3DaHR0cHMlM2ElMmYlMmZ3d3cuYWdvcy5jby5qcCUyZmxhbmQlMmZsbG0lMmYlM2Z1dG1fc291cmNlJTNkYmluZyVjMiVhMCUyNnV0bV9tZWRpdW0lM2RjcGMlMjZ1dG1fY2FtcGFpZ24lM2Rub3Rfc3l1eW91JTI2bXNjbGtpZCUzZDA5ODZlN2Y3OTRiZTE4ZThhNWJjODI1NDY5NTJkZmI1JTI2dXRtX3Rlcm0lM2RMTE0lMjZ1dG1fY29udGVudCUzZEtXRF9BTExfQUxfKExMTSk%26rlid%3D0986e7f794be18e8a5bc82546952dfb5&vqd=4-174227029600136465336090119074482095864&iurl=%7B1%7DIG%3D6793BB4EFA564D10A45BB0C3C75BF04C%26CID%3D342CD9B7486D6452145CCDB1496D6555%26ID%3DDevEx%2C5058.1\",\"d\":\"agos.co.jp\",\"h\":0,\"i\":\"\",\"k\":0,\"m\":0,\"o\":\"\",\"p\":1,\"relevancy\":{\"abstract\":\"%E6%B5%B7%E5%A4%96%E3%83%88%E3%83%83%E3%83%97%E6%A0%A1%E3%81%B810%2C000%E4%BA%BA%E4%BB%A5%E4%B8%8A%E3%81%AE%E5%90%88%E6%A0%BC%E5%AE%9F%E7%B8%BE!%E3%81%BE%E3%81%9A%E3%81%AF%E7%84%A1%E6%96%99%E5%80%8B%E5%88%A5%E7%9B%B8%E8%AB%87%E3%80%81%E7%84%A1%E6%96%99%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%81%B8.%20%E9%81%B8%E3%81%B9%E3%82%8B%E5%AD%A6%E7%BF%92%E5%BD%A2%E6%85%8B%E3%80%81%E5%8F%97%E8%AC%9B%E6%9C%9F%E9%96%933%E5%B9%B4%E3%80%81%E5%86%8D%E5%8F%97%E8%AC%9B%E7%84%A1%E6%96%99%E3%81%AE%E3%82%A2%E3%82%B4%E3%82%B9%E3%81%A7%E3%82%B0%E3%83%AD%E3%83%BC%E3%83%90%E3%83%AB%E3%83%AC%E3%83%99%E3%83%AB%E3%81%AE%E3%82%AD%E3%83%A3%E3%83%AA%E3%82%A2%E3%82%92%E7%9B%AE%E6%8C%87%E3%81%9B!\",\"adx_name\":\"none\",\"cq_retail\":\"high\",\"is_good_v10\":0,\"q\":\"llm\",\"q_words\":1,\"q_words_fuzzy\":0,\"q_words_in_ad\":\"0\",\"root_domain\":\"agos.co.jp\",\"start\":\"0\",\"title\":\"LLM%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AE%E8%A9%A6%E9%A8%93%E5%AF%BE%E7%AD%96%E3%81%AA%E3%82%89%20%2D%20%E5%85%A8%E5%9B%BD%E3%83%88%E3%83%83%E3%83%97%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%AE%E6%B5%B7%E5%A4%96%E7%95%99%E5%AD%A6%E5%AF%BE%E7%AD%96\"},\"s\":\"bingv7aa\",\"t\":\"LLM\\u306e\\u305f\\u3081\\u306e\\u8a66\\u9a13\\u5bfe\\u7b56\\u306a\\u3089 - \\u5168\\u56fd\\u30c8\\u30c3\\u30d7\\u30af\\u30e9\\u30b9\\u306e\\u6d77\\u5916\\u7559\\u5b66\\u5bfe\\u7b56\",\"tid\":\"1,4,8,9,11[10],13[12],15[14],17[16]\",\"u\":\"https://duckduckgo.com/y.js?ad_domain=agos.co.jp&ad_provider=bingv7aa&ad_type=txad&eddgt=obP7zXz7zpZHDybCoDxesg%3D%3D&rut=b63afcc493f34d7a7c7d3518e392551bed85e9ae7571c18b4dc955aaf8259616&u3=https%3A%2F%2Fwww.bing.com%2Faclick%3Fld%3De8rhXDjLYuOBw8iJkGNRsNfzVUCUwxbNLujGJigHwGI9U5xO6%2DITqhX%2DRxoJEeElFXLF7C9j%2DxBG6M752LwJ8JIWQMG9aHf9eRSn8J307_mnj%2DzyVlkx3nyY0oZxNIfHP8d_eF8Bl_Gv8mmnjESk_mCDLz9CtFNkGvFdusnGnhhSX20uHcFptCvdD5h78HZ7eC9J8%2DwA%26u%3DaHR0cHMlM2ElMmYlMmZ3d3cuYWdvcy5jby5qcCUyZmxhbmQlMmZsbG0lMmYlM2Z1dG1fc291cmNlJTNkYmluZyVjMiVhMCUyNnV0bV9tZWRpdW0lM2RjcGMlMjZ1dG1fY2FtcGFpZ24lM2Rub3Rfc3l1eW91JTI2bXNjbGtpZCUzZDA5ODZlN2Y3OTRiZTE4ZThhNWJjODI1NDY5NTJkZmI1JTI2dXRtX3Rlcm0lM2RMTE0lMjZ1dG1fY29udGVudCUzZEtXRF9BTExfQUxfKExMTSk%26rlid%3D0986e7f794be18e8a5bc82546952dfb5&vqd=4-174227029600136465336090119074482095864&iurl=%7B1%7DIG%3D6793BB4EFA564D10A45BB0C3C75BF04C%26CID%3D342CD9B7486D6452145CCDB1496D6555%26ID%3DDevEx%2C5058.1\"}], {\"page_load_url\":\"https://duckduckgo.com/y.js?ifu=%7B3%7Dappid%3D055AAD1BA669BEB8B048128DC89A107C678B527B%26rguid%3D413d05f2a7f54c8f8ad4685aa4a1d7d9&iurl=%7B2%7DIG%3D6793BB4EFA564D10A45BB0C3C75BF04C%26CID%3D342CD9B7486D6452145CCDB1496D6555%26Type%3DEvent.CPT%26DATA%3D0\",\"visibility_url\":\"https://duckduckgo.com/y.js?ivu=%7B4%7Dtype%3Dmv%26reqver%3D1.0%26rg%3D413d05f2a7f54c8f8ad4685aa4a1d7d9\"});DDG.duckbar.future_signal_tab({signal:'medium',from:'deep_answer'});DDG.duckbar.add({\"data\":{\"Abstract\":\"A large language model is a language model notable for its ability to achieve general-purpose language understanding and generation. LLMs acquire these abilities by learning statistical relationships from text documents during a computationally intensive self-supervised and semi-supervised training process. LLMs are artificial neural networks following a transformer architecture. They can be used for text generation by taking an input text and repeatedly predicting the next token or word. Up to 2020, fine tuning was the only way a model could be adapted to be able to accomplish specific tasks. Larger sized models, such as GPT-3, however, can be prompt-engineered to achieve similar results. They are thought to acquire knowledge about syntax, semantics and \\\"ontology\\\" inherent in human language corpora, but also inaccuracies and biases present in the corpora.\",\"AbstractSource\":\"Wikipedia\",\"AbstractText\":\"A large language model is a language model notable for its ability to achieve general-purpose language understanding and generation. LLMs acquire these abilities by learning statistical relationships from text documents during a computationally intensive self-supervised and semi-supervised training process. LLMs are artificial neural networks following a transformer architecture. They can be used for text generation by taking an input text and repeatedly predicting the next token or word. Up to 2020, fine tuning was the only way a model could be adapted to be able to accomplish specific tasks. Larger sized models, such as GPT-3, however, can be prompt-engineered to achieve similar results. They are thought to acquire knowledge about syntax, semantics and \\\"ontology\\\" inherent in human language corpora, but also inaccuracies and biases present in the corpora.\",\"AbstractURL\":\"https://en.wikipedia.org/wiki/Large_language_model\",\"Answer\":\"\",\"AnswerType\":\"\",\"Definition\":\"\",\"DefinitionSource\":\"\",\"DefinitionURL\":\"\",\"Entity\":\"\",\"Heading\":\"Large language model\",\"Image\":\"\",\"ImageHeight\":0,\"ImageIsLogo\":0,\"ImageWidth\":0,\"Infobox\":\"\",\"Redirect\":\"\",\"RelatedTopics\":[{\"FirstURL\":\"https://duckduckgo.com/Foundation_models\",\"Icon\":{\"Height\":\"\",\"URL\":\"\",\"Width\":\"\"},\"Result\":\"Foundation models - A foundation model is an AI model that is trained on broad data such that it can be applied across a wide range of use cases.\",\"Text\":\"Foundation models - A foundation model is an AI model that is trained on broad data such that it can be applied across a wide range of use cases.\"},{\"FirstURL\":\"https://duckduckgo.com/Generative_artificial_intelligence\",\"Icon\":{\"Height\":\"\",\"URL\":\"\",\"Width\":\"\"},\"Result\":\"Generative AI - Generative artificial intelligence is artificial intelligence capable of generating text, images, or other media, using generative models. Generative AI models learn the patterns and structure of their input training data and then generate new data that has similar characteristics.\",\"Text\":\"Generative AI - Generative artificial intelligence is artificial intelligence capable of generating text, images, or other media, using generative models. Generative AI models learn the patterns and structure of their input training data and then generate new data that has similar characteristics.\"},{\"FirstURL\":\"https://duckduckgo.com/c/Deep_learning\",\"Icon\":{\"Height\":\"\",\"URL\":\"\",\"Width\":\"\"},\"Result\":\"Deep learning\",\"Text\":\"Deep learning\"},{\"FirstURL\":\"https://duckduckgo.com/c/Natural_language_processing\",\"Icon\":{\"Height\":\"\",\"URL\":\"\",\"Width\":\"\"},\"Result\":\"Natural language processing\",\"Text\":\"Natural language processing\"}],\"Results\":[],\"Type\":\"A\",\"meta\":{\"attribution\":null,\"blockgroup\":null,\"created_date\":null,\"description\":\"Wikipedia\",\"designer\":null,\"dev_date\":null,\"dev_milestone\":\"live\",\"developer\":[{\"name\":\"DDG Team\",\"type\":\"ddg\",\"url\":\"http://www.duckduckhack.com\"}],\"example_query\":\"nikola tesla\",\"id\":\"wikipedia_fathead\",\"is_stackexchange\":null,\"js_callback_name\":\"wikipedia\",\"live_date\":null,\"maintainer\":{\"github\":\"duckduckgo\"},\"name\":\"Wikipedia\",\"perl_module\":\"DDG::Fathead::Wikipedia\",\"producer\":null,\"production_state\":\"online\",\"repo\":\"fathead\",\"signal_from\":\"wikipedia_fathead\",\"src_domain\":\"en.wikipedia.org\",\"src_id\":1,\"src_name\":\"Wikipedia\",\"src_options\":{\"directory\":\"\",\"is_fanon\":0,\"is_mediawiki\":1,\"is_wikipedia\":1,\"language\":\"en\",\"min_abstract_length\":\"20\",\"skip_abstract\":0,\"skip_abstract_paren\":0,\"skip_end\":\"0\",\"skip_icon\":0,\"skip_image_name\":0,\"skip_qr\":\"\",\"source_skip\":\"\",\"src_info\":\"\"},\"src_url\":null,\"status\":\"live\",\"tab\":\"About\",\"topic\":[\"productivity\"],\"unsafe\":0}},\"duckbar_topic\":\"About\",\"from\":\"deep_answer\",\"meta\":{\"attribution\":null,\"blockgroup\":null,\"created_date\":null,\"description\":\"Wikipedia\",\"designer\":null,\"dev_date\":null,\"dev_milestone\":\"live\",\"developer\":[{\"name\":\"DDG Team\",\"type\":\"ddg\",\"url\":\"http://www.duckduckhack.com\"}],\"example_query\":\"nikola tesla\",\"id\":\"wikipedia_fathead\",\"is_stackexchange\":null,\"js_callback_name\":\"wikipedia\",\"live_date\":null,\"maintainer\":{\"github\":\"duckduckgo\"},\"name\":\"Wikipedia\",\"perl_module\":\"DDG::Fathead::Wikipedia\",\"producer\":null,\"production_state\":\"online\",\"repo\":\"fathead\",\"signal_from\":\"wikipedia_fathead\",\"src_domain\":\"en.wikipedia.org\",\"src_id\":1,\"src_name\":\"Wikipedia\",\"src_options\":{\"directory\":\"\",\"is_fanon\":0,\"is_mediawiki\":1,\"is_wikipedia\":1,\"language\":\"en\",\"min_abstract_length\":\"20\",\"skip_abstract\":0,\"skip_abstract_paren\":0,\"skip_end\":\"0\",\"skip_icon\":0,\"skip_image_name\":0,\"skip_qr\":\"\",\"source_skip\":\"\",\"src_info\":\"\"},\"src_url\":null,\"status\":\"live\",\"tab\":\"About\",\"topic\":[\"productivity\"],\"unsafe\":0},\"model\":\"FatheadArticle\",\"pixel_id\":\"wikipedia_fathead_deep\",\"signal\":\"medium\",\"templates\":{\"detail\":\"info_detail\"}});DDG.deep.signalSummary = \"about:m,retail:h\";DDG.inject('DDG.Data.languages.resultLanguages', {\"en\":[\"https://en.wikipedia.org/wiki/Large_language_model\",\"https://en.wikipedia.org/wiki/Master_of_Laws\",\"https://www.lsac.org/discover-law/types-law-programs/llm-degree-programs\",\"https://llm-guide.com/what-is-an-llm\",\"https://hls.harvard.edu/graduate-program/ll-m-program/\",\"http://llm.lsac.org/\",\"https://hls.harvard.edu/graduate-program/graduate-program-admissions-and-financial-aid/ll-m-admissions/\",\"https://www.usnews.com/education/articles/getting-an-llm-degree-what-to-know\",\"https://law.stanford.edu/office-of-student-affairs/the-master-of-laws-llm-degree/\",\"https://gould.usc.edu/academics/degrees/online-llm/\",\"https://www.lawyeredu.org/LLM-degree/\",\"https://www.law.nyu.edu/llmjsd/master-of-laws\",\"https://gould.usc.edu/academics/degrees/llm/\",\"https://www.techtarget.com/whatis/definition/large-language-model-LLM\",\"https://aws.amazon.com/what-is/large-language-model/\",\"https://www.computerworld.com/article/3697649/what-are-large-language-models-and-how-are-they-used-in-generative-ai.html\",\"https://graduate.northeastern.edu/program/master-of-laws-llm-online-17868/\",\"https://www.law.northwestern.edu/academics/degree-programs/llms/\",\"https://www.gartner.com/en/information-technology/glossary/large-language-models-llm\",\"https://en.wikipedia.org/wiki/Wikipedia:Large_language_models\",\"https://www.elastic.co/what-is/large-language-models\",\"https://www.geeksforgeeks.org/large-language-model-llm/\",\"https://developers.google.com/machine-learning/resources/intro-llms\"]});DDG.deep.pageLayoutSummary = \"a1w5dic1w18r1,e1\";DDG.inject('DDG.Data.languages.adLanguages', {});if (DDG.pageLayout) DDG.pageLayout.load('d',[{\"a\":\"A large language model (LLM) is a language model notable for its ability to achieve general-purpose language understanding and generation. LLMs acquire these abilities by learning statistical relationships from text documents during a computationally intensive self-supervised and semi-supervised training process. LLMs are artificial neural networks following a transformer architecture.\",\"ae\":null,\"b\":\"w\\tWikipedia\\ten.wikipedia.org\",\"c\":\"https://en.wikipedia.org/wiki/Large_language_model\",\"d\":\"en.wikipedia.org/wiki/Large_language_model\",\"da\":\"en_wikipedia_queries,nlp_fathead,nlp_wiki\",\"h\":0,\"i\":\"en.wikipedia.org\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Large language model - Wikipedia\",\"u\":\"https://en.wikipedia.org/wiki/Large_language_model\"},{\"a\":\"A Master of Laws (M.L. or LL.M.; Latin: Magister Legum or Legum Magister) is an advanced postgraduate academic degree, pursued by those either holding an undergraduate academic law degree, a professional law degree, or an undergraduate degree in a related subject.In most jurisdictions, the LL.M. is the advanced professional degree for those usually already admitted into legal practice.\",\"ae\":null,\"b\":\"w\\tWikipedia\\ten.wikipedia.org\",\"c\":\"https://en.wikipedia.org/wiki/Master_of_Laws\",\"d\":\"en.wikipedia.org/wiki/Master_of_Laws\",\"da\":\"en_wikipedia_queries,nlp_fathead,nlp_wiki\",\"h\":0,\"i\":\"en.wikipedia.org\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Master of Laws - Wikipedia\",\"u\":\"https://en.wikipedia.org/wiki/Master_of_Laws\"},{\"a\":\"An LLM, or Master of Laws, is a graduate qualification in the field of law. The LLM was created for lawyers to expand their knowledge, study a specialized area of law, and gain international qualifications if they have earned a law degree outside the U.S. or Canada. If you're looking to advance your legal career or take the next step in your ...\",\"ae\":null,\"c\":\"https://www.lsac.org/discover-law/types-law-programs/llm-degree-programs\",\"d\":\"www.lsac.org/discover-law/types-law-programs/llm-degree-programs\",\"da\":\"\",\"h\":0,\"i\":\"www.lsac.org\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"LLM Degree | Masters of Laws | The Law School Admission Council\",\"u\":\"https://www.lsac.org/discover-law/types-law-programs/llm-degree-programs\"},{\"a\":\"The LLM - short for Master of Laws - is an internationally recognized postgraduate law degree that is usually completed in one year of full-time studies. It's different from a JD or an LLB, which are first law degrees and are generally required to practice law. Specialized LLMs can be found in tax law, business law, and other subjects.\",\"ae\":null,\"c\":\"https://llm-guide.com/what-is-an-llm\",\"d\":\"llm-guide.com/what-is-an-llm\",\"da\":\"\",\"h\":0,\"i\":\"llm-guide.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"What is an LL.M.? | LLM GUIDE\",\"u\":\"https://llm-guide.com/what-is-an-llm\"},{\"a\":\"Learn about the LL.M. (Master of Laws) program at Harvard Law School, a one-year degree program for students from various legal systems and backgrounds. Find out the degree requirements, academic resources, and class profile of the LL.M. students.\",\"ae\":null,\"c\":\"https://hls.harvard.edu/graduate-program/ll-m-program/\",\"d\":\"hls.harvard.edu/graduate-program/ll-m-program/\",\"da\":\"\",\"h\":0,\"i\":\"hls.harvard.edu\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"LL.M. Program - Harvard Law School | Harvard Law School\",\"u\":\"https://hls.harvard.edu/graduate-program/ll-m-program/\"},{\"a\":\"LSAC offers services for various types of law programs offered by ABA-approved law schools, such as LLM, MCL, MLS, JM, MSLS, JSD, SJD, and DCL. Sign up now to search, apply, and credential assemble for over 130 law programs.\",\"ae\":null,\"c\":\"http://llm.lsac.org/\",\"d\":\"llm.lsac.org\",\"da\":\"\",\"h\":0,\"i\":\"llm.lsac.org\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Welcome to LLM & Other Law Programs | Law School Admission Council\",\"u\":\"http://llm.lsac.org/\"},{\"a\":\"Learn about the eligibility, criteria, and application process for the one-year LL.M. (Master of Laws) program at Harvard Law School, which typically includes 180 students from some 70 countries. Find out the tuition and financial aid options, and see sample applications and FAQs.\",\"ae\":null,\"c\":\"https://hls.harvard.edu/graduate-program/graduate-program-admissions-and-financial-aid/ll-m-admissions/\",\"d\":\"hls.harvard.edu/graduate-program/graduate-program-admissions-and-financial-aid/ll-m-admissions/\",\"da\":\"\",\"h\":0,\"i\":\"hls.harvard.edu\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"LL.M. Admissions - Harvard Law School | Harvard Law School\",\"u\":\"https://hls.harvard.edu/graduate-program/graduate-program-admissions-and-financial-aid/ll-m-admissions/\"},{\"a\":\"An LL.M. is geared towards those whom either have a J.D. degree and want to gain additional training in areas such as tax law or health-care law, or those who earned a degree outside of the U.S ...\",\"ae\":null,\"c\":\"https://www.usnews.com/education/articles/getting-an-llm-degree-what-to-know\",\"d\":\"www.usnews.com/education/articles/getting-an-llm-degree-what-to-know\",\"da\":\"\",\"e\":\"2022-12-28T00:00:00.0000000\",\"h\":0,\"i\":\"www.usnews.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Getting an LL.M. Degree: What to Know | Education | U.S. News\",\"u\":\"https://www.usnews.com/education/articles/getting-an-llm-degree-what-to-know\"},{\"a\":\"To obtain an LLM degree, students must complete at least 35 but no more than 45 approved quarter units of course work. At least 26 of these units must be in Law School courses; however, see below for the policies and limitations on enrolling in courses from elsewhere in the University, and see the section on the California or New York bar exam for special unit requirements for students ...\",\"ae\":null,\"c\":\"https://law.stanford.edu/office-of-student-affairs/the-master-of-laws-llm-degree/\",\"d\":\"law.stanford.edu/office-of-student-affairs/the-master-of-laws-llm-degree/\",\"da\":\"\",\"h\":0,\"i\":\"law.stanford.edu\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"The Master of Laws (LLM) Degree | Stanford Law School\",\"u\":\"https://law.stanford.edu/office-of-student-affairs/the-master-of-laws-llm-degree/\"},{\"a\":\"Earn a Master of Laws degree from a top-ranked law school in the U.S. with a part-time, flexible and interdisciplinary curriculum. Learn from world-class faculty, seasoned academics and policymakers, and join the global Trojan Family network of more than 15,000 law school alumni.\",\"ae\":null,\"c\":\"https://gould.usc.edu/academics/degrees/online-llm/\",\"d\":\"gould.usc.edu/academics/degrees/online-llm/\",\"da\":\"\",\"h\":0,\"i\":\"gould.usc.edu\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Master of Laws (LLM) - Online - USC Gould School of Law\",\"u\":\"https://gould.usc.edu/academics/degrees/online-llm/\"},{\"a\":\"LLM in Taxation. The Master of Laws (LLM) is the degree of choice for career advancement and international credibility, particularly in today's competitive and globally focused legal environment. Early- and mid-career lawyers pursue the LLM voluntarily when looking to expand their proficiency in a specific area of law.\",\"ae\":null,\"c\":\"https://www.lawyeredu.org/LLM-degree/\",\"d\":\"www.lawyeredu.org/LLM-degree/\",\"da\":\"\",\"h\":0,\"i\":\"www.lawyeredu.org\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"What is an LLM | What is a Master of Laws - Lawyeredu.org\",\"u\":\"https://www.lawyeredu.org/LLM-degree/\"},{\"a\":\"Design Your Own LLM. You will choose from 300+ courses to plan a curriculum that meets your intellectual and professional interests. You can choose to specialize in one or two areas, or take a broad range of classes. You also will have the chance to write a paper in close consultation with a professor, or expand a typical research assignment into a master's thesis.\",\"ae\":null,\"c\":\"https://www.law.nyu.edu/llmjsd/master-of-laws\",\"d\":\"www.law.nyu.edu/llmjsd/master-of-laws\",\"da\":\"\",\"h\":0,\"i\":\"www.law.nyu.edu\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Master of Laws (LLM) | NYU School of Law - New York University\",\"u\":\"https://www.law.nyu.edu/llmjsd/master-of-laws\"},{\"a\":\"Learn about the Master of Laws (LLM) degree programs at USC Gould School of Law, which focus on the U.S. legal system and prepare students for leadership roles in law. Choose from various formats, eligibility criteria, and specialization tracks to suit your goals and interests.\",\"ae\":null,\"c\":\"https://gould.usc.edu/academics/degrees/llm/\",\"d\":\"gould.usc.edu/academics/degrees/llm/\",\"da\":\"\",\"h\":0,\"i\":\"gould.usc.edu\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Master of Laws (LLM) Degree Programs | USC Gould School of Law\",\"u\":\"https://gould.usc.edu/academics/degrees/llm/\"},{\"a\":\"A large language model (LLM) is a type of artificial intelligence ( AI) algorithm that uses deep learning techniques and massively large data sets to understand, summarize, generate and predict new content. The term generative AI also is closely connected with LLMs, which are, in fact, a type of generative AI that has been specifically ...\",\"ae\":null,\"b\":\"whatis\\tWhatIs.com\\twhatis.techtarget.com\",\"c\":\"https://www.techtarget.com/whatis/definition/large-language-model-LLM\",\"d\":\"www.techtarget.com/whatis/definition/large-language-model-LLM\",\"da\":\"\",\"h\":0,\"i\":\"www.techtarget.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"What are Large Language Models? | Definition from TechTarget\",\"u\":\"https://www.techtarget.com/whatis/definition/large-language-model-LLM\"},{\"a\":\"Large language models (LLM) are very large deep learning models that are pre-trained on vast amounts of data. The underlying transformer is a set of neural networks that consist of an encoder and a decoder with self-attention capabilities. The encoder and decoder extract meanings from a sequence of text and understand the relationships between words and phrases in it.\",\"ae\":null,\"b\":\"a\\tAmazon.com\\twww.amazon.com\",\"c\":\"https://aws.amazon.com/what-is/large-language-model/\",\"d\":\"aws.amazon.com/what-is/large-language-model/\",\"da\":\"products\",\"h\":0,\"i\":\"aws.amazon.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"What are Large Language Models? - LLM AI Explained - AWS\",\"u\":\"https://aws.amazon.com/what-is/large-language-model/\"},{\"a\":\"The LLM could come back with "cereal," or "rice," or "steak tartare." There's no 100% right answer, but there is a probability based on the data already ingested in the model. The ...\",\"ae\":null,\"c\":\"https://www.computerworld.com/article/3697649/what-are-large-language-models-and-how-are-they-used-in-generative-ai.html\",\"d\":\"www.computerworld.com/article/3697649/what-are-large-language-models-and-how-are-they-used-in-generative-ai.html\",\"da\":\"\",\"e\":\"2023-05-30T10:00:00.0000000\",\"h\":0,\"i\":\"www.computerworld.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"What are LLMs, and how are they used in generative AI?\",\"u\":\"https://www.computerworld.com/article/3697649/what-are-large-language-models-and-how-are-they-used-in-generative-ai.html\"},{\"a\":\"Northeastern University offers a Master of Laws (LLM) program with a 100% online learning format option designed for internationally trained lawyers and U.S.-trained lawyers to enhance their practical skills and foundational knowledge of the ever-changing U.S. legal environment, and the global practice of law. The online LLM program positions students to take advantage of Northeastern ...\",\"ae\":null,\"c\":\"https://graduate.northeastern.edu/program/master-of-laws-llm-online-17868/\",\"d\":\"graduate.northeastern.edu/program/master-of-laws-llm-online-17868/\",\"da\":\"\",\"h\":0,\"i\":\"graduate.northeastern.edu\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Master of Laws LLM-Online - Graduate Programs\",\"u\":\"https://graduate.northeastern.edu/program/master-of-laws-llm-online-17868/\"},{\"a\":\"LLM Programs. Our LLM (Master of Law) degree programs expand students' knowledge of law and legal processes and provide opportunities for them to gain expertise in a specialized field of law. To apply, students must have a JD from an ABA-accredited law school or a comparable legal degree from a university outside of the United States.\",\"ae\":null,\"c\":\"https://www.law.northwestern.edu/academics/degree-programs/llms/\",\"d\":\"www.law.northwestern.edu/academics/degree-programs/llms/\",\"da\":\"\",\"h\":0,\"i\":\"www.law.northwestern.edu\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"LLM Programs - Northwestern University Pritzker School of Law\",\"u\":\"https://www.law.northwestern.edu/academics/degree-programs/llms/\"},{\"a\":\"Large Language Models (LLMs) A large language model (LLM) is a specialized type of artificial intelligence (AI) that has been trained on vast amounts of text to understand existing content and generate original content.\",\"ae\":null,\"c\":\"https://www.gartner.com/en/information-technology/glossary/large-language-models-llm\",\"d\":\"www.gartner.com/en/information-technology/glossary/large-language-models-llm\",\"da\":\"\",\"h\":0,\"i\":\"www.gartner.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Large Language Models (LLMs) - Gartner\",\"u\":\"https://www.gartner.com/en/information-technology/glossary/large-language-models-llm\"},{\"a\":\"Large language models have limited reliability, limited understanding, limited range, and hence need human supervision. While large language models (colloquially termed "AI chatbots" in some contexts) can be very useful, machine-generated text (much like human-generated text) can contain errors or flaws, or be outright useless.\",\"ae\":null,\"b\":\"w\\tWikipedia\\ten.wikipedia.org\",\"c\":\"https://en.wikipedia.org/wiki/Wikipedia:Large_language_models\",\"d\":\"en.wikipedia.org/wiki/Wikipedia:Large_language_models\",\"da\":\"en_wikipedia_queries,nlp_fathead,nlp_wiki\",\"h\":0,\"i\":\"en.wikipedia.org\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Wikipedia:Large language models - Wikipedia\",\"u\":\"https://en.wikipedia.org/wiki/Wikipedia:Large_language_models\"},{\"a\":\"Large language model definition. A large language model (LLM) is a deep learning algorithm that can perform a variety of natural language processing (NLP) tasks. Large language models use transformer models and are trained using massive datasets \\u2014 hence, large. This enables them to recognize, translate, predict, or generate text or other content.\",\"ae\":null,\"c\":\"https://www.elastic.co/what-is/large-language-models\",\"d\":\"www.elastic.co/what-is/large-language-models\",\"da\":\"\",\"h\":0,\"i\":\"www.elastic.co\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"What is a large language model (LLM)? - Elastic\",\"u\":\"https://www.elastic.co/what-is/large-language-models\"},{\"a\":\"Difference Between NLP and LLM NLP is Natural Language Processing, a field of artificial intelligence (AI). It consists of the development of the algorithms. NLP is a broader field than LLM, which consists of algorithms and techniques. NLP rules two approaches i.e. Machine learning and the analyze language data. Applications of NLP are-\",\"ae\":null,\"c\":\"https://www.geeksforgeeks.org/large-language-model-llm/\",\"d\":\"www.geeksforgeeks.org/large-language-model-llm/\",\"da\":\"\",\"e\":\"2024-01-10T00:00:00.0000000\",\"h\":0,\"i\":\"www.geeksforgeeks.org\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"What is a Large Language Model (LLM) - GeeksforGeeks\",\"u\":\"https://www.geeksforgeeks.org/large-language-model-llm/\"},{\"a\":\"Define key LLM concepts, including Transformers and self-attention. Describe the costs and benefits of LLMs, along with common use cases. What is a language model? A language model is a machine learning model that aims to predict and generate plausible language. Autocomplete is a language model, for example.\",\"ae\":null,\"c\":\"https://developers.google.com/machine-learning/resources/intro-llms\",\"d\":\"developers.google.com/machine-learning/resources/intro-llms\",\"da\":\"\",\"e\":\"2023-08-08T00:00:00.0000000\",\"h\":0,\"i\":\"developers.google.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Introduction to Large Language Models - Google Developers\",\"u\":\"https://developers.google.com/machine-learning/resources/intro-llms\"},{\"n\":\"/d.js?q=llm&kl=wt-wt&l=wt-wt&p=&s=23&ex=-1&ct=US&sp=0&vqd=4-36212277936736004277629252433802891730\"}]);DDG.duckbar.load('images');DDG.duckbar.load('news');DDG.duckbar.load('videos');DDG.duckbar.loadModule('related_searches', {\"ads\":[],\"query\":\"llm\",\"queryEncoded\":\"llm\",\"response_type\":\"places\",\"results\":[{\"display_text\":\"what does #llm mean\",\"text\":\"what does #llm mean\",\"web_search_url\":\"?q=what%20does%20%23llm%20mean\"},{\"display_text\":\"llm meaning\",\"text\":\"llm meaning\",\"web_search_url\":\"?q=llm%20meaning\"},{\"display_text\":\"llm meaning in law\",\"text\":\"llm meaning in law\",\"web_search_url\":\"?q=llm%20meaning%20in%20law\"},{\"display_text\":\"what is llm stand for\",\"text\":\"what is llm stand for\",\"web_search_url\":\"?q=what%20is%20llm%20stand%20for\"},{\"display_text\":\"llm in artificial intelligence\",\"text\":\"llm in artificial intelligence\",\"web_search_url\":\"?q=llm%20in%20artificial%20intelligence\"},{\"display_text\":\"full meaning of llm\",\"text\":\"full meaning of llm\",\"web_search_url\":\"?q=full%20meaning%20of%20llm\"},{\"display_text\":\"what is llm in law\",\"text\":\"what is llm in law\",\"web_search_url\":\"?q=what%20is%20llm%20in%20law\"},{\"display_text\":\"examples of llm models\",\"text\":\"examples of llm models\",\"web_search_url\":\"?q=examples%20of%20llm%20models\"}],\"vqd\":{\"llm\":\"4-36212277936736004277629252433802891730\"}});if (DDG.pageLayout) DDG.pageLayout.initialize({\"mainline\":{\"items\":[[\"ad\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"dictionary_definition\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"related_searches\"]]},\"sidebar\":{\"items\":[[\"wikipedia_fathead\"]]}}, { start: 0 });DDG.deep.emit(\"load:completed\");", + "curl-cffi-POST-https://duckduckgo.com-{\"data\": {\"q\": \"MetaGPT use cases\"}}": "MetaGPT use cases at DuckDuckGo
", + "curl-cffi-GET-https://links.duckduckgo.com/d.js-{\"params\": {\"bing_market\": \"wt-WT\", \"df\": null, \"ex\": \"-1\", \"kl\": \"wt-wt\", \"l\": \"wt-wt\", \"q\": \"MetaGPT use cases\", \"s\": \"0\", \"sp\": \"0\", \"vqd\": \"4-206455801954364851330794682843954609879\"}}": "if (DDG.deep && DDG.deep.setUpstream) DDG.deep.setUpstream(\"bingv7aa\");DDG.deep.bn={'ivc':1};if (DDG.pageLayout) DDG.pageLayout.load('a',[], {\"page_load_url\":\"https://duckduckgo.com/y.js?iurl=%7B2%7DIG%3DA7C157D7FB464F86BD78A7B80D28A7BC%26CID%3D2B7157406A406D2B1C8943466B3F6C14%26Type%3DEvent.CPT%26DATA%3D0\"});DDG.deep.signalSummary = \"\";DDG.inject('DDG.Data.languages.resultLanguages', {\"en\":[\"https://incubity.ambilio.com/metagpt-deep-dive-into-multi-agent-system-with-use-cases/\",\"https://www.unite.ai/metagpt-complete-guide-to-the-best-ai-agent-available-right-now/\",\"https://ts2.pl/en/metagpt-in-action-use-cases-across-industries/\",\"https://geekflare.com/metagpt-multi-agent-framework/\",\"https://docs.deepwisdom.ai/main/en/guide/get_started/introduction.html\",\"https://www.straight.com/guides/software/a-complete-guide-to-metagpt-the-best-ai-agent-available-now/\",\"https://aibusiness.com/nlp/metagpt-text-to-app-ai-simplifies-web-dev\",\"https://levelup.gitconnected.com/metagpt-the-future-of-multi-agent-collaboration-in-ai-a-brief-guide-fd4b4429336d\",\"https://github.com/geekan/MetaGPT\",\"https://docs.deepwisdom.ai/main/en/guide/get_started/quickstart.html\",\"https://medium.com/mlearning-ai/metagpt-multi-agent-harmony-for-complex-problem-solving-97bcb8f3fe94\",\"https://docs.deepwisdom.ai/enus/guide/tutorials/concepts.html\",\"https://medium.com/aimonks/metagpt-a-framework-for-multi-agent-meta-programming-6c79f2eafb8e\",\"https://docs.deepwisdom.ai/main/en/guide/use_cases/agent/researcher.html\",\"https://github.com/PlaiD3/MetaGPT/blob/main/README.md\",\"https://generativeai.pub/analyzing-an-exciting-generative-ai-research-called-metagpt-2106385312db\",\"https://medium.com/technology-hits/autogpt-langchain-deep-lake-metagpt-a-revolutionary-framework-for-building-advanced-ai-e2c579d86494\",\"https://gpt3demo.com/apps/metagpt\",\"https://docs.deepwisdom.ai/main/en/guide/tutorials/use_memories.html\",\"https://smythos.com/ai-agents/agent-comparison/metagpt-vs-autogen/\",\"https://analyticsindiamag.com/metagpt-realising-the-gpt-4-dream/\",\"https://docs.deepwisdom.ai/main/en/guide/use_cases/agent/tutorial_assistant.html\",\"https://www.washingtonpost.com/technology/2024/01/04/nyt-ai-copyright-lawsuit-fair-use/\",\"https://blog.netwrix.com/2024/01/09/azure-storage/\",\"https://www.bloomberg.com/news/articles/2024-01-09/walmart-wmt-expands-rollout-of-generative-ai-shopping-search-tech\",\"https://health.ny.gov/press/releases/2024/docs/2024-01-08_masking_advisory.pdf\",\"https://www.bloomberg.com/news/articles/2024-01-10/lloyds-bank-manager-awarded-450-000-after-winning-case-over-racist-slur\",\"https://www.washingtonpost.com/world/2024/01/10/south-africa-israel-icj-genocide-case/\"]});DDG.deep.pageLayoutSummary = \"w29\";DDG.inject('DDG.Data.languages.adLanguages', {});if (DDG.pageLayout) DDG.pageLayout.load('d',[{\"a\":\"Some high-value business use cases where MetaGPT could be applied include: Software Development and Engineering: MetaGPT can streamline the software development lifecycle by orchestrating roles like Product Managers, Architects, Engineers, and QA Engineers. It can assist in requirements gathering, design, code generation, testing, and debugging ...\",\"ae\":null,\"c\":\"https://incubity.ambilio.com/metagpt-deep-dive-into-multi-agent-system-with-use-cases/\",\"d\":\"incubity.ambilio.com/metagpt-deep-dive-into-multi-agent-system-with-use-cases/\",\"da\":\"\",\"h\":0,\"i\":\"incubity.ambilio.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: Deep Dive into Multi-Agent System with Use Cases\",\"u\":\"https://incubity.ambilio.com/metagpt-deep-dive-into-multi-agent-system-with-use-cases/\"},{\"a\":\"Published 4 months ago on September 11, 2023 By Aayush Mittal With Large Language Models (LLMs) like ChatGPT, OpenAI has witnessed a surge in enterprise and user adoption, currently raking in around $80 million in monthly revenue.\",\"ae\":null,\"c\":\"https://www.unite.ai/metagpt-complete-guide-to-the-best-ai-agent-available-right-now/\",\"d\":\"www.unite.ai/metagpt-complete-guide-to-the-best-ai-agent-available-right-now/\",\"da\":\"\",\"e\":\"2023-09-11T00:00:00.0000000\",\"h\":0,\"i\":\"www.unite.ai\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: Complete Guide to the Best AI Agent Available Right Now\",\"u\":\"https://www.unite.ai/metagpt-complete-guide-to-the-best-ai-agent-available-right-now/\"},{\"a\":\"MetaGPT in Action: Use-cases Across Industries MetaGPT, a powerful language model developed by OpenAI, has been making waves across various industries due to its versatility and ability to generate human-like text. As artificial intelligence (AI) continues to advance, the potential applications of MetaGPT are becoming increasingly apparent.\",\"ae\":null,\"c\":\"https://ts2.pl/en/metagpt-in-action-use-cases-across-industries/\",\"d\":\"ts2.pl/en/metagpt-in-action-use-cases-across-industries/\",\"da\":\"\",\"e\":\"2023-06-12T00:00:00.0000000\",\"h\":0,\"i\":\"ts2.pl\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT in Action: Use-cases Across Industries\",\"u\":\"https://ts2.pl/en/metagpt-in-action-use-cases-across-industries/\"},{\"a\":\"MetaGPT is a multi-agent framework that takes one-line inputs to produce APIs, user stories, data structures, competitive analysis, and more. GPT is the short form for Generative Pretrained Transformers. MetaGPT framework can behave as a product manager, software engineer, and architect.\",\"ae\":null,\"c\":\"https://geekflare.com/metagpt-multi-agent-framework/\",\"d\":\"geekflare.com/metagpt-multi-agent-framework/\",\"da\":\"\",\"e\":\"2023-09-18T00:00:00.0000000\",\"h\":0,\"i\":\"geekflare.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: Is This the Best Multi-Agent Framework Yet? - Geekflare\",\"u\":\"https://geekflare.com/metagpt-multi-agent-framework/\"},{\"a\":\"Software Company Multi-Role Schematic MetaGPT's Abilities MetaGPT started as a software company, but its capabilities are not limited to that. You can use this multi-agent framework in your own scenario to build your own application. For details, you can refer to Researcher under Use Cases. Let's do it. Examples (fully generated by GPT-4)\",\"ae\":null,\"c\":\"https://docs.deepwisdom.ai/main/en/guide/get_started/introduction.html\",\"d\":\"docs.deepwisdom.ai/main/en/guide/get_started/introduction.html\",\"da\":\"\",\"h\":0,\"i\":\"docs.deepwisdom.ai\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: The Multi-Agent Framework | MetaGPT\",\"u\":\"https://docs.deepwisdom.ai/main/en/guide/get_started/introduction.html\"},{\"a\":\"MetaGPT is a multi-agent system that utilizes Large Language Models (LLMs) to perform complex tasks. ... MetaGPT has demonstrated its capabilities in various use cases, including developing a CLI ...\",\"ae\":null,\"c\":\"https://www.straight.com/guides/software/a-complete-guide-to-metagpt-the-best-ai-agent-available-now/\",\"d\":\"www.straight.com/guides/software/a-complete-guide-to-metagpt-the-best-ai-agent-available-now/\",\"da\":\"\",\"e\":\"2023-12-13T00:00:00.0000000\",\"h\":0,\"i\":\"www.straight.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"A Complete Guide to MetaGPT: The Best AI Agent Available Now\",\"u\":\"https://www.straight.com/guides/software/a-complete-guide-to-metagpt-the-best-ai-agent-available-now/\"},{\"a\":\"MetaGPT builds microapps - applications designed for specific tasks or use cases. Examples include Facebook Messenger, the project management app Trello, and even Microsoft Word. It only generates web apps - which can be viewed on mobile or desktop browsers but won't run as native apps on Android or iOS.\",\"ae\":null,\"c\":\"https://aibusiness.com/nlp/metagpt-text-to-app-ai-simplifies-web-dev\",\"d\":\"aibusiness.com/nlp/metagpt-text-to-app-ai-simplifies-web-dev\",\"da\":\"\",\"e\":\"2023-08-07T00:00:00.0000000\",\"h\":0,\"i\":\"aibusiness.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: Text-To-App AI Simplifies Web Dev\",\"u\":\"https://aibusiness.com/nlp/metagpt-text-to-app-ai-simplifies-web-dev\"},{\"a\":\"Here's a simple example of how to use MetaGPT: python startup.py "Write a cli snake game" # Use code review will cost more money, but will opt for better code quality. python startup.py "Write a cli snake game" --code_review True. ... Over the last few months, we have looked into around 100 agents with various use cases, studied SDKs and ...\",\"ae\":null,\"c\":\"https://levelup.gitconnected.com/metagpt-the-future-of-multi-agent-collaboration-in-ai-a-brief-guide-fd4b4429336d\",\"d\":\"levelup.gitconnected.com/metagpt-the-future-of-multi-agent-collaboration-in-ai-a-brief-guide-fd4b4429336d\",\"da\":\"\",\"e\":\"2023-08-09T00:00:00.0000000\",\"h\":0,\"i\":\"levelup.gitconnected.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: The Future of Multi-Agent Collaboration in AI (A Brief Guide)\",\"u\":\"https://levelup.gitconnected.com/metagpt-the-future-of-multi-agent-collaboration-in-ai-a-brief-guide-fd4b4429336d\"},{\"a\":\"MetaGPT: The Multi-Agent Framework Assign different roles to GPTs to form a collaborative software entity for complex tasks. MetaGPT takes a one line requirement as input and outputs user stories / competitive analysis / requirements / data structures / APIs / documents, etc.\",\"ae\":null,\"b\":\"gh\\tGitHub\\tgithub.com\",\"c\":\"https://github.com/geekan/MetaGPT\",\"d\":\"github.com/geekan/MetaGPT\",\"da\":\"\",\"h\":0,\"i\":\"github.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: The Multi-Agent Framework - GitHub\",\"u\":\"https://github.com/geekan/MetaGPT\"},{\"a\":\"You can check this by using:</span>\n<span class=\"pl-c\"><span class=\"pl-c\">#</span> You can use conda to initialize a new python env</span>\n<span class=\"pl-c\"><span class=\"pl-c\">#</span> conda create -n metagpt python=3.9</span>\n<span class=\"pl-c\"><span class=\"pl-c\">#</span> conda activate metagpt</span>\npython3 --version\n\n<span...\",\"ae\":null,\"b\":\"gh\\tGitHub\\tgithub.com\",\"c\":\"https://github.com/geekan/MetaGPT?search=1\",\"d\":\"github.com/geekan/MetaGPT?search=1\",\"da\":\"\",\"h\":0,\"i\":\"github.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: The Multi-Agent Framework - GitHub\",\"u\":\"https://github.com/geekan/MetaGPT?search=1\"},{\"a\":\"Now, let's get started! We will create a team of agents to write software based on one line of our instruction. First, import off-the-shelf roles. python. import asyncio from metagpt.roles import ( Architect, Engineer, ProductManager, ProjectManager, ) from metagpt.team import Team. Next, initiate the team, equip it with agents, set their ...\",\"ae\":null,\"c\":\"https://docs.deepwisdom.ai/main/en/guide/get_started/quickstart.html\",\"d\":\"docs.deepwisdom.ai/main/en/guide/get_started/quickstart.html\",\"da\":\"\",\"h\":0,\"i\":\"docs.deepwisdom.ai\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Quickstart | MetaGPT\",\"u\":\"https://docs.deepwisdom.ai/main/en/guide/get_started/quickstart.html\"},{\"a\":\"Stefan Silver \\u00b7 Follow Published in MLearning.ai \\u00b7 4 min read \\u00b7 Aug 9 4 Photo by Penfer on Unsplash Lately, there's been quite a buzz around automating problem-solving using multiagents...\",\"ae\":null,\"c\":\"https://medium.com/mlearning-ai/metagpt-multi-agent-harmony-for-complex-problem-solving-97bcb8f3fe94\",\"d\":\"medium.com/mlearning-ai/metagpt-multi-agent-harmony-for-complex-problem-solving-97bcb8f3fe94\",\"da\":\"\",\"e\":\"2023-08-09T00:00:00.0000000\",\"h\":0,\"i\":\"medium.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: Multi-Agent Harmony for Complex Problem Solving\",\"u\":\"https://medium.com/mlearning-ai/metagpt-multi-agent-harmony-for-complex-problem-solving-97bcb8f3fe94\"},{\"a\":\"Concepts. After this tutorial, you will be able to: Understand MetaGPT's concept of agent and environment. How agents interact with each other and what a multi-agent collaboration may look like. The goal is to provide an intuitive and simplified explanation of the concepts so that users have a background to further explore the tutorial series.\",\"ae\":null,\"c\":\"https://docs.deepwisdom.ai/enus/guide/tutorials/concepts.html\",\"d\":\"docs.deepwisdom.ai/enus/guide/tutorials/concepts.html\",\"da\":\"\",\"h\":0,\"i\":\"docs.deepwisdom.ai\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Concepts | MetaGPT\",\"u\":\"https://docs.deepwisdom.ai/enus/guide/tutorials/concepts.html\"},{\"a\":\"Capabilities/Use Case of MetaGPT MetaGPT has many potential applications and use cases in various fields and scenarios that involve multi-agent collaboration and coordination. Some of...\",\"ae\":null,\"c\":\"https://medium.com/aimonks/metagpt-a-framework-for-multi-agent-meta-programming-6c79f2eafb8e\",\"d\":\"medium.com/aimonks/metagpt-a-framework-for-multi-agent-meta-programming-6c79f2eafb8e\",\"da\":\"\",\"e\":\"2023-08-03T00:00:00.0000000\",\"h\":0,\"i\":\"medium.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: A Framework for Multi-Agent Meta Programming\",\"u\":\"https://medium.com/aimonks/metagpt-a-framework-for-multi-agent-meta-programming-6c79f2eafb8e\"},{\"a\":\"The metagpt.roles.researcher module provides a command-line interface for executing the functionalities of the Researcher. An example is as follows: bash. python3 -m metagpt.roles.researcher "dataiku vs. datarobot". Log output: log.txt Report output: dataiku vs. datarobot.md.\",\"ae\":null,\"c\":\"https://docs.deepwisdom.ai/main/en/guide/use_cases/agent/researcher.html\",\"d\":\"docs.deepwisdom.ai/main/en/guide/use_cases/agent/researcher.html\",\"da\":\"\",\"h\":0,\"i\":\"docs.deepwisdom.ai\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Researcher: Search Web and Write Reports | MetaGPT\",\"u\":\"https://docs.deepwisdom.ai/main/en/guide/use_cases/agent/researcher.html\"},{\"a\":\"You can check this by using:</span>\npython --version\n\n<span class=\"pl-c\"><span class=\"pl-c\">#</span> Step 3: Clone the repository to your local machine, and install it.</span>\ngit clone https://github.com/geekan/metagpt\n<span class=\"pl-c1\">cd</span> metagpt\npython setup.py install</pre></div>\n<h3 tabindex=\"-1\" dir=\"auto\"><a id=\...\",\"ae\":null,\"b\":\"gh\\tGitHub\\tgithub.com\",\"c\":\"https://github.com/PlaiD3/MetaGPT/blob/main/README.md\",\"d\":\"github.com/PlaiD3/MetaGPT/blob/main/README.md\",\"da\":\"\",\"h\":0,\"i\":\"github.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: Multi-Agent Meta Programming Framework - GitHub\",\"u\":\"https://github.com/PlaiD3/MetaGPT/blob/main/README.md\"},{\"a\":\"MetaGPT, as a cutting-edge framework, is not just a theoretical marvel but has been tested, showcasing its prowess in real-world applications. ... These articles cover a wide range of topics related to Generative AI, from introductions and use cases to exploring its potential and understanding its underlying layers. Happy reading!\",\"ae\":null,\"c\":\"https://generativeai.pub/analyzing-an-exciting-generative-ai-research-called-metagpt-2106385312db\",\"d\":\"generativeai.pub/analyzing-an-exciting-generative-ai-research-called-metagpt-2106385312db\",\"da\":\"translations\",\"e\":\"2023-08-14T00:00:00.0000000\",\"h\":0,\"i\":\"generativeai.pub\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Analyzing an exciting Generative AI research called MetaGPT.\",\"u\":\"https://generativeai.pub/analyzing-an-exciting-generative-ai-research-called-metagpt-2106385312db\"},{\"a\":\"Here are 10 compelling use cases that demonstrate the vast potential of LangChain: Uses-Cases of LangChain. 1. Conversational AI and Chatbots ... MetaGPT, or multimodal Generative Pretrained ...\",\"ae\":null,\"c\":\"https://medium.com/technology-hits/autogpt-langchain-deep-lake-metagpt-a-revolutionary-framework-for-building-advanced-ai-e2c579d86494\",\"d\":\"medium.com/technology-hits/autogpt-langchain-deep-lake-metagpt-a-revolutionary-framework-for-building-advanced-ai-e2c579d86494\",\"da\":\"\",\"e\":\"2023-08-28T00:00:00.0000000\",\"h\":0,\"i\":\"medium.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"AutoGPT \\u2014 LangChain \\u2014 Deep Lake \\u2014 MetaGPT: A ... - Medium\",\"u\":\"https://medium.com/technology-hits/autogpt-langchain-deep-lake-metagpt-a-revolutionary-framework-for-building-advanced-ai-e2c579d86494\"},{\"a\":\"MetaGPT takes a one-line requirement as input and outputs user stories / competitive analysis/requirements/data structures / APIs / documents, etc. Internally, MetaGPT includes product managers/architects/project managers/engineers. It provides the entire process of a software company along with carefully orchestrated SOPs.\",\"ae\":null,\"c\":\"https://gpt3demo.com/apps/metagpt\",\"d\":\"gpt3demo.com/apps/metagpt\",\"da\":\"\",\"h\":0,\"i\":\"gpt3demo.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT | Discover AI use cases - GPT-3 Demo\",\"u\":\"https://gpt3demo.com/apps/metagpt\"},{\"a\":\"Retrieve memory. When recorded memories are needed, such as serving as context for a LLM call, you can use self.get_memories. The function definition is as follows: python. def get_memories(self, k=0) -> list [Message]: """A wrapper to return the most recent k memories of this role, return all when k=0""" return self.rc.memory.get (k=k) For ...\",\"ae\":null,\"c\":\"https://docs.deepwisdom.ai/main/en/guide/tutorials/use_memories.html\",\"d\":\"docs.deepwisdom.ai/main/en/guide/tutorials/use_memories.html\",\"da\":\"\",\"h\":0,\"i\":\"docs.deepwisdom.ai\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Use Memories | MetaGPT\",\"u\":\"https://docs.deepwisdom.ai/main/en/guide/tutorials/use_memories.html\"},{\"a\":\"6 Conclusion Introduction Are you struggling to choose between MetaGPT Vs AutoGen? Comparing these two leading companies can help you make an informed decision. MetaGPT is a powerful tool designed for software developers, project managers, startups, technology companies, and AI enthusiasts.\",\"ae\":null,\"c\":\"https://smythos.com/ai-agents/agent-comparison/metagpt-vs-autogen/\",\"d\":\"smythos.com/ai-agents/agent-comparison/metagpt-vs-autogen/\",\"da\":\"\",\"h\":0,\"i\":\"smythos.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT Vs AutoGen: A Comprehensive Comparison\",\"u\":\"https://smythos.com/ai-agents/agent-comparison/metagpt-vs-autogen/\"},{\"a\":\"While some are just wrappers of OpenAI's APIs with added functionality like Forefront.ai or AnonChatGPT, others, like MemeCam or Bing Chat use the GPT-4 API to facilitate new use-cases altogether. OpenAI now needs to move faster, or risk their dream being stolen by others who are on the bleeding edge. Anirudh VK\",\"ae\":null,\"c\":\"https://analyticsindiamag.com/metagpt-realising-the-gpt-4-dream/\",\"d\":\"analyticsindiamag.com/metagpt-realising-the-gpt-4-dream/\",\"da\":\"\",\"e\":\"2023-04-26T00:00:00.0000000\",\"h\":0,\"i\":\"analyticsindiamag.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT \\u2014 Realising the GPT-4 Dream - Analytics India Magazine\",\"u\":\"https://analyticsindiamag.com/metagpt-realising-the-gpt-4-dream/\"},{\"a\":\"Override the _act method. The _act method is responsible for executing the action.Use todo = self.rc.todo to get the next action to be executed from the context, and then execute the run method of the action.Here, it first obtains the tutorial directory structure through WriteDirectory, then chunks the directory, generates a WriteContent action for each chunk, and initializes the newly added ...\",\"ae\":null,\"c\":\"https://docs.deepwisdom.ai/main/en/guide/use_cases/agent/tutorial_assistant.html\",\"d\":\"docs.deepwisdom.ai/main/en/guide/use_cases/agent/tutorial_assistant.html\",\"da\":\"\",\"h\":0,\"i\":\"docs.deepwisdom.ai\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Tutorial Assistant: Generate technology tutorial | MetaGPT\",\"u\":\"https://docs.deepwisdom.ai/main/en/guide/use_cases/agent/tutorial_assistant.html\"},{\"a\":\"AI's future could hinge on one thorny legal question. A lawsuit accuses OpenAI and Microsoft of violating the New York Times's copyright. But the law is anything but clear. By Will Oremus. and ...\",\"ae\":null,\"c\":\"https://www.washingtonpost.com/technology/2024/01/04/nyt-ai-copyright-lawsuit-fair-use/\",\"d\":\"www.washingtonpost.com/technology/2024/01/04/nyt-ai-copyright-lawsuit-fair-use/\",\"da\":\"news,translations\",\"e\":\"2024-01-04T12:01:54.0000000\",\"h\":0,\"i\":\"www.washingtonpost.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"AI copyright lawsuit hinges on the legal concept of 'fair use' - The ...\",\"u\":\"https://www.washingtonpost.com/technology/2024/01/04/nyt-ai-copyright-lawsuit-fair-use/\"},{\"a\":\"Here are some common use cases for Azure Table Storage: Centralized storage of logs, telemetry data and monitoring data. Storage of catalog and shopping cart data for e-commerce applications. Scalable task scheduling and metadata storage. Storage of sensory data and IoT telemetry data.\",\"ae\":null,\"c\":\"https://blog.netwrix.com/2024/01/09/azure-storage/\",\"d\":\"blog.netwrix.com/2024/01/09/azure-storage/\",\"da\":\"translations\",\"e\":\"2024-01-09T00:00:00.0000000\",\"h\":0,\"i\":\"blog.netwrix.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Understanding Six Popular Azure Storage Types and Their Use Cases\",\"u\":\"https://blog.netwrix.com/2024/01/09/azure-storage/\"},{\"a\":\"January 10, 2024 at 8:10 AM PST. Walmart Inc. opened up access to a generative artificial intelligence tool that allows shoppers to search for products by specific use cases, rather than look up ...\",\"ae\":null,\"c\":\"https://www.bloomberg.com/news/articles/2024-01-09/walmart-wmt-expands-rollout-of-generative-ai-shopping-search-tech\",\"d\":\"www.bloomberg.com/news/articles/2024-01-09/walmart-wmt-expands-rollout-of-generative-ai-shopping-search-tech\",\"da\":\"news,translations\",\"e\":\"2024-01-09T16:10:00.0000000\",\"h\":0,\"i\":\"www.bloomberg.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Walmart Expands Rollout of Generative AI Shopping Search, Tech\",\"u\":\"https://www.bloomberg.com/news/articles/2024-01-09/walmart-wmt-expands-rollout-of-generative-ai-shopping-search-tech\"},{\"a\":\"The purpose of this advisory is to alert healthcare providers and facilities to substantial increases in cases of influenza and COVID-19, at least partially driven by an emerging SARS-CoV-2 variant, and to recommend that healthcare and residential facilities advocate strongly for the use of masks within their facility to prevent transmission\",\"ae\":null,\"c\":\"https://health.ny.gov/press/releases/2024/docs/2024-01-08_masking_advisory.pdf\",\"d\":\"health.ny.gov/press/releases/2024/docs/2024-01-08_masking_advisory.pdf\",\"da\":\"translations\",\"e\":\"2024-01-08T00:00:00.0000000\",\"h\":0,\"i\":\"health.ny.gov\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"PDF Health Advisory: Nys Department of Health Recommends Masking in ...\",\"u\":\"https://health.ny.gov/press/releases/2024/docs/2024-01-08_masking_advisory.pdf\"},{\"a\":\"2:29. The ex- Lloyds Banking Group Plc manager who won his unfair dismissal case over his use of a racist slur was awarded more than \\u00a3450,000 ($572,560) from an employment tribunal that said he ...\",\"ae\":null,\"c\":\"https://www.bloomberg.com/news/articles/2024-01-10/lloyds-bank-manager-awarded-450-000-after-winning-case-over-racist-slur\",\"d\":\"www.bloomberg.com/news/articles/2024-01-10/lloyds-bank-manager-awarded-450-000-after-winning-case-over-racist-slur\",\"da\":\"news,translations\",\"e\":\"2024-01-10T13:25:00.0000000\",\"h\":0,\"i\":\"www.bloomberg.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Lloyds Bank Manager Awarded \\u00a3450,000 After Winning Case Over Racist ...\",\"u\":\"https://www.bloomberg.com/news/articles/2024-01-10/lloyds-bank-manager-awarded-450-000-after-winning-case-over-racist-slur\"},{\"a\":\"The ICJ case adds to international pressure on Israel to scale back or end its war against Hamas, which health officials in Gaza say has killed more than 23,000 people \\u2014 many of them women and ...\",\"ae\":null,\"c\":\"https://www.washingtonpost.com/world/2024/01/10/south-africa-israel-icj-genocide-case/\",\"d\":\"www.washingtonpost.com/world/2024/01/10/south-africa-israel-icj-genocide-case/\",\"da\":\"news,translations\",\"e\":\"2024-01-10T22:24:00.0000000\",\"h\":0,\"i\":\"www.washingtonpost.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"What to know about the genocide case against Israel at the ICJ\",\"u\":\"https://www.washingtonpost.com/world/2024/01/10/south-africa-israel-icj-genocide-case/\"},{\"n\":\"/d.js?q=MetaGPT%20use%20cases&kl=wt-wt&l=wt-wt&p=&s=29&ex=-1&ct=US&sp=0&vqd=4-206455801954364851330794682843954609879\"}]);DDG.duckbar.load('images');DDG.duckbar.load('news');DDG.duckbar.load('videos');DDG.duckbar.loadModule('related_searches');if (DDG.pageLayout) DDG.pageLayout.initialize({\"mainline\":{\"items\":[[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"]]}}, { start: 0 });DDG.deep.emit(\"load:completed\");", + "curl-cffi-POST-https://duckduckgo.com-{\"data\": {\"q\": \"The roadmap of MetaGPT\"}}": "The roadmap of MetaGPT at DuckDuckGo
", + "curl-cffi-GET-https://links.duckduckgo.com/d.js-{\"params\": {\"bing_market\": \"wt-WT\", \"df\": null, \"ex\": \"-1\", \"kl\": \"wt-wt\", \"l\": \"wt-wt\", \"q\": \"The roadmap of MetaGPT\", \"s\": \"0\", \"sp\": \"0\", \"vqd\": \"4-25941261128344049410840372626152530092\"}}": "if (DDG.deep && DDG.deep.setUpstream) DDG.deep.setUpstream(\"bingv7aa\");DDG.deep.bn={'ivc':1};if (DDG.pageLayout) DDG.pageLayout.load('a',[], {\"page_load_url\":\"https://duckduckgo.com/y.js?iurl=%7B2%7DIG%3DF478763C5197469DB7B1E366C8182CF2%26CID%3D192D84C7D0B167C5000690C1D1D466C6%26Type%3DEvent.CPT%26DATA%3D0\"});DDG.deep.signalSummary = \"\";DDG.inject('DDG.Data.languages.resultLanguages', {\"en\":[\"https://github.com/geekan/MetaGPT/blob/main/docs/ROADMAP.md\",\"https://github.com/geekan/MetaGPT\",\"https://www.almabetter.com/bytes/articles/metagpt\",\"https://lablab.ai/blog/this-week-in-ai-exploring-the-latest-from-metagpt-and-gpt4-and-more\",\"https://arxiv.org/abs/2308.00352\",\"https://github.com/geekan/MetaGPT/blob/main/README.md\",\"https://github.com/PlaiD3/MetaGPT/blob/main/README.md\",\"https://www.unite.ai/metagpt-complete-guide-to-the-best-ai-agent-available-right-now/\",\"https://www.linkedin.com/pulse/metagpt-important-conceptual-advance-multi-agent-systems-brad-edwards\",\"https://www.straight.com/guides/software/a-complete-guide-to-metagpt-the-best-ai-agent-available-now/\",\"https://generativeai.pub/analyzing-an-exciting-generative-ai-research-called-metagpt-2106385312db\",\"https://docs.deepwisdom.ai/main/en/guide/get_started/introduction.html\",\"https://geekflare.com/metagpt-multi-agent-framework/\",\"https://www.marktechpost.com/2023/08/09/meet-metagpt-the-open-source-ai-framework-that-transforms-gpts-into-engineers-architects-and-managers/\",\"https://www.louisbouchard.ai/metagpt/\",\"https://pypi.org/project/metagpt/\",\"https://www.reddit.com/r/ChatGPT/comments/14qhn00/metagpt_the_roadmap_has_been_released_come_and/\",\"https://medium.com/aimonks/metagpt-a-framework-for-multi-agent-meta-programming-6c79f2eafb8e\",\"https://xthemadgenius.medium.com/how-to-use-metagpt-to-operate-as-a-full-engineering-team-c0f6e53c1dc3\",\"https://medium.com/@smraiyyan/metagpt-unleashed-crafting-your-virtual-software-company-from-scratch-6ea60cd70da1\",\"https://github.com/Ditto190/MetaGPT/blob/main/docs/ROADMAP.md\"],\"zh-CN\":[\"https://zhuanlan.zhihu.com/p/677608276\"]});DDG.deep.pageLayoutSummary = \"w1i1w4v1w18\";DDG.inject('DDG.Data.languages.adLanguages', {});if (DDG.pageLayout) DDG.pageLayout.load('d',[{\"a\":\"MetaGPT is an open source framework for building innovative AI powered applications with minimal coding. It leverages the power of GPT-3 and other models to generate various software artifacts from natural language inputs. Learn how to use MetaGPT and contribute to its development in this roadmap.\",\"ae\":null,\"b\":\"gh\\tGitHub\\tgithub.com\",\"c\":\"https://github.com/geekan/MetaGPT/blob/main/docs/ROADMAP.md\",\"d\":\"github.com/geekan/MetaGPT/blob/main/docs/ROADMAP.md\",\"da\":\"\",\"h\":0,\"i\":\"github.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Roadmap - GitHub\",\"u\":\"https://github.com/geekan/MetaGPT/blob/main/docs/ROADMAP.md\"},{\"a\":\"MetaGPT: The Multi-Agent Framework Assign different roles to GPTs to form a collaborative software entity for complex tasks. MetaGPT takes a one line requirement as input and outputs user stories / competitive analysis / requirements / data structures / APIs / documents, etc.\",\"ae\":null,\"b\":\"gh\\tGitHub\\tgithub.com\",\"c\":\"https://github.com/geekan/MetaGPT\",\"d\":\"github.com/geekan/MetaGPT\",\"da\":\"\",\"h\":0,\"i\":\"github.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: The Multi-Agent Framework - GitHub\",\"u\":\"https://github.com/geekan/MetaGPT\"},{\"a\":\"Understanding MetaGPT MetaGPT, a concept originating from a research paper that received significant attention, represents a leap forward in Artificial Intelligence, specifically in multi-agent collaboration using large language models (LLMs).\",\"ae\":null,\"c\":\"https://www.almabetter.com/bytes/articles/metagpt\",\"d\":\"www.almabetter.com/bytes/articles/metagpt\",\"da\":\"\",\"e\":\"2023-08-28T00:00:00.0000000\",\"h\":0,\"i\":\"www.almabetter.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: The Future of Multi-Agent Collaboration in AI\",\"u\":\"https://www.almabetter.com/bytes/articles/metagpt\"},{\"a\":\"MetaGPT is a groundbreaking multi-agent framework that is transforming the way software development is approached. By taking a single line of requirement as input, MetaGPT outputs a comprehensive array of development components, including user stories, competitive analysis, requirements, data structures, APIs, and documents.\",\"ae\":null,\"c\":\"https://lablab.ai/blog/this-week-in-ai-exploring-the-latest-from-metagpt-and-gpt4-and-more\",\"d\":\"lablab.ai/blog/this-week-in-ai-exploring-the-latest-from-metagpt-and-gpt4-and-more\",\"da\":\"\",\"e\":\"2023-08-11T00:00:00.0000000\",\"h\":0,\"i\":\"lablab.ai\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"This Week in AI: Exploring the Latest from MetaGPT and GPT-4 and more..\",\"u\":\"https://lablab.ai/blog/this-week-in-ai-exploring-the-latest-from-metagpt-and-gpt4-and-more\"},{\"a\":\"MetaGPT utilizes an assembly line paradigm to assign diverse roles to various agents, efficiently breaking down complex tasks into subtasks involving many agents working together. On collaborative software engineering benchmarks, MetaGPT generates more coherent solutions than previous chat-based multi-agent systems.\",\"ae\":null,\"b\":\"arx\\tarXiv.org\\tarxiv.org\",\"c\":\"https://arxiv.org/abs/2308.00352\",\"d\":\"arxiv.org/abs/2308.00352\",\"da\":\"translations\",\"e\":\"2023-08-01T00:00:00.0000000\",\"h\":0,\"i\":\"arxiv.org\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: Meta Programming for A Multi-Agent Collaborative Framework\",\"u\":\"https://arxiv.org/abs/2308.00352\"},{\"a\":\"MetaGPT takes a one line requirement as input and outputs user stories / competitive analysis / requirements / data structures / APIs / documents, etc. \n Internally, MetaGPT includes product managers / architects / project managers / engineers.\",\"ae\":null,\"b\":\"gh\\tGitHub\\tgithub.com\",\"c\":\"https://github.com/geekan/MetaGPT/blob/main/README.md\",\"d\":\"github.com/geekan/MetaGPT/blob/main/README.md\",\"da\":\"\",\"h\":0,\"i\":\"github.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: The Multi-Agent Framework - GitHub\",\"u\":\"https://github.com/geekan/MetaGPT/blob/main/README.md\"},{\"a\":\"arXiv.org\",\"ae\":null,\"b\":\"arx\\tarXiv.org\\tarxiv.org\",\"c\":\"https://arxiv.org/pdf/2308.00352.pdf\",\"d\":\"arxiv.org/pdf/2308.00352.pdf\",\"da\":\"translations\",\"h\":0,\"i\":\"arxiv.org\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"PDF arXiv.org\",\"u\":\"https://arxiv.org/pdf/2308.00352.pdf\"},{\"a\":\"MetaGPT takes a one line requirement as input and outputs user stories / competitive analysis / requirements / data structures / APIs / documents, etc. \n; Internally, MetaGPT includes product managers / architects / project managers / engineers. It provides the entire process of a software company along with carefully orchestrated SOPs.\n \n\",\"ae\":null,\"b\":\"gh\\tGitHub\\tgithub.com\",\"c\":\"https://github.com/PlaiD3/MetaGPT/blob/main/README.md\",\"d\":\"github.com/PlaiD3/MetaGPT/blob/main/README.md\",\"da\":\"\",\"h\":0,\"i\":\"github.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: Multi-Agent Meta Programming Framework - GitHub\",\"u\":\"https://github.com/PlaiD3/MetaGPT/blob/main/README.md\"},{\"a\":\"MetaGPT's architecture is divided into two layers: the Foundational Components Layer and the Collaboration Layer. Foundational Components Layer: This layer focuses on individual agent operations and facilitates system-wide information exchange. It introduces core building blocks such as Environment, Memory, Roles, Actions, and Tools.\",\"ae\":null,\"c\":\"https://www.unite.ai/metagpt-complete-guide-to-the-best-ai-agent-available-right-now/\",\"d\":\"www.unite.ai/metagpt-complete-guide-to-the-best-ai-agent-available-right-now/\",\"da\":\"\",\"e\":\"2023-09-11T00:00:00.0000000\",\"h\":0,\"i\":\"www.unite.ai\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: Complete Guide to the Best AI Agent Available Right Now\",\"u\":\"https://www.unite.ai/metagpt-complete-guide-to-the-best-ai-agent-available-right-now/\"},{\"a\":\"Published Aug 6, 2023 + Follow Recent advances in large language models (LLMs) have opened up new opportunities for developing intelligent software agents capable of replicating human-level...\",\"ae\":null,\"b\":\"li\\tLinkedIn\\twww.linkedin.com\",\"c\":\"https://www.linkedin.com/pulse/metagpt-important-conceptual-advance-multi-agent-systems-brad-edwards\",\"d\":\"www.linkedin.com/pulse/metagpt-important-conceptual-advance-multi-agent-systems-brad-edwards\",\"da\":\"\",\"e\":\"2023-08-06T00:00:00.0000000\",\"h\":0,\"i\":\"www.linkedin.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: Important Conceptual Advance in Multi-Agent Systems - LinkedIn\",\"u\":\"https://www.linkedin.com/pulse/metagpt-important-conceptual-advance-multi-agent-systems-brad-edwards\"},{\"a\":\"1. Enhanced Operational Efficiency. MetaGPT is designed to store, retrieve, and share information at varying levels, reducing redundancy and enhancing operational efficiency. This means that ...\",\"ae\":null,\"c\":\"https://www.straight.com/guides/software/a-complete-guide-to-metagpt-the-best-ai-agent-available-now/\",\"d\":\"www.straight.com/guides/software/a-complete-guide-to-metagpt-the-best-ai-agent-available-now/\",\"da\":\"\",\"e\":\"2023-12-13T00:00:00.0000000\",\"h\":0,\"i\":\"www.straight.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"A Complete Guide to MetaGPT: The Best AI Agent Available Now\",\"u\":\"https://www.straight.com/guides/software/a-complete-guide-to-metagpt-the-best-ai-agent-available-now/\"},{\"a\":\"The MetaGPT approach showcases its ability to decompose highlevel tasks into detailed actionable components handled by distinct roles (ProductManager, Architect, ProjectManager, Engineer, QA Engineer), thereby facilitating role-specific expertise and coordination. This methodology mirrors human software development teams.\",\"ae\":null,\"c\":\"https://generativeai.pub/analyzing-an-exciting-generative-ai-research-called-metagpt-2106385312db\",\"d\":\"generativeai.pub/analyzing-an-exciting-generative-ai-research-called-metagpt-2106385312db\",\"da\":\"translations\",\"e\":\"2023-08-14T00:00:00.0000000\",\"h\":0,\"i\":\"generativeai.pub\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Analyzing an exciting Generative AI research called MetaGPT.\",\"u\":\"https://generativeai.pub/analyzing-an-exciting-generative-ai-research-called-metagpt-2106385312db\"},{\"a\":\"Internally, MetaGPT includes product managers / architects / project managers / engineers. It provides the entire process of a software company along with carefully orchestrated SOPs. Code = SOP (Team) is the core philosophy. We materialize SOP and apply it to teams composed of LLMs. Software Company Multi-Role Schematic.\",\"ae\":null,\"c\":\"https://docs.deepwisdom.ai/main/en/guide/get_started/introduction.html\",\"d\":\"docs.deepwisdom.ai/main/en/guide/get_started/introduction.html\",\"da\":\"\",\"h\":0,\"i\":\"docs.deepwisdom.ai\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: The Multi-Agent Framework | MetaGPT\",\"u\":\"https://docs.deepwisdom.ai/main/en/guide/get_started/introduction.html\"},{\"a\":\"MetaGPT is a multi-agent framework that takes one-line inputs to produce APIs, user stories, data structures, competitive analysis, and more. GPT is the short form for Generative Pretrained Transformers. MetaGPT framework can behave as a product manager, software engineer, and architect. This framework can act as an entire software company with ...\",\"ae\":null,\"c\":\"https://geekflare.com/metagpt-multi-agent-framework/\",\"d\":\"geekflare.com/metagpt-multi-agent-framework/\",\"da\":\"\",\"e\":\"2023-09-18T00:00:00.0000000\",\"h\":0,\"i\":\"geekflare.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: Is This the Best Multi-Agent Framework Yet? - Geekflare\",\"u\":\"https://geekflare.com/metagpt-multi-agent-framework/\"},{\"a\":\"MetaGPT manages far more software complexity than GPT-3.5 or other open-source frameworks like AutoGPT and AgentVerse, measured by lines of produced code. Additionally, MetaGPT generates high-quality requirement papers, design artifacts, flowcharts, and interface specifications throughout the automated end-to-end process. ...\",\"ae\":null,\"c\":\"https://www.marktechpost.com/2023/08/09/meet-metagpt-the-open-source-ai-framework-that-transforms-gpts-into-engineers-architects-and-managers/\",\"d\":\"www.marktechpost.com/2023/08/09/meet-metagpt-the-open-source-ai-framework-that-transforms-gpts-into-engineers-architects-and-managers/\",\"da\":\"translations\",\"e\":\"2023-08-09T00:00:00.0000000\",\"h\":0,\"i\":\"www.marktechpost.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Meet MetaGPT: The Open-Source AI Framework That Transforms GPTs into ...\",\"u\":\"https://www.marktechpost.com/2023/08/09/meet-metagpt-the-open-source-ai-framework-that-transforms-gpts-into-engineers-architects-and-managers/\"},{\"a\":\"MetaGPT is a new paper and open-source work that is making a lot of noise on GitHub! The researchers developed a new framework for combining or chaining large language models and mitigating hallucination risks by integrating human standardized operating procedures (SOPs) into the chaining process. This new design scheme allows the system to ...\",\"ae\":null,\"c\":\"https://www.louisbouchard.ai/metagpt/\",\"d\":\"www.louisbouchard.ai/metagpt/\",\"da\":\"\",\"e\":\"2023-08-27T00:00:00.0000000\",\"h\":0,\"i\":\"www.louisbouchard.ai\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Mitigating AI Hallucinations: Exploring MetaGPT's Collaborative Framework\",\"u\":\"https://www.louisbouchard.ai/metagpt/\"},{\"a\":\"MetaGPT takes a one line requirement as input and outputs user stories / competitive analysis / requirements / data structures / APIs / documents, etc. Internally, MetaGPT includes product managers / architects / project managers / engineers. It provides the entire process of a software company along with carefully orchestrated SOPs.\",\"ae\":null,\"c\":\"https://pypi.org/project/metagpt/\",\"d\":\"pypi.org/project/metagpt/\",\"da\":\"\",\"e\":\"2024-01-10T00:00:00.0000000\",\"h\":0,\"i\":\"pypi.org\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"metagpt \\u00b7 PyPI\",\"u\":\"https://pypi.org/project/metagpt/\"},{\"a\":\"Hey u/embessoaat, if your post is a ChatGPT conversation screenshot, please reply with the conversation link or prompt. Thanks! We have a public discord server.There's a free Chatgpt bot, Open Assistant bot (Open-source model), AI image generator bot, Perplexity AI bot, \\ud83e\\udd16 GPT-4 bot (Now with Visual capabilities (cloud vision)!) and channel for latest prompts.\",\"ae\":null,\"b\":\"r\\tReddit\\twww.reddit.com\",\"c\":\"https://www.reddit.com/r/ChatGPT/comments/14qhn00/metagpt_the_roadmap_has_been_released_come_and/\",\"d\":\"www.reddit.com/r/ChatGPT/comments/14qhn00/metagpt_the_roadmap_has_been_released_come_and/\",\"da\":\"translations\",\"h\":0,\"i\":\"www.reddit.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: The roadmap has been released! Come and take a look ... - Reddit\",\"u\":\"https://www.reddit.com/r/ChatGPT/comments/14qhn00/metagpt_the_roadmap_has_been_released_come_and/\"},{\"a\":\"Business: MetaGPT can be used to create and execute business programs that can optimize or automate various processes, such as scheduling, planning, budgeting, marketing, etc. MetaGPT can also...\",\"ae\":null,\"c\":\"https://medium.com/aimonks/metagpt-a-framework-for-multi-agent-meta-programming-6c79f2eafb8e\",\"d\":\"medium.com/aimonks/metagpt-a-framework-for-multi-agent-meta-programming-6c79f2eafb8e\",\"da\":\"\",\"e\":\"2023-08-03T00:00:00.0000000\",\"h\":0,\"i\":\"medium.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: A Framework for Multi-Agent Meta Programming\",\"u\":\"https://medium.com/aimonks/metagpt-a-framework-for-multi-agent-meta-programming-6c79f2eafb8e\"},{\"a\":\"MetaGPT is an innovative solution that allows us to assign different roles to GPTs, forging a collaborative software force. In this guide, we'll explore how to harness the power of MetaGPT for...\",\"ae\":null,\"c\":\"https://xthemadgenius.medium.com/how-to-use-metagpt-to-operate-as-a-full-engineering-team-c0f6e53c1dc3\",\"d\":\"xthemadgenius.medium.com/how-to-use-metagpt-to-operate-as-a-full-engineering-team-c0f6e53c1dc3\",\"da\":\"translations\",\"e\":\"2023-08-12T00:00:00.0000000\",\"h\":0,\"i\":\"xthemadgenius.medium.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"How to use MetaGPT to Operate as a full Engineering Team\",\"u\":\"https://xthemadgenius.medium.com/how-to-use-metagpt-to-operate-as-a-full-engineering-team-c0f6e53c1dc3\"},{\"a\":\"MetaGPT, available on Github (crossed 13,000 stars), aims to change the way we make software.This exciting tool can take a single line of what you want to do and turn it into many things like user ...\",\"ae\":null,\"c\":\"https://medium.com/@smraiyyan/metagpt-unleashed-crafting-your-virtual-software-company-from-scratch-6ea60cd70da1\",\"d\":\"medium.com/@smraiyyan/metagpt-unleashed-crafting-your-virtual-software-company-from-scratch-6ea60cd70da1\",\"da\":\"translations\",\"e\":\"2023-08-07T00:00:00.0000000\",\"h\":0,\"i\":\"medium.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT Lets You Create Your Own Virtual Software Company from ... - Medium\",\"u\":\"https://medium.com/@smraiyyan/metagpt-unleashed-crafting-your-virtual-software-company-from-scratch-6ea60cd70da1\"},{\"a\":\"\\u57282023\\u5e7412\\u670819\\u65e5\\u65f6\\uff0c\\u542c\\u4e86\\u6797\\u4e49\\u7ae0\\u8001\\u5e08\\u5173\\u4e8e"\\u57fa\\u4e8eMetaGPT\\u8fdb\\u884c\\u667a\\u80fd\\u4f53\\u5f00\\u53d1"\\u7684\\u8bb2\\u5ea7\\uff1a \\u89c9\\u5f97\\u65b0\\u5947\\u6709\\u8da3\\uff0c\\u5982\\u679c\\u80fd\\u8fd9\\u6837\\u5728\\u5de5\\u4f5c\\u751f\\u6d3b\\u4e2d\\u5b8c\\u6210\\u81ea\\u5df1\\u7684\\u4efb\\u52a1\\uff0c\\u90a3\\u7b80\\u76f4\\u662f\\u4e8b\\u534a\\u529f\\u500d\\u3002\\u4e8e\\u662f\\u8fd9\\u4e24\\u5929\\u53c8\\u5b66\\u4e60\\u4e86\\u300aMetaGPT\\u667a\\u80fd\\u4f53\\u5f00\\u53d1\\u5165\\u95e8\\u300b\\u6559\\u2026\",\"ae\":null,\"c\":\"https://zhuanlan.zhihu.com/p/677608276\",\"d\":\"zhuanlan.zhihu.com/p/677608276\",\"da\":\"translations\",\"e\":\"2024-01-12T00:00:00.0000000\",\"h\":0,\"i\":\"zhuanlan.zhihu.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"\\u5b66\\u4e60\\u7b14\\u8bb0-\\u300aMetaGPT\\u667a\\u80fd\\u4f53\\u5f00\\u53d1\\u5165\\u95e8\\u300b\\u6559\\u7a0b - \\u77e5\\u4e4e\",\"u\":\"https://zhuanlan.zhihu.com/p/677608276\"},{\"a\":\"Roadmap \n Long-term Objective \n. Enable MetaGPT to self-evolve, accomplishing self-training, fine-tuning, optimization, utilization, and updates. \n Short-term Objective \n \n; Become the multi-agent framework with the highest ROI. \n; Support fully automatic implementation of medium-sized projects (around 2000 lines of code). \n\",\"ae\":null,\"b\":\"gh\\tGitHub\\tgithub.com\",\"c\":\"https://github.com/Ditto190/MetaGPT/blob/main/docs/ROADMAP.md\",\"d\":\"github.com/Ditto190/MetaGPT/blob/main/docs/ROADMAP.md\",\"da\":\"translations\",\"h\":0,\"i\":\"github.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Roadmap - GitHub\",\"u\":\"https://github.com/Ditto190/MetaGPT/blob/main/docs/ROADMAP.md\"},{\"n\":\"/d.js?q=The%20roadmap%20of%20MetaGPT&kl=wt-wt&l=wt-wt&p=&s=23&ex=-1&ct=US&sp=0&vqd=4-25941261128344049410840372626152530092\"}]);DDG.duckbar.load('images', {\"ads\":[],\"query\":\"The roadmap of MetaGPT\",\"queryEncoded\":\"The%20roadmap%20of%20MetaGPT\",\"response_type\":\"places\",\"results\":[{\"height\":720,\"image\":\"https://i.ytimg.com/vi/8cxLdYtwx4M/maxresdefault.jpg\",\"image_token\":\"25476bee58d891e0b100edecfc9022b60c5c458e8d078f04908c2fe341449aad\",\"source\":\"Bing\",\"thumbnail\":\"https://tse1.mm.bing.net/th?id=OIP.Sbc4rHZKxrQ7JJKFfx4pggHaEK&pid=Api\",\"thumbnail_token\":\"b8ca3fc5d5109341b5fa2a52479d696225049ce7a678a01730e9440c44454ef0\",\"title\":\"How to use metagpt || What is MetaGPT || Meta AI Tool - YouTube\",\"url\":\"https://www.youtube.com/watch?v=8cxLdYtwx4M\",\"width\":1280},{\"height\":1699,\"image\":\"https://cdn-cashy-static-assets.lucidchart.com/lucidspark/marketing/blog/2020Q4/product-roadmap/product-roadmap-example.png\",\"image_token\":\"45bef86b333d99b8975de0658ef5da261356dcfea369bc8c529fe98c02e9508f\",\"source\":\"Bing\",\"thumbnail\":\"https://tse1.mm.bing.net/th?id=OIP.zBKuJobHBxYMSdOL3b7oOAHaG1&pid=Api\",\"thumbnail_token\":\"4480400578522cbc135bd7ce753dee35cbbb49e2709c4e5775b3f6334842c409\",\"title\":\"How to Build a Product Roadmap | Lucidspark\",\"url\":\"https://lucidspark.com/blog/how-to-build-a-product-roadmap\",\"width\":1839},{\"height\":688,\"image\":\"https://fanpu.io/assets/img/summaries/metagpt-overview.webp\",\"image_token\":\"7b40dc9efbd841a6810483fe46865f2a8fbc6def3902f385bb02c8199488d8d1\",\"source\":\"Bing\",\"thumbnail\":\"https://tse1.mm.bing.net/th?id=OIP.jEHUvpk8FOPdH12J79rFXQHaFR&pid=Api\",\"thumbnail_token\":\"8490d5575bc2435a2ae2adde324f594a1fd905d74bc8c8849565a99d1cf8e658\",\"title\":\"MetaGPT: Meta Programming for Multi-Agent Collaborative Framework | Fan ...\",\"url\":\"https://fanpu.io/summaries/2023-08-11-metagpt-meta-programming-for-multi-agent-collaborative-framework/\",\"width\":966},{\"height\":920,\"image\":\"https://roadmunk.com/guides/content/images/2020/09/Timeline-Roadmap-1.png\",\"image_token\":\"96f5e93a0d50706ce051a1024537055b9f1f1f7e2db4da2f158f13b98d56cd9d\",\"source\":\"Bing\",\"thumbnail\":\"https://tse1.mm.bing.net/th?id=OIP.hchaQb3VwheAODLNSOIAdQHaEe&pid=Api\",\"thumbnail_token\":\"28d9e72b560992d38447bd1a9d050d154d32f1eb0bf8690fe33d04c56ecdd618\",\"title\":\"What is a roadmap? The guide to roadmapping - Roadmunk\",\"url\":\"https://roadmunk.com/guides/roadmap-definition/\",\"width\":1520},{\"height\":920,\"image\":\"https://lh3.googleusercontent.com/H1-gvtbro-_27q3NnYbGO37i7a_xTqVRQ0ZvqJtRJBkfRjuPMg-11djNlPDLFJpjY8hKCzKwIlKiy040Us5unlwBPLUyqEMHIOUm7qqcEobhB-Uqsf2qHEyzEQywl9dkdErjkkrZ\",\"image_token\":\"8912b09257d9b4cef5ed84acd84b034f77aca601cf6d10094d87836b7b2bf7b5\",\"source\":\"Bing\",\"thumbnail\":\"https://tse1.mm.bing.net/th?id=OIP.qjGOCXm2nvDzRX8JRwxTagHaEe&pid=Api\",\"thumbnail_token\":\"7b3a7ba7cbcec30ecf21e2ac3f06daf1f8d55a9e3a7b4c657b9e1f1656c21f01\",\"title\":\"What is a product roadmap and how to create it? - Weje.io\",\"url\":\"https://weje.io/blog/product-roadmap\",\"width\":1520},{\"height\":3500,\"image\":\"https://static.vecteezy.com/system/resources/previews/000/680/342/original/infographic-business-roadmap-timeline.jpg\",\"image_token\":\"f78e25fbc66da7d380c8b378b3b51d2e3aabb98e01b39239b0184e8eff3f3b1d\",\"source\":\"Bing\",\"thumbnail\":\"https://tse1.mm.bing.net/th?id=OIP.1XTASxs0KABNJjUYwMRIWQHaFL&pid=Api\",\"thumbnail_token\":\"b41dcc02238e5f5150208de2a5f4d508fbecccc0040c24314e76107185be772a\",\"title\":\"Business Roadmap Vector Art, Icons, and Graphics for Free Download\",\"url\":\"https://www.vecteezy.com/free-vector/business-roadmap\",\"width\":5000},{\"height\":3871,\"image\":\"https://uniserveit.com/uploads/Building-A-Technology-Roadmap.jpg\",\"image_token\":\"2cd4a4a7699e095734ebd1278facc076305f4cc59854186bef8b98152a794f08\",\"source\":\"Bing\",\"thumbnail\":\"https://tse1.mm.bing.net/th?id=OIP.2ZnbiyLbkRcKHXL1_6u8JwHaE2&pid=Api\",\"thumbnail_token\":\"de73a3f195d39fb167707545a4761d3a15d0ef308a4b29e2e244202c52d76628\",\"title\":\"How To Build A Technology Roadmap | Uniserve IT Soltutions\",\"url\":\"https://uniserveit.com/blog/building-a-technology-roadmap\",\"width\":5903},{\"height\":940,\"image\":\"https://media.nngroup.com/media/editor/2020/10/28/screen-shot-2020-10-28-at-12537-pm.png\",\"image_token\":\"e90c0ef520ee067896af8acc0653d9ea2082d41fc2d82075c551a61de427ee42\",\"source\":\"Bing\",\"thumbnail\":\"https://tse1.mm.bing.net/th?id=OIP.RDV9bbgkuW_SJ28N8n_R9AHaDu&pid=Api\",\"thumbnail_token\":\"c242005da7b72897176ef634488b5025eb2d4925e6aae4aa6cec3ace673f62e3\",\"title\":\"The 6 steps to roadmapping (2022)\",\"url\":\"https://edduls.pics/article/the-6-steps-to-roadmapping\",\"width\":1872},{\"height\":720,\"image\":\"https://slidevilla.com/wp-content/uploads/2019/02/b7548f226f6de734c5dfa5f141a5918d-6.jpg\",\"image_token\":\"c6ac89b3b79b7e7d2a8f1246dbe131f95256f0ee2de78c478b92733ef40e63e1\",\"source\":\"Bing\",\"thumbnail\":\"https://tse1.mm.bing.net/th?id=OIP.PJUe_oduaD-FLmTMHHYp2wHaFj&pid=Api\",\"thumbnail_token\":\"e9193de0027c0317d3ea8ac3f6ffd3e30ec2043df987c29e4c002e9b2476681d\",\"title\":\"Roadmap with milestones powerpoint template - Slidevilla\",\"url\":\"https://slidevilla.com/shop/powerpoint-templates/roadmap-with-milestones/\",\"width\":960},{\"height\":2050,\"image\":\"https://www.jibility.com/wp-content/uploads/2021/09/digital-transformation-example-roadmap-detail.png\",\"image_token\":\"45a07b34c3b25676ca4f531482db5c858f6bbfd24ca0e593808aebfbb150acaa\",\"source\":\"Bing\",\"thumbnail\":\"https://tse1.mm.bing.net/th?id=OIP.sbKPggSDMYdKDZ5jbz14iwHaFJ&pid=Api\",\"thumbnail_token\":\"2a087acc29b5f757b8f7469d08f6c80701eb0393627932e93ecf7206714120c1\",\"title\":\"Strategic Roadmap Tool | Jibility | Professional Plan from $39\",\"url\":\"https://www.jibility.com/pricing/\",\"width\":2951},{\"height\":2095,\"image\":\"https://media.nngroup.com/media/articles/opengraph_images/6_Steps_Roadmapping_Social-Media-Posts_2020-38.png\",\"image_token\":\"855d0a05e27aee26c8f4ca738a99fd20f7b0efa43bfa177a22ce0717463e8a1b\",\"source\":\"Bing\",\"thumbnail\":\"https://tse1.mm.bing.net/th?id=OIP.bkZKyld4JdobfUy5xZaMzAHaD4&pid=Api\",\"thumbnail_token\":\"a0046653e2b02e1b76f8bc160d0c7734914e5cea3a37d5d61534943ccafb3f00\",\"title\":\"The 6 Steps to Roadmapping\",\"url\":\"https://www.nngroup.com/articles/roadmapping-steps/\",\"width\":4001},{\"height\":1440,\"image\":\"https://www.ciloart.com/files/free-process-roadmap-timeline-infographics-for-powerpoint-templates.jpg\",\"image_token\":\"f8a130afc6893bb6cec1a071b2f3fe6fec48bc8f15eb0efc0ad415a4e2c9d162\",\"source\":\"Bing\",\"thumbnail\":\"https://tse1.mm.bing.net/th?id=OIP.wDWVEEfr5zg9vz7HAnK0DQHaEK&pid=Api\",\"thumbnail_token\":\"fafeddafec61e36c6e39575442bfd415d85c909580b432618d4bc79efcc5b9b5\",\"title\":\"Roadmap ppt template free - plmnoble\",\"url\":\"https://plmnoble.weebly.com/blog/roadmap-ppt-template-free\",\"width\":2560},{\"height\":576,\"image\":\"https://cdn.infodiagram.com/c/a84123/team-roadmap-engineering-chart-development-innovation-technology.png\",\"image_token\":\"aaf2d11a998569ac9cebb8b0441fca9a1a5f9b88a902737cca46a41b2a3d5b1f\",\"source\":\"Bing\",\"thumbnail\":\"https://tse1.mm.bing.net/th?id=OIP.IBaC7bT304-c6nyTTJiijAHaEK&pid=Api\",\"thumbnail_token\":\"788dc194a239068cff396bf9d134d6dd3aa22adb27fa480345951cb142fc37ec\",\"title\":\"Technology Roadmap PPT Template\",\"url\":\"https://www.infodiagram.co.uk/slides/roadmap-technology-template/\",\"width\":1024},{\"height\":2048,\"image\":\"https://1.bp.blogspot.com/-Dv1SChkX87k/YD3c7mfegKI/AAAAAAAARSI/8thS6TtRC30DiAwzBXXfKw1IwgLp695JQCLcBGAsYHQ/s2048/Wed%2BRoadmap.jpeg\",\"image_token\":\"9744a089f465662e7961b05bbf9859438a69af2f7d7190af5059835b9c3001d6\",\"source\":\"Bing\",\"thumbnail\":\"https://tse1.mm.bing.net/th?id=OIP.xTQzIRZCy3BS7DuA2YWXVgHaLG&pid=Api\",\"thumbnail_token\":\"d3f95531ab49a8166b2eb7b49aafe8b09b08ecb4224151456a63fd892ae077f7\",\"title\":\"Learn Web Development as an absolute Beginner Roadmap & What Skills you ...\",\"url\":\"https://codewithwastik.blogspot.com/2021/03/learn-web-development-as-absolute.html\",\"width\":1367},{\"height\":1440,\"image\":\"https://www.itce.com/wp-content/uploads/2018/11/SAFe-Implementation-Roadmap-ITCE-1920x1440.png\",\"image_token\":\"922478c3965e75a6f91ba2aec69832a2a725feffed976e0422377ce7e1c61146\",\"source\":\"Bing\",\"thumbnail\":\"https://tse1.mm.bing.net/th?id=OIP.Mkqd375BYhPyxasV59LLPwHaFj&pid=Api\",\"thumbnail_token\":\"2319f3ca85bae584140ce010bacecfa89823bc3ac15760fc2bc620eaa4c09492\",\"title\":\"SAFe Roadmap Implementation - ITCE\",\"url\":\"https://www.itce.com/services/safe-implementation-roadmap/\",\"width\":1920},{\"height\":1214,\"image\":\"https://graphicpanda.net/wp-content/uploads/2019/12/09.jpg\",\"image_token\":\"8c3fec9753bddf54dbbc4334adcb7c3845ebc2c9ac9d9919a625b4e81b931227\",\"source\":\"Bing\",\"thumbnail\":\"https://tse1.mm.bing.net/th?id=OIP.aer6Fq6Foz4Ugx0PGAUX1wHaE8&pid=Api\",\"thumbnail_token\":\"5cda767e92851048094f8b0eb9e5782c590d178766a35e12f102bdaf12eebb55\",\"title\":\"Top 48 Best Roadmap Infographics of 2019\",\"url\":\"https://graphicpanda.net/top-48-best-roadmap-infographics-of-2019/\",\"width\":1820},{\"height\":858,\"image\":\"https://47billion.com/wp-content/uploads/2022/02/Roadmap-for-Transforming-into-a-Data-Driven-Organization-e1645684780855.png\",\"image_token\":\"89aa4e1ff6bbf4325203cf48edcd389e876ad5b4aece7c79c127206fd7af53e3\",\"source\":\"Bing\",\"thumbnail\":\"https://tse1.mm.bing.net/th?id=OIP.8_44ACp4csLk-5DvQxJ1WgHaF4&pid=Api\",\"thumbnail_token\":\"e34f7de48ebea075b768b643bdca97ca0f5780511cbd5d512360baf9f61e5a74\",\"title\":\"Roadmap for Transforming into a Data-Driven Organization - 47billion.com\",\"url\":\"https://47billion.com/blog/roadmap-for-transforming-into-a-data-driven-organization/\",\"width\":1080},{\"height\":1656,\"image\":\"https://business-docs.co.uk/wp-content/uploads/2021/06/BDUK43StrategyRoadmapTemplatePowerpoint16x90501.png\",\"image_token\":\"4c7ed30a8f7f563a9a9c556ff684737bd5c57e4f2fa9643543a0961a6ea81748\",\"source\":\"Bing\",\"thumbnail\":\"https://tse1.mm.bing.net/th?id=OIP.9SJg6AkiAlFFaiSaR_wt-AHaEQ&pid=Api\",\"thumbnail_token\":\"1aa46a29b71fb20bce0482e7d652e25fd9b0068d27c153dc8934c0cdabe8be87\",\"title\":\"Strategy Roadmap Template PowerPoint - Present your strategic plans!\",\"url\":\"https://business-docs.co.uk/downloads/strategy-roadmap-template-powerpoint/\",\"width\":2880},{\"height\":1163,\"image\":\"https://i.pinimg.com/originals/55/17/fb/5517fbb701b86448db0e2027c3143d24.png\",\"image_token\":\"26a8a386dc179392ce51475382e003749b26eab3020410abee2451f837227e1e\",\"source\":\"Bing\",\"thumbnail\":\"https://tse1.mm.bing.net/th?id=OIP.m3dqCemXYTHDwnTNHr9CzgHaEj&pid=Api\",\"thumbnail_token\":\"be77ab5985646acf21a6268137d9c9108f60731a90cd9a36e116e30ba89ba445\",\"title\":\"Marketing Roadmap - Template and Examples | Roadmunk | Marketing ...\",\"url\":\"https://www.pinterest.de/pin/588704982531603300/\",\"width\":1893},{\"height\":1170,\"image\":\"https://d2slcw3kip6qmk.cloudfront.net/marketing/blog/2019Q4/technology-roadmap/it-roadmap-example.png\",\"image_token\":\"093a2a27e4cd0988223d8947a600db27208ce6c77f522eaca579c3eddab2d6e7\",\"source\":\"Bing\",\"thumbnail\":\"https://tse1.mm.bing.net/th?id=OIP.WRAD5IO2lrB7GGe7fLSJwgHaFN&pid=Api\",\"thumbnail_token\":\"910b3be01ddda5ea65a713080fb458385fb72d8911f434b59d08c18cf7ad5d38\",\"title\":\"What Is a Product Roadmap and How to Create One? | LaunchPad Lab\",\"url\":\"https://launchpadlab.com/blog/what-is-a-product-roadmap-and-why-you-need-one/\",\"width\":1662}],\"vqd\":{\"The%20roadmap%20of%20MetaGPT\":\"4-25941261128344049410840372626152530092\"}});DDG.duckbar.load('news');DDG.duckbar.load('videos', {\"ads\":[],\"query\":\"The roadmap of MetaGPT\",\"queryEncoded\":\"The%20roadmap%20of%20MetaGPT\",\"response_type\":\"places\",\"results\":[{\"content\":\"https://www.youtube.com/watch?v=uT75J_KG_aY\",\"description\":\"In this video, we review MetaGPT, a new project that aims to recreate an entire engineering organization using AI. MetaGPT is a CEO, Product Manager, Architect, Project Manager, Engineering, and QA. Write a simple prompt, and you get everything from the requirements to the PRDs to the code and tests. How To Find Me: Become a Patron \\ud83d\\udd25 - https ...\",\"duration\":\"6:36\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/uT75J_KG_aY?autoplay=1\",\"image_token\":\"57974159b78b309485721c0bce280219d9927e071e542a34777864767d6cb8d4\",\"images\":{\"large\":\"https://tse3.mm.bing.net/th?id=OVP.BbSKV8N1vyYv-3m8vyuCoQEsDh&pid=Api\",\"medium\":\"https://tse3.mm.bing.net/th?id=OVP.BbSKV8N1vyYv-3m8vyuCoQEsDh&pid=Api\",\"motion\":\"https://tse3.mm.bing.net/th?id=OM1.bsXxoMoJ9ZWQBw&pid=Api\",\"small\":\"https://tse3.mm.bing.net/th?id=OVP.BbSKV8N1vyYv-3m8vyuCoQEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-08-14T14:09:10.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":75408},\"title\":\"How To Install MetaGPT - Build A Startup With One Prompt!!\",\"uploader\":\"Matthew Berman\"},{\"content\":\"https://www.youtube.com/watch?v=YtxMderNrzU\",\"description\":\"Subscribe to my Newsletter (My AI updates and news clearly explained): https://louisbouchard.substack.com/ References: Read the full article: https://www.louisbouchard.ai/metagpt/ Hong et al., 2023: MetaGPT, https://arxiv.org/pdf/2308.00352.pdf Code: https://github.com/geekan/MetaGPT/blob/main/README.md Twitter: https://twitter.com/Whats_AI ...\",\"duration\":\"7:38\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/YtxMderNrzU?autoplay=1\",\"image_token\":\"2e0774ace2e34bbe23ece04e80b7bb2ee976fd8ef7f53001e8f8b137763561dc\",\"images\":{\"large\":\"https://tse2.mm.bing.net/th?id=OVP.HP81CZ34ap22GZZG2l024QHgFo&pid=Api\",\"medium\":\"https://tse2.mm.bing.net/th?id=OVP.HP81CZ34ap22GZZG2l024QHgFo&pid=Api\",\"motion\":\"https://tse2.mm.bing.net/th?id=OM2.xArTjo5bOxSBhg&pid=Api\",\"small\":\"https://tse2.mm.bing.net/th?id=OVP.HP81CZ34ap22GZZG2l024QHgFo&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-08-27T15:05:12.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":9594},\"title\":\"MetaGPT: Redefining Multi-Agent Collaboration for Complex Tasks\",\"uploader\":\"What's AI by Louis Bouchard\"},{\"content\":\"https://www.youtube.com/watch?v=nqZlTV_L6Ao\",\"description\":\"Welcome to our video review! \\ud83c\\udfa5 Dive into the world of MetaGPT, a revolutionary project that's redefining the boundaries of AI. \\ud83e\\udd16 Imagine having an entire engineering team - from CEO to QA - compacted into one AI system. Just input a prompt, and voila! You're handed everything from requirements, PRDs, to the actual code and tests. Let ...\",\"duration\":\"14:15\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/nqZlTV_L6Ao?autoplay=1\",\"image_token\":\"9d13b27084400da23ef8d8567bd6b5c8a3758d4129f2b28c3619c0e2e1ba8276\",\"images\":{\"large\":\"https://tse4.mm.bing.net/th?id=OVP.VBEy5DF-0BQshjEkqA9T0wHgFo&pid=Api\",\"medium\":\"https://tse4.mm.bing.net/th?id=OVP.VBEy5DF-0BQshjEkqA9T0wHgFo&pid=Api\",\"motion\":\"https://tse4.mm.bing.net/th?id=OM2.N7S3-wAngkj7VA&pid=Api\",\"small\":\"https://tse4.mm.bing.net/th?id=OVP.VBEy5DF-0BQshjEkqA9T0wHgFo&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-09-04T11:45:06.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":23248},\"title\":\"\\ud83d\\ude80 MetaGPT Setup: Launch a Startup with One \\u270d\\ufe0f Prompt!\",\"uploader\":\"Prompt Engineering\"},{\"content\":\"https://www.youtube.com/watch?v=12X4pupy4No\",\"description\":\"Simple as Plug n Play Visit www.MetaIDT.com\",\"duration\":\"1:00\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/12X4pupy4No?autoplay=1\",\"image_token\":\"3bf00a4528bef3408e273fd9403d2bce8428fc915c51ba5d0b09527abb7b47ce\",\"images\":{\"large\":\"https://tse2.mm.bing.net/th?id=OVP.gqgtA4cHlbRoVhYkFEkUuQEsDh&pid=Api\",\"medium\":\"https://tse2.mm.bing.net/th?id=OVP.gqgtA4cHlbRoVhYkFEkUuQEsDh&pid=Api\",\"motion\":\"https://tse2.mm.bing.net/th?id=OM1.dgsWk4LJc4VGrQ_1691420084&pid=Api\",\"small\":\"https://tse2.mm.bing.net/th?id=OVP.gqgtA4cHlbRoVhYkFEkUuQEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-07-17T09:43:32.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":470},\"title\":\"MetaGPT Key Drive Installation Guide\",\"uploader\":\"MetaGPT\"},{\"content\":\"https://www.youtube.com/watch?v=EgipcKPhqME\",\"description\":\"In this video I provide a great demo and overview of a project called MetaGPT. Have you ever wondered if each person in a development project (such as the project manager, developers, architects, QA testers, etc.) were all AI's and how they'd behave? MetaGPT is doing just that. Not only are all the docs, designs, and tasks delivered, but also a ...\",\"duration\":\"7:35\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/EgipcKPhqME?autoplay=1\",\"image_token\":\"624d4ccdb6d1605da1e388e85c9124957bcba9c70a11a575e751ba6fc09bc5f8\",\"images\":{\"large\":\"https://tse4.mm.bing.net/th?id=OVP.hG0c3nw7X-uz0gzUjnOVNwEsDh&pid=Api\",\"medium\":\"https://tse4.mm.bing.net/th?id=OVP.hG0c3nw7X-uz0gzUjnOVNwEsDh&pid=Api\",\"motion\":\"https://tse4.mm.bing.net/th?id=OM1.8F2lEMy1JlCKsQ_1698986522&pid=Api\",\"small\":\"https://tse4.mm.bing.net/th?id=OVP.hG0c3nw7X-uz0gzUjnOVNwEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-09-24T08:00:11.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":1587},\"title\":\"MetaGPT Tutorial | It builds an entire project (with working source code) with just one prompt!!\",\"uploader\":\"CraceCasts\"},{\"content\":\"https://www.youtube.com/watch?v=T_wBUpzxxPY\",\"description\":\"In this video i talk about this awesome project called MetaGPT in my video. Now, MetaGPT is like an all-in-one AI powerhouse. It can do everything from being a CEO to a QA tester for an engineering organization. And the cool thing is, you just give it a simple prompt, and it spits out everything you need - requirements, PRDs, code, and tests ...\",\"duration\":\"4:00\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/T_wBUpzxxPY?autoplay=1\",\"image_token\":\"ef14791d7faff848cb15177567e9f4f9c04ccae4fafc7ef7386e69df3a012010\",\"images\":{\"large\":\"https://tse1.mm.bing.net/th?id=OVP.EWCOFStB_tQza4SLrUA0AAEsDh&pid=Api\",\"medium\":\"https://tse1.mm.bing.net/th?id=OVP.EWCOFStB_tQza4SLrUA0AAEsDh&pid=Api\",\"motion\":\"https://tse1.mm.bing.net/th?id=OM1.itG5pHJg6MKYzg_1696190983&pid=Api\",\"small\":\"https://tse1.mm.bing.net/th?id=OVP.EWCOFStB_tQza4SLrUA0AAEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-09-11T10:41:22.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":368},\"title\":\"MetaGPT Installation Guide: From Setup to Startup With One Prompt!\",\"uploader\":\"Py Man\"},{\"content\":\"https://www.youtube.com/watch?v=AwnltW8n74A\",\"description\":\"MetaGPT is a framework that uses GPT-4 to automate multiple roles within a software company. For example product managers, software architects, project managers and software engineers. It writes code, documentation, user stories, competitive analysis, and creates diagrams. Github: https://github.com/geekan/MetaGPT #ai #gpt4\",\"duration\":\"7:53\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/AwnltW8n74A?autoplay=1\",\"image_token\":\"b7c3d4481f0f7b7b7c7c43d3da07368a2feb28b2fbdbd8b86b8d5c64b19833fd\",\"images\":{\"large\":\"https://tse1.mm.bing.net/th?id=OVP.J4PD9qpp3rIqXai84Jsu2wEsDh&pid=Api\",\"medium\":\"https://tse1.mm.bing.net/th?id=OVP.J4PD9qpp3rIqXai84Jsu2wEsDh&pid=Api\",\"motion\":\"https://tse1.mm.bing.net/th?id=OM1.EGElEVpTnZYCdQ_1691938448&pid=Api\",\"small\":\"https://tse1.mm.bing.net/th?id=OVP.J4PD9qpp3rIqXai84Jsu2wEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-08-06T23:09:15.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":6497},\"title\":\"MetaGPT - Multi-Agent Framework with GPT-4\",\"uploader\":\"Tosh Velaga\"},{\"content\":\"https://www.youtube.com/watch?v=D80u__nYYWw\",\"description\":\"Learn how to quickly build a roadmap alongside the same table and board views you already know and love in GitHub Projects. With Senior Product Manager, Riley Broughten and Developer Advocate, Kedasha Kerr (@itsthatladydev) Blog: https://gh.io/roadmaps-changelog Project Roadmaps Docs: https://gh.io/roadmaps Tell us what you think!: https://gh ...\",\"duration\":\"7:01\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/D80u__nYYWw?autoplay=1\",\"image_token\":\"2a89cec713d7aae159325e0cb365581ed2715f02621e9f83a9738ffa92664166\",\"images\":{\"large\":\"https://tse3.mm.bing.net/th?id=OVP.46T5385YNZojaEJclzxHKQEsDh&pid=Api\",\"medium\":\"https://tse3.mm.bing.net/th?id=OVP.46T5385YNZojaEJclzxHKQEsDh&pid=Api\",\"motion\":\"https://tse3.mm.bing.net/th?id=OM.jWPqoPJyqHDaVw_1685085608&pid=Api\",\"small\":\"https://tse3.mm.bing.net/th?id=OVP.46T5385YNZojaEJclzxHKQEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-04-18T13:48:05.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":19906},\"title\":\"Learn how to use Project Roadmaps - GitHub Checkout\",\"uploader\":\"GitHub\"},{\"content\":\"https://www.youtube.com/watch?v=pJwR5pv0_gs\",\"description\":\"Multi agent framework tutorial of MetaGPT & chatDev; Check the Hubspot x Jasper research of Using Generative AI to Scale Your Content Operations: https://offers.hubspot.com/generative-ai-for-content-operations?utm_source=youtube&utm_medium=social&utm_campaign=CR0087Sep2023_AIJason/partner_youtube \\ud83d\\udd17 Links - Follow me on twitter: https ...\",\"duration\":\"13:41\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/pJwR5pv0_gs?autoplay=1\",\"image_token\":\"18ac54a8e5144c74f2010219781c47c295099a6eed7479645733832910d19aec\",\"images\":{\"large\":\"https://tse4.mm.bing.net/th?id=OVP.LJ0SK8DLWjCcwVVh-PEcOwHgFo&pid=Api\",\"medium\":\"https://tse4.mm.bing.net/th?id=OVP.LJ0SK8DLWjCcwVVh-PEcOwHgFo&pid=Api\",\"motion\":\"https://tse4.mm.bing.net/th?id=OM2.PxMMOsse4Yi_FQ&pid=Api\",\"small\":\"https://tse4.mm.bing.net/th?id=OVP.LJ0SK8DLWjCcwVVh-PEcOwHgFo&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-09-08T11:36:03.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":167793},\"title\":\"Build AI agent workforce - Multi agent framework with MetaGPT & chatDev\",\"uploader\":\"AI Jason\"},{\"content\":\"https://www.youtube.com/watch?v=q16Gi9pTG_M\",\"description\":\"In this captivating video, we explore the core concept of MetaGPT, which centers on task distribution and coordination among individual GPT agents. Each agent is bestowed with specific roles that capitalize on their unique strengths and expertise. Imagine one GPT excelling in natural language understanding, while another showcases prowess in ...\",\"duration\":\"14:56\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/q16Gi9pTG_M?autoplay=1\",\"image_token\":\"bee3657ef83c9da2bc4ccfea770244e18958f5789a39d0136c3a049cc22a0e54\",\"images\":{\"large\":\"https://tse4.mm.bing.net/th?id=OVP.eiPUmQWRU1sE-01-x5Kn7gEsDh&pid=Api\",\"medium\":\"https://tse4.mm.bing.net/th?id=OVP.eiPUmQWRU1sE-01-x5Kn7gEsDh&pid=Api\",\"motion\":\"https://tse4.mm.bing.net/th?id=OM2.eWDmjf8nvrSrhw&pid=Api\",\"small\":\"https://tse4.mm.bing.net/th?id=OVP.eiPUmQWRU1sE-01-x5Kn7gEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-07-25T00:37:40.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":14365},\"title\":\"MetaGPT: Deploy POWERFUL Autonomous Ai Agents BETTER Than SUPERAGI! (Installation Tutorial)\",\"uploader\":\"WorldofAI\"}],\"vqd\":{\"The%20roadmap%20of%20MetaGPT\":\"4-25941261128344049410840372626152530092\"}});DDG.duckbar.loadModule('related_searches');if (DDG.pageLayout) DDG.pageLayout.initialize({\"mainline\":{\"items\":[[\"organic\"],[\"images\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"videos\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"]]}}, { start: 0 });DDG.deep.emit(\"load:completed\");", + "curl-cffi-POST-https://duckduckgo.com-{\"data\": {\"q\": \"The function of MetaGPT\"}}": "The function of MetaGPT at DuckDuckGo
", + "curl-cffi-GET-https://links.duckduckgo.com/d.js-{\"params\": {\"bing_market\": \"wt-WT\", \"df\": null, \"ex\": \"-1\", \"kl\": \"wt-wt\", \"l\": \"wt-wt\", \"q\": \"The function of MetaGPT\", \"s\": \"0\", \"sp\": \"0\", \"vqd\": \"4-148519746540767190220111387879117509726\"}}": "if (DDG.deep && DDG.deep.setUpstream) DDG.deep.setUpstream(\"bingv7aa\");DDG.deep.bn={'ivc':1};if (DDG.pageLayout) DDG.pageLayout.load('a',[], {\"page_load_url\":\"https://duckduckgo.com/y.js?iurl=%7B2%7DIG%3D3A2CFCB179EC4A63AF1E2047F34A7CBB%26CID%3D3280021F2FA667E6037316192E5B66D4%26Type%3DEvent.CPT%26DATA%3D0\"});DDG.deep.signalSummary = \"\";DDG.inject('DDG.Data.languages.resultLanguages', {\"en\":[\"https://www.unite.ai/metagpt-complete-guide-to-the-best-ai-agent-available-right-now/\",\"https://www.straight.com/guides/software/a-complete-guide-to-metagpt-the-best-ai-agent-available-now/\",\"https://levelup.gitconnected.com/metagpt-the-future-of-multi-agent-collaboration-in-ai-a-brief-guide-fd4b4429336d\",\"https://geekflare.com/metagpt-multi-agent-framework/\",\"https://medium.com/aimonks/metagpt-a-framework-for-multi-agent-meta-programming-6c79f2eafb8e\",\"https://arxiv.org/abs/2308.00352\",\"https://github.com/geekan/MetaGPT\",\"https://medium.com/@reddy.khoushik/metagpt-the-multi-agent-framework-revolutionizing-software-collaboration-38e48397021f\",\"https://www.marktechpost.com/2023/08/09/meet-metagpt-the-open-source-ai-framework-that-transforms-gpts-into-engineers-architects-and-managers/\",\"https://ai-scholar.tech/en/articles/agent-simulation/meta-gpt\",\"https://docs.deepwisdom.ai/main/en/guide/tutorials/multi_agent_101.html\",\"https://lablab.ai/blog/this-week-in-ai-exploring-the-latest-from-metagpt-and-gpt4-and-more\",\"https://analyticsindiamag.com/metagpt-realising-the-gpt-4-dream/\",\"https://docs.deepwisdom.ai/main/en/guide/tutorials/agent_101.html\",\"https://www.almabetter.com/bytes/articles/metagpt\",\"https://medium.com/@korolalexei/metagpt-a-multi-agent-framework-revolutionizing-software-development-f585fe1aa950\",\"https://github.com/PlaiD3/MetaGPT/blob/main/README.md\",\"https://eightify.app/summary/computer-science-and-technology/metagpt-advanced-autonomous-ai-agents-installation-tutorial\",\"https://ar5iv.labs.arxiv.org/html/2308.00352\",\"https://www.freegpttools.org/metagpt\",\"https://github.com/geekan/MetaGPT/releases\",\"https://theventurecation.com/metagpt-complete-guide-to-the-best-ai-agent-available-right-now/\",\"https://blogs.windows.com/windowsexperience/2024/01/04/introducing-a-new-copilot-key-to-kick-off-the-year-of-ai-powered-windows-pcs/\",\"https://www.instagram.com/richfieldmusic/p/C1bpt1eucbU/\"]});DDG.deep.pageLayoutSummary = \"w25\";DDG.inject('DDG.Data.languages.adLanguages', {});if (DDG.pageLayout) DDG.pageLayout.load('d',[{\"a\":\"To actualize an agile, flexible software architecture that can adapt to dynamic programming tasks. Agile Development SOPs act as a meta-function here, coordinating agents to auto-generate code based on defined inputs.\",\"ae\":null,\"c\":\"https://www.unite.ai/metagpt-complete-guide-to-the-best-ai-agent-available-right-now/\",\"d\":\"www.unite.ai/metagpt-complete-guide-to-the-best-ai-agent-available-right-now/\",\"da\":\"\",\"e\":\"2023-09-11T00:00:00.0000000\",\"h\":0,\"i\":\"www.unite.ai\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: Complete Guide to the Best AI Agent Available Right Now\",\"u\":\"https://www.unite.ai/metagpt-complete-guide-to-the-best-ai-agent-available-right-now/\"},{\"a\":\"MetaGPT is a multi-agent system that utilizes Large Language Models (LLMs) to perform complex tasks. It is designed to overcome the limitations of LLMs in fostering effective collaboration and...\",\"ae\":null,\"c\":\"https://www.straight.com/guides/software/a-complete-guide-to-metagpt-the-best-ai-agent-available-now/\",\"d\":\"www.straight.com/guides/software/a-complete-guide-to-metagpt-the-best-ai-agent-available-now/\",\"da\":\"\",\"e\":\"2023-12-13T00:00:00.0000000\",\"h\":0,\"i\":\"www.straight.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"A Complete Guide to MetaGPT: The Best AI Agent Available Now\",\"u\":\"https://www.straight.com/guides/software/a-complete-guide-to-metagpt-the-best-ai-agent-available-now/\"},{\"a\":\"1 Created by Bing In the ever-evolving world of artificial intelligence, one term has recently taken the spotlight: MetaGPT. As the digital landscape becomes more competitive, understanding and leveraging the capabilities of MetaGPT can be a game-changer for businesses, developers, and AI enthusiasts alike.\",\"ae\":null,\"c\":\"https://levelup.gitconnected.com/metagpt-the-future-of-multi-agent-collaboration-in-ai-a-brief-guide-fd4b4429336d\",\"d\":\"levelup.gitconnected.com/metagpt-the-future-of-multi-agent-collaboration-in-ai-a-brief-guide-fd4b4429336d\",\"da\":\"\",\"e\":\"2023-08-09T00:00:00.0000000\",\"h\":0,\"i\":\"levelup.gitconnected.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: The Future of Multi-Agent Collaboration in AI (A Brief Guide)\",\"u\":\"https://levelup.gitconnected.com/metagpt-the-future-of-multi-agent-collaboration-in-ai-a-brief-guide-fd4b4429336d\"},{\"a\":\"MetaGPT is a multi-agent framework that takes one-line inputs to produce APIs, user stories, data structures, competitive analysis, and more. GPT is the short form for Generative Pretrained Transformers. MetaGPT framework can behave as a product manager, software engineer, and architect. This framework can act as an entire software company with ...\",\"ae\":null,\"c\":\"https://geekflare.com/metagpt-multi-agent-framework/\",\"d\":\"geekflare.com/metagpt-multi-agent-framework/\",\"da\":\"\",\"e\":\"2023-09-18T00:00:00.0000000\",\"h\":0,\"i\":\"geekflare.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: Is This the Best Multi-Agent Framework Yet? - Geekflare\",\"u\":\"https://geekflare.com/metagpt-multi-agent-framework/\"},{\"a\":\"Gaming: MetaGPT can be used to create and control intelligent agents that can cooperate or compete with human players or other agents in various games, such as board games, card games, video...\",\"ae\":null,\"c\":\"https://medium.com/aimonks/metagpt-a-framework-for-multi-agent-meta-programming-6c79f2eafb8e\",\"d\":\"medium.com/aimonks/metagpt-a-framework-for-multi-agent-meta-programming-6c79f2eafb8e\",\"da\":\"\",\"e\":\"2023-08-03T00:00:00.0000000\",\"h\":0,\"i\":\"medium.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: A Framework for Multi-Agent Meta Programming\",\"u\":\"https://medium.com/aimonks/metagpt-a-framework-for-multi-agent-meta-programming-6c79f2eafb8e\"},{\"a\":\"You can check this by using:</span>\n<span class=\"pl-c\"><span class=\"pl-c\">#</span> You can use conda to initialize a new python env</span>\n<span class=\"pl-c\"><span class=\"pl-c\">#</span> conda create -n metagpt python=3.9</span>\n<span class=\"pl-c\"><span class=\"pl-c\">#</span> conda activate metagpt</span>\npython3 --version\n\n<span...\",\"ae\":null,\"b\":\"gh\\tGitHub\\tgithub.com\",\"c\":\"https://github.com/geekan/MetaGPT/blob/main/README.md\",\"d\":\"github.com/geekan/MetaGPT/blob/main/README.md\",\"da\":\"\",\"h\":0,\"i\":\"github.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: The Multi-Agent Framework - GitHub\",\"u\":\"https://github.com/geekan/MetaGPT/blob/main/README.md\"},{\"a\":\"MetaGPT utilizes an assembly line paradigm to assign diverse roles to various agents, efficiently breaking down complex tasks into subtasks involving many agents working together. On collaborative software engineering benchmarks, MetaGPT generates more coherent solutions than previous chat-based multi-agent systems.\",\"ae\":null,\"b\":\"arx\\tarXiv.org\\tarxiv.org\",\"c\":\"https://arxiv.org/abs/2308.00352\",\"d\":\"arxiv.org/abs/2308.00352\",\"da\":\"translations\",\"e\":\"2023-08-01T00:00:00.0000000\",\"h\":0,\"i\":\"arxiv.org\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: Meta Programming for A Multi-Agent Collaborative Framework\",\"u\":\"https://arxiv.org/abs/2308.00352\"},{\"a\":\"MetaGPT: The Multi-Agent Framework Assign different roles to GPTs to form a collaborative software entity for complex tasks. MetaGPT takes a one line requirement as input and outputs user stories / competitive analysis / requirements / data structures / APIs / documents, etc.\",\"ae\":null,\"b\":\"gh\\tGitHub\\tgithub.com\",\"c\":\"https://github.com/geekan/MetaGPT\",\"d\":\"github.com/geekan/MetaGPT\",\"da\":\"\",\"h\":0,\"i\":\"github.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: The Multi-Agent Framework - GitHub\",\"u\":\"https://github.com/geekan/MetaGPT\"},{\"a\":\"MetaGPT's innovative approach to collaborative AI has the potential to reshape the landscape of software development. By harnessing the collective power of specialized AI roles, developers can ...\",\"ae\":null,\"c\":\"https://medium.com/@reddy.khoushik/metagpt-the-multi-agent-framework-revolutionizing-software-collaboration-38e48397021f\",\"d\":\"medium.com/@reddy.khoushik/metagpt-the-multi-agent-framework-revolutionizing-software-collaboration-38e48397021f\",\"da\":\"translations\",\"e\":\"2023-08-18T00:00:00.0000000\",\"h\":0,\"i\":\"medium.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: The Multi-Agent Framework Revolutionizing Software ... - Medium\",\"u\":\"https://medium.com/@reddy.khoushik/metagpt-the-multi-agent-framework-revolutionizing-software-collaboration-38e48397021f\"},{\"a\":\"MetaGPT streamlines the coordination between interdependent jobs by formalizing the artifacts that human experts exchange. Agents are connected by a shared environment that offers insight into activities and shared use of tools and resources. All communications between agents are contained in this environment.\",\"ae\":null,\"c\":\"https://www.marktechpost.com/2023/08/09/meet-metagpt-the-open-source-ai-framework-that-transforms-gpts-into-engineers-architects-and-managers/\",\"d\":\"www.marktechpost.com/2023/08/09/meet-metagpt-the-open-source-ai-framework-that-transforms-gpts-into-engineers-architects-and-managers/\",\"da\":\"translations\",\"e\":\"2023-08-09T00:00:00.0000000\",\"h\":0,\"i\":\"www.marktechpost.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Meet MetaGPT: The Open-Source AI Framework That Transforms GPTs into ...\",\"u\":\"https://www.marktechpost.com/2023/08/09/meet-metagpt-the-open-source-ai-framework-that-transforms-gpts-into-engineers-architects-and-managers/\"},{\"a\":\"This paper presents MetaGPT, a multi-agent framework that extends complex problem solving capabilities by encoding SOPs that incorporate real-world expertise into LLM agents, and shows through experiments that it can generate more consistent and comprehensive solutionsthan existing methods.\",\"ae\":null,\"c\":\"https://ai-scholar.tech/en/articles/agent-simulation/meta-gpt\",\"d\":\"ai-scholar.tech/en/articles/agent-simulation/meta-gpt\",\"da\":\"\",\"e\":\"2023-08-18T00:00:00.0000000\",\"h\":0,\"i\":\"ai-scholar.tech\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT, a multi-agent framework in which AI consistently develops ...\",\"u\":\"https://ai-scholar.tech/en/articles/agent-simulation/meta-gpt\"},{\"a\":\"The core advantage of MetaGPT also lies in the easy and flexible development of a team of agents. Under MetaGPT framework, users can enable interactions between agents with a minimal amount of codes. ... we need three steps to set up the team and make it function: Define each role capable of intended actions; Think about the Standard Operating ...\",\"ae\":null,\"c\":\"https://docs.deepwisdom.ai/main/en/guide/tutorials/multi_agent_101.html\",\"d\":\"docs.deepwisdom.ai/main/en/guide/tutorials/multi_agent_101.html\",\"da\":\"translations\",\"h\":0,\"i\":\"docs.deepwisdom.ai\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MultiAgent 101 | MetaGPT\",\"u\":\"https://docs.deepwisdom.ai/main/en/guide/tutorials/multi_agent_101.html\"},{\"a\":\"MetaGPT is a groundbreaking multi-agent framework that is transforming the way software development is approached. By taking a single line of requirement as input, MetaGPT outputs a comprehensive array of development components, including user stories, competitive analysis, requirements, data structures, APIs, and documents.\",\"ae\":null,\"c\":\"https://lablab.ai/blog/this-week-in-ai-exploring-the-latest-from-metagpt-and-gpt4-and-more\",\"d\":\"lablab.ai/blog/this-week-in-ai-exploring-the-latest-from-metagpt-and-gpt4-and-more\",\"da\":\"\",\"e\":\"2023-08-11T00:00:00.0000000\",\"h\":0,\"i\":\"lablab.ai\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"This Week in AI: Exploring the Latest from MetaGPT and GPT-4 and more..\",\"u\":\"https://lablab.ai/blog/this-week-in-ai-exploring-the-latest-from-metagpt-and-gpt4-and-more\"},{\"a\":\"MetaGPT then asks for a few additional details, such as the required inputs from the user. Subscribe to our Newsletter. ... One only needs to look at the success of AutoGPT, an open-source project looking to allow GPT-4 to function autonomously. Other similar projects include BabyAGI, a GPT API powered task management system, ...\",\"ae\":null,\"c\":\"https://analyticsindiamag.com/metagpt-realising-the-gpt-4-dream/\",\"d\":\"analyticsindiamag.com/metagpt-realising-the-gpt-4-dream/\",\"da\":\"\",\"e\":\"2023-04-26T00:00:00.0000000\",\"h\":0,\"i\":\"analyticsindiamag.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT \\u2014 Realising the GPT-4 Dream - Analytics India Magazine\",\"u\":\"https://analyticsindiamag.com/metagpt-realising-the-gpt-4-dream/\"},{\"a\":\"In MetaGPT, class Action is the logical abstraction for an action. Users may use LLM to empower this Action by simply invoking the self._aask function, which will make LLM api call under the hood. In our scenario, we define a SimpleWriteCode subclassed Action.\",\"ae\":null,\"c\":\"https://docs.deepwisdom.ai/main/en/guide/tutorials/agent_101.html\",\"d\":\"docs.deepwisdom.ai/main/en/guide/tutorials/agent_101.html\",\"da\":\"translations\",\"h\":0,\"i\":\"docs.deepwisdom.ai\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Agent 101 | MetaGPT\",\"u\":\"https://docs.deepwisdom.ai/main/en/guide/tutorials/agent_101.html\"},{\"a\":\"Understanding MetaGPT MetaGPT, a concept originating from a research paper that received significant attention, represents a leap forward in Artificial Intelligence, specifically in multi-agent collaboration using large language models (LLMs).\",\"ae\":null,\"c\":\"https://www.almabetter.com/bytes/articles/metagpt\",\"d\":\"www.almabetter.com/bytes/articles/metagpt\",\"da\":\"\",\"e\":\"2023-08-28T00:00:00.0000000\",\"h\":0,\"i\":\"www.almabetter.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: The Future of Multi-Agent Collaboration in AI\",\"u\":\"https://www.almabetter.com/bytes/articles/metagpt\"},{\"a\":\"MetaGPT is a trending GitHub repository that simulates different roles in a software company using GPT-4. It's like a software company in a box (or CLI to be precise).\",\"ae\":null,\"c\":\"https://medium.com/@korolalexei/metagpt-a-multi-agent-framework-revolutionizing-software-development-f585fe1aa950\",\"d\":\"medium.com/@korolalexei/metagpt-a-multi-agent-framework-revolutionizing-software-development-f585fe1aa950\",\"da\":\"translations\",\"e\":\"2023-08-09T00:00:00.0000000\",\"h\":0,\"i\":\"medium.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: A Multi-Agent Framework Revolutionizing Software ... - Medium\",\"u\":\"https://medium.com/@korolalexei/metagpt-a-multi-agent-framework-revolutionizing-software-development-f585fe1aa950\"},{\"a\":\"You can check this by using:</span>\npython --version\n\n<span class=\"pl-c\"><span class=\"pl-c\">#</span> Step 3: Clone the repository to your local machine, and install it.</span>\ngit clone https://github.com/geekan/metagpt\n<span class=\"pl-c1\">cd</span> metagpt\npython setup.py install</pre></div>\n<h3 tabindex=\"-1\" dir=\"auto\"><a id=\...\",\"ae\":null,\"b\":\"gh\\tGitHub\\tgithub.com\",\"c\":\"https://github.com/PlaiD3/MetaGPT/blob/main/README.md\",\"d\":\"github.com/PlaiD3/MetaGPT/blob/main/README.md\",\"da\":\"\",\"h\":0,\"i\":\"github.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: Multi-Agent Meta Programming Framework - GitHub\",\"u\":\"https://github.com/PlaiD3/MetaGPT/blob/main/README.md\"},{\"a\":\"\\u2014 MetaGPT is a multi-agent framework that enables collaboration among AI agents to tackle complex tasks and achieve collective intelligence. How does MetaGPT work? \\u2014 MetaGPT assigns specific roles to GPT agents based on their strengths and expertise, allowing them to collaborate, communicate, and share information to effectively tackle ...\",\"ae\":null,\"c\":\"https://eightify.app/summary/computer-science-and-technology/metagpt-advanced-autonomous-ai-agents-installation-tutorial\",\"d\":\"eightify.app/summary/computer-science-and-technology/metagpt-advanced-autonomous-ai-agents-installation-tutorial\",\"da\":\"\",\"h\":0,\"i\":\"eightify.app\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: Advanced Autonomous AI Agents Installation Tutorial\",\"u\":\"https://eightify.app/summary/computer-science-and-technology/metagpt-advanced-autonomous-ai-agents-installation-tutorial\"},{\"a\":\"Therefore, we introduce MetaGPT, an innovative framework that incorporates efficient human workflows as a meta programming approach into LLM-based multi-agent collaboration. Specifically, MetaGPT encodes Standardized Operating Procedures (SOPs) into prompts to enhance structured coordination. ... SOPs act as a meta-function, taking the team and ...\",\"ae\":null,\"b\":\"arx\\tarXiv.org\\tarxiv.org\",\"c\":\"https://ar5iv.labs.arxiv.org/html/2308.00352\",\"d\":\"ar5iv.labs.arxiv.org/html/2308.00352\",\"da\":\"translations\",\"e\":\"2023-09-05T00:00:00.0000000\",\"h\":0,\"i\":\"ar5iv.labs.arxiv.org\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: Meta Programming for Multi-Agent Collaborative Framework\",\"u\":\"https://ar5iv.labs.arxiv.org/html/2308.00352\"},{\"a\":\"Discover MetaGPT, a cutting-edge technology that harnesses Standardized Operating Procedures (SOPs) to orchestrate Large Language Model (LLM)-driven multi-agent systems, revolutionizing software development and collaborative task resolution. Explore its key features, delve into the core mechanisms, and learn how it enhances collaboration efficiency.\",\"ae\":null,\"c\":\"https://www.freegpttools.org/metagpt\",\"d\":\"www.freegpttools.org/metagpt\",\"da\":\"\",\"h\":0,\"i\":\"www.freegpttools.org\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Unlocking the Power of MetaGPT: A Multi-Agent Framework for Complex ...\",\"u\":\"https://www.freegpttools.org/metagpt\"},{\"a\":\"Message Function: Retained for event notification, weakened data transportation. Configuration Optimization: Default to gpt-4-1106-preview. ~/.metagpt for highest priority config, reading config.yaml. METAGPT_PROJECT_ROOT for workspace path specification. project_name specification via command line, generated by ProductManager. CLI Support\",\"ae\":null,\"b\":\"gh\\tGitHub\\tgithub.com\",\"c\":\"https://github.com/geekan/MetaGPT/releases\",\"d\":\"github.com/geekan/MetaGPT/releases\",\"da\":\"\",\"h\":0,\"i\":\"github.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Releases \\u00b7 geekan/MetaGPT \\u00b7 GitHub\",\"u\":\"https://github.com/geekan/MetaGPT/releases\"},{\"a\":\"SOPs act as a meta-function here, coordinating agents to auto-generate code based on defined inputs. In simple terms, it's as if you've turned a highly coordinated team of software engineers into an adaptable, intelligent software system. ... MetaGPT's architecture is divided into two layers: the Foundational Components Layer and the ...\",\"ae\":null,\"c\":\"https://theventurecation.com/metagpt-complete-guide-to-the-best-ai-agent-available-right-now/\",\"d\":\"theventurecation.com/metagpt-complete-guide-to-the-best-ai-agent-available-right-now/\",\"da\":\"\",\"e\":\"2023-09-11T00:00:00.0000000\",\"h\":0,\"i\":\"theventurecation.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: Complete Guide to the Best AI Agent Available Right Now\",\"u\":\"https://theventurecation.com/metagpt-complete-guide-to-the-best-ai-agent-available-right-now/\"},{\"a\":\"Today, we are excited to take the next significant step forward and introduce a new Copilot key to Windows 11 PCs. In this new year, we will be ushering in a significant shift toward a more personal and intelligent computing future where AI will be seamlessly woven into Windows from the system, to the silicon, to the hardware.\",\"ae\":null,\"c\":\"https://blogs.windows.com/windowsexperience/2024/01/04/introducing-a-new-copilot-key-to-kick-off-the-year-of-ai-powered-windows-pcs/\",\"d\":\"blogs.windows.com/windowsexperience/2024/01/04/introducing-a-new-copilot-key-to-kick-off-the-year-of-ai-powered-windows-pcs/\",\"da\":\"translations\",\"e\":\"2024-01-04T00:00:00.0000000\",\"h\":0,\"i\":\"blogs.windows.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Introducing a new Copilot key to kick off the year of AI-powered ...\",\"u\":\"https://blogs.windows.com/windowsexperience/2024/01/04/introducing-a-new-copilot-key-to-kick-off-the-year-of-ai-powered-windows-pcs/\"},{\"a\":\"11 Cosmic Contingencies About 600,000 words between ChatGPT and I later. Please see pinned post on profile for modification of the Einstein field equations including contributions from quantum ram...\",\"ae\":null,\"c\":\"https://www.instagram.com/richfieldmusic/p/C1bpt1eucbU/\",\"d\":\"www.instagram.com/richfieldmusic/p/C1bpt1eucbU/\",\"da\":\"\",\"h\":0,\"i\":\"www.instagram.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"\\u042f\\u13c6\\u13df\\u13bb\\u0192\\u13c6\\u13ac\\u13de\\u13a0 on Instagram\",\"u\":\"https://www.instagram.com/richfieldmusic/p/C1bpt1eucbU/\"},{\"n\":\"/d.js?q=The%20function%20of%20MetaGPT&kl=wt-wt&l=wt-wt&p=&s=25&ex=-1&ct=US&sp=0&vqd=4-148519746540767190220111387879117509726\"}]);DDG.duckbar.load('images');DDG.duckbar.load('news');DDG.duckbar.load('videos');DDG.duckbar.loadModule('related_searches');if (DDG.pageLayout) DDG.pageLayout.initialize({\"mainline\":{\"items\":[[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"]]}}, { start: 0 });DDG.deep.emit(\"load:completed\");", + "curl-cffi-POST-https://duckduckgo.com-{\"data\": {\"q\": \"What llm MetaGPT support\"}}": "What llm MetaGPT support at DuckDuckGo
", + "curl-cffi-GET-https://links.duckduckgo.com/d.js-{\"params\": {\"bing_market\": \"wt-WT\", \"df\": null, \"ex\": \"-1\", \"kl\": \"wt-wt\", \"l\": \"wt-wt\", \"q\": \"What llm MetaGPT support\", \"s\": \"0\", \"sp\": \"0\", \"vqd\": \"4-73487995398881375915343809280473758117\"}}": "if (DDG.deep && DDG.deep.setUpstream) DDG.deep.setUpstream(\"bingv7aa\");DDG.deep.bn={'ivc':1};if (DDG.pageLayout) DDG.pageLayout.load('a',[], {\"page_load_url\":\"https://duckduckgo.com/y.js?iurl=%7B2%7DIG%3D0C43B15D5A884367BD85DF1F28ABDA06%26CID%3D26CF35F2E42B60EF229421F4E5D6611D%26Type%3DEvent.CPT%26DATA%3D0\"});DDG.deep.signalSummary = \"\";DDG.inject('DDG.Data.languages.resultLanguages', {\"en\":[\"https://www.unite.ai/metagpt-complete-guide-to-the-best-ai-agent-available-right-now/\",\"https://docs.deepwisdom.ai/main/en/guide/tutorials/integration_with_open_llm.html\",\"https://www.straight.com/guides/software/a-complete-guide-to-metagpt-the-best-ai-agent-available-now/\",\"https://mathaware.org/ai/metagpt-encoding-sops-with-llm-agents/\",\"https://arxiv.org/abs/2308.00352\",\"https://github.com/geekan/MetaGPT\",\"https://www.louisbouchard.ai/metagpt/\",\"https://lablab.ai/blog/this-week-in-ai-exploring-the-latest-from-metagpt-and-gpt4-and-more\",\"https://www.linkedin.com/pulse/what-metagpt-llm-agents-collaborating-solve-complex-bouchard-\",\"https://towardsai.net/p/machine-learning/what-is-metagpt-llm-agents-collaborating-to-solve-complex-tasks\",\"https://medium.com/mlearning-ai/metagpt-multi-agent-harmony-for-complex-problem-solving-97bcb8f3fe94\",\"https://medium.com/@yousra.aoudi/navigating-the-future-metagpts-innovative-approach-to-multi-agent-collaboration-ed1cc5835011\",\"https://openreview.net/forum?id=VtmBAGCN7o\",\"https://hackernoon.com/autogpt-langchain-deep-lake-metagpt-building-the-ultimate-llm-app\",\"https://generativeai.pub/analyzing-an-exciting-generative-ai-research-called-metagpt-2106385312db\",\"https://ar5iv.labs.arxiv.org/html/2308.00352\",\"https://louisbouchard.substack.com/p/what-is-metagpt-llm-agents-collaborating\",\"https://developer.nvidia.com/blog/supercharging-llm-applications-on-windows-pcs-with-nvidia-rtx-systems/\",\"https://github.com/geekan/MetaGPT/releases\",\"https://github.com/geekan/MetaGPT-docs/blob/main/src/en/guide/tutorials/integration_with_open_llm.md\",\"https://mathaware.org/ai/empowering-ai-with-llm-based-agents-metagpt-framework-transforms-human-sops/\",\"https://stackshare.io/metagpt\",\"https://medium.com/gta-generative-tech-advances/metagpt-an-interesting-approach-to-multi-agent-collaboration-5ace263c4fd8\",\"https://nvidianews.nvidia.com/news/generative-ai-rtx-pcs-and-workstations\",\"https://www.intel.com/content/www/us/en/developer/articles/technical/finetuning-llms-on-intel-gpus-using-bigdl-llm.html\",\"https://arxiv.org/abs/2401.05778\",\"https://www.ft.com/content/116f3541-bf2f-483e-a36a-ed3618548f9b\"],\"zh-CN\":[\"https://community.modelscope.cn/659cb258d4226e0eb42708e5.html\"]});DDG.deep.pageLayoutSummary = \"w29\";DDG.inject('DDG.Data.languages.adLanguages', {});if (DDG.pageLayout) DDG.pageLayout.load('d',[{\"a\":\"Enter MetaGPT \\u2014 a Multi-agent system that utilizes Large Language models by Sirui Hong fuses Standardized Operating Procedures (SOPs) with LLM-based multi-agent systems. This emerging paradigm disrupts the existing limitations of LLMs in fostering effective collaboration and task decomposition in complex, real-world applications.\",\"ae\":null,\"c\":\"https://www.unite.ai/metagpt-complete-guide-to-the-best-ai-agent-available-right-now/\",\"d\":\"www.unite.ai/metagpt-complete-guide-to-the-best-ai-agent-available-right-now/\",\"da\":\"\",\"e\":\"2023-09-11T00:00:00.0000000\",\"h\":0,\"i\":\"www.unite.ai\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: Complete Guide to the Best AI Agent Available Right Now\",\"u\":\"https://www.unite.ai/metagpt-complete-guide-to-the-best-ai-agent-available-right-now/\"},{\"a\":\"The methods of integrating open source LLM and integrating some non-openai closed source models (such as Baidu Wenxinyiyan, iFLYTEK Spark, Zhipu ChatGLM, etc.) are similar, the main difference is the configuration. For details on the configuration of other closed-source LLMs, please refer to other LLM configuration documents under the online ...\",\"ae\":null,\"c\":\"https://docs.deepwisdom.ai/main/en/guide/tutorials/integration_with_open_llm.html\",\"d\":\"docs.deepwisdom.ai/main/en/guide/tutorials/integration_with_open_llm.html\",\"da\":\"\",\"e\":\"2023-12-21T00:00:00.0000000\",\"h\":0,\"i\":\"docs.deepwisdom.ai\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Integration with open LLM | MetaGPT\",\"u\":\"https://docs.deepwisdom.ai/main/en/guide/tutorials/integration_with_open_llm.html\"},{\"a\":\"MetaGPT is a multi-agent system that utilizes Large Language Models (LLMs) to perform complex tasks. It is designed to overcome the limitations of LLMs in fostering effective collaboration and...\",\"ae\":null,\"c\":\"https://www.straight.com/guides/software/a-complete-guide-to-metagpt-the-best-ai-agent-available-now/\",\"d\":\"www.straight.com/guides/software/a-complete-guide-to-metagpt-the-best-ai-agent-available-now/\",\"da\":\"\",\"e\":\"2023-12-13T00:00:00.0000000\",\"h\":0,\"i\":\"www.straight.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"A Complete Guide to MetaGPT: The Best AI Agent Available Now\",\"u\":\"https://www.straight.com/guides/software/a-complete-guide-to-metagpt-the-best-ai-agent-available-now/\"},{\"a\":\"The advent of MetaGPT and similar LLM agents represents a significant leap forward in the realm of process management and optimization. By addressing the complexities of encoding SOPs and acknowledging the nuanced nature of their integration, these intelligent systems are poised to redefine operational efficiency.\",\"ae\":null,\"c\":\"https://mathaware.org/ai/metagpt-encoding-sops-with-llm-agents/\",\"d\":\"mathaware.org/ai/metagpt-encoding-sops-with-llm-agents/\",\"da\":\"\",\"h\":0,\"i\":\"mathaware.org\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: Encoding SOPs with LLM Agents | MathAware AI\",\"u\":\"https://mathaware.org/ai/metagpt-encoding-sops-with-llm-agents/\"},{\"a\":\"Remarkable progress has been made on automated problem solving through societies of agents based on large language models (LLMs). Existing LLM-based multi-agent systems can already solve simple dialogue tasks.\",\"ae\":null,\"b\":\"arx\\tarXiv.org\\tarxiv.org\",\"c\":\"https://arxiv.org/abs/2308.00352\",\"d\":\"arxiv.org/abs/2308.00352\",\"da\":\"translations\",\"e\":\"2023-08-01T00:00:00.0000000\",\"h\":0,\"i\":\"arxiv.org\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: Meta Programming for A Multi-Agent Collaborative Framework\",\"u\":\"https://arxiv.org/abs/2308.00352\"},{\"a\":\"agent multi-agent gpt hacktoberfest llm metagpt Resources. Readme License. MIT license Activity. Stars. 33.2k stars Watchers. 802 watching Forks. 3.9k forks Report repository Releases 11. Patch release: v0.6.4 Latest Jan 12, 2024 + 10 releases Packages 0. No packages published . Contributors 55 + 41 contributors Languages.\",\"ae\":null,\"b\":\"gh\\tGitHub\\tgithub.com\",\"c\":\"https://github.com/geekan/MetaGPT\",\"d\":\"github.com/geekan/MetaGPT\",\"da\":\"\",\"h\":0,\"i\":\"github.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: The Multi-Agent Framework - GitHub\",\"u\":\"https://github.com/geekan/MetaGPT\"},{\"a\":\"Aug 27, 2023 \\u2022 6 min read Watch the video! MetaGPT: Redefining Multi-Agent Collaboration for Complex Tasks Watch on Thanks to GPT and the recent large language models, we've seen the popularization of a new type of AI-based system\\u2026 agents. An agent is basically an AI model like ChatGPT that can access and interact with one or more applications.\",\"ae\":null,\"c\":\"https://www.louisbouchard.ai/metagpt/\",\"d\":\"www.louisbouchard.ai/metagpt/\",\"da\":\"\",\"e\":\"2023-08-27T00:00:00.0000000\",\"h\":0,\"i\":\"www.louisbouchard.ai\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Mitigating AI Hallucinations: Exploring MetaGPT's Collaborative Framework\",\"u\":\"https://www.louisbouchard.ai/metagpt/\"},{\"a\":\"MetaGPT is a groundbreaking multi-agent framework that is transforming the way software development is approached. By taking a single line of requirement as input, MetaGPT outputs a comprehensive array of development components, including user stories, competitive analysis, requirements, data structures, APIs, and documents.\",\"ae\":null,\"c\":\"https://lablab.ai/blog/this-week-in-ai-exploring-the-latest-from-metagpt-and-gpt4-and-more\",\"d\":\"lablab.ai/blog/this-week-in-ai-exploring-the-latest-from-metagpt-and-gpt4-and-more\",\"da\":\"\",\"e\":\"2023-08-11T00:00:00.0000000\",\"h\":0,\"i\":\"lablab.ai\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"This Week in AI: Exploring the Latest from MetaGPT and GPT-4 and more..\",\"u\":\"https://lablab.ai/blog/this-week-in-ai-exploring-the-latest-from-metagpt-and-gpt4-and-more\"},{\"a\":\"This iteration focuses on MetaGPT, a new approach to improving collaborations between AI agents (e.g., ChatGPT-based entities mimicking human roles). ... 3D-LLM Unleashes Language Models into the ...\",\"ae\":null,\"b\":\"li\\tLinkedIn\\twww.linkedin.com\",\"c\":\"https://www.linkedin.com/pulse/what-metagpt-llm-agents-collaborating-solve-complex-bouchard-\",\"d\":\"www.linkedin.com/pulse/what-metagpt-llm-agents-collaborating-solve-complex-bouchard-\",\"da\":\"\",\"e\":\"2023-08-28T00:00:00.0000000\",\"h\":0,\"i\":\"www.linkedin.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"What is MetaGPT? LLM Agents Collaborating to Solve Complex Tasks - LinkedIn\",\"u\":\"https://www.linkedin.com/pulse/what-metagpt-llm-agents-collaborating-solve-complex-bouchard-\"},{\"a\":\"Latest Machine Learning What is MetaGPT? LLM Agents Collaborating to Solve Complex Tasks August 28, 2023 Last Updated on August 29, 2023 by Editorial Team Author (s): Louis Bouchard Watch the video! This member-only story is on us. Upgrade to access all of Medium. Originally published on louisbouchard.ai, read it 2 days before on my blog!\",\"ae\":null,\"c\":\"https://towardsai.net/p/machine-learning/what-is-metagpt-llm-agents-collaborating-to-solve-complex-tasks\",\"d\":\"towardsai.net/p/machine-learning/what-is-metagpt-llm-agents-collaborating-to-solve-complex-tasks\",\"da\":\"\",\"e\":\"2023-08-29T00:00:00.0000000\",\"h\":0,\"i\":\"towardsai.net\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"What is MetaGPT? LLM Agents Collaborating to Solve Complex Tasks\",\"u\":\"https://towardsai.net/p/machine-learning/what-is-metagpt-llm-agents-collaborating-to-solve-complex-tasks\"},{\"a\":\"You know how those multi-agent systems powered by Large Language Models (LLMs) have the potential to mimic and jazz up human workflows? But, the real world's a tangled place, and these systems...\",\"ae\":null,\"c\":\"https://medium.com/mlearning-ai/metagpt-multi-agent-harmony-for-complex-problem-solving-97bcb8f3fe94\",\"d\":\"medium.com/mlearning-ai/metagpt-multi-agent-harmony-for-complex-problem-solving-97bcb8f3fe94\",\"da\":\"\",\"e\":\"2023-08-09T00:00:00.0000000\",\"h\":0,\"i\":\"medium.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: Multi-Agent Harmony for Complex Problem Solving\",\"u\":\"https://medium.com/mlearning-ai/metagpt-multi-agent-harmony-for-complex-problem-solving-97bcb8f3fe94\"},{\"a\":\"T he essence of MetaGPT is the seamless integration of SOPs to craft a highly coordinated LLM-based multi-agent ecosystem. With a focus on emulating human-like roles and intricate workflows, it...\",\"ae\":null,\"c\":\"https://medium.com/@yousra.aoudi/navigating-the-future-metagpts-innovative-approach-to-multi-agent-collaboration-ed1cc5835011\",\"d\":\"medium.com/@yousra.aoudi/navigating-the-future-metagpts-innovative-approach-to-multi-agent-collaboration-ed1cc5835011\",\"da\":\"translations\",\"e\":\"2023-08-10T00:00:00.0000000\",\"h\":0,\"i\":\"medium.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Navigating the Future: MetaGPT's Innovative Approach to ... - Medium\",\"u\":\"https://medium.com/@yousra.aoudi/navigating-the-future-metagpts-innovative-approach-to-multi-agent-collaboration-ed1cc5835011\"},{\"a\":\"Recently, remarkable progress has been made on automated problem solving through societies of agents based on large language models (LLMs). Previous LLM-based multi-agent systems can already solve simple dialogue tasks.\",\"ae\":null,\"c\":\"https://openreview.net/forum?id=VtmBAGCN7o\",\"d\":\"openreview.net/forum?id=VtmBAGCN7o\",\"da\":\"\",\"e\":\"2023-09-22T00:00:00.0000000\",\"h\":0,\"i\":\"openreview.net\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: Meta Programming for Multi-Agent Collaborative Framework\",\"u\":\"https://openreview.net/forum?id=VtmBAGCN7o\"},{\"a\":\"Deep Lake is a remarkable answer to the problem of storing gigabytes of data for LLMs \\u2014 efficiently, easily, and practically. Its unique configuration allows the optimal usage of finances. OpenAI's LLM Operational Cost Daily is on average 700,000 USD a day. Some are even predicting bankruptcy for the company.\",\"ae\":null,\"c\":\"https://hackernoon.com/autogpt-langchain-deep-lake-metagpt-building-the-ultimate-llm-app\",\"d\":\"hackernoon.com/autogpt-langchain-deep-lake-metagpt-building-the-ultimate-llm-app\",\"da\":\"\",\"e\":\"2023-08-29T00:00:00.0000000\",\"h\":0,\"i\":\"hackernoon.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Deep Lake \\u2014 MetaGPT: Building the Ultimate LLM App - HackerNoon\",\"u\":\"https://hackernoon.com/autogpt-langchain-deep-lake-metagpt-building-the-ultimate-llm-app\"},{\"a\":\"The MetaGPT approach showcases its ability to decompose highlevel tasks into detailed actionable components handled by distinct roles (ProductManager, Architect, ProjectManager, Engineer, QA Engineer), thereby facilitating role-specific expertise and coordination. This methodology mirrors human software development teams.\",\"ae\":null,\"c\":\"https://generativeai.pub/analyzing-an-exciting-generative-ai-research-called-metagpt-2106385312db\",\"d\":\"generativeai.pub/analyzing-an-exciting-generative-ai-research-called-metagpt-2106385312db\",\"da\":\"translations\",\"e\":\"2023-08-14T00:00:00.0000000\",\"h\":0,\"i\":\"generativeai.pub\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Analyzing an exciting Generative AI research called MetaGPT.\",\"u\":\"https://generativeai.pub/analyzing-an-exciting-generative-ai-research-called-metagpt-2106385312db\"},{\"a\":\"In this work, we present MetaGPT, a promising framework for collaborative agents using SOPs that leverages LLMs to mimic efficient human workflows. MetaGPT is a meta programming technology that utilizes SOPs to coordinate LLM-based multi-agent systems. Specifically, to encode SOPs into prompts, MetaGPT manages multi-agents through role ...\",\"ae\":null,\"b\":\"arx\\tarXiv.org\\tarxiv.org\",\"c\":\"https://ar5iv.labs.arxiv.org/html/2308.00352\",\"d\":\"ar5iv.labs.arxiv.org/html/2308.00352\",\"da\":\"translations\",\"e\":\"2023-09-05T00:00:00.0000000\",\"h\":0,\"i\":\"ar5iv.labs.arxiv.org\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: Meta Programming for Multi-Agent Collaborative Framework\",\"u\":\"https://ar5iv.labs.arxiv.org/html/2308.00352\"},{\"a\":\"MetaGPT: Meta Programming for Multi-Agent Collaborative Framework. Topsakal, O., & Akinci, T.C. (2023). Creating Large Language Model Applications Utilizing LangChain: A Primer on Developing LLM ...\",\"ae\":null,\"c\":\"https://medium.com/technology-hits/autogpt-langchain-deep-lake-metagpt-a-revolutionary-framework-for-building-advanced-ai-e2c579d86494\",\"d\":\"medium.com/technology-hits/autogpt-langchain-deep-lake-metagpt-a-revolutionary-framework-for-building-advanced-ai-e2c579d86494\",\"da\":\"\",\"e\":\"2023-08-28T00:00:00.0000000\",\"h\":0,\"i\":\"medium.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"AutoGPT \\u2014 LangChain \\u2014 Deep Lake \\u2014 MetaGPT: A ... - Medium\",\"u\":\"https://medium.com/technology-hits/autogpt-langchain-deep-lake-metagpt-a-revolutionary-framework-for-building-advanced-ai-e2c579d86494\"},{\"a\":\"Louis , starting with a trending topic: AI agents! This iteration focuses on MetaGPT, a new approach to improving collaborations between AI agents (e.g., ChatGPT-based entities mimicking human roles).\",\"ae\":null,\"c\":\"https://louisbouchard.substack.com/p/what-is-metagpt-llm-agents-collaborating\",\"d\":\"louisbouchard.substack.com/p/what-is-metagpt-llm-agents-collaborating\",\"da\":\"\",\"e\":\"2023-08-28T00:00:00.0000000\",\"h\":0,\"i\":\"louisbouchard.substack.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"What is MetaGPT? LLM Agents Collaborating to Solve Complex Tasks\",\"u\":\"https://louisbouchard.substack.com/p/what-is-metagpt-llm-agents-collaborating\"},{\"a\":\"Today, LLM-powered applications are running predominantly in the cloud. However, many use cases that would benefit from running LLMs locally on Windows PCs, including gaming, creativity, productivity, and developer experiences. AT CES 2024, NVIDIA announced several developer tools to accelerate LLM inference and development on NVIDIA RTX ...\",\"ae\":null,\"c\":\"https://developer.nvidia.com/blog/supercharging-llm-applications-on-windows-pcs-with-nvidia-rtx-systems/\",\"d\":\"developer.nvidia.com/blog/supercharging-llm-applications-on-windows-pcs-with-nvidia-rtx-systems/\",\"da\":\"\",\"e\":\"2024-01-08T00:00:00.0000000\",\"h\":0,\"i\":\"developer.nvidia.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Supercharging LLM Applications on Windows PCs with NVIDIA RTX Systems\",\"u\":\"https://developer.nvidia.com/blog/supercharging-llm-applications-on-windows-pcs-with-nvidia-rtx-systems/\"},{\"a\":\"Supported Ollama as underlying LLM #603 by @better629; Enabled MetaGPT to be used as a dependency for web applications, such as https: ... PIP Support: pip install metagpt is now available for installing and using metagpt, enabling direct access to the command-line version of metagpt.\",\"ae\":null,\"b\":\"gh\\tGitHub\\tgithub.com\",\"c\":\"https://github.com/geekan/MetaGPT/releases\",\"d\":\"github.com/geekan/MetaGPT/releases\",\"da\":\"\",\"h\":0,\"i\":\"github.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Releases \\u00b7 geekan/MetaGPT \\u00b7 GitHub\",\"u\":\"https://github.com/geekan/MetaGPT/releases\"},{\"a\":\"If you want to support <code>http: //ip:11434/api/chat</code>, you can do as follows:</p>\n<div class=\"highlight highlight-source-shell notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"service ollama stop\n\nOLLAMA_HOST=0.0.0.0 OLLAMA_ORIGINS=* ollama serve # one terminal\n\nollama run llama2 # ot...\",\"ae\":null,\"b\":\"gh\\tGitHub\\tgithub.com\",\"c\":\"https://github.com/geekan/MetaGPT-docs/blob/main/src/en/guide/tutorials/integration_with_open_llm.md\",\"d\":\"github.com/geekan/MetaGPT-docs/blob/main/src/en/guide/tutorials/integration_with_open_llm.md\",\"da\":\"\",\"h\":0,\"i\":\"github.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Integration with open LLM - GitHub\",\"u\":\"https://github.com/geekan/MetaGPT-docs/blob/main/src/en/guide/tutorials/integration_with_open_llm.md\"},{\"a\":\"With LLM-based agents empowered by the MetaGPT framework, companies can streamline their workflows and improve productivity. These agents can assist employees by automating repetitive tasks, generating reports, and even coming up with creative solutions to problems. The MetaGPT framework allows for fine-tuning the LLM-based agents based on ...\",\"ae\":null,\"c\":\"https://mathaware.org/ai/empowering-ai-with-llm-based-agents-metagpt-framework-transforms-human-sops/\",\"d\":\"mathaware.org/ai/empowering-ai-with-llm-based-agents-metagpt-framework-transforms-human-sops/\",\"da\":\"\",\"h\":0,\"i\":\"mathaware.org\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Empowering AI with LLM-based Agents: MetaGPT Framework Transforms Human ...\",\"u\":\"https://mathaware.org/ai/empowering-ai-with-llm-based-agents-metagpt-framework-transforms-human-sops/\"},{\"a\":\"Check out popular companies that use MetaGPT and some tools that integrate with MetaGPT. ... On top of llm, there is a CLI application, llm-cli, which provides a convenient interface for running inference on supported models. Chroma. It is an open-source embedding database. Chroma makes it easy to build LLM apps by making knowledge, facts, and ...\",\"ae\":null,\"c\":\"https://stackshare.io/metagpt\",\"d\":\"stackshare.io/metagpt\",\"da\":\"\",\"h\":0,\"i\":\"stackshare.io\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT - Reviews, Pros & Cons | Companies using MetaGPT - StackShare\",\"u\":\"https://stackshare.io/metagpt\"},{\"a\":\"MetaGPT is the maestro who brings harmony to this chaos. By encoding Standardized Operating Procedures (SOPs) into prompts, MetaGPT ensures structured collaboration akin to a well-rehearsed ...\",\"ae\":null,\"c\":\"https://medium.com/gta-generative-tech-advances/metagpt-an-interesting-approach-to-multi-agent-collaboration-5ace263c4fd8\",\"d\":\"medium.com/gta-generative-tech-advances/metagpt-an-interesting-approach-to-multi-agent-collaboration-5ace263c4fd8\",\"da\":\"\",\"e\":\"2023-08-15T00:00:00.0000000\",\"h\":0,\"i\":\"medium.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MetaGPT: An Interesting Approach to Multi-Agent Collaboration\",\"u\":\"https://medium.com/gta-generative-tech-advances/metagpt-an-interesting-approach-to-multi-agent-collaboration-5ace263c4fd8\"},{\"a\":\"NVIDIA recently extended TensorRT to text-based applications with TensorRT-LLM for Windows, an open-source library for accelerating LLMs. The latest update to TensorRT-LLM, available now, adds Phi-2 to the growing list of pre-optimized models for PC, which run up to 5x faster compared to other inference backends.\",\"ae\":null,\"c\":\"https://nvidianews.nvidia.com/news/generative-ai-rtx-pcs-and-workstations\",\"d\":\"nvidianews.nvidia.com/news/generative-ai-rtx-pcs-and-workstations\",\"da\":\"\",\"e\":\"2024-01-08T00:00:00.0000000\",\"h\":0,\"i\":\"nvidianews.nvidia.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"NVIDIA Brings Generative AI to Millions, With Tensor Core GPUs, LLMs ...\",\"u\":\"https://nvidianews.nvidia.com/news/generative-ai-rtx-pcs-and-workstations\"},{\"a\":\"The BigDL LLM library extends support for fine-tuning LLMs to a variety of Intel GPUs, including the Intel\\u00ae Data Center GPU Flex 170 and Intel\\u00ae Arc\\u2122 series graphics. Specifically, using the Intel\\u00ae Data Center GPU Flex 170 hardware as an example, you can complete the fine-tuning of the Llama 2 7B model in approximately 2 hours on a single ...\",\"ae\":null,\"b\":\"ark\\tIntel Processor Specification\\tark.intel.com\",\"c\":\"https://www.intel.com/content/www/us/en/developer/articles/technical/finetuning-llms-on-intel-gpus-using-bigdl-llm.html\",\"d\":\"www.intel.com/content/www/us/en/developer/articles/technical/finetuning-llms-on-intel-gpus-using-bigdl-llm.html\",\"da\":\"\",\"e\":\"2023-12-22T00:00:00.0000000\",\"h\":0,\"i\":\"www.intel.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Fine-tuning Llama 2 models on Intel\\u00ae Data Center GPUs using BigDL LLM\",\"u\":\"https://www.intel.com/content/www/us/en/developer/articles/technical/finetuning-llms-on-intel-gpus-using-bigdl-llm.html\"},{\"a\":\"Large language models (LLMs) have strong capabilities in solving diverse natural language processing tasks. However, the safety and security issues of LLM systems have become the major obstacle to their widespread application. Many studies have extensively investigated risks in LLM systems and developed the corresponding mitigation strategies. Leading-edge enterprises such as OpenAI, Google ...\",\"ae\":null,\"b\":\"arx\\tarXiv.org\\tarxiv.org\",\"c\":\"https://arxiv.org/abs/2401.05778\",\"d\":\"arxiv.org/abs/2401.05778\",\"da\":\"translations\",\"e\":\"2024-01-11T09:29:00.0000000\",\"h\":0,\"i\":\"arxiv.org\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"[2401.05778] Risk Taxonomy, Mitigation, and Assessment Benchmarks of ...\",\"u\":\"https://arxiv.org/abs/2401.05778\"},{\"a\":\"It carries on regardless and gives a definitive answer anyway," the BIS paper noted. The tendency of LLMs to make hilariously confident mistakes (eg inventing case law) is sometimes innocuously ...\",\"ae\":null,\"c\":\"https://www.ft.com/content/116f3541-bf2f-483e-a36a-ed3618548f9b\",\"d\":\"www.ft.com/content/116f3541-bf2f-483e-a36a-ed3618548f9b\",\"da\":\"\",\"e\":\"2024-01-05T12:13:51.0000000\",\"h\":0,\"i\":\"www.ft.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"BIS vs LLM - Financial Times\",\"u\":\"https://www.ft.com/content/116f3541-bf2f-483e-a36a-ed3618548f9b\"},{\"a\":\"\\u5927\\u578b\\u8bed\\u8a00\\u6a21\\u578b\\uff08LLM\\uff09\\u7684\\u51fa\\u73b0\\u5e26\\u706b\\u4e86Agent\\u3002\\u5229\\u7528LLM\\u7406\\u89e3\\u4eba\\u7c7b\\u610f\\u56fe\\u3001\\u751f\\u6210\\u590d\\u6742\\u8ba1\\u5212\\u5e76\\u4e14\\u80fd\\u591f\\u81ea\\u4e3b\\u884c\\u52a8\\u7684\\u80fd\\u529b\\u3002Agent\\u5177\\u6709\\u65e0\\u4e0e\\u4f26\\u6bd4\\u7684\\u80fd\\u529b\\uff0c\\u80fd\\u591f\\u505a\\u51fa\\u7c7b\\u4f3c\\u4e8e\\u4eba\\u7c7b\\u590d\\u6742\\u6027\\u7684\\u51b3\\u7b56\\u548c\\u5b8c\\u6210\\u4e00\\u4e9b\\u590d\\u6742\\u7684\\u5de5\\u4f5c\\u3002\\u76ee\\u524d\\u5e02\\u9762\\u4e0a\\u5df2\\u7ecf\\u51fa\\u73b0\\u975e\\u5e38\\u591a\\u5f97Agent\\u6846\\u67b6\\uff1aXAgent, AutoGPT\\u3001BabyAGI\\u3001CAMEL\\u3001MetaGPT\\u3001AutoGen\\u3001DSPy\\u3001AutoAgents\\u3001OpenAgents ...\",\"ae\":null,\"c\":\"https://community.modelscope.cn/659cb258d4226e0eb42708e5.html\",\"d\":\"community.modelscope.cn/659cb258d4226e0eb42708e5.html\",\"da\":\"translations\",\"e\":\"2024-01-09T00:00:00.0000000\",\"h\":0,\"i\":\"community.modelscope.cn\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"\\u5982\\u4f55\\u63d0\\u5347\\u5927\\u6a21\\u578bAgent\\u7684\\u80fd\\u529b \\u2014\\u2014LLM Agent\\u6846\\u67b6 modelscope-agent \\u5b9e\\u6218\",\"u\":\"https://community.modelscope.cn/659cb258d4226e0eb42708e5.html\"},{\"n\":\"/d.js?q=What%20llm%20MetaGPT%20support&kl=wt-wt&l=wt-wt&p=&s=29&ex=-1&ct=US&sp=0&vqd=4-73487995398881375915343809280473758117\"}]);DDG.duckbar.load('images');DDG.duckbar.load('news');DDG.duckbar.load('videos');DDG.duckbar.loadModule('related_searches');if (DDG.pageLayout) DDG.pageLayout.initialize({\"mainline\":{\"items\":[[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"]]}}, { start: 0 });DDG.deep.emit(\"load:completed\");", + "curl-cffi-POST-https://duckduckgo.com-{\"data\": {\"q\": \"dataiku\"}}": "dataiku at DuckDuckGo
", + "curl-cffi-POST-https://duckduckgo.com-{\"data\": {\"q\": \"datarobot\"}}": "datarobot at DuckDuckGo
", + "curl-cffi-GET-https://links.duckduckgo.com/d.js-{\"params\": {\"bing_market\": \"wt-WT\", \"df\": null, \"ex\": \"-1\", \"kl\": \"wt-wt\", \"l\": \"wt-wt\", \"q\": \"datarobot\", \"s\": \"0\", \"sp\": \"0\", \"vqd\": \"4-305438614248843041332096319235456803678\"}}": "DDG.search.altIsNavigational = 1;DDG.search.isNavigational = 1;if (DDG.deep && DDG.deep.setUpstream) DDG.deep.setUpstream(\"bingv7aa\");DDG.deep.bn={'ivc':1};if (DDG.pageLayout) DDG.pageLayout.load('a',[{\"a\":\"\\u9ad8\\u7cbe\\u5ea6\\u306a\\u6a5f\\u68b0\\u5b66\\u7fd2\\u30e2\\u30c7\\u30eb\\u3092\\u69cb\\u7bc9\\u3001\\u5b9f\\u88c5\\u3001\\u904b\\u7528\\u3002DataRobot\\u306f\\u793e\\u5185\\u30c7\\u30fc\\u30bf\\u304b\\u3089\\u65b0\\u3057\\u3044\\u4fa1\\u5024\\u3092\\u5275\\u9020\\u3057\\u307e\\u3059. DataRobot\\u306f\\u4f01\\u696d\\u306e\\u8ab2\\u984c\\u89e3\\u6c7a\\u306b\\u7279\\u5316\\u3002\\u610f\\u601d\\u6c7a\\u5b9a\\u306e\\u81ea\\u52d5\\u5316\\u304b\\u3089\\u9700\\u8981\\u4e88\\u6e2c\\u3001\\u8981\\u56e0\\u5206\\u6790\\u307e\\u3067\\u3053\\u306a\\u3059AI\\u30c4\\u30fc\\u30eb\",\"adext\":{\"callout\":{\"t\":\"Accelerate Time to Impact \\u00b7 Trust Your AI Models\",\"tid\":\"6\"},\"filterlinks\":{\"l\":[],\"tid\":\"\"},\"sitelinks\":{\"l\":[{\"snippet\":\"Explore the DataRobot AI Platform Get Started With a 30-Day Trial\",\"targetUrl\":\"https://duckduckgo.com/y.js?ad_domain=datarobot.com&ad_provider=bingv7aa&ad_type=txad&eddgt=4t4PwhnMmcY7atJfp41CAw%3D%3D&rut=e62e2cb72625106a43222549f786956db024f90f62131cbc4a16a28c8968f74c&u3=https%3A%2F%2Fwww.bing.com%2Faclick%3Fld%3De8CWdnSSqYRxodv2hK6GQYijVUCUw3ed58BZbCDycwDIpFPyWnwhXm4uvKBLQcGynMpDz_AvA1H9RQnhR9goEmV9Ya3_wHg3eI0DOkTWJinjiudX5vGJJNWufOQNDYFdDHFVXChhUck3LNxc3D4UakPcG3Fgqa6IzJHoPcJWXkWGOqxuYGxVGkxJO3aqSinbclEqi5d0At_9OjMeDihix9q58SuTw%26u%3DaHR0cHMlM2ElMmYlMmZ3d3cuZGF0YXJvYm90LmNvbSUyZnRyaWFsJTJmJTNmdXRtX21lZGl1bSUzZHNlYXJjaCUyNnV0bV9zb3VyY2UlM2RiaW5nJTI2dXRtX2NhbXBhaWduJTNkRnJlZVRyaWFsMjAyM1dXMDgxNkdQU2FkZXh0JTI2Y2FtcGFpZ25pZCUzZDUzMDcwODA5OSUyNmFkZ3JvdXBpZCUzZDEzNTAyMDI3NzQyMTc2OTglMjZhZGlkJTNkJTI2bXNjbGtpZCUzZDFlNjcxOTNiY2UzZDFlNzBhZTQ2N2M5ODMxM2UxOTI0%26rlid%3D1e67193bce3d1e70ae467c98313e1924&vqd=4-304301655096002530435728103296846286110&iurl=%7B1%7DIG%3DEFC9E56534984E60A01E1AD18F949C41%26CID%3D17342A5B7AD96D85041C3E5D7B5C6C74%26ID%3DDevEx%2C5065.1\",\"text\":\"DataRobot Free Trial\"},{\"snippet\":\"Unlock Your AI Success in 2023 Tips on the Path of Value-Driven AI\",\"targetUrl\":\"https://duckduckgo.com/y.js?ad_domain=datarobot.com&ad_provider=bingv7aa&ad_type=txad&eddgt=4t4PwhnMmcY7atJfp41CAw%3D%3D&rut=25d9bbfaa9cbfaaf08d9a6b444b633a6ce8d72930552b70b34d90df428188bf0&u3=https%3A%2F%2Fwww.bing.com%2Faclick%3Fld%3De8qLHGkuDcZ8xxbn%2DtJPQw1jVUCUxi3KfzBWZ8dNgaK8%2DSoRj4nbRrRFfQVuiV7AIxRtZS4mXd7F1paHlN1BDqwOZNm7_O3fvHO59UXgQmhFNY1DQ7kMdZ%2Did8G0JnG_Kzxief1jx20D1mrfhnsLHb6_5GY1RZ4vzYXUnjih6Hh8nrgoRbxzYS5s8evGIka2E8%2Dq%2DIbZtX%2DEHSMg1sz2yxt770lqc%26u%3DaHR0cHMlM2ElMmYlMmZ3d3cuZGF0YXJvYm90LmNvbSUyZnJlc291cmNlcyUyZmFpc3VjY2VzczIwMjMlMmYlM2Z1dG1fbWVkaXVtJTNkc2VhcmNoJTI2dXRtX3NvdXJjZSUzZGJpbmclMjZ1dG1fY2FtcGFpZ24lM2RDb250ZW50MTBLZXlzdG9BSVN1Y2Nlc3MyMDIzV1cwNTIyR1BTYWRleHQlMjZ1dG1fdGVybSUzZGRhdGFyb2JvdCUyNnV0bV9jb250ZW50JTNkYWRfZXh0JTI2Y2FtcGFpZ25pZCUzZDUzMDcwODA5OSUyNmFkZ3JvdXBpZCUzZDEzNTAyMDI3NzQyMTc2OTglMjZhZGlkJTNkJTI2bXNjbGtpZCUzZDBiNTgwOTJlZGMzNjFlNGFiOTgyNmM4ZDIzMzViNTFm%26rlid%3D0b58092edc361e4ab9826c8d2335b51f&vqd=4-17900397268746114651881769682671895645&iurl=%7B1%7DIG%3DEFC9E56534984E60A01E1AD18F949C41%26CID%3D17342A5B7AD96D85041C3E5D7B5C6C74%26ID%3DDevEx%2C5067.1\",\"text\":\"10 Keys to AI Success\"},{\"snippet\":\"Our Platform Includes Four Fully Integrated Products. Read More.\",\"targetUrl\":\"https://duckduckgo.com/y.js?ad_domain=datarobot.com&ad_provider=bingv7aa&ad_type=txad&eddgt=4t4PwhnMmcY7atJfp41CAw%3D%3D&rut=9b50c0e6e845f83a4b6122341b7007b55cc637bf24bc814ebfbdb41c4570e842&u3=https%3A%2F%2Fwww.bing.com%2Faclick%3Fld%3De80Tber2J5eyzs7ehxic3bQzVUCUyYDJpssb8FQM4q5TzHPQTbxhVuzWgr30VBdcQ%2Du_fAfiqmWEHQ13X%2DWe_zzqhxfJqe8TH1WdLsIIKUrxWqqxfZyPQuZ818htUh82k2s2Co_K3ZgklXSA%2Duj9j4sghZ155%2DCpGDXbSizpxOVw8TwgUDyW_ZxEZDxtS0Rk5iH4G6PuzQvhP02YdMD_rMZTOt42M%26u%3DaHR0cHMlM2ElMmYlMmZ3d3cuZGF0YXJvYm90LmNvbSUyZnByb2R1Y3QlMmYlM2ZjYW1wYWlnbmlkJTNkNTMwNzA4MDk5JTI2YWRncm91cGlkJTNkMTM1MDIwMjc3NDIxNzY5OCUyNmFkaWQlM2QlMjZtc2Nsa2lkJTNkZDQ0ZDJiNzQ0YjEzMWFmZTcyZDQ0ZWQ3YjQxMDY3MDI%26rlid%3Dd44d2b744b131afe72d44ed7b4106702&vqd=4-135405568356283083312160903053804829572&iurl=%7B1%7DIG%3DEFC9E56534984E60A01E1AD18F949C41%26CID%3D17342A5B7AD96D85041C3E5D7B5C6C74%26ID%3DDevEx%2C5069.1\",\"text\":\"Product Overview\"}],\"tid\":\"7\\t9[8]\\t11[10]\\t13[12]\",\"type\":\"EnhancedSiteLink\"},\"tid\":\"1\"},\"ae\":{\"callout\":[\"Accelerate Time to Impact \\u00b7 Trust Your AI Models\"]},\"c\":\"https://duckduckgo.com/y.js?ad_domain=datarobot.com&ad_provider=bingv7aa&ad_type=txad&eddgt=4t4PwhnMmcY7atJfp41CAw%3D%3D&rut=4e8bc6239074926ffa0595d2f0838a03d95a9f20653372406dff4b73bb47a802&u3=https%3A%2F%2Fwww.bing.com%2Faclick%3Fld%3De87ziwl19iJFSSt9AnpU7A2TVUCUyhd2uAWuXw7lbtLG3tobaae8IKDFy1RftUyaK7hmjFguIcBXLMF0__8U%2DYMqp1lRmGet90G40qSPB8wuC4MyZjuA8D06WqXRZsdl4uyGEuNXLmrp1n4swCoXfw3cJtq1Sl1iqwNQBdH4Ev%2DJQUf54N5TQ274OfwCR8PMamVlYCWg%26u%3DaHR0cHMlM2ElMmYlMmZ3d3cuZGF0YXJvYm90LmNvbSUyZmpwJTJmbHAlMmZhaS1mb3ItYnVzaW5lc3MlMmYlM2Z1dG1fbWVkaXVtJTNkc2VhcmNoJTI2dXRtX3NvdXJjZSUzZGJpbmclMjZ1dG1fY2FtcGFpZ24lM2RERU1PMjAyM0FsbFByb2R1Y3RzSlAwNjI2QlBTJTI2dXRtX3Rlcm0lM2RkYXRhcm9ib3QlMjZ1dG1fY29udGVudCUzZERSX2JyYW5kZWRfcnNhJTI2Y2FtcGFpZ25pZCUzZDUzMDcwODA5OSUyNmFkZ3JvdXBpZCUzZDEzNTAyMDI3NzQyMTc2OTglMjZhZGlkJTNkJTI2bXNjbGtpZCUzZDdlMDA4NWE0OTQ3MzE5ODdlNzJjZTAwNzZiNzAxMmFm%26rlid%3D7e0085a494731987e72ce0076b7012af&vqd=4-129716528454193272263152190229962811420&iurl=%7B1%7DIG%3DEFC9E56534984E60A01E1AD18F949C41%26CID%3D17342A5B7AD96D85041C3E5D7B5C6C74%26ID%3DDevEx%2C5060.1\",\"d\":\"datarobot.com\",\"h\":0,\"i\":\"\",\"k\":0,\"m\":0,\"o\":\"\",\"p\":1,\"relevancy\":{\"abstract\":\"%E9%AB%98%E7%B2%BE%E5%BA%A6%E3%81%AA%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%E3%83%A2%E3%83%87%E3%83%AB%E3%82%92%E6%A7%8B%E7%AF%89%E3%80%81%E5%AE%9F%E8%A3%85%E3%80%81%E9%81%8B%E7%94%A8%E3%80%82%3Cb%3EDataRobot%3C%2Fb%3E%E3%81%AF%E7%A4%BE%E5%86%85%E3%83%87%E3%83%BC%E3%82%BF%E3%81%8B%E3%82%89%E6%96%B0%E3%81%97%E3%81%84%E4%BE%A1%E5%80%A4%E3%82%92%E5%89%B5%E9%80%A0%E3%81%97%E3%81%BE%E3%81%99.%20DataRobot%E3%81%AF%E4%BC%81%E6%A5%AD%E3%81%AE%E8%AA%B2%E9%A1%8C%E8%A7%A3%E6%B1%BA%E3%81%AB%E7%89%B9%E5%8C%96%E3%80%82%E6%84%8F%E6%80%9D%E6%B1%BA%E5%AE%9A%E3%81%AE%E8%87%AA%E5%8B%95%E5%8C%96%E3%81%8B%E3%82%89%E9%9C%80%E8%A6%81%E4%BA%88%E6%B8%AC%E3%80%81%E8%A6%81%E5%9B%A0%E5%88%86%E6%9E%90%E3%81%BE%E3%81%A7%E3%81%93%E3%81%AA%E3%81%99AI%E3%83%84%E3%83%BC%E3%83%AB\",\"adx_name\":\"none\",\"is_good_v10\":1,\"organic_ranks\":[\"0\",1,2,3,5,6,7,8,9,10,11,12,13,14,15,18],\"q\":\"datarobot\",\"q_words\":1,\"q_words_fuzzy\":1,\"q_words_in_ad\":1,\"root_domain\":\"datarobot.com\",\"start\":\"0\",\"title\":\"%E3%83%87%E3%83%BC%E3%82%BF%E3%81%8B%E3%82%89%E6%96%B0%E3%81%97%E3%81%84%E4%BE%A1%E5%80%A4%E3%82%92%20%2D%20%E7%A4%BE%E5%86%85%E3%83%87%E3%83%BC%E3%82%BF%E3%81%8B%E3%82%89%E4%BE%A1%E5%80%A4%E5%89%B5%E5%87%BA\"},\"s\":\"bingv7aa\",\"t\":\"\\u30c7\\u30fc\\u30bf\\u304b\\u3089\\u65b0\\u3057\\u3044\\u4fa1\\u5024\\u3092 - \\u793e\\u5185\\u30c7\\u30fc\\u30bf\\u304b\\u3089\\u4fa1\\u5024\\u5275\\u51fa\",\"tid\":\"1,6,7,9[8],11[10],13[12]\",\"u\":\"https://duckduckgo.com/y.js?ad_domain=datarobot.com&ad_provider=bingv7aa&ad_type=txad&eddgt=4t4PwhnMmcY7atJfp41CAw%3D%3D&rut=4e8bc6239074926ffa0595d2f0838a03d95a9f20653372406dff4b73bb47a802&u3=https%3A%2F%2Fwww.bing.com%2Faclick%3Fld%3De87ziwl19iJFSSt9AnpU7A2TVUCUyhd2uAWuXw7lbtLG3tobaae8IKDFy1RftUyaK7hmjFguIcBXLMF0__8U%2DYMqp1lRmGet90G40qSPB8wuC4MyZjuA8D06WqXRZsdl4uyGEuNXLmrp1n4swCoXfw3cJtq1Sl1iqwNQBdH4Ev%2DJQUf54N5TQ274OfwCR8PMamVlYCWg%26u%3DaHR0cHMlM2ElMmYlMmZ3d3cuZGF0YXJvYm90LmNvbSUyZmpwJTJmbHAlMmZhaS1mb3ItYnVzaW5lc3MlMmYlM2Z1dG1fbWVkaXVtJTNkc2VhcmNoJTI2dXRtX3NvdXJjZSUzZGJpbmclMjZ1dG1fY2FtcGFpZ24lM2RERU1PMjAyM0FsbFByb2R1Y3RzSlAwNjI2QlBTJTI2dXRtX3Rlcm0lM2RkYXRhcm9ib3QlMjZ1dG1fY29udGVudCUzZERSX2JyYW5kZWRfcnNhJTI2Y2FtcGFpZ25pZCUzZDUzMDcwODA5OSUyNmFkZ3JvdXBpZCUzZDEzNTAyMDI3NzQyMTc2OTglMjZhZGlkJTNkJTI2bXNjbGtpZCUzZDdlMDA4NWE0OTQ3MzE5ODdlNzJjZTAwNzZiNzAxMmFm%26rlid%3D7e0085a494731987e72ce0076b7012af&vqd=4-129716528454193272263152190229962811420&iurl=%7B1%7DIG%3DEFC9E56534984E60A01E1AD18F949C41%26CID%3D17342A5B7AD96D85041C3E5D7B5C6C74%26ID%3DDevEx%2C5060.1\"}], {\"page_load_url\":\"https://duckduckgo.com/y.js?ifu=%7B3%7Dappid%3D055AAD1BA669BEB8B048128DC89A107C678B527B%26rguid%3Dbff00ba70e8f4285a2ad22f803500ac4&iurl=%7B2%7DIG%3DEFC9E56534984E60A01E1AD18F949C41%26CID%3D17342A5B7AD96D85041C3E5D7B5C6C74%26Type%3DEvent.CPT%26DATA%3D0\",\"visibility_url\":\"https://duckduckgo.com/y.js?ivu=%7B4%7Dtype%3Dmv%26reqver%3D1.0%26rg%3Dbff00ba70e8f4285a2ad22f803500ac4\"});DDG.deep.signalSummary = \"\";DDG.inject('DDG.Data.languages.resultLanguages', {\"en\":[\"https://www.datarobot.com/\",\"https://www.datarobot.com/trial/\",\"https://www.datarobot.com/pricing/\",\"https://www.datarobot.com/platform/new/datarobot-9-0/\",\"https://www.linkedin.com/company/datarobot/\",\"https://docs.datarobot.com/\",\"https://docs.datarobot.com/en/docs/get-started/index.html\",\"https://www.datarobot.com/newsroom/press/datarobot-launches-pathfinder-a-comprehensive-library-of-100-ai-use-cases/\",\"https://www.datarobot.com/blog/introducing-the-datarobot-use-case-value-tracker/\",\"https://docs.datarobot.com/en/docs/data/index.html\",\"https://app.datarobot.com/\",\"https://pathfinder.datarobot.com/integrations\",\"https://docs.datarobot.com/en/docs/api/api-quickstart/index.html\",\"https://docs.datarobot.com/en/docs/data/connect-data/data-conn.html\",\"https://app.datarobot.com/sign-in\",\"https://learn.datarobot.com/\",\"https://www.carahsoft.com/datarobot\",\"https://www.afa.org/company/datarobot/\",\"https://www.datarobot.com/use-cases/\",\"https://www.nomuraholdings.com/top.html\",\"https://www.nomura.com/\"],\"es\":[\"https://vivevirtual.es/inteligencia-artificial/tutoriales-ia/como-integrar-datarobot-en-squarespace/\"]});DDG.deep.pageLayoutSummary = \"a1w22v1r1\";DDG.inject('DDG.Data.languages.adLanguages', {});if (DDG.pageLayout) DDG.pageLayout.load('d',[{\"a\":\"DataRobot is a leading platform for generative and predictive AI that lets you focus on tangible business outcomes, not infrastructure. Learn how DataRobot helps customers across industries and organizations scale AI with enterprise monitoring, governance, and open ecosystems.\",\"ae\":null,\"c\":\"https://www.datarobot.com/\",\"d\":\"www.datarobot.com\",\"da\":\"\",\"h\":0,\"i\":\"www.datarobot.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"DataRobot AI Platform | Deliver Value from AI\",\"u\":\"https://www.datarobot.com/\"},{\"a\":\"DataRobot is a single platform that streamlines your predictive and generative AI workflows. Start your free 30-day trial to experience how to fast-track preparing data, running experiments, and testing models, and to automate your AI processes with a single solution.\",\"ae\":null,\"c\":\"https://www.datarobot.com/trial/\",\"d\":\"www.datarobot.com/trial/\",\"da\":\"\",\"h\":0,\"i\":\"www.datarobot.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"DataRobot AI Platform Free Trial | DataRobot AI Platform\",\"u\":\"https://www.datarobot.com/trial/\"},{\"a\":\"DataRobot offers a range of pricing plans to suit your business needs and goals, from Essentials to Business Critical. Learn how to customize your solution, get support, and see the ROI and savings of DataRobot AI Platform.\",\"ae\":null,\"c\":\"https://www.datarobot.com/pricing/\",\"d\":\"www.datarobot.com/pricing/\",\"da\":\"\",\"h\":0,\"i\":\"www.datarobot.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Pricing | DataRobot AI Platform\",\"u\":\"https://www.datarobot.com/pricing/\"},{\"a\":\"DataRobot AI Platform 9.0 is a complete and open AI lifecycle platform that leverages machine learning and supports Experimentation, Production, and Compliance. Learn about the new features, such as collaborative experimentation, workbench, data preparation, notebooks, drift management, and more, and how they integrate with Snowflake, GitHub, SAP, and Kubernetes.\",\"ae\":null,\"c\":\"https://www.datarobot.com/platform/new/datarobot-9-0/\",\"d\":\"www.datarobot.com/platform/new/datarobot-9-0/\",\"da\":\"translations\",\"h\":0,\"i\":\"www.datarobot.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"DataRobot AI Platform 9.0 Release | DataRobot AI Platform\",\"u\":\"https://www.datarobot.com/platform/new/datarobot-9-0/\"},{\"a\":\"DataRobot is the Value-Driven AI company, empowering organizations to accelerate AI from idea to impact. With over a decade at the forefront of AI innovation, we know what it takes to make a real ...\",\"ae\":null,\"b\":\"li\\tLinkedIn\\twww.linkedin.com\",\"c\":\"https://www.linkedin.com/company/datarobot/\",\"d\":\"www.linkedin.com/company/datarobot/\",\"da\":\"\",\"h\":0,\"i\":\"www.linkedin.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"DataRobot | LinkedIn\",\"u\":\"https://www.linkedin.com/company/datarobot/\"},{\"a\":\"Learn how to use DataRobot, a platform for data science and machine learning, with UI and API docs, tutorials, and release information. Find out how to get started, manage the platform, and access additional resources for modeling success.\",\"ae\":null,\"c\":\"https://docs.datarobot.com/\",\"d\":\"docs.datarobot.com\",\"da\":\"\",\"h\":0,\"i\":\"docs.datarobot.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"DataRobot Product Documentation\",\"u\":\"https://docs.datarobot.com/\"},{\"a\":\"Learn how to use DataRobot's value-driven AI to analyze data, create and deploy models, and work with code-first notebooks. Explore the topics of workbench, data preparation, model building, model exploration, model deployment, and more.\",\"ae\":null,\"c\":\"https://docs.datarobot.com/en/docs/get-started/index.html\",\"d\":\"docs.datarobot.com/en/docs/get-started/index-html\",\"da\":\"\",\"e\":\"2023-08-21T00:00:00.0000000\",\"h\":0,\"i\":\"docs.datarobot.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Get started: DataRobot docs\",\"u\":\"https://docs.datarobot.com/en/docs/get-started/index.html\"},{\"a\":\"DataRobot is the leader in enterprise AI, delivering trusted AI technology and enablement services to global enterprises competing in today's Intelligence Revolution. DataRobot's enterprise AI platform democratizes data science with end-to-end automation for building, deploying, and managing machine learning models. This platform maximizes ...\",\"ae\":null,\"c\":\"https://www.datarobot.com/newsroom/press/datarobot-launches-pathfinder-a-comprehensive-library-of-100-ai-use-cases/\",\"d\":\"www.datarobot.com/newsroom/press/datarobot-launches-pathfinder-a-comprehensive-library-of-100-ai-use-cases/\",\"da\":\"translations\",\"h\":0,\"i\":\"www.datarobot.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"DataRobot Launches Pathfinder: A Comprehensive Library of 100+ AI Use ...\",\"u\":\"https://www.datarobot.com/newsroom/press/datarobot-launches-pathfinder-a-comprehensive-library-of-100-ai-use-cases/\"},{\"a\":\"In Release 6.1, we are thrilled to introduce the DataRobot Use Case Value Tracker, designed to help you with the business operationalization of your AI.As a centralized hub to collaborate with team members on any machine learning initiative from start to finish, it provides a systematic way to manage, monitor, and track the return on investment generated from your AI efforts.\",\"ae\":null,\"c\":\"https://www.datarobot.com/blog/introducing-the-datarobot-use-case-value-tracker/\",\"d\":\"www.datarobot.com/blog/introducing-the-datarobot-use-case-value-tracker/\",\"da\":\"\",\"h\":0,\"i\":\"www.datarobot.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Introducing the DataRobot Use Case Value Tracker\",\"u\":\"https://www.datarobot.com/blog/introducing-the-datarobot-use-case-value-tracker/\"},{\"a\":\"Learn how to import, transform, analyze, and manage data for machine learning projects using DataRobot tools and visualizations. Find out the data requirements, limitations, and best practices for different data types, sources, and scenarios.\",\"ae\":null,\"c\":\"https://docs.datarobot.com/en/docs/data/index.html\",\"d\":\"docs.datarobot.com/en/docs/data/index-html\",\"da\":\"\",\"e\":\"2023-06-15T00:00:00.0000000\",\"h\":0,\"i\":\"docs.datarobot.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Data: DataRobot docs\",\"u\":\"https://docs.datarobot.com/en/docs/data/index.html\"},{\"a\":\"DataRobot\",\"ae\":null,\"c\":\"https://app.datarobot.com/\",\"d\":\"app.datarobot.com\",\"da\":\"\",\"h\":0,\"i\":\"app.datarobot.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"DataRobot\",\"u\":\"https://app.datarobot.com/\"},{\"a\":\"DataRobot does not share customer data, personal data, or sensitive use cases on Pathfinder. The AI applications and frameworks shared in Pathfinder are widespread and commonplace across their respective industries. This tool was developed to provide a framework for how you can solve AI use cases with problems that are within the context of ...\",\"ae\":null,\"c\":\"https://pathfinder.datarobot.com/integrations\",\"d\":\"pathfinder.datarobot.com/integrations\",\"da\":\"\",\"h\":0,\"i\":\"pathfinder.datarobot.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Integrations | Explore 100+ AI Use Cases | DataRobot Pathfinder\",\"u\":\"https://pathfinder.datarobot.com/integrations\"},{\"a\":\"API quickstart. The DataRobot API provides a programmatic alternative to the web interface for creating and managing DataRobot projects. The API can be used via REST or with DataRobot's Python or R clients in Windows, UNIX, and OS X environments. This guide walks you through setting up your environment and then you can follow a sample problem ...\",\"ae\":null,\"c\":\"https://docs.datarobot.com/en/docs/api/api-quickstart/index.html\",\"d\":\"docs.datarobot.com/en/docs/api/api-quickstart/index-html\",\"da\":\"\",\"e\":\"2023-08-07T00:00:00.0000000\",\"h\":0,\"i\":\"docs.datarobot.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"API Quickstart: DataRobot docs\",\"u\":\"https://docs.datarobot.com/en/docs/api/api-quickstart/index.html\"},{\"a\":\"From the Data Connections tab, select the data connection in the left-panel connections list. Click the Delete button in the upper right ( ). DataRobot prompts for confirmation. Click Delete to remove the data connection. If there are data sources dependent on the data connection, DataRobot returns a notification.\",\"ae\":null,\"c\":\"https://docs.datarobot.com/en/docs/data/connect-data/data-conn.html\",\"d\":\"docs.datarobot.com/en/docs/data/connect-data/data-conn.html\",\"da\":\"\",\"e\":\"2023-11-15T00:00:00.0000000\",\"h\":0,\"i\":\"docs.datarobot.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Data connections: DataRobot docs - DataRobot AI Platform\",\"u\":\"https://docs.datarobot.com/en/docs/data/connect-data/data-conn.html\"},{\"a\":\"Sign in to DataRobot, the leading enterprise AI platform that democratizes data science and automates machine learning. DataRobot offers comprehensive solutions for MLOps, AI use cases, and AI cloud. Learn from DataRobot University and Algorithmia, and join the Intelligence Revolution.\",\"ae\":null,\"c\":\"https://app.datarobot.com/sign-in\",\"d\":\"app.datarobot.com/sign-in\",\"da\":\"\",\"h\":0,\"i\":\"app.datarobot.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"DataRobot\",\"u\":\"https://app.datarobot.com/sign-in\"},{\"a\":\"This article will help you better navigate DataRobot University. View Details. What's New in 9.0. Class. Learn about new features in the DataRobot 9.0 release. View Details. Integrating Snowflake with DataRobot. Class. Connect Snowflake to DataRobot to build models and make predictions.\",\"ae\":null,\"c\":\"https://learn.datarobot.com/\",\"d\":\"learn.datarobot.com\",\"da\":\"\",\"h\":0,\"i\":\"learn.datarobot.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"DataRobot University\",\"u\":\"https://learn.datarobot.com/\"},{\"a\":\"DataRobot is the leader in enterprise AI, delivering trusted AI technology and ROI enablement services to global enterprises. DataRobot's enterprise AI platform democratizes data science with end-to-end automation for building, deploying, and managing machine learning models. This platform maximizes value to the mission by delivering AI at ...\",\"ae\":null,\"c\":\"https://www.carahsoft.com/datarobot\",\"d\":\"www.carahsoft.com/datarobot\",\"da\":\"\",\"h\":0,\"i\":\"www.carahsoft.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"DataRobot - Enterprise AI Cloud Platform | Carahsoft\",\"u\":\"https://www.carahsoft.com/datarobot\"},{\"a\":\"DataRobot is the leading Augmented Intelligence platform, delivering trusted AI technology and ROI enablement services to global enterprises competing in today's Intelligence Revolution. Its enterprise AI platform maximizes business value by delivering AI at scale and continuously optimizing performance over time.\",\"ae\":null,\"c\":\"https://www.afa.org/company/datarobot/\",\"d\":\"www.afa.org/company/datarobot/\",\"da\":\"\",\"e\":\"2024-01-09T00:00:00.0000000\",\"h\":0,\"i\":\"www.afa.org\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"DataRobot - Air & Space Forces Association\",\"u\":\"https://www.afa.org/company/datarobot/\"},{\"a\":\"DataRobot is a platform for generative and predictive AI that helps you deploy and run AI solutions across various industries and outcomes. Learn how DataRobot customers use their platform to solve business problems, innovate, and drive value with AI.\",\"ae\":null,\"c\":\"https://www.datarobot.com/use-cases/\",\"d\":\"www.datarobot.com/use-cases/\",\"da\":\"\",\"h\":0,\"i\":\"www.datarobot.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Machine Learning Use Cases | DataRobot AI Platform\",\"u\":\"https://www.datarobot.com/use-cases/\"},{\"a\":\"Para integrar DataRobot en tu sitio de Squarespace, iniciar con la configuraci\\u00f3n de una cuenta en la plataforma de DataRobot es esencial. El proceso es directo y f\\u00e1cil de seguir y asegurar\\u00e1 que dispongas de todas las herramientas anal\\u00edticas avanzadas para potenciar tu sitio web.. Crear Tu Cuenta de DataRobot. Visita el sitio web de DataRobot y localiza la opci\\u00f3n de registro.\",\"ae\":null,\"c\":\"https://vivevirtual.es/inteligencia-artificial/tutoriales-ia/como-integrar-datarobot-en-squarespace/\",\"d\":\"vivevirtual.es/inteligencia-artificial/tutoriales-ia/como-integrar-datarobot-en-squarespace/\",\"da\":\"\",\"e\":\"2024-01-11T00:00:00.0000000\",\"h\":0,\"i\":\"vivevirtual.es\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"C\\u00f3mo Integrar DataRobot En Squarespace \\ufe0f 2024 - \\u00a9Vive Virtual\",\"u\":\"https://vivevirtual.es/inteligencia-artificial/tutoriales-ia/como-integrar-datarobot-en-squarespace/\"},{\"a\":\"Nomura is a global financial services group with an integrated network spanning over 30 countries. By connecting markets East & West, Nomura services the needs of individuals, institutions, corporates and governments through its four business divisions: Retail, Asset Management, Wholesale (Global Markets and Investment Banking), and Merchant Banking.\",\"ae\":null,\"c\":\"https://www.nomuraholdings.com/top.html\",\"d\":\"www.nomuraholdings.com/top-html\",\"da\":\"\",\"h\":0,\"i\":\"www.nomuraholdings.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Home | NOMURA\",\"u\":\"https://www.nomuraholdings.com/top.html\"},{\"a\":\"Nomura is a global financial services group with an integrated network spanning over 30 countries. By connecting markets East & West, Nomura services the needs of individuals, institutions, corporates and governments through its four business divisions: Retail, Asset Management, Wholesale (Global Markets and Investment Banking), and Merchant ...\",\"ae\":null,\"c\":\"https://www.nomura.com/\",\"d\":\"www.nomura.com\",\"da\":\"\",\"h\":0,\"i\":\"www.nomura.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Home - NOMURA\",\"u\":\"https://www.nomura.com/\"},{\"n\":\"/d.js?q=datarobot&kl=wt-wt&l=wt-wt&p=&s=22&ex=-1&ct=US&sp=0&vqd=4-305438614248843041332096319235456803678\"}]);DDG.duckbar.load('images');DDG.duckbar.load('news');DDG.duckbar.load('videos', {\"ads\":[],\"query\":\"datarobot\",\"queryEncoded\":\"datarobot\",\"response_type\":\"places\",\"results\":[{\"content\":\"https://www.youtube.com/watch?v=vyi_0D-rJ1A\",\"description\":\"Demonstration of how the DataRobot AI Platform works covering both ML Experimentation and ML Production. Request a live, personalized demonstration at https://www.datarobot.com/request-a-demo. This demo shows the workflows in DataRobot data ingest and preparation, model development, insight extraction, model deployment, on-going monitoring and ...\",\"duration\":\"13:17\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/vyi_0D-rJ1A?autoplay=1\",\"image_token\":\"46e31c38546fa6c15f77d2eaca52158f719c396ddbc4e84f07f660a20f050f23\",\"images\":{\"large\":\"https://tse2.mm.bing.net/th?id=OVP.RjJmdFbBiUCndVAZOmjitwEsDh&pid=Api\",\"medium\":\"https://tse2.mm.bing.net/th?id=OVP.RjJmdFbBiUCndVAZOmjitwEsDh&pid=Api\",\"motion\":\"https://tse2.mm.bing.net/th?id=OM2.GlK_gSFTQdYbbg_1695678156&pid=Api\",\"small\":\"https://tse2.mm.bing.net/th?id=OVP.RjJmdFbBiUCndVAZOmjitwEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-06-13T21:14:17.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":5574},\"title\":\"DataRobot AI Platform Demo 2023 | End-to-end Workflow | How DataRobot Works\",\"uploader\":\"DataRobot\"},{\"content\":\"https://www.youtube.com/watch?v=cOV5dss8xo0\",\"description\":\"DataRobot offers a platform and a reusable framework for developing AI applications that have a generative and a predictive component to them. Watch the process of creating a fully functioning joint generative and predictive AI solution for a real-world business problem that delivers tangible value. Use this framework and process to deliver ...\",\"duration\":\"5:06\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/cOV5dss8xo0?autoplay=1\",\"image_token\":\"1f5b660aa742a554ce19c9e8b001eb5d5a89059afab5ab4ef499f34bbef8ae2d\",\"images\":{\"large\":\"https://tse1.mm.bing.net/th?id=OVP.z0cOprAHCz_P_Hpd5bMHWgEsDh&pid=Api\",\"medium\":\"https://tse1.mm.bing.net/th?id=OVP.z0cOprAHCz_P_Hpd5bMHWgEsDh&pid=Api\",\"motion\":\"https://tse1.mm.bing.net/th?id=OM1.pQ6zRQHggxqCCw_1696646714&pid=Api\",\"small\":\"https://tse1.mm.bing.net/th?id=OVP.z0cOprAHCz_P_Hpd5bMHWgEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-08-18T22:22:31.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":7622},\"title\":\"End-to-end Generative AI Applications with DataRobot | Develop, Deploy, Monitor and Maintain\",\"uploader\":\"DataRobot\"},{\"content\":\"https://www.youtube.com/watch?v=IMP0OZC6wPw\",\"description\":\"DataRobot is the leading end-to-end enterprise AI/ML platform that automates the process of building, training and deploying AI models at scale. Download the data and slides here: https://drive.google.com/drive/folders/1Zl1XO24zkbY7fHEh59Ux3NssNPXQD19t?usp=sharing In this video, we will learn how to build, train and deploy a machine learning ...\",\"duration\":\"21:22\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/IMP0OZC6wPw?autoplay=1\",\"image_token\":\"1a5261fccec96060141db7cec373afd6834e80eed802a6ed6c7ee3f67f6228ec\",\"images\":{\"large\":\"https://tse4.mm.bing.net/th?id=OVP.uJ2Vw0goB-5yx8dORs0CPgHgFo&pid=Api\",\"medium\":\"https://tse4.mm.bing.net/th?id=OVP.uJ2Vw0goB-5yx8dORs0CPgHgFo&pid=Api\",\"motion\":\"https://tse4.mm.bing.net/th?id=OM2.21pythA4GgwdwQ&pid=Api\",\"small\":\"https://tse4.mm.bing.net/th?id=OVP.uJ2Vw0goB-5yx8dORs0CPgHgFo&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2021-08-08T15:42:18.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":18696},\"title\":\"DataRobot AI For Absolute Beginners (Part 1) | Build, Train & Deploy an AI in 30 Minutes\",\"uploader\":\"Prof. Ryan Ahmed\"},{\"content\":\"https://www.youtube.com/watch?v=Grip5G1EUgY\",\"description\":\"Machine learning models developed with DataRobot can be deployed to Azure ML with complete service health, drift and accuracy monitoring. In this video Brian Bell demonstrates the Azure ML deployment capability. Users can create a DataRobot-managed AzureML prediction environment to deploy DataRobot Scoring Code in AzureML. With DataRobot ...\",\"duration\":\"3:22\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/Grip5G1EUgY?autoplay=1\",\"image_token\":\"8db48ef7fbe873efad3cfa34f62c3b13b4e77e556767a5d60a215edbb13b3e14\",\"images\":{\"large\":\"https://tse3.mm.bing.net/th?id=OVP.klj-M0G2PlRq58QgzSlIwAEsDh&pid=Api\",\"medium\":\"https://tse3.mm.bing.net/th?id=OVP.klj-M0G2PlRq58QgzSlIwAEsDh&pid=Api\",\"motion\":\"https://tse3.mm.bing.net/th?id=OM.-_UGz9qtcxRXvw_1704026949&pid=Api\",\"small\":\"https://tse3.mm.bing.net/th?id=OVP.klj-M0G2PlRq58QgzSlIwAEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-06-09T20:38:50.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":409},\"title\":\"Deploy DataRobot Models to AzureML | Demonstration of Azure ML Deployment Capability\",\"uploader\":\"DataRobot\"},{\"content\":\"https://www.youtube.com/watch?v=xZUaBvPQfXY\",\"description\":\"Get a high-level introduction to DataRobot's AI Production through a tour of several live Deployments. See the monitoring, alerting, lifecycle management and reporting capabilities in action for both predictive and generative AI via a series of examples. Learn more at: https://www.datarobot.com/platform/ https://docs.datarobot.com/en/docs/mlops ...\",\"duration\":\"7:08\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/xZUaBvPQfXY?autoplay=1\",\"image_token\":\"faecb8d567fd4445c52cd8e60c649952b393188212856a19ce331371c4b4b21b\",\"images\":{\"large\":\"https://tse3.mm.bing.net/th?id=OVP.V95ETtZnbbbKDAA2lzjFHgEsDh&pid=Api\",\"medium\":\"https://tse3.mm.bing.net/th?id=OVP.V95ETtZnbbbKDAA2lzjFHgEsDh&pid=Api\",\"motion\":\"https://tse3.mm.bing.net/th?id=OM1.oMwA1JZw78kwsw_1701730108&pid=Api\",\"small\":\"https://tse3.mm.bing.net/th?id=OVP.V95ETtZnbbbKDAA2lzjFHgEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-11-09T05:05:53.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":329},\"title\":\"Operate AI with DataRobot | Welcome to the AI Operations Console for New DataRobot Users\",\"uploader\":\"DataRobot\"},{\"content\":\"https://www.youtube.com/watch?v=Y00VSO6Uq60\",\"description\":\"Complete all phases of building, operating and governing a Predictive AI solution following the starter Flight Delays Use Case. This starter use case showcases essential DataRobot capabilities, but is not comprehensive. Learn about the complete capabilities of the DataRobot AI Platform at https://www.datarobot.com/platform. Watch the video as ...\",\"duration\":\"14:12\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/Y00VSO6Uq60?autoplay=1\",\"image_token\":\"e687f48c41420bafef42b0d7aec2ea6e0d55ae6c8c51adbb6d76c5d197637430\",\"images\":{\"large\":\"https://tse3.mm.bing.net/th?id=OVP.WMkyYYho3O9V85gpNsutVAEsDh&pid=Api\",\"medium\":\"https://tse3.mm.bing.net/th?id=OVP.WMkyYYho3O9V85gpNsutVAEsDh&pid=Api\",\"motion\":\"https://tse3.mm.bing.net/th?id=OM1.s30yJWAIBWugpw_1704039193&pid=Api\",\"small\":\"https://tse3.mm.bing.net/th?id=OVP.WMkyYYho3O9V85gpNsutVAEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-11-28T02:29:50.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":829},\"title\":\"Predictive AI: Build, Operate, and Govern with the DataRobot AI Platform | Flight Delays Use Case\",\"uploader\":\"DataRobot\"},{\"content\":\"https://www.youtube.com/watch?v=Jj8JovBRflA\",\"description\":\"Quick overview of DataRobot AI Accelerators including how to access them, what topics are covered, and how to get started using them with the data science notebook of your choice. Learn more https://community.datarobot.com/t5/ai-accelerators-library/tkb-p/ai-accelerators-library https://github.com/datarobot-community/ai-accelerators Transcript ...\",\"duration\":\"1:45\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/Jj8JovBRflA?autoplay=1\",\"image_token\":\"2749c14ed9db6fea61b6d86d8b55a56c24a8ac5900035d1ce6b326d0f9d807fe\",\"images\":{\"large\":\"https://tse1.mm.bing.net/th?id=OVP.yk9KOy_GBh3vO51YxcV8NQEsDh&pid=Api\",\"medium\":\"https://tse1.mm.bing.net/th?id=OVP.yk9KOy_GBh3vO51YxcV8NQEsDh&pid=Api\",\"motion\":\"https://tse1.mm.bing.net/th?id=OM2.w0xOKKBTuyvZdw_1699233002&pid=Api\",\"small\":\"https://tse1.mm.bing.net/th?id=OVP.yk9KOy_GBh3vO51YxcV8NQEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-09-28T17:01:32.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":1},\"title\":\"AI Accelerators Overview | Repeatable Workflows using the DataRobot API\",\"uploader\":\"DataRobot\"},{\"content\":\"https://www.youtube.com/watch?v=fm6nxsAo5J0\",\"description\":\"This demo showcases the end-to-end capabilities in the DataRobot Enterprise AI Platform using a house price listings dataset containing diverse feature types including numeric, categorical, raw text, images, and geospatial data. The demo takes us on a journey from raw data to value, and highlights DataRobot's governance, explainability, and ...\",\"duration\":\"4:56\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/fm6nxsAo5J0?autoplay=1\",\"image_token\":\"8aee60e8b0fc16a6e77cd5a9391dd3c6214fa2fab314b27a320055efcb29f75e\",\"images\":{\"large\":\"https://tse4.mm.bing.net/th?id=OVP.chafFyMzRYblao3a5sr79gEsDh&pid=Api\",\"medium\":\"https://tse4.mm.bing.net/th?id=OVP.chafFyMzRYblao3a5sr79gEsDh&pid=Api\",\"motion\":\"https://tse4.mm.bing.net/th?id=OM1.lHW5r59lMDBG5w_1684178452&pid=Api\",\"small\":\"https://tse4.mm.bing.net/th?id=OVP.chafFyMzRYblao3a5sr79gEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2020-08-11T16:00:10.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":17912},\"title\":\"DataRobot AI Vision Demo\",\"uploader\":\"DataRobot\"},{\"content\":\"https://www.youtube.com/watch?v=XhipOG-S1q8\",\"description\":\"This end-to-end demo shows the tight integrations between the DataRobot AI Platform and AWS services. By natively connecting to data in Amazon S3, you can build, test, and evaluate models in DataRobot, and then deploy them in Amazon SageMaker. These models can be monitored for drift and other relevant parameters via DataRobot MLOps. In this ...\",\"duration\":\"13:27\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/XhipOG-S1q8?autoplay=1\",\"image_token\":\"d2a00656028a69312df6f3ae2ff1e484fd23dc4bceff58bc5157813853d6db90\",\"images\":{\"large\":\"https://tse2.mm.bing.net/th?id=OVP.BC-1oDSg7Hw6OofUPNCBFwEsDh&pid=Api\",\"medium\":\"https://tse2.mm.bing.net/th?id=OVP.BC-1oDSg7Hw6OofUPNCBFwEsDh&pid=Api\",\"motion\":\"https://tse2.mm.bing.net/th?id=OM1.gmEOiAfzgn4Y-A_1684167397&pid=Api\",\"small\":\"https://tse2.mm.bing.net/th?id=OVP.BC-1oDSg7Hw6OofUPNCBFwEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-02-08T10:14:17.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":2025},\"title\":\"DataRobot and AWS: Rapidly Prototype and Deploy AI Models | Demo Tutorial\",\"uploader\":\"DataRobot\"},{\"content\":\"https://www.youtube.com/watch?v=RrbJLm6atwc\",\"description\":\"Please visit the 2023 DataRobot Product Demo at https://www.youtube.com/watch?v=vyi_0D-rJ1A This video shows the DataRobot AI Platform in action as of 2018. DataRobot is the category creator of AutoML and MLOps with a rich history of innovation as detailed here - https://www.datarobot.com/innovation. Our Platform is constantly evolving with new ...\",\"duration\":\"1:32\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/RrbJLm6atwc?autoplay=1\",\"image_token\":\"f08d101a4ecaa1ecbc1702f740cddd0e35c504901e82dcfe0276733759319a8e\",\"images\":{\"large\":\"https://tse1.mm.bing.net/th?id=OVP.NzKh9KZZ5OuUbK1j19RfbwHgFo&pid=Api\",\"medium\":\"https://tse1.mm.bing.net/th?id=OVP.NzKh9KZZ5OuUbK1j19RfbwHgFo&pid=Api\",\"motion\":\"https://tse1.mm.bing.net/th?id=OM1.AWDbr86dOILmXQ_1645855537&pid=Api\",\"small\":\"https://tse1.mm.bing.net/th?id=OVP.NzKh9KZZ5OuUbK1j19RfbwHgFo&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2018-04-16T14:01:36.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":49270},\"title\":\"DataRobot AI Platform [2018 Version - Update Available]\",\"uploader\":\"DataRobot\"}],\"vqd\":{\"datarobot\":\"4-305438614248843041332096319235456803678\"}});DDG.duckbar.loadModule('related_searches', {\"ads\":[],\"query\":\"datarobot\",\"queryEncoded\":\"datarobot\",\"response_type\":\"places\",\"results\":[{\"display_text\":\"datarobot login\",\"text\":\"datarobot login\",\"web_search_url\":\"?q=datarobot%20login\"},{\"display_text\":\"datarobot download\",\"text\":\"datarobot download\",\"web_search_url\":\"?q=datarobot%20download\"},{\"display_text\":\"datarobot products\",\"text\":\"datarobot products\",\"web_search_url\":\"?q=datarobot%20products\"},{\"display_text\":\"datarobot company\",\"text\":\"datarobot company\",\"web_search_url\":\"?q=datarobot%20company\"},{\"display_text\":\"datarobot wikipedia\",\"text\":\"datarobot wikipedia\",\"web_search_url\":\"?q=datarobot%20wikipedia\"},{\"display_text\":\"datarobot artificial intelligence\",\"text\":\"datarobot artificial intelligence\",\"web_search_url\":\"?q=datarobot%20artificial%20intelligence\"},{\"display_text\":\"datarobot for your daily life\",\"text\":\"datarobot for your daily life\",\"web_search_url\":\"?q=datarobot%20for%20your%20daily%20life\"},{\"display_text\":\"data robot tool\",\"text\":\"data robot tool\",\"web_search_url\":\"?q=data%20robot%20tool\"}],\"vqd\":{\"datarobot\":\"4-305438614248843041332096319235456803678\"}});if (DDG.pageLayout) DDG.pageLayout.initialize({\"mainline\":{\"items\":[[\"ad\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"videos\"],[\"related_searches\"]]}}, { start: 0 });DDG.deep.emit(\"load:completed\");", + "curl-cffi-GET-https://links.duckduckgo.com/d.js-{\"params\": {\"bing_market\": \"wt-WT\", \"df\": null, \"ex\": \"-1\", \"kl\": \"wt-wt\", \"l\": \"wt-wt\", \"q\": \"dataiku\", \"s\": \"0\", \"sp\": \"0\", \"vqd\": \"4-337400409169293617055811118659485228425\"}}": "DDG.search.altIsNavigational = 1;DDG.search.isNavigational = 1;if (DDG.deep && DDG.deep.setUpstream) DDG.deep.setUpstream(\"bingv7aa\");DDG.deep.bn={'ivc':1};if (DDG.pageLayout) DDG.pageLayout.load('a',[], {\"page_load_url\":\"https://duckduckgo.com/y.js?iurl=%7B2%7DIG%3D4B3C73388F5840C299E628F54B339615%26CID%3D15DC198D7A096819249D0D8B7B3C6947%26Type%3DEvent.CPT%26DATA%3D0\"});DDG.duckbar.future_signal_tab({signal:'medium',from:'deep_answer'});DDG.duckbar.add({\"data\":{\"Abstract\":\"Dataiku is an artificial intelligence and machine learning company which was founded in 2013. In December 2019, Dataiku announced that CapitalG\\u2014the late-stage growth venture capital fund financed by Alphabet Inc.\\u2014joined Dataiku as an investor and that it had achieved unicorn status. As of 2021, Dataiku is valued at $4.6 billion. Dataiku currently employs more than 1,000 people worldwide between offices in New York, Denver, Washington DC, Los Angeles, Paris, London, Munich, Frankfurt, Sydney, Singapore, Tokyo, and Dubai.\",\"AbstractSource\":\"Wikipedia\",\"AbstractText\":\"Dataiku is an artificial intelligence and machine learning company which was founded in 2013. In December 2019, Dataiku announced that CapitalG\\u2014the late-stage growth venture capital fund financed by Alphabet Inc.\\u2014joined Dataiku as an investor and that it had achieved unicorn status. As of 2021, Dataiku is valued at $4.6 billion. Dataiku currently employs more than 1,000 people worldwide between offices in New York, Denver, Washington DC, Los Angeles, Paris, London, Munich, Frankfurt, Sydney, Singapore, Tokyo, and Dubai.\",\"AbstractURL\":\"https://en.wikipedia.org/wiki/Dataiku\",\"Answer\":\"\",\"AnswerType\":\"\",\"Definition\":\"\",\"DefinitionSource\":\"\",\"DefinitionURL\":\"\",\"Entity\":\"company\",\"Heading\":\"Dataiku\",\"Image\":\"/i/b50c1c3f.png\",\"ImageHeight\":270,\"ImageIsLogo\":1,\"ImageWidth\":483,\"Infobox\":{\"content\":[{\"data_type\":\"string\",\"label\":\"Type\",\"sort_order\":\"1000\",\"value\":\"Private\",\"wiki_order\":0},{\"data_type\":\"string\",\"label\":\"Industry\",\"sort_order\":\"1001\",\"value\":\"Computer software\",\"wiki_order\":1},{\"data_type\":\"string\",\"label\":\"Founded\",\"sort_order\":\"1\",\"value\":\"February 14, 2013 in Paris, France\",\"wiki_order\":2},{\"data_type\":\"string\",\"label\":\"Founders\",\"sort_order\":\"1002\",\"value\":\"Florian Douetteau, Cl\\u00e9ment Stenac, Marc Batty, Thomas Cabrol\",\"wiki_order\":3},{\"data_type\":\"string\",\"label\":\"Key people\",\"sort_order\":\"2\",\"value\":\"Florian Douetteau (CEO)\",\"wiki_order\":4},{\"data_type\":\"string\",\"label\":\"Products\",\"sort_order\":\"1003\",\"value\":\"Dataiku Data Science Studio\",\"wiki_order\":5},{\"data_type\":\"string\",\"label\":\"Revenue\",\"sort_order\":\"3\",\"value\":\"US$ 150 million (2021)\",\"wiki_order\":6},{\"data_type\":\"string\",\"label\":\"Number of employees\",\"sort_order\":\"1004\",\"value\":\"1,000+ (2022)\",\"wiki_order\":7},{\"data_type\":\"string\",\"label\":\"Website\",\"sort_order\":\"1005\",\"value\":\"[dataiku.com]\",\"wiki_order\":8},{\"data_type\":\"twitter_profile\",\"label\":\"Twitter profile\",\"value\":\"dataiku\",\"wiki_order\":\"102\"},{\"data_type\":\"instance\",\"label\":\"Instance of\",\"value\":{\"entity-type\":\"item\",\"id\":\"Q4830453\",\"numeric-id\":4830453},\"wiki_order\":\"207\"},{\"data_type\":\"official_website\",\"label\":\"Official Website\",\"value\":\"http://www.dataiku.com/\",\"wiki_order\":\"208\"}],\"meta\":[{\"data_type\":\"string\",\"label\":\"article_title\",\"value\":\"Dataiku\"},{\"data_type\":\"string\",\"label\":\"template_name\",\"value\":\"infobox company\"},{\"data_type\":\"string\",\"label\":\"formatting_rules\",\"value\":\"company\"}]},\"OfficialDomain\":\"dataiku.com\",\"OfficialWebsite\":\"https://dataiku.com\",\"Redirect\":\"\",\"RelatedTopics\":[{\"FirstURL\":\"https://duckduckgo.com/c/Big_data_companies\",\"Icon\":{\"Height\":\"\",\"URL\":\"\",\"Width\":\"\"},\"Result\":\"Big data companies\",\"Text\":\"Big data companies\"},{\"FirstURL\":\"https://duckduckgo.com/c/Data_analysis_software\",\"Icon\":{\"Height\":\"\",\"URL\":\"\",\"Width\":\"\"},\"Result\":\"Data analysis software\",\"Text\":\"Data analysis software\"},{\"FirstURL\":\"https://duckduckgo.com/c/Privately_held_companies_based_in_New_York_City\",\"Icon\":{\"Height\":\"\",\"URL\":\"\",\"Width\":\"\"},\"Result\":\"Privately held companies based in New York City\",\"Text\":\"Privately held companies based in New York City\"},{\"FirstURL\":\"https://duckduckgo.com/c/Proprietary_software\",\"Icon\":{\"Height\":\"\",\"URL\":\"\",\"Width\":\"\"},\"Result\":\"Proprietary software\",\"Text\":\"Proprietary software\"}],\"Results\":[{\"FirstURL\":\"https://dataiku.com\",\"Icon\":{\"Height\":16,\"URL\":\"/i/dataiku.com.ico\",\"Width\":16},\"Result\":\"Official site\",\"Text\":\"Official site\"},{\"FirstURL\":\"http://www.dataiku.com/\",\"Icon\":{\"Height\":16,\"URL\":\"/i/dataiku.com.ico\",\"Width\":16},\"Result\":\"Official site - Dataiku\",\"Text\":\"Official site - Dataiku\"}],\"Type\":\"A\",\"meta\":{\"attribution\":null,\"blockgroup\":null,\"created_date\":null,\"description\":\"Wikipedia\",\"designer\":null,\"dev_date\":null,\"dev_milestone\":\"live\",\"developer\":[{\"name\":\"DDG Team\",\"type\":\"ddg\",\"url\":\"http://www.duckduckhack.com\"}],\"example_query\":\"nikola tesla\",\"id\":\"wikipedia_fathead\",\"is_stackexchange\":null,\"js_callback_name\":\"wikipedia\",\"live_date\":null,\"maintainer\":{\"github\":\"duckduckgo\"},\"name\":\"Wikipedia\",\"perl_module\":\"DDG::Fathead::Wikipedia\",\"producer\":null,\"production_state\":\"online\",\"repo\":\"fathead\",\"signal_from\":\"wikipedia_fathead\",\"src_domain\":\"en.wikipedia.org\",\"src_id\":1,\"src_name\":\"Wikipedia\",\"src_options\":{\"directory\":\"\",\"is_fanon\":0,\"is_mediawiki\":1,\"is_wikipedia\":1,\"language\":\"en\",\"min_abstract_length\":\"20\",\"skip_abstract\":0,\"skip_abstract_paren\":0,\"skip_end\":\"0\",\"skip_icon\":0,\"skip_image_name\":0,\"skip_qr\":\"\",\"source_skip\":\"\",\"src_info\":\"\"},\"src_url\":null,\"status\":\"live\",\"tab\":\"About\",\"topic\":[\"productivity\"],\"unsafe\":0}},\"duckbar_topic\":\"About\",\"from\":\"deep_answer\",\"meta\":{\"attribution\":null,\"blockgroup\":null,\"created_date\":null,\"description\":\"Wikipedia\",\"designer\":null,\"dev_date\":null,\"dev_milestone\":\"live\",\"developer\":[{\"name\":\"DDG Team\",\"type\":\"ddg\",\"url\":\"http://www.duckduckhack.com\"}],\"example_query\":\"nikola tesla\",\"id\":\"wikipedia_fathead\",\"is_stackexchange\":null,\"js_callback_name\":\"wikipedia\",\"live_date\":null,\"maintainer\":{\"github\":\"duckduckgo\"},\"name\":\"Wikipedia\",\"perl_module\":\"DDG::Fathead::Wikipedia\",\"producer\":null,\"production_state\":\"online\",\"repo\":\"fathead\",\"signal_from\":\"wikipedia_fathead\",\"src_domain\":\"en.wikipedia.org\",\"src_id\":1,\"src_name\":\"Wikipedia\",\"src_options\":{\"directory\":\"\",\"is_fanon\":0,\"is_mediawiki\":1,\"is_wikipedia\":1,\"language\":\"en\",\"min_abstract_length\":\"20\",\"skip_abstract\":0,\"skip_abstract_paren\":0,\"skip_end\":\"0\",\"skip_icon\":0,\"skip_image_name\":0,\"skip_qr\":\"\",\"source_skip\":\"\",\"src_info\":\"\"},\"src_url\":null,\"status\":\"live\",\"tab\":\"About\",\"topic\":[\"productivity\"],\"unsafe\":0},\"model\":\"FatheadArticle\",\"official_site\":\"https://dataiku.com\",\"pixel_id\":\"wikipedia_fathead_deep\",\"signal\":\"medium\",\"templates\":{\"detail\":\"info_detail\"}});DDG.deep.signalSummary = \"about:m\";DDG.inject('DDG.Data.languages.resultLanguages', {\"en\":[\"https://www.dataiku.com/\",\"https://en.wikipedia.org/wiki/Dataiku\",\"https://www.dataiku.com/product/get-started/\",\"https://academy.dataiku.com/basics-101\",\"https://www.dataiku.com/product/dataiku-as-a-managed-service/\",\"https://knowledge.dataiku.com/latest/getting-started/about-dataiku/index.html\",\"https://discover.dataiku.com/data-quality/\",\"https://discover.dataiku.com/dataiku-12/\",\"https://www.linkedin.com/company/dataiku\",\"https://pages.dataiku.com/interactive-data-sheet\",\"https://developer.dataiku.com/latest/tutorials/index.html\",\"https://discover.dataiku.com/dataiku-for-generative-ai/\",\"https://blog.dataiku.com/dataiku-12\",\"https://www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/vendor/dataiku/product/dataiku\",\"https://blog.dataiku.com/why-users-love-dataiku\",\"https://knowledge.dataiku.com/latest/getting-started/about-dataiku/concept-value-proposition.html\",\"https://twitter.com/dataiku\",\"https://in.linkedin.com/company/persistent-systems\",\"https://www.freelancer.com/projects/database-administration/snowflake-dbt-dataiku-support\"],\"ja\":[\"https://jp.linkedin.com/in/tadashi-mishima-32638445\",\"https://www.intellilink.co.jp/topics/seminar_event/2024/dataiku-fy2023-2h.aspx\"]});DDG.deep.pageLayoutSummary = \"w5v1w16r1,e1\";DDG.inject('DDG.Data.languages.adLanguages', {});if (DDG.pageLayout) DDG.pageLayout.load('d',[{\"a\":\"Dataiku is a platform that lets you build, deploy, and manage data and AI projects all in one place. Whether you need to prepare data, train models, deploy models, or monitor and govern models, Dataiku has the tools and features to help you achieve your goals.\",\"ae\":null,\"c\":\"https://www.dataiku.com/\",\"d\":\"www.dataiku.com\",\"da\":\"\",\"h\":0,\"i\":\"www.dataiku.com\",\"k\":null,\"l\":[{\"snippet\":\"Dataiku saves time with quick visual analysis of columns, including the distribution of values, top values, outliers, invalids, and overall good statistics. For categorical data, the visual analysis includes the distribution by value. Martin Leijen . Business Data Architect at Action ...\",\"targetUrl\":\"https://www.dataiku.com/product/\",\"text\":\"Product\"},{\"snippet\":\"At Dataiku, we believe that diversity of people and thought is inherent to creating not only a top-quality product but an environment of inclusivity and belonging in which everyone can bring their full selves to work. It is the responsibility of every Dataiker to build a community of tolerance and open-mindedness.\",\"targetUrl\":\"https://www.dataiku.com/careers/\",\"text\":\"Careers\"},{\"snippet\":\"Dataiku lets you access and process data using the coding language of your choice, and lets you use code notebooks to prototype your recipes. Check out the use cases! Learn more Dataiku APIs . APIs in Dataiku allow coders to programmatically interact with various Dataiku objects and with the instance itself to accomplish a wide variety of tasks\",\"targetUrl\":\"https://www.dataiku.com/learn/\",\"text\":\"Learn\"},{\"snippet\":\"Thrive SPC Uses Dataiku, Snowflake, and Snow Fox Data to Improve Clinical Home Care. By moving to Dataiku and working with Dataiku partners, Snowflake and Snow Fox Data, Thrive Skilled Pediatric Care (Thrive SPC) has been able to advance from complicated spreadsheets to a central platform that provides clear insights and metrics to fuel their data-driven healthcare solutions.\",\"targetUrl\":\"https://www.dataiku.com/stories/\",\"text\":\"Stories\"},{\"snippet\":\"Dataiku was founded on the principle that in order to succeed in the world's rapidly evolving ecosystem, companies \\u2014 no matter what their industry or size \\u2014 must elevate their people to continuously innovate. Since 2013, Dataiku has been the leader in democratizing data and empowering organization-wide collaboration. We've been a part ...\",\"targetUrl\":\"https://www.dataiku.com/company/\",\"text\":\"Company\"},{\"snippet\":\"Join the Dataiku Partner Ecosystem. Become a part of the growing Dataiku service partner ecosystem or get in touch to talk integrations and technical partnerships.\",\"targetUrl\":\"https://www.dataiku.com/partners/\",\"text\":\"Partners\"}],\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku | Everyday AI, Extraordinary People\",\"u\":\"https://www.dataiku.com/\"},{\"a\":\"Dataiku is an artificial intelligence (AI) and machine learning company which was founded in 2013. In December 2019, Dataiku announced that CapitalG\\u2014the late-stage growth venture capital fund financed by Alphabet Inc.\\u2014joined Dataiku as an investor and that it had achieved unicorn status.\",\"ae\":null,\"b\":\"w\\tWikipedia\\ten.wikipedia.org\",\"c\":\"https://en.wikipedia.org/wiki/Dataiku\",\"d\":\"en.wikipedia.org/wiki/Dataiku\",\"da\":\"en_wikipedia_queries,nlp_fathead,nlp_wiki\",\"h\":0,\"i\":\"en.wikipedia.org\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku - Wikipedia\",\"u\":\"https://en.wikipedia.org/wiki/Dataiku\"},{\"a\":\"Dataiku is a fully managed online and installed platform that helps you build and deploy AI projects with data preparation, pipelines, AutoML, and automation. Start a 14-day free trial or download the free edition for up to 3 users and explore the features and benefits of Dataiku.\",\"ae\":null,\"c\":\"https://www.dataiku.com/product/get-started/\",\"d\":\"www.dataiku.com/product/get-started/\",\"da\":\"\",\"e\":\"2022-03-01T00:00:00.0000000\",\"h\":0,\"i\":\"www.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Get Started With Dataiku - Start an Online Trial or Download for Free\",\"u\":\"https://www.dataiku.com/product/get-started/\"},{\"a\":\"Dataiku Academy is a free online course that introduces the basics of Dataiku, a data analysis platform that allows you to create and explore data projects. You will learn how to create a project, a dataset, a connection, and a chart using Dataiku's interface and tools.\",\"ae\":null,\"c\":\"https://academy.dataiku.com/basics-101\",\"d\":\"academy.dataiku.com/basics-101\",\"da\":\"translations\",\"h\":0,\"i\":\"academy.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Basics 101 - Dataiku\",\"u\":\"https://academy.dataiku.com/basics-101\"},{\"a\":\"Dataiku Cloud is a fully managed service that lets you create AI and analytics insights from your data using modern cloud platforms and easy-to-use tools. Learn how to use Dataiku Cloud with built-in data connectors, AutoML, and online learning and support.\",\"ae\":null,\"c\":\"https://www.dataiku.com/product/dataiku-as-a-managed-service/\",\"d\":\"www.dataiku.com/product/dataiku-as-a-managed-service/\",\"da\":\"\",\"h\":0,\"i\":\"www.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku Cloud | Dataiku\",\"u\":\"https://www.dataiku.com/product/dataiku-as-a-managed-service/\"},{\"a\":\"About Dataiku# The following resources walk you through the main principles of the platform and how those core concepts can be applied to build an end-to-end solution. Concepts# Concept | The value proposition of Dataiku; Concept | Dataiku project walkthrough; Next.\",\"ae\":null,\"c\":\"https://knowledge.dataiku.com/latest/getting-started/about-dataiku/index.html\",\"d\":\"knowledge.dataiku.com/latest/getting-started/about-dataiku/index.html\",\"da\":\"\",\"h\":0,\"i\":\"knowledge.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"About Dataiku - Dataiku Knowledge Base\",\"u\":\"https://knowledge.dataiku.com/latest/getting-started/about-dataiku/index.html\"},{\"a\":\"Dataiku is a universal data, analytics, and AI platform that helps you detect and improve data quality challenges. Learn how to use Dataiku's discovery capabilities, data catalog, and data quality rules to deliver trusted data at speed across your organization.\",\"ae\":null,\"c\":\"https://discover.dataiku.com/data-quality/\",\"d\":\"discover.dataiku.com/data-quality/\",\"da\":\"\",\"h\":0,\"i\":\"discover.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Data Quality - Discover Dataiku\",\"u\":\"https://discover.dataiku.com/data-quality/\"},{\"a\":\"Dataiku 12 is a platform that connects data experts with generative AI models like OpenAI GPT and ChatGPT to create data projects. Learn how to use Dataiku 12 to do more with generative AI and data using a visual interface and natural language prompts.\",\"ae\":null,\"c\":\"https://discover.dataiku.com/dataiku-12/\",\"d\":\"discover.dataiku.com/dataiku-12/\",\"da\":\"\",\"h\":0,\"i\":\"discover.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku 12 - Discover Dataiku\",\"u\":\"https://discover.dataiku.com/dataiku-12/\"},{\"a\":\"Dataiku is the platform for Everyday AI, systemizing the use of data for exceptional business results. Organizations that use Dataiku elevate their people (whether technical and working in code or ...\",\"ae\":null,\"b\":\"li\\tLinkedIn\\twww.linkedin.com\",\"c\":\"https://www.linkedin.com/company/dataiku\",\"d\":\"www.linkedin.com/company/dataiku\",\"da\":\"\",\"h\":0,\"i\":\"www.linkedin.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku | LinkedIn\",\"u\":\"https://www.linkedin.com/company/dataiku\"},{\"a\":\"Dataiku is a platform that enables data experts and domain experts to work together to build AI into their daily operations. Learn about the key benefits and features of Dataiku, such as a visual lineage, governance, and collaboration, with this interactive data sheet.\",\"ae\":null,\"c\":\"https://pages.dataiku.com/interactive-data-sheet\",\"d\":\"pages.dataiku.com/interactive-data-sheet\",\"da\":\"\",\"h\":0,\"i\":\"pages.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku Interactive Data Sheet\",\"u\":\"https://pages.dataiku.com/interactive-data-sheet\"},{\"a\":\"Plugin development. Extend the native capabilities of Dataiku with custom-built components. This section contains tutorials which will help you learn how to use and combine programmatic features of Dataiku through step-by-step exercises. Developer tools Tooling and guidance to write code ...\",\"ae\":null,\"c\":\"https://developer.dataiku.com/latest/tutorials/index.html\",\"d\":\"developer.dataiku.com/latest/tutorials/index-html\",\"da\":\"\",\"h\":0,\"i\":\"developer.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Tutorials - Dataiku Developer Guide\",\"u\":\"https://developer.dataiku.com/latest/tutorials/index.html\"},{\"a\":\"With Dataiku's Prompt Studios. Prompt engineering is the key to developing robust interactions and reliable outputs from Generative AI services. With Dataiku's Prompt Studios, data scientists and engineers can design and operationalize high-performing, reusable prompts, complete with cost estimates across different LLM providers and models.\",\"ae\":null,\"c\":\"https://discover.dataiku.com/dataiku-for-generative-ai/\",\"d\":\"discover.dataiku.com/dataiku-for-generative-ai/\",\"da\":\"\",\"h\":0,\"i\":\"discover.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku for Generative AI - Discover Dataiku\",\"u\":\"https://discover.dataiku.com/dataiku-for-generative-ai/\"},{\"a\":\"Dataiku 12 includes new capabilities for data and IT teams to streamline MLOps and governance processes to deploy models faster, better manage production models, and improve model governance. A core principle of AI safety is keeping a human in the loop. Models don't always have the best or safest answer.\",\"ae\":null,\"c\":\"https://blog.dataiku.com/dataiku-12\",\"d\":\"blog.dataiku.com/dataiku-12\",\"da\":\"\",\"e\":\"2023-05-31T00:00:00.0000000\",\"h\":0,\"i\":\"blog.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Keep AI Under Control With Dataiku 12\",\"u\":\"https://blog.dataiku.com/dataiku-12\"},{\"a\":\"Dataiku is a platform for data science and machine learning, enabling data experts and domain experts to work together to build data into their daily operations. Read customer reviews, ratings, features, and alternatives of Dataiku from Gartner Peer Insights.\",\"ae\":null,\"c\":\"https://www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/vendor/dataiku/product/dataiku\",\"d\":\"www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/vendor/dataiku/product/dataiku\",\"da\":\"\",\"h\":0,\"i\":\"www.gartner.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku Reviews, Ratings & Features 2024 | Gartner Peer Insights\",\"u\":\"https://www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/vendor/dataiku/product/dataiku\"},{\"a\":\"" Dataiku has greatly impacted my day-to-day work by streamlining and automating many of the data processing and analysis tasks that were previously time consuming and labor intensive. Overall, Dataiku has significantly increased the efficiency and effectiveness of an organization's data-driven decision-making processes.\",\"ae\":null,\"c\":\"https://blog.dataiku.com/why-users-love-dataiku\",\"d\":\"blog.dataiku.com/why-users-love-dataiku\",\"da\":\"\",\"e\":\"2023-02-22T00:00:00.0000000\",\"h\":0,\"i\":\"blog.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Why Users Love Dataiku: Stories From the Community\",\"u\":\"https://blog.dataiku.com/why-users-love-dataiku\"},{\"a\":\"Dataiku is the platform for Everyday AI, systemizing the use of data for exceptional business results. In the same way that computers or the internet have become embedded in the everyday activities of organizations, AI can help organizations transform processes and help make better and wiser decisions.\",\"ae\":null,\"c\":\"https://knowledge.dataiku.com/latest/getting-started/about-dataiku/concept-value-proposition.html\",\"d\":\"knowledge.dataiku.com/latest/getting-started/about-dataiku/concept-value-proposition.html\",\"da\":\"\",\"h\":0,\"i\":\"knowledge.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Concept | The value proposition of Dataiku\",\"u\":\"https://knowledge.dataiku.com/latest/getting-started/about-dataiku/concept-value-proposition.html\"},{\"a\":\"Dataiku. @dataiku. Dataiku is the only AI platform that connects data and doers, enabling anyone across organizations to transform business data into real business impact. Software Company New York, NY dataiku.com Joined September 2012. 690 Following.\",\"ae\":null,\"b\":\"@\\tTwitter User Page\\ttwitter.com\",\"c\":\"https://twitter.com/dataiku\",\"d\":\"twitter.com/dataiku\",\"da\":\"\",\"h\":0,\"i\":\"twitter.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku (@dataiku) | Twitter\",\"u\":\"https://twitter.com/dataiku\"},{\"a\":\"Persistent Systems. 1,142,095 followers. 2mo. We're delighted to share that we continued our growth momentum as we reported $291.71M in revenue in Q2 FY24, delivering 14.1% year-over-year revenue growth. Our focus on client-centricity has enabled us to register the highest-ever TCV with more than $475M in the current quarter.\",\"ae\":null,\"b\":\"li\\tLinkedIn\\twww.linkedin.com\",\"c\":\"https://in.linkedin.com/company/persistent-systems\",\"d\":\"in.linkedin.com/company/persistent-systems\",\"da\":\"\",\"h\":0,\"i\":\"in.linkedin.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Persistent Systems | LinkedIn\",\"u\":\"https://in.linkedin.com/company/persistent-systems\"},{\"a\":\"Dataiku\\u306f\\u3001\\u30ad\\u30fc\\u30a6\\u30a9\\u30fc\\u30ab\\u30fc\\u69d8\\u3068\\u30b3\\u30f3\\u30b5\\u30eb\\u30c6\\u30a3\\u30f3\\u30b0\\u30d1\\u30fc\\u30c8\\u30ca\\u30fc\\u5951\\u7d04\\u3092\\u7de0\\u7d50\\u3057\\u307e\\u3057\\u305f\\u3002\\u30ad\\u30fc\\u30a6\\u30a9\\u30fc\\u30ab\\u30fc\\u69d8\\u306fDataiku\\u306e\\u6d3b\\u7528\\u3092\\u901a\\u3058\\u3066\\u304a\\u5ba2\\u69d8\\u306e\\u30c7\\u30fc\\u30bf\\u6d3b\\u7528\\u652f\\u63f4\\u3084\\u5185\\u88fd\\u5316\\u652f\\u63f4\\u3092\\u884c\\u3044\\u3001\\u30b5\\u30a4\\u30ed\\u5316\\u3055\\u308c\\u305f\\u30b7\\u30b9\\u30c6\\u30e0\\u304b\\u3089\\u306e\\u8131\\u5374\\u3084\\u30c7\\u30b8\\u30bf\\u30eb\\u4eba\\u6750\\u306e\\u78ba\\u4fdd\\u3068\\u3044\\u3063\\u305f\\u8ab2\\u984c\\u306b\\u5bfe\\u5fdc\\u3057\\u307e\\u3059\\u3002\",\"ae\":null,\"b\":\"li\\tLinkedIn\\twww.linkedin.com\",\"c\":\"https://jp.linkedin.com/in/tadashi-mishima-32638445\",\"d\":\"jp.linkedin.com/in/tadashi-mishima-32638445\",\"da\":\"\",\"h\":0,\"i\":\"jp.linkedin.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Tadashi Mishima - Growth Sales Specialist - UiPath Japan | LinkedIn\",\"u\":\"https://jp.linkedin.com/in/tadashi-mishima-32638445\"},{\"a\":\"Snowflake, DBT and Dataiku Support. Open Posted 10 minutes ago \\u2022 Ends in 6 days. $10-30 USD. Paid on delivery. Project Title: Snowflake, DBT, Dataiku project Support - Urgent. I am in need of a data engineer freelancer who can provide urgent support for the project. The ideal candidate should have experience and expertise in working with ...\",\"ae\":null,\"c\":\"https://www.freelancer.com/projects/database-administration/snowflake-dbt-dataiku-support\",\"d\":\"www.freelancer.com/projects/database-administration/snowflake-dbt-dataiku-support\",\"da\":\"\",\"e\":\"2024-01-14T00:00:00.0000000\",\"h\":0,\"i\":\"www.freelancer.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Snowflake, DBT and Dataiku Support | Freelancer\",\"u\":\"https://www.freelancer.com/projects/database-administration/snowflake-dbt-dataiku-support\"},{\"a\":\"Dataiku\\u30bb\\u30df\\u30ca\\u30fc\\u300c\\u30c7\\u30fc\\u30bf\\u99c6\\u52d5\\u578b\\u30d3\\u30b8\\u30cd\\u30b9\\u306e\\u8ab2\\u984c\\u3068\\u89e3\\u6c7a\\u6cd5\\u300d\\u3092\\u5f53\\u793e\\u4e3b\\u50ac\\u3067\\u958b\\u50ac\\u3044\\u305f\\u3057\\u307e\\u3059\\u3002 \\u696d\\u52d9\\u5909\\u9769\\u306e\\u63a8\\u9032\\u3092\\u76ee\\u7684\\u3068\\u3057\\u305f\\u3001\\u30c7\\u30fc\\u30bf\\u306e\\u96c6\\u7d04\\u30fb\\u7ba1\\u7406\\u30fb\\u904b\\u7528\\u30fb\\u6d3b\\u7528\\u30d7\\u30ed\\u30bb\\u30b9\\u306e\\u5b9a\\u7740\\u3092\\u3069\\u3046\\u5b9f\\u73fe\\u3059\\u308b\\u304b\\u306f\\u3001\\u30c7\\u30fc\\u30bf\\u30c9\\u30ea\\u30d6\\u30f3\\u7d4c\\u55b6\\u306e\\u5b9f\\u73fe\\u306b\\u304a\\u3051\\u308b\\u3001\\u3088\\u304f\\u3042\\u308b\\u8ab2\\u984c\\u306e1\\u3064\\u3067\\u3059\\u3002\",\"ae\":null,\"c\":\"https://www.intellilink.co.jp/topics/seminar_event/2024/dataiku-fy2023-2h.aspx\",\"d\":\"www.intellilink.co.jp/topics/seminar_event/2024/dataiku-fy2023-2h.aspx\",\"da\":\"translations\",\"e\":\"2024-01-12T00:00:00.0000000\",\"h\":0,\"i\":\"www.intellilink.co.jp\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"\\u5f53\\u793e\\u4e3b\\u50ac\\u30bb\\u30df\\u30ca\\u30fc\\u300c\\u30c7\\u30fc\\u30bf\\u99c6\\u52d5\\u578b\\u30d3\\u30b8\\u30cd\\u30b9\\u306e\\u8ab2\\u984c\\u3068\\u89e3\\u6c7a\\u6cd5\\u300d\\u3092\\u958b\\u50ac | Ntt\\u30c7\\u30fc\\u30bf\\u5148\\u7aef\\u6280\\u8853\\u682a\\u5f0f\\u4f1a\\u793e\",\"u\":\"https://www.intellilink.co.jp/topics/seminar_event/2024/dataiku-fy2023-2h.aspx\"},{\"n\":\"/d.js?q=dataiku&kl=wt-wt&l=wt-wt&p=&s=21&ex=-1&ct=US&sp=0&vqd=4-337400409169293617055811118659485228425\"}]);DDG.duckbar.load('images');DDG.duckbar.load('news');DDG.duckbar.load('videos', {\"ads\":[],\"query\":\"dataiku\",\"queryEncoded\":\"dataiku\",\"response_type\":\"places\",\"results\":[{\"content\":\"https://www.youtube.com/watch?v=Mmt7IluxE0M\",\"description\":\"Learn more about Dataiku and how to better use your enterprise data in this 3 minute demo. CHECK OUT DATAIKU: https://bit.ly/36XBlpK BRIGHTTALK WEBINARS: htt...\",\"duration\":\"3:35\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/Mmt7IluxE0M?autoplay=1\",\"image_token\":\"9fbd7f08d3fe9c5ae80966f30b030c5de63ce962d42298ce9455edc487f55f54\",\"images\":{\"large\":\"https://tse3.mm.bing.net/th?id=OVP.TMea5u6ptVU_48mCeCUUlQEsDh&pid=Api\",\"medium\":\"https://tse3.mm.bing.net/th?id=OVP.TMea5u6ptVU_48mCeCUUlQEsDh&pid=Api\",\"motion\":\"https://tse3.mm.bing.net/th?id=OM.2qQ9AZ4EgLzxPA_1699065185&pid=Api\",\"small\":\"https://tse3.mm.bing.net/th?id=OVP.TMea5u6ptVU_48mCeCUUlQEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2021-09-23T18:53:57.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":45758},\"title\":\"Dataiku 3-Minute Demo\",\"uploader\":\"Dataiku\"},{\"content\":\"https://www.youtube.com/watch?v=ryZRRIjQ5Z8\",\"description\":\"If you're a code-first data practitioner, Dataiku helps you efficiently build high quality data pipelines and models in a number of ways. CHECK OUT DATAIKU: https://bit.ly/36XBlpK EGG ON AIR: https://bit.ly/37GhXMY BRIGHTTALK WEBINARS: https://bit.ly/33TIRjn DATA SCIENCE PIONEERS DOCUMENTARY: https://bit.ly/36V3rBF PARTNER ECOSYSTEM: https ...\",\"duration\":\"10:43\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/ryZRRIjQ5Z8?autoplay=1\",\"image_token\":\"8a4abca8613c6680a108591849e5d7b13b86111004ae004898a7f059b64c8355\",\"images\":{\"large\":\"https://tse4.mm.bing.net/th?id=OVP.WoendyuZJ9qxql-n6jit5AEsDh&pid=Api\",\"medium\":\"https://tse4.mm.bing.net/th?id=OVP.WoendyuZJ9qxql-n6jit5AEsDh&pid=Api\",\"motion\":\"https://tse4.mm.bing.net/th?id=OM1.cmvppfhHVUeE4Q_1684256861&pid=Api\",\"small\":\"https://tse4.mm.bing.net/th?id=OVP.WoendyuZJ9qxql-n6jit5AEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2021-06-08T21:15:02.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":12391},\"title\":\"Dataiku Demo for Data Scientists and Coders\",\"uploader\":\"Dataiku\"},{\"content\":\"https://www.youtube.com/watch?v=1IgcAAPW4fQ\",\"description\":\"Dataiku 11 is now out! This release is jam-packed with features designed to help organizations deliver on the promise of Everyday AI. Check out this video to get an introduction to V11. CHECK OUT DATAIKU: https://bit.ly/36XBlpK DATAIKU ACADEMY: https://bit.ly/2LjsEgZ DATAIKU COMMUNITY: https://bit.ly/2K8lOtV DATA SCIENCE AND ANALYTICS MEETUPS ...\",\"duration\":\"30:52\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/1IgcAAPW4fQ?autoplay=1\",\"image_token\":\"885849fd3f3285ae15a77b9c7e40acc1fbe9d37fa39ac789ecf88e4b889aa796\",\"images\":{\"large\":\"https://tse1.mm.bing.net/th?id=OVP.mQkABdeGdBHH8E6VyTt64AEsDh&pid=Api\",\"medium\":\"https://tse1.mm.bing.net/th?id=OVP.mQkABdeGdBHH8E6VyTt64AEsDh&pid=Api\",\"motion\":\"https://tse1.mm.bing.net/th?id=OM1.mCQeogW3-u_iVw_1684181286&pid=Api\",\"small\":\"https://tse1.mm.bing.net/th?id=OVP.mQkABdeGdBHH8E6VyTt64AEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2022-07-15T15:35:31.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":3314},\"title\":\"Introduction to Dataiku 11!\",\"uploader\":\"Dataiku\"},{\"content\":\"https://www.youtube.com/watch?v=FluiuHuaU8A\",\"description\":\"In this breakout session of Dataiku's Product Days 2021, you will see a demo of Dataiku's Data Science Studio, the centralized, collaborative, and end-to-end platform for data science in the enterprise. CHECK OUT DATAIKU: https://bit.ly/36XBlpK EGG ON AIR: https://bit.ly/37GhXMY BRIGHTTALK WEBINARS: https://bit.ly/33TIRjn DATA SCIENCE PIONEERS ...\",\"duration\":\"13:50\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/FluiuHuaU8A?autoplay=1\",\"image_token\":\"2943fa8c1580f2936fc11667d670c0b827b94ff3d16b897f8b5ef2e2426487b3\",\"images\":{\"large\":\"https://tse2.mm.bing.net/th?id=OVP.RIM-ftwDZjYP58RimJfgwwEsDh&pid=Api\",\"medium\":\"https://tse2.mm.bing.net/th?id=OVP.RIM-ftwDZjYP58RimJfgwwEsDh&pid=Api\",\"motion\":\"https://tse2.mm.bing.net/th?id=OM1.MIQ7BoQz1MVkNw_1662248868&pid=Api\",\"small\":\"https://tse2.mm.bing.net/th?id=OVP.RIM-ftwDZjYP58RimJfgwwEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2021-07-08T15:56:22.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":3844},\"title\":\"Introduction to Dataiku Data Science | Product Days 2021\",\"uploader\":\"Dataiku\"},{\"content\":\"https://www.youtube.com/watch?v=gp8QeJJ4KuE\",\"description\":\"This tutorial is to quickly help users become familiar with the Dataiku platform (DSS). Links for setting up the tutorial. Step 1: https://www.dataiku.com/ Step 2: https://www.dataiku.com/product/get-started/virtualbox/ Step 3: http://127.0.0.1:10000/ Step 4: https://github.com/ageron/handson-ml Step 5: https://raw.githubusercontent.com/ageron ...\",\"duration\":\"10:24\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/gp8QeJJ4KuE?autoplay=1\",\"image_token\":\"36a6e666bdcb9e34aacad09f504181152667f35deab62b50ee48da1a76c38303\",\"images\":{\"large\":\"https://tse1.mm.bing.net/th?id=OVP.htgX0HRO9l8nlfoFzmlA5AHgFo&pid=Api\",\"medium\":\"https://tse1.mm.bing.net/th?id=OVP.htgX0HRO9l8nlfoFzmlA5AHgFo&pid=Api\",\"motion\":\"https://tse1.mm.bing.net/th?id=OM2.SsfUJx35-DP9OA_1632775689&pid=Api\",\"small\":\"https://tse1.mm.bing.net/th?id=OVP.htgX0HRO9l8nlfoFzmlA5AHgFo&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2020-06-09T17:48:44.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":37512},\"title\":\"Get started with Dataiku | From data to machine learning predictions in 10 minutes\",\"uploader\":\"Jose RazGuzman\"},{\"content\":\"https://www.youtube.com/watch?v=S6AY-q_5Bd0\",\"description\":\"Learn more about Dataiku's demand forecast solution to optimize your sorting and production planning, inventory management, and much more. Without you, it's just data. Learn more at https://www.dataiku.com/without-you/ CHECK OUT DATAIKU: https://bit.ly/36XBlpK BRIGHTTALK WEBINARS: https://bit.ly/33TIRjn DATA SCIENCE PIONEERS DOCUMENTARY: https ...\",\"duration\":\"2:00\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/S6AY-q_5Bd0?autoplay=1\",\"image_token\":\"456216e1949ad91527408a297243af22822d03602cc51e3fb1c7324680e27e90\",\"images\":{\"large\":\"https://tse2.mm.bing.net/th?id=OVP.B1QN6Sk8tATAQDmEZgvp8wEsDh&pid=Api\",\"medium\":\"https://tse2.mm.bing.net/th?id=OVP.B1QN6Sk8tATAQDmEZgvp8wEsDh&pid=Api\",\"motion\":\"https://tse2.mm.bing.net/th?id=OM2.BiTArkALFRqLyQ_1684244069&pid=Api\",\"small\":\"https://tse2.mm.bing.net/th?id=OVP.B1QN6Sk8tATAQDmEZgvp8wEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2022-08-09T20:43:58.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":569},\"title\":\"Improve Your Demand Forecasting with Dataiku\",\"uploader\":\"Dataiku\"},{\"content\":\"https://www.youtube.com/watch?v=tyd262JRo9g\",\"description\":\"In this session from Everyday AI Tech Day 2023, hear from Jacqueline Kuo, one of our solutions engineers, on how to build sustainable pipelines. Optimizing and automating data pipelines to transform, prepare, and analyze data on an ongoing basis is critical for production-ready AI projects. In this session, learn how Dataiku supports the ...\",\"duration\":\"20:35\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/tyd262JRo9g?autoplay=1\",\"image_token\":\"e946d16ff6600b2df00b4cddf4f97fa56d41df90c0a1608bcd33fedf69af63bd\",\"images\":{\"large\":\"https://tse2.mm.bing.net/th?id=OVP.xmckza2EYvyQk4nTqTsfZgEsDh&pid=Api\",\"medium\":\"https://tse2.mm.bing.net/th?id=OVP.xmckza2EYvyQk4nTqTsfZgEsDh&pid=Api\",\"motion\":\"https://tse2.mm.bing.net/th?id=OM1.VfASmCqEmyb4NA_1700289203&pid=Api\",\"small\":\"https://tse2.mm.bing.net/th?id=OVP.xmckza2EYvyQk4nTqTsfZgEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-10-20T12:35:12.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":98},\"title\":\"A Well-Oiled Machine: Create Sustainable Data Pipelines With Dataiku\",\"uploader\":\"Dataiku\"},{\"content\":\"https://www.youtube.com/watch?v=-amc9iVauuE\",\"description\":\"Dataiku is the leading platform for Everyday AI, systemizing the use of data for exceptional business results. In today's video we will take a tour of Dataiku's end to end capabilities by exploring a real life use case around environmental impact. Let's take a look at how a data science team with different skills can work together to turn ...\",\"duration\":\"12:35\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/-amc9iVauuE?autoplay=1\",\"image_token\":\"2a05a65ad8a2727aa5c48b8daa7f9ec363a24d4336a3509016d4b200c9d003cd\",\"images\":{\"large\":\"https://tse1.mm.bing.net/th?id=OVP.Az9RhdSVwpXe56mGcs6FqQEsDh&pid=Api\",\"medium\":\"https://tse1.mm.bing.net/th?id=OVP.Az9RhdSVwpXe56mGcs6FqQEsDh&pid=Api\",\"motion\":\"https://tse1.mm.bing.net/th?id=OM1.Q2OhN9DzfowU6A_1685345657&pid=Api\",\"small\":\"https://tse1.mm.bing.net/th?id=OVP.Az9RhdSVwpXe56mGcs6FqQEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-01-09T21:12:27.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":9768},\"title\":\"End to End Demo 2023\",\"uploader\":\"Dataiku\"},{\"content\":\"https://www.youtube.com/watch?v=MxKNdVNyLJY\",\"description\":\"Simply collecting vast amounts of data isn't enough to unlock its potentially massive value to your business. In this video, we will explore Dataiku's data preparation capabilities that will help you access, cleanse, transform, and enrich data faster than ever before. Timestamps: 0:00 Introduction 0:53 The Flow 1:18 Accessing and Exploring ...\",\"duration\":\"6:12\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/MxKNdVNyLJY?autoplay=1\",\"image_token\":\"652157f0560bd0c12f1731a0c4876335e712bb986dda679c0157545e9083ab50\",\"images\":{\"large\":\"https://tse4.mm.bing.net/th?id=OVP.xh9SlNNNrKuJLXO5kEtsLgEsDh&pid=Api\",\"medium\":\"https://tse4.mm.bing.net/th?id=OVP.xh9SlNNNrKuJLXO5kEtsLgEsDh&pid=Api\",\"motion\":\"https://tse4.mm.bing.net/th?id=OM2.u85J8gWHLhZhSA_1680067810&pid=Api\",\"small\":\"https://tse4.mm.bing.net/th?id=OVP.xh9SlNNNrKuJLXO5kEtsLgEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-01-09T20:46:27.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":2948},\"title\":\"Key Capabilities: Data Preparation\",\"uploader\":\"Dataiku\"},{\"content\":\"https://www.youtube.com/watch?v=GJA_PAnqGY8\",\"description\":\"In this video we walk through a series of real-world data analysis tasks using a Netflix movie & TV show dataset. We start by solving the tasks using the Python Pandas library. We then complete the same problems using the Dataiku Data Science Studio. Being knowledgeable about various tools in the data science space is very important to becoming ...\",\"duration\":\"58:15\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/GJA_PAnqGY8?autoplay=1\",\"image_token\":\"e00b743925487c5466b2bf1a024869f528876917c05bdd1e7cce1d78ad3d9a3a\",\"images\":{\"large\":\"https://tse2.mm.bing.net/th?id=OVP.D7OGs04gyoaehil3qvxX7gEsDh&pid=Api\",\"medium\":\"https://tse2.mm.bing.net/th?id=OVP.D7OGs04gyoaehil3qvxX7gEsDh&pid=Api\",\"motion\":\"https://tse2.mm.bing.net/th?id=OM1.3R2zQEuONEzSww_1664997993&pid=Api\",\"small\":\"https://tse2.mm.bing.net/th?id=OVP.D7OGs04gyoaehil3qvxX7gEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2022-08-03T15:00:11.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":7769},\"title\":\"Solving Real-World Data Analysis Tasks with Python Pandas & Dataiku DSS (Movie Analysis)\",\"uploader\":\"Recall by Dataiku\"}],\"vqd\":{\"dataiku\":\"4-337400409169293617055811118659485228425\"}});DDG.duckbar.loadModule('related_searches', {\"ads\":[],\"query\":\"dataiku\",\"queryEncoded\":\"dataiku\",\"response_type\":\"places\",\"results\":[{\"display_text\":\"dataiku login\",\"text\":\"dataiku login\",\"web_search_url\":\"?q=dataiku%20login\"},{\"display_text\":\"dataiku japan\",\"text\":\"dataiku japan\",\"web_search_url\":\"?q=dataiku%20japan\"},{\"display_text\":\"dataiku vs tableau\",\"text\":\"dataiku vs tableau\",\"web_search_url\":\"?q=dataiku%20vs%20tableau\"},{\"display_text\":\"dataiku vs alteryx\",\"text\":\"dataiku vs alteryx\",\"web_search_url\":\"?q=dataiku%20vs%20alteryx\"},{\"display_text\":\"dataiku vs databricks\",\"text\":\"dataiku vs databricks\",\"web_search_url\":\"?q=dataiku%20vs%20databricks\"},{\"display_text\":\"what is dataiku used for\",\"text\":\"what is dataiku used for\",\"web_search_url\":\"?q=what%20is%20dataiku%20used%20for\"},{\"display_text\":\"how to pronounce dataiku\",\"text\":\"how to pronounce dataiku\",\"web_search_url\":\"?q=how%20to%20pronounce%20dataiku\"},{\"display_text\":\"dataiku products\",\"text\":\"dataiku products\",\"web_search_url\":\"?q=dataiku%20products\"}],\"vqd\":{\"dataiku\":\"4-337400409169293617055811118659485228425\"}});if (DDG.pageLayout) DDG.pageLayout.initialize({\"mainline\":{\"items\":[[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"videos\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"related_searches\"]]},\"sidebar\":{\"items\":[[\"wikipedia_fathead\"]]}}, { start: 0 });DDG.deep.emit(\"load:completed\");", + "curl-cffi-POST-https://duckduckgo.com-{\"data\": {\"q\": \"Dataiku machine learning platform\"}}": "Dataiku machine learning platform at DuckDuckGo
", + "curl-cffi-GET-https://links.duckduckgo.com/d.js-{\"params\": {\"bing_market\": \"wt-WT\", \"df\": null, \"ex\": \"-1\", \"kl\": \"wt-wt\", \"l\": \"wt-wt\", \"q\": \"Dataiku machine learning platform\", \"s\": \"0\", \"sp\": \"0\", \"vqd\": \"4-135627661863888098952136153695171249901\"}}": "if (DDG.deep && DDG.deep.setUpstream) DDG.deep.setUpstream(\"bingv7aa\");DDG.deep.bn={'ivc':1};if (DDG.pageLayout) DDG.pageLayout.load('a',[], {\"page_load_url\":\"https://duckduckgo.com/y.js?iurl=%7B2%7DIG%3DD926DA28CBA14E6D9DA10C17531E1D6B%26CID%3D37369241D5576E2736ED8647D4286F5F%26Type%3DEvent.CPT%26DATA%3D0\"});DDG.deep.signalSummary = \"\";DDG.inject('DDG.Data.languages.resultLanguages', {\"en\":[\"https://www.dataiku.com/\",\"https://www.dataiku.com/product/key-capabilities/machine-learning/\",\"https://www.dataiku.com/product/key-capabilities/mlops/\",\"https://www.dataiku.com/product/key-capabilities/\",\"https://developer.dataiku.com/latest/tutorials/machine-learning/index.html\",\"https://knowledge.dataiku.com/latest/ml-analytics/index.html\",\"https://www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/vendor/dataiku/product/dataiku\",\"https://www.dataiku.com/product/dataiku-as-a-managed-service/\",\"https://pages.dataiku.com/dataiku-enterprise-ai-info\",\"https://blog.dataiku.com/dataiku-ml-key-capabilities\",\"https://discover.dataiku.com/data-scientists/\",\"https://academy.dataiku.com/\",\"https://academy.dataiku.com/machine-learning-basics\",\"https://www.infoworld.com/article/3618837/dataiku-review-data-science-fit-for-the-enterprise.html\",\"https://pages.dataiku.com/gartner-2021-pr\",\"https://blog.dataiku.com/what-is-machine-learning-model-deployment\",\"https://datascientest.com/en/dataiku-a-must-have-tool-for-data-science-and-ai\",\"https://blog.dataiku.com/gartner-2020\",\"https://www.infoq.com/news/2024/01/instacart-machine-learning/\",\"https://seekingalpha.com/article/4662201-c3ai-missing-the-boat\",\"https://jobs.apple.com/en-us/details/200516237/aiml-software-engineer-machine-learning-platform-infrastructure\",\"https://www.tokyodev.com/companies/rapyuta-robotics\",\"https://www.servicenow.com/partners/partner-finder/ntt-data-corp.html\",\"https://www.linkedin.com/company/maruha-nichiro-corporation\",\"https://www.gatech.edu/event/2024/01/12/access-big-data-machine-learning-workshop\",\"https://apply.workable.com/rapyuta-robotics/j/3ADFC72B04/\"]});DDG.deep.pageLayoutSummary = \"w26r1,e1\";DDG.inject('DDG.Data.languages.adLanguages', {});if (DDG.pageLayout) DDG.pageLayout.load('d',[{\"a\":\"MLOps Deploy, monitor, and maintain machine learning models, all in a single platform. Explore the Capability Collaboration With Dataiku, teams can move beyond the lab and build real and safe Generative AI applications at enterprise scale. Explore the Capability Governance\",\"ae\":null,\"c\":\"https://www.dataiku.com/\",\"d\":\"www.dataiku.com\",\"da\":\"\",\"h\":0,\"i\":\"www.dataiku.com\",\"k\":null,\"l\":[{\"snippet\":\"The Platform for Everyday AI. Empower people across your business to do more with data and AI, build projects faster, and work together, all in a shared and safe environment. With Dataiku, everyone can get involved in data and AI projects on a single platform for design and production that delivers use cases in days, not months.\",\"targetUrl\":\"https://www.dataiku.com/product/\",\"text\":\"Product\"},{\"snippet\":\"Check out Dataiku's job openings and apply to help companies transform raw data into business impacting predictions and products.\",\"targetUrl\":\"https://www.dataiku.com/careers/\",\"text\":\"Careers\"},{\"snippet\":\"A Single Platform For. Generative AI; Data Preparation; Visualization; Machine Learning ... Get started with the basics, quickly move on to advanced courses, and become a certified user on Dataiku's online learning and certification platform. Master Dataiku ... Learn how Dataiku makes it easy to build machine learning models, deploy them to a ...\",\"targetUrl\":\"https://www.dataiku.com/learn/\",\"text\":\"Learn\"},{\"snippet\":\"Thrive SPC Uses Dataiku, Snowflake, and Snow Fox Data to Improve Clinical Home Care. By moving to Dataiku and working with Dataiku partners, Snowflake and Snow Fox Data, Thrive Skilled Pediatric Care (Thrive SPC) has been able to advance from complicated spreadsheets to a central platform that provides clear insights and metrics to fuel their data-driven healthcare solutions.\",\"targetUrl\":\"https://www.dataiku.com/stories/\",\"text\":\"Stories\"},{\"snippet\":\"Dataiku was founded on the principle that in order to succeed in the world's rapidly evolving ecosystem, companies \\u2014 no matter what their industry or size \\u2014 must elevate their people to continuously innovate. Since 2013, Dataiku has been the leader in democratizing data and empowering organization-wide collaboration.\",\"targetUrl\":\"https://www.dataiku.com/company/\",\"text\":\"Company\"},{\"snippet\":\"Join the Dataiku Partner Ecosystem. Become a part of the growing Dataiku service partner ecosystem or get in touch to talk integrations and technical partnerships.\",\"targetUrl\":\"https://www.dataiku.com/partners/\",\"text\":\"Partners\"}],\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku | Everyday AI, Extraordinary People\",\"u\":\"https://www.dataiku.com/\"},{\"a\":\"AI and Machine Learning with Dataiku Build and evaluate advanced machine learning models using AutoML and the latest AI techniques. See Dataiku ML in Action START FOR FREE Visualization DataOps Feature Engineering\",\"ae\":null,\"c\":\"https://www.dataiku.com/product/key-capabilities/machine-learning/\",\"d\":\"www.dataiku.com/product/key-capabilities/machine-learning/\",\"da\":\"\",\"h\":0,\"i\":\"www.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"AI and Machine Learning with Dataiku | Dataiku\",\"u\":\"https://www.dataiku.com/product/key-capabilities/machine-learning/\"},{\"a\":\"Product Dataiku Key Capabilities MLOps with Dataiku MLOps with Dataiku Deploy, monitor, and manage machine learning models and projects in production. See MLOps in Action START FOR FREE DataOps Analytic Apps Deploying Projects to Production\",\"ae\":null,\"c\":\"https://www.dataiku.com/product/key-capabilities/mlops/\",\"d\":\"www.dataiku.com/product/key-capabilities/mlops/\",\"da\":\"\",\"h\":0,\"i\":\"www.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MLOps with Dataiku | Dataiku\",\"u\":\"https://www.dataiku.com/product/key-capabilities/mlops/\"},{\"a\":\"AI & Machine Learning Dataiku AutoML accelerates the model development process with a guided framework for AI and machine learning including prompt engineering, prediction, clustering, time series forecasting, computer vision tasks, causal ML, and more.\",\"ae\":null,\"c\":\"https://www.dataiku.com/product/key-capabilities/\",\"d\":\"www.dataiku.com/product/key-capabilities/\",\"da\":\"\",\"h\":0,\"i\":\"www.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku Key Capabilities | Dataiku\",\"u\":\"https://www.dataiku.com/product/key-capabilities/\"},{\"a\":\"This tutorial section contains learning material on programmatically training, managing and deploying machine learning models in Dataiku. Generative AI - NLP Programmatic RAG with Dataiku's LLM Mesh and Langchain Using LLM Mesh to benchmark zero-shot classification models GPT-based zero-shot text classification with the OpenAI API\",\"ae\":null,\"c\":\"https://developer.dataiku.com/latest/tutorials/machine-learning/index.html\",\"d\":\"developer.dataiku.com/latest/tutorials/machine-learning/index.html\",\"da\":\"\",\"h\":0,\"i\":\"developer.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Machine Learning - Dataiku Developer Guide\",\"u\":\"https://developer.dataiku.com/latest/tutorials/machine-learning/index.html\"},{\"a\":\"Dataiku supports a wide range of machine learning and analytic tasks, such as prediction, clustering, time series, image classification and much more. Explore the resources here for improving your building machine learning models and analytics tasks. Tip\",\"ae\":null,\"c\":\"https://knowledge.dataiku.com/latest/ml-analytics/index.html\",\"d\":\"knowledge.dataiku.com/latest/ml-analytics/index.html\",\"da\":\"\",\"h\":0,\"i\":\"knowledge.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Machine Learning & Analytics - Dataiku Knowledge Base\",\"u\":\"https://knowledge.dataiku.com/latest/ml-analytics/index.html\"},{\"a\":\"by Dataiku in Data Science and Machine Learning Platforms 4.8 504 Ratings compare_arrows Compare rate_review Write a Review download_2 Download PDF Related markets: Dataiku in Data Preparation Tools (18 Reviews), Dataiku in Cloud AI Developer Services (14 Reviews) Overview Reviews Alternatives Likes and Dislikes Dataiku Ratings Overview\",\"ae\":null,\"c\":\"https://www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/vendor/dataiku/product/dataiku\",\"d\":\"www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/vendor/dataiku/product/dataiku\",\"da\":\"\",\"h\":0,\"i\":\"www.gartner.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku Reviews, Ratings & Features 2024 | Gartner Peer Insights\",\"u\":\"https://www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/vendor/dataiku/product/dataiku\"},{\"a\":\"The fully managed data science and machine learning platform for your team to create AI and analytics insights. "Dataiku Cloud allows us to focus on analysis, not server administration. Data insights fuel our growth, and Dataiku Cloud enables us to develop insights faster than our competitors." Scott Walker, Managing Partner Sarissa Partners\",\"ae\":null,\"c\":\"https://www.dataiku.com/product/dataiku-as-a-managed-service/\",\"d\":\"www.dataiku.com/product/dataiku-as-a-managed-service/\",\"da\":\"\",\"h\":0,\"i\":\"www.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku Cloud | Dataiku\",\"u\":\"https://www.dataiku.com/product/dataiku-as-a-managed-service/\"},{\"a\":\"Dataiku is the end-to-end platform democratizing access to data. Manage the entire data science workflow from data prep to auto ML to model maintenance. ... Dataiku offers the latest machine learning technologies all in one place, including: Automated machine learning (AutoML) - choose between several ML backends to train models. ...\",\"ae\":null,\"c\":\"https://pages.dataiku.com/dataiku-enterprise-ai-info\",\"d\":\"pages.dataiku.com/dataiku-enterprise-ai-info\",\"da\":\"\",\"h\":0,\"i\":\"pages.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku: Your Path to Enterprise AI\",\"u\":\"https://pages.dataiku.com/dataiku-enterprise-ai-info\"},{\"a\":\"Dataiku Makes Machine Learning Customizable, Accessible, & Transparent May 17, 2023 Dataiku Product Lauren Anderson It only takes a quick look around to see that the use of machine learning (ML) is more prevalent across industries than ever before!\",\"ae\":null,\"c\":\"https://blog.dataiku.com/dataiku-ml-key-capabilities\",\"d\":\"blog.dataiku.com/dataiku-ml-key-capabilities\",\"da\":\"\",\"e\":\"2023-05-17T00:00:00.0000000\",\"h\":0,\"i\":\"blog.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku Makes Machine Learning Customizable, Accessible, & Transparent\",\"u\":\"https://blog.dataiku.com/dataiku-ml-key-capabilities\"},{\"a\":\"Jump Right In Dataiku is an end-to-end data and machine learning platform. Build and maintain predictive models throughout their entire lifecycles while pushing computation to the most efficient engines.\",\"ae\":null,\"c\":\"https://discover.dataiku.com/data-scientists/\",\"d\":\"discover.dataiku.com/data-scientists/\",\"da\":\"\",\"h\":0,\"i\":\"discover.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Data Scientists - Discover Dataiku\",\"u\":\"https://discover.dataiku.com/data-scientists/\"},{\"a\":\"Academy Your Path to Dataiku Mastery Quick Starts Follow our quick starts that introduce using Dataiku for different tasks View More Learning Paths From novice to expert, follow guided sets of curriculums to master Dataiku View More Certifications Test your Dataiku knowledge in key thematic areas View More Crash Course\",\"ae\":null,\"c\":\"https://academy.dataiku.com/\",\"d\":\"academy.dataiku.com\",\"da\":\"\",\"h\":0,\"i\":\"academy.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku Academy\",\"u\":\"https://academy.dataiku.com/\"},{\"a\":\"The Machine Learning Course is designed to provide a first hands-on overview of basic Dataiku DSS machine learning concepts so that you can easily create and evaluate your first models in DSS. Completion of this course will enable you to move on to more advanced courses. In this course, we'll work with two use cases.\",\"ae\":null,\"c\":\"https://academy.dataiku.com/machine-learning-basics\",\"d\":\"academy.dataiku.com/machine-learning-basics\",\"da\":\"\",\"h\":0,\"i\":\"academy.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Machine Learning Basics - Dataiku\",\"u\":\"https://academy.dataiku.com/machine-learning-basics\"},{\"a\":\"Dataiku Data Science Studio (DSS) is a platform that tries to span the needs of data scientists, data engineers, business analysts, and AI consumers. It mostly succeeds. In addition, Dataiku...\",\"ae\":null,\"c\":\"https://www.infoworld.com/article/3618837/dataiku-review-data-science-fit-for-the-enterprise.html\",\"d\":\"www.infoworld.com/article/3618837/dataiku-review-data-science-fit-for-the-enterprise.html\",\"da\":\"\",\"h\":0,\"i\":\"www.infoworld.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku review: Data science fit for the enterprise | InfoWorld\",\"u\":\"https://www.infoworld.com/article/3618837/dataiku-review-data-science-fit-for-the-enterprise.html\"},{\"a\":\"NEW YORK - March 4, 2021 - Today Dataiku, the world's most advanced Enterprise AI platform, was named a Leader in the Gartner 2021 Magic Quadrant for Data Science and Machine-Learning Platforms, marking its second consecutive year in the Leaders quadrant.Dataiku believes the placement amid the fast-moving market for AI tools cements its position as the driving force behind breakthroughs in ...\",\"ae\":null,\"c\":\"https://pages.dataiku.com/gartner-2021-pr\",\"d\":\"pages.dataiku.com/gartner-2021-pr\",\"da\":\"translations\",\"h\":0,\"i\":\"pages.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku Again Named a Leader in the Gartner 2021 Magic Quadrant for ...\",\"u\":\"https://pages.dataiku.com/gartner-2021-pr\"},{\"a\":\"An ML model is considered in production once it's been successfully deployed and being used by end users to realize business value. This article will shed more light on what exactly model deployment means and how Dataiku's end-to-end platform makes the model deployment process seamless. Why Is Model Deployment So Important (and So Hard)?\",\"ae\":null,\"c\":\"https://blog.dataiku.com/what-is-machine-learning-model-deployment\",\"d\":\"blog.dataiku.com/what-is-machine-learning-model-deployment\",\"da\":\"\",\"e\":\"2023-04-10T00:00:00.0000000\",\"h\":0,\"i\":\"blog.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"What Is Machine Learning Model Deployment? - Dataiku\",\"u\":\"https://blog.dataiku.com/what-is-machine-learning-model-deployment\"},{\"a\":\"A visual interface makes it very easy to apply Machine Learning models. Additionally, the platform-as-a-service approach eliminates the need for infrastructure. Furthermore, Dataiku is also compatible with Bayesian search. This allows running a second AI model in a loop to test different settings and parameters until the optimal configuration ...\",\"ae\":null,\"c\":\"https://datascientest.com/en/dataiku-a-must-have-tool-for-data-science-and-ai\",\"d\":\"datascientest.com/en/dataiku-a-must-have-tool-for-data-science-and-ai\",\"da\":\"\",\"e\":\"2023-11-28T00:00:00.0000000\",\"h\":0,\"i\":\"datascientest.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku: A must-have tool for Data Science and AI\",\"u\":\"https://datascientest.com/en/dataiku-a-must-have-tool-for-data-science-and-ai\"},{\"a\":\"Dataiku: A Gartner Magic Quadrant Leader in Data Science and Machine-Learning Platforms February 17, 2020 Dataiku Company, Dataiku Product Lynn Heidmann Our 2019 ended with a bang with the announcement that Dataiku became a unicorn valued at $1.4 billion and gained a new investor (CapitalG).\",\"ae\":null,\"c\":\"https://blog.dataiku.com/gartner-2020\",\"d\":\"blog.dataiku.com/gartner-2020\",\"da\":\"translations\",\"h\":0,\"i\":\"blog.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku: A Gartner Magic Quadrant Leader in Data Science and Machine ...\",\"u\":\"https://blog.dataiku.com/gartner-2020\"},{\"a\":\"Instacart introduced its original Griffin platform in 2022 to support its journey toward leveraging machine learning for product development. Using a unified platform helped triple the number of ...\",\"ae\":null,\"c\":\"https://www.infoq.com/news/2024/01/instacart-machine-learning/\",\"d\":\"www.infoq.com/news/2024/01/instacart-machine-learning/\",\"da\":\"translations\",\"e\":\"2024-01-01T08:02:59.0000000\",\"h\":0,\"i\":\"www.infoq.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Griffin 2.0: Instacart Revamps Its Machine Learning Platform - InfoQ\",\"u\":\"https://www.infoq.com/news/2024/01/instacart-machine-learning/\"},{\"a\":\"Their core product is Data Science Studio, which is focused on cross-discipline collaboration and ease of use and enables users to start machine-learning projects rapidly. Dataiku is focused on ...\",\"ae\":null,\"c\":\"https://seekingalpha.com/article/4662201-c3ai-missing-the-boat\",\"d\":\"seekingalpha.com/article/4662201-c3ai-missing-the-boat\",\"da\":\"translations\",\"e\":\"2024-01-10T19:38:00.0000000\",\"h\":0,\"i\":\"seekingalpha.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"C3.ai: Missing The Boat (NYSE:AI) | Seeking Alpha\",\"u\":\"https://seekingalpha.com/article/4662201-c3ai-missing-the-boat\"},{\"a\":\"The Information Intelligence teams are building groundbreaking technology for algorithmic search, machine learning, natural language processing, and artificial intelligence. The features we build are redefining how hundreds of millions of people use their computers and mobile devices to search and find what they are looking for.\",\"ae\":null,\"b\":\"apple\\tApple\\twww.apple.com\",\"c\":\"https://jobs.apple.com/en-us/details/200516237/aiml-software-engineer-machine-learning-platform-infrastructure\",\"d\":\"jobs.apple.com/en-us/details/200516237/aiml-software-engineer-machine-learning-platform-infrastructure\",\"da\":\"translations\",\"e\":\"2024-01-09T00:00:00.0000000\",\"h\":0,\"i\":\"jobs.apple.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Software Engineer, Machine Learning Platform & Infrastructure - Apple\",\"u\":\"https://jobs.apple.com/en-us/details/200516237/aiml-software-engineer-machine-learning-platform-infrastructure\"},{\"a\":\"One platform for all your robotics needs. Rapyuta Robotics aims at building low\\u00ad cost, lightweight autonomous mobile robots with high-level intelligence distributed in the cloud, enabling such robots to offload some of their heavy computation and seamlessly learn and share experiences with one another. Live your best life - at work and outside\",\"ae\":null,\"c\":\"https://www.tokyodev.com/companies/rapyuta-robotics\",\"d\":\"www.tokyodev.com/companies/rapyuta-robotics\",\"da\":\"\",\"h\":0,\"i\":\"www.tokyodev.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Rapyuta Robotics | TokyoDev\",\"u\":\"https://www.tokyodev.com/companies/rapyuta-robotics\"},{\"a\":\"NTT DATA - a part of NTT Group - is a trusted global innovator of IT and business services headquartered in Tokyo. We help clients transform through consulting, industry solutions, business process services, IT modernization and managed services. NTT DATA enables clients, as well as society, to move confidently into the digital future. We are committed to our clients' long-term success ...\",\"ae\":null,\"c\":\"https://www.servicenow.com/partners/partner-finder/ntt-data-corp.html\",\"d\":\"www.servicenow.com/partners/partner-finder/ntt-data-corp.html\",\"da\":\"\",\"h\":0,\"i\":\"www.servicenow.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"NTT DATA Corporation - ServiceNow\",\"u\":\"https://www.servicenow.com/partners/partner-finder/ntt-data-corp.html\"},{\"a\":\"Maruha Nichiro Corporation Food Production Toyosu, Koto-ku, Tokyo 1,941 followers "Bringing Delicious Delight to the World."\",\"ae\":null,\"b\":\"li\\tLinkedIn\\twww.linkedin.com\",\"c\":\"https://www.linkedin.com/company/maruha-nichiro-corporation\",\"d\":\"www.linkedin.com/company/maruha-nichiro-corporation\",\"da\":\"\",\"h\":0,\"i\":\"www.linkedin.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Maruha Nichiro Corporation | LinkedIn\",\"u\":\"https://www.linkedin.com/company/maruha-nichiro-corporation\"},{\"a\":\"PACE and SoX in collaboration with ACCESS and the Pittsburgh Supercomputing Center are pleased to host an HPC workshop on Big Data & Machine Learning, to be held on January 29 and 31, 2024. This workshop will focus on topics including big data analytics and machine learning with Spark, and deep learning using Tensorflow. It will have a hands-on component using the Bridges-2 computing platform ...\",\"ae\":null,\"c\":\"https://www.gatech.edu/event/2024/01/12/access-big-data-machine-learning-workshop\",\"d\":\"www.gatech.edu/event/2024/01/12/access-big-data-machine-learning-workshop\",\"da\":\"translations\",\"e\":\"2024-01-12T00:00:00.0000000\",\"h\":0,\"i\":\"www.gatech.edu\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"ACCESS Big Data & Machine Learning Workshop - Georgia Institute of ...\",\"u\":\"https://www.gatech.edu/event/2024/01/12/access-big-data-machine-learning-workshop\"},{\"a\":\"Start date: January 2024 or later. Our Tokyo engineering team is looking for robotics software interns for a minimum duration of six months capable of supporting the team to build state-of-the-art, scalable, autonomous mobile robots. The team works closely with some of the leading Japanese companies to build pioneering robotics solutions by leveraging rapyuta.io, our cloud robotics platform.\",\"ae\":null,\"c\":\"https://apply.workable.com/rapyuta-robotics/j/3ADFC72B04/\",\"d\":\"apply.workable.com/rapyuta-robotics/j/3ADFC72B04/\",\"da\":\"translations\",\"h\":0,\"i\":\"apply.workable.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Robotics Software Intern 2024 - Rapyuta Robotics\",\"u\":\"https://apply.workable.com/rapyuta-robotics/j/3ADFC72B04/\"},{\"n\":\"/d.js?q=Dataiku%20machine%20learning%20platform&kl=wt-wt&l=wt-wt&p=&s=26&ex=-1&ct=US&sp=0&vqd=4-135627661863888098952136153695171249901\"}]);DDG.duckbar.load('images');DDG.duckbar.load('news');DDG.duckbar.load('videos');DDG.duckbar.loadModule('related_searches', {\"ads\":[],\"query\":\"Dataiku machine learning platform\",\"queryEncoded\":\"Dataiku%20machine%20learning%20platform\",\"response_type\":\"places\",\"results\":[{\"display_text\":\"dataiku online machine learning\",\"text\":\"dataiku online machine learning\",\"web_search_url\":\"?q=dataiku%20online%20machine%20learning\"},{\"display_text\":\"dataiku machine learning plugin\",\"text\":\"dataiku machine learning plugin\",\"web_search_url\":\"?q=dataiku%20machine%20learning%20plugin\"},{\"display_text\":\"dataiku machine learning model\",\"text\":\"dataiku machine learning model\",\"web_search_url\":\"?q=dataiku%20machine%20learning%20model\"},{\"display_text\":\"dataiku machine learning extension\",\"text\":\"dataiku machine learning extension\",\"web_search_url\":\"?q=dataiku%20machine%20learning%20extension\"},{\"display_text\":\"automated machine learning dataiku\",\"text\":\"automated machine learning dataiku\",\"web_search_url\":\"?q=automated%20machine%20learning%20dataiku\"},{\"display_text\":\"dataiku production server\",\"text\":\"dataiku production server\",\"web_search_url\":\"?q=dataiku%20production%20server\"},{\"display_text\":\"dataiku automated automation\",\"text\":\"dataiku automated automation\",\"web_search_url\":\"?q=dataiku%20automated%20automation\"},{\"display_text\":\"dataiku key capabilities\",\"text\":\"dataiku key capabilities\",\"web_search_url\":\"?q=dataiku%20key%20capabilities\"}],\"vqd\":{\"Dataiku%20machine%20learning%20platform\":\"4-135627661863888098952136153695171249901\"}});if (DDG.pageLayout) DDG.pageLayout.initialize({\"mainline\":{\"items\":[[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"related_searches\"]]},\"sidebar\":{\"items\":[[\"wikipedia_fathead\"]]}}, { start: 0 });DDG.deep.emit(\"load:completed\");", + "curl-cffi-POST-https://duckduckgo.com-{\"data\": {\"q\": \"DataRobot AI platform comparison\"}}": "DataRobot AI platform comparison at DuckDuckGo
", + "curl-cffi-GET-https://links.duckduckgo.com/d.js-{\"params\": {\"bing_market\": \"wt-WT\", \"df\": null, \"ex\": \"-1\", \"kl\": \"wt-wt\", \"l\": \"wt-wt\", \"q\": \"DataRobot AI platform comparison\", \"s\": \"0\", \"sp\": \"0\", \"vqd\": \"4-58620545585474558767320902709740831322\"}}": "if (DDG.deep && DDG.deep.setUpstream) DDG.deep.setUpstream(\"bingv7aa\");DDG.deep.bn={'ivc':1};if (DDG.pageLayout) DDG.pageLayout.load('a',[{\"a\":\"\\u9ad8\\u7cbe\\u5ea6\\u306a\\u6a5f\\u68b0\\u5b66\\u7fd2\\u30e2\\u30c7\\u30eb\\u3092\\u69cb\\u7bc9\\u3001\\u5b9f\\u88c5\\u3001\\u904b\\u7528\\u3002DataRobot\\u306f\\u793e\\u5185\\u30c7\\u30fc\\u30bf\\u304b\\u3089\\u65b0\\u3057\\u3044\\u4fa1\\u5024\\u3092\\u5275\\u9020\\u3057\\u307e\\u3059. DataRobot\\u306f\\u7c21\\u5358\\u306a\\u64cd\\u4f5c\\u3067\\u30c7\\u30fc\\u30bf\\u304b\\u3089\\u4fa1\\u5024\\u3092\\u5275\\u51fa\",\"adext\":{\"filterlinks\":{\"l\":[],\"tid\":\"\"},\"sitelinks\":{\"l\":[{\"snippet\":\"Explore the DataRobot AI Platform Get Started With a 30-Day Trial\",\"targetUrl\":\"https://duckduckgo.com/y.js?ad_domain=datarobot.com&ad_provider=bingv7aa&ad_type=txad&eddgt=TADjssf2xUPiUP4Z%2DpL%2Djw%3D%3D&rut=458d772e8fcb4e4324d9389389d604bf4a83994e8046495904402de9aadd8689&u3=https%3A%2F%2Fwww.bing.com%2Faclick%3Fld%3De8wZWcT1GcY01mZtMuvlFYRDVUCUwR59UrUKaRMOBuANnCWi%2D8iLso5PuAoywij1cMeNLxieP5AAeMQUBqIlxZTsdWNA7YZoBicc1jtvLW_ZEmp6X0lumVtbFw9IJCDFojWdEcfYE0O0SvTWErWCBN9jCLQwEegRDrKirFobgUYBqEYLfhMVAeD3x862y3EMIPxcrug60dWItb0_gnTv06GzMdT9Q%26u%3DaHR0cHMlM2ElMmYlMmZ3d3cuZGF0YXJvYm90LmNvbSUyZnRyaWFsJTJmJTNmdXRtX21lZGl1bSUzZHNlYXJjaCUyNnV0bV9zb3VyY2UlM2RiaW5nJTI2dXRtX2NhbXBhaWduJTNkRnJlZVRyaWFsMjAyM1dXMDgxNkdQU2FkZXh0JTI2Y2FtcGFpZ25pZCUzZDUzMDcwODA5OSUyNmFkZ3JvdXBpZCUzZDEzNTAyMDI3NzQyMTc2OTglMjZhZGlkJTNkJTI2bXNjbGtpZCUzZDgwNjM0Y2YzMGRlMDE3MTAyNjVmMjk1MGYyZTYxNzM1%26rlid%3D80634cf30de01710265f2950f2e61735&vqd=4-266956621161894169747609047079670999441&iurl=%7B1%7DIG%3D4E234A2A70BD47BA8D9437C2FE20102A%26CID%3D0DBF2147217B62AE10B83541204E63FB%26ID%3DDevEx%2C5065.1\",\"text\":\"DataRobot Free Trial\"},{\"snippet\":\"Unlock Your AI Success in 2023 Tips on the Path of Value-Driven AI\",\"targetUrl\":\"https://duckduckgo.com/y.js?ad_domain=datarobot.com&ad_provider=bingv7aa&ad_type=txad&eddgt=TADjssf2xUPiUP4Z%2DpL%2Djw%3D%3D&rut=d527db3345d9139883b504b6a057be69be3347eeb5acd23b8e64f7449bb73b50&u3=https%3A%2F%2Fwww.bing.com%2Faclick%3Fld%3De8UTprN2eQMdFB5SJFU5fAVjVUCUxyH9ey14F3ix7IMGUN9R8j4XI%2DxHFXyG6wW8QyDclA1ah53V6Dl1LRU3JgQHXtprRWsm0zG%2DDqcpZf1i6kJFAmi315DCmvKoT6C3z97QkhAnr4yX%2Dv3glHLhN9uc3yL9wfU5U7nv5YTzG9UKxaD_%2DK2eubvLD7ldJaBI4tjPKQUhliUQqV4yr72OBpGoew774%26u%3DaHR0cHMlM2ElMmYlMmZ3d3cuZGF0YXJvYm90LmNvbSUyZnJlc291cmNlcyUyZmFpc3VjY2VzczIwMjMlMmYlM2Z1dG1fbWVkaXVtJTNkc2VhcmNoJTI2dXRtX3NvdXJjZSUzZGJpbmclMjZ1dG1fY2FtcGFpZ24lM2RDb250ZW50MTBLZXlzdG9BSVN1Y2Nlc3MyMDIzV1cwNTIyR1BTYWRleHQlMjZ1dG1fdGVybSUzZGRhdGFyb2JvdCUyNnV0bV9jb250ZW50JTNkYWRfZXh0JTI2Y2FtcGFpZ25pZCUzZDUzMDcwODA5OSUyNmFkZ3JvdXBpZCUzZDEzNTAyMDI3NzQyMTc2OTglMjZhZGlkJTNkJTI2bXNjbGtpZCUzZDRiOWQwYTViOGQ4YTE4YTNkOGI1NmZmODcyOGM1Yzg4%26rlid%3D4b9d0a5b8d8a18a3d8b56ff8728c5c88&vqd=4-186891061025271488176703891649000666566&iurl=%7B1%7DIG%3D4E234A2A70BD47BA8D9437C2FE20102A%26CID%3D0DBF2147217B62AE10B83541204E63FB%26ID%3DDevEx%2C5067.1\",\"text\":\"10 Keys to AI Success\"},{\"snippet\":\"Our Platform Includes Four Fully Integrated Products. Read More.\",\"targetUrl\":\"https://duckduckgo.com/y.js?ad_domain=datarobot.com&ad_provider=bingv7aa&ad_type=txad&eddgt=TADjssf2xUPiUP4Z%2DpL%2Djw%3D%3D&rut=9fb136d3bde9c28bb5c474789d38421625142c03b6cd89976b66ce5517d4b669&u3=https%3A%2F%2Fwww.bing.com%2Faclick%3Fld%3De8ouxsAouiEsi%2DDc9BM8u61DVUCUxad20UaEBujK70scJAQPZlPSbbKQxos2Uiw4fxi21%2DVgvVutJWxTj1GAp5dA40ea3WyEU8c7sfEzgUyRqLe5kCWLFg_dSdKKL5y1cUUcQ8Vz7ZK25elf6NLz9GTXdqOHZ9m5%2D1nK%2DKxXXXJEwnP9Hq6S9AujOSKuU63ixP2CmCTMdq9C64CzxUWU_R17nOAG0%26u%3DaHR0cHMlM2ElMmYlMmZ3d3cuZGF0YXJvYm90LmNvbSUyZnByb2R1Y3QlMmYlM2ZjYW1wYWlnbmlkJTNkNTMwNzA4MDk5JTI2YWRncm91cGlkJTNkMTM1MDIwMjc3NDIxNzY5OCUyNmFkaWQlM2QlMjZtc2Nsa2lkJTNkM2E2NzBlMjdhODcxMThhODkzNDQ2Yjg0NTc3Nzk1YmQ%26rlid%3D3a670e27a87118a893446b84577795bd&vqd=4-73641448877716277028608780106696479949&iurl=%7B1%7DIG%3D4E234A2A70BD47BA8D9437C2FE20102A%26CID%3D0DBF2147217B62AE10B83541204E63FB%26ID%3DDevEx%2C5069.1\",\"text\":\"Product Overview\"}],\"tid\":\"6\\t8[7]\\t10[9]\\t12[11]\",\"type\":\"EnhancedSiteLink\"},\"tid\":\"1\"},\"ae\":null,\"c\":\"https://duckduckgo.com/y.js?ad_domain=datarobot.com&ad_provider=bingv7aa&ad_type=txad&eddgt=TADjssf2xUPiUP4Z%2DpL%2Djw%3D%3D&rut=489d897f192221406105931c23952ee9ddfcf9255a24130474c891f263e96e00&u3=https%3A%2F%2Fwww.bing.com%2Faclick%3Fld%3De839_LMH5pa8tva7WIdtsEfDVUCUwdFj2%2D%2DKtKdG6HDyt85Ce7V%2DiiQ2w5qD19CAl57L1dYymA6REaydrRBR2k46ZVmaiPv9HjtdlGliBcpsrqORKeHvMrkxqdZFZpqnPhGXg22zPoUr7K1CebeDBNfuES4v6ILDlFk4%2DMyDHiYvcYYoW2JyhgHVssKxbFEZG7OHwmGw%26u%3DaHR0cHMlM2ElMmYlMmZ3d3cuZGF0YXJvYm90LmNvbSUyZmpwJTJmbHAlMmZhaS1mb3ItYnVzaW5lc3MlMmYlM2Z1dG1fbWVkaXVtJTNkc2VhcmNoJTI2dXRtX3NvdXJjZSUzZGJpbmclMjZ1dG1fY2FtcGFpZ24lM2RERU1PMjAyM0FsbFByb2R1Y3RzSlAwNjI2QlBTJTI2dXRtX3Rlcm0lM2RkYXRhcm9ib3QlMjZ1dG1fY29udGVudCUzZERSX2JyYW5kZWRfcnNhJTI2Y2FtcGFpZ25pZCUzZDUzMDcwODA5OSUyNmFkZ3JvdXBpZCUzZDEzNTAyMDI3NzQyMTc2OTglMjZhZGlkJTNkJTI2bXNjbGtpZCUzZDQ3ZTc1ZGU1Y2FjYzFlODRlOTUzMzg0NjM1Y2FjNTc2%26rlid%3D47e75de5cacc1e84e953384635cac576&vqd=4-261134921179772841597192846410308597281&iurl=%7B1%7DIG%3D4E234A2A70BD47BA8D9437C2FE20102A%26CID%3D0DBF2147217B62AE10B83541204E63FB%26ID%3DDevEx%2C5061.1\",\"d\":\"datarobot.com\",\"h\":0,\"i\":\"\",\"k\":0,\"m\":0,\"o\":\"\",\"p\":1,\"relevancy\":{\"abstract\":\"%E9%AB%98%E7%B2%BE%E5%BA%A6%E3%81%AA%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%E3%83%A2%E3%83%87%E3%83%AB%E3%82%92%E6%A7%8B%E7%AF%89%E3%80%81%E5%AE%9F%E8%A3%85%E3%80%81%E9%81%8B%E7%94%A8%E3%80%82%3Cb%3EDataRobot%3C%2Fb%3E%E3%81%AF%E7%A4%BE%E5%86%85%E3%83%87%E3%83%BC%E3%82%BF%E3%81%8B%E3%82%89%E6%96%B0%E3%81%97%E3%81%84%E4%BE%A1%E5%80%A4%E3%82%92%E5%89%B5%E9%80%A0%E3%81%97%E3%81%BE%E3%81%99.%20%3Cb%3EDataRobot%3C%2Fb%3E%E3%81%AF%E7%B0%A1%E5%8D%98%E3%81%AA%E6%93%8D%E4%BD%9C%E3%81%A7%E3%83%87%E3%83%BC%E3%82%BF%E3%81%8B%E3%82%89%E4%BE%A1%E5%80%A4%E3%82%92%E5%89%B5%E5%87%BA\",\"adx_name\":\"none\",\"is_good_v10\":1,\"organic_ranks\":[\"0\",12,15,20,23],\"q\":\"DataRobot%20AI%20platform%20comparison\",\"q_words\":4,\"q_words_fuzzy\":0.25,\"q_words_in_ad\":1,\"root_domain\":\"datarobot.com\",\"start\":\"0\",\"title\":\"%E3%83%93%E3%83%83%E3%82%B0%E3%83%87%E3%83%BC%E3%82%BF%E5%88%86%E6%9E%90%E3%82%92%E9%AB%98%E9%80%9F%E5%8C%96%20%2D%20%E3%83%87%E3%83%BC%E3%82%BF%E3%81%8B%E3%82%89%E6%96%B0%E3%81%97%E3%81%84%E4%BE%A1%E5%80%A4%E3%82%92\"},\"s\":\"bingv7aa\",\"t\":\"\\u30d3\\u30c3\\u30b0\\u30c7\\u30fc\\u30bf\\u5206\\u6790\\u3092\\u9ad8\\u901f\\u5316 - \\u30c7\\u30fc\\u30bf\\u304b\\u3089\\u65b0\\u3057\\u3044\\u4fa1\\u5024\\u3092\",\"tid\":\"1,6,8[7],10[9],12[11]\",\"u\":\"https://duckduckgo.com/y.js?ad_domain=datarobot.com&ad_provider=bingv7aa&ad_type=txad&eddgt=TADjssf2xUPiUP4Z%2DpL%2Djw%3D%3D&rut=489d897f192221406105931c23952ee9ddfcf9255a24130474c891f263e96e00&u3=https%3A%2F%2Fwww.bing.com%2Faclick%3Fld%3De839_LMH5pa8tva7WIdtsEfDVUCUwdFj2%2D%2DKtKdG6HDyt85Ce7V%2DiiQ2w5qD19CAl57L1dYymA6REaydrRBR2k46ZVmaiPv9HjtdlGliBcpsrqORKeHvMrkxqdZFZpqnPhGXg22zPoUr7K1CebeDBNfuES4v6ILDlFk4%2DMyDHiYvcYYoW2JyhgHVssKxbFEZG7OHwmGw%26u%3DaHR0cHMlM2ElMmYlMmZ3d3cuZGF0YXJvYm90LmNvbSUyZmpwJTJmbHAlMmZhaS1mb3ItYnVzaW5lc3MlMmYlM2Z1dG1fbWVkaXVtJTNkc2VhcmNoJTI2dXRtX3NvdXJjZSUzZGJpbmclMjZ1dG1fY2FtcGFpZ24lM2RERU1PMjAyM0FsbFByb2R1Y3RzSlAwNjI2QlBTJTI2dXRtX3Rlcm0lM2RkYXRhcm9ib3QlMjZ1dG1fY29udGVudCUzZERSX2JyYW5kZWRfcnNhJTI2Y2FtcGFpZ25pZCUzZDUzMDcwODA5OSUyNmFkZ3JvdXBpZCUzZDEzNTAyMDI3NzQyMTc2OTglMjZhZGlkJTNkJTI2bXNjbGtpZCUzZDQ3ZTc1ZGU1Y2FjYzFlODRlOTUzMzg0NjM1Y2FjNTc2%26rlid%3D47e75de5cacc1e84e953384635cac576&vqd=4-261134921179772841597192846410308597281&iurl=%7B1%7DIG%3D4E234A2A70BD47BA8D9437C2FE20102A%26CID%3D0DBF2147217B62AE10B83541204E63FB%26ID%3DDevEx%2C5061.1\"}], {\"page_load_url\":\"https://duckduckgo.com/y.js?ifu=%7B3%7Dappid%3D055AAD1BA669BEB8B048128DC89A107C678B527B%26rguid%3D0716358df9934510b6d5d49119d2d6d3&iurl=%7B2%7DIG%3D4E234A2A70BD47BA8D9437C2FE20102A%26CID%3D0DBF2147217B62AE10B83541204E63FB%26Type%3DEvent.CPT%26DATA%3D0\",\"visibility_url\":\"https://duckduckgo.com/y.js?ivu=%7B4%7Dtype%3Dmv%26reqver%3D1.0%26rg%3D0716358df9934510b6d5d49119d2d6d3\"});DDG.deep.signalSummary = \"\";DDG.inject('DDG.Data.languages.resultLanguages', {\"en\":[\"https://www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/vendor/datarobot/product/datarobot-ai-platform\",\"https://www.gartner.com/reviews/market/data-preparation-tools/vendor/datarobot/product/datarobot-ai-platform\",\"https://www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/compare/datarobot-vs-h2o-ai\",\"https://www.trustradius.com/compare-products/datarobot-vs-google-cloud-ai\",\"https://www.trustradius.com/compare-products/dataiku-dss-vs-datarobot\",\"https://www.trustradius.com/compare-products/datarobot-vs-h2o\",\"https://www.eweek.com/big-data-and-analytics/c3-ai-vs-datarobot/\",\"https://research.aimultiple.com/automl-comparison/\",\"https://valohai.com/mlops-platforms-compared/\",\"https://www.gartner.com/reviews/market/augmented-data-quality-solutions/vendor/datarobot/product/datarobot-ai-platform\",\"https://internetstack.com/comparison/datarobot/vs/h2o-ai/\",\"https://www.datarobot.com/\",\"https://www.datarevenue.com/en-blog/ml-platforms-dataiku-vs-alteryx-vs-sagemaker\",\"https://solutionsreview.com/business-intelligence/the-best-ai-tools-for-data-science/\",\"https://docs.datarobot.com/en/docs/modeling/analyze-models/other/model-compare.html\",\"https://www.g2.com/products/datarobot/competitors/alternatives\",\"https://openai.com/blog/introducing-chatgpt-team\",\"https://www.datarobot.com/blog/big-data-and-artificial-intelligence-a-quick-comparison/\",\"https://www.gartner.com/reviews/market/data-preparation-tools/vendor/datarobot/product/datarobot-ai-platform/alternatives\",\"https://nvidianews.nvidia.com/news/geforce-rtx-40-super-series\",\"https://www.datarobot.com/blog/2023-a-year-of-innovation-and-impact/\"]});DDG.deep.pageLayoutSummary = \"a1w24r1\";DDG.inject('DDG.Data.languages.adLanguages', {});if (DDG.pageLayout) DDG.pageLayout.load('d',[{\"a\":\"AI APIs AND FRAMEWORKS DATA PLATFORMS Custom Chat APPLICATIONS Compose and Compare Compose and Compare Train and Tune Train and Tune Analyze and Transform Analyze and Transform BUILD BUILD Document and Comply Document and Comply Audit and Approve Audit and Approve Register and Manage Register and Manage GOVERN GOVERN Learn and Optimize Learn and...\",\"ae\":null,\"c\":\"https://www.datarobot.com/platform/\",\"d\":\"www.datarobot.com/platform/\",\"da\":\"\",\"h\":0,\"i\":\"www.datarobot.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Platform Overview | DataRobot AI Platform\",\"u\":\"https://www.datarobot.com/platform/\"},{\"a\":\"Reviewed in Last 12 Months mail_outline Email Page 4.6 508 Ratings (All Time) Rating Distribution 5 Star 63% 4 Star 33% 3 Star 3% 2 Star 0% 1 Star 0% Distribution based on 508 ratings Customer Experience Evaluation & Contracting 4.5 Integration & Deployment 4.5 Service & Support 4.7 Product Capabilities 4.6 FREE\",\"ae\":null,\"c\":\"https://www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/vendor/datarobot/product/datarobot-ai-platform\",\"d\":\"www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/vendor/datarobot/product/datarobot-ai-platform\",\"da\":\"\",\"h\":0,\"i\":\"www.gartner.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"DataRobot AI Platform Reviews - Gartner\",\"u\":\"https://www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/vendor/datarobot/product/datarobot-ai-platform\"},{\"a\":\"4 Star 42% 3 Star 3% 2 Star 0% 1 Star 0% Distribution based on 36 ratings Customer Experience Evaluation & Contracting 4.6 Integration & Deployment 4.4 Service & Support 4.6 Product Capabilities 4.3 FREE View and Download Peer Insights About DataRobot AI Platform\",\"ae\":null,\"c\":\"https://www.gartner.com/reviews/market/data-preparation-tools/vendor/datarobot/product/datarobot-ai-platform\",\"d\":\"www.gartner.com/reviews/market/data-preparation-tools/vendor/datarobot/product/datarobot-ai-platform\",\"da\":\"\",\"h\":0,\"i\":\"www.gartner.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"DataRobot AI Platform Reviews - Gartner\",\"u\":\"https://www.gartner.com/reviews/market/data-preparation-tools/vendor/datarobot/product/datarobot-ai-platform\"},{\"a\":\"DataRobot vs H2O.ai Based on verified reviews from real users in the Data Science and Machine Learning Platforms market. DataRobot has a rating of 4.6 stars with 508 reviews. H2O.ai has a rating of 4.4 stars with 108 reviews.\",\"ae\":null,\"c\":\"https://www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/compare/datarobot-vs-h2o-ai\",\"d\":\"www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/compare/datarobot-vs-h2o-ai\",\"da\":\"\",\"h\":0,\"i\":\"www.gartner.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"DataRobot vs H2O.ai 2024 | Gartner Peer Insights\",\"u\":\"https://www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/compare/datarobot-vs-h2o-ai\"},{\"a\":\"84 Reviews and Ratings Path to AI Success Google Cloud AI 84 Reviews and Ratings Have you used any of these products before? No, I use something else Compare DataRobot vs Google Cloud AI. 168 verified user reviews and ratings of features, pros, cons, pricing, support and more.\",\"ae\":null,\"c\":\"https://www.trustradius.com/compare-products/datarobot-vs-google-cloud-ai\",\"d\":\"www.trustradius.com/compare-products/datarobot-vs-google-cloud-ai\",\"da\":\"\",\"h\":0,\"i\":\"www.trustradius.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"DataRobot vs Google Cloud AI | TrustRadius\",\"u\":\"https://www.trustradius.com/compare-products/datarobot-vs-google-cloud-ai\"},{\"a\":\"DataRobot 84 Reviews and Ratings Path to AI Success Compare Dataiku DSS vs DataRobot. 103 verified user reviews and ratings of features, pros, cons, pricing, support and more.\",\"ae\":null,\"c\":\"https://www.trustradius.com/compare-products/dataiku-dss-vs-datarobot\",\"d\":\"www.trustradius.com/compare-products/dataiku-dss-vs-datarobot\",\"da\":\"\",\"h\":0,\"i\":\"www.trustradius.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku DSS vs DataRobot | TrustRadius\",\"u\":\"https://www.trustradius.com/compare-products/dataiku-dss-vs-datarobot\"},{\"a\":\"The DataRobot AI Platform is presented as a solution that accelerates and democratizes data science by automating the end-to-end journey from data to value and allows users to deploy AI applications at scale. DataRobot provides a centrally governed platform that gives users AI to drive business outcomes, that is available on the user's cloud ...\",\"ae\":null,\"c\":\"https://www.trustradius.com/compare-products/datarobot-vs-h2o\",\"d\":\"www.trustradius.com/compare-products/datarobot-vs-h2o\",\"da\":\"\",\"h\":0,\"i\":\"www.trustradius.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"DataRobot vs H2O | TrustRadius\",\"u\":\"https://www.trustradius.com/compare-products/datarobot-vs-h2o\"},{\"a\":\"C3 AI and DataRobot are two of the leading AI cloud platforms. As such, this is a close comparison. Each has an extensive set of artificial intelligence features. Which is best for your...\",\"ae\":null,\"c\":\"https://www.eweek.com/big-data-and-analytics/c3-ai-vs-datarobot/\",\"d\":\"www.eweek.com/big-data-and-analytics/c3-ai-vs-datarobot/\",\"da\":\"\",\"e\":\"2022-12-02T00:00:00.0000000\",\"h\":0,\"i\":\"www.eweek.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"C3 AI vs. DataRobot: Top AI CloudPlatforms | eWEEK\",\"u\":\"https://www.eweek.com/big-data-and-analytics/c3-ai-vs-datarobot/\"},{\"a\":\"Performance: H2O.ai has greater performance measures in classification and regression tasks. Automation: Tazi.ai and DataRobot offer greater automation rates. Popularity: Along with the Google Cloud AutoML platform, H2O.ai is also the most searched autoML vendor. DataRobot, H2O.ai, and Google Cloud AutoML are the leading vendors. However, you ...\",\"ae\":null,\"c\":\"https://research.aimultiple.com/automl-comparison/\",\"d\":\"research.aimultiple.com/automl-comparison/\",\"da\":\"\",\"e\":\"2023-12-14T00:00:00.0000000\",\"h\":0,\"i\":\"research.aimultiple.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"AutoML Tech / Products Comparison & Market Landscape in 2024 - AIMultiple\",\"u\":\"https://research.aimultiple.com/automl-comparison/\"},{\"a\":\"The platforms we've chosen for our analysis are ClearML, cnvrg.io, Dataiku, Datarobot, Iguazio, Sagemaker, Seldon and Valohai from the managed side, and Flyte, Kubeflow, MLflow and Metaflow from the open-source side. This is by no means an exhaustive list of all the MLOps tools out there. Most of these are tools that describe themselves as ...\",\"ae\":null,\"c\":\"https://valohai.com/mlops-platforms-compared/\",\"d\":\"valohai.com/mlops-platforms-compared/\",\"da\":\"\",\"h\":0,\"i\":\"valohai.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MLOps Platforms Compared - Valohai\",\"u\":\"https://valohai.com/mlops-platforms-compared/\"},{\"a\":\"5 Star 33% 4 Star 67% 3 Star 0% 2 Star 0% 1 Star 0% Distribution based on 3 ratings Customer Experience Evaluation & Contracting 4.5 Integration & Deployment 4.7 Service & Support 4.7 Product Capabilities 4.7 FREE View and Download Peer Insights About DataRobot AI Platform\",\"ae\":null,\"c\":\"https://www.gartner.com/reviews/market/augmented-data-quality-solutions/vendor/datarobot/product/datarobot-ai-platform\",\"d\":\"www.gartner.com/reviews/market/augmented-data-quality-solutions/vendor/datarobot/product/datarobot-ai-platform\",\"da\":\"\",\"h\":0,\"i\":\"www.gartner.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"DataRobot AI Platform Reviews - Gartner\",\"u\":\"https://www.gartner.com/reviews/market/augmented-data-quality-solutions/vendor/datarobot/product/datarobot-ai-platform\"},{\"a\":\"H2O.ai. H2O.ai is an open source platform for machine learning and predictive analytics. It is designed to help businesses and organizations make better decisions by leveraging the power of data. H2O.ai is used by data scientists, engineers, and business analysts to build and deploy machine learning models quickly and easily.\",\"ae\":null,\"c\":\"https://internetstack.com/comparison/datarobot/vs/h2o-ai/\",\"d\":\"internetstack.com/comparison/datarobot/vs/h2o-ai/\",\"da\":\"\",\"h\":0,\"i\":\"internetstack.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"DataRobot vs H2O.ai - Which is better? (Comparison)\",\"u\":\"https://internetstack.com/comparison/datarobot/vs/h2o-ai/\"},{\"a\":\"75% faster from start to implementation with AI automation 18X greater likelihood to buy for the highest-scored leads See the Story\",\"ae\":null,\"c\":\"https://www.datarobot.com/\",\"d\":\"www.datarobot.com\",\"da\":\"\",\"h\":0,\"i\":\"www.datarobot.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"DataRobot AI Platform | Deliver Value from AI\",\"u\":\"https://www.datarobot.com/\"},{\"a\":\"Dataiku vs. Alteryx. Dataiku and Alteryx are both managed machine learning platforms, but Dataiku focuses on the engineering aspects, while Alteryx focuses on analytics and presentation. Dataiku provides Data Science Studio (DSS), a cross-platform desktop application that includes a notebook (similar to Jupyter Notebook) for engineers to write ...\",\"ae\":null,\"c\":\"https://www.datarevenue.com/en-blog/ml-platforms-dataiku-vs-alteryx-vs-sagemaker\",\"d\":\"www.datarevenue.com/en-blog/ml-platforms-dataiku-vs-alteryx-vs-sagemaker\",\"da\":\"\",\"h\":0,\"i\":\"www.datarevenue.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"ML Platforms: Dataiku vs. Alteryx vs. Sagemaker vs. Datarobot\",\"u\":\"https://www.datarevenue.com/en-blog/ml-platforms-dataiku-vs-alteryx-vs-sagemaker\"},{\"a\":\"DataRobot. Platform: DataRobot Enterprise AI Platform Related products: Paxata Data Preparation, Automated Machine Learning, Automated Time Series, MLOps Description: DataRobot offers an enterprise AI platform that automates the end-to-end process for building, deploying, and maintaining AI. The product is powered by open-source algorithms and can be leveraged on-prem, in the cloud or as a ...\",\"ae\":null,\"c\":\"https://solutionsreview.com/business-intelligence/the-best-ai-tools-for-data-science/\",\"d\":\"solutionsreview.com/business-intelligence/the-best-ai-tools-for-data-science/\",\"da\":\"\",\"e\":\"2024-01-11T00:00:00.0000000\",\"h\":0,\"i\":\"solutionsreview.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"The 11 Best AI Tools for Data Science to Consider in 2024\",\"u\":\"https://solutionsreview.com/business-intelligence/the-best-ai-tools-for-data-science/\"},{\"a\":\"Compare models. To compare models in a project with at least two models built, either: Select the Model Comparison tab. Select two models from the Leaderboard and use the Leaderboard menu's Compare Selected option. Once on the page, select models from the dropdown. The associated model statistics update to reflect the currently selected model ...\",\"ae\":null,\"c\":\"https://docs.datarobot.com/en/docs/modeling/analyze-models/other/model-compare.html\",\"d\":\"docs.datarobot.com/en/docs/modeling/analyze-models/other/model-compare.html\",\"da\":\"\",\"e\":\"2022-11-01T00:00:00.0000000\",\"h\":0,\"i\":\"docs.datarobot.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Model Comparison: DataRobot docs - DataRobot AI Platform\",\"u\":\"https://docs.datarobot.com/en/docs/modeling/analyze-models/other/model-compare.html\"},{\"a\":\"Top DataRobot AI Platform Alternatives (All Time) How alternatives are selected Dataiku MATLAB Alteryx Designer IBM SPSS Statistics RapidMiner Studio Base SAS Anaconda Enterprise Databricks Data Intelligence Platform Considering alternatives to DataRobot AI Platform?\",\"ae\":null,\"c\":\"https://www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/vendor/datarobot/product/datarobot-ai-platform/alternatives\",\"d\":\"www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/vendor/datarobot/product/datarobot-ai-platform/alternatives\",\"da\":\"\",\"h\":0,\"i\":\"www.gartner.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"DataRobot AI Platform Alternatives - Gartner\",\"u\":\"https://www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/vendor/datarobot/product/datarobot-ai-platform/alternatives\"},{\"a\":\"Top Alternatives to DataRobot AI Platform MathWorks Matlab Databricks Lakehouse Platform Dataiku TensorFlow TFX Google Cloud Vertex AI Alteryx View All Alternatives Best Alternatives and Competitors to DataRobot AI Platform\",\"ae\":null,\"c\":\"https://www.softwarereviews.com/categories/200/products/6813/alternatives\",\"d\":\"www.softwarereviews.com/categories/200/products/6813/alternatives\",\"da\":\"translations\",\"h\":0,\"i\":\"www.softwarereviews.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"DataRobot AI Platform Alternatives and Competitors | Machine ...\",\"u\":\"https://www.softwarereviews.com/categories/200/products/6813/alternatives\"},{\"a\":\"#1 Alteryx (458) 4.6 out of 5 Alteryx drives transformational business outcomes through unified analytics, data science, and process automation. Categories in common with DataRobot: Data Science and Machine Learning Platforms Predictive Analytics Try for free Reviewers say compared to DataRobot, Alteryx is: Easier to set up More expensive\",\"ae\":null,\"c\":\"https://www.g2.com/products/datarobot/competitors/alternatives\",\"d\":\"www.g2.com/products/datarobot/competitors/alternatives\",\"da\":\"\",\"h\":0,\"i\":\"www.g2.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Top 10 DataRobot Alternatives & Competitors (Free/Paid) - G2\",\"u\":\"https://www.g2.com/products/datarobot/competitors/alternatives\"},{\"a\":\"Today, we're adding a new self-serve plan: ChatGPT Team. ChatGPT Team offers access to our advanced models like GPT-4 and DALL\\u00b7E 3, and tools like Advanced Data Analysis. It additionally includes a dedicated collaborative workspace for your team and admin tools for team management. As with ChatGPT Enterprise, you own and control your ...\",\"ae\":null,\"c\":\"https://openai.com/blog/introducing-chatgpt-team\",\"d\":\"openai.com/blog/introducing-chatgpt-team\",\"da\":\"\",\"e\":\"2024-01-10T00:00:00.0000000\",\"h\":0,\"i\":\"openai.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Introducing ChatGPT Team - OpenAI\",\"u\":\"https://openai.com/blog/introducing-chatgpt-team\"},{\"a\":\"Big data and artificial intelligence: a quick comparison | DataRobot AI Platform Blog Big data and artificial intelligence: a quick comparison Big data and artificial intelligence: a quick comparison March 3, 2020 by DataRobot \\u00b7 3 min read This article was originally published at Algorithimia's website.\",\"ae\":null,\"c\":\"https://www.datarobot.com/blog/big-data-and-artificial-intelligence-a-quick-comparison/\",\"d\":\"www.datarobot.com/blog/big-data-and-artificial-intelligence-a-quick-comparison/\",\"da\":\"\",\"h\":0,\"i\":\"www.datarobot.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Big data and artificial intelligence: a quick comparison | DataRobot AI ...\",\"u\":\"https://www.datarobot.com/blog/big-data-and-artificial-intelligence-a-quick-comparison/\"},{\"a\":\"36 Ratings compare_arrows Compare rate_review Write a Review download_2 Download PDF Related markets: DataRobot AI Platform in Data Science and Machine Learning Platforms (508 Reviews), DataRobot AI Platform in Augmented Data Quality Solutions (3 Reviews), DataRobot AI Platform in Predictive Analytics Software (1 Review)\",\"ae\":null,\"c\":\"https://www.gartner.com/reviews/market/data-preparation-tools/vendor/datarobot/product/datarobot-ai-platform/alternatives\",\"d\":\"www.gartner.com/reviews/market/data-preparation-tools/vendor/datarobot/product/datarobot-ai-platform/alternatives\",\"da\":\"\",\"h\":0,\"i\":\"www.gartner.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"DataRobot AI Platform Alternatives - Gartner\",\"u\":\"https://www.gartner.com/reviews/market/data-preparation-tools/vendor/datarobot/product/datarobot-ai-platform/alternatives\"},{\"a\":\"An AI-Powered Leap in PC Computing The new GeForce RTX SUPER GPUs are the ultimate way to experience AI on PCs. Specialized AI Tensor Cores deliver up to 836 AI TOPS to deliver transformative capabilities for AI in gaming, creating and everyday productivity. The rich software stack built on top of RTX GPUs further accelerates AI.\",\"ae\":null,\"c\":\"https://nvidianews.nvidia.com/news/geforce-rtx-40-super-series\",\"d\":\"nvidianews.nvidia.com/news/geforce-rtx-40-super-series\",\"da\":\"translations\",\"e\":\"2024-01-08T00:00:00.0000000\",\"h\":0,\"i\":\"nvidianews.nvidia.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"GeForce RTX 40 SUPER Series: New Heroes Debut in the Gaming and ...\",\"u\":\"https://nvidianews.nvidia.com/news/geforce-rtx-40-super-series\"},{\"a\":\"We unveiled new enterprise-grade generative AI functionality to close the confidence gap and accelerate adoption, including generative AI application cost and performance monitoring, a unified observability console and registry for governance, multi-provider comparison playground and other enhancements to deliver greater transparency and governa...\",\"ae\":null,\"c\":\"https://www.datarobot.com/blog/2023-a-year-of-innovation-and-impact/\",\"d\":\"www.datarobot.com/blog/2023-a-year-of-innovation-and-impact/\",\"da\":\"translations\",\"e\":\"2023-12-21T00:00:00.0000000\",\"h\":0,\"i\":\"www.datarobot.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"2023: A Year of Innovation and Impact | DataRobot AI Platform\",\"u\":\"https://www.datarobot.com/blog/2023-a-year-of-innovation-and-impact/\"},{\"n\":\"/d.js?q=DataRobot%20AI%20platform%20comparison&kl=wt-wt&l=wt-wt&p=&s=24&ex=-1&ct=US&sp=0&vqd=4-58620545585474558767320902709740831322\"}]);DDG.duckbar.load('images');DDG.duckbar.load('news');DDG.duckbar.load('videos');DDG.duckbar.loadModule('related_searches', {\"ads\":[],\"query\":\"DataRobot AI platform comparison\",\"queryEncoded\":\"DataRobot%20AI%20platform%20comparison\",\"response_type\":\"places\",\"results\":[{\"display_text\":\"data robot ai platform\",\"text\":\"data robot ai platform\",\"web_search_url\":\"?q=data%20robot%20ai%20platform\"},{\"display_text\":\"datarobot ai partners\",\"text\":\"datarobot ai partners\",\"web_search_url\":\"?q=datarobot%20ai%20partners\"},{\"display_text\":\"data robot platforms\",\"text\":\"data robot platforms\",\"web_search_url\":\"?q=data%20robot%20platforms\"},{\"display_text\":\"datarobot partner portal\",\"text\":\"datarobot partner portal\",\"web_search_url\":\"?q=datarobot%20partner%20portal\"},{\"display_text\":\"data robot partners\",\"text\":\"data robot partners\",\"web_search_url\":\"?q=data%20robot%20partners\"},{\"display_text\":\"data robot data warehouse\",\"text\":\"data robot data warehouse\",\"web_search_url\":\"?q=data%20robot%20data%20warehouse\"}],\"vqd\":{\"DataRobot%20AI%20platform%20comparison\":\"4-58620545585474558767320902709740831322\"}});if (DDG.pageLayout) DDG.pageLayout.initialize({\"mainline\":{\"items\":[[\"ad\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"related_searches\"]]}}, { start: 0 });DDG.deep.emit(\"load:completed\");", + "curl-cffi-POST-https://duckduckgo.com-{\"data\": {\"q\": \"Dataiku vs DataRobot features\"}}": "Dataiku vs DataRobot features at DuckDuckGo
", + "curl-cffi-GET-https://links.duckduckgo.com/d.js-{\"params\": {\"bing_market\": \"wt-WT\", \"df\": null, \"ex\": \"-1\", \"kl\": \"wt-wt\", \"l\": \"wt-wt\", \"q\": \"Dataiku vs DataRobot features\", \"s\": \"0\", \"sp\": \"0\", \"vqd\": \"4-334935250614046875026454141242803242982\"}}": "if (DDG.deep && DDG.deep.setUpstream) DDG.deep.setUpstream(\"bingv7aa\");DDG.deep.bn={'ivc':1};if (DDG.pageLayout) DDG.pageLayout.load('a',[{\"a\":\"\\u9ad8\\u7cbe\\u5ea6\\u306a\\u6a5f\\u68b0\\u5b66\\u7fd2\\u30e2\\u30c7\\u30eb\\u3092\\u69cb\\u7bc9\\u3001\\u5b9f\\u88c5\\u3001\\u904b\\u7528\\u3002DataRobot\\u306f\\u793e\\u5185\\u30c7\\u30fc\\u30bf\\u304b\\u3089\\u65b0\\u3057\\u3044\\u4fa1\\u5024\\u3092\\u5275\\u9020\\u3057\\u307e\\u3059. DataRobot\\u306f\\u4f01\\u696d\\u306e\\u8ab2\\u984c\\u89e3\\u6c7a\\u306b\\u7279\\u5316\\u3002\\u610f\\u601d\\u6c7a\\u5b9a\\u306e\\u81ea\\u52d5\\u5316\\u304b\\u3089\\u9700\\u8981\\u4e88\\u6e2c\\u3001\\u8981\\u56e0\\u5206\\u6790\\u307e\\u3067\\u3053\\u306a\\u3059AI\\u30c4\\u30fc\\u30eb\",\"adext\":{\"callout\":{\"t\":\"Data Science Guardrails \\u00b7 Applied AI Expertise \\u00b7 Trusted by Fortune 50\",\"tid\":\"6\"},\"filterlinks\":{\"l\":[],\"tid\":\"\"},\"sitelinks\":{\"l\":[{\"snippet\":\"Explore the DataRobot AI Platform Get Started With a 30-Day Trial\",\"targetUrl\":\"https://duckduckgo.com/y.js?ad_domain=datarobot.com&ad_provider=bingv7aa&ad_type=txad&eddgt=uchwI3Eul8XsE%2DSUlPqxXg%3D%3D&rut=2381550f96a087800d427735905717264a1a708643136f2736a970e740068621&u3=https%3A%2F%2Fwww.bing.com%2Faclick%3Fld%3De8BGV0WifLHqlNArHdJt3WDTVUCUzDyrVI_ULomBTgn_xk1MKGRFElGY7vQ8fpE4l__S3CnH6%2D2cXlBQayeIz9CbLU7C4XEu8BgG6oZNQ6EtjG6vrYe5hjw1GZN7VBIkj6nn%2DsoUXy14mVbvkM5ojXVf8oeoz8pwdOc4ANH2TiL9vqJe6Lud2IZXvxJf1I%2DA935XcPQobPZKQaFNFMyygI3Y4TW8k%26u%3DaHR0cHMlM2ElMmYlMmZ3d3cuZGF0YXJvYm90LmNvbSUyZnRyaWFsJTJmJTNmdXRtX21lZGl1bSUzZHNlYXJjaCUyNnV0bV9zb3VyY2UlM2RiaW5nJTI2dXRtX2NhbXBhaWduJTNkRnJlZVRyaWFsMjAyM1dXMDgxNkdQU2FkZXh0JTI2Y2FtcGFpZ25pZCUzZDUzMDcwODA5OSUyNmFkZ3JvdXBpZCUzZDEzNTAyMDI3NzQyMTc2OTglMjZhZGlkJTNkJTI2bXNjbGtpZCUzZDFmMzU0ODE0ODNmMTEyM2Y5NGMzMmRiNzdjZjk5OWFm%26rlid%3D1f35481483f1123f94c32db77cf999af&vqd=4-25671318592048362755712261648304518289&iurl=%7B1%7DIG%3D3EB403B8C4EA42F4B7FF0CE90CB46EF0%26CID%3D2F20CB6F269D6DD02331DF69279D6C12%26ID%3DDevEx%2C5064.1\",\"text\":\"DataRobot Free Trial\"},{\"snippet\":\"Unlock Your AI Success in 2023 Tips on the Path of Value-Driven AI\",\"targetUrl\":\"https://duckduckgo.com/y.js?ad_domain=datarobot.com&ad_provider=bingv7aa&ad_type=txad&eddgt=uchwI3Eul8XsE%2DSUlPqxXg%3D%3D&rut=08def2477dd7311fbcffe4c409d28fcdbe68925a50cd2894a7502f8a11785352&u3=https%3A%2F%2Fwww.bing.com%2Faclick%3Fld%3De8lYdQlfjG0%2Dh77MMyzT0CuDVUCUyAuuZDH6K8NWyD2XSLoABvrUNVChVbIVOVgzl4xdT3EEUvHgd9P_FWLUDT2My42qKUP3iV87B7hLXXHLdGf7yjst8tWjp%2DcaQz3uiI0c5oom%2DRo8D7A4nohZAtS9199RQLYbNcbOpJnrNMCFmz6EiWk7JqMQ9DE1t9AjaMUWEkEV%2D3W2e8XmBq5bKtRsWnT0E%26u%3DaHR0cHMlM2ElMmYlMmZ3d3cuZGF0YXJvYm90LmNvbSUyZnJlc291cmNlcyUyZmFpc3VjY2VzczIwMjMlMmYlM2Z1dG1fbWVkaXVtJTNkc2VhcmNoJTI2dXRtX3NvdXJjZSUzZGJpbmclMjZ1dG1fY2FtcGFpZ24lM2RDb250ZW50MTBLZXlzdG9BSVN1Y2Nlc3MyMDIzV1cwNTIyR1BTYWRleHQlMjZ1dG1fdGVybSUzZGRhdGFyb2JvdCUyNnV0bV9jb250ZW50JTNkYWRfZXh0JTI2Y2FtcGFpZ25pZCUzZDUzMDcwODA5OSUyNmFkZ3JvdXBpZCUzZDEzNTAyMDI3NzQyMTc2OTglMjZhZGlkJTNkJTI2bXNjbGtpZCUzZDI5Zjc0NWY1MzNiNzE2NDU5ZGY0MjA1NmNjYmYyYWU0%26rlid%3D29f745f533b716459df42056ccbf2ae4&vqd=4-333465595216651803104351585568313334233&iurl=%7B1%7DIG%3D3EB403B8C4EA42F4B7FF0CE90CB46EF0%26CID%3D2F20CB6F269D6DD02331DF69279D6C12%26ID%3DDevEx%2C5066.1\",\"text\":\"10 Keys to AI Success\"},{\"snippet\":\"Our Platform Includes Four Fully Integrated Products. Read More.\",\"targetUrl\":\"https://duckduckgo.com/y.js?ad_domain=datarobot.com&ad_provider=bingv7aa&ad_type=txad&eddgt=uchwI3Eul8XsE%2DSUlPqxXg%3D%3D&rut=fbe7591a97a4b400635f8cfafd71893553c70fc90218355b7d5622310d9567db&u3=https%3A%2F%2Fwww.bing.com%2Faclick%3Fld%3De8cB2vIW6%2D5rxeC5vl08jFZjVUCUw2oN7vfXdo8rlxVmZIfw2bF94_ya9lvPQwUYXJFtTGXBslf_XCcVTiFtj2KJzp9yzLPOdWafvxxwBzn2iwextOSL%2Daq20iQ8nZNktMLYBD1xp3WjThLdejbBCFrR_RvD1YZcHcKf5y5auyV04F_V6x_D6nUwdRYFDmdyciLcpT7JO12EZkmM%2D1buahlzuiBmw%26u%3DaHR0cHMlM2ElMmYlMmZ3d3cuZGF0YXJvYm90LmNvbSUyZnByb2R1Y3QlMmYlM2ZjYW1wYWlnbmlkJTNkNTMwNzA4MDk5JTI2YWRncm91cGlkJTNkMTM1MDIwMjc3NDIxNzY5OCUyNmFkaWQlM2QlMjZtc2Nsa2lkJTNkMGZhOTg4ZjJkYWU2MWE3MGJhOTVlZDUxMjVlZWFlNDA%26rlid%3D0fa988f2dae61a70ba95ed5125eeae40&vqd=4-211419575679328898707892660118042825990&iurl=%7B1%7DIG%3D3EB403B8C4EA42F4B7FF0CE90CB46EF0%26CID%3D2F20CB6F269D6DD02331DF69279D6C12%26ID%3DDevEx%2C5068.1\",\"text\":\"Product Overview\"}],\"tid\":\"7\\t9[8]\\t11[10]\\t13[12]\",\"type\":\"EnhancedSiteLink\"},\"tid\":\"1\"},\"ae\":{\"callout\":[\"Data Science Guardrails \\u00b7 Applied AI Expertise \\u00b7 Trusted by Fortune 50\"]},\"c\":\"https://duckduckgo.com/y.js?ad_domain=datarobot.com&ad_provider=bingv7aa&ad_type=txad&eddgt=uchwI3Eul8XsE%2DSUlPqxXg%3D%3D&rut=94a279ed1549c0107c5c13f21161fd5aaa0d3f08d19e7afd2ed4a19463b69d7d&u3=https%3A%2F%2Fwww.bing.com%2Faclick%3Fld%3De8XX6qufLbIkEZRIFo_zgmlDVUCUwOnCSpTtxK0dn2QInSfOGU5eU24GjiRwhmSr89Qa92PcEtK2h6KVoghC%2DNwNrkANG4L6sVirCfv5kl7GPWO9gqgcdw8x5ELjGH7N2HWgbdtH%2D7TWKtxZVdVIFwYJUQDUgM_ODwTspzwBbKKLHD4EPAO5U3RDO3R_igFUlsxkeFXA%26u%3DaHR0cHMlM2ElMmYlMmZ3d3cuZGF0YXJvYm90LmNvbSUyZmpwJTJmbHAlMmZhaS1mb3ItYnVzaW5lc3MlMmYlM2Z1dG1fbWVkaXVtJTNkc2VhcmNoJTI2dXRtX3NvdXJjZSUzZGJpbmclMjZ1dG1fY2FtcGFpZ24lM2RERU1PMjAyM0FsbFByb2R1Y3RzSlAwNjI2QlBTJTI2dXRtX3Rlcm0lM2RkYXRhcm9ib3QlMjZ1dG1fY29udGVudCUzZERSX2JyYW5kZWRfcnNhJTI2Y2FtcGFpZ25pZCUzZDUzMDcwODA5OSUyNmFkZ3JvdXBpZCUzZDEzNTAyMDI3NzQyMTc2OTglMjZhZGlkJTNkJTI2bXNjbGtpZCUzZGQxMGY4ZjY4ZDYxZjFiOTg2NTc1ZWFjYjI5MTczYTQ1%26rlid%3Dd10f8f68d61f1b986575eacb29173a45&vqd=4-152568096679810917558416500867559274982&iurl=%7B1%7DIG%3D3EB403B8C4EA42F4B7FF0CE90CB46EF0%26CID%3D2F20CB6F269D6DD02331DF69279D6C12%26ID%3DDevEx%2C5059.1\",\"d\":\"datarobot.com\",\"h\":0,\"i\":\"\",\"k\":0,\"m\":0,\"o\":\"\",\"p\":1,\"relevancy\":{\"abstract\":\"%E9%AB%98%E7%B2%BE%E5%BA%A6%E3%81%AA%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%E3%83%A2%E3%83%87%E3%83%AB%E3%82%92%E6%A7%8B%E7%AF%89%E3%80%81%E5%AE%9F%E8%A3%85%E3%80%81%E9%81%8B%E7%94%A8%E3%80%82%3Cb%3EDataRobot%3C%2Fb%3E%E3%81%AF%E7%A4%BE%E5%86%85%E3%83%87%E3%83%BC%E3%82%BF%E3%81%8B%E3%82%89%E6%96%B0%E3%81%97%E3%81%84%E4%BE%A1%E5%80%A4%E3%82%92%E5%89%B5%E9%80%A0%E3%81%97%E3%81%BE%E3%81%99.%20DataRobot%E3%81%AF%E4%BC%81%E6%A5%AD%E3%81%AE%E8%AA%B2%E9%A1%8C%E8%A7%A3%E6%B1%BA%E3%81%AB%E7%89%B9%E5%8C%96%E3%80%82%E6%84%8F%E6%80%9D%E6%B1%BA%E5%AE%9A%E3%81%AE%E8%87%AA%E5%8B%95%E5%8C%96%E3%81%8B%E3%82%89%E9%9C%80%E8%A6%81%E4%BA%88%E6%B8%AC%E3%80%81%E8%A6%81%E5%9B%A0%E5%88%86%E6%9E%90%E3%81%BE%E3%81%A7%E3%81%93%E3%81%AA%E3%81%99AI%E3%83%84%E3%83%BC%E3%83%AB\",\"adx_name\":\"none\",\"is_good_v10\":0,\"q\":\"Dataiku%20vs%20DataRobot%20features\",\"q_words\":4,\"q_words_fuzzy\":0.25,\"q_words_in_ad\":1,\"root_domain\":\"datarobot.com\",\"start\":\"0\",\"title\":\"%E3%83%87%E3%83%BC%E3%82%BF%E3%81%8B%E3%82%89%E6%96%B0%E3%81%97%E3%81%84%E4%BE%A1%E5%80%A4%E3%82%92%20%2D%20%E7%A4%BE%E5%86%85%E3%83%87%E3%83%BC%E3%82%BF%E3%81%8B%E3%82%89%E4%BE%A1%E5%80%A4%E5%89%B5%E5%87%BA\"},\"s\":\"bingv7aa\",\"t\":\"\\u30c7\\u30fc\\u30bf\\u304b\\u3089\\u65b0\\u3057\\u3044\\u4fa1\\u5024\\u3092 - \\u793e\\u5185\\u30c7\\u30fc\\u30bf\\u304b\\u3089\\u4fa1\\u5024\\u5275\\u51fa\",\"tid\":\"1,6,7,9[8],11[10],13[12]\",\"u\":\"https://duckduckgo.com/y.js?ad_domain=datarobot.com&ad_provider=bingv7aa&ad_type=txad&eddgt=uchwI3Eul8XsE%2DSUlPqxXg%3D%3D&rut=94a279ed1549c0107c5c13f21161fd5aaa0d3f08d19e7afd2ed4a19463b69d7d&u3=https%3A%2F%2Fwww.bing.com%2Faclick%3Fld%3De8XX6qufLbIkEZRIFo_zgmlDVUCUwOnCSpTtxK0dn2QInSfOGU5eU24GjiRwhmSr89Qa92PcEtK2h6KVoghC%2DNwNrkANG4L6sVirCfv5kl7GPWO9gqgcdw8x5ELjGH7N2HWgbdtH%2D7TWKtxZVdVIFwYJUQDUgM_ODwTspzwBbKKLHD4EPAO5U3RDO3R_igFUlsxkeFXA%26u%3DaHR0cHMlM2ElMmYlMmZ3d3cuZGF0YXJvYm90LmNvbSUyZmpwJTJmbHAlMmZhaS1mb3ItYnVzaW5lc3MlMmYlM2Z1dG1fbWVkaXVtJTNkc2VhcmNoJTI2dXRtX3NvdXJjZSUzZGJpbmclMjZ1dG1fY2FtcGFpZ24lM2RERU1PMjAyM0FsbFByb2R1Y3RzSlAwNjI2QlBTJTI2dXRtX3Rlcm0lM2RkYXRhcm9ib3QlMjZ1dG1fY29udGVudCUzZERSX2JyYW5kZWRfcnNhJTI2Y2FtcGFpZ25pZCUzZDUzMDcwODA5OSUyNmFkZ3JvdXBpZCUzZDEzNTAyMDI3NzQyMTc2OTglMjZhZGlkJTNkJTI2bXNjbGtpZCUzZGQxMGY4ZjY4ZDYxZjFiOTg2NTc1ZWFjYjI5MTczYTQ1%26rlid%3Dd10f8f68d61f1b986575eacb29173a45&vqd=4-152568096679810917558416500867559274982&iurl=%7B1%7DIG%3D3EB403B8C4EA42F4B7FF0CE90CB46EF0%26CID%3D2F20CB6F269D6DD02331DF69279D6C12%26ID%3DDevEx%2C5059.1\"}], {\"page_load_url\":\"https://duckduckgo.com/y.js?ifu=%7B3%7Dappid%3D055AAD1BA669BEB8B048128DC89A107C678B527B%26rguid%3D280881b97b9245e6a74bddebc1a6cbda&iurl=%7B2%7DIG%3D3EB403B8C4EA42F4B7FF0CE90CB46EF0%26CID%3D2F20CB6F269D6DD02331DF69279D6C12%26Type%3DEvent.CPT%26DATA%3D0\",\"visibility_url\":\"https://duckduckgo.com/y.js?ivu=%7B4%7Dtype%3Dmv%26reqver%3D1.0%26rg%3D280881b97b9245e6a74bddebc1a6cbda\"});DDG.deep.signalSummary = \"\";DDG.inject('DDG.Data.languages.resultLanguages', {\"en\":[\"https://www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/compare/dataiku-vs-datarobot\",\"https://www.trustradius.com/compare-products/dataiku-dss-vs-datarobot\",\"https://community.dataiku.com/t5/General-Discussion/Dataiku-vs-DataRobot/m-p/9315\",\"https://www.g2.com/compare/datarobot-vs-dataiku-dss\",\"https://www.datarevenue.com/en-blog/ml-platforms-dataiku-vs-alteryx-vs-sagemaker\",\"https://comparisons.financesonline.com/datarobot-vs-dataiku-dss\",\"https://slashdot.org/software/comparison/DataRobot-vs-Dataiku-DSS/\",\"https://www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/vendor/dataiku/product/dataiku\",\"https://www.gartner.com/reviews/market/dsml-engineering-platforms/compare/dataiku-vs-datarobot\",\"https://www.linkedin.com/pulse/managed-machine-learning-platforms-comparative-analysis/\",\"https://www.trustradius.com/products/datarobot/reviews?qs=pros-and-cons\",\"https://www.getapp.com/emerging-technology-software/a/dataiku-dss/compare/datarobot/\",\"https://www.softwarereviews.com/categories/machine-learning-platforms/compare/dataiku-vs-datarobot-ai-platform\",\"https://slashdot.org/software/comparison/Alteryx-vs-DataRobot-vs-Dataiku-DSS/\",\"https://slashdot.org/software/comparison/DataRobot-vs-Databricks-vs-Dataiku-DSS/\",\"https://valohai.com/mlops-platforms-compared/\",\"https://www.dataiku.com/product/plans-and-features/\",\"https://slashdot.org/software/comparison/DataRobot-vs-Databricks-vs-Dataiku-DSS-vs-datagym/\",\"https://sourceforge.net/software/compare/C3-AI-Suite-vs-DataRobot-vs-Dataiku-DSS/\",\"https://www.softwarereviews.com/categories/machine-learning-platforms/compare/datarobot-ai-platform-vs-dataiku\",\"https://slashdot.org/software/comparison/Amazon-SageMaker-vs-DataRobot-vs-Dataiku-DSS/\",\"https://sourceforge.net/software/compare/Analance-vs-DataRobot-vs-Dataiku-DSS/\"]});DDG.deep.pageLayoutSummary = \"a1w23r1,e1\";DDG.inject('DDG.Data.languages.adLanguages', {});if (DDG.pageLayout) DDG.pageLayout.load('d',[{\"a\":\"1 Star 0% Ratings breakdown Overall Capability Score Overall Rating 4.7 ( 504 reviews) 4.7 (20) Data Access and Manipulation 4.5 (224) Data Exploration and Visualization 4.7\",\"ae\":null,\"c\":\"https://www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/compare/dataiku-vs-datarobot\",\"d\":\"www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/compare/dataiku-vs-datarobot\",\"da\":\"\",\"h\":0,\"i\":\"www.gartner.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku vs DataRobot 2024 | Gartner Peer Insights\",\"u\":\"https://www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/compare/dataiku-vs-datarobot\"},{\"a\":\"Path to AI Success Compare Dataiku DSS vs DataRobot. 103 verified user reviews and ratings of features, pros, cons, pricing, support and more.\",\"ae\":null,\"c\":\"https://www.trustradius.com/compare-products/dataiku-dss-vs-datarobot\",\"d\":\"www.trustradius.com/compare-products/dataiku-dss-vs-datarobot\",\"da\":\"\",\"h\":0,\"i\":\"www.trustradius.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku DSS vs DataRobot | TrustRadius\",\"u\":\"https://www.trustradius.com/compare-products/dataiku-dss-vs-datarobot\"},{\"a\":\"General Discussion Dataiku vs DataRobot Solved! Raja Level 2 08-22-2020 03:16 AM Please enlighten me, What distinguishes Dataiku from tools like DataRobot? They appear to be similar, trying to know how dataiku has an upper hand, would make it easy for placing option to customers. 1 Reply 2 Solutions Solutions shown first - Read whole discussion\",\"ae\":null,\"c\":\"https://community.dataiku.com/t5/General-Discussion/Dataiku-vs-DataRobot/m-p/9315\",\"d\":\"community.dataiku.com/t5/General-Discussion/Dataiku-vs-DataRobot/m-p/9315\",\"da\":\"\",\"h\":0,\"i\":\"community.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Solved: Dataiku vs DataRobot - Dataiku Community\",\"u\":\"https://community.dataiku.com/t5/General-Discussion/Dataiku-vs-DataRobot/m-p/9315\"},{\"a\":\"DataRobot vs Dataiku DSS When assessing the two solutions, reviewers found Dataiku DSS easier to use and administer. However, reviewers preferred the ease of set up, and doing business with DataRobot overall. Reviewers felt that DataRobot meets the needs of their business better than Dataiku DSS.\",\"ae\":null,\"c\":\"https://www.g2.com/compare/datarobot-vs-dataiku-dss\",\"d\":\"www.g2.com/compare/datarobot-vs-dataiku-dss\",\"da\":\"\",\"h\":0,\"i\":\"www.g2.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Compare DataRobot vs. Dataiku DSS | G2\",\"u\":\"https://www.g2.com/compare/datarobot-vs-dataiku-dss\"},{\"a\":\"Quick overview Before we get into a detailed comparison, here's a quick overview of each platform. Dataiku is a cross-platform desktop application that includes a broad range of tools, such as notebooks (similar to Jupyter Notebook), workflow management (similar to Apache Airflow), and automated machine learning.\",\"ae\":null,\"c\":\"https://www.datarevenue.com/en-blog/ml-platforms-dataiku-vs-alteryx-vs-sagemaker\",\"d\":\"www.datarevenue.com/en-blog/ml-platforms-dataiku-vs-alteryx-vs-sagemaker\",\"da\":\"\",\"h\":0,\"i\":\"www.datarevenue.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"ML Platforms: Dataiku vs. Alteryx vs. Sagemaker vs. Datarobot\",\"u\":\"https://www.datarevenue.com/en-blog/ml-platforms-dataiku-vs-alteryx-vs-sagemaker\"},{\"a\":\"Home Predictive Analysis Software DataRobot Dataiku DSS Why is FinancesOnline free Compare DataRobot vs Dataiku DSS What is better DataRobot or Dataiku DSS? Examining products to find the best Predictive Analysis Software does not always have to be tough.\",\"ae\":null,\"c\":\"https://comparisons.financesonline.com/datarobot-vs-dataiku-dss\",\"d\":\"comparisons.financesonline.com/datarobot-vs-dataiku-dss\",\"da\":\"\",\"h\":0,\"i\":\"comparisons.financesonline.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"DataRobot vs Dataiku DSS 2024 Comparison | FinancesOnline\",\"u\":\"https://comparisons.financesonline.com/datarobot-vs-dataiku-dss\"},{\"a\":\"Machine Learning Software Dataiku vs DataRobot Dataiku vs DataRobot Share How Capterra Verifies Reviews Pricing Best for Screenshots Features Reviews Pros & Cons Deployment & Support Alternatives Company Details Dataiku VISIT PROFILE DataRobot VISIT PROFILE Pricing Starting from $ 0.01 /Year Pricing Model: Not provided by vendor Free Trial\",\"ae\":null,\"c\":\"https://www.capterra.com/machine-learning-software/compare/142192-179303/Data-Science-Studio-DSS-vs-DataRobot\",\"d\":\"www.capterra.com/machine-learning-software/compare/142192-179303/Data-Science-Studio-DSS-vs-DataRobot\",\"da\":\"translations\",\"h\":0,\"i\":\"www.capterra.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Compare Dataiku vs DataRobot 2024 | Capterra\",\"u\":\"https://www.capterra.com/machine-learning-software/compare/142192-179303/Data-Science-Studio-DSS-vs-DataRobot\"},{\"a\":\"What's the difference between DataRobot and Dataiku DSS? Compare DataRobot vs. Dataiku DSS in 2023 by cost, reviews, features, integrations, deployment, target market, support options, trial offers, training options, years in business, region, and more using the chart below.\",\"ae\":null,\"b\":\"/.\\tSlashdot\\tslashdot.org\",\"c\":\"https://slashdot.org/software/comparison/DataRobot-vs-Dataiku-DSS/\",\"d\":\"slashdot.org/software/comparison/DataRobot-vs-Dataiku-DSS/\",\"da\":\"\",\"h\":0,\"i\":\"slashdot.org\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Compare DataRobot vs. Dataiku DSS in 2023 - Slashdot\",\"u\":\"https://slashdot.org/software/comparison/DataRobot-vs-Dataiku-DSS/\"},{\"a\":\"1 Star 0% Distribution based on 504 ratings Customer Experience Evaluation & Contracting 4.6 Integration & Deployment 4.7 Service & Support 4.8 Product Capabilities 4.8 FREE View and Download Peer Insights About Dataiku\",\"ae\":null,\"c\":\"https://www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/vendor/dataiku/product/dataiku\",\"d\":\"www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/vendor/dataiku/product/dataiku\",\"da\":\"\",\"h\":0,\"i\":\"www.gartner.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku Reviews, Ratings & Features 2024 | Gartner Peer Insights\",\"u\":\"https://www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/vendor/dataiku/product/dataiku\"},{\"a\":\"1329 reviews on 16 vendors. chevron_right. Yard Management. 25 reviews on 28 vendors. chevron_right. Zero Trust Network Access. 733 reviews on 47 vendors. chevron_right. Read the latest Gartner-verified reviews covering over 500+ software categories and find the best enterprise software or services for your organization.\",\"ae\":null,\"c\":\"https://www.gartner.com/reviews/market/dsml-engineering-platforms/compare/dataiku-vs-datarobot\",\"d\":\"www.gartner.com/reviews/market/dsml-engineering-platforms/compare/dataiku-vs-datarobot\",\"da\":\"\",\"h\":0,\"i\":\"www.gartner.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Explore Enterprise Software Categories | Gartner Peer Insights\",\"u\":\"https://www.gartner.com/reviews/market/dsml-engineering-platforms/compare/dataiku-vs-datarobot\"},{\"a\":\"1. Dataiku is a versatile desktop application comprised of a wide range of tools, including automated machine learning, notebooks, and workflow management. It aims to replace pre-existing tools...\",\"ae\":null,\"b\":\"li\\tLinkedIn\\twww.linkedin.com\",\"c\":\"https://www.linkedin.com/pulse/managed-machine-learning-platforms-comparative-analysis/\",\"d\":\"www.linkedin.com/pulse/managed-machine-learning-platforms-comparative-analysis/\",\"da\":\"\",\"e\":\"2023-08-11T00:00:00.0000000\",\"h\":0,\"i\":\"www.linkedin.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Managed Machine Learning Platforms: A Comparative Analysis - LinkedIn\",\"u\":\"https://www.linkedin.com/pulse/managed-machine-learning-platforms-comparative-analysis/\"},{\"a\":\"Dataiku DSS, H2O, and Google Cloud AI are common alternatives for DataRobot. What is DataRobot's best feature? Reviewers rate Automated Machine Learning highest, with a score of 9.3. Who uses DataRobot? The most common users of DataRobot are from Mid-sized Companies (51-1,000 employees).\",\"ae\":null,\"c\":\"https://www.trustradius.com/products/datarobot/reviews?qs=pros-and-cons\",\"d\":\"www.trustradius.com/products/datarobot/reviews?qs=pros-and-cons\",\"da\":\"\",\"h\":0,\"i\":\"www.trustradius.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Pros and Cons of DataRobot 2024 - TrustRadius\",\"u\":\"https://www.trustradius.com/products/datarobot/reviews?qs=pros-and-cons\"},{\"a\":\"Compare Dataiku and DataRobot based on features, pricing, verified reviews, integrations & more. Find out which software is best for your business today. 0. App comparison. Add up to 4 apps below to see how they compare. You can also use the "Compare" buttons while browsing.\",\"ae\":null,\"c\":\"https://www.getapp.com/emerging-technology-software/a/dataiku-dss/compare/datarobot/\",\"d\":\"www.getapp.com/emerging-technology-software/a/dataiku-dss/compare/datarobot/\",\"da\":\"\",\"h\":0,\"i\":\"www.getapp.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku vs DataRobot Comparison | GetApp\",\"u\":\"https://www.getapp.com/emerging-technology-software/a/dataiku-dss/compare/datarobot/\"},{\"a\":\"Dataiku vs DataRobot AI Platform Compare Dataiku and DataRobot AI Platform using real user data focused on features, satisfaction, business value, and the vendor relationship. What is Machine Learning Platforms (ML) Software?\",\"ae\":null,\"c\":\"https://www.softwarereviews.com/categories/machine-learning-platforms/compare/dataiku-vs-datarobot-ai-platform\",\"d\":\"www.softwarereviews.com/categories/machine-learning-platforms/compare/dataiku-vs-datarobot-ai-platform\",\"da\":\"\",\"h\":0,\"i\":\"www.softwarereviews.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku vs DataRobot AI Platform - Machine Learning Platforms\",\"u\":\"https://www.softwarereviews.com/categories/machine-learning-platforms/compare/dataiku-vs-datarobot-ai-platform\"},{\"a\":\"What's the difference between Alteryx, DataRobot, and Dataiku DSS? Compare Alteryx vs. DataRobot vs. Dataiku DSS in 2024 by cost, reviews, features, integrations, deployment, target market, support options, trial offers, training options, years in business, region, and more using the chart below. Alteryx View Product DataRobot View Product\",\"ae\":null,\"b\":\"/.\\tSlashdot\\tslashdot.org\",\"c\":\"https://slashdot.org/software/comparison/Alteryx-vs-DataRobot-vs-Dataiku-DSS/\",\"d\":\"slashdot.org/software/comparison/Alteryx-vs-DataRobot-vs-Dataiku-DSS/\",\"da\":\"\",\"h\":0,\"i\":\"slashdot.org\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Compare Alteryx vs. DataRobot vs. Dataiku DSS in 2023 - Slashdot\",\"u\":\"https://slashdot.org/software/comparison/Alteryx-vs-DataRobot-vs-Dataiku-DSS/\"},{\"a\":\"What's the difference between DataRobot, Databricks Lakehouse, and Dataiku DSS? Compare DataRobot vs. Databricks Lakehouse vs. Dataiku DSS in 2023 by cost, reviews, features, integrations, deployment, target market, support options, trial offers, training options, years in business, region, and more using the chart below.\",\"ae\":null,\"b\":\"/.\\tSlashdot\\tslashdot.org\",\"c\":\"https://slashdot.org/software/comparison/DataRobot-vs-Databricks-vs-Dataiku-DSS/\",\"d\":\"slashdot.org/software/comparison/DataRobot-vs-Databricks-vs-Dataiku-DSS/\",\"da\":\"\",\"h\":0,\"i\":\"slashdot.org\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Compare DataRobot vs. Databricks Lakehouse vs. Dataiku DSS - Slashdot\",\"u\":\"https://slashdot.org/software/comparison/DataRobot-vs-Databricks-vs-Dataiku-DSS/\"},{\"a\":\"The platforms we've chosen for our analysis are ClearML, cnvrg.io, Dataiku, Datarobot, Iguazio, Sagemaker, Seldon and Valohai from the managed side, and Flyte, Kubeflow, MLflow and Metaflow from the open-source side. This is by no means an exhaustive list of all the MLOps tools out there. Most of these are tools that describe themselves as ...\",\"ae\":null,\"c\":\"https://valohai.com/mlops-platforms-compared/\",\"d\":\"valohai.com/mlops-platforms-compared/\",\"da\":\"\",\"h\":0,\"i\":\"valohai.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MLOps Platforms Compared - Valohai\",\"u\":\"https://valohai.com/mlops-platforms-compared/\"},{\"a\":\"Visual Machine Learning and automated features preprocessing: Builtin charts and dashboards: Code notebooks and recipes: Custom web applications and plugins: Collaboration: DEPLOYMENT OPTIONS; ... Dataiku Scores an overall 4.8 out of 5 rating Based on 249 ratings for the DSMLP market, as of March 1, 2022\",\"ae\":null,\"c\":\"https://www.dataiku.com/product/plans-and-features/\",\"d\":\"www.dataiku.com/product/plans-and-features/\",\"da\":\"\",\"h\":0,\"i\":\"www.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Explore Dataiku Plans and Features | Online or Installed\",\"u\":\"https://www.dataiku.com/product/plans-and-features/\"},{\"a\":\"What's the difference between DataRobot, Databricks Lakehouse, Dataiku DSS, and DATAGYM? Compare DataRobot vs. Databricks Lakehouse vs. Dataiku DSS vs. DATAGYM in 2024 by cost, reviews, features, integrations, deployment, target market, support options, trial offers, training options, years in business, region, and more using the chart below.\",\"ae\":null,\"b\":\"/.\\tSlashdot\\tslashdot.org\",\"c\":\"https://slashdot.org/software/comparison/DataRobot-vs-Databricks-vs-Dataiku-DSS-vs-datagym/\",\"d\":\"slashdot.org/software/comparison/DataRobot-vs-Databricks-vs-Dataiku-DSS-vs-datagym/\",\"da\":\"\",\"h\":0,\"i\":\"slashdot.org\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Compare DataRobot vs. Databricks Lakehouse vs. Dataiku DSS vs. DATAGYM ...\",\"u\":\"https://slashdot.org/software/comparison/DataRobot-vs-Databricks-vs-Dataiku-DSS-vs-datagym/\"},{\"a\":\"Claim Dataiku DSS and update features and information. Compare C3 AI Suite vs. DataRobot vs. Dataiku DSS using this comparison chart. Compare price, features, and reviews of the software side-by-side to make the best choice for your business.\",\"ae\":null,\"b\":\"srcforge\\tSourceForge\\tsourceforge.net\",\"c\":\"https://sourceforge.net/software/compare/C3-AI-Suite-vs-DataRobot-vs-Dataiku-DSS/\",\"d\":\"sourceforge.net/software/compare/C3-AI-Suite-vs-DataRobot-vs-Dataiku-DSS/\",\"da\":\"\",\"h\":0,\"i\":\"sourceforge.net\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"C3 AI Suite vs. DataRobot vs. Dataiku DSS Comparison - SourceForge\",\"u\":\"https://sourceforge.net/software/compare/C3-AI-Suite-vs-DataRobot-vs-Dataiku-DSS/\"},{\"a\":\"Compare DataRobot AI Platform and Dataiku using real user data focused on features, satisfaction, business value, and the vendor relationship. What is Machine Learning Platforms (ML) Software?\",\"ae\":null,\"c\":\"https://www.softwarereviews.com/categories/machine-learning-platforms/compare/datarobot-ai-platform-vs-dataiku\",\"d\":\"www.softwarereviews.com/categories/machine-learning-platforms/compare/datarobot-ai-platform-vs-dataiku\",\"da\":\"\",\"h\":0,\"i\":\"www.softwarereviews.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"DataRobot AI Platform vs Dataiku - Machine Learning Platforms\",\"u\":\"https://www.softwarereviews.com/categories/machine-learning-platforms/compare/datarobot-ai-platform-vs-dataiku\"},{\"a\":\"What's the difference between Amazon SageMaker, DataRobot, and Dataiku DSS? Compare Amazon SageMaker vs. DataRobot vs. Dataiku DSS in 2024 by cost, reviews, features, integrations, deployment, target market, support options, trial offers, training options, years in business, region, and more using the chart below.\",\"ae\":null,\"b\":\"/.\\tSlashdot\\tslashdot.org\",\"c\":\"https://slashdot.org/software/comparison/Amazon-SageMaker-vs-DataRobot-vs-Dataiku-DSS/\",\"d\":\"slashdot.org/software/comparison/Amazon-SageMaker-vs-DataRobot-vs-Dataiku-DSS/\",\"da\":\"\",\"h\":0,\"i\":\"slashdot.org\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Compare Amazon SageMaker vs. DataRobot vs. Dataiku DSS in 2024 - Slashdot\",\"u\":\"https://slashdot.org/software/comparison/Amazon-SageMaker-vs-DataRobot-vs-Dataiku-DSS/\"},{\"a\":\"Compare Analance vs. DataRobot vs. Dataiku DSS using this comparison chart. Compare price, features, and reviews of the software side-by-side to make the best choice for your business.\",\"ae\":null,\"b\":\"srcforge\\tSourceForge\\tsourceforge.net\",\"c\":\"https://sourceforge.net/software/compare/Analance-vs-DataRobot-vs-Dataiku-DSS/\",\"d\":\"sourceforge.net/software/compare/Analance-vs-DataRobot-vs-Dataiku-DSS/\",\"da\":\"\",\"h\":0,\"i\":\"sourceforge.net\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Analance vs. DataRobot vs. Dataiku DSS Comparison - SourceForge\",\"u\":\"https://sourceforge.net/software/compare/Analance-vs-DataRobot-vs-Dataiku-DSS/\"},{\"n\":\"/d.js?q=Dataiku%20vs%20DataRobot%20features&kl=wt-wt&l=wt-wt&p=&s=23&ex=-1&ct=US&sp=0&vqd=4-334935250614046875026454141242803242982\"}]);DDG.duckbar.load('images');DDG.duckbar.load('news');DDG.duckbar.load('videos');DDG.duckbar.loadModule('related_searches', {\"ads\":[],\"query\":\"Dataiku vs DataRobot features\",\"queryEncoded\":\"Dataiku%20vs%20DataRobot%20features\",\"response_type\":\"places\",\"results\":[{\"display_text\":\"dataiku vs datarobot review\",\"text\":\"dataiku vs datarobot review\",\"web_search_url\":\"?q=dataiku%20vs%20datarobot%20review\"},{\"display_text\":\"dataiku vs alteryx\",\"text\":\"dataiku vs alteryx\",\"web_search_url\":\"?q=dataiku%20vs%20alteryx\"},{\"display_text\":\"gartner dataiku reviews\",\"text\":\"gartner dataiku reviews\",\"web_search_url\":\"?q=gartner%20dataiku%20reviews\"},{\"display_text\":\"alteryx vs dataiku knime\",\"text\":\"alteryx vs dataiku knime\",\"web_search_url\":\"?q=alteryx%20vs%20dataiku%20knime\"},{\"display_text\":\"dataiku vs rapidminer\",\"text\":\"dataiku vs rapidminer\",\"web_search_url\":\"?q=dataiku%20vs%20rapidminer\"},{\"display_text\":\"dataiku vs azure ml\",\"text\":\"dataiku vs azure ml\",\"web_search_url\":\"?q=dataiku%20vs%20azure%20ml\"},{\"display_text\":\"sagemaker vs dataiku\",\"text\":\"sagemaker vs dataiku\",\"web_search_url\":\"?q=sagemaker%20vs%20dataiku\"},{\"display_text\":\"dataiku reviews\",\"text\":\"dataiku reviews\",\"web_search_url\":\"?q=dataiku%20reviews\"}],\"vqd\":{\"Dataiku%20vs%20DataRobot%20features\":\"4-334935250614046875026454141242803242982\"}});if (DDG.pageLayout) DDG.pageLayout.initialize({\"mainline\":{\"items\":[[\"ad\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"related_searches\"]]},\"sidebar\":{\"items\":[[\"wikipedia_fathead\"]]}}, { start: 0 });DDG.deep.emit(\"load:completed\");", + "curl-cffi-POST-https://duckduckgo.com-{\"data\": {\"q\": \"Dataiku and DataRobot use cases\"}}": "Dataiku and DataRobot use cases at DuckDuckGo
", + "curl-cffi-GET-https://links.duckduckgo.com/d.js-{\"params\": {\"bing_market\": \"wt-WT\", \"df\": null, \"ex\": \"-1\", \"kl\": \"wt-wt\", \"l\": \"wt-wt\", \"q\": \"Dataiku and DataRobot use cases\", \"s\": \"0\", \"sp\": \"0\", \"vqd\": \"4-60481969350525797892441552954401970387\"}}": "if (DDG.deep && DDG.deep.setUpstream) DDG.deep.setUpstream(\"bingv7aa\");DDG.deep.bn={'ivc':1};if (DDG.pageLayout) DDG.pageLayout.load('a',[{\"a\":\"\\u9ad8\\u7cbe\\u5ea6\\u306a\\u6a5f\\u68b0\\u5b66\\u7fd2\\u30e2\\u30c7\\u30eb\\u3092\\u69cb\\u7bc9\\u3001\\u5b9f\\u88c5\\u3001\\u904b\\u7528\\u3002DataRobot\\u306f\\u793e\\u5185\\u30c7\\u30fc\\u30bf\\u304b\\u3089\\u65b0\\u3057\\u3044\\u4fa1\\u5024\\u3092\\u5275\\u9020\\u3057\\u307e\\u3059. AI\\u3092\\u6d3b\\u7528\\u3057\\u30c7\\u30fc\\u30bf\\u3092\\u5206\\u6790\\u3001\\u5b9f\\u7528\\u7684\\u306a\\u30a4\\u30f3\\u30b5\\u30a4\\u30c8\\u3092\\u660e\\u3089\\u304b\\u306b\\u3002\\u30d3\\u30b8\\u30cd\\u30b9\\u306e\\u8ab2\\u984c\\u3092\\u3088\\u308a\\u65e9\\u304f\\u89e3\\u6c7a\",\"adext\":{\"callout\":{\"t\":\"30-Day Free Trial \\u00b7 Trusted by Fortune 50 \\u00b7 No Vendor Lock-in\",\"tid\":\"6\"},\"filterlinks\":{\"l\":[],\"tid\":\"\"},\"sitelinks\":{\"l\":[{\"snippet\":\"Explore the DataRobot AI Platform Get Started With a 30-Day Trial\",\"targetUrl\":\"https://duckduckgo.com/y.js?ad_domain=datarobot.com&ad_provider=bingv7aa&ad_type=txad&eddgt=2_trBPli7jgDj1WnqJbnww%3D%3D&rut=d0faee2c8c1aae9ac3a012e21d37352a1181970dce9edeba4107839fbfbf097a&u3=https%3A%2F%2Fwww.bing.com%2Faclick%3Fld%3De81Q_rqdj1ZxH5XXGh4PG6pjVUCUzdB7rGpyykWEihNc_sSp5n%2DJ9jIyTjOSnXg0OUazrpKgDJrNvBOdNa5PjBGtyLGt23nrBAabI6opJXrliWQ4o%2DTyxIsqOeCXqzLOOJ3jJb74k6KEx20zilzwKmzSg3nBop2A9JqsasC17VVDPc3_i3EzPbWeRNS4nhxXWJqBKd55GfhuEOg2RZUbmmuAUhWvM%26u%3DaHR0cHMlM2ElMmYlMmZ3d3cuZGF0YXJvYm90LmNvbSUyZnRyaWFsJTJmJTNmdXRtX21lZGl1bSUzZHNlYXJjaCUyNnV0bV9zb3VyY2UlM2RiaW5nJTI2dXRtX2NhbXBhaWduJTNkRnJlZVRyaWFsMjAyM1dXMDgxNkdQU2FkZXh0JTI2Y2FtcGFpZ25pZCUzZDUzMDcwODA5OSUyNmFkZ3JvdXBpZCUzZDEzNTAyMDI3NzQyMTc2OTglMjZhZGlkJTNkJTI2bXNjbGtpZCUzZDc2YmMwNmFmNTA0NDFjOGVjOGYxNjMwY2FmNGU4ZTVk%26rlid%3D76bc06af50441c8ec8f1630caf4e8e5d&vqd=4-164177780916400746369660096493208330918&iurl=%7B1%7DIG%3D5E053B32922A4B5781ED405D9621559B%26CID%3D0176CEA622686E9D34D1DAA0235D6F30%26ID%3DDevEx%2C5063.1\",\"text\":\"DataRobot Free Trial\"},{\"snippet\":\"Unlock Your AI Success in 2023 Tips on the Path of Value-Driven AI\",\"targetUrl\":\"https://duckduckgo.com/y.js?ad_domain=datarobot.com&ad_provider=bingv7aa&ad_type=txad&eddgt=2_trBPli7jgDj1WnqJbnww%3D%3D&rut=fdb107a4de6fffdec2bdf43b561b2c63ca700daaef68f0e683547361efbbc2b0&u3=https%3A%2F%2Fwww.bing.com%2Faclick%3Fld%3De8%2DT0j3GTQEgr%2DmtHPM1LzNzVUCUyRxvVKYHe6LbNa2mmCfCZh3Ept1NM%2DP%2DM1AAluh_OL3VQw_FWI0A3YxC3pzzqthf3gpxan_Lv7CjKenge%2DwMYUz3bRFoFyHtQBMdgqv6T7gMGfyYwN3UCj6FNYwVVn9UNN0h1dIQanHNB6Ya9gRrPBACknA8qtsf6A2oUG1xhq7AOF98NzGphnfQ_38fySnRU%26u%3DaHR0cHMlM2ElMmYlMmZ3d3cuZGF0YXJvYm90LmNvbSUyZnJlc291cmNlcyUyZmFpc3VjY2VzczIwMjMlMmYlM2Z1dG1fbWVkaXVtJTNkc2VhcmNoJTI2dXRtX3NvdXJjZSUzZGJpbmclMjZ1dG1fY2FtcGFpZ24lM2RDb250ZW50MTBLZXlzdG9BSVN1Y2Nlc3MyMDIzV1cwNTIyR1BTYWRleHQlMjZ1dG1fdGVybSUzZGRhdGFyb2JvdCUyNnV0bV9jb250ZW50JTNkYWRfZXh0JTI2Y2FtcGFpZ25pZCUzZDUzMDcwODA5OSUyNmFkZ3JvdXBpZCUzZDEzNTAyMDI3NzQyMTc2OTglMjZhZGlkJTNkJTI2bXNjbGtpZCUzZGQzNmQ2MzlkMmFlNTEwMTM3ZTIwMDYzZWQ1ZWY3M2Yz%26rlid%3Dd36d639d2ae510137e20063ed5ef73f3&vqd=4-117927704271333462986714580056949079639&iurl=%7B1%7DIG%3D5E053B32922A4B5781ED405D9621559B%26CID%3D0176CEA622686E9D34D1DAA0235D6F30%26ID%3DDevEx%2C5065.1\",\"text\":\"10 Keys to AI Success\"},{\"snippet\":\"Our Platform Includes Four Fully Integrated Products. Read More.\",\"targetUrl\":\"https://duckduckgo.com/y.js?ad_domain=datarobot.com&ad_provider=bingv7aa&ad_type=txad&eddgt=2_trBPli7jgDj1WnqJbnww%3D%3D&rut=4f06bd3312172b8e61d65ee2626dea6e26d941c3a16aa546b4e11b79e8bf027f&u3=https%3A%2F%2Fwww.bing.com%2Faclick%3Fld%3De8885tVmNmhi65Jmp3f2wYSzVUCUyFey1LCmrSNpGfkWzQnoC7QIbU3ztthJ%2DqKpgCmRfxudhbLK927YN84jvZlV2zTKo9DOULVj5wB8mcGXy_F42SnsrO1jZpY9NnMnzqMYPb5xZTTdgrTO1_w3Bgpd0e0VzO81_O3%2Dfo2z4UiLuVETFVqfACqR6NEwz0yfjzJe6ED9tvi_gPDiUL9iWATrNIrsw%26u%3DaHR0cHMlM2ElMmYlMmZ3d3cuZGF0YXJvYm90LmNvbSUyZnByb2R1Y3QlMmYlM2ZjYW1wYWlnbmlkJTNkNTMwNzA4MDk5JTI2YWRncm91cGlkJTNkMTM1MDIwMjc3NDIxNzY5OCUyNmFkaWQlM2QlMjZtc2Nsa2lkJTNkY2U4NzQ1ZDViODBlMTJmNjQ2N2QyMDc2NDcwNDY2YjI%26rlid%3Dce8745d5b80e12f6467d2076470466b2&vqd=4-169069202740993895017985472268973083525&iurl=%7B1%7DIG%3D5E053B32922A4B5781ED405D9621559B%26CID%3D0176CEA622686E9D34D1DAA0235D6F30%26ID%3DDevEx%2C5067.1\",\"text\":\"Product Overview\"}],\"tid\":\"7\\t9[8]\\t11[10]\\t13[12]\",\"type\":\"EnhancedSiteLink\"},\"tid\":\"1\"},\"ae\":{\"callout\":[\"30-Day Free Trial \\u00b7 Trusted by Fortune 50 \\u00b7 No Vendor Lock-in\"]},\"c\":\"https://duckduckgo.com/y.js?ad_domain=datarobot.com&ad_provider=bingv7aa&ad_type=txad&eddgt=2_trBPli7jgDj1WnqJbnww%3D%3D&rut=e744d99a8df00b24df71f821ad4d1332080aa03267e50f0e988d284f58d9d2ef&u3=https%3A%2F%2Fwww.bing.com%2Faclick%3Fld%3De8tT9soRYLZabP1ukFkRsgNzVUCUzl89Y8xEqpxoqHqIlCI5wWbydNnN_PoAKHAa2Vsio83mXA_ax16t6rJ7XGkBv0Cg7_D1eg2QAuJgPKEam4VWI3rW40B03r1p11ZXN1Gd1847Vj05bAnJnPfgVyC8ZzFQxLxONmOI0Hg182z2bZUVII26BUAlUHaVZ7O_9FEXLJWw%26u%3DaHR0cHMlM2ElMmYlMmZ3d3cuZGF0YXJvYm90LmNvbSUyZmpwJTJmbHAlMmZhaS1mb3ItYnVzaW5lc3MlMmYlM2Z1dG1fbWVkaXVtJTNkc2VhcmNoJTI2dXRtX3NvdXJjZSUzZGJpbmclMjZ1dG1fY2FtcGFpZ24lM2RERU1PMjAyM0FsbFByb2R1Y3RzSlAwNjI2QlBTJTI2dXRtX3Rlcm0lM2RkYXRhcm9ib3QlMjZ1dG1fY29udGVudCUzZERSX2JyYW5kZWRfcnNhJTI2Y2FtcGFpZ25pZCUzZDUzMDcwODA5OSUyNmFkZ3JvdXBpZCUzZDEzNTAyMDI3NzQyMTc2OTglMjZhZGlkJTNkJTI2bXNjbGtpZCUzZDA2MTIwYzhmMTAxNzEwYTZiNmRiNjkyY2VmMWRiOTY1%26rlid%3D06120c8f101710a6b6db692cef1db965&vqd=4-91027509783546726889708070523412001433&iurl=%7B1%7DIG%3D5E053B32922A4B5781ED405D9621559B%26CID%3D0176CEA622686E9D34D1DAA0235D6F30%26ID%3DDevEx%2C5058.1\",\"d\":\"datarobot.com\",\"h\":0,\"i\":\"\",\"k\":0,\"m\":0,\"o\":\"\",\"p\":1,\"relevancy\":{\"abstract\":\"%E9%AB%98%E7%B2%BE%E5%BA%A6%E3%81%AA%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%E3%83%A2%E3%83%87%E3%83%AB%E3%82%92%E6%A7%8B%E7%AF%89%E3%80%81%E5%AE%9F%E8%A3%85%E3%80%81%E9%81%8B%E7%94%A8%E3%80%82%3Cb%3EDataRobot%3C%2Fb%3E%E3%81%AF%E7%A4%BE%E5%86%85%E3%83%87%E3%83%BC%E3%82%BF%E3%81%8B%E3%82%89%E6%96%B0%E3%81%97%E3%81%84%E4%BE%A1%E5%80%A4%E3%82%92%E5%89%B5%E9%80%A0%E3%81%97%E3%81%BE%E3%81%99.%20AI%E3%82%92%E6%B4%BB%E7%94%A8%E3%81%97%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E5%88%86%E6%9E%90%E3%80%81%E5%AE%9F%E7%94%A8%E7%9A%84%E3%81%AA%E3%82%A4%E3%83%B3%E3%82%B5%E3%82%A4%E3%83%88%E3%82%92%E6%98%8E%E3%82%89%E3%81%8B%E3%81%AB%E3%80%82%E3%83%93%E3%82%B8%E3%83%8D%E3%82%B9%E3%81%AE%E8%AA%B2%E9%A1%8C%E3%82%92%E3%82%88%E3%82%8A%E6%97%A9%E3%81%8F%E8%A7%A3%E6%B1%BA\",\"adx_name\":\"none\",\"is_good_v10\":0,\"organic_ranks\":[5,11,12,13],\"q\":\"Dataiku%20and%20DataRobot%20use%20cases\",\"q_words\":4,\"q_words_fuzzy\":0.25,\"q_words_in_ad\":1,\"root_domain\":\"datarobot.com\",\"start\":\"0\",\"title\":\"%E3%83%93%E3%83%83%E3%82%B0%E3%83%87%E3%83%BC%E3%82%BF%E5%88%86%E6%9E%90%E3%82%92%E9%AB%98%E9%80%9F%E5%8C%96%20%2D%20%E3%83%87%E3%83%BC%E3%82%BF%E3%81%8B%E3%82%89%E6%96%B0%E3%81%97%E3%81%84%E4%BE%A1%E5%80%A4%E3%82%92\"},\"s\":\"bingv7aa\",\"t\":\"\\u30d3\\u30c3\\u30b0\\u30c7\\u30fc\\u30bf\\u5206\\u6790\\u3092\\u9ad8\\u901f\\u5316 - \\u30c7\\u30fc\\u30bf\\u304b\\u3089\\u65b0\\u3057\\u3044\\u4fa1\\u5024\\u3092\",\"tid\":\"1,6,7,9[8],11[10],13[12]\",\"u\":\"https://duckduckgo.com/y.js?ad_domain=datarobot.com&ad_provider=bingv7aa&ad_type=txad&eddgt=2_trBPli7jgDj1WnqJbnww%3D%3D&rut=e744d99a8df00b24df71f821ad4d1332080aa03267e50f0e988d284f58d9d2ef&u3=https%3A%2F%2Fwww.bing.com%2Faclick%3Fld%3De8tT9soRYLZabP1ukFkRsgNzVUCUzl89Y8xEqpxoqHqIlCI5wWbydNnN_PoAKHAa2Vsio83mXA_ax16t6rJ7XGkBv0Cg7_D1eg2QAuJgPKEam4VWI3rW40B03r1p11ZXN1Gd1847Vj05bAnJnPfgVyC8ZzFQxLxONmOI0Hg182z2bZUVII26BUAlUHaVZ7O_9FEXLJWw%26u%3DaHR0cHMlM2ElMmYlMmZ3d3cuZGF0YXJvYm90LmNvbSUyZmpwJTJmbHAlMmZhaS1mb3ItYnVzaW5lc3MlMmYlM2Z1dG1fbWVkaXVtJTNkc2VhcmNoJTI2dXRtX3NvdXJjZSUzZGJpbmclMjZ1dG1fY2FtcGFpZ24lM2RERU1PMjAyM0FsbFByb2R1Y3RzSlAwNjI2QlBTJTI2dXRtX3Rlcm0lM2RkYXRhcm9ib3QlMjZ1dG1fY29udGVudCUzZERSX2JyYW5kZWRfcnNhJTI2Y2FtcGFpZ25pZCUzZDUzMDcwODA5OSUyNmFkZ3JvdXBpZCUzZDEzNTAyMDI3NzQyMTc2OTglMjZhZGlkJTNkJTI2bXNjbGtpZCUzZDA2MTIwYzhmMTAxNzEwYTZiNmRiNjkyY2VmMWRiOTY1%26rlid%3D06120c8f101710a6b6db692cef1db965&vqd=4-91027509783546726889708070523412001433&iurl=%7B1%7DIG%3D5E053B32922A4B5781ED405D9621559B%26CID%3D0176CEA622686E9D34D1DAA0235D6F30%26ID%3DDevEx%2C5058.1\"}], {\"page_load_url\":\"https://duckduckgo.com/y.js?ifu=%7B3%7Dappid%3D055AAD1BA669BEB8B048128DC89A107C678B527B%26rguid%3D309794dc72f748f6a2b95ce5c34fbcec&iurl=%7B2%7DIG%3D5E053B32922A4B5781ED405D9621559B%26CID%3D0176CEA622686E9D34D1DAA0235D6F30%26Type%3DEvent.CPT%26DATA%3D0\",\"visibility_url\":\"https://duckduckgo.com/y.js?ivu=%7B4%7Dtype%3Dmv%26reqver%3D1.0%26rg%3D309794dc72f748f6a2b95ce5c34fbcec\"});DDG.deep.signalSummary = \"\";DDG.inject('DDG.Data.languages.resultLanguages', {\"en\":[\"https://knowledge.dataiku.com/latest/use-cases/index.html\",\"https://community.dataiku.com/t5/Dataiku-Use-Cases-Success/tkb-p/use-cases\",\"https://www.datarevenue.com/en-blog/ml-platforms-dataiku-vs-alteryx-vs-sagemaker\",\"https://www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/compare/dataiku-vs-datarobot\",\"https://community.dataiku.com/t5/General-Discussion/Dataiku-vs-DataRobot/m-p/9315\",\"https://www.datarobot.com/use-cases/\",\"https://academy.dataiku.com/page/use-cases\",\"https://www.trustradius.com/compare-products/dataiku-dss-vs-datarobot\",\"https://www.g2.com/compare/datarobot-vs-dataiku-dss\",\"https://www.linkedin.com/pulse/managed-machine-learning-platforms-comparative-analysis/\",\"https://londondataconsulting.medium.com/dataiku-what-is-it-how-to-use-it-ultimate-guide-2023-47602c85a48b\",\"https://docs.datarobot.com/en/docs/api/guide/common-case/index.html\",\"https://www.datarobot.com/blog/introducing-the-datarobot-use-case-value-tracker/\",\"https://docs.datarobot.com/en/docs/workbench/wb-usecase/wb-build-usecase.html\",\"https://blog.dataiku.com/topic/use-cases-projects\",\"https://valohai.com/mlops-platforms-compared/\",\"https://www.capterra.com/machine-learning-software/compare/142192-179303/Data-Science-Studio-DSS-vs-DataRobot\",\"https://pages.dataiku.com/experience-a-dataiku-demo\",\"https://www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/vendor/dataiku/product/dataiku\",\"https://www.dataiku.com/stories/\",\"https://www.dataiku.com/\",\"https://techcrunch.com/2022/12/13/ai-and-analytics-platform-dataiku-raises-200m-at-a-reduced-valuation/\",\"https://www.globenewswire.com/news-release/2022/11/17/2558152/0/en/Ben-Taylor-Joins-Dataiku-as-Chief-AI-Strategist.html\"]});DDG.deep.pageLayoutSummary = \"a1w4v1w19,w1\";DDG.inject('DDG.Data.languages.adLanguages', {});if (DDG.pageLayout) DDG.pageLayout.load('d',[{\"a\":\"Use Cases - Dataiku Knowledge Base Use Cases # These use cases allow you to practice what you've learned by building simplified, but complete use cases in Dataiku. Topics # Data Preparation Use Cases Classification Use Cases Clustering Use Cases Plugin Use Cases\",\"ae\":null,\"c\":\"https://knowledge.dataiku.com/latest/use-cases/index.html\",\"d\":\"knowledge.dataiku.com/latest/use-cases/index.html\",\"da\":\"\",\"h\":0,\"i\":\"knowledge.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Use Cases - Dataiku Knowledge Base\",\"u\":\"https://knowledge.dataiku.com/latest/use-cases/index.html\"},{\"a\":\"Community Dataiku Use Cases & Success Stories \\u26a0\\ufe0f Discover pioneering Dataiku use cases and success stories shared by customers, partners, academics, and nonprofits participating in the Dataiku Frontrunner Awards. Use the following labels to filter submissions by industry:\",\"ae\":null,\"c\":\"https://community.dataiku.com/t5/Dataiku-Use-Cases-Success/tkb-p/use-cases\",\"d\":\"community.dataiku.com/t5/Dataiku-Use-Cases-Success/tkb-p/use-cases\",\"da\":\"\",\"h\":0,\"i\":\"community.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku Use Cases & Success Stories - Dataiku Community\",\"u\":\"https://community.dataiku.com/t5/Dataiku-Use-Cases-Success/tkb-p/use-cases\"},{\"a\":\"Dataiku is a cross-platform desktop application that includes a broad range of tools, such as notebooks (similar to Jupyter Notebook), workflow management (similar to Apache Airflow), and automated machine learning. In general, Dataiku aims to replace many of your existing tools rather than to integrate with them.\",\"ae\":null,\"c\":\"https://www.datarevenue.com/en-blog/ml-platforms-dataiku-vs-alteryx-vs-sagemaker\",\"d\":\"www.datarevenue.com/en-blog/ml-platforms-dataiku-vs-alteryx-vs-sagemaker\",\"da\":\"\",\"h\":0,\"i\":\"www.datarevenue.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"ML Platforms: Dataiku vs. Alteryx vs. Sagemaker vs. Datarobot\",\"u\":\"https://www.datarevenue.com/en-blog/ml-platforms-dataiku-vs-alteryx-vs-sagemaker\"},{\"a\":\"Dataiku has a rating of 4.8 stars with 504 reviews. DataRobot has a rating of 4.6 stars with 508 reviews. See side-by-side comparisons of product capabilities, customer experience, pros and cons, and reviewer demographics to find the best fit for your organization. See more companies in the Data Science and Machine Learning Platforms market. PDF.\",\"ae\":null,\"c\":\"https://www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/compare/dataiku-vs-datarobot\",\"d\":\"www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/compare/dataiku-vs-datarobot\",\"da\":\"\",\"h\":0,\"i\":\"www.gartner.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku vs DataRobot 2024 | Gartner Peer Insights\",\"u\":\"https://www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/compare/dataiku-vs-datarobot\"},{\"a\":\"In my humble opinion DSS is a more a 'toolbox', where as DataRobot is an autoML platform. DataRobot is really good at what it does - if you have non-technical team who want to drop in data and leave everything to autoML then this may be the option for them.\",\"ae\":null,\"c\":\"https://community.dataiku.com/t5/General-Discussion/Dataiku-vs-DataRobot/m-p/9315\",\"d\":\"community.dataiku.com/t5/General-Discussion/Dataiku-vs-DataRobot/m-p/9315\",\"da\":\"\",\"h\":0,\"i\":\"community.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Solved: Dataiku vs DataRobot - Dataiku Community\",\"u\":\"https://community.dataiku.com/t5/General-Discussion/Dataiku-vs-DataRobot/m-p/9315\"},{\"a\":\"Use cases AI Use Cases AI-driven organizations around the world use DataRobot to solve their most pressing business problems. Build with Free Trial Recent Popular Filters Ready to Get Started? See how a value-driven approach to AI can accelerate time to impact. Start Free Trial\",\"ae\":null,\"c\":\"https://www.datarobot.com/use-cases/\",\"d\":\"www.datarobot.com/use-cases/\",\"da\":\"\",\"h\":0,\"i\":\"www.datarobot.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Machine Learning Use Cases | DataRobot AI Platform\",\"u\":\"https://www.datarobot.com/use-cases/\"},{\"a\":\"With Dataiku's AI Prepare assistant, you can work smarter, not harder. Simply describe the transformation you want to apply in natural language and the AI assistant automatically generates the necessary data preparation steps. The ability to modify both your prompt and the resulting steps means you can prepare data faster than ever, yet still ...\",\"ae\":null,\"c\":\"https://academy.dataiku.com/page/use-cases\",\"d\":\"academy.dataiku.com/page/use-cases\",\"da\":\"\",\"h\":0,\"i\":\"academy.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Use Cases - Dataiku\",\"u\":\"https://academy.dataiku.com/page/use-cases\"},{\"a\":\"84 Reviews and Ratings Path to AI Success Compare Dataiku DSS vs DataRobot. 103 verified user reviews and ratings of features, pros, cons, pricing, support and more.\",\"ae\":null,\"c\":\"https://www.trustradius.com/compare-products/dataiku-dss-vs-datarobot\",\"d\":\"www.trustradius.com/compare-products/dataiku-dss-vs-datarobot\",\"da\":\"\",\"h\":0,\"i\":\"www.trustradius.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku DSS vs DataRobot | TrustRadius\",\"u\":\"https://www.trustradius.com/compare-products/dataiku-dss-vs-datarobot\"},{\"a\":\"side-by-side comparison of DataRobot vs. Dataiku DSS. based on preference data from user reviews. DataRobot rates 4.4/5 stars with 26 reviews. By contrast, Dataiku DSS rates 4.3/5 stars with 36 reviews. Each product's score is calculated with real-time data from verified user reviews, to help you make the best choice between these two options ...\",\"ae\":null,\"c\":\"https://www.g2.com/compare/datarobot-vs-dataiku-dss\",\"d\":\"www.g2.com/compare/datarobot-vs-dataiku-dss\",\"da\":\"\",\"h\":0,\"i\":\"www.g2.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Compare DataRobot vs. Dataiku DSS | G2\",\"u\":\"https://www.g2.com/compare/datarobot-vs-dataiku-dss\"},{\"a\":\"Use case: Choose Datarobot if you have data stored in spreadsheets and are seeking a platform that is the simplest, albeit one with limited flexibility, ... Dataiku vs. Datarobot .\",\"ae\":null,\"b\":\"li\\tLinkedIn\\twww.linkedin.com\",\"c\":\"https://www.linkedin.com/pulse/managed-machine-learning-platforms-comparative-analysis/\",\"d\":\"www.linkedin.com/pulse/managed-machine-learning-platforms-comparative-analysis/\",\"da\":\"\",\"e\":\"2023-08-11T00:00:00.0000000\",\"h\":0,\"i\":\"www.linkedin.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Managed Machine Learning Platforms: A Comparative Analysis - LinkedIn\",\"u\":\"https://www.linkedin.com/pulse/managed-machine-learning-platforms-comparative-analysis/\"},{\"a\":\"Jan 11, 2023 Dataiku is an artificial intelligence platform created in France in 2013. It has since become one of the world's benchmarks for data science and machine learning studios. What is...\",\"ae\":null,\"c\":\"https://londondataconsulting.medium.com/dataiku-what-is-it-how-to-use-it-ultimate-guide-2023-47602c85a48b\",\"d\":\"londondataconsulting.medium.com/dataiku-what-is-it-how-to-use-it-ultimate-guide-2023-47602c85a48b\",\"da\":\"translations\",\"e\":\"2023-01-11T00:00:00.0000000\",\"h\":0,\"i\":\"londondataconsulting.medium.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku: What is it? How to use it? Ultimate Guide 2023\",\"u\":\"https://londondataconsulting.medium.com/dataiku-what-is-it-how-to-use-it-ultimate-guide-2023-47602c85a48b\"},{\"a\":\"Use cases for version 2.x. Notebooks for uses cases that use methods for 2.x versions of DataRobot's Python client. Measure price elasticity of demand. A use case to identify relationships between price and demand, maximize revenue by properly pricing products, and monitor price elasticities for changes in price and demand. Insurance claim triage.\",\"ae\":null,\"c\":\"https://docs.datarobot.com/en/docs/api/guide/common-case/index.html\",\"d\":\"docs.datarobot.com/en/docs/api/guide/common-case/index.html\",\"da\":\"\",\"h\":0,\"i\":\"docs.datarobot.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Common use cases: DataRobot docs - DataRobot AI Platform\",\"u\":\"https://docs.datarobot.com/en/docs/api/guide/common-case/index.html\"},{\"a\":\"With the Use Case Value Tracker, you can manage the project lifecycle and understand the value associated with each step. It also enables you to associate and organize all your DataRobot artifacts (e.g., datasets, models, deployments, applications, etc.) around a given use case for a holistic view. In addition to the project management aspects ...\",\"ae\":null,\"c\":\"https://www.datarobot.com/blog/introducing-the-datarobot-use-case-value-tracker/\",\"d\":\"www.datarobot.com/blog/introducing-the-datarobot-use-case-value-tracker/\",\"da\":\"\",\"h\":0,\"i\":\"www.datarobot.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Introducing the DataRobot Use Case Value Tracker\",\"u\":\"https://www.datarobot.com/blog/introducing-the-datarobot-use-case-value-tracker/\"},{\"a\":\"Use Cases are folder-like containers inside of DataRobot Workbench that allow you to group everything related to solving a specific business problem\\u2014datasets, models, experiments, No-Code AI Apps, and notebooks\\u2014inside of a single, manageable entity. You can share whole Use Cases as well as the individual assets they contain.\",\"ae\":null,\"c\":\"https://docs.datarobot.com/en/docs/workbench/wb-usecase/wb-build-usecase.html\",\"d\":\"docs.datarobot.com/en/docs/workbench/wb-usecase/wb-build-usecase.html\",\"da\":\"\",\"e\":\"2023-09-15T00:00:00.0000000\",\"h\":0,\"i\":\"docs.datarobot.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Use Cases: DataRobot docs\",\"u\":\"https://docs.datarobot.com/en/docs/workbench/wb-usecase/wb-build-usecase.html\"},{\"a\":\"January 2, 2024 Use Cases & Projects, Featured Sophie Dionnet Leveraging AI to Cut Costs December 29, 2023 Data Basics, Featured\",\"ae\":null,\"c\":\"https://blog.dataiku.com/topic/use-cases-projects\",\"d\":\"blog.dataiku.com/topic/use-cases-projects\",\"da\":\"\",\"h\":0,\"i\":\"blog.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Blog - Dataiku | Use Cases & Projects\",\"u\":\"https://blog.dataiku.com/topic/use-cases-projects\"},{\"a\":\"The platforms we've chosen for our analysis are ClearML, cnvrg.io, Dataiku, Datarobot, Iguazio, Sagemaker, Seldon and Valohai from the managed side, and Flyte, Kubeflow, MLflow and Metaflow from the open-source side. This is by no means an exhaustive list of all the MLOps tools out there.\",\"ae\":null,\"c\":\"https://valohai.com/mlops-platforms-compared/\",\"d\":\"valohai.com/mlops-platforms-compared/\",\"da\":\"\",\"h\":0,\"i\":\"valohai.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"MLOps Platforms Compared - Valohai\",\"u\":\"https://valohai.com/mlops-platforms-compared/\"},{\"a\":\"DataRobot. DSS is for all companies, whatever their expertise, industry or size, that want to create their own data-driven strategic advantages by transforming their raw data into business impacting predictions. Cloud based machine learning platform which helps enterprises scale data science capabilities through deploying machine learning ...\",\"ae\":null,\"c\":\"https://www.capterra.com/machine-learning-software/compare/142192-179303/Data-Science-Studio-DSS-vs-DataRobot\",\"d\":\"www.capterra.com/machine-learning-software/compare/142192-179303/Data-Science-Studio-DSS-vs-DataRobot\",\"da\":\"translations\",\"h\":0,\"i\":\"www.capterra.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Compare Dataiku vs DataRobot 2024 | Capterra\",\"u\":\"https://www.capterra.com/machine-learning-software/compare/142192-179303/Data-Science-Studio-DSS-vs-DataRobot\"},{\"a\":\"For Every Industry & Use Case. Organizations that use Dataiku elevate their people (whether technical and working in code or on the business side and low- or no-code) to extraordinary, arming them with the ability to make better day-to-day decisions with data across: Banking & Insurance. Pharmaceuticals. Manufacturing. Telecommunications.\",\"ae\":null,\"c\":\"https://pages.dataiku.com/experience-a-dataiku-demo\",\"d\":\"pages.dataiku.com/experience-a-dataiku-demo\",\"da\":\"\",\"h\":0,\"i\":\"pages.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Check Out This Dataiku Demo\",\"u\":\"https://pages.dataiku.com/experience-a-dataiku-demo\"},{\"a\":\"4 Star 24% 3 Star 1% 2 Star 0% 1 Star 0% Distribution based on 504 ratings Customer Experience Evaluation & Contracting 4.6 Integration & Deployment 4.7 Service & Support 4.8 Product Capabilities 4.8 FREE View and Download Peer Insights About Dataiku\",\"ae\":null,\"c\":\"https://www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/vendor/dataiku/product/dataiku\",\"d\":\"www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/vendor/dataiku/product/dataiku\",\"da\":\"\",\"h\":0,\"i\":\"www.gartner.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku Reviews, Ratings & Features 2024 | Gartner Peer Insights\",\"u\":\"https://www.gartner.com/reviews/market/data-science-and-machine-learning-platforms/vendor/dataiku/product/dataiku\"},{\"a\":\"Read The Full Case Study U.S. Venture + Dataiku: Upskilling Analysts to Save Thousands of Hours The Data and Analytics team at U.S. Venture was built to usher the company into the future of data science and AI.\",\"ae\":null,\"c\":\"https://www.dataiku.com/stories/\",\"d\":\"www.dataiku.com/stories/\",\"da\":\"\",\"h\":0,\"i\":\"www.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Stories | Dataiku\",\"u\":\"https://www.dataiku.com/stories/\"},{\"a\":\"MLOps Deploy, monitor, and maintain machine learning models, all in a single platform. Explore the Capability Collaboration With Dataiku, teams can move beyond the lab and build real and safe Generative AI applications at enterprise scale. Explore the Capability Governance\",\"ae\":null,\"c\":\"https://www.dataiku.com/\",\"d\":\"www.dataiku.com\",\"da\":\"\",\"h\":0,\"i\":\"www.dataiku.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Dataiku | Everyday AI, Extraordinary People\",\"u\":\"https://www.dataiku.com/\"},{\"a\":\"The company today announced that it raised $200 million in a Series F round led by Wellington Management at a $3.7 billion valuation, down from the $4.6 billion that Dataiku received in August ...\",\"ae\":null,\"b\":\"tc\\tTechcrunch\\ttechcrunch.com\",\"c\":\"https://techcrunch.com/2022/12/13/ai-and-analytics-platform-dataiku-raises-200m-at-a-reduced-valuation/\",\"d\":\"techcrunch.com/2022/12/13/ai-and-analytics-platform-dataiku-raises-200m-at-a-reduced-valuation/\",\"da\":\"translations\",\"e\":\"2022-12-13T17:10:00.0000000\",\"h\":0,\"i\":\"techcrunch.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"AI and analytics platform Dataiku raises $200M at a reduced valuation\",\"u\":\"https://techcrunch.com/2022/12/13/ai-and-analytics-platform-dataiku-raises-200m-at-a-reduced-valuation/\"},{\"a\":\"Today, more than 500 companies worldwide use Dataiku to integrate and streamline their use of data, analytics, and AI, driving diverse use cases from fraud detection and customer churn prevention ...\",\"ae\":null,\"c\":\"https://www.globenewswire.com/news-release/2022/11/17/2558152/0/en/Ben-Taylor-Joins-Dataiku-as-Chief-AI-Strategist.html\",\"d\":\"www.globenewswire.com/news-release/2022/11/17/2558152/0/en/Ben-Taylor-Joins-Dataiku-as-Chief-AI-Strategist.html\",\"da\":\"translations\",\"e\":\"2022-11-17T13:00:00.0000000\",\"h\":0,\"i\":\"www.globenewswire.com\",\"k\":null,\"m\":0,\"o\":0,\"p\":0,\"s\":\"bingv7aa\",\"t\":\"Ben Taylor Joins Dataiku as Chief AI Strategist - GlobeNewswire\",\"u\":\"https://www.globenewswire.com/news-release/2022/11/17/2558152/0/en/Ben-Taylor-Joins-Dataiku-as-Chief-AI-Strategist.html\"},{\"n\":\"/d.js?q=Dataiku%20and%20DataRobot%20use%20cases&kl=wt-wt&l=wt-wt&p=&s=23&ex=-1&ct=US&sp=0&vqd=4-60481969350525797892441552954401970387\"}]);DDG.duckbar.load('images');DDG.duckbar.load('news');DDG.duckbar.load('videos', {\"ads\":[],\"query\":\"Dataiku and DataRobot use cases\",\"queryEncoded\":\"Dataiku%20and%20DataRobot%20use%20cases\",\"response_type\":\"places\",\"results\":[{\"content\":\"https://www.youtube.com/watch?v=ryZRRIjQ5Z8\",\"description\":\"If you're a code-first data practitioner, Dataiku helps you efficiently build high quality data pipelines and models in a number of ways. CHECK OUT DATAIKU: https://bit.ly/36XBlpK EGG ON AIR: https://bit.ly/37GhXMY BRIGHTTALK WEBINARS: https://bit.ly/33TIRjn DATA SCIENCE PIONEERS DOCUMENTARY: https://bit.ly/36V3rBF PARTNER ECOSYSTEM: https ...\",\"duration\":\"10:43\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/ryZRRIjQ5Z8?autoplay=1\",\"image_token\":\"8a4abca8613c6680a108591849e5d7b13b86111004ae004898a7f059b64c8355\",\"images\":{\"large\":\"https://tse4.mm.bing.net/th?id=OVP.WoendyuZJ9qxql-n6jit5AEsDh&pid=Api\",\"medium\":\"https://tse4.mm.bing.net/th?id=OVP.WoendyuZJ9qxql-n6jit5AEsDh&pid=Api\",\"motion\":\"https://tse4.mm.bing.net/th?id=OM1.cmvppfhHVUeE4Q_1684256861&pid=Api\",\"small\":\"https://tse4.mm.bing.net/th?id=OVP.WoendyuZJ9qxql-n6jit5AEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2021-06-08T21:15:02.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":12391},\"title\":\"Dataiku Demo for Data Scientists and Coders\",\"uploader\":\"Dataiku\"},{\"content\":\"https://www.youtube.com/watch?v=jKL_I0SCl_E\",\"description\":\"This video showcases the Clinical Trial Explorer use case from the Dataiku Generative AI Use Case Collection. Learn more about Dataiku for Generative AI | https://www.dataiku.com/product/generative-ai/ Explore more Generative AI use cases | https://experience.dataiku.com/generative-ai To explore more about Dataiku, check out the rest of our ...\",\"duration\":\"1:50\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/jKL_I0SCl_E?autoplay=1\",\"image_token\":\"7b19602fe6d9b761aa3cc138448cc632ddbed31da3abf2687f36705f5945973d\",\"images\":{\"large\":\"https://tse3.mm.bing.net/th?id=OVP.wfgHp53woiVosZ37-1HtnwEsDh&pid=Api\",\"medium\":\"https://tse3.mm.bing.net/th?id=OVP.wfgHp53woiVosZ37-1HtnwEsDh&pid=Api\",\"motion\":\"https://tse3.mm.bing.net/th?id=OM2.ZX_yq0xmyCGZBg_1696372683&pid=Api\",\"small\":\"https://tse3.mm.bing.net/th?id=OVP.wfgHp53woiVosZ37-1HtnwEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-06-22T12:19:00.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":150},\"title\":\"Clinical Trial Explorer\",\"uploader\":\"Dataiku\"},{\"content\":\"https://www.youtube.com/watch?v=lASesA4gNFI\",\"description\":\"This video showcases the CO2 Forecast Analyzer use case from the Dataiku Generative AI Use Case Collection. Learn more about Dataiku for Generative AI | https://www.dataiku.com/product/generative-ai/ Explore more Generative AI use cases | https://experience.dataiku.com/generative-ai To explore more about Dataiku, check out the rest of our ...\",\"duration\":\"1:50\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/lASesA4gNFI?autoplay=1\",\"image_token\":\"a09328adca01a788783d759561c2f9c9d4d214e5a26f1462d2b6b69f21a2d478\",\"images\":{\"large\":\"https://tse2.mm.bing.net/th?id=OVP.ZhQI7z-IMlcVnyeMJuGlAQEsDh&pid=Api\",\"medium\":\"https://tse2.mm.bing.net/th?id=OVP.ZhQI7z-IMlcVnyeMJuGlAQEsDh&pid=Api\",\"motion\":\"\",\"small\":\"https://tse2.mm.bing.net/th?id=OVP.ZhQI7z-IMlcVnyeMJuGlAQEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-06-22T12:16:20.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":49},\"title\":\"CO2 Forecast Analyzer\",\"uploader\":\"Dataiku\"},{\"content\":\"https://www.youtube.com/watch?v=RecpD6Vtzj4\",\"description\":\"This video showcases the LLM-Enhanced ESG Document Intelligence use case from the Dataiku Generative AI Use Case Collection. Learn more about Dataiku for Generative AI | https://www.dataiku.com/product/generative-ai/ Explore more Generative AI use cases | https://experience.dataiku.com/generative-ai To explore more about Dataiku, check out the ...\",\"duration\":\"1:20\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/RecpD6Vtzj4?autoplay=1\",\"image_token\":\"6f797accb167e2e6ff7265e35116cdeb9f1c641b1df47932d9597b61b0108614\",\"images\":{\"large\":\"https://tse2.mm.bing.net/th?id=OVP.bm4gAiJOOmKV7uup6LU9pgEsDh&pid=Api\",\"medium\":\"https://tse2.mm.bing.net/th?id=OVP.bm4gAiJOOmKV7uup6LU9pgEsDh&pid=Api\",\"motion\":\"https://tse2.mm.bing.net/th?id=OM1.M8WXwCQ79nrqEA_1691502936&pid=Api\",\"small\":\"https://tse2.mm.bing.net/th?id=OVP.bm4gAiJOOmKV7uup6LU9pgEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-06-22T12:30:00.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":382},\"title\":\"LLM-Enhanced ESG Document Intelligence\",\"uploader\":\"Dataiku\"},{\"content\":\"https://www.youtube.com/watch?v=zLW0TkJoHLw\",\"description\":\"This video showcases the Demand Forecast Analyzer use case from the Dataiku Generative AI Use Case Collection. Learn more about Dataiku for Generative AI | https://www.dataiku.com/product/generative-ai/ Explore more Generative AI use cases | https://experience.dataiku.com/generative-ai To explore more about Dataiku, check out the rest of our ...\",\"duration\":\"1:42\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/zLW0TkJoHLw?autoplay=1\",\"image_token\":\"524eb9572bf9342b859509285d39ec4661fc572cb1452307acc5341b56bab921\",\"images\":{\"large\":\"https://tse1.mm.bing.net/th?id=OVP.yIekQ2cMUesOPJTfsYIZzQHgFo&pid=Api\",\"medium\":\"https://tse1.mm.bing.net/th?id=OVP.yIekQ2cMUesOPJTfsYIZzQHgFo&pid=Api\",\"motion\":\"\",\"small\":\"https://tse1.mm.bing.net/th?id=OVP.yIekQ2cMUesOPJTfsYIZzQHgFo&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-06-22T12:15:02.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":4},\"title\":\"LLM-Enhanced Demand Forecast\",\"uploader\":\"Dataiku\"},{\"content\":\"https://www.youtube.com/watch?v=L-Yys0fzuVY\",\"description\":\"This video showcases the Production Quality Data Explorer use case from the Dataiku Generative AI Use Case Collection. Learn more about Dataiku for Generative AI | https://www.dataiku.com/product/generative-ai/ Explore more Generative AI use cases | https://experience.dataiku.com/generative-ai To explore more about Dataiku, check out the rest ...\",\"duration\":\"1:47\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/L-Yys0fzuVY?autoplay=1\",\"image_token\":\"82924713be1dba83d67124fcaa6cc6afd163900a0c40f25fcf6c144ed0e36536\",\"images\":{\"large\":\"https://tse4.mm.bing.net/th?id=OVP.teiC0mX9nKCbH8qeo52udwEsDh&pid=Api\",\"medium\":\"https://tse4.mm.bing.net/th?id=OVP.teiC0mX9nKCbH8qeo52udwEsDh&pid=Api\",\"motion\":\"https://tse4.mm.bing.net/th?id=OM2.IwRaoLRWcQXzag_1691419996&pid=Api\",\"small\":\"https://tse4.mm.bing.net/th?id=OVP.teiC0mX9nKCbH8qeo52udwEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-06-22T12:28:29.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":175},\"title\":\"Production Quality Data Explorer\",\"uploader\":\"Dataiku\"},{\"content\":\"https://www.youtube.com/watch?v=FluiuHuaU8A\",\"description\":\"In this breakout session of Dataiku's Product Days 2021, you will see a demo of Dataiku's Data Science Studio, the centralized, collaborative, and end-to-end platform for data science in the enterprise. CHECK OUT DATAIKU: https://bit.ly/36XBlpK EGG ON AIR: https://bit.ly/37GhXMY BRIGHTTALK WEBINARS: https://bit.ly/33TIRjn DATA SCIENCE PIONEERS ...\",\"duration\":\"13:50\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/FluiuHuaU8A?autoplay=1\",\"image_token\":\"2943fa8c1580f2936fc11667d670c0b827b94ff3d16b897f8b5ef2e2426487b3\",\"images\":{\"large\":\"https://tse2.mm.bing.net/th?id=OVP.RIM-ftwDZjYP58RimJfgwwEsDh&pid=Api\",\"medium\":\"https://tse2.mm.bing.net/th?id=OVP.RIM-ftwDZjYP58RimJfgwwEsDh&pid=Api\",\"motion\":\"https://tse2.mm.bing.net/th?id=OM1.MIQ7BoQz1MVkNw_1662248868&pid=Api\",\"small\":\"https://tse2.mm.bing.net/th?id=OVP.RIM-ftwDZjYP58RimJfgwwEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2021-07-08T15:56:22.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":3844},\"title\":\"Introduction to Dataiku Data Science | Product Days 2021\",\"uploader\":\"Dataiku\"},{\"content\":\"https://www.youtube.com/watch?v=6TEU5JboP7k\",\"description\":\"This video showcases the LLM-Enhanced Next Best Offer use case from the Dataiku Generative AI Use Case Collection. Learn more about Dataiku for Generative AI | https://www.dataiku.com/product/generative-ai/ Explore more Generative AI use cases | https://experience.dataiku.com/generative-ai To explore more about Dataiku, check out the rest of ...\",\"duration\":\"1:47\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/6TEU5JboP7k?autoplay=1\",\"image_token\":\"a3bc327ff2f099462935a8979bb599655c7a88a44e25a64bfea7e5973f773158\",\"images\":{\"large\":\"https://tse2.mm.bing.net/th?id=OVP.Q6SP0MmL89M_TnLvNPG4oQEsDh&pid=Api\",\"medium\":\"https://tse2.mm.bing.net/th?id=OVP.Q6SP0MmL89M_TnLvNPG4oQEsDh&pid=Api\",\"motion\":\"https://tse2.mm.bing.net/th?id=OM2.wXNo1CUgYV4Flg_1694065487&pid=Api\",\"small\":\"https://tse2.mm.bing.net/th?id=OVP.Q6SP0MmL89M_TnLvNPG4oQEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-06-22T12:34:32.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":462},\"title\":\"LLM-Enhanced Next Best Offer\",\"uploader\":\"Dataiku\"},{\"content\":\"https://www.youtube.com/watch?v=UVbrpX8Zkn8\",\"description\":\"Move beyond the lab and build real and safe Generative AI applications at enterprise scale. Dataiku brings enterprise-grade development tools, pre-built use cases, and AI-powered assistants throughout the platform. Learn more about Dataiku for Generative AI | https://www.dataiku.com/product/generative-ai/ Explore Generative AI use cases | https ...\",\"duration\":\"2:07\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/UVbrpX8Zkn8?autoplay=1\",\"image_token\":\"3968d2d01ff722efa156290344ab0b37164e57d7efa50905c346ea1cc1a5d369\",\"images\":{\"large\":\"https://tse1.mm.bing.net/th?id=OVP.F-xH3-wKfTjM3YMshnjWwwEsDh&pid=Api\",\"medium\":\"https://tse1.mm.bing.net/th?id=OVP.F-xH3-wKfTjM3YMshnjWwwEsDh&pid=Api\",\"motion\":\"https://tse1.mm.bing.net/th?id=OM1.z-FRwxQ_NByK_A_1689135755&pid=Api\",\"small\":\"https://tse1.mm.bing.net/th?id=OVP.F-xH3-wKfTjM3YMshnjWwwEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-06-22T12:25:16.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":1100},\"title\":\"Dataiku for Generative AI: Real Applications, Real Safety\",\"uploader\":\"Dataiku\"},{\"content\":\"https://www.youtube.com/watch?v=-amc9iVauuE\",\"description\":\"Dataiku is the leading platform for Everyday AI, systemizing the use of data for exceptional business results. In today's video we will take a tour of Dataiku's end to end capabilities by exploring a real life use case around environmental impact. Let's take a look at how a data science team with different skills can work together to turn ...\",\"duration\":\"12:35\",\"embed_html\":\"\",\"embed_url\":\"http://www.youtube.com/embed/-amc9iVauuE?autoplay=1\",\"image_token\":\"2a05a65ad8a2727aa5c48b8daa7f9ec363a24d4336a3509016d4b200c9d003cd\",\"images\":{\"large\":\"https://tse1.mm.bing.net/th?id=OVP.Az9RhdSVwpXe56mGcs6FqQEsDh&pid=Api\",\"medium\":\"https://tse1.mm.bing.net/th?id=OVP.Az9RhdSVwpXe56mGcs6FqQEsDh&pid=Api\",\"motion\":\"https://tse1.mm.bing.net/th?id=OM1.Q2OhN9DzfowU6A_1685345657&pid=Api\",\"small\":\"https://tse1.mm.bing.net/th?id=OVP.Az9RhdSVwpXe56mGcs6FqQEsDh&pid=Api\"},\"provider\":\"Bing\",\"published\":\"2023-01-09T21:12:27.0000000\",\"publisher\":\"YouTube\",\"statistics\":{\"viewCount\":9768},\"title\":\"End to End Demo 2023\",\"uploader\":\"Dataiku\"}],\"vqd\":{\"Dataiku%20and%20DataRobot%20use%20cases\":\"4-60481969350525797892441552954401970387\"}});DDG.duckbar.loadModule('related_searches');if (DDG.pageLayout) DDG.pageLayout.initialize({\"mainline\":{\"items\":[[\"ad\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"videos\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"],[\"organic\"]]},\"sidebar\":{\"items\":[[\"organic\"]]}}, { start: 0 });DDG.deep.emit(\"load:completed\");", + "aiohttp-post-https://google.serper.dev/search-{\"data\": \"[{\\\"num\\\": 6, \\\"page\\\": 1, \\\"q\\\": \\\"ai agent\\\"}]\", \"headers\": {\"Content-Type\": \"application/json\", \"X-API-KEY\": \"mock-serper-key\"}}": [ + { + "searchParameters": { + "q": "ai agent", + "num": 6, + "page": 1, + "type": "search", + "engine": "google" + }, + "organic": [ + { + "title": "AI Agent • Supercharge Your Workflows with AI", + "link": "https://aiagent.app/", + "snippet": "A web app that makes choices and performs tasks on its own, based on the goals set by you. How Does it Work?", + "position": 1 + }, + { + "title": "Intelligent agent - Wikipedia", + "link": "https://en.wikipedia.org/wiki/Intelligent_agent", + "snippet": "In artificial intelligence, an intelligent agent (IA) is an agent acting in an intelligent manner; It perceives its environment, takes actions autonomously ...", + "position": 2 + }, + { + "title": "What is an AI agent? | Zapier", + "link": "https://zapier.com/blog/ai-agent/", + "snippet": "AI Agent is a flexible app that lets you create your own agents, by picking a name, an objective, and the AI model it should use (GPT-3.5 Turbo ...", + "date": "Jun 1, 2023", + "position": 3 + }, + { + "title": "Google DeepMind Veteran Departs to Launch AI Agent Startup", + "link": "https://www.theinformation.com/articles/google-deepmind-veteran-departs-to-launch-ai-agent-startup", + "snippet": "The concept of AI agents—bots with the ability to plan and work toward a goal with minimal user guidance—first took off a year ago after ...", + "date": "10 hours ago", + "position": 4 + }, + { + "title": "AI Agents And The Era Of The Intelligent Interface - Forbes", + "link": "https://www.forbes.com/sites/davidarmano/2023/12/07/ai-agents-and-the-era-of-the-intelligent-interface/", + "snippet": "Conversing with several AI Agents connected to various systems is poised to become the next significant evolution of human-computer ...", + "date": "Dec 7, 2023", + "position": 5 + } + ], + "topStories": [ + { + "title": "Google DeepMind Veteran Departs to Launch AI Agent Startup", + "link": "https://www.theinformation.com/articles/google-deepmind-veteran-departs-to-launch-ai-agent-startup", + "source": "The Information", + "date": "10 hours ago", + "imageUrl": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSOUiQnjnTQpnKKDk0hnXhpIdVvwyifhK3VjZuTey9Uq3J1S8l7OB95iWMrKQ&s" + }, + { + "title": "Bitcoin to Become Native Currency for AI Agents, Former Meta Exec Predicts", + "link": "https://u.today/bitcoin-to-become-native-currency-for-ai-agents-former-meta-exec-predicts", + "source": "U.Today", + "date": "2 days ago", + "imageUrl": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRV9Ydu_Dou8HvI9E25KAn7nKmxk6Q-CB1cvT0dIxSudXhZPpGCR1vj0NCdaw&s" + }, + { + "title": "Building AI agents with Semantic Kernel", + "link": "https://www.infoworld.com/article/3712423/building-ai-agents-with-semantic-kernel.html", + "source": "InfoWorld", + "date": "5 days ago", + "imageUrl": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcS0NAiT2vVMB14Ff56syKnS3g4jrNN5LIskrtxqqdViPyrBsLCuCrQWu9ojdA&s" + }, + { + "title": "AI agents help explain other AI systems", + "link": "https://news.mit.edu/2024/ai-agents-help-explain-other-ai-systems-0103", + "source": "MIT News", + "date": "4 weeks ago", + "imageUrl": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcR-0QJKlbSMOP0wYSfB70p4_JCWHEkc6oAkLhBXVHX3ZVATBCRWTp08JY8x4w&s" + }, + { + "title": "CES 2024: LG announces walking, talking, 'Jetsons-esque' smart home robot", + "link": "https://mashable.com/article/ces-2024-lg-announcement-ai-agent-smart-home-robot", + "source": "Mashable", + "date": "3 weeks ago", + "imageUrl": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRVIiyOId49n_-CwPODLHIP9t6HioG05EeI_dvwvg6WNfFBcsLliI_Xhr6U-Q&s" + }, + { + "title": "Develop Your First AI Agent: Deep Q-Learning", + "link": "https://towardsdatascience.com/develop-your-first-ai-agent-deep-q-learning-375876ee2472", + "source": "Towards Data Science", + "date": "1 month ago", + "imageUrl": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSEzOgQuvwfalC2s5HasdRiv2IqdMLgtuUOBJv1xkVGH-Vg_bJmavQk88I1eA&s" + } + ], + "relatedSearches": [ + { + "query": "AI agent GPT" + }, + { + "query": "AI agent OpenAI" + }, + { + "query": "AI agent examples" + }, + { + "query": "Ai agent jobs" + }, + { + "query": "AI agents ChatGPT" + }, + { + "query": "AI agent Microsoft" + }, + { + "query": "AI agent free" + }, + { + "query": "AI Agent app" + } + ] + } + ] +} \ No newline at end of file diff --git a/tests/metagpt/actions/ci/test_ask_review.py b/tests/metagpt/actions/ci/test_ask_review.py new file mode 100644 index 000000000..4f02fe10b --- /dev/null +++ b/tests/metagpt/actions/ci/test_ask_review.py @@ -0,0 +1,12 @@ +import pytest + +from metagpt.actions.ci.ask_review import AskReview + + +@pytest.mark.asyncio +async def test_ask_review(mocker): + mock_review_input = "confirm" + mocker.patch("builtins.input", return_value=mock_review_input) + rsp, confirmed = await AskReview().run() + assert rsp == mock_review_input + assert confirmed diff --git a/tests/metagpt/actions/ci/test_debug_code.py b/tests/metagpt/actions/ci/test_debug_code.py new file mode 100644 index 000000000..0307ac17e --- /dev/null +++ b/tests/metagpt/actions/ci/test_debug_code.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +# @Date : 1/11/2024 8:51 PM +# @Author : stellahong (stellahong@fuzhi.ai) +# @Desc : + +import pytest + +from metagpt.actions.ci.debug_code import DebugCode +from metagpt.schema import Message + +ErrorStr = """Tested passed: + +Tests failed: +assert sort_array([1, 5, 2, 3, 4]) == [1, 2, 3, 4, 5] # output: [1, 2, 4, 3, 5] +""" + +CODE = """ +def sort_array(arr): + # Helper function to count the number of ones in the binary representation + def count_ones(n): + return bin(n).count('1') + + # Sort the array using a custom key function + # The key function returns a tuple (number of ones, value) for each element + # This ensures that if two elements have the same number of ones, they are sorted by their value + sorted_arr = sorted(arr, key=lambda x: (count_ones(x), x)) + + return sorted_arr +``` +""" + +DebugContext = '''Solve the problem in Python: +def sort_array(arr): + """ + In this Kata, you have to sort an array of non-negative integers according to + number of ones in their binary representation in ascending order. + For similar number of ones, sort based on decimal value. + + It must be implemented like this: + >>> sort_array([1, 5, 2, 3, 4]) == [1, 2, 3, 4, 5] + >>> sort_array([-2, -3, -4, -5, -6]) == [-6, -5, -4, -3, -2] + >>> sort_array([1, 0, 2, 3, 4]) [0, 1, 2, 3, 4] + """ +''' + + +@pytest.mark.asyncio +async def test_debug_code(): + debug_context = Message(content=DebugContext) + new_code = await DebugCode().run(context=debug_context, code=CODE, runtime_result=ErrorStr) + assert "def sort_array(arr)" in new_code["code"] diff --git a/tests/metagpt/actions/ci/test_execute_nb_code.py b/tests/metagpt/actions/ci/test_execute_nb_code.py new file mode 100644 index 000000000..72a85dd08 --- /dev/null +++ b/tests/metagpt/actions/ci/test_execute_nb_code.py @@ -0,0 +1,116 @@ +import pytest + +from metagpt.actions.ci.execute_nb_code import ExecuteNbCode, truncate + + +@pytest.mark.asyncio +async def test_code_running(): + executor = ExecuteNbCode() + output, is_success = await executor.run("print('hello world!')") + assert is_success + + +@pytest.mark.asyncio +async def test_split_code_running(): + executor = ExecuteNbCode() + _ = await executor.run("x=1\ny=2") + _ = await executor.run("z=x+y") + output, is_success = await executor.run("assert z==3") + assert is_success + + +@pytest.mark.asyncio +async def test_execute_error(): + executor = ExecuteNbCode() + output, is_success = await executor.run("z=1/0") + assert not is_success + + +PLOT_CODE = """ +import numpy as np +import matplotlib.pyplot as plt + +# 生成随机数据 +random_data = np.random.randn(1000) # 生成1000个符合标准正态分布的随机数 + +# 绘制直方图 +plt.hist(random_data, bins=30, density=True, alpha=0.7, color='blue', edgecolor='black') + +# 添加标题和标签 +plt.title('Histogram of Random Data') +plt.xlabel('Value') +plt.ylabel('Frequency') + +# 显示图形 +plt.show() +plt.close() +""" + + +@pytest.mark.asyncio +async def test_plotting_code(): + executor = ExecuteNbCode() + output, is_success = await executor.run(PLOT_CODE) + assert is_success + + +def test_truncate(): + # 代码执行成功 + output, is_success = truncate("hello world", 5, True) + assert "Truncated to show only first 5 characters\nhello" in output + assert is_success + # 代码执行失败 + output, is_success = truncate("hello world", 5, False) + assert "Truncated to show only last 5 characters\nworld" in output + assert not is_success + # 异步 + output, is_success = truncate(" 0 + print(code_with_ml) diff --git a/tests/metagpt/actions/ci/test_write_analysis_code.py b/tests/metagpt/actions/ci/test_write_analysis_code.py new file mode 100644 index 000000000..95c7dfca8 --- /dev/null +++ b/tests/metagpt/actions/ci/test_write_analysis_code.py @@ -0,0 +1,324 @@ +import asyncio + +import pytest + +from metagpt.actions.ci.execute_nb_code import ExecuteNbCode +from metagpt.actions.ci.write_analysis_code import ( + WriteCodeWithoutTools, + WriteCodeWithTools, +) +from metagpt.logs import logger +from metagpt.schema import Message, Plan, Task +from metagpt.strategy.planner import STRUCTURAL_CONTEXT + + +@pytest.mark.skip +@pytest.mark.asyncio +async def test_write_code_by_list_plan(): + write_code = WriteCodeWithoutTools() + execute_code = ExecuteNbCode() + messages = [] + plan = ["随机生成一个pandas DataFrame时间序列", "绘制这个时间序列的直方图", "回顾已完成的任务", "求均值", "总结"] + for task in plan: + print(f"\n任务: {task}\n\n") + messages.append(Message(task, role="assistant")) + code = await write_code.run(messages) + if task.startswith(("回顾", "总结")): + assert code["language"] == "markdown" + else: + assert code["language"] == "python" + messages.append(Message(code["code"], role="assistant")) + assert len(code) > 0 + output, _ = await execute_code.run(**code) + print(f"\n[Output]: 任务{task}的执行结果是: \n{output}\n") + messages.append(output) + + +@pytest.mark.asyncio +async def test_tool_recommendation(): + task = "clean and preprocess the data" + available_tools = { + "FillMissingValue": "Filling missing values", + "SplitBins": "Bin continuous data into intervals and return the bin identifier encoded as an integer value", + } + write_code = WriteCodeWithTools() + tools = await write_code._recommend_tool(task, available_tools) + + assert len(tools) == 1 + assert "FillMissingValue" in tools + + +@pytest.mark.asyncio +async def test_write_code_with_tools(): + write_code = WriteCodeWithTools() + + requirement = "构造数据集并进行数据清洗" + task_map = { + "1": Task( + task_id="1", + instruction="随机生成一个pandas DataFrame数据集", + task_type="other", + dependent_task_ids=[], + code=""" + import pandas as pd + df = pd.DataFrame({ + 'a': [1, 2, 3, 4, 5], + 'b': [1.1, 2.2, 3.3, 4.4, np.nan], + 'c': ['aa', 'bb', 'cc', 'dd', 'ee'], + 'd': [1, 2, 3, 4, 5] + }) + """, + is_finished=True, + ), + "2": Task( + task_id="2", + instruction="对数据集进行数据清洗", + task_type="data_preprocess", + dependent_task_ids=["1"], + ), + } + plan = Plan( + goal="构造数据集并进行数据清洗", + tasks=list(task_map.values()), + task_map=task_map, + current_task_id="2", + ) + + context = STRUCTURAL_CONTEXT.format( + user_requirement=requirement, + context=plan.context, + tasks=list(task_map.values()), + current_task=plan.current_task.model_dump_json(), + ) + context_msg = [Message(content=context, role="user")] + + code = await write_code.run(context_msg, plan) + code = code["code"] + assert len(code) > 0 + print(code) + + +@pytest.mark.asyncio +async def test_write_code_to_correct_error(): + structural_context = """ + ## User Requirement + read a dataset test.csv and print its head + ## Current Plan + [ + { + "task_id": "1", + "dependent_task_ids": [], + "instruction": "import pandas and load the dataset from 'test.csv'.", + "task_type": "", + "code": "", + "result": "", + "is_finished": false + }, + { + "task_id": "2", + "dependent_task_ids": [ + "1" + ], + "instruction": "Print the head of the dataset to display the first few rows.", + "task_type": "", + "code": "", + "result": "", + "is_finished": false + } + ] + ## Current Task + {"task_id": "1", "dependent_task_ids": [], "instruction": "import pandas and load the dataset from 'test.csv'.", "task_type": "", "code": "", "result": "", "is_finished": false} + """ + wrong_code = """import pandas as pd\ndata = pd.read_excel('test.csv')\ndata""" # use read_excel to read a csv + error = """ + Traceback (most recent call last): + File "", line 2, in + File "/Users/gary/miniconda3/envs/py39_scratch/lib/python3.9/site-packages/pandas/io/excel/_base.py", line 478, in read_excel + io = ExcelFile(io, storage_options=storage_options, engine=engine) + File "/Users/gary/miniconda3/envs/py39_scratch/lib/python3.9/site-packages/pandas/io/excel/_base.py", line 1500, in __init__ + raise ValueError( + ValueError: Excel file format cannot be determined, you must specify an engine manually. + """ + context = [ + Message(content=structural_context, role="user"), + Message(content=wrong_code, role="assistant"), + Message(content=error, role="user"), + ] + new_code = await WriteCodeWithoutTools().run(context=context) + new_code = new_code["code"] + print(new_code) + assert "read_csv" in new_code # should correct read_excel to read_csv + + +@pytest.mark.asyncio +async def test_write_code_reuse_code_simple(): + structural_context = """ + ## User Requirement + read a dataset test.csv and print its head + ## Current Plan + [ + { + "task_id": "1", + "dependent_task_ids": [], + "instruction": "import pandas and load the dataset from 'test.csv'.", + "task_type": "", + "code": "import pandas as pd\ndata = pd.read_csv('test.csv')", + "result": "", + "is_finished": true + }, + { + "task_id": "2", + "dependent_task_ids": [ + "1" + ], + "instruction": "Print the head of the dataset to display the first few rows.", + "task_type": "", + "code": "", + "result": "", + "is_finished": false + } + ] + ## Current Task + {"task_id": "2", "dependent_task_ids": ["1"], "instruction": "Print the head of the dataset to display the first few rows.", "task_type": "", "code": "", "result": "", "is_finished": false} + """ + context = [ + Message(content=structural_context, role="user"), + ] + code = await WriteCodeWithoutTools().run(context=context) + code = code["code"] + print(code) + assert "pandas" not in code and "read_csv" not in code # should reuse import and read statement from previous one + + +@pytest.mark.skip +@pytest.mark.asyncio +async def test_write_code_reuse_code_long(): + """test code reuse for long context""" + + structural_context = """ + ## User Requirement + Run data analysis on sklearn Iris dataset, include a plot + ## Current Plan + [ + { + "task_id": "1", + "dependent_task_ids": [], + "instruction": "Load the Iris dataset from sklearn.", + "task_type": "", + "code": "from sklearn.datasets import load_iris\niris_data = load_iris()\niris_data['data'][0:5], iris_data['target'][0:5]", + "result": "(array([[5.1, 3.5, 1.4, 0.2],\n [4.9, 3. , 1.4, 0.2],\n [4.7, 3.2, 1.3, 0.2],\n [4.6, 3.1, 1.5, 0.2],\n [5. , 3.6, 1.4, 0.2]]),\n array([0, 0, 0, 0, 0]))", + "is_finished": true + }, + { + "task_id": "2", + "dependent_task_ids": [ + "1" + ], + "instruction": "Perform exploratory data analysis on the Iris dataset.", + "task_type": "", + "code": "", + "result": "", + "is_finished": false + }, + { + "task_id": "3", + "dependent_task_ids": [ + "2" + ], + "instruction": "Create a plot visualizing the Iris dataset features.", + "task_type": "", + "code": "", + "result": "", + "is_finished": false + } + ] + ## Current Task + {"task_id": "2", "dependent_task_ids": ["1"], "instruction": "Perform exploratory data analysis on the Iris dataset.", "task_type": "", "code": "", "result": "", "is_finished": false} + """ + context = [ + Message(content=structural_context, role="user"), + ] + trials_num = 5 + trials = [WriteCodeWithoutTools().run(context=context, temperature=0.0) for _ in range(trials_num)] + trial_results = await asyncio.gather(*trials) + print(*trial_results, sep="\n\n***\n\n") + success = [ + "load_iris" not in result["code"] and "iris_data" in result["code"] for result in trial_results + ] # should reuse iris_data from previous tasks + success_rate = sum(success) / trials_num + logger.info(f"success rate: {success_rate :.2f}") + assert success_rate >= 0.8 + + +@pytest.mark.skip +@pytest.mark.asyncio +async def test_write_code_reuse_code_long_for_wine(): + """test code reuse for long context""" + + structural_context = """ + ## User Requirement + Run data analysis on sklearn Wisconsin Breast Cancer dataset, include a plot, train a model to predict targets (20% as validation), and show validation accuracy + ## Current Plan + [ + { + "task_id": "1", + "dependent_task_ids": [], + "instruction": "Load the sklearn Wine recognition dataset and perform exploratory data analysis." + "task_type": "", + "code": "from sklearn.datasets import load_wine\n# Load the Wine recognition dataset\nwine_data = load_wine()\n# Perform exploratory data analysis\nwine_data.keys()", + "result": "Truncated to show only the last 1000 characters\ndict_keys(['data', 'target', 'frame', 'target_names', 'DESCR', 'feature_names'])", + "is_finished": true + }, + { + "task_id": "2", + "dependent_task_ids": ["1"], + "instruction": "Create a plot to visualize some aspect of the wine dataset." + "task_type": "", + "code": "", + "result": "", + "is_finished": false + }, + { + "task_id": "3", + "dependent_task_ids": ["1"], + "instruction": "Split the dataset into training and validation sets with a 20% validation size.", + "task_type": "", + "code": "", + "result": "", + "is_finished": false + }, + { + "task_id": "4", + "dependent_task_ids": ["3"], + "instruction": "Train a model on the training set to predict wine class.", + "task_type": "", + "code": "", + "result": "", + "is_finished": false + }, + { + "task_id": "5", + "dependent_task_ids": ["4"], + "instruction": "Evaluate the model on the validation set and report the accuracy.", + "task_type": "", + "code": "", + "result": "", + "is_finished": false + } + ] + ## Current Task + {"task_id": "2", "dependent_task_ids": ["1"], "instruction": "Create a plot to visualize some aspect of the Wine dataset.", "task_type": "", "code": "", "result": "", "is_finished": false} + """ + context = [ + Message(content=structural_context, role="user"), + ] + trials_num = 5 + trials = [WriteCodeWithoutTools().run(context=context, temperature=0.0) for _ in range(trials_num)] + trial_results = await asyncio.gather(*trials) + print(*trial_results, sep="\n\n***\n\n") + success = [ + "load_wine" not in result["code"] and "wine_data" in result["code"] for result in trial_results + ] # should reuse iris_data from previous tasks + success_rate = sum(success) / trials_num + logger.info(f"success rate: {success_rate :.2f}") + assert success_rate >= 0.8 diff --git a/tests/metagpt/actions/ci/test_write_plan.py b/tests/metagpt/actions/ci/test_write_plan.py new file mode 100644 index 000000000..3eb80ca3e --- /dev/null +++ b/tests/metagpt/actions/ci/test_write_plan.py @@ -0,0 +1,34 @@ +import pytest + +from metagpt.actions.ci.write_plan import ( + Plan, + Task, + WritePlan, + precheck_update_plan_from_rsp, +) +from metagpt.schema import Message + + +def test_precheck_update_plan_from_rsp(): + plan = Plan(goal="") + plan.add_tasks([Task(task_id="1")]) + rsp = '[{"task_id": "2"}]' + success, _ = precheck_update_plan_from_rsp(rsp, plan) + assert success + assert len(plan.tasks) == 1 and plan.tasks[0].task_id == "1" # precheck should not change the original one + + invalid_rsp = "wrong" + success, _ = precheck_update_plan_from_rsp(invalid_rsp, plan) + assert not success + + +@pytest.mark.asyncio +@pytest.mark.parametrize("use_tools", [(False), (True)]) +async def test_write_plan(use_tools): + rsp = await WritePlan().run( + context=[Message("run analysis on sklearn iris dataset", role="user")], use_tools=use_tools + ) + + assert "task_id" in rsp + assert "instruction" in rsp + assert "json" not in rsp # the output should be the content inside ```json ``` diff --git a/tests/metagpt/actions/test_action_node.py b/tests/metagpt/actions/test_action_node.py index 384c4507b..989e2249c 100644 --- a/tests/metagpt/actions/test_action_node.py +++ b/tests/metagpt/actions/test_action_node.py @@ -5,32 +5,32 @@ @Author : alexanderwu @File : test_action_node.py """ +from pathlib import Path from typing import List, Tuple import pytest -from pydantic import ValidationError +from pydantic import BaseModel, Field, ValidationError from metagpt.actions import Action -from metagpt.actions.action_node import ActionNode +from metagpt.actions.action_node import ActionNode, ReviewMode, ReviseMode from metagpt.environment import Environment from metagpt.llm import LLM from metagpt.roles import Role from metagpt.schema import Message from metagpt.team import Team +from metagpt.utils.common import encode_image @pytest.mark.asyncio async def test_debate_two_roles(): action1 = Action(name="AlexSay", instruction="Express your opinion with emotion and don't repeat it") action2 = Action(name="BobSay", instruction="Express your opinion with emotion and don't repeat it") - biden = Role( + alex = Role( name="Alex", profile="Democratic candidate", goal="Win the election", actions=[action1], watch=[action2] ) - trump = Role( - name="Bob", profile="Republican candidate", goal="Win the election", actions=[action2], watch=[action1] - ) + bob = Role(name="Bob", profile="Republican candidate", goal="Win the election", actions=[action2], watch=[action1]) env = Environment(desc="US election live broadcast") - team = Team(investment=10.0, env=env, roles=[biden, trump]) + team = Team(investment=10.0, env=env, roles=[alex, bob]) history = await team.run(idea="Topic: climate change. Under 80 words per message.", send_to="Alex", n_round=3) assert "Alex" in history @@ -39,9 +39,9 @@ async def test_debate_two_roles(): @pytest.mark.asyncio async def test_debate_one_role_in_env(): action = Action(name="Debate", instruction="Express your opinion with emotion and don't repeat it") - biden = Role(name="Alex", profile="Democratic candidate", goal="Win the election", actions=[action]) + alex = Role(name="Alex", profile="Democratic candidate", goal="Win the election", actions=[action]) env = Environment(desc="US election live broadcast") - team = Team(investment=10.0, env=env, roles=[biden]) + team = Team(investment=10.0, env=env, roles=[alex]) history = await team.run(idea="Topic: climate change. Under 80 words per message.", send_to="Alex", n_round=3) assert "Alex" in history @@ -49,8 +49,8 @@ async def test_debate_one_role_in_env(): @pytest.mark.asyncio async def test_debate_one_role(): action = Action(name="Debate", instruction="Express your opinion with emotion and don't repeat it") - biden = Role(name="Alex", profile="Democratic candidate", goal="Win the election", actions=[action]) - msg: Message = await biden.run("Topic: climate change. Under 80 words per message.") + alex = Role(name="Alex", profile="Democratic candidate", goal="Win the election", actions=[action]) + msg: Message = await alex.run("Topic: climate change. Under 80 words per message.") assert len(msg.content) > 10 assert msg.sent_from == "metagpt.roles.role.Role" @@ -98,6 +98,83 @@ async def test_action_node_two_layer(): assert "579" in answer2.content +@pytest.mark.asyncio +async def test_action_node_review(): + key = "Project Name" + node_a = ActionNode( + key=key, + expected_type=str, + instruction='According to the content of "Original Requirements," name the project using snake case style ' + "with underline, like 'game_2048' or 'simple_crm.", + example="game_2048", + ) + + with pytest.raises(RuntimeError): + _ = await node_a.review() + + _ = await node_a.fill(context=None, llm=LLM()) + setattr(node_a.instruct_content, key, "game snake") # wrong content to review + + review_comments = await node_a.review(review_mode=ReviewMode.AUTO) + assert len(review_comments) == 1 + assert list(review_comments.keys())[0] == key + + review_comments = await node_a.review(strgy="complex", review_mode=ReviewMode.AUTO) + assert len(review_comments) == 0 + + node = ActionNode.from_children(key="WritePRD", nodes=[node_a]) + with pytest.raises(RuntimeError): + _ = await node.review() + + _ = await node.fill(context=None, llm=LLM()) + + review_comments = await node.review(review_mode=ReviewMode.AUTO) + assert len(review_comments) == 1 + assert list(review_comments.keys())[0] == key + + review_comments = await node.review(strgy="complex", review_mode=ReviewMode.AUTO) + assert len(review_comments) == 1 + assert list(review_comments.keys())[0] == key + + +@pytest.mark.asyncio +async def test_action_node_revise(): + key = "Project Name" + node_a = ActionNode( + key=key, + expected_type=str, + instruction='According to the content of "Original Requirements," name the project using snake case style ' + "with underline, like 'game_2048' or 'simple_crm.", + example="game_2048", + ) + + with pytest.raises(RuntimeError): + _ = await node_a.review() + + _ = await node_a.fill(context=None, llm=LLM()) + setattr(node_a.instruct_content, key, "game snake") # wrong content to revise + revise_contents = await node_a.revise(revise_mode=ReviseMode.AUTO) + assert len(revise_contents) == 1 + assert "game_snake" in getattr(node_a.instruct_content, key) + + revise_contents = await node_a.revise(strgy="complex", revise_mode=ReviseMode.AUTO) + assert len(revise_contents) == 0 + + node = ActionNode.from_children(key="WritePRD", nodes=[node_a]) + with pytest.raises(RuntimeError): + _ = await node.revise() + + _ = await node.fill(context=None, llm=LLM()) + setattr(node.instruct_content, key, "game snake") + revise_contents = await node.revise(revise_mode=ReviseMode.AUTO) + assert len(revise_contents) == 1 + assert "game_snake" in getattr(node.instruct_content, key) + + revise_contents = await node.revise(strgy="complex", revise_mode=ReviseMode.AUTO) + assert len(revise_contents) == 1 + assert "game_snake" in getattr(node.instruct_content, key) + + t_dict = { "Required Python third-party packages": '"""\nflask==1.1.2\npygame==2.0.1\n"""\n', "Required Other language third-party packages": '"""\nNo third-party packages required for other languages.\n"""\n', @@ -138,10 +215,10 @@ def test_create_model_class(): assert test_class.__name__ == "test_class" output = test_class(**t_dict) - print(output.schema()) - assert output.schema()["title"] == "test_class" - assert output.schema()["type"] == "object" - assert output.schema()["properties"]["Full API spec"] + print(output.model_json_schema()) + assert output.model_json_schema()["title"] == "test_class" + assert output.model_json_schema()["type"] == "object" + assert output.model_json_schema()["properties"]["Full API spec"] def test_create_model_class_with_fields_unrecognized(): @@ -166,6 +243,65 @@ def test_create_model_class_with_mapping(): assert value == ["game.py", "app.py", "static/css/styles.css", "static/js/script.js", "templates/index.html"] +@pytest.mark.asyncio +async def test_action_node_with_image(mocker): + # add a mock to update model in unittest, due to the gloabl MockLLM + def _cons_kwargs(self, messages: list[dict], timeout=3, **extra_kwargs) -> dict: + kwargs = {"messages": messages, "temperature": 0.3, "model": "gpt-4-vision-preview"} + return kwargs + + invoice = ActionNode( + key="invoice", expected_type=bool, instruction="if it's a invoice file, return True else False", example="False" + ) + + invoice_path = Path(__file__).parent.joinpath("..", "..", "data", "invoices", "invoice-2.png") + img_base64 = encode_image(invoice_path) + mocker.patch("metagpt.provider.openai_api.OpenAILLM._cons_kwargs", _cons_kwargs) + node = await invoice.fill(context="", llm=LLM(), images=[img_base64]) + assert node.instruct_content.invoice + + +class ToolDef(BaseModel): + tool_name: str = Field(default="a", description="tool name", examples=[]) + description: str = Field(default="b", description="tool description", examples=[]) + + +class Task(BaseModel): + task_id: int = Field(default=1, description="task id", examples=[1, 2, 3]) + name: str = Field(default="Get data from ...", description="task name", examples=[]) + dependent_task_ids: List[int] = Field(default=[], description="dependent task ids", examples=[1, 2, 3]) + tool: ToolDef = Field(default=ToolDef(), description="tool use", examples=[]) + + +class Tasks(BaseModel): + tasks: List[Task] = Field(default=[], description="tasks", examples=[]) + + +def test_action_node_from_pydantic_and_print_everything(): + node = ActionNode.from_pydantic(Task) + print("1. Tasks") + print(Task().model_dump_json(indent=4)) + print(Tasks.model_json_schema()) + print("2. Task") + print(Task.model_json_schema()) + print("3. ActionNode") + print(node) + print("4. node.compile prompt") + prompt = node.compile(context="") + assert "tool_name" in prompt, "tool_name should be in prompt" + print(prompt) + print("5. node.get_children_mapping") + print(node._get_children_mapping()) + print("6. node.create_children_class") + children_class = node._create_children_class() + print(children_class) + import inspect + + code = inspect.getsource(Tasks) + print(code) + assert "tasks" in code, "tasks should be in code" + + if __name__ == "__main__": test_create_model_class() test_create_model_class_with_mapping() diff --git a/tests/metagpt/actions/test_action_outcls_registry.py b/tests/metagpt/actions/test_action_outcls_registry.py new file mode 100644 index 000000000..eac0ba4d9 --- /dev/null +++ b/tests/metagpt/actions/test_action_outcls_registry.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : unittest of action_outcls_registry + +from typing import List + +from metagpt.actions.action_node import ActionNode + + +def test_action_outcls_registry(): + class_name = "test" + out_mapping = {"field": (list[str], ...), "field1": (str, ...)} + out_data = {"field": ["field value1", "field value2"], "field1": "field1 value1"} + + outcls = ActionNode.create_model_class(class_name, mapping=out_mapping) + outinst = outcls(**out_data) + + outcls1 = ActionNode.create_model_class(class_name=class_name, mapping=out_mapping) + outinst1 = outcls1(**out_data) + assert outinst1 == outinst + + outcls2 = ActionNode(key="", expected_type=str, instruction="", example="").create_model_class( + class_name, out_mapping + ) + outinst2 = outcls2(**out_data) + assert outinst2 == outinst + + out_mapping = {"field1": (str, ...), "field": (list[str], ...)} # different order + outcls3 = ActionNode.create_model_class(class_name=class_name, mapping=out_mapping) + outinst3 = outcls3(**out_data) + assert outinst3 == outinst + + out_mapping2 = {"field1": (str, ...), "field": (List[str], ...)} # typing case + outcls4 = ActionNode.create_model_class(class_name=class_name, mapping=out_mapping2) + outinst4 = outcls4(**out_data) + assert outinst4 == outinst + + out_data2 = {"field2": ["field2 value1", "field2 value2"], "field1": "field1 value1"} + out_mapping = {"field1": (str, ...), "field2": (List[str], ...)} # List first + outcls5 = ActionNode.create_model_class(class_name, out_mapping) + outinst5 = outcls5(**out_data2) + + out_mapping = {"field1": (str, ...), "field2": (list[str], ...)} + outcls6 = ActionNode.create_model_class(class_name, out_mapping) + outinst6 = outcls6(**out_data2) + assert outinst5 == outinst6 diff --git a/tests/metagpt/actions/test_debug_error.py b/tests/metagpt/actions/test_debug_error.py index e512c370a..c88818bbd 100644 --- a/tests/metagpt/actions/test_debug_error.py +++ b/tests/metagpt/actions/test_debug_error.py @@ -11,10 +11,7 @@ import pytest from metagpt.actions.debug_error import DebugError -from metagpt.config import CONFIG -from metagpt.const import TEST_CODES_FILE_REPO, TEST_OUTPUTS_FILE_REPO from metagpt.schema import RunCodeContext, RunCodeResult -from metagpt.utils.file_repository import FileRepository CODE_CONTENT = ''' from typing import List @@ -117,8 +114,8 @@ def test_player_calculate_score_with_multiple_aces(self): @pytest.mark.asyncio -async def test_debug_error(): - CONFIG.src_workspace = CONFIG.git_repo.workdir / uuid.uuid4().hex +async def test_debug_error(context): + context.src_workspace = context.git_repo.workdir / uuid.uuid4().hex ctx = RunCodeContext( code_filename="player.py", test_filename="test_player.py", @@ -126,8 +123,8 @@ async def test_debug_error(): output_filename="output.log", ) - await FileRepository.save_file(filename=ctx.code_filename, content=CODE_CONTENT, relative_path=CONFIG.src_workspace) - await FileRepository.save_file(filename=ctx.test_filename, content=TEST_CONTENT, relative_path=TEST_CODES_FILE_REPO) + await context.repo.with_src_path(context.src_workspace).srcs.save(filename=ctx.code_filename, content=CODE_CONTENT) + await context.repo.tests.save(filename=ctx.test_filename, content=TEST_CONTENT) output_data = RunCodeResult( stdout=";", stderr="", @@ -141,24 +138,11 @@ async def test_debug_error(): "----------------------------------------------------------------------\n" "Ran 5 tests in 0.007s\n\nFAILED (failures=1)\n;\n", ) - await FileRepository.save_file( - filename=ctx.output_filename, content=output_data.model_dump_json(), relative_path=TEST_OUTPUTS_FILE_REPO - ) - debug_error = DebugError(context=ctx) + await context.repo.test_outputs.save(filename=ctx.output_filename, content=output_data.model_dump_json()) + debug_error = DebugError(i_context=ctx, context=context) rsp = await debug_error.run() assert "class Player" in rsp # rewrite the same class - # Problematic code: - # ``` - # if self.score > 21 and any(card.rank == 'A' for card in self.hand): - # self.score -= 10 - # ``` - # Should rewrite to (used "gpt-3.5-turbo-1106"): - # ``` - # ace_count = sum(1 for card in self.hand if card.rank == 'A') - # while self.score > 21 and ace_count > 0: - # self.score -= 10 - # ace_count -= 1 - # ``` - assert "while self.score > 21" in rsp + # a key logic to rewrite to (original one is "if self.score > 12") + assert "self.score" in rsp diff --git a/tests/metagpt/actions/test_design_api.py b/tests/metagpt/actions/test_design_api.py index 8d4720570..7d3efa7ff 100644 --- a/tests/metagpt/actions/test_design_api.py +++ b/tests/metagpt/actions/test_design_api.py @@ -9,20 +9,17 @@ import pytest from metagpt.actions.design_api import WriteDesign -from metagpt.const import PRDS_FILE_REPO from metagpt.logs import logger from metagpt.schema import Message -from metagpt.utils.file_repository import FileRepository -from tests.metagpt.actions.mock_markdown import PRD_SAMPLE @pytest.mark.asyncio -async def test_design_api(): - inputs = ["我们需要一个音乐播放器,它应该有播放、暂停、上一曲、下一曲等功能。", PRD_SAMPLE] +async def test_design_api(context): + inputs = ["我们需要一个音乐播放器,它应该有播放、暂停、上一曲、下一曲等功能。"] # PRD_SAMPLE for prd in inputs: - await FileRepository.save_file("new_prd.txt", content=prd, relative_path=PRDS_FILE_REPO) + await context.repo.docs.prd.save(filename="new_prd.txt", content=prd) - design_api = WriteDesign() + design_api = WriteDesign(context=context) result = await design_api.run(Message(content=prd, instruct_content=None)) logger.info(result) diff --git a/tests/metagpt/actions/test_design_api_review.py b/tests/metagpt/actions/test_design_api_review.py index cfc29056f..a648dba3f 100644 --- a/tests/metagpt/actions/test_design_api_review.py +++ b/tests/metagpt/actions/test_design_api_review.py @@ -11,7 +11,7 @@ @pytest.mark.asyncio -async def test_design_api_review(): +async def test_design_api_review(context): prd = "我们需要一个音乐播放器,它应该有播放、暂停、上一曲、下一曲等功能。" api_design = """ 数据结构: @@ -26,7 +26,7 @@ async def test_design_api_review(): """ _ = "API设计看起来非常合理,满足了PRD中的所有需求。" - design_api_review = DesignReview() + design_api_review = DesignReview(context=context) result = await design_api_review.run(prd, api_design) diff --git a/tests/metagpt/actions/test_fix_bug.py b/tests/metagpt/actions/test_fix_bug.py index b2dc8d0f4..cbd9d0b57 100644 --- a/tests/metagpt/actions/test_fix_bug.py +++ b/tests/metagpt/actions/test_fix_bug.py @@ -12,6 +12,6 @@ @pytest.mark.asyncio -async def test_fix_bug(): - fix_bug = FixBug() +async def test_fix_bug(context): + fix_bug = FixBug(context=context) assert fix_bug.name == "FixBug" diff --git a/tests/metagpt/actions/test_generate_questions.py b/tests/metagpt/actions/test_generate_questions.py index b7c9d3984..6adb2e617 100644 --- a/tests/metagpt/actions/test_generate_questions.py +++ b/tests/metagpt/actions/test_generate_questions.py @@ -10,7 +10,7 @@ from metagpt.actions.generate_questions import GenerateQuestions from metagpt.logs import logger -context = """ +msg = """ ## topic 如何做一个生日蛋糕 @@ -20,9 +20,9 @@ @pytest.mark.asyncio -async def test_generate_questions(): - action = GenerateQuestions() - rsp = await action.run(context) +async def test_generate_questions(context): + action = GenerateQuestions(context=context) + rsp = await action.run(msg) logger.info(f"{rsp.content=}") assert "Questions" in rsp.content diff --git a/tests/metagpt/actions/test_invoice_ocr.py b/tests/metagpt/actions/test_invoice_ocr.py index b4560f61b..4df0cf4f8 100644 --- a/tests/metagpt/actions/test_invoice_ocr.py +++ b/tests/metagpt/actions/test_invoice_ocr.py @@ -23,9 +23,9 @@ Path("invoices/invoice-4.zip"), ], ) -async def test_invoice_ocr(invoice_path: Path): +async def test_invoice_ocr(invoice_path: Path, context): invoice_path = TEST_DATA_PATH / invoice_path - resp = await InvoiceOCR().run(file_path=Path(invoice_path)) + resp = await InvoiceOCR(context=context).run(file_path=Path(invoice_path)) assert isinstance(resp, list) diff --git a/tests/metagpt/actions/test_prepare_documents.py b/tests/metagpt/actions/test_prepare_documents.py index 31c8bcb80..7ad0dee4e 100644 --- a/tests/metagpt/actions/test_prepare_documents.py +++ b/tests/metagpt/actions/test_prepare_documents.py @@ -9,22 +9,19 @@ import pytest from metagpt.actions.prepare_documents import PrepareDocuments -from metagpt.config import CONFIG -from metagpt.const import DOCS_FILE_REPO, REQUIREMENT_FILENAME +from metagpt.const import REQUIREMENT_FILENAME +from metagpt.context import Context from metagpt.schema import Message -from metagpt.utils.file_repository import FileRepository @pytest.mark.asyncio async def test_prepare_documents(): msg = Message(content="New user requirements balabala...") + context = Context() - if CONFIG.git_repo: - CONFIG.git_repo.delete_repository() - CONFIG.git_repo = None - - await PrepareDocuments().run(with_messages=[msg]) - assert CONFIG.git_repo - doc = await FileRepository.get_file(filename=REQUIREMENT_FILENAME, relative_path=DOCS_FILE_REPO) + await PrepareDocuments(context=context).run(with_messages=[msg]) + assert context.git_repo + assert context.repo + doc = await context.repo.docs.get(filename=REQUIREMENT_FILENAME) assert doc assert doc.content == msg.content diff --git a/tests/metagpt/actions/test_prepare_interview.py b/tests/metagpt/actions/test_prepare_interview.py index cd0c850ed..111f24d5f 100644 --- a/tests/metagpt/actions/test_prepare_interview.py +++ b/tests/metagpt/actions/test_prepare_interview.py @@ -12,8 +12,8 @@ @pytest.mark.asyncio -async def test_prepare_interview(): - action = PrepareInterview() +async def test_prepare_interview(context): + action = PrepareInterview(context=context) rsp = await action.run("I just graduated and hope to find a job as a Python engineer") logger.info(f"{rsp.content=}") diff --git a/tests/metagpt/actions/test_project_management.py b/tests/metagpt/actions/test_project_management.py index 88263ff29..f3bb405ca 100644 --- a/tests/metagpt/actions/test_project_management.py +++ b/tests/metagpt/actions/test_project_management.py @@ -9,21 +9,18 @@ import pytest from metagpt.actions.project_management import WriteTasks -from metagpt.config import CONFIG -from metagpt.const import PRDS_FILE_REPO, SYSTEM_DESIGN_FILE_REPO from metagpt.logs import logger from metagpt.schema import Message -from metagpt.utils.file_repository import FileRepository from tests.metagpt.actions.mock_json import DESIGN, PRD @pytest.mark.asyncio -async def test_design_api(): - await FileRepository.save_file("1.txt", content=str(PRD), relative_path=PRDS_FILE_REPO) - await FileRepository.save_file("1.txt", content=str(DESIGN), relative_path=SYSTEM_DESIGN_FILE_REPO) - logger.info(CONFIG.git_repo) +async def test_design_api(context): + await context.repo.docs.prd.save("1.txt", content=str(PRD)) + await context.repo.docs.system_design.save("1.txt", content=str(DESIGN)) + logger.info(context.git_repo) - action = WriteTasks() + action = WriteTasks(context=context) result = await action.run(Message(content="", instruct_content=None)) logger.info(result) diff --git a/tests/metagpt/actions/test_project_management_an.py b/tests/metagpt/actions/test_project_management_an.py index aa759aec8..ddbb56569 100644 --- a/tests/metagpt/actions/test_project_management_an.py +++ b/tests/metagpt/actions/test_project_management_an.py @@ -37,7 +37,7 @@ async def test_project_management_an(mocker): root.instruct_content.model_dump = mock_refined_tasks_json mocker.patch("metagpt.actions.project_management_an.REFINED_PM_NODE.fill", return_value=root) - prompt = NEW_REQ_TEMPLATE.format(old_tasks=TASKS_SAMPLE, context=dict_to_markdown(REFINED_DESIGN_JSON)) + prompt = NEW_REQ_TEMPLATE.format(old_task=TASKS_SAMPLE, context=dict_to_markdown(REFINED_DESIGN_JSON)) node = await REFINED_PM_NODE.fill(prompt, llm) assert "Refined Logic Analysis" in node.instruct_content.model_dump() diff --git a/tests/metagpt/actions/test_rebuild_class_view.py b/tests/metagpt/actions/test_rebuild_class_view.py index 207ba4be1..403109cc0 100644 --- a/tests/metagpt/actions/test_rebuild_class_view.py +++ b/tests/metagpt/actions/test_rebuild_class_view.py @@ -11,27 +11,27 @@ import pytest from metagpt.actions.rebuild_class_view import RebuildClassView -from metagpt.config import CONFIG -from metagpt.const import GRAPH_REPO_FILE_REPO from metagpt.llm import LLM @pytest.mark.asyncio -async def test_rebuild(): +async def test_rebuild(context): action = RebuildClassView( - name="RedBean", context=str(Path(__file__).parent.parent.parent.parent / "metagpt"), llm=LLM() + name="RedBean", + i_context=str(Path(__file__).parent.parent.parent.parent / "metagpt"), + llm=LLM(), + context=context, ) await action.run() - graph_file_repo = CONFIG.git_repo.new_file_repository(relative_path=GRAPH_REPO_FILE_REPO) - assert graph_file_repo.changed_files + assert context.repo.docs.graph_repo.changed_files @pytest.mark.parametrize( ("path", "direction", "diff", "want"), [ - ("metagpt/startup.py", "=", ".", "metagpt/startup.py"), - ("metagpt/startup.py", "+", "MetaGPT", "MetaGPT/metagpt/startup.py"), - ("metagpt/startup.py", "-", "metagpt", "startup.py"), + ("metagpt/software_company.py", "=", ".", "metagpt/software_company.py"), + ("metagpt/software_company.py", "+", "MetaGPT", "MetaGPT/metagpt/software_company.py"), + ("metagpt/software_company.py", "-", "metagpt", "software_company.py"), ], ) def test_align_path(path, direction, diff, want): diff --git a/tests/metagpt/actions/test_rebuild_sequence_view.py b/tests/metagpt/actions/test_rebuild_sequence_view.py index 939412fe7..e0e65c3cc 100644 --- a/tests/metagpt/actions/test_rebuild_sequence_view.py +++ b/tests/metagpt/actions/test_rebuild_sequence_view.py @@ -10,33 +10,30 @@ import pytest from metagpt.actions.rebuild_sequence_view import RebuildSequenceView -from metagpt.config import CONFIG from metagpt.const import GRAPH_REPO_FILE_REPO from metagpt.llm import LLM from metagpt.utils.common import aread -from metagpt.utils.file_repository import FileRepository from metagpt.utils.git_repository import ChangeType @pytest.mark.asyncio -async def test_rebuild(): +@pytest.mark.skip +async def test_rebuild(context): # Mock data = await aread(filename=Path(__file__).parent / "../../data/graph_db/networkx.json") - graph_db_filename = Path(CONFIG.git_repo.workdir.name).with_suffix(".json") - await FileRepository.save_file( - filename=str(graph_db_filename), - relative_path=GRAPH_REPO_FILE_REPO, - content=data, - ) - CONFIG.git_repo.add_change({f"{GRAPH_REPO_FILE_REPO}/{graph_db_filename}": ChangeType.UNTRACTED}) - CONFIG.git_repo.commit("commit1") + graph_db_filename = Path(context.repo.workdir.name).with_suffix(".json") + await context.repo.docs.graph_repo.save(filename=str(graph_db_filename), content=data) + context.git_repo.add_change({f"{GRAPH_REPO_FILE_REPO}/{graph_db_filename}": ChangeType.UNTRACTED}) + context.git_repo.commit("commit1") action = RebuildSequenceView( - name="RedBean", context=str(Path(__file__).parent.parent.parent.parent / "metagpt"), llm=LLM() + name="RedBean", + i_context=str(Path(__file__).parent.parent.parent.parent / "metagpt"), + llm=LLM(), + context=context, ) await action.run() - graph_file_repo = CONFIG.git_repo.new_file_repository(relative_path=GRAPH_REPO_FILE_REPO) - assert graph_file_repo.changed_files + assert context.repo.docs.graph_repo.changed_files @pytest.mark.parametrize( diff --git a/tests/metagpt/actions/test_research.py b/tests/metagpt/actions/test_research.py index dfbcce4ae..ed83ce58c 100644 --- a/tests/metagpt/actions/test_research.py +++ b/tests/metagpt/actions/test_research.py @@ -9,10 +9,12 @@ import pytest from metagpt.actions import research +from metagpt.tools import SearchEngineType +from metagpt.tools.search_engine import SearchEngine @pytest.mark.asyncio -async def test_collect_links(mocker): +async def test_collect_links(mocker, search_engine_mocker, context): async def mock_llm_ask(self, prompt: str, system_msgs): if "Please provide up to 2 necessary keywords" in prompt: return '["metagpt", "llm"]' @@ -26,13 +28,15 @@ async def mock_llm_ask(self, prompt: str, system_msgs): return "[1,2]" mocker.patch("metagpt.provider.base_llm.BaseLLM.aask", mock_llm_ask) - resp = await research.CollectLinks().run("The application of MetaGPT") + resp = await research.CollectLinks( + search_engine=SearchEngine(engine=SearchEngineType.DUCK_DUCK_GO), context=context + ).run("The application of MetaGPT") for i in ["MetaGPT use cases", "The roadmap of MetaGPT", "The function of MetaGPT", "What llm MetaGPT support"]: assert i in resp @pytest.mark.asyncio -async def test_collect_links_with_rank_func(mocker): +async def test_collect_links_with_rank_func(mocker, search_engine_mocker, context): rank_before = [] rank_after = [] url_per_query = 4 @@ -45,14 +49,18 @@ def rank_func(results): return results mocker.patch("metagpt.provider.base_llm.BaseLLM.aask", mock_collect_links_llm_ask) - resp = await research.CollectLinks(rank_func=rank_func).run("The application of MetaGPT") + resp = await research.CollectLinks( + search_engine=SearchEngine(engine=SearchEngineType.DUCK_DUCK_GO), + rank_func=rank_func, + context=context, + ).run("The application of MetaGPT") for x, y, z in zip(rank_before, rank_after, resp.values()): assert x[::-1] == y assert [i["link"] for i in y] == z @pytest.mark.asyncio -async def test_web_browse_and_summarize(mocker): +async def test_web_browse_and_summarize(mocker, context): async def mock_llm_ask(*args, **kwargs): return "metagpt" @@ -60,20 +68,20 @@ async def mock_llm_ask(*args, **kwargs): url = "https://github.com/geekan/MetaGPT" url2 = "https://github.com/trending" query = "What's new in metagpt" - resp = await research.WebBrowseAndSummarize().run(url, query=query) + resp = await research.WebBrowseAndSummarize(context=context).run(url, query=query) assert len(resp) == 1 assert url in resp assert resp[url] == "metagpt" - resp = await research.WebBrowseAndSummarize().run(url, url2, query=query) + resp = await research.WebBrowseAndSummarize(context=context).run(url, url2, query=query) assert len(resp) == 2 async def mock_llm_ask(*args, **kwargs): return "Not relevant." mocker.patch("metagpt.provider.base_llm.BaseLLM.aask", mock_llm_ask) - resp = await research.WebBrowseAndSummarize().run(url, query=query) + resp = await research.WebBrowseAndSummarize(context=context).run(url, query=query) assert len(resp) == 1 assert url in resp @@ -81,7 +89,7 @@ async def mock_llm_ask(*args, **kwargs): @pytest.mark.asyncio -async def test_conduct_research(mocker): +async def test_conduct_research(mocker, context): data = None async def mock_llm_ask(*args, **kwargs): @@ -95,7 +103,7 @@ async def mock_llm_ask(*args, **kwargs): "outputs user stories / competitive analysis / requirements / data structures / APIs / documents, etc." ) - resp = await research.ConductResearch().run("The application of MetaGPT", content) + resp = await research.ConductResearch(context=context).run("The application of MetaGPT", content) assert resp == data diff --git a/tests/metagpt/actions/test_run_code.py b/tests/metagpt/actions/test_run_code.py index ad08b5738..2ec8a7748 100644 --- a/tests/metagpt/actions/test_run_code.py +++ b/tests/metagpt/actions/test_run_code.py @@ -24,21 +24,21 @@ async def test_run_text(): @pytest.mark.asyncio -async def test_run_script(): +async def test_run_script(context): # Successful command - out, err = await RunCode.run_script(".", command=["echo", "Hello World"]) + out, err = await RunCode(context=context).run_script(".", command=["echo", "Hello World"]) assert out.strip() == "Hello World" assert err == "" # Unsuccessful command - out, err = await RunCode.run_script(".", command=["python", "-c", "print(1/0)"]) + out, err = await RunCode(context=context).run_script(".", command=["python", "-c", "print(1/0)"]) assert "ZeroDivisionError" in err @pytest.mark.asyncio -async def test_run(): +async def test_run(context): inputs = [ - (RunCodeContext(mode="text", code_filename="a.txt", code="print('Hello, World')"), "PASS"), + (RunCodeContext(mode="text", code_filename="a.txt", code="result = 'helloworld'"), "PASS"), ( RunCodeContext( mode="script", @@ -61,5 +61,5 @@ async def test_run(): ), ] for ctx, result in inputs: - rsp = await RunCode(context=ctx).run() + rsp = await RunCode(i_context=ctx, context=context).run() assert result in rsp.summary diff --git a/tests/metagpt/actions/test_skill_action.py b/tests/metagpt/actions/test_skill_action.py index 69cd8129d..d667d6d70 100644 --- a/tests/metagpt/actions/test_skill_action.py +++ b/tests/metagpt/actions/test_skill_action.py @@ -23,9 +23,9 @@ class TestSkillAction: "type": "string", "description": "OpenAI API key, For more details, checkout: `https://platform.openai.com/account/api-keys`", }, - "METAGPT_TEXT_TO_IMAGE_MODEL_URL": {"type": "string", "description": "Model url."}, + "metagpt_tti_url": {"type": "string", "description": "Model url."}, }, - "required": {"oneOf": ["OPENAI_API_KEY", "METAGPT_TEXT_TO_IMAGE_MODEL_URL"]}, + "required": {"oneOf": ["OPENAI_API_KEY", "metagpt_tti_url"]}, }, parameters={ "text": Parameter(type="string", description="The text used for image conversion."), @@ -47,18 +47,18 @@ async def test_parser(self): assert args.get("size_type") == "512x512" @pytest.mark.asyncio - async def test_parser_action(self, mocker): + async def test_parser_action(self, mocker, context): # mock mocker.patch("metagpt.learn.text_to_image", return_value="https://mock.com/xxx") - parser_action = ArgumentsParingAction(skill=self.skill, ask="Draw an apple") + parser_action = ArgumentsParingAction(skill=self.skill, ask="Draw an apple", context=context) rsp = await parser_action.run() assert rsp assert parser_action.args assert parser_action.args.get("text") == "Draw an apple" assert parser_action.args.get("size_type") == "512x512" - action = SkillAction(skill=self.skill, args=parser_action.args) + action = SkillAction(skill=self.skill, args=parser_action.args, context=context) rsp = await action.run() assert rsp assert "image/png;base64," in rsp.content or "http" in rsp.content @@ -81,8 +81,8 @@ async def test_find_and_call_function_error(self): await SkillAction.find_and_call_function("dummy_call", {"a": 1}) @pytest.mark.asyncio - async def test_skill_action_error(self): - action = SkillAction(skill=self.skill, args={}) + async def test_skill_action_error(self, context): + action = SkillAction(skill=self.skill, args={}, context=context) rsp = await action.run() assert "Error" in rsp.content diff --git a/tests/metagpt/actions/test_summarize_code.py b/tests/metagpt/actions/test_summarize_code.py index 7ecb67afd..a404047c1 100644 --- a/tests/metagpt/actions/test_summarize_code.py +++ b/tests/metagpt/actions/test_summarize_code.py @@ -6,14 +6,14 @@ @File : test_summarize_code.py @Modifiled By: mashenquan, 2023-12-6. Unit test for summarize_code.py """ +import uuid +from pathlib import Path + import pytest from metagpt.actions.summarize_code import SummarizeCode -from metagpt.config import CONFIG -from metagpt.const import SYSTEM_DESIGN_FILE_REPO, TASK_FILE_REPO from metagpt.logs import logger from metagpt.schema import CodeSummarizeContext -from metagpt.utils.file_repository import FileRepository DESIGN_CONTENT = """ {"Implementation approach": "To develop this snake game, we will use the Python language and choose the Pygame library. Pygame is an open-source Python module collection specifically designed for writing video games. It provides functionalities such as displaying images and playing sounds, making it suitable for creating intuitive and responsive user interfaces. We will ensure efficient game logic to prevent any delays during gameplay. The scoring system will be simple, with the snake gaining points for each food it eats. We will use Pygame's event handling system to implement pause and resume functionality, as well as high-score tracking. The difficulty will increase by speeding up the snake's movement. In the initial version, we will focus on single-player mode and consider adding multiplayer mode and customizable skins in future updates. Based on the new requirement, we will also add a moving obstacle that appears randomly. If the snake eats this obstacle, the game will end. If the snake does not eat the obstacle, it will disappear after 5 seconds. For this, we need to add mechanisms for obstacle generation, movement, and disappearance in the game logic.", "Project_name": "snake_game", "File list": ["main.py", "game.py", "snake.py", "food.py", "obstacle.py", "scoreboard.py", "constants.py", "assets/styles.css", "assets/index.html"], "Data structures and interfaces": "```mermaid\n classDiagram\n class Game{\n +int score\n +int speed\n +bool game_over\n +bool paused\n +Snake snake\n +Food food\n +Obstacle obstacle\n +Scoreboard scoreboard\n +start_game() void\n +pause_game() void\n +resume_game() void\n +end_game() void\n +increase_difficulty() void\n +update() void\n +render() void\n Game()\n }\n class Snake{\n +list body_parts\n +str direction\n +bool grow\n +move() void\n +grow() void\n +check_collision() bool\n Snake()\n }\n class Food{\n +tuple position\n +spawn() void\n Food()\n }\n class Obstacle{\n +tuple position\n +int lifetime\n +bool active\n +spawn() void\n +move() void\n +check_collision() bool\n +disappear() void\n Obstacle()\n }\n class Scoreboard{\n +int high_score\n +update_score(int) void\n +reset_score() void\n +load_high_score() void\n +save_high_score() void\n Scoreboard()\n }\n class Constants{\n }\n Game \"1\" -- \"1\" Snake: has\n Game \"1\" -- \"1\" Food: has\n Game \"1\" -- \"1\" Obstacle: has\n Game \"1\" -- \"1\" Scoreboard: has\n ```", "Program call flow": "```sequenceDiagram\n participant M as Main\n participant G as Game\n participant S as Snake\n participant F as Food\n participant O as Obstacle\n participant SB as Scoreboard\n M->>G: start_game()\n loop game loop\n G->>S: move()\n G->>S: check_collision()\n G->>F: spawn()\n G->>O: spawn()\n G->>O: move()\n G->>O: check_collision()\n G->>O: disappear()\n G->>SB: update_score(score)\n G->>G: update()\n G->>G: render()\n alt if paused\n M->>G: pause_game()\n M->>G: resume_game()\n end\n alt if game_over\n G->>M: end_game()\n end\n end\n```", "Anything UNCLEAR": "There is no need for further clarification as the requirements are already clear."} @@ -177,19 +177,28 @@ def get_body(self): @pytest.mark.asyncio -async def test_summarize_code(): - CONFIG.src_workspace = CONFIG.git_repo.workdir / "src" - await FileRepository.save_file(filename="1.json", relative_path=SYSTEM_DESIGN_FILE_REPO, content=DESIGN_CONTENT) - await FileRepository.save_file(filename="1.json", relative_path=TASK_FILE_REPO, content=TASK_CONTENT) - await FileRepository.save_file(filename="food.py", relative_path=CONFIG.src_workspace, content=FOOD_PY) - await FileRepository.save_file(filename="game.py", relative_path=CONFIG.src_workspace, content=GAME_PY) - await FileRepository.save_file(filename="main.py", relative_path=CONFIG.src_workspace, content=MAIN_PY) - await FileRepository.save_file(filename="snake.py", relative_path=CONFIG.src_workspace, content=SNAKE_PY) - - src_file_repo = CONFIG.git_repo.new_file_repository(relative_path=CONFIG.src_workspace) - all_files = src_file_repo.all_files - ctx = CodeSummarizeContext(design_filename="1.json", task_filename="1.json", codes_filenames=all_files) - action = SummarizeCode(context=ctx) +async def test_summarize_code(context): + git_dir = Path(__file__).parent / f"unittest/{uuid.uuid4().hex}" + git_dir.mkdir(parents=True, exist_ok=True) + + context.src_workspace = context.git_repo.workdir / "src" + await context.repo.docs.system_design.save(filename="1.json", content=DESIGN_CONTENT) + await context.repo.docs.task.save(filename="1.json", content=TASK_CONTENT) + await context.repo.with_src_path(context.src_workspace).srcs.save(filename="food.py", content=FOOD_PY) + assert context.repo.srcs.workdir == context.src_workspace + await context.repo.srcs.save(filename="game.py", content=GAME_PY) + await context.repo.srcs.save(filename="main.py", content=MAIN_PY) + await context.repo.srcs.save(filename="snake.py", content=SNAKE_PY) + + all_files = context.repo.srcs.all_files + summarization_context = CodeSummarizeContext( + design_filename="1.json", task_filename="1.json", codes_filenames=all_files + ) + action = SummarizeCode(context=context, i_context=summarization_context) rsp = await action.run() assert rsp logger.info(rsp) + + +if __name__ == "__main__": + pytest.main([__file__, "-s"]) diff --git a/tests/metagpt/actions/test_talk_action.py b/tests/metagpt/actions/test_talk_action.py index 953fdf44a..206abfbae 100644 --- a/tests/metagpt/actions/test_talk_action.py +++ b/tests/metagpt/actions/test_talk_action.py @@ -9,13 +9,12 @@ import pytest from metagpt.actions.talk_action import TalkAction -from metagpt.config import CONFIG from metagpt.schema import Message @pytest.mark.asyncio @pytest.mark.parametrize( - ("agent_description", "language", "context", "knowledge", "history_summary"), + ("agent_description", "language", "talk_context", "knowledge", "history_summary"), [ ( "mathematician", @@ -33,12 +32,12 @@ ), ], ) -async def test_prompt(agent_description, language, context, knowledge, history_summary): +async def test_prompt(agent_description, language, talk_context, knowledge, history_summary, context): # Prerequisites - CONFIG.agent_description = agent_description - CONFIG.language = language + context.kwargs.agent_description = agent_description + context.kwargs.language = language - action = TalkAction(context=context, knowledge=knowledge, history_summary=history_summary) + action = TalkAction(i_context=talk_context, knowledge=knowledge, history_summary=history_summary, context=context) assert "{" not in action.prompt assert "{" not in action.prompt_gpt4 diff --git a/tests/metagpt/actions/test_write_code.py b/tests/metagpt/actions/test_write_code.py index 249145c92..ee05e0f7d 100644 --- a/tests/metagpt/actions/test_write_code.py +++ b/tests/metagpt/actions/test_write_code.py @@ -12,28 +12,22 @@ import pytest from metagpt.actions.write_code import WriteCode -from metagpt.config import CONFIG -from metagpt.const import ( - CODE_SUMMARIES_FILE_REPO, - SYSTEM_DESIGN_FILE_REPO, - TASK_FILE_REPO, - TEST_OUTPUTS_FILE_REPO, -) from metagpt.logs import logger -from metagpt.provider.openai_api import OpenAILLM as LLM from metagpt.schema import CodingContext, Document from metagpt.utils.common import aread -from metagpt.utils.file_repository import FileRepository from tests.metagpt.actions.mock_markdown import TASKS_2, WRITE_CODE_PROMPT_SAMPLE @pytest.mark.asyncio -async def test_write_code(): - context = CodingContext( +async def test_write_code(context): + # Prerequisites + context.src_workspace = context.git_repo.workdir / "writecode" + + coding_ctx = CodingContext( filename="task_filename.py", design_doc=Document(content="设计一个名为'add'的函数,该函数接受两个整数作为输入,并返回它们的和。") ) - doc = Document(content=context.model_dump_json()) - write_code = WriteCode(context=doc) + doc = Document(content=coding_ctx.model_dump_json()) + write_code = WriteCode(i_context=doc, context=context) code = await write_code.run() logger.info(code.model_dump_json()) @@ -44,48 +38,44 @@ async def test_write_code(): @pytest.mark.asyncio -async def test_write_code_directly(): +async def test_write_code_directly(context): prompt = WRITE_CODE_PROMPT_SAMPLE + "\n" + TASKS_2[0] - llm = LLM() + llm = context.llm_with_cost_manager_from_llm_config(context.config.llm) rsp = await llm.aask(prompt) logger.info(rsp) @pytest.mark.asyncio -async def test_write_code_deps(): +async def test_write_code_deps(context): # Prerequisites - CONFIG.src_workspace = CONFIG.git_repo.workdir / "snake1/snake1" + context.src_workspace = context.git_repo.workdir / "snake1/snake1" demo_path = Path(__file__).parent / "../../data/demo_project" - await FileRepository.save_file( - filename="test_game.py.json", - content=await aread(str(demo_path / "test_game.py.json")), - relative_path=TEST_OUTPUTS_FILE_REPO, + await context.repo.test_outputs.save( + filename="test_game.py.json", content=await aread(str(demo_path / "test_game.py.json")) ) - await FileRepository.save_file( + await context.repo.docs.code_summary.save( filename="20231221155954.json", content=await aread(str(demo_path / "code_summaries.json")), - relative_path=CODE_SUMMARIES_FILE_REPO, ) - await FileRepository.save_file( + await context.repo.docs.system_design.save( filename="20231221155954.json", content=await aread(str(demo_path / "system_design.json")), - relative_path=SYSTEM_DESIGN_FILE_REPO, ) - await FileRepository.save_file( - filename="20231221155954.json", content=await aread(str(demo_path / "tasks.json")), relative_path=TASK_FILE_REPO + await context.repo.docs.task.save( + filename="20231221155954.json", content=await aread(str(demo_path / "tasks.json")) ) - await FileRepository.save_file( - filename="main.py", content='if __name__ == "__main__":\nmain()', relative_path=CONFIG.src_workspace + await context.repo.with_src_path(context.src_workspace).srcs.save( + filename="main.py", content='if __name__ == "__main__":\nmain()' ) - context = CodingContext( + ccontext = CodingContext( filename="game.py", - design_doc=await FileRepository.get_file(filename="20231221155954.json", relative_path=SYSTEM_DESIGN_FILE_REPO), - task_doc=await FileRepository.get_file(filename="20231221155954.json", relative_path=TASK_FILE_REPO), + design_doc=await context.repo.docs.system_design.get(filename="20231221155954.json"), + task_doc=await context.repo.docs.task.get(filename="20231221155954.json"), code_doc=Document(filename="game.py", content="", root_path="snake1"), ) - coding_doc = Document(root_path="snake1", filename="game.py", content=context.json()) + coding_doc = Document(root_path="snake1", filename="game.py", content=ccontext.json()) - action = WriteCode(context=coding_doc) + action = WriteCode(i_context=coding_doc, context=context) rsp = await action.run() assert rsp assert rsp.code_doc.content diff --git a/tests/metagpt/actions/test_write_code_plan_and_change_an.py b/tests/metagpt/actions/test_write_code_plan_and_change_an.py index 33114dfcf..9cd51398f 100644 --- a/tests/metagpt/actions/test_write_code_plan_and_change_an.py +++ b/tests/metagpt/actions/test_write_code_plan_and_change_an.py @@ -14,7 +14,7 @@ REFINED_TEMPLATE, WriteCodePlanAndChange, ) -from metagpt.schema import CodePlanAndChangeContext, Document +from metagpt.schema import CodePlanAndChangeContext from tests.data.incremental_dev_project.mock import ( CODE_PLAN_AND_CHANGE_SAMPLE, DESIGN_SAMPLE, @@ -38,29 +38,29 @@ async def test_write_code_plan_and_change_an(mocker): root.instruct_content.model_dump = mock_code_plan_and_change mocker.patch("metagpt.actions.write_code_plan_and_change_an.WriteCodePlanAndChange.run", return_value=root) - requirement_doc = Document() - prd_docs = [Document()] - design_docs = [Document()] - tasks_docs = [Document()] + requirement = "New requirement" + prd_filename = "prd.md" + design_filename = "design.md" + task_filename = "task.md" code_plan_and_change_context = CodePlanAndChangeContext( - requirement_doc=requirement_doc, - prd_docs=prd_docs, - design_docs=design_docs, - tasks_docs=tasks_docs, + requirement=requirement, + prd_filename=prd_filename, + design_filename=design_filename, + task_filename=task_filename, ) - node = await WriteCodePlanAndChange(context=code_plan_and_change_context).run() + node = await WriteCodePlanAndChange(i_context=code_plan_and_change_context).run() - assert "Plan" in node.instruct_content.model_dump() + assert "Code Plan And Change" in node.instruct_content.model_dump() @pytest.mark.asyncio async def test_refine_code(mocker): - mocker.patch("metagpt.actions.write_code.WriteCodePlanAndChange.write_code", return_value=REFINED_CODE_SAMPLE) + mocker.patch.object(WriteCode, "_aask", return_value=REFINED_CODE_SAMPLE) prompt = REFINED_TEMPLATE.format( user_requirement=NEW_REQUIREMENT_SAMPLE, code_plan_and_change=CODE_PLAN_AND_CHANGE_SAMPLE, design=DESIGN_SAMPLE, - tasks=TASKS_SAMPLE, + task=TASKS_SAMPLE, code=REFINED_CODE_INPUT_SAMPLE, logs="", feedback="", @@ -68,5 +68,4 @@ async def test_refine_code(mocker): summary_log="", ) code = await WriteCode().write_code(prompt=prompt) - assert code assert "def" in code diff --git a/tests/metagpt/actions/test_write_code_review.py b/tests/metagpt/actions/test_write_code_review.py index 3343b42b4..a08dd07bc 100644 --- a/tests/metagpt/actions/test_write_code_review.py +++ b/tests/metagpt/actions/test_write_code_review.py @@ -12,28 +12,25 @@ @pytest.mark.asyncio -async def test_write_code_review(capfd): +async def test_write_code_review(capfd, context): + context.src_workspace = context.repo.workdir / "srcs" code = """ def add(a, b): return a + """ - context = CodingContext( + coding_context = CodingContext( filename="math.py", design_doc=Document(content="编写一个从a加b的函数,返回a+b"), code_doc=Document(content=code) ) - context = await WriteCodeReview(context=context).run() + await WriteCodeReview(i_context=coding_context, context=context).run() # 我们不能精确地预测生成的代码评审,但我们可以检查返回的是否为字符串 - assert isinstance(context.code_doc.content, str) - assert len(context.code_doc.content) > 0 + assert isinstance(coding_context.code_doc.content, str) + assert len(coding_context.code_doc.content) > 0 captured = capfd.readouterr() print(f"输出内容: {captured.out}") -# @pytest.mark.asyncio -# async def test_write_code_review_directly(): -# code = SEARCH_CODE_SAMPLE -# write_code_review = WriteCodeReview("write_code_review") -# review = await write_code_review.run(code) -# logger.info(review) +if __name__ == "__main__": + pytest.main([__file__, "-s"]) diff --git a/tests/metagpt/actions/test_write_docstring.py b/tests/metagpt/actions/test_write_docstring.py index a0fc46ebd..ebb7e8cb1 100644 --- a/tests/metagpt/actions/test_write_docstring.py +++ b/tests/metagpt/actions/test_write_docstring.py @@ -27,8 +27,8 @@ def greet(self): ], ids=["google", "numpy", "sphinx"], ) -async def test_write_docstring(style: str, part: str): - ret = await WriteDocstring().run(code, style=style) +async def test_write_docstring(style: str, part: str, context): + ret = await WriteDocstring(context=context).run(code, style=style) assert part in ret diff --git a/tests/metagpt/actions/test_write_prd.py b/tests/metagpt/actions/test_write_prd.py index 7317bba76..31d20018e 100644 --- a/tests/metagpt/actions/test_write_prd.py +++ b/tests/metagpt/actions/test_write_prd.py @@ -9,21 +9,19 @@ import pytest from metagpt.actions import UserRequirement, WritePRD -from metagpt.config import CONFIG -from metagpt.const import DOCS_FILE_REPO, PRDS_FILE_REPO, REQUIREMENT_FILENAME +from metagpt.const import REQUIREMENT_FILENAME from metagpt.logs import logger from metagpt.roles.product_manager import ProductManager from metagpt.roles.role import RoleReactMode from metagpt.schema import Message from metagpt.utils.common import any_to_str -from metagpt.utils.file_repository import FileRepository @pytest.mark.asyncio -async def test_write_prd(new_filename): - product_manager = ProductManager() +async def test_write_prd(new_filename, context): + product_manager = ProductManager(context=context) requirements = "开发一个基于大语言模型与私有知识库的搜索引擎,希望可以基于大语言模型进行搜索总结" - await FileRepository.save_file(filename=REQUIREMENT_FILENAME, content=requirements, relative_path=DOCS_FILE_REPO) + await context.repo.docs.save(filename=REQUIREMENT_FILENAME, content=requirements) product_manager.rc.react_mode = RoleReactMode.BY_ORDER prd = await product_manager.run(Message(content=requirements, cause_by=UserRequirement)) assert prd.cause_by == any_to_str(WritePRD) @@ -33,7 +31,7 @@ async def test_write_prd(new_filename): # Assert the prd is not None or empty assert prd is not None assert prd.content != "" - assert CONFIG.git_repo.new_file_repository(relative_path=PRDS_FILE_REPO).changed_files + assert product_manager.context.repo.docs.prd.changed_files if __name__ == "__main__": diff --git a/tests/metagpt/actions/test_write_prd_an.py b/tests/metagpt/actions/test_write_prd_an.py index 3bfb4d3be..378ce42c3 100644 --- a/tests/metagpt/actions/test_write_prd_an.py +++ b/tests/metagpt/actions/test_write_prd_an.py @@ -9,7 +9,8 @@ from openai._models import BaseModel from metagpt.actions.action_node import ActionNode -from metagpt.actions.write_prd_an import REFINED_PRD_NODE, REFINED_TEMPLATE +from metagpt.actions.write_prd import NEW_REQ_TEMPLATE +from metagpt.actions.write_prd_an import REFINED_PRD_NODE from metagpt.llm import LLM from tests.data.incremental_dev_project.mock import ( NEW_REQUIREMENT_SAMPLE, @@ -34,10 +35,9 @@ async def test_write_prd_an(mocker): root.instruct_content.model_dump = mock_refined_prd_json mocker.patch("metagpt.actions.write_prd_an.REFINED_PRD_NODE.fill", return_value=root) - prompt = REFINED_TEMPLATE.format( + prompt = NEW_REQ_TEMPLATE.format( requirements=NEW_REQUIREMENT_SAMPLE, old_prd=PRD_SAMPLE, - project_name="", ) node = await REFINED_PRD_NODE.fill(prompt, llm) diff --git a/tests/metagpt/actions/test_write_prd_review.py b/tests/metagpt/actions/test_write_prd_review.py index 9b3f0a285..8e1601b2e 100644 --- a/tests/metagpt/actions/test_write_prd_review.py +++ b/tests/metagpt/actions/test_write_prd_review.py @@ -11,7 +11,7 @@ @pytest.mark.asyncio -async def test_write_prd_review(): +async def test_write_prd_review(context): prd = """ Introduction: This is a new feature for our product. Goals: The goal is to improve user engagement. @@ -23,7 +23,7 @@ async def test_write_prd_review(): Timeline: The feature should be ready for testing in 1.5 months. """ - write_prd_review = WritePRDReview(name="write_prd_review") + write_prd_review = WritePRDReview(name="write_prd_review", context=context) prd_review = await write_prd_review.run(prd) diff --git a/tests/metagpt/actions/test_write_review.py b/tests/metagpt/actions/test_write_review.py index 2d188b720..0274a3532 100644 --- a/tests/metagpt/actions/test_write_review.py +++ b/tests/metagpt/actions/test_write_review.py @@ -9,7 +9,7 @@ from metagpt.actions.write_review import WriteReview -CONTEXT = """ +TEMPLATE_CONTEXT = """ { "Language": "zh_cn", "Programming Language": "Python", @@ -46,8 +46,8 @@ @pytest.mark.asyncio -async def test_write_review(): - write_review = WriteReview() - review = await write_review.run(CONTEXT) +async def test_write_review(context): + write_review = WriteReview(context=context) + review = await write_review.run(TEMPLATE_CONTEXT) assert review.instruct_content assert review.get("LGTM") in ["LGTM", "LBTM"] diff --git a/tests/metagpt/actions/test_write_teaching_plan.py b/tests/metagpt/actions/test_write_teaching_plan.py index 57a4f5eb0..bb68d4286 100644 --- a/tests/metagpt/actions/test_write_teaching_plan.py +++ b/tests/metagpt/actions/test_write_teaching_plan.py @@ -13,11 +13,11 @@ @pytest.mark.asyncio @pytest.mark.parametrize( - ("topic", "context"), + ("topic", "content"), [("Title", "Lesson 1: Learn to draw an apple."), ("Teaching Content", "Lesson 1: Learn to draw an apple.")], ) -async def test_write_teaching_plan_part(topic, context): - action = WriteTeachingPlanPart(topic=topic, context=context) +async def test_write_teaching_plan_part(topic, content, context): + action = WriteTeachingPlanPart(topic=topic, i_context=content, context=context) rsp = await action.run() assert rsp diff --git a/tests/metagpt/actions/test_write_test.py b/tests/metagpt/actions/test_write_test.py index 9649b9abb..9469dd312 100644 --- a/tests/metagpt/actions/test_write_test.py +++ b/tests/metagpt/actions/test_write_test.py @@ -13,7 +13,7 @@ @pytest.mark.asyncio -async def test_write_test(): +async def test_write_test(context): code = """ import random from typing import Tuple @@ -25,8 +25,8 @@ def __init__(self, position: Tuple[int, int]): def generate(self, max_y: int, max_x: int): self.position = (random.randint(1, max_y - 1), random.randint(1, max_x - 1)) """ - context = TestingContext(filename="food.py", code_doc=Document(filename="food.py", content=code)) - write_test = WriteTest(context=context) + testing_context = TestingContext(filename="food.py", code_doc=Document(filename="food.py", content=code)) + write_test = WriteTest(i_context=testing_context, context=context) context = await write_test.run() logger.info(context.model_dump_json()) @@ -39,12 +39,12 @@ def generate(self, max_y: int, max_x: int): @pytest.mark.asyncio -async def test_write_code_invalid_code(mocker): +async def test_write_code_invalid_code(mocker, context): # Mock the _aask method to return an invalid code string mocker.patch.object(WriteTest, "_aask", return_value="Invalid Code String") # Create an instance of WriteTest - write_test = WriteTest() + write_test = WriteTest(context=context) # Call the write_code method code = await write_test.write_code("Some prompt:") diff --git a/tests/metagpt/actions/test_write_tutorial.py b/tests/metagpt/actions/test_write_tutorial.py index 27a323b44..a83da1a1c 100644 --- a/tests/metagpt/actions/test_write_tutorial.py +++ b/tests/metagpt/actions/test_write_tutorial.py @@ -14,8 +14,8 @@ @pytest.mark.asyncio @pytest.mark.parametrize(("language", "topic"), [("English", "Write a tutorial about Python")]) -async def test_write_directory(language: str, topic: str): - ret = await WriteDirectory(language=language).run(topic=topic) +async def test_write_directory(language: str, topic: str, context): + ret = await WriteDirectory(language=language, context=context).run(topic=topic) assert isinstance(ret, dict) assert "title" in ret assert "directory" in ret @@ -29,8 +29,8 @@ async def test_write_directory(language: str, topic: str): ("language", "topic", "directory"), [("English", "Write a tutorial about Python", {"Introduction": ["What is Python?", "Why learn Python?"]})], ) -async def test_write_content(language: str, topic: str, directory: Dict): - ret = await WriteContent(language=language, directory=directory).run(topic=topic) +async def test_write_content(language: str, topic: str, directory: Dict, context): + ret = await WriteContent(language=language, directory=directory, context=context).run(topic=topic) assert isinstance(ret, str) assert list(directory.keys())[0] in ret for value in list(directory.values())[0]: diff --git a/tests/metagpt/environment/android_env/__init__.py b/tests/metagpt/environment/android_env/__init__.py new file mode 100644 index 000000000..2bcf8efd0 --- /dev/null +++ b/tests/metagpt/environment/android_env/__init__.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : diff --git a/tests/metagpt/environment/android_env/test_android_ext_env.py b/tests/metagpt/environment/android_env/test_android_ext_env.py new file mode 100644 index 000000000..c9dfc718b --- /dev/null +++ b/tests/metagpt/environment/android_env/test_android_ext_env.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : the unittest of AndroidExtEnv + +from pathlib import Path + +from metagpt.environment.android_env.android_ext_env import AndroidExtEnv +from metagpt.environment.android_env.const import ADB_EXEC_FAIL + + +def mock_device_shape(self, adb_cmd: str) -> str: + return "shape: 720x1080" + + +def mock_device_shape_invalid(self, adb_cmd: str) -> str: + return ADB_EXEC_FAIL + + +def mock_list_devices(self, adb_cmd: str) -> str: + return "devices\nemulator-5554" + + +def mock_get_screenshot(self, adb_cmd: str) -> str: + return "screenshot_xxxx-xx-xx" + + +def mock_get_xml(self, adb_cmd: str) -> str: + return "xml_xxxx-xx-xx" + + +def mock_write_read_operation(self, adb_cmd: str) -> str: + return "OK" + + +def test_android_ext_env(mocker): + device_id = "emulator-5554" + mocker.patch( + "metagpt.environment.android_env.android_ext_env.AndroidExtEnv.execute_adb_with_cmd", mock_device_shape + ) + + ext_env = AndroidExtEnv(device_id=device_id, screenshot_dir="/data2/", xml_dir="/data2/") + assert ext_env.adb_prefix == f"adb -s {device_id} " + assert ext_env.adb_prefix_shell == f"adb -s {device_id} shell " + assert ext_env.adb_prefix_si == f"adb -s {device_id} shell input " + + assert ext_env.device_shape == (720, 1080) + + mocker.patch( + "metagpt.environment.android_env.android_ext_env.AndroidExtEnv.execute_adb_with_cmd", mock_device_shape_invalid + ) + assert ext_env.device_shape == (0, 0) + + mocker.patch( + "metagpt.environment.android_env.android_ext_env.AndroidExtEnv.execute_adb_with_cmd", mock_list_devices + ) + assert ext_env.list_devices() == [device_id] + + mocker.patch( + "metagpt.environment.android_env.android_ext_env.AndroidExtEnv.execute_adb_with_cmd", mock_get_screenshot + ) + assert ext_env.get_screenshot("screenshot_xxxx-xx-xx", "/data/") == Path("/data/screenshot_xxxx-xx-xx.png") + + mocker.patch("metagpt.environment.android_env.android_ext_env.AndroidExtEnv.execute_adb_with_cmd", mock_get_xml) + assert ext_env.get_xml("xml_xxxx-xx-xx", "/data/") == Path("/data/xml_xxxx-xx-xx.xml") + + mocker.patch( + "metagpt.environment.android_env.android_ext_env.AndroidExtEnv.execute_adb_with_cmd", mock_write_read_operation + ) + res = "OK" + assert ext_env.system_back() == res + assert ext_env.system_tap(10, 10) == res + assert ext_env.user_input("test_input") == res + assert ext_env.user_longpress(10, 10) == res + assert ext_env.user_swipe(10, 10) == res + assert ext_env.user_swipe_to((10, 10), (20, 20)) == res diff --git a/tests/metagpt/environment/api/__init__.py b/tests/metagpt/environment/api/__init__.py new file mode 100644 index 000000000..2bcf8efd0 --- /dev/null +++ b/tests/metagpt/environment/api/__init__.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : diff --git a/tests/metagpt/environment/api/test_env_api.py b/tests/metagpt/environment/api/test_env_api.py new file mode 100644 index 000000000..53f98c0d3 --- /dev/null +++ b/tests/metagpt/environment/api/test_env_api.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : + +from metagpt.environment.api.env_api import EnvAPIRegistry + + +def test_env_api_registry(): + def test_func(): + pass + + env_api_registry = EnvAPIRegistry() + env_api_registry["test"] = test_func + + env_api_registry.get("test") == test_func diff --git a/tests/metagpt/environment/mincraft_env/__init__.py b/tests/metagpt/environment/mincraft_env/__init__.py new file mode 100644 index 000000000..2bcf8efd0 --- /dev/null +++ b/tests/metagpt/environment/mincraft_env/__init__.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : diff --git a/tests/metagpt/environment/mincraft_env/test_mincraft_ext_env.py b/tests/metagpt/environment/mincraft_env/test_mincraft_ext_env.py new file mode 100644 index 000000000..ad3376141 --- /dev/null +++ b/tests/metagpt/environment/mincraft_env/test_mincraft_ext_env.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : the unittest of MincraftExtEnv + + +from metagpt.environment.mincraft_env.const import MC_CKPT_DIR +from metagpt.environment.mincraft_env.mincraft_ext_env import MincraftExtEnv + + +def test_mincraft_ext_env(): + ext_env = MincraftExtEnv() + assert ext_env.server, f"{ext_env.server_host}:{ext_env.server_port}" + assert MC_CKPT_DIR.joinpath("skill/code").exists() + assert ext_env.warm_up.get("optional_inventory_items") == 7 diff --git a/tests/metagpt/environment/stanford_town_env/__init__.py b/tests/metagpt/environment/stanford_town_env/__init__.py new file mode 100644 index 000000000..2bcf8efd0 --- /dev/null +++ b/tests/metagpt/environment/stanford_town_env/__init__.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : diff --git a/tests/metagpt/environment/stanford_town_env/test_stanford_town_ext_env.py b/tests/metagpt/environment/stanford_town_env/test_stanford_town_ext_env.py new file mode 100644 index 000000000..3071f9deb --- /dev/null +++ b/tests/metagpt/environment/stanford_town_env/test_stanford_town_ext_env.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : the unittest of StanfordTownExtEnv + +from pathlib import Path + +from metagpt.environment.stanford_town_env.stanford_town_ext_env import ( + StanfordTownExtEnv, +) + +maze_asset_path = ( + Path(__file__).absolute().parent.joinpath("..", "..", "..", "data", "environment", "stanford_town", "the_ville") +) + + +def test_stanford_town_ext_env(): + ext_env = StanfordTownExtEnv(maze_asset_path=maze_asset_path) + + tile_coord = ext_env.turn_coordinate_to_tile((64, 64)) + assert tile_coord == (2, 2) + + tile = (58, 9) + assert len(ext_env.get_collision_maze()) == 100 + assert len(ext_env.get_address_tiles()) == 306 + assert ext_env.access_tile(tile=tile)["world"] == "the Ville" + assert ext_env.get_tile_path(tile=tile, level="world") == "the Ville" + assert len(ext_env.get_nearby_tiles(tile=tile, vision_r=5)) == 121 + + event = ("double studio:double studio:bedroom 2:bed", None, None, None) + ext_env.add_tiles_event(tile[1], tile[0], event=event) + ext_env.add_event_from_tile(event, tile) + assert len(ext_env.tiles[tile[1]][tile[0]]["events"]) == 1 + + ext_env.turn_event_from_tile_idle(event, tile) + + ext_env.remove_event_from_tile(event, tile) + assert len(ext_env.tiles[tile[1]][tile[0]]["events"]) == 0 + + ext_env.remove_subject_events_from_tile(subject=event[0], tile=tile) + assert len(ext_env.tiles[tile[1]][tile[0]]["events"]) == 0 diff --git a/tests/metagpt/environment/test_base_env.py b/tests/metagpt/environment/test_base_env.py new file mode 100644 index 000000000..fd73679d8 --- /dev/null +++ b/tests/metagpt/environment/test_base_env.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : the unittest of ExtEnv&Env + +import pytest + +from metagpt.environment.api.env_api import EnvAPIAbstract +from metagpt.environment.base_env import ( + Environment, + env_read_api_registry, + env_write_api_registry, + mark_as_readable, + mark_as_writeable, +) + + +class ForTestEnv(Environment): + value: int = 0 + + @mark_as_readable + def read_api_no_param(self): + return self.value + + @mark_as_readable + def read_api(self, a: int, b: int): + return a + b + + @mark_as_writeable + def write_api(self, a: int, b: int): + self.value = a + b + + @mark_as_writeable + async def async_read_api(self, a: int, b: int): + return a + b + + +@pytest.mark.asyncio +async def test_ext_env(): + env = ForTestEnv() + assert len(env_read_api_registry) > 0 + assert len(env_write_api_registry) > 0 + + apis = env.get_all_available_apis(mode="read") + assert len(apis) > 0 + assert len(apis["read_api"]) == 3 + + _ = await env.step(EnvAPIAbstract(api_name="write_api", kwargs={"a": 5, "b": 10})) + assert env.value == 15 + + with pytest.raises(ValueError): + await env.observe("not_exist_api") + + assert await env.observe("read_api_no_param") == 15 + assert await env.observe(EnvAPIAbstract(api_name="read_api", kwargs={"a": 5, "b": 5})) == 10 diff --git a/tests/metagpt/environment/werewolf_env/__init__.py b/tests/metagpt/environment/werewolf_env/__init__.py new file mode 100644 index 000000000..2bcf8efd0 --- /dev/null +++ b/tests/metagpt/environment/werewolf_env/__init__.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : diff --git a/tests/metagpt/environment/werewolf_env/test_werewolf_ext_env.py b/tests/metagpt/environment/werewolf_env/test_werewolf_ext_env.py new file mode 100644 index 000000000..0694c5c3d --- /dev/null +++ b/tests/metagpt/environment/werewolf_env/test_werewolf_ext_env.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : the unittest of WerewolfExtEnv + +from metagpt.environment.werewolf_env.werewolf_ext_env import RoleState, WerewolfExtEnv +from metagpt.roles.role import Role + + +class Werewolf(Role): + profile: str = "Werewolf" + + +class Villager(Role): + profile: str = "Villager" + + +class Witch(Role): + profile: str = "Witch" + + +class Guard(Role): + profile: str = "Guard" + + +def test_werewolf_ext_env(): + players_state = { + "Player0": ("Werewolf", RoleState.ALIVE), + "Player1": ("Werewolf", RoleState.ALIVE), + "Player2": ("Villager", RoleState.ALIVE), + "Player3": ("Witch", RoleState.ALIVE), + "Player4": ("Guard", RoleState.ALIVE), + } + ext_env = WerewolfExtEnv(players_state=players_state, step_idx=4, special_role_players=["Player3", "Player4"]) + + assert len(ext_env.living_players) == 5 + assert len(ext_env.special_role_players) == 2 + assert len(ext_env.werewolf_players) == 2 + + curr_instr = ext_env.curr_step_instruction() + assert ext_env.step_idx == 5 + assert "Werewolves, please open your eyes" in curr_instr["content"] + + # current step_idx = 5 + ext_env.wolf_kill_someone(wolf=Role(name="Player10"), player_name="Player4") + ext_env.wolf_kill_someone(wolf=Werewolf(name="Player0"), player_name="Player4") + ext_env.wolf_kill_someone(wolf=Werewolf(name="Player1"), player_name="Player4") + assert ext_env.player_hunted == "Player4" + assert len(ext_env.living_players) == 5 # hunted but can be saved by witch + + for idx in range(13): + _ = ext_env.curr_step_instruction() + + # current step_idx = 18 + assert ext_env.step_idx == 18 + ext_env.vote_kill_someone(voteer=Werewolf(name="Player0"), player_name="Player2") + ext_env.vote_kill_someone(voteer=Werewolf(name="Player1"), player_name="Player3") + ext_env.vote_kill_someone(voteer=Villager(name="Player2"), player_name="Player3") + ext_env.vote_kill_someone(voteer=Witch(name="Player3"), player_name="Player4") + ext_env.vote_kill_someone(voteer=Guard(name="Player4"), player_name="Player2") + assert ext_env.player_current_dead == "Player2" + assert len(ext_env.living_players) == 4 + + player_names = ["Player0", "Player2"] + assert ext_env.get_players_state(player_names) == dict(zip(player_names, [RoleState.ALIVE, RoleState.KILLED])) diff --git a/tests/metagpt/learn/test_google_search.py b/tests/metagpt/learn/test_google_search.py index da32e8923..70a146878 100644 --- a/tests/metagpt/learn/test_google_search.py +++ b/tests/metagpt/learn/test_google_search.py @@ -1,27 +1,21 @@ -import asyncio - +import pytest from pydantic import BaseModel from metagpt.learn.google_search import google_search +from metagpt.tools import SearchEngineType -async def mock_google_search(): +@pytest.mark.asyncio +async def test_google_search(search_engine_mocker): class Input(BaseModel): input: str inputs = [{"input": "ai agent"}] - for i in inputs: seed = Input(**i) - result = await google_search(seed.input) + result = await google_search( + seed.input, + engine=SearchEngineType.SERPER_GOOGLE, + api_key="mock-serper-key", + ) assert result != "" - - -def test_suite(): - loop = asyncio.get_event_loop() - task = loop.create_task(mock_google_search()) - loop.run_until_complete(task) - - -if __name__ == "__main__": - test_suite() diff --git a/tests/metagpt/learn/test_skill_loader.py b/tests/metagpt/learn/test_skill_loader.py index 529a490c8..f1952c275 100644 --- a/tests/metagpt/learn/test_skill_loader.py +++ b/tests/metagpt/learn/test_skill_loader.py @@ -10,13 +10,12 @@ import pytest -from metagpt.config import CONFIG from metagpt.learn.skill_loader import SkillsDeclaration @pytest.mark.asyncio -async def test_suite(): - CONFIG.agent_skills = [ +async def test_suite(context): + context.kwargs.agent_skills = [ {"id": 1, "name": "text_to_speech", "type": "builtin", "config": {}, "enabled": True}, {"id": 2, "name": "text_to_image", "type": "builtin", "config": {}, "enabled": True}, {"id": 3, "name": "ai_call", "type": "builtin", "config": {}, "enabled": True}, @@ -27,7 +26,7 @@ async def test_suite(): ] pathname = Path(__file__).parent / "../../../docs/.well-known/skills.yaml" loader = await SkillsDeclaration.load(skill_yaml_file_name=pathname) - skills = loader.get_skill_list() + skills = loader.get_skill_list(context=context) assert skills assert len(skills) >= 3 for desc, name in skills.items(): diff --git a/tests/metagpt/learn/test_text_to_embedding.py b/tests/metagpt/learn/test_text_to_embedding.py index cbd1bbbbc..f50f6a7aa 100644 --- a/tests/metagpt/learn/test_text_to_embedding.py +++ b/tests/metagpt/learn/test_text_to_embedding.py @@ -6,19 +6,33 @@ @File : test_text_to_embedding.py @Desc : Unit tests. """ +import json +from pathlib import Path import pytest -from metagpt.config import CONFIG +from metagpt.config2 import Config from metagpt.learn.text_to_embedding import text_to_embedding +from metagpt.utils.common import aread @pytest.mark.asyncio -async def test_text_to_embedding(): +async def test_text_to_embedding(mocker): + # mock + config = Config.default() + mock_post = mocker.patch("aiohttp.ClientSession.post") + mock_response = mocker.AsyncMock() + mock_response.status = 200 + data = await aread(Path(__file__).parent / "../../data/openai/embedding.json") + mock_response.json.return_value = json.loads(data) + mock_post.return_value.__aenter__.return_value = mock_response + config.get_openai_llm().proxy = mocker.PropertyMock(return_value="http://mock.proxy") + # Prerequisites - assert CONFIG.OPENAI_API_KEY + assert config.get_openai_llm().api_key + assert config.get_openai_llm().proxy - v = await text_to_embedding(text="Panda emoji") + v = await text_to_embedding(text="Panda emoji", config=config) assert len(v.data) > 0 diff --git a/tests/metagpt/learn/test_text_to_image.py b/tests/metagpt/learn/test_text_to_image.py index 1485df5c6..d3272dadd 100644 --- a/tests/metagpt/learn/test_text_to_image.py +++ b/tests/metagpt/learn/test_text_to_image.py @@ -6,11 +6,13 @@ @File : test_text_to_image.py @Desc : Unit tests. """ +import base64 - +import openai import pytest +from pydantic import BaseModel -from metagpt.config import CONFIG +from metagpt.config2 import Config from metagpt.learn.text_to_image import text_to_image from metagpt.tools.metagpt_text_to_image import MetaGPTText2Image from metagpt.tools.openai_text_to_image import OpenAIText2Image @@ -24,23 +26,37 @@ async def test_text_to_image(mocker): mocker.patch.object(OpenAIText2Image, "text_2_image", return_value=b"mock OpenAIText2Image") mocker.patch.object(S3, "cache", return_value="http://mock/s3") - # Prerequisites - assert CONFIG.METAGPT_TEXT_TO_IMAGE_MODEL_URL - assert CONFIG.OPENAI_API_KEY + config = Config.default() + assert config.metagpt_tti_url - data = await text_to_image("Panda emoji", size_type="512x512") + data = await text_to_image("Panda emoji", size_type="512x512", config=config) assert "base64" in data or "http" in data - # Mock session env - old_options = CONFIG.options.copy() - new_options = old_options.copy() - new_options["METAGPT_TEXT_TO_IMAGE_MODEL_URL"] = None - CONFIG.set_context(new_options) - try: - data = await text_to_image("Panda emoji", size_type="512x512") - assert "base64" in data or "http" in data - finally: - CONFIG.set_context(old_options) + +@pytest.mark.asyncio +async def test_openai_text_to_image(mocker): + # mocker + mock_url = mocker.Mock() + mock_url.url.return_value = "http://mock.com/0.png" + + class _MockData(BaseModel): + data: list + + mock_data = _MockData(data=[mock_url]) + mocker.patch.object(openai.resources.images.AsyncImages, "generate", return_value=mock_data) + mock_post = mocker.patch("aiohttp.ClientSession.get") + mock_response = mocker.AsyncMock() + mock_response.status = 200 + mock_response.read.return_value = base64.b64encode(b"success") + mock_post.return_value.__aenter__.return_value = mock_response + mocker.patch.object(S3, "cache", return_value="http://mock.s3.com/0.png") + + config = Config.default() + config.metagpt_tti_url = None + assert config.get_openai_llm() + + data = await text_to_image("Panda emoji", size_type="512x512", config=config) + assert "base64" in data or "http" in data if __name__ == "__main__": diff --git a/tests/metagpt/learn/test_text_to_speech.py b/tests/metagpt/learn/test_text_to_speech.py index aca08b9a2..f01e5d132 100644 --- a/tests/metagpt/learn/test_text_to_speech.py +++ b/tests/metagpt/learn/test_text_to_speech.py @@ -8,35 +8,65 @@ """ import pytest +from azure.cognitiveservices.speech import ResultReason, SpeechSynthesizer -from metagpt.config import CONFIG +from metagpt.config2 import Config from metagpt.learn.text_to_speech import text_to_speech +from metagpt.tools.iflytek_tts import IFlyTekTTS +from metagpt.utils.s3 import S3 @pytest.mark.asyncio -async def test_text_to_speech(): +async def test_azure_text_to_speech(mocker): + # mock + config = Config.default() + config.iflytek_api_key = None + config.iflytek_api_secret = None + config.iflytek_app_id = None + mock_result = mocker.Mock() + mock_result.audio_data = b"mock audio data" + mock_result.reason = ResultReason.SynthesizingAudioCompleted + mock_data = mocker.Mock() + mock_data.get.return_value = mock_result + mocker.patch.object(SpeechSynthesizer, "speak_ssml_async", return_value=mock_data) + mocker.patch.object(S3, "cache", return_value="http://mock.s3.com/1.wav") + # Prerequisites - assert CONFIG.IFLYTEK_APP_ID - assert CONFIG.IFLYTEK_API_KEY - assert CONFIG.IFLYTEK_API_SECRET - assert CONFIG.AZURE_TTS_SUBSCRIPTION_KEY and CONFIG.AZURE_TTS_SUBSCRIPTION_KEY != "YOUR_API_KEY" - assert CONFIG.AZURE_TTS_REGION + assert not config.iflytek_app_id + assert not config.iflytek_api_key + assert not config.iflytek_api_secret + assert config.azure_tts_subscription_key and config.azure_tts_subscription_key != "YOUR_API_KEY" + assert config.azure_tts_region + config.copy() # test azure - data = await text_to_speech("panda emoji") + data = await text_to_speech("panda emoji", config=config) assert "base64" in data or "http" in data - # test iflytek - ## Mock session env - old_options = CONFIG.options.copy() - new_options = old_options.copy() - new_options["AZURE_TTS_SUBSCRIPTION_KEY"] = "" - CONFIG.set_context(new_options) - try: - data = await text_to_speech("panda emoji") - assert "base64" in data or "http" in data - finally: - CONFIG.set_context(old_options) + +@pytest.mark.asyncio +async def test_iflytek_text_to_speech(mocker): + # mock + config = Config.default() + config.azure_tts_subscription_key = None + config.azure_tts_region = None + mocker.patch.object(IFlyTekTTS, "synthesize_speech", return_value=None) + mock_data = mocker.AsyncMock() + mock_data.read.return_value = b"mock iflytek" + mock_reader = mocker.patch("aiofiles.open") + mock_reader.return_value.__aenter__.return_value = mock_data + mocker.patch.object(S3, "cache", return_value="http://mock.s3.com/1.mp3") + + # Prerequisites + assert config.iflytek_app_id + assert config.iflytek_api_key + assert config.iflytek_api_secret + assert not config.azure_tts_subscription_key or config.azure_tts_subscription_key == "YOUR_API_KEY" + assert not config.azure_tts_region + + # test azure + data = await text_to_speech("panda emoji", config=config) + assert "base64" in data or "http" in data if __name__ == "__main__": diff --git a/tests/metagpt/memory/test_brain_memory.py b/tests/metagpt/memory/test_brain_memory.py index 32dcd672a..72ffcc538 100644 --- a/tests/metagpt/memory/test_brain_memory.py +++ b/tests/metagpt/memory/test_brain_memory.py @@ -8,7 +8,6 @@ import pytest -from metagpt.config import LLMProviderEnum from metagpt.llm import LLM from metagpt.memory.brain_memory import BrainMemory from metagpt.schema import Message @@ -46,7 +45,7 @@ def test_extract_info(input, tag, val): @pytest.mark.asyncio -@pytest.mark.parametrize("llm", [LLM(provider=LLMProviderEnum.OPENAI), LLM(provider=LLMProviderEnum.METAGPT)]) +@pytest.mark.parametrize("llm", [LLM()]) async def test_memory_llm(llm): memory = BrainMemory() for i in range(500): diff --git a/tests/metagpt/memory/test_longterm_memory.py b/tests/metagpt/memory/test_longterm_memory.py index 0f7a4fac4..5c71ddd13 100644 --- a/tests/metagpt/memory/test_longterm_memory.py +++ b/tests/metagpt/memory/test_longterm_memory.py @@ -2,7 +2,6 @@ # -*- coding: utf-8 -*- """ @Desc : unittest of `metagpt/memory/longterm_memory.py` -@Modified By: mashenquan, 2023/8/20. Remove global configuration `CONFIG`, enable configuration support for business isolation. """ import os @@ -10,17 +9,15 @@ import pytest from metagpt.actions import UserRequirement -from metagpt.config import CONFIG +from metagpt.config2 import config from metagpt.memory.longterm_memory import LongTermMemory from metagpt.roles.role import RoleContext from metagpt.schema import Message +os.environ.setdefault("OPENAI_API_KEY", config.get_openai_llm().api_key) -def test_ltm_search(): - assert hasattr(CONFIG, "long_term_memory") is True - os.environ.setdefault("OPENAI_API_KEY", CONFIG.openai_api_key) - assert len(CONFIG.openai_api_key) > 20 +def test_ltm_search(): role_id = "UTUserLtm(Product Manager)" from metagpt.environment import Environment diff --git a/tests/metagpt/memory/test_memory.py b/tests/metagpt/memory/test_memory.py index 36d7ad488..a072b61de 100644 --- a/tests/metagpt/memory/test_memory.py +++ b/tests/metagpt/memory/test_memory.py @@ -32,7 +32,7 @@ def test_memory(): messages = memory.get_by_action(UserRequirement) assert len(messages) == 2 - messages = memory.get_by_actions([UserRequirement]) + messages = memory.get_by_actions({UserRequirement}) assert len(messages) == 2 messages = memory.try_remember("test message") diff --git a/tests/metagpt/memory/test_memory_storage.py b/tests/metagpt/memory/test_memory_storage.py index 0eb1069d5..e82a82fc8 100644 --- a/tests/metagpt/memory/test_memory_storage.py +++ b/tests/metagpt/memory/test_memory_storage.py @@ -11,12 +11,12 @@ from metagpt.actions import UserRequirement, WritePRD from metagpt.actions.action_node import ActionNode -from metagpt.config import CONFIG +from metagpt.config2 import config from metagpt.const import DATA_PATH from metagpt.memory.memory_storage import MemoryStorage from metagpt.schema import Message -os.environ.setdefault("OPENAI_API_KEY", CONFIG.openai_api_key) +os.environ.setdefault("OPENAI_API_KEY", config.get_openai_llm().api_key) def test_idea_message(): diff --git a/tests/metagpt/provider/mock_llm_config.py b/tests/metagpt/provider/mock_llm_config.py new file mode 100644 index 000000000..e2f626a6a --- /dev/null +++ b/tests/metagpt/provider/mock_llm_config.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2024/1/8 17:03 +@Author : alexanderwu +@File : mock_llm_config.py +""" + +from metagpt.configs.llm_config import LLMConfig + +mock_llm_config = LLMConfig( + llm_type="mock", + api_key="mock_api_key", + base_url="mock_base_url", + app_id="mock_app_id", + api_secret="mock_api_secret", + domain="mock_domain", +) + + +mock_llm_config_proxy = LLMConfig( + llm_type="mock", + api_key="mock_api_key", + base_url="mock_base_url", + proxy="http://localhost:8080", +) + + +mock_llm_config_azure = LLMConfig( + llm_type="azure", + api_version="2023-09-01-preview", + api_key="mock_api_key", + base_url="mock_base_url", + proxy="http://localhost:8080", +) + + +mock_llm_config_zhipu = LLMConfig( + llm_type="zhipu", + api_key="mock_api_key.zhipu", + base_url="mock_base_url", + model="mock_zhipu_model", + proxy="http://localhost:8080", +) diff --git a/tests/metagpt/provider/test_anthropic_api.py b/tests/metagpt/provider/test_anthropic_api.py index 4410717a9..6962ab064 100644 --- a/tests/metagpt/provider/test_anthropic_api.py +++ b/tests/metagpt/provider/test_anthropic_api.py @@ -6,10 +6,8 @@ import pytest from anthropic.resources.completions import Completion -from metagpt.config import CONFIG from metagpt.provider.anthropic_api import Claude2 - -CONFIG.anthropic_api_key = "xxx" +from tests.metagpt.provider.mock_llm_config import mock_llm_config prompt = "who are you" resp = "I'am Claude2" @@ -25,10 +23,10 @@ async def mock_anthropic_acompletions_create(self, model: str, prompt: str, max_ def test_claude2_ask(mocker): mocker.patch("anthropic.resources.completions.Completions.create", mock_anthropic_completions_create) - assert resp == Claude2().ask(prompt) + assert resp == Claude2(mock_llm_config).ask(prompt) @pytest.mark.asyncio async def test_claude2_aask(mocker): mocker.patch("anthropic.resources.completions.AsyncCompletions.create", mock_anthropic_acompletions_create) - assert resp == await Claude2().aask(prompt) + assert resp == await Claude2(mock_llm_config).aask(prompt) diff --git a/tests/metagpt/provider/test_azure_llm.py b/tests/metagpt/provider/test_azure_llm.py new file mode 100644 index 000000000..51e051145 --- /dev/null +++ b/tests/metagpt/provider/test_azure_llm.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : + +from metagpt.provider import AzureOpenAILLM +from tests.metagpt.provider.mock_llm_config import mock_llm_config_azure + + +def test_azure_llm(): + llm = AzureOpenAILLM(mock_llm_config_azure) + kwargs = llm._make_client_kwargs() + assert kwargs["azure_endpoint"] == mock_llm_config_azure.base_url diff --git a/tests/metagpt/provider/test_azure_openai_api.py b/tests/metagpt/provider/test_azure_openai_api.py deleted file mode 100644 index f36740e65..000000000 --- a/tests/metagpt/provider/test_azure_openai_api.py +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# @Desc : - - -from metagpt.config import CONFIG -from metagpt.provider.azure_openai_api import AzureOpenAILLM - -CONFIG.OPENAI_API_VERSION = "xx" -CONFIG.openai_proxy = "http://127.0.0.1:80" # fake value - - -def test_azure_openai_api(): - _ = AzureOpenAILLM() diff --git a/tests/metagpt/provider/test_base_gpt_api.py b/tests/metagpt/provider/test_base_llm.py similarity index 93% rename from tests/metagpt/provider/test_base_gpt_api.py rename to tests/metagpt/provider/test_base_llm.py index 3443b5078..cc781f78a 100644 --- a/tests/metagpt/provider/test_base_gpt_api.py +++ b/tests/metagpt/provider/test_base_llm.py @@ -8,6 +8,7 @@ import pytest +from metagpt.configs.llm_config import LLMConfig from metagpt.provider.base_llm import BaseLLM from metagpt.schema import Message @@ -28,6 +29,9 @@ class MockBaseLLM(BaseLLM): + def __init__(self, config: LLMConfig = None): + pass + def completion(self, messages: list[dict], timeout=3): return default_chat_resp @@ -102,5 +106,5 @@ async def test_async_base_llm(): resp = await base_llm.aask_batch([prompt_msg]) assert resp == resp_content - resp = await base_llm.aask_code([prompt_msg]) - assert resp == resp_content + # resp = await base_llm.aask_code([prompt_msg]) + # assert resp == resp_content diff --git a/tests/metagpt/provider/test_fireworks_api.py b/tests/metagpt/provider/test_fireworks_llm.py similarity index 96% rename from tests/metagpt/provider/test_fireworks_api.py rename to tests/metagpt/provider/test_fireworks_llm.py index d48686eaa..66b55e5b2 100644 --- a/tests/metagpt/provider/test_fireworks_api.py +++ b/tests/metagpt/provider/test_fireworks_llm.py @@ -13,17 +13,13 @@ from openai.types.chat.chat_completion_chunk import ChoiceDelta from openai.types.completion_usage import CompletionUsage -from metagpt.config import CONFIG from metagpt.provider.fireworks_api import ( MODEL_GRADE_TOKEN_COSTS, FireworksCostManager, FireworksLLM, ) from metagpt.utils.cost_manager import Costs - -CONFIG.fireworks_api_key = "xxx" -CONFIG.max_budget = 10 -CONFIG.calc_usage = True +from tests.metagpt.provider.mock_llm_config import mock_llm_config resp_content = "I'm fireworks" default_resp = ChatCompletion( @@ -92,7 +88,7 @@ async def __aiter__(self): async def test_fireworks_acompletion(mocker): mocker.patch("openai.resources.chat.completions.AsyncCompletions.create", mock_openai_acompletions_create) - fireworks_gpt = FireworksLLM() + fireworks_gpt = FireworksLLM(mock_llm_config) fireworks_gpt.model = "llama-v2-13b-chat" fireworks_gpt._update_costs( diff --git a/tests/metagpt/provider/test_google_gemini_api.py b/tests/metagpt/provider/test_google_gemini_api.py index ffd10df7f..404ae1e90 100644 --- a/tests/metagpt/provider/test_google_gemini_api.py +++ b/tests/metagpt/provider/test_google_gemini_api.py @@ -9,10 +9,8 @@ from google.ai import generativelanguage as glm from google.generativeai.types import content_types -from metagpt.config import CONFIG from metagpt.provider.google_gemini_api import GeminiLLM - -CONFIG.gemini_api_key = "xx" +from tests.metagpt.provider.mock_llm_config import mock_llm_config @dataclass @@ -62,7 +60,7 @@ async def test_gemini_acompletion(mocker): mock_gemini_generate_content_async, ) - gemini_gpt = GeminiLLM() + gemini_gpt = GeminiLLM(mock_llm_config) assert gemini_gpt._user_msg(prompt_msg) == {"role": "user", "parts": [prompt_msg]} assert gemini_gpt._assistant_msg(prompt_msg) == {"role": "model", "parts": [prompt_msg]} diff --git a/tests/metagpt/provider/test_human_provider.py b/tests/metagpt/provider/test_human_provider.py index 3f63410c0..97ed8bae6 100644 --- a/tests/metagpt/provider/test_human_provider.py +++ b/tests/metagpt/provider/test_human_provider.py @@ -5,6 +5,7 @@ import pytest from metagpt.provider.human_provider import HumanProvider +from tests.metagpt.provider.mock_llm_config import mock_llm_config resp_content = "test" resp_exit = "exit" @@ -13,7 +14,7 @@ @pytest.mark.asyncio async def test_async_human_provider(mocker): mocker.patch("builtins.input", lambda _: resp_content) - human_provider = HumanProvider() + human_provider = HumanProvider(mock_llm_config) resp = human_provider.ask(resp_content) assert resp == resp_content diff --git a/tests/metagpt/provider/test_metagpt_api.py b/tests/metagpt/provider/test_metagpt_api.py deleted file mode 100644 index 1f00cb653..000000000 --- a/tests/metagpt/provider/test_metagpt_api.py +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -@Time : 2023/12/28 -@Author : mashenquan -@File : test_metagpt_api.py -""" -from metagpt.config import LLMProviderEnum -from metagpt.llm import LLM - - -def test_llm(): - llm = LLM(provider=LLMProviderEnum.METAGPT) - assert llm diff --git a/tests/metagpt/provider/test_metagpt_llm_api.py b/tests/metagpt/provider/test_metagpt_llm.py similarity index 63% rename from tests/metagpt/provider/test_metagpt_llm_api.py rename to tests/metagpt/provider/test_metagpt_llm.py index 8fce6b6b0..0263fe508 100644 --- a/tests/metagpt/provider/test_metagpt_llm_api.py +++ b/tests/metagpt/provider/test_metagpt_llm.py @@ -3,13 +3,14 @@ """ @Time : 2023/8/30 @Author : mashenquan -@File : test_metagpt_llm_api.py +@File : test_metagpt_llm.py """ from metagpt.provider.metagpt_api import MetaGPTLLM +from tests.metagpt.provider.mock_llm_config import mock_llm_config def test_metagpt(): - llm = MetaGPTLLM() + llm = MetaGPTLLM(mock_llm_config) assert llm diff --git a/tests/metagpt/provider/test_ollama_api.py b/tests/metagpt/provider/test_ollama_api.py index 1c604768e..5d942598b 100644 --- a/tests/metagpt/provider/test_ollama_api.py +++ b/tests/metagpt/provider/test_ollama_api.py @@ -7,8 +7,8 @@ import pytest -from metagpt.config import CONFIG from metagpt.provider.ollama_api import OllamaLLM +from tests.metagpt.provider.mock_llm_config import mock_llm_config prompt_msg = "who are you" messages = [{"role": "user", "content": prompt_msg}] @@ -16,9 +16,6 @@ resp_content = "I'm ollama" default_resp = {"message": {"role": "assistant", "content": resp_content}} -CONFIG.ollama_api_base = "http://xxx" -CONFIG.max_budget = 10 - async def mock_ollama_arequest(self, stream: bool = False, **kwargs) -> Tuple[Any, Any, bool]: if stream: @@ -44,7 +41,7 @@ async def __aiter__(self): async def test_gemini_acompletion(mocker): mocker.patch("metagpt.provider.general_api_requestor.GeneralAPIRequestor.arequest", mock_ollama_arequest) - ollama_gpt = OllamaLLM() + ollama_gpt = OllamaLLM(mock_llm_config) resp = await ollama_gpt.acompletion(messages) assert resp["message"]["content"] == default_resp["message"]["content"] diff --git a/tests/metagpt/provider/test_open_llm_api.py b/tests/metagpt/provider/test_open_llm_api.py index 85069c5e1..fc7b510cc 100644 --- a/tests/metagpt/provider/test_open_llm_api.py +++ b/tests/metagpt/provider/test_open_llm_api.py @@ -13,12 +13,9 @@ from openai.types.chat.chat_completion_chunk import ChoiceDelta from openai.types.completion_usage import CompletionUsage -from metagpt.config import CONFIG from metagpt.provider.open_llm_api import OpenLLM from metagpt.utils.cost_manager import Costs - -CONFIG.max_budget = 10 -CONFIG.calc_usage = True +from tests.metagpt.provider.mock_llm_config import mock_llm_config resp_content = "I'm llama2" default_resp = ChatCompletion( @@ -71,7 +68,7 @@ async def __aiter__(self): async def test_openllm_acompletion(mocker): mocker.patch("openai.resources.chat.completions.AsyncCompletions.create", mock_openai_acompletions_create) - openllm_gpt = OpenLLM() + openllm_gpt = OpenLLM(mock_llm_config) openllm_gpt.model = "llama-v2-13b-chat" openllm_gpt._update_costs(usage=CompletionUsage(prompt_tokens=100, completion_tokens=100, total_tokens=200)) diff --git a/tests/metagpt/provider/test_openai.py b/tests/metagpt/provider/test_openai.py index 6166a82de..d6aa04c7b 100644 --- a/tests/metagpt/provider/test_openai.py +++ b/tests/metagpt/provider/test_openai.py @@ -1,113 +1,123 @@ -from unittest.mock import Mock +import json import pytest - -from metagpt.config import CONFIG -from metagpt.provider.openai_api import OpenAILLM -from metagpt.schema import UserMessage - -CONFIG.openai_proxy = None - - -@pytest.mark.asyncio -async def test_aask_code(): - llm = OpenAILLM() - msg = [{"role": "user", "content": "Write a python hello world code."}] - rsp = await llm.aask_code(msg) # -> {'language': 'python', 'code': "print('Hello, World!')"} - assert "language" in rsp - assert "code" in rsp - assert len(rsp["code"]) > 0 +from openai.types.chat import ( + ChatCompletion, + ChatCompletionMessage, + ChatCompletionMessageToolCall, +) +from openai.types.chat.chat_completion import Choice +from openai.types.chat.chat_completion_message_tool_call import Function +from PIL import Image + +from metagpt.const import TEST_DATA_PATH +from metagpt.llm import LLM +from metagpt.logs import logger +from metagpt.provider import OpenAILLM +from tests.metagpt.provider.mock_llm_config import ( + mock_llm_config, + mock_llm_config_proxy, +) @pytest.mark.asyncio -async def test_aask_code_str(): - llm = OpenAILLM() - msg = "Write a python hello world code." - rsp = await llm.aask_code(msg) # -> {'language': 'python', 'code': "print('Hello, World!')"} - assert "language" in rsp - assert "code" in rsp - assert len(rsp["code"]) > 0 +async def test_text_to_speech(): + llm = LLM() + resp = await llm.atext_to_speech( + model="tts-1", + voice="alloy", + input="人生说起来长,但直到一个岁月回头看,许多事件仅是仓促的。一段一段拼凑一起,合成了人生。苦难当头时,当下不免觉得是折磨;回头看,也不够是一段短短的人生旅程。", + ) + assert 200 == resp.response.status_code @pytest.mark.asyncio -async def test_aask_code_Message(): - llm = OpenAILLM() - msg = UserMessage("Write a python hello world code.") - rsp = await llm.aask_code(msg) # -> {'language': 'python', 'code': "print('Hello, World!')"} - assert "language" in rsp - assert "code" in rsp - assert len(rsp["code"]) > 0 +async def test_speech_to_text(): + llm = LLM() + audio_file = open(f"{TEST_DATA_PATH}/audio/hello.mp3", "rb") + resp = await llm.aspeech_to_text(file=audio_file, model="whisper-1") + assert "你好" == resp.text + + +@pytest.fixture +def tool_calls_rsp(): + function_rsps = [ + Function(arguments='{\n"language": "python",\n"code": "print(\'hello world\')"}', name="execute"), + ] + tool_calls = [ + ChatCompletionMessageToolCall(type="function", id=f"call_{i}", function=f) for i, f in enumerate(function_rsps) + ] + messages = [ChatCompletionMessage(content=None, role="assistant", tool_calls=[t]) for t in tool_calls] + # 添加一个纯文本响应 + messages.append( + ChatCompletionMessage(content="Completed a python code for hello world!", role="assistant", tool_calls=None) + ) + # 添加 openai tool calls respond bug, code 出现在ChatCompletionMessage.content中 + messages.extend( + [ + ChatCompletionMessage(content="```python\nprint('hello world')```", role="assistant", tool_calls=None), + ] + ) + choices = [ + Choice(finish_reason="tool_calls", logprobs=None, index=i, message=msg) for i, msg in enumerate(messages) + ] + return [ + ChatCompletion(id=str(i), choices=[c], created=i, model="gpt-4", object="chat.completion") + for i, c in enumerate(choices) + ] + + +@pytest.fixture +def json_decode_error(): + function_rsp = Function(arguments='{\n"language": \'python\',\n"code": "print(\'hello world\')"}', name="execute") + tool_calls = [ChatCompletionMessageToolCall(type="function", id=f"call_{0}", function=function_rsp)] + message = ChatCompletionMessage(content=None, role="assistant", tool_calls=tool_calls) + choices = [Choice(finish_reason="tool_calls", logprobs=None, index=0, message=message)] + return ChatCompletion(id="0", choices=choices, created=0, model="gpt-4", object="chat.completion") class TestOpenAI: - @pytest.fixture - def config(self): - return Mock( - openai_api_key="test_key", - OPENAI_API_KEY="test_key", - openai_base_url="test_url", - OPENAI_BASE_URL="test_url", - openai_proxy=None, - openai_api_type="other", - ) - - @pytest.fixture - def config_azure(self): - return Mock( - openai_api_key="test_key", - OPENAI_API_KEY="test_key", - openai_api_version="test_version", - openai_base_url="test_url", - OPENAI_BASE_URL="test_url", - openai_proxy=None, - openai_api_type="azure", - ) - - @pytest.fixture - def config_proxy(self): - return Mock( - openai_api_key="test_key", - OPENAI_API_KEY="test_key", - openai_base_url="test_url", - OPENAI_BASE_URL="test_url", - openai_proxy="http://proxy.com", - openai_api_type="other", - ) - - @pytest.fixture - def config_azure_proxy(self): - return Mock( - openai_api_key="test_key", - OPENAI_API_KEY="test_key", - openai_api_version="test_version", - openai_base_url="test_url", - OPENAI_BASE_URL="test_url", - openai_proxy="http://proxy.com", - openai_api_type="azure", - ) - - def test_make_client_kwargs_without_proxy(self, config): - instance = OpenAILLM() - instance.config = config - kwargs = instance._make_client_kwargs() - assert kwargs == {"api_key": "test_key", "base_url": "test_url"} - assert "http_client" not in kwargs - - def test_make_client_kwargs_without_proxy_azure(self, config_azure): - instance = OpenAILLM() - instance.config = config_azure + def test_make_client_kwargs_without_proxy(self): + instance = OpenAILLM(mock_llm_config) kwargs = instance._make_client_kwargs() - assert kwargs == {"api_key": "test_key", "base_url": "test_url"} + assert kwargs["api_key"] == "mock_api_key" + assert kwargs["base_url"] == "mock_base_url" assert "http_client" not in kwargs - def test_make_client_kwargs_with_proxy(self, config_proxy): - instance = OpenAILLM() - instance.config = config_proxy + def test_make_client_kwargs_with_proxy(self): + instance = OpenAILLM(mock_llm_config_proxy) kwargs = instance._make_client_kwargs() assert "http_client" in kwargs - def test_make_client_kwargs_with_proxy_azure(self, config_azure_proxy): - instance = OpenAILLM() - instance.config = config_azure_proxy - kwargs = instance._make_client_kwargs() - assert "http_client" in kwargs + def test_get_choice_function_arguments_for_aask_code(self, tool_calls_rsp): + instance = OpenAILLM(mock_llm_config_proxy) + for i, rsp in enumerate(tool_calls_rsp): + code = instance.get_choice_function_arguments(rsp) + logger.info(f"\ntest get function call arguments {i}: {code}") + assert "code" in code + assert "language" in code + assert "hello world" in code["code"] + logger.info(f'code is : {code["code"]}') + + if "Completed a python code for hello world!" == code["code"]: + code["language"] == "markdown" + else: + code["language"] == "python" + + def test_aask_code_json_decode_error(self, json_decode_error): + instance = OpenAILLM(mock_llm_config) + with pytest.raises(json.decoder.JSONDecodeError) as e: + instance.get_choice_function_arguments(json_decode_error) + assert "JSONDecodeError" in str(e) + + +@pytest.mark.asyncio +async def test_gen_image(): + llm = LLM() + model = "dall-e-3" + prompt = 'a logo with word "MetaGPT"' + images: list[Image] = await llm.gen_image(model=model, prompt=prompt) + assert images[0].size == (1024, 1024) + + images: list[Image] = await llm.gen_image(model=model, prompt=prompt, resp_format="b64_json") + assert images[0].size == (1024, 1024) diff --git a/tests/metagpt/provider/test_spark_api.py b/tests/metagpt/provider/test_spark_api.py index ee2d02c97..f5a6f66fd 100644 --- a/tests/metagpt/provider/test_spark_api.py +++ b/tests/metagpt/provider/test_spark_api.py @@ -4,14 +4,9 @@ import pytest -from metagpt.config import CONFIG +from metagpt.config2 import Config from metagpt.provider.spark_api import GetMessageFromWeb, SparkLLM - -CONFIG.spark_appid = "xxx" -CONFIG.spark_api_secret = "xxx" -CONFIG.spark_api_key = "xxx" -CONFIG.domain = "xxxxxx" -CONFIG.spark_url = "xxxx" +from tests.metagpt.provider.mock_llm_config import mock_llm_config prompt_msg = "who are you" resp_content = "I'm Spark" @@ -28,8 +23,8 @@ def run_forever(self, sslopt=None): def test_get_msg_from_web(mocker): mocker.patch("websocket.WebSocketApp", MockWebSocketApp) - get_msg_from_web = GetMessageFromWeb(text=prompt_msg) - assert get_msg_from_web.gen_params()["parameter"]["chat"]["domain"] == "xxxxxx" + get_msg_from_web = GetMessageFromWeb(prompt_msg, mock_llm_config) + assert get_msg_from_web.gen_params()["parameter"]["chat"]["domain"] == "mock_domain" ret = get_msg_from_web.run() assert ret == "" @@ -39,11 +34,19 @@ def mock_spark_get_msg_from_web_run(self) -> str: return resp_content +@pytest.mark.asyncio +async def test_spark_aask(): + llm = SparkLLM(Config.from_home("spark.yaml").llm) + + resp = await llm.aask("Hello!") + print(resp) + + @pytest.mark.asyncio async def test_spark_acompletion(mocker): mocker.patch("metagpt.provider.spark_api.GetMessageFromWeb.run", mock_spark_get_msg_from_web_run) - spark_gpt = SparkLLM() + spark_gpt = SparkLLM(mock_llm_config) resp = await spark_gpt.acompletion([]) assert resp == resp_content diff --git a/tests/metagpt/provider/test_zhipuai_api.py b/tests/metagpt/provider/test_zhipuai_api.py index 8f06fc717..798209710 100644 --- a/tests/metagpt/provider/test_zhipuai_api.py +++ b/tests/metagpt/provider/test_zhipuai_api.py @@ -4,10 +4,8 @@ import pytest -from metagpt.config import CONFIG from metagpt.provider.zhipuai_api import ZhiPuAILLM - -CONFIG.zhipuai_api_key = "xxx.xxx" +from tests.metagpt.provider.mock_llm_config import mock_llm_config_zhipu prompt_msg = "who are you" messages = [{"role": "user", "content": prompt_msg}] @@ -19,7 +17,7 @@ } -async def mock_zhipuai_acreate_stream(self, **kwargs): +async def mock_zhipuai_acreate_stream(**kwargs): class MockResponse(object): async def _aread(self): class Iterator(object): @@ -39,7 +37,7 @@ async def stream(self): return MockResponse() -async def mock_zhipuai_acreate(self, **kwargs) -> dict: +async def mock_zhipuai_acreate(**kwargs) -> dict: return default_resp @@ -48,7 +46,7 @@ async def test_zhipuai_acompletion(mocker): mocker.patch("metagpt.provider.zhipuai.zhipu_model_api.ZhiPuModelAPI.acreate", mock_zhipuai_acreate) mocker.patch("metagpt.provider.zhipuai.zhipu_model_api.ZhiPuModelAPI.acreate_stream", mock_zhipuai_acreate_stream) - zhipu_gpt = ZhiPuAILLM() + zhipu_gpt = ZhiPuAILLM(mock_llm_config_zhipu) resp = await zhipu_gpt.acompletion(messages) assert resp["choices"][0]["message"]["content"] == resp_content @@ -67,6 +65,7 @@ async def test_zhipuai_acompletion(mocker): def test_zhipuai_proxy(): - # CONFIG.openai_proxy = "http://127.0.0.1:8080" - _ = ZhiPuAILLM() - # assert openai.proxy == CONFIG.openai_proxy + # it seems like zhipuai would be inflected by the proxy of openai, maybe it's a bug + # but someone may want to use openai.proxy, so we keep this test case + # assert openai.proxy == config.llm.proxy + _ = ZhiPuAILLM(mock_llm_config_zhipu) diff --git a/tests/metagpt/roles/ci/test_code_interpreter.py b/tests/metagpt/roles/ci/test_code_interpreter.py new file mode 100644 index 000000000..f23292965 --- /dev/null +++ b/tests/metagpt/roles/ci/test_code_interpreter.py @@ -0,0 +1,19 @@ +import pytest + +from metagpt.logs import logger +from metagpt.roles.ci.code_interpreter import CodeInterpreter + + +@pytest.mark.asyncio +@pytest.mark.parametrize("auto_run", [(True), (False)]) +async def test_code_interpreter(mocker, auto_run): + mocker.patch("metagpt.actions.ci.execute_nb_code.ExecuteNbCode.run", return_value=("a successful run", True)) + mocker.patch("builtins.input", return_value="confirm") + + requirement = "Run data analysis on sklearn Iris dataset, include a plot" + tools = [] + + ci = CodeInterpreter(auto_run=auto_run, use_tools=True, tools=tools) + rsp = await ci.run(requirement) + logger.info(rsp) + assert len(rsp.content) > 0 diff --git a/tests/metagpt/roles/ci/test_ml_engineer.py b/tests/metagpt/roles/ci/test_ml_engineer.py new file mode 100644 index 000000000..3bf9f3b92 --- /dev/null +++ b/tests/metagpt/roles/ci/test_ml_engineer.py @@ -0,0 +1,90 @@ +import pytest + +from metagpt.actions.ci.execute_nb_code import ExecuteNbCode +from metagpt.logs import logger +from metagpt.roles.ci.ml_engineer import MLEngineer +from metagpt.schema import Message, Plan, Task +from metagpt.tools.tool_type import ToolType +from tests.metagpt.actions.ci.test_debug_code import CODE, DebugContext, ErrorStr + + +def test_mle_init(): + ci = MLEngineer(goal="test", auto_run=True, use_tools=True, tools=["tool1", "tool2"]) + assert ci.tools == [] + + +MockPlan = Plan( + goal="This is a titanic passenger survival dataset, your goal is to predict passenger survival outcome. The target column is Survived. Perform data analysis, data preprocessing, feature engineering, and modeling to predict the target. Report accuracy on the eval data. Train data path: 'tests/data/ml_datasets/titanic/split_train.csv', eval data path: 'tests/data/ml_datasets/titanic/split_eval.csv'.", + context="", + tasks=[ + Task( + task_id="1", + dependent_task_ids=[], + instruction="Perform exploratory data analysis on the train dataset to understand the features and target variable.", + task_type="eda", + code="", + result="", + is_success=False, + is_finished=False, + ) + ], + task_map={ + "1": Task( + task_id="1", + dependent_task_ids=[], + instruction="Perform exploratory data analysis on the train dataset to understand the features and target variable.", + task_type="eda", + code="", + result="", + is_success=False, + is_finished=False, + ) + }, + current_task_id="1", +) + + +@pytest.mark.asyncio +async def test_mle_write_code(mocker): + data_path = "tests/data/ml_datasets/titanic" + + mle = MLEngineer(auto_run=True, use_tools=True) + mle.planner.plan = MockPlan + + code, _ = await mle._write_code() + assert data_path in code["code"] + + +@pytest.mark.asyncio +async def test_mle_update_data_columns(mocker): + mle = MLEngineer(auto_run=True, use_tools=True) + mle.planner.plan = MockPlan + + # manually update task type to test update + mle.planner.plan.current_task.task_type = ToolType.DATA_PREPROCESS.value + + result = await mle._update_data_columns() + assert result is not None + + +@pytest.mark.asyncio +async def test_mle_debug_code(mocker): + mle = MLEngineer(auto_run=True, use_tools=True) + mle.working_memory.add(Message(content=ErrorStr, cause_by=ExecuteNbCode)) + mle.latest_code = CODE + mle.debug_context = DebugContext + code, _ = await mle._write_code() + assert len(code) > 0 + + +@pytest.mark.skip +@pytest.mark.asyncio +async def test_ml_engineer(): + data_path = "tests/data/ml_datasets/titanic" + requirement = f"This is a titanic passenger survival dataset, your goal is to predict passenger survival outcome. The target column is Survived. Perform data analysis, data preprocessing, feature engineering, and modeling to predict the target. Report accuracy on the eval data. Train data path: '{data_path}/split_train.csv', eval data path: '{data_path}/split_eval.csv'." + tools = ["FillMissingValue", "CatCross", "dummy_tool"] + + mle = MLEngineer(auto_run=True, use_tools=True, tools=tools) + rsp = await mle.run(requirement) + logger.info(rsp) + assert len(rsp.content) > 0 diff --git a/tests/metagpt/roles/test_architect.py b/tests/metagpt/roles/test_architect.py index 06e4b2d11..b02242ed2 100644 --- a/tests/metagpt/roles/test_architect.py +++ b/tests/metagpt/roles/test_architect.py @@ -12,7 +12,6 @@ import pytest from metagpt.actions import WriteDesign, WritePRD -from metagpt.config import CONFIG from metagpt.const import PRDS_FILE_REPO from metagpt.logs import logger from metagpt.roles import Architect @@ -22,12 +21,12 @@ @pytest.mark.asyncio -async def test_architect(): +async def test_architect(context): # Prerequisites filename = uuid.uuid4().hex + ".json" - await awrite(CONFIG.git_repo.workdir / PRDS_FILE_REPO / filename, data=MockMessages.prd.content) + await awrite(context.repo.workdir / PRDS_FILE_REPO / filename, data=MockMessages.prd.content) - role = Architect() + role = Architect(context=context) rsp = await role.run(with_message=Message(content="", cause_by=WritePRD)) logger.info(rsp) assert len(rsp.content) > 0 diff --git a/tests/metagpt/roles/test_assistant.py b/tests/metagpt/roles/test_assistant.py index 24096b357..bd0efea35 100644 --- a/tests/metagpt/roles/test_assistant.py +++ b/tests/metagpt/roles/test_assistant.py @@ -12,7 +12,6 @@ from metagpt.actions.skill_action import SkillAction from metagpt.actions.talk_action import TalkAction -from metagpt.config import CONFIG from metagpt.memory.brain_memory import BrainMemory from metagpt.roles.assistant import Assistant from metagpt.schema import Message @@ -20,8 +19,11 @@ @pytest.mark.asyncio -async def test_run(): - CONFIG.language = "Chinese" +async def test_run(mocker, context): + # mock + mocker.patch("metagpt.learn.text_to_image", return_value="http://mock.com/1.png") + + context.kwargs.language = "Chinese" class Input(BaseModel): memory: BrainMemory @@ -65,7 +67,7 @@ class Input(BaseModel): "cause_by": any_to_str(SkillAction), }, ] - CONFIG.agent_skills = [ + agent_skills = [ {"id": 1, "name": "text_to_speech", "type": "builtin", "config": {}, "enabled": True}, {"id": 2, "name": "text_to_image", "type": "builtin", "config": {}, "enabled": True}, {"id": 3, "name": "ai_call", "type": "builtin", "config": {}, "enabled": True}, @@ -77,9 +79,11 @@ class Input(BaseModel): for i in inputs: seed = Input(**i) - CONFIG.language = seed.language - CONFIG.agent_description = seed.agent_description - role = Assistant(language="Chinese") + role = Assistant(language="Chinese", context=context) + role.context.kwargs.language = seed.language + role.context.kwargs.agent_description = seed.agent_description + role.context.kwargs.agent_skills = agent_skills + role.memory = seed.memory # Restore historical conversation content. while True: has_action = await role.think() @@ -110,21 +114,16 @@ class Input(BaseModel): ], ) @pytest.mark.asyncio -async def test_memory(memory): - role = Assistant() +async def test_memory(memory, context): + role = Assistant(context=context) + role.context.kwargs.agent_skills = [] role.load_memory(memory) val = role.get_memory() assert val await role.talk("draw apple") - - agent_skills = CONFIG.agent_skills - CONFIG.agent_skills = [] - try: - await role.think() - finally: - CONFIG.agent_skills = agent_skills + await role.think() assert isinstance(role.rc.todo, TalkAction) diff --git a/tests/metagpt/roles/test_engineer.py b/tests/metagpt/roles/test_engineer.py index d03aea0a6..d263a8a2f 100644 --- a/tests/metagpt/roles/test_engineer.py +++ b/tests/metagpt/roles/test_engineer.py @@ -13,40 +13,30 @@ import pytest from metagpt.actions import WriteCode, WriteTasks -from metagpt.config import CONFIG -from metagpt.const import ( - PRDS_FILE_REPO, - REQUIREMENT_FILENAME, - SYSTEM_DESIGN_FILE_REPO, - TASK_FILE_REPO, -) +from metagpt.const import REQUIREMENT_FILENAME, SYSTEM_DESIGN_FILE_REPO, TASK_FILE_REPO from metagpt.logs import logger from metagpt.roles.engineer import Engineer from metagpt.schema import CodingContext, Message from metagpt.utils.common import CodeParser, any_to_name, any_to_str, aread, awrite -from metagpt.utils.file_repository import FileRepository from metagpt.utils.git_repository import ChangeType from tests.metagpt.roles.mock import STRS_FOR_PARSING, TASKS, MockMessages @pytest.mark.asyncio -async def test_engineer(): +async def test_engineer(context): # Prerequisites rqno = "20231221155954.json" - await FileRepository.save_file(REQUIREMENT_FILENAME, content=MockMessages.req.content) - await FileRepository.save_file(rqno, relative_path=PRDS_FILE_REPO, content=MockMessages.prd.content) - await FileRepository.save_file( - rqno, relative_path=SYSTEM_DESIGN_FILE_REPO, content=MockMessages.system_design.content - ) - await FileRepository.save_file(rqno, relative_path=TASK_FILE_REPO, content=MockMessages.json_tasks.content) - - engineer = Engineer() + await context.repo.save(REQUIREMENT_FILENAME, content=MockMessages.req.content) + await context.repo.docs.prd.save(rqno, content=MockMessages.prd.content) + await context.repo.docs.system_design.save(rqno, content=MockMessages.system_design.content) + await context.repo.docs.task.save(rqno, content=MockMessages.json_tasks.content) + + engineer = Engineer(context=context) rsp = await engineer.run(Message(content="", cause_by=WriteTasks)) logger.info(rsp) assert rsp.cause_by == any_to_str(WriteCode) - src_file_repo = CONFIG.git_repo.new_file_repository(CONFIG.src_workspace) - assert src_file_repo.changed_files + assert context.repo.with_src_path(context.src_workspace).srcs.changed_files def test_parse_str(): @@ -109,54 +99,52 @@ def test_parse_code(): def test_todo(): role = Engineer() - assert role.todo == any_to_name(WriteCode) + assert role.action_description == any_to_name(WriteCode) @pytest.mark.asyncio -async def test_new_coding_context(): +async def test_new_coding_context(context): # Prerequisites demo_path = Path(__file__).parent / "../../data/demo_project" deps = json.loads(await aread(demo_path / "dependencies.json")) - dependency = await CONFIG.git_repo.get_dependency() + dependency = await context.git_repo.get_dependency() for k, v in deps.items(): await dependency.update(k, set(v)) data = await aread(demo_path / "system_design.json") rqno = "20231221155954.json" - await awrite(CONFIG.git_repo.workdir / SYSTEM_DESIGN_FILE_REPO / rqno, data) + await awrite(context.repo.workdir / SYSTEM_DESIGN_FILE_REPO / rqno, data) data = await aread(demo_path / "tasks.json") - await awrite(CONFIG.git_repo.workdir / TASK_FILE_REPO / rqno, data) - - CONFIG.src_workspace = Path(CONFIG.git_repo.workdir) / "game_2048" - src_file_repo = CONFIG.git_repo.new_file_repository(relative_path=CONFIG.src_workspace) - task_file_repo = CONFIG.git_repo.new_file_repository(relative_path=TASK_FILE_REPO) - design_file_repo = CONFIG.git_repo.new_file_repository(relative_path=SYSTEM_DESIGN_FILE_REPO) - - filename = "game.py" - ctx_doc = await Engineer._new_coding_doc( - filename=filename, - src_file_repo=src_file_repo, - task_file_repo=task_file_repo, - design_file_repo=design_file_repo, - dependency=dependency, - ) - assert ctx_doc - assert ctx_doc.filename == filename - assert ctx_doc.content - ctx = CodingContext.model_validate_json(ctx_doc.content) - assert ctx.filename == filename - assert ctx.design_doc - assert ctx.design_doc.content - assert ctx.task_doc - assert ctx.task_doc.content - assert ctx.code_doc - - CONFIG.git_repo.add_change({f"{TASK_FILE_REPO}/{rqno}": ChangeType.UNTRACTED}) - CONFIG.git_repo.commit("mock env") - await src_file_repo.save(filename=filename, content="content") - role = Engineer() - assert not role.code_todos - await role._new_code_actions() - assert role.code_todos + await awrite(context.repo.workdir / TASK_FILE_REPO / rqno, data) + + context.src_workspace = Path(context.repo.workdir) / "game_2048" + + try: + filename = "game.py" + engineer = Engineer(context=context) + ctx_doc = await engineer._new_coding_doc( + filename=filename, + dependency=dependency, + ) + assert ctx_doc + assert ctx_doc.filename == filename + assert ctx_doc.content + ctx = CodingContext.model_validate_json(ctx_doc.content) + assert ctx.filename == filename + assert ctx.design_doc + assert ctx.design_doc.content + assert ctx.task_doc + assert ctx.task_doc.content + assert ctx.code_doc + + context.git_repo.add_change({f"{TASK_FILE_REPO}/{rqno}": ChangeType.UNTRACTED}) + context.git_repo.commit("mock env") + await context.repo.with_src_path(context.src_workspace).srcs.save(filename=filename, content="content") + role = Engineer(context=context) + assert not role.code_todos + await role._new_code_actions() + assert role.code_todos + finally: + context.git_repo.delete_repository() if __name__ == "__main__": diff --git a/tests/metagpt/roles/test_invoice_ocr_assistant.py b/tests/metagpt/roles/test_invoice_ocr_assistant.py index e3a9259da..bedcd6712 100644 --- a/tests/metagpt/roles/test_invoice_ocr_assistant.py +++ b/tests/metagpt/roles/test_invoice_ocr_assistant.py @@ -41,9 +41,11 @@ ), ], ) -async def test_invoice_ocr_assistant(query: str, invoice_path: Path, invoice_table_path: Path, expected_result: dict): +async def test_invoice_ocr_assistant( + query: str, invoice_path: Path, invoice_table_path: Path, expected_result: dict, context +): invoice_path = TEST_DATA_PATH / invoice_path - role = InvoiceOCRAssistant() + role = InvoiceOCRAssistant(context=context) await role.run(Message(content=query, instruct_content=InvoicePath(file_path=invoice_path))) invoice_table_path = DATA_PATH / invoice_table_path df = pd.read_excel(invoice_table_path) diff --git a/tests/metagpt/roles/test_product_manager.py b/tests/metagpt/roles/test_product_manager.py index 1083e81b0..59b5aa81a 100644 --- a/tests/metagpt/roles/test_product_manager.py +++ b/tests/metagpt/roles/test_product_manager.py @@ -5,17 +5,51 @@ @Author : alexanderwu @File : test_product_manager.py """ +import json + import pytest +from metagpt.actions import WritePRD +from metagpt.actions.prepare_documents import PrepareDocuments +from metagpt.const import REQUIREMENT_FILENAME +from metagpt.context import Context from metagpt.logs import logger from metagpt.roles import ProductManager +from metagpt.utils.common import any_to_str from tests.metagpt.roles.mock import MockMessages @pytest.mark.asyncio async def test_product_manager(new_filename): - product_manager = ProductManager() - rsp = await product_manager.run(MockMessages.req) - logger.info(rsp) - assert len(rsp.content) > 0 - assert rsp.content == MockMessages.req.content + context = Context() + try: + assert context.git_repo is None + assert context.repo is None + product_manager = ProductManager(context=context) + # prepare documents + rsp = await product_manager.run(MockMessages.req) + assert context.git_repo + assert context.repo + assert rsp.cause_by == any_to_str(PrepareDocuments) + assert REQUIREMENT_FILENAME in context.repo.docs.changed_files + + # write prd + rsp = await product_manager.run(rsp) + assert rsp.cause_by == any_to_str(WritePRD) + logger.info(rsp) + assert len(rsp.content) > 0 + doc = list(rsp.instruct_content.docs.values())[0] + m = json.loads(doc.content) + assert m["Original Requirements"] == MockMessages.req.content + + # nothing to do + rsp = await product_manager.run(rsp) + assert rsp is None + except Exception as e: + assert not e + finally: + context.git_repo.delete_repository() + + +if __name__ == "__main__": + pytest.main([__file__, "-s"]) diff --git a/tests/metagpt/roles/test_project_manager.py b/tests/metagpt/roles/test_project_manager.py index 9207623bc..9b016927e 100644 --- a/tests/metagpt/roles/test_project_manager.py +++ b/tests/metagpt/roles/test_project_manager.py @@ -13,7 +13,7 @@ @pytest.mark.asyncio -async def test_project_manager(): - project_manager = ProjectManager() +async def test_project_manager(context): + project_manager = ProjectManager(context=context) rsp = await project_manager.run(MockMessages.system_design) logger.info(rsp) diff --git a/tests/metagpt/roles/test_qa_engineer.py b/tests/metagpt/roles/test_qa_engineer.py index 784c26a06..b89e7d5eb 100644 --- a/tests/metagpt/roles/test_qa_engineer.py +++ b/tests/metagpt/roles/test_qa_engineer.py @@ -13,20 +13,19 @@ from metagpt.actions import DebugError, RunCode, WriteTest from metagpt.actions.summarize_code import SummarizeCode -from metagpt.config import CONFIG from metagpt.environment import Environment from metagpt.roles import QaEngineer from metagpt.schema import Message from metagpt.utils.common import any_to_str, aread, awrite -async def test_qa(): +async def test_qa(context): # Prerequisites demo_path = Path(__file__).parent / "../../data/demo_project" - CONFIG.src_workspace = Path(CONFIG.git_repo.workdir) / "qa/game_2048" + context.src_workspace = Path(context.repo.workdir) / "qa/game_2048" data = await aread(filename=demo_path / "game.py", encoding="utf-8") - await awrite(filename=CONFIG.src_workspace / "game.py", data=data, encoding="utf-8") - await awrite(filename=Path(CONFIG.git_repo.workdir) / "requirements.txt", data="") + await awrite(filename=context.src_workspace / "game.py", data=data, encoding="utf-8") + await awrite(filename=Path(context.repo.workdir) / "requirements.txt", data="") class MockEnv(Environment): msgs: List[Message] = Field(default_factory=list) @@ -37,7 +36,7 @@ def publish_message(self, message: Message, peekable: bool = True) -> bool: env = MockEnv() - role = QaEngineer() + role = QaEngineer(context=context) role.set_env(env) await role.run(with_message=Message(content="", cause_by=SummarizeCode)) assert env.msgs diff --git a/tests/metagpt/roles/test_researcher.py b/tests/metagpt/roles/test_researcher.py index 891befa38..ba05e1296 100644 --- a/tests/metagpt/roles/test_researcher.py +++ b/tests/metagpt/roles/test_researcher.py @@ -4,7 +4,10 @@ import pytest +from metagpt.actions.research import CollectLinks from metagpt.roles import researcher +from metagpt.tools import SearchEngineType +from metagpt.tools.search_engine import SearchEngine async def mock_llm_ask(self, prompt: str, system_msgs): @@ -25,16 +28,20 @@ async def mock_llm_ask(self, prompt: str, system_msgs): @pytest.mark.asyncio -async def test_researcher(mocker): +async def test_researcher(mocker, search_engine_mocker, context): with TemporaryDirectory() as dirname: topic = "dataiku vs. datarobot" mocker.patch("metagpt.provider.base_llm.BaseLLM.aask", mock_llm_ask) researcher.RESEARCH_PATH = Path(dirname) - await researcher.Researcher().run(topic) + role = researcher.Researcher(context=context) + for i in role.actions: + if isinstance(i, CollectLinks): + i.search_engine = SearchEngine(engine=SearchEngineType.DUCK_DUCK_GO) + await role.run(topic) assert (researcher.RESEARCH_PATH / f"{topic}.md").read_text().startswith("# Research Report") -def test_write_report(mocker): +def test_write_report(mocker, context): with TemporaryDirectory() as dirname: for i, topic in enumerate( [ @@ -46,7 +53,7 @@ def test_write_report(mocker): ): researcher.RESEARCH_PATH = Path(dirname) content = "# Research Report" - researcher.Researcher().write_report(topic, content) + researcher.Researcher(context=context).write_report(topic, content) assert (researcher.RESEARCH_PATH / f"{i+1}. metagpt.md").read_text().startswith("# Research Report") diff --git a/tests/metagpt/roles/test_role.py b/tests/metagpt/roles/test_role.py index bef71f9a5..8b11e2d4a 100644 --- a/tests/metagpt/roles/test_role.py +++ b/tests/metagpt/roles/test_role.py @@ -3,7 +3,7 @@ # @Desc : unittest of Role import pytest -from metagpt.llm import HumanProvider +from metagpt.provider.human_provider import HumanProvider from metagpt.roles.role import Role @@ -13,8 +13,8 @@ def test_role_desc(): assert role.desc == "Best Seller" -def test_role_human(): - role = Role(is_human=True) +def test_role_human(context): + role = Role(is_human=True, context=context) assert isinstance(role.llm, HumanProvider) diff --git a/tests/metagpt/roles/test_teacher.py b/tests/metagpt/roles/test_teacher.py index 1efc329db..83a7e382a 100644 --- a/tests/metagpt/roles/test_teacher.py +++ b/tests/metagpt/roles/test_teacher.py @@ -5,19 +5,17 @@ @Author : mashenquan @File : test_teacher.py """ -import os from typing import Dict, Optional import pytest -from pydantic import BaseModel +from pydantic import BaseModel, Field -from metagpt.config import CONFIG, Config +from metagpt.context import Context from metagpt.roles.teacher import Teacher from metagpt.schema import Message @pytest.mark.asyncio -@pytest.mark.skip async def test_init(): class Inputs(BaseModel): name: str @@ -31,6 +29,7 @@ class Inputs(BaseModel): expect_goal: str expect_constraints: str expect_desc: str + exclude: list = Field(default_factory=list) inputs = [ { @@ -45,6 +44,7 @@ class Inputs(BaseModel): "kwargs": {}, "desc": "aaa{language}", "expect_desc": "aaa{language}", + "exclude": ["language", "key1", "something_big", "teaching_language"], }, { "name": "Lily{language}", @@ -58,20 +58,21 @@ class Inputs(BaseModel): "kwargs": {"language": "CN", "key1": "HaHa", "something_big": "sleep", "teaching_language": "EN"}, "desc": "aaa{language}", "expect_desc": "aaaCN", + "language": "CN", + "teaching_language": "EN", }, ] - env = os.environ.copy() for i in inputs: seed = Inputs(**i) - os.environ.clear() - os.environ.update(env) - CONFIG = Config() - CONFIG.set_context(seed.kwargs) - print(CONFIG.options) - assert bool("language" in seed.kwargs) == bool("language" in CONFIG.options) + context = Context() + for k in seed.exclude: + context.kwargs.set(k, None) + for k, v in seed.kwargs.items(): + context.kwargs.set(k, v) teacher = Teacher( + context=context, name=seed.name, profile=seed.profile, goal=seed.goal, @@ -105,7 +106,6 @@ class Inputs(BaseModel): @pytest.mark.asyncio async def test_run(): - CONFIG.set_context({"language": "Chinese", "teaching_language": "English"}) lesson = """ UNIT 1 Making New Friends TOPIC 1 Welcome to China! @@ -149,7 +149,10 @@ async def test_run(): 3c Match the big letters with the small ones. Then write them on the lines. """ - teacher = Teacher() + context = Context() + context.kwargs.language = "Chinese" + context.kwargs.teaching_language = "English" + teacher = Teacher(context=context) rsp = await teacher.run(Message(content=lesson)) assert rsp diff --git a/tests/metagpt/roles/test_tutorial_assistant.py b/tests/metagpt/roles/test_tutorial_assistant.py index 0e6c1efb9..c12c2b26e 100644 --- a/tests/metagpt/roles/test_tutorial_assistant.py +++ b/tests/metagpt/roles/test_tutorial_assistant.py @@ -15,8 +15,8 @@ @pytest.mark.asyncio @pytest.mark.parametrize(("language", "topic"), [("Chinese", "Write a tutorial about pip")]) -async def test_tutorial_assistant(language: str, topic: str): - role = TutorialAssistant(language=language) +async def test_tutorial_assistant(language: str, topic: str, context): + role = TutorialAssistant(language=language, context=context) msg = await role.run(topic) assert TUTORIAL_PATH.exists() filename = msg.content diff --git a/tests/metagpt/serialize_deserialize/test_action.py b/tests/metagpt/serialize_deserialize/test_action.py index 81879e34e..d234a160f 100644 --- a/tests/metagpt/serialize_deserialize/test_action.py +++ b/tests/metagpt/serialize_deserialize/test_action.py @@ -5,28 +5,22 @@ import pytest from metagpt.actions import Action -from metagpt.llm import LLM -def test_action_serialize(): - action = Action() +@pytest.mark.asyncio +async def test_action_serdeser(context): + action = Action(context=context) ser_action_dict = action.model_dump() assert "name" in ser_action_dict assert "llm" not in ser_action_dict # not export - assert "__module_class_name" not in ser_action_dict + assert "__module_class_name" in ser_action_dict - action = Action(name="test") + action = Action(name="test", context=context) ser_action_dict = action.model_dump() assert "test" in ser_action_dict["name"] + new_action = Action(**ser_action_dict, context=context) -@pytest.mark.asyncio -async def test_action_deserialize(): - action = Action() - serialized_data = action.model_dump() - - new_action = Action(**serialized_data) - - assert new_action.name == "Action" - assert isinstance(new_action.llm, type(LLM())) + assert new_action.name == "test" + assert isinstance(new_action.llm, type(context.llm())) assert len(await new_action._aask("who are you")) > 0 diff --git a/tests/metagpt/serialize_deserialize/test_architect_deserialize.py b/tests/metagpt/serialize_deserialize/test_architect.py similarity index 69% rename from tests/metagpt/serialize_deserialize/test_architect_deserialize.py rename to tests/metagpt/serialize_deserialize/test_architect.py index b113912a7..e3c2703fa 100644 --- a/tests/metagpt/serialize_deserialize/test_architect_deserialize.py +++ b/tests/metagpt/serialize_deserialize/test_architect.py @@ -8,21 +8,21 @@ from metagpt.roles.architect import Architect -def test_architect_serialize(): - role = Architect() +@pytest.mark.asyncio +async def test_architect_serdeser(context): + role = Architect(context=context) ser_role_dict = role.model_dump(by_alias=True) assert "name" in ser_role_dict assert "states" in ser_role_dict assert "actions" in ser_role_dict - -@pytest.mark.asyncio -async def test_architect_deserialize(): - role = Architect() - ser_role_dict = role.model_dump(by_alias=True) - new_role = Architect(**ser_role_dict) - # new_role = Architect.deserialize(ser_role_dict) + new_role = Architect(**ser_role_dict, context=context) assert new_role.name == "Bob" assert len(new_role.actions) == 1 + assert len(new_role.rc.watch) == 1 assert isinstance(new_role.actions[0], Action) await new_role.actions[0].run(with_messages="write a cli snake game") + + +if __name__ == "__main__": + pytest.main([__file__, "-s"]) diff --git a/tests/metagpt/serialize_deserialize/test_environment.py b/tests/metagpt/serialize_deserialize/test_environment.py index 5a68288a6..4e6ea93b5 100644 --- a/tests/metagpt/serialize_deserialize/test_environment.py +++ b/tests/metagpt/serialize_deserialize/test_environment.py @@ -2,7 +2,6 @@ # -*- coding: utf-8 -*- # @Desc : -import shutil from metagpt.actions.action_node import ActionNode from metagpt.actions.add_requirement import UserRequirement @@ -10,7 +9,7 @@ from metagpt.environment import Environment from metagpt.roles.project_manager import ProjectManager from metagpt.schema import Message -from metagpt.utils.common import any_to_str +from metagpt.utils.common import any_to_str, read_json_file, write_json_file from tests.metagpt.serialize_deserialize.test_serdeser_base import ( ActionOK, ActionRaise, @@ -19,23 +18,20 @@ ) -def test_env_serialize(): - env = Environment() +def test_env_serdeser(context): + env = Environment(context=context) + env.publish_message(message=Message(content="test env serialize")) + ser_env_dict = env.model_dump() assert "roles" in ser_env_dict assert len(ser_env_dict["roles"]) == 0 - -def test_env_deserialize(): - env = Environment() - env.publish_message(message=Message(content="test env serialize")) - ser_env_dict = env.model_dump() - new_env = Environment(**ser_env_dict) + new_env = Environment(**ser_env_dict, context=context) assert len(new_env.roles) == 0 assert len(new_env.history) == 25 -def test_environment_serdeser(): +def test_environment_serdeser(context): out_mapping = {"field1": (list[str], ...)} out_data = {"field1": ["field1 value1", "field1 value2"]} ic_obj = ActionNode.create_model_class("prd", out_mapping) @@ -44,7 +40,7 @@ def test_environment_serdeser(): content="prd", instruct_content=ic_obj(**out_data), role="product manager", cause_by=any_to_str(UserRequirement) ) - environment = Environment() + environment = Environment(context=context) role_c = RoleC() environment.add_role(role_c) environment.publish_message(message) @@ -52,7 +48,7 @@ def test_environment_serdeser(): ser_data = environment.model_dump() assert ser_data["roles"]["Role C"]["name"] == "RoleC" - new_env: Environment = Environment(**ser_data) + new_env: Environment = Environment(**ser_data, context=context) assert len(new_env.roles) == 1 assert list(new_env.roles.values())[0].states == list(environment.roles.values())[0].states @@ -61,30 +57,31 @@ def test_environment_serdeser(): assert type(list(new_env.roles.values())[0].actions[1]) == ActionRaise -def test_environment_serdeser_v2(): - environment = Environment() +def test_environment_serdeser_v2(context): + environment = Environment(context=context) pm = ProjectManager() environment.add_role(pm) ser_data = environment.model_dump() - new_env: Environment = Environment(**ser_data) + new_env: Environment = Environment(**ser_data, context=context) role = new_env.get_role(pm.profile) assert isinstance(role, ProjectManager) assert isinstance(role.actions[0], WriteTasks) assert isinstance(list(new_env.roles.values())[0].actions[0], WriteTasks) -def test_environment_serdeser_save(): - environment = Environment() +def test_environment_serdeser_save(context): + environment = Environment(context=context) role_c = RoleC() - shutil.rmtree(serdeser_path.joinpath("team"), ignore_errors=True) - stg_path = serdeser_path.joinpath("team", "environment") + env_path = stg_path.joinpath("env.json") environment.add_role(role_c) - environment.serialize(stg_path) - new_env: Environment = Environment.deserialize(stg_path) + write_json_file(env_path, environment.model_dump()) + + env_dict = read_json_file(env_path) + new_env: Environment = Environment(**env_dict, context=context) assert len(new_env.roles) == 1 assert type(list(new_env.roles.values())[0].actions[0]) == ActionOK diff --git a/tests/metagpt/serialize_deserialize/test_memory.py b/tests/metagpt/serialize_deserialize/test_memory.py index aa3e2a465..560ae2c51 100644 --- a/tests/metagpt/serialize_deserialize/test_memory.py +++ b/tests/metagpt/serialize_deserialize/test_memory.py @@ -9,11 +9,11 @@ from metagpt.actions.design_api import WriteDesign from metagpt.memory.memory import Memory from metagpt.schema import Message -from metagpt.utils.common import any_to_str +from metagpt.utils.common import any_to_str, read_json_file, write_json_file from tests.metagpt.serialize_deserialize.test_serdeser_base import serdeser_path -def test_memory_serdeser(): +def test_memory_serdeser(context): msg1 = Message(role="Boss", content="write a snake game", cause_by=UserRequirement) out_mapping = {"field2": (list[str], ...)} @@ -39,7 +39,7 @@ def test_memory_serdeser(): assert memory.count() == 2 -def test_memory_serdeser_save(): +def test_memory_serdeser_save(context): msg1 = Message(role="User", content="write a 2048 game", cause_by=UserRequirement) out_mapping = {"field1": (list[str], ...)} @@ -53,14 +53,14 @@ def test_memory_serdeser_save(): memory.add_batch([msg1, msg2]) stg_path = serdeser_path.joinpath("team", "environment") - memory.serialize(stg_path) - assert stg_path.joinpath("memory.json").exists() + memory_path = stg_path.joinpath("memory.json") + write_json_file(memory_path, memory.model_dump()) + assert memory_path.exists() - new_memory = Memory.deserialize(stg_path) + memory_dict = read_json_file(memory_path) + new_memory = Memory(**memory_dict) assert new_memory.count() == 2 new_msg2 = new_memory.get(1)[0] assert new_msg2.instruct_content.field1 == ["field1 value1", "field1 value2"] assert new_msg2.cause_by == any_to_str(WriteDesign) assert len(new_memory.index) == 2 - - stg_path.joinpath("memory.json").unlink() diff --git a/tests/metagpt/serialize_deserialize/test_polymorphic.py b/tests/metagpt/serialize_deserialize/test_polymorphic.py index ed0482c34..e5f8ec8d6 100644 --- a/tests/metagpt/serialize_deserialize/test_polymorphic.py +++ b/tests/metagpt/serialize_deserialize/test_polymorphic.py @@ -1,6 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # @Desc : unittest of polymorphic conditions +import copy from pydantic import BaseModel, ConfigDict, SerializeAsAny @@ -12,6 +13,8 @@ class ActionSubClasses(BaseModel): + model_config = ConfigDict(arbitrary_types_allowed=True) + actions: list[SerializeAsAny[Action]] = [] @@ -40,19 +43,21 @@ def test_no_serialize_as_any(): def test_polymorphic(): - _ = ActionOKV2( + ok_v2 = ActionOKV2( **{"name": "ActionOKV2", "context": "", "prefix": "", "desc": "", "extra_field": "ActionOKV2 Extra Info"} ) action_subcls = ActionSubClasses(actions=[ActionOKV2(), ActionPass()]) action_subcls_dict = action_subcls.model_dump() + action_subcls_dict2 = copy.deepcopy(action_subcls_dict) assert "__module_class_name" in action_subcls_dict["actions"][0] new_action_subcls = ActionSubClasses(**action_subcls_dict) assert isinstance(new_action_subcls.actions[0], ActionOKV2) + assert new_action_subcls.actions[0].extra_field == ok_v2.extra_field assert isinstance(new_action_subcls.actions[1], ActionPass) - new_action_subcls = ActionSubClasses.model_validate(action_subcls_dict) + new_action_subcls = ActionSubClasses.model_validate(action_subcls_dict2) assert isinstance(new_action_subcls.actions[0], ActionOKV2) assert isinstance(new_action_subcls.actions[1], ActionPass) diff --git a/tests/metagpt/serialize_deserialize/test_prepare_interview.py b/tests/metagpt/serialize_deserialize/test_prepare_interview.py index cd9912103..a3e3edafc 100644 --- a/tests/metagpt/serialize_deserialize/test_prepare_interview.py +++ b/tests/metagpt/serialize_deserialize/test_prepare_interview.py @@ -8,12 +8,12 @@ @pytest.mark.asyncio -async def test_action_deserialize(): - action = PrepareInterview() +async def test_action_serdeser(context): + action = PrepareInterview(context=context) serialized_data = action.model_dump() assert serialized_data["name"] == "PrepareInterview" - new_action = PrepareInterview(**serialized_data) + new_action = PrepareInterview(**serialized_data, context=context) assert new_action.name == "PrepareInterview" assert type(await new_action.run("python developer")) == ActionNode diff --git a/tests/metagpt/serialize_deserialize/test_product_manager.py b/tests/metagpt/serialize_deserialize/test_product_manager.py index 094943900..2338b406d 100644 --- a/tests/metagpt/serialize_deserialize/test_product_manager.py +++ b/tests/metagpt/serialize_deserialize/test_product_manager.py @@ -10,10 +10,10 @@ @pytest.mark.asyncio -async def test_product_manager_deserialize(new_filename): - role = ProductManager() +async def test_product_manager_serdeser(new_filename, context): + role = ProductManager(context=context) ser_role_dict = role.model_dump(by_alias=True) - new_role = ProductManager(**ser_role_dict) + new_role = ProductManager(**ser_role_dict, context=context) assert new_role.name == "Alice" assert len(new_role.actions) == 2 diff --git a/tests/metagpt/serialize_deserialize/test_project_manager.py b/tests/metagpt/serialize_deserialize/test_project_manager.py index 1088a4461..fb998ae31 100644 --- a/tests/metagpt/serialize_deserialize/test_project_manager.py +++ b/tests/metagpt/serialize_deserialize/test_project_manager.py @@ -9,20 +9,15 @@ from metagpt.roles.project_manager import ProjectManager -def test_project_manager_serialize(): - role = ProjectManager() +@pytest.mark.asyncio +async def test_project_manager_serdeser(context): + role = ProjectManager(context=context) ser_role_dict = role.model_dump(by_alias=True) assert "name" in ser_role_dict assert "states" in ser_role_dict assert "actions" in ser_role_dict - -@pytest.mark.asyncio -async def test_project_manager_deserialize(): - role = ProjectManager() - ser_role_dict = role.model_dump(by_alias=True) - - new_role = ProjectManager(**ser_role_dict) + new_role = ProjectManager(**ser_role_dict, context=context) assert new_role.name == "Eve" assert len(new_role.actions) == 1 assert isinstance(new_role.actions[0], Action) diff --git a/tests/metagpt/serialize_deserialize/test_reasearcher.py b/tests/metagpt/serialize_deserialize/test_reasearcher.py index 1b8dbf2c7..67c52e692 100644 --- a/tests/metagpt/serialize_deserialize/test_reasearcher.py +++ b/tests/metagpt/serialize_deserialize/test_reasearcher.py @@ -8,13 +8,13 @@ @pytest.mark.asyncio -async def test_tutorial_assistant_deserialize(): - role = Researcher() +async def test_tutorial_assistant_serdeser(context): + role = Researcher(context=context) ser_role_dict = role.model_dump() assert "name" in ser_role_dict assert "language" in ser_role_dict - new_role = Researcher(**ser_role_dict) + new_role = Researcher(**ser_role_dict, context=context) assert new_role.language == "en-us" assert len(new_role.actions) == 3 assert isinstance(new_role.actions[0], CollectLinks) diff --git a/tests/metagpt/serialize_deserialize/test_role.py b/tests/metagpt/serialize_deserialize/test_role.py index d38797baf..aaf7c1935 100644 --- a/tests/metagpt/serialize_deserialize/test_role.py +++ b/tests/metagpt/serialize_deserialize/test_role.py @@ -10,13 +10,12 @@ from metagpt.actions import WriteCode from metagpt.actions.add_requirement import UserRequirement -from metagpt.const import SERDESER_PATH from metagpt.logs import logger from metagpt.roles.engineer import Engineer from metagpt.roles.product_manager import ProductManager from metagpt.roles.role import Role from metagpt.schema import Message -from metagpt.utils.common import format_trackback_info +from metagpt.utils.common import format_trackback_info, read_json_file, write_json_file from tests.metagpt.serialize_deserialize.test_serdeser_base import ( ActionOK, RoleA, @@ -27,7 +26,7 @@ ) -def test_roles(): +def test_roles(context): role_a = RoleA() assert len(role_a.rc.watch) == 1 role_b = RoleB() @@ -38,7 +37,7 @@ def test_roles(): assert len(role_d.actions) == 1 -def test_role_subclasses(): +def test_role_subclasses(context): """test subclasses of role with same fields in ser&deser""" class RoleSubClasses(BaseModel): @@ -52,7 +51,7 @@ class RoleSubClasses(BaseModel): assert isinstance(new_role_subcls.roles[1], RoleB) -def test_role_serialize(): +def test_role_serialize(context): role = Role() ser_role_dict = role.model_dump() assert "name" in ser_role_dict @@ -60,60 +59,56 @@ def test_role_serialize(): assert "actions" in ser_role_dict -def test_engineer_serialize(): +def test_engineer_serdeser(context): role = Engineer() ser_role_dict = role.model_dump() assert "name" in ser_role_dict assert "states" in ser_role_dict assert "actions" in ser_role_dict - -@pytest.mark.asyncio -async def test_engineer_deserialize(): - role = Engineer(use_code_review=True) - ser_role_dict = role.model_dump() - new_role = Engineer(**ser_role_dict) assert new_role.name == "Alex" - assert new_role.use_code_review is True + assert new_role.use_code_review is False assert len(new_role.actions) == 1 assert isinstance(new_role.actions[0], WriteCode) - # await new_role.actions[0].run(context="write a cli snake game", filename="test_code") -def test_role_serdeser_save(): - stg_path_prefix = serdeser_path.joinpath("team", "environment", "roles") +def test_role_serdeser_save(context): shutil.rmtree(serdeser_path.joinpath("team"), ignore_errors=True) pm = ProductManager() - role_tag = f"{pm.__class__.__name__}_{pm.name}" - stg_path = stg_path_prefix.joinpath(role_tag) - pm.serialize(stg_path) - new_pm = Role.deserialize(stg_path) + stg_path = serdeser_path.joinpath("team", "environment", "roles", f"{pm.__class__.__name__}_{pm.name}") + role_path = stg_path.joinpath("role.json") + write_json_file(role_path, pm.model_dump()) + + role_dict = read_json_file(role_path) + new_pm = ProductManager(**role_dict) assert new_pm.name == pm.name assert len(new_pm.get_memories(1)) == 0 @pytest.mark.asyncio -async def test_role_serdeser_interrupt(): +async def test_role_serdeser_interrupt(context): role_c = RoleC() - shutil.rmtree(SERDESER_PATH.joinpath("team"), ignore_errors=True) + shutil.rmtree(serdeser_path.joinpath("team"), ignore_errors=True) - stg_path = SERDESER_PATH.joinpath("team", "environment", "roles", f"{role_c.__class__.__name__}_{role_c.name}") + stg_path = serdeser_path.joinpath("team", "environment", "roles", f"{role_c.__class__.__name__}_{role_c.name}") + role_path = stg_path.joinpath("role.json") try: await role_c.run(with_message=Message(content="demo", cause_by=UserRequirement)) except Exception: - logger.error(f"Exception in `role_a.run`, detail: {format_trackback_info()}") - role_c.serialize(stg_path) + logger.error(f"Exception in `role_c.run`, detail: {format_trackback_info()}") + write_json_file(role_path, role_c.model_dump()) assert role_c.rc.memory.count() == 1 - new_role_a: Role = Role.deserialize(stg_path) - assert new_role_a.rc.state == 1 + role_dict = read_json_file(role_path) + new_role_c: Role = RoleC(**role_dict) + assert new_role_c.rc.state == 1 with pytest.raises(Exception): - await new_role_a.run(with_message=Message(content="demo", cause_by=UserRequirement)) + await new_role_c.run(with_message=Message(content="demo", cause_by=UserRequirement)) if __name__ == "__main__": diff --git a/tests/metagpt/serialize_deserialize/test_schema.py b/tests/metagpt/serialize_deserialize/test_schema.py index e793079f0..c5a457a1e 100644 --- a/tests/metagpt/serialize_deserialize/test_schema.py +++ b/tests/metagpt/serialize_deserialize/test_schema.py @@ -1,10 +1,11 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- # @Desc : unittest of schema ser&deser +import pytest from metagpt.actions.action_node import ActionNode from metagpt.actions.write_code import WriteCode -from metagpt.schema import Document, Documents, Message +from metagpt.schema import CodingContext, Document, Documents, Message, TestingContext from metagpt.utils.common import any_to_str from tests.metagpt.serialize_deserialize.test_serdeser_base import ( MockICMessage, @@ -12,12 +13,16 @@ ) -def test_message_serdeser(): +def test_message_serdeser_from_create_model(): + with pytest.raises(KeyError): + _ = Message(content="code", instruct_content={"class": "test", "key": "value"}) + out_mapping = {"field3": (str, ...), "field4": (list[str], ...)} out_data = {"field3": "field3 value3", "field4": ["field4 value1", "field4 value2"]} ic_obj = ActionNode.create_model_class("code", out_mapping) + ic_inst = ic_obj(**out_data) - message = Message(content="code", instruct_content=ic_obj(**out_data), role="engineer", cause_by=WriteCode) + message = Message(content="code", instruct_content=ic_inst, role="engineer", cause_by=WriteCode) ser_data = message.model_dump() assert ser_data["cause_by"] == "metagpt.actions.write_code.WriteCode" assert ser_data["instruct_content"]["class"] == "code" @@ -25,28 +30,72 @@ def test_message_serdeser(): new_message = Message(**ser_data) assert new_message.cause_by == any_to_str(WriteCode) assert new_message.cause_by in [any_to_str(WriteCode)] - assert new_message.instruct_content != ic_obj(**out_data) # TODO find why `!=` + + assert new_message.instruct_content == ic_obj(**out_data) + assert new_message.instruct_content == ic_inst assert new_message.instruct_content.model_dump() == ic_obj(**out_data).model_dump() + assert new_message == message - message = Message(content="test_ic", instruct_content=MockICMessage()) + mock_msg = MockMessage() + message = Message(content="test_ic", instruct_content=mock_msg) ser_data = message.model_dump() new_message = Message(**ser_data) - assert new_message.instruct_content != MockICMessage() # TODO - - message = Message(content="test_documents", instruct_content=Documents(docs={"doc1": Document(content="test doc")})) - ser_data = message.model_dump() - assert "class" in ser_data["instruct_content"] + assert new_message.instruct_content == mock_msg + assert new_message == message def test_message_without_postprocess(): - """to explain `instruct_content` should be postprocessed""" + """to explain `instruct_content` from `create_model_class` should be postprocessed""" out_mapping = {"field1": (list[str], ...)} out_data = {"field1": ["field1 value1", "field1 value2"]} ic_obj = ActionNode.create_model_class("code", out_mapping) - message = MockMessage(content="code", instruct_content=ic_obj(**out_data)) + message = MockICMessage(content="code", instruct_content=ic_obj(**out_data)) ser_data = message.model_dump() assert ser_data["instruct_content"] == {} ser_data["instruct_content"] = None - new_message = MockMessage(**ser_data) + new_message = MockICMessage(**ser_data) assert new_message.instruct_content != ic_obj(**out_data) + assert new_message != message + + +def test_message_serdeser_from_basecontext(): + doc_msg = Message(content="test_document", instruct_content=Document(content="test doc")) + ser_data = doc_msg.model_dump() + assert ser_data["instruct_content"]["value"]["content"] == "test doc" + assert ser_data["instruct_content"]["value"]["filename"] == "" + + docs_msg = Message( + content="test_documents", instruct_content=Documents(docs={"doc1": Document(content="test doc")}) + ) + ser_data = docs_msg.model_dump() + assert ser_data["instruct_content"]["class"] == "Documents" + assert ser_data["instruct_content"]["value"]["docs"]["doc1"]["content"] == "test doc" + assert ser_data["instruct_content"]["value"]["docs"]["doc1"]["filename"] == "" + + code_ctxt = CodingContext( + filename="game.py", + design_doc=Document(root_path="docs/system_design", filename="xx.json", content="xxx"), + task_doc=Document(root_path="docs/tasks", filename="xx.json", content="xxx"), + code_doc=Document(root_path="xxx", filename="game.py", content="xxx"), + ) + code_ctxt_msg = Message(content="coding_context", instruct_content=code_ctxt) + ser_data = code_ctxt_msg.model_dump() + assert ser_data["instruct_content"]["class"] == "CodingContext" + + new_code_ctxt_msg = Message(**ser_data) + assert new_code_ctxt_msg.instruct_content == code_ctxt + assert new_code_ctxt_msg.instruct_content.code_doc.filename == "game.py" + assert new_code_ctxt_msg == code_ctxt_msg + + testing_ctxt = TestingContext( + filename="test.py", + code_doc=Document(root_path="xxx", filename="game.py", content="xxx"), + test_doc=Document(root_path="docs/tests", filename="test.py", content="xxx"), + ) + testing_ctxt_msg = Message(content="testing_context", instruct_content=testing_ctxt) + ser_data = testing_ctxt_msg.model_dump() + new_testing_ctxt_msg = Message(**ser_data) + assert new_testing_ctxt_msg.instruct_content == testing_ctxt + assert new_testing_ctxt_msg.instruct_content.test_doc.filename == "test.py" + assert new_testing_ctxt_msg == testing_ctxt_msg diff --git a/tests/metagpt/serialize_deserialize/test_serdeser_base.py b/tests/metagpt/serialize_deserialize/test_serdeser_base.py index daa46c99c..62ab26d72 100644 --- a/tests/metagpt/serialize_deserialize/test_serdeser_base.py +++ b/tests/metagpt/serialize_deserialize/test_serdeser_base.py @@ -16,14 +16,14 @@ serdeser_path = Path(__file__).absolute().parent.joinpath("..", "..", "data", "serdeser_storage") -class MockICMessage(BaseModel): - content: str = "test_ic" +class MockMessage(BaseModel): + content: str = "test_msg" -class MockMessage(BaseModel): +class MockICMessage(BaseModel): """to test normal dict without postprocess""" - content: str = "" + content: str = "test_ic_msg" instruct_content: Optional[BaseModel] = Field(default=None) @@ -67,7 +67,7 @@ class RoleA(Role): def __init__(self, **kwargs): super(RoleA, self).__init__(**kwargs) - self._init_actions([ActionPass]) + self.set_actions([ActionPass]) self._watch([UserRequirement]) @@ -79,7 +79,7 @@ class RoleB(Role): def __init__(self, **kwargs): super(RoleB, self).__init__(**kwargs) - self._init_actions([ActionOK, ActionRaise]) + self.set_actions([ActionOK, ActionRaise]) self._watch([ActionPass]) self.rc.react_mode = RoleReactMode.BY_ORDER @@ -92,7 +92,7 @@ class RoleC(Role): def __init__(self, **kwargs): super(RoleC, self).__init__(**kwargs) - self._init_actions([ActionOK, ActionRaise]) + self.set_actions([ActionOK, ActionRaise]) self._watch([UserRequirement]) self.rc.react_mode = RoleReactMode.BY_ORDER self.rc.memory.ignore_id = True diff --git a/tests/metagpt/serialize_deserialize/test_sk_agent.py b/tests/metagpt/serialize_deserialize/test_sk_agent.py index 7f287b8f9..97c0ade99 100644 --- a/tests/metagpt/serialize_deserialize/test_sk_agent.py +++ b/tests/metagpt/serialize_deserialize/test_sk_agent.py @@ -5,15 +5,8 @@ from metagpt.roles.sk_agent import SkAgent -def test_sk_agent_serialize(): - role = SkAgent() - ser_role_dict = role.model_dump(exclude={"import_semantic_skill_from_directory", "import_skill"}) - assert "name" in ser_role_dict - assert "planner" in ser_role_dict - - @pytest.mark.asyncio -async def test_sk_agent_deserialize(): +async def test_sk_agent_serdeser(): role = SkAgent() ser_role_dict = role.model_dump(exclude={"import_semantic_skill_from_directory", "import_skill"}) assert "name" in ser_role_dict diff --git a/tests/metagpt/serialize_deserialize/test_team.py b/tests/metagpt/serialize_deserialize/test_team.py index 566f63c3d..dbd38422d 100644 --- a/tests/metagpt/serialize_deserialize/test_team.py +++ b/tests/metagpt/serialize_deserialize/test_team.py @@ -4,13 +4,14 @@ # @Desc : import shutil +from pathlib import Path import pytest -from metagpt.const import SERDESER_PATH from metagpt.logs import logger from metagpt.roles import Architect, ProductManager, ProjectManager from metagpt.team import Team +from metagpt.utils.common import write_json_file from tests.metagpt.serialize_deserialize.test_serdeser_base import ( ActionOK, RoleA, @@ -20,8 +21,8 @@ ) -def test_team_deserialize(): - company = Team() +def test_team_deserialize(context): + company = Team(context=context) pm = ProductManager() arch = Architect() @@ -45,9 +46,16 @@ def test_team_deserialize(): assert new_company.env.get_role(arch.profile) is not None -def test_team_serdeser_save(): - company = Team() +def mock_team_serialize(self, stg_path: Path = serdeser_path.joinpath("team")): + team_info_path = stg_path.joinpath("team.json") + write_json_file(team_info_path, self.model_dump()) + + +def test_team_serdeser_save(mocker, context): + mocker.patch("metagpt.team.Team.serialize", mock_team_serialize) + + company = Team(context=context) company.hire([RoleC()]) stg_path = serdeser_path.joinpath("team") @@ -61,12 +69,14 @@ def test_team_serdeser_save(): @pytest.mark.asyncio -async def test_team_recover(): +async def test_team_recover(mocker, context): + mocker.patch("metagpt.team.Team.serialize", mock_team_serialize) + idea = "write a snake game" - stg_path = SERDESER_PATH.joinpath("team") + stg_path = serdeser_path.joinpath("team") shutil.rmtree(stg_path, ignore_errors=True) - company = Team() + company = Team(context=context) role_c = RoleC() company.hire([role_c]) company.run_project(idea) @@ -75,9 +85,9 @@ async def test_team_recover(): ser_data = company.model_dump() new_company = Team(**ser_data) - new_company.env.get_role(role_c.profile) - # assert new_role_c.rc.memory == role_c.rc.memory # TODO - # assert new_role_c.rc.env != role_c.rc.env # TODO + new_role_c = new_company.env.get_role(role_c.profile) + assert new_role_c.rc.memory == role_c.rc.memory + assert new_role_c.rc.env != role_c.rc.env assert type(list(new_company.env.roles.values())[0].actions[0]) == ActionOK new_company.run_project(idea) @@ -85,12 +95,14 @@ async def test_team_recover(): @pytest.mark.asyncio -async def test_team_recover_save(): +async def test_team_recover_save(mocker, context): + mocker.patch("metagpt.team.Team.serialize", mock_team_serialize) + idea = "write a 2048 web game" - stg_path = SERDESER_PATH.joinpath("team") + stg_path = serdeser_path.joinpath("team") shutil.rmtree(stg_path, ignore_errors=True) - company = Team() + company = Team(context=context) role_c = RoleC() company.hire([role_c]) company.run_project(idea) @@ -98,8 +110,8 @@ async def test_team_recover_save(): new_company = Team.deserialize(stg_path) new_role_c = new_company.env.get_role(role_c.profile) - # assert new_role_c.rc.memory == role_c.rc.memory - # assert new_role_c.rc.env != role_c.rc.env + assert new_role_c.rc.memory == role_c.rc.memory + assert new_role_c.rc.env != role_c.rc.env assert new_role_c.recovered != role_c.recovered # here cause previous ut is `!=` assert new_role_c.rc.todo != role_c.rc.todo # serialize exclude `rc.todo` assert new_role_c.rc.news != role_c.rc.news # serialize exclude `rc.news` @@ -109,15 +121,17 @@ async def test_team_recover_save(): @pytest.mark.asyncio -async def test_team_recover_multi_roles_save(): +async def test_team_recover_multi_roles_save(mocker, context): + mocker.patch("metagpt.team.Team.serialize", mock_team_serialize) + idea = "write a snake game" - stg_path = SERDESER_PATH.joinpath("team") + stg_path = serdeser_path.joinpath("team") shutil.rmtree(stg_path, ignore_errors=True) role_a = RoleA() role_b = RoleB() - company = Team() + company = Team(context=context) company.hire([role_a, role_b]) company.run_project(idea) await company.run(n_round=4) @@ -130,3 +144,7 @@ async def test_team_recover_multi_roles_save(): assert new_company.env.get_role(role_b.profile).rc.state == 1 await new_company.run(n_round=4) + + +if __name__ == "__main__": + pytest.main([__file__, "-s"]) diff --git a/tests/metagpt/serialize_deserialize/test_tutorial_assistant.py b/tests/metagpt/serialize_deserialize/test_tutorial_assistant.py index e642dae54..ab5db4c57 100644 --- a/tests/metagpt/serialize_deserialize/test_tutorial_assistant.py +++ b/tests/metagpt/serialize_deserialize/test_tutorial_assistant.py @@ -7,7 +7,7 @@ @pytest.mark.asyncio -async def test_tutorial_assistant_deserialize(): +async def test_tutorial_assistant_serdeser(context): role = TutorialAssistant() ser_role_dict = role.model_dump() assert "name" in ser_role_dict diff --git a/tests/metagpt/serialize_deserialize/test_write_code.py b/tests/metagpt/serialize_deserialize/test_write_code.py index cb262bb45..2f3c08f9b 100644 --- a/tests/metagpt/serialize_deserialize/test_write_code.py +++ b/tests/metagpt/serialize_deserialize/test_write_code.py @@ -9,22 +9,23 @@ from metagpt.schema import CodingContext, Document -def test_write_design_serialize(): - action = WriteCode() +def test_write_design_serdeser(context): + action = WriteCode(context=context) ser_action_dict = action.model_dump() assert ser_action_dict["name"] == "WriteCode" assert "llm" not in ser_action_dict # not export @pytest.mark.asyncio -async def test_write_code_deserialize(): - context = CodingContext( +async def test_write_code_serdeser(context): + context.src_workspace = context.repo.workdir / "srcs" + coding_context = CodingContext( filename="test_code.py", design_doc=Document(content="write add function to calculate two numbers") ) - doc = Document(content=context.model_dump_json()) - action = WriteCode(context=doc) + doc = Document(content=coding_context.model_dump_json()) + action = WriteCode(i_context=doc, context=context) serialized_data = action.model_dump() - new_action = WriteCode(**serialized_data) + new_action = WriteCode(**serialized_data, context=context) assert new_action.name == "WriteCode" await action.run() diff --git a/tests/metagpt/serialize_deserialize/test_write_code_review.py b/tests/metagpt/serialize_deserialize/test_write_code_review.py index 991b3c13b..32a017a97 100644 --- a/tests/metagpt/serialize_deserialize/test_write_code_review.py +++ b/tests/metagpt/serialize_deserialize/test_write_code_review.py @@ -9,22 +9,23 @@ @pytest.mark.asyncio -async def test_write_code_review_deserialize(): +async def test_write_code_review_serdeser(context): + context.src_workspace = context.repo.workdir / "srcs" code_content = """ def div(a: int, b: int = 0): return a / b """ - context = CodingContext( + coding_context = CodingContext( filename="test_op.py", design_doc=Document(content="divide two numbers"), code_doc=Document(content=code_content), ) - action = WriteCodeReview(context=context) + action = WriteCodeReview(i_context=coding_context) serialized_data = action.model_dump() assert serialized_data["name"] == "WriteCodeReview" - new_action = WriteCodeReview(**serialized_data) + new_action = WriteCodeReview(**serialized_data, context=context) assert new_action.name == "WriteCodeReview" await new_action.run() diff --git a/tests/metagpt/serialize_deserialize/test_write_design.py b/tests/metagpt/serialize_deserialize/test_write_design.py index 7bcba3fc8..6519d8cdc 100644 --- a/tests/metagpt/serialize_deserialize/test_write_design.py +++ b/tests/metagpt/serialize_deserialize/test_write_design.py @@ -7,33 +7,25 @@ from metagpt.actions import WriteDesign, WriteTasks -def test_write_design_serialize(): - action = WriteDesign() - ser_action_dict = action.model_dump() - assert "name" in ser_action_dict - assert "llm" not in ser_action_dict # not export - - -def test_write_task_serialize(): - action = WriteTasks() +@pytest.mark.asyncio +async def test_write_design_serialize(context): + action = WriteDesign(context=context) ser_action_dict = action.model_dump() assert "name" in ser_action_dict assert "llm" not in ser_action_dict # not export - -@pytest.mark.asyncio -async def test_write_design_deserialize(): - action = WriteDesign() - serialized_data = action.model_dump() - new_action = WriteDesign(**serialized_data) + new_action = WriteDesign(**ser_action_dict, context=context) assert new_action.name == "WriteDesign" await new_action.run(with_messages="write a cli snake game") @pytest.mark.asyncio -async def test_write_task_deserialize(): - action = WriteTasks() - serialized_data = action.model_dump() - new_action = WriteTasks(**serialized_data) +async def test_write_task_serialize(context): + action = WriteTasks(context=context) + ser_action_dict = action.model_dump() + assert "name" in ser_action_dict + assert "llm" not in ser_action_dict # not export + + new_action = WriteTasks(**ser_action_dict, context=context) assert new_action.name == "WriteTasks" await new_action.run(with_messages="write a cli snake game") diff --git a/tests/metagpt/serialize_deserialize/test_write_docstring.py b/tests/metagpt/serialize_deserialize/test_write_docstring.py index e4116ab30..363bed05e 100644 --- a/tests/metagpt/serialize_deserialize/test_write_docstring.py +++ b/tests/metagpt/serialize_deserialize/test_write_docstring.py @@ -29,14 +29,14 @@ def greet(self): ], ids=["google", "numpy", "sphinx"], ) -async def test_action_deserialize(style: str, part: str): - action = WriteDocstring() +async def test_action_serdeser(style: str, part: str, context): + action = WriteDocstring(context=context) serialized_data = action.model_dump() assert "name" in serialized_data assert serialized_data["desc"] == "Write docstring for code." - new_action = WriteDocstring(**serialized_data) + new_action = WriteDocstring(**serialized_data, context=context) assert new_action.name == "WriteDocstring" assert new_action.desc == "Write docstring for code." diff --git a/tests/metagpt/serialize_deserialize/test_write_prd.py b/tests/metagpt/serialize_deserialize/test_write_prd.py index b9eff5a19..e4951efb7 100644 --- a/tests/metagpt/serialize_deserialize/test_write_prd.py +++ b/tests/metagpt/serialize_deserialize/test_write_prd.py @@ -9,18 +9,14 @@ from metagpt.schema import Message -def test_action_serialize(new_filename): - action = WritePRD() +@pytest.mark.asyncio +async def test_action_serdeser(new_filename, context): + action = WritePRD(context=context) ser_action_dict = action.model_dump() assert "name" in ser_action_dict assert "llm" not in ser_action_dict # not export - -@pytest.mark.asyncio -async def test_action_deserialize(new_filename): - action = WritePRD() - serialized_data = action.model_dump() - new_action = WritePRD(**serialized_data) + new_action = WritePRD(**ser_action_dict, context=context) assert new_action.name == "WritePRD" - action_output = await new_action.run(with_messages=Message(content="write a cli snake game")) - assert len(action_output.content) > 0 + with pytest.raises(FileNotFoundError): + await new_action.run(with_messages=Message(content="write a cli snake game")) diff --git a/tests/metagpt/serialize_deserialize/test_write_review.py b/tests/metagpt/serialize_deserialize/test_write_review.py index f02a01910..de2fd9d7a 100644 --- a/tests/metagpt/serialize_deserialize/test_write_review.py +++ b/tests/metagpt/serialize_deserialize/test_write_review.py @@ -5,7 +5,7 @@ from metagpt.actions.action_node import ActionNode from metagpt.actions.write_review import WriteReview -CONTEXT = """ +TEMPLATE_CONTEXT = """ { "Language": "zh_cn", "Programming Language": "Python", @@ -42,13 +42,13 @@ @pytest.mark.asyncio -async def test_action_deserialize(): - action = WriteReview() +async def test_action_serdeser(context): + action = WriteReview(context=context) serialized_data = action.model_dump() assert serialized_data["name"] == "WriteReview" - new_action = WriteReview(**serialized_data) - review = await new_action.run(CONTEXT) + new_action = WriteReview(**serialized_data, context=context) + review = await new_action.run(TEMPLATE_CONTEXT) assert new_action.name == "WriteReview" assert type(review) == ActionNode diff --git a/tests/metagpt/serialize_deserialize/test_write_tutorial.py b/tests/metagpt/serialize_deserialize/test_write_tutorial.py index 606a90f8c..d41b7b341 100644 --- a/tests/metagpt/serialize_deserialize/test_write_tutorial.py +++ b/tests/metagpt/serialize_deserialize/test_write_tutorial.py @@ -9,13 +9,13 @@ @pytest.mark.asyncio @pytest.mark.parametrize(("language", "topic"), [("English", "Write a tutorial about Python")]) -async def test_write_directory_deserialize(language: str, topic: str): - action = WriteDirectory() +async def test_write_directory_serdeser(language: str, topic: str, context): + action = WriteDirectory(context=context) serialized_data = action.model_dump() assert serialized_data["name"] == "WriteDirectory" assert serialized_data["language"] == "Chinese" - new_action = WriteDirectory(**serialized_data) + new_action = WriteDirectory(**serialized_data, context=context) ret = await new_action.run(topic=topic) assert isinstance(ret, dict) assert "title" in ret @@ -30,12 +30,12 @@ async def test_write_directory_deserialize(language: str, topic: str): ("language", "topic", "directory"), [("English", "Write a tutorial about Python", {"Introduction": ["What is Python?", "Why learn Python?"]})], ) -async def test_write_content_deserialize(language: str, topic: str, directory: Dict): - action = WriteContent(language=language, directory=directory) +async def test_write_content_serdeser(language: str, topic: str, directory: Dict, context): + action = WriteContent(language=language, directory=directory, context=context) serialized_data = action.model_dump() assert serialized_data["name"] == "WriteContent" - new_action = WriteContent(**serialized_data) + new_action = WriteContent(**serialized_data, context=context) ret = await new_action.run(topic=topic) assert isinstance(ret, str) assert list(directory.keys())[0] in ret diff --git a/tests/metagpt/strategy/examples/creative_writing.py b/tests/metagpt/strategy/examples/test_creative_writing.py similarity index 100% rename from tests/metagpt/strategy/examples/creative_writing.py rename to tests/metagpt/strategy/examples/test_creative_writing.py diff --git a/tests/metagpt/strategy/examples/game24.py b/tests/metagpt/strategy/examples/test_game24.py similarity index 100% rename from tests/metagpt/strategy/examples/game24.py rename to tests/metagpt/strategy/examples/test_game24.py diff --git a/tests/metagpt/strategy/test_solver.py b/tests/metagpt/strategy/test_solver.py new file mode 100644 index 000000000..eae4a5a2a --- /dev/null +++ b/tests/metagpt/strategy/test_solver.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2024/1/31 13:54 +@Author : alexanderwu +@File : test_solver.py +""" +import pytest + +from metagpt.actions.action_graph import ActionGraph +from metagpt.llm import LLM +from metagpt.strategy.search_space import SearchSpace +from metagpt.strategy.solver import NaiveSolver + + +@pytest.mark.asyncio +async def test_solver(): + from metagpt.actions.write_prd_an import ( + COMPETITIVE_ANALYSIS, + ISSUE_TYPE, + PRODUCT_GOALS, + REQUIREMENT_POOL, + ) + + graph = ActionGraph() + graph.add_node(ISSUE_TYPE) + graph.add_node(PRODUCT_GOALS) + graph.add_node(COMPETITIVE_ANALYSIS) + graph.add_node(REQUIREMENT_POOL) + graph.add_edge(ISSUE_TYPE, PRODUCT_GOALS) + graph.add_edge(PRODUCT_GOALS, COMPETITIVE_ANALYSIS) + graph.add_edge(PRODUCT_GOALS, REQUIREMENT_POOL) + graph.add_edge(COMPETITIVE_ANALYSIS, REQUIREMENT_POOL) + search_space = SearchSpace() + llm = LLM() + context = "Create a 2048 game" + solver = NaiveSolver(graph, search_space, llm, context) + await solver.solve() + + print("## graph.nodes") + print(graph.nodes) + for k, v in graph.nodes.items(): + print(f"{v.key} | prevs: {[i.key for i in v.prevs]} | nexts: {[i.key for i in v.nexts]}") + + assert len(graph.nodes) == 4 + assert len(graph.execution_order) == 4 + assert graph.execution_order == [ISSUE_TYPE.key, PRODUCT_GOALS.key, COMPETITIVE_ANALYSIS.key, REQUIREMENT_POOL.key] diff --git a/tests/metagpt/test_config.py b/tests/metagpt/test_config.py new file mode 100644 index 000000000..7ce5765cf --- /dev/null +++ b/tests/metagpt/test_config.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2024/1/9 15:57 +@Author : alexanderwu +@File : test_config.py +""" + +from metagpt.config2 import Config +from metagpt.configs.llm_config import LLMType +from tests.metagpt.provider.mock_llm_config import mock_llm_config + + +def test_config_1(): + cfg = Config.default() + llm = cfg.get_openai_llm() + assert llm is not None + assert llm.api_type == LLMType.OPENAI + + +def test_config_from_dict(): + cfg = Config(llm=mock_llm_config) + assert cfg + assert cfg.llm.api_key == "mock_api_key" diff --git a/tests/metagpt/test_context.py b/tests/metagpt/test_context.py new file mode 100644 index 000000000..f8218c44d --- /dev/null +++ b/tests/metagpt/test_context.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2024/1/9 13:52 +@Author : alexanderwu +@File : test_context.py +""" +from metagpt.configs.llm_config import LLMType +from metagpt.context import AttrDict, Context + + +def test_attr_dict_1(): + ad = AttrDict(name="John", age=30) + assert ad.name == "John" + assert ad.age == 30 + assert ad.height is None + + +def test_attr_dict_2(): + ad = AttrDict(name="John", age=30) + ad.height = 180 + assert ad.height == 180 + + +def test_attr_dict_3(): + ad = AttrDict(name="John", age=30) + del ad.age + assert ad.age is None + + +def test_attr_dict_4(): + ad = AttrDict(name="John", age=30) + try: + del ad.weight + except AttributeError as e: + assert str(e) == "No such attribute: weight" + + +def test_attr_dict_5(): + ad = AttrDict.model_validate({"name": "John", "age": 30}) + assert ad.name == "John" + assert ad.age == 30 + + +def test_context_1(): + ctx = Context() + assert ctx.config is not None + assert ctx.git_repo is None + assert ctx.src_workspace is None + assert ctx.cost_manager is not None + + +def test_context_2(): + ctx = Context() + llm = ctx.config.get_openai_llm() + assert llm is not None + assert llm.api_type == LLMType.OPENAI + + kwargs = ctx.kwargs + assert kwargs is not None + + kwargs.test_key = "test_value" + assert kwargs.test_key == "test_value" + + +def test_context_3(): + # ctx = Context() + # ctx.use_llm(provider=LLMType.OPENAI) + # assert ctx._llm_config is not None + # assert ctx._llm_config.api_type == LLMType.OPENAI + # assert ctx.llm() is not None + # assert "gpt" in ctx.llm().model + pass diff --git a/tests/metagpt/test_context_mixin.py b/tests/metagpt/test_context_mixin.py new file mode 100644 index 000000000..4389dc251 --- /dev/null +++ b/tests/metagpt/test_context_mixin.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2024/1/11 19:24 +@Author : alexanderwu +@File : test_context_mixin.py +""" +from pathlib import Path + +import pytest +from pydantic import BaseModel + +from metagpt.actions import Action +from metagpt.config2 import Config +from metagpt.const import CONFIG_ROOT +from metagpt.context_mixin import ContextMixin +from metagpt.environment import Environment +from metagpt.roles import Role +from metagpt.team import Team +from tests.metagpt.provider.mock_llm_config import ( + mock_llm_config, + mock_llm_config_proxy, + mock_llm_config_zhipu, +) + + +class ModelX(ContextMixin, BaseModel): + a: str = "a" + b: str = "b" + + +class WTFMixin(BaseModel): + c: str = "c" + d: str = "d" + + +class ModelY(WTFMixin, ModelX): + pass + + +def test_config_mixin_1(): + new_model = ModelX() + assert new_model.a == "a" + assert new_model.b == "b" + + +def test_config_mixin_2(): + i = Config(llm=mock_llm_config) + j = Config(llm=mock_llm_config_proxy) + obj = ModelX(config=i) + assert obj.config == i + assert obj.config.llm == mock_llm_config + + obj.set_config(j) + # obj already has a config, so it will not be set + assert obj.config == i + + +def test_config_mixin_3_multi_inheritance_not_override_config(): + """Test config mixin with multiple inheritance""" + i = Config(llm=mock_llm_config) + j = Config(llm=mock_llm_config_proxy) + obj = ModelY(config=i) + assert obj.config == i + assert obj.config.llm == mock_llm_config + + obj.set_config(j) + # obj already has a config, so it will not be set + assert obj.config == i + assert obj.config.llm == mock_llm_config + + assert obj.a == "a" + assert obj.b == "b" + assert obj.c == "c" + assert obj.d == "d" + + print(obj.__dict__.keys()) + assert "private_config" in obj.__dict__.keys() + + +def test_config_mixin_4_multi_inheritance_override_config(): + """Test config mixin with multiple inheritance""" + i = Config(llm=mock_llm_config) + j = Config(llm=mock_llm_config_zhipu) + obj = ModelY(config=i) + assert obj.config == i + assert obj.config.llm == mock_llm_config + + obj.set_config(j, override=True) + # override obj.config + assert obj.config == j + assert obj.config.llm == mock_llm_config_zhipu + + assert obj.a == "a" + assert obj.b == "b" + assert obj.c == "c" + assert obj.d == "d" + + print(obj.__dict__.keys()) + assert "private_config" in obj.__dict__.keys() + assert obj.config.llm.model == "mock_zhipu_model" + + +@pytest.mark.asyncio +async def test_config_priority(): + """If action's config is set, then its llm will be set, otherwise, it will use the role's llm""" + home_dir = Path.home() / CONFIG_ROOT + gpt4t = Config.from_home("gpt-4-1106-preview.yaml") + if not home_dir.exists(): + assert gpt4t is None + gpt35 = Config.default() + gpt35.llm.model = "gpt-3.5-turbo-1106" + gpt4 = Config.default() + gpt4.llm.model = "gpt-4-0613" + + a1 = Action(config=gpt4t, name="Say", instruction="Say your opinion with emotion and don't repeat it") + a2 = Action(name="Say", instruction="Say your opinion with emotion and don't repeat it") + a3 = Action(name="Vote", instruction="Vote for the candidate, and say why you vote for him/her") + + # it will not work for a1 because the config is already set + A = Role(name="A", profile="Democratic candidate", goal="Win the election", actions=[a1], watch=[a2], config=gpt4) + # it will work for a2 because the config is not set + B = Role(name="B", profile="Republican candidate", goal="Win the election", actions=[a2], watch=[a1], config=gpt4) + # ditto + C = Role(name="C", profile="Voter", goal="Vote for the candidate", actions=[a3], watch=[a1, a2], config=gpt35) + + env = Environment(desc="US election live broadcast") + Team(investment=10.0, env=env, roles=[A, B, C]) + + assert a1.llm.model == "gpt-4-1106-preview" if Path(home_dir / "gpt-4-1106-preview.yaml").exists() else "gpt-4-0613" + assert a2.llm.model == "gpt-4-0613" + assert a3.llm.model == "gpt-3.5-turbo-1106" + + # history = await team.run(idea="Topic: climate change. Under 80 words per message.", send_to="a1", n_round=3) diff --git a/tests/metagpt/test_document.py b/tests/metagpt/test_document.py index 18650e112..9c076f4e6 100644 --- a/tests/metagpt/test_document.py +++ b/tests/metagpt/test_document.py @@ -5,7 +5,7 @@ @Author : alexanderwu @File : test_document.py """ -from metagpt.config import CONFIG +from metagpt.config2 import config from metagpt.document import Repo from metagpt.logs import logger @@ -28,6 +28,6 @@ def load_existing_repo(path): def test_repo_set_load(): - repo_path = CONFIG.workspace_path / "test_repo" + repo_path = config.workspace.path / "test_repo" set_existing_repo(repo_path) load_existing_repo(repo_path) diff --git a/tests/metagpt/test_environment.py b/tests/metagpt/test_environment.py index 90e4b5b42..7559655d3 100644 --- a/tests/metagpt/test_environment.py +++ b/tests/metagpt/test_environment.py @@ -4,8 +4,6 @@ @Time : 2023/5/12 00:47 @Author : alexanderwu @File : test_environment.py -@Modified By: mashenquan, 2023/8/20. Remove global configuration `CONFIG`, enable configuration support for business isolation. - """ from pathlib import Path @@ -13,7 +11,6 @@ import pytest from metagpt.actions import UserRequirement -from metagpt.config import CONFIG from metagpt.environment import Environment from metagpt.logs import logger from metagpt.roles import Architect, ProductManager, Role @@ -45,10 +42,10 @@ def test_get_roles(env: Environment): @pytest.mark.asyncio -async def test_publish_and_process_message(env: Environment, new_filename): - if CONFIG.git_repo: - CONFIG.git_repo.delete_repository() - CONFIG.git_repo = None +async def test_publish_and_process_message(env: Environment): + if env.context.git_repo: + env.context.git_repo.delete_repository() + env.context.git_repo = None product_manager = ProductManager(name="Alice", profile="Product Manager", goal="做AI Native产品", constraints="资源有限") architect = Architect( diff --git a/tests/metagpt/test_gpt.py b/tests/metagpt/test_gpt.py deleted file mode 100644 index 2b19f173d..000000000 --- a/tests/metagpt/test_gpt.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -@Time : 2023/4/29 19:47 -@Author : alexanderwu -@File : test_gpt.py -""" -import openai -import pytest - -from metagpt.config import CONFIG -from metagpt.logs import logger - - -@pytest.mark.usefixtures("llm_api") -class TestGPT: - @pytest.mark.asyncio - async def test_llm_api_aask(self, llm_api): - answer = await llm_api.aask("hello chatgpt", stream=False) - logger.info(answer) - assert len(answer) > 0 - - answer = await llm_api.aask("hello chatgpt", stream=True) - logger.info(answer) - assert len(answer) > 0 - - @pytest.mark.asyncio - async def test_llm_api_aask_code(self, llm_api): - try: - answer = await llm_api.aask_code(["请扮演一个Google Python专家工程师,如果理解,回复明白", "写一个hello world"], timeout=60) - logger.info(answer) - assert len(answer) > 0 - except openai.BadRequestError: - assert CONFIG.OPENAI_API_TYPE == "azure" - - @pytest.mark.asyncio - async def test_llm_api_costs(self, llm_api): - await llm_api.aask("hello chatgpt", stream=False) - costs = llm_api.get_costs() - logger.info(costs) - assert costs.total_cost > 0 - - -if __name__ == "__main__": - pytest.main([__file__, "-s"]) diff --git a/tests/metagpt/test_incremental_dev.py b/tests/metagpt/test_incremental_dev.py index 41ba785c4..c47397dd7 100644 --- a/tests/metagpt/test_incremental_dev.py +++ b/tests/metagpt/test_incremental_dev.py @@ -6,6 +6,7 @@ @File : test_incremental_dev.py """ import os +import shutil import subprocess import time @@ -14,7 +15,7 @@ from metagpt.const import TEST_DATA_PATH from metagpt.logs import logger -from metagpt.startup import app +from metagpt.software_company import app runner = CliRunner() @@ -45,38 +46,45 @@ ] +@pytest.mark.skip def test_simple_add_calculator(): result = get_incremental_dev_result(IDEAS[0], PROJECT_NAMES[0]) log_and_check_result(result) +@pytest.mark.skip def test_number_guessing_game(): result = get_incremental_dev_result(IDEAS[1], PROJECT_NAMES[1]) log_and_check_result(result) +@pytest.mark.skip def test_word_cloud(): result = get_incremental_dev_result(IDEAS[2], PROJECT_NAMES[2]) log_and_check_result(result) +@pytest.mark.skip def test_gomoku(): result = get_incremental_dev_result(IDEAS[3], PROJECT_NAMES[3]) log_and_check_result(result) +@pytest.mark.skip def test_dice_simulator_new(): for i, (idea, project_name) in enumerate(zip(IDEAS[4:6], PROJECT_NAMES[4:6]), start=1): result = get_incremental_dev_result(idea, project_name) log_and_check_result(result, "refine_" + str(i)) +@pytest.mark.skip def test_refined_pygame_2048(): for i, (idea, project_name) in enumerate(zip(IDEAS[6:8], PROJECT_NAMES[6:8]), start=1): result = get_incremental_dev_result(idea, project_name) log_and_check_result(result, "refine_" + str(i)) +@pytest.mark.skip def test_refined_snake_game(): for i, (idea, project_name) in enumerate(zip(IDEAS[8:10], PROJECT_NAMES[8:10]), start=1): result = get_incremental_dev_result(idea, project_name) @@ -105,10 +113,22 @@ def log_and_check_result(result, tag_name="refine"): def get_incremental_dev_result(idea, project_name, use_review=True): project_path = TEST_DATA_PATH / "incremental_dev_project" / project_name - if project_path.exists(): - raise Exception(f"Project {project_name} not exists") + # Check if the project path exists + if not project_path.exists(): + # If the project does not exist, extract the project file + try: + if shutil.which("unzip"): + subprocess.run(["unzip", f"{project_path}.zip", "-d", str(project_path.parent)], check=True) + elif shutil.which("tar"): + subprocess.run(["tar", "-xf", f"{project_path}.zip", "-C", str(project_path.parent)], check=True) + logger.info(f"Extracted project {project_name} successfully.") + except FileNotFoundError as e: + raise FileNotFoundError(f"Neither 'unzip' nor 'tar' command found. Error: {e}") + except subprocess.CalledProcessError as e: + raise Exception(f"Failed to extract project {project_name}. Error: {e}") + check_or_create_base_tag(project_path) - args = [idea, "--inc", "--project-path", project_path] + args = [idea, "--inc", "--project-path", project_path, "--n-round", "20"] if not use_review: args.append("--no-code-review") result = runner.invoke(app, args) @@ -146,14 +166,18 @@ def check_or_create_base_tag(project_path): logger.info("Base tag doesn't exist.") # Add and commit the current code if 'base' tag doesn't exist add_cmd = ["git", "add", "."] - commit_cmd = ["git", "commit", "-m", "Initial commit"] try: subprocess.run(add_cmd, check=True) + logger.info("Files added successfully.") + except subprocess.CalledProcessError as e: + logger.error(f"Failed to add files: {e}") + + commit_cmd = ["git", "commit", "-m", "Initial commit"] + try: subprocess.run(commit_cmd, check=True) - logger.info("Added and committed all files with the message 'Initial commit'.") - except Exception as e: - logger.error("Failed to add and commit all files.") - raise e + logger.info("Committed all files with the message 'Initial commit'.") + except subprocess.CalledProcessError as e: + logger.error(f"Failed to commit: {e.stderr}") # Add 'base' tag add_base_tag_cmd = ["git", "tag", "base"] diff --git a/tests/metagpt/test_llm.py b/tests/metagpt/test_llm.py index 247f043e2..d46a29c7f 100644 --- a/tests/metagpt/test_llm.py +++ b/tests/metagpt/test_llm.py @@ -4,12 +4,11 @@ @Time : 2023/5/11 14:45 @Author : alexanderwu @File : test_llm.py -@Modified By: mashenquan, 2023/8/20. Remove global configuration `CONFIG`, enable configuration support for business isolation. """ import pytest -from metagpt.provider.openai_api import OpenAILLM as LLM +from metagpt.llm import LLM @pytest.fixture() @@ -23,6 +22,12 @@ async def test_llm_aask(llm): assert len(rsp) > 0 +@pytest.mark.asyncio +async def test_llm_aask_stream(llm): + rsp = await llm.aask("hello world", stream=True) + assert len(rsp) > 0 + + @pytest.mark.asyncio async def test_llm_acompletion(llm): hello_msg = [{"role": "user", "content": "hello"}] diff --git a/tests/metagpt/test_manager.py b/tests/metagpt/test_manager.py deleted file mode 100644 index 5c2a2c795..000000000 --- a/tests/metagpt/test_manager.py +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -@Time : 2023/5/11 14:45 -@Author : alexanderwu -@File : test_manager.py -""" diff --git a/tests/metagpt/test_role.py b/tests/metagpt/test_role.py index 52d08e92e..7e707803b 100644 --- a/tests/metagpt/test_role.py +++ b/tests/metagpt/test_role.py @@ -33,16 +33,16 @@ async def run(self, messages, *args, **kwargs): class MockRole(Role): def __init__(self, name="", profile="", goal="", constraints="", desc=""): super().__init__(name=name, profile=profile, goal=goal, constraints=constraints, desc=desc) - self._init_actions([MockAction()]) + self.set_actions([MockAction()]) def test_basic(): mock_role = MockRole() - assert mock_role.subscription == {"tests.metagpt.test_role.MockRole"} + assert mock_role.addresses == ({"tests.metagpt.test_role.MockRole"}) assert mock_role.rc.watch == {"metagpt.actions.add_requirement.UserRequirement"} mock_role = MockRole(name="mock_role") - assert mock_role.subscription == {"tests.metagpt.test_role.MockRole", "mock_role"} + assert mock_role.addresses == {"tests.metagpt.test_role.MockRole", "mock_role"} @pytest.mark.asyncio @@ -53,7 +53,7 @@ class Input(BaseModel): goal: str constraints: str desc: str - subscription: str + address: str inputs = [ { @@ -62,7 +62,7 @@ class Input(BaseModel): "goal": "Test", "constraints": "constraints", "desc": "desc", - "subscription": "start", + "address": "start", } ] @@ -71,7 +71,7 @@ class Input(BaseModel): role = MockRole( name=seed.name, profile=seed.profile, goal=seed.goal, constraints=seed.constraints, desc=seed.desc ) - role.subscribe({seed.subscription}) + role.set_addresses({seed.address}) assert role.rc.watch == {any_to_str(UserRequirement)} assert role.name == seed.name assert role.profile == seed.profile @@ -81,20 +81,20 @@ class Input(BaseModel): assert role.is_idle env = Environment() env.add_role(role) - assert env.get_subscription(role) == {seed.subscription} - env.publish_message(Message(content="test", msg_to=seed.subscription)) + assert env.get_addresses(role) == {seed.address} + env.publish_message(Message(content="test", msg_to=seed.address)) assert not role.is_idle while not env.is_idle: await env.run() assert role.is_idle - env.publish_message(Message(content="test", cause_by=seed.subscription)) + env.publish_message(Message(content="test", cause_by=seed.address)) assert not role.is_idle while not env.is_idle: await env.run() assert role.is_idle tag = uuid.uuid4().hex - role.subscribe({tag}) - assert env.get_subscription(role) == {tag} + role.set_addresses({tag}) + assert env.get_addresses(role) == {tag} @pytest.mark.asyncio @@ -111,8 +111,8 @@ async def test_send_to(): def test_init_action(): role = Role() - role.init_actions([MockAction, MockAction]) - assert role.action_count == 2 + role.set_actions([MockAction, MockAction]) + assert len(role.actions) == 2 @pytest.mark.asyncio @@ -127,11 +127,11 @@ async def test_recover(): role.publish_message(None) role.llm = mock_llm - role.init_actions([MockAction, MockAction]) + role.set_actions([MockAction, MockAction]) role.recovered = True role.latest_observed_msg = Message(content="recover_test") role.rc.state = 0 - assert role.todo == any_to_name(MockAction) + assert role.action_description == any_to_name(MockAction) rsp = await role.run() assert rsp.cause_by == any_to_str(MockAction) @@ -144,7 +144,7 @@ async def test_think_act(): mock_llm.aask.side_effect = ["ok"] role = Role() - role.init_actions([MockAction]) + role.set_actions([MockAction]) await role.think() role.rc.memory.add(Message("run")) assert len(role.get_memories()) == 1 diff --git a/tests/metagpt/test_schema.py b/tests/metagpt/test_schema.py index b6e334fbe..a8fa27151 100644 --- a/tests/metagpt/test_schema.py +++ b/tests/metagpt/test_schema.py @@ -15,7 +15,6 @@ from metagpt.actions import Action from metagpt.actions.action_node import ActionNode from metagpt.actions.write_code import WriteCode -from metagpt.config import CONFIG from metagpt.const import SYSTEM_DESIGN_FILE_REPO, TASK_FILE_REPO from metagpt.schema import ( AIMessage, @@ -26,7 +25,9 @@ Document, Message, MessageQueue, + Plan, SystemMessage, + Task, UserMessage, ) from metagpt.utils.common import any_to_str @@ -103,7 +104,7 @@ def test_message_serdeser(): new_message = Message.model_validate(message_dict) assert new_message.content == message.content assert new_message.instruct_content.model_dump() == message.instruct_content.model_dump() - assert new_message.instruct_content != message.instruct_content # TODO + assert new_message.instruct_content == message.instruct_content # TODO assert new_message.cause_by == message.cause_by assert new_message.instruct_content.field3 == out_data["field3"] @@ -122,8 +123,6 @@ def test_document(): assert doc.filename == meta_doc.filename assert meta_doc.content == "" - assert doc.full_path == str(CONFIG.git_repo.workdir / doc.root_path / doc.filename) - @pytest.mark.asyncio async def test_message_queue(): @@ -184,5 +183,173 @@ def test_class_view(): ) +class TestPlan: + def test_add_tasks_ordering(self): + plan = Plan(goal="") + + tasks = [ + Task(task_id="1", dependent_task_ids=["2", "3"], instruction="Third"), + Task(task_id="2", instruction="First"), + Task(task_id="3", dependent_task_ids=["2"], instruction="Second"), + ] # 2 -> 3 -> 1 + plan.add_tasks(tasks) + + assert [task.task_id for task in plan.tasks] == ["2", "3", "1"] + + def test_add_tasks_to_existing_no_common_prefix(self): + plan = Plan(goal="") + + tasks = [ + Task(task_id="1", dependent_task_ids=["2", "3"], instruction="Third"), + Task(task_id="2", instruction="First"), + Task(task_id="3", dependent_task_ids=["2"], instruction="Second", is_finished=True), + ] # 2 -> 3 -> 1 + plan.add_tasks(tasks) + + new_tasks = [Task(task_id="3", instruction="")] + plan.add_tasks(new_tasks) + + assert [task.task_id for task in plan.tasks] == ["3"] + assert not plan.tasks[0].is_finished # must be the new unfinished task + + def test_add_tasks_to_existing_with_common_prefix(self): + plan = Plan(goal="") + + tasks = [ + Task(task_id="1", dependent_task_ids=["2", "3"], instruction="Third"), + Task(task_id="2", instruction="First"), + Task(task_id="3", dependent_task_ids=["2"], instruction="Second"), + ] # 2 -> 3 -> 1 + plan.add_tasks(tasks) + plan.finish_current_task() # finish 2 + plan.finish_current_task() # finish 3 + + new_tasks = [ + Task(task_id="4", dependent_task_ids=["3"], instruction="Third"), + Task(task_id="2", instruction="First"), + Task(task_id="3", dependent_task_ids=["2"], instruction="Second"), + ] # 2 -> 3 -> 4, so the common prefix is 2 -> 3, and these two should be obtained from the existing tasks + plan.add_tasks(new_tasks) + + assert [task.task_id for task in plan.tasks] == ["2", "3", "4"] + assert ( + plan.tasks[0].is_finished and plan.tasks[1].is_finished + ) # "2" and "3" should be the original finished one + assert plan.current_task_id == "4" + + def test_current_task(self): + plan = Plan(goal="") + tasks = [ + Task(task_id="1", dependent_task_ids=["2"], instruction="Second"), + Task(task_id="2", instruction="First"), + ] + plan.add_tasks(tasks) + assert plan.current_task.task_id == "2" + + def test_finish_task(self): + plan = Plan(goal="") + tasks = [ + Task(task_id="1", instruction="First"), + Task(task_id="2", dependent_task_ids=["1"], instruction="Second"), + ] + plan.add_tasks(tasks) + plan.finish_current_task() + assert plan.current_task.task_id == "2" + + def test_finished_tasks(self): + plan = Plan(goal="") + tasks = [ + Task(task_id="1", instruction="First"), + Task(task_id="2", dependent_task_ids=["1"], instruction="Second"), + ] + plan.add_tasks(tasks) + plan.finish_current_task() + finished_tasks = plan.get_finished_tasks() + assert len(finished_tasks) == 1 + assert finished_tasks[0].task_id == "1" + + def test_reset_task_existing(self): + plan = Plan(goal="") + task = Task(task_id="1", instruction="Do something", code="print('Hello')", result="Hello", finished=True) + plan.add_tasks([task]) + plan.reset_task("1") + reset_task = plan.task_map["1"] + assert reset_task.code == "" + assert reset_task.result == "" + assert not reset_task.is_finished + + def test_reset_task_non_existing(self): + plan = Plan(goal="") + task = Task(task_id="1", instruction="Do something", code="print('Hello')", result="Hello", finished=True) + plan.add_tasks([task]) + plan.reset_task("2") # Task with ID 2 does not exist + assert "1" in plan.task_map + assert "2" not in plan.task_map + + def test_replace_task_with_dependents(self): + plan = Plan(goal="") + tasks = [ + Task(task_id="1", instruction="First Task", finished=True), + Task(task_id="2", instruction="Second Task", dependent_task_ids=["1"], finished=True), + ] + plan.add_tasks(tasks) + new_task = Task(task_id="1", instruction="Updated First Task") + plan.replace_task(new_task) + assert plan.task_map["1"].instruction == "Updated First Task" + assert not plan.task_map["2"].is_finished # Dependent task should be reset + assert plan.task_map["2"].code == "" + assert plan.task_map["2"].result == "" + + def test_replace_task_non_existing(self): + plan = Plan(goal="") + task = Task(task_id="1", instruction="First Task") + plan.add_tasks([task]) + new_task = Task(task_id="2", instruction="New Task") + with pytest.raises(AssertionError): + plan.replace_task(new_task) # Task with ID 2 does not exist in plan + assert "1" in plan.task_map + assert "2" not in plan.task_map + + def test_append_task_with_valid_dependencies(self): + plan = Plan(goal="Test") + existing_task = [Task(task_id="1")] + plan.add_tasks(existing_task) + new_task = Task(task_id="2", dependent_task_ids=["1"]) + plan.append_task(new_task) + assert plan.tasks[-1].task_id == "2" + assert plan.task_map["2"] == new_task + + def test_append_task_with_invalid_dependencies(self): + new_task = Task(task_id="2", dependent_task_ids=["3"]) + plan = Plan(goal="Test") + with pytest.raises(AssertionError): + plan.append_task(new_task) + + def test_append_task_without_dependencies(self): + plan = Plan(goal="Test") + existing_task = [Task(task_id="1")] + plan.add_tasks(existing_task) + + new_task = Task(task_id="2") + plan.append_task(new_task) + + assert len(plan.tasks) == 2 + assert plan.current_task_id == "1" + + def test_append_task_updates_current_task(self): + finished_task = Task(task_id="1", is_finished=True) + new_task = Task(task_id="2") + plan = Plan(goal="Test", tasks=[finished_task]) + plan.append_task(new_task) + assert plan.current_task_id == "2" + + def test_update_current_task(self): + task1 = Task(task_id="1", is_finished=True) + task2 = Task(task_id="2") + plan = Plan(goal="Test", tasks=[task1, task2]) + plan._update_current_task() + assert plan.current_task_id == "2" + + if __name__ == "__main__": pytest.main([__file__, "-s"]) diff --git a/tests/metagpt/test_startup.py b/tests/metagpt/test_software_company.py similarity index 86% rename from tests/metagpt/test_startup.py rename to tests/metagpt/test_software_company.py index 095a74e3b..1b6477260 100644 --- a/tests/metagpt/test_startup.py +++ b/tests/metagpt/test_software_company.py @@ -3,13 +3,13 @@ """ @Time : 2023/5/15 11:40 @Author : alexanderwu -@File : test_startup.py +@File : test_software_company.py """ import pytest from typer.testing import CliRunner from metagpt.logs import logger -from metagpt.startup import app +from metagpt.software_company import app from metagpt.team import Team runner = CliRunner() @@ -23,7 +23,7 @@ async def test_empty_team(new_filename): logger.info(history) -def test_startup(new_filename): +def test_software_company(new_filename): args = ["Make a cli snake game"] result = runner.invoke(app, args) logger.info(result) diff --git a/tests/metagpt/tools/libs/__init__.py b/tests/metagpt/tools/libs/__init__.py new file mode 100644 index 000000000..0321f694a --- /dev/null +++ b/tests/metagpt/tools/libs/__init__.py @@ -0,0 +1,6 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Time : 2024/1/11 16:14 +# @Author : lidanyang +# @File : __init__.py +# @Desc : diff --git a/tests/metagpt/tools/libs/test_data_preprocess.py b/tests/metagpt/tools/libs/test_data_preprocess.py new file mode 100644 index 000000000..418f8adee --- /dev/null +++ b/tests/metagpt/tools/libs/test_data_preprocess.py @@ -0,0 +1,111 @@ +from datetime import datetime + +import numpy as np +import numpy.testing as npt +import pandas as pd +import pytest + +from metagpt.tools.libs.data_preprocess import ( + FillMissingValue, + LabelEncode, + MaxAbsScale, + MinMaxScale, + OneHotEncode, + OrdinalEncode, + RobustScale, + StandardScale, + get_column_info, +) + + +@pytest.fixture +def mock_datasets(): + return pd.DataFrame( + { + "num1": [1, 2, np.nan, 4, 5], + "cat1": ["A", "B", np.nan, "D", "A"], + "date1": [ + datetime(2020, 1, 1), + datetime(2020, 1, 2), + datetime(2020, 1, 3), + datetime(2020, 1, 4), + datetime(2020, 1, 5), + ], + } + ) + + +def test_fill_missing_value(mock_datasets): + fm = FillMissingValue(features=["num1"], strategy="mean") + transformed = fm.fit_transform(mock_datasets.copy()) + + assert transformed["num1"].isnull().sum() == 0 + + +def test_min_max_scale(mock_datasets): + mms = MinMaxScale(features=["num1"]) + transformed = mms.fit_transform(mock_datasets.copy()) + + npt.assert_allclose(transformed["num1"].min(), 0) + npt.assert_allclose(transformed["num1"].max(), 1) + + +def test_standard_scale(mock_datasets): + ss = StandardScale(features=["num1"]) + transformed = ss.fit_transform(mock_datasets.copy()) + + assert int(transformed["num1"].mean()) == 0 + assert int(transformed["num1"].std()) == 1 + + +def test_max_abs_scale(mock_datasets): + mas = MaxAbsScale(features=["num1"]) + transformed = mas.fit_transform(mock_datasets.copy()) + + npt.assert_allclose(transformed["num1"].abs().max(), 1) + + +def test_robust_scale(mock_datasets): + rs = RobustScale(features=["num1"]) + transformed = rs.fit_transform(mock_datasets.copy()) + + assert int(transformed["num1"].median()) == 0 + + +def test_ordinal_encode(mock_datasets): + oe = OrdinalEncode(features=["cat1"]) + transformed = oe.fit_transform(mock_datasets.copy()) + + assert transformed["cat1"].max() == 2 + + +def test_one_hot_encode(mock_datasets): + ohe = OneHotEncode(features=["cat1"]) + transformed = ohe.fit_transform(mock_datasets.copy()) + + assert transformed["cat1_A"].max() == 1 + + +def test_label_encode(mock_datasets): + le = LabelEncode(features=["cat1"]) + transformed = le.fit_transform(mock_datasets.copy()) + + assert transformed["cat1"].max() == 3 + + # test transform with unseen data + test = mock_datasets.copy() + test["cat1"] = ["A", "B", "C", "D", "E"] + transformed = le.transform(test) + assert transformed["cat1"].max() == 4 + + +def test_get_column_info(mock_datasets): + df = mock_datasets + column_info = get_column_info(df) + + assert column_info == { + "Category": ["cat1"], + "Numeric": ["num1"], + "Datetime": ["date1"], + "Others": [], + } diff --git a/tests/metagpt/tools/libs/test_feature_engineering.py b/tests/metagpt/tools/libs/test_feature_engineering.py new file mode 100644 index 000000000..3cfd5dacd --- /dev/null +++ b/tests/metagpt/tools/libs/test_feature_engineering.py @@ -0,0 +1,175 @@ +import numpy as np +import pandas as pd +import pytest +from sklearn.datasets import fetch_california_housing, load_breast_cancer, load_iris + +from metagpt.tools.libs.feature_engineering import ( + CatCount, + CatCross, + ExtractTimeComps, + GeneralSelection, + GroupStat, + KFoldTargetMeanEncoder, + PolynomialExpansion, + SplitBins, + TargetMeanEncoder, + TreeBasedSelection, + VarianceBasedSelection, +) + + +@pytest.fixture +def mock_dataset(): + return pd.DataFrame( + { + "num1": [1, 2, np.nan, 4, 5, 6, 7, 3], + "num2": [1, 3, 2, 1, np.nan, 5, 6, 4], + "num3": [np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan], + "cat1": ["A", "B", np.nan, "D", "E", "C", "B", "A"], + "cat2": ["A", "A", "A", "A", "A", "A", "A", "A"], + "date1": [ + "2020-01-01", + "2020-01-02", + "2020-01-03", + "2020-01-04", + "2020-01-05", + "2020-01-06", + "2020-01-07", + "2020-01-08", + ], + "label": [0, 1, 0, 1, 0, 1, 0, 1], + } + ) + + +def load_sklearn_data(data_name): + if data_name == "iris": + data = load_iris() + elif data_name == "breast_cancer": + data = load_breast_cancer() + elif data_name == "housing": + data = fetch_california_housing() + else: + raise ValueError("data_name not supported") + + X, y, feature_names = data.data, data.target, data.feature_names + data = pd.DataFrame(X, columns=feature_names) + data["label"] = y + return data + + +def test_polynomial_expansion(mock_dataset): + pe = PolynomialExpansion(cols=["num1", "num2", "label"], degree=2, label_col="label") + transformed = pe.fit_transform(mock_dataset) + + assert len(transformed.columns) == len(mock_dataset.columns) + 3 + + # when too many columns + data = load_sklearn_data("breast_cancer") + cols = [c for c in data.columns if c != "label"] + pe = PolynomialExpansion(cols=cols, degree=2, label_col="label") + transformed = pe.fit_transform(data) + + assert len(transformed.columns) == len(data.columns) + 55 + + +def test_cat_count(mock_dataset): + cc = CatCount(col="cat1") + transformed = cc.fit_transform(mock_dataset) + + assert "cat1_cnt" in transformed.columns + assert transformed["cat1_cnt"][0] == 2 + + +def test_target_mean_encoder(mock_dataset): + tme = TargetMeanEncoder(col="cat1", label="label") + transformed = tme.fit_transform(mock_dataset) + + assert "cat1_target_mean" in transformed.columns + assert transformed["cat1_target_mean"][0] == 0.5 + + +def test_kfold_target_mean_encoder(mock_dataset): + kfme = KFoldTargetMeanEncoder(col="cat1", label="label") + transformed = kfme.fit_transform(mock_dataset) + + assert "cat1_kf_target_mean" in transformed.columns + + +def test_cat_cross(mock_dataset): + cc = CatCross(cols=["cat1", "cat2"]) + transformed = cc.fit_transform(mock_dataset) + + assert "cat1_cat2" in transformed.columns + + cc = CatCross(cols=["cat1", "cat2"], max_cat_num=3) + transformed = cc.fit_transform(mock_dataset) + + assert "cat1_cat2" not in transformed.columns + + +def test_group_stat(mock_dataset): + gs = GroupStat(group_col="cat1", agg_col="num1", agg_funcs=["mean", "sum"]) + transformed = gs.fit_transform(mock_dataset) + + assert "num1_mean_by_cat1" in transformed.columns + assert "num1_sum_by_cat1" in transformed.columns + + +def test_split_bins(mock_dataset): + sb = SplitBins(cols=["num1"]) + transformed = sb.fit_transform(mock_dataset) + + assert transformed["num1"].nunique() <= 5 + assert all(0 <= x < 5 for x in transformed["num1"]) + + +def test_extract_time_comps(mock_dataset): + time_comps = ["year", "month", "day", "hour", "dayofweek", "is_weekend"] + etc = ExtractTimeComps(time_col="date1", time_comps=time_comps) + transformed = etc.fit_transform(mock_dataset.copy()) + + for comp in time_comps: + assert comp in transformed.columns + assert transformed["year"][0] == 2020 + assert transformed["month"][0] == 1 + assert transformed["day"][0] == 1 + assert transformed["hour"][0] == 0 + assert transformed["dayofweek"][0] == 3 + assert transformed["is_weekend"][0] == 0 + + +def test_general_selection(mock_dataset): + gs = GeneralSelection(label_col="label") + transformed = gs.fit_transform(mock_dataset.copy()) + + assert "num3" not in transformed.columns + assert "cat2" not in transformed.columns + + +@pytest.mark.skip # skip because TreeBasedSelection needs lgb as dependency +def test_tree_based_selection(mock_dataset): + # regression + data = load_sklearn_data("housing") + tbs = TreeBasedSelection(label_col="label", task_type="reg") + transformed = tbs.fit_transform(data) + assert len(transformed.columns) > 1 + + # classification + data = load_sklearn_data("breast_cancer") + tbs = TreeBasedSelection(label_col="label", task_type="cls") + transformed = tbs.fit_transform(data) + assert len(transformed.columns) > 1 + + # multi-classification + data = load_sklearn_data("iris") + tbs = TreeBasedSelection(label_col="label", task_type="mcls") + transformed = tbs.fit_transform(data) + assert len(transformed.columns) > 1 + + +def test_variance_based_selection(mock_dataset): + vbs = VarianceBasedSelection(label_col="label") + transformed = vbs.fit_transform(mock_dataset.copy()) + + assert "num3" not in transformed.columns diff --git a/tests/metagpt/tools/libs/test_gpt_v_generator.py b/tests/metagpt/tools/libs/test_gpt_v_generator.py new file mode 100644 index 000000000..907006765 --- /dev/null +++ b/tests/metagpt/tools/libs/test_gpt_v_generator.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2024/01/15 +@Author : mannaandpoem +@File : test_gpt_v_generator.py +""" +from pathlib import Path + +import pytest + +from metagpt import logs +from metagpt.const import METAGPT_ROOT +from metagpt.tools.libs.gpt_v_generator import GPTvGenerator + + +@pytest.fixture +def mock_webpage_filename_with_styles_and_scripts(mocker): + mock_data = """```html\n\n +\n\n```\n +```css\n/* styles.css */\n```\n +```javascript\n// scripts.js\n```\n""" + mocker.patch("metagpt.provider.base_llm.BaseLLM.aask", return_value=mock_data) + return mocker + + +@pytest.fixture +def mock_webpage_filename_with_style_and_script(mocker): + mock_data = """```html\n\n +\n\n```\n +```css\n/* style.css */\n```\n +```javascript\n// script.js\n```\n""" + mocker.patch("metagpt.provider.base_llm.BaseLLM.aask", return_value=mock_data) + return mocker + + +@pytest.fixture +def mock_image_layout(mocker): + image_layout = "The layout information of the sketch image is ..." + mocker.patch("metagpt.provider.base_llm.BaseLLM.aask", return_value=image_layout) + return mocker + + +@pytest.fixture +def image_path(): + return f"{METAGPT_ROOT}/docs/resources/workspace/content_rec_sys/resources/competitive_analysis.png" + + +@pytest.mark.asyncio +async def test_generate_webpages(mock_webpage_filename_with_styles_and_scripts, image_path): + generator = GPTvGenerator() + rsp = await generator.generate_webpages(image_path=image_path) + logs.logger.info(rsp) + assert "html" in rsp + assert "css" in rsp + assert "javascript" in rsp + + +@pytest.mark.asyncio +async def test_save_webpages_with_styles_and_scripts(mock_webpage_filename_with_styles_and_scripts, image_path): + generator = GPTvGenerator() + webpages = await generator.generate_webpages(image_path) + webpages_dir = generator.save_webpages(image_path=image_path, webpages=webpages) + logs.logger.info(webpages_dir) + assert webpages_dir.exists() + + +@pytest.mark.asyncio +async def test_save_webpages_with_style_and_script(mock_webpage_filename_with_style_and_script, image_path): + generator = GPTvGenerator() + webpages = await generator.generate_webpages(image_path) + webpages_dir = generator.save_webpages(image_path=image_path, webpages=webpages) + logs.logger.info(webpages_dir) + assert webpages_dir.exists() + + +@pytest.mark.asyncio +async def test_analyze_layout(mock_image_layout, image_path): + layout = await GPTvGenerator().analyze_layout(Path(image_path)) + logs.logger.info(layout) + assert layout + + +if __name__ == "__main__": + pytest.main([__file__, "-s"]) diff --git a/tests/metagpt/tools/libs/test_sd_engine.py b/tests/metagpt/tools/libs/test_sd_engine.py new file mode 100644 index 000000000..e2c46e72a --- /dev/null +++ b/tests/metagpt/tools/libs/test_sd_engine.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +# @Date : 1/10/2024 10:07 PM +# @Author : stellahong (stellahong@fuzhi.ai) +# @Desc : +import base64 +import io +import json + +import pytest +from PIL import Image, ImageDraw + +from metagpt.tools.libs.sd_engine import SDEngine + + +def generate_mock_image_data(): + # 创建一个简单的图片对象 + image = Image.new("RGB", (100, 100), color="white") + draw = ImageDraw.Draw(image) + draw.text((10, 10), "Mock Image", fill="black") + + # 将图片转换为二进制数据 + with io.BytesIO() as buffer: + image.save(buffer, format="PNG") + image_binary = buffer.getvalue() + + # 对图片二进制数据进行 base64 编码 + image_base64 = base64.b64encode(image_binary).decode("utf-8") + + return image_base64 + + +def test_sd_tools(mocker): + mock_response = mocker.MagicMock() + mock_response.json.return_value = {"images": [generate_mock_image_data()]} + mocker.patch("requests.Session.post", return_value=mock_response) + + engine = SDEngine(sd_url="http://example_localhost:7860") + prompt = "1boy, hansom" + engine.construct_payload(prompt) + engine.simple_run_t2i(engine.payload) + + +def test_sd_construct_payload(): + engine = SDEngine(sd_url="http://example_localhost:7860") + prompt = "1boy, hansom" + engine.construct_payload(prompt) + assert "negative_prompt" in engine.payload + + +@pytest.mark.asyncio +async def test_sd_asyn_t2i(mocker): + mock_post = mocker.patch("aiohttp.ClientSession.post") + mock_response = mocker.AsyncMock() + mock_response.read.return_value = json.dumps({"images": [generate_mock_image_data()]}) + mock_post.return_value.__aenter__.return_value = mock_response + + engine = SDEngine(sd_url="http://example_localhost:7860") + prompt = "1boy, hansom" + engine.construct_payload(prompt) + await engine.run_t2i([engine.payload]) + assert "negative_prompt" in engine.payload diff --git a/tests/metagpt/tools/libs/test_web_scraping.py b/tests/metagpt/tools/libs/test_web_scraping.py new file mode 100644 index 000000000..c11960e68 --- /dev/null +++ b/tests/metagpt/tools/libs/test_web_scraping.py @@ -0,0 +1,23 @@ +import pytest + +from metagpt.tools.libs.web_scraping import scrape_web_playwright + + +@pytest.mark.asyncio +async def test_scrape_web_playwright(): + test_url = "https://www.deepwisdom.ai" + + result = await scrape_web_playwright(test_url) + + # Assert that the result is a dictionary + assert isinstance(result, dict) + + # Assert that the result contains 'inner_text' and 'html' keys + assert "inner_text" in result + assert "html" in result + + # Assert startswith and endswith + assert not result["inner_text"].startswith(" ") + assert not result["inner_text"].endswith(" ") + assert not result["html"].startswith(" ") + assert not result["html"].endswith(" ") diff --git a/tests/metagpt/tools/test_azure_tts.py b/tests/metagpt/tools/test_azure_tts.py index 38fef557e..f72b5663b 100644 --- a/tests/metagpt/tools/test_azure_tts.py +++ b/tests/metagpt/tools/test_azure_tts.py @@ -7,21 +7,31 @@ @Modified By: mashenquan, 2023-8-9, add more text formatting options @Modified By: mashenquan, 2023-8-17, move to `tools` folder. """ +from pathlib import Path import pytest -from azure.cognitiveservices.speech import ResultReason +from azure.cognitiveservices.speech import ResultReason, SpeechSynthesizer -from metagpt.config import CONFIG +from metagpt.config2 import config from metagpt.tools.azure_tts import AzureTTS @pytest.mark.asyncio -async def test_azure_tts(): +async def test_azure_tts(mocker): + # mock + mock_result = mocker.Mock() + mock_result.audio_data = b"mock audio data" + mock_result.reason = ResultReason.SynthesizingAudioCompleted + mock_data = mocker.Mock() + mock_data.get.return_value = mock_result + mocker.patch.object(SpeechSynthesizer, "speak_ssml_async", return_value=mock_data) + mocker.patch.object(Path, "exists", return_value=True) + # Prerequisites - assert CONFIG.AZURE_TTS_SUBSCRIPTION_KEY and CONFIG.AZURE_TTS_SUBSCRIPTION_KEY != "YOUR_API_KEY" - assert CONFIG.AZURE_TTS_REGION + assert config.azure_tts_subscription_key and config.azure_tts_subscription_key != "YOUR_API_KEY" + assert config.azure_tts_region - azure_tts = AzureTTS(subscription_key="", region="") + azure_tts = AzureTTS(subscription_key=config.azure_tts_subscription_key, region=config.azure_tts_region) text = """ 女儿看见父亲走了进来,问道: @@ -32,7 +42,7 @@ async def test_azure_tts(): “Writing a binary file in Python is similar to writing a regular text file, but you'll work with bytes instead of strings.” """ - path = CONFIG.workspace_path / "tts" + path = config.workspace.path / "tts" path.mkdir(exist_ok=True, parents=True) filename = path / "girl.wav" filename.unlink(missing_ok=True) diff --git a/tests/metagpt/tools/test_iflytek_tts.py b/tests/metagpt/tools/test_iflytek_tts.py index 58d8a83ce..c51f62b8e 100644 --- a/tests/metagpt/tools/test_iflytek_tts.py +++ b/tests/metagpt/tools/test_iflytek_tts.py @@ -7,22 +7,32 @@ """ import pytest -from metagpt.config import CONFIG -from metagpt.tools.iflytek_tts import oas3_iflytek_tts +from metagpt.config2 import Config +from metagpt.tools.iflytek_tts import IFlyTekTTS, oas3_iflytek_tts @pytest.mark.asyncio -async def test_tts(): +async def test_iflytek_tts(mocker): + # mock + config = Config.default() + config.azure_tts_subscription_key = None + config.azure_tts_region = None + mocker.patch.object(IFlyTekTTS, "synthesize_speech", return_value=None) + mock_data = mocker.AsyncMock() + mock_data.read.return_value = b"mock iflytek" + mock_reader = mocker.patch("aiofiles.open") + mock_reader.return_value.__aenter__.return_value = mock_data + # Prerequisites - assert CONFIG.IFLYTEK_APP_ID - assert CONFIG.IFLYTEK_API_KEY - assert CONFIG.IFLYTEK_API_SECRET + assert config.iflytek_app_id + assert config.iflytek_api_key + assert config.iflytek_api_secret result = await oas3_iflytek_tts( text="你好,hello", - app_id=CONFIG.IFLYTEK_APP_ID, - api_key=CONFIG.IFLYTEK_API_KEY, - api_secret=CONFIG.IFLYTEK_API_SECRET, + app_id=config.iflytek_app_id, + api_key=config.iflytek_api_key, + api_secret=config.iflytek_api_secret, ) assert result diff --git a/tests/metagpt/tools/test_metagpt_oas3_api_svc.py b/tests/metagpt/tools/test_metagpt_oas3_api_svc.py index 5f52b28cc..5be139106 100644 --- a/tests/metagpt/tools/test_metagpt_oas3_api_svc.py +++ b/tests/metagpt/tools/test_metagpt_oas3_api_svc.py @@ -12,14 +12,12 @@ import pytest import requests -from metagpt.config import CONFIG - @pytest.mark.asyncio -async def test_oas2_svc(): +async def test_oas2_svc(context): workdir = Path(__file__).parent.parent.parent.parent script_pathname = workdir / "metagpt/tools/metagpt_oas3_api_svc.py" - env = CONFIG.new_environ() + env = context.new_environ() env["PYTHONPATH"] = str(workdir) + ":" + env.get("PYTHONPATH", "") process = subprocess.Popen(["python", str(script_pathname)], cwd=str(workdir), env=env) await asyncio.sleep(5) diff --git a/tests/metagpt/tools/test_metagpt_text_to_image.py b/tests/metagpt/tools/test_metagpt_text_to_image.py index b765119f0..d3797a460 100644 --- a/tests/metagpt/tools/test_metagpt_text_to_image.py +++ b/tests/metagpt/tools/test_metagpt_text_to_image.py @@ -10,7 +10,7 @@ import pytest -from metagpt.config import CONFIG +from metagpt.config2 import config from metagpt.tools.metagpt_text_to_image import oas3_metagpt_text_to_image @@ -24,7 +24,7 @@ async def test_draw(mocker): mock_post.return_value.__aenter__.return_value = mock_response # Prerequisites - assert CONFIG.METAGPT_TEXT_TO_IMAGE_MODEL_URL + assert config.metagpt_tti_url binary_data = await oas3_metagpt_text_to_image("Panda emoji") assert binary_data diff --git a/tests/metagpt/tools/test_moderation.py b/tests/metagpt/tools/test_moderation.py index 534fe812a..8dc9e9d5e 100644 --- a/tests/metagpt/tools/test_moderation.py +++ b/tests/metagpt/tools/test_moderation.py @@ -8,7 +8,8 @@ import pytest -from metagpt.config import CONFIG +from metagpt.config2 import config +from metagpt.llm import LLM from metagpt.tools.moderation import Moderation @@ -23,11 +24,9 @@ ) async def test_amoderation(content): # Prerequisites - assert CONFIG.OPENAI_API_KEY and CONFIG.OPENAI_API_KEY != "YOUR_API_KEY" - assert not CONFIG.OPENAI_API_TYPE - assert CONFIG.OPENAI_API_MODEL + assert config.get_openai_llm() - moderation = Moderation() + moderation = Moderation(LLM()) results = await moderation.amoderation(content=content) assert isinstance(results, list) assert len(results) == len(content) diff --git a/tests/metagpt/tools/test_openai_text_to_embedding.py b/tests/metagpt/tools/test_openai_text_to_embedding.py index 086c9d45b..81b3895c3 100644 --- a/tests/metagpt/tools/test_openai_text_to_embedding.py +++ b/tests/metagpt/tools/test_openai_text_to_embedding.py @@ -5,21 +5,36 @@ @Author : mashenquan @File : test_openai_text_to_embedding.py """ +import json +from pathlib import Path import pytest -from metagpt.config import CONFIG +from metagpt.config2 import Config from metagpt.tools.openai_text_to_embedding import oas3_openai_text_to_embedding +from metagpt.utils.common import aread @pytest.mark.asyncio -async def test_embedding(): +async def test_embedding(mocker): + # mock + config = Config.default() + mock_post = mocker.patch("aiohttp.ClientSession.post") + mock_response = mocker.AsyncMock() + mock_response.status = 200 + data = await aread(Path(__file__).parent / "../../data/openai/embedding.json") + mock_response.json.return_value = json.loads(data) + mock_post.return_value.__aenter__.return_value = mock_response + type(config.get_openai_llm()).proxy = mocker.PropertyMock(return_value="http://mock.proxy") + # Prerequisites - assert CONFIG.OPENAI_API_KEY and CONFIG.OPENAI_API_KEY != "YOUR_API_KEY" - assert not CONFIG.OPENAI_API_TYPE - assert CONFIG.OPENAI_API_MODEL + llm_config = config.get_openai_llm() + assert llm_config + assert llm_config.proxy - result = await oas3_openai_text_to_embedding("Panda emoji") + result = await oas3_openai_text_to_embedding( + "Panda emoji", openai_api_key=llm_config.api_key, proxy=llm_config.proxy + ) assert result assert result.model assert len(result.data) > 0 diff --git a/tests/metagpt/tools/test_openai_text_to_image.py b/tests/metagpt/tools/test_openai_text_to_image.py index e560da798..3f9169ddd 100644 --- a/tests/metagpt/tools/test_openai_text_to_image.py +++ b/tests/metagpt/tools/test_openai_text_to_image.py @@ -5,24 +5,44 @@ @Author : mashenquan @File : test_openai_text_to_image.py """ +import base64 +import openai import pytest +from pydantic import BaseModel -from metagpt.config import CONFIG +from metagpt.config2 import config +from metagpt.llm import LLM from metagpt.tools.openai_text_to_image import ( OpenAIText2Image, oas3_openai_text_to_image, ) +from metagpt.utils.s3 import S3 @pytest.mark.asyncio -async def test_draw(): +async def test_draw(mocker): + # mock + mock_url = mocker.Mock() + mock_url.url.return_value = "http://mock.com/0.png" + + class _MockData(BaseModel): + data: list + + mock_data = _MockData(data=[mock_url]) + mocker.patch.object(openai.resources.images.AsyncImages, "generate", return_value=mock_data) + mock_post = mocker.patch("aiohttp.ClientSession.get") + mock_response = mocker.AsyncMock() + mock_response.status = 200 + mock_response.read.return_value = base64.b64encode(b"success") + mock_post.return_value.__aenter__.return_value = mock_response + mocker.patch.object(S3, "cache", return_value="http://mock.s3.com/0.png") + # Prerequisites - assert CONFIG.OPENAI_API_KEY and CONFIG.OPENAI_API_KEY != "YOUR_API_KEY" - assert not CONFIG.OPENAI_API_TYPE - assert CONFIG.OPENAI_API_MODEL + llm_config = config.get_openai_llm() + assert llm_config - binary_data = await oas3_openai_text_to_image("Panda emoji") + binary_data = await oas3_openai_text_to_image("Panda emoji", llm=LLM(llm_config=llm_config)) assert binary_data diff --git a/tests/metagpt/tools/test_openapi_v3_hello.py b/tests/metagpt/tools/test_openapi_v3_hello.py index 5726cf8e0..f49b8412a 100644 --- a/tests/metagpt/tools/test_openapi_v3_hello.py +++ b/tests/metagpt/tools/test_openapi_v3_hello.py @@ -12,14 +12,12 @@ import pytest import requests -from metagpt.config import CONFIG - @pytest.mark.asyncio -async def test_hello(): +async def test_hello(context): workdir = Path(__file__).parent.parent.parent.parent script_pathname = workdir / "metagpt/tools/openapi_v3_hello.py" - env = CONFIG.new_environ() + env = context.new_environ() env["PYTHONPATH"] = str(workdir) + ":" + env.get("PYTHONPATH", "") process = subprocess.Popen(["python", str(script_pathname)], cwd=workdir, env=env) await asyncio.sleep(5) diff --git a/tests/metagpt/tools/test_search_engine.py b/tests/metagpt/tools/test_search_engine.py index dab466af7..a1f03ef7b 100644 --- a/tests/metagpt/tools/test_search_engine.py +++ b/tests/metagpt/tools/test_search_engine.py @@ -7,20 +7,16 @@ """ from __future__ import annotations -import json -from pathlib import Path from typing import Callable import pytest -import tests.data.search -from metagpt.config import CONFIG +from metagpt.config2 import config +from metagpt.configs.search_config import SearchConfig from metagpt.logs import logger from metagpt.tools import SearchEngineType from metagpt.tools.search_engine import SearchEngine -search_cache_path = Path(tests.data.search.__path__[0]) - class MockSearchEnine: async def run(self, query: str, max_results: int = 8, as_string: bool = True) -> str | list[dict[str, str]]: @@ -46,31 +42,42 @@ async def run(self, query: str, max_results: int = 8, as_string: bool = True) -> (SearchEngineType.CUSTOM_ENGINE, MockSearchEnine().run, 6, False), ], ) -async def test_search_engine(search_engine_type, run_func: Callable, max_results: int, as_string: bool, aiohttp_mocker): +async def test_search_engine( + search_engine_type, + run_func: Callable, + max_results: int, + as_string: bool, + search_engine_mocker, +): # Prerequisites - cache_json_path = None + search_engine_config = {"engine": search_engine_type, "run_func": run_func} + if search_engine_type is SearchEngineType.SERPAPI_GOOGLE: - assert CONFIG.SERPAPI_API_KEY and CONFIG.SERPAPI_API_KEY != "YOUR_API_KEY" - cache_json_path = search_cache_path / f"serpapi-metagpt-{max_results}.json" + assert config.search + search_engine_config["api_key"] = "mock-serpapi-key" elif search_engine_type is SearchEngineType.DIRECT_GOOGLE: - assert CONFIG.GOOGLE_API_KEY and CONFIG.GOOGLE_API_KEY != "YOUR_API_KEY" - assert CONFIG.GOOGLE_CSE_ID and CONFIG.GOOGLE_CSE_ID != "YOUR_CSE_ID" + assert config.search + search_engine_config["api_key"] = "mock-google-key" + search_engine_config["cse_id"] = "mock-google-cse" elif search_engine_type is SearchEngineType.SERPER_GOOGLE: - assert CONFIG.SERPER_API_KEY and CONFIG.SERPER_API_KEY != "YOUR_API_KEY" - cache_json_path = search_cache_path / f"serper-metagpt-{max_results}.json" + assert config.search + search_engine_config["api_key"] = "mock-serper-key" + + async def test(search_engine): + rsp = await search_engine.run("metagpt", max_results, as_string) + logger.info(rsp) + if as_string: + assert isinstance(rsp, str) + else: + assert isinstance(rsp, list) + assert len(rsp) <= max_results - if cache_json_path: - with open(cache_json_path) as f: - data = json.load(f) - aiohttp_mocker.set_json(data) - search_engine = SearchEngine(search_engine_type, run_func) - rsp = await search_engine.run("metagpt", max_results, as_string) - logger.info(rsp) - if as_string: - assert isinstance(rsp, str) - else: - assert isinstance(rsp, list) - assert len(rsp) <= max_results + await test(SearchEngine(**search_engine_config)) + search_engine_config["api_type"] = search_engine_config.pop("engine") + if run_func: + await test(SearchEngine.from_search_func(run_func)) + search_engine_config["search_func"] = search_engine_config.pop("run_func") + await test(SearchEngine.from_search_config(SearchConfig(**search_engine_config))) if __name__ == "__main__": diff --git a/tests/metagpt/tools/test_tool_convert.py b/tests/metagpt/tools/test_tool_convert.py new file mode 100644 index 000000000..8f26a211c --- /dev/null +++ b/tests/metagpt/tools/test_tool_convert.py @@ -0,0 +1,175 @@ +import pandas as pd + +from metagpt.tools.tool_convert import convert_code_to_tool_schema, docstring_to_schema + + +def test_docstring_to_schema(): + docstring = """ + Some test desc. + + Args: + features (list): Columns to be processed. + strategy (str, optional): The imputation strategy, notice 'mean' and 'median' can only be + used for numeric features. Enum: ['mean', 'median', 'most_frequent', 'constant']. Defaults to 'mean'. + fill_value (int, optional): Fill_value is used to replace all occurrences of missing_values. + Defaults to None. + Returns: + pd.DataFrame: The transformed DataFrame. + """ + expected = { + "description": "Some test desc.", + "parameters": { + "properties": { + "features": {"type": "list", "description": "Columns to be processed."}, + "strategy": { + "type": "str", + "description": "The imputation strategy, notice 'mean' and 'median' can only be used for numeric features. Enum: ['mean', 'median', 'most_frequent', 'constant']. Defaults to 'mean'.", + "default": "'mean'", + "enum": ["'mean'", "'median'", "'most_frequent'", "'constant'"], + }, + "fill_value": { + "type": "int", + "description": "Fill_value is used to replace all occurrences of missing_values. Defaults to None.", + "default": "None", + }, + }, + "required": ["features"], + }, + "returns": [{"type": "pd.DataFrame", "description": "The transformed DataFrame."}], + } + schema = docstring_to_schema(docstring) + assert schema == expected + + +class DummyClass: + """ + Completing missing values with simple strategies. + """ + + def __init__(self, features: list, strategy: str = "mean", fill_value=None): + """ + Initialize self. + + Args: + features (list): Columns to be processed. + strategy (str, optional): The imputation strategy, notice 'mean' and 'median' can only + be used for numeric features. Enum: ['mean', 'median', 'most_frequent', 'constant']. Defaults to 'mean'. + fill_value (int, optional): Fill_value is used to replace all occurrences of missing_values. + Defaults to None. + """ + pass + + def fit(self, df: pd.DataFrame): + """ + Fit the FillMissingValue model. + + Args: + df (pd.DataFrame): The input DataFrame. + """ + pass + + def transform(self, df: pd.DataFrame) -> pd.DataFrame: + """ + Transform the input DataFrame with the fitted model. + + Args: + df (pd.DataFrame): The input DataFrame. + + Returns: + pd.DataFrame: The transformed DataFrame. + """ + pass + + +def dummy_fn(df: pd.DataFrame) -> dict: + """ + Analyzes a DataFrame and categorizes its columns based on data types. + + Args: + df (pd.DataFrame): The DataFrame to be analyzed. + + Returns: + dict: A dictionary with four keys ('Category', 'Numeric', 'Datetime', 'Others'). + Each key corresponds to a list of column names belonging to that category. + """ + pass + + +async def dummy_async_fn(df: pd.DataFrame) -> dict: + """ + A dummy async function for test + + Args: + df (pd.DataFrame): test args. + + Returns: + dict: test returns. + """ + pass + + +def test_convert_code_to_tool_schema_class(): + expected = { + "type": "class", + "description": "Completing missing values with simple strategies.", + "methods": { + "__init__": { + "type": "function", + "description": "Initialize self.", + "parameters": { + "properties": { + "features": {"type": "list", "description": "Columns to be processed."}, + "strategy": { + "type": "str", + "description": "The imputation strategy, notice 'mean' and 'median' can only be used for numeric features. Enum: ['mean', 'median', 'most_frequent', 'constant']. Defaults to 'mean'.", + "default": "'mean'", + "enum": ["'mean'", "'median'", "'most_frequent'", "'constant'"], + }, + "fill_value": { + "type": "int", + "description": "Fill_value is used to replace all occurrences of missing_values. Defaults to None.", + "default": "None", + }, + }, + "required": ["features"], + }, + }, + "fit": { + "type": "function", + "description": "Fit the FillMissingValue model.", + "parameters": { + "properties": {"df": {"type": "pd.DataFrame", "description": "The input DataFrame."}}, + "required": ["df"], + }, + }, + "transform": { + "type": "function", + "description": "Transform the input DataFrame with the fitted model.", + "parameters": { + "properties": {"df": {"type": "pd.DataFrame", "description": "The input DataFrame."}}, + "required": ["df"], + }, + "returns": [{"type": "pd.DataFrame", "description": "The transformed DataFrame."}], + }, + }, + } + schema = convert_code_to_tool_schema(DummyClass) + assert schema == expected + + +def test_convert_code_to_tool_schema_function(): + expected = { + "type": "function", + "description": "Analyzes a DataFrame and categorizes its columns based on data types.", + "parameters": { + "properties": {"df": {"type": "pd.DataFrame", "description": "The DataFrame to be analyzed."}}, + "required": ["df"], + }, + } + schema = convert_code_to_tool_schema(dummy_fn) + assert schema == expected + + +def test_convert_code_to_tool_schema_async_function(): + schema = convert_code_to_tool_schema(dummy_async_fn) + assert schema.get("type") == "async_function" diff --git a/tests/metagpt/tools/test_tool_registry.py b/tests/metagpt/tools/test_tool_registry.py new file mode 100644 index 000000000..2fd487fb7 --- /dev/null +++ b/tests/metagpt/tools/test_tool_registry.py @@ -0,0 +1,102 @@ +import pytest + +from metagpt.tools.tool_registry import ToolRegistry +from metagpt.tools.tool_type import ToolType + + +@pytest.fixture +def tool_registry(): + return ToolRegistry() + + +@pytest.fixture +def tool_registry_full(): + return ToolRegistry(tool_types=ToolType) + + +# Test Initialization +def test_initialization(tool_registry): + assert isinstance(tool_registry, ToolRegistry) + assert tool_registry.tools == {} + assert tool_registry.tool_types == {} + assert tool_registry.tools_by_types == {} + + +# Test Initialization with tool types +def test_initialize_with_tool_types(tool_registry_full): + assert isinstance(tool_registry_full, ToolRegistry) + assert tool_registry_full.tools == {} + assert tool_registry_full.tools_by_types == {} + assert "data_preprocess" in tool_registry_full.tool_types + + +class TestClassTool: + """test class""" + + def test_class_fn(self): + """test class fn""" + pass + + +def test_fn(): + """test function""" + pass + + +# Test Tool Registration Class +def test_register_tool_class(tool_registry): + tool_registry.register_tool("TestClassTool", "/path/to/tool", tool_source_object=TestClassTool) + assert "TestClassTool" in tool_registry.tools + + +# Test Tool Registration Function +def test_register_tool_fn(tool_registry): + tool_registry.register_tool("test_fn", "/path/to/tool", tool_source_object=test_fn) + assert "test_fn" in tool_registry.tools + + +# Test Tool Existence Checks +def test_has_tool(tool_registry): + tool_registry.register_tool("TestClassTool", "/path/to/tool", tool_source_object=TestClassTool) + assert tool_registry.has_tool("TestClassTool") + assert not tool_registry.has_tool("NonexistentTool") + + +# Test Tool Retrieval +def test_get_tool(tool_registry): + tool_registry.register_tool("TestClassTool", "/path/to/tool", tool_source_object=TestClassTool) + tool = tool_registry.get_tool("TestClassTool") + assert tool is not None + assert tool.name == "TestClassTool" + assert tool.path == "/path/to/tool" + assert "description" in tool.schemas + + +# Similar tests for has_tool_type, get_tool_type, get_tools_by_type +def test_has_tool_type(tool_registry_full): + assert tool_registry_full.has_tool_type("data_preprocess") + assert not tool_registry_full.has_tool_type("NonexistentType") + + +def test_get_tool_type(tool_registry_full): + retrieved_type = tool_registry_full.get_tool_type("data_preprocess") + assert retrieved_type is not None + assert retrieved_type.name == "data_preprocess" + + +def test_get_tools_by_type(tool_registry): + tool_type_name = "TestType" + tool_name = "TestTool" + tool_path = "/path/to/tool" + + tool_registry.register_tool(tool_name, tool_path, tool_type=tool_type_name, tool_source_object=TestClassTool) + + tools_by_type = tool_registry.get_tools_by_type(tool_type_name) + assert tools_by_type is not None + assert tool_name in tools_by_type + + +# Test case for when the tool type does not exist +def test_get_tools_by_nonexistent_type(tool_registry): + tools_by_type = tool_registry.get_tools_by_type("NonexistentType") + assert not tools_by_type diff --git a/tests/metagpt/tools/test_ut_writer.py b/tests/metagpt/tools/test_ut_writer.py index eac28d56f..3cc7e86bb 100644 --- a/tests/metagpt/tools/test_ut_writer.py +++ b/tests/metagpt/tools/test_ut_writer.py @@ -8,21 +8,66 @@ from pathlib import Path import pytest +from openai.resources.chat.completions import AsyncCompletions +from openai.types import CompletionUsage +from openai.types.chat.chat_completion import ( + ChatCompletion, + ChatCompletionMessage, + Choice, +) +from openai.types.chat.chat_completion_message_tool_call import ( + ChatCompletionMessageToolCall, + Function, +) -from metagpt.config import CONFIG +from metagpt.config2 import config from metagpt.const import API_QUESTIONS_PATH, UT_PY_PATH from metagpt.tools.ut_writer import YFT_PROMPT_PREFIX, UTGenerator class TestUTWriter: @pytest.mark.asyncio - async def test_api_to_ut_sample(self): + async def test_api_to_ut_sample(self, mocker): + async def mock_create(*args, **kwargs): + return ChatCompletion( + id="chatcmpl-8n5fAd21w2J1IIFkI4qxWlNfM7QRC", + choices=[ + Choice( + finish_reason="stop", + index=0, + logprobs=None, + message=ChatCompletionMessage( + content=None, + role="assistant", + function_call=None, + tool_calls=[ + ChatCompletionMessageToolCall( + id="call_EjjmIY7GMspHu3r9mx8gPA2k", + function=Function( + arguments='{"code":"import string\\nimport random\\n\\ndef random_string' + "(length=10):\\n return ''.join(random.choice(string.ascii_" + 'lowercase) for i in range(length))"}', + name="execute", + ), + type="function", + ) + ], + ), + ) + ], + created=1706710532, + model="gpt-3.5-turbo-1106", + object="chat.completion", + system_fingerprint="fp_04f9a1eebf", + usage=CompletionUsage(completion_tokens=35, prompt_tokens=1982, total_tokens=2017), + ) + + mocker.patch.object(AsyncCompletions, "create", mock_create) + # Prerequisites swagger_file = Path(__file__).parent / "../../data/ut_writer/yft_swaggerApi.json" assert swagger_file.exists() - assert CONFIG.OPENAI_API_KEY and CONFIG.OPENAI_API_KEY != "YOUR_API_KEY" - assert not CONFIG.OPENAI_API_TYPE - assert CONFIG.OPENAI_API_MODEL + assert config.get_openai_llm() tags = ["测试", "作业"] # 这里在文件中手动加入了两个测试标签的API diff --git a/tests/metagpt/tools/test_web_browser_engine.py b/tests/metagpt/tools/test_web_browser_engine.py index 289edda2f..ceebd67fc 100644 --- a/tests/metagpt/tools/test_web_browser_engine.py +++ b/tests/metagpt/tools/test_web_browser_engine.py @@ -1,6 +1,5 @@ -""" -@Modified By: mashenquan, 2023/8/20. Remove global configuration `CONFIG`, enable configuration support for business isolation. -""" +#!/usr/bin/env python +# -*- coding: utf-8 -*- import pytest diff --git a/tests/metagpt/tools/test_web_browser_engine_playwright.py b/tests/metagpt/tools/test_web_browser_engine_playwright.py index 0f2679531..f35848cf4 100644 --- a/tests/metagpt/tools/test_web_browser_engine_playwright.py +++ b/tests/metagpt/tools/test_web_browser_engine_playwright.py @@ -1,10 +1,8 @@ -""" -@Modified By: mashenquan, 2023/8/20. Remove global configuration `CONFIG`, enable configuration support for business isolation. -""" +#!/usr/bin/env python +# -*- coding: utf-8 -*- import pytest -from metagpt.config import CONFIG from metagpt.tools import web_browser_engine_playwright from metagpt.utils.parse_html import WebPage @@ -20,26 +18,22 @@ ids=["chromium-normal", "firefox-normal", "webkit-normal"], ) async def test_scrape_web_page(browser_type, use_proxy, kwagrs, url, urls, proxy, capfd): - global_proxy = CONFIG.global_proxy - try: - if use_proxy: - server, proxy = await proxy - CONFIG.global_proxy = proxy - browser = web_browser_engine_playwright.PlaywrightWrapper(browser_type=browser_type, **kwagrs) - result = await browser.run(url) - assert isinstance(result, WebPage) - assert "MetaGPT" in result.inner_text + proxy_url = None + if use_proxy: + server, proxy_url = await proxy() + browser = web_browser_engine_playwright.PlaywrightWrapper(browser_type=browser_type, proxy=proxy_url, **kwagrs) + result = await browser.run(url) + assert isinstance(result, WebPage) + assert "MetaGPT" in result.inner_text - if urls: - results = await browser.run(url, *urls) - assert isinstance(results, list) - assert len(results) == len(urls) + 1 - assert all(("MetaGPT" in i.inner_text) for i in results) - if use_proxy: - server.close() - assert "Proxy:" in capfd.readouterr().out - finally: - CONFIG.global_proxy = global_proxy + if urls: + results = await browser.run(url, *urls) + assert isinstance(results, list) + assert len(results) == len(urls) + 1 + assert all(("MetaGPT" in i.inner_text) for i in results) + if use_proxy: + server.close() + assert "Proxy:" in capfd.readouterr().out if __name__ == "__main__": diff --git a/tests/metagpt/tools/test_web_browser_engine_selenium.py b/tests/metagpt/tools/test_web_browser_engine_selenium.py index 8fe365352..a88a5d0f4 100644 --- a/tests/metagpt/tools/test_web_browser_engine_selenium.py +++ b/tests/metagpt/tools/test_web_browser_engine_selenium.py @@ -1,10 +1,9 @@ -""" -@Modified By: mashenquan, 2023/8/20. Remove global configuration `CONFIG`, enable configuration support for business isolation. -""" +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import browsers import pytest -from metagpt.config import CONFIG from metagpt.tools import web_browser_engine_selenium from metagpt.utils.parse_html import WebPage @@ -13,36 +12,49 @@ @pytest.mark.parametrize( "browser_type, use_proxy, url, urls", [ - ("chrome", True, "https://deepwisdom.ai", ("https://deepwisdom.ai",)), - ("firefox", False, "https://deepwisdom.ai", ("https://deepwisdom.ai",)), - ("edge", False, "https://deepwisdom.ai", ("https://deepwisdom.ai",)), + pytest.param( + "chrome", + True, + "https://deepwisdom.ai", + ("https://deepwisdom.ai",), + marks=pytest.mark.skipif(not browsers.get("chrome"), reason="chrome browser not found"), + ), + pytest.param( + "firefox", + False, + "https://deepwisdom.ai", + ("https://deepwisdom.ai",), + marks=pytest.mark.skipif(not browsers.get("firefox"), reason="firefox browser not found"), + ), + pytest.param( + "edge", + False, + "https://deepwisdom.ai", + ("https://deepwisdom.ai",), + marks=pytest.mark.skipif(not browsers.get("msedge"), reason="edge browser not found"), + ), ], ids=["chrome-normal", "firefox-normal", "edge-normal"], ) async def test_scrape_web_page(browser_type, use_proxy, url, urls, proxy, capfd): # Prerequisites # firefox, chrome, Microsoft Edge - - global_proxy = CONFIG.global_proxy - try: - if use_proxy: - server, proxy = await proxy - CONFIG.global_proxy = proxy - browser = web_browser_engine_selenium.SeleniumWrapper(browser_type=browser_type) - result = await browser.run(url) - assert isinstance(result, WebPage) - assert "MetaGPT" in result.inner_text - - if urls: - results = await browser.run(url, *urls) - assert isinstance(results, list) - assert len(results) == len(urls) + 1 - assert all(("MetaGPT" in i.inner_text) for i in results) - if use_proxy: - server.close() - assert "Proxy:" in capfd.readouterr().out - finally: - CONFIG.global_proxy = global_proxy + proxy_url = None + if use_proxy: + server, proxy_url = await proxy() + browser = web_browser_engine_selenium.SeleniumWrapper(browser_type=browser_type, proxy=proxy_url) + result = await browser.run(url) + assert isinstance(result, WebPage) + assert "MetaGPT" in result.inner_text + + if urls: + results = await browser.run(url, *urls) + assert isinstance(results, list) + assert len(results) == len(urls) + 1 + assert all(("MetaGPT" in i.inner_text) for i in results) + if use_proxy: + server.close() + assert "Proxy:" in capfd.readouterr().out if __name__ == "__main__": diff --git a/tests/metagpt/utils/test_config.py b/tests/metagpt/utils/test_config.py deleted file mode 100644 index 4ca7a225c..000000000 --- a/tests/metagpt/utils/test_config.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -@Time : 2023/5/1 11:19 -@Author : alexanderwu -@File : test_config.py -@Modified By: mashenquan, 2013/8/20, Add `test_options`; remove global configuration `CONFIG`, enable configuration support for business isolation. -""" -from pathlib import Path - -import pytest - -from metagpt.config import Config - - -def test_config_class_get_key_exception(): - with pytest.raises(Exception) as exc_info: - config = Config() - config.get("wtf") - assert str(exc_info.value) == "Key 'wtf' not found in environment variables or in the YAML file" - - -def test_config_yaml_file_not_exists(): - # FIXME: 由于这里是单例,所以会导致Config重新创建失效。后续要将Config改为非单例模式。 - _ = Config("wtf.yaml") - # with pytest.raises(Exception) as exc_info: - # config.get("OPENAI_BASE_URL") - # assert str(exc_info.value) == "Set OPENAI_API_KEY or Anthropic_API_KEY first" - - -def test_options(): - filename = Path(__file__).resolve().parent.parent.parent.parent / "config/config.yaml" - config = Config(filename) - assert config.options - - -if __name__ == "__main__": - test_options() diff --git a/tests/metagpt/utils/test_human_interaction.py b/tests/metagpt/utils/test_human_interaction.py new file mode 100644 index 000000000..24dbac61c --- /dev/null +++ b/tests/metagpt/utils/test_human_interaction.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# @Desc : unittest of human_interaction + +from pydantic import BaseModel + +from metagpt.utils.human_interaction import HumanInteraction + + +class InstructContent(BaseModel): + test_field1: str = "" + test_field2: list[str] = [] + + +data_mapping = {"test_field1": (str, ...), "test_field2": (list[str], ...)} + +human_interaction = HumanInteraction() + + +def test_input_num(mocker): + mocker.patch("builtins.input", lambda _: "quit") + + interact_contents = human_interaction.interact_with_instruct_content(InstructContent(), data_mapping) + assert len(interact_contents) == 0 + + mocker.patch("builtins.input", lambda _: "1") + input_num = human_interaction.input_num_until_valid(2) + assert input_num == 1 + + +def test_check_input_type(): + ret, _ = human_interaction.check_input_type(input_str="test string", req_type=str) + assert ret + + ret, _ = human_interaction.check_input_type(input_str='["test string"]', req_type=list[str]) + assert ret + + ret, _ = human_interaction.check_input_type(input_str='{"key", "value"}', req_type=list[str]) + assert not ret + + +global_index = 0 + + +def mock_input(*args, **kwargs): + """there are multi input call, return it by global_index""" + arr = ["1", '["test"]', "ignore", "quit"] + global global_index + global_index += 1 + if global_index == 3: + raise EOFError() + val = arr[global_index - 1] + return val + + +def test_human_interact_valid_content(mocker): + mocker.patch("builtins.input", mock_input) + input_contents = HumanInteraction().interact_with_instruct_content(InstructContent(), data_mapping, "review") + assert len(input_contents) == 1 + assert input_contents["test_field2"] == '["test"]' + + global global_index + global_index = 0 + input_contents = HumanInteraction().interact_with_instruct_content(InstructContent(), data_mapping, "revise") + assert len(input_contents) == 1 + assert input_contents["test_field2"] == ["test"] diff --git a/tests/metagpt/utils/test_mermaid.py b/tests/metagpt/utils/test_mermaid.py index b7b97a3f1..7e9129314 100644 --- a/tests/metagpt/utils/test_mermaid.py +++ b/tests/metagpt/utils/test_mermaid.py @@ -8,23 +8,20 @@ import pytest -from metagpt.config import CONFIG from metagpt.utils.common import check_cmd_exists from metagpt.utils.mermaid import MMC1, mermaid_to_file @pytest.mark.asyncio @pytest.mark.parametrize("engine", ["nodejs", "ink"]) # TODO: playwright and pyppeteer -async def test_mermaid(engine): +async def test_mermaid(engine, context): # nodejs prerequisites: npm install -g @mermaid-js/mermaid-cli # ink prerequisites: connected to internet # playwright prerequisites: playwright install --with-deps chromium assert check_cmd_exists("npm") == 0 - assert CONFIG.PYPPETEER_EXECUTABLE_PATH - CONFIG.mermaid_engine = engine - save_to = CONFIG.git_repo.workdir / f"{CONFIG.mermaid_engine}/1" - await mermaid_to_file(MMC1, save_to) + save_to = context.git_repo.workdir / f"{engine}/1" + await mermaid_to_file(engine, MMC1, save_to) # ink does not support pdf if engine == "ink": diff --git a/tests/metagpt/utils/test_project_repo.py b/tests/metagpt/utils/test_project_repo.py new file mode 100644 index 000000000..667927a1d --- /dev/null +++ b/tests/metagpt/utils/test_project_repo.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2024/1/8 +@Author : mashenquan +""" +import uuid +from pathlib import Path + +import pytest + +from metagpt.const import ( + BUGFIX_FILENAME, + PACKAGE_REQUIREMENTS_FILENAME, + PRDS_FILE_REPO, + REQUIREMENT_FILENAME, +) +from metagpt.utils.project_repo import ProjectRepo + + +async def test_project_repo(): + root = Path(__file__).parent / f"../../../workspace/unittest/{uuid.uuid4().hex}" + root = root.resolve() + + pr = ProjectRepo(root=str(root)) + assert pr.git_repo.workdir == root + assert pr.workdir == pr.git_repo.workdir + + await pr.save(filename=REQUIREMENT_FILENAME, content=REQUIREMENT_FILENAME) + doc = await pr.get(filename=REQUIREMENT_FILENAME) + assert doc.content == REQUIREMENT_FILENAME + await pr.save(filename=BUGFIX_FILENAME, content=BUGFIX_FILENAME) + doc = await pr.get(filename=BUGFIX_FILENAME) + assert doc.content == BUGFIX_FILENAME + await pr.save(filename=PACKAGE_REQUIREMENTS_FILENAME, content=PACKAGE_REQUIREMENTS_FILENAME) + doc = await pr.get(filename=PACKAGE_REQUIREMENTS_FILENAME) + assert doc.content == PACKAGE_REQUIREMENTS_FILENAME + await pr.docs.prd.save(filename="1.prd", content="1.prd", dependencies=[REQUIREMENT_FILENAME]) + doc = await pr.docs.prd.get(filename="1.prd") + assert doc.content == "1.prd" + await pr.resources.prd.save( + filename="1.prd", + content="1.prd", + dependencies=[REQUIREMENT_FILENAME, f"{PRDS_FILE_REPO}/1.prd"], + ) + doc = await pr.resources.prd.get(filename="1.prd") + assert doc.content == "1.prd" + dependencies = await pr.resources.prd.get_dependency(filename="1.prd") + assert len(dependencies) == 2 + + assert pr.changed_files + assert pr.docs.prd.changed_files + assert not pr.tests.changed_files + + with pytest.raises(ValueError): + pr.srcs + assert pr.with_src_path("test_src").srcs.root_path == Path("test_src") + assert pr.src_relative_path == Path("test_src") + + pr.git_repo.delete_repository() + + +if __name__ == "__main__": + pytest.main([__file__, "-s"]) diff --git a/tests/metagpt/utils/test_redis.py b/tests/metagpt/utils/test_redis.py index 140c04f6b..6fd4250a6 100644 --- a/tests/metagpt/utils/test_redis.py +++ b/tests/metagpt/utils/test_redis.py @@ -9,47 +9,29 @@ import pytest -from metagpt.config import CONFIG from metagpt.utils.redis import Redis -async def async_mock_from_url(*args, **kwargs): - mock_client = AsyncMock() - mock_client.set.return_value = None - mock_client.get.side_effect = [b"test", b""] - return mock_client - - @pytest.mark.asyncio async def test_redis(mocker): - # Mock - mocker.patch("aioredis.from_url", return_value=async_mock_from_url()) + async def async_mock_from_url(*args, **kwargs): + mock_client = AsyncMock() + mock_client.set.return_value = None + mock_client.get.return_value = b"test" + return mock_client - # Prerequisites - CONFIG.REDIS_HOST = "MOCK_REDIS_HOST" - CONFIG.REDIS_PORT = "MOCK_REDIS_PORT" - CONFIG.REDIS_PASSWORD = "MOCK_REDIS_PASSWORD" - CONFIG.REDIS_DB = 0 + mocker.patch("aioredis.from_url", return_value=async_mock_from_url()) + mock_config = mocker.Mock() + mock_config.to_url.return_value = "http://mock.com" + mock_config.username = "mockusername" + mock_config.password = "mockpwd" + mock_config.db = "0" - conn = Redis() - assert not conn.is_valid + conn = Redis(mock_config) await conn.set("test", "test", timeout_sec=0) assert await conn.get("test") == b"test" await conn.close() - # Mock session env - old_options = CONFIG.options.copy() - new_options = old_options.copy() - new_options["REDIS_HOST"] = "YOUR_REDIS_HOST" - CONFIG.set_context(new_options) - try: - conn = Redis() - await conn.set("test", "test", timeout_sec=0) - assert not await conn.get("test") == b"test" - await conn.close() - finally: - CONFIG.set_context(old_options) - if __name__ == "__main__": pytest.main([__file__, "-s"]) diff --git a/tests/metagpt/utils/test_repair_llm_raw_output.py b/tests/metagpt/utils/test_repair_llm_raw_output.py index 9d53b8243..e28423b91 100644 --- a/tests/metagpt/utils/test_repair_llm_raw_output.py +++ b/tests/metagpt/utils/test_repair_llm_raw_output.py @@ -2,13 +2,13 @@ # -*- coding: utf-8 -*- # @Desc : unittest of repair_llm_raw_output -from metagpt.config import CONFIG +from metagpt.config2 import config """ CONFIG.repair_llm_output should be True before retry_parse_json_text imported. so we move `from ... impot ...` into each `test_xx` to avoid `Module level import not at top of file` format warning. """ -CONFIG.repair_llm_output = True +config.repair_llm_output = True def test_repair_case_sensitivity(): @@ -135,7 +135,7 @@ def test_repair_json_format(): } """ target_output = """{ - "Language": "en_us", + "Language": "en_us", "Programming Language": "Python" }""" output = repair_llm_raw_output(output=raw_output, req_keys=[None], repair_type=RepairType.JSON) @@ -148,7 +148,7 @@ def test_repair_json_format(): } """ target_output = """{ - "Language": "en_us", + "Language": "en_us", "Programming Language": "Python" }""" output = repair_llm_raw_output(output=raw_output, req_keys=[None], repair_type=RepairType.JSON) @@ -161,7 +161,7 @@ def test_repair_json_format(): } """ target_output = """{ - "Language": "#en_us#", + "Language": "#en_us#", "Programming Language": "//Python # Code // Language//" }""" output = repair_llm_raw_output(output=raw_output, req_keys=[None], repair_type=RepairType.JSON) diff --git a/tests/metagpt/utils/test_s3.py b/tests/metagpt/utils/test_s3.py index 132aa0635..b26ebe94d 100644 --- a/tests/metagpt/utils/test_s3.py +++ b/tests/metagpt/utils/test_s3.py @@ -8,49 +8,42 @@ import uuid from pathlib import Path +import aioboto3 import aiofiles -import mock import pytest -from metagpt.config import CONFIG +from metagpt.config2 import Config from metagpt.utils.common import aread from metagpt.utils.s3 import S3 @pytest.mark.asyncio -@mock.patch("aioboto3.Session") -async def test_s3(mock_session_class): +async def test_s3(mocker): # Set up the mock response data = await aread(__file__, "utf-8") - mock_session_object = mock.Mock() - reader_mock = mock.AsyncMock() + reader_mock = mocker.AsyncMock() reader_mock.read.side_effect = [data.encode("utf-8"), b"", data.encode("utf-8")] - type(reader_mock).url = mock.PropertyMock(return_value="https://mock") - mock_client = mock.AsyncMock() + type(reader_mock).url = mocker.PropertyMock(return_value="https://mock") + mock_client = mocker.AsyncMock() mock_client.put_object.return_value = None mock_client.get_object.return_value = {"Body": reader_mock} mock_client.__aenter__.return_value = mock_client mock_client.__aexit__.return_value = None - mock_session_object.client.return_value = mock_client - mock_session_class.return_value = mock_session_object + mocker.patch.object(aioboto3.Session, "client", return_value=mock_client) # Prerequisites - # assert CONFIG.S3_ACCESS_KEY and CONFIG.S3_ACCESS_KEY != "YOUR_S3_ACCESS_KEY" - # assert CONFIG.S3_SECRET_KEY and CONFIG.S3_SECRET_KEY != "YOUR_S3_SECRET_KEY" - # assert CONFIG.S3_ENDPOINT_URL and CONFIG.S3_ENDPOINT_URL != "YOUR_S3_ENDPOINT_URL" - # assert CONFIG.S3_BUCKET and CONFIG.S3_BUCKET != "YOUR_S3_BUCKET" - - conn = S3() - assert conn.is_valid + s3 = Config.default().s3 + assert s3 + conn = S3(s3) object_name = "unittest.bak" - await conn.upload_file(bucket=CONFIG.S3_BUCKET, local_path=__file__, object_name=object_name) + await conn.upload_file(bucket=s3.bucket, local_path=__file__, object_name=object_name) pathname = (Path(__file__).parent / uuid.uuid4().hex).with_suffix(".bak") pathname.unlink(missing_ok=True) - await conn.download_file(bucket=CONFIG.S3_BUCKET, object_name=object_name, local_path=str(pathname)) + await conn.download_file(bucket=s3.bucket, object_name=object_name, local_path=str(pathname)) assert pathname.exists() - url = await conn.get_object_url(bucket=CONFIG.S3_BUCKET, object_name=object_name) + url = await conn.get_object_url(bucket=s3.bucket, object_name=object_name) assert url - bin_data = await conn.get_object(bucket=CONFIG.S3_BUCKET, object_name=object_name) + bin_data = await conn.get_object(bucket=s3.bucket, object_name=object_name) assert bin_data async with aiofiles.open(__file__, mode="r", encoding="utf-8") as reader: data = await reader.read() @@ -58,18 +51,14 @@ async def test_s3(mock_session_class): assert "http" in res # Mock session env - type(reader_mock).url = mock.PropertyMock(return_value="") - old_options = CONFIG.options.copy() - new_options = old_options.copy() - new_options["S3_ACCESS_KEY"] = "YOUR_S3_ACCESS_KEY" - CONFIG.set_context(new_options) + s3.access_key = "ABC" + type(reader_mock).url = mocker.PropertyMock(return_value="") try: - conn = S3() - assert not conn.is_valid + conn = S3(s3) res = await conn.cache("ABC", ".bak", "script") assert not res - finally: - CONFIG.set_context(old_options) + except Exception: + pass await reader.close() diff --git a/tests/metagpt/utils/test_save_code.py b/tests/metagpt/utils/test_save_code.py new file mode 100644 index 000000000..35ad84baf --- /dev/null +++ b/tests/metagpt/utils/test_save_code.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +# @Date : 12/12/2023 4:17 PM +# @Author : stellahong (stellahong@fuzhi.ai) +# @Desc : + +import nbformat +import pytest + +from metagpt.actions.ci.execute_nb_code import ExecuteNbCode +from metagpt.utils.common import read_json_file +from metagpt.utils.save_code import DATA_PATH, save_code_file + + +def test_save_code_file_python(): + save_code_file("example", "print('Hello, World!')") + file_path = DATA_PATH / "output" / "example" / "code.py" + assert file_path.exists(), f"File does not exist: {file_path}" + content = file_path.read_text() + assert "print('Hello, World!')" in content, "File content does not match" + + +def test_save_code_file_json(): + save_code_file("example_json", "print('Hello, JSON!')", file_format="json") + file_path = DATA_PATH / "output" / "example_json" / "code.json" + data = read_json_file(file_path) + assert "code" in data, "JSON key 'code' is missing" + assert data["code"] == "print('Hello, JSON!')", "JSON content does not match" + + +@pytest.mark.asyncio +async def test_save_code_file_notebook(): + code = "print('Hello, World!')" + executor = ExecuteNbCode() + await executor.run(code) + # Save as a Notebook file + save_code_file("example_nb", executor.nb, file_format="ipynb") + file_path = DATA_PATH / "output" / "example_nb" / "code.ipynb" + assert file_path.exists(), f"Notebook file does not exist: {file_path}" + + # Additional checks specific to notebook format + notebook = nbformat.read(file_path, as_version=4) + assert len(notebook.cells) > 0, "Notebook should have at least one cell" + first_cell_source = notebook.cells[0].source + assert "print" in first_cell_source, "Notebook cell content does not match" diff --git a/tests/mock/mock_aiohttp.py b/tests/mock/mock_aiohttp.py new file mode 100644 index 000000000..a7c022a4b --- /dev/null +++ b/tests/mock/mock_aiohttp.py @@ -0,0 +1,46 @@ +import json +from typing import Callable + +from aiohttp.client import ClientSession + +origin_request = ClientSession.request + + +class MockAioResponse: + check_funcs: dict[tuple[str, str], Callable[[dict], str]] = {} + rsp_cache: dict[str, str] = {} + name = "aiohttp" + + def __init__(self, session, method, url, **kwargs) -> None: + fn = self.check_funcs.get((method, url)) + _kwargs = {k: v for k, v in kwargs.items() if k != "proxy"} + self.key = f"{self.name}-{method}-{url}-{fn(kwargs) if fn else json.dumps(_kwargs, sort_keys=True)}" + self.mng = self.response = None + if self.key not in self.rsp_cache: + self.mng = origin_request(session, method, url, **kwargs) + + async def __aenter__(self): + if self.response: + await self.response.__aenter__() + elif self.mng: + self.response = await self.mng.__aenter__() + return self + + async def __aexit__(self, *args, **kwargs): + if self.response: + await self.response.__aexit__(*args, **kwargs) + self.response = None + elif self.mng: + await self.mng.__aexit__(*args, **kwargs) + self.mng = None + + async def json(self, *args, **kwargs): + if self.key in self.rsp_cache: + return self.rsp_cache[self.key] + data = await self.response.json(*args, **kwargs) + self.rsp_cache[self.key] = data + return data + + def raise_for_status(self): + if self.response: + self.response.raise_for_status() diff --git a/tests/mock/mock_curl_cffi.py b/tests/mock/mock_curl_cffi.py new file mode 100644 index 000000000..3f2bea4a7 --- /dev/null +++ b/tests/mock/mock_curl_cffi.py @@ -0,0 +1,22 @@ +import json +from typing import Callable + +from curl_cffi import requests + +origin_request = requests.Session.request + + +class MockCurlCffiResponse(requests.Response): + check_funcs: dict[tuple[str, str], Callable[[dict], str]] = {} + rsp_cache: dict[str, str] = {} + name = "curl-cffi" + + def __init__(self, session, method, url, **kwargs) -> None: + super().__init__() + fn = self.check_funcs.get((method, url)) + self.key = f"{self.name}-{method}-{url}-{fn(kwargs) if fn else json.dumps(kwargs, sort_keys=True)}" + self.response = None + if self.key not in self.rsp_cache: + response = origin_request(session, method, url, **kwargs) + self.rsp_cache[self.key] = response.content.decode() + self.content = self.rsp_cache[self.key].encode() diff --git a/tests/mock/mock_httplib2.py b/tests/mock/mock_httplib2.py new file mode 100644 index 000000000..b6dd0b77b --- /dev/null +++ b/tests/mock/mock_httplib2.py @@ -0,0 +1,29 @@ +import json +from typing import Callable +from urllib.parse import parse_qsl, urlparse + +import httplib2 + +origin_request = httplib2.Http.request + + +class MockHttplib2Response(httplib2.Response): + check_funcs: dict[tuple[str, str], Callable[[dict], str]] = {} + rsp_cache: dict[str, str] = {} + name = "httplib2" + + def __init__(self, http, uri, method="GET", **kwargs) -> None: + url = uri.split("?")[0] + result = urlparse(uri) + params = dict(parse_qsl(result.query)) + fn = self.check_funcs.get((method, uri)) + new_kwargs = {"params": params} + key = f"{self.name}-{method}-{url}-{fn(new_kwargs) if fn else json.dumps(new_kwargs)}" + if key not in self.rsp_cache: + _, self.content = origin_request(http, uri, method, **kwargs) + self.rsp_cache[key] = self.content.decode() + self.content = self.rsp_cache[key] + + def __iter__(self): + yield self + yield self.content.encode() diff --git a/tests/mock/mock_llm.py b/tests/mock/mock_llm.py index 35e0e9ee9..50e75dabf 100644 --- a/tests/mock/mock_llm.py +++ b/tests/mock/mock_llm.py @@ -1,12 +1,22 @@ -from typing import Optional +import json +from typing import Optional, Union +from metagpt.config2 import config +from metagpt.configs.llm_config import LLMType from metagpt.logs import log_llm_stream, logger +from metagpt.provider.azure_openai_api import AzureOpenAILLM from metagpt.provider.openai_api import OpenAILLM +from metagpt.schema import Message +OriginalLLM = OpenAILLM if config.llm.api_type == LLMType.OPENAI else AzureOpenAILLM -class MockLLM(OpenAILLM): + +class MockLLM(OriginalLLM): def __init__(self, allow_open_api_call): - super().__init__() + original_llm_config = ( + config.get_openai_llm() if config.llm.api_type == LLMType.OPENAI else config.get_azure_llm() + ) + super().__init__(original_llm_config) self.allow_open_api_call = allow_open_api_call self.rsp_cache: dict = {} self.rsp_candidates: list[dict] = [] # a test can have multiple calls with the same llm, thus a list @@ -34,6 +44,7 @@ async def original_aask( msg: str, system_msgs: Optional[list[str]] = None, format_msgs: Optional[list[dict[str, str]]] = None, + images: Optional[Union[str, list[str]]] = None, timeout=3, stream=True, ): @@ -46,7 +57,7 @@ async def original_aask( message = [] if format_msgs: message.extend(format_msgs) - message.append(self._user_msg(msg)) + message.append(self._user_msg(msg, images=images)) rsp = await self.acompletion_text(message, stream=stream, timeout=timeout) return rsp @@ -60,11 +71,20 @@ async def original_aask_batch(self, msgs: list, timeout=3) -> str: context.append(self._assistant_msg(rsp_text)) return self._extract_assistant_rsp(context) + async def original_aask_code(self, messages: Union[str, Message, list[dict]], **kwargs) -> dict: + """ + A copy of metagpt.provider.openai_api.OpenAILLM.aask_code, we can't use super().aask because it will be mocked. + Since openai_api.OpenAILLM.aask_code is different from base_llm.BaseLLM.aask_code, we use the former. + """ + rsp = await self._achat_completion_function(messages, **kwargs) + return self.get_choice_function_arguments(rsp) + async def aask( self, msg: str, system_msgs: Optional[list[str]] = None, format_msgs: Optional[list[dict[str, str]]] = None, + images: Optional[Union[str, list[str]]] = None, timeout=3, stream=True, ) -> str: @@ -72,7 +92,7 @@ async def aask( if system_msgs: joined_system_msg = "#MSG_SEP#".join(system_msgs) + "#SYSTEM_MSG_END#" msg_key = joined_system_msg + msg_key - rsp = await self._mock_rsp(msg_key, self.original_aask, msg, system_msgs, format_msgs, timeout, stream) + rsp = await self._mock_rsp(msg_key, self.original_aask, msg, system_msgs, format_msgs, images, timeout, stream) return rsp async def aask_batch(self, msgs: list, timeout=3) -> str: @@ -80,6 +100,12 @@ async def aask_batch(self, msgs: list, timeout=3) -> str: rsp = await self._mock_rsp(msg_key, self.original_aask_batch, msgs, timeout) return rsp + async def aask_code(self, messages: Union[str, Message, list[dict]], **kwargs) -> dict: + messages = self._process_message(messages) + msg_key = json.dumps(messages, ensure_ascii=False) + rsp = await self._mock_rsp(msg_key, self.original_aask_code, messages, **kwargs) + return rsp + async def _mock_rsp(self, msg_key, ask_func, *args, **kwargs): if msg_key not in self.rsp_cache: if not self.allow_open_api_call: diff --git a/tests/spark.yaml b/tests/spark.yaml new file mode 100644 index 000000000..a5bbd98bd --- /dev/null +++ b/tests/spark.yaml @@ -0,0 +1,7 @@ +llm: + api_type: "spark" + app_id: "xxx" + api_key: "xxx" + api_secret: "xxx" + domain: "generalv2" + base_url: "wss://spark-api.xf-yun.com/v3.1/chat" \ No newline at end of file