local-deep-research 0.5.9__py3-none-any.whl → 0.6.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/__version__.py +1 -1
- local_deep_research/advanced_search_system/candidate_exploration/progressive_explorer.py +11 -1
- local_deep_research/advanced_search_system/questions/browsecomp_question.py +32 -6
- local_deep_research/advanced_search_system/strategies/focused_iteration_strategy.py +32 -8
- local_deep_research/advanced_search_system/strategies/source_based_strategy.py +2 -0
- local_deep_research/api/__init__.py +2 -0
- local_deep_research/api/research_functions.py +177 -3
- local_deep_research/benchmarks/graders.py +150 -5
- local_deep_research/benchmarks/models/__init__.py +19 -0
- local_deep_research/benchmarks/models/benchmark_models.py +283 -0
- local_deep_research/benchmarks/ui/__init__.py +1 -0
- local_deep_research/benchmarks/web_api/__init__.py +6 -0
- local_deep_research/benchmarks/web_api/benchmark_routes.py +862 -0
- local_deep_research/benchmarks/web_api/benchmark_service.py +920 -0
- local_deep_research/config/llm_config.py +106 -21
- local_deep_research/defaults/default_settings.json +447 -2
- local_deep_research/error_handling/report_generator.py +10 -0
- local_deep_research/llm/__init__.py +19 -0
- local_deep_research/llm/llm_registry.py +155 -0
- local_deep_research/metrics/db_models.py +3 -7
- local_deep_research/metrics/search_tracker.py +25 -11
- local_deep_research/search_system.py +12 -9
- local_deep_research/utilities/log_utils.py +23 -10
- local_deep_research/utilities/thread_context.py +99 -0
- local_deep_research/web/app_factory.py +32 -8
- local_deep_research/web/database/benchmark_schema.py +230 -0
- local_deep_research/web/database/convert_research_id_to_string.py +161 -0
- local_deep_research/web/database/models.py +55 -1
- local_deep_research/web/database/schema_upgrade.py +397 -2
- local_deep_research/web/database/uuid_migration.py +265 -0
- local_deep_research/web/routes/api_routes.py +62 -31
- local_deep_research/web/routes/history_routes.py +13 -6
- local_deep_research/web/routes/metrics_routes.py +264 -4
- local_deep_research/web/routes/research_routes.py +45 -18
- local_deep_research/web/routes/route_registry.py +352 -0
- local_deep_research/web/routes/settings_routes.py +382 -22
- local_deep_research/web/services/research_service.py +22 -29
- local_deep_research/web/services/settings_manager.py +53 -0
- local_deep_research/web/services/settings_service.py +2 -0
- local_deep_research/web/static/css/styles.css +8 -0
- local_deep_research/web/static/js/components/detail.js +7 -14
- local_deep_research/web/static/js/components/details.js +8 -10
- local_deep_research/web/static/js/components/fallback/ui.js +4 -4
- local_deep_research/web/static/js/components/history.js +6 -6
- local_deep_research/web/static/js/components/logpanel.js +14 -11
- local_deep_research/web/static/js/components/progress.js +51 -46
- local_deep_research/web/static/js/components/research.js +250 -89
- local_deep_research/web/static/js/components/results.js +5 -7
- local_deep_research/web/static/js/components/settings.js +32 -26
- local_deep_research/web/static/js/components/settings_sync.js +24 -23
- local_deep_research/web/static/js/config/urls.js +285 -0
- local_deep_research/web/static/js/main.js +8 -8
- local_deep_research/web/static/js/research_form.js +267 -12
- local_deep_research/web/static/js/services/api.js +18 -18
- local_deep_research/web/static/js/services/keyboard.js +8 -8
- local_deep_research/web/static/js/services/socket.js +53 -35
- local_deep_research/web/static/js/services/ui.js +1 -1
- local_deep_research/web/templates/base.html +4 -1
- local_deep_research/web/templates/components/custom_dropdown.html +5 -3
- local_deep_research/web/templates/components/mobile_nav.html +3 -3
- local_deep_research/web/templates/components/sidebar.html +9 -3
- local_deep_research/web/templates/pages/benchmark.html +2697 -0
- local_deep_research/web/templates/pages/benchmark_results.html +1136 -0
- local_deep_research/web/templates/pages/benchmark_simple.html +453 -0
- local_deep_research/web/templates/pages/cost_analytics.html +1 -1
- local_deep_research/web/templates/pages/metrics.html +212 -39
- local_deep_research/web/templates/pages/research.html +8 -6
- local_deep_research/web/templates/pages/star_reviews.html +1 -1
- local_deep_research/web_search_engines/engines/search_engine_arxiv.py +14 -1
- local_deep_research/web_search_engines/engines/search_engine_brave.py +15 -1
- local_deep_research/web_search_engines/engines/search_engine_ddg.py +20 -1
- local_deep_research/web_search_engines/engines/search_engine_google_pse.py +26 -2
- local_deep_research/web_search_engines/engines/search_engine_pubmed.py +15 -1
- local_deep_research/web_search_engines/engines/search_engine_retriever.py +192 -0
- local_deep_research/web_search_engines/engines/search_engine_tavily.py +307 -0
- local_deep_research/web_search_engines/rate_limiting/__init__.py +14 -0
- local_deep_research/web_search_engines/rate_limiting/__main__.py +9 -0
- local_deep_research/web_search_engines/rate_limiting/cli.py +209 -0
- local_deep_research/web_search_engines/rate_limiting/exceptions.py +21 -0
- local_deep_research/web_search_engines/rate_limiting/tracker.py +506 -0
- local_deep_research/web_search_engines/retriever_registry.py +108 -0
- local_deep_research/web_search_engines/search_engine_base.py +161 -43
- local_deep_research/web_search_engines/search_engine_factory.py +14 -0
- local_deep_research/web_search_engines/search_engines_config.py +20 -0
- local_deep_research-0.6.0.dist-info/METADATA +374 -0
- {local_deep_research-0.5.9.dist-info → local_deep_research-0.6.0.dist-info}/RECORD +89 -64
- local_deep_research-0.5.9.dist-info/METADATA +0 -420
- {local_deep_research-0.5.9.dist-info → local_deep_research-0.6.0.dist-info}/WHEEL +0 -0
- {local_deep_research-0.5.9.dist-info → local_deep_research-0.6.0.dist-info}/entry_points.txt +0 -0
- {local_deep_research-0.5.9.dist-info → local_deep_research-0.6.0.dist-info}/licenses/LICENSE +0 -0
@@ -682,6 +682,77 @@
|
|
682
682
|
</div>
|
683
683
|
</div>
|
684
684
|
|
685
|
+
<!-- Rate Limiting Analytics -->
|
686
|
+
<div class="card" style="margin-top: 2rem;">
|
687
|
+
<div class="card-header">
|
688
|
+
<h2><i class="fas fa-clock"></i> Rate Limiting Analytics</h2>
|
689
|
+
</div>
|
690
|
+
<div class="card-content">
|
691
|
+
<!-- Rate Limiting Overview Cards -->
|
692
|
+
<div class="metrics-grid" style="margin-bottom: 2rem;">
|
693
|
+
<div class="metric-card">
|
694
|
+
<div class="metric-icon">
|
695
|
+
<i class="fas fa-stopwatch"></i>
|
696
|
+
</div>
|
697
|
+
<div class="metric-label tooltip" data-tooltip="Percentage of retry attempts that succeeded after rate limiting">
|
698
|
+
Rate Limit Success Rate
|
699
|
+
<i class="fas fa-info-circle info-icon"></i>
|
700
|
+
</div>
|
701
|
+
<div class="metric-value" id="rate-limit-success-rate">0%</div>
|
702
|
+
</div>
|
703
|
+
|
704
|
+
<div class="metric-card">
|
705
|
+
<div class="metric-icon">
|
706
|
+
<i class="fas fa-exclamation-triangle"></i>
|
707
|
+
</div>
|
708
|
+
<div class="metric-label tooltip" data-tooltip="Number of times search engines were rate limited">
|
709
|
+
Rate Limit Events
|
710
|
+
<i class="fas fa-info-circle info-icon"></i>
|
711
|
+
</div>
|
712
|
+
<div class="metric-value" id="rate-limit-events">0</div>
|
713
|
+
</div>
|
714
|
+
|
715
|
+
<div class="metric-card">
|
716
|
+
<div class="metric-icon">
|
717
|
+
<i class="fas fa-hourglass-half"></i>
|
718
|
+
</div>
|
719
|
+
<div class="metric-label tooltip" data-tooltip="Average time waited before retrying after rate limits">
|
720
|
+
Avg Wait Time
|
721
|
+
<i class="fas fa-info-circle info-icon"></i>
|
722
|
+
</div>
|
723
|
+
<div class="metric-value" id="avg-wait-time">0s</div>
|
724
|
+
</div>
|
725
|
+
|
726
|
+
<div class="metric-card">
|
727
|
+
<div class="metric-icon">
|
728
|
+
<i class="fas fa-server"></i>
|
729
|
+
</div>
|
730
|
+
<div class="metric-label tooltip" data-tooltip="Number of search engines being tracked for rate limiting">
|
731
|
+
Engines Tracked
|
732
|
+
<i class="fas fa-info-circle info-icon"></i>
|
733
|
+
</div>
|
734
|
+
<div class="metric-value" id="engines-tracked">0</div>
|
735
|
+
</div>
|
736
|
+
</div>
|
737
|
+
|
738
|
+
<!-- Engine Status Grid -->
|
739
|
+
<div style="margin-bottom: 2rem;">
|
740
|
+
<h3><i class="fas fa-tachometer-alt"></i> Search Engine Status</h3>
|
741
|
+
<div id="engine-status-grid" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1rem; margin-top: 1rem;">
|
742
|
+
<!-- Populated dynamically -->
|
743
|
+
</div>
|
744
|
+
</div>
|
745
|
+
|
746
|
+
<!-- Rate Limiting Chart -->
|
747
|
+
<div style="margin-top: 2rem;">
|
748
|
+
<h3><i class="fas fa-chart-line"></i> Rate Limiting Activity Over Time</h3>
|
749
|
+
<div class="chart-container" style="height: 300px;">
|
750
|
+
<canvas id="rate-limiting-chart"></canvas>
|
751
|
+
</div>
|
752
|
+
</div>
|
753
|
+
</div>
|
754
|
+
</div>
|
755
|
+
|
685
756
|
<!-- Developer Insights -->
|
686
757
|
<div class="card" style="margin-top: 2rem;">
|
687
758
|
<div class="card-header">
|
@@ -817,7 +888,6 @@
|
|
817
888
|
<script>
|
818
889
|
// Metrics Dashboard JavaScript
|
819
890
|
(function() {
|
820
|
-
console.log("=== METRICS SCRIPT STARTED ===");
|
821
891
|
|
822
892
|
let metricsData = null;
|
823
893
|
let tokenChart = null;
|
@@ -871,40 +941,30 @@
|
|
871
941
|
|
872
942
|
// Load metrics data for current time period
|
873
943
|
async function loadMetrics(period = currentPeriod) {
|
874
|
-
console.log('=== STARTING LOADMETRICS ===');
|
875
|
-
console.log('Period:', period, 'Mode:', currentMode);
|
876
944
|
|
877
945
|
try {
|
878
946
|
// Show loading state
|
879
|
-
console.log('Setting loading state...');
|
880
947
|
document.getElementById('loading').style.display = 'block';
|
881
948
|
document.getElementById('metrics-content').style.display = 'none';
|
882
949
|
document.getElementById('error').style.display = 'none';
|
883
950
|
|
884
|
-
console.log('Making basic API call...');
|
885
951
|
const basicResponse = await fetch(`/metrics/api/metrics?period=${period}&mode=${currentMode}`);
|
886
|
-
console.log('Basic response status:', basicResponse.status);
|
887
952
|
|
888
953
|
if (!basicResponse.ok) {
|
889
954
|
throw new Error(`Basic API failed: ${basicResponse.status}`);
|
890
955
|
}
|
891
956
|
|
892
957
|
const basicData = await basicResponse.json();
|
893
|
-
console.log('Basic data received:', basicData);
|
894
958
|
|
895
|
-
console.log('Making enhanced API call...');
|
896
959
|
const enhancedResponse = await fetch(`/metrics/api/metrics/enhanced?period=${period}&mode=${currentMode}`);
|
897
|
-
console.log('Enhanced response status:', enhancedResponse.status);
|
898
960
|
|
899
961
|
if (!enhancedResponse.ok) {
|
900
962
|
throw new Error(`Enhanced API failed: ${enhancedResponse.status}`);
|
901
963
|
}
|
902
964
|
|
903
965
|
const enhancedData = await enhancedResponse.json();
|
904
|
-
console.log('Enhanced data received:', enhancedData);
|
905
966
|
|
906
967
|
if (basicData.status === 'success') {
|
907
|
-
console.log('Basic data success, setting metricsData');
|
908
968
|
metricsData = basicData.metrics;
|
909
969
|
|
910
970
|
// Check if we have any data at all
|
@@ -913,15 +973,12 @@
|
|
913
973
|
(metricsData.by_model && metricsData.by_model.length > 0) ||
|
914
974
|
(metricsData.recent_researches && metricsData.recent_researches.length > 0);
|
915
975
|
|
916
|
-
console.log('Has data check:', hasData, 'tokens:', metricsData.total_tokens, 'researches:', metricsData.total_researches);
|
917
976
|
|
918
977
|
if (hasData) {
|
919
|
-
console.log('Displaying metrics...');
|
920
978
|
// We have data, display normally
|
921
979
|
displayMetrics();
|
922
980
|
|
923
981
|
if (enhancedData.status === 'success') {
|
924
|
-
console.log('Displaying enhanced metrics...');
|
925
982
|
displayEnhancedMetrics(enhancedData.metrics);
|
926
983
|
createTimeSeriesChart(enhancedData.metrics.time_series_data);
|
927
984
|
createSearchActivityChart(enhancedData.metrics.search_time_series);
|
@@ -932,40 +989,35 @@
|
|
932
989
|
// Load cost analytics
|
933
990
|
loadCostAnalytics(period);
|
934
991
|
|
992
|
+
// Load rate limiting analytics
|
993
|
+
loadRateLimitingAnalytics(period);
|
994
|
+
|
935
995
|
document.getElementById('loading').style.display = 'none';
|
936
996
|
document.getElementById('metrics-content').style.display = 'block';
|
937
|
-
|
938
|
-
} else {
|
939
|
-
console.log('No data, showing empty state');
|
997
|
+
} else {
|
940
998
|
// No data yet, show empty state with helpful messages
|
941
999
|
showEmptyState();
|
942
1000
|
|
943
1001
|
// Still try to display enhanced metrics in case there's some enhanced data
|
944
1002
|
if (enhancedData.status === 'success') {
|
945
|
-
console.log('Displaying enhanced metrics in empty state...');
|
946
1003
|
displayEnhancedMetrics(enhancedData.metrics);
|
947
1004
|
// Re-setup tooltips for any new content
|
948
1005
|
setTimeout(setupTooltipPositioning, 100);
|
949
1006
|
}
|
950
1007
|
}
|
951
1008
|
} else {
|
952
|
-
console.log('Basic data failed, showing error');
|
953
1009
|
showError();
|
954
1010
|
}
|
955
1011
|
} catch (error) {
|
956
|
-
console.error('Error loading metrics:', error);
|
957
1012
|
showError();
|
958
1013
|
}
|
959
1014
|
}
|
960
1015
|
|
961
1016
|
// Display metrics on the page
|
962
1017
|
function displayMetrics() {
|
963
|
-
console.log('displayMetrics called with metricsData:', metricsData);
|
964
1018
|
|
965
1019
|
// Update summary cards
|
966
|
-
console.log('Updating total tokens:', metricsData.total_tokens);
|
967
1020
|
document.getElementById('total-tokens').textContent = formatNumber(metricsData.total_tokens);
|
968
|
-
console.log('Updating total researches:', metricsData.total_researches);
|
969
1021
|
document.getElementById('total-researches').textContent = formatNumber(metricsData.total_researches);
|
970
1022
|
|
971
1023
|
// Update token breakdown details
|
@@ -984,15 +1036,12 @@
|
|
984
1036
|
document.getElementById('success-rate').textContent = '0%';
|
985
1037
|
|
986
1038
|
// Update user satisfaction rating
|
987
|
-
console.log('User satisfaction data:', metricsData.user_satisfaction);
|
988
1039
|
if (metricsData.user_satisfaction && metricsData.user_satisfaction.avg_rating !== null) {
|
989
1040
|
const rating = metricsData.user_satisfaction.avg_rating;
|
990
1041
|
const stars = '⭐'.repeat(Math.floor(rating));
|
991
1042
|
const displayText = `${rating} ${stars}`;
|
992
|
-
console.log('Setting user rating to:', displayText);
|
993
1043
|
document.getElementById('avg-user-rating').textContent = displayText;
|
994
1044
|
} else {
|
995
|
-
console.log('No user satisfaction data available');
|
996
1045
|
document.getElementById('avg-user-rating').textContent = '-';
|
997
1046
|
}
|
998
1047
|
|
@@ -1120,7 +1169,7 @@
|
|
1120
1169
|
<div class="research-tokens">${formatNumber(research.tokens)} tokens</div>
|
1121
1170
|
`;
|
1122
1171
|
item.onclick = () => {
|
1123
|
-
window.location.href = `/
|
1172
|
+
window.location.href = `/details/${research.id}`;
|
1124
1173
|
};
|
1125
1174
|
container.appendChild(item);
|
1126
1175
|
});
|
@@ -1194,8 +1243,6 @@
|
|
1194
1243
|
// Get strategy data from metricsData (added by our API)
|
1195
1244
|
const strategyData = metricsData.strategy_analytics;
|
1196
1245
|
|
1197
|
-
// Log strategy data for debugging
|
1198
|
-
console.log('Strategy data received:', strategyData);
|
1199
1246
|
|
1200
1247
|
if (strategyData && strategyData.strategy_usage && strategyData.strategy_usage.length > 0) {
|
1201
1248
|
strategyData.strategy_usage.forEach(strategy => {
|
@@ -1219,7 +1266,7 @@
|
|
1219
1266
|
noDataMsg.style.color = 'var(--text-secondary)';
|
1220
1267
|
noDataMsg.style.padding = '1rem';
|
1221
1268
|
|
1222
|
-
let message = 'No strategy data yet. <a href="/
|
1269
|
+
let message = 'No strategy data yet. <a href="/" style="color: var(--accent-tertiary);">Start a research</a> to track strategies!';
|
1223
1270
|
|
1224
1271
|
// Show different message based on what data we have
|
1225
1272
|
if (strategyData) {
|
@@ -1285,7 +1332,7 @@
|
|
1285
1332
|
noDataMsg.style.textAlign = 'center';
|
1286
1333
|
noDataMsg.style.color = 'var(--text-secondary)';
|
1287
1334
|
noDataMsg.style.padding = '1rem';
|
1288
|
-
noDataMsg.innerHTML = 'No ratings yet. <a href="/
|
1335
|
+
noDataMsg.innerHTML = 'No ratings yet. <a href="/" style="color: var(--accent-tertiary);">Start a research</a> and rate it!';
|
1289
1336
|
ratingContainer.appendChild(noDataMsg);
|
1290
1337
|
}
|
1291
1338
|
|
@@ -1322,7 +1369,7 @@
|
|
1322
1369
|
// Add click handler to navigate to research details
|
1323
1370
|
if (item.research_id) {
|
1324
1371
|
row.onclick = () => {
|
1325
|
-
window.location.href = `/
|
1372
|
+
window.location.href = `/details/${item.research_id}`;
|
1326
1373
|
};
|
1327
1374
|
row.title = `Click to view research details`;
|
1328
1375
|
}
|
@@ -1651,26 +1698,20 @@
|
|
1651
1698
|
// Load cost analytics data
|
1652
1699
|
async function loadCostAnalytics(period = currentPeriod) {
|
1653
1700
|
try {
|
1654
|
-
console.log('Loading cost analytics for period:', period);
|
1655
|
-
|
1656
1701
|
const response = await fetch(`/metrics/api/cost-analytics?period=${period}`);
|
1657
1702
|
if (!response.ok) {
|
1658
|
-
console.warn('Cost analytics API failed:', response.status);
|
1659
1703
|
displayCostData(null);
|
1660
1704
|
return;
|
1661
1705
|
}
|
1662
1706
|
|
1663
1707
|
const data = await response.json();
|
1664
|
-
console.log('Cost analytics data received:', data);
|
1665
1708
|
|
1666
1709
|
if (data.status === 'success') {
|
1667
1710
|
displayCostData(data);
|
1668
1711
|
} else {
|
1669
|
-
console.warn('Cost analytics returned error:', data.message);
|
1670
1712
|
displayCostData(null);
|
1671
1713
|
}
|
1672
1714
|
} catch (error) {
|
1673
|
-
console.error('Error loading cost analytics:', error);
|
1674
1715
|
displayCostData(null);
|
1675
1716
|
}
|
1676
1717
|
}
|
@@ -1700,7 +1741,133 @@
|
|
1700
1741
|
document.getElementById('total-output-cost').textContent = formatCurrency(overview.completion_cost);
|
1701
1742
|
document.getElementById('total-cost-breakdown').textContent = formatCurrency(overview.total_cost);
|
1702
1743
|
|
1703
|
-
|
1744
|
+
}
|
1745
|
+
|
1746
|
+
// Load rate limiting analytics
|
1747
|
+
async function loadRateLimitingAnalytics(period = currentPeriod) {
|
1748
|
+
try {
|
1749
|
+
const response = await fetch(`/metrics/api/rate-limiting?period=${period}`);
|
1750
|
+
if (!response.ok) {
|
1751
|
+
displayRateLimitingData(null);
|
1752
|
+
return;
|
1753
|
+
}
|
1754
|
+
|
1755
|
+
const data = await response.json();
|
1756
|
+
|
1757
|
+
if (data.status === 'success') {
|
1758
|
+
displayRateLimitingData(data.data);
|
1759
|
+
} else {
|
1760
|
+
displayRateLimitingData(null);
|
1761
|
+
}
|
1762
|
+
} catch (error) {
|
1763
|
+
displayRateLimitingData(null);
|
1764
|
+
}
|
1765
|
+
}
|
1766
|
+
|
1767
|
+
// Display rate limiting data in the dashboard
|
1768
|
+
function displayRateLimitingData(rateLimitData) {
|
1769
|
+
if (!rateLimitData || !rateLimitData.rate_limiting_analytics) {
|
1770
|
+
// No rate limiting data available, show zeros
|
1771
|
+
document.getElementById('rate-limit-success-rate').textContent = '0%';
|
1772
|
+
document.getElementById('rate-limit-events').textContent = '0';
|
1773
|
+
document.getElementById('avg-wait-time').textContent = '0s';
|
1774
|
+
document.getElementById('engines-tracked').textContent = '0';
|
1775
|
+
|
1776
|
+
// Clear engine status grid
|
1777
|
+
const statusGrid = document.getElementById('engine-status-grid');
|
1778
|
+
statusGrid.innerHTML = '<div style="grid-column: 1 / -1; text-align: center; padding: 2rem; color: var(--text-secondary);"><i class="fas fa-clock fa-2x" style="margin-bottom: 1rem;"></i><p>No rate limiting data yet</p><p style="font-size: 0.875rem;">Rate limiting will appear after search engines are used</p></div>';
|
1779
|
+
return;
|
1780
|
+
}
|
1781
|
+
|
1782
|
+
const analytics = rateLimitData.rate_limiting_analytics;
|
1783
|
+
|
1784
|
+
// Update overview cards
|
1785
|
+
document.getElementById('rate-limit-success-rate').textContent = `${analytics.success_rate}%`;
|
1786
|
+
document.getElementById('rate-limit-events').textContent = formatNumber(analytics.rate_limit_events);
|
1787
|
+
document.getElementById('avg-wait-time').textContent = `${analytics.avg_wait_time}s`;
|
1788
|
+
document.getElementById('engines-tracked').textContent = formatNumber(analytics.total_engines_tracked);
|
1789
|
+
|
1790
|
+
// Update engine status grid
|
1791
|
+
const statusGrid = document.getElementById('engine-status-grid');
|
1792
|
+
statusGrid.innerHTML = '';
|
1793
|
+
|
1794
|
+
if (Object.keys(analytics.engine_stats).length === 0) {
|
1795
|
+
statusGrid.innerHTML = '<div style="grid-column: 1 / -1; text-align: center; padding: 2rem; color: var(--text-secondary);"><i class="fas fa-clock fa-2x" style="margin-bottom: 1rem;"></i><p>No engine data yet</p><p style="font-size: 0.875rem;">Search engine metrics will appear after rate limiting occurs</p></div>';
|
1796
|
+
} else {
|
1797
|
+
Object.entries(analytics.engine_stats).forEach(([engineType, stats]) => {
|
1798
|
+
const statusCard = document.createElement('div');
|
1799
|
+
statusCard.className = 'metric-card';
|
1800
|
+
statusCard.style.borderLeft = `4px solid ${getStatusColor(stats.status)}`;
|
1801
|
+
|
1802
|
+
statusCard.innerHTML = `
|
1803
|
+
<div class="metric-header" style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem;">
|
1804
|
+
<h4 style="margin: 0; color: var(--text-primary);">${engineType.replace('SearchEngine', '')}</h4>
|
1805
|
+
<span class="status-badge" style="
|
1806
|
+
background: ${getStatusColor(stats.status)};
|
1807
|
+
color: white;
|
1808
|
+
padding: 2px 8px;
|
1809
|
+
border-radius: 12px;
|
1810
|
+
font-size: 0.75rem;
|
1811
|
+
font-weight: 600;
|
1812
|
+
text-transform: uppercase;
|
1813
|
+
">${stats.status}</span>
|
1814
|
+
</div>
|
1815
|
+
<div class="engine-stats-grid" style="display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; font-size: 0.875rem;">
|
1816
|
+
<div>
|
1817
|
+
<div style="color: var(--text-secondary);">Base Wait:</div>
|
1818
|
+
<div style="font-weight: 600;">${stats.base_wait_seconds}s</div>
|
1819
|
+
</div>
|
1820
|
+
<div>
|
1821
|
+
<div style="color: var(--text-secondary);">Success Rate:</div>
|
1822
|
+
<div style="font-weight: 600; color: ${stats.success_rate > 80 ? '#4CAF50' : stats.success_rate > 50 ? '#FF9800' : '#F44336'};">${stats.success_rate}%</div>
|
1823
|
+
</div>
|
1824
|
+
<div>
|
1825
|
+
<div style="color: var(--text-secondary);">Total Attempts:</div>
|
1826
|
+
<div style="font-weight: 600;">${formatNumber(stats.total_attempts)}</div>
|
1827
|
+
</div>
|
1828
|
+
<div>
|
1829
|
+
<div style="color: var(--text-secondary);">Last Updated:</div>
|
1830
|
+
<div style="font-weight: 600;">${formatTime(stats.last_updated)}</div>
|
1831
|
+
</div>
|
1832
|
+
</div>
|
1833
|
+
<div style="margin-top: 0.75rem; font-size: 0.75rem; color: var(--text-secondary);">
|
1834
|
+
Range: ${stats.min_wait_seconds}s - ${stats.max_wait_seconds}s
|
1835
|
+
</div>
|
1836
|
+
`;
|
1837
|
+
|
1838
|
+
statusGrid.appendChild(statusCard);
|
1839
|
+
});
|
1840
|
+
}
|
1841
|
+
|
1842
|
+
}
|
1843
|
+
|
1844
|
+
// Get status color based on engine health
|
1845
|
+
function getStatusColor(status) {
|
1846
|
+
switch (status) {
|
1847
|
+
case 'healthy': return '#4CAF50';
|
1848
|
+
case 'degraded': return '#FF9800';
|
1849
|
+
case 'poor': return '#F44336';
|
1850
|
+
default: return '#9E9E9E';
|
1851
|
+
}
|
1852
|
+
}
|
1853
|
+
|
1854
|
+
// Format time for display
|
1855
|
+
function formatTime(timeString) {
|
1856
|
+
try {
|
1857
|
+
const date = new Date(timeString);
|
1858
|
+
const now = new Date();
|
1859
|
+
const diffHours = (now - date) / (1000 * 60 * 60);
|
1860
|
+
|
1861
|
+
if (diffHours < 1) {
|
1862
|
+
return 'Just now';
|
1863
|
+
} else if (diffHours < 24) {
|
1864
|
+
return `${Math.floor(diffHours)}h ago`;
|
1865
|
+
} else {
|
1866
|
+
return `${Math.floor(diffHours / 24)}d ago`;
|
1867
|
+
}
|
1868
|
+
} catch {
|
1869
|
+
return timeString;
|
1870
|
+
}
|
1704
1871
|
}
|
1705
1872
|
|
1706
1873
|
// Show error message
|
@@ -1736,6 +1903,12 @@
|
|
1736
1903
|
document.getElementById('total-output-cost').textContent = '-';
|
1737
1904
|
document.getElementById('total-cost-breakdown').textContent = '-';
|
1738
1905
|
|
1906
|
+
// Reset rate limiting metrics
|
1907
|
+
document.getElementById('rate-limit-success-rate').textContent = '0%';
|
1908
|
+
document.getElementById('rate-limit-events').textContent = '0';
|
1909
|
+
document.getElementById('avg-wait-time').textContent = '0s';
|
1910
|
+
document.getElementById('engines-tracked').textContent = '0';
|
1911
|
+
|
1739
1912
|
// Show helpful message in charts
|
1740
1913
|
showEmptyChartsAndTables();
|
1741
1914
|
}
|
@@ -81,7 +81,7 @@
|
|
81
81
|
<!-- Model Provider Selection -->
|
82
82
|
<div class="form-group half">
|
83
83
|
<label for="model_provider">Model Provider</label>
|
84
|
-
<select id="model_provider" name="model_provider" class="form-control">
|
84
|
+
<select id="model_provider" name="model_provider" class="form-control" data-initial-value="{{ settings.llm_provider }}">
|
85
85
|
<!-- Will be populated dynamically -->
|
86
86
|
<option value="">Loading providers...</option>
|
87
87
|
</select>
|
@@ -91,7 +91,7 @@
|
|
91
91
|
<!-- Custom Endpoint (hidden by default) -->
|
92
92
|
<div class="form-group half" id="endpoint_container" style="display: none;">
|
93
93
|
<label for="custom_endpoint">Custom Endpoint</label>
|
94
|
-
<input type="text" id="custom_endpoint" name="custom_endpoint" class="form-control" placeholder="https://your-endpoint-url/v1">
|
94
|
+
<input type="text" id="custom_endpoint" name="custom_endpoint" class="form-control" placeholder="https://your-endpoint-url/v1" value="{{ settings.llm_openai_endpoint_url }}">
|
95
95
|
<span class="input-help">Enter the OpenAI-compatible API endpoint URL</span>
|
96
96
|
</div>
|
97
97
|
</div>
|
@@ -106,7 +106,8 @@
|
|
106
106
|
label="Language Model",
|
107
107
|
help_text="Select or enter a custom model name",
|
108
108
|
show_refresh=True,
|
109
|
-
refresh_aria_label="Refresh model list"
|
109
|
+
refresh_aria_label="Refresh model list",
|
110
|
+
data_initial_value=settings.llm_model
|
110
111
|
) }}
|
111
112
|
</div>
|
112
113
|
|
@@ -119,7 +120,8 @@
|
|
119
120
|
label="Search Engine",
|
120
121
|
help_text="Select the search engine to use for research",
|
121
122
|
show_refresh=True,
|
122
|
-
refresh_aria_label="Refresh search engine list"
|
123
|
+
refresh_aria_label="Refresh search engine list",
|
124
|
+
data_initial_value=settings.search_tool
|
123
125
|
) }}
|
124
126
|
</div>
|
125
127
|
</div>
|
@@ -128,14 +130,14 @@
|
|
128
130
|
<!-- Search Iterations -->
|
129
131
|
<div class="form-group half">
|
130
132
|
<label for="iterations">Search Iterations</label>
|
131
|
-
<input type="number" id="iterations" name="iterations" class="form-control" min="1" max="5" value="
|
133
|
+
<input type="number" id="iterations" name="iterations" class="form-control" min="1" max="5" value="{{ settings.search_iterations }}">
|
132
134
|
<span class="input-help">Number of research cycles to perform</span>
|
133
135
|
</div>
|
134
136
|
|
135
137
|
<!-- Questions Per Iteration -->
|
136
138
|
<div class="form-group half">
|
137
139
|
<label for="questions_per_iteration">Questions Per Iteration</label>
|
138
|
-
<input type="number" id="questions_per_iteration" name="questions_per_iteration" class="form-control" min="1" max="10" value="
|
140
|
+
<input type="number" id="questions_per_iteration" name="questions_per_iteration" class="form-control" min="1" max="10" value="{{ settings.search_questions_per_iteration }}">
|
139
141
|
<span class="input-help">Follow-up questions in each cycle</span>
|
140
142
|
</div>
|
141
143
|
</div>
|
@@ -782,7 +782,7 @@ function updateRecentRatings(recentRatings) {
|
|
782
782
|
item.innerHTML = `
|
783
783
|
<div class="rating-details">
|
784
784
|
<div class="rating-query">
|
785
|
-
<a href="/
|
785
|
+
<a href="/results/${rating.research_id}" class="rating-link">
|
786
786
|
${rating.query}
|
787
787
|
</a>
|
788
788
|
</div>
|
@@ -9,6 +9,7 @@ from ...advanced_search_system.filters.journal_reputation_filter import (
|
|
9
9
|
)
|
10
10
|
from ...config import search_config
|
11
11
|
from ..search_engine_base import BaseSearchEngine
|
12
|
+
from ..rate_limiting import RateLimitError
|
12
13
|
|
13
14
|
|
14
15
|
class ArXivSearchEngine(BaseSearchEngine):
|
@@ -155,8 +156,20 @@ class ArXivSearchEngine(BaseSearchEngine):
|
|
155
156
|
|
156
157
|
return previews
|
157
158
|
|
158
|
-
except Exception:
|
159
|
+
except Exception as e:
|
160
|
+
error_msg = str(e)
|
159
161
|
logger.exception("Error getting arXiv previews")
|
162
|
+
|
163
|
+
# Check for rate limiting patterns
|
164
|
+
if (
|
165
|
+
"429" in error_msg
|
166
|
+
or "too many requests" in error_msg.lower()
|
167
|
+
or "rate limit" in error_msg.lower()
|
168
|
+
or "service unavailable" in error_msg.lower()
|
169
|
+
or "503" in error_msg
|
170
|
+
):
|
171
|
+
raise RateLimitError(f"arXiv rate limit hit: {error_msg}")
|
172
|
+
|
160
173
|
return []
|
161
174
|
|
162
175
|
def _get_full_content(
|
@@ -7,6 +7,7 @@ from langchain_core.language_models import BaseLLM
|
|
7
7
|
|
8
8
|
from ...config import search_config
|
9
9
|
from ..search_engine_base import BaseSearchEngine
|
10
|
+
from ..rate_limiting import RateLimitError
|
10
11
|
|
11
12
|
logger = logging.getLogger(__name__)
|
12
13
|
|
@@ -174,7 +175,20 @@ class BraveSearchEngine(BaseSearchEngine):
|
|
174
175
|
return previews
|
175
176
|
|
176
177
|
except Exception as e:
|
177
|
-
|
178
|
+
error_msg = str(e)
|
179
|
+
logger.error(f"Error getting Brave Search results: {error_msg}")
|
180
|
+
|
181
|
+
# Check for rate limit patterns
|
182
|
+
if (
|
183
|
+
"429" in error_msg
|
184
|
+
or "too many requests" in error_msg.lower()
|
185
|
+
or "rate limit" in error_msg.lower()
|
186
|
+
or "quota" in error_msg.lower()
|
187
|
+
):
|
188
|
+
raise RateLimitError(
|
189
|
+
f"Brave Search rate limit hit: {error_msg}"
|
190
|
+
)
|
191
|
+
|
178
192
|
return []
|
179
193
|
|
180
194
|
def _get_full_content(
|
@@ -5,6 +5,7 @@ from langchain_community.utilities import DuckDuckGoSearchAPIWrapper
|
|
5
5
|
from langchain_core.language_models import BaseLLM
|
6
6
|
|
7
7
|
from ..search_engine_base import BaseSearchEngine
|
8
|
+
from ..rate_limiting import RateLimitError
|
8
9
|
from .full_search import FullSearchResults # Import the FullSearchResults class
|
9
10
|
|
10
11
|
logger = logging.getLogger(__name__)
|
@@ -114,7 +115,25 @@ class DuckDuckGoSearchEngine(BaseSearchEngine):
|
|
114
115
|
return previews
|
115
116
|
|
116
117
|
except Exception as e:
|
117
|
-
|
118
|
+
error_msg = str(e)
|
119
|
+
logger.error(f"Error getting DuckDuckGo previews: {error_msg}")
|
120
|
+
|
121
|
+
# Check for known rate limit patterns
|
122
|
+
if "202 Ratelimit" in error_msg or "ratelimit" in error_msg.lower():
|
123
|
+
raise RateLimitError(f"DuckDuckGo rate limit hit: {error_msg}")
|
124
|
+
elif "403" in error_msg or "forbidden" in error_msg.lower():
|
125
|
+
raise RateLimitError(
|
126
|
+
f"DuckDuckGo access forbidden (possible rate limit): {error_msg}"
|
127
|
+
)
|
128
|
+
elif (
|
129
|
+
"timeout" in error_msg.lower()
|
130
|
+
or "timed out" in error_msg.lower()
|
131
|
+
):
|
132
|
+
# Timeouts can sometimes indicate rate limiting
|
133
|
+
raise RateLimitError(
|
134
|
+
f"DuckDuckGo timeout (possible rate limit): {error_msg}"
|
135
|
+
)
|
136
|
+
|
118
137
|
return []
|
119
138
|
|
120
139
|
def _get_full_content(
|
@@ -8,6 +8,7 @@ from langchain_core.language_models import BaseLLM
|
|
8
8
|
from requests.exceptions import RequestException
|
9
9
|
|
10
10
|
from ..search_engine_base import BaseSearchEngine
|
11
|
+
from ..rate_limiting import RateLimitError
|
11
12
|
|
12
13
|
# Set up logging
|
13
14
|
logging.basicConfig(level=logging.INFO)
|
@@ -220,20 +221,43 @@ class GooglePSESearchEngine(BaseSearchEngine):
|
|
220
221
|
return response.json()
|
221
222
|
|
222
223
|
except RequestException as e:
|
224
|
+
error_msg = str(e)
|
223
225
|
logger.warning(
|
224
226
|
"Request error on attempt %s / %s: %s",
|
225
227
|
attempt + 1,
|
226
228
|
self.max_retries,
|
227
|
-
|
229
|
+
error_msg,
|
228
230
|
)
|
231
|
+
|
232
|
+
# Check for rate limiting patterns
|
233
|
+
if (
|
234
|
+
"quota" in error_msg.lower()
|
235
|
+
or "quotaExceeded" in error_msg
|
236
|
+
or "dailyLimitExceeded" in error_msg
|
237
|
+
or "rateLimitExceeded" in error_msg
|
238
|
+
or "429" in error_msg
|
239
|
+
or "403" in error_msg
|
240
|
+
):
|
241
|
+
raise RateLimitError(
|
242
|
+
f"Google PSE rate limit/quota exceeded: {error_msg}"
|
243
|
+
)
|
244
|
+
|
229
245
|
last_exception = e
|
230
246
|
except Exception as e:
|
247
|
+
error_msg = str(e)
|
231
248
|
logger.warning(
|
232
249
|
"Error on attempt %s / %s: %s",
|
233
250
|
attempt + 1,
|
234
251
|
self.max_retries,
|
235
|
-
|
252
|
+
error_msg,
|
236
253
|
)
|
254
|
+
|
255
|
+
# Check for rate limiting patterns in general errors
|
256
|
+
if "quota" in error_msg.lower() or "limit" in error_msg.lower():
|
257
|
+
raise RateLimitError(
|
258
|
+
f"Google PSE error (possible rate limit): {error_msg}"
|
259
|
+
)
|
260
|
+
|
237
261
|
last_exception = e
|
238
262
|
|
239
263
|
attempt += 1
|
@@ -9,6 +9,7 @@ from langchain_core.language_models import BaseLLM
|
|
9
9
|
|
10
10
|
from ...config import search_config
|
11
11
|
from ..search_engine_base import BaseSearchEngine
|
12
|
+
from ..rate_limiting import RateLimitError
|
12
13
|
|
13
14
|
# Setup logging
|
14
15
|
logging.basicConfig(level=logging.INFO)
|
@@ -639,7 +640,20 @@ The default assumption should be that medical and scientific queries want RECENT
|
|
639
640
|
return summaries
|
640
641
|
|
641
642
|
except Exception as e:
|
642
|
-
|
643
|
+
error_msg = str(e)
|
644
|
+
logger.error(f"Error getting article summaries: {error_msg}")
|
645
|
+
|
646
|
+
# Check for rate limiting patterns
|
647
|
+
if (
|
648
|
+
"429" in error_msg
|
649
|
+
or "too many requests" in error_msg.lower()
|
650
|
+
or "rate limit" in error_msg.lower()
|
651
|
+
or "service unavailable" in error_msg.lower()
|
652
|
+
or "503" in error_msg
|
653
|
+
or "403" in error_msg
|
654
|
+
):
|
655
|
+
raise RateLimitError(f"PubMed rate limit hit: {error_msg}")
|
656
|
+
|
643
657
|
return []
|
644
658
|
|
645
659
|
def _get_article_abstracts(self, id_list: List[str]) -> Dict[str, str]:
|