moodlesolver 2.0.0__tar.gz → 2.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.
- {moodlesolver-2.0.0 → moodlesolver-2.1.0}/PKG-INFO +7 -2
- {moodlesolver-2.0.0 → moodlesolver-2.1.0}/README.md +6 -1
- {moodlesolver-2.0.0 → moodlesolver-2.1.0}/moodlesolver/__init__.py +2 -1
- moodlesolver-2.1.0/moodlesolver/aipreview.py +26 -0
- {moodlesolver-2.0.0 → moodlesolver-2.1.0}/moodlesolver/jsonapi.py +17 -4
- {moodlesolver-2.0.0 → moodlesolver-2.1.0}/moodlesolver.egg-info/PKG-INFO +7 -2
- {moodlesolver-2.0.0 → moodlesolver-2.1.0}/moodlesolver.egg-info/SOURCES.txt +1 -0
- {moodlesolver-2.0.0 → moodlesolver-2.1.0}/pyproject.toml +1 -1
- {moodlesolver-2.0.0 → moodlesolver-2.1.0}/LICENSE +0 -0
- {moodlesolver-2.0.0 → moodlesolver-2.1.0}/moodlesolver/airunner.py +0 -0
- {moodlesolver-2.0.0 → moodlesolver-2.1.0}/moodlesolver/cdn.py +0 -0
- {moodlesolver-2.0.0 → moodlesolver-2.1.0}/moodlesolver/client.py +0 -0
- {moodlesolver-2.0.0 → moodlesolver-2.1.0}/moodlesolver/ctx.py +0 -0
- {moodlesolver-2.0.0 → moodlesolver-2.1.0}/moodlesolver/errors.py +0 -0
- {moodlesolver-2.0.0 → moodlesolver-2.1.0}/moodlesolver/mcp_server.py +0 -0
- {moodlesolver-2.0.0 → moodlesolver-2.1.0}/moodlesolver/parse.py +0 -0
- {moodlesolver-2.0.0 → moodlesolver-2.1.0}/moodlesolver/quizapi.py +0 -0
- {moodlesolver-2.0.0 → moodlesolver-2.1.0}/moodlesolver/schema/quiz_protocol.json +0 -0
- {moodlesolver-2.0.0 → moodlesolver-2.1.0}/moodlesolver/vision.py +0 -0
- {moodlesolver-2.0.0 → moodlesolver-2.1.0}/moodlesolver.egg-info/dependency_links.txt +0 -0
- {moodlesolver-2.0.0 → moodlesolver-2.1.0}/moodlesolver.egg-info/entry_points.txt +0 -0
- {moodlesolver-2.0.0 → moodlesolver-2.1.0}/moodlesolver.egg-info/requires.txt +0 -0
- {moodlesolver-2.0.0 → moodlesolver-2.1.0}/moodlesolver.egg-info/top_level.txt +0 -0
- {moodlesolver-2.0.0 → moodlesolver-2.1.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: moodlesolver
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Summary: Moodle quiz automation library and MCP server with local OCR
|
|
5
5
|
Author: i-execute
|
|
6
6
|
License: MIT
|
|
@@ -80,7 +80,12 @@ asyncio.run(main())
|
|
|
80
80
|
Каждый ответ содержит: `page`, `question`, `question_hash`, `qtype`, `options`, `checked` (что реально отмечено на сервере), `selects`, `text_inputs`, `timeleft`, `buttons`, а также:
|
|
81
81
|
|
|
82
82
|
- `question_image_b64` / `option_images_b64` — картинки в base64 (или ссылки при `image_mode="url"`)
|
|
83
|
-
- `ocr` — локальное превью каждой картинки: `{"formula": "…", "ru": "…", "en": "…"}`
|
|
83
|
+
- `ocr` — локальное превью каждой картинки: `{"formula": "…", "ru": "…", "en": "…"}`, а при `ai_preview=True` ещё `ai_preview` — описание картинок vision-моделью OpenCode (mimo-v2.6-flash-free по умолчанию)
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
api = JsonAPI(q, ai_preview=True) # включить AI-описание картинок
|
|
87
|
+
api = JsonAPI(q, ai_preview=True, ai_model="opencode/longcat-2.5-preview-free")
|
|
88
|
+
```
|
|
84
89
|
|
|
85
90
|
Подробная схема: `moodlesolver/schema/quiz_protocol.json`.
|
|
86
91
|
|
|
@@ -63,7 +63,12 @@ asyncio.run(main())
|
|
|
63
63
|
Каждый ответ содержит: `page`, `question`, `question_hash`, `qtype`, `options`, `checked` (что реально отмечено на сервере), `selects`, `text_inputs`, `timeleft`, `buttons`, а также:
|
|
64
64
|
|
|
65
65
|
- `question_image_b64` / `option_images_b64` — картинки в base64 (или ссылки при `image_mode="url"`)
|
|
66
|
-
- `ocr` — локальное превью каждой картинки: `{"formula": "…", "ru": "…", "en": "…"}`
|
|
66
|
+
- `ocr` — локальное превью каждой картинки: `{"formula": "…", "ru": "…", "en": "…"}`, а при `ai_preview=True` ещё `ai_preview` — описание картинок vision-моделью OpenCode (mimo-v2.6-flash-free по умолчанию)
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
api = JsonAPI(q, ai_preview=True) # включить AI-описание картинок
|
|
70
|
+
api = JsonAPI(q, ai_preview=True, ai_model="opencode/longcat-2.5-preview-free")
|
|
71
|
+
```
|
|
67
72
|
|
|
68
73
|
Подробная схема: `moodlesolver/schema/quiz_protocol.json`.
|
|
69
74
|
|
|
@@ -5,6 +5,7 @@ from .ctx import CtxAPI
|
|
|
5
5
|
from .airunner import AIRunner
|
|
6
6
|
from .jsonapi import JsonAPI
|
|
7
7
|
from .vision import ocr_file, make_on_ocr
|
|
8
|
+
from .aipreview import ai_preview, opencode_available
|
|
8
9
|
from .cdn import serve_dumps
|
|
9
10
|
from .mcp_server import main as mcp_main
|
|
10
|
-
__all__ = ['AsyncMoodle', 'AsyncTab', 'CamofoxError', 'parse_page', 'QuizAPI', 'CtxAPI', 'AIRunner', 'JsonAPI', 'mcp_main']
|
|
11
|
+
__all__ = ['AsyncMoodle', 'AsyncTab', 'CamofoxError', 'parse_page', 'QuizAPI', 'CtxAPI', 'AIRunner', 'JsonAPI', 'mcp_main', 'ai_preview', 'opencode_available', 'serve_dumps', 'ocr_file', 'make_on_ocr']
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import shutil
|
|
2
|
+
import subprocess
|
|
3
|
+
|
|
4
|
+
OC_DEFAULT_MODEL = "opencode/mimo-v2.6-flash-free"
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def opencode_available():
|
|
8
|
+
return shutil.which("opencode") is not None
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def ai_preview(paths, prompt="Опиши что на картинке, весь текст перепиши",
|
|
12
|
+
model=OC_DEFAULT_MODEL, timeout=120):
|
|
13
|
+
if not opencode_available() or not paths:
|
|
14
|
+
return None
|
|
15
|
+
cmd = ["opencode", "run", prompt, "--model", model]
|
|
16
|
+
for p in paths:
|
|
17
|
+
cmd += ["-f", p]
|
|
18
|
+
try:
|
|
19
|
+
out = subprocess.run(cmd, capture_output=True, text=True,
|
|
20
|
+
timeout=timeout).stdout.strip()
|
|
21
|
+
except Exception:
|
|
22
|
+
return None
|
|
23
|
+
if not out or "Error" in out.splitlines()[0]:
|
|
24
|
+
return None
|
|
25
|
+
lines = [ln for ln in out.splitlines() if not ln.startswith(">")]
|
|
26
|
+
return "\n".join(lines).strip() or None
|
|
@@ -28,8 +28,9 @@ def _qtype(st):
|
|
|
28
28
|
return 'unknown'
|
|
29
29
|
|
|
30
30
|
class JsonAPI:
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
def __init__(self, quiz_api, on_ocr=None, image_mode='b64',
|
|
32
|
+
image_base_url=None, ai_preview=False,
|
|
33
|
+
ai_model=None):
|
|
33
34
|
self.q = quiz_api
|
|
34
35
|
if on_ocr is None:
|
|
35
36
|
from .vision import make_on_ocr
|
|
@@ -37,6 +38,9 @@ class JsonAPI:
|
|
|
37
38
|
self.on_ocr = on_ocr
|
|
38
39
|
self.image_mode = image_mode
|
|
39
40
|
self.image_base_url = image_base_url
|
|
41
|
+
self.ai = bool(ai_preview)
|
|
42
|
+
from .aipreview import OC_DEFAULT_MODEL
|
|
43
|
+
self.ai_model = ai_model or OC_DEFAULT_MODEL
|
|
40
44
|
|
|
41
45
|
async def handle(self, command):
|
|
42
46
|
c = json.loads(command) if isinstance(command, str) else command
|
|
@@ -78,8 +82,6 @@ class JsonAPI:
|
|
|
78
82
|
return v
|
|
79
83
|
|
|
80
84
|
async def _ocr_preview(self, st):
|
|
81
|
-
if not self.on_ocr:
|
|
82
|
-
return None
|
|
83
85
|
import asyncio as aio
|
|
84
86
|
files = []
|
|
85
87
|
if st.get('question_image_file'):
|
|
@@ -88,6 +90,7 @@ class JsonAPI:
|
|
|
88
90
|
files.append((f, f))
|
|
89
91
|
if not files:
|
|
90
92
|
return None
|
|
93
|
+
from .vision import _resolve
|
|
91
94
|
|
|
92
95
|
async def one(name, f):
|
|
93
96
|
ru, en, formula = await aio.gather(aio.to_thread(self.on_ocr, f, 'текст русский'), aio.to_thread(self.on_ocr, f, 'text english'), aio.to_thread(self.on_ocr, f, 'формула'), return_exceptions=True)
|
|
@@ -102,6 +105,16 @@ class JsonAPI:
|
|
|
102
105
|
out.setdefault('errors', []).append(str(p)[:60])
|
|
103
106
|
else:
|
|
104
107
|
out[p[0]] = p[1]
|
|
108
|
+
if self.ai:
|
|
109
|
+
try:
|
|
110
|
+
from .aipreview import ai_preview
|
|
111
|
+
paths = [_resolve(f) for _, f in files]
|
|
112
|
+
text = await aio.to_thread(ai_preview, paths,
|
|
113
|
+
model=self.ai_model)
|
|
114
|
+
if text:
|
|
115
|
+
out['ai_preview'] = text[:2000]
|
|
116
|
+
except Exception:
|
|
117
|
+
pass
|
|
105
118
|
return out or None
|
|
106
119
|
|
|
107
120
|
async def _select(self, c):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: moodlesolver
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Summary: Moodle quiz automation library and MCP server with local OCR
|
|
5
5
|
Author: i-execute
|
|
6
6
|
License: MIT
|
|
@@ -80,7 +80,12 @@ asyncio.run(main())
|
|
|
80
80
|
Каждый ответ содержит: `page`, `question`, `question_hash`, `qtype`, `options`, `checked` (что реально отмечено на сервере), `selects`, `text_inputs`, `timeleft`, `buttons`, а также:
|
|
81
81
|
|
|
82
82
|
- `question_image_b64` / `option_images_b64` — картинки в base64 (или ссылки при `image_mode="url"`)
|
|
83
|
-
- `ocr` — локальное превью каждой картинки: `{"formula": "…", "ru": "…", "en": "…"}`
|
|
83
|
+
- `ocr` — локальное превью каждой картинки: `{"formula": "…", "ru": "…", "en": "…"}`, а при `ai_preview=True` ещё `ai_preview` — описание картинок vision-моделью OpenCode (mimo-v2.6-flash-free по умолчанию)
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
api = JsonAPI(q, ai_preview=True) # включить AI-описание картинок
|
|
87
|
+
api = JsonAPI(q, ai_preview=True, ai_model="opencode/longcat-2.5-preview-free")
|
|
88
|
+
```
|
|
84
89
|
|
|
85
90
|
Подробная схема: `moodlesolver/schema/quiz_protocol.json`.
|
|
86
91
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|