更新 providers

This commit is contained in:
2025-11-02 03:15:18 +08:00
parent f689e08a1d
commit 723046a876
20 changed files with 160 additions and 249 deletions

View File

@@ -4,6 +4,7 @@ __all__ = [
'SM2Algorithm', 'SM2Algorithm',
] ]
registry = { algorithms = {
"SM-2": SM2Algorithm, "SM-2": SM2Algorithm,
"supermemo2": SM2Algorithm,
} }

View 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

View File

@@ -1,7 +1,8 @@
from .base import BaseAlgorithm
import heurams.services.timer as timer import heurams.services.timer as timer
from typing import TypedDict from typing import TypedDict
class SM2Algorithm: class SM2Algorithm(BaseAlgorithm):
algo_name = "SM-2" algo_name = "SM-2"
class AlgodataDict(TypedDict): class AlgodataDict(TypedDict):

View File

@@ -2,4 +2,13 @@ from .electron import Electron
from .nucleon import Nucleon from .nucleon import Nucleon
from .orbital import Orbital from .orbital import Orbital
from .atom import Atom 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",
]

View File

@@ -1,11 +1,11 @@
import heurams.services.timer as timer import heurams.services.timer as timer
from heurams.context import config_var from heurams.context import config_var
from heurams.kernel.algorithms import get_algorithm from heurams.kernel.algorithms import algorithms
class Electron: class Electron:
"""电子: 记忆分析元数据及算法""" """电子: 记忆分析元数据及算法"""
def __init__(self, ident: str, algodata: dict = {}, algo: str = "supermemo2"): def __init__(self, ident: str, algodata: dict = {}, algo_name: str = "supermemo2"):
"""初始化电子对象 (记忆数据) """初始化电子对象 (记忆数据)
Args: Args:
@@ -15,14 +15,12 @@ class Electron:
""" """
self.algodata = algodata self.algodata = algodata
self.ident = ident self.ident = ident
self.algo = algo self.algo = algorithms[algo_name]
algorithm_config = get_algorithm(self.algo)
if self.algo not in self.algodata.keys(): if self.algo not in self.algodata.keys():
self.algodata[self.algo] = {} self.algodata[self.algo] = {}
if not 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): def _default_init(self, defaults: dict):
"""默认初始化包装""" """默认初始化包装"""
@@ -41,6 +39,11 @@ class Electron:
else: else:
print(f"警告: '{var}' 非已知元数据字段") 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): def revisor(self, quality: int = 5, is_new_activation: bool = False):
"""算法迭代决策机制实现 """算法迭代决策机制实现
@@ -48,8 +51,7 @@ class Electron:
quality (int): 记忆保留率量化参数 (0-5) quality (int): 记忆保留率量化参数 (0-5)
is_new_activation (bool): 是否为初次激活 is_new_activation (bool): 是否为初次激活
""" """
algorithm_config = get_algorithm(self.algo) self.algo.revisor(self.algodata, quality, is_new_activation)
algorithm_config['revisor'](self.algodata, quality, is_new_activation)
def __str__(self): def __str__(self):
return ( return (

View File

@@ -2,7 +2,7 @@ from heurams.context import config_var
import pathlib import pathlib
def probe_by_filename(filename): def probe_by_filename(filename):
"""探测指定文件的所有信息""" """探测指定文件 (无扩展名) 的所有信息"""
paths: dict = config_var.get().get("paths") paths: dict = config_var.get().get("paths")
formats = ["toml", "json"] formats = ["toml", "json"]
result = {} result = {}

View File

@@ -7,13 +7,46 @@ Puzzle 模块 - 谜题生成系统
from .base import BasePuzzle from .base import BasePuzzle
from .cloze import ClozePuzzle from .cloze import ClozePuzzle
from .mcq import MCQPuzzle from .mcq import MCQPuzzle
from .factory import PuzzleFactory
from .loader import PuzzleLoader
__all__ = [ __all__ = [
'BasePuzzle', 'BasePuzzle',
'ClozePuzzle', 'ClozePuzzle',
'MCQPuzzle', '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}")

View File

@@ -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}")

View File

@@ -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

View File

@@ -0,0 +1,10 @@
# 音频播放器, 必须基于文件操作
from .termux_audio import player as termux_player
__all__ = [
"termux_player"
]
players = {
"termux": termux_player
}

View File

@@ -0,0 +1,4 @@
""" 通用音频适配器
基于 playsound 库的音频播放器, 在绝大多数 python 环境上提供音频服务
注意: 在未配置 pulseaudio 的 termux 不可用
"""

View File

@@ -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}")

View File

@@ -0,0 +1 @@
# 大语言模型

View File

@@ -0,0 +1,12 @@
from .base import BaseTTS
from .edge_tts import EdgeTTS
__all__ = [
"BaseTTS",
"EdgeTTS",
]
TTSs = {
"BaseTTS": BaseTTS,
"EdgeTTS": EdgeTTS,
}

View File

@@ -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

View File

@@ -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

View File

@@ -1 +1,3 @@
# 版本控制集成服务 # 版本控制集成服务
ver = "0.4.0"