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,294 @@
|
|
1
|
+
import json
|
2
|
+
import logging
|
3
|
+
import os
|
4
|
+
import sqlite3
|
5
|
+
import traceback
|
6
|
+
from datetime import datetime
|
7
|
+
|
8
|
+
# Initialize logger
|
9
|
+
logger = logging.getLogger(__name__)
|
10
|
+
|
11
|
+
# Database path
|
12
|
+
# Use unified database in data directory
|
13
|
+
DATA_DIR = os.path.abspath(
|
14
|
+
os.path.join(os.path.dirname(__file__), "..", "..", "..", "data")
|
15
|
+
)
|
16
|
+
os.makedirs(DATA_DIR, exist_ok=True)
|
17
|
+
DB_PATH = os.path.join(DATA_DIR, "ldr.db")
|
18
|
+
|
19
|
+
# Legacy database paths (for migration)
|
20
|
+
LEGACY_RESEARCH_HISTORY_DB = os.path.abspath(
|
21
|
+
os.path.join(os.path.dirname(__file__), "..", "..", "..", "research_history.db")
|
22
|
+
)
|
23
|
+
LEGACY_DEEP_RESEARCH_DB = os.path.join(
|
24
|
+
os.path.abspath(
|
25
|
+
os.path.join(os.path.dirname(__file__), "..", "..", "..", "..", "data")
|
26
|
+
),
|
27
|
+
"deep_research.db",
|
28
|
+
)
|
29
|
+
|
30
|
+
|
31
|
+
def get_db_connection():
|
32
|
+
"""
|
33
|
+
Get a connection to the SQLite database.
|
34
|
+
Allows for custom row factory if needed.
|
35
|
+
"""
|
36
|
+
conn = sqlite3.connect(DB_PATH)
|
37
|
+
return conn
|
38
|
+
|
39
|
+
|
40
|
+
def init_db():
|
41
|
+
"""Initialize the database with necessary tables."""
|
42
|
+
conn = get_db_connection()
|
43
|
+
cursor = conn.cursor()
|
44
|
+
|
45
|
+
# Create the table if it doesn't exist
|
46
|
+
cursor.execute(
|
47
|
+
"""
|
48
|
+
CREATE TABLE IF NOT EXISTS research_history (
|
49
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
50
|
+
query TEXT NOT NULL,
|
51
|
+
mode TEXT NOT NULL,
|
52
|
+
status TEXT NOT NULL,
|
53
|
+
created_at TEXT NOT NULL,
|
54
|
+
completed_at TEXT,
|
55
|
+
duration_seconds INTEGER,
|
56
|
+
report_path TEXT,
|
57
|
+
metadata TEXT,
|
58
|
+
progress_log TEXT,
|
59
|
+
progress INTEGER,
|
60
|
+
title TEXT
|
61
|
+
)
|
62
|
+
"""
|
63
|
+
)
|
64
|
+
|
65
|
+
# Create a dedicated table for research logs
|
66
|
+
cursor.execute(
|
67
|
+
"""
|
68
|
+
CREATE TABLE IF NOT EXISTS research_logs (
|
69
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
70
|
+
research_id INTEGER NOT NULL,
|
71
|
+
timestamp TEXT NOT NULL,
|
72
|
+
message TEXT NOT NULL,
|
73
|
+
log_type TEXT NOT NULL,
|
74
|
+
progress INTEGER,
|
75
|
+
metadata TEXT,
|
76
|
+
FOREIGN KEY (research_id) REFERENCES research_history (id) ON DELETE CASCADE
|
77
|
+
)
|
78
|
+
"""
|
79
|
+
)
|
80
|
+
|
81
|
+
# Create a dedicated table for research resources
|
82
|
+
cursor.execute(
|
83
|
+
"""
|
84
|
+
CREATE TABLE IF NOT EXISTS research_resources (
|
85
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
86
|
+
research_id INTEGER NOT NULL,
|
87
|
+
title TEXT,
|
88
|
+
url TEXT,
|
89
|
+
content_preview TEXT,
|
90
|
+
source_type TEXT,
|
91
|
+
metadata TEXT,
|
92
|
+
created_at TEXT NOT NULL,
|
93
|
+
FOREIGN KEY (research_id) REFERENCES research_history (id) ON DELETE CASCADE
|
94
|
+
)
|
95
|
+
"""
|
96
|
+
)
|
97
|
+
|
98
|
+
# Check if the duration_seconds column exists, add it if missing
|
99
|
+
cursor.execute("PRAGMA table_info(research_history)")
|
100
|
+
columns = [column[1] for column in cursor.fetchall()]
|
101
|
+
|
102
|
+
if "duration_seconds" not in columns:
|
103
|
+
print("Adding missing 'duration_seconds' column to research_history table")
|
104
|
+
cursor.execute(
|
105
|
+
"ALTER TABLE research_history ADD COLUMN duration_seconds INTEGER"
|
106
|
+
)
|
107
|
+
|
108
|
+
# Check if the progress column exists, add it if missing
|
109
|
+
if "progress" not in columns:
|
110
|
+
print("Adding missing 'progress' column to research_history table")
|
111
|
+
cursor.execute("ALTER TABLE research_history ADD COLUMN progress INTEGER")
|
112
|
+
|
113
|
+
# Check if the title column exists, add it if missing
|
114
|
+
if "title" not in columns:
|
115
|
+
print("Adding missing 'title' column to research_history table")
|
116
|
+
cursor.execute("ALTER TABLE research_history ADD COLUMN title TEXT")
|
117
|
+
|
118
|
+
# Enable foreign key support
|
119
|
+
cursor.execute("PRAGMA foreign_keys = ON")
|
120
|
+
|
121
|
+
conn.commit()
|
122
|
+
conn.close()
|
123
|
+
|
124
|
+
|
125
|
+
def calculate_duration(created_at_str, completed_at_str=None):
|
126
|
+
"""
|
127
|
+
Calculate duration in seconds between created_at timestamp and completed_at or now.
|
128
|
+
Handles various timestamp formats and returns None if calculation fails.
|
129
|
+
|
130
|
+
Args:
|
131
|
+
created_at_str: The start timestamp
|
132
|
+
completed_at_str: Optional end timestamp, defaults to current time if None
|
133
|
+
|
134
|
+
Returns:
|
135
|
+
Duration in seconds or None if calculation fails
|
136
|
+
"""
|
137
|
+
if not created_at_str:
|
138
|
+
return None
|
139
|
+
|
140
|
+
end_time = None
|
141
|
+
if completed_at_str:
|
142
|
+
# Use completed_at time if provided
|
143
|
+
try:
|
144
|
+
if "T" in completed_at_str: # ISO format with T separator
|
145
|
+
end_time = datetime.fromisoformat(completed_at_str)
|
146
|
+
else: # Older format without T
|
147
|
+
# Try different formats
|
148
|
+
try:
|
149
|
+
end_time = datetime.strptime(
|
150
|
+
completed_at_str, "%Y-%m-%d %H:%M:%S.%f"
|
151
|
+
)
|
152
|
+
except ValueError:
|
153
|
+
try:
|
154
|
+
end_time = datetime.strptime(
|
155
|
+
completed_at_str, "%Y-%m-%d %H:%M:%S"
|
156
|
+
)
|
157
|
+
except ValueError:
|
158
|
+
# Last resort fallback
|
159
|
+
end_time = datetime.fromisoformat(
|
160
|
+
completed_at_str.replace(" ", "T")
|
161
|
+
)
|
162
|
+
except Exception as e:
|
163
|
+
print(f"Error parsing completed_at timestamp: {str(e)}")
|
164
|
+
try:
|
165
|
+
from dateutil import parser
|
166
|
+
|
167
|
+
end_time = parser.parse(completed_at_str)
|
168
|
+
except Exception:
|
169
|
+
print(
|
170
|
+
f"Fallback parsing also failed for completed_at: {completed_at_str}"
|
171
|
+
)
|
172
|
+
# Fall back to current time
|
173
|
+
end_time = datetime.utcnow()
|
174
|
+
else:
|
175
|
+
# Use current time if no completed_at provided
|
176
|
+
end_time = datetime.utcnow()
|
177
|
+
|
178
|
+
start_time = None
|
179
|
+
try:
|
180
|
+
# Proper parsing of ISO format
|
181
|
+
if "T" in created_at_str: # ISO format with T separator
|
182
|
+
start_time = datetime.fromisoformat(created_at_str)
|
183
|
+
else: # Older format without T
|
184
|
+
# Try different formats
|
185
|
+
try:
|
186
|
+
start_time = datetime.strptime(created_at_str, "%Y-%m-%d %H:%M:%S.%f")
|
187
|
+
except ValueError:
|
188
|
+
try:
|
189
|
+
start_time = datetime.strptime(created_at_str, "%Y-%m-%d %H:%M:%S")
|
190
|
+
except ValueError:
|
191
|
+
# Last resort fallback
|
192
|
+
start_time = datetime.fromisoformat(
|
193
|
+
created_at_str.replace(" ", "T")
|
194
|
+
)
|
195
|
+
except Exception as e:
|
196
|
+
print(f"Error parsing created_at timestamp: {str(e)}")
|
197
|
+
# Fallback method if parsing fails
|
198
|
+
try:
|
199
|
+
from dateutil import parser
|
200
|
+
|
201
|
+
start_time = parser.parse(created_at_str)
|
202
|
+
except Exception:
|
203
|
+
print(f"Fallback parsing also failed for created_at: {created_at_str}")
|
204
|
+
return None
|
205
|
+
|
206
|
+
# Calculate duration if both timestamps are valid
|
207
|
+
if start_time and end_time:
|
208
|
+
try:
|
209
|
+
return int((end_time - start_time).total_seconds())
|
210
|
+
except Exception as e:
|
211
|
+
print(f"Error calculating duration: {str(e)}")
|
212
|
+
|
213
|
+
return None
|
214
|
+
|
215
|
+
|
216
|
+
def add_log_to_db(research_id, message, log_type="info", progress=None, metadata=None):
|
217
|
+
"""
|
218
|
+
Store a log entry in the database
|
219
|
+
|
220
|
+
Args:
|
221
|
+
research_id: ID of the research
|
222
|
+
message: Log message text
|
223
|
+
log_type: Type of log (info, error, milestone)
|
224
|
+
progress: Progress percentage (0-100)
|
225
|
+
metadata: Additional metadata as dictionary (will be stored as JSON)
|
226
|
+
"""
|
227
|
+
try:
|
228
|
+
timestamp = datetime.utcnow().isoformat()
|
229
|
+
metadata_json = json.dumps(metadata) if metadata else None
|
230
|
+
|
231
|
+
conn = get_db_connection()
|
232
|
+
cursor = conn.cursor()
|
233
|
+
cursor.execute(
|
234
|
+
"INSERT INTO research_logs (research_id, timestamp, message, log_type, progress, metadata) "
|
235
|
+
"VALUES (?, ?, ?, ?, ?, ?)",
|
236
|
+
(research_id, timestamp, message, log_type, progress, metadata_json),
|
237
|
+
)
|
238
|
+
conn.commit()
|
239
|
+
conn.close()
|
240
|
+
return True
|
241
|
+
except Exception as e:
|
242
|
+
print(f"Error adding log to database: {str(e)}")
|
243
|
+
print(traceback.format_exc())
|
244
|
+
return False
|
245
|
+
|
246
|
+
|
247
|
+
def get_logs_for_research(research_id):
|
248
|
+
"""
|
249
|
+
Retrieve all logs for a specific research ID
|
250
|
+
|
251
|
+
Args:
|
252
|
+
research_id: ID of the research
|
253
|
+
|
254
|
+
Returns:
|
255
|
+
List of log entries as dictionaries
|
256
|
+
"""
|
257
|
+
try:
|
258
|
+
conn = get_db_connection()
|
259
|
+
conn.row_factory = sqlite3.Row
|
260
|
+
cursor = conn.cursor()
|
261
|
+
cursor.execute(
|
262
|
+
"SELECT * FROM research_logs WHERE research_id = ? ORDER BY timestamp ASC",
|
263
|
+
(research_id,),
|
264
|
+
)
|
265
|
+
results = cursor.fetchall()
|
266
|
+
conn.close()
|
267
|
+
|
268
|
+
logs = []
|
269
|
+
for result in results:
|
270
|
+
log_entry = dict(result)
|
271
|
+
# Parse metadata JSON if it exists
|
272
|
+
if log_entry.get("metadata"):
|
273
|
+
try:
|
274
|
+
log_entry["metadata"] = json.loads(log_entry["metadata"])
|
275
|
+
except Exception:
|
276
|
+
log_entry["metadata"] = {}
|
277
|
+
else:
|
278
|
+
log_entry["metadata"] = {}
|
279
|
+
|
280
|
+
# Convert entry for frontend consumption
|
281
|
+
formatted_entry = {
|
282
|
+
"time": log_entry["timestamp"],
|
283
|
+
"message": log_entry["message"],
|
284
|
+
"progress": log_entry["progress"],
|
285
|
+
"metadata": log_entry["metadata"],
|
286
|
+
"type": log_entry["log_type"],
|
287
|
+
}
|
288
|
+
logs.append(formatted_entry)
|
289
|
+
|
290
|
+
return logs
|
291
|
+
except Exception as e:
|
292
|
+
print(f"Error retrieving logs from database: {str(e)}")
|
293
|
+
print(traceback.format_exc())
|
294
|
+
return []
|
@@ -0,0 +1,94 @@
|
|
1
|
+
from enum import Enum
|
2
|
+
from typing import Any, Dict, List, Optional
|
3
|
+
|
4
|
+
from pydantic import BaseModel, field_validator
|
5
|
+
|
6
|
+
|
7
|
+
class SettingType(str, Enum):
|
8
|
+
"""Types of settings in the system"""
|
9
|
+
|
10
|
+
APP = "app"
|
11
|
+
LLM = "llm"
|
12
|
+
SEARCH = "search"
|
13
|
+
REPORT = "report"
|
14
|
+
|
15
|
+
|
16
|
+
class BaseSetting(BaseModel):
|
17
|
+
"""Base model for all settings"""
|
18
|
+
|
19
|
+
key: str
|
20
|
+
value: Any
|
21
|
+
type: SettingType
|
22
|
+
name: str
|
23
|
+
description: Optional[str] = None
|
24
|
+
category: Optional[str] = None
|
25
|
+
ui_element: Optional[str] = "text" # text, select, checkbox, slider, etc.
|
26
|
+
options: Optional[List[Dict[str, Any]]] = None # For select elements
|
27
|
+
min_value: Optional[float] = None # For numeric inputs
|
28
|
+
max_value: Optional[float] = None # For numeric inputs
|
29
|
+
step: Optional[float] = None # For sliders
|
30
|
+
visible: bool = True
|
31
|
+
editable: bool = True
|
32
|
+
|
33
|
+
class Config:
|
34
|
+
from_attributes = True
|
35
|
+
|
36
|
+
|
37
|
+
class LLMSetting(BaseSetting):
|
38
|
+
"""LLM-specific settings"""
|
39
|
+
|
40
|
+
type: SettingType = SettingType.LLM
|
41
|
+
|
42
|
+
@field_validator("key")
|
43
|
+
def validate_llm_key(cls, v):
|
44
|
+
# Ensure LLM settings follow a convention
|
45
|
+
if not v.startswith("llm."):
|
46
|
+
return f"llm.{v}"
|
47
|
+
return v
|
48
|
+
|
49
|
+
|
50
|
+
class SearchSetting(BaseSetting):
|
51
|
+
"""Search-specific settings"""
|
52
|
+
|
53
|
+
type: SettingType = SettingType.SEARCH
|
54
|
+
|
55
|
+
@field_validator("key")
|
56
|
+
def validate_search_key(cls, v):
|
57
|
+
# Ensure search settings follow a convention
|
58
|
+
if not v.startswith("search."):
|
59
|
+
return f"search.{v}"
|
60
|
+
return v
|
61
|
+
|
62
|
+
|
63
|
+
class ReportSetting(BaseSetting):
|
64
|
+
"""Report generation settings"""
|
65
|
+
|
66
|
+
type: SettingType = SettingType.REPORT
|
67
|
+
|
68
|
+
@field_validator("key")
|
69
|
+
def validate_report_key(cls, v):
|
70
|
+
# Ensure report settings follow a convention
|
71
|
+
if not v.startswith("report."):
|
72
|
+
return f"report.{v}"
|
73
|
+
return v
|
74
|
+
|
75
|
+
|
76
|
+
class AppSetting(BaseSetting):
|
77
|
+
"""Application-wide settings"""
|
78
|
+
|
79
|
+
type: SettingType = SettingType.APP
|
80
|
+
|
81
|
+
@field_validator("key")
|
82
|
+
def validate_app_key(cls, v):
|
83
|
+
# Ensure app settings follow a convention
|
84
|
+
if not v.startswith("app."):
|
85
|
+
return f"app.{v}"
|
86
|
+
return v
|
87
|
+
|
88
|
+
|
89
|
+
class SettingsGroup(BaseModel):
|
90
|
+
"""A group of related settings"""
|
91
|
+
|
92
|
+
name: str
|
93
|
+
description: Optional[str] = None
|
94
|
+
settings: List[BaseSetting]
|