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,313 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""
|
|
4
|
+
Config Validator - Configuration validator
|
|
5
|
+
Validates the completeness and correctness of config.yaml
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
import yaml
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ConfigValidator:
|
|
15
|
+
"""Configuration validator"""
|
|
16
|
+
|
|
17
|
+
# Required top-level configuration items
|
|
18
|
+
# Note: llm configuration has been moved to environment variables (env_config.py), no longer required in config.yaml
|
|
19
|
+
REQUIRED_SECTIONS = ["system", "agents"]
|
|
20
|
+
|
|
21
|
+
# Required system configuration
|
|
22
|
+
REQUIRED_SYSTEM_CONFIGS = [
|
|
23
|
+
"output_base_dir",
|
|
24
|
+
"save_intermediate_results",
|
|
25
|
+
# Note: language is now unified in config/main.yaml, not required in sub-configs
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
# Supported languages (supports multiple formats)
|
|
29
|
+
SUPPORTED_LANGUAGES = ["zh", "en", "English", "Chinese"]
|
|
30
|
+
|
|
31
|
+
# Supported log levels
|
|
32
|
+
SUPPORTED_LOG_LEVELS = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
|
|
33
|
+
|
|
34
|
+
# Agent list - dual-loop architecture
|
|
35
|
+
STANDARD_AGENTS = [
|
|
36
|
+
# Analysis Loop
|
|
37
|
+
"investigate_agent",
|
|
38
|
+
"note_agent",
|
|
39
|
+
# Solve Loop
|
|
40
|
+
"manager_agent",
|
|
41
|
+
"solve_agent",
|
|
42
|
+
"tool_agent",
|
|
43
|
+
"response_agent",
|
|
44
|
+
"precision_answer_agent",
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
def __init__(self):
|
|
48
|
+
self.errors: list[str] = []
|
|
49
|
+
self.warnings: list[str] = []
|
|
50
|
+
|
|
51
|
+
def validate(self, config: dict[str, Any]) -> tuple[bool, list[str], list[str]]:
|
|
52
|
+
"""
|
|
53
|
+
Validate configuration
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
config: Configuration dictionary
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
(is_valid, errors, warnings)
|
|
60
|
+
"""
|
|
61
|
+
self.errors = []
|
|
62
|
+
self.warnings = []
|
|
63
|
+
|
|
64
|
+
# 1. Validate top-level structure
|
|
65
|
+
self._validate_structure(config)
|
|
66
|
+
|
|
67
|
+
# 2. Validate system configuration
|
|
68
|
+
if "system" in config:
|
|
69
|
+
self._validate_system(config["system"])
|
|
70
|
+
|
|
71
|
+
# 3. Validate agents configuration
|
|
72
|
+
if "agents" in config:
|
|
73
|
+
self._validate_agents(config["agents"])
|
|
74
|
+
|
|
75
|
+
# 4. Validate llm configuration (optional, LLM config now mainly comes from environment variables)
|
|
76
|
+
if "llm" in config:
|
|
77
|
+
self._validate_llm(config["llm"])
|
|
78
|
+
|
|
79
|
+
# 5. Validate logging configuration (optional)
|
|
80
|
+
if "logging" in config:
|
|
81
|
+
self._validate_logging(config["logging"])
|
|
82
|
+
|
|
83
|
+
# 6. Validate monitoring configuration (optional)
|
|
84
|
+
if "monitoring" in config:
|
|
85
|
+
self._validate_monitoring(config["monitoring"])
|
|
86
|
+
|
|
87
|
+
is_valid = len(self.errors) == 0
|
|
88
|
+
return is_valid, self.errors, self.warnings
|
|
89
|
+
|
|
90
|
+
def _validate_structure(self, config: dict[str, Any]):
|
|
91
|
+
"""Validate top-level structure"""
|
|
92
|
+
for section in self.REQUIRED_SECTIONS:
|
|
93
|
+
if section not in config:
|
|
94
|
+
self.errors.append(f"Missing required configuration section: {section}")
|
|
95
|
+
|
|
96
|
+
def _validate_system(self, system_config: dict[str, Any]):
|
|
97
|
+
"""Validate system configuration"""
|
|
98
|
+
if system_config is None:
|
|
99
|
+
self.errors.append("system configuration cannot be None")
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
# Check required fields
|
|
103
|
+
for field in self.REQUIRED_SYSTEM_CONFIGS:
|
|
104
|
+
if field not in system_config:
|
|
105
|
+
self.errors.append(f"system config missing required field: {field}")
|
|
106
|
+
|
|
107
|
+
# Check language configuration (unified in config/main.yaml, optional in sub-configs)
|
|
108
|
+
if "language" in system_config:
|
|
109
|
+
lang = system_config["language"]
|
|
110
|
+
if lang not in self.SUPPORTED_LANGUAGES:
|
|
111
|
+
self.warnings.append(
|
|
112
|
+
f"language '{lang}' not in supported list: {self.SUPPORTED_LANGUAGES}. Suggest unified configuration in config/main.yaml."
|
|
113
|
+
)
|
|
114
|
+
# Backward compatibility: if output_language still exists, give warning
|
|
115
|
+
if "output_language" in system_config:
|
|
116
|
+
self.warnings.append(
|
|
117
|
+
"output_language is deprecated, please use language field. Language configuration has been unified to system.language in config/main.yaml"
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
# Check auto_solve configuration
|
|
121
|
+
if "auto_solve" in system_config:
|
|
122
|
+
if not isinstance(system_config["auto_solve"], bool):
|
|
123
|
+
self.errors.append("auto_solve must be a boolean value")
|
|
124
|
+
|
|
125
|
+
def _validate_agents(self, agents_config: dict[str, Any]):
|
|
126
|
+
"""Validate agents configuration"""
|
|
127
|
+
if agents_config is None:
|
|
128
|
+
self.errors.append("agents config cannot be None")
|
|
129
|
+
return
|
|
130
|
+
|
|
131
|
+
# Check if standard agents are configured
|
|
132
|
+
for agent_name in self.STANDARD_AGENTS:
|
|
133
|
+
if agent_name not in agents_config:
|
|
134
|
+
self.warnings.append(f"Agent not configured: {agent_name}")
|
|
135
|
+
else:
|
|
136
|
+
self._validate_agent_config(agent_name, agents_config[agent_name])
|
|
137
|
+
|
|
138
|
+
def _validate_agent_config(self, agent_name: str, agent_config: dict[str, Any]):
|
|
139
|
+
"""Validate single Agent configuration"""
|
|
140
|
+
if agent_config is None:
|
|
141
|
+
self.errors.append(f"{agent_name} config cannot be None")
|
|
142
|
+
return
|
|
143
|
+
|
|
144
|
+
# Check enabled field
|
|
145
|
+
if "enabled" in agent_config:
|
|
146
|
+
if not isinstance(agent_config["enabled"], bool):
|
|
147
|
+
self.errors.append(f"{agent_name}.enabled must be a boolean value")
|
|
148
|
+
|
|
149
|
+
# Check model field
|
|
150
|
+
if "model" in agent_config:
|
|
151
|
+
if not isinstance(agent_config["model"], str):
|
|
152
|
+
self.errors.append(f"{agent_name}.model must be a string")
|
|
153
|
+
|
|
154
|
+
# Check temperature field
|
|
155
|
+
if "temperature" in agent_config:
|
|
156
|
+
temp = agent_config["temperature"]
|
|
157
|
+
if not isinstance(temp, (int, float)):
|
|
158
|
+
self.errors.append(f"{agent_name}.temperature must be a number")
|
|
159
|
+
elif not 0 <= temp <= 2:
|
|
160
|
+
self.warnings.append(
|
|
161
|
+
f"{agent_name}.temperature={temp} exceeds recommended range [0, 2]"
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
# Check max_retries field
|
|
165
|
+
if "max_retries" in agent_config:
|
|
166
|
+
if not isinstance(agent_config["max_retries"], int):
|
|
167
|
+
self.errors.append(f"{agent_name}.max_retries must be an integer")
|
|
168
|
+
elif agent_config["max_retries"] < 0:
|
|
169
|
+
self.errors.append(f"{agent_name}.max_retries cannot be negative")
|
|
170
|
+
|
|
171
|
+
def _validate_llm(self, llm_config: dict[str, Any]):
|
|
172
|
+
"""Validate llm configuration"""
|
|
173
|
+
if llm_config is None:
|
|
174
|
+
self.errors.append("llm config cannot be None")
|
|
175
|
+
return
|
|
176
|
+
|
|
177
|
+
# Check default_model
|
|
178
|
+
if "default_model" not in llm_config:
|
|
179
|
+
self.warnings.append("llm config missing default_model field")
|
|
180
|
+
|
|
181
|
+
# Check max_retries
|
|
182
|
+
if "max_retries" in llm_config:
|
|
183
|
+
if not isinstance(llm_config["max_retries"], int):
|
|
184
|
+
self.errors.append("llm.max_retries must be an integer")
|
|
185
|
+
elif llm_config["max_retries"] < 0:
|
|
186
|
+
self.errors.append("llm.max_retries cannot be negative")
|
|
187
|
+
|
|
188
|
+
# Check timeout
|
|
189
|
+
if "timeout" in llm_config:
|
|
190
|
+
if not isinstance(llm_config["timeout"], (int, float)):
|
|
191
|
+
self.errors.append("llm.timeout must be a number")
|
|
192
|
+
elif llm_config["timeout"] <= 0:
|
|
193
|
+
self.warnings.append("llm.timeout should not be negative or zero")
|
|
194
|
+
|
|
195
|
+
def _validate_logging(self, logging_config: dict[str, Any]):
|
|
196
|
+
"""Validate logging configuration"""
|
|
197
|
+
if logging_config is None:
|
|
198
|
+
# logging is optional, if None but not empty key, may be considered error or ignored
|
|
199
|
+
self.errors.append("logging config cannot be None")
|
|
200
|
+
return
|
|
201
|
+
|
|
202
|
+
# Check level
|
|
203
|
+
if "level" in logging_config:
|
|
204
|
+
level = logging_config["level"]
|
|
205
|
+
if level not in self.SUPPORTED_LOG_LEVELS:
|
|
206
|
+
self.errors.append(
|
|
207
|
+
f"logging.level '{level}' not in supported list: {self.SUPPORTED_LOG_LEVELS}"
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
# Check save_to_file
|
|
211
|
+
if "save_to_file" in logging_config:
|
|
212
|
+
if not isinstance(logging_config["save_to_file"], bool):
|
|
213
|
+
self.errors.append("logging.save_to_file must be a boolean value")
|
|
214
|
+
|
|
215
|
+
# Check verbose
|
|
216
|
+
if "verbose" in logging_config:
|
|
217
|
+
if not isinstance(logging_config["verbose"], bool):
|
|
218
|
+
self.errors.append("logging.verbose must be a boolean value")
|
|
219
|
+
|
|
220
|
+
def _validate_monitoring(self, monitoring_config: dict[str, Any]):
|
|
221
|
+
"""Validate monitoring configuration"""
|
|
222
|
+
if monitoring_config is None:
|
|
223
|
+
self.errors.append("monitoring config cannot be None")
|
|
224
|
+
return
|
|
225
|
+
|
|
226
|
+
# Check enabled
|
|
227
|
+
if "enabled" in monitoring_config:
|
|
228
|
+
if not isinstance(monitoring_config["enabled"], bool):
|
|
229
|
+
self.errors.append("monitoring.enabled must be a boolean value")
|
|
230
|
+
|
|
231
|
+
# Check track_token_usage
|
|
232
|
+
if "track_token_usage" in monitoring_config:
|
|
233
|
+
if not isinstance(monitoring_config["track_token_usage"], bool):
|
|
234
|
+
self.errors.append("monitoring.track_token_usage must be a boolean value")
|
|
235
|
+
|
|
236
|
+
# Check track_time
|
|
237
|
+
if "track_time" in monitoring_config:
|
|
238
|
+
if not isinstance(monitoring_config["track_time"], bool):
|
|
239
|
+
self.errors.append("monitoring.track_time must be a boolean value")
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def validate_config_file(config_path: str) -> tuple[bool, list[str], list[str]]:
|
|
243
|
+
"""
|
|
244
|
+
Validate configuration file
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
config_path: Configuration file path
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
(is_valid, errors, warnings)
|
|
251
|
+
"""
|
|
252
|
+
try:
|
|
253
|
+
with open(config_path, encoding="utf-8") as f:
|
|
254
|
+
config = yaml.safe_load(f)
|
|
255
|
+
except FileNotFoundError:
|
|
256
|
+
return False, [f"Configuration file does not exist: {config_path}"], []
|
|
257
|
+
except yaml.YAMLError as e:
|
|
258
|
+
return False, [f"YAML parsing error: {e!s}"], []
|
|
259
|
+
except Exception as e:
|
|
260
|
+
return False, [f"Failed to load configuration file: {e!s}"], []
|
|
261
|
+
|
|
262
|
+
validator = ConfigValidator()
|
|
263
|
+
return validator.validate(config)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def print_validation_result(is_valid: bool, errors: list[str], warnings: list[str]):
|
|
267
|
+
"""
|
|
268
|
+
Print validation result
|
|
269
|
+
|
|
270
|
+
Args:
|
|
271
|
+
is_valid: Whether configuration is valid
|
|
272
|
+
errors: List of errors
|
|
273
|
+
warnings: List of warnings
|
|
274
|
+
"""
|
|
275
|
+
print("=" * 60)
|
|
276
|
+
print("Configuration Validation Result")
|
|
277
|
+
print("=" * 60)
|
|
278
|
+
|
|
279
|
+
if is_valid:
|
|
280
|
+
print("✓ Configuration validation passed")
|
|
281
|
+
else:
|
|
282
|
+
print("✗ Configuration validation failed")
|
|
283
|
+
|
|
284
|
+
print()
|
|
285
|
+
|
|
286
|
+
if errors:
|
|
287
|
+
print(f"Errors ({len(errors)}):")
|
|
288
|
+
for i, error in enumerate(errors, 1):
|
|
289
|
+
print(f" {i}. {error}")
|
|
290
|
+
print()
|
|
291
|
+
|
|
292
|
+
if warnings:
|
|
293
|
+
print(f"Warnings ({len(warnings)}):")
|
|
294
|
+
for i, warning in enumerate(warnings, 1):
|
|
295
|
+
print(f" {i}. {warning}")
|
|
296
|
+
print()
|
|
297
|
+
|
|
298
|
+
print("=" * 60)
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
if __name__ == "__main__":
|
|
302
|
+
# Test configuration validation
|
|
303
|
+
print("Configuration Validation Test")
|
|
304
|
+
print("=" * 60)
|
|
305
|
+
|
|
306
|
+
# Validate config.yaml in current directory
|
|
307
|
+
config_path = Path(__file__).parent.parent.parent.parent / "config.yaml"
|
|
308
|
+
|
|
309
|
+
if config_path.exists():
|
|
310
|
+
is_valid, errors, warnings = validate_config_file(str(config_path))
|
|
311
|
+
print_validation_result(is_valid, errors, warnings)
|
|
312
|
+
else:
|
|
313
|
+
print(f"Configuration file not found: {config_path}")
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Display Manager - Terminal display manager
|
|
4
|
+
Uses rich library to implement beautiful terminal interface, including fixed header (status/statistics) and scrolling log area
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
import sys
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
try:
|
|
12
|
+
from rich.console import Console
|
|
13
|
+
from rich.layout import Layout
|
|
14
|
+
from rich.live import Live
|
|
15
|
+
from rich.panel import Panel
|
|
16
|
+
from rich.table import Table
|
|
17
|
+
from rich.text import Text
|
|
18
|
+
|
|
19
|
+
RICH_AVAILABLE = True
|
|
20
|
+
except ImportError:
|
|
21
|
+
RICH_AVAILABLE = False
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class DisplayManager:
|
|
25
|
+
"""
|
|
26
|
+
Terminal display manager
|
|
27
|
+
|
|
28
|
+
If rich is available, use TUI interface:
|
|
29
|
+
- Top: Agent status checklist + Token/Cost statistics
|
|
30
|
+
- Bottom: Scrolling logs
|
|
31
|
+
|
|
32
|
+
If rich is not available, fall back to standard stdout output
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __init__(self):
|
|
36
|
+
self.rich_available = RICH_AVAILABLE
|
|
37
|
+
|
|
38
|
+
self.agents_status = {
|
|
39
|
+
"InvestigateAgent": "pending",
|
|
40
|
+
"NoteAgent": "pending",
|
|
41
|
+
"ManagerAgent": "pending",
|
|
42
|
+
"SolveAgent": "pending",
|
|
43
|
+
"ToolAgent": "pending",
|
|
44
|
+
"ResponseAgent": "pending",
|
|
45
|
+
"PrecisionAnswerAgent": "pending",
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
self.stats = {
|
|
49
|
+
"model": "Unknown",
|
|
50
|
+
"calls": 0,
|
|
51
|
+
"tokens": 0,
|
|
52
|
+
"input_tokens": 0,
|
|
53
|
+
"output_tokens": 0,
|
|
54
|
+
"cost": 0.0,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# Log buffer
|
|
58
|
+
self.log_buffer = []
|
|
59
|
+
self.max_log_lines = 20
|
|
60
|
+
|
|
61
|
+
# Early return if rich not available (but after initializing core attributes)
|
|
62
|
+
if not self.rich_available:
|
|
63
|
+
self.console = None
|
|
64
|
+
self.layout = None
|
|
65
|
+
self.live = None
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
# Explicitly use raw stdout, bypass logger redirection
|
|
69
|
+
self.console = Console(file=sys.__stdout__)
|
|
70
|
+
|
|
71
|
+
# Layout
|
|
72
|
+
self.layout = self._make_layout()
|
|
73
|
+
|
|
74
|
+
# Live update context
|
|
75
|
+
self.live = None
|
|
76
|
+
|
|
77
|
+
def start(self):
|
|
78
|
+
"""Start Live Display"""
|
|
79
|
+
if self.rich_available and self.live is None:
|
|
80
|
+
self.live = Live(
|
|
81
|
+
self.layout,
|
|
82
|
+
refresh_per_second=4,
|
|
83
|
+
console=self.console,
|
|
84
|
+
transient=True, # Remove interface on exit to avoid blocking subsequent input
|
|
85
|
+
)
|
|
86
|
+
self.live.start()
|
|
87
|
+
|
|
88
|
+
def stop(self):
|
|
89
|
+
"""Stop Live Display"""
|
|
90
|
+
if self.rich_available and self.live:
|
|
91
|
+
self.live.stop()
|
|
92
|
+
self.live = None
|
|
93
|
+
|
|
94
|
+
def _make_layout(self) -> "Layout":
|
|
95
|
+
"""Create layout"""
|
|
96
|
+
layout = Layout()
|
|
97
|
+
|
|
98
|
+
# Split into upper part (fixed info) and lower part (logs)
|
|
99
|
+
layout.split_column(Layout(name="header", size=10), Layout(name="body"))
|
|
100
|
+
|
|
101
|
+
# Split upper part into left (Agents) and right (Stats)
|
|
102
|
+
layout["header"].split_row(Layout(name="agents", ratio=1), Layout(name="stats", ratio=2))
|
|
103
|
+
|
|
104
|
+
return layout
|
|
105
|
+
|
|
106
|
+
def update(self):
|
|
107
|
+
"""Update interface content"""
|
|
108
|
+
if not self.rich_available:
|
|
109
|
+
return
|
|
110
|
+
|
|
111
|
+
# Update Agents status panel
|
|
112
|
+
agents_table = Table(show_header=False, box=None, padding=(0, 1), expand=True)
|
|
113
|
+
agents_table.add_column("Status", width=2)
|
|
114
|
+
agents_table.add_column("Name")
|
|
115
|
+
|
|
116
|
+
for name, status in self.agents_status.items():
|
|
117
|
+
if status == "done":
|
|
118
|
+
icon = "✓"
|
|
119
|
+
style = "green"
|
|
120
|
+
elif status == "running":
|
|
121
|
+
icon = "●"
|
|
122
|
+
style = "yellow"
|
|
123
|
+
elif status == "error":
|
|
124
|
+
icon = "✗"
|
|
125
|
+
style = "red"
|
|
126
|
+
else:
|
|
127
|
+
icon = " "
|
|
128
|
+
style = "dim"
|
|
129
|
+
|
|
130
|
+
agents_table.add_row(Text(icon, style=style), Text(name, style=style))
|
|
131
|
+
|
|
132
|
+
self.layout["agents"].update(
|
|
133
|
+
Panel(
|
|
134
|
+
agents_table,
|
|
135
|
+
title="📦 Agents Status",
|
|
136
|
+
border_style="blue",
|
|
137
|
+
padding=(0, 1), # Increase panel padding
|
|
138
|
+
)
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
# Update statistics panel
|
|
142
|
+
stats_content = (
|
|
143
|
+
f"[bold]{self.stats['model']}[/bold]\n"
|
|
144
|
+
f"Calls: {self.stats['calls']}\n"
|
|
145
|
+
f"Tokens: {self.stats['tokens']:,} "
|
|
146
|
+
f"(Input: {self.stats['input_tokens']:,}, Output: {self.stats['output_tokens']:,})\n"
|
|
147
|
+
f"[bold yellow]Cost: ${self.stats['cost']:.6f} USD[/bold yellow]"
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
self.layout["stats"].update(
|
|
151
|
+
Panel(
|
|
152
|
+
stats_content,
|
|
153
|
+
title="📊 Performance & Cost",
|
|
154
|
+
border_style="green",
|
|
155
|
+
padding=(0, 1), # Increase panel padding
|
|
156
|
+
)
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
# Update log panel
|
|
160
|
+
log_text = "\n".join(self.log_buffer[-self.max_log_lines :])
|
|
161
|
+
self.layout["body"].update(
|
|
162
|
+
Panel(
|
|
163
|
+
log_text,
|
|
164
|
+
title="📝 Activity Log",
|
|
165
|
+
border_style="white",
|
|
166
|
+
padding=(0, 1), # Increase panel padding
|
|
167
|
+
)
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
def set_agent_status(self, agent_name: str, status: str):
|
|
171
|
+
"""Set Agent status (pending, running, done, error)"""
|
|
172
|
+
self.agents_status[agent_name] = status
|
|
173
|
+
self.update()
|
|
174
|
+
|
|
175
|
+
def update_token_stats(self, summary: dict[str, Any]):
|
|
176
|
+
"""Update Token statistics"""
|
|
177
|
+
self.stats["calls"] = summary.get("total_calls", 0)
|
|
178
|
+
self.stats["tokens"] = summary.get("total_tokens", 0)
|
|
179
|
+
self.stats["input_tokens"] = summary.get("total_prompt_tokens", 0)
|
|
180
|
+
self.stats["output_tokens"] = summary.get("total_completion_tokens", 0)
|
|
181
|
+
self.stats["cost"] = summary.get("total_cost_usd", 0.0)
|
|
182
|
+
|
|
183
|
+
# Try to get the main model used
|
|
184
|
+
by_model = summary.get("by_model", {})
|
|
185
|
+
if by_model:
|
|
186
|
+
# Find the most called model
|
|
187
|
+
top_model = max(by_model.items(), key=lambda x: x[1]["calls"])[0]
|
|
188
|
+
self.stats["model"] = top_model
|
|
189
|
+
|
|
190
|
+
self.update()
|
|
191
|
+
|
|
192
|
+
def log(self, message: str):
|
|
193
|
+
"""Add log"""
|
|
194
|
+
if not self.rich_available:
|
|
195
|
+
print(message)
|
|
196
|
+
return
|
|
197
|
+
|
|
198
|
+
# Simple cleaning
|
|
199
|
+
clean_msg = message.rstrip()
|
|
200
|
+
if not clean_msg:
|
|
201
|
+
return
|
|
202
|
+
|
|
203
|
+
# Add timestamp
|
|
204
|
+
timestamp = datetime.now().strftime("%H:%M:%S")
|
|
205
|
+
formatted_msg = f"[{timestamp}] {clean_msg}"
|
|
206
|
+
|
|
207
|
+
self.log_buffer.append(formatted_msg)
|
|
208
|
+
# Maintain buffer size
|
|
209
|
+
if len(self.log_buffer) > 100:
|
|
210
|
+
self.log_buffer = self.log_buffer[-50:]
|
|
211
|
+
|
|
212
|
+
self.update()
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
# Global instance
|
|
216
|
+
_display_manager = None
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def get_display_manager() -> DisplayManager:
|
|
220
|
+
global _display_manager
|
|
221
|
+
if _display_manager is None:
|
|
222
|
+
_display_manager = DisplayManager()
|
|
223
|
+
return _display_manager
|