calcbox 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
calcbox-0.3.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 calcbox contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
calcbox-0.3.0/PKG-INFO ADDED
@@ -0,0 +1,171 @@
1
+ Metadata-Version: 2.4
2
+ Name: calcbox
3
+ Version: 0.3.0
4
+ Summary: Backend-agnostic math compute kernel: symbolic, numeric, units, control — with provenance in every answer
5
+ Author: calcbox contributors
6
+ License: MIT
7
+ Project-URL: Repository, https://github.com/Icefox12138/calcbox
8
+ Keywords: math,sympy,scipy,units,mcp,compute
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
12
+ Requires-Python: >=3.10
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Provides-Extra: symbolic
16
+ Requires-Dist: sympy>=1.12; extra == "symbolic"
17
+ Provides-Extra: numeric
18
+ Requires-Dist: scipy>=1.11; extra == "numeric"
19
+ Requires-Dist: numpy; extra == "numeric"
20
+ Provides-Extra: units
21
+ Requires-Dist: pint>=0.23; extra == "units"
22
+ Provides-Extra: control
23
+ Requires-Dist: control>=0.9; extra == "control"
24
+ Requires-Dist: numpy; extra == "control"
25
+ Provides-Extra: api
26
+ Requires-Dist: fastapi>=0.110; extra == "api"
27
+ Requires-Dist: pydantic>=2; extra == "api"
28
+ Provides-Extra: mcp
29
+ Requires-Dist: mcp>=1.0; extra == "mcp"
30
+ Provides-Extra: full
31
+ Requires-Dist: sympy>=1.12; extra == "full"
32
+ Requires-Dist: scipy>=1.11; extra == "full"
33
+ Requires-Dist: numpy; extra == "full"
34
+ Requires-Dist: pint>=0.23; extra == "full"
35
+ Requires-Dist: control>=0.9; extra == "full"
36
+ Dynamic: license-file
37
+
38
+ # calcbox
39
+
40
+ Backend-agnostic math compute kernel: **symbolic, numeric, units, control** — and every
41
+ answer carries provenance (backend, engine version, assumptions, verification, input hash).
42
+
43
+ It knows nothing about teaching, documents or users. It is a calculator that can prove
44
+ what it did.
45
+
46
+ ```python
47
+ from calcbox import compute
48
+
49
+ out = compute("x**2", "integrate")
50
+ # {'result': 'x**3/3', 'backend': 'sympy', 'engine_version': '1.13.1',
51
+ # 'verification': {'method': 'differentiate-back', 'passed': True}, ...}
52
+ ```
53
+
54
+ ## Why another math tool
55
+
56
+ There are MCP servers that evaluate arithmetic and matrix operations. What they do not
57
+ report is **how the answer was produced**: which backend, which engine version, which
58
+ assumptions, and whether a second method agreed. For tutoring, for reproducible
59
+ research notes, and for anything a human will quote, that provenance is the point.
60
+
61
+ ```json
62
+ {
63
+ "result": "x**3/3",
64
+ "latex": "\\frac{x^{3}}{3}",
65
+ "backend": "sympy",
66
+ "engine_version": "1.13.1",
67
+ "assumptions": {"variable": "x", "domain": "real"},
68
+ "verification": {"method": "differentiate-back", "passed": true},
69
+ "disputed": false,
70
+ "sandbox": "audit-only",
71
+ "input_sha256": "9f2c…",
72
+ "elapsed_ms": 12.4
73
+ }
74
+ ```
75
+
76
+ ## Install
77
+
78
+ ```bash
79
+ pip install calcbox # core, no backends
80
+ pip install "calcbox[symbolic]" # sympy (calculus, algebra, exact values)
81
+ pip install "calcbox[full]" # sympy + scipy + pint + control
82
+ pip install "calcbox[mcp]" # MCP server (stdio)
83
+ pip install "calcbox[api]" # FastAPI router
84
+ ```
85
+
86
+ Nothing is a hard dependency. A missing backend is reported as unavailable **with a
87
+ reason**, never as a crash.
88
+
89
+ ## Use it
90
+
91
+ **Python**
92
+
93
+ ```python
94
+ compute("x**2", "integrate") # indefinite
95
+ compute("x", "integrate", target="0,1") # definite
96
+ compute("x**3", "diff")
97
+ compute("2+3*4", "value")
98
+ compute("x**2-1", "solve")
99
+ compute("sin(x)/x", "limit", point="0")
100
+ compute("x**2", "simplify", backend="sympy")
101
+ ```
102
+
103
+ **CLI**
104
+
105
+ ```bash
106
+ calcbox "x**2" --op integrate
107
+ calcbox "sin(x)/x" --op limit --point 0 --json
108
+ ```
109
+
110
+ **HTTP** (optional; no auth by design — that belongs to the host)
111
+
112
+ ```python
113
+ from fastapi import FastAPI
114
+ from calcbox.api import create_router
115
+
116
+ app = FastAPI()
117
+ app.include_router(create_router())
118
+ # GET /v1/compute/backends
119
+ # POST /v1/compute {"expression": "x**2", "operation": "integrate"}
120
+ ```
121
+
122
+ **MCP**
123
+
124
+ ```json
125
+ {
126
+ "mcpServers": {
127
+ "calcbox": {"command": "calcbox-mcp", "args": []}
128
+ }
129
+ }
130
+ ```
131
+
132
+ Tools: `compute`, `backends`.
133
+
134
+ ## Contract
135
+
136
+ | Field | Meaning |
137
+ |---|---|
138
+ | `operation` | `simplify`, `diff`, `integrate`, `solve`, `limit`, `value`, `latex`, `factor`, `expand`, `quad`, `minimize`, `root`, `convert`, `step`, `eval`, `check` |
139
+ | `backend` | `auto` infers from the operation; otherwise one of `sympy`/`scipy`/`pint`/`control`/`wolfram`/`lean` |
140
+ | `verification` | method + `passed`; e.g. differentiate-back for integrals, substitute-back for solutions |
141
+ | `disputed` | two backends disagreed (only when a cross-check runs) |
142
+ | `sandbox` | always `audit-only` for in-process backends |
143
+
144
+ Errors: `ComputeError` (bad request → 400) and `BackendUnavailable` (missing dependency
145
+ or disabled external engine → 503).
146
+
147
+ ## Security
148
+
149
+ **The sandbox is audit-only.** It is a set of `sys.audit` hooks, not an OS jail. Read
150
+ [SECURITY.md](SECURITY.md) before exposing this to untrusted input. External engines
151
+ (Wolfram, Lean) are disabled unless `CALCBOX_EXTERNAL=1`.
152
+
153
+ ## Status
154
+
155
+ 0.3.0 — all six backends wired: SymPy, SciPy, Pint, Control, and the external
156
+ engines Wolfram and Lean (disabled unless CALCBOX_EXTERNAL=1; both fail with a
157
+ reason when the binary is absent, never silently).
158
+
159
+ 15/15 parity with the implementation this was ported from, 36 tests.
160
+
161
+ Every local call runs in a killable subprocess worker (~0.2-1.2 s overhead);
162
+ CALCBOX_WORKER=0 runs in-process for debugging. External engines run in-process
163
+ because they are already separate processes.
164
+
165
+ ## Releasing
166
+
167
+ See [RELEASING.md](RELEASING.md): PyPI Trusted Publishing, no tokens.
168
+
169
+ ## License
170
+
171
+ MIT.
@@ -0,0 +1,134 @@
1
+ # calcbox
2
+
3
+ Backend-agnostic math compute kernel: **symbolic, numeric, units, control** — and every
4
+ answer carries provenance (backend, engine version, assumptions, verification, input hash).
5
+
6
+ It knows nothing about teaching, documents or users. It is a calculator that can prove
7
+ what it did.
8
+
9
+ ```python
10
+ from calcbox import compute
11
+
12
+ out = compute("x**2", "integrate")
13
+ # {'result': 'x**3/3', 'backend': 'sympy', 'engine_version': '1.13.1',
14
+ # 'verification': {'method': 'differentiate-back', 'passed': True}, ...}
15
+ ```
16
+
17
+ ## Why another math tool
18
+
19
+ There are MCP servers that evaluate arithmetic and matrix operations. What they do not
20
+ report is **how the answer was produced**: which backend, which engine version, which
21
+ assumptions, and whether a second method agreed. For tutoring, for reproducible
22
+ research notes, and for anything a human will quote, that provenance is the point.
23
+
24
+ ```json
25
+ {
26
+ "result": "x**3/3",
27
+ "latex": "\\frac{x^{3}}{3}",
28
+ "backend": "sympy",
29
+ "engine_version": "1.13.1",
30
+ "assumptions": {"variable": "x", "domain": "real"},
31
+ "verification": {"method": "differentiate-back", "passed": true},
32
+ "disputed": false,
33
+ "sandbox": "audit-only",
34
+ "input_sha256": "9f2c…",
35
+ "elapsed_ms": 12.4
36
+ }
37
+ ```
38
+
39
+ ## Install
40
+
41
+ ```bash
42
+ pip install calcbox # core, no backends
43
+ pip install "calcbox[symbolic]" # sympy (calculus, algebra, exact values)
44
+ pip install "calcbox[full]" # sympy + scipy + pint + control
45
+ pip install "calcbox[mcp]" # MCP server (stdio)
46
+ pip install "calcbox[api]" # FastAPI router
47
+ ```
48
+
49
+ Nothing is a hard dependency. A missing backend is reported as unavailable **with a
50
+ reason**, never as a crash.
51
+
52
+ ## Use it
53
+
54
+ **Python**
55
+
56
+ ```python
57
+ compute("x**2", "integrate") # indefinite
58
+ compute("x", "integrate", target="0,1") # definite
59
+ compute("x**3", "diff")
60
+ compute("2+3*4", "value")
61
+ compute("x**2-1", "solve")
62
+ compute("sin(x)/x", "limit", point="0")
63
+ compute("x**2", "simplify", backend="sympy")
64
+ ```
65
+
66
+ **CLI**
67
+
68
+ ```bash
69
+ calcbox "x**2" --op integrate
70
+ calcbox "sin(x)/x" --op limit --point 0 --json
71
+ ```
72
+
73
+ **HTTP** (optional; no auth by design — that belongs to the host)
74
+
75
+ ```python
76
+ from fastapi import FastAPI
77
+ from calcbox.api import create_router
78
+
79
+ app = FastAPI()
80
+ app.include_router(create_router())
81
+ # GET /v1/compute/backends
82
+ # POST /v1/compute {"expression": "x**2", "operation": "integrate"}
83
+ ```
84
+
85
+ **MCP**
86
+
87
+ ```json
88
+ {
89
+ "mcpServers": {
90
+ "calcbox": {"command": "calcbox-mcp", "args": []}
91
+ }
92
+ }
93
+ ```
94
+
95
+ Tools: `compute`, `backends`.
96
+
97
+ ## Contract
98
+
99
+ | Field | Meaning |
100
+ |---|---|
101
+ | `operation` | `simplify`, `diff`, `integrate`, `solve`, `limit`, `value`, `latex`, `factor`, `expand`, `quad`, `minimize`, `root`, `convert`, `step`, `eval`, `check` |
102
+ | `backend` | `auto` infers from the operation; otherwise one of `sympy`/`scipy`/`pint`/`control`/`wolfram`/`lean` |
103
+ | `verification` | method + `passed`; e.g. differentiate-back for integrals, substitute-back for solutions |
104
+ | `disputed` | two backends disagreed (only when a cross-check runs) |
105
+ | `sandbox` | always `audit-only` for in-process backends |
106
+
107
+ Errors: `ComputeError` (bad request → 400) and `BackendUnavailable` (missing dependency
108
+ or disabled external engine → 503).
109
+
110
+ ## Security
111
+
112
+ **The sandbox is audit-only.** It is a set of `sys.audit` hooks, not an OS jail. Read
113
+ [SECURITY.md](SECURITY.md) before exposing this to untrusted input. External engines
114
+ (Wolfram, Lean) are disabled unless `CALCBOX_EXTERNAL=1`.
115
+
116
+ ## Status
117
+
118
+ 0.3.0 — all six backends wired: SymPy, SciPy, Pint, Control, and the external
119
+ engines Wolfram and Lean (disabled unless CALCBOX_EXTERNAL=1; both fail with a
120
+ reason when the binary is absent, never silently).
121
+
122
+ 15/15 parity with the implementation this was ported from, 36 tests.
123
+
124
+ Every local call runs in a killable subprocess worker (~0.2-1.2 s overhead);
125
+ CALCBOX_WORKER=0 runs in-process for debugging. External engines run in-process
126
+ because they are already separate processes.
127
+
128
+ ## Releasing
129
+
130
+ See [RELEASING.md](RELEASING.md): PyPI Trusted Publishing, no tokens.
131
+
132
+ ## License
133
+
134
+ MIT.
@@ -0,0 +1,16 @@
1
+ """calcbox — backend-agnostic compute kernel.
2
+
3
+ from calcbox import compute, backends
4
+ compute("x**2", "integrate") # -> dict, see calcbox.contract
5
+ backend_status() # -> {"sympy": {"available": True, ...}}
6
+
7
+ Every answer carries provenance: backend, engine version, assumptions,
8
+ verification and an input hash. The sandbox is audit-only; see README.
9
+ """
10
+ from .backends import backend_status, compute
11
+ from .contract import (BackendStatus, BackendUnavailable, ComputeError,
12
+ ComputeRequest, ComputeResult)
13
+
14
+ __version__ = "0.3.0"
15
+ __all__ = ["compute", "backend_status", "ComputeError", "BackendUnavailable",
16
+ "ComputeRequest", "ComputeResult", "BackendStatus", "__version__"]
@@ -0,0 +1,52 @@
1
+ """Optional HTTP surface.
2
+
3
+ from fastapi import FastAPI
4
+ from calcbox.api import create_router
5
+
6
+ app = FastAPI()
7
+ app.include_router(create_router())
8
+
9
+ The routes mirror the contract exactly; they carry no authentication, because
10
+ that is the host's job (calcbox has no notion of users).
11
+ """
12
+ from typing import Any, Dict, Optional
13
+
14
+ from . import backends as _backends
15
+ from .contract import BackendUnavailable, ComputeError
16
+
17
+
18
+ def create_router():
19
+ from fastapi import APIRouter, HTTPException
20
+ from pydantic import BaseModel
21
+
22
+ router = APIRouter()
23
+
24
+ class ComputeIn(BaseModel):
25
+ expression: str
26
+ operation: str = "simplify"
27
+ variable: str = "x"
28
+ point: str = "0"
29
+ target: Optional[str] = None
30
+ data: Optional[Dict[str, Any]] = None
31
+ timeout: float = 8.0
32
+ backend: str = "auto"
33
+
34
+ @router.get("/v1/compute/backends")
35
+ def get_backends():
36
+ return {"backends": _backends.backend_status()}
37
+
38
+ @router.post("/v1/compute")
39
+ def post_compute(body: ComputeIn):
40
+ try:
41
+ return _backends.compute(body.expression, operation=body.operation,
42
+ variable=body.variable, point=body.point,
43
+ target=body.target, data=body.data,
44
+ timeout=body.timeout, backend=body.backend)
45
+ except BackendUnavailable as e:
46
+ raise HTTPException(status_code=503, detail={"code": "backend_unavailable",
47
+ "detail": str(e)})
48
+ except ComputeError as e:
49
+ raise HTTPException(status_code=400, detail={"code": "bad_request",
50
+ "detail": str(e)})
51
+
52
+ return router