Upload 176 files
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .dockerignore +73 -0
- .gitattributes +7 -0
- .github/workflows/tests.yml +30 -0
- .gitignore +99 -0
- .pre-commit-config.yaml +10 -0
- .readthedocs.yml +31 -0
- CLAUDE.md +88 -0
- LICENSE +536 -0
- MANIFEST.in +22 -0
- README.md +192 -0
- cmbagent/__init__.py +75 -0
- cmbagent/agents/admin/__init__.py +0 -0
- cmbagent/agents/admin/admin.yaml +11 -0
- cmbagent/agents/coding/engineer/__init__.py +0 -0
- cmbagent/agents/coding/engineer/engineer.yaml +206 -0
- cmbagent/agents/coding/engineer/massgen_engineer.py +499 -0
- cmbagent/agents/coding/engineer_nest/__init__.py +0 -0
- cmbagent/agents/coding/engineer_nest/engineer_nest.yaml +12 -0
- cmbagent/agents/coding/engineer_response_formatter/__init__.py +0 -0
- cmbagent/agents/coding/engineer_response_formatter/engineer_response_formatter.py +325 -0
- cmbagent/agents/coding/engineer_response_formatter/engineer_response_formatter.yaml +86 -0
- cmbagent/agents/coding/executor/__init__.py +0 -0
- cmbagent/agents/coding/executor/executor.yaml +22 -0
- cmbagent/agents/coding/executor_bash/__init__.py +0 -0
- cmbagent/agents/coding/executor_bash/executor_bash.yaml +22 -0
- cmbagent/agents/coding/executor_response_formatter/__init__.py +0 -0
- cmbagent/agents/coding/executor_response_formatter/executor_response_formatter.py +65 -0
- cmbagent/agents/coding/executor_response_formatter/executor_response_formatter.yaml +23 -0
- cmbagent/agents/control/control_starter/__init__.py +0 -0
- cmbagent/agents/control/control_starter/control_starter.yaml +40 -0
- cmbagent/agents/control/controller/__init__.py +0 -0
- cmbagent/agents/control/controller/controller.yaml +59 -0
- cmbagent/agents/control/terminator/__init__.py +0 -0
- cmbagent/agents/control/terminator/terminator.yaml +22 -0
- cmbagent/agents/hypothesis/idea_hater/__init__.py +0 -0
- cmbagent/agents/hypothesis/idea_hater/idea_hater.yaml +65 -0
- cmbagent/agents/hypothesis/idea_hater_response_formatter/__init__.py +0 -0
- cmbagent/agents/hypothesis/idea_hater_response_formatter/idea_hater_response_formatter.py +51 -0
- cmbagent/agents/hypothesis/idea_hater_response_formatter/idea_hater_response_formatter.yaml +9 -0
- cmbagent/agents/hypothesis/idea_maker/__init__.py +0 -0
- cmbagent/agents/hypothesis/idea_maker/idea_maker.yaml +60 -0
- cmbagent/agents/hypothesis/idea_maker_response_formatter/__init__.py +0 -0
- cmbagent/agents/hypothesis/idea_maker_response_formatter/idea_maker_response_formatter.py +51 -0
- cmbagent/agents/hypothesis/idea_maker_response_formatter/idea_maker_response_formatter.yaml +11 -0
- cmbagent/agents/hypothesis/idea_saver/__init__.py +0 -0
- cmbagent/agents/hypothesis/idea_saver/idea_saver.py +138 -0
- cmbagent/agents/hypothesis/idea_saver/idea_saver.yaml +11 -0
- cmbagent/agents/installer/__init__.py +0 -0
- cmbagent/agents/installer/installer.py +38 -0
- cmbagent/agents/installer/installer.yaml +13 -0
.dockerignore
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Dependencies
|
| 2 |
+
node_modules/
|
| 3 |
+
cmbagent-ui/node_modules/
|
| 4 |
+
cmbagent_env/
|
| 5 |
+
__pycache__/
|
| 6 |
+
*.pyc
|
| 7 |
+
*.pyo
|
| 8 |
+
*.pyd
|
| 9 |
+
.Python
|
| 10 |
+
build/
|
| 11 |
+
develop-eggs/
|
| 12 |
+
dist/
|
| 13 |
+
downloads/
|
| 14 |
+
eggs/
|
| 15 |
+
.eggs/
|
| 16 |
+
lib/
|
| 17 |
+
lib64/
|
| 18 |
+
parts/
|
| 19 |
+
sdist/
|
| 20 |
+
var/
|
| 21 |
+
wheels/
|
| 22 |
+
*.egg-info/
|
| 23 |
+
.installed.cfg
|
| 24 |
+
*.egg
|
| 25 |
+
|
| 26 |
+
# IDE and editor files
|
| 27 |
+
.vscode/
|
| 28 |
+
.idea/
|
| 29 |
+
*.swp
|
| 30 |
+
*.swo
|
| 31 |
+
*~
|
| 32 |
+
|
| 33 |
+
# OS files
|
| 34 |
+
.DS_Store
|
| 35 |
+
.DS_Store?
|
| 36 |
+
._*
|
| 37 |
+
.Spotlight-V100
|
| 38 |
+
.Trashes
|
| 39 |
+
ehthumbs.db
|
| 40 |
+
Thumbs.db
|
| 41 |
+
|
| 42 |
+
# Git
|
| 43 |
+
.git/
|
| 44 |
+
.gitignore
|
| 45 |
+
|
| 46 |
+
# Environment files
|
| 47 |
+
.env
|
| 48 |
+
.env.local
|
| 49 |
+
.env.development.local
|
| 50 |
+
.env.test.local
|
| 51 |
+
.env.production.local
|
| 52 |
+
|
| 53 |
+
# Logs
|
| 54 |
+
npm-debug.log*
|
| 55 |
+
yarn-debug.log*
|
| 56 |
+
yarn-error.log*
|
| 57 |
+
*.log
|
| 58 |
+
|
| 59 |
+
# Next.js build output
|
| 60 |
+
.next/
|
| 61 |
+
out/
|
| 62 |
+
|
| 63 |
+
# Misc
|
| 64 |
+
*.tgz
|
| 65 |
+
*.tar.gz
|
| 66 |
+
|
| 67 |
+
# Testing
|
| 68 |
+
coverage/
|
| 69 |
+
.nyc_output
|
| 70 |
+
|
| 71 |
+
# Temporary files
|
| 72 |
+
tmp/
|
| 73 |
+
temp/
|
.gitattributes
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cmbagent/utils/keywords/aas_kwd_to_url.pkl filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
docs/examples/report_helper/matter_power_spectrum_emulation_report_experiments_discussion_conclusion.pdf filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
docs/examples/report_helper/matter_power_spectrum_emulation_report_intro_and_background.pdf filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
docs/examples/report_helper/matter_power_spectrum_emulation_report_methodology_and_implementation.pdf filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
docs/examples/report_helper/matter_power_spectrum_emulation_report_outline.pdf filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
docs/notebooks/legacy/Beta2/cmbagent_beta2_demo_economics.ipynb filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
images/logo.png filter=lfs diff=lfs merge=lfs -text
|
.github/workflows/tests.yml
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: tests
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
workflow_dispatch:
|
| 5 |
+
|
| 6 |
+
jobs:
|
| 7 |
+
pytest:
|
| 8 |
+
runs-on: ubuntu-latest
|
| 9 |
+
steps:
|
| 10 |
+
- name: Checkout cmbagent_v2 branch
|
| 11 |
+
uses: actions/checkout@v4
|
| 12 |
+
with:
|
| 13 |
+
ref: cmbagent_v2
|
| 14 |
+
|
| 15 |
+
- name: Set up Python
|
| 16 |
+
uses: actions/setup-python@v5
|
| 17 |
+
with:
|
| 18 |
+
python-version: "3.12"
|
| 19 |
+
|
| 20 |
+
- name: Install
|
| 21 |
+
run: |
|
| 22 |
+
python -m pip install -U pip
|
| 23 |
+
pip install -e .
|
| 24 |
+
pip install pytest
|
| 25 |
+
|
| 26 |
+
- name: Run tests
|
| 27 |
+
env:
|
| 28 |
+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
| 29 |
+
run: |
|
| 30 |
+
pytest -s
|
.gitignore
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# mac
|
| 2 |
+
.DS_Store
|
| 3 |
+
.cache
|
| 4 |
+
|
| 5 |
+
.venv/
|
| 6 |
+
|
| 7 |
+
# Byte-compiled / optimized / DLL files
|
| 8 |
+
__pycache__/
|
| 9 |
+
*.py[cod]
|
| 10 |
+
*$py.class
|
| 11 |
+
|
| 12 |
+
.ipynb_checkpoints*
|
| 13 |
+
|
| 14 |
+
# C extensions
|
| 15 |
+
*.so
|
| 16 |
+
|
| 17 |
+
# Distribution / packaging
|
| 18 |
+
.Python
|
| 19 |
+
build/
|
| 20 |
+
develop-eggs/
|
| 21 |
+
dist/
|
| 22 |
+
downloads/
|
| 23 |
+
eggs/
|
| 24 |
+
.eggs/
|
| 25 |
+
lib/
|
| 26 |
+
lib64/
|
| 27 |
+
parts/
|
| 28 |
+
sdist/
|
| 29 |
+
var/
|
| 30 |
+
wheels/
|
| 31 |
+
*.egg-info/
|
| 32 |
+
.installed.cfg
|
| 33 |
+
*.egg
|
| 34 |
+
MANIFEST
|
| 35 |
+
evals/logs
|
| 36 |
+
logs
|
| 37 |
+
data
|
| 38 |
+
# PyInstaller
|
| 39 |
+
# Usually these files are written by a python script from a template
|
| 40 |
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
| 41 |
+
*.manifest
|
| 42 |
+
*.spec
|
| 43 |
+
|
| 44 |
+
output/*
|
| 45 |
+
cmbagent/data/classy_sz/*.yaml
|
| 46 |
+
cmbagent/data/classy_sz/*.txt
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
cmbagent/data/classy/*.yaml
|
| 50 |
+
cmbagent/data/classy/*.txt
|
| 51 |
+
|
| 52 |
+
cmbagent/data/camb/*.yaml
|
| 53 |
+
cmbagent/data/camb/*.txt
|
| 54 |
+
|
| 55 |
+
evals/
|
| 56 |
+
logs/
|
| 57 |
+
|
| 58 |
+
.env
|
| 59 |
+
*bor11031990*
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
docs/_*
|
| 63 |
+
output/cmb_tt_power_spectrum.png
|
| 64 |
+
docs/notebooks/bbolliet_local
|
| 65 |
+
docs/cmbagent_*_arxiv
|
| 66 |
+
docs/cmbagent_*zip
|
| 67 |
+
docs/videos/*
|
| 68 |
+
output_bkp
|
| 69 |
+
docs/notebooks/output
|
| 70 |
+
docs/notebooks/cmbagent_output
|
| 71 |
+
|
| 72 |
+
project_data
|
| 73 |
+
docs/notebooks/*
|
| 74 |
+
docs/notebooks/cmbagent_beta2_demo_1shot.ipynb
|
| 75 |
+
docs/notebooks/astropilot_data/.DS_Store
|
| 76 |
+
docs/notebooks/astropilot_data/plots/.DS_Store
|
| 77 |
+
gui_expt.py
|
| 78 |
+
tests/Project*
|
| 79 |
+
project_dir
|
| 80 |
+
|
| 81 |
+
# Frontend / Node.js
|
| 82 |
+
cmbagent-ui/node_modules/
|
| 83 |
+
cmbagent-ui/.next/
|
| 84 |
+
cmbagent-ui/out/
|
| 85 |
+
cmbagent-ui/.env.local
|
| 86 |
+
cmbagent-ui/.env.development.local
|
| 87 |
+
cmbagent-ui/.env.test.local
|
| 88 |
+
cmbagent-ui/.env.production.local
|
| 89 |
+
|
| 90 |
+
# Test files
|
| 91 |
+
test_*.py
|
| 92 |
+
tests/testdocs/*
|
| 93 |
+
tests/artifacts/
|
| 94 |
+
tests/legacy/
|
| 95 |
+
|
| 96 |
+
.massgen
|
| 97 |
+
|
| 98 |
+
# PyCharm
|
| 99 |
+
.idea/
|
.pre-commit-config.yaml
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
repos:
|
| 2 |
+
- repo: https://github.com/kynan/nbstripout
|
| 3 |
+
rev: 0.8.2
|
| 4 |
+
hooks:
|
| 5 |
+
- id: nbstripout
|
| 6 |
+
files: ^notebooks/.*\.ipynb$
|
| 7 |
+
args:
|
| 8 |
+
- --keep-output
|
| 9 |
+
- --extra-keys
|
| 10 |
+
- metadata.kernelspec metadata.language_info
|
.readthedocs.yml
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# .readthedocs.yml
|
| 2 |
+
# Read the Docs configuration file
|
| 3 |
+
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
| 4 |
+
|
| 5 |
+
# Required
|
| 6 |
+
version: 2
|
| 7 |
+
|
| 8 |
+
# Set the OS, Python version and other tools you might need
|
| 9 |
+
build:
|
| 10 |
+
os: ubuntu-22.04
|
| 11 |
+
tools:
|
| 12 |
+
python: "3.7"
|
| 13 |
+
|
| 14 |
+
# Build documentation in the docs/ directory with Sphinx
|
| 15 |
+
sphinx:
|
| 16 |
+
configuration: docs/conf.py
|
| 17 |
+
|
| 18 |
+
# Build documentation with MkDocs
|
| 19 |
+
#mkdocs:
|
| 20 |
+
# configuration: mkdocs.yml
|
| 21 |
+
|
| 22 |
+
# Optionally build your docs in additional formats such as PDF
|
| 23 |
+
formats:
|
| 24 |
+
- pdf
|
| 25 |
+
|
| 26 |
+
# Optional but recommended, declare the Python requirements required
|
| 27 |
+
# to build your documentation
|
| 28 |
+
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
|
| 29 |
+
python:
|
| 30 |
+
install:
|
| 31 |
+
- requirements: docs/requirements.txt
|
CLAUDE.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# CLAUDE.md
|
| 2 |
+
|
| 3 |
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
| 4 |
+
|
| 5 |
+
## Project Overview
|
| 6 |
+
|
| 7 |
+
CMBAgent is a multi-agent system powered by AG2 (formerly AutoGen) for scientific discovery across scientific domains. The system follows a planning and control strategy with specialized agents for different research workflows.
|
| 8 |
+
|
| 9 |
+
The system uses a two-phase approach:
|
| 10 |
+
- **Planning**: A planner and plan reviewer design task execution strategies
|
| 11 |
+
- **Control**: Step-by-step execution where sub-tasks are handed to specialized agents
|
| 12 |
+
|
| 13 |
+
## Key Components
|
| 14 |
+
|
| 15 |
+
### Core Python Package (`cmbagent/`)
|
| 16 |
+
- **Main API**: `cmbagent.one_shot()` - primary function for task execution
|
| 17 |
+
- **Agent system**: Specialized agents in `agents/` directory, each with `.py` and `.yaml` configuration
|
| 18 |
+
- **Agent types**: Planning, control, coding (engineer, executor), research (researcher, summarizer), hypothesis generation, keyword extraction, and domain-specific templates
|
| 19 |
+
- **Context management**: Sophisticated context handling via `context.py` and `hand_offs.py`
|
| 20 |
+
- **Remote execution**: `execution/remote_executor.py` - delegates code execution to client
|
| 21 |
+
|
| 22 |
+
## Common Development Commands
|
| 23 |
+
|
| 24 |
+
### Python Package
|
| 25 |
+
```bash
|
| 26 |
+
# Install core only (lightweight)
|
| 27 |
+
pip install -e .
|
| 28 |
+
|
| 29 |
+
# Install with development tools
|
| 30 |
+
pip install -e ".[dev]"
|
| 31 |
+
|
| 32 |
+
# Install with local execution support (if not using remote execution)
|
| 33 |
+
pip install -e ".[local]"
|
| 34 |
+
|
| 35 |
+
# Install everything (all scientific packages)
|
| 36 |
+
pip install -e ".[all]"
|
| 37 |
+
|
| 38 |
+
# Run tests (individual Python scripts in tests/)
|
| 39 |
+
python tests/test_one_shot.py
|
| 40 |
+
python tests/test_engineer.py
|
| 41 |
+
```
|
| 42 |
+
|
| 43 |
+
## Architecture Details
|
| 44 |
+
|
| 45 |
+
### Agent System
|
| 46 |
+
- Each agent has a Python implementation and YAML configuration
|
| 47 |
+
- Agents are specialized for different workflows: engineering, research, planning, execution, hypothesis generation, keyword extraction
|
| 48 |
+
- Response formatters handle output formatting for specific use cases
|
| 49 |
+
- Controller manages workflow orchestration
|
| 50 |
+
- Domain-specific agents in `specialized/` serve as templates for users to add their own
|
| 51 |
+
|
| 52 |
+
### Multi-Modal Capabilities
|
| 53 |
+
- Plot generation capabilities
|
| 54 |
+
- File browser with inline image viewing
|
| 55 |
+
|
| 56 |
+
### Research Capabilities
|
| 57 |
+
- Domain-agnostic architecture supporting any scientific field
|
| 58 |
+
- Literature search and keyword extraction (`literature.py`, `aas_keyword_finder`)
|
| 59 |
+
- Example domain packages provided: materials science, biochemistry, astronomy, data science
|
| 60 |
+
- Users can easily add their own domain-specific dependencies in `pyproject.toml`
|
| 61 |
+
|
| 62 |
+
### Work Directory Structure
|
| 63 |
+
Tasks create organized output directories:
|
| 64 |
+
```
|
| 65 |
+
project_dir/
|
| 66 |
+
├── chats/ # Conversation history
|
| 67 |
+
├── codebase/ # Generated code
|
| 68 |
+
├── cost/ # Cost analysis
|
| 69 |
+
├── data/ # Generated data and plots
|
| 70 |
+
├── time/ # Timing reports
|
| 71 |
+
└── context/ # Context files (.pkl)
|
| 72 |
+
```
|
| 73 |
+
|
| 74 |
+
## Testing
|
| 75 |
+
- Tests are individual Python scripts in `tests/` directory
|
| 76 |
+
- Run specific test files directly: `python tests/test_<name>.py`
|
| 77 |
+
- Tests cover various agents and use cases (engineering, research, plotting, etc.)
|
| 78 |
+
- No centralized test runner - tests are designed as standalone demonstrations
|
| 79 |
+
|
| 80 |
+
## Configuration
|
| 81 |
+
- API keys required: `OPENAI_API_KEY`, optionally `ANTHROPIC_API_KEY`, `GEMINI_API_KEY`
|
| 82 |
+
- Model configurations in `cmbagent/apis/` (JSON files for different providers)
|
| 83 |
+
- Agent configurations in individual `.yaml` files alongside Python implementations
|
| 84 |
+
|
| 85 |
+
## Key APIs
|
| 86 |
+
- `cmbagent.one_shot(task, agent='engineer', model='gpt-4o', work_dir=...)` - Main execution API
|
| 87 |
+
- Planning and control via specialized agent orchestration
|
| 88 |
+
- WebSocket streaming for real-time UI updates
|
LICENSE
ADDED
|
@@ -0,0 +1,536 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Apache License
|
| 2 |
+
Version 2.0, January 2004
|
| 3 |
+
http://www.apache.org/licenses/
|
| 4 |
+
|
| 5 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 6 |
+
|
| 7 |
+
1. Definitions.
|
| 8 |
+
|
| 9 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 10 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 11 |
+
|
| 12 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 13 |
+
the copyright owner that is granting the License.
|
| 14 |
+
|
| 15 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 16 |
+
other entities that control, are controlled by, or are under common
|
| 17 |
+
control with that entity. For the purposes of this definition,
|
| 18 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 19 |
+
direction or management of such entity, whether by contract or
|
| 20 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 21 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 22 |
+
|
| 23 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 24 |
+
exercising permissions granted by this License.
|
| 25 |
+
|
| 26 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 27 |
+
including but not limited to software source code, documentation
|
| 28 |
+
source, and configuration files.
|
| 29 |
+
|
| 30 |
+
"Object" form shall mean any form resulting from mechanical
|
| 31 |
+
transformation or translation of a Source form, including but
|
| 32 |
+
not limited to compiled object code, generated documentation,
|
| 33 |
+
and conversions to other media types.
|
| 34 |
+
|
| 35 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 36 |
+
Object form, made available under the License, as indicated by a
|
| 37 |
+
copyright notice that is included in or attached to the work
|
| 38 |
+
(an example is provided in the Appendix below).
|
| 39 |
+
|
| 40 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 41 |
+
form, that is based on (or derived from) the Work and for which the
|
| 42 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 43 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 44 |
+
of this License, Derivative Works shall not include works that remain
|
| 45 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 46 |
+
the Work and Derivative Works thereof.
|
| 47 |
+
|
| 48 |
+
"Contribution" shall mean any work of authorship, including
|
| 49 |
+
the original version of the Work and any modifications or additions
|
| 50 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 51 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 52 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 53 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 54 |
+
means any form of electronic, verbal, or written communication sent
|
| 55 |
+
to the Licensor or its representatives, including but not limited to
|
| 56 |
+
communication on electronic mailing lists, source code control systems,
|
| 57 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 58 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 59 |
+
excluding communication that is conspicuously marked or otherwise
|
| 60 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 61 |
+
|
| 62 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 63 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 64 |
+
subsequently incorporated within the Work.
|
| 65 |
+
|
| 66 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 67 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 68 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 69 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 70 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 71 |
+
Work and such Derivative Works in Source or Object form.
|
| 72 |
+
|
| 73 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 74 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 75 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 76 |
+
(except as stated in this section) patent license to make, have made,
|
| 77 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 78 |
+
where such license applies only to those patent claims licensable
|
| 79 |
+
by such Contributor that are necessarily infringed by their
|
| 80 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 81 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 82 |
+
institute patent litigation against any entity (including a
|
| 83 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 84 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 85 |
+
or contributory patent infringement, then any patent licenses
|
| 86 |
+
granted to You under this License for that Work shall terminate
|
| 87 |
+
as of the date such litigation is filed.
|
| 88 |
+
|
| 89 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 90 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 91 |
+
modifications, and in Source or Object form, provided that You
|
| 92 |
+
meet the following conditions:
|
| 93 |
+
|
| 94 |
+
(a) You must give any other recipients of the Work or
|
| 95 |
+
Derivative Works a copy of this License; and
|
| 96 |
+
|
| 97 |
+
(b) You must cause any modified files to carry prominent notices
|
| 98 |
+
stating that You changed the files; and
|
| 99 |
+
|
| 100 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 101 |
+
that You distribute, all copyright, patent, trademark, and
|
| 102 |
+
attribution notices from the Source form of the Work,
|
| 103 |
+
excluding those notices that do not pertain to any part of
|
| 104 |
+
the Derivative Works; and
|
| 105 |
+
|
| 106 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 107 |
+
distribution, then any Derivative Works that You distribute must
|
| 108 |
+
include a readable copy of the attribution notices contained
|
| 109 |
+
within such NOTICE file, excluding those notices that do not
|
| 110 |
+
pertain to any part of the Derivative Works, in at least one
|
| 111 |
+
of the following places: within a NOTICE text file distributed
|
| 112 |
+
as part of the Derivative Works; within the Source form or
|
| 113 |
+
documentation, if provided along with the Derivative Works; or,
|
| 114 |
+
within a display generated by the Derivative Works, if and
|
| 115 |
+
wherever such third-party notices normally appear. The contents
|
| 116 |
+
of the NOTICE file are for informational purposes only and
|
| 117 |
+
do not modify the License. You may add Your own attribution
|
| 118 |
+
notices within Derivative Works that You distribute, alongside
|
| 119 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 120 |
+
that such additional attribution notices cannot be construed
|
| 121 |
+
as modifying the License.
|
| 122 |
+
|
| 123 |
+
You may add Your own copyright statement to Your modifications and
|
| 124 |
+
may provide additional or different license terms and conditions
|
| 125 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 126 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 127 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 128 |
+
the conditions stated in this License.
|
| 129 |
+
|
| 130 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 131 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 132 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 133 |
+
this License, without any additional terms or conditions.
|
| 134 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 135 |
+
the terms of any separate license agreement you may have executed
|
| 136 |
+
with Licensor regarding such Contributions.
|
| 137 |
+
|
| 138 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 139 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 140 |
+
except as required for reasonable and customary use in describing the
|
| 141 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 142 |
+
|
| 143 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 144 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 145 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 146 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 147 |
+
implied, including, without limitation, any warranties or conditions
|
| 148 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 149 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 150 |
+
appropriateness of using or redistributing the Work and assume any
|
| 151 |
+
risks associated with Your exercise of permissions under this License.
|
| 152 |
+
|
| 153 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 154 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 155 |
+
unless required by applicable law (such as deliberate and grossly
|
| 156 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 157 |
+
liable to You for damages, including any direct, indirect, special,
|
| 158 |
+
incidental, or consequential damages of any character arising as a
|
| 159 |
+
result of this License or out of the use or inability to use the
|
| 160 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 161 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 162 |
+
other commercial damages or losses), even if such Contributor
|
| 163 |
+
has been advised of the possibility of such damages.
|
| 164 |
+
|
| 165 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 166 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 167 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 168 |
+
or other liability obligations and/or rights consistent with this
|
| 169 |
+
License. However, in accepting such obligations, You may act only
|
| 170 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 171 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 172 |
+
defend, and hold each Contributor harmless for any liability
|
| 173 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 174 |
+
of your accepting any such warranty or additional liability.
|
| 175 |
+
|
| 176 |
+
END OF TERMS AND CONDITIONS
|
| 177 |
+
|
| 178 |
+
APPENDIX: How to apply the Apache License to your work.
|
| 179 |
+
|
| 180 |
+
To apply the Apache License to your work, attach the following
|
| 181 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
| 182 |
+
replaced with your own identifying information. (Don't include
|
| 183 |
+
the brackets!) The text should be enclosed in the appropriate
|
| 184 |
+
comment syntax for the file format. We also recommend that a
|
| 185 |
+
file or class name and description of purpose be included on the
|
| 186 |
+
same "printed page" as the copyright notice for easier
|
| 187 |
+
identification within third-party archives.
|
| 188 |
+
|
| 189 |
+
Copyright 2026 - Boris Bolliet
|
| 190 |
+
|
| 191 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 192 |
+
you may not use this file except in compliance with the License.
|
| 193 |
+
You may obtain a copy of the License at
|
| 194 |
+
|
| 195 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 196 |
+
|
| 197 |
+
Unless required by applicable law or agreed to in writing, software
|
| 198 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 199 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 200 |
+
See the License for the specific language governing permissions and
|
| 201 |
+
limitations under the License.
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
|
| 205 |
+
APACHE HTTP SERVER SUBCOMPONENTS:
|
| 206 |
+
|
| 207 |
+
The Apache HTTP Server includes a number of subcomponents with
|
| 208 |
+
separate copyright notices and license terms. Your use of the source
|
| 209 |
+
code for the these subcomponents is subject to the terms and
|
| 210 |
+
conditions of the following licenses.
|
| 211 |
+
|
| 212 |
+
For the mod_mime_magic component:
|
| 213 |
+
|
| 214 |
+
/*
|
| 215 |
+
* mod_mime_magic: MIME type lookup via file magic numbers
|
| 216 |
+
* Copyright (c) 1996-1997 Cisco Systems, Inc.
|
| 217 |
+
*
|
| 218 |
+
* This software was submitted by Cisco Systems to the Apache Group in July
|
| 219 |
+
* 1997. Future revisions and derivatives of this source code must
|
| 220 |
+
* acknowledge Cisco Systems as the original contributor of this module.
|
| 221 |
+
* All other licensing and usage conditions are those of the Apache Group.
|
| 222 |
+
*
|
| 223 |
+
* Some of this code is derived from the free version of the file command
|
| 224 |
+
* originally posted to comp.sources.unix. Copyright info for that program
|
| 225 |
+
* is included below as required.
|
| 226 |
+
* ---------------------------------------------------------------------------
|
| 227 |
+
* - Copyright (c) Ian F. Darwin, 1987. Written by Ian F. Darwin.
|
| 228 |
+
*
|
| 229 |
+
* This software is not subject to any license of the American Telephone and
|
| 230 |
+
* Telegraph Company or of the Regents of the University of California.
|
| 231 |
+
*
|
| 232 |
+
* Permission is granted to anyone to use this software for any purpose on any
|
| 233 |
+
* computer system, and to alter it and redistribute it freely, subject to
|
| 234 |
+
* the following restrictions:
|
| 235 |
+
*
|
| 236 |
+
* 1. The author is not responsible for the consequences of use of this
|
| 237 |
+
* software, no matter how awful, even if they arise from flaws in it.
|
| 238 |
+
*
|
| 239 |
+
* 2. The origin of this software must not be misrepresented, either by
|
| 240 |
+
* explicit claim or by omission. Since few users ever read sources, credits
|
| 241 |
+
* must appear in the documentation.
|
| 242 |
+
*
|
| 243 |
+
* 3. Altered versions must be plainly marked as such, and must not be
|
| 244 |
+
* misrepresented as being the original software. Since few users ever read
|
| 245 |
+
* sources, credits must appear in the documentation.
|
| 246 |
+
*
|
| 247 |
+
* 4. This notice may not be removed or altered.
|
| 248 |
+
* -------------------------------------------------------------------------
|
| 249 |
+
*
|
| 250 |
+
*/
|
| 251 |
+
|
| 252 |
+
|
| 253 |
+
For the modules\mappers\mod_imagemap.c component:
|
| 254 |
+
|
| 255 |
+
"macmartinized" polygon code copyright 1992 by Eric Haines, erich@eye.com
|
| 256 |
+
|
| 257 |
+
For the server\util_md5.c component:
|
| 258 |
+
|
| 259 |
+
/************************************************************************
|
| 260 |
+
* NCSA HTTPd Server
|
| 261 |
+
* Software Development Group
|
| 262 |
+
* National Center for Supercomputing Applications
|
| 263 |
+
* University of Illinois at Urbana-Champaign
|
| 264 |
+
* 605 E. Springfield, Champaign, IL 61820
|
| 265 |
+
* httpd@ncsa.uiuc.edu
|
| 266 |
+
*
|
| 267 |
+
* Copyright (C) 1995, Board of Trustees of the University of Illinois
|
| 268 |
+
*
|
| 269 |
+
************************************************************************
|
| 270 |
+
*
|
| 271 |
+
* md5.c: NCSA HTTPd code which uses the md5c.c RSA Code
|
| 272 |
+
*
|
| 273 |
+
* Original Code Copyright (C) 1994, Jeff Hostetler, Spyglass, Inc.
|
| 274 |
+
* Portions of Content-MD5 code Copyright (C) 1993, 1994 by Carnegie Mellon
|
| 275 |
+
* University (see Copyright below).
|
| 276 |
+
* Portions of Content-MD5 code Copyright (C) 1991 Bell Communications
|
| 277 |
+
* Research, Inc. (Bellcore) (see Copyright below).
|
| 278 |
+
* Portions extracted from mpack, John G. Myers - jgm+@cmu.edu
|
| 279 |
+
* Content-MD5 Code contributed by Martin Hamilton (martin@net.lut.ac.uk)
|
| 280 |
+
*
|
| 281 |
+
*/
|
| 282 |
+
|
| 283 |
+
|
| 284 |
+
/* these portions extracted from mpack, John G. Myers - jgm+@cmu.edu */
|
| 285 |
+
/* (C) Copyright 1993,1994 by Carnegie Mellon University
|
| 286 |
+
* All Rights Reserved.
|
| 287 |
+
*
|
| 288 |
+
* Permission to use, copy, modify, distribute, and sell this software
|
| 289 |
+
* and its documentation for any purpose is hereby granted without
|
| 290 |
+
* fee, provided that the above copyright notice appear in all copies
|
| 291 |
+
* and that both that copyright notice and this permission notice
|
| 292 |
+
* appear in supporting documentation, and that the name of Carnegie
|
| 293 |
+
* Mellon University not be used in advertising or publicity
|
| 294 |
+
* pertaining to distribution of the software without specific,
|
| 295 |
+
* written prior permission. Carnegie Mellon University makes no
|
| 296 |
+
* representations about the suitability of this software for any
|
| 297 |
+
* purpose. It is provided "as is" without express or implied
|
| 298 |
+
* warranty.
|
| 299 |
+
*
|
| 300 |
+
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
|
| 301 |
+
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
| 302 |
+
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
|
| 303 |
+
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
| 304 |
+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
|
| 305 |
+
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
| 306 |
+
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
| 307 |
+
* SOFTWARE.
|
| 308 |
+
*/
|
| 309 |
+
|
| 310 |
+
/*
|
| 311 |
+
* Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore)
|
| 312 |
+
*
|
| 313 |
+
* Permission to use, copy, modify, and distribute this material
|
| 314 |
+
* for any purpose and without fee is hereby granted, provided
|
| 315 |
+
* that the above copyright notice and this permission notice
|
| 316 |
+
* appear in all copies, and that the name of Bellcore not be
|
| 317 |
+
* used in advertising or publicity pertaining to this
|
| 318 |
+
* material without the specific, prior written permission
|
| 319 |
+
* of an authorized representative of Bellcore. BELLCORE
|
| 320 |
+
* MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY
|
| 321 |
+
* OF THIS MATERIAL FOR ANY PURPOSE. IT IS PROVIDED "AS IS",
|
| 322 |
+
* WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
|
| 323 |
+
*/
|
| 324 |
+
|
| 325 |
+
|
| 326 |
+
For the util_pcre.c and ap_regex.h components:
|
| 327 |
+
|
| 328 |
+
Copyright (c) 1997-2004 University of Cambridge
|
| 329 |
+
|
| 330 |
+
-----------------------------------------------------------------------------
|
| 331 |
+
Redistribution and use in source and binary forms, with or without
|
| 332 |
+
modification, are permitted provided that the following conditions are met:
|
| 333 |
+
|
| 334 |
+
* Redistributions of source code must retain the above copyright notice,
|
| 335 |
+
this list of conditions and the following disclaimer.
|
| 336 |
+
|
| 337 |
+
* Redistributions in binary form must reproduce the above copyright
|
| 338 |
+
notice, this list of conditions and the following disclaimer in the
|
| 339 |
+
documentation and/or other materials provided with the distribution.
|
| 340 |
+
|
| 341 |
+
* Neither the name of the University of Cambridge nor the names of its
|
| 342 |
+
contributors may be used to endorse or promote products derived from
|
| 343 |
+
this software without specific prior written permission.
|
| 344 |
+
|
| 345 |
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
| 346 |
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
| 347 |
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
| 348 |
+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
| 349 |
+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
| 350 |
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
| 351 |
+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
| 352 |
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
| 353 |
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
| 354 |
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
| 355 |
+
POSSIBILITY OF SUCH DAMAGE.
|
| 356 |
+
-----------------------------------------------------------------------------
|
| 357 |
+
|
| 358 |
+
|
| 359 |
+
For the srclib\apr\include\apr_md5.h component:
|
| 360 |
+
/*
|
| 361 |
+
* This is work is derived from material Copyright RSA Data Security, Inc.
|
| 362 |
+
*
|
| 363 |
+
* The RSA copyright statement and Licence for that original material is
|
| 364 |
+
* included below. This is followed by the Apache copyright statement and
|
| 365 |
+
* licence for the modifications made to that material.
|
| 366 |
+
*/
|
| 367 |
+
|
| 368 |
+
/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
|
| 369 |
+
rights reserved.
|
| 370 |
+
|
| 371 |
+
License to copy and use this software is granted provided that it
|
| 372 |
+
is identified as the "RSA Data Security, Inc. MD5 Message-Digest
|
| 373 |
+
Algorithm" in all material mentioning or referencing this software
|
| 374 |
+
or this function.
|
| 375 |
+
|
| 376 |
+
License is also granted to make and use derivative works provided
|
| 377 |
+
that such works are identified as "derived from the RSA Data
|
| 378 |
+
Security, Inc. MD5 Message-Digest Algorithm" in all material
|
| 379 |
+
mentioning or referencing the derived work.
|
| 380 |
+
|
| 381 |
+
RSA Data Security, Inc. makes no representations concerning either
|
| 382 |
+
the merchantability of this software or the suitability of this
|
| 383 |
+
software for any particular purpose. It is provided "as is"
|
| 384 |
+
without express or implied warranty of any kind.
|
| 385 |
+
|
| 386 |
+
These notices must be retained in any copies of any part of this
|
| 387 |
+
documentation and/or software.
|
| 388 |
+
*/
|
| 389 |
+
|
| 390 |
+
For the srclib\apr\passwd\apr_md5.c component:
|
| 391 |
+
|
| 392 |
+
/*
|
| 393 |
+
* This is work is derived from material Copyright RSA Data Security, Inc.
|
| 394 |
+
*
|
| 395 |
+
* The RSA copyright statement and Licence for that original material is
|
| 396 |
+
* included below. This is followed by the Apache copyright statement and
|
| 397 |
+
* licence for the modifications made to that material.
|
| 398 |
+
*/
|
| 399 |
+
|
| 400 |
+
/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
|
| 401 |
+
*/
|
| 402 |
+
|
| 403 |
+
/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
|
| 404 |
+
rights reserved.
|
| 405 |
+
|
| 406 |
+
License to copy and use this software is granted provided that it
|
| 407 |
+
is identified as the "RSA Data Security, Inc. MD5 Message-Digest
|
| 408 |
+
Algorithm" in all material mentioning or referencing this software
|
| 409 |
+
or this function.
|
| 410 |
+
|
| 411 |
+
License is also granted to make and use derivative works provided
|
| 412 |
+
that such works are identified as "derived from the RSA Data
|
| 413 |
+
Security, Inc. MD5 Message-Digest Algorithm" in all material
|
| 414 |
+
mentioning or referencing the derived work.
|
| 415 |
+
|
| 416 |
+
RSA Data Security, Inc. makes no representations concerning either
|
| 417 |
+
the merchantability of this software or the suitability of this
|
| 418 |
+
software for any particular purpose. It is provided "as is"
|
| 419 |
+
without express or implied warranty of any kind.
|
| 420 |
+
|
| 421 |
+
These notices must be retained in any copies of any part of this
|
| 422 |
+
documentation and/or software.
|
| 423 |
+
*/
|
| 424 |
+
/*
|
| 425 |
+
* The apr_md5_encode() routine uses much code obtained from the FreeBSD 3.0
|
| 426 |
+
* MD5 crypt() function, which is licenced as follows:
|
| 427 |
+
* ----------------------------------------------------------------------------
|
| 428 |
+
* "THE BEER-WARE LICENSE" (Revision 42):
|
| 429 |
+
* <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
|
| 430 |
+
* can do whatever you want with this stuff. If we meet some day, and you think
|
| 431 |
+
* this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
|
| 432 |
+
* ----------------------------------------------------------------------------
|
| 433 |
+
*/
|
| 434 |
+
|
| 435 |
+
For the srclib\apr-util\crypto\apr_md4.c component:
|
| 436 |
+
|
| 437 |
+
* This is derived from material copyright RSA Data Security, Inc.
|
| 438 |
+
* Their notice is reproduced below in its entirety.
|
| 439 |
+
*
|
| 440 |
+
* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
|
| 441 |
+
* rights reserved.
|
| 442 |
+
*
|
| 443 |
+
* License to copy and use this software is granted provided that it
|
| 444 |
+
* is identified as the "RSA Data Security, Inc. MD4 Message-Digest
|
| 445 |
+
* Algorithm" in all material mentioning or referencing this software
|
| 446 |
+
* or this function.
|
| 447 |
+
*
|
| 448 |
+
* License is also granted to make and use derivative works provided
|
| 449 |
+
* that such works are identified as "derived from the RSA Data
|
| 450 |
+
* Security, Inc. MD4 Message-Digest Algorithm" in all material
|
| 451 |
+
* mentioning or referencing the derived work.
|
| 452 |
+
*
|
| 453 |
+
* RSA Data Security, Inc. makes no representations concerning either
|
| 454 |
+
* the merchantability of this software or the suitability of this
|
| 455 |
+
* software for any particular purpose. It is provided "as is"
|
| 456 |
+
* without express or implied warranty of any kind.
|
| 457 |
+
*
|
| 458 |
+
* These notices must be retained in any copies of any part of this
|
| 459 |
+
* documentation and/or software.
|
| 460 |
+
*/
|
| 461 |
+
|
| 462 |
+
For the srclib\apr-util\include\apr_md4.h component:
|
| 463 |
+
|
| 464 |
+
*
|
| 465 |
+
* This is derived from material copyright RSA Data Security, Inc.
|
| 466 |
+
* Their notice is reproduced below in its entirety.
|
| 467 |
+
*
|
| 468 |
+
* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
|
| 469 |
+
* rights reserved.
|
| 470 |
+
*
|
| 471 |
+
* License to copy and use this software is granted provided that it
|
| 472 |
+
* is identified as the "RSA Data Security, Inc. MD4 Message-Digest
|
| 473 |
+
* Algorithm" in all material mentioning or referencing this software
|
| 474 |
+
* or this function.
|
| 475 |
+
*
|
| 476 |
+
* License is also granted to make and use derivative works provided
|
| 477 |
+
* that such works are identified as "derived from the RSA Data
|
| 478 |
+
* Security, Inc. MD4 Message-Digest Algorithm" in all material
|
| 479 |
+
* mentioning or referencing the derived work.
|
| 480 |
+
*
|
| 481 |
+
* RSA Data Security, Inc. makes no representations concerning either
|
| 482 |
+
* the merchantability of this software or the suitability of this
|
| 483 |
+
* software for any particular purpose. It is provided "as is"
|
| 484 |
+
* without express or implied warranty of any kind.
|
| 485 |
+
*
|
| 486 |
+
* These notices must be retained in any copies of any part of this
|
| 487 |
+
* documentation and/or software.
|
| 488 |
+
*/
|
| 489 |
+
|
| 490 |
+
|
| 491 |
+
For the srclib\apr-util\test\testmd4.c component:
|
| 492 |
+
|
| 493 |
+
*
|
| 494 |
+
* This is derived from material copyright RSA Data Security, Inc.
|
| 495 |
+
* Their notice is reproduced below in its entirety.
|
| 496 |
+
*
|
| 497 |
+
* Copyright (C) 1990-2, RSA Data Security, Inc. Created 1990. All
|
| 498 |
+
* rights reserved.
|
| 499 |
+
*
|
| 500 |
+
* RSA Data Security, Inc. makes no representations concerning either
|
| 501 |
+
* the merchantability of this software or the suitability of this
|
| 502 |
+
* software for any particular purpose. It is provided "as is"
|
| 503 |
+
* without express or implied warranty of any kind.
|
| 504 |
+
*
|
| 505 |
+
* These notices must be retained in any copies of any part of this
|
| 506 |
+
* documentation and/or software.
|
| 507 |
+
*/
|
| 508 |
+
|
| 509 |
+
For the test\zb.c component:
|
| 510 |
+
|
| 511 |
+
/* ZeusBench V1.01
|
| 512 |
+
===============
|
| 513 |
+
|
| 514 |
+
This program is Copyright (C) Zeus Technology Limited 1996.
|
| 515 |
+
|
| 516 |
+
This program may be used and copied freely providing this copyright notice
|
| 517 |
+
is not removed.
|
| 518 |
+
|
| 519 |
+
This software is provided "as is" and any express or implied warranties,
|
| 520 |
+
including but not limited to, the implied warranties of merchantability and
|
| 521 |
+
fitness for a particular purpose are disclaimed. In no event shall
|
| 522 |
+
Zeus Technology Ltd. be liable for any direct, indirect, incidental, special,
|
| 523 |
+
exemplary, or consequential damaged (including, but not limited to,
|
| 524 |
+
procurement of substitute good or services; loss of use, data, or profits;
|
| 525 |
+
or business interruption) however caused and on theory of liability. Whether
|
| 526 |
+
in contract, strict liability or tort (including negligence or otherwise)
|
| 527 |
+
arising in any way out of the use of this software, even if advised of the
|
| 528 |
+
possibility of such damage.
|
| 529 |
+
|
| 530 |
+
Written by Adam Twiss (adam@zeus.co.uk). March 1996
|
| 531 |
+
|
| 532 |
+
Thanks to the following people for their input:
|
| 533 |
+
Mike Belshe (mbelshe@netscape.com)
|
| 534 |
+
Michael Campanella (campanella@stevms.enet.dec.com)
|
| 535 |
+
|
| 536 |
+
*/
|
MANIFEST.in
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
include LICENSE
|
| 2 |
+
include README.md
|
| 3 |
+
recursive-exclude cmbagent/output *
|
| 4 |
+
recursive-exclude cmbagent/cmbagent/data *
|
| 5 |
+
global-exclude __pycache__/
|
| 6 |
+
global-exclude *.py[cod]
|
| 7 |
+
global-exclude *.egg-info/
|
| 8 |
+
global-exclude .DS_Store
|
| 9 |
+
recursive-exclude cmbagent/docs *
|
| 10 |
+
include cmbagent/apis/*.json
|
| 11 |
+
include cmbagent/planner/*yaml
|
| 12 |
+
include cmbagent/engineer/*yaml
|
| 13 |
+
include cmbagent/executor/*yaml
|
| 14 |
+
include cmbagent/admin/*yaml
|
| 15 |
+
|
| 16 |
+
prune tests
|
| 17 |
+
prune docs
|
| 18 |
+
prune evals
|
| 19 |
+
prune output
|
| 20 |
+
exclude *.ipynb
|
| 21 |
+
exclude *backup*.py
|
| 22 |
+
exclude *.sh
|
README.md
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# cmbagent
|
| 2 |
+
|
| 3 |
+
[](LICENSE) [](https://arxiv.org/abs/2507.07257)
|
| 4 |
+
[](https://pypi.org/project/cmbagent/)
|
| 5 |
+
|
| 6 |
+
<a href="https://www.youtube.com/@cmbagent" target="_blank">
|
| 7 |
+
<img src="https://img.shields.io/badge/YouTube-Subscribe-red?style=flat-square&logo=youtube" alt="Subscribe on YouTube" width="140"/>
|
| 8 |
+
</a>
|
| 9 |
+
|
| 10 |
+
<a href="https://discord.gg/UG47Yb6gHG" target="_blank">
|
| 11 |
+
<img src="https://img.shields.io/badge/Discord-Join%20Chat-5865F2?logo=discord&logoColor=white&style=flat-square" alt="Join us on Discord" width="140"/>
|
| 12 |
+
</a>
|
| 13 |
+
|
| 14 |
+
Autonomous Research System, Powered by [AG2](https://github.com/ag2ai/ag2). Developed by Boris Bolliet for the Open Source Software community since May 2024. Under Apache 2 license, no private or public organizations/individuals other than the copyright holder can claim IP and/or ownership on this software package.
|
| 15 |
+
|
| 16 |
+
CMBAgent is a generalist multi-agent system for autonomous scientific research. Despite the name — a nod to its origins in astrophysics — it is domain-agnostic and works across any scientific field.
|
| 17 |
+
|
| 18 |
+
🎉 **News**: Cmbagent won a **first place award** at the **NeurIPS 2025 [Fair Universe Competition](https://fair-universe.lbl.gov/)**.
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
Cmbagent is the autonomous research engine for [Denario](https://astropilot-ai.github.io/DenarioPaperPage/), our end-to-end research system.
|
| 22 |
+
|
| 23 |
+
We are currently deploying cmbagent on the cloud, it will be in production soon!
|
| 24 |
+
|
| 25 |
+
Check our [demo videos](https://www.youtube.com/@cmbagent) on YouTube!
|
| 26 |
+
|
| 27 |
+
Join our [Discord Server](https://discord.gg/UG47Yb6gHG) to ask all your questions!
|
| 28 |
+
|
| 29 |
+
This is open-source research-ready software.
|
| 30 |
+
|
| 31 |
+
- Check the [demo notebooks](https://github.com/CMBAgents/cmbagent/tree/main/docs/notebooks).
|
| 32 |
+
|
| 33 |
+
- Best performances are obtained with [top-scoring models](https://lmarena.ai/?leaderboard).
|
| 34 |
+
|
| 35 |
+
We emphasize that [cmbagent](https://github.com/CMBAgents/cmbagent) is under active development and apologize for any bugs.
|
| 36 |
+
|
| 37 |
+
**The backbone of [cmbagent](https://github.com/CMBAgents/cmbagent) is [AG2](https://github.com/ag2ai/ag2)**. **Please star the [AG2](https://github.com/ag2ai/ag2) repo ⭐ and cite [Wu et al (2023)](https://arxiv.org/abs/2308.08155)!**
|
| 38 |
+
|
| 39 |
+
## Strategy
|
| 40 |
+
|
| 41 |
+
**Cmbagent** acts according to a **Planning and Control** strategy with **no human-in-the-loop**.
|
| 42 |
+
|
| 43 |
+
You give a task to solve, then:
|
| 44 |
+
|
| 45 |
+
**Planning**
|
| 46 |
+
|
| 47 |
+
- A plan is designed from a conversation between a planner and a plan reviewer.
|
| 48 |
+
- Once the number of feedbacks (reviews) is exhausted the plan is recorded in context and **cmbagent** switches to **control**.
|
| 49 |
+
|
| 50 |
+
**Control**
|
| 51 |
+
|
| 52 |
+
- The plan is executed **step-by-step**.
|
| 53 |
+
- Sub-tasks are handed over to a single agent in each step.
|
| 54 |
+
|
| 55 |
+
## Installation
|
| 56 |
+
|
| 57 |
+
With Python 3.12 or above:
|
| 58 |
+
|
| 59 |
+
```bash
|
| 60 |
+
python3 -m venv cmbagent_env
|
| 61 |
+
source cmbagent_env/bin/activate
|
| 62 |
+
pip install cmbagent
|
| 63 |
+
```
|
| 64 |
+
|
| 65 |
+
### Optional Dependencies
|
| 66 |
+
|
| 67 |
+
```bash
|
| 68 |
+
# Development tools (pytest, ipython, jupyter)
|
| 69 |
+
pip install cmbagent[dev]
|
| 70 |
+
|
| 71 |
+
# Local execution (if not using the web UI)
|
| 72 |
+
pip install cmbagent[local]
|
| 73 |
+
|
| 74 |
+
# Domain-specific packages (for specialized agents or local execution)
|
| 75 |
+
pip install cmbagent[materials] # pymatgen, ASE, phonopy, matminer
|
| 76 |
+
pip install cmbagent[biochem] # BioPython, RDKit, MDAnalysis, ProDy
|
| 77 |
+
pip install cmbagent[astro] # CAMB, AstroPy, HEALPix, Cobaya
|
| 78 |
+
pip install cmbagent[data] # scipy, scikit-learn, seaborn, plotly
|
| 79 |
+
|
| 80 |
+
# Everything
|
| 81 |
+
pip install cmbagent[all]
|
| 82 |
+
|
| 83 |
+
# Or combine multiple domains
|
| 84 |
+
pip install cmbagent[materials,biochem,data]
|
| 85 |
+
```
|
| 86 |
+
|
| 87 |
+
## Installation for developers
|
| 88 |
+
|
| 89 |
+
```bash
|
| 90 |
+
git clone https://github.com/CMBAgents/cmbagent.git
|
| 91 |
+
cd cmbagent
|
| 92 |
+
python3 -m venv cmbagent_env
|
| 93 |
+
source cmbagent_env/bin/activate
|
| 94 |
+
pip install -e .
|
| 95 |
+
```
|
| 96 |
+
|
| 97 |
+
You can then open the folder in your VSCode/Cursor/Emacs/... and work on the source code.
|
| 98 |
+
|
| 99 |
+
To install optional domain-specific packages (examples provided, add your own in `pyproject.toml`):
|
| 100 |
+
|
| 101 |
+
```bash
|
| 102 |
+
# Single domain
|
| 103 |
+
pip install -e .[materials]
|
| 104 |
+
pip install -e .[biochem]
|
| 105 |
+
pip install -e .[astro]
|
| 106 |
+
pip install -e .[data]
|
| 107 |
+
|
| 108 |
+
# Or combine multiple domains
|
| 109 |
+
pip install -e .[materials,biochem,data]
|
| 110 |
+
```
|
| 111 |
+
|
| 112 |
+
## Run
|
| 113 |
+
|
| 114 |
+
We assume you are in the virtual environment where you installed cmbagent.
|
| 115 |
+
|
| 116 |
+
Here is a one-liner you can run in terminal:
|
| 117 |
+
|
| 118 |
+
```bash
|
| 119 |
+
python -c "import cmbagent; task='''Draw two random numbers and give me their sum'''; results=cmbagent.one_shot(task, agent='engineer', engineer_model='gpt-4o-mini');"
|
| 120 |
+
```
|
| 121 |
+
|
| 122 |
+
If you want to run the notebooks, first create the ipykernel (assuming your virtual environment is called cmbagent_env):
|
| 123 |
+
|
| 124 |
+
```bash
|
| 125 |
+
pip install ipykernel jupyterlab
|
| 126 |
+
python -m ipykernel install --user --name cmbagent_env --display-name "Python (cmbagent_env)"
|
| 127 |
+
```
|
| 128 |
+
|
| 129 |
+
Then launch jupyterlab:
|
| 130 |
+
|
| 131 |
+
```bash
|
| 132 |
+
jupyter-lab
|
| 133 |
+
```
|
| 134 |
+
|
| 135 |
+
Select the cmbagent kernel, and run the notebook.
|
| 136 |
+
|
| 137 |
+
## API Keys
|
| 138 |
+
|
| 139 |
+
Before you can use cmbagent, you need to set your OpenAI API key as an environment variable. Do this in a terminal, before launching Jupyter-lab.
|
| 140 |
+
|
| 141 |
+
For Unix-based systems (Linux, macOS), do:
|
| 142 |
+
|
| 143 |
+
```bash
|
| 144 |
+
export OPENAI_API_KEY="sk-..." ## mandatory for the RAG agents
|
| 145 |
+
export ANTHROPIC_API_KEY="sk-..." ## optional
|
| 146 |
+
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account-key.json" ## optional for Vertex AI
|
| 147 |
+
```
|
| 148 |
+
|
| 149 |
+
(paste in your bashrc or zshrc file, if possible.)
|
| 150 |
+
|
| 151 |
+
For Windows, use [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) and the same command.
|
| 152 |
+
|
| 153 |
+
By default, cmbagent uses models from oai/anthropic/google. If you want to pick different LLMs, just adapt `agent_llm_configs` as above, or the `default_agent_llm_configs` in [utils.py](https://github.com/CMBAgents/cmbagent/blob/main/cmbagent/utils.py).
|
| 154 |
+
|
| 155 |
+
## References
|
| 156 |
+
|
| 157 |
+
```bash
|
| 158 |
+
|
| 159 |
+
@software{CMBAGENT_2025,
|
| 160 |
+
author = {Boris Bolliet},
|
| 161 |
+
title = {CMBAGENT: Open-Source Multi-Agent System for Science},
|
| 162 |
+
year = {2025},
|
| 163 |
+
url = {https://github.com/CMBAgents/cmbagent},
|
| 164 |
+
note = {Available at https://github.com/CMBAgents/cmbagent},
|
| 165 |
+
version = {latest}
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
@misc{xu2025opensourceplanning,
|
| 169 |
+
title={Open Source Planning & Control System with Language Agents for Autonomous Scientific Discovery},
|
| 170 |
+
author={Licong Xu and Milind Sarkar and Anto I. Lonappan and Íñigo Zubeldia and Pablo Villanueva-Domingo and Santiago Casas and Christian Fidler and Chetana Amancharla and Ujjwal Tiwari and Adrian Bayer and Chadi Ait Ekiou and Miles Cranmer and Adrian Dimitrov and James Fergusson and Kahaan Gandhi and Sven Krippendorf and Andrew Laverick and Julien Lesgourgues and Antony Lewis and Thomas Meier and Blake Sherwin and Kristen Surrao and Francisco Villaescusa-Navarro and Chi Wang and Xueqing Xu and Boris Bolliet},
|
| 171 |
+
year={2025},
|
| 172 |
+
eprint={2507.07257},
|
| 173 |
+
archivePrefix={arXiv},
|
| 174 |
+
primaryClass={cs.AI},
|
| 175 |
+
url={https://arxiv.org/abs/2507.07257},
|
| 176 |
+
}
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
@misc{Laverick:2024fyh,
|
| 180 |
+
author = "Laverick, Andrew and Surrao, Kristen and Zubeldia, Inigo and Bolliet, Boris and Cranmer, Miles and Lewis, Antony and Sherwin, Blake and Lesgourgues, Julien",
|
| 181 |
+
title = "{Multi-Agent System for Cosmological Parameter Analysis}",
|
| 182 |
+
eprint = "2412.00431",
|
| 183 |
+
archivePrefix = "arXiv",
|
| 184 |
+
primaryClass = "astro-ph.IM",
|
| 185 |
+
month = "11",
|
| 186 |
+
year = "2024"
|
| 187 |
+
}
|
| 188 |
+
```
|
| 189 |
+
|
| 190 |
+
## Acknowledgments
|
| 191 |
+
|
| 192 |
+
Our project is funded by the [Cambridge Centre for Data-Driven Discovery Accelerate Programme](https://science.ai.cam.ac.uk). We are grateful to [Mark Sze](https://github.com/marklysze) for help with [AG2](https://github.com/ag2ai/ag2).
|
cmbagent/__init__.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# import warnings
|
| 2 |
+
# warnings.filterwarnings("ignore", message=r'Field "model_client_cls" in LLMConfigEntry has conflict with protected namespace "model_"')
|
| 3 |
+
|
| 4 |
+
import warnings
|
| 5 |
+
|
| 6 |
+
warnings.filterwarnings(
|
| 7 |
+
"ignore", # action
|
| 8 |
+
message=r"Update function string contains no variables\.", # regex
|
| 9 |
+
category=UserWarning, # same category that's raised
|
| 10 |
+
module=r"autogen\.agentchat\.conversable_agent" # where it comes from
|
| 11 |
+
)
|
| 12 |
+
|
| 13 |
+
# Suppress Vertex AI deprecation warning - preview module internally still uses deprecated code
|
| 14 |
+
warnings.filterwarnings(
|
| 15 |
+
"ignore",
|
| 16 |
+
message=r"This feature is deprecated as of June 24, 2025.*",
|
| 17 |
+
category=UserWarning,
|
| 18 |
+
module=r"vertexai\.generative_models\._generative_models"
|
| 19 |
+
)
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
from .cmbagent import CMBAgent
|
| 23 |
+
from .version import __version__
|
| 24 |
+
import os
|
| 25 |
+
from IPython.display import Image, display, Markdown
|
| 26 |
+
from autogen.cmbagent_utils import LOGO, IMG_WIDTH, cmbagent_disable_display
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
from .cmbagent import planning_and_control, one_shot, human_in_the_loop, control, load_plan, deep_research, work_dir_default
|
| 31 |
+
from .cmbagent import get_keywords, get_keywords_from_aaai, get_keywords_from_string, get_aas_keywords
|
| 32 |
+
|
| 33 |
+
# OCR functionality
|
| 34 |
+
from .utils.ocr import process_single_pdf, process_folder
|
| 35 |
+
|
| 36 |
+
# arXiv downloader functionality
|
| 37 |
+
from .utils.arxiv_downloader import arxiv_filter
|
| 38 |
+
|
| 39 |
+
# Summarization functionality
|
| 40 |
+
from .utils.summarization import preprocess_task, summarize_document, summarize_documents
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def print_cmbagent_logo():
|
| 44 |
+
base_dir = os.path.dirname(__file__)
|
| 45 |
+
# logo.png is in the images directory at the project root
|
| 46 |
+
# base_dir is cmbagent/cmbagent/, so go up one level to get to cmbagent/
|
| 47 |
+
project_root = os.path.dirname(base_dir)
|
| 48 |
+
png_path = os.path.join(project_root, "images", "logo.png")
|
| 49 |
+
# print(png_path)
|
| 50 |
+
# print(base_dir)
|
| 51 |
+
if not cmbagent_disable_display:
|
| 52 |
+
display(Image(filename=png_path, width=IMG_WIDTH))
|
| 53 |
+
display(Markdown(LOGO))
|
| 54 |
+
# ANSI hyperlink: Note that support varies by terminal.
|
| 55 |
+
# HTML link for GitHub with icon
|
| 56 |
+
# github_html = '''
|
| 57 |
+
# <a href="https://github.com/CMBAgents/cmbagent" target="_blank" style="text-decoration: none;">
|
| 58 |
+
# <img src="https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png"
|
| 59 |
+
# alt="GitHub Icon" width="20" style="vertical-align:middle; margin-right:5px;">
|
| 60 |
+
# Star me on GitHub
|
| 61 |
+
# </a>
|
| 62 |
+
# '''
|
| 63 |
+
# # HTML link for YouTube with icon
|
| 64 |
+
# youtube_html = '''
|
| 65 |
+
# <a href="https://www.youtube.com/@cmbagent" target="_blank" style="text-decoration: none;">
|
| 66 |
+
# <img src="https://upload.wikimedia.org/wikipedia/commons/4/42/YouTube_icon_%282013-2017%29.png"
|
| 67 |
+
# alt="YouTube Icon" width="20" style="vertical-align:middle; margin-right:5px;">
|
| 68 |
+
# Watch me on YouTube
|
| 69 |
+
# </a>
|
| 70 |
+
# '''
|
| 71 |
+
|
| 72 |
+
# display(HTML(github_html))
|
| 73 |
+
# display(HTML(youtube_html))
|
| 74 |
+
|
| 75 |
+
print_cmbagent_logo()
|
cmbagent/agents/admin/__init__.py
ADDED
|
File without changes
|
cmbagent/agents/admin/admin.yaml
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: "admin"
|
| 2 |
+
|
| 3 |
+
instructions: |
|
| 4 |
+
A human admin.
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
code_execution_config: False
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
|
cmbagent/agents/coding/engineer/__init__.py
ADDED
|
File without changes
|
cmbagent/agents/coding/engineer/engineer.yaml
ADDED
|
@@ -0,0 +1,206 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: "engineer"
|
| 2 |
+
|
| 3 |
+
instructions: |
|
| 4 |
+
|
| 5 |
+
You are the engineer agent.
|
| 6 |
+
|
| 7 |
+
The following instructions must be followed strictly at all times.
|
| 8 |
+
|
| 9 |
+
You provide single self-consistent Python code, ready to be executed. The code itself should not have any comment lines or explanations in it other than docstrings.
|
| 10 |
+
You must not provide any other text than the Python code (and a concise explanation of the code, including description of modifications provided to fix the previous code if any).
|
| 11 |
+
|
| 12 |
+
--------------------
|
| 13 |
+
**RESPONSE**
|
| 14 |
+
|
| 15 |
+
Your response is structured as follows:
|
| 16 |
+
|
| 17 |
+
**Code Explanation:**
|
| 18 |
+
|
| 19 |
+
<provide a concise explanation of the code>
|
| 20 |
+
|
| 21 |
+
**Modifications:** (Optional)
|
| 22 |
+
|
| 23 |
+
<provide a summary of any modifications made to fix errors from the previous version>
|
| 24 |
+
|
| 25 |
+
**Python Code:**
|
| 26 |
+
|
| 27 |
+
<Python code ready to be executed>
|
| 28 |
+
--------------------
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
**IMPORTANT**:
|
| 32 |
+
|
| 33 |
+
- Return one and only one Python code block in your response.
|
| 34 |
+
- Focus on one step at a time.
|
| 35 |
+
- Do not suggest incomplete code.
|
| 36 |
+
- Do not produce code blocks that are not intended for execution.
|
| 37 |
+
- Include only one code block per response.
|
| 38 |
+
- When a plot is requested, it must be saved into a png file at high resolution (dpi>=300).
|
| 39 |
+
- The filename for the plots must have the following format: `<plot_name>_<plot_number>_<timestamp>.<format>` and must be saved in the folder `{database_path}`.
|
| 40 |
+
- For plots, add relevant units to the axes labels, where appropriate.
|
| 41 |
+
- Every time a plot is saved, print a concise description of the plot to the console.
|
| 42 |
+
- Do not use '.show()' for plots.
|
| 43 |
+
- Do not check for installed packages.
|
| 44 |
+
- Do not install new packages, another agent will do that. Nonetheless, following a successful package installation, you must report the entire code that you were trying to run and that failed due to the missing package.
|
| 45 |
+
- Write very detailed docstrings for all methods or classes.
|
| 46 |
+
- Avoid f-strings when possible.
|
| 47 |
+
- Strictly avoid comments in the code. The explanations go in the code explanation section.
|
| 48 |
+
- NEVER use .format, use string concatenation instead. Prefer simple strings for your prints.
|
| 49 |
+
- ALWAYS wrap all top-level execution code (everything that is not a function/class definition or import) inside `if __name__ == '__main__':`. This is mandatory because on macOS, Python uses the "spawn" multiprocessing start method, which re-imports the script in child processes. Without this guard, any library that uses multiprocessing internally (memory_profiler, joblib, torch, etc.) will crash with a RuntimeError. It also makes functions safely importable from other steps.
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
Workflow involving creating several Python files or modules:
|
| 53 |
+
- Only provide code for one file/module at a time.
|
| 54 |
+
- If an issue is found in a specific file/module, regenerate the full code for that file only.
|
| 55 |
+
|
| 56 |
+
Plotting:
|
| 57 |
+
- Never use LaTeX rendering.
|
| 58 |
+
- Avoid multiple dollar signs within one label.
|
| 59 |
+
- Do not use nested math mode.
|
| 60 |
+
- Do not use LaTeX expressions with optional arguments or complex macros.
|
| 61 |
+
- Keep labels as short, clear and simple as possible, convey the details of the information needed.
|
| 62 |
+
- Always set concise and informative titles to plots.
|
| 63 |
+
- Always call `plt.tight_layout()` (or `fig.tight_layout()`) after setting titles and labels, to prevent any overlap.
|
| 64 |
+
- Rotate axis tick labels if needed to avoid overlap, especially if they are long or dense.
|
| 65 |
+
- Increase the figure size if necessary to ensure no elements overlap.
|
| 66 |
+
- Do not return plot code unless all labels, titles, and ticks are fully visible and not overlapping.
|
| 67 |
+
|
| 68 |
+
f-strings:
|
| 69 |
+
- Avoid using f-strings.
|
| 70 |
+
|
| 71 |
+
To print a blank line or add a newline, use "\n" or a triple-quoted string; never hit the physical newline inside a single-quoted r"" string.
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
NEVER use .format, use string concatenation instead.
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
Math mode and LaTeX: Never use LateX rendering.
|
| 78 |
+
|
| 79 |
+
For training ML models (e.g., neural networks):
|
| 80 |
+
- Disable Verbose Output for Training: Configure any training routines (e.g., using Keras, TensorFlow, torch, etc.) to disable ongoing progress messages. For instance, in Keras, always set the verbose parameter to 0 in methods like model.fit(), and similar for other frameworks.
|
| 81 |
+
- Suppress Repetitive Status Messages: Remove or deactivate print statements and logging within custom training loops that output redundant progress updates (e.g., "Epoch 1/100", "16/16 ━━━━━━", etc.) to avoid generating large volumes of unnecessary output.
|
| 82 |
+
- Retain Essential Evaluation Metrics: Ensure that important quantitative results, such as the final training score, final loss, and other key numerical evaluations, are clearly printed to provide a concise summary of the training performance.
|
| 83 |
+
- Prevent Unintended Re-enabling of Verbose Logging: Verify that code modifications do not inadvertently re-enable detailed logging during training, keeping the output concise while still reporting critical evaluation information.
|
| 84 |
+
- Suppress verbose output during training (e.g., use `verbose=0` for Keras training).
|
| 85 |
+
- Suppress progress bars and repeated print/log outputs during loops.
|
| 86 |
+
- Retain and print only key evaluation metrics (e.g., final loss, accuracy).
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
Progress bars:
|
| 90 |
+
- If the code involves training or fitting a model, make sure ALL progress bars are not shown (i.e., silence them).
|
| 91 |
+
|
| 92 |
+
Units:
|
| 93 |
+
- For quantities that are not dimensionless, always include explanation of the units in the code explanation section, or in the docstrings. Never use code annotations.
|
| 94 |
+
- The units of all the quanities and functions should be explained in the code explanation section, or in the docstrings.
|
| 95 |
+
|
| 96 |
+
ALL important numerical results from simulations or analysis must be printed to the console in a detailed manner,
|
| 97 |
+
with concise but precise textual description of the results (without truncation). For this, it may be necessary to change pandas (if using it) display options.
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
Further instructions:
|
| 102 |
+
- you should not aim to discuss the results of the code, only to write the code. The discussion should be done by the researcher agent.
|
| 103 |
+
- don't use latex in dictionary keys.
|
| 104 |
+
- When using Exception handling, never provide dummy summaries/solutions. The errors should be printed in full to be addressed properly.
|
| 105 |
+
- Make sure you don't print error message that may appear many times in long loops (just print once).
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
For projects that require many plots, you should not generate many plots. Instead, you should generate a single plot with multiple subplots, only the ones that are definitely needed to convey the information.
|
| 109 |
+
|
| 110 |
+
Python Error avoidance:
|
| 111 |
+
- Make sure you avoid RuntimeWarning: invalid value encountered in divide.
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
**Use Python language only.**
|
| 116 |
+
|
| 117 |
+
When generating code that produces a plot, you must: Save the plot to disk file using the savefig method or similar.
|
| 118 |
+
|
| 119 |
+
For plots, make sure you use detailed labeling and grid lines unless asked otherwise.
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
Also, make sure you **never use LaTeX rendering**, i.e., set:
|
| 123 |
+
`rcParams['text.usetex'] = False`.
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
----- DATA/PROBLEM OF INTEREST------------
|
| 127 |
+
{improved_main_task}
|
| 128 |
+
------------------------------------------
|
| 129 |
+
|
| 130 |
+
----Task-specific instructions for coding-----
|
| 131 |
+
{engineer_append_instructions}
|
| 132 |
+
----------------------------------------------
|
| 133 |
+
|
| 134 |
+
We follow the established plan:
|
| 135 |
+
|
| 136 |
+
<PLAN>
|
| 137 |
+
{final_plan}
|
| 138 |
+
</PLAN>
|
| 139 |
+
|
| 140 |
+
<CURRENT_STEP_IN_PLAN>
|
| 141 |
+
**Current step in plan:**
|
| 142 |
+
{current_plan_step_number}
|
| 143 |
+
</CURRENT_STEP_IN_PLAN>
|
| 144 |
+
|
| 145 |
+
<CURRENT_STATUS>
|
| 146 |
+
**Current status:**
|
| 147 |
+
{current_status}
|
| 148 |
+
</CURRENT_STATUS>
|
| 149 |
+
|
| 150 |
+
<CURRENT_SUB_TASK>
|
| 151 |
+
**Current sub-task:**
|
| 152 |
+
{current_sub_task}
|
| 153 |
+
</CURRENT_SUB_TASK>
|
| 154 |
+
|
| 155 |
+
<CURRENT_INSTRUCTIONS>
|
| 156 |
+
**Current instructions:**
|
| 157 |
+
{current_instructions}
|
| 158 |
+
</CURRENT_INSTRUCTIONS>
|
| 159 |
+
|
| 160 |
+
Your implementation much achieve the best speed in terms of compute. For instance, you make sure all initialization steps are outside of loops.
|
| 161 |
+
|
| 162 |
+
**Saving and Loading Data**
|
| 163 |
+
|
| 164 |
+
CRITICAL - User-provided file paths:
|
| 165 |
+
- If the user provides an absolute file path (e.g., `/path/to/data.xyz`), you MUST use that EXACT path.
|
| 166 |
+
- NEVER modify, relocate, or wrap user-provided absolute paths in variables or fallback logic.
|
| 167 |
+
- NEVER create "possible_paths" lists that include both user paths and other locations.
|
| 168 |
+
- User-provided paths are authoritative - trust them and use them directly.
|
| 169 |
+
|
| 170 |
+
For OUTPUT files (plots, generated data, intermediate results):
|
| 171 |
+
- The output data folder for this task is: `{database_path}`
|
| 172 |
+
- You **must** save generated data (e.g., plots, datasets, `.csv/.npz/.npy` files) under this folder
|
| 173 |
+
- You **must** load data files from previous steps using the SAME folder
|
| 174 |
+
- Use: `data_dir = "{database_path}"` and `filepath = os.path.join(data_dir, "my_data.npy")`
|
| 175 |
+
- While you save extended information, you must also print it to the console in a detailed and concise manner.
|
| 176 |
+
|
| 177 |
+
Summary:
|
| 178 |
+
- INPUT data from user: Use the EXACT path provided by the user (e.g., `/path/to/input.xyz`)
|
| 179 |
+
- OUTPUT data you generate: Save to `{database_path}`
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
**AUTOMATIC PREAMBLE**: Every script automatically gets the following lines injected at the top by the system — do NOT include them yourself:
|
| 184 |
+
import sys
|
| 185 |
+
import os
|
| 186 |
+
sys.path.insert(0, os.path.abspath("codebase"))
|
| 187 |
+
This means you can always import from previous step files directly (e.g., `from step_1 import function`).
|
| 188 |
+
|
| 189 |
+
Rather than writing code from scratch, you should prioritize importing functions from the python scripts in the codebase folder if some of them are relevant to the current sub-task (e.g., "from step_1 import function" etc, note that all generated scripts are in the codebase folder).
|
| 190 |
+
|
| 191 |
+
**Context**
|
| 192 |
+
Summary of previous steps execution and python scripts in the codebase folder.
|
| 193 |
+
For relative imports, use the relative path to the file, making sure you use the correct filename (i.e., from step_1 import function, from step_2 import function, etc.).
|
| 194 |
+
<PREVIOUS_STEPS_EXECUTION_SUMMARY>
|
| 195 |
+
{previous_steps_execution_summary}
|
| 196 |
+
-----------------------------------
|
| 197 |
+
</PREVIOUS_STEPS_EXECUTION_SUMMARY>
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
description: |
|
| 204 |
+
To generate the results and do the computations, plots and key statistics via code pipelines.
|
| 205 |
+
|
| 206 |
+
|
cmbagent/agents/coding/engineer/massgen_engineer.py
ADDED
|
@@ -0,0 +1,499 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""MassGen-powered engineer agent for multi-agent code generation.
|
| 2 |
+
|
| 3 |
+
This module provides an engineer agent that uses MassGen (multi-agent system)
|
| 4 |
+
for code generation instead of a single LLM. Multiple agents collaborate to
|
| 5 |
+
produce higher-quality code through planning, voting, and consensus.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
import os
|
| 9 |
+
import asyncio
|
| 10 |
+
import re
|
| 11 |
+
from pathlib import Path
|
| 12 |
+
from typing import Dict, Optional, Union, List, Callable, Any
|
| 13 |
+
from autogen.agentchat import ConversableAgent, UpdateSystemMessage
|
| 14 |
+
from cmbagent.base_agent import CmbAgentSwarmAgent
|
| 15 |
+
|
| 16 |
+
# Lazy import massgen to avoid dependency issues if not installed
|
| 17 |
+
_massgen = None
|
| 18 |
+
|
| 19 |
+
def get_massgen():
|
| 20 |
+
"""Lazy import of massgen."""
|
| 21 |
+
global _massgen
|
| 22 |
+
if _massgen is None:
|
| 23 |
+
try:
|
| 24 |
+
import massgen
|
| 25 |
+
_massgen = massgen
|
| 26 |
+
except ImportError:
|
| 27 |
+
raise ImportError(
|
| 28 |
+
"MassGen is required for engineer_backend='massgen'. "
|
| 29 |
+
"Install it with: pip install massgen"
|
| 30 |
+
)
|
| 31 |
+
return _massgen
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
class MassGenEngineerAgent(CmbAgentSwarmAgent):
|
| 35 |
+
"""Hybrid engineer agent that uses MassGen for initial code generation,
|
| 36 |
+
then falls back to standard LLM for debugging.
|
| 37 |
+
|
| 38 |
+
Strategy:
|
| 39 |
+
- First code generation: Use MassGen (multi-agent collaboration)
|
| 40 |
+
- Debugging/retries (after code failures): Use single LLM (fast iteration)
|
| 41 |
+
|
| 42 |
+
This provides quality initial code with efficient debugging cycles.
|
| 43 |
+
|
| 44 |
+
Args:
|
| 45 |
+
name: Agent name
|
| 46 |
+
massgen_config: Path to MassGen YAML configuration file
|
| 47 |
+
extract_code: Whether to extract code from MassGen response (default: True)
|
| 48 |
+
use_massgen_for_retries: If True, use MassGen even for retries (default: False)
|
| 49 |
+
**kwargs: Additional arguments passed to CmbAgentSwarmAgent
|
| 50 |
+
"""
|
| 51 |
+
|
| 52 |
+
DEFAULT_MASSGEN_CONFIG = str(
|
| 53 |
+
Path(__file__).parent.parent.parent.parent.parent /
|
| 54 |
+
"massgen_configs" / "engineer_massgen.yaml"
|
| 55 |
+
)
|
| 56 |
+
|
| 57 |
+
def __init__(
|
| 58 |
+
self,
|
| 59 |
+
name: str,
|
| 60 |
+
massgen_config: Optional[str] = None,
|
| 61 |
+
extract_code: bool = True,
|
| 62 |
+
verbose: bool = False,
|
| 63 |
+
enable_logging: bool = True,
|
| 64 |
+
use_massgen_for_retries: bool = False,
|
| 65 |
+
**kwargs
|
| 66 |
+
):
|
| 67 |
+
super().__init__(name=name, **kwargs)
|
| 68 |
+
|
| 69 |
+
self.massgen_config = massgen_config or self.DEFAULT_MASSGEN_CONFIG
|
| 70 |
+
self.extract_code = extract_code
|
| 71 |
+
self.verbose = verbose
|
| 72 |
+
self.enable_logging = enable_logging
|
| 73 |
+
self.use_massgen_for_retries = use_massgen_for_retries
|
| 74 |
+
|
| 75 |
+
# Track code generation attempts
|
| 76 |
+
self._generation_count = 0
|
| 77 |
+
|
| 78 |
+
# Verify config exists
|
| 79 |
+
if not os.path.exists(self.massgen_config):
|
| 80 |
+
raise FileNotFoundError(
|
| 81 |
+
f"MassGen config not found: {self.massgen_config}\n"
|
| 82 |
+
f"Expected at: {self.DEFAULT_MASSGEN_CONFIG}"
|
| 83 |
+
)
|
| 84 |
+
|
| 85 |
+
print(f"[MassGenEngineer] Initialized (hybrid mode)")
|
| 86 |
+
print(f"[MassGenEngineer] Config: {self.massgen_config}")
|
| 87 |
+
print(f"[MassGenEngineer] Strategy: MassGen for initial, single LLM for retries")
|
| 88 |
+
|
| 89 |
+
# Register MassGen reply function with highest priority
|
| 90 |
+
# This will be called before the default LLM reply function
|
| 91 |
+
self.register_reply(
|
| 92 |
+
trigger=ConversableAgent,
|
| 93 |
+
reply_func=self._massgen_reply_func,
|
| 94 |
+
position=0, # Highest priority
|
| 95 |
+
)
|
| 96 |
+
|
| 97 |
+
def _extract_code_from_response(self, response: str) -> str:
|
| 98 |
+
"""Extract Python code from MassGen structured response.
|
| 99 |
+
|
| 100 |
+
Expects format:
|
| 101 |
+
**Code Explanation:**
|
| 102 |
+
<explanation>
|
| 103 |
+
|
| 104 |
+
**Python Code:**
|
| 105 |
+
<code>
|
| 106 |
+
"""
|
| 107 |
+
# Try to extract Python Code section
|
| 108 |
+
match = re.search(
|
| 109 |
+
r'\*\*Python Code:\*\*\s*```python\s*(.*?)```',
|
| 110 |
+
response,
|
| 111 |
+
re.DOTALL | re.IGNORECASE
|
| 112 |
+
)
|
| 113 |
+
if match:
|
| 114 |
+
return match.group(1).strip()
|
| 115 |
+
|
| 116 |
+
match = re.search(
|
| 117 |
+
r'\*\*Python Code:\*\*\s*(.*?)(?=\*\*|$)',
|
| 118 |
+
response,
|
| 119 |
+
re.DOTALL | re.IGNORECASE
|
| 120 |
+
)
|
| 121 |
+
if match:
|
| 122 |
+
code = match.group(1).strip()
|
| 123 |
+
# Remove markdown code fences if present
|
| 124 |
+
code = re.sub(r'^```python\s*', '', code)
|
| 125 |
+
code = re.sub(r'```\s*$', '', code)
|
| 126 |
+
return code.strip()
|
| 127 |
+
|
| 128 |
+
# Fallback: try to find any code block
|
| 129 |
+
match = re.search(r'```python\s*(.*?)```', response, re.DOTALL)
|
| 130 |
+
if match:
|
| 131 |
+
return match.group(1).strip()
|
| 132 |
+
|
| 133 |
+
# Last resort: return the response as-is
|
| 134 |
+
return response
|
| 135 |
+
|
| 136 |
+
def _get_context_variables(self, messages: Optional[List[Dict]] = None) -> Dict[str, Any]:
|
| 137 |
+
"""Extract context variables from AG2's ContextVariables.
|
| 138 |
+
|
| 139 |
+
These are the variables that the engineer.yaml template expects.
|
| 140 |
+
|
| 141 |
+
Args:
|
| 142 |
+
messages: Message history (unused, but kept for compatibility)
|
| 143 |
+
"""
|
| 144 |
+
context = {}
|
| 145 |
+
|
| 146 |
+
# Try to get context from AG2's ContextVariables
|
| 147 |
+
# The context is stored in the agent's state during UpdateSystemMessage
|
| 148 |
+
if hasattr(self, 'context_variables') and self.context_variables:
|
| 149 |
+
# AG2's ContextVariables has a `data` attribute
|
| 150 |
+
if hasattr(self.context_variables, 'data'):
|
| 151 |
+
context = dict(self.context_variables.data)
|
| 152 |
+
elif isinstance(self.context_variables, dict):
|
| 153 |
+
context = self.context_variables.copy()
|
| 154 |
+
|
| 155 |
+
# Also check _agent_state (backup)
|
| 156 |
+
if not context and hasattr(self, '_agent_state') and self._agent_state:
|
| 157 |
+
context = self._agent_state.copy()
|
| 158 |
+
|
| 159 |
+
# Set defaults for required template variables
|
| 160 |
+
defaults = {
|
| 161 |
+
'improved_main_task': '',
|
| 162 |
+
'engineer_append_instructions': '',
|
| 163 |
+
'final_plan': 'No formal plan - one-shot execution',
|
| 164 |
+
'current_plan_step_number': '1',
|
| 165 |
+
'current_status': 'Starting task execution',
|
| 166 |
+
'current_sub_task': '',
|
| 167 |
+
'current_instructions': 'Execute the task as described',
|
| 168 |
+
'database_path': './data',
|
| 169 |
+
'codebase_path': './codebase',
|
| 170 |
+
'previous_steps_execution_summary': 'No previous steps - this is the first step.',
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
+
# Merge defaults with any existing context
|
| 174 |
+
for key, value in defaults.items():
|
| 175 |
+
if key not in context:
|
| 176 |
+
context[key] = value
|
| 177 |
+
|
| 178 |
+
return context
|
| 179 |
+
|
| 180 |
+
def _format_engineer_prompt(self, task: str, messages: Optional[List[Dict]] = None) -> str:
|
| 181 |
+
"""Format the full engineer prompt with all context variables.
|
| 182 |
+
|
| 183 |
+
This ensures MassGen receives the same detailed instructions as
|
| 184 |
+
the standard engineer would.
|
| 185 |
+
|
| 186 |
+
Args:
|
| 187 |
+
task: The raw task from the conversation
|
| 188 |
+
messages: Message history for context
|
| 189 |
+
|
| 190 |
+
Returns:
|
| 191 |
+
Fully formatted prompt with all context variables
|
| 192 |
+
"""
|
| 193 |
+
from cmbagent.utils.yaml import yaml_load_file
|
| 194 |
+
import os
|
| 195 |
+
|
| 196 |
+
# Load engineer template
|
| 197 |
+
engineer_yaml = os.path.join(
|
| 198 |
+
os.path.dirname(__file__),
|
| 199 |
+
"engineer.yaml"
|
| 200 |
+
)
|
| 201 |
+
engineer_info = yaml_load_file(engineer_yaml)
|
| 202 |
+
template = engineer_info['instructions']
|
| 203 |
+
|
| 204 |
+
# Get context variables from AG2's ContextVariables
|
| 205 |
+
context = self._get_context_variables(messages)
|
| 206 |
+
|
| 207 |
+
# Update task-specific context
|
| 208 |
+
# If improved_main_task is not set, use the task
|
| 209 |
+
if not context.get('improved_main_task') or context['improved_main_task'] == '':
|
| 210 |
+
context['improved_main_task'] = task
|
| 211 |
+
|
| 212 |
+
if not context.get('current_sub_task') or context['current_sub_task'] == '':
|
| 213 |
+
context['current_sub_task'] = task
|
| 214 |
+
|
| 215 |
+
if self.verbose:
|
| 216 |
+
print(f"[MassGenEngineer] Context variables:")
|
| 217 |
+
for key in ['improved_main_task', 'final_plan', 'current_plan_step_number',
|
| 218 |
+
'current_status', 'database_path', 'codebase_path']:
|
| 219 |
+
print(f" {key}: {context.get(key, 'NOT SET')}")
|
| 220 |
+
|
| 221 |
+
# Format the template
|
| 222 |
+
try:
|
| 223 |
+
formatted_prompt = template.format(**context)
|
| 224 |
+
except KeyError as e:
|
| 225 |
+
# Missing variable - log warning and continue with defaults
|
| 226 |
+
print(f"[MassGenEngineer] Warning: Missing context variable {e}")
|
| 227 |
+
print(f"[MassGenEngineer] Using defaults and continuing...")
|
| 228 |
+
# Try again with just the essential variables
|
| 229 |
+
essential_context = {
|
| 230 |
+
'improved_main_task': task,
|
| 231 |
+
'engineer_append_instructions': '',
|
| 232 |
+
'final_plan': 'No formal plan - one-shot execution',
|
| 233 |
+
'current_plan_step_number': '1',
|
| 234 |
+
'current_status': 'Starting task execution',
|
| 235 |
+
'current_sub_task': task,
|
| 236 |
+
'current_instructions': 'Execute the task as described',
|
| 237 |
+
'database_path': context.get('database_path', './data'),
|
| 238 |
+
'codebase_path': context.get('codebase_path', './codebase'),
|
| 239 |
+
'previous_steps_execution_summary': 'No previous steps - this is the first step.',
|
| 240 |
+
}
|
| 241 |
+
try:
|
| 242 |
+
formatted_prompt = template.format(**essential_context)
|
| 243 |
+
except Exception as e2:
|
| 244 |
+
print(f"[MassGenEngineer] Error formatting template: {e2}")
|
| 245 |
+
print(f"[MassGenEngineer] Falling back to task-only prompt")
|
| 246 |
+
formatted_prompt = task
|
| 247 |
+
|
| 248 |
+
return formatted_prompt
|
| 249 |
+
|
| 250 |
+
async def _call_massgen(self, prompt: str, messages: Optional[List[Dict]] = None) -> str:
|
| 251 |
+
"""Call MassGen asynchronously to generate code.
|
| 252 |
+
|
| 253 |
+
Args:
|
| 254 |
+
prompt: The raw task/prompt from the conversation
|
| 255 |
+
messages: Message history for context
|
| 256 |
+
|
| 257 |
+
Returns:
|
| 258 |
+
Formatted response ready for AG2 (includes code block)
|
| 259 |
+
"""
|
| 260 |
+
massgen = get_massgen()
|
| 261 |
+
|
| 262 |
+
# Format the full engineer prompt with all context variables
|
| 263 |
+
formatted_prompt = self._format_engineer_prompt(prompt, messages)
|
| 264 |
+
|
| 265 |
+
if self.verbose:
|
| 266 |
+
print(f"\n[MassGenEngineer] Calling MassGen...")
|
| 267 |
+
print(f"[MassGenEngineer] Config: {self.massgen_config}")
|
| 268 |
+
print(f"[MassGenEngineer] Raw prompt length: {len(prompt)} chars")
|
| 269 |
+
print(f"[MassGenEngineer] Formatted prompt length: {len(formatted_prompt)} chars")
|
| 270 |
+
print(f"[MassGenEngineer] Raw prompt: {prompt}")
|
| 271 |
+
|
| 272 |
+
# Call MassGen with the FULL formatted prompt
|
| 273 |
+
result = await massgen.run(
|
| 274 |
+
query=formatted_prompt,
|
| 275 |
+
config=self.massgen_config,
|
| 276 |
+
verbose=self.verbose,
|
| 277 |
+
enable_logging=self.enable_logging
|
| 278 |
+
)
|
| 279 |
+
|
| 280 |
+
if self.verbose:
|
| 281 |
+
print(f"[MassGenEngineer] Selected agent: {result.get('selected_agent', 'N/A')}")
|
| 282 |
+
if result.get('log_directory'):
|
| 283 |
+
print(f"[MassGenEngineer] Logs: {result['log_directory']}")
|
| 284 |
+
|
| 285 |
+
response = result['final_answer']
|
| 286 |
+
|
| 287 |
+
# Format response for AG2 executor
|
| 288 |
+
# The response should include the full explanation + code in a code block
|
| 289 |
+
# This matches what the engineer normally returns
|
| 290 |
+
formatted_response = self._format_response_for_ag2(response)
|
| 291 |
+
|
| 292 |
+
if self.verbose:
|
| 293 |
+
print(f"[MassGenEngineer] Formatted response length: {len(formatted_response)} chars")
|
| 294 |
+
|
| 295 |
+
return formatted_response
|
| 296 |
+
|
| 297 |
+
def _format_response_for_ag2(self, massgen_response: str) -> str:
|
| 298 |
+
"""Format MassGen response for AG2 executor.
|
| 299 |
+
|
| 300 |
+
Ensures the response includes code in ```python``` blocks
|
| 301 |
+
which the executor expects.
|
| 302 |
+
"""
|
| 303 |
+
# Check if response already has code blocks
|
| 304 |
+
if "```python" in massgen_response:
|
| 305 |
+
# Already formatted properly
|
| 306 |
+
return massgen_response
|
| 307 |
+
|
| 308 |
+
# Try to extract code and wrap it properly
|
| 309 |
+
code = self._extract_code_from_response(massgen_response)
|
| 310 |
+
|
| 311 |
+
# Build formatted response matching engineer's expected format
|
| 312 |
+
formatted = f"""**Code Explanation:**
|
| 313 |
+
|
| 314 |
+
Generated by MassGen multi-agent collaboration.
|
| 315 |
+
|
| 316 |
+
**Python Code:**
|
| 317 |
+
|
| 318 |
+
```python
|
| 319 |
+
{code}
|
| 320 |
+
```
|
| 321 |
+
"""
|
| 322 |
+
return formatted
|
| 323 |
+
|
| 324 |
+
def _is_retry_attempt(self, messages: List[Dict]) -> bool:
|
| 325 |
+
"""Detect if this is a retry/debugging attempt.
|
| 326 |
+
|
| 327 |
+
Returns True if:
|
| 328 |
+
- Code execution errors are present in history
|
| 329 |
+
- Previous code blocks exist (indicating a retry)
|
| 330 |
+
- Error-related keywords in recent messages
|
| 331 |
+
"""
|
| 332 |
+
if len(messages) < 2:
|
| 333 |
+
# First message - definitely not a retry
|
| 334 |
+
return False
|
| 335 |
+
|
| 336 |
+
# Check last few messages for error indicators
|
| 337 |
+
recent_messages = messages[-5:] if len(messages) >= 5 else messages
|
| 338 |
+
|
| 339 |
+
for msg in recent_messages:
|
| 340 |
+
content = msg.get("content", "").lower()
|
| 341 |
+
|
| 342 |
+
# Check for execution results or errors
|
| 343 |
+
if any(keyword in content for keyword in [
|
| 344 |
+
"exitcode",
|
| 345 |
+
"traceback",
|
| 346 |
+
"error:",
|
| 347 |
+
"exception",
|
| 348 |
+
"failed",
|
| 349 |
+
"stderr:",
|
| 350 |
+
"exit code",
|
| 351 |
+
"returned non-zero",
|
| 352 |
+
]):
|
| 353 |
+
return True
|
| 354 |
+
|
| 355 |
+
# Check for code execution output
|
| 356 |
+
if "```python" in content and any(err in content for err in [
|
| 357 |
+
"error", "exception", "failed", "traceback"
|
| 358 |
+
]):
|
| 359 |
+
return True
|
| 360 |
+
|
| 361 |
+
return False
|
| 362 |
+
|
| 363 |
+
def _massgen_reply_func(
|
| 364 |
+
self,
|
| 365 |
+
recipient: ConversableAgent,
|
| 366 |
+
messages: Optional[List[Dict]] = None,
|
| 367 |
+
sender: Optional[ConversableAgent] = None,
|
| 368 |
+
config: Optional[Any] = None,
|
| 369 |
+
) -> tuple[bool, Union[str, Dict, None]]:
|
| 370 |
+
"""Custom reply function for MassGen hybrid approach.
|
| 371 |
+
|
| 372 |
+
This is called by AG2's reply system. The config parameter may contain
|
| 373 |
+
context_variables from the conversation.
|
| 374 |
+
|
| 375 |
+
Strategy:
|
| 376 |
+
- First code generation: Use MassGen (multi-agent collaboration)
|
| 377 |
+
- Retry/debugging: Use standard LLM (fast, cheap iteration)
|
| 378 |
+
|
| 379 |
+
Returns:
|
| 380 |
+
tuple: (should_reply, reply_content)
|
| 381 |
+
- should_reply: True if this function generated a reply
|
| 382 |
+
- reply_content: The generated reply or None
|
| 383 |
+
"""
|
| 384 |
+
if messages is None:
|
| 385 |
+
print("[MassGenEngineer] Warning: messages is None, skipping")
|
| 386 |
+
return False, None
|
| 387 |
+
|
| 388 |
+
if not messages:
|
| 389 |
+
print("[MassGenEngineer] Warning: messages is empty, skipping")
|
| 390 |
+
return False, None
|
| 391 |
+
|
| 392 |
+
# Store context_variables if available (passed through config or recipient)
|
| 393 |
+
if config and isinstance(config, dict) and 'context_variables' in config:
|
| 394 |
+
self.context_variables = config['context_variables']
|
| 395 |
+
elif hasattr(recipient, 'context_variables'):
|
| 396 |
+
self.context_variables = recipient.context_variables
|
| 397 |
+
elif hasattr(sender, 'context_variables'):
|
| 398 |
+
self.context_variables = sender.context_variables
|
| 399 |
+
|
| 400 |
+
# Determine if this is a retry
|
| 401 |
+
is_retry = self._is_retry_attempt(messages)
|
| 402 |
+
|
| 403 |
+
self._generation_count += 1
|
| 404 |
+
|
| 405 |
+
try:
|
| 406 |
+
if is_retry and not self.use_massgen_for_retries:
|
| 407 |
+
# RETRY MODE: Skip to next reply function (standard LLM)
|
| 408 |
+
print(f"\n[MassGenEngineer] Attempt #{self._generation_count} - RETRY MODE (single LLM)")
|
| 409 |
+
print(f"[MassGenEngineer] Delegating to standard LLM for fast debugging")
|
| 410 |
+
|
| 411 |
+
# Return False to let next reply function (default LLM) handle it
|
| 412 |
+
return False, None
|
| 413 |
+
|
| 414 |
+
else:
|
| 415 |
+
# INITIAL MODE: Use MassGen for quality code generation
|
| 416 |
+
print(f"\n[MassGenEngineer] Attempt #{self._generation_count} - INITIAL MODE (MassGen multi-agent)")
|
| 417 |
+
print(f"[MassGenEngineer] Using MassGen for high-quality code generation")
|
| 418 |
+
|
| 419 |
+
last_message = messages[-1]
|
| 420 |
+
prompt = last_message.get("content", "")
|
| 421 |
+
|
| 422 |
+
if not prompt:
|
| 423 |
+
print("[MassGenEngineer] Warning: prompt is empty")
|
| 424 |
+
return False, None
|
| 425 |
+
|
| 426 |
+
# Call MassGen synchronously (AG2 expects sync)
|
| 427 |
+
try:
|
| 428 |
+
loop = asyncio.get_event_loop()
|
| 429 |
+
if loop.is_running():
|
| 430 |
+
# Already in async context, create new loop
|
| 431 |
+
import nest_asyncio
|
| 432 |
+
try:
|
| 433 |
+
nest_asyncio.apply()
|
| 434 |
+
except:
|
| 435 |
+
# nest_asyncio not available, try different approach
|
| 436 |
+
loop = asyncio.new_event_loop()
|
| 437 |
+
asyncio.set_event_loop(loop)
|
| 438 |
+
except RuntimeError:
|
| 439 |
+
loop = asyncio.new_event_loop()
|
| 440 |
+
asyncio.set_event_loop(loop)
|
| 441 |
+
|
| 442 |
+
print(f"[MassGenEngineer] Calling MassGen...")
|
| 443 |
+
response = loop.run_until_complete(self._call_massgen(prompt, messages))
|
| 444 |
+
|
| 445 |
+
if response:
|
| 446 |
+
print(f"[MassGenEngineer] MassGen generated response (length: {len(response)})")
|
| 447 |
+
return True, response # We generated a reply
|
| 448 |
+
else:
|
| 449 |
+
print(f"[MassGenEngineer] Warning: MassGen returned empty response")
|
| 450 |
+
return False, None # Let next reply function try
|
| 451 |
+
|
| 452 |
+
except Exception as e:
|
| 453 |
+
print(f"[MassGenEngineer] Error in _massgen_reply_func: {type(e).__name__}: {e}")
|
| 454 |
+
import traceback
|
| 455 |
+
traceback.print_exc()
|
| 456 |
+
return False, None # Let next reply function try
|
| 457 |
+
|
| 458 |
+
|
| 459 |
+
def create_massgen_engineer_agent(
|
| 460 |
+
name: str,
|
| 461 |
+
llm_config: Dict,
|
| 462 |
+
instructions: str,
|
| 463 |
+
description: str,
|
| 464 |
+
massgen_config: Optional[str] = None,
|
| 465 |
+
verbose: bool = False,
|
| 466 |
+
enable_logging: bool = True,
|
| 467 |
+
use_massgen_for_retries: bool = False,
|
| 468 |
+
**kwargs
|
| 469 |
+
) -> MassGenEngineerAgent:
|
| 470 |
+
"""Factory function to create a hybrid MassGen engineer agent.
|
| 471 |
+
|
| 472 |
+
Args:
|
| 473 |
+
name: Agent name (typically "engineer")
|
| 474 |
+
llm_config: LLM config (used for retry attempts with single LLM)
|
| 475 |
+
instructions: Engineer instructions/system message
|
| 476 |
+
description: Agent description
|
| 477 |
+
massgen_config: Path to MassGen config (optional)
|
| 478 |
+
verbose: Enable verbose output
|
| 479 |
+
enable_logging: Enable MassGen logging
|
| 480 |
+
use_massgen_for_retries: Use MassGen even for retries (default: False)
|
| 481 |
+
**kwargs: Additional arguments
|
| 482 |
+
|
| 483 |
+
Returns:
|
| 484 |
+
Configured MassGenEngineerAgent with hybrid strategy
|
| 485 |
+
"""
|
| 486 |
+
agent = MassGenEngineerAgent(
|
| 487 |
+
name=name,
|
| 488 |
+
massgen_config=massgen_config,
|
| 489 |
+
extract_code=True,
|
| 490 |
+
verbose=verbose,
|
| 491 |
+
enable_logging=enable_logging,
|
| 492 |
+
use_massgen_for_retries=use_massgen_for_retries,
|
| 493 |
+
update_agent_state_before_reply=[UpdateSystemMessage(instructions)],
|
| 494 |
+
description=description,
|
| 495 |
+
llm_config=llm_config, # Used for retry attempts
|
| 496 |
+
**kwargs
|
| 497 |
+
)
|
| 498 |
+
|
| 499 |
+
return agent
|
cmbagent/agents/coding/engineer_nest/__init__.py
ADDED
|
File without changes
|
cmbagent/agents/coding/engineer_nest/engineer_nest.yaml
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: "engineer_nest"
|
| 2 |
+
|
| 3 |
+
instructions: |
|
| 4 |
+
|
| 5 |
+
You trigger the nested chat for the engineer agent.
|
| 6 |
+
|
| 7 |
+
description: |
|
| 8 |
+
Engineer nest agent, to implement the plan.
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
|
cmbagent/agents/coding/engineer_response_formatter/__init__.py
ADDED
|
File without changes
|
cmbagent/agents/coding/engineer_response_formatter/engineer_response_formatter.py
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import re
|
| 3 |
+
from cmbagent.base_agent import BaseAgent
|
| 4 |
+
from pydantic import BaseModel, Field
|
| 5 |
+
from typing import Optional
|
| 6 |
+
class EngineerResponseFormatterAgent(BaseAgent):
|
| 7 |
+
|
| 8 |
+
def __init__(self, llm_config=None, **kwargs):
|
| 9 |
+
|
| 10 |
+
agent_id = os.path.splitext(os.path.abspath(__file__))[0]
|
| 11 |
+
|
| 12 |
+
llm_config['config_list'][0]['response_format'] = self.EngineerResponse
|
| 13 |
+
# llm_config['config_list'][0]['response_mime_type'] = "application/json"
|
| 14 |
+
# llm_config['config_list'][0]['response_schema'] = list[self.EngineerResponse]
|
| 15 |
+
|
| 16 |
+
super().__init__(llm_config=llm_config, agent_id=agent_id, **kwargs)
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def set_agent(self,**kwargs):
|
| 20 |
+
|
| 21 |
+
super().set_assistant_agent(**kwargs)
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
class EngineerResponse(BaseModel):
|
| 26 |
+
filename: str = Field(..., description="The name to give to this Python script")
|
| 27 |
+
relative_path: Optional[str] = Field(
|
| 28 |
+
None, description="The relative path to the file (exclude <filename>.py itself)"
|
| 29 |
+
)
|
| 30 |
+
code_explanation: str = Field(
|
| 31 |
+
..., description="Copy of the engineer's explanation of the Python code provided. Including the docstrings of the methods used."
|
| 32 |
+
)
|
| 33 |
+
modification_summary: Optional[str] = Field(
|
| 34 |
+
None,
|
| 35 |
+
description="Copy of the engineer's summary of any modifications made to fix errors from the previous version."
|
| 36 |
+
)
|
| 37 |
+
python_code: str = Field(
|
| 38 |
+
..., description="Copy of the engineer's Python code in a form ready to execute. Should not contain anything else than code. Indentation has to be carefully checked, line by line, and fixed."
|
| 39 |
+
)
|
| 40 |
+
|
| 41 |
+
@staticmethod
|
| 42 |
+
def _fix_indentation(code: str, max_attempts: int = 10) -> str:
|
| 43 |
+
"""Auto-fix indentation errors caused by LLM structured output.
|
| 44 |
+
|
| 45 |
+
When an LLM generates Python code inside a JSON string field
|
| 46 |
+
(e.g., the ``python_code`` field of a Pydantic structured response),
|
| 47 |
+
it occasionally introduces stray whitespace — typically one extra
|
| 48 |
+
space at the start of a line. This breaks Python's indentation
|
| 49 |
+
rules and causes ``IndentationError`` at execution time.
|
| 50 |
+
|
| 51 |
+
This method uses ``compile()`` to detect such errors and attempts
|
| 52 |
+
to repair them automatically. For each offending line reported by
|
| 53 |
+
the compiler, it builds a set of candidate indentation levels from
|
| 54 |
+
the surrounding context:
|
| 55 |
+
|
| 56 |
+
- **Same level** as the nearest non-empty line above.
|
| 57 |
+
- **One level deeper** if that line opens a block (ends with ``:``,
|
| 58 |
+
e.g., ``for``, ``if``, ``def``, ``while``, ``with``, ...).
|
| 59 |
+
- **One level shallower** (dedent) — for lines that follow the
|
| 60 |
+
end of a block body and return to the parent scope.
|
| 61 |
+
- **Same level** as the nearest non-empty line below.
|
| 62 |
+
- **Zero indent** (module level) as a fallback.
|
| 63 |
+
|
| 64 |
+
Each candidate is tried with ``compile()``; the first one that
|
| 65 |
+
produces valid Python wins. The process repeats up to
|
| 66 |
+
``max_attempts`` times to handle multiple affected lines.
|
| 67 |
+
|
| 68 |
+
Parameters
|
| 69 |
+
----------
|
| 70 |
+
code : str
|
| 71 |
+
The full Python source code to check and fix.
|
| 72 |
+
max_attempts : int, optional
|
| 73 |
+
Maximum number of lines to fix in one pass (default 10).
|
| 74 |
+
|
| 75 |
+
Returns
|
| 76 |
+
-------
|
| 77 |
+
str
|
| 78 |
+
The code with indentation errors fixed where possible.
|
| 79 |
+
If the code has non-indentation ``SyntaxError``s, or if no
|
| 80 |
+
candidate fixes compile, the code is returned as-is.
|
| 81 |
+
"""
|
| 82 |
+
print("[_fix_indentation] Checking code for indentation errors...")
|
| 83 |
+
for attempt in range(max_attempts):
|
| 84 |
+
try:
|
| 85 |
+
compile(code, "<string>", "exec")
|
| 86 |
+
if attempt == 0:
|
| 87 |
+
print("[_fix_indentation] Code compiles cleanly — no fixes needed.")
|
| 88 |
+
else:
|
| 89 |
+
print("[_fix_indentation] Code now compiles after " + str(attempt) + " fix(es).")
|
| 90 |
+
return code
|
| 91 |
+
except IndentationError as exc:
|
| 92 |
+
if exc.lineno is None:
|
| 93 |
+
print("[_fix_indentation] IndentationError with no line number — cannot fix.")
|
| 94 |
+
return code
|
| 95 |
+
lines = code.splitlines()
|
| 96 |
+
bad_idx = exc.lineno - 1
|
| 97 |
+
if bad_idx < 0 or bad_idx >= len(lines):
|
| 98 |
+
print("[_fix_indentation] Bad line index out of range — cannot fix.")
|
| 99 |
+
return code
|
| 100 |
+
|
| 101 |
+
bad_content = lines[bad_idx].lstrip()
|
| 102 |
+
if not bad_content:
|
| 103 |
+
print("[_fix_indentation] Offending line is empty — cannot fix.")
|
| 104 |
+
return code
|
| 105 |
+
|
| 106 |
+
print("[_fix_indentation] IndentationError on line " + str(exc.lineno) + ": " + repr(lines[bad_idx]))
|
| 107 |
+
|
| 108 |
+
# Collect candidate indentation levels from context
|
| 109 |
+
candidates = []
|
| 110 |
+
|
| 111 |
+
# From nearest non-empty neighbour above
|
| 112 |
+
for i in range(bad_idx - 1, -1, -1):
|
| 113 |
+
above = lines[i]
|
| 114 |
+
stripped = above.lstrip()
|
| 115 |
+
if stripped and not stripped.startswith("#"):
|
| 116 |
+
above_indent = above[: len(above) - len(stripped)]
|
| 117 |
+
# Same level as neighbour above
|
| 118 |
+
candidates.append(above_indent)
|
| 119 |
+
# One level deeper (if neighbour ends with ':')
|
| 120 |
+
if stripped.rstrip().endswith(":"):
|
| 121 |
+
candidates.append(above_indent + " ")
|
| 122 |
+
# One level shallower (dedent)
|
| 123 |
+
if len(above_indent) >= 4:
|
| 124 |
+
candidates.append(above_indent[:-4])
|
| 125 |
+
break
|
| 126 |
+
|
| 127 |
+
# From nearest non-empty neighbour below
|
| 128 |
+
for i in range(bad_idx + 1, len(lines)):
|
| 129 |
+
below = lines[i]
|
| 130 |
+
stripped = below.lstrip()
|
| 131 |
+
if stripped and not stripped.startswith("#"):
|
| 132 |
+
below_indent = below[: len(below) - len(stripped)]
|
| 133 |
+
candidates.append(below_indent)
|
| 134 |
+
break
|
| 135 |
+
|
| 136 |
+
# Always try zero indent (module level)
|
| 137 |
+
candidates.append("")
|
| 138 |
+
|
| 139 |
+
# Deduplicate while preserving order
|
| 140 |
+
seen = set()
|
| 141 |
+
unique_candidates = []
|
| 142 |
+
for c in candidates:
|
| 143 |
+
if c not in seen:
|
| 144 |
+
seen.add(c)
|
| 145 |
+
unique_candidates.append(c)
|
| 146 |
+
|
| 147 |
+
print("[_fix_indentation] Trying " + str(len(unique_candidates)) + " candidate indent(s): " + str([str(len(c)) + " spaces" for c in unique_candidates]))
|
| 148 |
+
|
| 149 |
+
# Try each candidate — use the first one that compiles
|
| 150 |
+
fixed = False
|
| 151 |
+
for candidate in unique_candidates:
|
| 152 |
+
trial_lines = lines[:]
|
| 153 |
+
trial_lines[bad_idx] = candidate + bad_content
|
| 154 |
+
trial_code = "\n".join(trial_lines)
|
| 155 |
+
try:
|
| 156 |
+
compile(trial_code, "<string>", "exec")
|
| 157 |
+
code = trial_code
|
| 158 |
+
fixed = True
|
| 159 |
+
print("[_fix_indentation] Fixed line " + str(exc.lineno) + " -> " + str(len(candidate)) + " spaces indent: " + repr(candidate + bad_content))
|
| 160 |
+
break
|
| 161 |
+
except SyntaxError:
|
| 162 |
+
continue
|
| 163 |
+
|
| 164 |
+
if not fixed:
|
| 165 |
+
# None of the candidates compiled on their own, but
|
| 166 |
+
# picking the best guess still lets the next iteration
|
| 167 |
+
# fix a subsequent line. Prefer the neighbour-below
|
| 168 |
+
# indent if available, otherwise the first candidate.
|
| 169 |
+
best = unique_candidates[-2] if len(unique_candidates) >= 2 else unique_candidates[0]
|
| 170 |
+
lines[bad_idx] = best + bad_content
|
| 171 |
+
code = "\n".join(lines)
|
| 172 |
+
print("[_fix_indentation] No candidate compiled — using best guess (" + str(len(best)) + " spaces) for line " + str(exc.lineno))
|
| 173 |
+
|
| 174 |
+
except SyntaxError:
|
| 175 |
+
# Not an indentation issue — nothing we can auto-fix
|
| 176 |
+
print("[_fix_indentation] Non-indentation SyntaxError — returning code as-is.")
|
| 177 |
+
return code
|
| 178 |
+
print("[_fix_indentation] Reached max attempts (" + str(max_attempts) + ") — returning best effort.")
|
| 179 |
+
return code
|
| 180 |
+
|
| 181 |
+
@staticmethod
|
| 182 |
+
def _fix_data_paths(code: str, database_path: str = "data/") -> str:
|
| 183 |
+
"""Fix incorrect data directory paths in generated code.
|
| 184 |
+
|
| 185 |
+
Engineers sometimes use hardcoded paths like "./data", "../data", or
|
| 186 |
+
inconsistent variations instead of the designated database_path.
|
| 187 |
+
This method scans the code and fixes these to ensure all data
|
| 188 |
+
operations use the correct, consistent path.
|
| 189 |
+
|
| 190 |
+
Parameters
|
| 191 |
+
----------
|
| 192 |
+
code : str
|
| 193 |
+
The Python source code to check and fix.
|
| 194 |
+
database_path : str, optional
|
| 195 |
+
The correct data directory path (default "data/").
|
| 196 |
+
|
| 197 |
+
Returns
|
| 198 |
+
-------
|
| 199 |
+
str
|
| 200 |
+
The code with data paths fixed.
|
| 201 |
+
"""
|
| 202 |
+
# Normalize database_path (ensure it doesn't have trailing slash for matching)
|
| 203 |
+
db_path_clean = database_path.rstrip("/")
|
| 204 |
+
|
| 205 |
+
# Track if any fixes were made
|
| 206 |
+
fixes_made = []
|
| 207 |
+
|
| 208 |
+
# Pattern 1: data_dir = "..." or data_dir = '...' assignments
|
| 209 |
+
# Matches variations like: data_dir = "./data", data_dir = "../data", data_dir="data"
|
| 210 |
+
data_dir_pattern = r'''(data_dir\s*=\s*)(['"])(\.{0,2}/?data/?)\2'''
|
| 211 |
+
|
| 212 |
+
def fix_data_dir(match):
|
| 213 |
+
prefix = match.group(1)
|
| 214 |
+
quote = match.group(2)
|
| 215 |
+
old_path = match.group(3)
|
| 216 |
+
if old_path.rstrip("/") != db_path_clean:
|
| 217 |
+
fixes_made.append("data_dir assignment: " + repr(old_path) + " -> " + repr(database_path))
|
| 218 |
+
return prefix + quote + database_path + quote
|
| 219 |
+
return match.group(0)
|
| 220 |
+
|
| 221 |
+
code = re.sub(data_dir_pattern, fix_data_dir, code)
|
| 222 |
+
|
| 223 |
+
# Pattern 2: Standalone path strings in os.path.join or open() calls
|
| 224 |
+
# Match patterns like: os.path.join("./data", ...) or open("../data/file.csv", ...)
|
| 225 |
+
path_in_call_pattern = r'''(os\.path\.join\s*\(\s*|open\s*\(\s*)(['"])(\.{1,2}/data)(/[^'"]*)?(\2)'''
|
| 226 |
+
|
| 227 |
+
def fix_path_in_call(match):
|
| 228 |
+
prefix = match.group(1)
|
| 229 |
+
quote = match.group(2)
|
| 230 |
+
bad_prefix = match.group(3)
|
| 231 |
+
rest = match.group(4) or ""
|
| 232 |
+
end_quote = match.group(5)
|
| 233 |
+
fixes_made.append("path in function call: " + repr(bad_prefix + rest) + " -> " + repr(database_path + rest.lstrip("/")))
|
| 234 |
+
return prefix + quote + database_path + rest.lstrip("/") + end_quote
|
| 235 |
+
|
| 236 |
+
code = re.sub(path_in_call_pattern, fix_path_in_call, code)
|
| 237 |
+
|
| 238 |
+
# Pattern 3: Direct string paths like "../data/" or "./data/" used in concatenation
|
| 239 |
+
# Match: + "../data/" + or + "./data/" +
|
| 240 |
+
concat_pattern = r'''(\+\s*)(['"])(\.{1,2}/data/?)(\2)(\s*\+)'''
|
| 241 |
+
|
| 242 |
+
def fix_concat_path(match):
|
| 243 |
+
pre_plus = match.group(1)
|
| 244 |
+
quote = match.group(2)
|
| 245 |
+
old_path = match.group(3)
|
| 246 |
+
end_quote = match.group(4)
|
| 247 |
+
post_plus = match.group(5)
|
| 248 |
+
fixes_made.append("concatenated path: " + repr(old_path) + " -> " + repr(database_path))
|
| 249 |
+
return pre_plus + quote + database_path + end_quote + post_plus
|
| 250 |
+
|
| 251 |
+
code = re.sub(concat_pattern, fix_concat_path, code)
|
| 252 |
+
|
| 253 |
+
# Pattern 4: Save/load paths starting with incorrect relative paths
|
| 254 |
+
# Match: savefig("./data/...", savefig("../data/..., np.save("./data/...
|
| 255 |
+
save_load_pattern = r'''(savefig|np\.save|np\.load|pd\.to_csv|pd\.read_csv|to_csv|read_csv|save|load)\s*\(\s*(['"])(\.{1,2}/data/)([^'"]+)\2'''
|
| 256 |
+
|
| 257 |
+
def fix_save_load(match):
|
| 258 |
+
func = match.group(1)
|
| 259 |
+
quote = match.group(2)
|
| 260 |
+
bad_prefix = match.group(3)
|
| 261 |
+
filename = match.group(4)
|
| 262 |
+
fixes_made.append(func + " path: " + repr(bad_prefix + filename) + " -> " + repr(database_path + filename))
|
| 263 |
+
return func + "(" + quote + database_path + filename + quote
|
| 264 |
+
|
| 265 |
+
code = re.sub(save_load_pattern, fix_save_load, code)
|
| 266 |
+
|
| 267 |
+
# Log fixes
|
| 268 |
+
if fixes_made:
|
| 269 |
+
print("[_fix_data_paths] Fixed " + str(len(fixes_made)) + " data path issue(s):")
|
| 270 |
+
for fix in fixes_made:
|
| 271 |
+
print(" - " + fix)
|
| 272 |
+
else:
|
| 273 |
+
print("[_fix_data_paths] No data path issues found.")
|
| 274 |
+
|
| 275 |
+
return code
|
| 276 |
+
|
| 277 |
+
def format(self) -> str:
|
| 278 |
+
final_filename = self.filename if self.filename.endswith(".py") else self.filename + ".py"
|
| 279 |
+
|
| 280 |
+
if self.relative_path:
|
| 281 |
+
cleaned_path = self.relative_path.rstrip("/\\")
|
| 282 |
+
full_path = os.path.join(cleaned_path, os.path.basename(final_filename))
|
| 283 |
+
else:
|
| 284 |
+
full_path = final_filename
|
| 285 |
+
|
| 286 |
+
# Preamble: filename comment + sys.path for codebase imports
|
| 287 |
+
preamble_lines = [
|
| 288 |
+
f"# filename: {full_path}",
|
| 289 |
+
"import sys",
|
| 290 |
+
"import os",
|
| 291 |
+
'sys.path.insert(0, os.path.abspath("codebase"))',
|
| 292 |
+
]
|
| 293 |
+
|
| 294 |
+
code_lines = self.python_code.splitlines()
|
| 295 |
+
|
| 296 |
+
# Strip any existing preamble the LLM may have included
|
| 297 |
+
while code_lines and code_lines[0].strip() in (
|
| 298 |
+
"", "import sys", "import os",
|
| 299 |
+
'sys.path.insert(0, os.path.abspath("codebase"))',
|
| 300 |
+
"sys.path.insert(0, os.path.abspath('codebase'))",
|
| 301 |
+
) or (code_lines and code_lines[0].strip().startswith("# filename:")):
|
| 302 |
+
code_lines.pop(0)
|
| 303 |
+
|
| 304 |
+
updated_python_code = "\n".join(preamble_lines + code_lines)
|
| 305 |
+
|
| 306 |
+
# Fix indentation errors introduced by LLM structured output
|
| 307 |
+
updated_python_code = self._fix_indentation(updated_python_code)
|
| 308 |
+
|
| 309 |
+
# Fix incorrect data directory paths (e.g., "./data" -> "data/")
|
| 310 |
+
updated_python_code = self._fix_data_paths(updated_python_code)
|
| 311 |
+
|
| 312 |
+
response_parts = [f"**Code Explanation:**\n\n{self.code_explanation}"]
|
| 313 |
+
|
| 314 |
+
if self.modification_summary:
|
| 315 |
+
response_parts.append(
|
| 316 |
+
f"**Modifications:**\n\n{self.modification_summary}"
|
| 317 |
+
)
|
| 318 |
+
|
| 319 |
+
response_parts.append(
|
| 320 |
+
f"**Python Code:**\n\n```python\n{updated_python_code}\n```"
|
| 321 |
+
)
|
| 322 |
+
|
| 323 |
+
return "\n\n".join(response_parts)
|
| 324 |
+
|
| 325 |
+
|
cmbagent/agents/coding/engineer_response_formatter/engineer_response_formatter.yaml
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: "engineer_response_formatter"
|
| 2 |
+
|
| 3 |
+
instructions: |
|
| 4 |
+
You are a formatting agent; you format the response provided by the engineer agent and fix errors in the formatting or Python code.
|
| 5 |
+
You are also responsible for checking the Python code for errors and fixing them.
|
| 6 |
+
|
| 7 |
+
Indentation should be carefully checked, line by line in the python code, and fixed.
|
| 8 |
+
|
| 9 |
+
Carefully check the relative imports across the codebase. Since all files are written in the codebase folder, you should use relative imports (not from codebase.filename import function, but from filename import function)
|
| 10 |
+
Filenames in relative imports are always in the format: step_N.py where N is the step number.
|
| 11 |
+
|
| 12 |
+
**AUTOMATIC PREAMBLE**: The following lines are automatically injected at the top of every script by the system (do NOT include them in the code yourself):
|
| 13 |
+
# filename: codebase/step_N.py
|
| 14 |
+
import sys
|
| 15 |
+
import os
|
| 16 |
+
sys.path.insert(0, os.path.abspath("codebase"))
|
| 17 |
+
If the engineer's code already contains these lines, remove them from python_code — they will be added automatically.
|
| 18 |
+
|
| 19 |
+
You **must not alter the content of the response provided to you by the engineer agent**.
|
| 20 |
+
You **must** not add or remove any information.
|
| 21 |
+
You should only structure the input text into the output response format, in doing so you must preserve the modifications made by the engineer agent when it fixed errors.
|
| 22 |
+
|
| 23 |
+
**MAIN GUARD**: All top-level execution code (anything that is not a function/class definition or import) MUST be inside `if __name__ == '__main__':`. If the engineer forgot this, you must add it. This prevents multiprocessing crashes on macOS and makes functions safely importable from other steps.
|
| 24 |
+
|
| 25 |
+
IMPORTANT ERRORS TO PAY ATTENTION TO: (YOU MUST CHECK ALL OF THIS EACH TIME! SOME CAN OCCUR MULTIPLE TIMES!)
|
| 26 |
+
|
| 27 |
+
NEVER use .format, use string concatenation instead.
|
| 28 |
+
|
| 29 |
+
Make sure your LaTeX strings are written on a single line. Fix LaTeX or raw strings if needed.
|
| 30 |
+
Make sure to escape all backslashes properly (e.g., use '\\'instead of '\') to avoid SyntaxWarning: invalid escape sequence '\<x>' where <x> is any character.
|
| 31 |
+
Make sure you avoid SyntaxWarning: invalid escape sequence '\(' and '\)'.
|
| 32 |
+
Make sure you avoid errors like SyntaxError: closing parenthesis ')' does not match opening parenthesis '['
|
| 33 |
+
If errors are caused by LaTeX, remove the LaTeX rendering.
|
| 34 |
+
Don't use f-strings unless you are sure they are not causing errors.
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
**IMPORTANT**:
|
| 38 |
+
NEVER use .format, use string concatenation instead.
|
| 39 |
+
Make sure no LaTeX rendering is used.
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
If the code involves training or fitting a model, make sure ALL progress bars are not shown (i.e., silence them).
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
For setting relative_path, remember that:
|
| 48 |
+
- all generated data (e.g., plots, datasets, CSV files, etc.) is saved in the root folder `{database_path}`
|
| 49 |
+
- the root folder `{codebase_path}` should serve as the relative path to <filename>.py
|
| 50 |
+
|
| 51 |
+
**CRITICAL DATA PATH RULES:**
|
| 52 |
+
|
| 53 |
+
User-provided absolute paths (INPUT data):
|
| 54 |
+
- NEVER modify absolute paths that the user explicitly provided (e.g., `/path/to/data.xyz`)
|
| 55 |
+
- These are paths to the user's external data files and MUST be used exactly as given
|
| 56 |
+
- Do NOT wrap them in variables, fallback lists, or try/except blocks that check other locations
|
| 57 |
+
- If you see the engineer creating "possible_paths" lists that include user paths, remove that logic and use the user path directly
|
| 58 |
+
|
| 59 |
+
Relative paths and output data:
|
| 60 |
+
- If the engineer uses relative paths like `"data"`, `"./data"`, `"../data"` for GENERATED output, fix these to use `{database_path}`
|
| 61 |
+
- Correct pattern for output: `data_dir = "{database_path}"` then `os.path.join(data_dir, filename)`
|
| 62 |
+
- This ensures all generated files are saved to the correct location
|
| 63 |
+
|
| 64 |
+
Summary:
|
| 65 |
+
- Absolute paths from user input: PRESERVE exactly as provided
|
| 66 |
+
- Relative paths for output: FIX to use `{database_path}`
|
| 67 |
+
|
| 68 |
+
**CRITICAL FILENAME CONVENTION:**
|
| 69 |
+
- You MUST set the filename to: step_{current_plan_step_number}.py
|
| 70 |
+
- For example, if current_plan_step_number is 1, the filename should be: step_1.py
|
| 71 |
+
- For example, if current_plan_step_number is 3, the filename should be: step_3.py
|
| 72 |
+
- Do NOT use descriptive names like "calculate_sum.py" or "hello_world.py"
|
| 73 |
+
- ALWAYS use the pattern: step_N.py where N is {current_plan_step_number}
|
| 74 |
+
- The relative_path field MUST be set to `{codebase_path}` so that scripts are saved in the codebase folder
|
| 75 |
+
|
| 76 |
+
You adhere strictly to your response format, which is based on the following template:
|
| 77 |
+
filename: str = Field(..., description="The name to give to this Python script - MUST be step_{current_plan_step_number}.py")
|
| 78 |
+
relative_path: Optional[str] = Field(None, description="The relative path to the file (exclude <filename>.py itself)")
|
| 79 |
+
code_explanation: str = Field(..., description="Copy of the engineer's explanation of the Python code provided. You must fix any LaTeX formatting issues if any.")
|
| 80 |
+
modification_summary: Optional[str] = Field(None, description="Copy of the engineer's summary of any modifications made to fix errors from the previous version.")
|
| 81 |
+
python_code: str = Field(..., description="Copy of the engineer's Python code in a form ready to execute. Should not contain anything else than code.")
|
| 82 |
+
|
| 83 |
+
description: |
|
| 84 |
+
Formatter agent, to format the response provided by the engineer agent.
|
| 85 |
+
|
| 86 |
+
|
cmbagent/agents/coding/executor/__init__.py
ADDED
|
File without changes
|
cmbagent/agents/coding/executor/executor.yaml
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: "executor"
|
| 2 |
+
|
| 3 |
+
human_input_mode: "NEVER"
|
| 4 |
+
# conversable_agent.py
|
| 5 |
+
# When "NEVER", the agent will never prompt for human input.
|
| 6 |
+
# Under this mode, the conversation stops
|
| 7 |
+
# when the number of auto reply reaches the max_consecutive_auto_reply or when is_termination_msg is True.
|
| 8 |
+
# "NEVER": "A computer terminal that performs no other action than running Python scripts (provided to it quoted in ```python code blocks), or sh shell scripts (provided to it quoted in ```sh code blocks).",
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
max_consecutive_auto_reply: 50
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
instructions: |
|
| 16 |
+
You execute python code provided to you by the engineer.
|
| 17 |
+
|
| 18 |
+
description: |
|
| 19 |
+
Executes python code provided by the engineer.
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
timeout: 7200
|
cmbagent/agents/coding/executor_bash/__init__.py
ADDED
|
File without changes
|
cmbagent/agents/coding/executor_bash/executor_bash.yaml
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: "executor_bash"
|
| 2 |
+
|
| 3 |
+
human_input_mode: "NEVER"
|
| 4 |
+
# conversable_agent.py
|
| 5 |
+
# When "NEVER", the agent will never prompt for human input.
|
| 6 |
+
# Under this mode, the conversation stops
|
| 7 |
+
# when the number of auto reply reaches the max_consecutive_auto_reply or when is_termination_msg is True.
|
| 8 |
+
# "NEVER": "A computer terminal that performs no other action than running Python scripts (provided to it quoted in ```python code blocks), or sh shell scripts (provided to it quoted in ```sh code blocks).",
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
max_consecutive_auto_reply: 50
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
instructions: |
|
| 16 |
+
You execute bash code provided to you by the installer agent.
|
| 17 |
+
|
| 18 |
+
description: |
|
| 19 |
+
Executes bash code provided by the installer agent.
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
timeout: 7200
|
cmbagent/agents/coding/executor_response_formatter/__init__.py
ADDED
|
File without changes
|
cmbagent/agents/coding/executor_response_formatter/executor_response_formatter.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from cmbagent.base_agent import BaseAgent
|
| 3 |
+
from pydantic import BaseModel, Field
|
| 4 |
+
from typing import Literal
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class ExecutorResponseFormatterAgent(BaseAgent):
|
| 10 |
+
|
| 11 |
+
def __init__(self, llm_config=None, **kwargs):
|
| 12 |
+
|
| 13 |
+
agent_id = os.path.splitext(os.path.abspath(__file__))[0]
|
| 14 |
+
|
| 15 |
+
# llm_config['config_list'][0]['response_format'] = self.ExecutorResponse
|
| 16 |
+
|
| 17 |
+
super().__init__(llm_config=llm_config, agent_id=agent_id, **kwargs)
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def set_agent(self,**kwargs):
|
| 21 |
+
|
| 22 |
+
super().set_assistant_agent(**kwargs)
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
### currently not used
|
| 26 |
+
# class ExecutorResponse(BaseModel):
|
| 27 |
+
# execution_summary: str = Field(
|
| 28 |
+
# ..., description="Summay of the Execution output"
|
| 29 |
+
# )
|
| 30 |
+
# execution_status: Literal["success", "failure"] = Field(
|
| 31 |
+
# ..., description="Status of the execution."
|
| 32 |
+
# )
|
| 33 |
+
# next_agent_suggestion: Literal["engineer", "control", "installer"] = Field(
|
| 34 |
+
# None, ## default value
|
| 35 |
+
# description=r"""
|
| 36 |
+
# Suggestion for the next agent to call:
|
| 37 |
+
# Suggest the engineer agent if error related to generic Python code.
|
| 38 |
+
# Suggest the installer agent if error related to missing Python modules (i.e., ModuleNotFoundError).
|
| 39 |
+
# Suggest the controller if execution was successful.
|
| 40 |
+
# """
|
| 41 |
+
# )
|
| 42 |
+
# current_step_in_plan: int = Field(
|
| 43 |
+
# ..., description="Current step in plan."
|
| 44 |
+
# )
|
| 45 |
+
# def format(self) -> str:
|
| 46 |
+
# return f"""
|
| 47 |
+
# **Execution Summary:**
|
| 48 |
+
# {self.execution_summary}
|
| 49 |
+
|
| 50 |
+
# **Execution Status:**
|
| 51 |
+
# {self.execution_status}
|
| 52 |
+
|
| 53 |
+
# **Next Agent Suggestion:**
|
| 54 |
+
# {self.next_agent_suggestion}
|
| 55 |
+
|
| 56 |
+
# **Current Step in Plan:**
|
| 57 |
+
# {self.current_step_in_plan}
|
| 58 |
+
# """
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
|
cmbagent/agents/coding/executor_response_formatter/executor_response_formatter.yaml
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: "executor_response_formatter"
|
| 2 |
+
|
| 3 |
+
instructions: |
|
| 4 |
+
|
| 5 |
+
You call your tool post_execution_transfer.
|
| 6 |
+
|
| 7 |
+
**IMPORTANT:** You report the CODE execution status, not whether the step is complete.
|
| 8 |
+
- execution_status="success" means the code/command ran without errors
|
| 9 |
+
- execution_status="failure" means there was an error during execution
|
| 10 |
+
|
| 11 |
+
The CONTROLLER decides if the step goal is achieved, not you.
|
| 12 |
+
|
| 13 |
+
For the next agent suggestion, follow these rules:
|
| 14 |
+
|
| 15 |
+
- Suggest the installer agent if error related to missing Python modules (i.e., ModuleNotFoundError).
|
| 16 |
+
- Suggest the camb_context agent if CAMB documentation should be consulted.
|
| 17 |
+
- Suggest camb_context to fix Python errors related to the camb code.
|
| 18 |
+
- Suggest the engineer agent if error related to generic Python code. Don't prioritize the engineer agent if the error is related to the camb code, in this case suggest camb_context instead.
|
| 19 |
+
- Suggest the controller if execution was successful (controller will evaluate if step is complete).
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
description: |
|
| 23 |
+
Process the response provided by the executor agent. Reports code execution status to the controller.
|
cmbagent/agents/control/control_starter/__init__.py
ADDED
|
File without changes
|
cmbagent/agents/control/control_starter/control_starter.yaml
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: "control_starter"
|
| 2 |
+
|
| 3 |
+
instructions: |
|
| 4 |
+
You are the control_starter agent in the team. You don't respond. You should only call record_status tool.
|
| 5 |
+
|
| 6 |
+
You must call record_status **before** calling the agent in charge of the up-coming sub-task.
|
| 7 |
+
|
| 8 |
+
You follow step-by-step the established plan:
|
| 9 |
+
|
| 10 |
+
{final_plan}
|
| 11 |
+
|
| 12 |
+
The current status of this workflow is:
|
| 13 |
+
|
| 14 |
+
**Current step in plan:**
|
| 15 |
+
{current_plan_step_number}
|
| 16 |
+
|
| 17 |
+
**Current status:**
|
| 18 |
+
{current_status}
|
| 19 |
+
|
| 20 |
+
**Current sub-task:**
|
| 21 |
+
{current_sub_task}
|
| 22 |
+
|
| 23 |
+
**Agent in charge:**
|
| 24 |
+
{agent_for_sub_task}
|
| 25 |
+
|
| 26 |
+
**Instructions:**
|
| 27 |
+
{current_instructions}
|
| 28 |
+
|
| 29 |
+
You must implement the plan step-by-step until the final step and never call the terminator agent unless **ALL** the steps in plan have been fully **successfully** implemented one by one.
|
| 30 |
+
|
| 31 |
+
If a code execution has failed, it must be fixed before moving to subsequent step in the plan!
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
description: |
|
| 36 |
+
Control starter agent, to start the control stage.
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
|
cmbagent/agents/control/controller/__init__.py
ADDED
|
File without changes
|
cmbagent/agents/control/controller/controller.yaml
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: "controller"
|
| 2 |
+
|
| 3 |
+
instructions: |
|
| 4 |
+
You are the controller agent in the team. You don't respond. You should only call record_status tool.
|
| 5 |
+
|
| 6 |
+
You must call record_status **before** calling the agent in charge of the up-coming sub-task.
|
| 7 |
+
|
| 8 |
+
**IMPORTANT: Distinguish between code execution status and step completion:**
|
| 9 |
+
- `code_execution_status`: Whether code/commands ran without errors ("success"/"failure")
|
| 10 |
+
- `step_execution_status`: Whether the plan step GOAL is achieved
|
| 11 |
+
|
| 12 |
+
**Only mark a step as "completed" when the actual step goal is achieved**, not just when code runs successfully!
|
| 13 |
+
- Package installation success → step is NOT complete, continue with main task
|
| 14 |
+
- Debugging/fix success → step is NOT complete, verify the fix works
|
| 15 |
+
- Main task code runs and produces expected output → step IS complete
|
| 16 |
+
|
| 17 |
+
You follow step-by-step the established plan:
|
| 18 |
+
|
| 19 |
+
{final_plan}
|
| 20 |
+
|
| 21 |
+
The current status of this workflow is:
|
| 22 |
+
|
| 23 |
+
**Current step in plan:**
|
| 24 |
+
{current_plan_step_number}
|
| 25 |
+
|
| 26 |
+
**Current status:**
|
| 27 |
+
{current_status}
|
| 28 |
+
|
| 29 |
+
**Code execution status:**
|
| 30 |
+
{code_execution_status}
|
| 31 |
+
|
| 32 |
+
**Current sub-task:**
|
| 33 |
+
{current_sub_task}
|
| 34 |
+
|
| 35 |
+
**Agent in charge:**
|
| 36 |
+
{agent_for_sub_task}
|
| 37 |
+
|
| 38 |
+
**Instructions:**
|
| 39 |
+
{current_instructions}
|
| 40 |
+
|
| 41 |
+
You must implement the plan step-by-step until the final step and never call the terminator agent unless **ALL** the steps in plan have been fully **successfully** implemented one by one.
|
| 42 |
+
|
| 43 |
+
If a code execution has failed, it must be fixed before moving to subsequent step in the plan!
|
| 44 |
+
|
| 45 |
+
**Context**
|
| 46 |
+
Summary of previous steps execution and codebase:
|
| 47 |
+
<PREVIOUS_STEPS_EXECUTION_SUMMARY>
|
| 48 |
+
{previous_steps_execution_summary}
|
| 49 |
+
-----------------------------------
|
| 50 |
+
</PREVIOUS_STEPS_EXECUTION_SUMMARY>
|
| 51 |
+
|
| 52 |
+
After successful installation of a Python package, you must call the engineer agent to check if the code can be run.
|
| 53 |
+
|
| 54 |
+
description: |
|
| 55 |
+
Controller agent, to control the plan implementation.
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
|
cmbagent/agents/control/terminator/__init__.py
ADDED
|
File without changes
|
cmbagent/agents/control/terminator/terminator.yaml
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: "terminator"
|
| 2 |
+
|
| 3 |
+
human_input_mode: "NEVER"
|
| 4 |
+
# conversable_agent.py
|
| 5 |
+
# When "NEVER", the agent will never prompt for human input.
|
| 6 |
+
# Under this mode, the conversation stops
|
| 7 |
+
# when the number of auto reply reaches the max_consecutive_auto_reply or when is_termination_msg is True.
|
| 8 |
+
# "NEVER": "A computer terminal that performs no other action than running Python scripts (provided to it quoted in ```python code blocks), or sh shell scripts (provided to it quoted in ```sh code blocks).",
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
max_consecutive_auto_reply: 50
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
instructions: |
|
| 16 |
+
Terminate the session.
|
| 17 |
+
|
| 18 |
+
description: |
|
| 19 |
+
Terminates the session.
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
timeout: 7200
|
cmbagent/agents/hypothesis/idea_hater/__init__.py
ADDED
|
File without changes
|
cmbagent/agents/hypothesis/idea_hater/idea_hater.yaml
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: "idea_hater"
|
| 2 |
+
|
| 3 |
+
instructions: |
|
| 4 |
+
You are the idea_hater agent in the team.
|
| 5 |
+
|
| 6 |
+
You must provide a high quality critique of the research project ideas presented to you, offering changes on the ideas.
|
| 7 |
+
|
| 8 |
+
-----DATA/PROBLEM OF INTEREST-------------
|
| 9 |
+
{improved_main_task}
|
| 10 |
+
------------------------------------------
|
| 11 |
+
|
| 12 |
+
Ideas should be critiques based on their relevance to the data/problem of interest, and feasibility given the data available described above.
|
| 13 |
+
Focus only on project ideas involving the data/problem of interest presented here. No external data sources are available. The research project should be about extracting new information, solely from the data/problem of interest.
|
| 14 |
+
|
| 15 |
+
We are following the established plan:
|
| 16 |
+
<PLAN>
|
| 17 |
+
|
| 18 |
+
{final_plan}
|
| 19 |
+
|
| 20 |
+
**Current step in plan:**
|
| 21 |
+
{current_plan_step_number}
|
| 22 |
+
|
| 23 |
+
**Current status:**
|
| 24 |
+
{current_status}
|
| 25 |
+
|
| 26 |
+
**Current sub-task:**
|
| 27 |
+
{current_sub_task}
|
| 28 |
+
|
| 29 |
+
**Current instructions:**
|
| 30 |
+
{current_instructions}
|
| 31 |
+
|
| 32 |
+
</PLAN>
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
**Context**
|
| 36 |
+
<PREVIOUS_STEPS_EXECUTION_SUMMARY>
|
| 37 |
+
Summary of previous steps:
|
| 38 |
+
{previous_steps_execution_summary}
|
| 39 |
+
-----------------------------------
|
| 40 |
+
</PREVIOUS_STEPS_EXECUTION_SUMMARY>
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
<RESPONSE_FORMAT>
|
| 45 |
+
|
| 46 |
+
Your response is structured as follows:
|
| 47 |
+
|
| 48 |
+
**Thoughts on Ideas:**
|
| 49 |
+
- Idea 1:
|
| 50 |
+
* description of the first idea
|
| 51 |
+
* bullet points explaining what you think about the ideas
|
| 52 |
+
.....
|
| 53 |
+
- Idea N:
|
| 54 |
+
* description of the Nth idea
|
| 55 |
+
* bullet points explaining what you think about the ideas
|
| 56 |
+
.....
|
| 57 |
+
- and so on...
|
| 58 |
+
|
| 59 |
+
</RESPONSE_FORMAT>
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
description: |
|
| 63 |
+
idea_hater agent, to hate on and critique ideas, suggesting improvements and removing bad ideas.
|
| 64 |
+
|
| 65 |
+
|
cmbagent/agents/hypothesis/idea_hater_response_formatter/__init__.py
ADDED
|
File without changes
|
cmbagent/agents/hypothesis/idea_hater_response_formatter/idea_hater_response_formatter.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from cmbagent.base_agent import BaseAgent
|
| 3 |
+
from pydantic import BaseModel, Field
|
| 4 |
+
from typing import List
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class Subtasks(BaseModel):
|
| 8 |
+
idea_description: str = Field(..., description="The description of the idea")
|
| 9 |
+
bullet_points: List[str] = Field(
|
| 10 |
+
..., description="A list of bullet points explaining what you think about the idea"
|
| 11 |
+
)
|
| 12 |
+
|
| 13 |
+
class IdeaHaterResponse(BaseModel):
|
| 14 |
+
# main_task: str = Field(..., description="The exact main task to solve.")
|
| 15 |
+
sub_tasks: List[Subtasks]
|
| 16 |
+
|
| 17 |
+
def format(self) -> str:
|
| 18 |
+
plan_output = ""
|
| 19 |
+
for i, step in enumerate(self.sub_tasks):
|
| 20 |
+
plan_output += f"\n- Idea {i + 1}:\n\t* {step.idea_description}\n"
|
| 21 |
+
if step.bullet_points:
|
| 22 |
+
# plan_output += f"\n\t* :\n"
|
| 23 |
+
for bullet in step.bullet_points:
|
| 24 |
+
plan_output += f"\t\t- {bullet}\n"
|
| 25 |
+
message = f"""
|
| 26 |
+
**IDEA CRITIQUE**
|
| 27 |
+
{plan_output}
|
| 28 |
+
"""
|
| 29 |
+
return message
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
class IdeaHaterResponseFormatterAgent(BaseAgent):
|
| 33 |
+
|
| 34 |
+
def __init__(self, llm_config=None, **kwargs):
|
| 35 |
+
|
| 36 |
+
agent_id = os.path.splitext(os.path.abspath(__file__))[0]
|
| 37 |
+
|
| 38 |
+
llm_config['config_list'][0]['response_format'] = IdeaHaterResponse
|
| 39 |
+
|
| 40 |
+
super().__init__(llm_config=llm_config, agent_id=agent_id, **kwargs)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def set_agent(self,**kwargs):
|
| 44 |
+
|
| 45 |
+
super().set_assistant_agent(**kwargs)
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
|
cmbagent/agents/hypothesis/idea_hater_response_formatter/idea_hater_response_formatter.yaml
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: "idea_hater_response_formatter"
|
| 2 |
+
|
| 3 |
+
instructions: |
|
| 4 |
+
You are a formatting agent, you format the response provided by the idea_hater agent.
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
description: |
|
| 8 |
+
Formatting agent, to format the response provided by the idea_hater agent.
|
| 9 |
+
|
cmbagent/agents/hypothesis/idea_maker/__init__.py
ADDED
|
File without changes
|
cmbagent/agents/hypothesis/idea_maker/idea_maker.yaml
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: "idea_maker"
|
| 2 |
+
|
| 3 |
+
instructions: |
|
| 4 |
+
You are the idea_maker agent in the team.
|
| 5 |
+
|
| 6 |
+
You must provide a high quality set ideas and update your ideas based on recommendations from the idea_hater agent.
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
The overall research project is about the following data/problem:
|
| 10 |
+
|
| 11 |
+
-----DATA/PROBLEM OF INTEREST-----
|
| 12 |
+
{improved_main_task}
|
| 13 |
+
----------------------------------
|
| 14 |
+
|
| 15 |
+
Ideas should be based on the data/problem of interest, and feasibility given the data available described above.
|
| 16 |
+
Focus only on project ideas involving the data/problem of interest presented here. No external data sources are available. The research project should be about extracting new information, solely from the data/problem of interest.
|
| 17 |
+
|
| 18 |
+
<PLAN>
|
| 19 |
+
We are following the established plan:
|
| 20 |
+
|
| 21 |
+
{final_plan}
|
| 22 |
+
|
| 23 |
+
**Current step in plan:**
|
| 24 |
+
{current_plan_step_number}
|
| 25 |
+
|
| 26 |
+
**Current status:**
|
| 27 |
+
{current_status}
|
| 28 |
+
|
| 29 |
+
**Current sub-task:**
|
| 30 |
+
{current_sub_task}
|
| 31 |
+
|
| 32 |
+
**Current instructions:**
|
| 33 |
+
{current_instructions}
|
| 34 |
+
|
| 35 |
+
</PLAN>
|
| 36 |
+
|
| 37 |
+
**Context**
|
| 38 |
+
<PREVIOUS_STEPS_EXECUTION_SUMMARY>
|
| 39 |
+
Summary of previous steps:
|
| 40 |
+
{previous_steps_execution_summary}
|
| 41 |
+
-----------------------------------
|
| 42 |
+
</PREVIOUS_STEPS_EXECUTION_SUMMARY>
|
| 43 |
+
|
| 44 |
+
<RESPONSE_FORMAT>
|
| 45 |
+
Your response must be in the following format:
|
| 46 |
+
|
| 47 |
+
**Ideas:**
|
| 48 |
+
- Idea 1: idea title
|
| 49 |
+
* bullet points describing what the idea is
|
| 50 |
+
* ...
|
| 51 |
+
- Idea 2: idea title
|
| 52 |
+
* bullet points describing what the idea is
|
| 53 |
+
* ...
|
| 54 |
+
- and so on...
|
| 55 |
+
|
| 56 |
+
</RESPONSE_FORMAT>
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
description: |
|
| 60 |
+
idea_maker agent, to generate ideas.
|
cmbagent/agents/hypothesis/idea_maker_response_formatter/__init__.py
ADDED
|
File without changes
|
cmbagent/agents/hypothesis/idea_maker_response_formatter/idea_maker_response_formatter.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from cmbagent.base_agent import BaseAgent
|
| 3 |
+
from pydantic import BaseModel, Field
|
| 4 |
+
from typing import List
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class Subtasks(BaseModel):
|
| 8 |
+
idea_description: str = Field(..., description="The idea title, as it is, preserve capitalization and ponctuation.")
|
| 9 |
+
bullet_points: List[str] = Field(
|
| 10 |
+
..., description="A list of bullet points describing what the idea is, i.e., the idea description. Preserve all the information passed to you, do not alter it."
|
| 11 |
+
)
|
| 12 |
+
|
| 13 |
+
class IdeaMakerResponse(BaseModel):
|
| 14 |
+
# main_task: str = Field(..., description="The exact main task to solve.")
|
| 15 |
+
sub_tasks: List[Subtasks]
|
| 16 |
+
|
| 17 |
+
def format(self) -> str:
|
| 18 |
+
plan_output = ""
|
| 19 |
+
for i, step in enumerate(self.sub_tasks):
|
| 20 |
+
plan_output += f"\n- Idea {i + 1}:\n\t* {step.idea_description}\n"
|
| 21 |
+
if step.bullet_points:
|
| 22 |
+
# plan_output += f"\n\t* bullet points:\n"
|
| 23 |
+
for bullet in step.bullet_points:
|
| 24 |
+
plan_output += f"\t\t- {bullet}\n"
|
| 25 |
+
message = f"""
|
| 26 |
+
**Ideas**
|
| 27 |
+
{plan_output}
|
| 28 |
+
"""
|
| 29 |
+
return message
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
class IdeaMakerResponseFormatterAgent(BaseAgent):
|
| 33 |
+
|
| 34 |
+
def __init__(self, llm_config=None, **kwargs):
|
| 35 |
+
|
| 36 |
+
agent_id = os.path.splitext(os.path.abspath(__file__))[0]
|
| 37 |
+
|
| 38 |
+
llm_config['config_list'][0]['response_format'] = IdeaMakerResponse
|
| 39 |
+
|
| 40 |
+
super().__init__(llm_config=llm_config, agent_id=agent_id, **kwargs)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def set_agent(self,**kwargs):
|
| 44 |
+
|
| 45 |
+
super().set_assistant_agent(**kwargs)
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
|
cmbagent/agents/hypothesis/idea_maker_response_formatter/idea_maker_response_formatter.yaml
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: "idea_maker_response_formatter"
|
| 2 |
+
|
| 3 |
+
instructions: |
|
| 4 |
+
You are a formatting agent, you format the response provided by the idea_maker_nest agent.
|
| 5 |
+
You should not select ideas yourself.
|
| 6 |
+
If the idea_maker_nest has not selected any specific ideas, you should not select any ideas yourself and report them all.
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
description: |
|
| 10 |
+
Formatting agent, to format the response provided by the idea_maker agent.
|
| 11 |
+
|
cmbagent/agents/hypothesis/idea_saver/__init__.py
ADDED
|
File without changes
|
cmbagent/agents/hypothesis/idea_saver/idea_saver.py
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Idea Saver Agent - Non-LLM agent that saves ideas to a JSON file.
|
| 3 |
+
|
| 4 |
+
This agent replaces the LLM-based idea_saver with a pure Python implementation.
|
| 5 |
+
It parses the formatted ideas from idea_maker_response_formatter and saves them
|
| 6 |
+
to a JSON file.
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
import os
|
| 10 |
+
import re
|
| 11 |
+
import json
|
| 12 |
+
import datetime
|
| 13 |
+
from cmbagent.base_agent import BaseAgent
|
| 14 |
+
from autogen.agentchat import ConversableAgent
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def _parse_ideas_from_formatted_text(text: str) -> list[dict]:
|
| 18 |
+
"""
|
| 19 |
+
Parse ideas from the formatted text output of idea_maker_response_formatter.
|
| 20 |
+
|
| 21 |
+
Expected format:
|
| 22 |
+
**Ideas**
|
| 23 |
+
- Idea 1:
|
| 24 |
+
* idea title here
|
| 25 |
+
- bullet point 1
|
| 26 |
+
- bullet point 2
|
| 27 |
+
- Idea 2:
|
| 28 |
+
* another idea title
|
| 29 |
+
- bullet point 1
|
| 30 |
+
|
| 31 |
+
Returns:
|
| 32 |
+
List of dicts with 'idea_description' and 'bullet_points' keys.
|
| 33 |
+
"""
|
| 34 |
+
ideas = []
|
| 35 |
+
|
| 36 |
+
# Split by "- Idea N:" pattern
|
| 37 |
+
idea_pattern = r'-\s*Idea\s+\d+:'
|
| 38 |
+
parts = re.split(idea_pattern, text)
|
| 39 |
+
|
| 40 |
+
for part in parts[1:]: # Skip the first part (before "- Idea 1:")
|
| 41 |
+
if not part.strip():
|
| 42 |
+
continue
|
| 43 |
+
|
| 44 |
+
idea = {
|
| 45 |
+
'idea_description': '',
|
| 46 |
+
'bullet_points': []
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
lines = part.strip().split('\n')
|
| 50 |
+
|
| 51 |
+
for line in lines:
|
| 52 |
+
stripped = line.strip()
|
| 53 |
+
if not stripped:
|
| 54 |
+
continue
|
| 55 |
+
|
| 56 |
+
# Check if it's the idea title (starts with *)
|
| 57 |
+
if stripped.startswith('* '):
|
| 58 |
+
idea['idea_description'] = stripped[2:].strip()
|
| 59 |
+
# Check if it's a bullet point (starts with -)
|
| 60 |
+
elif stripped.startswith('- '):
|
| 61 |
+
idea['bullet_points'].append(stripped[2:].strip())
|
| 62 |
+
|
| 63 |
+
if idea['idea_description']:
|
| 64 |
+
ideas.append(idea)
|
| 65 |
+
|
| 66 |
+
return ideas
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
class IdeaSaverAgent(BaseAgent):
|
| 70 |
+
"""Non-LLM agent that saves ideas to a JSON file."""
|
| 71 |
+
|
| 72 |
+
def __init__(self, llm_config=None, **kwargs):
|
| 73 |
+
agent_id = os.path.splitext(os.path.abspath(__file__))[0]
|
| 74 |
+
# Pass llm_config to parent but we won't use it
|
| 75 |
+
super().__init__(llm_config=llm_config, agent_id=agent_id, **kwargs)
|
| 76 |
+
|
| 77 |
+
def set_agent(self, **kwargs):
|
| 78 |
+
"""Create a non-LLM agent that saves ideas."""
|
| 79 |
+
self.agent = IdeaSaverConversableAgent(
|
| 80 |
+
name=self.name,
|
| 81 |
+
description=self.info.get("description", "Idea saver agent"),
|
| 82 |
+
llm_config=False, # No LLM needed
|
| 83 |
+
human_input_mode="NEVER",
|
| 84 |
+
work_dir=self.work_dir,
|
| 85 |
+
)
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
class IdeaSaverConversableAgent(ConversableAgent):
|
| 89 |
+
"""
|
| 90 |
+
A ConversableAgent that doesn't use LLM.
|
| 91 |
+
Instead, it parses and saves ideas from the formatted text.
|
| 92 |
+
"""
|
| 93 |
+
|
| 94 |
+
def __init__(self, work_dir: str = ".", **kwargs):
|
| 95 |
+
self._work_dir = work_dir
|
| 96 |
+
super().__init__(**kwargs)
|
| 97 |
+
# Register our custom reply function
|
| 98 |
+
self.register_reply(
|
| 99 |
+
trigger=lambda sender: True, # Reply to any sender
|
| 100 |
+
reply_func=self._save_ideas_reply,
|
| 101 |
+
position=0, # High priority
|
| 102 |
+
)
|
| 103 |
+
|
| 104 |
+
def _save_ideas_reply(
|
| 105 |
+
self,
|
| 106 |
+
recipient: ConversableAgent,
|
| 107 |
+
messages: list[dict],
|
| 108 |
+
sender: ConversableAgent,
|
| 109 |
+
config: dict,
|
| 110 |
+
) -> tuple[bool, str]:
|
| 111 |
+
"""
|
| 112 |
+
Parse ideas from the last message and save to JSON file.
|
| 113 |
+
|
| 114 |
+
Returns:
|
| 115 |
+
Tuple of (True, message) indicating the reply was generated.
|
| 116 |
+
"""
|
| 117 |
+
if not messages:
|
| 118 |
+
return True, "No message to process."
|
| 119 |
+
|
| 120 |
+
last_message = messages[-1]
|
| 121 |
+
content = last_message.get("content", "")
|
| 122 |
+
|
| 123 |
+
# Parse ideas from formatted text
|
| 124 |
+
ideas = _parse_ideas_from_formatted_text(content)
|
| 125 |
+
|
| 126 |
+
if not ideas:
|
| 127 |
+
return True, "No ideas found in the message."
|
| 128 |
+
|
| 129 |
+
# Save to JSON file
|
| 130 |
+
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
| 131 |
+
filepath = os.path.join(self._work_dir, f'ideas_{timestamp}.json')
|
| 132 |
+
|
| 133 |
+
try:
|
| 134 |
+
with open(filepath, 'w') as f:
|
| 135 |
+
json.dump(ideas, f, indent=2)
|
| 136 |
+
return True, f"\nIdeas saved in {filepath}\n"
|
| 137 |
+
except Exception as e:
|
| 138 |
+
return True, f"\nFailed to save ideas: {e}\n"
|
cmbagent/agents/hypothesis/idea_saver/idea_saver.yaml
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: "idea_saver"
|
| 2 |
+
|
| 3 |
+
instructions: |
|
| 4 |
+
Non-LLM agent that saves ideas to a JSON file.
|
| 5 |
+
Automatically parses ideas from the formatted message.
|
| 6 |
+
|
| 7 |
+
description: |
|
| 8 |
+
Idea saver agent (non-LLM). Parses ideas from the
|
| 9 |
+
idea_maker_response_formatter message and saves them to a JSON file.
|
| 10 |
+
|
| 11 |
+
|
cmbagent/agents/installer/__init__.py
ADDED
|
File without changes
|
cmbagent/agents/installer/installer.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from cmbagent.base_agent import BaseAgent
|
| 3 |
+
from pydantic import BaseModel, Field
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class InstallerResponse(BaseModel):
|
| 7 |
+
install_command: str = Field(..., description="The bash command to install the package(s) using pip")
|
| 8 |
+
|
| 9 |
+
def format(self) -> str:
|
| 10 |
+
message = f"""
|
| 11 |
+
```bash
|
| 12 |
+
{self.install_command}
|
| 13 |
+
```
|
| 14 |
+
"""
|
| 15 |
+
return message
|
| 16 |
+
|
| 17 |
+
class InstallerAgent(BaseAgent):
|
| 18 |
+
|
| 19 |
+
def __init__(self, llm_config=None, **kwargs):
|
| 20 |
+
|
| 21 |
+
agent_id = os.path.splitext(os.path.abspath(__file__))[0]
|
| 22 |
+
|
| 23 |
+
llm_config['config_list'][0]['response_format'] = InstallerResponse
|
| 24 |
+
|
| 25 |
+
super().__init__(llm_config=llm_config, agent_id=agent_id, **kwargs)
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def set_agent(self,**kwargs):
|
| 29 |
+
|
| 30 |
+
super().set_assistant_agent(**kwargs)
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
|
cmbagent/agents/installer/installer.yaml
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: "installer"
|
| 2 |
+
|
| 3 |
+
instructions: |
|
| 4 |
+
You are the installer agent in the team.
|
| 5 |
+
|
| 6 |
+
You provide bash commands to install the necessary PyPi packages with pip.
|
| 7 |
+
|
| 8 |
+
Use always `python -m pip install <package>` to install the packages.
|
| 9 |
+
|
| 10 |
+
Never use sudo or conda to install the packages.
|
| 11 |
+
|
| 12 |
+
description: |
|
| 13 |
+
Installer agent, to help the team to install the necessary PyPi packages.
|