local-deep-research 0.4.4__py3-none-any.whl → 0.5.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 +7 -0
- local_deep_research/__version__.py +1 -1
- local_deep_research/advanced_search_system/answer_decoding/__init__.py +5 -0
- local_deep_research/advanced_search_system/answer_decoding/browsecomp_answer_decoder.py +421 -0
- local_deep_research/advanced_search_system/candidate_exploration/README.md +219 -0
- local_deep_research/advanced_search_system/candidate_exploration/__init__.py +25 -0
- local_deep_research/advanced_search_system/candidate_exploration/adaptive_explorer.py +329 -0
- local_deep_research/advanced_search_system/candidate_exploration/base_explorer.py +341 -0
- local_deep_research/advanced_search_system/candidate_exploration/constraint_guided_explorer.py +436 -0
- local_deep_research/advanced_search_system/candidate_exploration/diversity_explorer.py +457 -0
- local_deep_research/advanced_search_system/candidate_exploration/parallel_explorer.py +250 -0
- local_deep_research/advanced_search_system/candidate_exploration/progressive_explorer.py +255 -0
- local_deep_research/advanced_search_system/candidates/__init__.py +5 -0
- local_deep_research/advanced_search_system/candidates/base_candidate.py +59 -0
- local_deep_research/advanced_search_system/constraint_checking/README.md +150 -0
- local_deep_research/advanced_search_system/constraint_checking/__init__.py +35 -0
- local_deep_research/advanced_search_system/constraint_checking/base_constraint_checker.py +122 -0
- local_deep_research/advanced_search_system/constraint_checking/constraint_checker.py +223 -0
- local_deep_research/advanced_search_system/constraint_checking/constraint_satisfaction_tracker.py +387 -0
- local_deep_research/advanced_search_system/constraint_checking/dual_confidence_checker.py +424 -0
- local_deep_research/advanced_search_system/constraint_checking/evidence_analyzer.py +174 -0
- local_deep_research/advanced_search_system/constraint_checking/intelligent_constraint_relaxer.py +503 -0
- local_deep_research/advanced_search_system/constraint_checking/rejection_engine.py +143 -0
- local_deep_research/advanced_search_system/constraint_checking/strict_checker.py +259 -0
- local_deep_research/advanced_search_system/constraint_checking/threshold_checker.py +213 -0
- local_deep_research/advanced_search_system/constraints/__init__.py +6 -0
- local_deep_research/advanced_search_system/constraints/base_constraint.py +58 -0
- local_deep_research/advanced_search_system/constraints/constraint_analyzer.py +143 -0
- local_deep_research/advanced_search_system/evidence/__init__.py +12 -0
- local_deep_research/advanced_search_system/evidence/base_evidence.py +57 -0
- local_deep_research/advanced_search_system/evidence/evaluator.py +159 -0
- local_deep_research/advanced_search_system/evidence/requirements.py +122 -0
- local_deep_research/advanced_search_system/filters/base_filter.py +3 -1
- local_deep_research/advanced_search_system/filters/cross_engine_filter.py +8 -2
- local_deep_research/advanced_search_system/filters/journal_reputation_filter.py +43 -29
- local_deep_research/advanced_search_system/findings/repository.py +54 -17
- local_deep_research/advanced_search_system/knowledge/standard_knowledge.py +3 -1
- local_deep_research/advanced_search_system/query_generation/adaptive_query_generator.py +405 -0
- local_deep_research/advanced_search_system/questions/__init__.py +16 -0
- local_deep_research/advanced_search_system/questions/atomic_fact_question.py +171 -0
- local_deep_research/advanced_search_system/questions/browsecomp_question.py +287 -0
- local_deep_research/advanced_search_system/questions/decomposition_question.py +13 -4
- local_deep_research/advanced_search_system/questions/entity_aware_question.py +184 -0
- local_deep_research/advanced_search_system/questions/standard_question.py +9 -3
- local_deep_research/advanced_search_system/search_optimization/cross_constraint_manager.py +624 -0
- local_deep_research/advanced_search_system/source_management/diversity_manager.py +613 -0
- local_deep_research/advanced_search_system/strategies/__init__.py +42 -0
- local_deep_research/advanced_search_system/strategies/adaptive_decomposition_strategy.py +564 -0
- local_deep_research/advanced_search_system/strategies/base_strategy.py +4 -4
- local_deep_research/advanced_search_system/strategies/browsecomp_entity_strategy.py +1031 -0
- local_deep_research/advanced_search_system/strategies/browsecomp_optimized_strategy.py +778 -0
- local_deep_research/advanced_search_system/strategies/concurrent_dual_confidence_strategy.py +446 -0
- local_deep_research/advanced_search_system/strategies/constrained_search_strategy.py +1348 -0
- local_deep_research/advanced_search_system/strategies/constraint_parallel_strategy.py +522 -0
- local_deep_research/advanced_search_system/strategies/direct_search_strategy.py +217 -0
- local_deep_research/advanced_search_system/strategies/dual_confidence_strategy.py +320 -0
- local_deep_research/advanced_search_system/strategies/dual_confidence_with_rejection.py +219 -0
- local_deep_research/advanced_search_system/strategies/early_stop_constrained_strategy.py +369 -0
- local_deep_research/advanced_search_system/strategies/entity_aware_source_strategy.py +140 -0
- local_deep_research/advanced_search_system/strategies/evidence_based_strategy.py +1248 -0
- local_deep_research/advanced_search_system/strategies/evidence_based_strategy_v2.py +1337 -0
- local_deep_research/advanced_search_system/strategies/focused_iteration_strategy.py +537 -0
- local_deep_research/advanced_search_system/strategies/improved_evidence_based_strategy.py +782 -0
- local_deep_research/advanced_search_system/strategies/iterative_reasoning_strategy.py +760 -0
- local_deep_research/advanced_search_system/strategies/iterdrag_strategy.py +55 -21
- local_deep_research/advanced_search_system/strategies/llm_driven_modular_strategy.py +865 -0
- local_deep_research/advanced_search_system/strategies/modular_strategy.py +1142 -0
- local_deep_research/advanced_search_system/strategies/parallel_constrained_strategy.py +506 -0
- local_deep_research/advanced_search_system/strategies/parallel_search_strategy.py +34 -16
- local_deep_research/advanced_search_system/strategies/rapid_search_strategy.py +29 -9
- local_deep_research/advanced_search_system/strategies/recursive_decomposition_strategy.py +492 -0
- local_deep_research/advanced_search_system/strategies/smart_decomposition_strategy.py +284 -0
- local_deep_research/advanced_search_system/strategies/smart_query_strategy.py +515 -0
- local_deep_research/advanced_search_system/strategies/source_based_strategy.py +48 -24
- local_deep_research/advanced_search_system/strategies/standard_strategy.py +34 -14
- local_deep_research/advanced_search_system/tools/base_tool.py +7 -2
- local_deep_research/api/benchmark_functions.py +6 -2
- local_deep_research/api/research_functions.py +10 -4
- local_deep_research/benchmarks/__init__.py +9 -7
- local_deep_research/benchmarks/benchmark_functions.py +6 -2
- local_deep_research/benchmarks/cli/benchmark_commands.py +27 -10
- local_deep_research/benchmarks/cli.py +38 -13
- local_deep_research/benchmarks/comparison/__init__.py +4 -2
- local_deep_research/benchmarks/comparison/evaluator.py +316 -239
- local_deep_research/benchmarks/datasets/__init__.py +1 -1
- local_deep_research/benchmarks/datasets/base.py +91 -72
- local_deep_research/benchmarks/datasets/browsecomp.py +54 -33
- local_deep_research/benchmarks/datasets/custom_dataset_template.py +19 -19
- local_deep_research/benchmarks/datasets/simpleqa.py +14 -14
- local_deep_research/benchmarks/datasets/utils.py +48 -29
- local_deep_research/benchmarks/datasets.py +4 -11
- local_deep_research/benchmarks/efficiency/__init__.py +8 -4
- local_deep_research/benchmarks/efficiency/resource_monitor.py +223 -171
- local_deep_research/benchmarks/efficiency/speed_profiler.py +62 -48
- local_deep_research/benchmarks/evaluators/browsecomp.py +3 -1
- local_deep_research/benchmarks/evaluators/composite.py +6 -2
- local_deep_research/benchmarks/evaluators/simpleqa.py +36 -13
- local_deep_research/benchmarks/graders.py +32 -10
- local_deep_research/benchmarks/metrics/README.md +1 -1
- local_deep_research/benchmarks/metrics/calculation.py +25 -10
- local_deep_research/benchmarks/metrics/reporting.py +7 -3
- local_deep_research/benchmarks/metrics/visualization.py +42 -23
- local_deep_research/benchmarks/metrics.py +1 -1
- local_deep_research/benchmarks/optimization/__init__.py +3 -1
- local_deep_research/benchmarks/optimization/api.py +7 -1
- local_deep_research/benchmarks/optimization/optuna_optimizer.py +75 -26
- local_deep_research/benchmarks/runners.py +48 -15
- local_deep_research/citation_handler.py +65 -92
- local_deep_research/citation_handlers/__init__.py +15 -0
- local_deep_research/citation_handlers/base_citation_handler.py +70 -0
- local_deep_research/citation_handlers/forced_answer_citation_handler.py +179 -0
- local_deep_research/citation_handlers/precision_extraction_handler.py +550 -0
- local_deep_research/citation_handlers/standard_citation_handler.py +80 -0
- local_deep_research/config/llm_config.py +271 -169
- local_deep_research/config/search_config.py +14 -5
- local_deep_research/defaults/__init__.py +0 -1
- local_deep_research/metrics/__init__.py +13 -0
- local_deep_research/metrics/database.py +58 -0
- local_deep_research/metrics/db_models.py +115 -0
- local_deep_research/metrics/migrate_add_provider_to_token_usage.py +148 -0
- local_deep_research/metrics/migrate_call_stack_tracking.py +105 -0
- local_deep_research/metrics/migrate_enhanced_tracking.py +75 -0
- local_deep_research/metrics/migrate_research_ratings.py +31 -0
- local_deep_research/metrics/models.py +61 -0
- local_deep_research/metrics/pricing/__init__.py +12 -0
- local_deep_research/metrics/pricing/cost_calculator.py +237 -0
- local_deep_research/metrics/pricing/pricing_cache.py +143 -0
- local_deep_research/metrics/pricing/pricing_fetcher.py +240 -0
- local_deep_research/metrics/query_utils.py +51 -0
- local_deep_research/metrics/search_tracker.py +380 -0
- local_deep_research/metrics/token_counter.py +1078 -0
- local_deep_research/migrate_db.py +3 -1
- local_deep_research/report_generator.py +22 -8
- local_deep_research/search_system.py +390 -9
- local_deep_research/test_migration.py +15 -5
- local_deep_research/utilities/db_utils.py +7 -4
- local_deep_research/utilities/es_utils.py +115 -104
- local_deep_research/utilities/llm_utils.py +15 -5
- local_deep_research/utilities/log_utils.py +151 -0
- local_deep_research/utilities/search_cache.py +387 -0
- local_deep_research/utilities/search_utilities.py +14 -6
- local_deep_research/utilities/threading_utils.py +92 -0
- local_deep_research/utilities/url_utils.py +6 -0
- local_deep_research/web/api.py +347 -0
- local_deep_research/web/app.py +13 -17
- local_deep_research/web/app_factory.py +71 -66
- local_deep_research/web/database/migrate_to_ldr_db.py +12 -4
- local_deep_research/web/database/migrations.py +20 -3
- local_deep_research/web/database/models.py +74 -25
- local_deep_research/web/database/schema_upgrade.py +49 -29
- local_deep_research/web/models/database.py +63 -83
- local_deep_research/web/routes/api_routes.py +56 -22
- local_deep_research/web/routes/benchmark_routes.py +4 -1
- local_deep_research/web/routes/globals.py +22 -0
- local_deep_research/web/routes/history_routes.py +71 -46
- local_deep_research/web/routes/metrics_routes.py +1155 -0
- local_deep_research/web/routes/research_routes.py +192 -54
- local_deep_research/web/routes/settings_routes.py +156 -55
- local_deep_research/web/services/research_service.py +412 -251
- local_deep_research/web/services/resource_service.py +36 -11
- local_deep_research/web/services/settings_manager.py +55 -17
- local_deep_research/web/services/settings_service.py +12 -4
- local_deep_research/web/services/socket_service.py +295 -188
- local_deep_research/web/static/css/custom_dropdown.css +180 -0
- local_deep_research/web/static/css/styles.css +39 -1
- local_deep_research/web/static/js/components/detail.js +633 -267
- local_deep_research/web/static/js/components/details.js +751 -0
- local_deep_research/web/static/js/components/fallback/formatting.js +11 -11
- local_deep_research/web/static/js/components/fallback/ui.js +23 -23
- local_deep_research/web/static/js/components/history.js +76 -76
- local_deep_research/web/static/js/components/logpanel.js +61 -13
- local_deep_research/web/static/js/components/progress.js +13 -2
- local_deep_research/web/static/js/components/research.js +99 -12
- local_deep_research/web/static/js/components/results.js +239 -106
- local_deep_research/web/static/js/main.js +40 -40
- local_deep_research/web/static/js/services/audio.js +1 -1
- local_deep_research/web/static/js/services/formatting.js +11 -11
- local_deep_research/web/static/js/services/keyboard.js +157 -0
- local_deep_research/web/static/js/services/pdf.js +80 -80
- local_deep_research/web/static/sounds/README.md +1 -1
- local_deep_research/web/templates/base.html +1 -0
- local_deep_research/web/templates/components/log_panel.html +7 -1
- local_deep_research/web/templates/components/mobile_nav.html +1 -1
- local_deep_research/web/templates/components/sidebar.html +3 -0
- local_deep_research/web/templates/pages/cost_analytics.html +1245 -0
- local_deep_research/web/templates/pages/details.html +325 -24
- local_deep_research/web/templates/pages/history.html +1 -1
- local_deep_research/web/templates/pages/metrics.html +1929 -0
- local_deep_research/web/templates/pages/progress.html +2 -2
- local_deep_research/web/templates/pages/research.html +53 -17
- local_deep_research/web/templates/pages/results.html +12 -1
- local_deep_research/web/templates/pages/star_reviews.html +803 -0
- local_deep_research/web/utils/formatters.py +9 -3
- local_deep_research/web_search_engines/default_search_engines.py +5 -3
- local_deep_research/web_search_engines/engines/full_search.py +8 -2
- local_deep_research/web_search_engines/engines/meta_search_engine.py +59 -20
- local_deep_research/web_search_engines/engines/search_engine_arxiv.py +19 -6
- local_deep_research/web_search_engines/engines/search_engine_brave.py +6 -2
- local_deep_research/web_search_engines/engines/search_engine_ddg.py +3 -1
- local_deep_research/web_search_engines/engines/search_engine_elasticsearch.py +81 -58
- local_deep_research/web_search_engines/engines/search_engine_github.py +46 -15
- local_deep_research/web_search_engines/engines/search_engine_google_pse.py +16 -6
- local_deep_research/web_search_engines/engines/search_engine_guardian.py +39 -15
- local_deep_research/web_search_engines/engines/search_engine_local.py +58 -25
- local_deep_research/web_search_engines/engines/search_engine_local_all.py +15 -5
- local_deep_research/web_search_engines/engines/search_engine_pubmed.py +63 -21
- local_deep_research/web_search_engines/engines/search_engine_searxng.py +37 -11
- local_deep_research/web_search_engines/engines/search_engine_semantic_scholar.py +27 -9
- local_deep_research/web_search_engines/engines/search_engine_serpapi.py +12 -4
- local_deep_research/web_search_engines/engines/search_engine_wayback.py +31 -10
- local_deep_research/web_search_engines/engines/search_engine_wikipedia.py +12 -3
- local_deep_research/web_search_engines/search_engine_base.py +83 -35
- local_deep_research/web_search_engines/search_engine_factory.py +25 -8
- local_deep_research/web_search_engines/search_engines_config.py +9 -3
- {local_deep_research-0.4.4.dist-info → local_deep_research-0.5.2.dist-info}/METADATA +7 -1
- local_deep_research-0.5.2.dist-info/RECORD +265 -0
- local_deep_research-0.4.4.dist-info/RECORD +0 -177
- {local_deep_research-0.4.4.dist-info → local_deep_research-0.5.2.dist-info}/WHEEL +0 -0
- {local_deep_research-0.4.4.dist-info → local_deep_research-0.5.2.dist-info}/entry_points.txt +0 -0
- {local_deep_research-0.4.4.dist-info → local_deep_research-0.5.2.dist-info}/licenses/LICENSE +0 -0
@@ -41,7 +41,9 @@ def get_db_session() -> Session:
|
|
41
41
|
return current_app.extensions["sqlalchemy"].session()
|
42
42
|
|
43
43
|
|
44
|
-
def validate_setting(
|
44
|
+
def validate_setting(
|
45
|
+
setting: Setting, value: Any
|
46
|
+
) -> Tuple[bool, Optional[str]]:
|
45
47
|
"""
|
46
48
|
Validate a setting value based on its type and constraints.
|
47
49
|
|
@@ -101,7 +103,9 @@ def save_all_settings():
|
|
101
103
|
form_data = request.get_json()
|
102
104
|
if not form_data:
|
103
105
|
return (
|
104
|
-
jsonify(
|
106
|
+
jsonify(
|
107
|
+
{"status": "error", "message": "No settings data provided"}
|
108
|
+
),
|
105
109
|
400,
|
106
110
|
)
|
107
111
|
|
@@ -163,7 +167,8 @@ def save_all_settings():
|
|
163
167
|
|
164
168
|
# Special handling for corrupted or empty values
|
165
169
|
if value == "[object Object]" or (
|
166
|
-
isinstance(value, str)
|
170
|
+
isinstance(value, str)
|
171
|
+
and value.strip() in ["{}", "[]", "{", "["]
|
167
172
|
):
|
168
173
|
if key.startswith("report."):
|
169
174
|
value = {}
|
@@ -199,18 +204,22 @@ def save_all_settings():
|
|
199
204
|
|
200
205
|
if current_setting:
|
201
206
|
# Validate the setting
|
202
|
-
is_valid, error_message = validate_setting(
|
207
|
+
is_valid, error_message = validate_setting(
|
208
|
+
current_setting, value
|
209
|
+
)
|
203
210
|
|
204
211
|
if is_valid:
|
205
212
|
# Save the setting
|
206
|
-
success = set_setting(key, value
|
213
|
+
success = set_setting(key, value)
|
207
214
|
if success:
|
208
215
|
updated_settings.append(key)
|
209
216
|
|
210
217
|
# Track settings by type for exporting
|
211
218
|
if current_setting.type not in settings_by_type:
|
212
219
|
settings_by_type[current_setting.type] = []
|
213
|
-
settings_by_type[current_setting.type].append(
|
220
|
+
settings_by_type[current_setting.type].append(
|
221
|
+
current_setting
|
222
|
+
)
|
214
223
|
else:
|
215
224
|
# Add to validation errors
|
216
225
|
validation_errors.append(
|
@@ -235,7 +244,9 @@ def save_all_settings():
|
|
235
244
|
# Determine better UI element based on value type
|
236
245
|
if isinstance(value, bool):
|
237
246
|
new_setting["ui_element"] = "checkbox"
|
238
|
-
elif isinstance(value, (int, float)) and not isinstance(
|
247
|
+
elif isinstance(value, (int, float)) and not isinstance(
|
248
|
+
value, bool
|
249
|
+
):
|
239
250
|
new_setting["ui_element"] = "number"
|
240
251
|
elif isinstance(value, (dict, list)):
|
241
252
|
new_setting["ui_element"] = "textarea"
|
@@ -339,10 +350,15 @@ def save_all_settings():
|
|
339
350
|
}
|
340
351
|
)
|
341
352
|
|
342
|
-
except Exception
|
353
|
+
except Exception:
|
343
354
|
logger.exception("Error saving settings")
|
344
355
|
return (
|
345
|
-
jsonify(
|
356
|
+
jsonify(
|
357
|
+
{
|
358
|
+
"status": "error",
|
359
|
+
"message": "An internal error occurred while saving settings.",
|
360
|
+
}
|
361
|
+
),
|
346
362
|
500,
|
347
363
|
)
|
348
364
|
|
@@ -367,7 +383,9 @@ def reset_to_defaults():
|
|
367
383
|
# Fallback to predefined settings if file import fails
|
368
384
|
logger.info("Falling back to predefined settings")
|
369
385
|
# Import here to avoid circular imports
|
370
|
-
from ..database.migrations import
|
386
|
+
from ..database.migrations import (
|
387
|
+
setup_predefined_settings as setup_settings,
|
388
|
+
)
|
371
389
|
|
372
390
|
setup_settings(db_session)
|
373
391
|
|
@@ -400,7 +418,9 @@ def api_get_all_settings():
|
|
400
418
|
filtered_settings = {}
|
401
419
|
# Need to get all setting details to check category
|
402
420
|
db_settings = db_session.query(Setting).all()
|
403
|
-
category_keys = [
|
421
|
+
category_keys = [
|
422
|
+
s.key for s in db_settings if s.category == category
|
423
|
+
]
|
404
424
|
|
405
425
|
# Filter settings by keys
|
406
426
|
for key, value in settings.items():
|
@@ -424,12 +444,14 @@ def api_get_setting(key):
|
|
424
444
|
# get_settings_manager(db_session)
|
425
445
|
|
426
446
|
# Get setting
|
427
|
-
value = get_setting(key
|
447
|
+
value = get_setting(key)
|
428
448
|
if value is None:
|
429
449
|
return jsonify({"error": f"Setting not found: {key}"}), 404
|
430
450
|
|
431
451
|
# Get additional metadata from database.
|
432
|
-
db_setting =
|
452
|
+
db_setting = (
|
453
|
+
db_session.query(Setting).filter(Setting.key == key).first()
|
454
|
+
)
|
433
455
|
|
434
456
|
if db_setting:
|
435
457
|
# Return full setting details
|
@@ -477,7 +499,9 @@ def api_update_setting(key):
|
|
477
499
|
# get_settings_manager(db_session)
|
478
500
|
|
479
501
|
# Check if setting exists
|
480
|
-
db_setting =
|
502
|
+
db_setting = (
|
503
|
+
db_session.query(Setting).filter(Setting.key == key).first()
|
504
|
+
)
|
481
505
|
|
482
506
|
if db_setting:
|
483
507
|
# Check if setting is editable
|
@@ -487,9 +511,13 @@ def api_update_setting(key):
|
|
487
511
|
# Update setting
|
488
512
|
success = set_setting(key, value)
|
489
513
|
if success:
|
490
|
-
return jsonify(
|
514
|
+
return jsonify(
|
515
|
+
{"message": f"Setting {key} updated successfully"}
|
516
|
+
)
|
491
517
|
else:
|
492
|
-
return jsonify(
|
518
|
+
return jsonify(
|
519
|
+
{"error": f"Failed to update setting {key}"}
|
520
|
+
), 500
|
493
521
|
else:
|
494
522
|
# Create new setting with default metadata
|
495
523
|
setting_dict = {
|
@@ -535,7 +563,9 @@ def api_update_setting(key):
|
|
535
563
|
201,
|
536
564
|
)
|
537
565
|
else:
|
538
|
-
return jsonify(
|
566
|
+
return jsonify(
|
567
|
+
{"error": f"Failed to create setting {key}"}
|
568
|
+
), 500
|
539
569
|
except Exception as e:
|
540
570
|
logger.exception(f"Error updating setting {key}")
|
541
571
|
return jsonify({"error": str(e)}), 500
|
@@ -549,7 +579,9 @@ def api_delete_setting(key):
|
|
549
579
|
settings_manager = get_settings_manager(db_session)
|
550
580
|
|
551
581
|
# Check if setting exists
|
552
|
-
db_setting =
|
582
|
+
db_setting = (
|
583
|
+
db_session.query(Setting).filter(Setting.key == key).first()
|
584
|
+
)
|
553
585
|
if not db_setting:
|
554
586
|
return jsonify({"error": f"Setting not found: {key}"}), 404
|
555
587
|
|
@@ -568,8 +600,7 @@ def api_delete_setting(key):
|
|
568
600
|
def api_import_settings():
|
569
601
|
"""Import settings from defaults file"""
|
570
602
|
try:
|
571
|
-
|
572
|
-
settings_manager = get_settings_manager(db_session)
|
603
|
+
settings_manager = get_settings_manager(get_db_session())
|
573
604
|
|
574
605
|
success = settings_manager.load_from_defaults_file()
|
575
606
|
|
@@ -673,7 +704,9 @@ def api_get_available_models():
|
|
673
704
|
else "http://localhost:11434"
|
674
705
|
)
|
675
706
|
|
676
|
-
ollama_response = requests.get(
|
707
|
+
ollama_response = requests.get(
|
708
|
+
f"{base_url}/api/tags", timeout=5
|
709
|
+
)
|
677
710
|
|
678
711
|
logger.debug(
|
679
712
|
f"Ollama API response: Status {ollama_response.status_code}"
|
@@ -681,7 +714,9 @@ def api_get_available_models():
|
|
681
714
|
|
682
715
|
# Try to parse the response even if status code is not 200 to help with debugging
|
683
716
|
response_text = ollama_response.text
|
684
|
-
logger.debug(
|
717
|
+
logger.debug(
|
718
|
+
f"Ollama API raw response: {response_text[:500]}..."
|
719
|
+
)
|
685
720
|
|
686
721
|
if ollama_response.status_code == 200:
|
687
722
|
try:
|
@@ -700,7 +735,9 @@ def api_get_available_models():
|
|
700
735
|
name = model.get("name", "")
|
701
736
|
if name:
|
702
737
|
# Improved display name formatting
|
703
|
-
display_name = re.sub(
|
738
|
+
display_name = re.sub(
|
739
|
+
r"[:/]", " ", name
|
740
|
+
).strip()
|
704
741
|
display_name = " ".join(
|
705
742
|
word.capitalize()
|
706
743
|
for word in display_name.split()
|
@@ -725,7 +762,9 @@ def api_get_available_models():
|
|
725
762
|
name = model.get("name", "")
|
726
763
|
if name:
|
727
764
|
# Improved display name formatting
|
728
|
-
display_name = re.sub(
|
765
|
+
display_name = re.sub(
|
766
|
+
r"[:/]", " ", name
|
767
|
+
).strip()
|
729
768
|
display_name = " ".join(
|
730
769
|
word.capitalize()
|
731
770
|
for word in display_name.split()
|
@@ -748,7 +787,9 @@ def api_get_available_models():
|
|
748
787
|
logger.error(
|
749
788
|
f"Failed to parse Ollama API response as JSON: {json_err}"
|
750
789
|
)
|
751
|
-
raise Exception(
|
790
|
+
raise Exception(
|
791
|
+
f"Ollama API returned invalid JSON: {json_err}"
|
792
|
+
)
|
752
793
|
else:
|
753
794
|
logger.warning(
|
754
795
|
f"Ollama API returned non-200 status code: {ollama_response.status_code}"
|
@@ -760,7 +801,9 @@ def api_get_available_models():
|
|
760
801
|
except requests.exceptions.RequestException as e:
|
761
802
|
logger.warning(f"Could not connect to Ollama API: {str(e)}")
|
762
803
|
# Fallback to default models if Ollama is not running
|
763
|
-
logger.info(
|
804
|
+
logger.info(
|
805
|
+
"Using fallback Ollama models due to connection error"
|
806
|
+
)
|
764
807
|
ollama_models = [
|
765
808
|
{
|
766
809
|
"value": "llama3",
|
@@ -793,8 +836,16 @@ def api_get_available_models():
|
|
793
836
|
# Use fallback models
|
794
837
|
logger.info("Using fallback Ollama models due to error")
|
795
838
|
providers["ollama_models"] = [
|
796
|
-
{
|
797
|
-
|
839
|
+
{
|
840
|
+
"value": "llama3",
|
841
|
+
"label": "Llama 3 (Ollama)",
|
842
|
+
"provider": "OLLAMA",
|
843
|
+
},
|
844
|
+
{
|
845
|
+
"value": "mistral",
|
846
|
+
"label": "Mistral (Ollama)",
|
847
|
+
"provider": "OLLAMA",
|
848
|
+
},
|
798
849
|
{
|
799
850
|
"value": "gemma:latest",
|
800
851
|
"label": "Gemma (Ollama)",
|
@@ -833,7 +884,8 @@ def api_get_available_models():
|
|
833
884
|
# Create a clean display name
|
834
885
|
display_name = model_id.replace("-", " ").strip()
|
835
886
|
display_name = " ".join(
|
836
|
-
word.capitalize()
|
887
|
+
word.capitalize()
|
888
|
+
for word in display_name.split()
|
837
889
|
)
|
838
890
|
|
839
891
|
openai_endpoint_models.append(
|
@@ -883,7 +935,9 @@ def api_get_available_models():
|
|
883
935
|
model_id = model.get("id", "")
|
884
936
|
if model_id:
|
885
937
|
# Create a clean display name
|
886
|
-
display_name = model_id.replace(
|
938
|
+
display_name = model_id.replace(
|
939
|
+
"-", " "
|
940
|
+
).strip()
|
887
941
|
display_name = " ".join(
|
888
942
|
word.capitalize()
|
889
943
|
for word in display_name.split()
|
@@ -902,7 +956,9 @@ def api_get_available_models():
|
|
902
956
|
except Exception as e:
|
903
957
|
logger.error(f"Error getting OpenAI Endpoint models: {str(e)}")
|
904
958
|
# Use fallback models (empty in this case)
|
905
|
-
logger.info(
|
959
|
+
logger.info(
|
960
|
+
"Using fallback (empty) OpenAI Endpoint models due to error"
|
961
|
+
)
|
906
962
|
|
907
963
|
# Always set the openai_endpoint_models in providers
|
908
964
|
providers["openai_endpoint_models"] = openai_endpoint_models
|
@@ -913,7 +969,9 @@ def api_get_available_models():
|
|
913
969
|
# Get OpenAI models using the OpenAI package
|
914
970
|
openai_models = []
|
915
971
|
try:
|
916
|
-
logger.info(
|
972
|
+
logger.info(
|
973
|
+
"Attempting to connect to OpenAI API using OpenAI package"
|
974
|
+
)
|
917
975
|
|
918
976
|
# Get the API key from settings
|
919
977
|
api_key = get_db_setting("llm.openai.api_key", "")
|
@@ -938,7 +996,8 @@ def api_get_available_models():
|
|
938
996
|
# Create a clean display name
|
939
997
|
display_name = model_id.replace("-", " ").strip()
|
940
998
|
display_name = " ".join(
|
941
|
-
word.capitalize()
|
999
|
+
word.capitalize()
|
1000
|
+
for word in display_name.split()
|
942
1001
|
)
|
943
1002
|
|
944
1003
|
openai_models.append(
|
@@ -960,7 +1019,9 @@ def api_get_available_models():
|
|
960
1019
|
logger.info("No OpenAI models found due to API error")
|
961
1020
|
|
962
1021
|
else:
|
963
|
-
logger.info(
|
1022
|
+
logger.info(
|
1023
|
+
"OpenAI API key not configured, no models available"
|
1024
|
+
)
|
964
1025
|
|
965
1026
|
except ImportError:
|
966
1027
|
logger.warning("OpenAI package not installed. No models available.")
|
@@ -1001,7 +1062,8 @@ def api_get_available_models():
|
|
1001
1062
|
# Create a clean display name
|
1002
1063
|
display_name = model_id.replace("-", " ").strip()
|
1003
1064
|
display_name = " ".join(
|
1004
|
-
word.capitalize()
|
1065
|
+
word.capitalize()
|
1066
|
+
for word in display_name.split()
|
1005
1067
|
)
|
1006
1068
|
|
1007
1069
|
anthropic_models.append(
|
@@ -1035,7 +1097,9 @@ def api_get_available_models():
|
|
1035
1097
|
logger.info(f"Final Anthropic models count: {len(anthropic_models)}")
|
1036
1098
|
|
1037
1099
|
# Return all options
|
1038
|
-
return jsonify(
|
1100
|
+
return jsonify(
|
1101
|
+
{"provider_options": provider_options, "providers": providers}
|
1102
|
+
)
|
1039
1103
|
|
1040
1104
|
except Exception as e:
|
1041
1105
|
logger.exception("Error getting available models")
|
@@ -1068,7 +1132,10 @@ def api_get_available_search_engines():
|
|
1068
1132
|
|
1069
1133
|
description = (
|
1070
1134
|
db_session.query(Setting)
|
1071
|
-
.filter(
|
1135
|
+
.filter(
|
1136
|
+
Setting.key
|
1137
|
+
== f"search.engine.web.{engine_name}.description"
|
1138
|
+
)
|
1072
1139
|
.first()
|
1073
1140
|
)
|
1074
1141
|
if description is None:
|
@@ -1078,7 +1145,9 @@ def api_get_available_search_engines():
|
|
1078
1145
|
|
1079
1146
|
strengths = (
|
1080
1147
|
db_session.query(Setting)
|
1081
|
-
.filter(
|
1148
|
+
.filter(
|
1149
|
+
Setting.key == f"search.engine.web.{engine_name}.strengths"
|
1150
|
+
)
|
1082
1151
|
.first()
|
1083
1152
|
)
|
1084
1153
|
if strengths is None:
|
@@ -1088,7 +1157,9 @@ def api_get_available_search_engines():
|
|
1088
1157
|
strengths = strengths.value
|
1089
1158
|
|
1090
1159
|
engines_dict[engine_name] = dict(
|
1091
|
-
display_name=display_name,
|
1160
|
+
display_name=display_name,
|
1161
|
+
strengths=strengths,
|
1162
|
+
description=description,
|
1092
1163
|
)
|
1093
1164
|
|
1094
1165
|
# Format as options for dropdown
|
@@ -1100,7 +1171,9 @@ def api_get_available_search_engines():
|
|
1100
1171
|
for key in engines_dict.keys()
|
1101
1172
|
]
|
1102
1173
|
|
1103
|
-
return jsonify(
|
1174
|
+
return jsonify(
|
1175
|
+
{"engines": engines_dict, "engine_options": engine_options}
|
1176
|
+
)
|
1104
1177
|
|
1105
1178
|
except Exception as e:
|
1106
1179
|
logger.exception("Error getting available search engines")
|
@@ -1141,13 +1214,19 @@ def open_file_location():
|
|
1141
1214
|
flash("No file path provided", "error")
|
1142
1215
|
return redirect(url_for("settings.settings_page"))
|
1143
1216
|
|
1144
|
-
# Get the directory containing the file
|
1145
|
-
dir_path = os.path.dirname(os.path.abspath(file_path))
|
1146
|
-
|
1147
|
-
# Open the directory in the file explorer
|
1148
1217
|
try:
|
1218
|
+
# Validate the file path
|
1219
|
+
file_path = os.path.abspath(file_path)
|
1220
|
+
if not os.path.exists(file_path):
|
1221
|
+
flash("File path does not exist", "error")
|
1222
|
+
return redirect(url_for("settings.settings_page"))
|
1223
|
+
|
1224
|
+
# Get the directory containing the file
|
1225
|
+
dir_path = os.path.dirname(file_path)
|
1226
|
+
|
1227
|
+
# Open the directory in the file explorer
|
1149
1228
|
if platform.system() == "Windows":
|
1150
|
-
subprocess.Popen(
|
1229
|
+
subprocess.Popen(["explorer", dir_path])
|
1151
1230
|
elif platform.system() == "Darwin": # macOS
|
1152
1231
|
subprocess.Popen(["open", dir_path])
|
1153
1232
|
else: # Linux
|
@@ -1215,12 +1294,16 @@ def fix_corrupted_settings():
|
|
1215
1294
|
"app.knowledge_accumulation_context_limit",
|
1216
1295
|
"app.output_dir",
|
1217
1296
|
]:
|
1218
|
-
setting =
|
1297
|
+
setting = (
|
1298
|
+
db_session.query(Setting).filter(Setting.key == key).first()
|
1299
|
+
)
|
1219
1300
|
if setting:
|
1220
1301
|
# Move to proper category if not already there
|
1221
1302
|
proper_key = key.replace("app.", "report.")
|
1222
1303
|
existing_proper = (
|
1223
|
-
db_session.query(Setting)
|
1304
|
+
db_session.query(Setting)
|
1305
|
+
.filter(Setting.key == proper_key)
|
1306
|
+
.first()
|
1224
1307
|
)
|
1225
1308
|
|
1226
1309
|
if not existing_proper:
|
@@ -1261,12 +1344,16 @@ def fix_corrupted_settings():
|
|
1261
1344
|
"app.search_language",
|
1262
1345
|
"app.snippets_only",
|
1263
1346
|
]:
|
1264
|
-
setting =
|
1347
|
+
setting = (
|
1348
|
+
db_session.query(Setting).filter(Setting.key == key).first()
|
1349
|
+
)
|
1265
1350
|
if setting:
|
1266
1351
|
# Move to proper category if not already there
|
1267
1352
|
proper_key = key.replace("app.", "search.")
|
1268
1353
|
existing_proper = (
|
1269
|
-
db_session.query(Setting)
|
1354
|
+
db_session.query(Setting)
|
1355
|
+
.filter(Setting.key == proper_key)
|
1356
|
+
.first()
|
1270
1357
|
)
|
1271
1358
|
|
1272
1359
|
if not existing_proper:
|
@@ -1306,12 +1393,16 @@ def fix_corrupted_settings():
|
|
1306
1393
|
"app.lmstudio_url",
|
1307
1394
|
"app.llamacpp_model_path",
|
1308
1395
|
]:
|
1309
|
-
setting =
|
1396
|
+
setting = (
|
1397
|
+
db_session.query(Setting).filter(Setting.key == key).first()
|
1398
|
+
)
|
1310
1399
|
if setting:
|
1311
1400
|
# Move to proper category if not already there
|
1312
1401
|
proper_key = key.replace("app.", "llm.")
|
1313
1402
|
existing_proper = (
|
1314
|
-
db_session.query(Setting)
|
1403
|
+
db_session.query(Setting)
|
1404
|
+
.filter(Setting.key == proper_key)
|
1405
|
+
.first()
|
1315
1406
|
)
|
1316
1407
|
|
1317
1408
|
if not existing_proper:
|
@@ -1407,7 +1498,10 @@ def fix_corrupted_settings():
|
|
1407
1498
|
elif setting.key == "report.detailed_citations":
|
1408
1499
|
default_value = True
|
1409
1500
|
elif setting.key.startswith("app."):
|
1410
|
-
if
|
1501
|
+
if (
|
1502
|
+
setting.key == "app.theme"
|
1503
|
+
or setting.key == "app.default_theme"
|
1504
|
+
):
|
1411
1505
|
default_value = "dark"
|
1412
1506
|
elif setting.key == "app.enable_notifications":
|
1413
1507
|
default_value = True
|
@@ -1476,16 +1570,23 @@ def check_ollama_status():
|
|
1476
1570
|
"""Check if Ollama is running and available"""
|
1477
1571
|
try:
|
1478
1572
|
# Get Ollama URL from settings
|
1479
|
-
raw_base_url = get_db_setting(
|
1573
|
+
raw_base_url = get_db_setting(
|
1574
|
+
"llm.ollama.url", "http://localhost:11434"
|
1575
|
+
)
|
1480
1576
|
base_url = (
|
1481
|
-
normalize_url(raw_base_url)
|
1577
|
+
normalize_url(raw_base_url)
|
1578
|
+
if raw_base_url
|
1579
|
+
else "http://localhost:11434"
|
1482
1580
|
)
|
1483
1581
|
|
1484
1582
|
response = requests.get(f"{base_url}/api/version", timeout=2.0)
|
1485
1583
|
|
1486
1584
|
if response.status_code == 200:
|
1487
1585
|
return jsonify(
|
1488
|
-
{
|
1586
|
+
{
|
1587
|
+
"running": True,
|
1588
|
+
"version": response.json().get("version", "unknown"),
|
1589
|
+
}
|
1489
1590
|
)
|
1490
1591
|
else:
|
1491
1592
|
return jsonify(
|