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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .project/logs/202604071545__telemetry-http-snapshots.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Telemetry HTTP Snapshots

## Scope

- add bounded structured HTTP response snapshots to action telemetry
- keep the feature generic in public MASE
- document the persisted telemetry surfaces and snapshot limits

## Changed Surfaces

- `services/agent-launcher/app/http_snapshot.py`
- `services/agent-launcher/app/config.py`
- `services/agent-launcher/app/executor.py`
- `services/agent-launcher/tests/unit/test_http_snapshot.py`
- `services/agent-launcher/tests/integration/test_heartbeat_endpoint_multiround.py`
- `docs/telemetry.md`
- `docs/run-and-inspect.md`
- `README.md`

## Notes

- snapshots are bounded and redacted, not raw body dumps
- controller storage/export path already carries payload JSON, so no controller schema change was needed
- this is intended to support later environment-specific projections such as feed score/rank analysis
21 changes: 21 additions & 0 deletions .zenodo.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"title": "MASE: Multi-Agent Simulation Environment",
"upload_type": "software",
"access_right": "open",
"license": "Apache-2.0",
"creators": [
{
"name": "Prandi, Matteo",
"affiliation": "Icaro Lab",
"orcid": "0009-0002-9258-5589"
}
],
"description": "MASE is experimentation infrastructure for studying AI agent interaction at scale.",
"keywords": [
"multi-agent systems",
"simulation",
"AI agents",
"research infrastructure"
],
"language": "eng"
}
32 changes: 32 additions & 0 deletions CITATION.cff
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
cff-version: 1.2.0
message: >-
If you use MASE in academic work, please cite the specific software release
you used. If you used unreleased code, cite the repository together with the
commit hash.
title: "MASE: Multi-Agent Simulation Environment"
type: software
authors:
- family-names: "Prandi"
given-names: "Matteo"
affiliation: "Icaro Lab"
orcid: "https://orcid.org/0009-0002-9258-5589"
repository-code: "https://github.com/icaro-lab/MASE"
url: "https://github.com/icaro-lab/MASE"
license: "Apache-2.0"
abstract: "Experimentation infrastructure for studying AI agent interaction at scale."
keywords:
- "multi-agent systems"
- "simulation"
- "AI agents"
- "research infrastructure"
preferred-citation:
type: software
authors:
- family-names: "Prandi"
given-names: "Matteo"
affiliation: "Icaro Lab"
orcid: "https://orcid.org/0009-0002-9258-5589"
title: "MASE: Multi-Agent Simulation Environment"
version: "v0.1.0"
doi: "10.5281/zenodo.19485472"
url: "https://doi.org/10.5281/zenodo.19485472"
20 changes: 20 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,24 @@ bash scripts/up_stack.sh
bash scripts/verify_platform.sh
```

## Branch Strategy

- `main` is the stable release branch.
- `dev` is the integration branch for active development.
- Open pull requests against `dev`, not `main`.
- Do not push feature work directly to `main`.

Recommended flow:

```bash
git fetch origin
git switch dev
git pull --rebase origin dev
git switch -c feat/<short-topic>
```

If you are contributing from a fork, open your PR from `feat/<short-topic>` into `icaro-lab/MASE:dev`.

## Main Contribution Paths

### New Environment
Expand Down Expand Up @@ -75,9 +93,11 @@ bash scripts/verify_platform.sh
## Pull Requests

- Keep PRs scoped.
- Base PRs on `dev`.
- Describe the user-facing change clearly.
- Include the commands you ran.
- Call out any deferred work explicitly.
- Rebase or merge from `origin/dev` before marking the PR ready.

## Design Rules

Expand Down
51 changes: 51 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<p>
<a href="https://github.com/icaro-lab/MASE/actions/workflows/ci.yml"><img src="https://github.com/icaro-lab/MASE/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
<a href="LICENSE"><img src="https://img.shields.io/github/license/icaro-lab/MASE" alt="License"></a>
<a href="https://doi.org/10.5281/zenodo.19485472"><img src="https://zenodo.org/badge/1188871480.svg" alt="DOI"></a>
<a href="#next-steps"><img src="https://img.shields.io/badge/status-alpha-8a6d3b" alt="Status"></a>
</p>
<p>
Expand Down Expand Up @@ -147,12 +148,54 @@ Public-facing docs for the extractable OSS surface now live under `docs/`:
- [`docs/concepts.md`](docs/concepts.md)
- [`docs/quickstart.md`](docs/quickstart.md)
- [`docs/openrouter.md`](docs/openrouter.md)
- [`docs/telemetry.md`](docs/telemetry.md)
- [`docs/runtime-contract.md`](docs/runtime-contract.md)
- [`docs/environment-contract.md`](docs/environment-contract.md)
- [`docs/create-environment.md`](docs/create-environment.md)
- [`docs/run-and-inspect.md`](docs/run-and-inspect.md)
- [`docs/troubleshooting.md`](docs/troubleshooting.md)

## Citation

MASE now includes both [`CITATION.cff`](CITATION.cff) for GitHub's built-in
repository citation support and [`.zenodo.json`](.zenodo.json) for release
archiving metadata on Zenodo.

If you use MASE in research:

- cite the specific software release you used
- if you used unreleased code, cite the repository together with the commit hash

The current DOI-backed software release is:

- `v0.1.0`
- DOI: [`10.5281/zenodo.19485472`](https://doi.org/10.5281/zenodo.19485472)

Preferred citation for the first archived release:

```bibtex
@software{prandi_mase_v010,
author = {Prandi, Matteo},
title = {MASE: Multi-Agent Simulation Environment},
version = {v0.1.0},
year = {2026},
doi = {10.5281/zenodo.19485472},
url = {https://doi.org/10.5281/zenodo.19485472}
}
```

For unreleased work on top of `main` or `dev`, cite the repository together with
the commit hash instead of the `v0.1.0` DOI.

### Maintainer Release Setup

The repository is set up for DOI-backed releases through GitHub + Zenodo:

1. Create a GitHub release with a new version tag.
2. Zenodo will archive that release and mint a new DOI for it.
3. Update [`CITATION.cff`](CITATION.cff) and this README so the preferred
citation points at the latest archived release DOI.

## Project Status

MASE is currently in alpha.
Expand All @@ -176,6 +219,14 @@ This repo now includes:
- [SECURITY.md](SECURITY.md)
- GitHub CI under `.github/workflows/ci.yml`

Contribution flow:

- `main` is the stable public default branch
- GitHub releases and release tags should be cut from `main`
- `dev` is the integration branch for ongoing work
- feature branches should usually branch from `dev` and open PRs back into `dev`
- promote `dev` into `main` when the integrated state is ready to become the new public baseline

## Repository Shape

```text
Expand Down
10 changes: 10 additions & 0 deletions docs/run-and-inspect.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@ Useful API endpoints:
- `GET /api/v1/runs/{run_id}/events`
- `GET /api/v1/runs/{run_id}/metrics`
- `GET /api/v1/runs/{run_id}/scheduler/status`
- `GET /api/v1/telemetry/events/{run_id}`
- `GET /api/v1/telemetry/metrics/{run_id}`

`action_attempt` telemetry rows now persist two response surfaces for HTTP actions:

- `response_preview`: short redacted string preview
- `response_snapshot`: bounded structured JSON snapshot for JSON-like bodies

The snapshot is generic and size-limited. Environments can later derive domain-specific analyses
from it without requiring raw full-body dumps in telemetry.

## Operate

Expand Down
58 changes: 58 additions & 0 deletions docs/telemetry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Telemetry

MASE persists run-scoped telemetry at two levels:

- `events`: general run/system events
- `agent_action_events`: per-action rows emitted by the runtime

The most useful starting point is `action_attempt` telemetry. Those rows already include:

- action identity (`action_type`, `action_name`, `action_key`)
- request metadata (`method`, `path`, `status_code`, `request_id`)
- timing and success/failure fields
- a bounded `response_preview`

For JSON-like HTTP responses, MASE now also stores:

- `response_snapshot`
- `response_snapshot_meta`

The snapshot is intentionally bounded rather than a raw body dump:

- nested depth is capped
- dict keys and list items are capped
- long strings are truncated
- sensitive-looking values are redacted

This makes telemetry usable for downstream analysis without turning the controller database into a
full packet capture.

## Querying

Useful endpoints:

- `GET /api/v1/telemetry/events/{run_id}`
- `GET /api/v1/telemetry/metrics/{run_id}`
- `GET /api/v1/runs/{run_id}/events`

## Tuning

Agent-launcher snapshot limits are configurable via environment variables:

- `AGENT_LAUNCHER_HTTP_RESPONSE_SNAPSHOT_ENABLED`
- `AGENT_LAUNCHER_HTTP_RESPONSE_SNAPSHOT_MAX_DEPTH`
- `AGENT_LAUNCHER_HTTP_RESPONSE_SNAPSHOT_MAX_DICT_KEYS`
- `AGENT_LAUNCHER_HTTP_RESPONSE_SNAPSHOT_MAX_LIST_ITEMS`
- `AGENT_LAUNCHER_HTTP_RESPONSE_SNAPSHOT_MAX_STRING_CHARS`
- `AGENT_LAUNCHER_HTTP_RESPONSE_SNAPSHOT_MAX_TOTAL_NODES`

## Extension Pattern

The generic snapshot layer should remain environment-agnostic.

If an environment needs domain-specific analysis, the recommended pattern is:

1. capture a bounded generic `response_snapshot` in MASE
2. derive environment-specific projections in the environment repo or downstream analysis pipeline

That keeps core telemetry reusable while still supporting richer experiment-specific metrics.
28 changes: 27 additions & 1 deletion scripts/runtime_env.sh
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,32 @@ PY
printf '%s' "$value"
}

