更新 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',
]
registry = {
algorithms = {
"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
from typing import TypedDict
class SM2Algorithm:
class SM2Algorithm(BaseAlgorithm):
algo_name = "SM-2"
class AlgodataDict(TypedDict):

View File

@@ -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",
]

View File

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

View File

@@ -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 = {}

View File

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

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"