local-deep-research 0.2.3__py3-none-any.whl → 0.3.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. local_deep_research/__init__.py +1 -1
  2. local_deep_research/__version__.py +1 -0
  3. local_deep_research/advanced_search_system/filters/cross_engine_filter.py +5 -1
  4. local_deep_research/advanced_search_system/strategies/base_strategy.py +5 -2
  5. local_deep_research/advanced_search_system/strategies/iterdrag_strategy.py +23 -16
  6. local_deep_research/advanced_search_system/strategies/parallel_search_strategy.py +13 -6
  7. local_deep_research/advanced_search_system/strategies/rapid_search_strategy.py +4 -3
  8. local_deep_research/advanced_search_system/strategies/source_based_strategy.py +57 -62
  9. local_deep_research/advanced_search_system/strategies/standard_strategy.py +8 -4
  10. local_deep_research/api/research_functions.py +0 -46
  11. local_deep_research/citation_handler.py +2 -5
  12. local_deep_research/config/llm_config.py +25 -68
  13. local_deep_research/config/search_config.py +8 -21
  14. local_deep_research/defaults/default_settings.json +3996 -0
  15. local_deep_research/search_system.py +34 -31
  16. local_deep_research/utilities/db_utils.py +22 -3
  17. local_deep_research/utilities/search_utilities.py +10 -7
  18. local_deep_research/web/app.py +3 -23
  19. local_deep_research/web/app_factory.py +1 -25
  20. local_deep_research/web/database/migrations.py +20 -418
  21. local_deep_research/web/routes/settings_routes.py +75 -364
  22. local_deep_research/web/services/research_service.py +43 -43
  23. local_deep_research/web/services/settings_manager.py +108 -315
  24. local_deep_research/web/services/settings_service.py +3 -56
  25. local_deep_research/web/static/js/components/research.js +1 -1
  26. local_deep_research/web/static/js/components/settings.js +16 -4
  27. local_deep_research/web/static/js/research_form.js +106 -0
  28. local_deep_research/web/templates/pages/research.html +3 -2
  29. local_deep_research/web_search_engines/engines/meta_search_engine.py +56 -21
  30. local_deep_research/web_search_engines/engines/search_engine_local.py +11 -2
  31. local_deep_research/web_search_engines/engines/search_engine_local_all.py +7 -11
  32. local_deep_research/web_search_engines/search_engine_factory.py +12 -64
  33. local_deep_research/web_search_engines/search_engines_config.py +123 -64
  34. {local_deep_research-0.2.3.dist-info → local_deep_research-0.3.1.dist-info}/METADATA +16 -1
  35. {local_deep_research-0.2.3.dist-info → local_deep_research-0.3.1.dist-info}/RECORD +38 -39
  36. local_deep_research/config/config_files.py +0 -245
  37. local_deep_research/defaults/local_collections.toml +0 -53
  38. local_deep_research/defaults/main.toml +0 -80
  39. local_deep_research/defaults/search_engines.toml +0 -291
  40. {local_deep_research-0.2.3.dist-info → local_deep_research-0.3.1.dist-info}/WHEEL +0 -0
  41. {local_deep_research-0.2.3.dist-info → local_deep_research-0.3.1.dist-info}/entry_points.txt +0 -0
  42. {local_deep_research-0.2.3.dist-info → local_deep_research-0.3.1.dist-info}/licenses/LICENSE +0 -0
@@ -3,11 +3,9 @@ import logging
3
3
  import os
4
4
  import platform
5
5
  import subprocess
6
- from pathlib import Path
7
6
  from typing import Any, Optional, Tuple
8
7
 
9
8
  import requests
