aimx 0.3.0__tar.gz → 0.3.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. aimx-0.3.2/.agents/skills/aimx/SKILL.md +123 -0
  2. aimx-0.3.2/.agents/skills/aimx/agents/openai.yaml +4 -0
  3. aimx-0.3.2/.agents/skills/aimx/references/aimx-cli.md +233 -0
  4. aimx-0.3.2/.agents/skills/aimx/scripts/collect_experiment_snapshot.py +217 -0
  5. {aimx-0.3.0 → aimx-0.3.2}/.gitignore +9 -1
  6. {aimx-0.3.0 → aimx-0.3.2}/AGENTS.md +4 -0
  7. aimx-0.3.2/PKG-INFO +309 -0
  8. aimx-0.3.2/README.md +297 -0
  9. aimx-0.3.2/TODO.md +3 -0
  10. {aimx-0.3.0 → aimx-0.3.2}/pyproject.toml +2 -1
  11. aimx-0.3.2/specs/003-query-images-terminal-render/checklists/requirements.md +35 -0
  12. aimx-0.3.2/specs/003-query-images-terminal-render/contracts/cli-output.md +122 -0
  13. aimx-0.3.2/specs/003-query-images-terminal-render/data-model.md +112 -0
  14. aimx-0.3.2/specs/003-query-images-terminal-render/plan.md +166 -0
  15. aimx-0.3.2/specs/003-query-images-terminal-render/quickstart.md +98 -0
  16. aimx-0.3.2/specs/003-query-images-terminal-render/research.md +138 -0
  17. aimx-0.3.2/specs/003-query-images-terminal-render/spec.md +119 -0
  18. aimx-0.3.2/specs/003-query-images-terminal-render/tasks.md +250 -0
  19. aimx-0.3.2/specs/004-run-params-query/checklists/requirements.md +42 -0
  20. aimx-0.3.2/specs/004-run-params-query/contracts/cli-output.md +107 -0
  21. aimx-0.3.2/specs/004-run-params-query/data-model.md +121 -0
  22. aimx-0.3.2/specs/004-run-params-query/plan.md +175 -0
  23. aimx-0.3.2/specs/004-run-params-query/quickstart.md +112 -0
  24. aimx-0.3.2/specs/004-run-params-query/research.md +127 -0
  25. aimx-0.3.2/specs/004-run-params-query/spec.md +206 -0
  26. aimx-0.3.2/specs/004-run-params-query/tasks.md +241 -0
  27. {aimx-0.3.0 → aimx-0.3.2}/src/aimx/__init__.py +1 -1
  28. {aimx-0.3.0 → aimx-0.3.2}/src/aimx/aim_bridge/metric_stats.py +201 -1
  29. aimx-0.3.2/src/aimx/aim_bridge/run_params.py +106 -0
  30. {aimx-0.3.0 → aimx-0.3.2}/src/aimx/commands/help.py +9 -3
  31. aimx-0.3.2/src/aimx/commands/query.py +423 -0
  32. aimx-0.3.2/src/aimx/rendering/image_render.py +269 -0
  33. aimx-0.3.2/src/aimx/rendering/params_views.py +160 -0
  34. aimx-0.3.2/static/images.png +0 -0
  35. aimx-0.3.2/static/params.png +0 -0
  36. aimx-0.3.2/tests/contract/test_query_contract.py +446 -0
  37. aimx-0.3.2/tests/integration/test_query_command.py +521 -0
  38. {aimx-0.3.0 → aimx-0.3.2}/tests/integration/test_short_hash_and_steps.py +105 -3
  39. aimx-0.3.2/tests/unit/test_image_render.py +309 -0
  40. aimx-0.3.2/tests/unit/test_query_helpers.py +468 -0
  41. aimx-0.3.2/tests/unit/test_run_params.py +91 -0
  42. {aimx-0.3.0 → aimx-0.3.2}/uv.lock +16 -1
  43. aimx-0.3.0/.agents/skills/speckit-analyze/SKILL.md +0 -255
  44. aimx-0.3.0/.agents/skills/speckit-checklist/SKILL.md +0 -367
  45. aimx-0.3.0/.agents/skills/speckit-clarify/SKILL.md +0 -249
  46. aimx-0.3.0/.agents/skills/speckit-constitution/SKILL.md +0 -152
  47. aimx-0.3.0/.agents/skills/speckit-git-commit/SKILL.md +0 -53
  48. aimx-0.3.0/.agents/skills/speckit-git-feature/SKILL.md +0 -72
  49. aimx-0.3.0/.agents/skills/speckit-git-initialize/SKILL.md +0 -54
  50. aimx-0.3.0/.agents/skills/speckit-git-remote/SKILL.md +0 -50
  51. aimx-0.3.0/.agents/skills/speckit-git-validate/SKILL.md +0 -54
  52. aimx-0.3.0/.agents/skills/speckit-implement/SKILL.md +0 -204
  53. aimx-0.3.0/.agents/skills/speckit-plan/SKILL.md +0 -151
  54. aimx-0.3.0/.agents/skills/speckit-specify/SKILL.md +0 -325
  55. aimx-0.3.0/.agents/skills/speckit-tasks/SKILL.md +0 -197
  56. aimx-0.3.0/.agents/skills/speckit-taskstoissues/SKILL.md +0 -101
  57. aimx-0.3.0/PKG-INFO +0 -182
  58. aimx-0.3.0/README.md +0 -171
  59. aimx-0.3.0/src/aimx/commands/query.py +0 -191
  60. aimx-0.3.0/tests/contract/test_query_contract.py +0 -108
  61. aimx-0.3.0/tests/integration/test_query_command.py +0 -139
  62. aimx-0.3.0/tests/unit/test_query_helpers.py +0 -115
  63. {aimx-0.3.0 → aimx-0.3.2}/.codex/hooks.json +0 -0
  64. {aimx-0.3.0 → aimx-0.3.2}/.github/workflows/CI.yaml +0 -0
  65. {aimx-0.3.0 → aimx-0.3.2}/.github/workflows/publish.yaml +0 -0
  66. {aimx-0.3.0 → aimx-0.3.2}/.python-version +0 -0
  67. {aimx-0.3.0 → aimx-0.3.2}/CLAUDE.md +0 -0
  68. {aimx-0.3.0 → aimx-0.3.2}/CONSTITUTION.md +0 -0
  69. {aimx-0.3.0 → aimx-0.3.2}/LICENSE +0 -0
  70. {aimx-0.3.0 → aimx-0.3.2}/specs/001-aim-command-passthrough/checklists/requirements.md +0 -0
  71. {aimx-0.3.0 → aimx-0.3.2}/specs/001-aim-command-passthrough/contracts/cli-contract.md +0 -0
  72. {aimx-0.3.0 → aimx-0.3.2}/specs/001-aim-command-passthrough/data-model.md +0 -0
  73. {aimx-0.3.0 → aimx-0.3.2}/specs/001-aim-command-passthrough/plan.md +0 -0
  74. {aimx-0.3.0 → aimx-0.3.2}/specs/001-aim-command-passthrough/quickstart.md +0 -0
  75. {aimx-0.3.0 → aimx-0.3.2}/specs/001-aim-command-passthrough/research.md +0 -0
  76. {aimx-0.3.0 → aimx-0.3.2}/specs/001-aim-command-passthrough/spec.md +0 -0
  77. {aimx-0.3.0 → aimx-0.3.2}/specs/001-aim-command-passthrough/tasks.md +0 -0
  78. {aimx-0.3.0 → aimx-0.3.2}/specs/002-aim-query-command/checklists/requirements.md +0 -0
  79. {aimx-0.3.0 → aimx-0.3.2}/specs/002-aim-query-command/contracts/query-command.md +0 -0
  80. {aimx-0.3.0 → aimx-0.3.2}/specs/002-aim-query-command/data-model.md +0 -0
  81. {aimx-0.3.0 → aimx-0.3.2}/specs/002-aim-query-command/plan.md +0 -0
  82. {aimx-0.3.0 → aimx-0.3.2}/specs/002-aim-query-command/quickstart.md +0 -0
  83. {aimx-0.3.0 → aimx-0.3.2}/specs/002-aim-query-command/research.md +0 -0
  84. {aimx-0.3.0 → aimx-0.3.2}/specs/002-aim-query-command/spec.md +0 -0
  85. {aimx-0.3.0 → aimx-0.3.2}/specs/002-aim-query-command/tasks.md +0 -0
  86. {aimx-0.3.0 → aimx-0.3.2}/src/aimx/__main__.py +0 -0
  87. {aimx-0.3.0 → aimx-0.3.2}/src/aimx/aim_bridge/__init__.py +0 -0
  88. {aimx-0.3.0 → aimx-0.3.2}/src/aimx/aim_bridge/hash_resolver.py +0 -0
  89. {aimx-0.3.0 → aimx-0.3.2}/src/aimx/cli.py +0 -0
  90. {aimx-0.3.0 → aimx-0.3.2}/src/aimx/commands/__init__.py +0 -0
  91. {aimx-0.3.0 → aimx-0.3.2}/src/aimx/commands/doctor.py +0 -0
  92. {aimx-0.3.0 → aimx-0.3.2}/src/aimx/commands/trace.py +0 -0
  93. {aimx-0.3.0 → aimx-0.3.2}/src/aimx/commands/version.py +0 -0
  94. {aimx-0.3.0 → aimx-0.3.2}/src/aimx/native_aim/__init__.py +0 -0
  95. {aimx-0.3.0 → aimx-0.3.2}/src/aimx/native_aim/locator.py +0 -0
  96. {aimx-0.3.0 → aimx-0.3.2}/src/aimx/native_aim/passthrough.py +0 -0
  97. {aimx-0.3.0 → aimx-0.3.2}/src/aimx/rendering/__init__.py +0 -0
  98. {aimx-0.3.0 → aimx-0.3.2}/src/aimx/rendering/colors.py +0 -0
  99. {aimx-0.3.0 → aimx-0.3.2}/src/aimx/rendering/query_views.py +0 -0
  100. {aimx-0.3.0 → aimx-0.3.2}/src/aimx/rendering/trace_views.py +0 -0
  101. {aimx-0.3.0 → aimx-0.3.2}/src/aimx/router.py +0 -0
  102. {aimx-0.3.0 → aimx-0.3.2}/static/metrics.png +0 -0
  103. {aimx-0.3.0 → aimx-0.3.2}/static/trace.png +0 -0
  104. {aimx-0.3.0 → aimx-0.3.2}/tests/conftest.py +0 -0
  105. {aimx-0.3.0 → aimx-0.3.2}/tests/contract/test_cli_contract.py +0 -0
  106. {aimx-0.3.0 → aimx-0.3.2}/tests/contract/test_trace_contract.py +0 -0
  107. {aimx-0.3.0 → aimx-0.3.2}/tests/integration/conftest.py +0 -0
  108. {aimx-0.3.0 → aimx-0.3.2}/tests/integration/test_missing_native_aim.py +0 -0
  109. {aimx-0.3.0 → aimx-0.3.2}/tests/integration/test_missing_python_aim_package.py +0 -0
  110. {aimx-0.3.0 → aimx-0.3.2}/tests/integration/test_passthrough_behavior.py +0 -0
  111. {aimx-0.3.0 → aimx-0.3.2}/tests/integration/test_trace_command.py +0 -0
  112. {aimx-0.3.0 → aimx-0.3.2}/tests/unit/test_hash_resolver.py +0 -0
  113. {aimx-0.3.0 → aimx-0.3.2}/tests/unit/test_locator.py +0 -0
  114. {aimx-0.3.0 → aimx-0.3.2}/tests/unit/test_metric_stats.py +0 -0
  115. {aimx-0.3.0 → aimx-0.3.2}/tests/unit/test_owned_commands.py +0 -0
  116. {aimx-0.3.0 → aimx-0.3.2}/tests/unit/test_router.py +0 -0
  117. {aimx-0.3.0 → aimx-0.3.2}/tests/unit/test_step_filter.py +0 -0
  118. {aimx-0.3.0 → aimx-0.3.2}/tests/unit/test_trace_helpers.py +0 -0
