docs: 修改文档
This commit is contained in:
@@ -24,7 +24,7 @@ logger.debug(f"工作目录: {workdir}")
|
||||
|
||||
(workdir / "data" / "config").mkdir(parents=True, exist_ok=True)
|
||||
|
||||
config_var: ContextVar[ConfigDict].get = ContextVar(
|
||||
config_var: ContextVar[ConfigDict] = ContextVar(
|
||||
"config_var",
|
||||
default=ConfigDict(workdir / "data" / "config"),
|
||||
)
|
||||
|
||||
@@ -85,7 +85,7 @@ class FavoriteManagerScreen(Screen):
|
||||
|
||||
def _encode_favorite_key(self, repo_path: str, ident: str) -> str:
|
||||
"""编码仓库路径和标识符为安全的按钮 ID 部分"""
|
||||
# 使用 \x00 分隔两部分,然后进行 base64 编码
|
||||
# 使用 \x00 分隔两部分, 然后进行 base64 编码
|
||||
combined = f"{repo_path}\x00{ident}"
|
||||
encoded = base64.urlsafe_b64encode(combined.encode()).decode()
|
||||
# 去掉填充的等号
|
||||
@@ -114,7 +114,7 @@ class FavoriteManagerScreen(Screen):
|
||||
|
||||
# 创建安全的按钮 ID
|
||||
button_key = self._encode_favorite_key(fav.repo_path, fav.ident)
|
||||
# 创建列表项,包含移除按钮
|
||||
# 创建列表项, 包含移除按钮
|
||||
container = Horizontal(
|
||||
Label(display_text, classes="favorite-content"),
|
||||
Button("移除", id=f"remove-{button_key}", variant="error", flat=True, classes="favorite-item-btn"),
|
||||
|
||||
@@ -237,14 +237,14 @@ class MemScreen(Screen):
|
||||
"""获取仓库相对路径(相对于 data/repo)"""
|
||||
if self.repo is None:
|
||||
return ""
|
||||
# self.repo.source 是 Path 对象,指向仓库目录
|
||||
# self.repo.source 是 Path 对象, 指向仓库目录
|
||||
repo_full_path = self.repo.source
|
||||
data_repo_path = Path(config_var.get()["global"]["paths"]["data"]) / "repo"
|
||||
try:
|
||||
rel_path = repo_full_path.relative_to(data_repo_path)
|
||||
return str(rel_path)
|
||||
except ValueError:
|
||||
# 如果不在 data/repo 下,则返回完整路径(字符串形式)
|
||||
# 如果不在 data/repo 下, 则返回完整路径(字符串形式)
|
||||
return str(repo_full_path)
|
||||
|
||||
def _is_current_atom_favorited(self) -> bool:
|
||||
|
||||
@@ -14,7 +14,7 @@ import heurams.kernel.particles as pt
|
||||
import heurams.services.hasher as hasher
|
||||
from heurams.context import *
|
||||
|
||||
# 兼容性缓存路径:优先使用 paths.cache,否则使用 data/cache
|
||||
# 兼容性缓存路径:优先使用 paths.cache, 否则使用 data/cache
|
||||
paths = config_var.get()["global"]["paths"]
|
||||
cache_dir = pathlib.Path(paths.get("cache", paths["data"] + "/cache")) / "voice"
|
||||
|
||||
@@ -55,7 +55,7 @@ class PrecachingScreen(Screen):
|
||||
self.precache_worker = None
|
||||
self.cancel_flag = 0
|
||||
self.desc = desc
|
||||
# 不再需要缓存配置,保留配置读取以兼容
|
||||
# 不再需要缓存配置, 保留配置读取以兼容
|
||||
self.cache_stats = {
|
||||
"total_size": 0,
|
||||
"file_count": 0,
|
||||
|
||||
@@ -130,7 +130,7 @@ class SyncScreen(Screen):
|
||||
log_widget = self.query_one("#log_output")
|
||||
log_widget.update("\n".join(self.log_messages)) # type: ignore
|
||||
except Exception:
|
||||
pass # 如果组件未就绪,忽略错误
|
||||
pass # 如果组件未就绪, 忽略错误
|
||||
|
||||
def on_button_pressed(self, event: Button.Pressed) -> None:
|
||||
"""处理按钮点击事件"""
|
||||
@@ -150,7 +150,7 @@ class SyncScreen(Screen):
|
||||
def test_connection(self):
|
||||
"""测试 WebDAV 服务器连接"""
|
||||
if not self.sync_service:
|
||||
self.log_message("同步服务未初始化,请检查配置", is_error=True)
|
||||
self.log_message("同步服务未初始化, 请检查配置", is_error=True)
|
||||
self.update_status("❌ 同步服务未初始化")
|
||||
return
|
||||
|
||||
@@ -172,7 +172,7 @@ class SyncScreen(Screen):
|
||||
def start_sync(self):
|
||||
"""开始同步"""
|
||||
if not self.sync_service:
|
||||
self.log_message("同步服务未初始化,无法开始同步", is_error=True)
|
||||
self.log_message("同步服务未初始化, 无法开始同步", is_error=True)
|
||||
return
|
||||
|
||||
if self.is_syncing:
|
||||
|
||||
@@ -26,13 +26,13 @@ _SCHEDULER_STATE_FILE = pathlib.Path(
|
||||
|
||||
|
||||
def _get_global_scheduler():
|
||||
"""获取全局 FSRS Scheduler 实例,从文件加载或创建新的"""
|
||||
"""获取全局 FSRS Scheduler 实例, 从文件加载或创建新的"""
|
||||
if os.path.exists(_SCHEDULER_STATE_FILE):
|
||||
try:
|
||||
with open(_SCHEDULER_STATE_FILE, "r", encoding="utf-8") as f:
|
||||
return Scheduler.from_json(f.read())
|
||||
except Exception:
|
||||
logger.warning("FSRS Scheduler 状态文件加载失败,创建新实例")
|
||||
logger.warning("FSRS Scheduler 状态文件加载失败, 创建新实例")
|
||||
return Scheduler()
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ class FSRSAlgorithm(BaseAlgorithm):
|
||||
# FSRS 特有字段
|
||||
fsrs_state: int # State 枚举值: 1=Learning, 2=Review, 3=Relearning
|
||||
fsrs_step: int # 当前学习步进索引, -1 表示 None (Review 状态)
|
||||
fsrs_stability: float # 稳定性(秒),0.0 表示尚未计算
|
||||
fsrs_stability: float # 稳定性(秒), 0.0 表示尚未计算
|
||||
fsrs_difficulty: float # 难度 [1.0, 10.0], 0.0 表示尚未计算
|
||||
# 标准 BaseAlgorithm 兼容字段
|
||||
real_rept: int
|
||||
@@ -194,7 +194,7 @@ class FSRSAlgorithm(BaseAlgorithm):
|
||||
|
||||
if is_new_activation:
|
||||
card = Card()
|
||||
logger.debug("新激活,创建新 Card")
|
||||
logger.debug("新激活, 创建新 Card")
|
||||
else:
|
||||
card = cls._algodata_to_card(algodata)
|
||||
|
||||
|
||||
@@ -611,7 +611,7 @@ def _get_global_sm():
|
||||
with open(_GLOBAL_STATE_FILE, "r", encoding="utf-8") as f:
|
||||
return SM.load(json.load(f))
|
||||
except Exception:
|
||||
logger.warning("SM-15M 全局状态文件加载失败,创建新实例")
|
||||
logger.warning("SM-15M 全局状态文件加载失败, 创建新实例")
|
||||
sm = SM()
|
||||
_save_global_sm(sm)
|
||||
return sm
|
||||
@@ -646,7 +646,7 @@ class SM15MAlgorithm(BaseAlgorithm):
|
||||
# 毫秒精度(子日排程)
|
||||
last_date_ms: int
|
||||
next_date_ms: int
|
||||
# BaseAlgorithm 兼容(天精度,向后兼容)
|
||||
# BaseAlgorithm 兼容(天精度, 向后兼容)
|
||||
real_rept: int
|
||||
rept: int
|
||||
interval: int
|
||||
@@ -694,7 +694,7 @@ class SM15MAlgorithm(BaseAlgorithm):
|
||||
opt_days * 24 * 60 * 60 * 1000 if opt_days > 0 else sm.interval_base
|
||||
)
|
||||
|
||||
# 毫秒精度优先,退化至天精度
|
||||
# 毫秒精度优先, 退化至天精度
|
||||
last_date_ms = data.get("last_date_ms", 0)
|
||||
if last_date_ms:
|
||||
item.previous_date = datetime.datetime(1970, 1, 1) + datetime.timedelta(
|
||||
|
||||
@@ -26,14 +26,14 @@ class BaseLLM:
|
||||
"""发送聊天消息并获取响应
|
||||
|
||||
Args:
|
||||
messages: 消息列表,每个消息为 {"role": "user"|"assistant"|"system", "content": "消息内容"}
|
||||
**kwargs: 其他参数,如 temperature, max_tokens 等
|
||||
messages: 消息列表, 每个消息为 {"role": "user"|"assistant"|"system", "content": "消息内容"}
|
||||
**kwargs: 其他参数, 如 temperature, max_tokens 等
|
||||
|
||||
Returns:
|
||||
模型返回的文本响应
|
||||
"""
|
||||
logger.debug("BaseLLM.chat: messages=%d, kwargs=%s", len(messages), kwargs)
|
||||
logger.warning("BaseLLM.chat 是基类方法,未实现具体功能")
|
||||
logger.warning("BaseLLM.chat 是基类方法, 未实现具体功能")
|
||||
await asyncio.sleep(0) # 避免未使用异步的警告
|
||||
return "BaseLLM 未实现具体功能"
|
||||
|
||||
@@ -50,6 +50,6 @@ class BaseLLM:
|
||||
logger.debug(
|
||||
"BaseLLM.chat_stream: messages=%d, kwargs=%s", len(messages), kwargs
|
||||
)
|
||||
logger.warning("BaseLLM.chat_stream 是基类方法,未实现具体功能")
|
||||
logger.warning("BaseLLM.chat_stream 是基类方法, 未实现具体功能")
|
||||
await asyncio.sleep(0)
|
||||
yield "BaseLLM 未实现流式功能"
|
||||
|
||||
@@ -27,8 +27,8 @@ class OpenAILLM(BaseLLM):
|
||||
try:
|
||||
from openai import AsyncOpenAI
|
||||
except ImportError:
|
||||
logger.error("未安装 openai 库,请运行: pip install openai")
|
||||
raise ImportError("未安装 openai 库,请运行: pip install openai")
|
||||
logger.error("未安装 openai 库, 请运行: pip install openai")
|
||||
raise ImportError("未安装 openai 库, 请运行: pip install openai")
|
||||
|
||||
self._client = AsyncOpenAI(
|
||||
api_key=self.api_key if self.api_key else None,
|
||||
@@ -49,7 +49,7 @@ class OpenAILLM(BaseLLM):
|
||||
"max_tokens": kwargs.get("max_tokens", 1000),
|
||||
}
|
||||
|
||||
# 合并参数,优先使用传入的 kwargs
|
||||
# 合并参数, 优先使用传入的 kwargs
|
||||
request_kwargs = {**default_kwargs, **kwargs}
|
||||
request_kwargs["messages"] = messages
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ from heurams.services.exceptions import WTFException
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class ConfigDict(UserDict): # 舒服了
|
||||
class ConfigDict(UserDict):
|
||||
_instances = {} # 必须使用单例模式, 不然有严重的多实例导致的配置无法持久化问题
|
||||
|
||||
def __new__(cls, config_path: pathlib.Path, dict=None):
|
||||
|
||||
@@ -73,7 +73,7 @@ class FavoriteManager:
|
||||
with open(self._file_path, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
self._favorites = [FavoriteItem.from_dict(item) for item in data]
|
||||
logger.debug("收藏列表加载成功,共 %d 项", len(self._favorites))
|
||||
logger.debug("收藏列表加载成功, 共 %d 项", len(self._favorites))
|
||||
except Exception as e:
|
||||
logger.error("加载收藏列表失败: %s", e)
|
||||
self._favorites = []
|
||||
@@ -86,7 +86,7 @@ class FavoriteManager:
|
||||
data = [item.to_dict() for item in self._favorites]
|
||||
with open(self._file_path, "w", encoding="utf-8") as f:
|
||||
json.dump(data, f, ensure_ascii=False, indent=2)
|
||||
logger.debug("收藏列表保存成功,共 %d 项", len(self._favorites))
|
||||
logger.debug("收藏列表保存成功, 共 %d 项", len(self._favorites))
|
||||
except Exception as e:
|
||||
logger.error("保存收藏列表失败: %s", e)
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
|
||||
转换规则:
|
||||
1. `ident` 列用作 TOML 的 section 标题(`[ident]`)
|
||||
2. 若某行的 `ident` 为空,则自动按顺序生成标识符,例如 `idx_1`、`idx_2` 等
|
||||
2. 若某行的 `ident` 为空, 则自动按顺序生成标识符, 例如 `idx_1`、`idx_2` 等
|
||||
3. 所有其他列(除 `ident` 外)都作为该 section 下的键值对
|
||||
4. 所有列都是可选的,但 `ident` 为空时会自动生成
|
||||
4. 所有列都是可选的, 但 `ident` 为空时会自动生成
|
||||
|
||||
示例 CSV:
|
||||
```csv
|
||||
@@ -61,8 +61,8 @@ meaning = "狗发出的声音"
|
||||
补充说明:
|
||||
- 自动生成的标识符使用 `idx_` 前缀加数字序列
|
||||
- 生成序列基于原始 CSV 中 `ident` 为空的行出现的顺序
|
||||
- 所有值都保留为字符串类型,符合 TOML 字符串格式要求
|
||||
- 如果 CSV 包含更多列,它们也会以相同方式转换为键值对
|
||||
- 所有值都保留为字符串类型, 符合 TOML 字符串格式要求
|
||||
- 如果 CSV 包含更多列, 它们也会以相同方式转换为键值对
|
||||
- 支持 `-r` 参数指定随机种子来打乱 section 顺序
|
||||
"""
|
||||
|
||||
@@ -79,8 +79,8 @@ def csv_to_toml(csv_path, toml_path=None, random_seed=None):
|
||||
|
||||
Args:
|
||||
csv_path (str): 输入CSV文件路径
|
||||
toml_path (str): 输出TOML文件路径,默认为相同目录下同名文件
|
||||
random_seed (int): 随机种子,用于打乱section顺序,None表示不打乱
|
||||
toml_path (str): 输出TOML文件路径, 默认为相同目录下同名文件
|
||||
random_seed (int): 随机种子, 用于打乱section顺序, None表示不打乱
|
||||
"""
|
||||
# 检查CSV文件是否存在
|
||||
csv_file = Path(csv_path)
|
||||
@@ -108,7 +108,7 @@ def csv_to_toml(csv_path, toml_path=None, random_seed=None):
|
||||
print("错误: CSV文件为空或格式不正确")
|
||||
sys.exit(1)
|
||||
|
||||
# 如果指定了随机种子,设置随机种子并打乱行顺序
|
||||
# 如果指定了随机种子, 设置随机种子并打乱行顺序
|
||||
if random_seed is not None:
|
||||
random.seed(random_seed)
|
||||
random.shuffle(rows)
|
||||
@@ -119,7 +119,7 @@ def csv_to_toml(csv_path, toml_path=None, random_seed=None):
|
||||
idx_counter = 1
|
||||
|
||||
for row in rows:
|
||||
# 处理ident列,为空时生成自动标识符
|
||||
# 处理ident列, 为空时生成自动标识符
|
||||
ident = row.get("ident", "").strip()
|
||||
if not ident:
|
||||
ident = f"idx_{idx_counter}"
|
||||
@@ -155,7 +155,7 @@ def csv_to_toml(csv_path, toml_path=None, random_seed=None):
|
||||
def main():
|
||||
"""主函数"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="将CSV文件转换为TOML格式,支持随机打乱section顺序",
|
||||
description="将CSV文件转换为TOML格式, 支持随机打乱section顺序",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
示例:
|
||||
@@ -168,10 +168,10 @@ def main():
|
||||
|
||||
parser.add_argument("csv_path", help="输入的CSV文件路径")
|
||||
parser.add_argument(
|
||||
"toml_path", nargs="?", help="输出的TOML文件路径,默认为CSV同名文件"
|
||||
"toml_path", nargs="?", help="输出的TOML文件路径, 默认为CSV同名文件"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-r", "--random-seed", type=int, help="随机种子,用于打乱TOML section的顺序"
|
||||
"-r", "--random-seed", type=int, help="随机种子, 用于打乱TOML section的顺序"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
Reference in New Issue
Block a user