binary-equalab 2.0.1__py3-none-any.whl → 3.0.0b1__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.
- binary_equalab/cli.py +76 -11
- binary_equalab/kimi_service.py +137 -0
- {binary_equalab-2.0.1.dist-info → binary_equalab-3.0.0b1.dist-info}/METADATA +17 -15
- {binary_equalab-2.0.1.dist-info → binary_equalab-3.0.0b1.dist-info}/RECORD +6 -5
- {binary_equalab-2.0.1.dist-info → binary_equalab-3.0.0b1.dist-info}/WHEEL +0 -0
- {binary_equalab-2.0.1.dist-info → binary_equalab-3.0.0b1.dist-info}/entry_points.txt +0 -0
binary_equalab/cli.py
CHANGED
|
@@ -99,7 +99,7 @@ def get_prompt_style():
|
|
|
99
99
|
def print_banner():
|
|
100
100
|
"""Print the CLI banner using Rich panels."""
|
|
101
101
|
title = Text("Binary EquaLab CLI", style="bold white")
|
|
102
|
-
version = Text("Aurora v2.0.
|
|
102
|
+
version = Text("Aurora v2.0.2", style="dim")
|
|
103
103
|
slogan = Text('"Las matemáticas también sienten,\npero estas no se equivocan."', style="dim italic")
|
|
104
104
|
|
|
105
105
|
content = Text.assemble(title, " ", version, "\n\n", slogan, justify="center")
|
|
@@ -153,15 +153,20 @@ def repl():
|
|
|
153
153
|
console.print(f"[dim]{i}.[/dim] {h}")
|
|
154
154
|
continue
|
|
155
155
|
|
|
156
|
-
# --- Easter Eggs ---
|
|
157
|
-
|
|
158
|
-
|
|
156
|
+
# --- Easter Eggs (Math Based) ---
|
|
157
|
+
# 1+1 -> 2
|
|
158
|
+
if user_input.replace(" ", "") == "1+1":
|
|
159
|
+
console.print(Panel("[bold cyan]2[/bold cyan]\n[dim italic]El principio de todo.[/dim italic]", border_style="cyan"))
|
|
159
160
|
continue
|
|
160
|
-
|
|
161
|
-
|
|
161
|
+
|
|
162
|
+
# (-1)*(-1) -> 1
|
|
163
|
+
if user_input.replace(" ", "") in ["(-1)*(-1)", "-1*-1"]:
|
|
164
|
+
console.print(Panel("[bold green]1[/bold green]\n[dim italic]Menos por menos es más... como en la vida.[/dim italic]", border_style="green"))
|
|
162
165
|
continue
|
|
163
|
-
|
|
164
|
-
|
|
166
|
+
|
|
167
|
+
# The Answer
|
|
168
|
+
if user_input.replace(" ", "") == "0b101010":
|
|
169
|
+
console.print(Panel("[bold magenta]42[/bold magenta]\n[dim italic]La respuesta a todo.[/dim italic]", border_style="magenta"))
|
|
165
170
|
continue
|
|
166
171
|
# -------------------
|
|
167
172
|
|
|
@@ -214,6 +219,66 @@ def main():
|
|
|
214
219
|
if len(sys.argv) > 1 and sys.argv[1] == 'setup-shell':
|
|
215
220
|
from .shell_setup import run_setup
|
|
216
221
|
run_setup()
|
|
222
|
+
elif len(sys.argv) > 1 and sys.argv[1] == 'ai':
|
|
223
|
+
# AI Commands Mode
|
|
224
|
+
from .kimi_service import kimi_service
|
|
225
|
+
|
|
226
|
+
if len(sys.argv) < 3:
|
|
227
|
+
console.print("[bold red]Uso:[/bold red] binary ai [solve|explain|exercises] \"consulta\"")
|
|
228
|
+
sys.exit(1)
|
|
229
|
+
|
|
230
|
+
subcmd = sys.argv[2]
|
|
231
|
+
query = " ".join(sys.argv[3:])
|
|
232
|
+
|
|
233
|
+
# 'exercises' command doesn't necessarily need a query if defaults are used, but we'll use query as topic
|
|
234
|
+
if subcmd != 'exercises' and not query:
|
|
235
|
+
console.print("[bold red]Error:[/bold red] Falta la consulta.")
|
|
236
|
+
sys.exit(1)
|
|
237
|
+
|
|
238
|
+
with console.status(f"[bold green]Kimi AI ({subcmd})...[/bold green]"):
|
|
239
|
+
if subcmd == "solve":
|
|
240
|
+
result = kimi_service.solve_math_problem(query)
|
|
241
|
+
if isinstance(result, dict):
|
|
242
|
+
console.print(Panel(
|
|
243
|
+
f"[bold]Solución:[/bold]\n{result.get('solution', '')}\n\n"
|
|
244
|
+
f"[bold]Dificultad:[/bold] {result.get('difficulty', '')}\n"
|
|
245
|
+
f"[bold]Conceptos:[/bold] {', '.join(result.get('concepts', []))}",
|
|
246
|
+
title="Kimi AI: Resolución", border_style="green"
|
|
247
|
+
))
|
|
248
|
+
if result.get('steps'):
|
|
249
|
+
console.print("\n[bold]Pasos:[/bold]")
|
|
250
|
+
for step in result['steps']:
|
|
251
|
+
console.print(f"• {step}")
|
|
252
|
+
console.print(f"\n[dim italic]{result.get('reasoning', '')}[/dim italic]")
|
|
253
|
+
else:
|
|
254
|
+
console.print(result)
|
|
255
|
+
|
|
256
|
+
elif subcmd == "explain":
|
|
257
|
+
response = kimi_service.explain_concept(query)
|
|
258
|
+
console.print(Panel(Markdown(response), title=f"Kimi AI: Explicación", border_style="blue"))
|
|
259
|
+
|
|
260
|
+
elif subcmd == "exercises":
|
|
261
|
+
# Uso: binary ai exercises "Derivadas" [opcional: count]
|
|
262
|
+
# Por simplicidad en sys.argv, asumimos que todo el resto es el topic
|
|
263
|
+
exercises = kimi_service.generate_exercises(query if query else "Matemáticas generales")
|
|
264
|
+
|
|
265
|
+
console.print(f"[bold u]Generando ejercicios para:[/bold u] {query}\n")
|
|
266
|
+
|
|
267
|
+
for i, ex in enumerate(exercises, 1):
|
|
268
|
+
console.print(Panel(
|
|
269
|
+
f"[bold]Pregunta:[/bold]\n{ex.get('problem')}\n\n"
|
|
270
|
+
f"[bold]Solución:[/bold]\n{ex.get('solution')}",
|
|
271
|
+
title=f"Ejercicio {i}", border_style="magenta"
|
|
272
|
+
))
|
|
273
|
+
if ex.get('steps'):
|
|
274
|
+
with console.status(f"[dim]Ver pasos...[/dim]"):
|
|
275
|
+
# Hack para ocultar pasos inicialmente si se quisiera, pero aquí los mostramos
|
|
276
|
+
pass
|
|
277
|
+
console.print(f"[dim]Pasos: {', '.join(ex.get('steps', []))}[/dim]\n")
|
|
278
|
+
else:
|
|
279
|
+
console.print(f"[bold red]Comando desconocido:[/bold red] {subcmd}")
|
|
280
|
+
sys.exit(1)
|
|
281
|
+
|
|
217
282
|
elif len(sys.argv) > 1 and sys.argv[1] == 'feedback':
|
|
218
283
|
import webbrowser
|
|
219
284
|
print("""
|
|
@@ -226,10 +291,10 @@ def main():
|
|
|
226
291
|
Estoy abierto a cualquier sugerencia, apoyo, financiamiento,
|
|
227
292
|
compañía, o reporte de errores.
|
|
228
293
|
|
|
229
|
-
🐛 Bugs / Mejoras: https://github.com/
|
|
230
|
-
📧 Contacto:
|
|
294
|
+
🐛 Bugs / Mejoras: https://github.com/AldrasTeam/BinaryEquaLab/issues
|
|
295
|
+
📧 Contacto: contact@aldra.dev
|
|
231
296
|
""")
|
|
232
|
-
webbrowser.open("https://github.com/
|
|
297
|
+
webbrowser.open("https://github.com/AldrasTeam/BinaryEquaLab")
|
|
233
298
|
|
|
234
299
|
elif len(sys.argv) > 1:
|
|
235
300
|
# One-liner mode
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
|
|
2
|
+
import os
|
|
3
|
+
import json
|
|
4
|
+
import requests
|
|
5
|
+
from typing import List, Dict, Optional, Generator
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
|
|
8
|
+
console = Console()
|
|
9
|
+
|
|
10
|
+
class KimiService:
|
|
11
|
+
"""Service to interact with Kimi K2 (Moonshot AI) via Direct API"""
|
|
12
|
+
|
|
13
|
+
def __init__(self):
|
|
14
|
+
# Priority: Env Var > .env File > Config File
|
|
15
|
+
self.api_key = os.getenv('KIMI_API_KEY')
|
|
16
|
+
|
|
17
|
+
# Manually load .env if not found in environment
|
|
18
|
+
if not self.api_key:
|
|
19
|
+
try:
|
|
20
|
+
env_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), '.env')
|
|
21
|
+
if os.path.exists(env_path):
|
|
22
|
+
with open(env_path, 'r', encoding='utf-8') as f:
|
|
23
|
+
for line in f:
|
|
24
|
+
if line.strip().startswith('KIMI_API_KEY='):
|
|
25
|
+
self.api_key = line.strip().split('=', 1)[1].strip('"\'')
|
|
26
|
+
os.environ['KIMI_API_KEY'] = self.api_key
|
|
27
|
+
break
|
|
28
|
+
except Exception as e:
|
|
29
|
+
console.print(f"[yellow]Warning: Could not load .env file: {e}[/yellow]")
|
|
30
|
+
|
|
31
|
+
self.base_url = 'https://api.moonshot.cn/v1'
|
|
32
|
+
self.model = 'moonshot-v1-128k' # Using 128k model as K2 proxy
|
|
33
|
+
|
|
34
|
+
def set_api_key(self, key: str):
|
|
35
|
+
self.api_key = key
|
|
36
|
+
# In a real app, save this to a config file
|
|
37
|
+
os.environ['KIMI_API_KEY'] = key
|
|
38
|
+
|
|
39
|
+
def _get_headers(self):
|
|
40
|
+
if not self.api_key:
|
|
41
|
+
raise ValueError("KIMI_API_KEY not configured. Run 'export KIMI_API_KEY=sk-...'")
|
|
42
|
+
return {
|
|
43
|
+
'Content-Type': 'application/json',
|
|
44
|
+
'Authorization': f'Bearer {self.api_key}'
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
def chat(self, messages: List[Dict[str, str]], temperature: float = 0.7) -> str:
|
|
48
|
+
"""Send message to Kimi"""
|
|
49
|
+
try:
|
|
50
|
+
payload = {
|
|
51
|
+
'model': self.model,
|
|
52
|
+
'messages': messages,
|
|
53
|
+
'temperature': temperature,
|
|
54
|
+
}
|
|
55
|
+
response = requests.post(f'{self.base_url}/chat/completions', headers=self._get_headers(), json=payload)
|
|
56
|
+
response.raise_for_status()
|
|
57
|
+
return response.json()['choices'][0]['message']['content']
|
|
58
|
+
except Exception as e:
|
|
59
|
+
return f"Error connecting to Kimi AI: {str(e)}"
|
|
60
|
+
|
|
61
|
+
def solve_math_problem(self, problem: str, show_steps: bool = True) -> Dict[str, any]:
|
|
62
|
+
"""
|
|
63
|
+
Resolver problema matemático con razonamiento paso a paso.
|
|
64
|
+
Retorna un diccionario estructurado.
|
|
65
|
+
"""
|
|
66
|
+
system_prompt = f"""Eres un asistente matemático experto de Aldra's Team (Binary EquaLab AI).
|
|
67
|
+
|
|
68
|
+
Resuelve el siguiente problema {'mostrando TODOS los pasos' if show_steps else 'directamente'}.
|
|
69
|
+
|
|
70
|
+
Responde ESTRICTAMENTE en formato JSON con esta estructura:
|
|
71
|
+
{{
|
|
72
|
+
"solution": "Respuesta final matemática (LaTeX/texto)",
|
|
73
|
+
"steps": ["Paso 1: ...", "Paso 2: ..."],
|
|
74
|
+
"reasoning": "Explicación breve del enfoque",
|
|
75
|
+
"difficulty": "fácil|medio|difícil",
|
|
76
|
+
"concepts": ["Concepto 1", "Concepto 2"]
|
|
77
|
+
}}"""
|
|
78
|
+
|
|
79
|
+
messages = [
|
|
80
|
+
{'role': 'system', 'content': system_prompt},
|
|
81
|
+
{'role': 'user', 'content': problem}
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
response_text = self.chat(messages, temperature=0.3)
|
|
85
|
+
try:
|
|
86
|
+
# Limpiar markdown si el modelo lo incluye (e.g. ```json ... ```)
|
|
87
|
+
cleaned = response_text.replace('```json', '').replace('```', '').strip()
|
|
88
|
+
return json.loads(cleaned)
|
|
89
|
+
except json.JSONDecodeError:
|
|
90
|
+
return {
|
|
91
|
+
"solution": response_text,
|
|
92
|
+
"steps": ["No se pudo parsear la respuesta estructurada."],
|
|
93
|
+
"reasoning": "Respuesta directa del modelo.",
|
|
94
|
+
"difficulty": "desconocido",
|
|
95
|
+
"concepts": []
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
def explain_concept(self, concept: str, level: str = 'intermedio') -> str:
|
|
99
|
+
"""Explicación pedagógica de conceptos"""
|
|
100
|
+
system_prompt = f"""Eres un profesor de matemáticas apasionado de Aldra's Team.
|
|
101
|
+
Explica el concepto solicitado para un nivel {level}.
|
|
102
|
+
Usa analogías, claridad y rigor."""
|
|
103
|
+
|
|
104
|
+
messages = [
|
|
105
|
+
{'role': 'system', 'content': system_prompt},
|
|
106
|
+
{'role': 'user', 'content': f"Explícame: {concept}"}
|
|
107
|
+
]
|
|
108
|
+
return self.chat(messages, temperature=0.5)
|
|
109
|
+
|
|
110
|
+
def generate_exercises(self, topic: str, count: int = 5, difficulty: str = 'medio') -> List[Dict[str, any]]:
|
|
111
|
+
"""Generar ejercicios de práctica"""
|
|
112
|
+
system_prompt = f"""Genera {count} ejercicios de {topic} con dificultad {difficulty}.
|
|
113
|
+
|
|
114
|
+
Responde ESTRICTAMENTE en formato JSON (Array de objetos):
|
|
115
|
+
[
|
|
116
|
+
{{
|
|
117
|
+
"problem": "Enunciado",
|
|
118
|
+
"solution": "Respuesta",
|
|
119
|
+
"steps": ["Paso 1", "Paso 2"],
|
|
120
|
+
"concepts": ["Concepto A"]
|
|
121
|
+
}}
|
|
122
|
+
]"""
|
|
123
|
+
|
|
124
|
+
messages = [
|
|
125
|
+
{'role': 'system', 'content': system_prompt},
|
|
126
|
+
{'role': 'user', 'content': "Genera los ejercicios."}
|
|
127
|
+
]
|
|
128
|
+
|
|
129
|
+
response_text = self.chat(messages, temperature=0.7)
|
|
130
|
+
try:
|
|
131
|
+
cleaned = response_text.replace('```json', '').replace('```', '').strip()
|
|
132
|
+
return json.loads(cleaned)
|
|
133
|
+
except json.JSONDecodeError:
|
|
134
|
+
return []
|
|
135
|
+
|
|
136
|
+
# Singleton
|
|
137
|
+
kimi_service = KimiService()
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: binary-equalab
|
|
3
|
-
Version:
|
|
4
|
-
Summary:
|
|
5
|
-
Project-URL: Homepage, https://github.com/
|
|
6
|
-
Project-URL: Repository, https://github.com/
|
|
7
|
-
Project-URL: Documentation, https://github.com/
|
|
8
|
-
Author-email:
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
Classifier: Environment :: Console
|
|
13
|
-
Classifier: Intended Audience :: Education
|
|
3
|
+
Version: 3.0.0b1
|
|
4
|
+
Summary: Binary Equalab is a symbolic mathematics toolkit focused on clarity, performance and educational value. Designed for students, engineers and curious minds who want math to feel alive.
|
|
5
|
+
Project-URL: Homepage, https://github.com/AldrasTeam/BinaryEquaLab
|
|
6
|
+
Project-URL: Repository, https://github.com/AldrasTeam/BinaryEquaLab
|
|
7
|
+
Project-URL: Documentation, https://github.com/AldrasTeam/BinaryEquaLab#readme
|
|
8
|
+
Author-email: Aldra <manhalev1520@gmail.com>, Aldra's Team <manhalev1520@gmail.com>
|
|
9
|
+
Maintainer-email: "Aldra (José Avilés)" <manhalev1520@gmail.com>
|
|
10
|
+
License: MIT
|
|
11
|
+
Keywords: aldra,cas,cli,education,engineering,math,scientific-computing,symbolic-math
|
|
14
12
|
Classifier: Intended Audience :: Science/Research
|
|
15
13
|
Classifier: License :: OSI Approved :: MIT License
|
|
16
14
|
Classifier: Operating System :: OS Independent
|
|
@@ -21,10 +19,14 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
21
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
22
20
|
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
23
21
|
Requires-Python: >=3.9
|
|
24
|
-
Requires-Dist:
|
|
25
|
-
Requires-Dist:
|
|
26
|
-
Requires-Dist:
|
|
22
|
+
Requires-Dist: matplotlib>=3.7.0
|
|
23
|
+
Requires-Dist: numpy>=1.24.0
|
|
24
|
+
Requires-Dist: prompt-toolkit>=3.0.0
|
|
25
|
+
Requires-Dist: requests>=2.31.0
|
|
26
|
+
Requires-Dist: rich>=13.0.0
|
|
27
|
+
Requires-Dist: scipy>=1.10.0
|
|
27
28
|
Requires-Dist: sympy>=1.12
|
|
29
|
+
Requires-Dist: typer>=0.9.0
|
|
28
30
|
Provides-Extra: dev
|
|
29
31
|
Requires-Dist: black>=23.0; extra == 'dev'
|
|
30
32
|
Requires-Dist: mypy>=1.0; extra == 'dev'
|
|
@@ -186,4 +188,4 @@ pytest
|
|
|
186
188
|
|
|
187
189
|
---
|
|
188
190
|
|
|
189
|
-
MIT ©
|
|
191
|
+
MIT © Aldra's Team
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
binary_equalab/__init__.py,sha256=WFF_aioc3t6xkz0qU_RI4FeqY4UT92c8wE5cDDJWenY,675
|
|
2
|
-
binary_equalab/cli.py,sha256
|
|
2
|
+
binary_equalab/cli.py,sha256=kEA7Vw3gQZrRJJRFxDppqUo253jem424ZuczIwEjz60,12219
|
|
3
3
|
binary_equalab/engine.py,sha256=WhkKpdDCg2zqH9cMXDPMQ7Ut7Awyn4WzkVrRVP7_dtE,15931
|
|
4
4
|
binary_equalab/functions.py,sha256=F6GPCXoScCHVZ4CDEE2Rns5i3x89DAcKRVBte88j9oE,569
|
|
5
5
|
binary_equalab/geometry.py,sha256=YPn59oQtTY_hb4LZyIEJUvN1pWJz_y5uk8gVBio1Gwo,2217
|
|
6
6
|
binary_equalab/giac_poc.py,sha256=_XdxE2lrvFH7M6qDfzSQHRdplxPzKhKfIWAcsOQq8Ho,2226
|
|
7
|
+
binary_equalab/kimi_service.py,sha256=wofChDT1tB9lN72jwC6DcMdd8Xg2VfbJj2Mt5seQ-R0,5491
|
|
7
8
|
binary_equalab/parser_enhanced.py,sha256=P7oMnGHWn3xoqVChkPJ6B_i_IR9TeJMJXBjKoCO13ts,2735
|
|
8
9
|
binary_equalab/shell_setup.py,sha256=9Ax_QcHEAopYIjShuBaHglg4HPysXJKbNL3D_FjexrU,4518
|
|
9
10
|
binary_equalab/sonify.py,sha256=HfAYPtXLRyb3miLBsd8liqk4D5hvXVBHEEyV6fj9Za8,3274
|
|
10
|
-
binary_equalab-
|
|
11
|
-
binary_equalab-
|
|
12
|
-
binary_equalab-
|
|
13
|
-
binary_equalab-
|
|
11
|
+
binary_equalab-3.0.0b1.dist-info/METADATA,sha256=is9DrY6iTKa1t6Z38P2SvgFbGfvzND3BhCWWGyP7_Jw,5357
|
|
12
|
+
binary_equalab-3.0.0b1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
13
|
+
binary_equalab-3.0.0b1.dist-info/entry_points.txt,sha256=2Io1Wi069dvgPHmGXmAT1a6rNA-DIq7tChai69jtbF0,119
|
|
14
|
+
binary_equalab-3.0.0b1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|