local-deep-research 0.1.26__py3-none-any.whl → 0.2.0__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 +96 -84
- 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 +72 -44
- local_deep_research/search_system.py +147 -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 +1592 -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 +211 -159
- 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.0.dist-info}/METADATA +177 -97
- local_deep_research-0.2.0.dist-info/RECORD +135 -0
- {local_deep_research-0.1.26.dist-info → local_deep_research-0.2.0.dist-info}/WHEEL +1 -2
- {local_deep_research-0.1.26.dist-info → local_deep_research-0.2.0.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.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,447 @@
|
|
1
|
+
import logging
|
2
|
+
|
3
|
+
from sqlalchemy import inspect
|
4
|
+
|
5
|
+
from ..services.settings_manager import SettingsManager
|
6
|
+
from .models import Base, Setting, SettingType
|
7
|
+
|
8
|
+
logger = logging.getLogger(__name__)
|
9
|
+
|
10
|
+
|
11
|
+
def migrate_settings_from_files(db_session):
|
12
|
+
"""
|
13
|
+
Migrate settings from files to database
|
14
|
+
"""
|
15
|
+
# Check if settings table is empty
|
16
|
+
settings_count = db_session.query(Setting).count()
|
17
|
+
|
18
|
+
if settings_count == 0:
|
19
|
+
logger.info("Settings table is empty, importing from files")
|
20
|
+
|
21
|
+
# Create settings manager and import settings
|
22
|
+
try:
|
23
|
+
settings_mgr = SettingsManager(db_session)
|
24
|
+
success = settings_mgr.import_from_file()
|
25
|
+
if success:
|
26
|
+
logger.info("Successfully imported settings from files")
|
27
|
+
else:
|
28
|
+
logger.warning("Failed to import some settings from files")
|
29
|
+
except Exception as e:
|
30
|
+
logger.error("Error importing settings from files: %s", e)
|
31
|
+
else:
|
32
|
+
logger.info(
|
33
|
+
"Settings table already has %s rows, skipping import", settings_count
|
34
|
+
)
|
35
|
+
|
36
|
+
|
37
|
+
def run_migrations(engine, db_session=None):
|
38
|
+
"""
|
39
|
+
Run any necessary database migrations
|
40
|
+
|
41
|
+
Args:
|
42
|
+
engine: SQLAlchemy engine
|
43
|
+
db_session: Optional SQLAlchemy session
|
44
|
+
"""
|
45
|
+
# Create all tables if they don't exist
|
46
|
+
inspector = inspect(engine)
|
47
|
+
if not inspector.has_table("settings"):
|
48
|
+
logger.info("Creating settings table")
|
49
|
+
Base.metadata.create_all(engine, tables=[Setting.__table__])
|
50
|
+
|
51
|
+
# Import existing settings from files
|
52
|
+
if db_session:
|
53
|
+
migrate_settings_from_files(db_session)
|
54
|
+
|
55
|
+
|
56
|
+
def setup_predefined_settings(db_session):
|
57
|
+
"""
|
58
|
+
Set up predefined settings with UI metadata
|
59
|
+
|
60
|
+
Args:
|
61
|
+
db_session: SQLAlchemy session
|
62
|
+
"""
|
63
|
+
# Define standard UI settings for LLM
|
64
|
+
llm_settings = [
|
65
|
+
{
|
66
|
+
"key": "llm.model",
|
67
|
+
"name": "LLM Model",
|
68
|
+
"description": "Language model to use for research and analysis",
|
69
|
+
"category": "llm_general",
|
70
|
+
"ui_element": "select",
|
71
|
+
"options": [
|
72
|
+
{"value": "gpt-4o", "label": "GPT-4o (OpenAI)"},
|
73
|
+
{"value": "gpt-3.5-turbo", "label": "GPT-3.5 Turbo (OpenAI)"},
|
74
|
+
{
|
75
|
+
"value": "claude-3-5-sonnet-latest",
|
76
|
+
"label": "Claude 3.5 Sonnet (Anthropic)",
|
77
|
+
},
|
78
|
+
{
|
79
|
+
"value": "claude-3-opus-20240229",
|
80
|
+
"label": "Claude 3 Opus (Anthropic)",
|
81
|
+
},
|
82
|
+
{"value": "llama3", "label": "Llama 3 (Meta)"},
|
83
|
+
{"value": "mistral", "label": "Mistral (Mistral AI)"},
|
84
|
+
{"value": "mixtral", "label": "Mixtral (Mistral AI)"},
|
85
|
+
],
|
86
|
+
"value": "gpt-3.5-turbo",
|
87
|
+
},
|
88
|
+
{
|
89
|
+
"key": "llm.provider",
|
90
|
+
"name": "LLM Provider",
|
91
|
+
"description": "Service provider for the language model",
|
92
|
+
"category": "llm_general",
|
93
|
+
"ui_element": "select",
|
94
|
+
"options": [
|
95
|
+
{"value": "openai", "label": "OpenAI API"},
|
96
|
+
{"value": "anthropic", "label": "Anthropic API"},
|
97
|
+
{"value": "ollama", "label": "Ollama (Local)"},
|
98
|
+
{"value": "lmstudio", "label": "LM Studio (Local)"},
|
99
|
+
{"value": "vllm", "label": "vLLM (Local)"},
|
100
|
+
{"value": "openai_endpoint", "label": "Custom OpenAI-compatible API"},
|
101
|
+
],
|
102
|
+
"value": "openai",
|
103
|
+
},
|
104
|
+
{
|
105
|
+
"key": "llm.temperature",
|
106
|
+
"name": "Temperature",
|
107
|
+
"description": "Controls randomness in model outputs (0.0 - 1.0)",
|
108
|
+
"category": "llm_parameters",
|
109
|
+
"ui_element": "slider",
|
110
|
+
"min_value": 0.0,
|
111
|
+
"max_value": 1.0,
|
112
|
+
"step": 0.05,
|
113
|
+
"value": 0.7,
|
114
|
+
},
|
115
|
+
{
|
116
|
+
"key": "llm.max_tokens",
|
117
|
+
"name": "Max Tokens",
|
118
|
+
"description": "Maximum number of tokens in model responses",
|
119
|
+
"category": "llm_parameters",
|
120
|
+
"ui_element": "number",
|
121
|
+
"min_value": 100,
|
122
|
+
"max_value": 4096,
|
123
|
+
"value": 1024,
|
124
|
+
},
|
125
|
+
]
|
126
|
+
|
127
|
+
# Define standard UI settings for Search
|
128
|
+
search_settings = [
|
129
|
+
{
|
130
|
+
"key": "search.tool",
|
131
|
+
"name": "Search Engine",
|
132
|
+
"description": "Web search engine to use for research",
|
133
|
+
"category": "search_general",
|
134
|
+
"ui_element": "select",
|
135
|
+
"options": [
|
136
|
+
{"value": "auto", "label": "Auto (Default)"},
|
137
|
+
{"value": "arxiv", "label": "Arxiv"},
|
138
|
+
{"value": "wikipedia", "label": "Wikipedia"},
|
139
|
+
{"value": "pubmed", "label": "Pubmed"},
|
140
|
+
{"value": "github", "label": "Github"},
|
141
|
+
{"value": "serpapi", "label": "SerpAPI (Google)"},
|
142
|
+
{"value": "searxng", "label": "SearXNG (Self-hosted)"},
|
143
|
+
{"value": "google_pse", "label": "Google Programmable Search Engine"},
|
144
|
+
{"value": "duckduckgo", "label": "DuckDuckGo"},
|
145
|
+
{"value": "brave", "label": "Brave"},
|
146
|
+
{"value": "wayback", "label": "Wayback"},
|
147
|
+
{"value": "local_all", "label": "Local All"},
|
148
|
+
],
|
149
|
+
"value": "auto",
|
150
|
+
},
|
151
|
+
{
|
152
|
+
"key": "search.max_results",
|
153
|
+
"name": "Max Results",
|
154
|
+
"description": "Maximum number of search results to retrieve",
|
155
|
+
"category": "search_parameters",
|
156
|
+
"ui_element": "number",
|
157
|
+
"min_value": 3,
|
158
|
+
"max_value": 50,
|
159
|
+
"value": 10,
|
160
|
+
},
|
161
|
+
{
|
162
|
+
"key": "search.region",
|
163
|
+
"name": "Search Region",
|
164
|
+
"description": "Geographic region for search results",
|
165
|
+
"category": "search_parameters",
|
166
|
+
"ui_element": "select",
|
167
|
+
"options": [
|
168
|
+
{"value": "us", "label": "United States"},
|
169
|
+
{"value": "uk", "label": "United Kingdom"},
|
170
|
+
{"value": "fr", "label": "France"},
|
171
|
+
{"value": "de", "label": "Germany"},
|
172
|
+
{"value": "jp", "label": "Japan"},
|
173
|
+
{"value": "wt-wt", "label": "No Region (Worldwide)"},
|
174
|
+
],
|
175
|
+
"value": "us",
|
176
|
+
},
|
177
|
+
{
|
178
|
+
"key": "search.time_period",
|
179
|
+
"name": "Time Period",
|
180
|
+
"description": "Time period for search results",
|
181
|
+
"category": "search_parameters",
|
182
|
+
"ui_element": "select",
|
183
|
+
"options": [
|
184
|
+
{"value": "d", "label": "Past 24 hours"},
|
185
|
+
{"value": "w", "label": "Past week"},
|
186
|
+
{"value": "m", "label": "Past month"},
|
187
|
+
{"value": "y", "label": "Past year"},
|
188
|
+
{"value": "all", "label": "All time"},
|
189
|
+
],
|
190
|
+
"value": "all",
|
191
|
+
},
|
192
|
+
{
|
193
|
+
"key": "search.snippets_only",
|
194
|
+
"name": "Snippets Only",
|
195
|
+
"description": "Only retrieve snippets instead of full search results",
|
196
|
+
"category": "search_parameters",
|
197
|
+
"ui_element": "checkbox",
|
198
|
+
"value": True,
|
199
|
+
},
|
200
|
+
]
|
201
|
+
|
202
|
+
# Define standard UI settings for Report generation
|
203
|
+
report_settings = [
|
204
|
+
{
|
205
|
+
"key": "report.searches_per_section",
|
206
|
+
"name": "Searches Per Section",
|
207
|
+
"description": "Number of searches to run per report section",
|
208
|
+
"category": "report_parameters",
|
209
|
+
"ui_element": "number",
|
210
|
+
"min_value": 1,
|
211
|
+
"max_value": 5,
|
212
|
+
"value": 2,
|
213
|
+
},
|
214
|
+
{
|
215
|
+
"key": "report.enable_fact_checking",
|
216
|
+
"name": "Enable Fact Checking",
|
217
|
+
"description": "Enable fact checking for report contents",
|
218
|
+
"category": "report_parameters",
|
219
|
+
"ui_element": "checkbox",
|
220
|
+
"value": True,
|
221
|
+
},
|
222
|
+
{
|
223
|
+
"key": "report.detailed_citations",
|
224
|
+
"name": "Detailed Citations",
|
225
|
+
"description": "Include detailed citations in reports",
|
226
|
+
"category": "report_parameters",
|
227
|
+
"ui_element": "checkbox",
|
228
|
+
"value": True,
|
229
|
+
},
|
230
|
+
]
|
231
|
+
|
232
|
+
# Define standard UI settings for App
|
233
|
+
app_settings = [
|
234
|
+
{
|
235
|
+
"key": "app.debug",
|
236
|
+
"name": "Debug Mode",
|
237
|
+
"description": "Enable debug mode for the web application",
|
238
|
+
"category": "app_interface",
|
239
|
+
"ui_element": "checkbox",
|
240
|
+
"value": True,
|
241
|
+
},
|
242
|
+
{
|
243
|
+
"key": "app.host",
|
244
|
+
"name": "Web Host",
|
245
|
+
"description": "Host address to bind the web server",
|
246
|
+
"category": "app_interface",
|
247
|
+
"ui_element": "text",
|
248
|
+
"value": "0.0.0.0",
|
249
|
+
},
|
250
|
+
{
|
251
|
+
"key": "app.port",
|
252
|
+
"name": "Web Port",
|
253
|
+
"description": "Port for the web server",
|
254
|
+
"category": "app_interface",
|
255
|
+
"ui_element": "number",
|
256
|
+
"min_value": 1,
|
257
|
+
"max_value": 65535,
|
258
|
+
"value": 5000,
|
259
|
+
},
|
260
|
+
{
|
261
|
+
"key": "app.enable_notifications",
|
262
|
+
"name": "Enable Notifications",
|
263
|
+
"description": "Enable browser notifications for research events",
|
264
|
+
"category": "app_interface",
|
265
|
+
"ui_element": "checkbox",
|
266
|
+
"value": True,
|
267
|
+
},
|
268
|
+
{
|
269
|
+
"key": "app.theme",
|
270
|
+
"name": "UI Theme",
|
271
|
+
"description": "User interface theme",
|
272
|
+
"category": "app_interface",
|
273
|
+
"ui_element": "select",
|
274
|
+
"options": [
|
275
|
+
{"value": "dark", "label": "Dark"},
|
276
|
+
{"value": "light", "label": "Light"},
|
277
|
+
{"value": "system", "label": "System Default"},
|
278
|
+
],
|
279
|
+
"value": "dark",
|
280
|
+
},
|
281
|
+
{
|
282
|
+
"key": "app.web_interface",
|
283
|
+
"name": "Web Interface",
|
284
|
+
"description": "Enable the web interface",
|
285
|
+
"category": "app_interface",
|
286
|
+
"ui_element": "checkbox",
|
287
|
+
"value": True,
|
288
|
+
},
|
289
|
+
{
|
290
|
+
"key": "app.enable_web",
|
291
|
+
"name": "Enable Web Server",
|
292
|
+
"description": "Enable the web server",
|
293
|
+
"category": "app_interface",
|
294
|
+
"ui_element": "checkbox",
|
295
|
+
"value": True,
|
296
|
+
},
|
297
|
+
]
|
298
|
+
|
299
|
+
# Update search settings with research-specific settings
|
300
|
+
search_settings.extend(
|
301
|
+
[
|
302
|
+
{
|
303
|
+
"key": "search.research_iterations",
|
304
|
+
"name": "Research Iterations",
|
305
|
+
"description": "Number of research cycles to perform",
|
306
|
+
"category": "search_parameters",
|
307
|
+
"ui_element": "number",
|
308
|
+
"min_value": 1,
|
309
|
+
"max_value": 5,
|
310
|
+
"value": 2,
|
311
|
+
},
|
312
|
+
{
|
313
|
+
"key": "search.questions_per_iteration",
|
314
|
+
"name": "Questions Per Iteration",
|
315
|
+
"description": "Number of questions to generate per research cycle",
|
316
|
+
"category": "search_parameters",
|
317
|
+
"ui_element": "number",
|
318
|
+
"min_value": 1,
|
319
|
+
"max_value": 10,
|
320
|
+
"value": 3,
|
321
|
+
},
|
322
|
+
{
|
323
|
+
"key": "search.searches_per_section",
|
324
|
+
"name": "Searches Per Section",
|
325
|
+
"description": "Number of searches to run per report section",
|
326
|
+
"category": "search_parameters",
|
327
|
+
"ui_element": "number",
|
328
|
+
"min_value": 1,
|
329
|
+
"max_value": 5,
|
330
|
+
"value": 2,
|
331
|
+
},
|
332
|
+
{
|
333
|
+
"key": "search.skip_relevance_filter",
|
334
|
+
"name": "Skip Relevance Filter",
|
335
|
+
"description": "Skip filtering search results for relevance",
|
336
|
+
"category": "search_parameters",
|
337
|
+
"ui_element": "checkbox",
|
338
|
+
"value": False,
|
339
|
+
},
|
340
|
+
{
|
341
|
+
"key": "search.safe_search",
|
342
|
+
"name": "Safe Search",
|
343
|
+
"description": "Enable safe search filtering",
|
344
|
+
"category": "search_parameters",
|
345
|
+
"ui_element": "checkbox",
|
346
|
+
"value": True,
|
347
|
+
},
|
348
|
+
{
|
349
|
+
"key": "search.search_language",
|
350
|
+
"name": "Search Language",
|
351
|
+
"description": "Language for search results",
|
352
|
+
"category": "search_parameters",
|
353
|
+
"ui_element": "select",
|
354
|
+
"options": [
|
355
|
+
{"value": "English", "label": "English"},
|
356
|
+
{"value": "French", "label": "French"},
|
357
|
+
{"value": "German", "label": "German"},
|
358
|
+
{"value": "Spanish", "label": "Spanish"},
|
359
|
+
{"value": "Italian", "label": "Italian"},
|
360
|
+
{"value": "Japanese", "label": "Japanese"},
|
361
|
+
{"value": "Chinese", "label": "Chinese"},
|
362
|
+
],
|
363
|
+
"value": "English",
|
364
|
+
},
|
365
|
+
]
|
366
|
+
)
|
367
|
+
|
368
|
+
# Ensure these predefined settings exist in the database
|
369
|
+
# This will update existing settings with the same key
|
370
|
+
all_settings = llm_settings + search_settings + report_settings + app_settings
|
371
|
+
|
372
|
+
# Add/update each setting
|
373
|
+
for setting_dict in all_settings:
|
374
|
+
try:
|
375
|
+
# Convert to correct type based on key prefix
|
376
|
+
setting_type = None
|
377
|
+
key = setting_dict.get("key", "")
|
378
|
+
|
379
|
+
if key.startswith("llm."):
|
380
|
+
setting_type = SettingType.LLM
|
381
|
+
elif key.startswith("search."):
|
382
|
+
setting_type = SettingType.SEARCH
|
383
|
+
elif key.startswith("report."):
|
384
|
+
setting_type = SettingType.REPORT
|
385
|
+
elif key.startswith("app."):
|
386
|
+
setting_type = SettingType.APP
|
387
|
+
|
388
|
+
# Skip if no valid type
|
389
|
+
if not setting_type:
|
390
|
+
logger.warning("Skipping setting %s - unknown type", key)
|
391
|
+
continue
|
392
|
+
|
393
|
+
# Check if setting exists
|
394
|
+
existing = db_session.query(Setting).filter(Setting.key == key).first()
|
395
|
+
|
396
|
+
if existing:
|
397
|
+
# Update existing setting
|
398
|
+
logger.debug("Updating existing setting: %s", key)
|
399
|
+
|
400
|
+
# Only update metadata, not the value (to preserve user settings)
|
401
|
+
existing.name = setting_dict.get("name", existing.name)
|
402
|
+
existing.description = setting_dict.get(
|
403
|
+
"description", existing.description
|
404
|
+
)
|
405
|
+
existing.category = setting_dict.get("category", existing.category)
|
406
|
+
existing.ui_element = setting_dict.get(
|
407
|
+
"ui_element", existing.ui_element
|
408
|
+
)
|
409
|
+
existing.options = setting_dict.get("options", existing.options)
|
410
|
+
existing.min_value = setting_dict.get("min_value", existing.min_value)
|
411
|
+
existing.max_value = setting_dict.get("max_value", existing.max_value)
|
412
|
+
existing.step = setting_dict.get("step", existing.step)
|
413
|
+
|
414
|
+
# Only set value if it's not already set
|
415
|
+
if existing.value is None and "value" in setting_dict:
|
416
|
+
existing.value = setting_dict["value"]
|
417
|
+
else:
|
418
|
+
# Create new setting
|
419
|
+
logger.info("Creating new setting: %s", key)
|
420
|
+
setting = Setting(
|
421
|
+
key=key,
|
422
|
+
value=setting_dict.get("value"),
|
423
|
+
type=setting_type,
|
424
|
+
name=setting_dict.get(
|
425
|
+
"name", key.split(".")[-1].replace("_", " ").title()
|
426
|
+
),
|
427
|
+
description=setting_dict.get("description", f"Setting for {key}"),
|
428
|
+
category=setting_dict.get("category"),
|
429
|
+
ui_element=setting_dict.get("ui_element", "text"),
|
430
|
+
options=setting_dict.get("options"),
|
431
|
+
min_value=setting_dict.get("min_value"),
|
432
|
+
max_value=setting_dict.get("max_value"),
|
433
|
+
step=setting_dict.get("step"),
|
434
|
+
visible=setting_dict.get("visible", True),
|
435
|
+
editable=setting_dict.get("editable", True),
|
436
|
+
)
|
437
|
+
db_session.add(setting)
|
438
|
+
|
439
|
+
# Commit after each successful setting
|
440
|
+
db_session.commit()
|
441
|
+
|
442
|
+
except Exception as e:
|
443
|
+
logger.error("Error ensuring setting %s: %s", setting_dict.get("key"), e)
|
444
|
+
db_session.rollback()
|
445
|
+
|
446
|
+
# Log completion
|
447
|
+
logger.info("Predefined settings setup complete")
|
@@ -0,0 +1,117 @@
|
|
1
|
+
import enum
|
2
|
+
|
3
|
+
from sqlalchemy import (
|
4
|
+
JSON,
|
5
|
+
Boolean,
|
6
|
+
Column,
|
7
|
+
DateTime,
|
8
|
+
Enum,
|
9
|
+
Float,
|
10
|
+
ForeignKey,
|
11
|
+
Integer,
|
12
|
+
String,
|
13
|
+
Text,
|
14
|
+
UniqueConstraint,
|
15
|
+
)
|
16
|
+
from sqlalchemy.ext.declarative import declarative_base
|
17
|
+
from sqlalchemy.orm import relationship
|
18
|
+
from sqlalchemy.sql import func
|
19
|
+
|
20
|
+
Base = declarative_base()
|
21
|
+
|
22
|
+
|
23
|
+
class ResearchMode(enum.Enum):
|
24
|
+
QUICK = "quick"
|
25
|
+
DETAILED = "detailed"
|
26
|
+
|
27
|
+
|
28
|
+
class ResearchStatus(enum.Enum):
|
29
|
+
PENDING = "pending"
|
30
|
+
IN_PROGRESS = "in_progress"
|
31
|
+
COMPLETED = "completed"
|
32
|
+
FAILED = "failed"
|
33
|
+
CANCELLED = "cancelled"
|
34
|
+
|
35
|
+
|
36
|
+
class Research(Base):
|
37
|
+
__tablename__ = "research"
|
38
|
+
|
39
|
+
id = Column(Integer, primary_key=True, index=True)
|
40
|
+
query = Column(String, nullable=False)
|
41
|
+
status = Column(
|
42
|
+
Enum(ResearchStatus), default=ResearchStatus.PENDING, nullable=False
|
43
|
+
)
|
44
|
+
mode = Column(Enum(ResearchMode), default=ResearchMode.QUICK, nullable=False)
|
45
|
+
created_at = Column(DateTime, server_default=func.now(), nullable=False)
|
46
|
+
updated_at = Column(
|
47
|
+
DateTime, server_default=func.now(), onupdate=func.now(), nullable=False
|
48
|
+
)
|
49
|
+
progress = Column(Float, default=0.0, nullable=False)
|
50
|
+
start_time = Column(DateTime, nullable=True)
|
51
|
+
end_time = Column(DateTime, nullable=True)
|
52
|
+
error_message = Column(Text, nullable=True)
|
53
|
+
|
54
|
+
# Relationships
|
55
|
+
report = relationship(
|
56
|
+
"ResearchReport",
|
57
|
+
back_populates="research",
|
58
|
+
uselist=False,
|
59
|
+
cascade="all, delete-orphan",
|
60
|
+
)
|
61
|
+
|
62
|
+
|
63
|
+
class ResearchReport(Base):
|
64
|
+
__tablename__ = "research_report"
|
65
|
+
|
66
|
+
id = Column(Integer, primary_key=True, index=True)
|
67
|
+
research_id = Column(
|
68
|
+
Integer,
|
69
|
+
ForeignKey("research.id", ondelete="CASCADE"),
|
70
|
+
nullable=False,
|
71
|
+
unique=True,
|
72
|
+
)
|
73
|
+
content = Column(Text, nullable=True)
|
74
|
+
created_at = Column(DateTime, server_default=func.now(), nullable=False)
|
75
|
+
updated_at = Column(
|
76
|
+
DateTime, server_default=func.now(), onupdate=func.now(), nullable=False
|
77
|
+
)
|
78
|
+
report_metadata = Column(
|
79
|
+
JSON, nullable=True
|
80
|
+
) # Additional metadata about the report
|
81
|
+
|
82
|
+
# Relationships
|
83
|
+
research = relationship("Research", back_populates="report")
|
84
|
+
|
85
|
+
|
86
|
+
class SettingType(enum.Enum):
|
87
|
+
APP = "app"
|
88
|
+
LLM = "llm"
|
89
|
+
SEARCH = "search"
|
90
|
+
REPORT = "report"
|
91
|
+
|
92
|
+
|
93
|
+
class Setting(Base):
|
94
|
+
"""Database model for storing settings"""
|
95
|
+
|
96
|
+
__tablename__ = "settings"
|
97
|
+
|
98
|
+
id = Column(Integer, primary_key=True, index=True)
|
99
|
+
key = Column(String(255), nullable=False, unique=True, index=True)
|
100
|
+
value = Column(JSON, nullable=True)
|
101
|
+
type = Column(Enum(SettingType), nullable=False, index=True)
|
102
|
+
name = Column(String(255), nullable=False)
|
103
|
+
description = Column(Text, nullable=True)
|
104
|
+
category = Column(String(100), nullable=True, index=True)
|
105
|
+
ui_element = Column(String(50), default="text", nullable=False)
|
106
|
+
options = Column(JSON, nullable=True) # For select elements
|
107
|
+
min_value = Column(Float, nullable=True) # For numeric inputs
|
108
|
+
max_value = Column(Float, nullable=True) # For numeric inputs
|
109
|
+
step = Column(Float, nullable=True) # For sliders
|
110
|
+
visible = Column(Boolean, default=True, nullable=False)
|
111
|
+
editable = Column(Boolean, default=True, nullable=False)
|
112
|
+
created_at = Column(DateTime, server_default=func.now(), nullable=False)
|
113
|
+
updated_at = Column(
|
114
|
+
DateTime, server_default=func.now(), onupdate=func.now(), nullable=False
|
115
|
+
)
|
116
|
+
|
117
|
+
__table_args__ = (UniqueConstraint("key", name="uix_settings_key"),)
|
@@ -0,0 +1,107 @@
|
|
1
|
+
"""
|
2
|
+
Schema upgrade script for Local Deep Research database.
|
3
|
+
Handles schema upgrades for existing ldr.db databases.
|
4
|
+
"""
|
5
|
+
|
6
|
+
import logging
|
7
|
+
import os
|
8
|
+
import sqlite3
|
9
|
+
import sys
|
10
|
+
|
11
|
+
# Set up logging
|
12
|
+
logging.basicConfig(level=logging.INFO)
|
13
|
+
logger = logging.getLogger(__name__)
|
14
|
+
|
15
|
+
# Add the parent directory to sys.path to allow relative imports
|
16
|
+
sys.path.append(
|
17
|
+
os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
|
18
|
+
)
|
19
|
+
|
20
|
+
try:
|
21
|
+
from src.local_deep_research.web.models.database import DB_PATH
|
22
|
+
except ImportError:
|
23
|
+
# Fallback path if import fails
|
24
|
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
25
|
+
project_root = os.path.abspath(os.path.join(current_dir, "..", "..", "..", ".."))
|
26
|
+
DB_PATH = os.path.join(project_root, "src", "data", "ldr.db")
|
27
|
+
|
28
|
+
|
29
|
+
def check_table_exists(conn, table_name):
|
30
|
+
"""
|
31
|
+
Check if a table exists in the database
|
32
|
+
|
33
|
+
Args:
|
34
|
+
conn: SQLite connection
|
35
|
+
table_name: Name of the table
|
36
|
+
|
37
|
+
Returns:
|
38
|
+
bool: True if table exists, False otherwise
|
39
|
+
"""
|
40
|
+
cursor = conn.cursor()
|
41
|
+
cursor.execute(
|
42
|
+
"SELECT name FROM sqlite_master WHERE type='table' AND name=?", (table_name,)
|
43
|
+
)
|
44
|
+
return cursor.fetchone() is not None
|
45
|
+
|
46
|
+
|
47
|
+
def remove_research_log_table(conn):
|
48
|
+
"""
|
49
|
+
Remove the redundant research_log table if it exists
|
50
|
+
|
51
|
+
Args:
|
52
|
+
conn: SQLite connection
|
53
|
+
|
54
|
+
Returns:
|
55
|
+
bool: True if operation was successful, False otherwise
|
56
|
+
"""
|
57
|
+
try:
|
58
|
+
cursor = conn.cursor()
|
59
|
+
|
60
|
+
# Check if table exists
|
61
|
+
if check_table_exists(conn, "research_log"):
|
62
|
+
# For SQLite, DROP TABLE is the way to remove a table
|
63
|
+
cursor.execute("DROP TABLE research_log")
|
64
|
+
conn.commit()
|
65
|
+
logger.info("Successfully removed redundant 'research_log' table")
|
66
|
+
return True
|
67
|
+
else:
|
68
|
+
logger.info("Table 'research_log' does not exist, no action needed")
|
69
|
+
return True
|
70
|
+
except Exception as e:
|
71
|
+
logger.error(f"Error removing research_log table: {e}")
|
72
|
+
return False
|
73
|
+
|
74
|
+
|
75
|
+
def run_schema_upgrades():
|
76
|
+
"""
|
77
|
+
Run all schema upgrade operations on the database
|
78
|
+
|
79
|
+
Returns:
|
80
|
+
bool: True if all upgrades successful, False otherwise
|
81
|
+
"""
|
82
|
+
# Check if database exists
|
83
|
+
if not os.path.exists(DB_PATH):
|
84
|
+
logger.warning(f"Database not found at {DB_PATH}, skipping schema upgrades")
|
85
|
+
return False
|
86
|
+
|
87
|
+
logger.info(f"Running schema upgrades on {DB_PATH}")
|
88
|
+
|
89
|
+
try:
|
90
|
+
# Connect to the database
|
91
|
+
conn = sqlite3.connect(DB_PATH)
|
92
|
+
|
93
|
+
# 1. Remove the redundant research_log table
|
94
|
+
remove_research_log_table(conn)
|
95
|
+
|
96
|
+
# Close connection
|
97
|
+
conn.close()
|
98
|
+
|
99
|
+
logger.info("Schema upgrades completed successfully")
|
100
|
+
return True
|
101
|
+
except Exception as e:
|
102
|
+
logger.error(f"Error during schema upgrades: {e}")
|
103
|
+
return False
|
104
|
+
|
105
|
+
|
106
|
+
if __name__ == "__main__":
|
107
|
+
run_schema_upgrades()
|