| <!doctype html> |
| <html lang="zh-CN"> |
| <head> |
| <meta charset="utf-8" /> |
| <meta name="viewport" content="width=device-width, initial-scale=1" /> |
| <link rel="icon" type="image/x-icon" href="/web/favicon.ico"> |
| <link rel="apple-touch-icon" href="/web/apple-touch-icon.png"> |
| <link rel="manifest" href="/web/manifest.json"> |
| <meta name="theme-color" content="#3b82f6"> |
| <title data-i18n="modelTest.title">模型测试 - Claude Code & Codex Proxy</title> |
| <link rel="stylesheet" href="/web/assets/css/styles.css?v=__VERSION__"> |
| <link rel="stylesheet" href="/web/assets/css/channels.css?v=__VERSION__"> |
| <script defer src="/web/assets/locales/zh-CN.js?v=__VERSION__"></script> |
| <script defer src="/web/assets/locales/en.js?v=__VERSION__"></script> |
| <script defer src="/web/assets/js/i18n.js?v=__VERSION__"></script> |
| <script defer src="/web/assets/js/template-engine.js?v=__VERSION__"></script> |
| <script defer src="/web/assets/js/ui.js?v=__VERSION__"></script> |
| <script defer src="/web/assets/js/model-test.js?v=__VERSION__"></script> |
| <script defer src="/web/assets/js/logs-channel-editor.js?v=__VERSION__"></script> |
| </head> |
| <body> |
| <div class="app-container"> |
| <main class="main-content"> |
| <div class="content-area"> |
| <section class="glass-card mt-2 mb-2 overflow-visible"> |
| |
| <div class="model-test-tabs"> |
| <button id="modeTabChannel" type="button" class="mode-tab-btn active" data-action="set-test-mode" data-mode="channel" data-i18n="modelTest.mode.channel">按渠道测试</button> |
| <button id="modeTabModel" type="button" class="mode-tab-btn" data-action="set-test-mode" data-mode="model" data-i18n="modelTest.mode.model">按模型测试</button> |
| </div> |
|
|
| |
| <div class="model-test-toolbar"> |
| <div class="model-test-toolbar-section model-test-toolbar-section--filters"> |
| <label id="channelSelectorLabel" class="model-test-control"> |
| <span class="model-test-control__label" data-i18n="modelTest.channel">渠道</span> |
| <div id="testChannelSelectContainer"></div> |
| </label> |
| <label id="modelTypeLabel" class="model-test-control model-test-control--type hidden"> |
| <span class="model-test-control__label" data-i18n="common.type">类型</span> |
| <select id="testModelType" class="filter-select model-test-inline-select" aria-label="类型"></select> |
| </label> |
| <label id="modelSelectorLabel" class="model-test-control model-test-control--model hidden"> |
| <span class="model-test-control__label" data-i18n="common.model">模型</span> |
| <div class="filter-combobox-wrapper model-test-model-combobox"> |
| <input |
| id="testModelSelect" |
| class="filter-select filter-combobox" |
| type="text" |
| autocomplete="off" |
| spellcheck="false" |
| data-i18n-placeholder="modelTest.modelInputPlaceholder" |
| placeholder="输入或选择模型" |
| > |
| <div id="testModelSelectDropdown" class="filter-dropdown" role="listbox" aria-label="模型"></div> |
| </div> |
| </label> |
| <div id="protocolTransformContainer" class="model-test-control model-test-control--protocol"> |
| <span class="model-test-control__label" data-i18n="modelTest.protocolTransform">协议转换</span> |
| <div id="protocolTransformOptions" class="channel-editor-radio-group" role="radiogroup" aria-label="协议转换"></div> |
| </div> |
| <div class="model-test-toolbar-toggles"> |
| <label class="model-test-toggle"> |
| <input type="checkbox" id="streamEnabled" class="control-checkbox control-checkbox--sm"> |
| <span data-i18n="modelTest.stream">流式</span> |
| </label> |
| <label class="model-test-toggle"> |
| <span data-i18n="modelTest.concurrency">并发</span> |
| <input type="number" id="concurrency" value="5" min="1" max="20" class="model-test-concurrency-input"> |
| </label> |
| </div> |
| <label class="model-test-control model-test-control--content"> |
| <span class="model-test-control__label" data-i18n="modelTest.content">内容</span> |
| <input type="text" id="modelTestContent" value="" class="model-test-inline-input" data-i18n-placeholder="common.loading" placeholder="加载中..."> |
| </label> |
| </div> |
| <div class="model-test-toolbar-section model-test-toolbar-section--meta"> |
| <label class="model-test-control model-test-control--name-filter"> |
| <span class="model-test-control__label" data-i18n="common.search">搜索</span> |
| <input type="text" id="modelTestMobileNameFilter" value="" class="model-test-inline-input" autocomplete="off" spellcheck="false" placeholder="搜索模型名称..."> |
| </label> |
| <span id="testProgress" class="model-test-progress"></span> |
| </div> |
| </div> |
|
|
| |
| <div class="table-container model-test-table-container mobile-card-table-container"> |
| <table class="modern-table model-test-table mobile-card-table"> |
| <thead class="table-head-sticky"> |
| <tr id="model-test-head-row"> |
| <th class="table-col-select mobile-card-select-header"><input type="checkbox" id="selectAllCheckbox" data-change-action="toggle-all-models"></th> |
| <th class="table-col-name" data-i18n="common.model" data-sort-key="name">模型</th> |
| <th class="first-byte-col table-col-duration" data-i18n="modelTest.firstByteDuration" data-sort-key="firstByteDuration">首字</th> |
| <th class="table-col-duration" data-i18n="modelTest.totalDuration" data-sort-key="duration">总耗时</th> |
| <th class="table-col-metric" data-i18n="common.input" data-sort-key="inputTokens">输入</th> |
| <th class="table-col-metric" data-i18n="common.output" data-sort-key="outputTokens">输出</th> |
| <th class="table-col-speed" data-i18n="modelTest.speed" data-sort-key="speed">速度(tok/s)</th> |
| <th class="table-col-metric" data-i18n="modelTest.cacheRead" data-sort-key="cacheRead">缓读</th> |
| <th class="table-col-metric" data-i18n="modelTest.cacheCreate" data-sort-key="cacheCreate">缓建</th> |
| <th class="table-col-cost" data-i18n="common.cost" data-sort-key="cost">费用</th> |
| <th class="table-col-response model-test-response-head" data-sort-key="response"> |
| <div class="model-test-response-head-inner"> |
| <div class="model-test-response-head-line"> |
| <span class="model-test-response-head-label" data-i18n="modelTest.responseContent">响应内容</span> |
| </div> |
| <div class="model-test-toolbar-section model-test-toolbar-section--actions model-test-head-actions"> |
| <button id="fetchModelsBtn" type="button" data-action="fetch-and-add-models" class="btn btn-secondary model-test-toolbar-btn" data-i18n="modelTest.fetchModels">获取模型</button> |
| <button id="addModelsBtn" type="button" data-action="open-add-models-modal" class="btn btn-secondary model-test-toolbar-btn hidden" data-i18n="modelTest.addModels">添加模型</button> |
| <button id="deleteModelsBtn" type="button" data-action="delete-selected-models" class="btn btn-secondary model-test-toolbar-btn model-test-toolbar-btn--danger" data-i18n="modelTest.deleteModels">删除模型</button> |
| <button id="runTestBtn" type="button" data-action="run-model-tests" class="btn btn-primary model-test-toolbar-btn" data-i18n="modelTest.startTest">开始测试</button> |
| </div> |
| </div> |
| </th> |
| </tr> |
| </thead> |
| <tbody id="model-test-tbody"> |
| <tr class="model-test-empty-row"><td colspan="11" data-i18n="modelTest.selectChannelFirst">请先选择渠道</td></tr> |
| </tbody> |
| </table> |
| </div> |
| </section> |
| </div> |
| </main> |
| </div> |
| |
| <template id="tpl-model-row"> |
| <tr class="mobile-card-row model-test-row" data-model="{{model}}" data-channel-id="{{channelId}}" data-cost-multiplier="{{costMultiplier}}"> |
| <td class="model-test-col-select mobile-card-no-label" data-mobile-label="{{mobileLabelSelect}}"><input type="checkbox" class="row-checkbox model-checkbox" checked></td> |
| <td class="model-test-col-name truncate-cell" title="{{model}}" data-mobile-label="{{mobileLabelName}}"> |
| <button type="button" class="channel-link" data-channel-id="{{channelId}}" title="{{model}}">{{displayName}}</button> |
| </td> |
| <td class="model-test-col-first-byte first-byte-duration" data-mobile-label="{{mobileLabelFirstByte}}">-</td> |
| <td class="model-test-col-duration duration" data-mobile-label="{{mobileLabelDuration}}">-</td> |
| <td class="model-test-col-input input-tokens" data-mobile-label="{{mobileLabelInput}}">-</td> |
| <td class="model-test-col-output output-tokens" data-mobile-label="{{mobileLabelOutput}}">-</td> |
| <td class="model-test-col-speed speed" data-mobile-label="{{mobileLabelSpeed}}">-</td> |
| <td class="model-test-col-cache-read cache-read" data-mobile-label="{{mobileLabelCacheRead}}">-</td> |
| <td class="model-test-col-cache-create cache-create" data-mobile-label="{{mobileLabelCacheCreate}}">-</td> |
| <td class="model-test-col-cost cost" data-mobile-label="{{mobileLabelCost}}">-</td> |
| <td class="model-test-col-response response" title="" data-mobile-label="{{mobileLabelResponse}}">-</td> |
| </tr> |
| </template> |
|
|
| |
| <template id="tpl-channel-row-by-model"> |
| <tr class="mobile-card-row model-test-row" data-channel-id="{{channelId}}" data-model="{{model}}" data-cost-multiplier="{{costMultiplier}}"> |
| <td class="model-test-col-select mobile-card-no-label" data-mobile-label="{{mobileLabelSelect}}"><input type="checkbox" class="row-checkbox channel-checkbox" checked></td> |
| <td class="model-test-col-name truncate-cell" data-mobile-label="{{mobileLabelName}}"> |
| <button type="button" class="channel-link" data-channel-id="{{channelId}}" title="{{channelName}}">{{channelName}}</button> |
| </td> |
| <td class="model-test-col-priority channel-priority" data-mobile-label="{{mobileLabelPriority}}">{{channelPriority}}</td> |
| <td class="model-test-col-first-byte first-byte-duration" data-mobile-label="{{mobileLabelFirstByte}}">-</td> |
| <td class="model-test-col-duration duration" data-mobile-label="{{mobileLabelDuration}}">-</td> |
| <td class="model-test-col-input input-tokens" data-mobile-label="{{mobileLabelInput}}">-</td> |
| <td class="model-test-col-output output-tokens" data-mobile-label="{{mobileLabelOutput}}">-</td> |
| <td class="model-test-col-speed speed" data-mobile-label="{{mobileLabelSpeed}}">-</td> |
| <td class="model-test-col-cache-read cache-read" data-mobile-label="{{mobileLabelCacheRead}}">-</td> |
| <td class="model-test-col-cache-create cache-create" data-mobile-label="{{mobileLabelCacheCreate}}">-</td> |
| <td class="model-test-col-cost cost" data-mobile-label="{{mobileLabelCost}}">-</td> |
| <td class="model-test-col-response response" title="" data-mobile-label="{{mobileLabelResponse}}">-</td> |
| </tr> |
| </template> |
|
|
| |
| <template id="tpl-empty-row"> |
| <tr class="model-test-empty-row"><td colspan="{{colspan}}">{{message}}</td></tr> |
| </template> |
|
|
| |
| <div id="upstreamDetailModal" class="modal"> |
| <div class="modal-content upstream-detail-modal-content"> |
| <div class="modal-header"> |
| <h2 class="modal-title" data-i18n="channels.test.upstreamDetail">上游请求/响应详情</h2> |
| <button type="button" class="close-btn" onclick="closeUpstreamDetailModal()">×</button> |
| </div> |
| <div class="upstream-detail-tabs"> |
| <button type="button" class="upstream-tab active" data-tab="request" data-i18n="channels.test.tabRequest">Request</button> |
| <button type="button" class="upstream-tab" data-tab="response" data-i18n="channels.test.tabResponse">Response</button> |
| <button type="button" class="upstream-copy-btn upstream-copy-btn--tabs" data-copy-target="upstreamReqRaw" data-i18n="common.copy">复制</button> |
| </div> |
| <div id="upstreamTabRequest" class="upstream-tab-panel active"> |
| <div class="upstream-field upstream-field--full"> |
| <pre id="upstreamReqRaw" class="upstream-pre upstream-pre--full"></pre> |
| </div> |
| </div> |
| <div id="upstreamTabResponse" class="upstream-tab-panel"> |
| <div class="upstream-field upstream-field--full"> |
| <pre id="upstreamRespRaw" class="upstream-pre upstream-pre--full"></pre> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="addModelsModal" class="modal"> |
| <div class="modal-content modal-content--lg"> |
| <div class="modal-header modal-header--compact"> |
| <h2 class="modal-title" data-i18n="modelTest.addModelsTitle">批量添加模型</h2> |
| <button id="addModelsCloseBtn" class="close-btn" aria-label="Close">×</button> |
| </div> |
| <label class="model-test-add-field"> |
| <span class="model-test-add-label" data-i18n="modelTest.addModelsInputLabel">输入模型名称(支持逗号或换行分隔)</span> |
| <textarea |
| id="addModelsTextarea" |
| class="model-test-batch-textarea" |
| spellcheck="false" |
| data-i18n-placeholder="modelTest.addModelsPlaceholder" |
| placeholder="gpt-4o,gpt-4o-mini claude-3-5-sonnet-20241022 claude-3-5-haiku-latest" |
| ></textarea> |
| </label> |
| <div class="model-test-add-help"> |
| <div class="model-test-add-help-title"> |
| <span class="model-test-add-help-icon" aria-hidden="true">!</span> |
| <strong data-i18n="modelTest.addModelsHelpTitle">使用说明:</strong> |
| </div> |
| <ul> |
| <li><span data-i18n="modelTest.addModelsHelpComma">支持逗号分隔:</span> <code>model1,model2,model3</code></li> |
| <li data-i18n="modelTest.addModelsHelpLine">支持换行分隔:每行一个模型</li> |
| <li data-i18n="modelTest.addModelsHelpDedupe">自动去除空格、空行和重复模型</li> |
| </ul> |
| </div> |
| <div class="confirm-actions confirm-actions--end"> |
| <button id="addModelsCancelBtn" class="btn btn-secondary" data-i18n="common.cancel">取消</button> |
| <button id="addModelsConfirmBtn" class="btn btn-primary" data-i18n="modelTest.addModelsConfirm">确认添加</button> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="deletePreviewModal" class="modal"> |
| <div class="modal-content modal-content--lg"> |
| <div class="modal-header modal-header--compact"> |
| <h2 class="modal-title" data-i18n="modelTest.deletePreviewTitle">确认删除模型</h2> |
| <button id="deletePreviewCloseBtn" class="close-btn" aria-label="Close">×</button> |
| </div> |
| <p class="modal-description" data-i18n="modelTest.deletePreviewDesc">将按以下分组删除,请确认:</p> |
| <pre id="deletePreviewContent" class="delete-preview-text">-</pre> |
| <p id="deletePreviewProgress" class="model-test-delete-preview-progress hidden">-</p> |
| <pre id="deletePreviewRuntimeLog" class="delete-preview-text model-test-delete-preview-log hidden">-</pre> |
| <div class="confirm-actions confirm-actions--end"> |
| <button id="deletePreviewCancelBtn" class="btn btn-secondary" data-i18n="common.cancel">取消</button> |
| <button id="deletePreviewConfirmBtn" class="btn btn-danger" data-i18n="modelTest.deletePreviewConfirm">确认删除</button> |
| </div> |
| </div> |
| </div> |
|
|
| </body> |
| </html> |
|
|