realtimex-deeptutor 0.5.0.post1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- realtimex_deeptutor/__init__.py +67 -0
- realtimex_deeptutor-0.5.0.post1.dist-info/METADATA +1612 -0
- realtimex_deeptutor-0.5.0.post1.dist-info/RECORD +276 -0
- realtimex_deeptutor-0.5.0.post1.dist-info/WHEEL +5 -0
- realtimex_deeptutor-0.5.0.post1.dist-info/entry_points.txt +2 -0
- realtimex_deeptutor-0.5.0.post1.dist-info/licenses/LICENSE +661 -0
- realtimex_deeptutor-0.5.0.post1.dist-info/top_level.txt +2 -0
- src/__init__.py +40 -0
- src/agents/__init__.py +24 -0
- src/agents/base_agent.py +657 -0
- src/agents/chat/__init__.py +24 -0
- src/agents/chat/chat_agent.py +435 -0
- src/agents/chat/prompts/en/chat_agent.yaml +35 -0
- src/agents/chat/prompts/zh/chat_agent.yaml +35 -0
- src/agents/chat/session_manager.py +311 -0
- src/agents/co_writer/__init__.py +0 -0
- src/agents/co_writer/edit_agent.py +260 -0
- src/agents/co_writer/narrator_agent.py +423 -0
- src/agents/co_writer/prompts/en/edit_agent.yaml +113 -0
- src/agents/co_writer/prompts/en/narrator_agent.yaml +88 -0
- src/agents/co_writer/prompts/zh/edit_agent.yaml +113 -0
- src/agents/co_writer/prompts/zh/narrator_agent.yaml +88 -0
- src/agents/guide/__init__.py +16 -0
- src/agents/guide/agents/__init__.py +11 -0
- src/agents/guide/agents/chat_agent.py +104 -0
- src/agents/guide/agents/interactive_agent.py +223 -0
- src/agents/guide/agents/locate_agent.py +149 -0
- src/agents/guide/agents/summary_agent.py +150 -0
- src/agents/guide/guide_manager.py +500 -0
- src/agents/guide/prompts/en/chat_agent.yaml +41 -0
- src/agents/guide/prompts/en/interactive_agent.yaml +202 -0
- src/agents/guide/prompts/en/locate_agent.yaml +68 -0
- src/agents/guide/prompts/en/summary_agent.yaml +157 -0
- src/agents/guide/prompts/zh/chat_agent.yaml +41 -0
- src/agents/guide/prompts/zh/interactive_agent.yaml +626 -0
- src/agents/guide/prompts/zh/locate_agent.yaml +68 -0
- src/agents/guide/prompts/zh/summary_agent.yaml +157 -0
- src/agents/ideagen/__init__.py +12 -0
- src/agents/ideagen/idea_generation_workflow.py +426 -0
- src/agents/ideagen/material_organizer_agent.py +173 -0
- src/agents/ideagen/prompts/en/idea_generation.yaml +187 -0
- src/agents/ideagen/prompts/en/material_organizer.yaml +69 -0
- src/agents/ideagen/prompts/zh/idea_generation.yaml +187 -0
- src/agents/ideagen/prompts/zh/material_organizer.yaml +69 -0
- src/agents/question/__init__.py +24 -0
- src/agents/question/agents/__init__.py +18 -0
- src/agents/question/agents/generate_agent.py +381 -0
- src/agents/question/agents/relevance_analyzer.py +207 -0
- src/agents/question/agents/retrieve_agent.py +239 -0
- src/agents/question/coordinator.py +718 -0
- src/agents/question/example.py +109 -0
- src/agents/question/prompts/en/coordinator.yaml +75 -0
- src/agents/question/prompts/en/generate_agent.yaml +77 -0
- src/agents/question/prompts/en/relevance_analyzer.yaml +41 -0
- src/agents/question/prompts/en/retrieve_agent.yaml +32 -0
- src/agents/question/prompts/zh/coordinator.yaml +75 -0
- src/agents/question/prompts/zh/generate_agent.yaml +77 -0
- src/agents/question/prompts/zh/relevance_analyzer.yaml +39 -0
- src/agents/question/prompts/zh/retrieve_agent.yaml +30 -0
- src/agents/research/agents/__init__.py +23 -0
- src/agents/research/agents/decompose_agent.py +507 -0
- src/agents/research/agents/manager_agent.py +228 -0
- src/agents/research/agents/note_agent.py +180 -0
- src/agents/research/agents/rephrase_agent.py +263 -0
- src/agents/research/agents/reporting_agent.py +1333 -0
- src/agents/research/agents/research_agent.py +714 -0
- src/agents/research/data_structures.py +451 -0
- src/agents/research/main.py +188 -0
- src/agents/research/prompts/en/decompose_agent.yaml +89 -0
- src/agents/research/prompts/en/manager_agent.yaml +24 -0
- src/agents/research/prompts/en/note_agent.yaml +121 -0
- src/agents/research/prompts/en/rephrase_agent.yaml +58 -0
- src/agents/research/prompts/en/reporting_agent.yaml +380 -0
- src/agents/research/prompts/en/research_agent.yaml +173 -0
- src/agents/research/prompts/zh/decompose_agent.yaml +89 -0
- src/agents/research/prompts/zh/manager_agent.yaml +24 -0
- src/agents/research/prompts/zh/note_agent.yaml +121 -0
- src/agents/research/prompts/zh/rephrase_agent.yaml +58 -0
- src/agents/research/prompts/zh/reporting_agent.yaml +380 -0
- src/agents/research/prompts/zh/research_agent.yaml +173 -0
- src/agents/research/research_pipeline.py +1309 -0
- src/agents/research/utils/__init__.py +60 -0
- src/agents/research/utils/citation_manager.py +799 -0
- src/agents/research/utils/json_utils.py +98 -0
- src/agents/research/utils/token_tracker.py +297 -0
- src/agents/solve/__init__.py +80 -0
- src/agents/solve/analysis_loop/__init__.py +14 -0
- src/agents/solve/analysis_loop/investigate_agent.py +414 -0
- src/agents/solve/analysis_loop/note_agent.py +190 -0
- src/agents/solve/main_solver.py +862 -0
- src/agents/solve/memory/__init__.py +34 -0
- src/agents/solve/memory/citation_memory.py +353 -0
- src/agents/solve/memory/investigate_memory.py +226 -0
- src/agents/solve/memory/solve_memory.py +340 -0
- src/agents/solve/prompts/en/analysis_loop/investigate_agent.yaml +55 -0
- src/agents/solve/prompts/en/analysis_loop/note_agent.yaml +54 -0
- src/agents/solve/prompts/en/solve_loop/manager_agent.yaml +67 -0
- src/agents/solve/prompts/en/solve_loop/precision_answer_agent.yaml +62 -0
- src/agents/solve/prompts/en/solve_loop/response_agent.yaml +90 -0
- src/agents/solve/prompts/en/solve_loop/solve_agent.yaml +75 -0
- src/agents/solve/prompts/en/solve_loop/tool_agent.yaml +38 -0
- src/agents/solve/prompts/zh/analysis_loop/investigate_agent.yaml +53 -0
- src/agents/solve/prompts/zh/analysis_loop/note_agent.yaml +54 -0
- src/agents/solve/prompts/zh/solve_loop/manager_agent.yaml +66 -0
- src/agents/solve/prompts/zh/solve_loop/precision_answer_agent.yaml +62 -0
- src/agents/solve/prompts/zh/solve_loop/response_agent.yaml +90 -0
- src/agents/solve/prompts/zh/solve_loop/solve_agent.yaml +76 -0
- src/agents/solve/prompts/zh/solve_loop/tool_agent.yaml +41 -0
- src/agents/solve/solve_loop/__init__.py +22 -0
- src/agents/solve/solve_loop/citation_manager.py +74 -0
- src/agents/solve/solve_loop/manager_agent.py +274 -0
- src/agents/solve/solve_loop/precision_answer_agent.py +96 -0
- src/agents/solve/solve_loop/response_agent.py +301 -0
- src/agents/solve/solve_loop/solve_agent.py +325 -0
- src/agents/solve/solve_loop/tool_agent.py +470 -0
- src/agents/solve/utils/__init__.py +64 -0
- src/agents/solve/utils/config_validator.py +313 -0
- src/agents/solve/utils/display_manager.py +223 -0
- src/agents/solve/utils/error_handler.py +363 -0
- src/agents/solve/utils/json_utils.py +98 -0
- src/agents/solve/utils/performance_monitor.py +407 -0
- src/agents/solve/utils/token_tracker.py +541 -0
- src/api/__init__.py +0 -0
- src/api/main.py +240 -0
- src/api/routers/__init__.py +1 -0
- src/api/routers/agent_config.py +69 -0
- src/api/routers/chat.py +296 -0
- src/api/routers/co_writer.py +337 -0
- src/api/routers/config.py +627 -0
- src/api/routers/dashboard.py +18 -0
- src/api/routers/guide.py +337 -0
- src/api/routers/ideagen.py +436 -0
- src/api/routers/knowledge.py +821 -0
- src/api/routers/notebook.py +247 -0
- src/api/routers/question.py +537 -0
- src/api/routers/research.py +394 -0
- src/api/routers/settings.py +164 -0
- src/api/routers/solve.py +305 -0
- src/api/routers/system.py +252 -0
- src/api/run_server.py +61 -0
- src/api/utils/history.py +172 -0
- src/api/utils/log_interceptor.py +21 -0
- src/api/utils/notebook_manager.py +415 -0
- src/api/utils/progress_broadcaster.py +72 -0
- src/api/utils/task_id_manager.py +100 -0
- src/config/__init__.py +0 -0
- src/config/accessors.py +18 -0
- src/config/constants.py +34 -0
- src/config/defaults.py +18 -0
- src/config/schema.py +38 -0
- src/config/settings.py +50 -0
- src/core/errors.py +62 -0
- src/knowledge/__init__.py +23 -0
- src/knowledge/add_documents.py +606 -0
- src/knowledge/config.py +65 -0
- src/knowledge/example_add_documents.py +236 -0
- src/knowledge/extract_numbered_items.py +1039 -0
- src/knowledge/initializer.py +621 -0
- src/knowledge/kb.py +22 -0
- src/knowledge/manager.py +782 -0
- src/knowledge/progress_tracker.py +182 -0
- src/knowledge/start_kb.py +535 -0
- src/logging/__init__.py +103 -0
- src/logging/adapters/__init__.py +17 -0
- src/logging/adapters/lightrag.py +184 -0
- src/logging/adapters/llamaindex.py +141 -0
- src/logging/config.py +80 -0
- src/logging/handlers/__init__.py +20 -0
- src/logging/handlers/console.py +75 -0
- src/logging/handlers/file.py +201 -0
- src/logging/handlers/websocket.py +127 -0
- src/logging/logger.py +709 -0
- src/logging/stats/__init__.py +16 -0
- src/logging/stats/llm_stats.py +179 -0
- src/services/__init__.py +56 -0
- src/services/config/__init__.py +61 -0
- src/services/config/knowledge_base_config.py +210 -0
- src/services/config/loader.py +260 -0
- src/services/config/unified_config.py +603 -0
- src/services/embedding/__init__.py +45 -0
- src/services/embedding/adapters/__init__.py +22 -0
- src/services/embedding/adapters/base.py +106 -0
- src/services/embedding/adapters/cohere.py +127 -0
- src/services/embedding/adapters/jina.py +99 -0
- src/services/embedding/adapters/ollama.py +116 -0
- src/services/embedding/adapters/openai_compatible.py +96 -0
- src/services/embedding/client.py +159 -0
- src/services/embedding/config.py +156 -0
- src/services/embedding/provider.py +119 -0
- src/services/llm/__init__.py +152 -0
- src/services/llm/capabilities.py +313 -0
- src/services/llm/client.py +302 -0
- src/services/llm/cloud_provider.py +530 -0
- src/services/llm/config.py +200 -0
- src/services/llm/error_mapping.py +103 -0
- src/services/llm/exceptions.py +152 -0
- src/services/llm/factory.py +450 -0
- src/services/llm/local_provider.py +347 -0
- src/services/llm/providers/anthropic.py +95 -0
- src/services/llm/providers/base_provider.py +93 -0
- src/services/llm/providers/open_ai.py +83 -0
- src/services/llm/registry.py +71 -0
- src/services/llm/telemetry.py +40 -0
- src/services/llm/types.py +27 -0
- src/services/llm/utils.py +333 -0
- src/services/prompt/__init__.py +25 -0
- src/services/prompt/manager.py +206 -0
- src/services/rag/__init__.py +64 -0
- src/services/rag/components/__init__.py +29 -0
- src/services/rag/components/base.py +59 -0
- src/services/rag/components/chunkers/__init__.py +18 -0
- src/services/rag/components/chunkers/base.py +34 -0
- src/services/rag/components/chunkers/fixed.py +71 -0
- src/services/rag/components/chunkers/numbered_item.py +94 -0
- src/services/rag/components/chunkers/semantic.py +97 -0
- src/services/rag/components/embedders/__init__.py +14 -0
- src/services/rag/components/embedders/base.py +32 -0
- src/services/rag/components/embedders/openai.py +63 -0
- src/services/rag/components/indexers/__init__.py +18 -0
- src/services/rag/components/indexers/base.py +35 -0
- src/services/rag/components/indexers/graph.py +172 -0
- src/services/rag/components/indexers/lightrag.py +156 -0
- src/services/rag/components/indexers/vector.py +146 -0
- src/services/rag/components/parsers/__init__.py +18 -0
- src/services/rag/components/parsers/base.py +35 -0
- src/services/rag/components/parsers/markdown.py +52 -0
- src/services/rag/components/parsers/pdf.py +115 -0
- src/services/rag/components/parsers/text.py +86 -0
- src/services/rag/components/retrievers/__init__.py +18 -0
- src/services/rag/components/retrievers/base.py +34 -0
- src/services/rag/components/retrievers/dense.py +200 -0
- src/services/rag/components/retrievers/hybrid.py +164 -0
- src/services/rag/components/retrievers/lightrag.py +169 -0
- src/services/rag/components/routing.py +286 -0
- src/services/rag/factory.py +234 -0
- src/services/rag/pipeline.py +215 -0
- src/services/rag/pipelines/__init__.py +32 -0
- src/services/rag/pipelines/academic.py +44 -0
- src/services/rag/pipelines/lightrag.py +43 -0
- src/services/rag/pipelines/llamaindex.py +313 -0
- src/services/rag/pipelines/raganything.py +384 -0
- src/services/rag/service.py +244 -0
- src/services/rag/types.py +73 -0
- src/services/search/__init__.py +284 -0
- src/services/search/base.py +87 -0
- src/services/search/consolidation.py +398 -0
- src/services/search/providers/__init__.py +128 -0
- src/services/search/providers/baidu.py +188 -0
- src/services/search/providers/exa.py +194 -0
- src/services/search/providers/jina.py +161 -0
- src/services/search/providers/perplexity.py +153 -0
- src/services/search/providers/serper.py +209 -0
- src/services/search/providers/tavily.py +161 -0
- src/services/search/types.py +114 -0
- src/services/setup/__init__.py +34 -0
- src/services/setup/init.py +285 -0
- src/services/tts/__init__.py +16 -0
- src/services/tts/config.py +99 -0
- src/tools/__init__.py +91 -0
- src/tools/code_executor.py +536 -0
- src/tools/paper_search_tool.py +171 -0
- src/tools/query_item_tool.py +310 -0
- src/tools/question/__init__.py +15 -0
- src/tools/question/exam_mimic.py +616 -0
- src/tools/question/pdf_parser.py +211 -0
- src/tools/question/question_extractor.py +397 -0
- src/tools/rag_tool.py +173 -0
- src/tools/tex_chunker.py +339 -0
- src/tools/tex_downloader.py +253 -0
- src/tools/web_search.py +71 -0
- src/utils/config_manager.py +206 -0
- src/utils/document_validator.py +168 -0
- src/utils/error_rate_tracker.py +111 -0
- src/utils/error_utils.py +82 -0
- src/utils/json_parser.py +110 -0
- src/utils/network/circuit_breaker.py +79 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Progress Tracker - Tracks knowledge base initialization progress
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
from collections.abc import Callable
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from enum import Enum
|
|
9
|
+
import json
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
import sys
|
|
12
|
+
|
|
13
|
+
# Add project root to path
|
|
14
|
+
project_root = Path(__file__).parent.parent.parent
|
|
15
|
+
sys.path.insert(0, str(project_root))
|
|
16
|
+
|
|
17
|
+
# Use unified logging system
|
|
18
|
+
from src.logging import get_logger
|
|
19
|
+
|
|
20
|
+
_logger = get_logger("KnowledgeInit")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _get_logger():
|
|
24
|
+
return _logger
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ProgressStage(Enum):
|
|
28
|
+
"""Initialization stage"""
|
|
29
|
+
|
|
30
|
+
INITIALIZING = "initializing" # Initializing
|
|
31
|
+
PROCESSING_DOCUMENTS = "processing_documents" # Processing documents
|
|
32
|
+
PROCESSING_FILE = "processing_file" # Processing single file
|
|
33
|
+
EXTRACTING_ITEMS = "extracting_items" # Extracting numbered items
|
|
34
|
+
COMPLETED = "completed" # Completed
|
|
35
|
+
ERROR = "error" # Error
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ProgressTracker:
|
|
39
|
+
"""Progress tracker"""
|
|
40
|
+
|
|
41
|
+
def __init__(self, kb_name: str, base_dir: Path):
|
|
42
|
+
self.kb_name = kb_name
|
|
43
|
+
self.base_dir = base_dir
|
|
44
|
+
self.kb_dir = base_dir / kb_name
|
|
45
|
+
self.progress_file = self.kb_dir / ".progress.json"
|
|
46
|
+
self._callbacks: list = [] # Support multiple callbacks
|
|
47
|
+
self.task_id: str | None = None # Task ID (for log identification)
|
|
48
|
+
|
|
49
|
+
def set_callback(self, callback: Callable[[dict], None]):
|
|
50
|
+
"""Set progress callback function (can be called multiple times to add multiple callbacks)"""
|
|
51
|
+
if callback not in self._callbacks:
|
|
52
|
+
self._callbacks.append(callback)
|
|
53
|
+
|
|
54
|
+
def remove_callback(self, callback: Callable[[dict], None]):
|
|
55
|
+
"""Remove progress callback function"""
|
|
56
|
+
if callback in self._callbacks:
|
|
57
|
+
self._callbacks.remove(callback)
|
|
58
|
+
|
|
59
|
+
def _notify(self, progress: dict):
|
|
60
|
+
"""Notify progress update (call all callbacks)"""
|
|
61
|
+
# Try to send via broadcaster (if available)
|
|
62
|
+
try:
|
|
63
|
+
from src.api.utils.progress_broadcaster import ProgressBroadcaster
|
|
64
|
+
|
|
65
|
+
broadcaster = ProgressBroadcaster.get_instance()
|
|
66
|
+
|
|
67
|
+
# Try to get current event loop and broadcast
|
|
68
|
+
try:
|
|
69
|
+
loop = asyncio.get_running_loop()
|
|
70
|
+
# If event loop is running, use create_task (non-blocking)
|
|
71
|
+
asyncio.create_task(broadcaster.broadcast(self.kb_name, progress))
|
|
72
|
+
except RuntimeError:
|
|
73
|
+
# No running event loop, try to get main event loop
|
|
74
|
+
try:
|
|
75
|
+
# Try to get main event loop (FastAPI main loop)
|
|
76
|
+
loop = asyncio.get_event_loop()
|
|
77
|
+
if loop.is_running():
|
|
78
|
+
# If loop is running, create task
|
|
79
|
+
asyncio.create_task(broadcaster.broadcast(self.kb_name, progress))
|
|
80
|
+
else:
|
|
81
|
+
# If loop exists but not running, try to run (may fail, but doesn't affect main flow)
|
|
82
|
+
try:
|
|
83
|
+
loop.run_until_complete(broadcaster.broadcast(self.kb_name, progress))
|
|
84
|
+
except RuntimeError:
|
|
85
|
+
# Cannot run, ignore
|
|
86
|
+
pass
|
|
87
|
+
except RuntimeError:
|
|
88
|
+
pass
|
|
89
|
+
except (ImportError, Exception):
|
|
90
|
+
# Broadcaster unavailable or error, continue using callbacks
|
|
91
|
+
# Don't print error to avoid interfering with normal flow
|
|
92
|
+
pass
|
|
93
|
+
|
|
94
|
+
# Call all registered callbacks
|
|
95
|
+
for callback in self._callbacks:
|
|
96
|
+
try:
|
|
97
|
+
callback(progress)
|
|
98
|
+
except Exception as e:
|
|
99
|
+
print(f"[ProgressTracker] Callback error: {e}")
|
|
100
|
+
|
|
101
|
+
def _save_progress(self, progress: dict):
|
|
102
|
+
"""Save progress to file"""
|
|
103
|
+
try:
|
|
104
|
+
self.kb_dir.mkdir(parents=True, exist_ok=True)
|
|
105
|
+
with open(self.progress_file, "w", encoding="utf-8") as f:
|
|
106
|
+
json.dump(progress, f, indent=2, ensure_ascii=False)
|
|
107
|
+
except Exception as e:
|
|
108
|
+
print(f"[ProgressTracker] Failed to save progress: {e}")
|
|
109
|
+
|
|
110
|
+
def update(
|
|
111
|
+
self,
|
|
112
|
+
stage: ProgressStage,
|
|
113
|
+
message: str = "",
|
|
114
|
+
current: int = 0,
|
|
115
|
+
total: int = 0,
|
|
116
|
+
file_name: str = "",
|
|
117
|
+
error: str | None = None,
|
|
118
|
+
):
|
|
119
|
+
"""Update progress"""
|
|
120
|
+
progress = {
|
|
121
|
+
"kb_name": self.kb_name,
|
|
122
|
+
"stage": stage.value,
|
|
123
|
+
"message": message,
|
|
124
|
+
"current": current,
|
|
125
|
+
"total": total,
|
|
126
|
+
"file_name": file_name,
|
|
127
|
+
"progress_percent": int(current / total * 100) if total > 0 else 0,
|
|
128
|
+
"timestamp": datetime.now().isoformat(),
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if error:
|
|
132
|
+
progress["error"] = error
|
|
133
|
+
progress["stage"] = ProgressStage.ERROR.value
|
|
134
|
+
|
|
135
|
+
# Output to logger (terminal and log file)
|
|
136
|
+
try:
|
|
137
|
+
logger = _get_logger()
|
|
138
|
+
prefix = f"[{self.task_id}]" if self.task_id else ""
|
|
139
|
+
|
|
140
|
+
if total > 0:
|
|
141
|
+
percent = progress["progress_percent"]
|
|
142
|
+
progress_msg = f"{prefix} {message} ({current}/{total}, {percent}%)"
|
|
143
|
+
if file_name:
|
|
144
|
+
progress_msg += f" - File: {file_name}"
|
|
145
|
+
else:
|
|
146
|
+
progress_msg = f"{prefix} {message}"
|
|
147
|
+
if file_name:
|
|
148
|
+
progress_msg += f" - File: {file_name}"
|
|
149
|
+
|
|
150
|
+
if error:
|
|
151
|
+
logger.error(f"{progress_msg} - Error: {error}")
|
|
152
|
+
else:
|
|
153
|
+
logger.progress(progress_msg)
|
|
154
|
+
except Exception:
|
|
155
|
+
# If logging fails, print to console
|
|
156
|
+
prefix = f"[{self.task_id}]" if self.task_id else ""
|
|
157
|
+
print(f"{prefix} [ProgressTracker] {message} ({current}/{total if total > 0 else '?'})")
|
|
158
|
+
if error:
|
|
159
|
+
print(f"{prefix} [ProgressTracker] Error: {error}")
|
|
160
|
+
|
|
161
|
+
self._save_progress(progress)
|
|
162
|
+
self._notify(progress)
|
|
163
|
+
|
|
164
|
+
def get_progress(self) -> dict | None:
|
|
165
|
+
"""Get current progress"""
|
|
166
|
+
if not self.progress_file.exists():
|
|
167
|
+
return None
|
|
168
|
+
|
|
169
|
+
try:
|
|
170
|
+
with open(self.progress_file, encoding="utf-8") as f:
|
|
171
|
+
return json.load(f)
|
|
172
|
+
except Exception as e:
|
|
173
|
+
print(f"[ProgressTracker] Failed to read progress: {e}")
|
|
174
|
+
return None
|
|
175
|
+
|
|
176
|
+
def clear(self):
|
|
177
|
+
"""Clear progress file"""
|
|
178
|
+
if self.progress_file.exists():
|
|
179
|
+
try:
|
|
180
|
+
self.progress_file.unlink()
|
|
181
|
+
except Exception as e:
|
|
182
|
+
print(f"[ProgressTracker] Failed to clear progress: {e}")
|