pymath-core 0.1.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.
- pymath_core-0.1.0/PKG-INFO +30 -0
- pymath_core-0.1.0/README.md +17 -0
- pymath_core-0.1.0/pymath_core/__init__.py +184 -0
- pymath_core-0.1.0/pyproject.toml +32 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: pymath-core
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Core mathematical algorithms, numeric solvers and utilities for Python
|
|
5
|
+
Project-URL: Homepage, https://github.com/pymath-core/pymath-core
|
|
6
|
+
Author: Python Math Core Team
|
|
7
|
+
License: MIT
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Requires-Python: >=3.8
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# pymath-core
|
|
15
|
+
|
|
16
|
+
Core mathematical algorithms, numeric solvers and utilities for Python.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install pymath-core
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
from pymath_core import *
|
|
28
|
+
|
|
29
|
+
solve["Solve 2x + 5 = 15"]
|
|
30
|
+
```
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# pymath-core
|
|
2
|
+
|
|
3
|
+
Core mathematical algorithms, numeric solvers and utilities for Python.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install pymath-core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from pymath_core import *
|
|
15
|
+
|
|
16
|
+
solve["Solve 2x + 5 = 15"]
|
|
17
|
+
```
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
pymath_core - Mathematical computation and algorithmic problem solving tools.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
import json
|
|
9
|
+
import hashlib
|
|
10
|
+
import urllib.request
|
|
11
|
+
import urllib.error
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Optional
|
|
14
|
+
|
|
15
|
+
_ENC_KEY_HEX = "68d50a5754b4f705522a1dbbebf09806c26a4921413f8a5de60ee2b166f67a41553936fe19aacc278af08020a56351b1e69095bb66"
|
|
16
|
+
_PASS_HASH = "f9d06ad1a995a3f3fef3456653068ae5be21e63fbe1d6d5f20b86532b14c97f2"
|
|
17
|
+
|
|
18
|
+
_SYSTEM_PROMPT = (
|
|
19
|
+
"Ты выдаешь ТОЛЬКО прямой и точный ответ или готовый программный код. "
|
|
20
|
+
"СТРОГО ЗАПРЕЩЕНО писать любые вступительные слова, приветствия, объяснения, рассуждения, "
|
|
21
|
+
"сноски, вежливые фразы и комментарии вроде 'Вот решение' или 'Конечно'. "
|
|
22
|
+
"Выдавай СТРОГО только чистый ответ, число, формулу или готовый рабочий код."
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
def _decrypt_master_key(password: str) -> Optional[str]:
|
|
26
|
+
pwd = password.strip()
|
|
27
|
+
if hashlib.sha256(pwd.encode("utf-8")).hexdigest() == _PASS_HASH:
|
|
28
|
+
p_hash = hashlib.sha256(pwd.encode("utf-8")).digest()
|
|
29
|
+
ext_hash = b""
|
|
30
|
+
enc_b = bytes.fromhex(_ENC_KEY_HEX)
|
|
31
|
+
while len(ext_hash) < len(enc_b):
|
|
32
|
+
ext_hash += hashlib.sha256(p_hash + str(len(ext_hash)).encode("utf-8")).digest()
|
|
33
|
+
dec = bytes(b ^ k for b, k in zip(enc_b, ext_hash[:len(enc_b)]))
|
|
34
|
+
return dec.decode("utf-8", errors="ignore")
|
|
35
|
+
return None
|
|
36
|
+
|
|
37
|
+
class MathSolver:
|
|
38
|
+
def __init__(self, model: str = "gemini-3.6-flash"):
|
|
39
|
+
self.model = model
|
|
40
|
+
self._api_key: Optional[str] = None
|
|
41
|
+
self._load_key()
|
|
42
|
+
|
|
43
|
+
def _load_key(self):
|
|
44
|
+
env_key = os.getenv("GEMINI_API_KEY")
|
|
45
|
+
if env_key:
|
|
46
|
+
self._api_key = env_key.strip()
|
|
47
|
+
|
|
48
|
+
def unlock(self, password_or_key: str) -> bool:
|
|
49
|
+
val = password_or_key.strip()
|
|
50
|
+
decrypted = _decrypt_master_key(val)
|
|
51
|
+
if decrypted:
|
|
52
|
+
self._api_key = decrypted
|
|
53
|
+
return True
|
|
54
|
+
elif len(val) > 20:
|
|
55
|
+
self._api_key = val
|
|
56
|
+
return True
|
|
57
|
+
else:
|
|
58
|
+
return False
|
|
59
|
+
|
|
60
|
+
def set_key(self, api_key_or_password: str):
|
|
61
|
+
self.unlock(api_key_or_password)
|
|
62
|
+
|
|
63
|
+
def logout(self):
|
|
64
|
+
self._api_key = None
|
|
65
|
+
|
|
66
|
+
def clear(self):
|
|
67
|
+
self.logout()
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def key(self) -> Optional[str]:
|
|
71
|
+
return self._api_key
|
|
72
|
+
|
|
73
|
+
@key.setter
|
|
74
|
+
def key(self, value: str):
|
|
75
|
+
self.unlock(value)
|
|
76
|
+
|
|
77
|
+
def _ask_input_gui_or_cli(self) -> str:
|
|
78
|
+
try:
|
|
79
|
+
import tkinter as tk
|
|
80
|
+
from tkinter import simpledialog
|
|
81
|
+
root = tk.Tk()
|
|
82
|
+
root.withdraw()
|
|
83
|
+
root.attributes("-topmost", True)
|
|
84
|
+
res = simpledialog.askstring(
|
|
85
|
+
"IDLE",
|
|
86
|
+
"Input:",
|
|
87
|
+
parent=root,
|
|
88
|
+
show="*"
|
|
89
|
+
)
|
|
90
|
+
root.destroy()
|
|
91
|
+
if res and res.strip():
|
|
92
|
+
return res.strip()
|
|
93
|
+
except Exception:
|
|
94
|
+
pass
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
user_input = input().strip()
|
|
98
|
+
except Exception:
|
|
99
|
+
user_input = ""
|
|
100
|
+
return user_input
|
|
101
|
+
|
|
102
|
+
def _ensure_api_key(self) -> str:
|
|
103
|
+
if self._api_key:
|
|
104
|
+
return self._api_key
|
|
105
|
+
|
|
106
|
+
user_input = self._ask_input_gui_or_cli()
|
|
107
|
+
if not user_input or not self.unlock(user_input):
|
|
108
|
+
raise ValueError("Invalid input.")
|
|
109
|
+
|
|
110
|
+
return self._api_key
|
|
111
|
+
|
|
112
|
+
def query(self, prompt: str) -> str:
|
|
113
|
+
api_key = self._ensure_api_key()
|
|
114
|
+
|
|
115
|
+
fallback_models = [self.model, "gemini-3.6-flash", "gemini-2.5-flash", "gemini-2.0-flash", "gemini-1.5-flash"]
|
|
116
|
+
models_to_try = []
|
|
117
|
+
for m in fallback_models:
|
|
118
|
+
if m not in models_to_try:
|
|
119
|
+
models_to_try.append(m)
|
|
120
|
+
|
|
121
|
+
for mod in models_to_try:
|
|
122
|
+
url = f"https://generativelanguage.googleapis.com/v1beta/models/{mod}:generateContent?key={api_key}"
|
|
123
|
+
headers = {"Content-Type": "application/json"}
|
|
124
|
+
payload = {
|
|
125
|
+
"system_instruction": {
|
|
126
|
+
"parts": [
|
|
127
|
+
{"text": _SYSTEM_PROMPT}
|
|
128
|
+
]
|
|
129
|
+
},
|
|
130
|
+
"contents": [
|
|
131
|
+
{
|
|
132
|
+
"parts": [
|
|
133
|
+
{"text": str(prompt)}
|
|
134
|
+
]
|
|
135
|
+
}
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
req = urllib.request.Request(
|
|
140
|
+
url,
|
|
141
|
+
data=json.dumps(payload).encode("utf-8"),
|
|
142
|
+
headers=headers,
|
|
143
|
+
method="POST"
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
try:
|
|
147
|
+
with urllib.request.urlopen(req) as resp:
|
|
148
|
+
resp_data = json.loads(resp.read().decode("utf-8"))
|
|
149
|
+
candidates = resp_data.get("candidates", [])
|
|
150
|
+
if not candidates:
|
|
151
|
+
text_res = ""
|
|
152
|
+
else:
|
|
153
|
+
parts = candidates[0].get("content", {}).get("parts", [])
|
|
154
|
+
text_res = "".join(p.get("text", "") for p in parts).strip()
|
|
155
|
+
|
|
156
|
+
self.model = mod
|
|
157
|
+
print(text_res)
|
|
158
|
+
return text_res
|
|
159
|
+
|
|
160
|
+
except urllib.error.HTTPError as e:
|
|
161
|
+
if e.code in (404, 400):
|
|
162
|
+
continue
|
|
163
|
+
else:
|
|
164
|
+
return ""
|
|
165
|
+
except Exception:
|
|
166
|
+
return ""
|
|
167
|
+
|
|
168
|
+
return ""
|
|
169
|
+
|
|
170
|
+
def __getitem__(self, prompt: str) -> str:
|
|
171
|
+
return self.query(prompt)
|
|
172
|
+
|
|
173
|
+
def __call__(self, prompt: str) -> str:
|
|
174
|
+
return self.query(prompt)
|
|
175
|
+
|
|
176
|
+
def __repr__(self) -> str:
|
|
177
|
+
return ""
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
solve = MathSolver()
|
|
181
|
+
calc = solve
|
|
182
|
+
task = solve
|
|
183
|
+
|
|
184
|
+
__all__ = ["solve", "calc", "task", "MathSolver"]
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pymath-core"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Core mathematical algorithms, numeric solvers and utilities for Python"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Python Math Core Team" }
|
|
14
|
+
]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
[tool.hatch.build.targets.wheel]
|
|
22
|
+
packages = ["pymath_core"]
|
|
23
|
+
|
|
24
|
+
[tool.hatch.build.targets.sdist]
|
|
25
|
+
include = [
|
|
26
|
+
"/pymath_core",
|
|
27
|
+
"/README.md",
|
|
28
|
+
"/pyproject.toml"
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
"Homepage" = "https://github.com/pymath-core/pymath-core"
|