feat(route-optimizer): add configurable download and upload weights for bandwidth scoring in AI settings
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m34s

This commit is contained in:
2026-03-05 11:00:46 +07:00
parent 2f4b1f6671
commit aab9ed0601
2 changed files with 58 additions and 2 deletions
+14 -2
View File
@@ -15,6 +15,8 @@ const DEFAULT_AI_SETTINGS = {
latencyWeight: 0.55,
bandwidthWeight: 0.35,
freshnessWeight: 0.1,
downloadWeight: 0.5,
uploadWeight: 0.5,
combineHomeToJumphostWeight: 0.45,
combineJumphostToExitWeight: 0.55,
probabilityScale: 5,
@@ -47,6 +49,11 @@ function positiveNumber(v, fallback) {
return Number.isFinite(n) && n > 0 ? n : fallback;
}
function nonNegativeNumber(v, fallback) {
const n = Number(v);
return Number.isFinite(n) && n >= 0 ? n : fallback;
}
function makeServerKey(server) {
if (!server || typeof server !== 'object') return '';
return String(server.id || server.dns || server.ip || '').trim();
@@ -97,10 +104,10 @@ function freshnessScore(cacheUpdatedAt, aiSettings) {
}
function bandwidthScore({ speedDownloadMbps, speedUploadMbps, aiSettings }) {
// DL/UL considered separately with equal 50/50 contribution.
// DL/UL considered separately with configurable weights.
const dl = normalizeBandwidthValue(speedDownloadMbps, aiSettings);
const ul = normalizeBandwidthValue(speedUploadMbps, aiSettings);
return clamp((dl + ul) / 2, 0, 1);
return clamp(dl * aiSettings.downloadWeight + ul * aiSettings.uploadWeight, 0, 1);
}
function buildSegmentScore({
@@ -252,6 +259,9 @@ async function loadAiSettings() {
const bandwidthWeight = positiveNumber(src.bandwidthWeight, DEFAULT_AI_SETTINGS.bandwidthWeight);
const freshnessWeight = positiveNumber(src.freshnessWeight, DEFAULT_AI_SETTINGS.freshnessWeight);
const sum = latencyWeight + bandwidthWeight + freshnessWeight || 1;
const downloadWeight = nonNegativeNumber(src.downloadWeight, DEFAULT_AI_SETTINGS.downloadWeight);
const uploadWeight = nonNegativeNumber(src.uploadWeight, DEFAULT_AI_SETTINGS.uploadWeight);
const sumDu = downloadWeight + uploadWeight;
const hjW = positiveNumber(
src.combineHomeToJumphostWeight,
DEFAULT_AI_SETTINGS.combineHomeToJumphostWeight
@@ -276,6 +286,8 @@ async function loadAiSettings() {
latencyWeight: latencyWeight / sum,
bandwidthWeight: bandwidthWeight / sum,
freshnessWeight: freshnessWeight / sum,
downloadWeight: sumDu > 0 ? downloadWeight / sumDu : DEFAULT_AI_SETTINGS.downloadWeight,
uploadWeight: sumDu > 0 ? uploadWeight / sumDu : DEFAULT_AI_SETTINGS.uploadWeight,
combineHomeToJumphostWeight: hjW / sum2,
combineJumphostToExitWeight: jeW / sum2,
probabilityScale: clamp(
+44
View File
@@ -125,6 +125,8 @@ export default function SettingsPage() {
const [aiLatencyWeight, setAiLatencyWeight] = useState('0.55');
const [aiBandwidthWeight, setAiBandwidthWeight] = useState('0.35');
const [aiFreshnessWeight, setAiFreshnessWeight] = useState('0.10');
const [aiDownloadWeight, setAiDownloadWeight] = useState('0.5');
const [aiUploadWeight, setAiUploadWeight] = useState('0.5');
const [aiHomeToJhWeight, setAiHomeToJhWeight] = useState('0.45');
const [aiJhToExitWeight, setAiJhToExitWeight] = useState('0.55');
const [aiProbabilityScale, setAiProbabilityScale] = useState('5');
@@ -529,6 +531,12 @@ export default function SettingsPage() {
setAiFreshnessWeight(
ai?.freshnessWeight != null ? String(ai.freshnessWeight) : '0.10'
);
setAiDownloadWeight(
ai?.downloadWeight != null ? String(ai.downloadWeight) : '0.5'
);
setAiUploadWeight(
ai?.uploadWeight != null ? String(ai.uploadWeight) : '0.5'
);
setAiHomeToJhWeight(
ai?.combineHomeToJumphostWeight != null
? String(ai.combineHomeToJumphostWeight)
@@ -773,6 +781,14 @@ export default function SettingsPage() {
latencyWeight: Math.max(0, parseFloat(aiLatencyWeight) || 0.55),
bandwidthWeight: Math.max(0, parseFloat(aiBandwidthWeight) || 0.35),
freshnessWeight: Math.max(0, parseFloat(aiFreshnessWeight) || 0.1),
downloadWeight: Math.max(
0,
Number.isFinite(parseFloat(aiDownloadWeight)) ? parseFloat(aiDownloadWeight) : 0.5
),
uploadWeight: Math.max(
0,
Number.isFinite(parseFloat(aiUploadWeight)) ? parseFloat(aiUploadWeight) : 0.5
),
combineHomeToJumphostWeight: Math.max(
0,
parseFloat(aiHomeToJhWeight) || 0.45
@@ -1585,6 +1601,34 @@ export default function SettingsPage() {
/>
</div>
<div className="col-12 mt-1"><h4 className="subheader">Баланс bandwidth (DL/UL)</h4></div>
<div className="col-12 col-md-6">
<FormField
label="Вес Download (DL)"
name="aiDownloadWeight"
type="number"
value={aiDownloadWeight}
onChange={setAiDownloadWeight}
helpText="Внутренний вес DL в bandwidth score сегмента."
disabled={saving}
min={0}
step="0.01"
/>
</div>
<div className="col-12 col-md-6">
<FormField
label="Вес Upload (UL)"
name="aiUploadWeight"
type="number"
value={aiUploadWeight}
onChange={setAiUploadWeight}
helpText="Внутренний вес UL в bandwidth score сегмента."
disabled={saving}
min={0}
step="0.01"
/>
</div>
<div className="col-12 mt-2"><h4 className="subheader">Сборка полного маршрута</h4></div>
<div className="col-12 col-md-6">
<FormField