更新 providers
This commit is contained in:
@@ -4,6 +4,7 @@ __all__ = [
|
||||
'SM2Algorithm',
|
||||
]
|
||||
|
||||
registry = {
|
||||
algorithms = {
|
||||
"SM-2": SM2Algorithm,
|
||||
"supermemo2": SM2Algorithm,
|
||||
}
|
||||
33
src/heurams/kernel/algorithms/base.py
Normal file
33
src/heurams/kernel/algorithms/base.py
Normal file
@@ -0,0 +1,33 @@
|
||||
import heurams.services.timer as timer
|
||||
from typing import TypedDict
|
||||
|
||||
class BaseAlgorithm:
|
||||
algo_name = "BaseAlgorithm"
|
||||
|
||||
class AlgodataDict(TypedDict):
|
||||
efactor: float
|
||||
real_rept: int
|
||||
rept: int
|
||||
interval: int
|
||||
last_date: int
|
||||
next_date: int
|
||||
is_activated: int
|
||||
last_modify: float
|
||||
|
||||
defaults = {
|
||||
'real_rept': 0,
|
||||
'rept': 0,
|
||||
'interval': 0,
|
||||
'last_date': 0,
|
||||
'next_date': 0,
|
||||
'is_activated': 0,
|
||||
'last_modify': timer.get_timestamp()
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def revisor(cls, algodata: dict, feedback: int = 5, is_new_activation: bool = False):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def is_due(cls, algodata):
|
||||
return 1
|
||||
@@ -1,7 +1,8 @@
|
||||
from .base import BaseAlgorithm
|
||||
import heurams.services.timer as timer
|
||||
from typing import TypedDict
|
||||
|
||||
class SM2Algorithm:
|
||||
class SM2Algorithm(BaseAlgorithm):
|
||||
algo_name = "SM-2"
|
||||
|
||||
class AlgodataDict(TypedDict):
|
||||
|
||||
@@ -2,4 +2,13 @@ from .electron import Electron
|
||||
from .nucleon import Nucleon
|
||||
from .orbital import Orbital
|
||||
from .atom import Atom
|
||||
from .probe import probe_all, probe_by_filename
|
||||
from .probe import probe_all, probe_by_filename
|
||||
|
||||
__all__ = [
|
||||
"Electron",
|
||||
"Nucleon",
|
||||
"Orbital",
|
||||
"Atom",
|
||||
"probe_all",
|
||||
"probe_by_filename",
|
||||
]
|
||||
@@ -1,11 +1,11 @@
|
||||
import heurams.services.timer as timer
|
||||
from heurams.context import config_var
|
||||
from heurams.kernel.algorithms import get_algorithm
|
||||
from heurams.kernel.algorithms import algorithms
|
||||
|
||||
class Electron:
|
||||
"""电子: 记忆分析元数据及算法"""
|
||||
|
||||
def __init__(self, ident: str, algodata: dict = {}, algo: str = "supermemo2"):
|
||||
def __init__(self, ident: str, algodata: dict = {}, algo_name: str = "supermemo2"):
|
||||
"""初始化电子对象 (记忆数据)
|
||||
|
||||
Args:
|
||||
@@ -15,14 +15,12 @@ class Electron:
|
||||
"""
|
||||
self.algodata = algodata
|
||||
self.ident = ident
|
||||
self.algo = algo
|
||||
|
||||
algorithm_config = get_algorithm(self.algo)
|
||||
self.algo = algorithms[algo_name]
|
||||
|
||||
if self.algo not in self.algodata.keys():
|
||||
self.algodata[self.algo] = {}
|
||||
if not self.algodata[self.algo]:
|
||||
self._default_init(algorithm_config['defaults'])
|
||||
self._default_init(self.algo.defaults)
|
||||
|
||||
def _default_init(self, defaults: dict):
|
||||
"""默认初始化包装"""
|
||||
@@ -41,6 +39,11 @@ class Electron:
|
||||
else:
|
||||
print(f"警告: '{var}' 非已知元数据字段")
|
||||
|
||||
def is_due(self):
|
||||
"""是否应该复习
|
||||
"""
|
||||
return self.algo.is_due(self.algodata)
|
||||
|
||||
def revisor(self, quality: int = 5, is_new_activation: bool = False):
|
||||
"""算法迭代决策机制实现
|
||||
|
||||
@@ -48,8 +51,7 @@ class Electron:
|
||||
quality (int): 记忆保留率量化参数 (0-5)
|
||||
is_new_activation (bool): 是否为初次激活
|
||||
"""
|
||||
algorithm_config = get_algorithm(self.algo)
|
||||
algorithm_config['revisor'](self.algodata, quality, is_new_activation)
|
||||
self.algo.revisor(self.algodata, quality, is_new_activation)
|
||||
|
||||
def __str__(self):
|
||||
return (
|
||||
|
||||
@@ -2,7 +2,7 @@ from heurams.context import config_var
|
||||
import pathlib
|
||||
|
||||
def probe_by_filename(filename):
|
||||
"""探测指定文件的所有信息"""
|
||||
"""探测指定文件 (无扩展名) 的所有信息"""
|
||||
paths: dict = config_var.get().get("paths")
|
||||
formats = ["toml", "json"]
|
||||
result = {}
|
||||
|
||||
@@ -7,13 +7,46 @@ Puzzle 模块 - 谜题生成系统
|
||||
from .base import BasePuzzle
|
||||
from .cloze import ClozePuzzle
|
||||
from .mcq import MCQPuzzle
|
||||
from .factory import PuzzleFactory
|
||||
from .loader import PuzzleLoader
|
||||
|
||||
__all__ = [
|
||||
'BasePuzzle',
|
||||
'ClozePuzzle',
|
||||
'MCQPuzzle',
|
||||
'PuzzleFactory',
|
||||
'PuzzleLoader',
|
||||
]
|
||||
]
|
||||
|
||||
puzzles = {
|
||||
"mcq": MCQPuzzle,
|
||||
"cloze": ClozePuzzle,
|
||||
"base": BasePuzzle,
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def create_by_dict(config_dict: dict) -> BasePuzzle:
|
||||
"""
|
||||
根据配置字典创建谜题
|
||||
|
||||
Args:
|
||||
config_dict: 配置字典,包含谜题类型和参数
|
||||
|
||||
Returns:
|
||||
BasePuzzle: 谜题实例
|
||||
|
||||
Raises:
|
||||
ValueError: 当配置无效时抛出
|
||||
"""
|
||||
puzzle_type = config_dict.get('type')
|
||||
|
||||
if puzzle_type == 'cloze':
|
||||
return puzzles["cloze"](
|
||||
text=config_dict['text'],
|
||||
min_denominator=config_dict.get('min_denominator', 7)
|
||||
)
|
||||
elif puzzle_type == 'mcq':
|
||||
return puzzles["mcq"](
|
||||
mapping=config_dict['mapping'],
|
||||
jammer=config_dict.get('jammer', []),
|
||||
max_riddles_num=config_dict.get('max_riddles_num', 2),
|
||||
prefix=config_dict.get('prefix', '')
|
||||
)
|
||||
else:
|
||||
raise ValueError(f"未知的谜题类型: {puzzle_type}")
|
||||
@@ -1,77 +0,0 @@
|
||||
"""
|
||||
谜题工厂类 - 统一创建各种类型的谜题
|
||||
"""
|
||||
|
||||
from typing import Dict, List, Union, Any
|
||||
from .base import BasePuzzle
|
||||
from .cloze import ClozePuzzle
|
||||
from .mcq import MCQPuzzle
|
||||
|
||||
class PuzzleFactory:
|
||||
"""谜题工厂,根据配置创建不同类型的谜题"""
|
||||
|
||||
@staticmethod
|
||||
def create_cloze(text: str, min_denominator: int = 7) -> ClozePuzzle:
|
||||
"""
|
||||
创建填空题谜题
|
||||
|
||||
Args:
|
||||
text: 原始字符串,使用 "/" 分割句子
|
||||
min_denominator: 最小概率倒数,控制填空数量
|
||||
|
||||
Returns:
|
||||
ClozePuzzle: 填空题实例
|
||||
"""
|
||||
return ClozePuzzle(text, min_denominator)
|
||||
|
||||
@staticmethod
|
||||
def create_mcq(
|
||||
mapping: Dict[str, str],
|
||||
jammer: List[str],
|
||||
max_riddles_num: int = 2,
|
||||
prefix: str = ""
|
||||
) -> MCQPuzzle:
|
||||
"""
|
||||
创建选择题谜题
|
||||
|
||||
Args:
|
||||
mapping: 题目-答案映射字典
|
||||
jammer: 干扰项列表
|
||||
max_riddles_num: 最大题目数量
|
||||
prefix: 题目前缀
|
||||
|
||||
Returns:
|
||||
MCQPuzzle: 选择题实例
|
||||
"""
|
||||
return MCQPuzzle(mapping, jammer, max_riddles_num, prefix)
|
||||
|
||||
@staticmethod
|
||||
def from_config(config: Dict[str, Any]) -> BasePuzzle:
|
||||
"""
|
||||
根据配置字典创建谜题
|
||||
|
||||
Args:
|
||||
config: 配置字典,包含谜题类型和参数
|
||||
|
||||
Returns:
|
||||
BasePuzzle: 谜题实例
|
||||
|
||||
Raises:
|
||||
ValueError: 当配置无效时抛出
|
||||
"""
|
||||
puzzle_type = config.get('type')
|
||||
|
||||
if puzzle_type == 'cloze':
|
||||
return PuzzleFactory.create_cloze(
|
||||
text=config['text'],
|
||||
min_denominator=config.get('min_denominator', 7)
|
||||
)
|
||||
elif puzzle_type == 'mcq':
|
||||
return PuzzleFactory.create_mcq(
|
||||
mapping=config['mapping'],
|
||||
jammer=config.get('jammer', []),
|
||||
max_riddles_num=config.get('max_riddles_num', 2),
|
||||
prefix=config.get('prefix', '')
|
||||
)
|
||||
else:
|
||||
raise ValueError(f"未知的谜题类型: {puzzle_type}")
|
||||
@@ -1,154 +0,0 @@
|
||||
"""
|
||||
谜题加载器 - 从多种数据源加载谜题配置
|
||||
"""
|
||||
|
||||
import json
|
||||
import yaml
|
||||
import re
|
||||
from typing import Dict, List, Union, Any
|
||||
from pathlib import Path
|
||||
from .factory import PuzzleFactory
|
||||
|
||||
|
||||
class PuzzleLoader:
|
||||
"""谜题加载器,支持从多种格式加载谜题配置"""
|
||||
|
||||
@staticmethod
|
||||
def from_json(file_path: Union[str, Path]) -> Dict[str, Any]:
|
||||
"""
|
||||
从JSON文件加载谜题配置
|
||||
|
||||
Args:
|
||||
file_path: JSON文件路径
|
||||
|
||||
Returns:
|
||||
Dict: 谜题配置字典
|
||||
"""
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
|
||||
@staticmethod
|
||||
def from_yaml(file_path: Union[str, Path]) -> Dict[str, Any]:
|
||||
"""
|
||||
从YAML文件加载谜题配置
|
||||
|
||||
Args:
|
||||
file_path: YAML文件路径
|
||||
|
||||
Returns:
|
||||
Dict: 谜题配置字典
|
||||
"""
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
return yaml.safe_load(f)
|
||||
|
||||
@staticmethod
|
||||
def from_text(text: str, puzzle_type: str = 'cloze', **kwargs) -> Dict[str, Any]:
|
||||
"""
|
||||
从纯文本字符串创建谜题配置
|
||||
|
||||
Args:
|
||||
text: 原始文本
|
||||
puzzle_type: 谜题类型 ('cloze' 或 'mcq')
|
||||
**kwargs: 其他参数
|
||||
|
||||
Returns:
|
||||
Dict: 谜题配置字典
|
||||
"""
|
||||
config = {'type': puzzle_type}
|
||||
|
||||
if puzzle_type == 'cloze':
|
||||
config['text'] = text
|
||||
config.update(kwargs)
|
||||
elif puzzle_type == 'mcq':
|
||||
# 从文本解析选择题配置
|
||||
config.update(PuzzleLoader._parse_mcq_from_text(text, **kwargs))
|
||||
else:
|
||||
raise ValueError(f"不支持的谜题类型: {puzzle_type}")
|
||||
|
||||
return config
|
||||
|
||||
@staticmethod
|
||||
def _parse_mcq_from_text(text: str, **kwargs) -> Dict[str, Any]:
|
||||
"""
|
||||
从文本解析选择题配置
|
||||
|
||||
Args:
|
||||
text: 原始文本
|
||||
**kwargs: 其他参数
|
||||
|
||||
Returns:
|
||||
Dict: 选择题配置
|
||||
"""
|
||||
# 简单的解析逻辑:每行一个题目,格式为 "题目:答案"
|
||||
mapping = {}
|
||||
lines = text.strip().split('\n')
|
||||
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if ':' in line:
|
||||
question, answer = line.split(':', 1)
|
||||
mapping[question.strip()] = answer.strip()
|
||||
|
||||
return {
|
||||
'mapping': mapping,
|
||||
'jammer': kwargs.get('jammer', []),
|
||||
'max_riddles_num': kwargs.get('max_riddles_num', 2),
|
||||
'prefix': kwargs.get('prefix', '')
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def from_csv(file_path: Union[str, Path], **kwargs) -> Dict[str, Any]:
|
||||
"""
|
||||
从CSV文件加载选择题配置
|
||||
|
||||
Args:
|
||||
file_path: CSV文件路径
|
||||
**kwargs: 其他参数
|
||||
|
||||
Returns:
|
||||
Dict: 选择题配置
|
||||
"""
|
||||
import csv
|
||||
mapping = {}
|
||||
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
reader = csv.reader(f)
|
||||
for row in reader:
|
||||
if len(row) >= 2:
|
||||
mapping[row[0].strip()] = row[1].strip()
|
||||
|
||||
return {
|
||||
'type': 'mcq',
|
||||
'mapping': mapping,
|
||||
'jammer': kwargs.get('jammer', []),
|
||||
'max_riddles_num': kwargs.get('max_riddles_num', 2),
|
||||
'prefix': kwargs.get('prefix', '')
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def load_puzzles_from_directory(directory: Union[str, Path]) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
从目录批量加载谜题配置
|
||||
|
||||
Args:
|
||||
directory: 目录路径
|
||||
|
||||
Returns:
|
||||
List[Dict]: 谜题配置列表
|
||||
"""
|
||||
puzzles = []
|
||||
dir_path = Path(directory)
|
||||
|
||||
# 加载JSON文件
|
||||
for json_file in dir_path.glob('*.json'):
|
||||
puzzles.append(PuzzleLoader.from_json(json_file))
|
||||
|
||||
# 加载YAML文件
|
||||
for yaml_file in dir_path.glob('*.yaml'):
|
||||
puzzles.append(PuzzleLoader.from_yaml(yaml_file))
|
||||
|
||||
# 加载CSV文件
|
||||
for csv_file in dir_path.glob('*.csv'):
|
||||
puzzles.append(PuzzleLoader.from_csv(csv_file))
|
||||
|
||||
return puzzles
|
||||
@@ -0,0 +1,10 @@
|
||||
# 音频播放器, 必须基于文件操作
|
||||
from .termux_audio import player as termux_player
|
||||
|
||||
__all__ = [
|
||||
"termux_player"
|
||||
]
|
||||
|
||||
players = {
|
||||
"termux": termux_player
|
||||
}
|
||||
4
src/heurams/providers/audio/system_audio.py
Normal file
4
src/heurams/providers/audio/system_audio.py
Normal file
@@ -0,0 +1,4 @@
|
||||
""" 通用音频适配器
|
||||
基于 playsound 库的音频播放器, 在绝大多数 python 环境上提供音频服务
|
||||
注意: 在未配置 pulseaudio 的 termux 不可用
|
||||
"""
|
||||
@@ -0,0 +1,10 @@
|
||||
""" Termux 音频适配
|
||||
适配 Termux 的 play-audio 命令, 以在 android 上提供可用的播放体验
|
||||
无需配置 pulseaudio
|
||||
"""
|
||||
|
||||
import os
|
||||
import pathlib
|
||||
|
||||
def player(path: pathlib.Path):
|
||||
os.system(f"play-audio {path}")
|
||||
@@ -0,0 +1 @@
|
||||
# 大语言模型
|
||||
@@ -0,0 +1,12 @@
|
||||
from .base import BaseTTS
|
||||
from .edge_tts import EdgeTTS
|
||||
|
||||
__all__ = [
|
||||
"BaseTTS",
|
||||
"EdgeTTS",
|
||||
]
|
||||
|
||||
TTSs = {
|
||||
"BaseTTS": BaseTTS,
|
||||
"EdgeTTS": EdgeTTS,
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import pathlib
|
||||
|
||||
class BaseTTS:
|
||||
name = "BaseTTS"
|
||||
|
||||
@classmethod
|
||||
def convert(cls, text: str, path: pathlib.Path | str = "") -> pathlib.Path:
|
||||
"""path 是可选参数, 不填则自动返回生成文件路径"""
|
||||
return path # type: ignore
|
||||
@@ -0,0 +1,15 @@
|
||||
from .base import BaseTTS
|
||||
import pathlib
|
||||
import edge_tts
|
||||
|
||||
class EdgeTTS(BaseTTS):
|
||||
name = "EdgeTTS"
|
||||
|
||||
@classmethod
|
||||
def convert(cls, text, path: pathlib.Path | str = "") -> pathlib.Path:
|
||||
communicate = edge_tts.Communicate(
|
||||
text,
|
||||
"zh-CN-YunjianNeural",
|
||||
)
|
||||
communicate.save_sync(str(path))
|
||||
return path # type: ignore
|
||||
@@ -1 +1,3 @@
|
||||
# 版本控制集成服务
|
||||
# 版本控制集成服务
|
||||
|
||||
ver = "0.4.0"
|
||||
Reference in New Issue
Block a user