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,16 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Statistics Tracking
|
|
3
|
+
===================
|
|
4
|
+
|
|
5
|
+
Utilities for tracking LLM usage, costs, and performance metrics.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .llm_stats import MODEL_PRICING, LLMCall, LLMStats, estimate_tokens, get_pricing
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"LLMStats",
|
|
12
|
+
"LLMCall",
|
|
13
|
+
"get_pricing",
|
|
14
|
+
"estimate_tokens",
|
|
15
|
+
"MODEL_PRICING",
|
|
16
|
+
]
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
LLM Stats Tracker
|
|
4
|
+
=================
|
|
5
|
+
|
|
6
|
+
Simple utility for tracking LLM token usage and costs across all modules.
|
|
7
|
+
Outputs summary to terminal at the end of processing.
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
from src.logging import LLMStats
|
|
11
|
+
|
|
12
|
+
stats = LLMStats()
|
|
13
|
+
|
|
14
|
+
# After each LLM call:
|
|
15
|
+
stats.add_call(
|
|
16
|
+
model="gpt-4o-mini",
|
|
17
|
+
prompt_tokens=100,
|
|
18
|
+
completion_tokens=50
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
# At the end:
|
|
22
|
+
stats.print_summary()
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from dataclasses import dataclass, field
|
|
26
|
+
from datetime import datetime
|
|
27
|
+
from typing import Any, Optional
|
|
28
|
+
|
|
29
|
+
# Model pricing per 1K tokens (USD)
|
|
30
|
+
MODEL_PRICING = {
|
|
31
|
+
"gpt-4o": {"input": 0.0025, "output": 0.010},
|
|
32
|
+
"gpt-4o-mini": {"input": 0.00015, "output": 0.0006},
|
|
33
|
+
"gpt-4-turbo": {"input": 0.01, "output": 0.03},
|
|
34
|
+
"gpt-4": {"input": 0.03, "output": 0.06},
|
|
35
|
+
"gpt-3.5-turbo": {"input": 0.0005, "output": 0.0015},
|
|
36
|
+
"deepseek-chat": {"input": 0.00014, "output": 0.00028},
|
|
37
|
+
"claude-3-5-sonnet": {"input": 0.003, "output": 0.015},
|
|
38
|
+
"claude-3-opus": {"input": 0.015, "output": 0.075},
|
|
39
|
+
"claude-3-haiku": {"input": 0.00025, "output": 0.00125},
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def get_pricing(model: str) -> dict[str, float]:
|
|
44
|
+
"""Get pricing for a model (fuzzy match)."""
|
|
45
|
+
model_lower = model.lower()
|
|
46
|
+
for key, pricing in MODEL_PRICING.items():
|
|
47
|
+
if key in model_lower or model_lower in key:
|
|
48
|
+
return pricing
|
|
49
|
+
return MODEL_PRICING.get("gpt-4o-mini", {"input": 0.00015, "output": 0.0006})
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def estimate_tokens(text: str) -> int:
|
|
53
|
+
"""Rough estimate of tokens (1.3 tokens per word)."""
|
|
54
|
+
return int(len(text.split()) * 1.3)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class LLMCall:
|
|
59
|
+
"""Single LLM call record."""
|
|
60
|
+
|
|
61
|
+
model: str
|
|
62
|
+
prompt_tokens: int
|
|
63
|
+
completion_tokens: int
|
|
64
|
+
cost: float
|
|
65
|
+
timestamp: str = field(default_factory=lambda: datetime.now().isoformat())
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class LLMStats:
|
|
69
|
+
"""
|
|
70
|
+
LLM usage statistics tracker.
|
|
71
|
+
Tracks token usage and costs, outputs summary to terminal.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
def __init__(self, module_name: str = "Module"):
|
|
75
|
+
"""
|
|
76
|
+
Initialize stats tracker.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
module_name: Name of the module (for display)
|
|
80
|
+
"""
|
|
81
|
+
self.module_name = module_name
|
|
82
|
+
self.calls: list[LLMCall] = []
|
|
83
|
+
self.total_prompt_tokens = 0
|
|
84
|
+
self.total_completion_tokens = 0
|
|
85
|
+
self.total_cost = 0.0
|
|
86
|
+
self.model_used: Optional[str] = None
|
|
87
|
+
|
|
88
|
+
def add_call(
|
|
89
|
+
self,
|
|
90
|
+
model: str,
|
|
91
|
+
prompt_tokens: Optional[int] = None,
|
|
92
|
+
completion_tokens: Optional[int] = None,
|
|
93
|
+
# Alternative: estimate from text
|
|
94
|
+
system_prompt: Optional[str] = None,
|
|
95
|
+
user_prompt: Optional[str] = None,
|
|
96
|
+
response: Optional[str] = None,
|
|
97
|
+
):
|
|
98
|
+
"""
|
|
99
|
+
Add an LLM call to the stats.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
model: Model name
|
|
103
|
+
prompt_tokens: Number of prompt tokens (if known)
|
|
104
|
+
completion_tokens: Number of completion tokens (if known)
|
|
105
|
+
system_prompt: System prompt text (for estimation)
|
|
106
|
+
user_prompt: User prompt text (for estimation)
|
|
107
|
+
response: Response text (for estimation)
|
|
108
|
+
"""
|
|
109
|
+
# Estimate tokens if not provided
|
|
110
|
+
if prompt_tokens is None and (system_prompt or user_prompt):
|
|
111
|
+
prompt_text = (system_prompt or "") + "\n" + (user_prompt or "")
|
|
112
|
+
prompt_tokens = estimate_tokens(prompt_text)
|
|
113
|
+
|
|
114
|
+
if completion_tokens is None and response:
|
|
115
|
+
completion_tokens = estimate_tokens(response)
|
|
116
|
+
|
|
117
|
+
prompt_tokens = prompt_tokens or 0
|
|
118
|
+
completion_tokens = completion_tokens or 0
|
|
119
|
+
|
|
120
|
+
# Calculate cost
|
|
121
|
+
pricing = get_pricing(model)
|
|
122
|
+
cost = (prompt_tokens / 1000.0) * pricing["input"] + (completion_tokens / 1000.0) * pricing[
|
|
123
|
+
"output"
|
|
124
|
+
]
|
|
125
|
+
|
|
126
|
+
# Record call
|
|
127
|
+
call = LLMCall(
|
|
128
|
+
model=model, prompt_tokens=prompt_tokens, completion_tokens=completion_tokens, cost=cost
|
|
129
|
+
)
|
|
130
|
+
self.calls.append(call)
|
|
131
|
+
|
|
132
|
+
# Update totals
|
|
133
|
+
self.total_prompt_tokens += prompt_tokens
|
|
134
|
+
self.total_completion_tokens += completion_tokens
|
|
135
|
+
self.total_cost += cost
|
|
136
|
+
|
|
137
|
+
# Track primary model
|
|
138
|
+
if self.model_used is None:
|
|
139
|
+
self.model_used = model
|
|
140
|
+
|
|
141
|
+
def get_summary(self) -> dict[str, Any]:
|
|
142
|
+
"""Get summary as dictionary."""
|
|
143
|
+
return {
|
|
144
|
+
"module": self.module_name,
|
|
145
|
+
"model": self.model_used or "Unknown",
|
|
146
|
+
"calls": len(self.calls),
|
|
147
|
+
"prompt_tokens": self.total_prompt_tokens,
|
|
148
|
+
"completion_tokens": self.total_completion_tokens,
|
|
149
|
+
"total_tokens": self.total_prompt_tokens + self.total_completion_tokens,
|
|
150
|
+
"cost_usd": self.total_cost,
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
def print_summary(self):
|
|
154
|
+
"""Print summary to terminal."""
|
|
155
|
+
if len(self.calls) == 0:
|
|
156
|
+
return
|
|
157
|
+
|
|
158
|
+
total_tokens = self.total_prompt_tokens + self.total_completion_tokens
|
|
159
|
+
|
|
160
|
+
print()
|
|
161
|
+
print("=" * 60)
|
|
162
|
+
print(f"📊 [{self.module_name}] LLM Usage Summary")
|
|
163
|
+
print("=" * 60)
|
|
164
|
+
print(f" Model : {self.model_used or 'Unknown'}")
|
|
165
|
+
print(f" API Calls : {len(self.calls)}")
|
|
166
|
+
print(
|
|
167
|
+
f" Tokens : {total_tokens:,} (Input: {self.total_prompt_tokens:,}, Output: {self.total_completion_tokens:,})"
|
|
168
|
+
)
|
|
169
|
+
print(f" Cost : ${self.total_cost:.6f} USD")
|
|
170
|
+
print("=" * 60)
|
|
171
|
+
print()
|
|
172
|
+
|
|
173
|
+
def reset(self):
|
|
174
|
+
"""Reset all statistics."""
|
|
175
|
+
self.calls.clear()
|
|
176
|
+
self.total_prompt_tokens = 0
|
|
177
|
+
self.total_completion_tokens = 0
|
|
178
|
+
self.total_cost = 0.0
|
|
179
|
+
self.model_used = None
|
src/services/__init__.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Services Layer
|
|
3
|
+
==============
|
|
4
|
+
|
|
5
|
+
Unified service layer for DeepTutor providing:
|
|
6
|
+
- LLM client and configuration
|
|
7
|
+
- Embedding client and configuration
|
|
8
|
+
- RAG pipelines and components
|
|
9
|
+
- Prompt management
|
|
10
|
+
- TTS configuration
|
|
11
|
+
- Web Search providers
|
|
12
|
+
- System setup utilities
|
|
13
|
+
- Configuration loading
|
|
14
|
+
|
|
15
|
+
Usage:
|
|
16
|
+
from src.services.llm import get_llm_client
|
|
17
|
+
from src.services.embedding import get_embedding_client
|
|
18
|
+
from src.services.rag import get_pipeline
|
|
19
|
+
from src.services.prompt import get_prompt_manager
|
|
20
|
+
from src.services.tts import get_tts_config
|
|
21
|
+
from src.services.search import web_search
|
|
22
|
+
from src.services.setup import init_user_directories
|
|
23
|
+
from src.services.config import load_config_with_main
|
|
24
|
+
|
|
25
|
+
# LLM
|
|
26
|
+
llm = get_llm_client()
|
|
27
|
+
response = await llm.complete("Hello, world!")
|
|
28
|
+
|
|
29
|
+
# Embedding
|
|
30
|
+
embed = get_embedding_client()
|
|
31
|
+
vectors = await embed.embed(["text1", "text2"])
|
|
32
|
+
|
|
33
|
+
# RAG
|
|
34
|
+
pipeline = get_pipeline("raganything")
|
|
35
|
+
result = await pipeline.search("query", "kb_name")
|
|
36
|
+
|
|
37
|
+
# Prompt
|
|
38
|
+
pm = get_prompt_manager()
|
|
39
|
+
prompts = pm.load_prompts("guide", "tutor_agent")
|
|
40
|
+
|
|
41
|
+
# Search
|
|
42
|
+
result = web_search("What is AI?")
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
from . import config, embedding, llm, prompt, rag, search, setup, tts
|
|
46
|
+
|
|
47
|
+
__all__ = [
|
|
48
|
+
"llm",
|
|
49
|
+
"embedding",
|
|
50
|
+
"rag",
|
|
51
|
+
"prompt",
|
|
52
|
+
"tts",
|
|
53
|
+
"search",
|
|
54
|
+
"setup",
|
|
55
|
+
"config",
|
|
56
|
+
]
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configuration Service
|
|
3
|
+
=====================
|
|
4
|
+
|
|
5
|
+
Provides three types of configuration:
|
|
6
|
+
|
|
7
|
+
1. **YAML Configuration (loader.py)** - For application settings from config/*.yaml
|
|
8
|
+
- PROJECT_ROOT, load_config_with_main, get_path_from_config, parse_language, get_agent_params
|
|
9
|
+
|
|
10
|
+
2. **Unified Config Service (unified_config.py)** - For service configurations (LLM, Embedding, TTS, Search)
|
|
11
|
+
- ConfigType, UnifiedConfigManager, get_config_manager
|
|
12
|
+
- get_active_llm_config, get_active_embedding_config, get_active_tts_config, get_active_search_config
|
|
13
|
+
|
|
14
|
+
3. **Knowledge Base Config Service (knowledge_base_config.py)** - For KB-specific settings
|
|
15
|
+
- KnowledgeBaseConfigService, get_kb_config_service
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
# Re-export everything from loader.py (existing functionality)
|
|
19
|
+
# Export knowledge base config service
|
|
20
|
+
from .knowledge_base_config import (
|
|
21
|
+
KnowledgeBaseConfigService,
|
|
22
|
+
get_kb_config_service,
|
|
23
|
+
)
|
|
24
|
+
from .loader import (
|
|
25
|
+
PROJECT_ROOT,
|
|
26
|
+
get_agent_params,
|
|
27
|
+
get_path_from_config,
|
|
28
|
+
load_config_with_main,
|
|
29
|
+
parse_language,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# Export new unified config service
|
|
33
|
+
from .unified_config import (
|
|
34
|
+
ConfigType,
|
|
35
|
+
UnifiedConfigManager,
|
|
36
|
+
get_active_embedding_config,
|
|
37
|
+
get_active_llm_config,
|
|
38
|
+
get_active_search_config,
|
|
39
|
+
get_active_tts_config,
|
|
40
|
+
get_config_manager,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
__all__ = [
|
|
44
|
+
# From loader.py
|
|
45
|
+
"PROJECT_ROOT",
|
|
46
|
+
"load_config_with_main",
|
|
47
|
+
"get_path_from_config",
|
|
48
|
+
"parse_language",
|
|
49
|
+
"get_agent_params",
|
|
50
|
+
# From unified_config.py
|
|
51
|
+
"ConfigType",
|
|
52
|
+
"UnifiedConfigManager",
|
|
53
|
+
"get_config_manager",
|
|
54
|
+
"get_active_llm_config",
|
|
55
|
+
"get_active_embedding_config",
|
|
56
|
+
"get_active_tts_config",
|
|
57
|
+
"get_active_search_config",
|
|
58
|
+
# From knowledge_base_config.py
|
|
59
|
+
"KnowledgeBaseConfigService",
|
|
60
|
+
"get_kb_config_service",
|
|
61
|
+
]
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Knowledge Base Configuration Service
|
|
3
|
+
=====================================
|
|
4
|
+
|
|
5
|
+
Centralized configuration management for knowledge bases.
|
|
6
|
+
Stores KB-specific settings like RAG provider, search mode, etc.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any, Dict, Optional
|
|
12
|
+
|
|
13
|
+
from src.logging import get_logger
|
|
14
|
+
|
|
15
|
+
logger = get_logger("KBConfigService")
|
|
16
|
+
|
|
17
|
+
# Default config file path
|
|
18
|
+
DEFAULT_CONFIG_PATH = (
|
|
19
|
+
Path(__file__).parent.parent.parent.parent
|
|
20
|
+
/ "data"
|
|
21
|
+
/ "user"
|
|
22
|
+
/ "settings"
|
|
23
|
+
/ "knowledge_base_configs.json"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class KnowledgeBaseConfigService:
|
|
28
|
+
"""
|
|
29
|
+
Service for managing knowledge base configurations.
|
|
30
|
+
|
|
31
|
+
Provides a centralized way to store and retrieve KB-specific settings,
|
|
32
|
+
separate from the per-KB metadata.json files.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
_instance: Optional["KnowledgeBaseConfigService"] = None
|
|
36
|
+
|
|
37
|
+
def __init__(self, config_path: Optional[Path] = None):
|
|
38
|
+
self.config_path = config_path or DEFAULT_CONFIG_PATH
|
|
39
|
+
self._config: Dict[str, Any] = self._load_config()
|
|
40
|
+
|
|
41
|
+
@classmethod
|
|
42
|
+
def get_instance(cls, config_path: Optional[Path] = None) -> "KnowledgeBaseConfigService":
|
|
43
|
+
"""Get singleton instance."""
|
|
44
|
+
if cls._instance is None:
|
|
45
|
+
cls._instance = cls(config_path)
|
|
46
|
+
return cls._instance
|
|
47
|
+
|
|
48
|
+
def _load_config(self) -> Dict[str, Any]:
|
|
49
|
+
"""Load configuration from file."""
|
|
50
|
+
if self.config_path.exists():
|
|
51
|
+
try:
|
|
52
|
+
with open(self.config_path, "r", encoding="utf-8") as f:
|
|
53
|
+
return json.load(f)
|
|
54
|
+
except Exception as e:
|
|
55
|
+
logger.warning(f"Failed to load KB config: {e}")
|
|
56
|
+
|
|
57
|
+
# Return default config
|
|
58
|
+
return {
|
|
59
|
+
"configs": {},
|
|
60
|
+
"default_kb": None,
|
|
61
|
+
"global_defaults": {"rag_provider": "llamaindex", "search_mode": "hybrid"},
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
def _save_config(self):
|
|
65
|
+
"""Save configuration to file."""
|
|
66
|
+
try:
|
|
67
|
+
self.config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
68
|
+
with open(self.config_path, "w", encoding="utf-8") as f:
|
|
69
|
+
json.dump(self._config, f, indent=2, ensure_ascii=False)
|
|
70
|
+
except Exception as e:
|
|
71
|
+
logger.error(f"Failed to save KB config: {e}")
|
|
72
|
+
|
|
73
|
+
def get_kb_config(self, kb_name: str) -> Dict[str, Any]:
|
|
74
|
+
"""
|
|
75
|
+
Get configuration for a specific knowledge base.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
kb_name: Knowledge base name
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
KB configuration dict with defaults applied
|
|
82
|
+
"""
|
|
83
|
+
kb_config = self._config.get("configs", {}).get(kb_name, {})
|
|
84
|
+
defaults = self._config.get("global_defaults", {})
|
|
85
|
+
|
|
86
|
+
# Merge with defaults
|
|
87
|
+
return {
|
|
88
|
+
"rag_provider": kb_config.get("rag_provider")
|
|
89
|
+
or defaults.get("rag_provider", "llamaindex"),
|
|
90
|
+
"search_mode": kb_config.get("search_mode") or defaults.get("search_mode", "hybrid"),
|
|
91
|
+
**kb_config,
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
def set_kb_config(self, kb_name: str, config: Dict[str, Any]):
|
|
95
|
+
"""
|
|
96
|
+
Set configuration for a specific knowledge base.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
kb_name: Knowledge base name
|
|
100
|
+
config: Configuration dict
|
|
101
|
+
"""
|
|
102
|
+
if "configs" not in self._config:
|
|
103
|
+
self._config["configs"] = {}
|
|
104
|
+
|
|
105
|
+
# Merge with existing config
|
|
106
|
+
existing = self._config["configs"].get(kb_name, {})
|
|
107
|
+
existing.update(config)
|
|
108
|
+
self._config["configs"][kb_name] = existing
|
|
109
|
+
|
|
110
|
+
self._save_config()
|
|
111
|
+
logger.info(f"Updated config for KB '{kb_name}': {config}")
|
|
112
|
+
|
|
113
|
+
def get_rag_provider(self, kb_name: str) -> str:
|
|
114
|
+
"""Get RAG provider for a knowledge base."""
|
|
115
|
+
return self.get_kb_config(kb_name).get("rag_provider", "llamaindex")
|
|
116
|
+
|
|
117
|
+
def set_rag_provider(self, kb_name: str, provider: str):
|
|
118
|
+
"""Set RAG provider for a knowledge base."""
|
|
119
|
+
self.set_kb_config(kb_name, {"rag_provider": provider})
|
|
120
|
+
|
|
121
|
+
def get_search_mode(self, kb_name: str) -> str:
|
|
122
|
+
"""Get search mode for a knowledge base."""
|
|
123
|
+
return self.get_kb_config(kb_name).get("search_mode", "hybrid")
|
|
124
|
+
|
|
125
|
+
def set_search_mode(self, kb_name: str, mode: str):
|
|
126
|
+
"""Set search mode for a knowledge base."""
|
|
127
|
+
self.set_kb_config(kb_name, {"search_mode": mode})
|
|
128
|
+
|
|
129
|
+
def delete_kb_config(self, kb_name: str):
|
|
130
|
+
"""Delete configuration for a knowledge base."""
|
|
131
|
+
if "configs" in self._config and kb_name in self._config["configs"]:
|
|
132
|
+
del self._config["configs"][kb_name]
|
|
133
|
+
self._save_config()
|
|
134
|
+
logger.info(f"Deleted config for KB '{kb_name}'")
|
|
135
|
+
|
|
136
|
+
def get_all_configs(self) -> Dict[str, Any]:
|
|
137
|
+
"""Get all knowledge base configurations."""
|
|
138
|
+
return self._config
|
|
139
|
+
|
|
140
|
+
def set_global_defaults(self, defaults: Dict[str, Any]):
|
|
141
|
+
"""Set global default values."""
|
|
142
|
+
if "global_defaults" not in self._config:
|
|
143
|
+
self._config["global_defaults"] = {}
|
|
144
|
+
|
|
145
|
+
self._config["global_defaults"].update(defaults)
|
|
146
|
+
self._save_config()
|
|
147
|
+
logger.info(f"Updated global defaults: {defaults}")
|
|
148
|
+
|
|
149
|
+
def set_default_kb(self, kb_name: Optional[str]):
|
|
150
|
+
"""Set the default knowledge base."""
|
|
151
|
+
self._config["default_kb"] = kb_name
|
|
152
|
+
self._save_config()
|
|
153
|
+
logger.info(f"Set default KB: {kb_name}")
|
|
154
|
+
|
|
155
|
+
def get_default_kb(self) -> Optional[str]:
|
|
156
|
+
"""Get the default knowledge base name."""
|
|
157
|
+
return self._config.get("default_kb")
|
|
158
|
+
|
|
159
|
+
def sync_from_metadata(self, kb_name: str, kb_base_dir: Path):
|
|
160
|
+
"""
|
|
161
|
+
Sync configuration from a KB's metadata.json file.
|
|
162
|
+
|
|
163
|
+
Useful for migrating existing KBs to the centralized config.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
kb_name: Knowledge base name
|
|
167
|
+
kb_base_dir: Base directory for knowledge bases
|
|
168
|
+
"""
|
|
169
|
+
metadata_file = kb_base_dir / kb_name / "metadata.json"
|
|
170
|
+
|
|
171
|
+
if not metadata_file.exists():
|
|
172
|
+
return
|
|
173
|
+
|
|
174
|
+
try:
|
|
175
|
+
with open(metadata_file, "r", encoding="utf-8") as f:
|
|
176
|
+
metadata = json.load(f)
|
|
177
|
+
|
|
178
|
+
# Extract relevant config from metadata
|
|
179
|
+
config = {}
|
|
180
|
+
if "rag_provider" in metadata and metadata["rag_provider"]:
|
|
181
|
+
config["rag_provider"] = metadata["rag_provider"]
|
|
182
|
+
|
|
183
|
+
if config:
|
|
184
|
+
self.set_kb_config(kb_name, config)
|
|
185
|
+
logger.info(f"Synced config for KB '{kb_name}' from metadata.json")
|
|
186
|
+
|
|
187
|
+
except Exception as e:
|
|
188
|
+
logger.warning(f"Failed to sync config from metadata for '{kb_name}': {e}")
|
|
189
|
+
|
|
190
|
+
def sync_all_from_metadata(self, kb_base_dir: Path):
|
|
191
|
+
"""
|
|
192
|
+
Sync configurations from all KBs' metadata.json files.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
kb_base_dir: Base directory for knowledge bases
|
|
196
|
+
"""
|
|
197
|
+
if not kb_base_dir.exists():
|
|
198
|
+
return
|
|
199
|
+
|
|
200
|
+
for kb_dir in kb_base_dir.iterdir():
|
|
201
|
+
if kb_dir.is_dir() and kb_dir.name != "__pycache__":
|
|
202
|
+
metadata_file = kb_dir / "metadata.json"
|
|
203
|
+
if metadata_file.exists():
|
|
204
|
+
self.sync_from_metadata(kb_dir.name, kb_base_dir)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
# Convenience function
|
|
208
|
+
def get_kb_config_service() -> KnowledgeBaseConfigService:
|
|
209
|
+
"""Get the knowledge base config service instance."""
|
|
210
|
+
return KnowledgeBaseConfigService.get_instance()
|