cct-cli 0.7.9.0__py3-none-any.whl
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.
- calc_terminal/__init__.py +14 -0
- calc_terminal/__main__.py +14 -0
- calc_terminal/activity.py +1334 -0
- calc_terminal/agent.py +3387 -0
- calc_terminal/agent_runtime.py +519 -0
- calc_terminal/ai_context.py +447 -0
- calc_terminal/ai_modes.py +752 -0
- calc_terminal/ai_personalization.py +286 -0
- calc_terminal/ai_preview_feedback.py +213 -0
- calc_terminal/aicore.py +2572 -0
- calc_terminal/anim.py +367 -0
- calc_terminal/app.py +3685 -0
- calc_terminal/art.py +639 -0
- calc_terminal/atomsim.py +368 -0
- calc_terminal/attachments.py +743 -0
- calc_terminal/benchmark_system.py +414 -0
- calc_terminal/browser/__init__.py +36 -0
- calc_terminal/browser/browser_state.py +346 -0
- calc_terminal/browser/devserver.py +176 -0
- calc_terminal/browser/engine.py +494 -0
- calc_terminal/browser/navigation.py +84 -0
- calc_terminal/browser/preview.py +429 -0
- calc_terminal/browser/preview_entry.py +95 -0
- calc_terminal/browser/project_detector.py +144 -0
- calc_terminal/browser/server.py +449 -0
- calc_terminal/browser/state.py +75 -0
- calc_terminal/browser/watcher.py +99 -0
- calc_terminal/browser_gui/__init__.py +1 -0
- calc_terminal/browser_gui/__main__.py +3 -0
- calc_terminal/browser_gui/launcher.py +173 -0
- calc_terminal/browser_gui/playwright_browser.py +117 -0
- calc_terminal/browser_gui/qt_browser.py +1501 -0
- calc_terminal/browser_gui/webview_browser.py +57 -0
- calc_terminal/capabilities/__init__.py +35 -0
- calc_terminal/capabilities/adapters/__init__.py +33 -0
- calc_terminal/capabilities/adapters/bioinformatics.py +204 -0
- calc_terminal/capabilities/adapters/browser_adapter.py +205 -0
- calc_terminal/capabilities/adapters/filesystem.py +206 -0
- calc_terminal/capabilities/adapters/git_adapter.py +202 -0
- calc_terminal/capabilities/adapters/jupyter_adapter.py +138 -0
- calc_terminal/capabilities/adapters/ml_frameworks.py +158 -0
- calc_terminal/capabilities/adapters/platforms.py +200 -0
- calc_terminal/capabilities/adapters/python_exec.py +93 -0
- calc_terminal/capabilities/adapters/quantum_adapter.py +150 -0
- calc_terminal/capabilities/adapters/scientific_comp.py +123 -0
- calc_terminal/capabilities/adapters/structural_bio.py +161 -0
- calc_terminal/capabilities/adapters/terminal.py +99 -0
- calc_terminal/capabilities/bus.py +178 -0
- calc_terminal/capabilities/discovery.py +207 -0
- calc_terminal/capabilities/schema.py +221 -0
- calc_terminal/cat.ico +0 -0
- calc_terminal/cat_browser.py +2018 -0
- calc_terminal/chat_store.py +703 -0
- calc_terminal/cli.py +1178 -0
- calc_terminal/code_editor.py +640 -0
- calc_terminal/collaboration.py +723 -0
- calc_terminal/commands_data.py +139 -0
- calc_terminal/compatibility_engine.py +352 -0
- calc_terminal/compute/__init__.py +31 -0
- calc_terminal/compute/fabric.py +350 -0
- calc_terminal/config.py +227 -0
- calc_terminal/core/__init__.py +41 -0
- calc_terminal/core/checkpoint.py +156 -0
- calc_terminal/core/input/__init__.py +45 -0
- calc_terminal/core/mode_registry.py +300 -0
- calc_terminal/core/project_graph.py +172 -0
- calc_terminal/core/recovery.py +129 -0
- calc_terminal/core/security_layer.py +112 -0
- calc_terminal/core/task_graph.py +202 -0
- calc_terminal/core/unified_runtime.py +184 -0
- calc_terminal/core/verification.py +257 -0
- calc_terminal/customization.py +1566 -0
- calc_terminal/derivations.py +153 -0
- calc_terminal/device_control.py +263 -0
- calc_terminal/diagnostics/__init__.py +27 -0
- calc_terminal/diagnostics/doctor_engine.py +382 -0
- calc_terminal/diagnostics/self_test.py +247 -0
- calc_terminal/doctor.py +519 -0
- calc_terminal/easter_eggs.py +274 -0
- calc_terminal/editor/__init__.py +1 -0
- calc_terminal/editor/actions.py +263 -0
- calc_terminal/editor/commands.py +160 -0
- calc_terminal/editor/shortcuts.py +226 -0
- calc_terminal/engine.py +259 -0
- calc_terminal/errors.py +120 -0
- calc_terminal/event_stream.py +146 -0
- calc_terminal/eventbus.py +133 -0
- calc_terminal/extensions.py +733 -0
- calc_terminal/fallback_cli.py +1321 -0
- calc_terminal/first_run.py +265 -0
- calc_terminal/fomoji_auth.py +1043 -0
- calc_terminal/formulas.py +82 -0
- calc_terminal/fs_cache.py +121 -0
- calc_terminal/fs_watcher.py +277 -0
- calc_terminal/game.py +193 -0
- calc_terminal/gen1.py +5 -0
- calc_terminal/generators.py +245 -0
- calc_terminal/gestures/__init__.py +42 -0
- calc_terminal/gestures/bindings.py +175 -0
- calc_terminal/gestures/manager.py +477 -0
- calc_terminal/goodbye.py +363 -0
- calc_terminal/gpu3d.py +290 -0
- calc_terminal/graphs.py +358 -0
- calc_terminal/hardware_analyzer.py +440 -0
- calc_terminal/host/__init__.py +30 -0
- calc_terminal/host/browser_manager.py +187 -0
- calc_terminal/host/desktop.py +1386 -0
- calc_terminal/host/launcher.py +395 -0
- calc_terminal/host/terminal.py +279 -0
- calc_terminal/identity.py +216 -0
- calc_terminal/input/__init__.py +54 -0
- calc_terminal/input/capabilities.py +258 -0
- calc_terminal/input/focus.py +87 -0
- calc_terminal/input/gestures.py +64 -0
- calc_terminal/input/pointer.py +114 -0
- calc_terminal/input/touch.py +345 -0
- calc_terminal/keys.py +84 -0
- calc_terminal/live_automation.py +165 -0
- calc_terminal/mathtext.py +433 -0
- calc_terminal/mcp.py +386 -0
- calc_terminal/memory.py +337 -0
- calc_terminal/memory_v2.py +479 -0
- calc_terminal/metrics.py +333 -0
- calc_terminal/mode_detection.py +146 -0
- calc_terminal/model.py +2431 -0
- calc_terminal/model_router.py +665 -0
- calc_terminal/models/__init__.py +0 -0
- calc_terminal/models/active_state.py +187 -0
- calc_terminal/models/dynamic_registry.py +584 -0
- calc_terminal/models/manager.py +781 -0
- calc_terminal/models/model_metadata.json +3526 -0
- calc_terminal/models/profiles.py +194 -0
- calc_terminal/models/registry.py +265 -0
- calc_terminal/models/schema.py +197 -0
- calc_terminal/models/validator.py +287 -0
- calc_terminal/models/verification_engine.py +368 -0
- calc_terminal/native_picker.py +215 -0
- calc_terminal/ollama_catalog.py +279 -0
- calc_terminal/ollama_download.py +233 -0
- calc_terminal/orchestrator.py +304 -0
- calc_terminal/package_research.py +322 -0
- calc_terminal/packages.py +1024 -0
- calc_terminal/pc_specs.py +116 -0
- calc_terminal/permissions.py +334 -0
- calc_terminal/pet.py +106 -0
- calc_terminal/pipeline.py +505 -0
- calc_terminal/platform/__init__.py +491 -0
- calc_terminal/platform/desktop.py +491 -0
- calc_terminal/platform/web.py +781 -0
- calc_terminal/preview/__init__.py +1 -0
- calc_terminal/preview/dev_server.py +303 -0
- calc_terminal/preview/diagnostics.py +131 -0
- calc_terminal/preview/live_reload.py +66 -0
- calc_terminal/preview/manager.py +129 -0
- calc_terminal/project_stats.py +209 -0
- calc_terminal/projects.py +328 -0
- calc_terminal/providers/__init__.py +0 -0
- calc_terminal/providers/adapters/__init__.py +80 -0
- calc_terminal/providers/adapters/anthropic_adapter.py +127 -0
- calc_terminal/providers/adapters/base.py +106 -0
- calc_terminal/providers/adapters/chinese_adapters.py +420 -0
- calc_terminal/providers/adapters/gemini_adapter.py +101 -0
- calc_terminal/providers/adapters/ollama_adapter.py +83 -0
- calc_terminal/providers/adapters/openai_adapter.py +159 -0
- calc_terminal/providers/adapters/other_adapters.py +246 -0
- calc_terminal/providers/anthropic_provider.py +172 -0
- calc_terminal/providers/auto_update.py +416 -0
- calc_terminal/providers/base_provider.py +105 -0
- calc_terminal/providers/discovery_manager.py +207 -0
- calc_terminal/providers/gemini_provider.py +178 -0
- calc_terminal/providers/lifecycle.py +767 -0
- calc_terminal/providers/ollama_adapter.py +707 -0
- calc_terminal/providers/openai_provider.py +254 -0
- calc_terminal/providers/provider_manager.py +1827 -0
- calc_terminal/providers/providers.json +4075 -0
- calc_terminal/reactionsim.py +279 -0
- calc_terminal/registry.py +337 -0
- calc_terminal/report.py +162 -0
- calc_terminal/research/__init__.py +45 -0
- calc_terminal/research/artifact_intel.py +126 -0
- calc_terminal/research/data_lineage.py +123 -0
- calc_terminal/research/experiment_ledger.py +303 -0
- calc_terminal/research/reproducibility.py +131 -0
- calc_terminal/resilience/__init__.py +47 -0
- calc_terminal/resilience/agent_state.py +121 -0
- calc_terminal/resilience/capability_matcher.py +174 -0
- calc_terminal/resilience/circuit_breaker.py +158 -0
- calc_terminal/resilience/failover_engine.py +230 -0
- calc_terminal/resilience/health_monitor.py +192 -0
- calc_terminal/resilience/ollama_adapter.py +125 -0
- calc_terminal/resilience/orchestrator.py +312 -0
- calc_terminal/resilience/types.py +134 -0
- calc_terminal/sandbox.py +98 -0
- calc_terminal/scires.py +558 -0
- calc_terminal/security_scanner.py +126 -0
- calc_terminal/session.py +294 -0
- calc_terminal/sim3d.py +206 -0
- calc_terminal/solver.py +276 -0
- calc_terminal/sound.py +127 -0
- calc_terminal/task_reports.py +287 -0
- calc_terminal/terminal_host.py +201 -0
- calc_terminal/terminal_identity.py +411 -0
- calc_terminal/test_ai_mode_reliability.py +344 -0
- calc_terminal/test_browser.py +368 -0
- calc_terminal/test_code_editor_upgrade.py +485 -0
- calc_terminal/test_customization.py +1148 -0
- calc_terminal/test_customization_ui.py +612 -0
- calc_terminal/test_dynamic_registry.py +304 -0
- calc_terminal/test_extensions.py +436 -0
- calc_terminal/test_overhaul.py +557 -0
- calc_terminal/test_project_detect.py +255 -0
- calc_terminal/test_root_cause_fix.py +527 -0
- calc_terminal/test_stability.py +532 -0
- calc_terminal/test_terminal_identity.py +132 -0
- calc_terminal/test_v079_speed.py +460 -0
- calc_terminal/theme.py +1107 -0
- calc_terminal/timeline.py +139 -0
- calc_terminal/todos.py +246 -0
- calc_terminal/tool_call_normalizer.py +419 -0
- calc_terminal/tui.py +104 -0
- calc_terminal/ui/__init__.py +8 -0
- calc_terminal/ui/activity_panel.py +231 -0
- calc_terminal/ui/activity_stream_panel.py +238 -0
- calc_terminal/ui/animations.py +122 -0
- calc_terminal/ui/app.py +7271 -0
- calc_terminal/ui/attach_panel.py +597 -0
- calc_terminal/ui/attachments.py +424 -0
- calc_terminal/ui/backup_panel.py +810 -0
- calc_terminal/ui/browser_shell.py +887 -0
- calc_terminal/ui/cat_agent.py +357 -0
- calc_terminal/ui/chats_panel.py +899 -0
- calc_terminal/ui/command_palette.py +125 -0
- calc_terminal/ui/command_palette_modal.py +166 -0
- calc_terminal/ui/composer.py +1141 -0
- calc_terminal/ui/context_menu.py +197 -0
- calc_terminal/ui/conversation.py +1435 -0
- calc_terminal/ui/customization_panel.py +1229 -0
- calc_terminal/ui/dashboard.py +404 -0
- calc_terminal/ui/design_system.py +557 -0
- calc_terminal/ui/diff_panel.py +213 -0
- calc_terminal/ui/editor.py +2102 -0
- calc_terminal/ui/empty_state.py +302 -0
- calc_terminal/ui/events.py +487 -0
- calc_terminal/ui/extensions_panel.py +815 -0
- calc_terminal/ui/footer.py +166 -0
- calc_terminal/ui/gestures_panel.py +383 -0
- calc_terminal/ui/goodbye_screen.py +100 -0
- calc_terminal/ui/header.py +1034 -0
- calc_terminal/ui/help_panel.py +254 -0
- calc_terminal/ui/live_activities.py +914 -0
- calc_terminal/ui/mcp_panel.py +570 -0
- calc_terminal/ui/memory_center.py +524 -0
- calc_terminal/ui/mode_colors_panel.py +525 -0
- calc_terminal/ui/nav_screens.py +747 -0
- calc_terminal/ui/ollama_panel.py +536 -0
- calc_terminal/ui/palette.py +221 -0
- calc_terminal/ui/permission_panel.py +269 -0
- calc_terminal/ui/personalization_panel.py +517 -0
- calc_terminal/ui/personalize_center.py +1568 -0
- calc_terminal/ui/preview_panel.py +441 -0
- calc_terminal/ui/resizers.py +402 -0
- calc_terminal/ui/sidebar.py +1285 -0
- calc_terminal/ui/statusbar.py +168 -0
- calc_terminal/ui/theme_css.py +1396 -0
- calc_terminal/ui/thinking.py +226 -0
- calc_terminal/ui/timeline_panel.py +102 -0
- calc_terminal/ui/todo_panel.py +193 -0
- calc_terminal/ui/viewport.py +136 -0
- calc_terminal/ui/vision_panel.py +489 -0
- calc_terminal/ui/welcome_modal.py +343 -0
- calc_terminal/ui/widgets.py +160 -0
- calc_terminal/ui/workspace.py +831 -0
- calc_terminal/viewers/__init__.py +1 -0
- calc_terminal/viewers/document_viewer.py +252 -0
- calc_terminal/viewers/image_viewer.py +241 -0
- calc_terminal/viewers/pdf_viewer.py +203 -0
- calc_terminal/viewers/presentation_viewer.py +164 -0
- calc_terminal/viewers/registry.py +120 -0
- calc_terminal/viewers/spreadsheet_viewer.py +204 -0
- calc_terminal/vision/__init__.py +89 -0
- calc_terminal/vision/analysis.py +194 -0
- calc_terminal/vision/annotations.py +297 -0
- calc_terminal/vision/capture.py +171 -0
- calc_terminal/vision/context.py +231 -0
- calc_terminal/vision/correlation.py +169 -0
- calc_terminal/vision/cursor.py +258 -0
- calc_terminal/vision/events.py +66 -0
- calc_terminal/vision/frame_pipeline.py +259 -0
- calc_terminal/vision/priority.py +218 -0
- calc_terminal/vision/provider.py +180 -0
- calc_terminal/vision/safety.py +149 -0
- calc_terminal/vision/session.py +281 -0
- calc_terminal/vision/verify.py +162 -0
- calc_terminal/vision.py +514 -0
- calc_terminal/vscode_integration.py +113 -0
- calc_terminal/web/__init__.py +8 -0
- calc_terminal/web/cat_runtime.py +710 -0
- calc_terminal/web/server.py +2891 -0
- calc_terminal/web/static/css/app.css +3152 -0
- calc_terminal/web/static/icons/badge-72.png +0 -0
- calc_terminal/web/static/icons/cat.ico +0 -0
- calc_terminal/web/static/icons/icon-128.png +0 -0
- calc_terminal/web/static/icons/icon-144.png +0 -0
- calc_terminal/web/static/icons/icon-152.png +0 -0
- calc_terminal/web/static/icons/icon-192.png +0 -0
- calc_terminal/web/static/icons/icon-384.png +0 -0
- calc_terminal/web/static/icons/icon-512.png +0 -0
- calc_terminal/web/static/icons/icon-72.png +0 -0
- calc_terminal/web/static/icons/icon-96.png +0 -0
- calc_terminal/web/static/icons/icon.svg +34 -0
- calc_terminal/web/static/icons/new-project.png +0 -0
- calc_terminal/web/static/icons/open-project.png +0 -0
- calc_terminal/web/static/index.html +734 -0
- calc_terminal/web/static/js/app.js +2403 -0
- calc_terminal/web/static/manifest.json +88 -0
- calc_terminal/web/static/sw.js +230 -0
- calc_terminal/workflow_engine.py +769 -0
- calc_terminal/workspace.py +593 -0
- calc_terminal/workspace_index.py +385 -0
- cct_cli-0.7.9.0.dist-info/METADATA +210 -0
- cct_cli-0.7.9.0.dist-info/RECORD +325 -0
- cct_cli-0.7.9.0.dist-info/WHEEL +5 -0
- cct_cli-0.7.9.0.dist-info/entry_points.txt +4 -0
- cct_cli-0.7.9.0.dist-info/licenses/LICENSE +21 -0
- cct_cli-0.7.9.0.dist-info/top_level.txt +1 -0
calc_terminal/report.py
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CCT Report Generator — exports solved notebooks (from session history)
|
|
3
|
+
to a real Markdown file and a real PDF (via reportlab, already a
|
|
4
|
+
dependency). This is a straight, faithful export of the same
|
|
5
|
+
Question/Given/Find/Formula/Substitution/Calculation/Units/Verification
|
|
6
|
+
/Answer structure already rendered on screen — not a separate "fake"
|
|
7
|
+
summary.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
import re
|
|
12
|
+
|
|
13
|
+
from .mathtext import compose, mathify
|
|
14
|
+
|
|
15
|
+
PDF_AVAILABLE = True
|
|
16
|
+
try:
|
|
17
|
+
from reportlab.lib.pagesizes import LETTER
|
|
18
|
+
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
|
|
19
|
+
from reportlab.lib.units import inch
|
|
20
|
+
from reportlab.lib import colors
|
|
21
|
+
from reportlab.platypus import (
|
|
22
|
+
SimpleDocTemplate, Paragraph, Spacer, ListFlowable, ListItem, HRFlowable
|
|
23
|
+
)
|
|
24
|
+
except Exception:
|
|
25
|
+
PDF_AVAILABLE = False
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
_ANSI_RE = re.compile(r"\x1b\[[0-9;]*m")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _plain(s):
|
|
32
|
+
return _ANSI_RE.sub("", str(s))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _notebook_to_markdown(nb, index):
|
|
36
|
+
lines = [f"## {index}. {nb.get('topic', 'Notebook')}", ""]
|
|
37
|
+
lines.append(f"**Question:** {_plain(nb.get('question', ''))}")
|
|
38
|
+
lines.append("")
|
|
39
|
+
lines.append("**Given data:**")
|
|
40
|
+
for g in nb.get("given", []):
|
|
41
|
+
lines.append(f"- {_plain(g)}")
|
|
42
|
+
lines.append("")
|
|
43
|
+
lines.append(f"**Find:** {_plain(nb.get('find', ''))}")
|
|
44
|
+
lines.append("")
|
|
45
|
+
lines.append("**Formula:**")
|
|
46
|
+
lines.append("```")
|
|
47
|
+
lines.append(_plain(compose(nb.get("formula", []))))
|
|
48
|
+
lines.append("```")
|
|
49
|
+
lines.append("")
|
|
50
|
+
lines.append("**Substitution:**")
|
|
51
|
+
lines.append("```")
|
|
52
|
+
lines.append(_plain(compose(nb.get("substitution", []))))
|
|
53
|
+
lines.append("```")
|
|
54
|
+
lines.append("")
|
|
55
|
+
calc = nb.get("calculation", [])
|
|
56
|
+
if calc:
|
|
57
|
+
lines.append("**Calculation:**")
|
|
58
|
+
for i, c in enumerate(calc, 1):
|
|
59
|
+
lines.append(f"{i}. {_plain(mathify(c))}")
|
|
60
|
+
lines.append("")
|
|
61
|
+
if nb.get("unit"):
|
|
62
|
+
lines.append(f"**Units:** {_plain(mathify(nb['unit']))}")
|
|
63
|
+
lines.append("")
|
|
64
|
+
if nb.get("verification"):
|
|
65
|
+
lines.append(f"**Verification:** {_plain(mathify(nb['verification']))}")
|
|
66
|
+
lines.append("")
|
|
67
|
+
lines.append(f"**Final answer:** {_plain(nb.get('final_answer', ''))}")
|
|
68
|
+
lines.append("")
|
|
69
|
+
lines.append("---")
|
|
70
|
+
lines.append("")
|
|
71
|
+
return lines
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def export_history_markdown(history, path, title="CAT Notebook Export"):
|
|
75
|
+
if not history:
|
|
76
|
+
raise ValueError("No history to export.")
|
|
77
|
+
lines = [f"# {title}", ""]
|
|
78
|
+
lines.append(f"_{len(history)} notebook(s), exported from Chemistry Calc Terminal_")
|
|
79
|
+
lines.append("")
|
|
80
|
+
for i, nb in enumerate(history, 1):
|
|
81
|
+
lines.extend(_notebook_to_markdown(nb, i))
|
|
82
|
+
text = "\n".join(lines)
|
|
83
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
84
|
+
f.write(text)
|
|
85
|
+
return path
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def export_history_pdf(history, path, title="CAT Notebook Export"):
|
|
89
|
+
if not PDF_AVAILABLE:
|
|
90
|
+
raise RuntimeError("reportlab is not installed — run: pip install reportlab")
|
|
91
|
+
if not history:
|
|
92
|
+
raise ValueError("No history to export.")
|
|
93
|
+
|
|
94
|
+
doc = SimpleDocTemplate(path, pagesize=LETTER,
|
|
95
|
+
topMargin=0.7 * inch, bottomMargin=0.7 * inch,
|
|
96
|
+
leftMargin=0.75 * inch, rightMargin=0.75 * inch)
|
|
97
|
+
styles = getSampleStyleSheet()
|
|
98
|
+
h1 = ParagraphStyle("h1", parent=styles["Heading1"], textColor=colors.HexColor("#3b2a86"))
|
|
99
|
+
h2 = ParagraphStyle("h2", parent=styles["Heading2"], textColor=colors.HexColor("#1f6fb2"),
|
|
100
|
+
spaceBefore=14)
|
|
101
|
+
body = ParagraphStyle("body", parent=styles["BodyText"], leading=15)
|
|
102
|
+
mono = ParagraphStyle("mono", parent=styles["Code"], backColor=colors.HexColor("#f4f4f8"),
|
|
103
|
+
borderPadding=6, leading=13)
|
|
104
|
+
answer = ParagraphStyle("answer", parent=styles["BodyText"],
|
|
105
|
+
textColor=colors.HexColor("#0a7d3c"), fontName="Helvetica-Bold")
|
|
106
|
+
|
|
107
|
+
def esc(s):
|
|
108
|
+
return (_plain(s).replace("&", "&").replace("<", "<").replace(">", ">"))
|
|
109
|
+
|
|
110
|
+
story = [Paragraph(esc(title), h1),
|
|
111
|
+
Paragraph(f"{len(history)} notebook(s) — Chemistry Calc Terminal", body),
|
|
112
|
+
Spacer(1, 12)]
|
|
113
|
+
|
|
114
|
+
for i, nb in enumerate(history, 1):
|
|
115
|
+
story.append(Paragraph(f"{i}. {esc(nb.get('topic', 'Notebook'))}", h2))
|
|
116
|
+
story.append(Paragraph(f"<b>Question:</b> {esc(nb.get('question', ''))}", body))
|
|
117
|
+
given = nb.get("given", [])
|
|
118
|
+
if given:
|
|
119
|
+
story.append(Paragraph("<b>Given data:</b>", body))
|
|
120
|
+
story.append(ListFlowable(
|
|
121
|
+
[ListItem(Paragraph(esc(g), body)) for g in given], bulletType="bullet"))
|
|
122
|
+
story.append(Paragraph(f"<b>Find:</b> {esc(nb.get('find', ''))}", body))
|
|
123
|
+
story.append(Paragraph("<b>Formula:</b>", body))
|
|
124
|
+
story.append(Paragraph(esc(compose(nb.get("formula", []))).replace("\n", "<br/>"), mono))
|
|
125
|
+
story.append(Paragraph("<b>Substitution:</b>", body))
|
|
126
|
+
story.append(Paragraph(esc(compose(nb.get("substitution", []))).replace("\n", "<br/>"), mono))
|
|
127
|
+
calc = nb.get("calculation", [])
|
|
128
|
+
if calc:
|
|
129
|
+
story.append(Paragraph("<b>Calculation:</b>", body))
|
|
130
|
+
story.append(ListFlowable(
|
|
131
|
+
[ListItem(Paragraph(esc(mathify(c)), body)) for c in calc], bulletType="1"))
|
|
132
|
+
if nb.get("unit"):
|
|
133
|
+
story.append(Paragraph(f"<b>Units:</b> {esc(mathify(nb['unit']))}", body))
|
|
134
|
+
if nb.get("verification"):
|
|
135
|
+
story.append(Paragraph(f"<b>Verification:</b> {esc(mathify(nb['verification']))}", body))
|
|
136
|
+
story.append(Paragraph(f"Final answer: {esc(nb.get('final_answer', ''))}", answer))
|
|
137
|
+
story.append(HRFlowable(width="100%", color=colors.HexColor("#dddddd"), spaceBefore=10, spaceAfter=10))
|
|
138
|
+
|
|
139
|
+
doc.build(story)
|
|
140
|
+
return path
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def default_export_dir():
|
|
144
|
+
"""Notebook exports go into the shared Project Workspace now (spec
|
|
145
|
+
section 16, calc_terminal/workspace.py) instead of report.py's own
|
|
146
|
+
~/CCT_exports — which, before this, was a *different* directory
|
|
147
|
+
from scires.py's ~/cct_exports (different casing, never the same
|
|
148
|
+
folder). Falls back to the old location only if the workspace
|
|
149
|
+
module can't be imported, so a broken workspace config never
|
|
150
|
+
blocks an export outright.
|
|
151
|
+
|
|
152
|
+
STRICT FIX v0.7.9.6: explicit ensure=True — this is a WRITE path
|
|
153
|
+
(the user explicitly requested an export), so directory creation
|
|
154
|
+
is authorized here. Read paths (summary/list_category) use
|
|
155
|
+
ensure=False and never create."""
|
|
156
|
+
try:
|
|
157
|
+
from . import workspace
|
|
158
|
+
return workspace.category_dir("reports", ensure=True)
|
|
159
|
+
except Exception:
|
|
160
|
+
d = os.path.join(os.path.expanduser("~"), "CCT_exports")
|
|
161
|
+
os.makedirs(d, exist_ok=True)
|
|
162
|
+
return d
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CAT Research Subsystem per §48-§51, §58-§63, §97-§98:
|
|
3
|
+
- Experiment Ledger & Scientific Time Machine
|
|
4
|
+
- Data Lineage Graph
|
|
5
|
+
- Semantic Artifact Intelligence
|
|
6
|
+
- Reproducibility Engine & Package Generation
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from calc_terminal.research.experiment_ledger import (
|
|
12
|
+
ExperimentRecord,
|
|
13
|
+
ExperimentVersion,
|
|
14
|
+
ExperimentLedger,
|
|
15
|
+
experiment_ledger,
|
|
16
|
+
)
|
|
17
|
+
from calc_terminal.research.data_lineage import (
|
|
18
|
+
LineageNode,
|
|
19
|
+
DataLineageGraph,
|
|
20
|
+
data_lineage,
|
|
21
|
+
)
|
|
22
|
+
from calc_terminal.research.artifact_intel import (
|
|
23
|
+
ArtifactSemantics,
|
|
24
|
+
analyze_artifact,
|
|
25
|
+
)
|
|
26
|
+
from calc_terminal.research.reproducibility import (
|
|
27
|
+
ReproductionReport,
|
|
28
|
+
ReproducibilityEngine,
|
|
29
|
+
reproducibility_engine,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
__all__ = [
|
|
33
|
+
"ExperimentRecord",
|
|
34
|
+
"ExperimentVersion",
|
|
35
|
+
"ExperimentLedger",
|
|
36
|
+
"experiment_ledger",
|
|
37
|
+
"LineageNode",
|
|
38
|
+
"DataLineageGraph",
|
|
39
|
+
"data_lineage",
|
|
40
|
+
"ArtifactSemantics",
|
|
41
|
+
"analyze_artifact",
|
|
42
|
+
"ReproductionReport",
|
|
43
|
+
"ReproducibilityEngine",
|
|
44
|
+
"reproducibility_engine",
|
|
45
|
+
]
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Semantic Artifact Intelligence per §51:
|
|
3
|
+
Understands the semantic domain and compatible tool capabilities for files:
|
|
4
|
+
- .pdb, .cif -> Protein 3D Structure
|
|
5
|
+
- .fasta, .fa -> Biological Sequence
|
|
6
|
+
- .safetensors, .pt -> Machine Learning Model
|
|
7
|
+
- .ipynb -> Jupyter Notebook
|
|
8
|
+
- .csv, .parquet -> Dataset
|
|
9
|
+
- .png, .svg -> Scientific Visualization
|
|
10
|
+
- .qasm -> Quantum Circuit
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import os
|
|
16
|
+
from dataclasses import dataclass
|
|
17
|
+
from typing import Any, Dict, List, Optional
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class ArtifactSemantics:
|
|
22
|
+
path: str
|
|
23
|
+
filename: str
|
|
24
|
+
semantic_type: str
|
|
25
|
+
domain: str
|
|
26
|
+
recommended_capabilities: List[str]
|
|
27
|
+
description: str
|
|
28
|
+
|
|
29
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
30
|
+
return {
|
|
31
|
+
"path": self.path,
|
|
32
|
+
"filename": self.filename,
|
|
33
|
+
"semantic_type": self.semantic_type,
|
|
34
|
+
"domain": self.domain,
|
|
35
|
+
"recommended_capabilities": self.recommended_capabilities,
|
|
36
|
+
"description": self.description,
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def analyze_artifact(file_path: str) -> ArtifactSemantics:
|
|
41
|
+
"""Classify and return semantic domain and recommended capabilities for an artifact."""
|
|
42
|
+
norm = os.path.normpath(file_path)
|
|
43
|
+
base = os.path.basename(norm).lower()
|
|
44
|
+
ext = os.path.splitext(base)[1].lower()
|
|
45
|
+
|
|
46
|
+
if ext in (".pdb", ".cif", ".mmcif"):
|
|
47
|
+
return ArtifactSemantics(
|
|
48
|
+
path=norm,
|
|
49
|
+
filename=base,
|
|
50
|
+
semantic_type="protein_structure",
|
|
51
|
+
domain="structural_biology",
|
|
52
|
+
recommended_capabilities=["pymol.open", "chimerax.open", "biopython.parse"],
|
|
53
|
+
description="3D macromolecular coordinate structure file",
|
|
54
|
+
)
|
|
55
|
+
elif ext in (".fasta", ".fa", ".fna", ".faa"):
|
|
56
|
+
return ArtifactSemantics(
|
|
57
|
+
path=norm,
|
|
58
|
+
filename=base,
|
|
59
|
+
semantic_type="biological_sequence",
|
|
60
|
+
domain="bioinformatics",
|
|
61
|
+
recommended_capabilities=["blast.search", "ncbi.query", "biopython.parse"],
|
|
62
|
+
description="Nucleotide or amino acid biological sequence file",
|
|
63
|
+
)
|
|
64
|
+
elif ext in (".safetensors", ".pt", ".pth", ".onnx", ".bin"):
|
|
65
|
+
return ArtifactSemantics(
|
|
66
|
+
path=norm,
|
|
67
|
+
filename=base,
|
|
68
|
+
semantic_type="ml_model",
|
|
69
|
+
domain="machine_learning",
|
|
70
|
+
recommended_capabilities=["pytorch.eval", "huggingface.model", "ml.detect_hardware"],
|
|
71
|
+
description="Trained neural network model weights or serialized computational graph",
|
|
72
|
+
)
|
|
73
|
+
elif ext == ".ipynb":
|
|
74
|
+
return ArtifactSemantics(
|
|
75
|
+
path=norm,
|
|
76
|
+
filename=base,
|
|
77
|
+
semantic_type="jupyter_notebook",
|
|
78
|
+
domain="notebook_computing",
|
|
79
|
+
recommended_capabilities=["jupyter.execute", "jupyter.list_servers", "python.execute"],
|
|
80
|
+
description="Interactive literate computing Jupyter notebook",
|
|
81
|
+
)
|
|
82
|
+
elif ext in (".csv", ".tsv", ".parquet", ".arrow", ".jsonl"):
|
|
83
|
+
return ArtifactSemantics(
|
|
84
|
+
path=norm,
|
|
85
|
+
filename=base,
|
|
86
|
+
semantic_type="dataset",
|
|
87
|
+
domain="data_science",
|
|
88
|
+
recommended_capabilities=["numpy.compute", "kaggle.dataset", "python.execute"],
|
|
89
|
+
description="Tabular or columnar scientific/machine learning dataset",
|
|
90
|
+
)
|
|
91
|
+
elif ext in (".png", ".svg", ".pdf", ".eps"):
|
|
92
|
+
return ArtifactSemantics(
|
|
93
|
+
path=norm,
|
|
94
|
+
filename=base,
|
|
95
|
+
semantic_type="scientific_figure",
|
|
96
|
+
domain="scientific_visualization",
|
|
97
|
+
recommended_capabilities=["browser.navigate", "filesystem.read"],
|
|
98
|
+
description="Rendered scientific visualization or publication figure",
|
|
99
|
+
)
|
|
100
|
+
elif ext in (".qasm",):
|
|
101
|
+
return ArtifactSemantics(
|
|
102
|
+
path=norm,
|
|
103
|
+
filename=base,
|
|
104
|
+
semantic_type="quantum_circuit",
|
|
105
|
+
domain="quantum_computing",
|
|
106
|
+
recommended_capabilities=["qiskit.run_circuit", "quantum.bloch_sphere"],
|
|
107
|
+
description="OpenQASM quantum circuit specification",
|
|
108
|
+
)
|
|
109
|
+
elif ext in (".py",):
|
|
110
|
+
return ArtifactSemantics(
|
|
111
|
+
path=norm,
|
|
112
|
+
filename=base,
|
|
113
|
+
semantic_type="source_code",
|
|
114
|
+
domain="software_engineering",
|
|
115
|
+
recommended_capabilities=["python.execute", "git.diff", "filesystem.read"],
|
|
116
|
+
description="Python executable source code",
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
return ArtifactSemantics(
|
|
120
|
+
path=norm,
|
|
121
|
+
filename=base,
|
|
122
|
+
semantic_type="generic_file",
|
|
123
|
+
domain="general",
|
|
124
|
+
recommended_capabilities=["filesystem.read"],
|
|
125
|
+
description="General file",
|
|
126
|
+
)
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CAT Data Lineage Graph per §58, §97:
|
|
3
|
+
Tracks end-to-end transformation lineage from raw source to figures and publication reports.
|
|
4
|
+
Explains dependency chains when upstream or downstream artifacts change.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import time
|
|
10
|
+
import os
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from typing import Any, Dict, List, Optional, Set
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class LineageNode:
|
|
17
|
+
path: str
|
|
18
|
+
artifact_type: str # dataset, features, model, predictions, figure, report
|
|
19
|
+
sources: List[str] = field(default_factory=list) # parent artifact paths
|
|
20
|
+
transform_tool: str = "" # tool or script that produced this artifact
|
|
21
|
+
experiment_id: Optional[str] = None
|
|
22
|
+
task_id: Optional[str] = None
|
|
23
|
+
created_at: float = field(default_factory=time.time)
|
|
24
|
+
metadata: Dict[str, Any] = field(default_factory=dict)
|
|
25
|
+
|
|
26
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
27
|
+
return {
|
|
28
|
+
"path": self.path,
|
|
29
|
+
"artifact_type": self.artifact_type,
|
|
30
|
+
"sources": self.sources,
|
|
31
|
+
"transform_tool": self.transform_tool,
|
|
32
|
+
"experiment_id": self.experiment_id,
|
|
33
|
+
"task_id": self.task_id,
|
|
34
|
+
"created_at": self.created_at,
|
|
35
|
+
"metadata": self.metadata,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class DataLineageGraph:
|
|
40
|
+
"""DAG tracking artifact generation, transformations, and dependency explanations."""
|
|
41
|
+
|
|
42
|
+
def __init__(self):
|
|
43
|
+
self.nodes: Dict[str, LineageNode] = {}
|
|
44
|
+
|
|
45
|
+
def record_transformation(
|
|
46
|
+
self,
|
|
47
|
+
target_path: Optional[str] = None,
|
|
48
|
+
sources: Optional[List[str]] = None,
|
|
49
|
+
transform_tool: str = "",
|
|
50
|
+
artifact_type: str = "artifact",
|
|
51
|
+
experiment_id: Optional[str] = None,
|
|
52
|
+
task_id: Optional[str] = None,
|
|
53
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
54
|
+
output_path: Optional[str] = None,
|
|
55
|
+
) -> LineageNode:
|
|
56
|
+
actual_path = target_path or output_path or ""
|
|
57
|
+
norm_target = os.path.normpath(actual_path)
|
|
58
|
+
norm_sources = [os.path.normpath(s) for s in (sources or [])]
|
|
59
|
+
node = LineageNode(
|
|
60
|
+
path=norm_target,
|
|
61
|
+
artifact_type=artifact_type,
|
|
62
|
+
sources=norm_sources,
|
|
63
|
+
transform_tool=transform_tool,
|
|
64
|
+
experiment_id=experiment_id,
|
|
65
|
+
task_id=task_id,
|
|
66
|
+
metadata=metadata or {},
|
|
67
|
+
)
|
|
68
|
+
self.nodes[norm_target] = node
|
|
69
|
+
return node
|
|
70
|
+
|
|
71
|
+
def get_upstream_lineage(self, target_path: str) -> List[str]:
|
|
72
|
+
"""Return list of all ancestor artifacts that produced target_path."""
|
|
73
|
+
norm_target = os.path.normpath(target_path)
|
|
74
|
+
visited = []
|
|
75
|
+
queue = [norm_target]
|
|
76
|
+
while queue:
|
|
77
|
+
curr = queue.pop(0)
|
|
78
|
+
node = self.nodes.get(curr)
|
|
79
|
+
if node:
|
|
80
|
+
for s in node.sources:
|
|
81
|
+
if s not in visited:
|
|
82
|
+
visited.append(s)
|
|
83
|
+
queue.append(s)
|
|
84
|
+
return visited
|
|
85
|
+
|
|
86
|
+
def get_upstream(self, target_path: str) -> List[str]:
|
|
87
|
+
return self.get_upstream_lineage(target_path)
|
|
88
|
+
|
|
89
|
+
def get_downstream_impact(self, source_path: str) -> List[str]:
|
|
90
|
+
"""Return list of all downstream artifacts dependent on source_path."""
|
|
91
|
+
norm_source = os.path.normpath(source_path)
|
|
92
|
+
impacted = []
|
|
93
|
+
for path, node in self.nodes.items():
|
|
94
|
+
if norm_source in node.sources:
|
|
95
|
+
impacted.append(path)
|
|
96
|
+
impacted.extend(self.get_downstream_impact(path))
|
|
97
|
+
return list(dict.fromkeys(impacted))
|
|
98
|
+
|
|
99
|
+
def get_downstream(self, source_path: str) -> List[str]:
|
|
100
|
+
return self.get_downstream_impact(source_path)
|
|
101
|
+
|
|
102
|
+
def explain_chain(self, target_path: str) -> str:
|
|
103
|
+
"""Human-readable explanation of how this artifact was generated."""
|
|
104
|
+
norm = os.path.normpath(target_path)
|
|
105
|
+
node = self.nodes.get(norm)
|
|
106
|
+
if not node:
|
|
107
|
+
return f"No recorded lineage for artifact: {os.path.basename(target_path)}"
|
|
108
|
+
|
|
109
|
+
lines = [f"Artifact: {os.path.basename(norm)} ({node.artifact_type})"]
|
|
110
|
+
if node.transform_tool:
|
|
111
|
+
lines.append(f"Produced by: {node.transform_tool}")
|
|
112
|
+
if node.experiment_id:
|
|
113
|
+
lines.append(f"Experiment: {node.experiment_id}")
|
|
114
|
+
if node.sources:
|
|
115
|
+
lines.append("Source dependencies:")
|
|
116
|
+
for s in node.sources:
|
|
117
|
+
src_node = self.nodes.get(s)
|
|
118
|
+
tool_note = f" (via {src_node.transform_tool})" if src_node and src_node.transform_tool else ""
|
|
119
|
+
lines.append(f" └── {os.path.basename(s)}{tool_note}")
|
|
120
|
+
return "\n".join(lines)
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
data_lineage = DataLineageGraph()
|