local-deep-research 0.1.26__py3-none-any.whl → 0.2.2__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.
- local_deep_research/__init__.py +23 -22
- local_deep_research/__main__.py +16 -0
- local_deep_research/advanced_search_system/__init__.py +7 -0
- local_deep_research/advanced_search_system/filters/__init__.py +8 -0
- local_deep_research/advanced_search_system/filters/base_filter.py +38 -0
- local_deep_research/advanced_search_system/filters/cross_engine_filter.py +200 -0
- local_deep_research/advanced_search_system/findings/base_findings.py +81 -0
- local_deep_research/advanced_search_system/findings/repository.py +452 -0
- local_deep_research/advanced_search_system/knowledge/__init__.py +1 -0
- local_deep_research/advanced_search_system/knowledge/base_knowledge.py +151 -0
- local_deep_research/advanced_search_system/knowledge/standard_knowledge.py +159 -0
- local_deep_research/advanced_search_system/questions/__init__.py +1 -0
- local_deep_research/advanced_search_system/questions/base_question.py +64 -0
- local_deep_research/advanced_search_system/questions/decomposition_question.py +445 -0
- local_deep_research/advanced_search_system/questions/standard_question.py +119 -0
- local_deep_research/advanced_search_system/repositories/__init__.py +7 -0
- local_deep_research/advanced_search_system/strategies/__init__.py +1 -0
- local_deep_research/advanced_search_system/strategies/base_strategy.py +118 -0
- local_deep_research/advanced_search_system/strategies/iterdrag_strategy.py +450 -0
- local_deep_research/advanced_search_system/strategies/parallel_search_strategy.py +312 -0
- local_deep_research/advanced_search_system/strategies/rapid_search_strategy.py +270 -0
- local_deep_research/advanced_search_system/strategies/standard_strategy.py +300 -0
- local_deep_research/advanced_search_system/tools/__init__.py +1 -0
- local_deep_research/advanced_search_system/tools/base_tool.py +100 -0
- local_deep_research/advanced_search_system/tools/knowledge_tools/__init__.py +1 -0
- local_deep_research/advanced_search_system/tools/question_tools/__init__.py +1 -0
- local_deep_research/advanced_search_system/tools/search_tools/__init__.py +1 -0
- local_deep_research/api/__init__.py +5 -5
- local_deep_research/api/research_functions.py +154 -160
- local_deep_research/app.py +8 -0
- local_deep_research/citation_handler.py +25 -16
- local_deep_research/{config.py → config/config_files.py} +102 -110
- local_deep_research/config/llm_config.py +472 -0
- local_deep_research/config/search_config.py +77 -0
- local_deep_research/defaults/__init__.py +10 -5
- local_deep_research/defaults/main.toml +2 -2
- local_deep_research/defaults/search_engines.toml +60 -34
- local_deep_research/main.py +121 -19
- local_deep_research/migrate_db.py +147 -0
- local_deep_research/report_generator.py +87 -45
- local_deep_research/search_system.py +153 -283
- local_deep_research/setup_data_dir.py +35 -0
- local_deep_research/test_migration.py +178 -0
- local_deep_research/utilities/__init__.py +0 -0
- local_deep_research/utilities/db_utils.py +49 -0
- local_deep_research/{utilties → utilities}/enums.py +2 -2
- local_deep_research/{utilties → utilities}/llm_utils.py +63 -29
- local_deep_research/utilities/search_utilities.py +242 -0
- local_deep_research/{utilties → utilities}/setup_utils.py +4 -2
- local_deep_research/web/__init__.py +0 -1
- local_deep_research/web/app.py +86 -1709
- local_deep_research/web/app_factory.py +289 -0
- local_deep_research/web/database/README.md +70 -0
- local_deep_research/web/database/migrate_to_ldr_db.py +289 -0
- local_deep_research/web/database/migrations.py +447 -0
- local_deep_research/web/database/models.py +117 -0
- local_deep_research/web/database/schema_upgrade.py +107 -0
- local_deep_research/web/models/database.py +294 -0
- local_deep_research/web/models/settings.py +94 -0
- local_deep_research/web/routes/api_routes.py +559 -0
- local_deep_research/web/routes/history_routes.py +354 -0
- local_deep_research/web/routes/research_routes.py +715 -0
- local_deep_research/web/routes/settings_routes.py +1583 -0
- local_deep_research/web/services/research_service.py +947 -0
- local_deep_research/web/services/resource_service.py +149 -0
- local_deep_research/web/services/settings_manager.py +669 -0
- local_deep_research/web/services/settings_service.py +187 -0
- local_deep_research/web/services/socket_service.py +210 -0
- local_deep_research/web/static/css/custom_dropdown.css +277 -0
- local_deep_research/web/static/css/settings.css +1223 -0
- local_deep_research/web/static/css/styles.css +525 -48
- local_deep_research/web/static/js/components/custom_dropdown.js +428 -0
- local_deep_research/web/static/js/components/detail.js +348 -0
- local_deep_research/web/static/js/components/fallback/formatting.js +122 -0
- local_deep_research/web/static/js/components/fallback/ui.js +215 -0
- local_deep_research/web/static/js/components/history.js +487 -0
- local_deep_research/web/static/js/components/logpanel.js +949 -0
- local_deep_research/web/static/js/components/progress.js +1107 -0
- local_deep_research/web/static/js/components/research.js +1865 -0
- local_deep_research/web/static/js/components/results.js +766 -0
- local_deep_research/web/static/js/components/settings.js +3981 -0
- local_deep_research/web/static/js/components/settings_sync.js +106 -0
- local_deep_research/web/static/js/main.js +226 -0
- local_deep_research/web/static/js/services/api.js +253 -0
- local_deep_research/web/static/js/services/audio.js +31 -0
- local_deep_research/web/static/js/services/formatting.js +119 -0
- local_deep_research/web/static/js/services/pdf.js +622 -0
- local_deep_research/web/static/js/services/socket.js +882 -0
- local_deep_research/web/static/js/services/ui.js +546 -0
- local_deep_research/web/templates/base.html +72 -0
- local_deep_research/web/templates/components/custom_dropdown.html +47 -0
- local_deep_research/web/templates/components/log_panel.html +32 -0
- local_deep_research/web/templates/components/mobile_nav.html +22 -0
- local_deep_research/web/templates/components/settings_form.html +299 -0
- local_deep_research/web/templates/components/sidebar.html +21 -0
- local_deep_research/web/templates/pages/details.html +73 -0
- local_deep_research/web/templates/pages/history.html +51 -0
- local_deep_research/web/templates/pages/progress.html +57 -0
- local_deep_research/web/templates/pages/research.html +139 -0
- local_deep_research/web/templates/pages/results.html +59 -0
- local_deep_research/web/templates/settings_dashboard.html +78 -192
- local_deep_research/web/utils/__init__.py +0 -0
- local_deep_research/web/utils/formatters.py +76 -0
- local_deep_research/web_search_engines/engines/full_search.py +18 -16
- local_deep_research/web_search_engines/engines/meta_search_engine.py +182 -131
- local_deep_research/web_search_engines/engines/search_engine_arxiv.py +224 -139
- local_deep_research/web_search_engines/engines/search_engine_brave.py +88 -71
- local_deep_research/web_search_engines/engines/search_engine_ddg.py +48 -39
- local_deep_research/web_search_engines/engines/search_engine_github.py +415 -204
- local_deep_research/web_search_engines/engines/search_engine_google_pse.py +123 -90
- local_deep_research/web_search_engines/engines/search_engine_guardian.py +210 -157
- local_deep_research/web_search_engines/engines/search_engine_local.py +532 -369
- local_deep_research/web_search_engines/engines/search_engine_local_all.py +42 -36
- local_deep_research/web_search_engines/engines/search_engine_pubmed.py +358 -266
- local_deep_research/web_search_engines/engines/search_engine_searxng.py +212 -160
- local_deep_research/web_search_engines/engines/search_engine_semantic_scholar.py +213 -170
- local_deep_research/web_search_engines/engines/search_engine_serpapi.py +84 -68
- local_deep_research/web_search_engines/engines/search_engine_wayback.py +186 -154
- local_deep_research/web_search_engines/engines/search_engine_wikipedia.py +115 -77
- local_deep_research/web_search_engines/search_engine_base.py +174 -99
- local_deep_research/web_search_engines/search_engine_factory.py +192 -102
- local_deep_research/web_search_engines/search_engines_config.py +22 -15
- {local_deep_research-0.1.26.dist-info → local_deep_research-0.2.2.dist-info}/METADATA +177 -97
- local_deep_research-0.2.2.dist-info/RECORD +135 -0
- {local_deep_research-0.1.26.dist-info → local_deep_research-0.2.2.dist-info}/WHEEL +1 -2
- {local_deep_research-0.1.26.dist-info → local_deep_research-0.2.2.dist-info}/entry_points.txt +3 -0
- local_deep_research/defaults/llm_config.py +0 -338
- local_deep_research/utilties/search_utilities.py +0 -114
- local_deep_research/web/static/js/app.js +0 -3763
- local_deep_research/web/templates/api_keys_config.html +0 -82
- local_deep_research/web/templates/collections_config.html +0 -90
- local_deep_research/web/templates/index.html +0 -348
- local_deep_research/web/templates/llm_config.html +0 -120
- local_deep_research/web/templates/main_config.html +0 -89
- local_deep_research/web/templates/search_engines_config.html +0 -154
- local_deep_research/web/templates/settings.html +0 -519
- local_deep_research-0.1.26.dist-info/RECORD +0 -61
- local_deep_research-0.1.26.dist-info/top_level.txt +0 -1
- /local_deep_research/{utilties → config}/__init__.py +0 -0
- {local_deep_research-0.1.26.dist-info → local_deep_research-0.2.2.dist-info}/licenses/LICENSE +0 -0
@@ -1,338 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
LLM configuration for Local Deep Research.
|
3
|
-
|
4
|
-
This file controls which language models are available and how they're configured.
|
5
|
-
You can customize model selection, parameters, and fallbacks here.
|
6
|
-
"""
|
7
|
-
|
8
|
-
from langchain_anthropic import ChatAnthropic
|
9
|
-
from langchain_openai import ChatOpenAI
|
10
|
-
from langchain_ollama import ChatOllama
|
11
|
-
from langchain_community.llms import VLLM
|
12
|
-
from local_deep_research.utilties.search_utilities import remove_think_tags
|
13
|
-
from local_deep_research.config import settings
|
14
|
-
import os
|
15
|
-
import logging
|
16
|
-
|
17
|
-
# Initialize environment
|
18
|
-
logger = logging.getLogger(__name__)
|
19
|
-
|
20
|
-
# Valid provider options
|
21
|
-
VALID_PROVIDERS = ["ollama", "openai", "anthropic", "vllm", "openai_endpoint", "lmstudio", "llamacpp", "none"]
|
22
|
-
|
23
|
-
# ================================
|
24
|
-
# LLM FUNCTIONS
|
25
|
-
# ================================
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
def get_llm(model_name=None, temperature=None, provider=None):
|
30
|
-
"""
|
31
|
-
Get LLM instance based on model name and provider.
|
32
|
-
|
33
|
-
Args:
|
34
|
-
model_name: Name of the model to use (if None, uses settings.llm.model)
|
35
|
-
temperature: Model temperature (if None, uses settings.llm.temperature)
|
36
|
-
provider: Provider to use (if None, uses settings.llm.provider)
|
37
|
-
|
38
|
-
Returns:
|
39
|
-
A LangChain LLM instance with automatic think-tag removal
|
40
|
-
"""
|
41
|
-
# Use settings values for parameters if not provided
|
42
|
-
if model_name is None:
|
43
|
-
model_name = settings.llm.model
|
44
|
-
|
45
|
-
if temperature is None:
|
46
|
-
temperature = settings.llm.temperature
|
47
|
-
|
48
|
-
if provider is None:
|
49
|
-
provider = settings.llm.provider.lower()
|
50
|
-
if provider not in VALID_PROVIDERS:
|
51
|
-
logger.error(f"Invalid provider in settings: {provider}")
|
52
|
-
raise ValueError(f"Invalid provider: {provider}. Must be one of: {VALID_PROVIDERS}")
|
53
|
-
|
54
|
-
# Common parameters for all models
|
55
|
-
common_params = {
|
56
|
-
"temperature": temperature,
|
57
|
-
"max_tokens": settings.llm.max_tokens,
|
58
|
-
}
|
59
|
-
|
60
|
-
# Handle different providers
|
61
|
-
if provider == "anthropic":
|
62
|
-
api_key_name = 'ANTHROPIC_API_KEY'
|
63
|
-
api_key = settings.get(api_key_name, '')
|
64
|
-
if not api_key:
|
65
|
-
api_key = os.getenv(api_key_name)
|
66
|
-
if not api_key:
|
67
|
-
api_key = os.getenv("LDR_" + api_key_name)
|
68
|
-
if not api_key:
|
69
|
-
logger.warning("ANTHROPIC_API_KEY not found. Falling back to default model.")
|
70
|
-
return get_fallback_model(temperature)
|
71
|
-
|
72
|
-
llm = ChatAnthropic(
|
73
|
-
model=model_name, anthropic_api_key=api_key, **common_params
|
74
|
-
)
|
75
|
-
return wrap_llm_without_think_tags(llm)
|
76
|
-
|
77
|
-
elif provider == "openai":
|
78
|
-
api_key_name = 'OPENAI_API_KEY'
|
79
|
-
api_key = settings.get(api_key_name, '')
|
80
|
-
if not api_key:
|
81
|
-
api_key = os.getenv(api_key_name)
|
82
|
-
if not api_key:
|
83
|
-
api_key = os.getenv("LDR_" + api_key_name)
|
84
|
-
if not api_key:
|
85
|
-
logger.warning("OPENAI_API_KEY not found. Falling back to default model.")
|
86
|
-
return get_fallback_model(temperature)
|
87
|
-
|
88
|
-
llm = ChatOpenAI(model=model_name, api_key=api_key, **common_params)
|
89
|
-
return wrap_llm_without_think_tags(llm)
|
90
|
-
|
91
|
-
elif provider == "openai_endpoint":
|
92
|
-
api_key_name = 'OPENAI_ENDPOINT_API_KEY'
|
93
|
-
api_key = settings.get(api_key_name, '')
|
94
|
-
if not api_key:
|
95
|
-
api_key = os.getenv(api_key_name)
|
96
|
-
if not api_key:
|
97
|
-
api_key = os.getenv("LDR_" + api_key_name)
|
98
|
-
if not api_key:
|
99
|
-
logger.warning("OPENAI_ENDPOINT_API_KEY not found. Falling back to default model.")
|
100
|
-
return get_fallback_model(temperature)
|
101
|
-
|
102
|
-
# Get endpoint URL from settings
|
103
|
-
openai_endpoint_url = settings.llm.openai_endpoint_url
|
104
|
-
|
105
|
-
llm = ChatOpenAI(
|
106
|
-
model=model_name,
|
107
|
-
api_key=api_key,
|
108
|
-
openai_api_base=openai_endpoint_url,
|
109
|
-
**common_params
|
110
|
-
)
|
111
|
-
return wrap_llm_without_think_tags(llm)
|
112
|
-
|
113
|
-
elif provider == "vllm":
|
114
|
-
try:
|
115
|
-
llm = VLLM(
|
116
|
-
model=model_name,
|
117
|
-
trust_remote_code=True,
|
118
|
-
max_new_tokens=128,
|
119
|
-
top_k=10,
|
120
|
-
top_p=0.95,
|
121
|
-
temperature=temperature,
|
122
|
-
)
|
123
|
-
return wrap_llm_without_think_tags(llm)
|
124
|
-
except Exception as e:
|
125
|
-
logger.error(f"Error loading VLLM model: {e}")
|
126
|
-
logger.warning("Falling back.")
|
127
|
-
return get_fallback_model(temperature)
|
128
|
-
|
129
|
-
elif provider == "ollama":
|
130
|
-
try:
|
131
|
-
# Use the configurable Ollama base URL
|
132
|
-
base_url = settings.get('OLLAMA_BASE_URL', settings.llm.get('ollama_base_url', 'http://localhost:11434'))
|
133
|
-
llm = ChatOllama(model=model_name, base_url=base_url, **common_params)
|
134
|
-
return wrap_llm_without_think_tags(llm)
|
135
|
-
except Exception as e:
|
136
|
-
logger.error(f"Error loading Ollama model: {e}")
|
137
|
-
return get_fallback_model(temperature)
|
138
|
-
|
139
|
-
elif provider == "lmstudio":
|
140
|
-
# LM Studio supports OpenAI API format, so we can use ChatOpenAI directly
|
141
|
-
lmstudio_url = settings.llm.get('lmstudio_url', "http://localhost:1234")
|
142
|
-
|
143
|
-
llm = ChatOpenAI(
|
144
|
-
model=model_name,
|
145
|
-
api_key="lm-studio", # LM Studio doesn't require a real API key
|
146
|
-
base_url=f"{lmstudio_url}/v1", # Use the configured URL with /v1 endpoint
|
147
|
-
temperature=temperature,
|
148
|
-
max_tokens=settings.llm.max_tokens
|
149
|
-
)
|
150
|
-
return wrap_llm_without_think_tags(llm)
|
151
|
-
|
152
|
-
elif provider == "llamacpp":
|
153
|
-
# Import LlamaCpp
|
154
|
-
from langchain_community.llms import LlamaCpp
|
155
|
-
|
156
|
-
# Get LlamaCpp model path from settings
|
157
|
-
model_path = settings.llm.get('llamacpp_model_path', "")
|
158
|
-
if not model_path:
|
159
|
-
logger.error("llamacpp_model_path not set in settings")
|
160
|
-
raise ValueError("llamacpp_model_path not set in settings.toml")
|
161
|
-
|
162
|
-
# Get additional LlamaCpp parameters
|
163
|
-
n_gpu_layers = settings.llm.get('llamacpp_n_gpu_layers', 1)
|
164
|
-
n_batch = settings.llm.get('llamacpp_n_batch', 512)
|
165
|
-
f16_kv = settings.llm.get('llamacpp_f16_kv', True)
|
166
|
-
|
167
|
-
# Create LlamaCpp instance
|
168
|
-
llm = LlamaCpp(
|
169
|
-
model_path=model_path,
|
170
|
-
temperature=temperature,
|
171
|
-
max_tokens=settings.llm.max_tokens,
|
172
|
-
n_gpu_layers=n_gpu_layers,
|
173
|
-
n_batch=n_batch,
|
174
|
-
f16_kv=f16_kv,
|
175
|
-
verbose=True
|
176
|
-
)
|
177
|
-
return wrap_llm_without_think_tags(llm)
|
178
|
-
|
179
|
-
else:
|
180
|
-
return wrap_llm_without_think_tags(get_fallback_model(temperature))
|
181
|
-
|
182
|
-
def get_fallback_model(temperature=None):
|
183
|
-
"""Create a dummy model for when no providers are available"""
|
184
|
-
from langchain_community.llms.fake import FakeListLLM
|
185
|
-
return FakeListLLM(
|
186
|
-
responses=["No language models are available. Please install Ollama or set up API keys."]
|
187
|
-
)
|
188
|
-
|
189
|
-
# ================================
|
190
|
-
# COMPATIBILITY FUNCTIONS
|
191
|
-
# ================================
|
192
|
-
|
193
|
-
def wrap_llm_without_think_tags(llm):
|
194
|
-
"""Create a wrapper class that processes LLM outputs with remove_think_tags"""
|
195
|
-
|
196
|
-
|
197
|
-
class ProcessingLLMWrapper:
|
198
|
-
def __init__(self, base_llm):
|
199
|
-
self.base_llm = base_llm
|
200
|
-
|
201
|
-
def invoke(self, *args, **kwargs):
|
202
|
-
response = self.base_llm.invoke(*args, **kwargs)
|
203
|
-
|
204
|
-
# Process the response content if it has a content attribute
|
205
|
-
if hasattr(response, 'content'):
|
206
|
-
response.content = remove_think_tags(response.content)
|
207
|
-
elif isinstance(response, str):
|
208
|
-
response = remove_think_tags(response)
|
209
|
-
|
210
|
-
return response
|
211
|
-
|
212
|
-
# Pass through any other attributes to the base LLM
|
213
|
-
def __getattr__(self, name):
|
214
|
-
return getattr(self.base_llm, name)
|
215
|
-
|
216
|
-
return ProcessingLLMWrapper(llm)
|
217
|
-
|
218
|
-
def get_available_provider_types():
|
219
|
-
"""Return available model providers"""
|
220
|
-
providers = {}
|
221
|
-
|
222
|
-
if is_ollama_available():
|
223
|
-
providers["ollama"] = "Ollama (local models)"
|
224
|
-
|
225
|
-
if is_openai_available():
|
226
|
-
providers["openai"] = "OpenAI API"
|
227
|
-
|
228
|
-
if is_anthropic_available():
|
229
|
-
providers["anthropic"] = "Anthropic API"
|
230
|
-
|
231
|
-
if is_openai_endpoint_available():
|
232
|
-
providers["openai_endpoint"] = "OpenAI-compatible Endpoint"
|
233
|
-
|
234
|
-
if is_lmstudio_available():
|
235
|
-
providers["lmstudio"] = "LM Studio (local models)"
|
236
|
-
|
237
|
-
if is_llamacpp_available():
|
238
|
-
providers["llamacpp"] = "LlamaCpp (local models)"
|
239
|
-
|
240
|
-
# Check for VLLM capability
|
241
|
-
try:
|
242
|
-
import torch
|
243
|
-
import transformers
|
244
|
-
providers["vllm"] = "VLLM (local models)"
|
245
|
-
except ImportError:
|
246
|
-
pass
|
247
|
-
|
248
|
-
# Default fallback
|
249
|
-
if not providers:
|
250
|
-
providers["none"] = "No model providers available"
|
251
|
-
|
252
|
-
return providers
|
253
|
-
|
254
|
-
# ================================
|
255
|
-
# HELPER FUNCTIONS
|
256
|
-
# ================================
|
257
|
-
|
258
|
-
def is_openai_available():
|
259
|
-
"""Check if OpenAI is available"""
|
260
|
-
try:
|
261
|
-
api_key = settings.get('OPENAI_API_KEY', '')
|
262
|
-
if not api_key:
|
263
|
-
api_key = os.getenv('OPENAI_API_KEY')
|
264
|
-
return bool(api_key)
|
265
|
-
except:
|
266
|
-
return False
|
267
|
-
|
268
|
-
def is_anthropic_available():
|
269
|
-
"""Check if Anthropic is available"""
|
270
|
-
try:
|
271
|
-
api_key = settings.get('ANTHROPIC_API_KEY', '')
|
272
|
-
if not api_key:
|
273
|
-
api_key = os.getenv('ANTHROPIC_API_KEY')
|
274
|
-
return bool(api_key)
|
275
|
-
except:
|
276
|
-
return False
|
277
|
-
|
278
|
-
def is_openai_endpoint_available():
|
279
|
-
"""Check if OpenAI endpoint is available"""
|
280
|
-
try:
|
281
|
-
api_key = settings.get('OPENAI_ENDPOINT_API_KEY', '')
|
282
|
-
if not api_key:
|
283
|
-
api_key = os.getenv('OPENAI_ENDPOINT_API_KEY')
|
284
|
-
return bool(api_key)
|
285
|
-
except:
|
286
|
-
return False
|
287
|
-
|
288
|
-
def is_ollama_available():
|
289
|
-
"""Check if Ollama is running"""
|
290
|
-
try:
|
291
|
-
import requests
|
292
|
-
base_url = settings.get('OLLAMA_BASE_URL', settings.llm.get('ollama_base_url', 'http://localhost:11434'))
|
293
|
-
response = requests.get(f"{base_url}/api/tags", timeout=1.0)
|
294
|
-
return response.status_code == 200
|
295
|
-
except:
|
296
|
-
return False
|
297
|
-
|
298
|
-
def is_vllm_available():
|
299
|
-
"""Check if VLLM capability is available"""
|
300
|
-
try:
|
301
|
-
import torch
|
302
|
-
import transformers
|
303
|
-
return True
|
304
|
-
except ImportError:
|
305
|
-
return False
|
306
|
-
|
307
|
-
def is_lmstudio_available():
|
308
|
-
"""Check if LM Studio is available"""
|
309
|
-
try:
|
310
|
-
import requests
|
311
|
-
lmstudio_url = settings.llm.get('lmstudio_url', 'http://localhost:1234')
|
312
|
-
# LM Studio typically uses OpenAI-compatible endpoints
|
313
|
-
response = requests.get(f"{lmstudio_url}/v1/models", timeout=1.0)
|
314
|
-
return response.status_code == 200
|
315
|
-
except:
|
316
|
-
return False
|
317
|
-
|
318
|
-
def is_llamacpp_available():
|
319
|
-
"""Check if LlamaCpp is available and configured"""
|
320
|
-
try:
|
321
|
-
from langchain_community.llms import LlamaCpp
|
322
|
-
model_path = settings.llm.get('llamacpp_model_path', '')
|
323
|
-
return bool(model_path) and os.path.exists(model_path)
|
324
|
-
except:
|
325
|
-
return False
|
326
|
-
|
327
|
-
def get_available_providers():
|
328
|
-
"""Get dictionary of available providers"""
|
329
|
-
return get_available_provider_types()
|
330
|
-
|
331
|
-
# Log which providers are available
|
332
|
-
AVAILABLE_PROVIDERS = get_available_providers()
|
333
|
-
logger.info(f"Available providers: {list(AVAILABLE_PROVIDERS.keys())}")
|
334
|
-
|
335
|
-
# Check if selected provider is available
|
336
|
-
selected_provider = settings.llm.provider.lower()
|
337
|
-
if selected_provider not in AVAILABLE_PROVIDERS and selected_provider != "none":
|
338
|
-
logger.warning(f"Selected provider {selected_provider} is not available.")
|
@@ -1,114 +0,0 @@
|
|
1
|
-
import re
|
2
|
-
|
3
|
-
|
4
|
-
def remove_think_tags(text: str) -> str:
|
5
|
-
text = re.sub(r"<think>.*?</think>", "", text, flags=re.DOTALL).strip()
|
6
|
-
return text
|
7
|
-
|
8
|
-
|
9
|
-
def extract_links_from_search_results(search_results: list) -> list:
|
10
|
-
"""
|
11
|
-
Extracts links and titles from a list of search result dictionaries.
|
12
|
-
|
13
|
-
Each dictionary is expected to have at least the keys "title" and "link".
|
14
|
-
|
15
|
-
Returns a list of dictionaries with 'title' and 'url' keys.
|
16
|
-
"""
|
17
|
-
links = []
|
18
|
-
for result in search_results:
|
19
|
-
|
20
|
-
try:
|
21
|
-
|
22
|
-
title = result.get("title", "").strip()
|
23
|
-
url = result.get("link", "").strip()
|
24
|
-
index = result.get("index", "").strip()
|
25
|
-
|
26
|
-
if title and url:
|
27
|
-
links.append({"title": title, "url": url, "index": index})
|
28
|
-
except Exception:
|
29
|
-
continue
|
30
|
-
return links
|
31
|
-
|
32
|
-
def format_links(links):
|
33
|
-
formatted_links =""
|
34
|
-
formatted_links += "SOURCES:\n"
|
35
|
-
for i, link in enumerate(links, 1):
|
36
|
-
formatted_links += f"{link['index']}. {link['title']}\n URL: {link['url']}\n"
|
37
|
-
formatted_links += "\n"
|
38
|
-
return formatted_links
|
39
|
-
|
40
|
-
|
41
|
-
def format_findings_to_text(findings_list, current_knowledge, questions_by_iteration):
|
42
|
-
formatted_text = "COMPLETE RESEARCH OUTPUT \n\n"
|
43
|
-
|
44
|
-
# Store the full current knowledge
|
45
|
-
|
46
|
-
formatted_text += f"{current_knowledge}\n\n"
|
47
|
-
formatted_text += "=" * 80 + "\n\n"
|
48
|
-
|
49
|
-
# Store questions by iteration
|
50
|
-
formatted_text += "SEARCH QUESTIONS BY ITERATION:\n"
|
51
|
-
for iter_num, questions in questions_by_iteration.items():
|
52
|
-
formatted_text += f"\nIteration {iter_num}:\n"
|
53
|
-
for i, q in enumerate(questions, 1):
|
54
|
-
formatted_text += f"{i}. {q}\n"
|
55
|
-
formatted_text += "\n" + "=" * 80 + "\n\n"
|
56
|
-
|
57
|
-
# Store detailed findings
|
58
|
-
formatted_text += "DETAILED FINDINGS:\n\n"
|
59
|
-
all_links = [] # To collect all sources
|
60
|
-
|
61
|
-
for finding in findings_list:
|
62
|
-
# Phase header
|
63
|
-
formatted_text += f"{'='*80}\n"
|
64
|
-
formatted_text += f"PHASE: {finding['phase']}\n"
|
65
|
-
formatted_text += f"{'='*80}\n\n"
|
66
|
-
|
67
|
-
# If this is a follow-up phase, show the corresponding question
|
68
|
-
if finding["phase"].startswith("Follow-up"):
|
69
|
-
iteration = int(finding["phase"].split(".")[0].split()[-1])
|
70
|
-
question_index = int(finding["phase"].split(".")[-1]) - 1
|
71
|
-
if iteration in questions_by_iteration and question_index < len(
|
72
|
-
questions_by_iteration[iteration]
|
73
|
-
):
|
74
|
-
formatted_text += f"SEARCH QUESTION:\n{questions_by_iteration[iteration][question_index]}\n\n"
|
75
|
-
|
76
|
-
# Content
|
77
|
-
formatted_text += f"CONTENT:\n{finding['content']}\n\n"
|
78
|
-
|
79
|
-
# Search results if they exist
|
80
|
-
if "search_results" in finding:
|
81
|
-
# formatted_text += "SEARCH RESULTS:\n"
|
82
|
-
# formatted_text += f"{finding['search_results']}\n\n"
|
83
|
-
|
84
|
-
# Extract and format links for this finding
|
85
|
-
links = extract_links_from_search_results(finding["search_results"])
|
86
|
-
if links:
|
87
|
-
formatted_text += "SOURCES USED IN THIS SECTION:\n"
|
88
|
-
for i, link in enumerate(links, 1):
|
89
|
-
formatted_text += f"{i}. {link['title']}\n URL: {link['url']}\n"
|
90
|
-
formatted_text += "\n"
|
91
|
-
all_links.extend(links)
|
92
|
-
|
93
|
-
formatted_text += f"{'_'*80}\n\n"
|
94
|
-
|
95
|
-
# Add summary of all sources at the end
|
96
|
-
if all_links:
|
97
|
-
formatted_text += "\nALL SOURCES USED IN RESEARCH:\n"
|
98
|
-
formatted_text += "=" * 80 + "\n\n"
|
99
|
-
seen_urls = set() # To prevent duplicates
|
100
|
-
for i, link in enumerate(all_links, 1):
|
101
|
-
if link["url"] not in seen_urls:
|
102
|
-
formatted_text += f"{i}. {link['title']}\n URL: {link['url']}\n"
|
103
|
-
seen_urls.add(link["url"])
|
104
|
-
formatted_text += "\n" + "=" * 80 + "\n"
|
105
|
-
|
106
|
-
return formatted_text
|
107
|
-
|
108
|
-
def print_search_results(search_results):
|
109
|
-
formatted_text=""
|
110
|
-
links = extract_links_from_search_results(search_results)
|
111
|
-
if links:
|
112
|
-
formatted_text=format_links(links=links)
|
113
|
-
logger.info(formatted_text)
|
114
|
-
|