• SeaMod

    由这个 https://www.nodeseek.com/post-908122-1 帖子的代码扔给claude魔改为油猴插件

    该插件只能检测是否降低模型,降低思考强度是无法检测的

    直接复制粘贴以下代码导入到油猴插件里:```
    // ==UserScript==
    // @name ChatGPT Route Checker v4
    // @name:zh-CN ChatGPT 模型路由检测器 v4
    // @namespace local.chatgpt.route-checker
    // @version 4.1.0
    // @description Display the requested model and model-routing metadata exposed by ChatGPT Web.
    // @description:zh-CN 显示 ChatGPT 网页请求的模型及服务端暴露的模型路由标注。
    // @match https://chatgpt.com/*
    // @match https://chat.openai.com/*
    // @run-at document-start
    // @sandbox raw
    // @grant none
    // @noframes
    // ==/UserScript==
    (() => {
    "use strict";
    if (window.__CHATGPT_ROUTE_CHECKER_V4__) {
    console.log("Route Checker v4 已在运行");
    return;
    }
    window.__CHATGPT_ROUTE_CHECKER_V4__ = true;

    const nativeFetch = window.fetch.bind(window);
    const nativeBeacon = navigator.sendBeacon
    ? navigator.sendBeacon.bind(navigator)
    : null;

    const state = {
    active: false,
    finished: false,
    requestModel: null,
    thinkingEffort: null,
    serverModel: null,
    assistantModel: null,
    resolvedModel: null,
    requestedExperience: null,
    domModel: null
    };

    // 供 scanObject 使用:本次扫描内“字段是否真的变化 / 是否命中 server_ste_metadata”
    let dirty = false;
    let sawServerMeta = false;
    function setField(key, value) {
    if (state[key] !== value) {
    state[key] = value;
    dirty = true;
    }
    }

    let finishTimer = null;

    // ---------------- UI ----------------
    const panel = document.createElement("div");
    Object.assign(panel.style, {
    position: "fixed",
    right: "16px",
    bottom: "16px",
    zIndex: "2147483647",
    width: "410px",
    maxWidth: "calc(100vw - 32px)",
    padding: "14px 16px",
    border: "1px solid #444",
    borderRadius: "12px",
    background: "#111",
    color: "#eee",
    font: "13px/1.7 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace",
    boxShadow: "0 8px 30px rgba(0,0,0,.4)",
    // 整个显示框缩小为原来的 70%
    transform: "scale(0.7)",
    transformOrigin: "right bottom"
    });

    function mountPanel() {
    if (!panel.isConnected && document.documentElement) {
    document.documentElement.appendChild(panel);
    }
    }
    mountPanel();

    const esc = value =>
    String(value).replace(/[&<>"']/g, c => ({
    "&": "&amp;",
    "<": "&lt;",
    ">": "&gt;",
    '"': "&quot;",
    "'": "&#39;"
    })[c]);

    function display(value) {
    return value ? esc(value) : state.finished ? "本次未返回" : "搜索中…";
    }

    function modelFamily(slug) {
    const m = String(slug || "").match(/^gpt-(\d+)-(\d+)/i);
    return m ? gpt-${m[1]}-${m[2]} : null;
    }

    function verdict() {
    if (!state.active) return "⏳ 等待发送一条新消息";
    if (!state.serverModel) return "🔎 检测中,等待服务端模型标注…";
    if (!state.requestModel) return "✅ 已取得服务端模型标注";
    if (state.requestModel === state.serverModel) {
    return "✅ 请求模型与服务端模型标注一致";
    }
    const reqFamily = modelFamily(state.requestModel);
    const srvFamily = modelFamily(state.serverModel);
    if (reqFamily && reqFamily === srvFamily) {
    return "✅ 同一模型系列(内部 slug 不同)";
    }
    return "🚨 请求模型与服务端模型标注不一致";
    }

    function refreshDomModel() {
    const els = document.querySelectorAll('[data-message-author-role="assistant"]');
    if (!els.length) return;
    const value = els[els.length - 1].getAttribute("data-message-model-slug");
    if (value) state.domModel = value;
    }

    function render() {
    mountPanel();
    refreshDomModel();
    panel.innerHTML = `
    <div style="font-weight:700;font-size:14px;margin-bottom:8px">
    ChatGPT Route Checker v4
    </div>
    <div style="margin-bottom:10px">${verdict()}</div>
    <div style="border-top:1px solid #333;border-bottom:1px solid #333;padding:10px 0;margin-bottom:10px">
    <div style="color:#aaa">实际回复模型标注</div>
    <div style="font-size:18px;font-weight:700;margin-top:2px">
    ${display(state.serverModel)}
    </div>
    <div style="color:#777;font-size:11px;margin-top:2px">
    server_ste_metadata.model_slug
    </div>
    </div>
    <div>
    请求的模型:<b>${display(state.requestModel)}</b><br>
    请求的思考强度:<b>${display(state.thinkingEffort)}</b><br><br>
    server_ste_metadata.model_slug:<b>${display(state.serverModel)}</b><br>
    assistant metadata.model_slug:<b>${display(state.assistantModel)}</b><br>
    resolved_model_slug:<b>${display(state.resolvedModel)}</b><br>
    requested_model_experience:<b>${display(state.requestedExperience)}</b><br>
    DOM data-message-model-slug:<b>${display(state.domModel)}</b>
    </div>
    <div style="border-top:1px solid #333;margin-top:10px;padding-top:7px;color:#777;font-size:11px">
    主判断:server_ste_metadata.model_slug<br>
    其余字段仅用于交叉验证
    </div>
    `;
    }

    // ---------------- 单轮状态管理 ----------------
    function beginTurn(req) {
    clearTimeout(finishTimer);
    state.active = true;
    state.finished = false;
    state.requestModel = typeof req?.model === "string" ? req.model : null;
    state.thinkingEffort =
    typeof req?.thinking_effort === "string" ? req.thinking_effort : null;
    state.serverModel = null;
    state.assistantModel = null;
    state.resolvedModel = null;
    state.requestedExperience = null;
    state.domModel = null;
    render();
    }

    function scheduleFinish(delay = 2000) {
    clearTimeout(finishTimer);
    finishTimer = setTimeout(() => {
    state.finished = true;
    refreshDomModel();
    render();
    }, delay);
    }

    // ---------------- 扫描 JSON ----------------
    function scanObject(obj, depth = 0, seen = new WeakSet()) {
    if (obj == null || depth > 15) return;

    if (typeof obj === "string") {
    const s = obj.trim();
    if (
    (s.startsWith("{") || s.startsWith("[")) &&
    (s.includes("model_slug") ||
    s.includes("server_ste_metadata") ||
    s.includes("resolved_model_slug"))
    ) {
    try {
    scanObject(JSON.parse(s), depth + 1, seen);
    } catch {}
    }
    return;
    }

    if (typeof obj !== "object" || seen.has(obj)) return;
    seen.add(obj);

    // server_ste_metadata 可能出现在三种位置:{type, metadata}、直属字段、turn_analytics 内
    const serverMeta =
    (obj.type === "server_ste_metadata" && obj.metadata) ||
    obj.server_ste_metadata ||
    obj.turn_analytics?.server_ste_metadata;
    if (serverMeta && typeof serverMeta === "object") {
    if (typeof serverMeta.model_slug === "string") {
    setField("serverModel", serverMeta.model_slug);
    }
    if (typeof serverMeta.requested_model_experience === "string") {
    setField("requestedExperience", serverMeta.requested_model_experience);
    }
    sawServerMeta = true;
    }

    // assistant 消息的 metadata.model_slug:可能是本对象,也可能包在 .message 里
    const assistantNode =
    obj.author?.role === "assistant"
    ? obj
    : obj.message?.author?.role === "assistant"
    ? obj.message
    : null;
    if (typeof assistantNode?.metadata?.model_slug === "string") {
    setField("assistantModel", assistantNode.metadata.model_slug);
    }

    if (typeof obj.resolved_model_slug === "string") {
    setField("resolvedModel", obj.resolved_model_slug);
    }

    for (const value of Object.values(obj)) {
    scanObject(value, depth + 1, seen);
    }
    }

    function parseData(raw) {
    if (!raw) return;
    const text = raw.trim();
    if (!text || text === "[DONE]") return;
    let obj;
    try {
    obj = JSON.parse(text);
    } catch {
    return;
    }
    dirty = false;
    sawServerMeta = false;
    scanObject(obj);
    if (dirty) render();
    if (sawServerMeta) scheduleFinish(1500);
    }

    function scanWholeText(text) {
    if (!text) return;
    parseData(text); // 整体可能就是一个 JSON
    for (let line of text.split(/\r?\n/)) {
    line = line.trim();
    if (!line) continue;
    if (line.startsWith("data:")) line = line.slice(5).trim();
    parseData(line);
    }
    }

    // ---------------- 实时 SSE 读取 ----------------
    async function watchStream(response) {
    if (!response.body) {
    try {
    scanWholeText(await response.text());
    } catch {}
    scheduleFinish();
    return;
    }

    const reader = response.body.getReader();
    const decoder = new TextDecoder();
    let buffer = "";
    let eventLines = [];
    const flushEvent = () => {
    if (eventLines.length) {
    parseData(eventLines.join("\n"));
    eventLines = [];
    }
    };

    try {
    while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    buffer += decoder.decode(value, { stream: true });

    let nl;
    while ((nl = buffer.indexOf("\n")) !== -1) {
    let line = buffer.slice(0, nl);
    buffer = buffer.slice(nl + 1);
    if (line.endsWith("\r")) line = line.slice(0, -1);

    if (line === "") {
    flushEvent();
    } else if (line.startsWith("data:")) {
    eventLines.push(line.slice(5).trimStart());
    } else {
    const t = line.trim();
    if (t.startsWith("{") || t.startsWith("[")) parseData(t);
    }
    }
    }
    const rest = buffer.trim();
    if (rest.startsWith("data:")) {
    eventLines.push(rest.slice(5).trimStart());
    } else if (rest) {
    parseData(rest);
    }
    flushEvent();
    } catch (err) {
    console.debug("[Route Checker] stream reader:", err);
    }
    scheduleFinish(3000);
    }

    // ---------------- Request body ----------------
    async function bodyText(body) {
    try {
    if (body == null) return "";
    if (typeof body === "string") return body;
    if (body instanceof Blob) return await body.text();
    if (body instanceof URLSearchParams) return body.toString();
    if (body instanceof ArrayBuffer) return new TextDecoder().decode(body);
    if (ArrayBuffer.isView(body)) return new TextDecoder().decode(body);
    } catch {}
    return "";
    }

    async function getRequestText(input, init) {
    if (init?.body != null) return await bodyText(init.body);
    if (input instanceof Request) {
    try {
    return await input.clone().text();
    } catch {}
    }
    return "";
    }

    const isConversation = (url, method) =>
    method === "POST" && /\/backend-api\/f\/conversation(?:\?|$)/.test(url);
    const isTelemetry = (url, method) =>
    method === "POST" && /\/ces\/v1\/telemetry\/intake(?:\?|$)/.test(url);

    // ---------------- fetch hook ----------------
    window.fetch = async function (input, init = {}) {
    const url = typeof input === "string" ? input : input?.url || "";
    const method = String(init.method || input?.method || "GET").toUpperCase();

    if (isConversation(url, method)) {
    const reqText = await getRequestText(input, init);
    try {
    beginTurn(JSON.parse(reqText));
    } catch {
    beginTurn({});
    }
    } else if (isTelemetry(url, method)) {
    scanWholeText(await getRequestText(input, init));
    }

    const response = await nativeFetch(input, init);

    if (isConversation(url, method)) {
    try {
    void watchStream(response.clone());
    } catch {}
    }
    return response;
    };

    // ---------------- sendBeacon telemetry ----------------
    if (nativeBeacon) {
    try {
    navigator.sendBeacon = function (url, data) {
    if (/\/ces\/v1\/telemetry\/intake(?:\?|$)/.test(String(url || ""))) {
    void bodyText(data).then(scanWholeText);
    }
    return nativeBeacon(url, data);
    };
    } catch {}
    }

    // ---------------- DOM observer(用 rAF 合并高频 DOM 变动) ----------------
    let domCheckQueued = false;
    const observer = new MutationObserver(() => {
    if (domCheckQueued) return;
    domCheckQueued = true;
    requestAnimationFrame(() => {
    domCheckQueued = false;
    mountPanel();
    const before = state.domModel;
    refreshDomModel();
    if (state.domModel !== before) render();
    });
    });
    observer.observe(document, {
    subtree: true,
    childList: true,
    attributes: true,
    attributeFilter: ["data-message-model-slug"]
    });

    render();
    console.log("ChatGPT Route Checker v4 已启动,发送一条新消息即可。");
    })();

    主楼
  • theliser

    这排班...

    你就不能放到markdown代码块里面去吗

    #1
  • barry66688

    大佬辛苦了

    #2
  • Opus94

    ^-^ ^-^ ^-^

    #3
  • niuniu

    好贴

    #4
  • SeaMod

    #5
  • morningstar

    mark

    #6
  • zyy3096

    mark

    #7
  • 让我们一起荡起双桨

    666

    #8
  • 让我们一起荡起双桨

    GPT-5.6 Thinking是GPT-5.6 Sol吗?

    图片

    图片

    #9
  • corenuclear

    感谢分享,收藏了

    #10
  • skyzc

    666

    #11

发表回复

登录后回复