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.
Files changed (193) hide show
  1. package/.github/skills/ai-chemistry-scientist/SKILL.md +97 -0
  2. package/.github/skills/ai-chemistry-scientist/manifest.json +156 -0
  3. package/.github/skills/ai-data-scientist/SKILL.md +330 -0
  4. package/.github/skills/ai-genomics-scientist/SKILL.md +98 -0
  5. package/.github/skills/ai-genomics-scientist/manifest.json +93 -0
  6. package/.github/skills/ai-materials-scientist/SKILL.md +51 -0
  7. package/.github/skills/ai-materials-scientist/manifest.json +58 -0
  8. package/.github/skills/ai-scientist/SKILL.md +69 -0
  9. package/.github/skills/ai-scientist/manifest.json +61 -0
  10. package/.github/skills/ai-structural-biology-scientist/SKILL.md +67 -0
  11. package/.github/skills/ai-structural-biology-scientist/manifest.json +72 -0
  12. package/.github/skills/japanese-prose/NOTICE.md +17 -0
  13. package/.github/skills/japanese-prose/SKILL.md +111 -0
  14. package/.github/skills/japanese-prose/references/review-workflow.md +50 -0
  15. package/.github/skills/japanese-prose/references/scoring.md +24 -0
  16. package/.github/skills/japanese-prose/references/writing-guidelines.md +60 -0
  17. package/.github/skills/japanese-prose/scripts/core.py +192 -0
  18. package/.github/skills/japanese-prose/scripts/fixtures/natural.md +5 -0
  19. package/.github/skills/japanese-prose/scripts/fixtures/unnatural.md +5 -0
  20. package/.github/skills/japanese-prose/scripts/lint.py +378 -0
  21. package/.github/skills/japanese-prose/scripts/outline.py +68 -0
  22. package/.github/skills/japanese-prose/scripts/terms.py +112 -0
  23. package/.github/skills/japanese-prose/scripts/test_engine.py +117 -0
  24. package/.github/skills/presentation-planner/SKILL.md +257 -0
  25. package/.github/skills/presentation-planner/assets/design-templates/data-report.yaml +97 -0
  26. package/.github/skills/presentation-planner/assets/design-templates/executive-proposal.yaml +92 -0
  27. package/.github/skills/presentation-planner/assets/design-templates/technical-briefing.yaml +96 -0
  28. package/.github/skills/presentation-planner/assets/scenario-templates/data-report.md +47 -0
  29. package/.github/skills/presentation-planner/assets/scenario-templates/executive-decision.md +43 -0
  30. package/.github/skills/presentation-planner/assets/scenario-templates/technical-briefing.md +45 -0
  31. package/.github/skills/presentation-planner/references/customizing-design-templates.md +160 -0
  32. package/.github/skills/presentation-planner/references/design-spec-schema.md +72 -0
  33. package/.github/skills/presentation-planner/references/handoff-contract.md +49 -0
  34. package/.github/skills/presentation-planner/references/responsibility-boundary.md +32 -0
  35. package/.github/skills/presentation-planner/references/scenario-templates.md +55 -0
  36. package/.github/skills/tech-writer/SKILL.md +434 -0
  37. package/.github/skills/tech-writer/assets/templates/blueprint.md +187 -0
  38. package/.github/skills/tech-writer/assets/templates/design-doc.md +29 -0
  39. package/.github/skills/tech-writer/assets/templates/migration-plan.md +173 -0
  40. package/.github/skills/tech-writer/assets/templates/operations-runbook.md +202 -0
  41. package/.github/skills/tech-writer/assets/templates/pr-description.md +23 -0
  42. package/.github/skills/tech-writer/assets/templates/qiita.md +44 -0
  43. package/.github/skills/tech-writer/assets/templates/readme.md +38 -0
  44. package/.github/skills/tech-writer/assets/templates/requirements-definition.md +170 -0
  45. package/.github/skills/tech-writer/assets/templates/rfi.md +113 -0
  46. package/.github/skills/tech-writer/assets/templates/rfp.md +180 -0
  47. package/.github/skills/tech-writer/assets/templates/security-design.md +167 -0
  48. package/.github/skills/tech-writer/assets/templates/system-design.md +220 -0
  49. package/.github/skills/tech-writer/assets/templates/technical-proposal.md +112 -0
  50. package/.github/skills/tech-writer/assets/templates/test-plan.md +153 -0
  51. package/.github/skills/tech-writer/assets/templates/user-manual.md +22 -0
  52. package/.github/skills/tech-writer/assets/templates/white-paper.md +192 -0
  53. package/.github/skills/tech-writer/references/doctypes/api-docs.md +33 -0
  54. package/.github/skills/tech-writer/references/doctypes/blueprint.md +81 -0
  55. package/.github/skills/tech-writer/references/doctypes/code-comments.md +39 -0
  56. package/.github/skills/tech-writer/references/doctypes/design-doc.md +42 -0
  57. package/.github/skills/tech-writer/references/doctypes/migration-plan.md +63 -0
  58. package/.github/skills/tech-writer/references/doctypes/operations-runbook.md +63 -0
  59. package/.github/skills/tech-writer/references/doctypes/pr-commit.md +82 -0
  60. package/.github/skills/tech-writer/references/doctypes/qiita.md +75 -0
  61. package/.github/skills/tech-writer/references/doctypes/readme.md +43 -0
  62. package/.github/skills/tech-writer/references/doctypes/release-notes.md +30 -0
  63. package/.github/skills/tech-writer/references/doctypes/requirements-definition.md +61 -0
  64. package/.github/skills/tech-writer/references/doctypes/rfi.md +43 -0
  65. package/.github/skills/tech-writer/references/doctypes/rfp.md +46 -0
  66. package/.github/skills/tech-writer/references/doctypes/security-design.md +71 -0
  67. package/.github/skills/tech-writer/references/doctypes/system-design.md +74 -0
  68. package/.github/skills/tech-writer/references/doctypes/technical-proposal.md +49 -0
  69. package/.github/skills/tech-writer/references/doctypes/test-plan.md +67 -0
  70. package/.github/skills/tech-writer/references/doctypes/user-manual.md +58 -0
  71. package/.github/skills/tech-writer/references/doctypes/white-paper.md +84 -0
  72. package/.github/skills/tech-writer/references/doctypes/zenn.md +66 -0
  73. package/.github/skills/tech-writer/references/japanese-prose-optimization.md +110 -0
  74. package/.github/skills/tech-writer/references/style-constitution.md +104 -0
  75. package/.github/skills/tech-writer/scripts/lint.py +412 -0
  76. package/LICENSE +21 -0
  77. package/README.md +92 -0
  78. package/bin/ai-data-scientist.js +123 -0
  79. package/package.json +41 -0
  80. package/pyproject.toml +45 -0
  81. package/src/ai_chemistry_scientist/__init__.py +0 -0
  82. package/src/ai_chemistry_scientist/admet_prediction.py +71 -0
  83. package/src/ai_chemistry_scientist/bioactivity_classification.py +73 -0
  84. package/src/ai_chemistry_scientist/data/sample_molecules.csv +21 -0
  85. package/src/ai_chemistry_scientist/dispatch.py +369 -0
  86. package/src/ai_chemistry_scientist/docking_score.py +97 -0
  87. package/src/ai_chemistry_scientist/drug_likeness_rules.py +84 -0
  88. package/src/ai_chemistry_scientist/evidence.py +41 -0
  89. package/src/ai_chemistry_scientist/molecular_descriptors.py +97 -0
  90. package/src/ai_chemistry_scientist/molecular_formula_mass.py +40 -0
  91. package/src/ai_chemistry_scientist/molecular_similarity.py +78 -0
  92. package/src/ai_chemistry_scientist/qsar_modeling.py +105 -0
  93. package/src/ai_chemistry_scientist/salt_standardization.py +81 -0
  94. package/src/ai_chemistry_scientist/structural_alerts.py +76 -0
  95. package/src/ai_chemistry_scientist/structure_format_conversion.py +84 -0
  96. package/src/ai_chemistry_scientist/validation.py +70 -0
  97. package/src/ai_data_scientist/__init__.py +0 -0
  98. package/src/ai_data_scientist/analysis_assumptions.py +121 -0
  99. package/src/ai_data_scientist/anomaly_detection.py +39 -0
  100. package/src/ai_data_scientist/automl.py +109 -0
  101. package/src/ai_data_scientist/cleaning.py +56 -0
  102. package/src/ai_data_scientist/cli.py +90 -0
  103. package/src/ai_data_scientist/clustering.py +54 -0
  104. package/src/ai_data_scientist/dashboard.py +33 -0
  105. package/src/ai_data_scientist/data_definition.py +100 -0
  106. package/src/ai_data_scientist/data_quality.py +164 -0
  107. package/src/ai_data_scientist/dataset_validation.py +135 -0
  108. package/src/ai_data_scientist/dependency_pins.py +60 -0
  109. package/src/ai_data_scientist/eda.py +82 -0
  110. package/src/ai_data_scientist/experiment_evaluation.py +635 -0
  111. package/src/ai_data_scientist/explainability.py +340 -0
  112. package/src/ai_data_scientist/feature_engineering.py +163 -0
  113. package/src/ai_data_scientist/gate_config.py +32 -0
  114. package/src/ai_data_scientist/ingestion.py +127 -0
  115. package/src/ai_data_scientist/insight_engine.py +180 -0
  116. package/src/ai_data_scientist/japanese_nlp.py +43 -0
  117. package/src/ai_data_scientist/jupyter_launcher.py +137 -0
  118. package/src/ai_data_scientist/jupyter_mcp_client.py +94 -0
  119. package/src/ai_data_scientist/language_router.py +28 -0
  120. package/src/ai_data_scientist/lifecycle.py +221 -0
  121. package/src/ai_data_scientist/mcp_gateway.py +113 -0
  122. package/src/ai_data_scientist/mcp_runtime.py +194 -0
  123. package/src/ai_data_scientist/mcp_transport.py +53 -0
  124. package/src/ai_data_scientist/ml_modeling.py +451 -0
  125. package/src/ai_data_scientist/model_tuning.py +104 -0
  126. package/src/ai_data_scientist/notebook_audit.py +574 -0
  127. package/src/ai_data_scientist/project_manager.py +243 -0
  128. package/src/ai_data_scientist/report_export.py +73 -0
  129. package/src/ai_data_scientist/sensitivity.py +445 -0
  130. package/src/ai_data_scientist/signal_analysis.py +201 -0
  131. package/src/ai_data_scientist/skill_packaging.py +40 -0
  132. package/src/ai_data_scientist/stats_analysis.py +88 -0
  133. package/src/ai_data_scientist/text_nlp.py +44 -0
  134. package/src/ai_data_scientist/timeseries.py +68 -0
  135. package/src/ai_data_scientist/visualization.py +708 -0
  136. package/src/ai_genomics_scientist/__init__.py +1 -0
  137. package/src/ai_genomics_scientist/differential_expression.py +147 -0
  138. package/src/ai_genomics_scientist/dispatch.py +267 -0
  139. package/src/ai_genomics_scientist/evidence.py +45 -0
  140. package/src/ai_genomics_scientist/gene_set_enrichment.py +76 -0
  141. package/src/ai_genomics_scientist/sequence_alignment.py +97 -0
  142. package/src/ai_genomics_scientist/sequence_features.py +111 -0
  143. package/src/ai_genomics_scientist/splice_site_scoring.py +66 -0
  144. package/src/ai_genomics_scientist/validation.py +83 -0
  145. package/src/ai_genomics_scientist/variant_effect.py +147 -0
  146. package/src/ai_genomics_scientist/variant_pathogenicity.py +125 -0
  147. package/src/ai_materials_scientist/__init__.py +0 -0
  148. package/src/ai_materials_scientist/calphad.py +117 -0
  149. package/src/ai_materials_scientist/classical_monte_carlo.py +165 -0
  150. package/src/ai_materials_scientist/crystal_plasticity.py +184 -0
  151. package/src/ai_materials_scientist/dispatch.py +100 -0
  152. package/src/ai_materials_scientist/evidence.py +84 -0
  153. package/src/ai_materials_scientist/fem.py +279 -0
  154. package/src/ai_materials_scientist/kinetic_monte_carlo.py +145 -0
  155. package/src/ai_materials_scientist/molecular_dynamics.py +240 -0
  156. package/src/ai_materials_scientist/phase_field.py +167 -0
  157. package/src/ai_materials_scientist/validation.py +70 -0
  158. package/src/ai_scientist/__init__.py +1 -0
  159. package/src/ai_scientist/completion_gate.py +15 -0
  160. package/src/ai_scientist/data_analysis.py +46 -0
  161. package/src/ai_scientist/evidence_registry.py +99 -0
  162. package/src/ai_scientist/experimental_design.py +20 -0
  163. package/src/ai_scientist/language.py +14 -0
  164. package/src/ai_scientist/latex_renderer.py +41 -0
  165. package/src/ai_scientist/literature_review.py +37 -0
  166. package/src/ai_scientist/manifest.py +87 -0
  167. package/src/ai_scientist/manuscript.py +94 -0
  168. package/src/ai_scientist/mcp_config.py +76 -0
  169. package/src/ai_scientist/mcp_external.py +42 -0
  170. package/src/ai_scientist/mcp_failures.py +23 -0
  171. package/src/ai_scientist/mcp_gateway.py +38 -0
  172. package/src/ai_scientist/mcp_managed.py +180 -0
  173. package/src/ai_scientist/npm_packaging.py +49 -0
  174. package/src/ai_scientist/orchestrator.py +133 -0
  175. package/src/ai_scientist/peer_review.py +60 -0
  176. package/src/ai_scientist/phase_gate.py +74 -0
  177. package/src/ai_scientist/phase_state.py +230 -0
  178. package/src/ai_scientist/presentation.py +56 -0
  179. package/src/ai_scientist/project_config.py +31 -0
  180. package/src/ai_scientist/project_handle.py +74 -0
  181. package/src/ai_scientist/reproducibility.py +20 -0
  182. package/src/ai_scientist/research_planning.py +20 -0
  183. package/src/ai_scientist/skill_invocation.py +21 -0
  184. package/src/ai_scientist/tdd_gate.py +99 -0
  185. package/src/ai_structural_biology_scientist/__init__.py +0 -0
  186. package/src/ai_structural_biology_scientist/contact_map.py +87 -0
  187. package/src/ai_structural_biology_scientist/dispatch.py +269 -0
  188. package/src/ai_structural_biology_scientist/evidence.py +43 -0
  189. package/src/ai_structural_biology_scientist/hydrophobicity.py +101 -0
  190. package/src/ai_structural_biology_scientist/protein_docking_score.py +104 -0
  191. package/src/ai_structural_biology_scientist/secondary_structure.py +95 -0
  192. package/src/ai_structural_biology_scientist/structural_similarity.py +74 -0
  193. package/src/ai_structural_biology_scientist/validation.py +100 -0