@@ -0,0 +1,123 @@
1
+ ---
2
+ name: aimx
3
+ description: Use when autoresearch, log_experiment, experiment analysis, or automatic iteration workflows need to inspect local Aim repositories with aimx, collect run params, metric summaries, traces, image evidence, compare training runs, or summarize model results without mutating Aim data.
4
+ ---
5
+
6
+ # Aimx
7
+
8
+ ## Overview
9
+
10
+ Use `aimx` as a read-only evidence collector for `autoresearch` `log_experiment`
11
+ steps. Prefer JSON output so downstream agents can compare runs, explain model
12
+ effects, and propose the next experiment from concrete Aim data.
13
+
14
+ ## Requirements
15
+
16
+ - Require `aimx` in the Python environment that runs `log_experiment`.
17
+ Verify with `aimx --help` or `python -m aimx --help` before collecting data.
18
+ - When `aimx` is missing, first identify and follow the user's current
19
+ dependency-management workflow from project files and instructions
20
+ (`pyproject.toml`, lockfiles, README, AGENTS/CLAUDE/GEMINI notes, CI config).
21
+ Use that manager to add or install `aimx`. In this repository, prefer
22
+ `uv sync`, `uv run ...`, and `uv add aimx` because repo policy requires `uv`.
23
+ - If there is no project environment to modify and the user only needs a
24
+ standalone CLI, install `aimx` as a tool using the user's preferred tool
25
+ manager. Use `uv tool install aimx` only when `uv` is already the chosen or
26
+ accepted tool workflow.
27
+ - Require read access to a local Aim repository path. Pass `--repo` explicitly
28
+ and keep collection commands read-only.
29
+ - If native Aim availability matters for passthrough checks, run `aimx doctor`;
30
+ do not initialize, repair, migrate, or rewrite Aim repositories from this
31
+ skill.
32
+
33
+ ## Workflow
34
+
35
+ 1. Locate the Aim repository. Pass `--repo <repo-root-or-.aim>` explicitly; in
36
+ this repository, use `--repo data` or `--repo data/.aim` for local checks.
37
+ 2. Define the run scope as an AimQL expression. Start broad with
38
+ `run.hash != ''`, then narrow by `run.hash`, `run.experiment`, or `run.name`.
39
+ 3. Collect run parameters:
40
+
41
+ ```bash
42
+ aimx query params "<run-scope>" --repo <repo> --json
43
+ aimx query params "<run-scope>" --repo <repo> --json --param hparam.lr --param model
44
+ ```
45
+
46
+ 4. Collect metric summaries for candidate objective metrics:
47
+
48
+ ```bash
49
+ aimx query metrics "(<run-scope>) and metric.name == 'loss'" --repo <repo> --json
50
+ aimx query metrics "(<run-scope>) and metric.name != ''" --repo <repo> --json
51
+ ```
52
+
53
+ 5. Collect traces only for decisive metrics or suspected anomalies:
54
+
55
+ ```bash
56
+ aimx trace "(<run-scope>) and metric.name == 'loss'" --repo <repo> --json --tail 50
57
+ ```
58
+
59
+ 6. Collect image metadata when qualitative outputs matter:
60
+
61
+ ```bash
62
+ aimx query images "images" --repo <repo> --json --head 20
63
+ ```
64
+
65
+ 7. Emit a compact `log_experiment` record containing:
66
+
67
+ ```json
68
+ {
69
+ "repo": "<repo>",
70
+ "run_scope": "<AimQL>",
71
+ "params": {},
72
+ "metric_summary": {},
73
+ "trace_evidence": {},
74
+ "image_evidence": {},
75
+ "interpretation": {
76
+ "best_runs": [],
77
+ "regressions": [],
78
+ "confidence": "low|medium|high",
79
+ "next_experiments": []
80
+ }
81
+ }
82
+ ```
83
+
84
+ ## Interpretation Rules
85
+
86
+ - Prefer validation, test, or held-out contexts over training contexts when
87
+ ranking runs.
88
+ - Treat `aimx query metrics` as summary data: `last`, `min`, `max`, and step
89
+ counts. Use `aimx trace --json` when shape, stability, divergence, or late
90
+ improvement matters.
91
+ - For minimization metrics such as loss or error, compare `min.value` and the
92
+ corresponding step. For maximization metrics such as accuracy, F1, AUC, or
93
+ IoU, compare `max.value`.
94
+ - Separate incomplete or failed runs from strong results before drawing
95
+ conclusions. Very low step counts, missing params, or missing validation
96
+ metrics should reduce confidence.
97
+ - Preserve read-only behavior. Do not run commands that initialize, repair,
98
+ migrate, delete, or rewrite Aim repositories during `log_experiment`.
99
+
100
+ ## Helper Script
101
+
102
+ Use `scripts/collect_experiment_snapshot.py` when an agent needs one structured
103
+ JSON bundle instead of several manual commands.
104
+
105
+ ```bash
106
+ uv run python skills/aimx/scripts/collect_experiment_snapshot.py \
107
+ --repo data \
108
+ --base-expr "run.experiment == 'cloud-segmentation'" \
109
+ --metric loss \
110
+ --trace-metric loss \
111
+ --param hparam.lr \
112
+ --param model \
113
+ --pretty
114
+ ```
115
+
116
+ The script uses the current Python interpreter as `python -m aimx` by default.
117
+ Pass `--aimx "aimx"` or `--aimx "uv run aimx"` when a different launcher is
118
+ needed. It writes only to stdout.
119
+
120
+ ## Reference
121
+
122
+ Read `references/aimx-cli.md` for command details, JSON envelope shapes, and
123
+ suggested `log_experiment` evidence fields.
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "Aimx"
3
+ short_description: "Collect Aim experiment evidence"
4
+ default_prompt: "Use $aimx to collect params, metrics, traces, and image evidence from an Aim repo before proposing the next experiment."
@@ -0,0 +1,233 @@
1
+ # aimx CLI Reference for log_experiment
2
+
3
+ Use this reference when building an `autoresearch` `log_experiment` step from a
4
+ local Aim repository. All commands below are read-only inspection commands.
5
+
6
+ ## Installation Requirement
7
+
8
+ `aimx` must be installed in the environment that runs the collection commands.
9
+ Verify one of these before collecting:
10
+
11
+ ```bash
12
+ aimx --help
13
+ python -m aimx --help
14
+ ```
15
+
16
+ If it is missing, inspect the current project first and use the dependency
17
+ manager it already uses. Check project instructions, `pyproject.toml`, lockfiles,
18
+ README setup docs, and CI config before choosing a command.
19
+
20
+ Examples:
21
+
22
+ ```bash
23
+ # uv-managed project
24
+ uv add aimx
25
+ uv sync
26
+
27
+ # Poetry-managed project
28
+ poetry add aimx
29
+
30
+ # Pipenv-managed project
31
+ pipenv install aimx
32
+
33
+ # requirements.txt project
34
+ python -m pip install aimx
35
+ python -m pip freeze > requirements.txt
36
+ ```
37
+
38
+ Use the project's normal lockfile/update flow. For this `aimx` repository,
39
+ prefer `uv sync`, `uv run ...`, and `uv add aimx` because repo policy requires
40
+ `uv`. For a standalone CLI with no project dependency to modify, use the user's
41
+ preferred tool manager; use `uv tool install aimx` only when `uv` is already the
42
+ chosen or accepted workflow.
43
+
44
+ ## Repository Paths
45
+
46
+ `--repo` accepts either the Aim repository root or the `.aim` metadata
47
+ directory. Prefer explicit repo paths so automation does not accidentally read a
48
+ user-level or unrelated Aim repository.
49
+
50
+ Examples:
51
+
52
+ ```bash
53
+ aimx query params "run.hash != ''" --repo data --json
54
+ aimx query params "run.hash != ''" --repo data/.aim --json
55
+ ```
56
+
57
+ ## AimQL Scoping
58
+
59
+ Use AimQL in the expression argument. Common run scopes:
60
+
61
+ ```text
62
+ run.hash != ''
63
+ run.hash == 'eca37394'
64
+ run.experiment == 'cloud-segmentation'
65
+ run.name == 'resnet-ft-0420'
66
+ ```
67
+
68
+ Metric expressions combine run fields with metric fields:
69
+
70
+ ```text
71
+ (run.experiment == 'cloud-segmentation') and metric.name == 'loss'
72
+ (run.hash == 'eca37394') and metric.name != ''
73
+ ```
74
+
75
+ Short run hashes are expanded by `aimx` where supported.
76
+
77
+ ## Params
78
+
79
+ Use params first to understand the design of each run.
80
+
81
+ ```bash
82
+ aimx query params "<run-scope>" --repo <repo> --json
83
+ aimx query params "<run-scope>" --repo <repo> --json --param hparam.lr --param model
84
+ ```
85
+
86
+ JSON shape:
87
+
88
+ ```json
89
+ {
90
+ "target": "params",
91
+ "repo": "data",
92
+ "expression": "run.hash != ''",
93
+ "runs_count": 2,
94
+ "param_keys": ["hparam.lr", "model"],
95
+ "runs": [
96
+ {
97
+ "hash": "full-run-hash",
98
+ "experiment": "experiment-name",
99
+ "name": "run-name",
100
+ "params": {"hparam.lr": 0.0001, "model": "ResNet"},
101
+ "missing_params": []
102
+ }
103
+ ]
104
+ }
105
+ ```
106
+
107
+ Use `missing_params` as a confidence signal when comparing runs.
108
+
109
+ ## Metric Summaries
110
+
111
+ Use metric summaries to rank runs cheaply.
112
+
113
+ ```bash
114
+ aimx query metrics "<metric-expr>" --repo <repo> --json
115
+ aimx query metrics "<metric-expr>" --repo <repo> --json --steps 100:500
116
+ aimx query metrics "<metric-expr>" --repo <repo> --json --epochs 1:10
117
+ ```
118
+
119
+ JSON shape:
120
+
121
+ ```json
122
+ {
123
+ "target": "metrics",
124
+ "repo": "data",
125
+ "expression": "metric.name == 'loss'",
126
+ "runs_count": 1,
127
+ "metrics_count": 2,
128
+ "runs": [
129
+ {
130
+ "hash": "full-run-hash",
131
+ "experiment": "experiment-name",
132
+ "name": "run-name",
133
+ "metrics": [
134
+ {
135
+ "name": "loss",
136
+ "context": {"subset": "val"},
137
+ "steps": 110,
138
+ "last": {"value": 0.43, "step": 110},
139
+ "min": {"value": 0.32, "step": 60},
140
+ "max": {"value": 0.48, "step": 107}
141
+ }
142
+ ]
143
+ }
144
+ ]
145
+ }
146
+ ```
147
+
148
+ Use `context` to distinguish train, val, test, dataset split, seed, or other
149
+ metric dimensions.
150
+
151
+ ## Traces
152
+
153
+ Use traces when a summary hides important behavior such as late overfitting,
154
+ instability, divergence, or plateauing.
155
+
156
+ ```bash
157
+ aimx trace "<metric-expr>" --repo <repo> --json
158
+ aimx trace "<metric-expr>" --repo <repo> --json --tail 50
159
+ aimx trace "<metric-expr>" --repo <repo> --json --steps 100:500 --every 5
160
+ ```
161
+
162
+ JSON shape:
163
+
164
+ ```json
165
+ [
166
+ {
167
+ "run": {
168
+ "hash": "full-run-hash",
169
+ "experiment": "experiment-name",
170
+ "name": "run-name"
171
+ },
172
+ "metric": "loss",
173
+ "context": {"subset": "val"},
174
+ "count": 50,
175
+ "steps": [1, 2, 3],
176
+ "epochs": [1.0, 2.0, 3.0],
177
+ "values": [0.9, 0.7, 0.5]
178
+ }
179
+ ]
180
+ ```
181
+
182
+ If no metrics match, current `aimx trace --json` may print a text message
183
+ instead of JSON. Treat that as no trace evidence rather than a parsing failure.
184
+
185
+ ## Images
186
+
187
+ Use images for qualitative checks such as sample predictions, masks, generated
188
+ outputs, confusion examples, or visual regressions.
189
+
190
+ ```bash
191
+ aimx query images "images" --repo <repo> --json
192
+ aimx query images "images" --repo <repo> --json --head 20
193
+ aimx query images "images" --repo <repo> --json --epochs 10:50
194
+ ```
195
+
196
+ JSON shape:
197
+
198
+ ```json
199
+ {
200
+ "target": "images",
201
+ "repo": "data",
202
+ "expression": "images",
203
+ "count": 1,
204
+ "rows": [
205
+ {
206
+ "run_hash": "full-run-hash",
207
+ "experiment": "experiment-name",
208
+ "name": "example",
209
+ "context": {"epoch": 10, "subset": "val"}
210
+ }
211
+ ]
212
+ }
213
+ ```
214
+
215
+ Use image metadata in automated logs; render images manually only when the user
216
+ asks for visual inspection.
217
+
218
+ ## log_experiment Evidence Fields
219
+
220
+ Recommended fields for autoresearch output:
221
+
222
+ - `run_scope`: AimQL expression and repo path used for evidence.
223
+ - `params`: selected hyperparameters and model identifiers per run.
224
+ - `metric_summary`: objective metric summaries per run and context.
225
+ - `trace_evidence`: sampled value arrays for decisive metrics.
226
+ - `image_evidence`: image row counts and representative contexts.
227
+ - `ranking`: best run per objective, objective direction, and tie-breakers.
228
+ - `regressions`: runs worse than baseline, incomplete runs, missing metrics, or
229
+ suspicious curves.
230
+ - `next_experiments`: concrete parameter changes grounded in the evidence.
231
+
232
+ Keep conclusions tied to the collected data. When metric direction is unknown,
233
+ state the assumption before ranking.
@@ -0,0 +1,217 @@
1
+ #!/usr/bin/env python3
2
+ """Collect a read-only aimx experiment snapshot for autoresearch logs."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ import json
8
+ import shlex
9
+ import subprocess
10
+ import sys
11
+ from dataclasses import dataclass
12
+ from typing import Any
13
+
14
+
15
+ @dataclass
16
+ class CommandPayload:
17
+ argv: list[str]
18
+ ok: bool
19
+ data: Any | None = None
20
+ message: str | None = None
21
+ error: str | None = None
22
+
23
+ def as_dict(self) -> dict[str, Any]:
24
+ payload: dict[str, Any] = {"argv": self.argv, "ok": self.ok}
25
+ if self.data is not None:
26
+ payload["data"] = self.data
27
+ if self.message:
28
+ payload["message"] = self.message
29
+ if self.error:
30
+ payload["error"] = self.error
31
+ return payload
32
+
33
+
34
+ def parse_args() -> argparse.Namespace:
35
+ parser = argparse.ArgumentParser(
36
+ description="Collect params, metric summaries, optional traces, and image metadata with aimx."
37
+ )
38
+ parser.add_argument("--repo", default=".", help="Aim repo root or .aim metadata path.")
39
+ parser.add_argument(
40
+ "--base-expr",
41
+ default="run.hash != ''",
42
+ help="AimQL run scope used for params and generated metric expressions.",
43
+ )
44
+ parser.add_argument(
45
+ "--metric",
46
+ action="append",
47
+ default=[],
48
+ help="Metric name to summarize; repeatable. Defaults to all metrics when no metric expression is supplied.",
49
+ )
50
+ parser.add_argument(
51
+ "--metric-expr",
52
+ action="append",
53
+ default=[],
54
+ help="Full AimQL metric expression to summarize; repeatable.",
55
+ )
56
+ parser.add_argument(
57
+ "--trace-metric",
58
+ action="append",
59
+ default=[],
60
+ help="Metric name to collect trace samples for; repeatable.",
61
+ )
62
+ parser.add_argument(
63
+ "--trace-expr",
64
+ action="append",
65
+ default=[],
66
+ help="Full AimQL trace expression; repeatable.",
67
+ )
68
+ parser.add_argument(
69
+ "--trace-tail",
70
+ type=int,
71
+ default=50,
72
+ help="Tail sample count for trace commands. Use 0 to disable tail sampling.",
73
+ )
74
+ parser.add_argument(
75
+ "--param",
76
+ action="append",
77
+ default=[],
78
+ help="Parameter key to select; repeatable. Omit to let aimx discover params.",
79
+ )
80
+ parser.add_argument(
81
+ "--include-images",
82
+ action="store_true",
83
+ help="Also collect image metadata with aimx query images.",
84
+ )
85
+ parser.add_argument(
86
+ "--image-expr",
87
+ default="images",
88
+ help="AimQL image expression used when --include-images is set.",
89
+ )
90
+ parser.add_argument(
91
+ "--image-head",
92
+ type=int,
93
+ default=20,
94
+ help="Limit image metadata rows when --include-images is set.",
95
+ )
96
+ parser.add_argument(
97
+ "--aimx",
98
+ default=f"{sys.executable} -m aimx",
99
+ help='Launcher for aimx, for example "aimx", "uv run aimx", or "python -m aimx".',
100
+ )
101
+ parser.add_argument("--timeout", type=int, default=60, help="Per-command timeout in seconds.")
102
+ parser.add_argument("--pretty", action="store_true", help="Pretty-print JSON output.")
103
+ return parser.parse_args()
104
+
105
+
106
+ def aimql_string(value: str) -> str:
107
+ return "'" + value.replace("\\", "\\\\").replace("'", "\\'") + "'"
108
+
109
+
110
+ def metric_expression(base_expr: str, metric_name: str) -> str:
111
+ return f"({base_expr}) and metric.name == {aimql_string(metric_name)}"
112
+
113
+
114
+ def run_json(base_cmd: list[str], args: list[str], timeout: int) -> CommandPayload:
115
+ argv = [*base_cmd, *args]
116
+ try:
117
+ completed = subprocess.run(
118
+ argv,
119
+ check=False,
120
+ capture_output=True,
121
+ text=True,
122
+ timeout=timeout,
123
+ )
124
+ except FileNotFoundError as exc:
125
+ return CommandPayload(argv=argv, ok=False, error=str(exc))
126
+ except subprocess.TimeoutExpired as exc:
127
+ return CommandPayload(argv=argv, ok=False, error=f"Timed out after {timeout}s: {exc}")
128
+
129
+ stdout = completed.stdout.strip()
130
+ stderr = completed.stderr.strip()
131
+ if completed.returncode != 0:
132
+ return CommandPayload(
133
+ argv=argv,
134
+ ok=False,
135
+ message=stdout or None,
136
+ error=stderr or f"Command exited with status {completed.returncode}",
137
+ )
138
+
139
+ if not stdout:
140
+ return CommandPayload(argv=argv, ok=True, data=None)
141
+
142
+ try:
143
+ return CommandPayload(argv=argv, ok=True, data=json.loads(stdout))
144
+ except json.JSONDecodeError:
145
+ return CommandPayload(argv=argv, ok=True, message=stdout)
146
+
147
+
148
+ def collect_snapshot(args: argparse.Namespace) -> tuple[dict[str, Any], bool]:
149
+ base_cmd = shlex.split(args.aimx)
150
+ metric_exprs = list(args.metric_expr)
151
+ metric_exprs.extend(metric_expression(args.base_expr, name) for name in args.metric)
152
+ if not metric_exprs:
153
+ metric_exprs.append(f"({args.base_expr}) and metric.name != ''")
154
+
155
+ trace_exprs = list(args.trace_expr)
156
+ trace_exprs.extend(metric_expression(args.base_expr, name) for name in args.trace_metric)
157
+
158
+ params_args = ["query", "params", args.base_expr, "--repo", args.repo, "--json"]
159
+ for key in args.param:
160
+ params_args.extend(["--param", key])
161
+
162
+ params = run_json(base_cmd, params_args, args.timeout)
163
+
164
+ metrics = [
165
+ run_json(base_cmd, ["query", "metrics", expr, "--repo", args.repo, "--json"], args.timeout)
166
+ for expr in metric_exprs
167
+ ]
168
+
169
+ trace_common = ["--repo", args.repo, "--json"]
170
+ if args.trace_tail > 0:
171
+ trace_common.extend(["--tail", str(args.trace_tail)])
172
+ traces = [
173
+ run_json(base_cmd, ["trace", expr, *trace_common], args.timeout)
174
+ for expr in trace_exprs
175
+ ]
176
+
177
+ images = None
178
+ if args.include_images:
179
+ image_args = [
180
+ "query",
181
+ "images",
182
+ args.image_expr,
183
+ "--repo",
184
+ args.repo,
185
+ "--json",
186
+ "--head",
187
+ str(args.image_head),
188
+ ]
189
+ images = run_json(base_cmd, image_args, args.timeout)
190
+
191
+ snapshot: dict[str, Any] = {
192
+ "repo": args.repo,
193
+ "base_expr": args.base_expr,
194
+ "read_only": True,
195
+ "params": params.as_dict(),
196
+ "metrics": [item.as_dict() for item in metrics],
197
+ "traces": [item.as_dict() for item in traces],
198
+ "images": images.as_dict() if images else None,
199
+ }
200
+
201
+ failures = [params, *metrics, *traces]
202
+ if images:
203
+ failures.append(images)
204
+ ok = all(item.ok for item in failures)
205
+ return snapshot, ok
206
+
207
+
208
+ def main() -> int:
209
+ args = parse_args()
210
+ snapshot, ok = collect_snapshot(args)
211
+ indent = 2 if args.pretty else None
212
+ print(json.dumps(snapshot, indent=indent, sort_keys=args.pretty))
213
+ return 0 if ok else 2
214
+
215
+
216
+ if __name__ == "__main__":
217
+ raise SystemExit(main())
@@ -137,6 +137,8 @@ celerybeat.pid
137
137
  # Environments
138
138
  .env
139
139
  .envrc
140
+ .env*
141
+ .venv/
140
142
  .venv
141
143
  env/
142
144
  venv/
@@ -188,6 +190,12 @@ cython_debug/
188
190
  # you could uncomment the following to ignore the entire vscode folder
189
191
  # .vscode/
190
192
 
193
+ # OS / editor
194
+ .DS_Store
195
+ Thumbs.db
196
+ *.tmp
197
+ *.swp
198
+
191
199
  # Ruff stuff:
192
200
  .ruff_cache/
193
201
 
@@ -210,4 +218,4 @@ __marimo__/
210
218
  .specify/
211
219
 
212
220
  # aim
213
- data/
221
+ data/
@@ -77,6 +77,10 @@ Why `3.12`:
77
77
  - Python 3.12 for development, runtime support `>=3.10,<3.13` + Python standard library, native Aim CLI (external runtime prerequisite for delegated commands), pytest for test automation (001-aim-command-passthrough)
78
78
  - Python 3.12 for development, runtime support `>=3.10,<3.13` + Python standard library, Aim SDK from the dev dependency group for local development and tests (001-aim-command-passthrough)
79
79
  - Existing local Aim repositories on disk, including repo roots that contain a `.aim` metadata directory (001-aim-command-passthrough)
80
+ - Python 3.12 for development, runtime support `>=3.10,<3.13` + `rich>=13.7`, `textual-image>=0.12.0` (already declared in (003-query-images-terminal-render)
81
+ - Existing local Aim repositories (read-only). Image bytes are read (003-query-images-terminal-render)
82
+ - Python 3.12 for development, runtime support `>=3.10,<3.13` + Python standard library, `numpy>=1.24`, `rich>=13.7`, `textual-image>=0.12.0`, existing Aim SDK usage for owned query commands; no new dependency planned (004-run-params-query)
83
+ - Existing local Aim repositories on disk (read-only); run params are read from Aim run metadata attributes under `.aim` (004-run-params-query)
80
84
 
81
85
  ## Recent Changes
82
86
  - 001-aim-command-passthrough: Added Python 3.12 for development, runtime support `>=3.10,<3.13` + Python standard library, native Aim CLI (external runtime prerequisite for delegated commands), pytest for test automation