jupytermind 0.3.0
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.
- package/.github/skills/ai-chemistry-scientist/SKILL.md +97 -0
- package/.github/skills/ai-chemistry-scientist/manifest.json +156 -0
- package/.github/skills/ai-data-scientist/SKILL.md +330 -0
- package/.github/skills/ai-genomics-scientist/SKILL.md +98 -0
- package/.github/skills/ai-genomics-scientist/manifest.json +93 -0
- package/.github/skills/ai-materials-scientist/SKILL.md +51 -0
- package/.github/skills/ai-materials-scientist/manifest.json +58 -0
- package/.github/skills/ai-scientist/SKILL.md +69 -0
- package/.github/skills/ai-scientist/manifest.json +61 -0
- package/.github/skills/ai-structural-biology-scientist/SKILL.md +67 -0
- package/.github/skills/ai-structural-biology-scientist/manifest.json +72 -0
- package/.github/skills/japanese-prose/NOTICE.md +17 -0
- package/.github/skills/japanese-prose/SKILL.md +111 -0
- package/.github/skills/japanese-prose/references/review-workflow.md +50 -0
- package/.github/skills/japanese-prose/references/scoring.md +24 -0
- package/.github/skills/japanese-prose/references/writing-guidelines.md +60 -0
- package/.github/skills/japanese-prose/scripts/core.py +192 -0
- package/.github/skills/japanese-prose/scripts/fixtures/natural.md +5 -0
- package/.github/skills/japanese-prose/scripts/fixtures/unnatural.md +5 -0
- package/.github/skills/japanese-prose/scripts/lint.py +378 -0
- package/.github/skills/japanese-prose/scripts/outline.py +68 -0
- package/.github/skills/japanese-prose/scripts/terms.py +112 -0
- package/.github/skills/japanese-prose/scripts/test_engine.py +117 -0
- package/.github/skills/presentation-planner/SKILL.md +257 -0
- package/.github/skills/presentation-planner/assets/design-templates/data-report.yaml +97 -0
- package/.github/skills/presentation-planner/assets/design-templates/executive-proposal.yaml +92 -0
- package/.github/skills/presentation-planner/assets/design-templates/technical-briefing.yaml +96 -0
- package/.github/skills/presentation-planner/assets/scenario-templates/data-report.md +47 -0
- package/.github/skills/presentation-planner/assets/scenario-templates/executive-decision.md +43 -0
- package/.github/skills/presentation-planner/assets/scenario-templates/technical-briefing.md +45 -0
- package/.github/skills/presentation-planner/references/customizing-design-templates.md +160 -0
- package/.github/skills/presentation-planner/references/design-spec-schema.md +72 -0
- package/.github/skills/presentation-planner/references/handoff-contract.md +49 -0
- package/.github/skills/presentation-planner/references/responsibility-boundary.md +32 -0
- package/.github/skills/presentation-planner/references/scenario-templates.md +55 -0
- package/.github/skills/tech-writer/SKILL.md +434 -0
- package/.github/skills/tech-writer/assets/templates/blueprint.md +187 -0
- package/.github/skills/tech-writer/assets/templates/design-doc.md +29 -0
- package/.github/skills/tech-writer/assets/templates/migration-plan.md +173 -0
- package/.github/skills/tech-writer/assets/templates/operations-runbook.md +202 -0
- package/.github/skills/tech-writer/assets/templates/pr-description.md +23 -0
- package/.github/skills/tech-writer/assets/templates/qiita.md +44 -0
- package/.github/skills/tech-writer/assets/templates/readme.md +38 -0
- package/.github/skills/tech-writer/assets/templates/requirements-definition.md +170 -0
- package/.github/skills/tech-writer/assets/templates/rfi.md +113 -0
- package/.github/skills/tech-writer/assets/templates/rfp.md +180 -0
- package/.github/skills/tech-writer/assets/templates/security-design.md +167 -0
- package/.github/skills/tech-writer/assets/templates/system-design.md +220 -0
- package/.github/skills/tech-writer/assets/templates/technical-proposal.md +112 -0
- package/.github/skills/tech-writer/assets/templates/test-plan.md +153 -0
- package/.github/skills/tech-writer/assets/templates/user-manual.md +22 -0
- package/.github/skills/tech-writer/assets/templates/white-paper.md +192 -0
- package/.github/skills/tech-writer/references/doctypes/api-docs.md +33 -0
- package/.github/skills/tech-writer/references/doctypes/blueprint.md +81 -0
- package/.github/skills/tech-writer/references/doctypes/code-comments.md +39 -0
- package/.github/skills/tech-writer/references/doctypes/design-doc.md +42 -0
- package/.github/skills/tech-writer/references/doctypes/migration-plan.md +63 -0
- package/.github/skills/tech-writer/references/doctypes/operations-runbook.md +63 -0
- package/.github/skills/tech-writer/references/doctypes/pr-commit.md +82 -0
- package/.github/skills/tech-writer/references/doctypes/qiita.md +75 -0
- package/.github/skills/tech-writer/references/doctypes/readme.md +43 -0
- package/.github/skills/tech-writer/references/doctypes/release-notes.md +30 -0
- package/.github/skills/tech-writer/references/doctypes/requirements-definition.md +61 -0
- package/.github/skills/tech-writer/references/doctypes/rfi.md +43 -0
- package/.github/skills/tech-writer/references/doctypes/rfp.md +46 -0
- package/.github/skills/tech-writer/references/doctypes/security-design.md +71 -0
- package/.github/skills/tech-writer/references/doctypes/system-design.md +74 -0
- package/.github/skills/tech-writer/references/doctypes/technical-proposal.md +49 -0
- package/.github/skills/tech-writer/references/doctypes/test-plan.md +67 -0
- package/.github/skills/tech-writer/references/doctypes/user-manual.md +58 -0
- package/.github/skills/tech-writer/references/doctypes/white-paper.md +84 -0
- package/.github/skills/tech-writer/references/doctypes/zenn.md +66 -0
- package/.github/skills/tech-writer/references/japanese-prose-optimization.md +110 -0
- package/.github/skills/tech-writer/references/style-constitution.md +104 -0
- package/.github/skills/tech-writer/scripts/lint.py +412 -0
- package/LICENSE +21 -0
- package/README.md +92 -0
- package/bin/ai-data-scientist.js +123 -0
- package/package.json +41 -0
- package/pyproject.toml +45 -0
- package/src/ai_chemistry_scientist/__init__.py +0 -0
- package/src/ai_chemistry_scientist/admet_prediction.py +71 -0
- package/src/ai_chemistry_scientist/bioactivity_classification.py +73 -0
- package/src/ai_chemistry_scientist/data/sample_molecules.csv +21 -0
- package/src/ai_chemistry_scientist/dispatch.py +369 -0
- package/src/ai_chemistry_scientist/docking_score.py +97 -0
- package/src/ai_chemistry_scientist/drug_likeness_rules.py +84 -0
- package/src/ai_chemistry_scientist/evidence.py +41 -0
- package/src/ai_chemistry_scientist/molecular_descriptors.py +97 -0
- package/src/ai_chemistry_scientist/molecular_formula_mass.py +40 -0
- package/src/ai_chemistry_scientist/molecular_similarity.py +78 -0
- package/src/ai_chemistry_scientist/qsar_modeling.py +105 -0
- package/src/ai_chemistry_scientist/salt_standardization.py +81 -0
- package/src/ai_chemistry_scientist/structural_alerts.py +76 -0
- package/src/ai_chemistry_scientist/structure_format_conversion.py +84 -0
- package/src/ai_chemistry_scientist/validation.py +70 -0
- package/src/ai_data_scientist/__init__.py +0 -0
- package/src/ai_data_scientist/analysis_assumptions.py +121 -0
- package/src/ai_data_scientist/anomaly_detection.py +39 -0
- package/src/ai_data_scientist/automl.py +109 -0
- package/src/ai_data_scientist/cleaning.py +56 -0
- package/src/ai_data_scientist/cli.py +90 -0
- package/src/ai_data_scientist/clustering.py +54 -0
- package/src/ai_data_scientist/dashboard.py +33 -0
- package/src/ai_data_scientist/data_definition.py +100 -0
- package/src/ai_data_scientist/data_quality.py +164 -0
- package/src/ai_data_scientist/dataset_validation.py +135 -0
- package/src/ai_data_scientist/dependency_pins.py +60 -0
- package/src/ai_data_scientist/eda.py +82 -0
- package/src/ai_data_scientist/experiment_evaluation.py +635 -0
- package/src/ai_data_scientist/explainability.py +340 -0
- package/src/ai_data_scientist/feature_engineering.py +163 -0
- package/src/ai_data_scientist/gate_config.py +32 -0
- package/src/ai_data_scientist/ingestion.py +127 -0
- package/src/ai_data_scientist/insight_engine.py +180 -0
- package/src/ai_data_scientist/japanese_nlp.py +43 -0
- package/src/ai_data_scientist/jupyter_launcher.py +137 -0
- package/src/ai_data_scientist/jupyter_mcp_client.py +94 -0
- package/src/ai_data_scientist/language_router.py +28 -0
- package/src/ai_data_scientist/lifecycle.py +221 -0
- package/src/ai_data_scientist/mcp_gateway.py +113 -0
- package/src/ai_data_scientist/mcp_runtime.py +194 -0
- package/src/ai_data_scientist/mcp_transport.py +53 -0
- package/src/ai_data_scientist/ml_modeling.py +451 -0
- package/src/ai_data_scientist/model_tuning.py +104 -0
- package/src/ai_data_scientist/notebook_audit.py +574 -0
- package/src/ai_data_scientist/project_manager.py +243 -0
- package/src/ai_data_scientist/report_export.py +73 -0
- package/src/ai_data_scientist/sensitivity.py +445 -0
- package/src/ai_data_scientist/signal_analysis.py +201 -0
- package/src/ai_data_scientist/skill_packaging.py +40 -0
- package/src/ai_data_scientist/stats_analysis.py +88 -0
- package/src/ai_data_scientist/text_nlp.py +44 -0
- package/src/ai_data_scientist/timeseries.py +68 -0
- package/src/ai_data_scientist/visualization.py +708 -0
- package/src/ai_genomics_scientist/__init__.py +1 -0
- package/src/ai_genomics_scientist/differential_expression.py +147 -0
- package/src/ai_genomics_scientist/dispatch.py +267 -0
- package/src/ai_genomics_scientist/evidence.py +45 -0
- package/src/ai_genomics_scientist/gene_set_enrichment.py +76 -0
- package/src/ai_genomics_scientist/sequence_alignment.py +97 -0
- package/src/ai_genomics_scientist/sequence_features.py +111 -0
- package/src/ai_genomics_scientist/splice_site_scoring.py +66 -0
- package/src/ai_genomics_scientist/validation.py +83 -0
- package/src/ai_genomics_scientist/variant_effect.py +147 -0
- package/src/ai_genomics_scientist/variant_pathogenicity.py +125 -0
- package/src/ai_materials_scientist/__init__.py +0 -0
- package/src/ai_materials_scientist/calphad.py +117 -0
- package/src/ai_materials_scientist/classical_monte_carlo.py +165 -0
- package/src/ai_materials_scientist/crystal_plasticity.py +184 -0
- package/src/ai_materials_scientist/dispatch.py +100 -0
- package/src/ai_materials_scientist/evidence.py +84 -0
- package/src/ai_materials_scientist/fem.py +279 -0
- package/src/ai_materials_scientist/kinetic_monte_carlo.py +145 -0
- package/src/ai_materials_scientist/molecular_dynamics.py +240 -0
- package/src/ai_materials_scientist/phase_field.py +167 -0
- package/src/ai_materials_scientist/validation.py +70 -0
- package/src/ai_scientist/__init__.py +1 -0
- package/src/ai_scientist/completion_gate.py +15 -0
- package/src/ai_scientist/data_analysis.py +46 -0
- package/src/ai_scientist/evidence_registry.py +99 -0
- package/src/ai_scientist/experimental_design.py +20 -0
- package/src/ai_scientist/language.py +14 -0
- package/src/ai_scientist/latex_renderer.py +41 -0
- package/src/ai_scientist/literature_review.py +37 -0
- package/src/ai_scientist/manifest.py +87 -0
- package/src/ai_scientist/manuscript.py +94 -0
- package/src/ai_scientist/mcp_config.py +76 -0
- package/src/ai_scientist/mcp_external.py +42 -0
- package/src/ai_scientist/mcp_failures.py +23 -0
- package/src/ai_scientist/mcp_gateway.py +38 -0
- package/src/ai_scientist/mcp_managed.py +180 -0
- package/src/ai_scientist/npm_packaging.py +49 -0
- package/src/ai_scientist/orchestrator.py +133 -0
- package/src/ai_scientist/peer_review.py +60 -0
- package/src/ai_scientist/phase_gate.py +74 -0
- package/src/ai_scientist/phase_state.py +230 -0
- package/src/ai_scientist/presentation.py +56 -0
- package/src/ai_scientist/project_config.py +31 -0
- package/src/ai_scientist/project_handle.py +74 -0
- package/src/ai_scientist/reproducibility.py +20 -0
- package/src/ai_scientist/research_planning.py +20 -0
- package/src/ai_scientist/skill_invocation.py +21 -0
- package/src/ai_scientist/tdd_gate.py +99 -0
- package/src/ai_structural_biology_scientist/__init__.py +0 -0
- package/src/ai_structural_biology_scientist/contact_map.py +87 -0
- package/src/ai_structural_biology_scientist/dispatch.py +269 -0
- package/src/ai_structural_biology_scientist/evidence.py +43 -0
- package/src/ai_structural_biology_scientist/hydrophobicity.py +101 -0
- package/src/ai_structural_biology_scientist/protein_docking_score.py +104 -0
- package/src/ai_structural_biology_scientist/secondary_structure.py +95 -0
- package/src/ai_structural_biology_scientist/structural_similarity.py +74 -0
- package/src/ai_structural_biology_scientist/validation.py +100 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""Phase-field microstructure evolution module (DES-AIMS-010 / REQ-AIMS-010)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
from ai_materials_scientist.evidence import record_run
|
|
8
|
+
from ai_materials_scientist.validation import (
|
|
9
|
+
check_finite_array,
|
|
10
|
+
check_positive_output_interval,
|
|
11
|
+
check_positive_step_count,
|
|
12
|
+
register_validator,
|
|
13
|
+
validate_parameters,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
_MODULE_NAME = "phase-field"
|
|
17
|
+
_MODELS = ("allen-cahn", "cahn-hilliard")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _laplacian(c: np.ndarray, dx: float) -> np.ndarray:
|
|
21
|
+
"""Periodic five-point discrete Laplacian L(c) (REQ-AIMS-010)."""
|
|
22
|
+
return (
|
|
23
|
+
np.roll(c, -1, axis=0)
|
|
24
|
+
+ np.roll(c, 1, axis=0)
|
|
25
|
+
+ np.roll(c, -1, axis=1)
|
|
26
|
+
+ np.roll(c, 1, axis=1)
|
|
27
|
+
- 4 * c
|
|
28
|
+
) / (dx**2)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _f_prime(c: np.ndarray) -> np.ndarray:
|
|
32
|
+
"""Derivative of f(c) = c^2 * (1-c)^2."""
|
|
33
|
+
return 2 * c * (1 - c) ** 2 - 2 * (c**2) * (1 - c)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# @id CODE-AIMS-010
|
|
37
|
+
# @implements REQ-AIMS-010
|
|
38
|
+
# @design DES-AIMS-010
|
|
39
|
+
def compute_dt_bound(model: str, M: float, kappa: float, dx: float) -> float:
|
|
40
|
+
"""Return the model-specific explicit stability time-step bound."""
|
|
41
|
+
lambda_max = 8.0 / (dx**2)
|
|
42
|
+
if model == "allen-cahn":
|
|
43
|
+
return 2.0 / (M * (2 + kappa * lambda_max))
|
|
44
|
+
if model == "cahn-hilliard":
|
|
45
|
+
return 2.0 / (M * lambda_max * (2 + kappa * lambda_max))
|
|
46
|
+
raise ValueError(f"model must be one of {_MODELS}, got {model!r}")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _phase_field_validator(params: dict) -> dict:
|
|
50
|
+
"""DES-AIMS-002 registered validator for module_name='phase-field'."""
|
|
51
|
+
model = params["model"]
|
|
52
|
+
M = params["M"]
|
|
53
|
+
kappa = params["kappa"]
|
|
54
|
+
dx = params["dx"]
|
|
55
|
+
dt = params["dt"]
|
|
56
|
+
|
|
57
|
+
if model not in _MODELS:
|
|
58
|
+
return {"ok": False, "parameter": "model", "constraint": f"model in {_MODELS}"}
|
|
59
|
+
if M <= 0:
|
|
60
|
+
return {"ok": False, "parameter": "M", "constraint": "M > 0"}
|
|
61
|
+
if kappa < 0:
|
|
62
|
+
return {"ok": False, "parameter": "kappa", "constraint": "kappa >= 0"}
|
|
63
|
+
if dx <= 0:
|
|
64
|
+
return {"ok": False, "parameter": "dx", "constraint": "dx > 0"}
|
|
65
|
+
if dt <= 0:
|
|
66
|
+
return {"ok": False, "parameter": "dt", "constraint": "dt > 0"}
|
|
67
|
+
|
|
68
|
+
finite_check = check_finite_array("field0", params["field0"])
|
|
69
|
+
if not finite_check["ok"]:
|
|
70
|
+
return finite_check
|
|
71
|
+
steps_check = check_positive_step_count(params["steps"])
|
|
72
|
+
if not steps_check["ok"]:
|
|
73
|
+
return steps_check
|
|
74
|
+
interval_check = check_positive_output_interval(params["output_every"])
|
|
75
|
+
if not interval_check["ok"]:
|
|
76
|
+
return interval_check
|
|
77
|
+
|
|
78
|
+
dt_bound = compute_dt_bound(model, M, kappa, dx)
|
|
79
|
+
if dt > dt_bound:
|
|
80
|
+
return {
|
|
81
|
+
"parameter": "dt",
|
|
82
|
+
"ok": False,
|
|
83
|
+
"constraint": f"dt <= dt_bound ({dt_bound}) for model {model!r}",
|
|
84
|
+
}
|
|
85
|
+
return {"ok": True}
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
register_validator(_MODULE_NAME, _phase_field_validator)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _validate(field0, model, dx, M, kappa, dt, steps, output_every) -> None:
|
|
92
|
+
"""Route through the shared DES-AIMS-002 registry (REQ-AIMS-003)."""
|
|
93
|
+
result = validate_parameters(
|
|
94
|
+
_MODULE_NAME,
|
|
95
|
+
{
|
|
96
|
+
"field0": field0,
|
|
97
|
+
"model": model,
|
|
98
|
+
"dx": dx,
|
|
99
|
+
"M": M,
|
|
100
|
+
"kappa": kappa,
|
|
101
|
+
"dt": dt,
|
|
102
|
+
"steps": steps,
|
|
103
|
+
"output_every": output_every,
|
|
104
|
+
},
|
|
105
|
+
)
|
|
106
|
+
if not result["ok"]:
|
|
107
|
+
raise ValueError(f"{result['parameter']}: {result['constraint']}")
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
# @id CODE-AIMS-911
|
|
111
|
+
# @implements REQ-AIMS-010 REQ-AIMS-003
|
|
112
|
+
# @design DES-AIMS-010
|
|
113
|
+
def run_phase_field(
|
|
114
|
+
field0: np.ndarray,
|
|
115
|
+
model: str,
|
|
116
|
+
dx: float,
|
|
117
|
+
M: float,
|
|
118
|
+
kappa: float,
|
|
119
|
+
dt: float,
|
|
120
|
+
steps: int,
|
|
121
|
+
output_every: int,
|
|
122
|
+
) -> dict:
|
|
123
|
+
"""Integrate the Allen-Cahn or Cahn-Hilliard PDE and report snapshots.
|
|
124
|
+
|
|
125
|
+
Rejects any out-of-domain parameter (REQ-AIMS-003) before performing any
|
|
126
|
+
integration step or mutating ``field0``.
|
|
127
|
+
"""
|
|
128
|
+
_validate(field0, model, dx, M, kappa, dt, steps, output_every)
|
|
129
|
+
|
|
130
|
+
c = field0.astype(np.float64).copy()
|
|
131
|
+
snapshots = [c.copy()]
|
|
132
|
+
times = [0.0]
|
|
133
|
+
|
|
134
|
+
for step in range(1, steps + 1):
|
|
135
|
+
mu = _f_prime(c) - kappa * _laplacian(c, dx)
|
|
136
|
+
if model == "allen-cahn":
|
|
137
|
+
c = c - dt * M * mu
|
|
138
|
+
else: # cahn-hilliard
|
|
139
|
+
c = c + dt * M * _laplacian(mu, dx)
|
|
140
|
+
if step % output_every == 0:
|
|
141
|
+
snapshots.append(c.copy())
|
|
142
|
+
times.append(step * dt)
|
|
143
|
+
|
|
144
|
+
dt_bound = compute_dt_bound(model, M, kappa, dx)
|
|
145
|
+
return {
|
|
146
|
+
"snapshots": snapshots,
|
|
147
|
+
"times": times,
|
|
148
|
+
"dt_bound": dt_bound,
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
# @id CODE-AIMS-912
|
|
153
|
+
# @implements REQ-AIMS-010 REQ-AIMS-004 REQ-AIMS-005
|
|
154
|
+
# @design DES-AIMS-010
|
|
155
|
+
def run_phase_field_with_evidence(**kwargs) -> dict:
|
|
156
|
+
"""Run phase-field and wrap the result as a reproducible RunRecord."""
|
|
157
|
+
result = run_phase_field(**kwargs)
|
|
158
|
+
return record_run(
|
|
159
|
+
module_name="phase-field",
|
|
160
|
+
unit_system="dimensionless-order-parameter",
|
|
161
|
+
params={k: v for k, v in kwargs.items() if k != "field0"},
|
|
162
|
+
arrays={
|
|
163
|
+
"snapshots": np.stack(result["snapshots"]),
|
|
164
|
+
"times": np.array(result["times"], dtype=np.float64),
|
|
165
|
+
},
|
|
166
|
+
seed=None,
|
|
167
|
+
)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""Shared parameter & stability validator (DES-AIMS-002 / REQ-AIMS-003)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
|
|
10
|
+
ValidationResult = dict
|
|
11
|
+
ValidatorFn = Callable[[dict[str, Any]], ValidationResult]
|
|
12
|
+
|
|
13
|
+
_REGISTRY: dict[str, ValidatorFn] = {}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _ok() -> ValidationResult:
|
|
17
|
+
return {"ok": True}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _fail(parameter: str, constraint: str) -> ValidationResult:
|
|
21
|
+
return {"ok": False, "parameter": parameter, "constraint": constraint}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# @id CODE-AIMS-002
|
|
25
|
+
# @implements REQ-AIMS-003
|
|
26
|
+
# @design DES-AIMS-002
|
|
27
|
+
def check_finite_array(parameter: str, array: np.ndarray) -> ValidationResult:
|
|
28
|
+
"""Reject a numeric array containing any non-finite (NaN/Inf) value."""
|
|
29
|
+
if not np.all(np.isfinite(array)):
|
|
30
|
+
return _fail(parameter, "all elements must be finite (no NaN/Inf)")
|
|
31
|
+
return _ok()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# @id CODE-AIMS-913
|
|
35
|
+
# @implements REQ-AIMS-003
|
|
36
|
+
# @design DES-AIMS-002
|
|
37
|
+
def check_positive_step_count(steps: int) -> ValidationResult:
|
|
38
|
+
"""Reject a step count that is not a positive integer."""
|
|
39
|
+
if steps <= 0:
|
|
40
|
+
return _fail("steps", "steps > 0")
|
|
41
|
+
return _ok()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# @id CODE-AIMS-914
|
|
45
|
+
# @implements REQ-AIMS-003
|
|
46
|
+
# @design DES-AIMS-002
|
|
47
|
+
def check_positive_output_interval(output_every: int) -> ValidationResult:
|
|
48
|
+
"""Reject an output interval that is not a positive integer."""
|
|
49
|
+
if output_every <= 0:
|
|
50
|
+
return _fail("output_every", "output_every > 0")
|
|
51
|
+
return _ok()
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# @id CODE-AIMS-915
|
|
55
|
+
# @implements REQ-AIMS-003
|
|
56
|
+
# @design DES-AIMS-002
|
|
57
|
+
def register_validator(module_name: str, validator: ValidatorFn) -> None:
|
|
58
|
+
"""Register ``module_name``'s own documented-domain validator function."""
|
|
59
|
+
_REGISTRY[module_name] = validator
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
# @id CODE-AIMS-916
|
|
63
|
+
# @implements REQ-AIMS-003
|
|
64
|
+
# @design DES-AIMS-002
|
|
65
|
+
def validate_parameters(module_name: str, params: dict[str, Any]) -> ValidationResult:
|
|
66
|
+
"""Dispatch to ``module_name``'s registered validator before any simulation step."""
|
|
67
|
+
validator = _REGISTRY.get(module_name)
|
|
68
|
+
if validator is None:
|
|
69
|
+
return _fail("module", f"no validator registered for module '{module_name}'")
|
|
70
|
+
return validator(params)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""AI Scientist research workflow package."""
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Evidence-based completion validation."""
|
|
2
|
+
|
|
3
|
+
from ai_scientist.evidence_registry import query_evidence
|
|
4
|
+
from ai_scientist.project_handle import ResearchProjectHandle
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# @id CODE-AISCI-021
|
|
8
|
+
# @implements REQ-AISCI-020
|
|
9
|
+
# @design DES-AISCI-017
|
|
10
|
+
def validate_completion_evidence(handle: ResearchProjectHandle, phase: str) -> bool:
|
|
11
|
+
"""Require at least one correctly attributed evidence record for ``phase``."""
|
|
12
|
+
return any(
|
|
13
|
+
record.project == handle.name and record.phase == phase and bool(record.created_at)
|
|
14
|
+
for record in query_evidence(handle, phase)
|
|
15
|
+
)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Data-analysis delegation to ai-data-scientist."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from datetime import datetime, timezone
|
|
6
|
+
|
|
7
|
+
import ai_data_scientist.project_manager as aids_project_manager
|
|
8
|
+
from ai_data_scientist.mcp_gateway import run_and_record
|
|
9
|
+
|
|
10
|
+
from ai_scientist.evidence_registry import record_evidence
|
|
11
|
+
from ai_scientist.project_handle import ResearchProjectHandle
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _now() -> str:
|
|
15
|
+
return datetime.now(timezone.utc).isoformat()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# @id CODE-AISCI-009
|
|
19
|
+
# @implements REQ-AISCI-008
|
|
20
|
+
# @design DES-AISCI-005
|
|
21
|
+
def delegate_data_analysis(
|
|
22
|
+
handle: ResearchProjectHandle,
|
|
23
|
+
instruction: str,
|
|
24
|
+
mcp_client=None,
|
|
25
|
+
) -> dict:
|
|
26
|
+
"""Delegate notebook setup and optional execution to ai-data-scientist."""
|
|
27
|
+
if mcp_client is None:
|
|
28
|
+
raise ValueError("Data-analysis requires an MCP client for ai-data-scientist execution.")
|
|
29
|
+
aids_handle = aids_project_manager.resolve_project(handle.name)
|
|
30
|
+
if aids_handle.root != handle.root or aids_handle.notebook_path != handle.notebook_path:
|
|
31
|
+
raise ValueError("ai-data-scientist handle diverged from ai-scientist project handle.")
|
|
32
|
+
notebook_path = aids_project_manager.ensure_notebook(aids_handle)
|
|
33
|
+
execution = run_and_record(mcp_client, aids_handle, instruction)
|
|
34
|
+
record_evidence(
|
|
35
|
+
handle,
|
|
36
|
+
"data-analysis",
|
|
37
|
+
notebook_path,
|
|
38
|
+
"notebook",
|
|
39
|
+
_now(),
|
|
40
|
+
metadata={"executionRoutedVia": "ai_data_scientist.mcp_gateway"},
|
|
41
|
+
)
|
|
42
|
+
return {
|
|
43
|
+
"phase": "data-analysis",
|
|
44
|
+
"notebook_path": str(notebook_path),
|
|
45
|
+
"execution": execution,
|
|
46
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""Durable evidence registry for ai_scientist phases."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import threading
|
|
7
|
+
from dataclasses import asdict, dataclass, field
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from ai_scientist.project_handle import ResearchProjectHandle
|
|
11
|
+
|
|
12
|
+
DEFAULT_EVIDENCE_PATH = ".ai_scientist_evidence.json"
|
|
13
|
+
_REGISTRY_LOCKS: dict[Path, threading.Lock] = {}
|
|
14
|
+
_REGISTRY_LOCKS_GUARD = threading.Lock()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True)
|
|
18
|
+
class EvidenceRecord:
|
|
19
|
+
"""Recorded project evidence with phase attribution."""
|
|
20
|
+
|
|
21
|
+
project: str
|
|
22
|
+
phase: str
|
|
23
|
+
artifact_path: str
|
|
24
|
+
artifact_kind: str
|
|
25
|
+
created_at: str
|
|
26
|
+
metadata: dict[str, str] = field(default_factory=dict)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _registry_path(handle: ResearchProjectHandle) -> Path:
|
|
30
|
+
return handle.root / DEFAULT_EVIDENCE_PATH
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _lock_for(path: Path) -> threading.Lock:
|
|
34
|
+
with _REGISTRY_LOCKS_GUARD:
|
|
35
|
+
lock = _REGISTRY_LOCKS.get(path)
|
|
36
|
+
if lock is None:
|
|
37
|
+
lock = threading.Lock()
|
|
38
|
+
_REGISTRY_LOCKS[path] = lock
|
|
39
|
+
return lock
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _read_records(handle: ResearchProjectHandle) -> list[EvidenceRecord]:
|
|
43
|
+
path = _registry_path(handle)
|
|
44
|
+
if not path.exists():
|
|
45
|
+
return []
|
|
46
|
+
payload = json.loads(path.read_text(encoding="utf-8"))
|
|
47
|
+
return [EvidenceRecord(**item) for item in payload]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _write_records(handle: ResearchProjectHandle, records: list[EvidenceRecord]) -> None:
|
|
51
|
+
path = _registry_path(handle)
|
|
52
|
+
temp_path = path.with_suffix(".tmp")
|
|
53
|
+
temp_path.write_text(
|
|
54
|
+
json.dumps([asdict(record) for record in records], indent=2, ensure_ascii=False),
|
|
55
|
+
encoding="utf-8",
|
|
56
|
+
)
|
|
57
|
+
temp_path.replace(path)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# @id CODE-AISCI-020
|
|
61
|
+
# @implements REQ-AISCI-020
|
|
62
|
+
# @design DES-AISCI-016
|
|
63
|
+
def record_evidence(
|
|
64
|
+
handle: ResearchProjectHandle,
|
|
65
|
+
phase: str,
|
|
66
|
+
artifact_path: Path | str,
|
|
67
|
+
artifact_kind: str,
|
|
68
|
+
timestamp: str,
|
|
69
|
+
metadata: dict[str, str] | None = None,
|
|
70
|
+
) -> EvidenceRecord:
|
|
71
|
+
"""Append one evidence record atomically."""
|
|
72
|
+
path = _registry_path(handle)
|
|
73
|
+
with _lock_for(path):
|
|
74
|
+
records = _read_records(handle)
|
|
75
|
+
record = EvidenceRecord(
|
|
76
|
+
project=handle.name,
|
|
77
|
+
phase=phase,
|
|
78
|
+
artifact_path=str(Path(artifact_path)),
|
|
79
|
+
artifact_kind=artifact_kind,
|
|
80
|
+
created_at=timestamp,
|
|
81
|
+
metadata=metadata or {},
|
|
82
|
+
)
|
|
83
|
+
records.append(record)
|
|
84
|
+
_write_records(handle, records)
|
|
85
|
+
return record
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def query_evidence(handle: ResearchProjectHandle, phase: str | None = None) -> list[EvidenceRecord]:
|
|
89
|
+
"""Return all evidence, optionally filtered to one phase."""
|
|
90
|
+
records = _read_records(handle)
|
|
91
|
+
if phase is None:
|
|
92
|
+
return records
|
|
93
|
+
return [record for record in records if record.phase == phase]
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def latest_evidence(handle: ResearchProjectHandle, phase: str) -> EvidenceRecord | None:
|
|
97
|
+
"""Return the most recently recorded evidence for ``phase``."""
|
|
98
|
+
records = query_evidence(handle, phase)
|
|
99
|
+
return records[-1] if records else None
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Experimental design phase handler."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from datetime import datetime, timezone
|
|
6
|
+
|
|
7
|
+
from ai_scientist.evidence_registry import record_evidence
|
|
8
|
+
from ai_scientist.project_handle import ResearchProjectHandle
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _now() -> str:
|
|
12
|
+
return datetime.now(timezone.utc).isoformat()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def handle_experimental_design(handle: ResearchProjectHandle, instruction: str) -> dict:
|
|
16
|
+
"""Create an experimental design artifact and record it."""
|
|
17
|
+
artifact = handle.design_dir / "experimental-design.md"
|
|
18
|
+
artifact.write_text(instruction, encoding="utf-8")
|
|
19
|
+
record_evidence(handle, "experimental-design", artifact, "markdown", _now())
|
|
20
|
+
return {"phase": "experimental-design", "artifact_path": str(artifact)}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Language detection for ai_scientist responses and metadata."""
|
|
2
|
+
|
|
3
|
+
from ai_data_scientist.language_router import detect_language as _detect_language
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# @id CODE-AISCI-001
|
|
7
|
+
# @implements REQ-AISCI-001
|
|
8
|
+
# @design DES-AISCI-001
|
|
9
|
+
# @id CODE-AISCI-010
|
|
10
|
+
# @implements REQ-AISCI-010
|
|
11
|
+
# @design DES-AISCI-007
|
|
12
|
+
def detect_language(text: str) -> str:
|
|
13
|
+
"""Reuse ai-data-scientist's deterministic language detector."""
|
|
14
|
+
return _detect_language(text)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""LaTeX rendering for tech-writer Markdown output."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _escape_latex(text: str) -> str:
|
|
9
|
+
replacements = {
|
|
10
|
+
"\\": r"\textbackslash{}",
|
|
11
|
+
"&": r"\&",
|
|
12
|
+
"%": r"\%",
|
|
13
|
+
"$": r"\$",
|
|
14
|
+
"#": r"\#",
|
|
15
|
+
"_": r"\_",
|
|
16
|
+
"{": r"\{",
|
|
17
|
+
"}": r"\}",
|
|
18
|
+
"~": r"\textasciitilde{}",
|
|
19
|
+
"^": r"\textasciicircum{}",
|
|
20
|
+
}
|
|
21
|
+
return "".join(replacements.get(character, character) for character in text)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# @id CODE-AISCI-022
|
|
25
|
+
# @implements REQ-AISCI-022
|
|
26
|
+
# @design DES-AISCI-018
|
|
27
|
+
def render_latex(markdown_path: Path) -> str:
|
|
28
|
+
"""Render a small LaTeX document while preserving section order/content."""
|
|
29
|
+
body_lines: list[str] = []
|
|
30
|
+
for raw_line in markdown_path.read_text(encoding="utf-8").splitlines():
|
|
31
|
+
line = raw_line.strip()
|
|
32
|
+
if not line:
|
|
33
|
+
body_lines.append("")
|
|
34
|
+
elif line.startswith("# "):
|
|
35
|
+
body_lines.append(f"\\section{{{_escape_latex(line[2:])}}}")
|
|
36
|
+
elif line.startswith("## "):
|
|
37
|
+
body_lines.append(f"\\subsection{{{_escape_latex(line[3:])}}}")
|
|
38
|
+
else:
|
|
39
|
+
body_lines.append(_escape_latex(line))
|
|
40
|
+
content = "\n".join(body_lines).strip()
|
|
41
|
+
return f"\\documentclass{{article}}\n\\begin{{document}}\n{content}\n\\end{{document}}\n"
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Literature review phase handler."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from datetime import datetime, timezone
|
|
6
|
+
|
|
7
|
+
from ai_scientist.evidence_registry import record_evidence
|
|
8
|
+
from ai_scientist.mcp_gateway import McpGateway
|
|
9
|
+
from ai_scientist.project_handle import ResearchProjectHandle
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _now() -> str:
|
|
13
|
+
return datetime.now(timezone.utc).isoformat()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def handle_literature_review(
|
|
17
|
+
handle: ResearchProjectHandle,
|
|
18
|
+
instruction: str,
|
|
19
|
+
gateway: McpGateway | None = None,
|
|
20
|
+
server_name: str | None = None,
|
|
21
|
+
tool_name: str = "literature-search",
|
|
22
|
+
tool_args: dict | None = None,
|
|
23
|
+
) -> dict:
|
|
24
|
+
"""Write a literature artifact, routing external lookups only through MCP."""
|
|
25
|
+
artifact = handle.literature_dir / "literature-review.md"
|
|
26
|
+
content = instruction
|
|
27
|
+
if gateway is not None and server_name is not None:
|
|
28
|
+
tool_result = gateway.call_tool(
|
|
29
|
+
server_name,
|
|
30
|
+
tool_name,
|
|
31
|
+
tool_args or {"query": instruction},
|
|
32
|
+
phase="literature-review",
|
|
33
|
+
)
|
|
34
|
+
content = str(tool_result.get("content", ""))
|
|
35
|
+
artifact.write_text(content, encoding="utf-8")
|
|
36
|
+
record_evidence(handle, "literature-review", artifact, "markdown", _now())
|
|
37
|
+
return {"phase": "literature-review", "artifact_path": str(artifact)}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""Phase manifest loading and repo-local skill registry verification."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib
|
|
6
|
+
import json
|
|
7
|
+
import re
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
REPO_ROOT = Path(__file__).resolve().parents[2]
|
|
11
|
+
DEFAULT_SKILL_DIR = REPO_ROOT / ".github" / "skills" / "ai-scientist"
|
|
12
|
+
DEFAULT_MANIFEST_PATH = DEFAULT_SKILL_DIR / "manifest.json"
|
|
13
|
+
VENDORED_PATH = REPO_ROOT / ".github" / "skills" / "VENDORED.md"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ManifestVerificationError(ValueError):
|
|
17
|
+
"""Manifest verification failed."""
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _import_handler(module_path: str, function_name: str) -> None:
|
|
21
|
+
module = importlib.import_module(module_path)
|
|
22
|
+
handler = getattr(module, function_name)
|
|
23
|
+
if not callable(handler):
|
|
24
|
+
raise ManifestVerificationError(f"{module_path}.{function_name} is not callable")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _validate_skill_dependencies(phase: str, payload):
|
|
28
|
+
if phase == "peer-review":
|
|
29
|
+
if not isinstance(payload, dict) or set(payload) != {"ja", "en"}:
|
|
30
|
+
raise ManifestVerificationError("peer-review skillDependencies must contain ja and en")
|
|
31
|
+
return
|
|
32
|
+
if not isinstance(payload, list):
|
|
33
|
+
raise ManifestVerificationError(f"{phase}: skillDependencies must be a list")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _load_vendored_versions() -> dict[str, str]:
|
|
37
|
+
text = VENDORED_PATH.read_text(encoding="utf-8")
|
|
38
|
+
versions: dict[str, str] = {}
|
|
39
|
+
for skill_id, version in re.findall(r"\| `([^`]+)` \| [^|]+ \| `([^`]+)` \|", text):
|
|
40
|
+
versions[skill_id] = version.removeprefix("v")
|
|
41
|
+
return versions
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def scan_repo_skill_registry() -> dict[str, str | None]:
|
|
45
|
+
"""Scan repo-local vendored skills as the verifiable startup registry."""
|
|
46
|
+
versions = _load_vendored_versions()
|
|
47
|
+
registry: dict[str, str | None] = {}
|
|
48
|
+
skills_dir = REPO_ROOT / ".github" / "skills"
|
|
49
|
+
for skill_path in skills_dir.glob("*/SKILL.md"):
|
|
50
|
+
lines = skill_path.read_text(encoding="utf-8").splitlines()
|
|
51
|
+
name_line = next((line for line in lines if line.startswith("name: ")), None)
|
|
52
|
+
if name_line is not None:
|
|
53
|
+
skill_id = name_line.split(":", 1)[1].strip()
|
|
54
|
+
registry[skill_id] = versions.get(skill_id)
|
|
55
|
+
return registry
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# @id CODE-AISCI-023
|
|
59
|
+
# @implements REQ-AISCI-023
|
|
60
|
+
# @design DES-AISCI-015
|
|
61
|
+
def load_phase_manifest(path: Path | str = DEFAULT_MANIFEST_PATH) -> dict:
|
|
62
|
+
"""Load the machine-readable ai_scientist phase manifest."""
|
|
63
|
+
manifest = json.loads(Path(path).read_text(encoding="utf-8"))
|
|
64
|
+
for phase, entry in manifest.items():
|
|
65
|
+
module_path = entry.get("modulePath")
|
|
66
|
+
function_name = entry.get("functionName")
|
|
67
|
+
if not module_path or not function_name:
|
|
68
|
+
raise ManifestVerificationError(f"{phase}: missing modulePath or functionName")
|
|
69
|
+
_validate_skill_dependencies(phase, entry.get("skillDependencies", []))
|
|
70
|
+
_import_handler(module_path, function_name)
|
|
71
|
+
return manifest
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def verify_manifest_against_registry(manifest: dict, registry: dict[str, str | None]) -> None:
|
|
75
|
+
"""Verify every declared dependency exists in the startup registry scan."""
|
|
76
|
+
for phase, entry in manifest.items():
|
|
77
|
+
dependencies = entry.get("skillDependencies", [])
|
|
78
|
+
phase_dependencies = (
|
|
79
|
+
dependencies.values() if isinstance(dependencies, dict) else dependencies
|
|
80
|
+
)
|
|
81
|
+
for dependency in phase_dependencies:
|
|
82
|
+
skill_id = dependency["skillId"]
|
|
83
|
+
version = dependency["version"]
|
|
84
|
+
if registry.get(skill_id) != version:
|
|
85
|
+
raise ManifestVerificationError(
|
|
86
|
+
f"{phase}: missing registry entry for {skill_id}@{version}"
|
|
87
|
+
)
|