@lynn123411/dsh-a6api 1.5.0 → 1.5.2
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.
- package/README.md +1 -0
- package/lib/client.js +40 -1
- package/lib/client.js.map +2 -2
- package/lib/index.js +233 -142
- package/lib/index.js.map +2 -2
- package/lib/types/server/a6api-client.d.ts +7 -0
- package/lib/types/types.d.ts +2 -0
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -386,6 +386,17 @@ function formatCnyPrice(micros, exchangeRate = 6.7209) {
|
|
|
386
386
|
if (cny < 1) return `\xA5${cny.toFixed(4)}`;
|
|
387
387
|
return `\xA5${cny.toFixed(3)}`;
|
|
388
388
|
}
|
|
389
|
+
var BLENDED_OUT_SHARE = 35e-4;
|
|
390
|
+
function computeBlendedPrice100m(inMicros, cacheReadMicros, outMicros, cacheHitRatePct, exchangeRate) {
|
|
391
|
+
const inY = inMicros / 1e6 * exchangeRate;
|
|
392
|
+
const hitY = cacheReadMicros / 1e6 * exchangeRate;
|
|
393
|
+
const outY = outMicros / 1e6 * exchangeRate;
|
|
394
|
+
if (inY <= 0 && hitY <= 0 && outY <= 0) return void 0;
|
|
395
|
+
const h = Math.min(1, Math.max(0, cacheHitRatePct / 100));
|
|
396
|
+
const inShare = 1 - BLENDED_OUT_SHARE;
|
|
397
|
+
const per1M = h * inShare * hitY + (1 - h) * inShare * inY + BLENDED_OUT_SHARE * outY;
|
|
398
|
+
return per1M * 100;
|
|
399
|
+
}
|
|
389
400
|
function buildWebHeaders2(userId, accessToken) {
|
|
390
401
|
const headers = {
|
|
391
402
|
Accept: "application/json",
|
|
@@ -648,6 +659,7 @@ async function fetchChannelDetails(channelId, userId, accessToken, targetModelNa
|
|
|
648
659
|
const recentSuccessRate = item.recent_success_rate !== void 0 ? Number(item.recent_success_rate) / 100 : 100;
|
|
649
660
|
const cacheHitRate = item.cache_hit_rate_24h !== void 0 ? Number(item.cache_hit_rate_24h) / 100 : 72;
|
|
650
661
|
const lastSuccessAt = Number(item.last_success_at || item.last_test_time || 0);
|
|
662
|
+
const blended100m = computeBlendedPrice100m(inMicros, cacheReadMicros, outMicros, cacheHitRate, rate);
|
|
651
663
|
const ratioCny = Number(item.realtime_ratio_cny || inMicros / 1e6 * rate || 0.0341);
|
|
652
664
|
const ratioFormatted = ratioCny.toFixed(4);
|
|
653
665
|
return {
|
|
@@ -686,6 +698,7 @@ async function fetchChannelDetails(channelId, userId, accessToken, targetModelNa
|
|
|
686
698
|
p50_ttft_ms: Number(item.p50_ttft_ms || 2273),
|
|
687
699
|
recent_p50_ms: Number(item.recent_p50_ms || item.last_test_response_ms || 2340),
|
|
688
700
|
cache_hit_rate_pct: cacheHitRate,
|
|
701
|
+
blended_price_100m_cny: blended100m,
|
|
689
702
|
labels,
|
|
690
703
|
last_success_at: lastSuccessAt,
|
|
691
704
|
last_success_text: formatRelativeTime(lastSuccessAt),
|
|
@@ -756,6 +769,7 @@ async function fetchChannelDetails(channelId, userId, accessToken, targetModelNa
|
|
|
756
769
|
recent_p50_ms: Number(logSnapshot.use_time ? logSnapshot.use_time * 1e3 : 2340),
|
|
757
770
|
p50_ttft_ms: 2273,
|
|
758
771
|
cache_hit_rate_pct: 72,
|
|
772
|
+
blended_price_100m_cny: computeBlendedPrice100m(inMicros, cacheReadMicros, outMicros, 72, rate),
|
|
759
773
|
labels: ["\u7A33\u5B9A", "\u4F4E\u4EF7", "\u9AD8\u901F", "\u9AD8\u8D28"],
|
|
760
774
|
last_success_at: Math.floor(Date.now() / 1e3),
|
|
761
775
|
last_success_text: "\u521A\u521A",
|
|
@@ -1799,6 +1813,197 @@ function overlayPinsOnModels(models, pins, tokenId) {
|
|
|
1799
1813
|
};
|
|
1800
1814
|
});
|
|
1801
1815
|
}
|
|
1816
|
+
function createUpstreamMemo(ttlMs, shouldCache) {
|
|
1817
|
+
let cache = /* @__PURE__ */ new Map();
|
|
1818
|
+
const inflight = /* @__PURE__ */ new Map();
|
|
1819
|
+
let epoch = 0;
|
|
1820
|
+
return {
|
|
1821
|
+
/**
|
|
1822
|
+
* 数据变更后调用:旧结果立即不可用。
|
|
1823
|
+
* 同时清空在途构建表:变更完成后才到达的请求不会「加入」变更前已开始的旧构建
|
|
1824
|
+
* (旧构建的写缓存仍有代际校验兜底),而是必然发起一次新构建拿到最新数据。
|
|
1825
|
+
*/
|
|
1826
|
+
invalidate() {
|
|
1827
|
+
epoch += 1;
|
|
1828
|
+
cache.clear();
|
|
1829
|
+
inflight.clear();
|
|
1830
|
+
},
|
|
1831
|
+
async get(key, fetcher) {
|
|
1832
|
+
const hit = cache.get(key);
|
|
1833
|
+
if (hit && hit.epoch === epoch && Date.now() - hit.at < ttlMs) return hit.data;
|
|
1834
|
+
let pending = inflight.get(key);
|
|
1835
|
+
if (!pending) {
|
|
1836
|
+
const buildEpoch = epoch;
|
|
1837
|
+
const run = Promise.resolve().then(fetcher).then((data) => {
|
|
1838
|
+
if (buildEpoch === epoch && (!shouldCache || shouldCache(data))) {
|
|
1839
|
+
cache.set(key, { data, at: Date.now(), epoch: buildEpoch });
|
|
1840
|
+
}
|
|
1841
|
+
return data;
|
|
1842
|
+
}).finally(() => {
|
|
1843
|
+
if (inflight.get(key) === run) inflight.delete(key);
|
|
1844
|
+
});
|
|
1845
|
+
inflight.set(key, run);
|
|
1846
|
+
pending = run;
|
|
1847
|
+
}
|
|
1848
|
+
return pending;
|
|
1849
|
+
}
|
|
1850
|
+
};
|
|
1851
|
+
}
|
|
1852
|
+
var stateMemo = createUpstreamMemo(12e4);
|
|
1853
|
+
var priceCountsMemo = createUpstreamMemo(12e4, (counts) => !counts.authError);
|
|
1854
|
+
function stateCacheKeyOf(config) {
|
|
1855
|
+
const fingerprint = (s) => {
|
|
1856
|
+
let h = 0;
|
|
1857
|
+
for (let i = 0; i < s.length; i++) h = h * 31 + s.charCodeAt(i) | 0;
|
|
1858
|
+
return (h >>> 0).toString(36);
|
|
1859
|
+
};
|
|
1860
|
+
return `${config.baseURL || ""}|${config.userId || ""}|${fingerprint(config.apiKey || "")}|${fingerprint(config.accessToken || "")}`;
|
|
1861
|
+
}
|
|
1862
|
+
async function getCachedStateResponse(config, configAccess) {
|
|
1863
|
+
return stateMemo.get(stateCacheKeyOf(config), () => buildStateResponse(config, configAccess));
|
|
1864
|
+
}
|
|
1865
|
+
async function buildStateResponse(config, configAccess) {
|
|
1866
|
+
const token = config.accessToken || "";
|
|
1867
|
+
const [balance, dshConfiguredModels, modelIdsRaw, allLogs, pins] = await Promise.all([
|
|
1868
|
+
fetchBalance(config.baseURL, config.apiKey, config.userId, token),
|
|
1869
|
+
configAccess.getDshConfiguredModels(),
|
|
1870
|
+
config.apiKey ? fetchTokenModels(config.baseURL, config.apiKey) : Promise.resolve([]),
|
|
1871
|
+
fetchRecentLogs(config.userId, token, 100),
|
|
1872
|
+
config.userId && token ? fetchMarketplacePins(config.userId, token).catch(() => []) : Promise.resolve([])
|
|
1873
|
+
]);
|
|
1874
|
+
if (balance?.userId && String(balance.userId) !== config.userId) {
|
|
1875
|
+
tokenResolveCache = null;
|
|
1876
|
+
config.userId = String(balance.userId);
|
|
1877
|
+
await configAccess.writeConfig({ userId: config.userId });
|
|
1878
|
+
}
|
|
1879
|
+
let modelIds = modelIdsRaw;
|
|
1880
|
+
if (modelIds.length === 0) {
|
|
1881
|
+
modelIds = [
|
|
1882
|
+
.../* @__PURE__ */ new Set([
|
|
1883
|
+
...dshConfiguredModels,
|
|
1884
|
+
"gpt-5.6-sol",
|
|
1885
|
+
"gpt-5.6-terra",
|
|
1886
|
+
"gpt-5.6-luna",
|
|
1887
|
+
"claude-fable-5",
|
|
1888
|
+
"claude-opus-5",
|
|
1889
|
+
"grok-4.6"
|
|
1890
|
+
])
|
|
1891
|
+
];
|
|
1892
|
+
}
|
|
1893
|
+
allLogs.sort((a, b) => (Number(b.created_at) || 0) - (Number(a.created_at) || 0));
|
|
1894
|
+
if (config.userId || token) {
|
|
1895
|
+
const missing = modelIds.filter((m) => {
|
|
1896
|
+
const entry = merchantCardCache.get(m.toLowerCase());
|
|
1897
|
+
return !entry || Date.now() - entry.at >= MERCHANT_CARD_TTL_MS;
|
|
1898
|
+
});
|
|
1899
|
+
if (missing.length > 0) {
|
|
1900
|
+
let found = {};
|
|
1901
|
+
try {
|
|
1902
|
+
found = await Promise.race([
|
|
1903
|
+
getKnownMerchantsFromLogs(config.userId, token, missing, allLogs),
|
|
1904
|
+
new Promise((resolve2) => setTimeout(() => resolve2({}), 1e4))
|
|
1905
|
+
]);
|
|
1906
|
+
} catch {
|
|
1907
|
+
found = {};
|
|
1908
|
+
}
|
|
1909
|
+
for (const [mName, card] of Object.entries(found)) {
|
|
1910
|
+
merchantCardCache.set(mName.toLowerCase(), { card, at: Date.now() });
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
const lastRoutedMap = /* @__PURE__ */ new Map();
|
|
1915
|
+
for (const log of allLogs) {
|
|
1916
|
+
const mName = log.model_name;
|
|
1917
|
+
const chId = Number(log.channel);
|
|
1918
|
+
const ts = Number(log.created_at) || 0;
|
|
1919
|
+
if (mName && chId > 0 && ts > 0 && !lastRoutedMap.has(mName.toLowerCase())) {
|
|
1920
|
+
lastRoutedMap.set(mName.toLowerCase(), ts);
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
const dshSet = new Set(dshConfiguredModels);
|
|
1924
|
+
let models = modelIds.map((mId) => {
|
|
1925
|
+
const meta = resolveModelMeta(mId);
|
|
1926
|
+
const cacheEntry = merchantCardCache.get(mId.toLowerCase());
|
|
1927
|
+
const cachedCard = cacheEntry && Date.now() - cacheEntry.at < MERCHANT_CARD_TTL_MS ? cacheEntry.card : void 0;
|
|
1928
|
+
const routedAt = lastRoutedMap.get(mId.toLowerCase());
|
|
1929
|
+
return {
|
|
1930
|
+
model_name: mId,
|
|
1931
|
+
brand: meta.brand,
|
|
1932
|
+
contextWindow: meta.contextWindow,
|
|
1933
|
+
maxTokens: meta.maxTokens,
|
|
1934
|
+
modalities: meta.modalities,
|
|
1935
|
+
hasReasoning: Boolean(meta.reasoningEfforts || meta.thinkingFormat),
|
|
1936
|
+
inDsh: dshSet.has(mId),
|
|
1937
|
+
merchant: cachedCard,
|
|
1938
|
+
probeStatus: cachedCard ? "success" : "idle",
|
|
1939
|
+
lastRoutedAt: routedAt,
|
|
1940
|
+
lastRoutedText: routedAt ? formatRelativeTime(routedAt) : void 0
|
|
1941
|
+
};
|
|
1942
|
+
});
|
|
1943
|
+
const resolvedTokenId = pins.length > 0 ? await resolveTokenId(config) : null;
|
|
1944
|
+
models = overlayPinsOnModels(models, pins, resolvedTokenId);
|
|
1945
|
+
const rePointTargets = models.filter(
|
|
1946
|
+
(m) => m.pinStatus === "pin_elsewhere" && m.pinTokenMatched === true && m.pinnedChannelId && m.pinnedChannelId > 0
|
|
1947
|
+
).map((m) => ({ modelName: m.model_name, channelId: m.pinnedChannelId }));
|
|
1948
|
+
if (rePointTargets.length > 0 && config.userId && token) {
|
|
1949
|
+
try {
|
|
1950
|
+
await Promise.race([
|
|
1951
|
+
(async () => {
|
|
1952
|
+
for (let i = 0; i < rePointTargets.length; i += 4) {
|
|
1953
|
+
const batch = rePointTargets.slice(i, i + 4);
|
|
1954
|
+
await Promise.all(
|
|
1955
|
+
batch.map(async ({ modelName, channelId }) => {
|
|
1956
|
+
try {
|
|
1957
|
+
const pinnedCard = await fetchChannelDetails(
|
|
1958
|
+
channelId,
|
|
1959
|
+
config.userId,
|
|
1960
|
+
token,
|
|
1961
|
+
modelName
|
|
1962
|
+
);
|
|
1963
|
+
if (pinnedCard && Number(pinnedCard.channel_id) === Number(channelId)) {
|
|
1964
|
+
merchantCardCache.set(modelName.toLowerCase(), { card: pinnedCard, at: Date.now() });
|
|
1965
|
+
}
|
|
1966
|
+
} catch {
|
|
1967
|
+
}
|
|
1968
|
+
})
|
|
1969
|
+
);
|
|
1970
|
+
}
|
|
1971
|
+
})(),
|
|
1972
|
+
new Promise((resolve2) => setTimeout(() => resolve2(), 1e4))
|
|
1973
|
+
]);
|
|
1974
|
+
} catch {
|
|
1975
|
+
}
|
|
1976
|
+
models = models.map((m) => {
|
|
1977
|
+
if (m.pinStatus !== "pin_elsewhere" || m.pinTokenMatched !== true) return m;
|
|
1978
|
+
const entry = merchantCardCache.get(m.model_name.toLowerCase());
|
|
1979
|
+
const card = entry && Date.now() - entry.at < MERCHANT_CARD_TTL_MS ? entry.card : void 0;
|
|
1980
|
+
if (card && Number(card.channel_id) === Number(m.pinnedChannelId)) {
|
|
1981
|
+
const pinnedLog = allLogs.find(
|
|
1982
|
+
(l) => l.model_name?.toLowerCase() === m.model_name.toLowerCase() && Number(l.channel) === m.pinnedChannelId
|
|
1983
|
+
);
|
|
1984
|
+
const pinnedAt = pinnedLog ? Number(pinnedLog.created_at) || 0 : void 0;
|
|
1985
|
+
return {
|
|
1986
|
+
...m,
|
|
1987
|
+
merchant: card,
|
|
1988
|
+
pinStatus: "pin_here",
|
|
1989
|
+
probeStatus: "success",
|
|
1990
|
+
lastRoutedAt: pinnedAt,
|
|
1991
|
+
lastRoutedText: pinnedAt ? formatRelativeTime(pinnedAt) : void 0
|
|
1992
|
+
};
|
|
1993
|
+
}
|
|
1994
|
+
return m;
|
|
1995
|
+
});
|
|
1996
|
+
}
|
|
1997
|
+
const recentLogs = allLogs.slice(0, 20);
|
|
1998
|
+
return {
|
|
1999
|
+
config: maskConfig(config),
|
|
2000
|
+
balance,
|
|
2001
|
+
models,
|
|
2002
|
+
dshConfiguredModels,
|
|
2003
|
+
recentLogs,
|
|
2004
|
+
pins
|
|
2005
|
+
};
|
|
2006
|
+
}
|
|
1802
2007
|
function apply(ctx) {
|
|
1803
2008
|
const configAccess = createConfigAccess(ctx);
|
|
1804
2009
|
void configAccess.ensureMigrated();
|
|
@@ -1821,146 +2026,7 @@ function apply(ctx) {
|
|
|
1821
2026
|
try {
|
|
1822
2027
|
if (pathname === "/state" && (req.method === "GET" || req.method === "HEAD")) {
|
|
1823
2028
|
const config = await configAccess.readConfig();
|
|
1824
|
-
const
|
|
1825
|
-
const [balance, dshConfiguredModels, modelIdsRaw, allLogs, pins] = await Promise.all([
|
|
1826
|
-
fetchBalance(config.baseURL, config.apiKey, config.userId, token),
|
|
1827
|
-
configAccess.getDshConfiguredModels(),
|
|
1828
|
-
config.apiKey ? fetchTokenModels(config.baseURL, config.apiKey) : Promise.resolve([]),
|
|
1829
|
-
fetchRecentLogs(config.userId, token, 100),
|
|
1830
|
-
config.userId && token ? fetchMarketplacePins(config.userId, token).catch(() => []) : Promise.resolve([])
|
|
1831
|
-
]);
|
|
1832
|
-
if (balance?.userId && String(balance.userId) !== config.userId) {
|
|
1833
|
-
tokenResolveCache = null;
|
|
1834
|
-
config.userId = String(balance.userId);
|
|
1835
|
-
await configAccess.writeConfig({ userId: config.userId });
|
|
1836
|
-
}
|
|
1837
|
-
let modelIds = modelIdsRaw;
|
|
1838
|
-
if (modelIds.length === 0) {
|
|
1839
|
-
modelIds = [
|
|
1840
|
-
.../* @__PURE__ */ new Set([
|
|
1841
|
-
...dshConfiguredModels,
|
|
1842
|
-
"gpt-5.6-sol",
|
|
1843
|
-
"gpt-5.6-terra",
|
|
1844
|
-
"gpt-5.6-luna",
|
|
1845
|
-
"claude-fable-5",
|
|
1846
|
-
"claude-opus-5",
|
|
1847
|
-
"grok-4.6"
|
|
1848
|
-
])
|
|
1849
|
-
];
|
|
1850
|
-
}
|
|
1851
|
-
allLogs.sort((a, b) => (Number(b.created_at) || 0) - (Number(a.created_at) || 0));
|
|
1852
|
-
if (config.userId || token) {
|
|
1853
|
-
const missing = modelIds.filter((m) => {
|
|
1854
|
-
const entry = merchantCardCache.get(m.toLowerCase());
|
|
1855
|
-
return !entry || Date.now() - entry.at >= MERCHANT_CARD_TTL_MS;
|
|
1856
|
-
});
|
|
1857
|
-
if (missing.length > 0) {
|
|
1858
|
-
let found = {};
|
|
1859
|
-
try {
|
|
1860
|
-
found = await Promise.race([
|
|
1861
|
-
getKnownMerchantsFromLogs(config.userId, token, missing, allLogs),
|
|
1862
|
-
new Promise((resolve2) => setTimeout(() => resolve2({}), 1e4))
|
|
1863
|
-
]);
|
|
1864
|
-
} catch {
|
|
1865
|
-
found = {};
|
|
1866
|
-
}
|
|
1867
|
-
for (const [mName, card] of Object.entries(found)) {
|
|
1868
|
-
merchantCardCache.set(mName.toLowerCase(), { card, at: Date.now() });
|
|
1869
|
-
}
|
|
1870
|
-
}
|
|
1871
|
-
}
|
|
1872
|
-
const lastRoutedMap = /* @__PURE__ */ new Map();
|
|
1873
|
-
for (const log of allLogs) {
|
|
1874
|
-
const mName = log.model_name;
|
|
1875
|
-
const chId = Number(log.channel);
|
|
1876
|
-
const ts = Number(log.created_at) || 0;
|
|
1877
|
-
if (mName && chId > 0 && ts > 0 && !lastRoutedMap.has(mName.toLowerCase())) {
|
|
1878
|
-
lastRoutedMap.set(mName.toLowerCase(), ts);
|
|
1879
|
-
}
|
|
1880
|
-
}
|
|
1881
|
-
const dshSet = new Set(dshConfiguredModels);
|
|
1882
|
-
let models = modelIds.map((mId) => {
|
|
1883
|
-
const meta = resolveModelMeta(mId);
|
|
1884
|
-
const cacheEntry = merchantCardCache.get(mId.toLowerCase());
|
|
1885
|
-
const cachedCard = cacheEntry && Date.now() - cacheEntry.at < MERCHANT_CARD_TTL_MS ? cacheEntry.card : void 0;
|
|
1886
|
-
const routedAt = lastRoutedMap.get(mId.toLowerCase());
|
|
1887
|
-
return {
|
|
1888
|
-
model_name: mId,
|
|
1889
|
-
brand: meta.brand,
|
|
1890
|
-
contextWindow: meta.contextWindow,
|
|
1891
|
-
maxTokens: meta.maxTokens,
|
|
1892
|
-
modalities: meta.modalities,
|
|
1893
|
-
hasReasoning: Boolean(meta.reasoningEfforts || meta.thinkingFormat),
|
|
1894
|
-
inDsh: dshSet.has(mId),
|
|
1895
|
-
merchant: cachedCard,
|
|
1896
|
-
probeStatus: cachedCard ? "success" : "idle",
|
|
1897
|
-
lastRoutedAt: routedAt,
|
|
1898
|
-
lastRoutedText: routedAt ? formatRelativeTime(routedAt) : void 0
|
|
1899
|
-
};
|
|
1900
|
-
});
|
|
1901
|
-
const resolvedTokenId = pins.length > 0 ? await resolveTokenId(config) : null;
|
|
1902
|
-
models = overlayPinsOnModels(models, pins, resolvedTokenId);
|
|
1903
|
-
const rePointTargets = models.filter(
|
|
1904
|
-
(m) => m.pinStatus === "pin_elsewhere" && m.pinTokenMatched === true && m.pinnedChannelId && m.pinnedChannelId > 0
|
|
1905
|
-
).map((m) => ({ modelName: m.model_name, channelId: m.pinnedChannelId }));
|
|
1906
|
-
if (rePointTargets.length > 0 && config.userId && token) {
|
|
1907
|
-
try {
|
|
1908
|
-
await Promise.race([
|
|
1909
|
-
(async () => {
|
|
1910
|
-
for (let i = 0; i < rePointTargets.length; i += 4) {
|
|
1911
|
-
const batch = rePointTargets.slice(i, i + 4);
|
|
1912
|
-
await Promise.all(
|
|
1913
|
-
batch.map(async ({ modelName, channelId }) => {
|
|
1914
|
-
try {
|
|
1915
|
-
const pinnedCard = await fetchChannelDetails(
|
|
1916
|
-
channelId,
|
|
1917
|
-
config.userId,
|
|
1918
|
-
token,
|
|
1919
|
-
modelName
|
|
1920
|
-
);
|
|
1921
|
-
if (pinnedCard && Number(pinnedCard.channel_id) === Number(channelId)) {
|
|
1922
|
-
merchantCardCache.set(modelName.toLowerCase(), { card: pinnedCard, at: Date.now() });
|
|
1923
|
-
}
|
|
1924
|
-
} catch {
|
|
1925
|
-
}
|
|
1926
|
-
})
|
|
1927
|
-
);
|
|
1928
|
-
}
|
|
1929
|
-
})(),
|
|
1930
|
-
new Promise((resolve2) => setTimeout(() => resolve2(), 1e4))
|
|
1931
|
-
]);
|
|
1932
|
-
} catch {
|
|
1933
|
-
}
|
|
1934
|
-
models = models.map((m) => {
|
|
1935
|
-
if (m.pinStatus !== "pin_elsewhere" || m.pinTokenMatched !== true) return m;
|
|
1936
|
-
const entry = merchantCardCache.get(m.model_name.toLowerCase());
|
|
1937
|
-
const card = entry && Date.now() - entry.at < MERCHANT_CARD_TTL_MS ? entry.card : void 0;
|
|
1938
|
-
if (card && Number(card.channel_id) === Number(m.pinnedChannelId)) {
|
|
1939
|
-
const pinnedLog = allLogs.find(
|
|
1940
|
-
(l) => l.model_name?.toLowerCase() === m.model_name.toLowerCase() && Number(l.channel) === m.pinnedChannelId
|
|
1941
|
-
);
|
|
1942
|
-
const pinnedAt = pinnedLog ? Number(pinnedLog.created_at) || 0 : void 0;
|
|
1943
|
-
return {
|
|
1944
|
-
...m,
|
|
1945
|
-
merchant: card,
|
|
1946
|
-
pinStatus: "pin_here",
|
|
1947
|
-
probeStatus: "success",
|
|
1948
|
-
lastRoutedAt: pinnedAt,
|
|
1949
|
-
lastRoutedText: pinnedAt ? formatRelativeTime(pinnedAt) : void 0
|
|
1950
|
-
};
|
|
1951
|
-
}
|
|
1952
|
-
return m;
|
|
1953
|
-
});
|
|
1954
|
-
}
|
|
1955
|
-
const recentLogs = allLogs.slice(0, 20);
|
|
1956
|
-
const response = {
|
|
1957
|
-
config: maskConfig(config),
|
|
1958
|
-
balance,
|
|
1959
|
-
models,
|
|
1960
|
-
dshConfiguredModels,
|
|
1961
|
-
recentLogs,
|
|
1962
|
-
pins
|
|
1963
|
-
};
|
|
2029
|
+
const response = await getCachedStateResponse(config, configAccess);
|
|
1964
2030
|
return sendJson(res, 200, { ok: true, data: response });
|
|
1965
2031
|
}
|
|
1966
2032
|
if (pathname === "/config" && req.method === "POST") {
|
|
@@ -1993,6 +2059,8 @@ function apply(ctx) {
|
|
|
1993
2059
|
if (updated.activeModels.length > 0) {
|
|
1994
2060
|
await configAccess.syncModels(updated.baseURL, updated.activeModels);
|
|
1995
2061
|
}
|
|
2062
|
+
stateMemo.invalidate();
|
|
2063
|
+
priceCountsMemo.invalidate();
|
|
1996
2064
|
return sendJson(res, 200, { ok: true, config: maskConfig(updated), balance });
|
|
1997
2065
|
}
|
|
1998
2066
|
if (pathname === "/balance" && (req.method === "GET" || req.method === "HEAD")) {
|
|
@@ -2018,6 +2086,7 @@ function apply(ctx) {
|
|
|
2018
2086
|
if (result.merchant) {
|
|
2019
2087
|
merchantCardCache.set(modelName.toLowerCase(), { card: result.merchant, at: Date.now() });
|
|
2020
2088
|
}
|
|
2089
|
+
stateMemo.invalidate();
|
|
2021
2090
|
return sendJson(res, 200, { ok: true, result });
|
|
2022
2091
|
}
|
|
2023
2092
|
let modelIds = body.modelNames;
|
|
@@ -2035,6 +2104,7 @@ function apply(ctx) {
|
|
|
2035
2104
|
}
|
|
2036
2105
|
results.push(r);
|
|
2037
2106
|
}
|
|
2107
|
+
stateMemo.invalidate();
|
|
2038
2108
|
return sendJson(res, 200, { ok: true, results });
|
|
2039
2109
|
}
|
|
2040
2110
|
if (pathname === "/sync-models" && req.method === "POST") {
|
|
@@ -2045,6 +2115,7 @@ function apply(ctx) {
|
|
|
2045
2115
|
const baseURL = body.baseURL || config.baseURL;
|
|
2046
2116
|
await configAccess.syncModels(baseURL, modelIds);
|
|
2047
2117
|
const dshConfiguredModels = await configAccess.getDshConfiguredModels();
|
|
2118
|
+
stateMemo.invalidate();
|
|
2048
2119
|
return sendJson(res, 200, { ok: true, dshConfiguredModels });
|
|
2049
2120
|
}
|
|
2050
2121
|
if (pathname === "/pin" && req.method === "POST") {
|
|
@@ -2059,8 +2130,10 @@ function apply(ctx) {
|
|
|
2059
2130
|
let card = cachedMerchantOf(modelName);
|
|
2060
2131
|
let tokenId = await resolveTokenId(config);
|
|
2061
2132
|
if ((!card || !tokenId) && config.apiKey) {
|
|
2133
|
+
let probedOk = false;
|
|
2062
2134
|
try {
|
|
2063
2135
|
const probe = await probeSingleModel(config.baseURL, config.apiKey, userId, token, modelName);
|
|
2136
|
+
probedOk = Boolean(probe && probe.success);
|
|
2064
2137
|
if (!tokenId && probe.tokenId && Number(probe.tokenId) > 0) tokenId = Number(probe.tokenId);
|
|
2065
2138
|
if (!card && probe.merchant) {
|
|
2066
2139
|
card = probe.merchant;
|
|
@@ -2068,6 +2141,7 @@ function apply(ctx) {
|
|
|
2068
2141
|
}
|
|
2069
2142
|
} catch {
|
|
2070
2143
|
}
|
|
2144
|
+
if (probedOk) stateMemo.invalidate();
|
|
2071
2145
|
}
|
|
2072
2146
|
if (!card) {
|
|
2073
2147
|
return sendJson(res, 400, { ok: false, error: "\u8BE5\u6A21\u578B\u6682\u65E0\u5546\u5BB6\u6570\u636E\uFF0C\u8BF7\u5148\u300C\u63A2\u6D4B\u5546\u5BB6\u300D" });
|
|
@@ -2094,6 +2168,7 @@ function apply(ctx) {
|
|
|
2094
2168
|
at: Date.now()
|
|
2095
2169
|
});
|
|
2096
2170
|
const pinList = await fetchMarketplacePins(userId, token);
|
|
2171
|
+
stateMemo.invalidate();
|
|
2097
2172
|
return sendJson(res, 200, { ok: true, message: `\u5DF2\u56FA\u5B9A ${modelName} \u81F3\u5546\u6237 #${card.channel_id}`, pins: pinList, tokenId });
|
|
2098
2173
|
}
|
|
2099
2174
|
if (pathname === "/unpin" && req.method === "POST") {
|
|
@@ -2121,6 +2196,7 @@ function apply(ctx) {
|
|
|
2121
2196
|
});
|
|
2122
2197
|
}
|
|
2123
2198
|
const pinList = await fetchMarketplacePins(userId, token);
|
|
2199
|
+
stateMemo.invalidate();
|
|
2124
2200
|
return sendJson(res, 200, { ok: true, message: `\u5DF2\u53D6\u6D88\u56FA\u5B9A ${modelName}`, pins: pinList, tokenId });
|
|
2125
2201
|
}
|
|
2126
2202
|
if (pathname === "/disable" && req.method === "POST") {
|
|
@@ -2130,14 +2206,17 @@ function apply(ctx) {
|
|
|
2130
2206
|
if (!modelName) return sendJson(res, 400, { ok: false, error: "\u7F3A\u5C11\u6A21\u578B\u540D\u79F0" });
|
|
2131
2207
|
let card = cachedMerchantOf(modelName);
|
|
2132
2208
|
if (!card && config.apiKey) {
|
|
2209
|
+
let probedOk = false;
|
|
2133
2210
|
try {
|
|
2134
2211
|
const probe = await probeSingleModel(config.baseURL, config.apiKey, config.userId, config.accessToken || "", modelName);
|
|
2212
|
+
probedOk = Boolean(probe && probe.success);
|
|
2135
2213
|
if (probe.merchant) {
|
|
2136
2214
|
card = probe.merchant;
|
|
2137
2215
|
merchantCardCache.set(modelName.toLowerCase(), { card, at: Date.now() });
|
|
2138
2216
|
}
|
|
2139
2217
|
} catch {
|
|
2140
2218
|
}
|
|
2219
|
+
if (probedOk) stateMemo.invalidate();
|
|
2141
2220
|
}
|
|
2142
2221
|
if (!card || !card.channel_id) {
|
|
2143
2222
|
return sendJson(res, 400, { ok: false, error: "\u8BE5\u6A21\u578B\u6682\u65E0\u5546\u5BB6\u6570\u636E\uFF0C\u8BF7\u5148\u300C\u63A2\u6D4B\u5546\u5BB6\u300D" });
|
|
@@ -2154,6 +2233,7 @@ function apply(ctx) {
|
|
|
2154
2233
|
card: { ...card, user_channel_disabled: true },
|
|
2155
2234
|
at: Date.now()
|
|
2156
2235
|
});
|
|
2236
|
+
stateMemo.invalidate();
|
|
2157
2237
|
return sendJson(res, 200, { ok: true, message: `\u5DF2\u7981\u7528\u5546\u6237 #${card.channel_id} \u5BF9\u8BE5\u6A21\u578B\u7684\u670D\u52A1` });
|
|
2158
2238
|
}
|
|
2159
2239
|
if (pathname === "/restore" && req.method === "POST") {
|
|
@@ -2163,14 +2243,17 @@ function apply(ctx) {
|
|
|
2163
2243
|
if (!modelName) return sendJson(res, 400, { ok: false, error: "\u7F3A\u5C11\u6A21\u578B\u540D\u79F0" });
|
|
2164
2244
|
let card = cachedMerchantOf(modelName);
|
|
2165
2245
|
if (!card && config.apiKey) {
|
|
2246
|
+
let probedOk = false;
|
|
2166
2247
|
try {
|
|
2167
2248
|
const probe = await probeSingleModel(config.baseURL, config.apiKey, config.userId, config.accessToken || "", modelName);
|
|
2249
|
+
probedOk = Boolean(probe && probe.success);
|
|
2168
2250
|
if (probe.merchant) {
|
|
2169
2251
|
card = probe.merchant;
|
|
2170
2252
|
merchantCardCache.set(modelName.toLowerCase(), { card, at: Date.now() });
|
|
2171
2253
|
}
|
|
2172
2254
|
} catch {
|
|
2173
2255
|
}
|
|
2256
|
+
if (probedOk) stateMemo.invalidate();
|
|
2174
2257
|
}
|
|
2175
2258
|
if (!card || !card.channel_id) {
|
|
2176
2259
|
return sendJson(res, 400, { ok: false, error: "\u8BE5\u6A21\u578B\u6682\u65E0\u5546\u5BB6\u6570\u636E\uFF0C\u8BF7\u5148\u300C\u63A2\u6D4B\u5546\u5BB6\u300D" });
|
|
@@ -2187,6 +2270,7 @@ function apply(ctx) {
|
|
|
2187
2270
|
card: { ...card, user_channel_disabled: false },
|
|
2188
2271
|
at: Date.now()
|
|
2189
2272
|
});
|
|
2273
|
+
stateMemo.invalidate();
|
|
2190
2274
|
return sendJson(res, 200, { ok: true, message: `\u5DF2\u6062\u590D\u5546\u6237 #${card.channel_id} \u5BF9\u8BE5\u6A21\u578B\u7684\u670D\u52A1` });
|
|
2191
2275
|
}
|
|
2192
2276
|
if (pathname === "/price-fluctuation" && (req.method === "GET" || req.method === "HEAD")) {
|
|
@@ -2195,8 +2279,11 @@ function apply(ctx) {
|
|
|
2195
2279
|
if (!token || !config.userId) {
|
|
2196
2280
|
return sendJson(res, 200, { ok: true, data: { pendingCount: 0, unseenCount: 0, totalCount: 0, hasAuth: false, authError: false, updatedAt: Date.now() } });
|
|
2197
2281
|
}
|
|
2198
|
-
const
|
|
2199
|
-
|
|
2282
|
+
const counts = await priceCountsMemo.get(stateCacheKeyOf(config), async () => {
|
|
2283
|
+
const result = await fetchPriceFluctuation(config.userId, token);
|
|
2284
|
+
const { notices, ...rest } = result;
|
|
2285
|
+
return rest;
|
|
2286
|
+
});
|
|
2200
2287
|
const hasAuth = !counts.authError;
|
|
2201
2288
|
return sendJson(res, 200, { ok: true, data: { pendingCount: counts.pendingCount, unseenCount: counts.unseenCount, totalCount: counts.totalCount, hasAuth, authError: Boolean(counts.authError), updatedAt: Date.now() } });
|
|
2202
2289
|
}
|
|
@@ -2219,6 +2306,7 @@ function apply(ctx) {
|
|
|
2219
2306
|
}
|
|
2220
2307
|
if (pathname === "/catalog/clear" && req.method === "POST") {
|
|
2221
2308
|
await clearCatalog();
|
|
2309
|
+
stateMemo.invalidate();
|
|
2222
2310
|
return sendJson(res, 200, { ok: true });
|
|
2223
2311
|
}
|
|
2224
2312
|
if (pathname === "/catalog/fetch-models" && req.method === "POST") {
|
|
@@ -2239,6 +2327,7 @@ function apply(ctx) {
|
|
|
2239
2327
|
await upsertCatalogEntries(
|
|
2240
2328
|
models.map((m) => ({ id: m.id, brand: m.brand, reasoningEfforts: m.reasoningEfforts }))
|
|
2241
2329
|
);
|
|
2330
|
+
stateMemo.invalidate();
|
|
2242
2331
|
return sendJson(res, 200, {
|
|
2243
2332
|
ok: true,
|
|
2244
2333
|
total: models.length,
|
|
@@ -2254,6 +2343,7 @@ function apply(ctx) {
|
|
|
2254
2343
|
return sendJson(res, 400, { ok: false, error: "\u76EE\u5F55\u4E3A\u7A7A\uFF0C\u8BF7\u5148\u300C\u4ECE A6API \u83B7\u53D6\u5E02\u573A\u6A21\u578B\u300D" });
|
|
2255
2344
|
}
|
|
2256
2345
|
const result = await queryOpenRouter(modelIds);
|
|
2346
|
+
stateMemo.invalidate();
|
|
2257
2347
|
return sendJson(res, 200, {
|
|
2258
2348
|
ok: true,
|
|
2259
2349
|
updated: result.updated.length,
|
|
@@ -2316,6 +2406,7 @@ function apply(ctx) {
|
|
|
2316
2406
|
} catch (err) {
|
|
2317
2407
|
console.warn("[dsh-a6api] catalog update: resync settings failed:", err?.message || err);
|
|
2318
2408
|
}
|
|
2409
|
+
stateMemo.invalidate();
|
|
2319
2410
|
return sendJson(res, 200, { ok: true, entry });
|
|
2320
2411
|
}
|
|
2321
2412
|
return sendJson(res, 404, { ok: false, error: "Not found" });
|