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/solver.py
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Universal Chemistry Formula Calculator.
|
|
3
|
+
|
|
4
|
+
Two modes:
|
|
5
|
+
|
|
6
|
+
1. Library mode — pick any of ~35 real chemistry formulas spanning
|
|
7
|
+
kinetics, gas laws, thermodynamics, electrochemistry, equilibrium,
|
|
8
|
+
solutions and atomic/quantum structure. Supply the values you know,
|
|
9
|
+
leave the one you want blank, and it's solved symbolically with
|
|
10
|
+
sympy for the missing variable — not hardcoded per-direction code.
|
|
11
|
+
|
|
12
|
+
2. Free-form mode — type ANY formula (e.g. "P*V = n*R*T" or
|
|
13
|
+
"y = m*x + c") plus known variable=value pairs, and calc_terminal
|
|
14
|
+
solves for whatever's left, using real symbolic algebra.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
try:
|
|
18
|
+
import sympy as sp
|
|
19
|
+
_HAS_SYMPY = True
|
|
20
|
+
except ImportError:
|
|
21
|
+
sp = None
|
|
22
|
+
_HAS_SYMPY = False
|
|
23
|
+
|
|
24
|
+
CONSTANTS = {
|
|
25
|
+
"R": 8.314, # J / (mol K)
|
|
26
|
+
"Rgas_atm": 0.0821, # L atm / (mol K)
|
|
27
|
+
"NA": 6.022e23, # Avogadro's number
|
|
28
|
+
"h": 6.626e-34, # Planck's constant, J s
|
|
29
|
+
"c": 3.0e8, # speed of light, m/s
|
|
30
|
+
"k_B": 1.381e-23, # Boltzmann constant
|
|
31
|
+
"F": 96500, # Faraday constant, C/mol
|
|
32
|
+
"e": 2.71828182845904523536,
|
|
33
|
+
"pi": 3.14159265358979323846,
|
|
34
|
+
"Rinf": 1.097e7, # Rydberg constant, 1/m
|
|
35
|
+
"a0": 5.29e-11, # Bohr radius, m
|
|
36
|
+
"me": 9.109e-31, # electron mass, kg
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# Each entry: id -> (Display name, category, "lhs = rhs" string, {symbol: description})
|
|
40
|
+
FORMULA_LIBRARY = {
|
|
41
|
+
"kin_zero": ("Zero Order Kinetics", "Kinetics", "R = R0 - k*t",
|
|
42
|
+
{"R": "concentration at t", "R0": "initial concentration", "k": "rate constant", "t": "time"}),
|
|
43
|
+
"kin_first": ("First Order Rate Law", "Kinetics", "k = (2.303/t) * log(R0/R, 10)",
|
|
44
|
+
{"k": "rate constant", "t": "time", "R0": "initial concentration", "R": "concentration at t"}),
|
|
45
|
+
"kin_second": ("Second Order Kinetics", "Kinetics", "1/R = 1/R0 + k*t",
|
|
46
|
+
{"R": "concentration at t", "R0": "initial concentration", "k": "rate constant", "t": "time"}),
|
|
47
|
+
"kin_third": ("Third Order Kinetics", "Kinetics", "1/R**2 = 1/R0**2 + 2*k*t",
|
|
48
|
+
{"R": "concentration at t", "R0": "initial concentration", "k": "rate constant", "t": "time"}),
|
|
49
|
+
"half_life_1": ("First Order Half-Life", "Kinetics", "t_half = 0.693/k",
|
|
50
|
+
{"t_half": "half life", "k": "rate constant"}),
|
|
51
|
+
"half_life_0": ("Zero Order Half-Life", "Kinetics", "t_half = R0/(2*k)",
|
|
52
|
+
{"t_half": "half life", "R0": "initial concentration", "k": "rate constant"}),
|
|
53
|
+
"half_life_2": ("Second Order Half-Life", "Kinetics", "t_half = 1/(k*R0)",
|
|
54
|
+
{"t_half": "half life", "k": "rate constant", "R0": "initial concentration"}),
|
|
55
|
+
"arrhenius": ("Arrhenius Equation", "Kinetics", "k = A * exp(-Ea/(R*T))",
|
|
56
|
+
{"k": "rate constant", "A": "pre-exponential factor", "Ea": "activation energy (J/mol)",
|
|
57
|
+
"R": "gas constant", "T": "temperature (K)"}),
|
|
58
|
+
"ideal_gas": ("Ideal Gas Law", "Gas Laws", "P*V = n*R*T",
|
|
59
|
+
{"P": "pressure", "V": "volume", "n": "moles", "R": "gas constant", "T": "temperature (K)"}),
|
|
60
|
+
"boyles": ("Boyle's Law", "Gas Laws", "P1*V1 = P2*V2",
|
|
61
|
+
{"P1": "initial pressure", "V1": "initial volume", "P2": "final pressure", "V2": "final volume"}),
|
|
62
|
+
"charles": ("Charles's Law", "Gas Laws", "V1/T1 = V2/T2",
|
|
63
|
+
{"V1": "initial volume", "T1": "initial temp (K)", "V2": "final volume", "T2": "final temp (K)"}),
|
|
64
|
+
"gay_lussac": ("Gay-Lussac's Law", "Gas Laws", "P1/T1 = P2/T2",
|
|
65
|
+
{"P1": "initial pressure", "T1": "initial temp (K)", "P2": "final pressure", "T2": "final temp (K)"}),
|
|
66
|
+
"combined_gas": ("Combined Gas Law", "Gas Laws", "(P1*V1)/T1 = (P2*V2)/T2",
|
|
67
|
+
{"P1": "P initial", "V1": "V initial", "T1": "T initial", "P2": "P final", "V2": "V final", "T2": "T final"}),
|
|
68
|
+
"van_der_waals": ("Van der Waals Equation", "Gas Laws", "(P + a*n**2/V**2) * (V - n*b) = n*R*T",
|
|
69
|
+
{"P": "pressure", "a": "vdW constant a", "n": "moles", "V": "volume", "b": "vdW constant b",
|
|
70
|
+
"R": "gas constant", "T": "temperature"}),
|
|
71
|
+
"molarity": ("Molarity", "Solutions", "M = n/V",
|
|
72
|
+
{"M": "molarity (mol/L)", "n": "moles of solute", "V": "volume of solution (L)"}),
|
|
73
|
+
"moles_mass": ("Moles from Mass", "Mole Concept", "n = m/Mm",
|
|
74
|
+
{"n": "moles", "m": "mass (g)", "Mm": "molar mass (g/mol)"}),
|
|
75
|
+
"dilution": ("Dilution Law", "Solutions", "C1*V1 = C2*V2",
|
|
76
|
+
{"C1": "initial conc.", "V1": "initial volume", "C2": "final conc.", "V2": "final volume"}),
|
|
77
|
+
"ph": ("pH from [H+]", "Equilibrium/Acid-Base", "pH = -log(H, 10)",
|
|
78
|
+
{"pH": "pH", "H": "[H+] concentration (mol/L)"}),
|
|
79
|
+
"poh": ("pOH from [OH-]", "Equilibrium/Acid-Base", "pOH = -log(OH, 10)",
|
|
80
|
+
{"pOH": "pOH", "OH": "[OH-] concentration (mol/L)"}),
|
|
81
|
+
"ph_poh": ("pH + pOH = 14", "Equilibrium/Acid-Base", "pH + pOH = 14",
|
|
82
|
+
{"pH": "pH", "pOH": "pOH"}),
|
|
83
|
+
"henderson": ("Henderson-Hasselbalch", "Equilibrium/Acid-Base", "pH = pKa + log(A/HA, 10)",
|
|
84
|
+
{"pH": "pH", "pKa": "acid dissociation constant (pKa)", "A": "[conjugate base]", "HA": "[acid]"}),
|
|
85
|
+
"kw": ("Ion Product of Water", "Equilibrium/Acid-Base", "H*OH = 1.0e-14",
|
|
86
|
+
{"H": "[H+]", "OH": "[OH-]"}),
|
|
87
|
+
"nernst": ("Nernst Equation", "Electrochemistry", "Ecell = Ecell0 - (0.0592/n) * log(Q, 10)",
|
|
88
|
+
{"Ecell": "cell potential", "Ecell0": "standard cell potential", "n": "electrons transferred", "Q": "reaction quotient"}),
|
|
89
|
+
"faraday": ("Faraday's Law of Electrolysis", "Electrochemistry", "m = (M*I*t)/(n*F)",
|
|
90
|
+
{"m": "mass deposited (g)", "M": "molar mass", "I": "current (A)", "t": "time (s)",
|
|
91
|
+
"n": "electrons transferred", "F": "Faraday constant"}),
|
|
92
|
+
"gibbs": ("Gibbs Free Energy", "Thermodynamics", "dG = dH - T*dS",
|
|
93
|
+
{"dG": "Gibbs free energy change", "dH": "enthalpy change", "T": "temperature (K)", "dS": "entropy change"}),
|
|
94
|
+
"gibbs_cell": ("Gibbs Energy <-> Cell Potential", "Thermodynamics", "dG = -n*F*Ecell",
|
|
95
|
+
{"dG": "Gibbs free energy change", "n": "electrons transferred", "F": "Faraday constant", "Ecell": "cell potential"}),
|
|
96
|
+
"eq_gibbs": ("Gibbs Energy <-> Equilibrium Const.", "Thermodynamics", "dG0 = -R*T*log(K)",
|
|
97
|
+
{"dG0": "standard Gibbs free energy", "R": "gas constant", "T": "temperature (K)", "K": "equilibrium constant"}),
|
|
98
|
+
"clausius": ("Clausius-Clapeyron Equation", "Thermodynamics",
|
|
99
|
+
"log(P2/P1) = -(dHvap/R) * (1/T2 - 1/T1)",
|
|
100
|
+
{"P2": "vapor pressure at T2", "P1": "vapor pressure at T1", "dHvap": "enthalpy of vaporization",
|
|
101
|
+
"R": "gas constant", "T2": "temp 2 (K)", "T1": "temp 1 (K)"}),
|
|
102
|
+
"raoult": ("Raoult's Law", "Solutions", "Psoln = Xsolvent * P0",
|
|
103
|
+
{"Psoln": "vapor pressure of solution", "Xsolvent": "mole fraction of solvent", "P0": "pure solvent vapor pressure"}),
|
|
104
|
+
"osmotic": ("Osmotic Pressure", "Solutions", "Pi = M*R*T",
|
|
105
|
+
{"Pi": "osmotic pressure", "M": "molarity", "R": "gas constant", "T": "temperature (K)"}),
|
|
106
|
+
"freezing_dep": ("Freezing Point Depression", "Solutions", "dTf = Kf*m*i",
|
|
107
|
+
{"dTf": "freezing point depression", "Kf": "cryoscopic constant", "m": "molality", "i": "van't Hoff factor"}),
|
|
108
|
+
"boiling_elev": ("Boiling Point Elevation", "Solutions", "dTb = Kb*m*i",
|
|
109
|
+
{"dTb": "boiling point elevation", "Kb": "ebullioscopic constant", "m": "molality", "i": "van't Hoff factor"}),
|
|
110
|
+
"planck": ("Planck's Energy Equation", "Atomic/Quantum", "E = h*f",
|
|
111
|
+
{"E": "photon energy (J)", "h": "Planck's constant", "f": "frequency (Hz)"}),
|
|
112
|
+
"photon_wave": ("Photon Energy from Wavelength", "Atomic/Quantum", "E = (h*c)/lam",
|
|
113
|
+
{"E": "photon energy (J)", "h": "Planck's constant", "c": "speed of light", "lam": "wavelength (m)"}),
|
|
114
|
+
"de_broglie": ("De Broglie Wavelength", "Atomic/Quantum", "lam = h/(m*v)",
|
|
115
|
+
{"lam": "wavelength", "h": "Planck's constant", "m": "mass (kg)", "v": "velocity (m/s)"}),
|
|
116
|
+
"rydberg": ("Rydberg Equation", "Atomic/Quantum", "invlam = Rinf*(1/n1**2 - 1/n2**2)",
|
|
117
|
+
{"invlam": "1/wavelength (1/m)", "Rinf": "Rydberg constant", "n1": "lower level", "n2": "higher level"}),
|
|
118
|
+
"bohr_energy": ("Bohr Energy Level", "Atomic/Quantum", "En = -2.18e-18/n**2",
|
|
119
|
+
{"En": "orbit energy (J)", "n": "principal quantum number"}),
|
|
120
|
+
"bohr_radius": ("Bohr Radius of Orbit", "Atomic/Quantum", "rn = a0*n**2/Z",
|
|
121
|
+
{"rn": "orbit radius (m)", "a0": "Bohr radius const.", "n": "principal quantum number", "Z": "atomic number"}),
|
|
122
|
+
"heisenberg": ("Heisenberg Uncertainty Principle", "Atomic/Quantum", "dx*dp = h/(4*pi)",
|
|
123
|
+
{"dx": "position uncertainty", "dp": "momentum uncertainty", "h": "Planck's constant", "pi": "pi"}),
|
|
124
|
+
"avg_ke_gas": ("Average KE of Gas Molecule", "Thermodynamics", "KE = (3/2)*k_B*T",
|
|
125
|
+
{"KE": "average kinetic energy", "k_B": "Boltzmann constant", "T": "temperature (K)"}),
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
CATEGORIES = sorted({v[1] for v in FORMULA_LIBRARY.values()})
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class SolveError(Exception):
|
|
132
|
+
pass
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _make_symbols(names):
|
|
136
|
+
return {n: sp.Symbol(n, real=True) for n in names}
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def solve_formula(formula_str, known, solve_for, extra_constants=None):
|
|
140
|
+
"""
|
|
141
|
+
formula_str: "lhs = rhs" style string, e.g. "P*V = n*R*T"
|
|
142
|
+
known: dict of {symbol_name: numeric value} for everything EXCEPT solve_for
|
|
143
|
+
solve_for: name of the symbol to solve for
|
|
144
|
+
Returns: (numeric_result, sympy_equation_used)
|
|
145
|
+
"""
|
|
146
|
+
if not _HAS_SYMPY:
|
|
147
|
+
raise SolveError("sympy is not installed. Run: pip install sympy --break-system-packages "
|
|
148
|
+
"(or 'pip install -r requirements.txt') to enable /solve.")
|
|
149
|
+
if "=" not in formula_str:
|
|
150
|
+
raise SolveError("Formula must contain '=' (e.g. 'P*V = n*R*T').")
|
|
151
|
+
lhs_s, rhs_s = formula_str.split("=", 1)
|
|
152
|
+
|
|
153
|
+
const_ns = dict(CONSTANTS)
|
|
154
|
+
if extra_constants:
|
|
155
|
+
const_ns.update(extra_constants)
|
|
156
|
+
|
|
157
|
+
all_names = set(known) | {solve_for}
|
|
158
|
+
# discover any additional free symbols mentioned in the formula text
|
|
159
|
+
import re as _re
|
|
160
|
+
tokens = set(_re.findall(r"[A-Za-z_][A-Za-z_0-9]*", formula_str))
|
|
161
|
+
tokens -= {"log", "exp", "sqrt", "sin", "cos", "tan"}
|
|
162
|
+
all_names |= tokens
|
|
163
|
+
|
|
164
|
+
symtab = _make_symbols(all_names)
|
|
165
|
+
local_dict = dict(symtab)
|
|
166
|
+
local_dict.update({
|
|
167
|
+
"log": sp.log, "exp": sp.exp, "sqrt": sp.sqrt,
|
|
168
|
+
"sin": sp.sin, "cos": sp.cos, "tan": sp.tan,
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
try:
|
|
172
|
+
lhs = sp.sympify(lhs_s.strip(), locals=local_dict)
|
|
173
|
+
rhs = sp.sympify(rhs_s.strip(), locals=local_dict)
|
|
174
|
+
except Exception as exc:
|
|
175
|
+
raise SolveError(f"Could not parse formula: {exc}")
|
|
176
|
+
|
|
177
|
+
eq = sp.Eq(lhs, rhs)
|
|
178
|
+
|
|
179
|
+
# substitute known numeric values (and any constants the formula uses,
|
|
180
|
+
# e.g. R, h, c, NA, F, Rinf, a0, pi, e — unless the user overrides them)
|
|
181
|
+
subs = {}
|
|
182
|
+
for name, val in known.items():
|
|
183
|
+
if name in symtab:
|
|
184
|
+
subs[symtab[name]] = val
|
|
185
|
+
for name, val in const_ns.items():
|
|
186
|
+
if name in symtab and name not in known and name != solve_for:
|
|
187
|
+
subs[symtab[name]] = val
|
|
188
|
+
|
|
189
|
+
eq_sub = eq.subs(subs)
|
|
190
|
+
|
|
191
|
+
target = symtab[solve_for]
|
|
192
|
+
if target not in eq_sub.free_symbols:
|
|
193
|
+
raise SolveError(f"'{solve_for}' does not appear in this formula, or all values were already substituted.")
|
|
194
|
+
|
|
195
|
+
solutions = sp.solve(eq_sub, target)
|
|
196
|
+
if not solutions:
|
|
197
|
+
raise SolveError("No solution found — check the values you supplied.")
|
|
198
|
+
|
|
199
|
+
numeric = []
|
|
200
|
+
for s in solutions:
|
|
201
|
+
try:
|
|
202
|
+
numeric.append(complex(sp.N(s)))
|
|
203
|
+
except Exception:
|
|
204
|
+
continue
|
|
205
|
+
if not numeric:
|
|
206
|
+
raise SolveError("Solution is symbolic / non-numeric — supply more known values.")
|
|
207
|
+
|
|
208
|
+
# prefer the real, positive root when several exist (typical in chemistry)
|
|
209
|
+
real_candidates = [z.real for z in numeric if abs(z.imag) < 1e-9]
|
|
210
|
+
if real_candidates:
|
|
211
|
+
positives = [v for v in real_candidates if v > 0]
|
|
212
|
+
result = positives[0] if positives else real_candidates[0]
|
|
213
|
+
else:
|
|
214
|
+
result = numeric[0]
|
|
215
|
+
|
|
216
|
+
return result, eq
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def pretty(obj):
|
|
220
|
+
"""Render a formula string (e.g. 'P*V = n*R*T') or a sympy Eq/expr as
|
|
221
|
+
notebook-style text: division becomes a stacked fraction bar (never
|
|
222
|
+
'/'), multiplication becomes 'x' (never '*'). Falls back to a plain
|
|
223
|
+
text substitution if sympy isn't available or parsing fails.
|
|
224
|
+
"""
|
|
225
|
+
def _fallback(s):
|
|
226
|
+
s = str(s).replace("**", "^")
|
|
227
|
+
# collapse "a*b*c" -> "a x b x c" without touching '**' (already gone)
|
|
228
|
+
return " x ".join(p.strip() for p in s.split("*"))
|
|
229
|
+
|
|
230
|
+
if not _HAS_SYMPY:
|
|
231
|
+
return _fallback(obj)
|
|
232
|
+
|
|
233
|
+
try:
|
|
234
|
+
if isinstance(obj, str):
|
|
235
|
+
import re as _re
|
|
236
|
+
if "=" in obj:
|
|
237
|
+
lhs_s, rhs_s = obj.split("=", 1)
|
|
238
|
+
tokens = set(_re.findall(r"[A-Za-z_][A-Za-z_0-9]*", obj))
|
|
239
|
+
tokens -= {"log", "exp", "sqrt", "sin", "cos", "tan"}
|
|
240
|
+
local_dict = dict(_make_symbols(tokens))
|
|
241
|
+
local_dict.update({
|
|
242
|
+
"log": sp.log, "exp": sp.exp, "sqrt": sp.sqrt,
|
|
243
|
+
"sin": sp.sin, "cos": sp.cos, "tan": sp.tan,
|
|
244
|
+
})
|
|
245
|
+
expr = sp.Eq(sp.sympify(lhs_s.strip(), locals=local_dict),
|
|
246
|
+
sp.sympify(rhs_s.strip(), locals=local_dict))
|
|
247
|
+
else:
|
|
248
|
+
expr = sp.sympify(obj)
|
|
249
|
+
else:
|
|
250
|
+
expr = obj
|
|
251
|
+
out = sp.pretty(expr, use_unicode=True)
|
|
252
|
+
except Exception:
|
|
253
|
+
return _fallback(obj)
|
|
254
|
+
|
|
255
|
+
# sympy's pretty-printer uses "\u22c5" (dot) for multiplication; the
|
|
256
|
+
# house style here is "x", never "*" or a bare dot.
|
|
257
|
+
out = out.replace("\u22c5", " x ")
|
|
258
|
+
return out
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def solve_library_formula(key, known):
|
|
262
|
+
"""Solve a named formula from FORMULA_LIBRARY. `known` must contain
|
|
263
|
+
every variable except exactly one (the one being solved for)."""
|
|
264
|
+
if key not in FORMULA_LIBRARY:
|
|
265
|
+
raise SolveError(f"Unknown formula id: {key}")
|
|
266
|
+
name, category, formula_str, var_desc = FORMULA_LIBRARY[key]
|
|
267
|
+
all_vars = set(var_desc)
|
|
268
|
+
missing = all_vars - set(known)
|
|
269
|
+
if len(missing) != 1:
|
|
270
|
+
raise SolveError(
|
|
271
|
+
f"Give values for exactly all-but-one variable. "
|
|
272
|
+
f"Variables: {', '.join(sorted(all_vars))}. Missing/target: {', '.join(missing) or 'none'}"
|
|
273
|
+
)
|
|
274
|
+
solve_for = next(iter(missing))
|
|
275
|
+
result, eq = solve_formula(formula_str, known, solve_for)
|
|
276
|
+
return solve_for, result, name, category, eq
|
calc_terminal/sound.py
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Terminal sound effects for CCT — dependency-free, cross-platform.
|
|
3
|
+
|
|
4
|
+
No audio libraries are required. On Windows, the built-in `winsound`
|
|
5
|
+
module plays real short tones. Everywhere else (macOS/Linux terminals,
|
|
6
|
+
and Windows too if winsound is unavailable for some reason) it falls
|
|
7
|
+
back to the ASCII BEL character (`\\a`), which every terminal emulator
|
|
8
|
+
interprets as "ring the system bell" — the same mechanism your shell
|
|
9
|
+
uses for tab-complete errors. Some terminals mute this by default; if
|
|
10
|
+
nothing is audible, check your terminal's preferences for "audible
|
|
11
|
+
bell" / "terminal bell" and make sure it's turned on.
|
|
12
|
+
|
|
13
|
+
This module never raises and never blocks noticeably — a sound cue is
|
|
14
|
+
a nice-to-have, not something that should ever break the app or stall
|
|
15
|
+
an animation.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import sys
|
|
19
|
+
import threading
|
|
20
|
+
import time
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
import winsound
|
|
24
|
+
_HAS_WINSOUND = True
|
|
25
|
+
except ImportError:
|
|
26
|
+
winsound = None
|
|
27
|
+
_HAS_WINSOUND = False
|
|
28
|
+
|
|
29
|
+
_ENABLED = True
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def set_enabled(value):
|
|
33
|
+
global _ENABLED
|
|
34
|
+
_ENABLED = bool(value)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def is_enabled():
|
|
38
|
+
return _ENABLED
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _bell(times=1, gap=0.08):
|
|
42
|
+
for i in range(times):
|
|
43
|
+
sys.stdout.write("\a")
|
|
44
|
+
sys.stdout.flush()
|
|
45
|
+
if i < times - 1:
|
|
46
|
+
time.sleep(gap)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _tone(freq, duration_ms):
|
|
50
|
+
"""Try a real tone via winsound. Returns True if it actually played
|
|
51
|
+
a tone (so callers know NOT to also fall back to a bell)."""
|
|
52
|
+
if not _HAS_WINSOUND:
|
|
53
|
+
return False
|
|
54
|
+
try:
|
|
55
|
+
winsound.Beep(int(freq), int(duration_ms))
|
|
56
|
+
return True
|
|
57
|
+
except Exception:
|
|
58
|
+
return False
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def play_async(event="notify"):
|
|
62
|
+
"""v0.7.9.0 (requirement #1/#31 — SPEED): `winsound.Beep` BLOCKS its
|
|
63
|
+
calling thread for the full tone duration (~45-160 ms per tone, and
|
|
64
|
+
'success' plays TWO). Inside the agent loop that meant every single
|
|
65
|
+
tool call paid 100-220 ms of pure sound latency — measured, not
|
|
66
|
+
guessed (see scripts/benchmark_cat.py: tool 'calculate' took ~219 ms,
|
|
67
|
+
all of it winsound.Beep). Sounds are now fired on a throwaway daemon
|
|
68
|
+
thread so the request path never waits on audio."""
|
|
69
|
+
if not _ENABLED:
|
|
70
|
+
return
|
|
71
|
+
threading.Thread(target=play, args=(event,), daemon=True).start()
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# ---------------------------------------------------------------- events --
|
|
75
|
+
# Each named event below is used consistently across the app: booting up,
|
|
76
|
+
# a notebook/solve/plot finishing successfully, an error, a live
|
|
77
|
+
# simulation starting, and a quiet tick for each agent tool call so an
|
|
78
|
+
# agent run happening on-screen is also audible turn-by-turn.
|
|
79
|
+
def _play_blocking(event="notify"):
|
|
80
|
+
"""The actual tone/bell sequence. Runs on a worker thread via play()
|
|
81
|
+
so no request/tool path ever waits on audio hardware."""
|
|
82
|
+
try:
|
|
83
|
+
if event == "startup":
|
|
84
|
+
if not _tone(660, 90):
|
|
85
|
+
_bell(1)
|
|
86
|
+
else:
|
|
87
|
+
_tone(880, 110)
|
|
88
|
+
elif event == "success":
|
|
89
|
+
if not _tone(880, 90):
|
|
90
|
+
_bell(1)
|
|
91
|
+
else:
|
|
92
|
+
_tone(1175, 120)
|
|
93
|
+
elif event == "error":
|
|
94
|
+
if not _tone(220, 160):
|
|
95
|
+
_bell(2, gap=0.12)
|
|
96
|
+
elif event == "sim_start":
|
|
97
|
+
if not _tone(600, 60):
|
|
98
|
+
_bell(1)
|
|
99
|
+
elif event == "tool":
|
|
100
|
+
# Deliberately quiet: a short tick per agent tool call, and
|
|
101
|
+
# NO bell fallback (repeated bells during a multi-step agent
|
|
102
|
+
# run would get annoying fast on terminals without winsound).
|
|
103
|
+
_tone(520, 45)
|
|
104
|
+
else:
|
|
105
|
+
_bell(1)
|
|
106
|
+
except Exception:
|
|
107
|
+
pass
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def play(event="notify"):
|
|
111
|
+
"""v0.7.9.0 SPEED FIX (measured, see scripts/benchmark_cat.py):
|
|
112
|
+
winsound.Beep BLOCKS its thread for the whole tone — 'success' costs
|
|
113
|
+
~220 ms, every agent 'tool' tick ~45 ms. Playing inline meant every
|
|
114
|
+
single tool execution in a run paid that latency in wall-clock time.
|
|
115
|
+
Tones now run on a short-lived daemon thread; the ASCII-bell fallback
|
|
116
|
+
stays inline because writing '\\a' is instantaneous."""
|
|
117
|
+
if not _ENABLED:
|
|
118
|
+
return
|
|
119
|
+
if _HAS_WINSOUND:
|
|
120
|
+
threading.Thread(target=_play_blocking, args=(event,),
|
|
121
|
+
daemon=True).start()
|
|
122
|
+
else:
|
|
123
|
+
_play_blocking(event)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
# Back-compat explicit-async alias (same behavior as play() now).
|
|
127
|
+
play_async = play
|