@@ -0,0 +1,87 @@
1
+ """Residue contact-map heuristic module (DES-ASTRUCT-050 / REQ-ASTRUCT-050)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import math
6
+ from numbers import Integral
7
+
8
+ from ai_structural_biology_scientist.validation import (
9
+ fail,
10
+ is_finite_real,
11
+ register_validator,
12
+ validate_coordinate,
13
+ )
14
+
15
+ _MODULE_NAME = "residue-contact-map"
16
+ _COORDINATE_CONSTRAINT = "must be a finite numeric 3-element sequence"
17
+
18
+
19
+ def _contact_map_validator(params: dict) -> dict:
20
+ """DES-ASTRUCT-002 registered atomic validator for this module."""
21
+ if "coordinates" not in params:
22
+ return fail("coordinates", "is required")
23
+
24
+ coordinates = params["coordinates"]
25
+ if not isinstance(coordinates, list):
26
+ return fail("coordinates", "must be a list")
27
+ if len(coordinates) < 2:
28
+ return fail("coordinates", "must contain at least 2 coordinates")
29
+ for index, coordinate in enumerate(coordinates):
30
+ coordinate_validation = validate_coordinate(
31
+ coordinate,
32
+ parameter=f"coordinates[{index}]",
33
+ constraint=_COORDINATE_CONSTRAINT,
34
+ )
35
+ if not coordinate_validation["ok"]:
36
+ return coordinate_validation
37
+
38
+ distance_threshold_a = params.get("distance_threshold_A", 8.0)
39
+ if not is_finite_real(distance_threshold_a) or distance_threshold_a <= 0:
40
+ return fail("distance_threshold_A", "must be > 0")
41
+
42
+ min_sequence_separation = params.get("min_sequence_separation", 3)
43
+ if (
44
+ not isinstance(min_sequence_separation, Integral)
45
+ or isinstance(min_sequence_separation, bool)
46
+ or min_sequence_separation < 1
47
+ ):
48
+ return fail("min_sequence_separation", "must be an integer >= 1")
49
+
50
+ return {"ok": True}
51
+
52
+
53
+ register_validator(_MODULE_NAME, _contact_map_validator)
54
+
55
+
56
+ # @id CODE-ASTRUCT-050
57
+ # @implements REQ-ASTRUCT-050
58
+ # @design DES-ASTRUCT-050
59
+ def run_contact_map(
60
+ coordinates: list,
61
+ distance_threshold_A: float = 8.0,
62
+ min_sequence_separation: int = 3,
63
+ ) -> dict:
64
+ """Compute the C-alpha contact-map heuristic."""
65
+ validation = _contact_map_validator(
66
+ {
67
+ "coordinates": coordinates,
68
+ "distance_threshold_A": distance_threshold_A,
69
+ "min_sequence_separation": min_sequence_separation,
70
+ }
71
+ )
72
+ if not validation["ok"]:
73
+ raise ValueError("parameters must already be validated by the handler wrapper")
74
+
75
+ contact_matrix = [[False for _ in coordinates] for _ in coordinates]
76
+ total_contacts = 0
77
+ for i in range(len(coordinates)):
78
+ for j in range(i + 1, len(coordinates)):
79
+ if abs(i - j) < min_sequence_separation:
80
+ continue
81
+ distance = math.dist(coordinates[i], coordinates[j])
82
+ if distance <= distance_threshold_A:
83
+ contact_matrix[i][j] = True
84
+ contact_matrix[j][i] = True
85
+ total_contacts += 1
86
+
87
+ return {"contact_matrix": contact_matrix, "total_contacts": total_contacts}
@@ -0,0 +1,269 @@
1
+ """Method manifest & request dispatcher (DES-ASTRUCT-001 / REQ-ASTRUCT-001/002)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import importlib
6
+ import json
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+ import numpy as np
11
+
12
+ import ai_structural_biology_scientist.contact_map as _contact_map # noqa: F401
13
+ import ai_structural_biology_scientist.hydrophobicity as _hydrophobicity # noqa: F401
14
+ import ai_structural_biology_scientist.protein_docking_score as _protein_docking_score # noqa: F401
15
+ import ai_structural_biology_scientist.secondary_structure as _secondary_structure # noqa: F401
16
+ import ai_structural_biology_scientist.structural_similarity as _structural_similarity # noqa: F401
17
+ from ai_data_scientist.language_router import detect_language as _detect_language
18
+ from ai_structural_biology_scientist.evidence import record_run
19
+ from ai_structural_biology_scientist.validation import validate_parameters
20
+
21
+ REPO_ROOT = Path(__file__).resolve().parents[2]
22
+ DEFAULT_MANIFEST_PATH = (
23
+ REPO_ROOT / ".github" / "skills" / "ai-structural-biology-scientist" / "manifest.json"
24
+ )
25
+
26
+ _LIMITATION_LABEL_MODULES = frozenset(
27
+ {"secondary-structure-heuristic", "protein-protein-docking-score"}
28
+ )
29
+ _RUN_MODULE_PATHS = {
30
+ "secondary-structure-heuristic": "ai_structural_biology_scientist.secondary_structure",
31
+ "hydrophobicity-burial-heuristic": "ai_structural_biology_scientist.hydrophobicity",
32
+ "protein-protein-docking-score": "ai_structural_biology_scientist.protein_docking_score",
33
+ "structural-similarity-rmsd": "ai_structural_biology_scientist.structural_similarity",
34
+ "residue-contact-map": "ai_structural_biology_scientist.contact_map",
35
+ }
36
+ _RUN_FUNCTION_NAMES = {
37
+ "secondary-structure-heuristic": "run_secondary_structure",
38
+ "hydrophobicity-burial-heuristic": "run_hydrophobicity",
39
+ "protein-protein-docking-score": "run_protein_docking_score",
40
+ "structural-similarity-rmsd": "run_structural_similarity",
41
+ "residue-contact-map": "run_contact_map",
42
+ }
43
+ _SUPPORTED_LANGUAGES = ("en", "ja")
44
+
45
+
46
+ def _require_manifest_string(entry: dict[str, Any], method: str, field_name: str) -> None:
47
+ value = entry.get(field_name)
48
+ if not isinstance(value, str) or value == "":
49
+ raise ValueError( # noqa: TRY004
50
+ f"manifest entry '{method}' must define a string '{field_name}'"
51
+ )
52
+
53
+
54
+ def _validate_manifest(manifest: Any) -> dict[str, dict[str, Any]]:
55
+ if not isinstance(manifest, dict):
56
+ raise ValueError("manifest must be a JSON object") # noqa: TRY004
57
+
58
+ for method, entry in manifest.items():
59
+ if not isinstance(entry, dict):
60
+ raise ValueError(f"manifest entry '{method}' must be a JSON object") # noqa: TRY004
61
+ _require_manifest_string(entry, method, "modulePath")
62
+ _require_manifest_string(entry, method, "functionName")
63
+ names = entry.get("names")
64
+ if not isinstance(names, dict):
65
+ raise ValueError(f"manifest entry '{method}' must define an object 'names'") # noqa: TRY004
66
+ for language in _SUPPORTED_LANGUAGES:
67
+ candidates = names.get(language)
68
+ if not isinstance(candidates, list) or any(
69
+ not isinstance(candidate, str) or candidate == "" for candidate in candidates
70
+ ):
71
+ raise ValueError( # noqa: TRY004
72
+ f"manifest entry '{method}' must define a list of non-empty strings at "
73
+ f"'names.{language}'"
74
+ )
75
+ return manifest
76
+
77
+
78
+ def load_manifest(manifest_path: Path | None = None) -> dict:
79
+ """Load the static method-name-to-module manifest."""
80
+ path = manifest_path or DEFAULT_MANIFEST_PATH
81
+ return _validate_manifest(json.loads(path.read_text(encoding="utf-8")))
82
+
83
+
84
+ def _matched_methods(request_text: str, manifest: dict) -> list[str]:
85
+ lowered = " ".join(request_text.lower().split())
86
+ matched = []
87
+ for method, entry in manifest.items():
88
+ names = entry.get("names", {})
89
+ candidates = list(names.get("en", [])) + list(names.get("ja", []))
90
+ if any(
91
+ (candidate.lower() in lowered) if candidate.isascii() else (candidate in request_text)
92
+ for candidate in candidates
93
+ ):
94
+ matched.append(method)
95
+ return matched
96
+
97
+
98
+ # @id CODE-ASTRUCT-901
99
+ # @implements REQ-ASTRUCT-002
100
+ # @design DES-ASTRUCT-001
101
+ def extract_params(request_text: Any) -> dict[str, Any] | None:
102
+ """Extract structured params or a single embedded JSON object from input."""
103
+ if isinstance(request_text, dict):
104
+ return dict(request_text)
105
+ if not isinstance(request_text, str):
106
+ return None
107
+ start = request_text.find("{")
108
+ if start == -1:
109
+ return None
110
+ depth = 0
111
+ for index in range(start, len(request_text)):
112
+ char = request_text[index]
113
+ if char == "{":
114
+ depth += 1
115
+ elif char == "}":
116
+ depth -= 1
117
+ if depth == 0:
118
+ candidate = request_text[start : index + 1]
119
+ try:
120
+ parsed = json.loads(candidate)
121
+ except json.JSONDecodeError:
122
+ return None
123
+ return parsed if isinstance(parsed, dict) else None
124
+ return None
125
+
126
+
127
+ def _resolve_run_function(method: str):
128
+ module = importlib.import_module(_RUN_MODULE_PATHS[method])
129
+ return getattr(module, _RUN_FUNCTION_NAMES[method])
130
+
131
+
132
+ def _localize_limitation_label(method: str, result: dict, language: str) -> dict:
133
+ if method not in _LIMITATION_LABEL_MODULES:
134
+ return result
135
+ module = importlib.import_module(_RUN_MODULE_PATHS[method])
136
+ text = module.LIMITATION_LABEL_TEXT[language]
137
+ localized = dict(result)
138
+ del localized["limitation_label_key"]
139
+ localized["limitation_label"] = text
140
+ return localized
141
+
142
+
143
+ def _no_params_outcome(language: str) -> dict:
144
+ return {
145
+ "ok": False,
146
+ "parameter": "params",
147
+ "constraint": "must be extractable as a JSON object embedded in request_text",
148
+ "language": language,
149
+ }
150
+
151
+
152
+ def _handle_module(method: str, request_text: str, language: str) -> dict:
153
+ params = extract_params(request_text)
154
+ if params is None:
155
+ return _no_params_outcome(language)
156
+
157
+ validation = validate_parameters(method, params)
158
+ if not validation["ok"]:
159
+ return {
160
+ "ok": False,
161
+ "parameter": validation["parameter"],
162
+ "constraint": validation["constraint"],
163
+ "language": language,
164
+ }
165
+
166
+ result = _resolve_run_function(method)(**params)
167
+ result = _localize_limitation_label(method, result, language)
168
+ run_record = record_run(
169
+ module_name=method,
170
+ params=params,
171
+ result=result,
172
+ numpy_version=np.__version__,
173
+ )
174
+ return {"ok": True, "run_record": run_record}
175
+
176
+
177
+ # @id CODE-ASTRUCT-011
178
+ # @implements REQ-ASTRUCT-002 REQ-ASTRUCT-003
179
+ # @design DES-ASTRUCT-001
180
+ def handle_secondary_structure(request_text: str, language: str) -> dict:
181
+ """Handler wrapper for the secondary-structure module."""
182
+ return _handle_module("secondary-structure-heuristic", request_text, language)
183
+
184
+
185
+ # @id CODE-ASTRUCT-021
186
+ # @implements REQ-ASTRUCT-002 REQ-ASTRUCT-003
187
+ # @design DES-ASTRUCT-001
188
+ def handle_hydrophobicity(request_text: str, language: str) -> dict:
189
+ """Handler wrapper for the hydrophobicity module."""
190
+ return _handle_module("hydrophobicity-burial-heuristic", request_text, language)
191
+
192
+
193
+ # @id CODE-ASTRUCT-031
194
+ # @implements REQ-ASTRUCT-002 REQ-ASTRUCT-003
195
+ # @design DES-ASTRUCT-001
196
+ def handle_protein_docking_score(request_text: str, language: str) -> dict:
197
+ """Handler wrapper for the protein-docking module."""
198
+ return _handle_module("protein-protein-docking-score", request_text, language)
199
+
200
+
201
+ # @id CODE-ASTRUCT-041
202
+ # @implements REQ-ASTRUCT-002 REQ-ASTRUCT-003
203
+ # @design DES-ASTRUCT-001
204
+ def handle_structural_similarity(request_text: str, language: str) -> dict:
205
+ """Handler wrapper for the structural-similarity module."""
206
+ return _handle_module("structural-similarity-rmsd", request_text, language)
207
+
208
+
209
+ # @id CODE-ASTRUCT-051
210
+ # @implements REQ-ASTRUCT-002 REQ-ASTRUCT-003
211
+ # @design DES-ASTRUCT-001
212
+ def handle_contact_map(request_text: str, language: str) -> dict:
213
+ """Handler wrapper for the contact-map module."""
214
+ return _handle_module("residue-contact-map", request_text, language)
215
+
216
+
217
+ def _render_clarification(candidates: list[str], language: str) -> str:
218
+ names = "、".join(candidates) if language == "ja" else ", ".join(candidates)
219
+ if language == "ja":
220
+ return f"{names} のどちらを意図していますか。明確にしてください。"
221
+ return f"Did you mean {names}? Please clarify which method you want."
222
+
223
+
224
+ def _render_rejection(language: str) -> str:
225
+ if language == "ja":
226
+ return "対応する手法が要求から認識されませんでした。"
227
+ return "No supported method was recognized in your request."
228
+
229
+
230
+ # @id CODE-ASTRUCT-001
231
+ # @implements REQ-ASTRUCT-001 REQ-ASTRUCT-002
232
+ # @design DES-ASTRUCT-001
233
+ def dispatch(
234
+ request_text: str,
235
+ language: str | None = None,
236
+ manifest_path: Path | None = None,
237
+ ) -> dict:
238
+ """Classify ``request_text`` and dispatch to exactly one matched module."""
239
+ if not isinstance(request_text, str):
240
+ raise ValueError("request_text: must be a str") # noqa: TRY004
241
+ if language is not None and language not in ("en", "ja"):
242
+ raise ValueError("language: must be 'en' or 'ja' when explicitly supplied")
243
+
244
+ detected_language = language or _detect_language(request_text)
245
+ manifest = load_manifest(manifest_path)
246
+ matched = _matched_methods(request_text, manifest)
247
+
248
+ if len(matched) == 1:
249
+ method = matched[0]
250
+ entry = manifest[method]
251
+ handler = getattr(importlib.import_module(entry["modulePath"]), entry["functionName"])
252
+ return {
253
+ "outcome": "dispatch",
254
+ "module": method,
255
+ "language": detected_language,
256
+ "handler_result": handler(request_text, detected_language),
257
+ }
258
+ if len(matched) > 1:
259
+ return {
260
+ "outcome": "clarification",
261
+ "candidates": matched,
262
+ "language": detected_language,
263
+ "clarification_question": _render_clarification(matched, detected_language),
264
+ }
265
+ return {
266
+ "outcome": "rejected",
267
+ "language": detected_language,
268
+ "rejected_method": _render_rejection(detected_language),
269
+ }
@@ -0,0 +1,43 @@
1
+ """Run evidence recorder (DES-ASTRUCT-003 / REQ-ASTRUCT-004)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ import numpy
8
+
9
+ SCHEMA_VERSION = 1
10
+
11
+
12
+ def _json_safe_value(value: Any) -> Any:
13
+ if value is None or isinstance(value, (str, bool, int, float)):
14
+ return value
15
+ if isinstance(value, numpy.generic):
16
+ return _json_safe_value(value.item())
17
+ if isinstance(value, dict):
18
+ return {key: _json_safe_value(nested) for key, nested in value.items()}
19
+ if isinstance(value, (list, tuple)):
20
+ return [_json_safe_value(item) for item in value]
21
+ raise TypeError(f"unsupported non-JSON-safe value: {type(value).__name__}")
22
+
23
+
24
+ # @id CODE-ASTRUCT-003
25
+ # @implements REQ-ASTRUCT-004
26
+ # @design DES-ASTRUCT-003
27
+ def record_run(
28
+ module_name: str,
29
+ params: dict[str, Any],
30
+ result: Any,
31
+ *,
32
+ numpy_version: str,
33
+ ) -> dict:
34
+ """Build a RunRecord with exactly `metadata`, `parameters`, `result`."""
35
+ return {
36
+ "metadata": {
37
+ "module": module_name,
38
+ "schema_version": SCHEMA_VERSION,
39
+ "numpy_version": numpy_version,
40
+ },
41
+ "parameters": _json_safe_value(params),
42
+ "result": _json_safe_value(result),
43
+ }
@@ -0,0 +1,101 @@
1
+ """Hydrophobicity / burial heuristic module (DES-ASTRUCT-020 / REQ-ASTRUCT-020)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from numbers import Integral
6
+
7
+ from ai_structural_biology_scientist.validation import (
8
+ fail,
9
+ is_finite_real,
10
+ register_validator,
11
+ validate_uppercase_sequence,
12
+ )
13
+
14
+ _MODULE_NAME = "hydrophobicity-burial-heuristic"
15
+ _KD_SCALE = {
16
+ "A": 1.8,
17
+ "R": -4.5,
18
+ "N": -3.5,
19
+ "D": -3.5,
20
+ "C": 2.5,
21
+ "Q": -3.5,
22
+ "E": -3.5,
23
+ "G": -0.4,
24
+ "H": -3.2,
25
+ "I": 4.5,
26
+ "L": 3.8,
27
+ "K": -3.9,
28
+ "M": 1.9,
29
+ "F": 2.8,
30
+ "P": -1.6,
31
+ "S": -0.8,
32
+ "T": -0.7,
33
+ "W": -0.9,
34
+ "Y": -1.3,
35
+ "V": 4.2,
36
+ }
37
+
38
+
39
+ def _hydrophobicity_validator(params: dict) -> dict:
40
+ """DES-ASTRUCT-002 registered atomic validator for this module."""
41
+ if "sequence" not in params:
42
+ return fail("sequence", "is required")
43
+ sequence_validation = validate_uppercase_sequence(params)
44
+ if not sequence_validation["ok"]:
45
+ return sequence_validation
46
+
47
+ window_size = params.get("window_size", 9)
48
+ if (
49
+ not isinstance(window_size, Integral)
50
+ or isinstance(window_size, bool)
51
+ or window_size < 1
52
+ or window_size % 2 == 0
53
+ ):
54
+ return fail("window_size", "must be a positive odd integer")
55
+
56
+ burial_threshold = params.get("burial_threshold", 1.5)
57
+ if not is_finite_real(burial_threshold):
58
+ return fail("burial_threshold", "must be a finite number")
59
+
60
+ return {"ok": True}
61
+
62
+
63
+ register_validator(_MODULE_NAME, _hydrophobicity_validator)
64
+
65
+
66
+ # @id CODE-ASTRUCT-020
67
+ # @implements REQ-ASTRUCT-020
68
+ # @design DES-ASTRUCT-020
69
+ def run_hydrophobicity(
70
+ sequence: str,
71
+ window_size: int = 9,
72
+ burial_threshold: float = 1.5,
73
+ ) -> dict:
74
+ """Compute the Kyte-Doolittle burial heuristic for ``sequence``."""
75
+ validation = _hydrophobicity_validator(
76
+ {
77
+ "sequence": sequence,
78
+ "window_size": window_size,
79
+ "burial_threshold": burial_threshold,
80
+ }
81
+ )
82
+ if not validation["ok"]:
83
+ raise ValueError("parameters must already be validated by the handler wrapper")
84
+
85
+ half_window = window_size // 2
86
+ residues = []
87
+ for position, residue in enumerate(sequence):
88
+ start = max(0, position - half_window)
89
+ stop = min(len(sequence), position + half_window + 1)
90
+ window_values = [_KD_SCALE[window_residue] for window_residue in sequence[start:stop]]
91
+ window_average = sum(window_values) / len(window_values)
92
+ residues.append(
93
+ {
94
+ "position": position,
95
+ "residue": residue,
96
+ "kd_value": _KD_SCALE[residue],
97
+ "window_average": window_average,
98
+ "label": "buried" if window_average > burial_threshold else "exposed",
99
+ }
100
+ )
101
+ return {"residues": residues}
@@ -0,0 +1,104 @@
1
+ """Protein docking-score heuristic module (DES-ASTRUCT-030 / REQ-ASTRUCT-030)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Mapping
6
+ from numbers import Integral
7
+
8
+ from ai_structural_biology_scientist.validation import fail, is_finite_real, register_validator
9
+
10
+ _MODULE_NAME = "protein-protein-docking-score"
11
+ _REFERENCE_INTERFACE_AREA_A2 = 800.0
12
+
13
+ LIMITATION_LABEL_KEY = "protein_docking_score_heuristic_limitation"
14
+ LIMITATION_LABEL_TEXT = {
15
+ "en": (
16
+ "Heuristic only: a fixed-formula geometric/compositional complementarity "
17
+ "score, not a physically accurate protein-protein docking simulation (no "
18
+ "3D structure, no energy function)."
19
+ ),
20
+ "ja": (
21
+ "ヒューリスティックのみ:固定式の幾何・組成補完性スコアであり、"
22
+ "物理的に正確なタンパク質間ドッキングシミュレーションではない"
23
+ "(3D 構造なし、エネルギー関数なし)。"
24
+ ),
25
+ }
26
+
27
+
28
+ def _is_non_negative_int(value) -> bool:
29
+ return isinstance(value, Integral) and not isinstance(value, bool) and value >= 0
30
+
31
+
32
+ def _validate_partner(params: dict, name: str) -> dict:
33
+ partner = params.get(name)
34
+ if not isinstance(partner, Mapping):
35
+ return fail(name, "must be a dict")
36
+ for key in ("hydrophobic_count", "charged_count"):
37
+ if key not in partner:
38
+ return fail(f"{name}.{key}", "is required")
39
+ if not _is_non_negative_int(partner[key]):
40
+ return fail(f"{name}.{key}", "must be a finite non-negative integer")
41
+ return {"ok": True}
42
+
43
+
44
+ def _protein_docking_score_validator(params: dict) -> dict:
45
+ """DES-ASTRUCT-002 registered atomic validator for this module."""
46
+ for name in ("partner_a", "partner_b"):
47
+ if name not in params:
48
+ return fail(name, "is required")
49
+ partner_validation = _validate_partner(params, name)
50
+ if not partner_validation["ok"]:
51
+ return partner_validation
52
+
53
+ interface_area_a2 = params.get("interface_area_A2")
54
+ if not is_finite_real(interface_area_a2) or interface_area_a2 <= 0:
55
+ return fail("interface_area_A2", "must be > 0")
56
+
57
+ return {"ok": True}
58
+
59
+
60
+ register_validator(_MODULE_NAME, _protein_docking_score_validator)
61
+
62
+
63
+ # @id CODE-ASTRUCT-030
64
+ # @implements REQ-ASTRUCT-030
65
+ # @design DES-ASTRUCT-030
66
+ def run_protein_docking_score(
67
+ partner_a: dict,
68
+ partner_b: dict,
69
+ interface_area_A2: float,
70
+ ) -> dict:
71
+ """Compute the fixed-formula protein docking-score heuristic."""
72
+ validation = _protein_docking_score_validator(
73
+ {
74
+ "partner_a": partner_a,
75
+ "partner_b": partner_b,
76
+ "interface_area_A2": interface_area_A2,
77
+ }
78
+ )
79
+ if not validation["ok"]:
80
+ raise ValueError("parameters must already be validated by the handler wrapper")
81
+
82
+ size_term = min(
83
+ 1.0,
84
+ max(
85
+ 0.0,
86
+ 1.0
87
+ - abs(interface_area_A2 - _REFERENCE_INTERFACE_AREA_A2) / _REFERENCE_INTERFACE_AREA_A2,
88
+ ),
89
+ )
90
+ hydrophobic_complementarity = min(
91
+ partner_a["hydrophobic_count"], partner_b["hydrophobic_count"]
92
+ ) / max(1, partner_a["hydrophobic_count"] + partner_b["hydrophobic_count"])
93
+ charge_complementarity = min(partner_a["charged_count"], partner_b["charged_count"]) / max(
94
+ 1, partner_a["charged_count"] + partner_b["charged_count"]
95
+ )
96
+ score = 0.5 * size_term + 0.25 * hydrophobic_complementarity + 0.25 * charge_complementarity
97
+
98
+ return {
99
+ "size_term": size_term,
100
+ "hydrophobic_complementarity": hydrophobic_complementarity,
101
+ "charge_complementarity": charge_complementarity,
102
+ "score": score,
103
+ "limitation_label_key": LIMITATION_LABEL_KEY,
104
+ }