realtimex-deeptutor 0.5.0.post1__py3-none-any.whl → 0.5.0.post3__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.
Files changed (145) hide show
  1. {realtimex_deeptutor-0.5.0.post1.dist-info → realtimex_deeptutor-0.5.0.post3.dist-info}/METADATA +24 -17
  2. {realtimex_deeptutor-0.5.0.post1.dist-info → realtimex_deeptutor-0.5.0.post3.dist-info}/RECORD +143 -123
  3. {realtimex_deeptutor-0.5.0.post1.dist-info → realtimex_deeptutor-0.5.0.post3.dist-info}/WHEEL +1 -1
  4. realtimex_deeptutor-0.5.0.post3.dist-info/entry_points.txt +4 -0
  5. {realtimex_deeptutor-0.5.0.post1.dist-info → realtimex_deeptutor-0.5.0.post3.dist-info}/top_level.txt +1 -0
  6. scripts/__init__.py +1 -0
  7. scripts/audit_prompts.py +179 -0
  8. scripts/check_install.py +460 -0
  9. scripts/generate_roster.py +327 -0
  10. scripts/install_all.py +653 -0
  11. scripts/migrate_kb.py +655 -0
  12. scripts/start.py +807 -0
  13. scripts/start_web.py +632 -0
  14. scripts/sync_prompts_from_en.py +147 -0
  15. src/__init__.py +2 -2
  16. src/agents/ideagen/material_organizer_agent.py +2 -0
  17. src/agents/solve/__init__.py +6 -0
  18. src/agents/solve/main_solver.py +9 -0
  19. src/agents/solve/prompts/zh/analysis_loop/investigate_agent.yaml +9 -7
  20. src/agents/solve/session_manager.py +345 -0
  21. src/api/main.py +14 -0
  22. src/api/routers/chat.py +3 -3
  23. src/api/routers/co_writer.py +12 -7
  24. src/api/routers/config.py +1 -0
  25. src/api/routers/guide.py +3 -1
  26. src/api/routers/ideagen.py +7 -0
  27. src/api/routers/knowledge.py +64 -12
  28. src/api/routers/question.py +2 -0
  29. src/api/routers/realtimex.py +137 -0
  30. src/api/routers/research.py +9 -0
  31. src/api/routers/solve.py +120 -2
  32. src/cli/__init__.py +13 -0
  33. src/cli/start.py +209 -0
  34. src/config/constants.py +11 -9
  35. src/knowledge/add_documents.py +453 -213
  36. src/knowledge/extract_numbered_items.py +9 -10
  37. src/knowledge/initializer.py +102 -101
  38. src/knowledge/manager.py +251 -74
  39. src/knowledge/progress_tracker.py +43 -2
  40. src/knowledge/start_kb.py +11 -2
  41. src/logging/__init__.py +5 -0
  42. src/logging/adapters/__init__.py +1 -0
  43. src/logging/adapters/lightrag.py +25 -18
  44. src/logging/adapters/llamaindex.py +1 -0
  45. src/logging/config.py +30 -27
  46. src/logging/handlers/__init__.py +1 -0
  47. src/logging/handlers/console.py +7 -50
  48. src/logging/handlers/file.py +5 -20
  49. src/logging/handlers/websocket.py +23 -19
  50. src/logging/logger.py +161 -126
  51. src/logging/stats/__init__.py +1 -0
  52. src/logging/stats/llm_stats.py +37 -17
  53. src/services/__init__.py +17 -1
  54. src/services/config/__init__.py +1 -0
  55. src/services/config/knowledge_base_config.py +1 -0
  56. src/services/config/loader.py +1 -1
  57. src/services/config/unified_config.py +211 -4
  58. src/services/embedding/__init__.py +1 -0
  59. src/services/embedding/adapters/__init__.py +3 -0
  60. src/services/embedding/adapters/base.py +1 -0
  61. src/services/embedding/adapters/cohere.py +1 -0
  62. src/services/embedding/adapters/jina.py +1 -0
  63. src/services/embedding/adapters/ollama.py +1 -0
  64. src/services/embedding/adapters/openai_compatible.py +1 -0
  65. src/services/embedding/adapters/realtimex.py +125 -0
  66. src/services/embedding/client.py +27 -0
  67. src/services/embedding/config.py +3 -0
  68. src/services/embedding/provider.py +1 -0
  69. src/services/llm/__init__.py +17 -3
  70. src/services/llm/capabilities.py +47 -0
  71. src/services/llm/client.py +32 -0
  72. src/services/llm/cloud_provider.py +21 -4
  73. src/services/llm/config.py +36 -2
  74. src/services/llm/error_mapping.py +1 -0
  75. src/services/llm/exceptions.py +30 -0
  76. src/services/llm/factory.py +55 -16
  77. src/services/llm/local_provider.py +1 -0
  78. src/services/llm/providers/anthropic.py +1 -0
  79. src/services/llm/providers/base_provider.py +1 -0
  80. src/services/llm/providers/open_ai.py +1 -0
  81. src/services/llm/realtimex_provider.py +240 -0
  82. src/services/llm/registry.py +1 -0
  83. src/services/llm/telemetry.py +1 -0
  84. src/services/llm/types.py +1 -0
  85. src/services/llm/utils.py +1 -0
  86. src/services/prompt/__init__.py +1 -0
  87. src/services/prompt/manager.py +3 -2
  88. src/services/rag/__init__.py +27 -5
  89. src/services/rag/components/__init__.py +1 -0
  90. src/services/rag/components/base.py +1 -0
  91. src/services/rag/components/chunkers/__init__.py +1 -0
  92. src/services/rag/components/chunkers/base.py +1 -0
  93. src/services/rag/components/chunkers/fixed.py +1 -0
  94. src/services/rag/components/chunkers/numbered_item.py +1 -0
  95. src/services/rag/components/chunkers/semantic.py +1 -0
  96. src/services/rag/components/embedders/__init__.py +1 -0
  97. src/services/rag/components/embedders/base.py +1 -0
  98. src/services/rag/components/embedders/openai.py +1 -0
  99. src/services/rag/components/indexers/__init__.py +1 -0
  100. src/services/rag/components/indexers/base.py +1 -0
  101. src/services/rag/components/indexers/graph.py +5 -44
  102. src/services/rag/components/indexers/lightrag.py +5 -44
  103. src/services/rag/components/indexers/vector.py +1 -0
  104. src/services/rag/components/parsers/__init__.py +1 -0
  105. src/services/rag/components/parsers/base.py +1 -0
  106. src/services/rag/components/parsers/markdown.py +1 -0
  107. src/services/rag/components/parsers/pdf.py +1 -0
  108. src/services/rag/components/parsers/text.py +1 -0
  109. src/services/rag/components/retrievers/__init__.py +1 -0
  110. src/services/rag/components/retrievers/base.py +1 -0
  111. src/services/rag/components/retrievers/dense.py +1 -0
  112. src/services/rag/components/retrievers/hybrid.py +5 -44
  113. src/services/rag/components/retrievers/lightrag.py +5 -44
  114. src/services/rag/components/routing.py +48 -0
  115. src/services/rag/factory.py +112 -46
  116. src/services/rag/pipeline.py +1 -0
  117. src/services/rag/pipelines/__init__.py +27 -18
  118. src/services/rag/pipelines/lightrag.py +1 -0
  119. src/services/rag/pipelines/llamaindex.py +99 -0
  120. src/services/rag/pipelines/raganything.py +67 -100
  121. src/services/rag/pipelines/raganything_docling.py +368 -0
  122. src/services/rag/service.py +5 -12
  123. src/services/rag/types.py +1 -0
  124. src/services/rag/utils/__init__.py +17 -0
  125. src/services/rag/utils/image_migration.py +279 -0
  126. src/services/search/__init__.py +1 -0
  127. src/services/search/base.py +1 -0
  128. src/services/search/consolidation.py +1 -0
  129. src/services/search/providers/__init__.py +1 -0
  130. src/services/search/providers/baidu.py +1 -0
  131. src/services/search/providers/exa.py +1 -0
  132. src/services/search/providers/jina.py +1 -0
  133. src/services/search/providers/perplexity.py +1 -0
  134. src/services/search/providers/serper.py +1 -0
  135. src/services/search/providers/tavily.py +1 -0
  136. src/services/search/types.py +1 -0
  137. src/services/settings/__init__.py +1 -0
  138. src/services/settings/interface_settings.py +78 -0
  139. src/services/setup/__init__.py +1 -0
  140. src/services/tts/__init__.py +1 -0
  141. src/services/tts/config.py +1 -0
  142. src/utils/realtimex.py +284 -0
  143. realtimex_deeptutor-0.5.0.post1.dist-info/entry_points.txt +0 -2
  144. src/services/rag/pipelines/academic.py +0 -44
  145. {realtimex_deeptutor-0.5.0.post1.dist-info → realtimex_deeptutor-0.5.0.post3.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,240 @@
1
+ """
2
+ RealTimeX LLM Provider
3
+ ======================
4
+
5
+ Provides LLM capabilities through RealTimeX SDK proxy.
6
+ Used when running as a local app within RealTimeX desktop.
7
+
8
+ This provider maps DeepTutor's LLM API to the RealTimeX SDK's ChatMessage format
9
+ and handles error translation from SDK exceptions to DeepTutor exception hierarchy.
10
+ """
11
+
12
+ from typing import TYPE_CHECKING, AsyncGenerator, Dict, List, Optional
13
+
14
+ from src.logging import get_logger
15
+ from src.utils.realtimex import get_realtimex_sdk
16
+
17
+ from .exceptions import LLMAPIError, LLMRateLimitError, RealTimeXError, RealTimeXPermissionError
18
+
19
+ if TYPE_CHECKING:
20
+ from realtimex_sdk import ChatMessage
21
+
22
+ logger = get_logger("RealTimeXProvider")
23
+
24
+
25
+ def _build_messages(
26
+ prompt: str,
27
+ system_prompt: str = "You are a helpful assistant.",
28
+ messages: Optional[List[Dict[str, str]]] = None,
29
+ ) -> List["ChatMessage"]:
30
+ """
31
+ Build ChatMessage array from DeepTutor's API parameters.
32
+
33
+ Args:
34
+ prompt: User message
35
+ system_prompt: System role instruction
36
+ messages: Optional pre-built messages array
37
+
38
+ Returns:
39
+ List[ChatMessage]: Messages formatted for SDK
40
+ """
41
+ from realtimex_sdk import ChatMessage
42
+
43
+ if messages:
44
+ # Convert dict format to ChatMessage objects
45
+ return [ChatMessage(role=m["role"], content=m["content"]) for m in messages]
46
+ else:
47
+ # Build from prompt and system_prompt
48
+ return [
49
+ ChatMessage(role="system", content=system_prompt),
50
+ ChatMessage(role="user", content=prompt),
51
+ ]
52
+
53
+
54
+ def _map_sdk_error(e: Exception) -> Exception:
55
+ """
56
+ Map SDK exceptions to DeepTutor exception hierarchy.
57
+
58
+ Args:
59
+ e: SDK exception
60
+
61
+ Returns:
62
+ Exception: Mapped DeepTutor exception
63
+ """
64
+ try:
65
+ from realtimex_sdk import LLMPermissionError, LLMProviderError
66
+
67
+ if isinstance(e, LLMPermissionError):
68
+ return RealTimeXPermissionError(
69
+ permission=e.permission, message=f"RealTimeX permission required: {e.permission}"
70
+ )
71
+
72
+ if isinstance(e, LLMProviderError):
73
+ if e.code == "RATE_LIMIT":
74
+ return LLMRateLimitError(str(e), provider="realtimex")
75
+ if e.code in ("LLM_STREAM_ERROR", "LLM_ERROR"):
76
+ return RealTimeXError(str(e), error_code=e.code)
77
+
78
+ # Generic provider error
79
+ return RealTimeXError(str(e), error_code=e.code)
80
+
81
+ except ImportError:
82
+ pass
83
+
84
+ # Fallback for unknown errors
85
+ return LLMAPIError(f"RealTimeX SDK error: {str(e)}", provider="realtimex")
86
+
87
+
88
+ async def complete(
89
+ prompt: str,
90
+ system_prompt: str = "You are a helpful assistant.",
91
+ model: Optional[str] = None,
92
+ temperature: float = 0.7,
93
+ max_tokens: int = 1000,
94
+ messages: Optional[List[Dict[str, str]]] = None,
95
+ response_format: Optional[Dict[str, str]] = None,
96
+ **kwargs,
97
+ ) -> str:
98
+ """
99
+ Complete request via RealTimeX SDK.
100
+
101
+ Maps DeepTutor's API to SDK's ChatMessage format and handles error translation.
102
+
103
+ Args:
104
+ prompt: User message
105
+ system_prompt: System role instruction (default: "You are a helpful assistant.")
106
+ model: Optional model override
107
+ temperature: Sampling temperature (0.0-2.0)
108
+ max_tokens: Maximum tokens to generate
109
+ messages: Optional pre-built messages array
110
+ response_format: Optional response format config (e.g., {"type": "json_object"})
111
+ **kwargs: Additional parameters (ignored for now)
112
+
113
+ Returns:
114
+ str: Generated completion text
115
+
116
+ Raises:
117
+ RealTimeXPermissionError: If permission is required/denied
118
+ RealTimeXError: If SDK request fails
119
+ LLMRateLimitError: If rate limited
120
+ """
121
+ from realtimex_sdk import ChatOptions
122
+
123
+ sdk = get_realtimex_sdk()
124
+
125
+ # Build messages array
126
+ chat_messages = _build_messages(prompt, system_prompt, messages)
127
+
128
+ # Build options
129
+ options = ChatOptions(
130
+ model=model,
131
+ temperature=temperature,
132
+ max_tokens=max_tokens,
133
+ response_format=response_format, # Pass through to SDK
134
+ )
135
+
136
+ # Log request
137
+ logger.debug(
138
+ f"RealTimeX complete: model={model or 'default'}, "
139
+ f"temp={temperature}, max_tokens={max_tokens}, "
140
+ f"messages_count={len(chat_messages)}, "
141
+ f"response_format={response_format}"
142
+ )
143
+
144
+ try:
145
+ response = await sdk.llm.chat(chat_messages, options)
146
+
147
+ if not response.success:
148
+ logger.error(f"RealTimeX request failed: {response.error}")
149
+ raise RealTimeXError(response.error or "SDK request failed", error_code=response.code)
150
+
151
+ # Log response metadata
152
+ logger.debug(
153
+ f"RealTimeX response: success={response.success}, "
154
+ f"provider={response.provider}, model={response.model}"
155
+ )
156
+
157
+ if response.metrics:
158
+ logger.debug(
159
+ f"Tokens: {response.metrics.total_tokens} "
160
+ f"(prompt={response.metrics.prompt_tokens}, "
161
+ f"completion={response.metrics.completion_tokens})"
162
+ )
163
+
164
+ return response.content or ""
165
+
166
+ except Exception as e:
167
+ # Map SDK errors to DeepTutor exceptions
168
+ mapped_error = _map_sdk_error(e)
169
+ logger.error(f"RealTimeX error: {mapped_error}")
170
+ raise mapped_error
171
+
172
+
173
+ async def stream(
174
+ prompt: str,
175
+ system_prompt: str = "You are a helpful assistant.",
176
+ model: Optional[str] = None,
177
+ temperature: float = 0.7,
178
+ max_tokens: int = 1000,
179
+ messages: Optional[List[Dict[str, str]]] = None,
180
+ response_format: Optional[Dict[str, str]] = None,
181
+ **kwargs,
182
+ ) -> AsyncGenerator[str, None]:
183
+ """
184
+ Stream response via RealTimeX SDK.
185
+
186
+ Args:
187
+ prompt: User message
188
+ system_prompt: System role instruction
189
+ model: Optional model override
190
+ temperature: Sampling temperature (0.0-2.0)
191
+ max_tokens: Maximum tokens to generate
192
+ messages: Optional pre-built messages array
193
+ response_format: Optional response format config
194
+ **kwargs: Additional parameters (ignored)
195
+
196
+ Yields:
197
+ str: Text chunks as they arrive
198
+
199
+ Raises:
200
+ RealTimeXPermissionError: If permission is required/denied
201
+ RealTimeXError: If SDK request fails
202
+ LLMRateLimitError: If rate limited
203
+ """
204
+ from realtimex_sdk import ChatOptions
205
+
206
+ sdk = get_realtimex_sdk()
207
+
208
+ # Build messages array
209
+ chat_messages = _build_messages(prompt, system_prompt, messages)
210
+
211
+ # Build options
212
+ options = ChatOptions(
213
+ model=model,
214
+ temperature=temperature,
215
+ max_tokens=max_tokens,
216
+ response_format=response_format, # Pass through to SDK
217
+ )
218
+
219
+ # Log request
220
+ logger.debug(
221
+ f"RealTimeX stream: model={model or 'default'}, "
222
+ f"temp={temperature}, max_tokens={max_tokens}, "
223
+ f"messages_count={len(chat_messages)}, "
224
+ f"response_format={response_format}"
225
+ )
226
+
227
+ try:
228
+ async for chunk in sdk.llm.chat_stream(chat_messages, options):
229
+ if chunk.error:
230
+ logger.error("RealTimeX stream error detected")
231
+ raise RealTimeXError("Stream error", error_code="LLM_STREAM_ERROR")
232
+
233
+ if chunk.text:
234
+ yield chunk.text
235
+
236
+ except Exception as e:
237
+ # Map SDK errors to DeepTutor exceptions
238
+ mapped_error = _map_sdk_error(e)
239
+ logger.error(f"RealTimeX stream error: {mapped_error}")
240
+ raise mapped_error
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  """
2
3
  LLM Provider Registry
3
4
  ====================
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  """
2
3
  LLM Telemetry
3
4
  =============
src/services/llm/types.py CHANGED
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  from typing import Any, AsyncGenerator, Dict, Optional
2
3
 
3
4
  from pydantic import BaseModel, Field
src/services/llm/utils.py CHANGED
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  """
2
3
  LLM Utilities
3
4
  =============
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  """
2
3
  Prompt Service
3
4
  ==============
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
2
3
  """
3
4
  Unified Prompt Manager - Single source of truth for all prompt loading.
4
5
  Supports multi-language, caching, and language fallbacks.
@@ -20,8 +21,8 @@ class PromptManager:
20
21
 
21
22
  # Language fallback chain: if primary language not found, try alternatives
22
23
  LANGUAGE_FALLBACKS = {
23
- "zh": ["zh", "en"],
24
- "en": ["en", "zh"],
24
+ "zh": ["zh", "cn", "en"],
25
+ "en": ["en", "zh", "cn"],
25
26
  }
26
27
 
27
28
  # Supported modules
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  """
2
3
  RAG Service
3
4
  ===========
@@ -7,7 +8,7 @@ Unified RAG pipeline service for DeepTutor.
7
8
  Provides:
8
9
  - RAGService: Unified entry point for all RAG operations
9
10
  - Composable RAG pipelines
10
- - Pre-configured pipelines (RAGAnything, LightRAG, LlamaIndex, Academic)
11
+ - Pre-configured pipelines (RAGAnything, LightRAG, LlamaIndex)
11
12
  - Modular components (parsers, chunkers, embedders, indexers, retrievers)
12
13
  - Factory for pipeline creation
13
14
 
@@ -39,12 +40,33 @@ Usage:
39
40
 
40
41
  from .factory import get_pipeline, has_pipeline, list_pipelines, register_pipeline
41
42
  from .pipeline import RAGPipeline
42
-
43
- # Import pipeline classes for convenience
44
- from .pipelines.raganything import RAGAnythingPipeline
45
43
  from .service import RAGService
46
44
  from .types import Chunk, Document, SearchResult
47
45
 
46
+
47
+ # Lazy import for RAGAnythingPipeline to avoid importing heavy dependencies at module load time
48
+ def __getattr__(name: str):
49
+ """Lazy import for pipeline classes that depend on heavy libraries."""
50
+ if name == "RAGAnythingPipeline":
51
+ from .pipelines.raganything import RAGAnythingPipeline
52
+
53
+ return RAGAnythingPipeline
54
+ if name == "RAGAnythingDoclingPipeline":
55
+ from .pipelines.raganything_docling import RAGAnythingDoclingPipeline
56
+
57
+ return RAGAnythingDoclingPipeline
58
+ if name == "LlamaIndexPipeline":
59
+ # Optional dependency: llama_index
60
+ from .pipelines.llamaindex import LlamaIndexPipeline
61
+
62
+ return LlamaIndexPipeline
63
+ if name == "LightRAGPipeline":
64
+ from .pipelines.lightrag import LightRAGPipeline
65
+
66
+ return LightRAGPipeline
67
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
68
+
69
+
48
70
  __all__ = [
49
71
  # Service (recommended entry point)
50
72
  "RAGService",
@@ -59,6 +81,6 @@ __all__ = [
59
81
  "list_pipelines",
60
82
  "register_pipeline",
61
83
  "has_pipeline",
62
- # Pipeline implementations
84
+ # Pipeline implementations (lazy loaded)
63
85
  "RAGAnythingPipeline",
64
86
  ]
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  """
2
3
  RAG Components
3
4
  ==============
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  """
2
3
  Base Component
3
4
  ==============
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  """
2
3
  Document Chunkers
3
4
  =================
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  """
2
3
  Base Chunker
3
4
  ============
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  """
2
3
  Fixed Size Chunker
3
4
  ==================
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  """
2
3
  Numbered Item Extractor
3
4
  =======================
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  """
2
3
  Semantic Chunker
3
4
  ================
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  """
2
3
  Document Embedders
3
4
  ==================
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  """
2
3
  Base Embedder
3
4
  =============
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  """
2
3
  OpenAI Embedder
3
4
  ===============
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  """
2
3
  Document Indexers
3
4
  =================
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  """
2
3
  Base Indexer
3
4
  ============
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  """
2
3
  Graph Indexer
3
4
  =============
@@ -51,58 +52,18 @@ class GraphIndexer(BaseComponent):
51
52
  sys.path.insert(0, str(raganything_path))
52
53
 
53
54
  try:
54
- from openai import AsyncOpenAI
55
55
  from raganything import RAGAnything, RAGAnythingConfig
56
56
 
57
57
  from src.services.embedding import get_embedding_client
58
58
  from src.services.llm import get_llm_client
59
59
 
60
+ # Use unified LLM client from src/services/llm
60
61
  llm_client = get_llm_client()
61
62
  embed_client = get_embedding_client()
62
63
 
63
- # Create AsyncOpenAI client directly
64
- openai_client = AsyncOpenAI(
65
- api_key=llm_client.config.api_key,
66
- base_url=llm_client.config.base_url,
67
- )
68
-
69
- # LLM function using services (ASYNC - LightRAG expects async functions)
70
- async def llm_model_func(prompt, system_prompt=None, history_messages=None, **kwargs):
71
- """Custom async LLM function that bypasses LightRAG's openai_complete_if_cache."""
72
- if history_messages is None:
73
- history_messages = []
74
-
75
- # Build messages
76
- messages = []
77
- if system_prompt:
78
- messages.append({"role": "system", "content": system_prompt})
79
- messages.extend(history_messages)
80
- messages.append({"role": "user", "content": prompt})
81
-
82
- # Whitelist only valid OpenAI parameters
83
- valid_params = {
84
- "temperature",
85
- "top_p",
86
- "n",
87
- "stream",
88
- "stop",
89
- "max_tokens",
90
- "presence_penalty",
91
- "frequency_penalty",
92
- "logit_bias",
93
- "user",
94
- "seed",
95
- }
96
- clean_kwargs = {k: v for k, v in kwargs.items() if k in valid_params}
97
-
98
- # Call OpenAI API directly (async)
99
- response = await openai_client.chat.completions.create(
100
- model=llm_client.config.model,
101
- messages=messages,
102
- **clean_kwargs,
103
- )
104
-
105
- return response.choices[0].message.content
64
+ # Get model function from unified LLM client
65
+ # This handles all provider differences and env var setup for LightRAG
66
+ llm_model_func = llm_client.get_model_func()
106
67
 
107
68
  config = RAGAnythingConfig(
108
69
  working_dir=working_dir,
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  """
2
3
  LightRAG Indexer
3
4
  ================
@@ -53,57 +54,17 @@ class LightRAGIndexer(BaseComponent):
53
54
 
54
55
  try:
55
56
  from lightrag import LightRAG
56
- from openai import AsyncOpenAI
57
57
 
58
58
  from src.services.embedding import get_embedding_client
59
59
  from src.services.llm import get_llm_client
60
60
 
61
+ # Use unified LLM client from src/services/llm
61
62
  llm_client = get_llm_client()
62
63
  embed_client = get_embedding_client()
63
64
 
64
- # Create AsyncOpenAI client directly
65
- openai_client = AsyncOpenAI(
66
- api_key=llm_client.config.api_key,
67
- base_url=llm_client.config.base_url,
68
- )
69
-
70
- # LLM function using services (ASYNC - LightRAG expects async functions)
71
- async def llm_model_func(prompt, system_prompt=None, history_messages=None, **kwargs):
72
- """Custom async LLM function that bypasses LightRAG's openai_complete_if_cache."""
73
- if history_messages is None:
74
- history_messages = []
75
-
76
- # Build messages
77
- messages = []
78
- if system_prompt:
79
- messages.append({"role": "system", "content": system_prompt})
80
- messages.extend(history_messages)
81
- messages.append({"role": "user", "content": prompt})
82
-
83
- # Whitelist only valid OpenAI parameters
84
- valid_params = {
85
- "temperature",
86
- "top_p",
87
- "n",
88
- "stream",
89
- "stop",
90
- "max_tokens",
91
- "presence_penalty",
92
- "frequency_penalty",
93
- "logit_bias",
94
- "user",
95
- "seed",
96
- }
97
- clean_kwargs = {k: v for k, v in kwargs.items() if k in valid_params}
98
-
99
- # Call OpenAI API directly (async)
100
- response = await openai_client.chat.completions.create(
101
- model=llm_client.config.model,
102
- messages=messages,
103
- **clean_kwargs,
104
- )
105
-
106
- return response.choices[0].message.content
65
+ # Get model function from unified LLM client
66
+ # This handles all provider differences and env var setup for LightRAG
67
+ llm_model_func = llm_client.get_model_func()
107
68
 
108
69
  # Create pure LightRAG instance (no multimodal)
109
70
  rag = LightRAG(
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  """
2
3
  Vector Indexer
3
4
  ==============
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  """
2
3
  Document Parsers
3
4
  ================
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  """
2
3
  Base Parser
3
4
  ===========
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  """
2
3
  Markdown Parser
3
4
  ===============
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  """
2
3
  PDF Parser
3
4
  ==========
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  """
2
3
  Text Parser
3
4
  ===========
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  """
2
3
  Document Retrievers
3
4
  ===================
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  """
2
3
  Base Retriever
3
4
  ==============
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  """
2
3
  Dense Retriever
3
4
  ===============
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  """
2
3
  Hybrid Retriever
3
4
  ================
@@ -50,58 +51,18 @@ class HybridRetriever(BaseComponent):
50
51
  sys.path.insert(0, str(raganything_path))
51
52
 
52
53
  try:
53
- from openai import AsyncOpenAI
54
54
  from raganything import RAGAnything, RAGAnythingConfig
55
55
 
56
56
  from src.services.embedding import get_embedding_client
57
57
  from src.services.llm import get_llm_client
58
58
 
59
+ # Use unified LLM client from src/services/llm
59
60
  llm_client = get_llm_client()
60
61
  embed_client = get_embedding_client()
61
62
 
62
- # Create AsyncOpenAI client directly
63
- openai_client = AsyncOpenAI(
64
- api_key=llm_client.config.api_key,
65
- base_url=llm_client.config.base_url,
66
- )
67
-
68
- # LLM function using services (ASYNC - LightRAG expects async functions)
69
- async def llm_model_func(prompt, system_prompt=None, history_messages=None, **kwargs):
70
- """Custom async LLM function that bypasses LightRAG's openai_complete_if_cache."""
71
- if history_messages is None:
72
- history_messages = []
73
-
74
- # Build messages
75
- messages = []
76
- if system_prompt:
77
- messages.append({"role": "system", "content": system_prompt})
78
- messages.extend(history_messages)
79
- messages.append({"role": "user", "content": prompt})
80
-
81
- # Whitelist only valid OpenAI parameters
82
- valid_params = {
83
- "temperature",
84
- "top_p",
85
- "n",
86
- "stream",
87
- "stop",
88
- "max_tokens",
89
- "presence_penalty",
90
- "frequency_penalty",
91
- "logit_bias",
92
- "user",
93
- "seed",
94
- }
95
- clean_kwargs = {k: v for k, v in kwargs.items() if k in valid_params}
96
-
97
- # Call OpenAI API directly (async)
98
- response = await openai_client.chat.completions.create(
99
- model=llm_client.config.model,
100
- messages=messages,
101
- **clean_kwargs,
102
- )
103
-
104
- return response.choices[0].message.content
63
+ # Get model function from unified LLM client
64
+ # This handles all provider differences and env var setup for LightRAG
65
+ llm_model_func = llm_client.get_model_func()
105
66
 
106
67
  config = RAGAnythingConfig(
107
68
  working_dir=working_dir,