Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d347471cc0 | |||
| 0a365b568a | |||
| e303d4dc1e | |||
| cb78290f05 | |||
| e0417981b1 | |||
| a0660d3348 | |||
| f5e0417292 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -15,8 +15,10 @@ old/
|
|||||||
data/cache/
|
data/cache/
|
||||||
data/electron/
|
data/electron/
|
||||||
data/nucleon/
|
data/nucleon/
|
||||||
!data/nucleon/test*
|
data/global/
|
||||||
|
!data/nucleon/TEST*
|
||||||
data/orbital/
|
data/orbital/
|
||||||
|
config/config_dev.toml
|
||||||
AGENTS.md
|
AGENTS.md
|
||||||
|
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
|
|||||||
@@ -10,13 +10,15 @@
|
|||||||
- `dev` 分支: 开发版本
|
- `dev` 分支: 开发版本
|
||||||
- 功能分支: 从 `dev` 分支创建, 命名格式为 `feature/描述` 或 `fix/描述` 或 `refactor/描述`
|
- 功能分支: 从 `dev` 分支创建, 命名格式为 `feature/描述` 或 `fix/描述` 或 `refactor/描述`
|
||||||
2. **代码风格**:
|
2. **代码风格**:
|
||||||
- 请使用 Black 格式化代码
|
- 请使用 Black 与 isort 格式化代码
|
||||||
- 遵循 PEP 8 规范
|
- 遵循 PEP 8 规范
|
||||||
- 添加适当的文档字符串
|
- 添加适当的文档字符串
|
||||||
3. **提交消息**:
|
3. **提交消息**:
|
||||||
- 使用简体中文或英文撰写清晰的提交消息
|
- 使用简体中文或英文撰写清晰的提交消息
|
||||||
- 格式: 遵循 Conventional Commits 规范
|
- 格式: 遵循 Conventional Commits 规范
|
||||||
|
4. **合并方式**:
|
||||||
|
- 不使用 Fast-forward 合并
|
||||||
|
- 可以设置 `git config merge.ff false`
|
||||||
## 设置开发环境
|
## 设置开发环境
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -14,6 +14,14 @@ scheduled_num = 8
|
|||||||
# UTC 时间戳修正 仅用于 UNIX 日时间戳的生成修正, 单位为秒
|
# UTC 时间戳修正 仅用于 UNIX 日时间戳的生成修正, 单位为秒
|
||||||
timezone_offset = +28800 # 中国标准时间 (UTC+8)
|
timezone_offset = +28800 # 中国标准时间 (UTC+8)
|
||||||
|
|
||||||
|
[interface]
|
||||||
|
|
||||||
|
[interface.memorizor]
|
||||||
|
autovoice = true # 自动语音播放, 仅限于 recognition 组件
|
||||||
|
|
||||||
|
[algorithm]
|
||||||
|
default = "SM-2" # 主要算法; 可选项: SM-2, SM-15M, FSRS
|
||||||
|
|
||||||
[puzzles] # 谜题默认配置
|
[puzzles] # 谜题默认配置
|
||||||
|
|
||||||
[puzzles.mcq]
|
[puzzles.mcq]
|
||||||
@@ -25,6 +33,7 @@ min_denominator = 3
|
|||||||
[paths] # 相对于配置文件的 ".." (即工作目录) 而言 或绝对路径
|
[paths] # 相对于配置文件的 ".." (即工作目录) 而言 或绝对路径
|
||||||
nucleon_dir = "./data/nucleon"
|
nucleon_dir = "./data/nucleon"
|
||||||
electron_dir = "./data/electron"
|
electron_dir = "./data/electron"
|
||||||
|
global_dir = "./data/global" # 全局数据路径, SM-15 等算法需要
|
||||||
orbital_dir = "./data/orbital"
|
orbital_dir = "./data/orbital"
|
||||||
cache_dir = "./data/cache"
|
cache_dir = "./data/cache"
|
||||||
template_dir = "./data/template"
|
template_dir = "./data/template"
|
||||||
@@ -34,6 +43,9 @@ audio = "playsound" # 可选项: playsound(通用), termux(仅用于支持 Andro
|
|||||||
tts = "edgetts" # 可选项: edgetts
|
tts = "edgetts" # 可选项: edgetts
|
||||||
llm = "openai" # 可选项: openai
|
llm = "openai" # 可选项: openai
|
||||||
|
|
||||||
|
[providers.tts.edgetts] # EdgeTTS 设置
|
||||||
|
voice = "zh-CN-XiaoxiaoNeural" # 可选项: zh-CN-YunjianNeural (男声), zh-CN-XiaoxiaoNeural (女声)
|
||||||
|
|
||||||
[providers.llm.openai] # 与 OpenAI 相容的语言模型接口服务设置
|
[providers.llm.openai] # 与 OpenAI 相容的语言模型接口服务设置
|
||||||
url = ""
|
url = ""
|
||||||
key = ""
|
key = ""
|
||||||
|
|||||||
@@ -25,3 +25,4 @@ readme = "README.md"
|
|||||||
|
|
||||||
[tool.setuptools.packages.find]
|
[tool.setuptools.packages.find]
|
||||||
where = ["src"]
|
where = ["src"]
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,11 @@ try:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("未能加载自定义用户配置")
|
print("未能加载自定义用户配置")
|
||||||
logger.warning("未能加载自定义用户配置, 错误: %s", e)
|
logger.warning("未能加载自定义用户配置, 错误: %s", e)
|
||||||
|
if pathlib.Path(rootdir / "default" / "config" / "config_dev.toml").exists():
|
||||||
|
logger.debug("使用开发设置")
|
||||||
|
config_var: ContextVar[ConfigFile] = ContextVar(
|
||||||
|
"config_var", default=ConfigFile(workdir / "config" / "config_dev.toml")
|
||||||
|
)
|
||||||
# runtime_var: ContextVar = ContextVar('runtime_var', default=dict()) # 运行时共享数据
|
# runtime_var: ContextVar = ContextVar('runtime_var', default=dict()) # 运行时共享数据
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
0
src/heurams/default/config/config_dev.toml
Normal file
0
src/heurams/default/config/config_dev.toml
Normal file
60
src/heurams/interface/__init__.py
Normal file
60
src/heurams/interface/__init__.py
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
from textual.app import App
|
||||||
|
from textual.widgets import Button
|
||||||
|
|
||||||
|
from heurams.context import config_var
|
||||||
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
|
from .screens.about import AboutScreen
|
||||||
|
from .screens.dashboard import DashboardScreen
|
||||||
|
from .screens.nucreator import NucleonCreatorScreen
|
||||||
|
from .screens.precache import PrecachingScreen
|
||||||
|
|
||||||
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def environment_check():
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
logger.debug("检查环境路径")
|
||||||
|
|
||||||
|
for i in config_var.get()["paths"].values():
|
||||||
|
i = Path(i)
|
||||||
|
if not i.exists():
|
||||||
|
logger.info("创建目录: %s", i)
|
||||||
|
print(f"创建 {i}")
|
||||||
|
i.mkdir(exist_ok=True, parents=True)
|
||||||
|
else:
|
||||||
|
logger.debug("目录已存在: %s", i)
|
||||||
|
print(f"找到 {i}")
|
||||||
|
logger.debug("环境检查完成")
|
||||||
|
|
||||||
|
|
||||||
|
class HeurAMSApp(App):
|
||||||
|
TITLE = "潜进"
|
||||||
|
CSS_PATH = "css/main.tcss"
|
||||||
|
SUB_TITLE = "启发式辅助记忆调度器"
|
||||||
|
BINDINGS = [
|
||||||
|
("q", "quit", "退出"),
|
||||||
|
("d", "toggle_dark", "切换色调"),
|
||||||
|
("1", "app.push_screen('dashboard')", "仪表盘"),
|
||||||
|
("2", "app.push_screen('precache_all')", "缓存管理器"),
|
||||||
|
("3", "app.push_screen('nucleon_creator')", "创建新单元"),
|
||||||
|
("0", "app.push_screen('about')", "版本信息"),
|
||||||
|
]
|
||||||
|
SCREENS = {
|
||||||
|
"dashboard": DashboardScreen,
|
||||||
|
"nucleon_creator": NucleonCreatorScreen,
|
||||||
|
"precache_all": PrecachingScreen,
|
||||||
|
"about": AboutScreen,
|
||||||
|
}
|
||||||
|
|
||||||
|
def on_mount(self) -> None:
|
||||||
|
environment_check()
|
||||||
|
self.push_screen("dashboard")
|
||||||
|
|
||||||
|
def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||||
|
self.exit(event.button.id)
|
||||||
|
|
||||||
|
def action_do_nothing(self):
|
||||||
|
print("DO NOTHING")
|
||||||
|
self.refresh()
|
||||||
@@ -2,6 +2,8 @@ from textual.app import App
|
|||||||
from textual.widgets import Button
|
from textual.widgets import Button
|
||||||
|
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
from heurams.context import config_var
|
||||||
|
from heurams.interface import HeurAMSApp
|
||||||
|
|
||||||
from .screens.about import AboutScreen
|
from .screens.about import AboutScreen
|
||||||
from .screens.dashboard import DashboardScreen
|
from .screens.dashboard import DashboardScreen
|
||||||
@@ -10,80 +12,7 @@ from .screens.precache import PrecachingScreen
|
|||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class HeurAMSApp(App):
|
|
||||||
TITLE = "潜进"
|
|
||||||
CSS_PATH = "css/main.tcss"
|
|
||||||
SUB_TITLE = "启发式辅助记忆调度器"
|
|
||||||
BINDINGS = [
|
|
||||||
("q", "quit", "退出"),
|
|
||||||
("d", "toggle_dark", "切换色调"),
|
|
||||||
("1", "app.push_screen('dashboard')", "仪表盘"),
|
|
||||||
("2", "app.push_screen('precache_all')", "缓存管理器"),
|
|
||||||
("3", "app.push_screen('nucleon_creator')", "创建新单元"),
|
|
||||||
("0", "app.push_screen('about')", "版本信息"),
|
|
||||||
]
|
|
||||||
SCREENS = {
|
|
||||||
"dashboard": DashboardScreen,
|
|
||||||
"nucleon_creator": NucleonCreatorScreen,
|
|
||||||
"precache_all": PrecachingScreen,
|
|
||||||
"about": AboutScreen,
|
|
||||||
}
|
|
||||||
|
|
||||||
def on_mount(self) -> None:
|
|
||||||
self.push_screen("dashboard")
|
|
||||||
|
|
||||||
def on_button_pressed(self, event: Button.Pressed) -> None:
|
|
||||||
self.exit(event.button.id)
|
|
||||||
|
|
||||||
def action_do_nothing(self):
|
|
||||||
print("DO NOTHING")
|
|
||||||
self.refresh()
|
|
||||||
|
|
||||||
|
|
||||||
def environment_check():
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
logger.debug("检查环境路径")
|
|
||||||
|
|
||||||
for i in config_var.get()["paths"].values():
|
|
||||||
i = Path(i)
|
|
||||||
if not i.exists():
|
|
||||||
logger.info("创建目录: %s", i)
|
|
||||||
print(f"创建 {i}")
|
|
||||||
i.mkdir(exist_ok=True, parents=True)
|
|
||||||
else:
|
|
||||||
logger.debug("目录已存在: %s", i)
|
|
||||||
print(f"找到 {i}")
|
|
||||||
logger.debug("环境检查完成")
|
|
||||||
|
|
||||||
|
|
||||||
def is_subdir(parent, child):
|
|
||||||
try:
|
|
||||||
child.relative_to(parent)
|
|
||||||
logger.debug("is_subdir: %s 是 %s 的子目录", child, parent)
|
|
||||||
return 1
|
|
||||||
except:
|
|
||||||
logger.debug("is_subdir: %s 不是 %s 的子目录", child, parent)
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
import os
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
# 开发模式
|
|
||||||
from heurams.context import config_var, rootdir, workdir
|
|
||||||
|
|
||||||
if is_subdir(Path(rootdir), Path(os.getcwd())):
|
|
||||||
os.chdir(Path(rootdir) / ".." / "..")
|
|
||||||
print(f'转入开发数据目录: {Path(rootdir)/".."/".."}')
|
|
||||||
|
|
||||||
environment_check()
|
|
||||||
|
|
||||||
app = HeurAMSApp()
|
app = HeurAMSApp()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app.run()
|
app.run()
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
app.run()
|
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ class DashboardScreen(Screen):
|
|||||||
is_activated = 1
|
is_activated = 1
|
||||||
nextdate = min(nextdate, i.nextdate())
|
nextdate = min(nextdate, i.nextdate())
|
||||||
res[1] = f"下一次复习: {nextdate}\n"
|
res[1] = f"下一次复习: {nextdate}\n"
|
||||||
res[1] += f"{is_due if "需要复习" else "当前无需复习"}"
|
res[1] += f"{"需要复习" if is_due else "当前无需复习"}"
|
||||||
if not is_activated:
|
if not is_activated:
|
||||||
res[1] = " 尚未激活"
|
res[1] = " 尚未激活"
|
||||||
return res
|
return res
|
||||||
|
|||||||
@@ -46,14 +46,14 @@ class MemScreen(Screen):
|
|||||||
) -> None:
|
) -> None:
|
||||||
super().__init__(name, id, classes)
|
super().__init__(name, id, classes)
|
||||||
self.atoms = atoms
|
self.atoms = atoms
|
||||||
|
for i in self.atoms:
|
||||||
|
i.do_eval()
|
||||||
self.phaser = Phaser(atoms)
|
self.phaser = Phaser(atoms)
|
||||||
# logger.debug(self.phaser.state)
|
# logger.debug(self.phaser.state)
|
||||||
self.procession: Procession = self.phaser.current_procession() # type: ignore
|
self.procession: Procession = self.phaser.current_procession() # type: ignore
|
||||||
self.atom: pt.Atom = self.procession.current_atom
|
self.atom: pt.Atom = self.procession.current_atom
|
||||||
# logger.debug(self.phaser.state)
|
# logger.debug(self.phaser.state)
|
||||||
# self.procession.forward(1)
|
# self.procession.forward(1)
|
||||||
for i in atoms:
|
|
||||||
i.do_eval()
|
|
||||||
|
|
||||||
def on_mount(self):
|
def on_mount(self):
|
||||||
self.load_puzzle()
|
self.load_puzzle()
|
||||||
@@ -144,8 +144,21 @@ class MemScreen(Screen):
|
|||||||
self.atom.lock(1)
|
self.atom.lock(1)
|
||||||
|
|
||||||
def action_play_voice(self):
|
def action_play_voice(self):
|
||||||
|
self.run_worker(self.play_voice, exclusive=True, thread=True)
|
||||||
|
|
||||||
|
def play_voice(self):
|
||||||
"""朗读当前内容"""
|
"""朗读当前内容"""
|
||||||
pass
|
from heurams.services.audio_service import play_by_path
|
||||||
|
from pathlib import Path
|
||||||
|
from heurams.services.hasher import get_md5
|
||||||
|
path = Path(config_var.get()['paths']["cache_dir"])
|
||||||
|
path = path / f"{get_md5(self.atom.registry['nucleon'].metadata["formation"]["tts_text"])}.wav"
|
||||||
|
if path.exists():
|
||||||
|
play_by_path(path)
|
||||||
|
else:
|
||||||
|
from heurams.services.tts_service import convertor
|
||||||
|
convertor(self.atom.registry['nucleon'].metadata["formation"]["tts_text"], path)
|
||||||
|
play_by_path(path)
|
||||||
|
|
||||||
def action_toggle_dark(self):
|
def action_toggle_dark(self):
|
||||||
self.app.action_toggle_dark()
|
self.app.action_toggle_dark()
|
||||||
|
|||||||
@@ -39,7 +39,9 @@ class PrecachingScreen(Screen):
|
|||||||
self.desc = desc
|
self.desc = desc
|
||||||
for i in nucleons:
|
for i in nucleons:
|
||||||
i: pt.Nucleon
|
i: pt.Nucleon
|
||||||
i.do_eval()
|
atom = pt.Atom()
|
||||||
|
atom.link("nucleon", i)
|
||||||
|
atom.do_eval()
|
||||||
# print("完成 EVAL")
|
# print("完成 EVAL")
|
||||||
|
|
||||||
def compose(self) -> ComposeResult:
|
def compose(self) -> ComposeResult:
|
||||||
@@ -95,11 +97,9 @@ class PrecachingScreen(Screen):
|
|||||||
cache_dir.mkdir(parents=True, exist_ok=True)
|
cache_dir.mkdir(parents=True, exist_ok=True)
|
||||||
cache_file = cache_dir / f"{hasher.get_md5(text)}.wav"
|
cache_file = cache_dir / f"{hasher.get_md5(text)}.wav"
|
||||||
if not cache_file.exists():
|
if not cache_file.exists():
|
||||||
try: # TODO: 调用模块消除tts耦合
|
try:
|
||||||
import edge_tts as tts
|
from heurams.services.tts_service import convertor
|
||||||
|
convertor(text, cache_file)
|
||||||
communicate = tts.Communicate(text, "zh-CN-XiaoxiaoNeural")
|
|
||||||
communicate.save_sync(str(cache_file))
|
|
||||||
return 1
|
return 1
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"预缓存失败 '{text}': {e}")
|
print(f"预缓存失败 '{text}': {e}")
|
||||||
@@ -178,7 +178,9 @@ class PrecachingScreen(Screen):
|
|||||||
self.total = len(nu)
|
self.total = len(nu)
|
||||||
for i in nu:
|
for i in nu:
|
||||||
i: pt.Nucleon
|
i: pt.Nucleon
|
||||||
i.do_eval()
|
atom = pt.Atom()
|
||||||
|
atom.link("nucleon", i)
|
||||||
|
atom.do_eval()
|
||||||
return self.precache_by_list(nu)
|
return self.precache_by_list(nu)
|
||||||
|
|
||||||
def on_button_pressed(self, event: Button.Pressed) -> None:
|
def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||||
|
|||||||
@@ -61,11 +61,16 @@ class MCQPuzzle(BasePuzzleWidget):
|
|||||||
self.puzzle.refresh()
|
self.puzzle.refresh()
|
||||||
|
|
||||||
def compose(self):
|
def compose(self):
|
||||||
self.atom.registry["nucleon"].do_eval()
|
|
||||||
setting: Setting = self.atom.registry["nucleon"].metadata["orbital"]["puzzles"][
|
setting: Setting = self.atom.registry["nucleon"].metadata["orbital"]["puzzles"][
|
||||||
self.alia
|
self.alia
|
||||||
]
|
]
|
||||||
logger.debug(f"Puzzle Setting: {setting}")
|
logger.debug(f"Puzzle Setting: {setting}")
|
||||||
|
logger.debug(f"WIRED INDEX: {len(self.inputlist)}")
|
||||||
|
if len(self.inputlist) > len(self.puzzle.options):
|
||||||
|
logger.debug("ERR IDX")
|
||||||
|
logger.debug(self.inputlist)
|
||||||
|
logger.debug(self.puzzle.options)
|
||||||
|
else:
|
||||||
current_options = self.puzzle.options[len(self.inputlist)]
|
current_options = self.puzzle.options[len(self.inputlist)]
|
||||||
yield Label(setting["primary"], id="sentence")
|
yield Label(setting["primary"], id="sentence")
|
||||||
yield Label(self.puzzle.wording[len(self.inputlist)], id="puzzle")
|
yield Label(self.puzzle.wording[len(self.inputlist)], id="puzzle")
|
||||||
|
|||||||
@@ -49,6 +49,10 @@ class Recognition(BasePuzzleWidget):
|
|||||||
self.alia = alia
|
self.alia = alia
|
||||||
|
|
||||||
def compose(self):
|
def compose(self):
|
||||||
|
from heurams.context import config_var
|
||||||
|
autovoice = config_var.get()['interface']['memorizor']['autovoice']
|
||||||
|
if autovoice:
|
||||||
|
self.screen.action_play_voice() # type: ignore
|
||||||
cfg: RecognitionConfig = self.atom.registry["orbital"]["puzzles"][self.alia]
|
cfg: RecognitionConfig = self.atom.registry["orbital"]["puzzles"][self.alia]
|
||||||
delim = self.atom.registry["nucleon"].metadata["formation"]["delimiter"]
|
delim = self.atom.registry["nucleon"].metadata["formation"]["delimiter"]
|
||||||
replace_dict = {
|
replace_dict = {
|
||||||
@@ -68,7 +72,8 @@ class Recognition(BasePuzzleWidget):
|
|||||||
primary = cfg["primary"]
|
primary = cfg["primary"]
|
||||||
|
|
||||||
with Center():
|
with Center():
|
||||||
yield Static(f"[dim]{cfg['top_dim']}[/]")
|
for i in cfg['top_dim']:
|
||||||
|
yield Static(f"[dim]{i}[/]")
|
||||||
yield Label("")
|
yield Label("")
|
||||||
|
|
||||||
for old, new in replace_dict.items():
|
for old, new in replace_dict.items():
|
||||||
|
|||||||
@@ -2,21 +2,23 @@
|
|||||||
SM-15 接口兼容实现, 基于 SM-15 算法的逆向工程
|
SM-15 接口兼容实现, 基于 SM-15 算法的逆向工程
|
||||||
全局状态保存在文件中, 项目状态通过 algodata 字典传递
|
全局状态保存在文件中, 项目状态通过 algodata 字典传递
|
||||||
|
|
||||||
基于: https://github.com/kazuaki/sm.js
|
基于: https://github.com/slaypni/sm.js
|
||||||
原始 CoffeeScript 代码: (c) 2014 Kazuaki Tanida (MIT 许可证)
|
原始 CoffeeScript 代码: (c) 2014 Kazuaki Tanida
|
||||||
|
MIT 许可证
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
from typing import TypedDict
|
from typing import TypedDict
|
||||||
|
import pathlib
|
||||||
|
from heurams.context import config_var
|
||||||
from heurams.kernel.algorithms.sm15m_calc import (MAX_AF, MIN_AF, NOTCH_AF,
|
from heurams.kernel.algorithms.sm15m_calc import (MAX_AF, MIN_AF, NOTCH_AF,
|
||||||
RANGE_AF, RANGE_REPETITION,
|
RANGE_AF, RANGE_REPETITION,
|
||||||
SM, THRESHOLD_RECALL, Item)
|
SM, THRESHOLD_RECALL, Item)
|
||||||
|
|
||||||
# 全局状态文件路径
|
# 全局状态文件路径
|
||||||
_GLOBAL_STATE_FILE = os.path.expanduser("~/.sm15_global_state.json")
|
_GLOBAL_STATE_FILE = os.path.expanduser(pathlib.Path(config_var.get()['paths']['global_dir']) / 'sm15m_global_state.json')
|
||||||
|
|
||||||
|
|
||||||
def _get_global_sm():
|
def _get_global_sm():
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
基于: https://github.com/kazuaki/sm.js
|
基于: https://github.com/slaypni/sm.js
|
||||||
原始 CoffeeScript 代码: (c) 2014 Kazuaki Tanida
|
原始 CoffeeScript 代码: (c) 2014 Kazuaki Tanida
|
||||||
MIT 许可证
|
MIT 许可证
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,6 @@ class Atom:
|
|||||||
"orbital_fmt": "toml",
|
"orbital_fmt": "toml",
|
||||||
"runtime": {"locked": False, "min_rate": 0x3F3F3F3F, "newact": False},
|
"runtime": {"locked": False, "min_rate": 0x3F3F3F3F, "newact": False},
|
||||||
}
|
}
|
||||||
self.do_eval()
|
|
||||||
logger.debug("Atom 初始化完成")
|
logger.debug("Atom 初始化完成")
|
||||||
|
|
||||||
def link(self, key, value):
|
def link(self, key, value):
|
||||||
@@ -70,7 +69,6 @@ class Atom:
|
|||||||
if key in self.registry.keys():
|
if key in self.registry.keys():
|
||||||
self.registry[key] = value
|
self.registry[key] = value
|
||||||
logger.debug("键 '%s' 已链接, 触发 do_eval", key)
|
logger.debug("键 '%s' 已链接, 触发 do_eval", key)
|
||||||
self.do_eval()
|
|
||||||
if key == "electron":
|
if key == "electron":
|
||||||
if self.registry["electron"].is_activated() == 0:
|
if self.registry["electron"].is_activated() == 0:
|
||||||
self.registry["runtime"]["newact"] = True
|
self.registry["runtime"]["newact"] = True
|
||||||
@@ -78,6 +76,56 @@ class Atom:
|
|||||||
logger.error("尝试链接不受支持的键: '%s'", key)
|
logger.error("尝试链接不受支持的键: '%s'", key)
|
||||||
raise ValueError("不受支持的原子元数据链接操作")
|
raise ValueError("不受支持的原子元数据链接操作")
|
||||||
|
|
||||||
|
def do_eval(self):
|
||||||
|
"""
|
||||||
|
执行并以结果替换当前单元的所有 eval 语句
|
||||||
|
TODO: 带有限制的 eval, 异步/多线程执行避免堵塞
|
||||||
|
"""
|
||||||
|
logger.debug("EVAL 开始")
|
||||||
|
|
||||||
|
# eval 环境设置
|
||||||
|
def eval_with_env(s: str):
|
||||||
|
default = config_var.get()["puzzles"]
|
||||||
|
payload = self.registry['nucleon'].payload
|
||||||
|
metadata = self.registry['nucleon'].metadata
|
||||||
|
eval_value = eval(s)
|
||||||
|
if isinstance(eval_value, (int, float)):
|
||||||
|
ret = str(eval_value)
|
||||||
|
else:
|
||||||
|
ret = eval_value
|
||||||
|
logger.debug(
|
||||||
|
"eval 执行成功: '%s' -> '%s'",
|
||||||
|
s,
|
||||||
|
str(ret)[:50] + "..." if len(ret) > 50 else ret,
|
||||||
|
)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def traverse(data, modifier):
|
||||||
|
if isinstance(data, dict):
|
||||||
|
for key, value in data.items():
|
||||||
|
data[key] = traverse(value, modifier)
|
||||||
|
return data
|
||||||
|
elif isinstance(data, list):
|
||||||
|
for i, item in enumerate(data):
|
||||||
|
data[i] = traverse(item, modifier)
|
||||||
|
return data
|
||||||
|
elif isinstance(data, tuple):
|
||||||
|
return tuple(traverse(item, modifier) for item in data)
|
||||||
|
else:
|
||||||
|
if isinstance(data, str):
|
||||||
|
if data.startswith("eval:"):
|
||||||
|
logger.debug("发现 eval 表达式: '%s'", data[5:])
|
||||||
|
return modifier(data[5:])
|
||||||
|
return data
|
||||||
|
try:
|
||||||
|
traverse(self.registry['nucleon'].payload, eval_with_env)
|
||||||
|
traverse(self.registry['nucleon'].metadata, eval_with_env)
|
||||||
|
traverse(self.registry['orbital'], eval_with_env)
|
||||||
|
except Exception as e:
|
||||||
|
ret = f"此 eval 实例发生错误: {e}"
|
||||||
|
logger.warning(ret)
|
||||||
|
logger.debug("EVAL 完成")
|
||||||
|
|
||||||
def minimize(self, rating):
|
def minimize(self, rating):
|
||||||
"""效果等同于 self.registry['runtime']['min_rate'] = min(rating, self.registry['runtime']['min_rate'])
|
"""效果等同于 self.registry['runtime']['min_rate'] = min(rating, self.registry['runtime']['min_rate'])
|
||||||
|
|
||||||
@@ -114,80 +162,6 @@ class Atom:
|
|||||||
else:
|
else:
|
||||||
logger.debug("禁止总评分")
|
logger.debug("禁止总评分")
|
||||||
|
|
||||||
def do_eval(self):
|
|
||||||
"""
|
|
||||||
执行并以结果替换当前单元的所有 eval 语句
|
|
||||||
TODO: 带有限制的 eval, 异步/多线程执行避免堵塞
|
|
||||||
"""
|
|
||||||
logger.debug("Atom.do_eval 开始")
|
|
||||||
|
|
||||||
# eval 环境设置
|
|
||||||
def eval_with_env(s: str):
|
|
||||||
# 初始化默认值
|
|
||||||
nucleon = self.registry["nucleon"]
|
|
||||||
default = {}
|
|
||||||
metadata = {}
|
|
||||||
try:
|
|
||||||
default = config_var.get()["puzzles"]
|
|
||||||
metadata = nucleon.metadata
|
|
||||||
except Exception:
|
|
||||||
# 如果无法获取配置或元数据, 使用空字典
|
|
||||||
logger.debug("无法获取配置或元数据, 使用空字典")
|
|
||||||
pass
|
|
||||||
try:
|
|
||||||
eval_value = eval(s)
|
|
||||||
if isinstance(eval_value, (list, dict)):
|
|
||||||
ret = eval_value
|
|
||||||
else:
|
|
||||||
ret = str(eval_value)
|
|
||||||
logger.debug(
|
|
||||||
"eval 执行成功: '%s' -> '%s'",
|
|
||||||
s,
|
|
||||||
str(ret)[:50] + "..." if len(ret) > 50 else ret,
|
|
||||||
)
|
|
||||||
except Exception as e:
|
|
||||||
ret = f"此 eval 实例发生错误: {e}"
|
|
||||||
logger.warning("eval 执行错误: '%s' -> %s", s, e)
|
|
||||||
return ret
|
|
||||||
|
|
||||||
def traverse(data, modifier):
|
|
||||||
if isinstance(data, dict):
|
|
||||||
for key, value in data.items():
|
|
||||||
data[key] = traverse(value, modifier)
|
|
||||||
return data
|
|
||||||
elif isinstance(data, list):
|
|
||||||
for i, item in enumerate(data):
|
|
||||||
data[i] = traverse(item, modifier)
|
|
||||||
return data
|
|
||||||
elif isinstance(data, tuple):
|
|
||||||
return tuple(traverse(item, modifier) for item in data)
|
|
||||||
else:
|
|
||||||
if isinstance(data, str):
|
|
||||||
if data.startswith("eval:"):
|
|
||||||
logger.debug("发现 eval 表达式: '%s'", data[5:])
|
|
||||||
return modifier(data[5:])
|
|
||||||
return data
|
|
||||||
|
|
||||||
# 如果 nucleon 存在且有 do_eval 方法, 调用它
|
|
||||||
nucleon = self.registry["nucleon"]
|
|
||||||
if nucleon is not None and hasattr(nucleon, "do_eval"):
|
|
||||||
nucleon.do_eval()
|
|
||||||
logger.debug("已调用 nucleon.do_eval")
|
|
||||||
|
|
||||||
# 如果 electron 存在且其 algodata 包含 eval 字符串, 遍历它
|
|
||||||
electron = self.registry["electron"]
|
|
||||||
if electron is not None and hasattr(electron, "algodata"):
|
|
||||||
traverse(electron.algodata, eval_with_env)
|
|
||||||
logger.debug("已处理 electron algodata eval")
|
|
||||||
|
|
||||||
# 如果 orbital 存在且是字典, 遍历它
|
|
||||||
orbital = self.registry["orbital"]
|
|
||||||
if orbital is not None and isinstance(orbital, dict):
|
|
||||||
traverse(orbital, eval_with_env)
|
|
||||||
logger.debug("orbital eval 完成")
|
|
||||||
|
|
||||||
logger.debug("Atom.do_eval 完成")
|
|
||||||
|
|
||||||
def persist(self, key):
|
def persist(self, key):
|
||||||
logger.debug("Atom.persist: key='%s'", key)
|
logger.debug("Atom.persist: key='%s'", key)
|
||||||
path: pathlib.Path | None = self.registry[key + "_path"]
|
path: pathlib.Path | None = self.registry[key + "_path"]
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ logger = get_logger(__name__)
|
|||||||
class Electron:
|
class Electron:
|
||||||
"""电子: 记忆分析元数据及算法"""
|
"""电子: 记忆分析元数据及算法"""
|
||||||
|
|
||||||
def __init__(self, ident: str, algodata: dict = {}, algo_name: str = "SM-2"):
|
def __init__(self, ident: str, algodata: dict = {}, algo_name: str = ""):
|
||||||
"""初始化电子对象 (记忆数据)
|
"""初始化电子对象 (记忆数据)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -17,19 +17,21 @@ class Electron:
|
|||||||
algodata: 算法数据字典, 包含算法的各项参数和设置
|
algodata: 算法数据字典, 包含算法的各项参数和设置
|
||||||
algo: 使用的算法模块标识
|
algo: 使用的算法模块标识
|
||||||
"""
|
"""
|
||||||
|
if algo_name == "":
|
||||||
|
algo_name = config_var.get()['algorithm']['default']
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"创建 Electron 实例, ident: '%s', algo_name: '%s'", ident, algo_name
|
"创建 Electron 实例, ident: '%s', algo_name: '%s', algodata: %s", ident, algo_name, algodata
|
||||||
)
|
)
|
||||||
self.algodata = algodata
|
self.algodata = algodata
|
||||||
self.ident = ident
|
self.ident = ident
|
||||||
self.algo = algorithms[algo_name]
|
self.algo = algorithms[algo_name]
|
||||||
logger.debug("使用的算法类: %s", self.algo.__name__)
|
logger.debug("使用的算法类: %s", self.algo.__name__)
|
||||||
|
|
||||||
if self.algo not in self.algodata.keys():
|
if self.algo.algo_name not in self.algodata.keys():
|
||||||
self.algodata[self.algo.algo_name] = {}
|
self.algodata[self.algo.algo_name] = {}
|
||||||
logger.debug("算法键 '%s' 不存在, 已创建空字典", self.algo)
|
logger.debug("算法键 '%s' 不存在, 已创建空字典", self.algo)
|
||||||
if not self.algodata[self.algo.algo_name]:
|
if not self.algodata[self.algo.algo_name]:
|
||||||
logger.debug("算法数据为空, 使用默认值初始化")
|
logger.debug(f"算法数据为空, 使用默认值初始化{self.algodata[self.algo.algo_name]}")
|
||||||
self._default_init(self.algo.defaults)
|
self._default_init(self.algo.defaults)
|
||||||
else:
|
else:
|
||||||
logger.debug("算法数据已存在, 跳过默认初始化")
|
logger.debug("算法数据已存在, 跳过默认初始化")
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ def load_electron(path: pathlib.Path, fmt="json") -> dict:
|
|||||||
logger.debug("JSON 解析成功, keys: %s", list(dictdata.keys()))
|
logger.debug("JSON 解析成功, keys: %s", list(dictdata.keys()))
|
||||||
dic = dict()
|
dic = dict()
|
||||||
for item, attr in dictdata.items():
|
for item, attr in dictdata.items():
|
||||||
logger.debug("处理电子项目: %s", item)
|
logger.debug("处理电子项目: %s, %s", item, attr)
|
||||||
dic[item] = Electron(item, attr)
|
dic[item] = Electron(item, attr)
|
||||||
logger.debug("load_electron 完成, 加载了 %d 个 Electron 对象", len(dic))
|
logger.debug("load_electron 完成, 加载了 %d 个 Electron 对象", len(dic))
|
||||||
return dic
|
return dic
|
||||||
|
|||||||
@@ -49,54 +49,6 @@ class Nucleon:
|
|||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
return hash(self.ident)
|
return hash(self.ident)
|
||||||
|
|
||||||
def do_eval(self):
|
|
||||||
"""
|
|
||||||
执行并以结果替换当前单元的所有 eval 语句
|
|
||||||
TODO: 带有限制的 eval, 异步/多线程执行避免堵塞
|
|
||||||
"""
|
|
||||||
logger.debug("Nucleon.do_eval 开始")
|
|
||||||
|
|
||||||
# eval 环境设置
|
|
||||||
def eval_with_env(s: str):
|
|
||||||
try:
|
|
||||||
nucleon = self
|
|
||||||
eval_value = eval(s)
|
|
||||||
if isinstance(eval_value, (int, float)):
|
|
||||||
ret = str(eval_value)
|
|
||||||
else:
|
|
||||||
ret = eval_value
|
|
||||||
logger.debug(
|
|
||||||
"eval 执行成功: '%s' -> '%s'",
|
|
||||||
s,
|
|
||||||
str(ret)[:50] + "..." if len(ret) > 50 else ret,
|
|
||||||
)
|
|
||||||
except Exception as e:
|
|
||||||
ret = f"此 eval 实例发生错误: {e}"
|
|
||||||
logger.warning("eval 执行错误: '%s' -> %s", s, e)
|
|
||||||
return ret
|
|
||||||
|
|
||||||
def traverse(data, modifier):
|
|
||||||
if isinstance(data, dict):
|
|
||||||
for key, value in data.items():
|
|
||||||
data[key] = traverse(value, modifier)
|
|
||||||
return data
|
|
||||||
elif isinstance(data, list):
|
|
||||||
for i, item in enumerate(data):
|
|
||||||
data[i] = traverse(item, modifier)
|
|
||||||
return data
|
|
||||||
elif isinstance(data, tuple):
|
|
||||||
return tuple(traverse(item, modifier) for item in data)
|
|
||||||
else:
|
|
||||||
if isinstance(data, str):
|
|
||||||
if data.startswith("eval:"):
|
|
||||||
logger.debug("发现 eval 表达式: '%s'", data[5:])
|
|
||||||
return modifier(data[5:])
|
|
||||||
return data
|
|
||||||
|
|
||||||
traverse(self.payload, eval_with_env)
|
|
||||||
traverse(self.metadata, eval_with_env)
|
|
||||||
logger.debug("Nucleon.do_eval 完成")
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def placeholder():
|
def placeholder():
|
||||||
"""生成一个占位原子核"""
|
"""生成一个占位原子核"""
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import pathlib
|
|||||||
import edge_tts
|
import edge_tts
|
||||||
|
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
from heurams.context import config_var
|
||||||
|
|
||||||
from .base import BaseTTS
|
from .base import BaseTTS
|
||||||
|
|
||||||
@@ -18,7 +19,7 @@ class EdgeTTS(BaseTTS):
|
|||||||
try:
|
try:
|
||||||
communicate = edge_tts.Communicate(
|
communicate = edge_tts.Communicate(
|
||||||
text,
|
text,
|
||||||
"zh-CN-YunjianNeural",
|
config_var.get()['providers']['tts']['edgetts']["voice"],
|
||||||
)
|
)
|
||||||
logger.debug("EdgeTTS 通信对象创建成功, 正在保存音频")
|
logger.debug("EdgeTTS 通信对象创建成功, 正在保存音频")
|
||||||
communicate.save_sync(str(path))
|
communicate.save_sync(str(path))
|
||||||
|
|||||||
@@ -9,5 +9,5 @@ logger = get_logger(__name__)
|
|||||||
|
|
||||||
play_by_path: Callable = prov[config_var.get()["services"]["audio"]].play_by_path
|
play_by_path: Callable = prov[config_var.get()["services"]["audio"]].play_by_path
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"音频服务初始化完成, 使用 provider: %s", config_var.get()["services"]["audio"]
|
"音频服务初始化完成, 使用 Provider: %s", config_var.get()["services"]["audio"]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,12 +2,12 @@
|
|||||||
from typing import Callable
|
from typing import Callable
|
||||||
|
|
||||||
from heurams.context import config_var
|
from heurams.context import config_var
|
||||||
from heurams.providers.tts import TTSs
|
from heurams.providers.tts import providers as prov
|
||||||
from heurams.services.logger import get_logger
|
from heurams.services.logger import get_logger
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
convert: Callable = TTSs[config_var.get().get("tts_provider")]
|
convertor: Callable = prov[config_var.get()["services"]["tts"]].convert
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"TTS服务初始化完成, 使用 provider: %s", config_var.get().get("tts_provider")
|
"TTS服务初始化完成, 使用 provider: %s", config_var.get()["services"]["tts"]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from heurams.services.logger import get_logger
|
|||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
ver = "0.4.1"
|
ver = "0.4.2"
|
||||||
stage = "prototype"
|
stage = "prototype"
|
||||||
codename = "fledge" # 雏鸟, 0.4.x 版本
|
codename = "fledge" # 雏鸟, 0.4.x 版本
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user