10
- import toml
11
9
  from flask import (
12
10
  Blueprint,
13
11
  current_app,
@@ -21,8 +19,6 @@ from flask import (
21
19
  from flask_wtf.csrf import generate_csrf
22
20
  from sqlalchemy.orm import Session
23
21
 
24
- from ...config.config_files import get_config_dir
25
- from ...web_search_engines.search_engine_factory import get_available_engines
26
22
  from ..database.models import Setting, SettingType
27
23
  from ..services.settings_service import (
28
24
  create_or_update_setting,
@@ -37,23 +33,6 @@ logger = logging.getLogger(__name__)
37
33
  # Create a Blueprint for settings
38
34
  settings_bp = Blueprint("settings", __name__, url_prefix="/research/settings")
39
35
 
40
- # Legacy config for backwards compatibility
41
- SEARCH_ENGINES_FILE = None
42
- CONFIG_DIR = None
43
- MAIN_CONFIG_FILE = None
44
- LOCAL_COLLECTIONS_FILE = None
45
-
46
-
47
- def set_config_paths(
48
- config_dir, search_engines_file, main_config_file, local_collections_file
49
- ):
50
- """Set the config paths for the settings routes (legacy support)"""
51
- global CONFIG_DIR, SEARCH_ENGINES_FILE, MAIN_CONFIG_FILE, LOCAL_COLLECTIONS_FILE
52
- CONFIG_DIR = config_dir
53
- SEARCH_ENGINES_FILE = search_engines_file
54
- MAIN_CONFIG_FILE = main_config_file
55
- LOCAL_COLLECTIONS_FILE = local_collections_file
56
-
57
36
 
58
37
  def get_db_session() -> Session:
59
38
  """Get the database session from the app context"""
@@ -105,56 +84,6 @@ def validate_setting(setting: Setting, value: Any) -> Tuple[bool, Optional[str]]
105
84
  return True, None
106
85
 
107
86
 
108
- def get_all_settings_json():
109
- """Get all settings as a JSON-serializable dictionary
110
-
111
- Returns:
112
- List of setting dictionaries
113
- """
114
- db_session = get_db_session()
115
- settings_list = []
116
-
117
- # Get all settings
118
- settings = (
119
- db_session.query(Setting)
120
- .order_by(Setting.type, Setting.category, Setting.name)
121
- .all()
122
- )
123
-
124
- # Convert to dictionaries
125
- for setting in settings:
126
- # Ensure objects are properly serialized
127
- value = setting.value
128
-
129
- # Convert objects to properly formatted JSON strings for display
130
- if isinstance(value, (dict, list)) and value:
131
- try:
132
- # For frontend display, we'll keep objects as they are
133
- # The javascript will handle formatting them
134
- pass
135
- except Exception as e:
136
- logger.error(f"Error serializing setting {setting.key}: {e}")
137
-
138
- setting_dict = {
139
- "key": setting.key,
140
- "value": value,
141
- "type": setting.type.value if setting.type else None,
142
- "name": setting.name,
143
- "description": setting.description,
144
- "category": setting.category,
145
- "ui_element": setting.ui_element,
146
- "options": setting.options,
147
- "min_value": setting.min_value,
148
- "max_value": setting.max_value,
149
- "step": setting.step,
150
- "visible": setting.visible,
151
- "editable": setting.editable,
152
- }
153
- settings_list.append(setting_dict)
154
-
155
- return settings_list
156
-
157
-
158
87
  @settings_bp.route("/", methods=["GET"])
159
88
  def settings_page():
160
89
  """Main settings dashboard with links to specialized config pages"""
@@ -202,7 +131,6 @@ def save_all_settings():
202
131
  original_values[key] = current_setting.value
203
132
 
204
133
  # Determine setting type and category
205
- setting_type = None
206
134
  if key.startswith("llm."):
207
135
  setting_type = SettingType.LLM
208
136
  category = "llm_general"
@@ -231,9 +159,8 @@ def save_all_settings():
231
159
  setting_type = SettingType.APP
232
160
  category = "app_interface"
233
161
  else:
234
- # Skip keys without a known prefix
235
- logger.warning(f"Skipping setting with unknown type: {key}")
236
- continue
162
+ setting_type = None
163
+ category = None
237
164
 
238
165
  # Special handling for corrupted or empty values
239
166
  if value == "[object Object]" or (
@@ -276,10 +203,6 @@ def save_all_settings():
276
203
  is_valid, error_message = validate_setting(current_setting, value)
277
204
 
278
205
  if is_valid:
279
- # Update category if different from our determination
280
- if category and current_setting.category != category:
281
- current_setting.category = category
282
-
283
206
  # Save the setting
284
207
  success = set_setting(key, value, db_session=db_session)
285
208
  if success:
@@ -425,128 +348,37 @@ def save_all_settings():
425
348
  )
426
349
 
427
350
 
428
- @settings_bp.route("/reset_to_defaults", methods=["POST"])
351
+ @settings_bp.route("/reset_to_defaults", methods=["GET"])
429
352
  def reset_to_defaults():
430
353
  """Reset all settings to their default values"""
431
354
  db_session = get_db_session()
432
355
 
356
+ # Import default settings from files
433
357
  try:
434
- # First, delete all existing settings to ensure clean state
435
- try:
436
- # Get count before deletion
437
- settings_count = db_session.query(Setting).count()
438
- logger.info(f"Deleting {settings_count} existing settings before reset")
439
-
440
- # Delete all settings
441
- db_session.query(Setting).delete()
442
- db_session.commit()
443
- logger.info("Successfully deleted all existing settings")
444
- except Exception as e:
445
- logger.error(f"Error deleting existing settings: {e}")
446
- db_session.rollback()
447
- return (
448
- jsonify(
449
- {
450
- "status": "error",
451
- "message": f"Error cleaning existing settings: {str(e)}",
452
- }
453
- ),
454
- 500,
455
- )
456
-
457
- # Import default settings from files
458
- try:
459
- # Import default config files from the defaults directory
460
- from importlib.resources import files
461
-
462
- try:
463
- defaults_dir = files("local_deep_research.defaults")
464
- except ImportError:
465
- # Fallback for older Python versions
466
- from pkg_resources import resource_filename
358
+ # Create settings manager for the temporary config
359
+ settings_mgr = get_settings_manager(db_session)
360
+ # Import settings from default files
361
+ settings_mgr.load_from_defaults_file()
467
362
 
468
- defaults_dir = Path(
469
- resource_filename("local_deep_research", "defaults")
470
- )
471
-
472
- logger.info(f"Loading defaults from: {defaults_dir}")
473
-
474
- # Get temporary path to default files
475
- import tempfile
476
-
477
- with tempfile.TemporaryDirectory() as temp_dir:
478
- # Copy default files to temp directory
479
- temp_main = Path(temp_dir) / "settings.toml"
480
- temp_search = Path(temp_dir) / "search_engines.toml"
481
- temp_collections = Path(temp_dir) / "local_collections.toml"
482
-
483
- # Copy default files (platform independent)
484
- import importlib.resources as pkg_resources
485
-
486
- from ... import defaults
487
-
488
- with open(temp_main, "wb") as f:
489
- f.write(pkg_resources.read_binary(defaults, "main.toml"))
490
-
491
- with open(temp_search, "wb") as f:
492
- f.write(pkg_resources.read_binary(defaults, "search_engines.toml"))
493
-
494
- with open(temp_collections, "wb") as f:
495
- f.write(
496
- pkg_resources.read_binary(defaults, "local_collections.toml")
497
- )
498
-
499
- # Create settings manager with temp files
500
- # Get configuration directory (not used currently but might be needed in future)
501
- # config_dir = get_config_dir() / "config"
502
-
503
- # Create settings manager for the temporary config
504
- settings_mgr = get_settings_manager(db_session)
505
-
506
- # Import settings from default files
507
- settings_mgr.import_default_settings(
508
- temp_main, temp_search, temp_collections
509
- )
510
-
511
- logger.info("Successfully imported settings from default files")
512
- except Exception as e:
513
- logger.error(f"Error importing default settings: {e}")
514
-
515
- # Fallback to predefined settings if file import fails
516
- logger.info("Falling back to predefined settings")
517
- # Import here to avoid circular imports
518
- from ..database.migrations import (
519
- setup_predefined_settings as setup_settings,
520
- )
521
-
522
- setup_settings(db_session)
523
-
524
- # Return success
525
- return jsonify(
526
- {
527
- "status": "success",
528
- "message": "All settings have been reset to default values",
529
- }
530
- )
363
+ logger.info("Successfully imported settings from default files")
531
364
 
532
365
  except Exception as e:
533
- logger.error(f"Error resetting settings to defaults: {e}")
534
- return (
535
- jsonify(
536
- {
537
- "status": "error",
538
- "message": f"Error resetting settings to defaults: {str(e)}",
539
- }
540
- ),
541
- 500,
542
- )
366
+ logger.error(f"Error importing default settings: {e}")
543
367
 
368
+ # Fallback to predefined settings if file import fails
369
+ logger.info("Falling back to predefined settings")
370
+ # Import here to avoid circular imports
371
+ from ..database.migrations import setup_predefined_settings as setup_settings
544
372
 
545
- @settings_bp.route("/all_settings", methods=["GET"])
546
- def get_all_settings_route():
547
- """Get all settings for the unified dashboard"""
548
- settings_list = get_all_settings_json()
549
- return jsonify({"status": "success", "settings": settings_list})
373
+ setup_settings(db_session)
374
+
375
+ # Return success
376
+ return jsonify(
377
+ {
378
+ "status": "success",
379
+ "message": "All settings have been reset to default values",
380
+ }
381
+ )
550
382
 
551
383
 
552
384
  # API Routes
@@ -555,7 +387,6 @@ def api_get_all_settings():
555
387
  """Get all settings"""
556
388
  try:
557
389
  # Get query parameters
558
- setting_type = request.args.get("type")
559
390
  category = request.args.get("category")
560
391
 
561
392
  # Create settings manager
@@ -563,14 +394,7 @@ def api_get_all_settings():
563
394
  settings_manager = get_settings_manager(db_session)
564
395
 
565
396
  # Get settings
566
- if setting_type:
567
- try:
568
- setting_type_enum = SettingType[setting_type.upper()]
569
- settings = settings_manager.get_all_settings(setting_type_enum)
570
- except KeyError:
571
- return jsonify({"error": f"Invalid setting type: {setting_type}"}), 400
572
- else:
573
- settings = settings_manager.get_all_settings()
397
+ settings = settings_manager.get_all_settings()
574
398
 
575
399
  # Filter by category if requested
576
400
  if category:
@@ -586,7 +410,7 @@ def api_get_all_settings():
586
410
 
587
411
  settings = filtered_settings
588
412
 
589
- return jsonify({"settings": settings})
413
+ return jsonify({"status": "success", "settings": settings})
590
414
  except Exception as e:
591
415
  logger.error(f"Error getting settings: {e}")
592
416
  return jsonify({"error": str(e)}), 500
@@ -741,60 +565,14 @@ def api_delete_setting(key):
741
565
  return jsonify({"error": str(e)}), 500
742
566
 
743
567
 
744
- @settings_bp.route("/api/export", methods=["POST"])
745
- def api_export_settings():
746
- """Export settings to file"""
747
- try:
748
- data = request.get_json() or {}
749
- setting_type_str = data.get("type")
750
-
751
- db_session = get_db_session()
752
- settings_manager = get_settings_manager(db_session)
753
-
754
- # Export settings
755
- if setting_type_str:
756
- try:
757
- setting_type = SettingType[setting_type_str.upper()]
758
- success = settings_manager.export_to_file(setting_type)
759
- except KeyError:
760
- return (
761
- jsonify({"error": f"Invalid setting type: {setting_type_str}"}),
762
- 400,
763
- )
764
- else:
765
- success = settings_manager.export_to_file()
766
-
767
- if success:
768
- return jsonify({"message": "Settings exported successfully"})
769
- else:
770
- return jsonify({"error": "Failed to export settings"}), 500
771
- except Exception as e:
772
- logger.error(f"Error exporting settings: {e}")
773
- return jsonify({"error": str(e)}), 500
774
-
775
-
776
568
  @settings_bp.route("/api/import", methods=["POST"])
777
569
  def api_import_settings():
778
- """Import settings from file"""
570
+ """Import settings from defaults file"""
779
571
  try:
780
- data = request.get_json() or {}
781
- setting_type_str = data.get("type")
782
-
783
572
  db_session = get_db_session()
784
573
  settings_manager = get_settings_manager(db_session)
785
574
 
786
- # Import settings
787
- if setting_type_str:
788
- try:
789
- setting_type = SettingType[setting_type_str.upper()]
790
- success = settings_manager.import_from_file(setting_type)
791
- except KeyError:
792
- return (
793
- jsonify({"error": f"Invalid setting type: {setting_type_str}"}),
794
- 400,
795
- )
796
- else:
797
- success = settings_manager.import_from_file()
575
+ success = settings_manager.load_from_defaults_file()
798
576
 
799
577
  if success:
800
578
  return jsonify({"message": "Settings imported successfully"})
@@ -1065,131 +843,67 @@ def api_get_available_models():
1065
843
  def api_get_available_search_engines():
1066
844
  """Get available search engines"""
1067
845
  try:
1068
- # First try to get engines from search_engines.toml file
1069
- engines_dict = get_engines_from_file()
1070
-
1071
- # If we got engines from file, use those
1072
- if engines_dict:
1073
- # Make sure searxng is included if it should be
1074
- if "searxng" not in engines_dict:
1075
- engines_dict["searxng"] = {
1076
- "display_name": "SearXNG (Self-hosted)",
1077
- "description": "Self-hosted metasearch engine",
1078
- "strengths": ["privacy", "customization", "no API key needed"],
1079
- }
1080
-
1081
- # Format as options for dropdown
1082
- engine_options = [
1083
- {
1084
- "value": key,
1085
- "label": engines_dict.get(key, {}).get("display_name", key),
1086
- }
1087
- for key in engines_dict.keys()
1088
- ]
1089
-
1090
- return jsonify({"engines": engines_dict, "engine_options": engine_options})
846
+ # Find search engines that are set in the DB.
847
+ db_session = get_db_session()
848
+ name_settings = (
849
+ db_session.query(Setting)
850
+ .filter(Setting.type == "SEARCH")
851
+ .filter(Setting.key.startswith("search.engine"))
852
+ .filter(Setting.key.endswith(".display_name"))
853
+ ).all()
854
+
855
+ # These should all correspond to different search engines.
856
+ engines_dict = {}
857
+ for setting in name_settings:
858
+ key_parts = setting.key.split(".")
859
+ if key_parts[2] == "auto":
860
+ # The auto engine is not in the web or local category.
861
+ engine_name = "auto"
862
+ else:
863
+ engine_name = setting.key.split(".")[3]
864
+ display_name = setting.value
1091
865
 
1092
- # Fallback to factory function if file method failed
1093
- try:
1094
- # Get available engines
1095
- search_engines = get_available_engines(include_api_key_services=True)
1096
-
1097
- # Handle if search_engines is a list (not a dict)
1098
- if isinstance(search_engines, list):
1099
- # Convert to dict with engine name as key and display name as value
1100
- engines_dict = {
1101
- engine: engine.replace("_", " ").title()
1102
- for engine in search_engines
1103
- }
866
+ description = (
867
+ db_session.query(Setting)
868
+ .filter(Setting.key == f"search.engine.web.{engine_name}.description")
869
+ .first()
870
+ )
871
+ if description is None:
872
+ description = ""
1104
873
  else:
1105
- engines_dict = search_engines
874
+ description = description.value
1106
875
 
1107
- # Make sure searxng is included
1108
- if "searxng" not in engines_dict:
1109
- engines_dict["searxng"] = "SearXNG (Self-hosted)"
876
+ strengths = (
877
+ db_session.query(Setting)
878
+ .filter(Setting.key == f"search.engine.web.{engine_name}.strengths")
879
+ .first()
880
+ )
881
+ if strengths is None:
882
+ # No strengths in DB.
883
+ strengths = []
884
+ else:
885
+ strengths = strengths.value
1110
886
 
1111
- # Format as options for dropdown
1112
- engine_options = [
1113
- {
1114
- "value": key,
1115
- "label": (
1116
- value
1117
- if isinstance(value, str)
1118
- else key.replace("_", " ").title()
1119
- ),
1120
- }
1121
- for key, value in engines_dict.items()
1122
- ]
887
+ engines_dict[engine_name] = dict(
888
+ display_name=display_name, strengths=strengths, description=description
889
+ )
1123
890
 
1124
- return jsonify({"engines": engines_dict, "engine_options": engine_options})
1125
- except Exception as e:
1126
- # If both methods fail, return default engines with searxng
1127
- logger.error(f"Error getting available search engines from factory: {e}")
1128
-
1129
- # Use hardcoded defaults from search_engines.toml
1130
- defaults = {
1131
- "wikipedia": "Wikipedia",
1132
- "arxiv": "ArXiv Papers",
1133
- "pubmed": "PubMed Medical",
1134
- "github": "GitHub Code",
1135
- "searxng": "SearXNG (Self-hosted)",
1136
- "serpapi": "SerpAPI (Google)",
1137
- "google_pse": "Google PSE",
1138
- "auto": "Auto-select",
891
+ # Format as options for dropdown
892
+ engine_options = [
893
+ {
894
+ "value": key,
895
+ "label": engines_dict.get(key, {}).get("display_name", key),
1139
896
  }
897
+ for key in engines_dict.keys()
898
+ ]
1140
899
 
1141
- engine_options = [
1142
- {"value": key, "label": value} for key, value in defaults.items()
1143
- ]
900
+ return jsonify({"engines": engines_dict, "engine_options": engine_options})
1144
901
 
1145
- return jsonify({"engines": defaults, "engine_options": engine_options})
1146
902
  except Exception as e:
1147
903
  logger.error(f"Error getting available search engines: {e}")
1148
904
  return jsonify({"error": str(e)}), 500
1149
905
 
1150
906
 
1151
- def get_engines_from_file():
1152
- """Get available search engines directly from the toml file"""
1153
- try:
1154
- # Try to load from the actual config directory
1155
- config_dir = get_config_dir()
1156
- search_engines_file = config_dir / "config" / "search_engines.toml"
1157
-
1158
- # If file doesn't exist in user config, try the defaults
1159
- if not search_engines_file.exists():
1160
- # Look in the defaults folder instead
1161
- import inspect
1162
-
1163
- from ...defaults import search_engines
1164
-
1165
- # Get the path to the search_engines.toml file
1166
- module_path = inspect.getfile(search_engines)
1167
- default_file = Path(module_path)
1168
-
1169
- if default_file.exists() and default_file.suffix == ".toml":
1170
- search_engines_file = default_file
1171
-
1172
- # If we found a file, load it
1173
- if search_engines_file.exists():
1174
- data = toml.load(search_engines_file)
1175
-
1176
- # Filter out the metadata entries (like DEFAULT_SEARCH_ENGINE)
1177
- engines = {k: v for k, v in data.items() if isinstance(v, dict)}
1178
-
1179
- # Add display names for each engine
1180
- for key, engine in engines.items():
1181
- if "display_name" not in engine:
1182
- # Create a display name from the key
1183
- engine["display_name"] = key.replace("_", " ").title()
1184
-
1185
- return engines
1186
-
1187
- return None
1188
- except Exception as e:
1189
- logger.error(f"Error loading search engines from file: {e}")
1190
- return None
1191
-
1192
-
1193
907
  # Legacy routes for backward compatibility - these will redirect to the new routes
1194
908
  @settings_bp.route("/main", methods=["GET"])
1195
909
  def main_config_page():
@@ -1334,7 +1048,6 @@ def fix_corrupted_settings():
1334
1048
 
1335
1049
  # Search settings
1336
1050
  for key in [
1337
- "app.research_iterations",
1338
1051
  "app.questions_per_iteration",
1339
1052
  "app.search_engine",
1340
1053
  "app.iterations",
@@ -1472,8 +1185,6 @@ def fix_corrupted_settings():
1472
1185
  default_value = 10
1473
1186
  elif setting.key == "search.region":
1474
1187
  default_value = "us"
1475
- elif setting.key == "search.research_iterations":
1476
- default_value = 2
1477
1188
  elif setting.key == "search.questions_per_iteration":
1478
1189
  default_value = 3
1479
1190
  elif setting.key == "search.searches_per_section":
@@ -259,53 +259,53 @@ def run_research_process(
259
259
  f"Overriding system settings with: provider={model_provider}, model={model}, search_engine={search_engine}"
260
260
  )
261
261
 
262
- # Override LLM if model or model_provider specified
263
- if model or model_provider:
264
- try:
265
- # Get LLM with the overridden settings
266
- # Explicitly create the model with parameters to avoid fallback issues
267
- use_llm = get_llm(
268
- model_name=model,
269
- provider=model_provider,
270
- openai_endpoint_url=custom_endpoint,
271
- )
262
+ # Override LLM if model or model_provider specified
263
+ if model or model_provider:
264
+ try:
265
+ # Get LLM with the overridden settings
266
+ # Explicitly create the model with parameters to avoid fallback issues
267
+ use_llm = get_llm(
268
+ model_name=model,
269
+ provider=model_provider,
270
+ openai_endpoint_url=custom_endpoint,
271
+ )
272
272
 
273
- logger.info(
274
- "Successfully set LLM to: provider=%s, model=%s",
275
- model_provider,
276
- model,
277
- )
278
- except Exception as e:
279
- logger.error(
280
- "Error setting LLM provider=%s, model=%s: %s",
281
- model_provider,
282
- model,
283
- str(e),
284
- )
285
- logger.error(traceback.format_exc())
273
+ logger.info(
274
+ "Successfully set LLM to: provider=%s, model=%s",
275
+ model_provider,
276
+ model,
277
+ )
278
+ except Exception as e:
279
+ logger.error(
280
+ "Error setting LLM provider=%s, model=%s: %s",
281
+ model_provider,
282
+ model,
283
+ str(e),
284
+ )
285
+ logger.error(traceback.format_exc())
286
286
 
287
- # Set the progress callback in the system
288
- system = AdvancedSearchSystem(llm=use_llm)
289
- system.set_progress_callback(progress_callback)
287
+ # Set the progress callback in the system
288
+ system = AdvancedSearchSystem(llm=use_llm)
289
+ system.set_progress_callback(progress_callback)
290
290
 
291
- # Override search engine if specified
292
- if search_engine:
293
- try:
294
- if iterations:
295
- system.max_iterations = int(iterations)
296
- if questions_per_iteration:
297
- system.questions_per_iteration = int(questions_per_iteration)
298
-
299
- # Create a new search object with these settings
300
- system.search = get_search(
301
- search_tool=search_engine, llm_instance=system.model
302
- )
291
+ # Override search engine if specified
292
+ if search_engine:
293
+ try:
294
+ if iterations:
295
+ system.max_iterations = int(iterations)
296
+ if questions_per_iteration:
297
+ system.questions_per_iteration = int(questions_per_iteration)
298
+
299
+ # Create a new search object with these settings
300
+ system.search = get_search(
301
+ search_tool=search_engine, llm_instance=system.model
302
+ )
303
303
 
304
- logger.info("Successfully set search engine to: %s", search_engine)
305
- except Exception as e:
306
- logger.error(
307
- "Error setting search engine to %s: %s", search_engine, str(e)
308
- )
304
+ logger.info("Successfully set search engine to: %s", search_engine)
305
+ except Exception as e:
306
+ logger.error(
307
+ "Error setting search engine to %s: %s", search_engine, str(e)
308
+ )
309
309
 
310
310
  # Run the search
311
311
  progress_callback("Starting research process", 5, {"phase": "init"})