resolve_dotenv_path() {
local root="$1"
local candidate="$root/.env"
local common_dir=""
local shared_root=""

if [[ -f "$candidate" ]]; then
printf '%s' "$candidate"
return 0
fi

common_dir="$(git -C "$root" rev-parse --git-common-dir 2>/dev/null || true)"
if [[ -n "$common_dir" ]]; then
if [[ "$common_dir" != /* ]]; then
common_dir="$(cd "$root" && cd "$common_dir" && pwd -P)"
fi
shared_root="$(cd "$common_dir/.." 2>/dev/null && pwd -P || true)"
if [[ -n "$shared_root" && -f "$shared_root/.env" ]]; then
printf '%s' "$shared_root/.env"
return 0
fi
fi

printf '%s' "$candidate"
}

emit "COMPOSE_PROJECT_NAME" "$namespace"
emit "HOST_PROJECT_ROOT" "$repo_root"
emit "MASE_NETWORK_NAME" "${namespace}-network"
Expand All @@ -79,7 +105,7 @@ emit "GRAFANA_PORT" "${GRAFANA_PORT:-3001}"
emit "AGENT_WORKER_IMAGE" "${AGENT_WORKER_IMAGE:-mase-agent-launcher:${namespace}}"
emit "MASE_IMAGE_NAMESPACE" "${MASE_IMAGE_NAMESPACE:-$namespace}"

dotenv_path="$repo_root/.env"
dotenv_path="$(resolve_dotenv_path "$repo_root")"
openrouter_api_key="$(
resolve_optional_from_dotenv "OPENROUTER_API_KEY" "$dotenv_path" \
|| resolve_optional_from_dotenv "AGENT_LAUNCHER_OPENROUTER_API_KEY" "$dotenv_path" \
Expand Down
6 changes: 6 additions & 0 deletions services/agent-launcher/app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ class Settings(BaseSettings):
# Execution Limits
max_actions_per_heartbeat: int = 10
http_timeout: int = 30
http_response_snapshot_enabled: bool = True
http_response_snapshot_max_depth: int = 4
http_response_snapshot_max_dict_keys: int = 24
http_response_snapshot_max_list_items: int = 64
http_response_snapshot_max_string_chars: int = 120
http_response_snapshot_max_total_nodes: int = 2048

# Service Configuration
environment_url: Optional[str] = None
Expand Down
14 changes: 14 additions & 0 deletions services/agent-launcher/app/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from .action_parser import Action, ActionType, HTTPMethod
from .agent_fs import AgentFilesystem
from .config import settings
from .http_snapshot import build_response_snapshot
from .telemetry_client import ActionCategory, record_action
from .scheduler import heartbeat_scheduler

Expand Down Expand Up @@ -1138,6 +1139,8 @@ async def _record_action_telemetry(

response_preview = None
response_body_chars = None
response_snapshot = None
response_snapshot_meta = None
response_payload = result.get("response")
if isinstance(response_payload, dict):
body = response_payload.get("body")
Expand All @@ -1150,6 +1153,15 @@ async def _record_action_telemetry(
response_body_chars = len(response_preview)
if len(response_preview) > 700:
response_preview = f"{response_preview[:697]}..."
response_snapshot, response_snapshot_meta = build_response_snapshot(
body,
enabled=settings.http_response_snapshot_enabled,
max_depth=settings.http_response_snapshot_max_depth,
max_dict_keys=settings.http_response_snapshot_max_dict_keys,
max_list_items=settings.http_response_snapshot_max_list_items,
max_string_chars=settings.http_response_snapshot_max_string_chars,
max_total_nodes=settings.http_response_snapshot_max_total_nodes,
)

action_url = None
if hasattr(action, "url"):
Expand All @@ -1174,6 +1186,8 @@ async def _record_action_telemetry(
"error_code": error_code if not success else None,
"response_preview": response_preview,
"response_body_chars": response_body_chars,
"response_snapshot": response_snapshot,
"response_snapshot_meta": response_snapshot_meta,
}

await record_action(
Expand Down
Loading
Loading