logo NodeSeekbeta

在deepseek网页上展示当前的缓存命中率

o8oiIuKQR8lFP6MF5BoyJMwRY4jf3kVN.webp

打开油猴,粘贴脚本 后 访问即可

// ==UserScript==
// @name         DeepSeek Usage 缓存命中率展示
// @namespace    http://tampermonkey.net/
// @version      6.0
// @description  支持中英文切换,完美恢复顶部与悬浮窗的缓存命中率显示
// @author       Your Name
// @match        https://platform.deepseek.com/usage*
// @run-at       document-start
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    console.log("DeepSeek Usage 缓存命中率");

    window.__ds_usage_map = {};
    window.__ds_usage_days = [];

    const originalFetch = window.fetch;
    window.fetch = async function(...args) {
        const response = await originalFetch.apply(this, args);
        try {
            const url = args[0] instanceof Request ? args[0].url : (typeof args[0] === 'string' ? args[0] : '');
            if (url.includes('/api/v0/usage/amount')) {
                const clone = response.clone();
                clone.json().then(data => extractAndDisplayHitRate(data)).catch(() => {});
            }
        } catch (e) {}
        return response;
    };

    const originalXhrOpen = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function(method, url) {
        this.addEventListener('load', function() {
            if (typeof url === 'string' && url.includes('/api/v0/usage/amount')) {
                try { extractAndDisplayHitRate(JSON.parse(this.responseText)); } catch(e) {}
            }
        });
        originalXhrOpen.apply(this, arguments);
    };

    function extractAndDisplayHitRate(payload) {
        try {
            const totals = payload?.data?.biz_data?.total;
            if (Array.isArray(totals)) {
                let newMap = {};
                for (const modelData of totals) {
                    if (!Array.isArray(modelData.usage)) continue;
                    let totalHit = 0; let totalMiss = 0; let totalResponse = 0; let totalTokens = 0;
                    for (const item of modelData.usage) {
                        const amount = parseInt(item.amount || '0', 10);
                        if (item.type === 'PROMPT_CACHE_HIT_TOKEN') { totalHit += amount; totalTokens += amount; }
                        else if (item.type === 'PROMPT_CACHE_MISS_TOKEN') { totalMiss += amount; totalTokens += amount; }
                        else if (item.type === 'RESPONSE_TOKEN') { totalResponse += amount; totalTokens += amount; }
                        else if (item.type === 'PROMPT_TOKEN') { totalTokens += amount; }
                    }
                    const totalPrompt = totalHit + totalMiss;
                    const hitRate = totalPrompt > 0 ? ((totalHit / totalPrompt) * 100).toFixed(2) : '0.00';
                    const rawNumberKey = totalTokens.toString();
                    newMap[rawNumberKey] = { model: modelData.model, hitRate, totalHit, totalMiss, totalResponse };
                }
                window.__ds_usage_map = newMap;
            }

            const days = payload?.data?.biz_data?.days;
            if (Array.isArray(days)) {
                window.__ds_usage_days = days;
            }
        } catch (e) {}
    }

    // 顶部整体注入
    setInterval(() => {
        const map = window.__ds_usage_map;
        if (Object.keys(map).length === 0) return;

        const spans = document.querySelectorAll('span.ds-text--lsp');
        for (let span of spans) {
            // "Tokens" 这个词在中文版也没有翻译,依然是 Tokens,所以这里不用改
            if (span.textContent.trim() === 'Tokens') {
                const container = span.parentElement;
                const numberSpan = span.nextElementSibling;
                if (!numberSpan) continue;

                const uiTokenKey = numberSpan.textContent.replace(/,/g, '').replace(/\s/g, '');
                const data = map[uiTokenKey];

                if (data) {
                    let existingBadge = container.querySelector('.ds-injected-hit-rate');
                    const titleText = `模型: ${data.model}\n------------------------\n【输入 Tokens】\n✅ 命中: ${data.totalHit.toLocaleString()} \n❌ 未命中: ${data.totalMiss.toLocaleString()}\n【输出 Tokens】\n📤 模型生成: ${data.totalResponse.toLocaleString()}`;

                    if (!existingBadge || !document.body.contains(existingBadge)) {
                        existingBadge = document.createElement('span');
                        existingBadge.className = 'ds-injected-hit-rate ds-text ds-text--label2 ds-text--fsp ds-text--lsp';
                        existingBadge.style.cssText = 'color:#10B981; background-color:rgba(16, 185, 129, 0.15); padding:2px 8px; border-radius:12px; margin-left:12px; cursor:help;';
                        container.appendChild(existingBadge);
                    }
                    if (existingBadge.dataset.rate !== data.hitRate) {
                        existingBadge.dataset.rate = data.hitRate;
                        existingBadge.textContent = `命中率: ${data.hitRate}%`;
                        existingBadge.title = titleText;
                    }
                }
            }
        }
    }, 500);

    // 悬浮窗指纹核对算法
    function initTooltipEnhancer() {
        setInterval(() => {
            const divs = document.getElementsByTagName('div');
            let tooltipDiv = null;

            for (let i = divs.length - 1; i >= 0; i--) {
                const div = divs[i];
                const text = div.textContent;

                // 【核心兼容】:同时支持检测英文的 "Input (Cache hit)" 和 中文的 "命中缓存"
                if (!text || (!text.includes('Input (Cache hit)') && !text.includes('命中缓存'))) continue;

                if (text.length > 300) continue;
                if (div.style.position === 'absolute' || div.style.position === 'fixed' || div.style.zIndex) {
                    tooltipDiv = div;
                    break;
                }
            }

            if (tooltipDiv) {
                const text = tooltipDiv.textContent;

                // 把弹窗里看到的所有数字全抓出来,放进“指纹袋”
                const rawNumbers = (text.match(/[\d,]+/g) || []).map(n => parseInt(n.replace(/,/g, ''), 10));
                const numberBag = new Set(rawNumbers);

                const dateMatch = text.match(/\d{4}-\d{2}-\d{2}/);
                const hoverDate = dateMatch ? dateMatch[0] : null;

                let matchedHit = -1;
                let matchedMiss = -1;

                const days = window.__ds_usage_days || [];
                for (const day of days) {
                    if (hoverDate && day.date !== hoverDate) continue;

                    for (const modelData of day.data) {
                        let hit = 0, miss = 0;
                        if (Array.isArray(modelData.usage)) {
                            for (const u of modelData.usage) {
                                if (u.type === 'PROMPT_CACHE_HIT_TOKEN') hit = parseInt(u.amount);
                                if (u.type === 'PROMPT_CACHE_MISS_TOKEN') miss = parseInt(u.amount);
                            }
                        }

                        if (numberBag.has(hit) && numberBag.has(miss)) {
                            matchedHit = hit;
                            matchedMiss = miss;
                            if (hit > 0 || miss > 0) break;
                        }
                    }
                    if (matchedHit > 0 || matchedMiss > 0) break;
                }

                if (matchedHit !== -1) {
                    const total = matchedHit + matchedMiss;
                    const hitRate = total > 0 ? ((matchedHit / total) * 100).toFixed(2) : '0.00';

                    let ourDiv = tooltipDiv.querySelector('.ds-tooltip-hit-rate');
                    if (!ourDiv || !tooltipDiv.contains(ourDiv)) {
                        ourDiv = document.createElement('div');
                        ourDiv.className = 'ds-tooltip-hit-rate';
                        ourDiv.style.cssText = 'margin-top: 6px; padding-top: 6px; border-top: 1px dashed rgba(255, 255, 255, 0.3); display: flex; align-items: center; justify-content: space-between; font-size: 14px; pointer-events: none; width: 100%;';

                        const leftSpan = document.createElement('span');
                        leftSpan.style.color = '#999';
                        leftSpan.textContent = '单日命中率';

                        const rightSpan = document.createElement('span');
                        rightSpan.className = 'ds-tooltip-rate-value';
                        rightSpan.style.cssText = 'color: #10B981; font-weight: bold; margin-left: auto; padding-left: 20px;';

                        ourDiv.appendChild(leftSpan);
                        ourDiv.appendChild(rightSpan);
                        tooltipDiv.appendChild(ourDiv);
                    }

                    const rightSpan = ourDiv.querySelector('.ds-tooltip-rate-value');
                    if (rightSpan.textContent !== `${hitRate}%`) {
                        rightSpan.textContent = `${hitRate}%`;
                    }
                }
            }
        }, 100);
    }
    initTooltipEnhancer();

})();

你好啊,陌生人!

我的朋友,看起来你是新来的,如果想参与到讨论中,点击下面的按钮!

📈用户数目📈

目前论坛共有59941位seeker

🎉欢迎新用户🎉