You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -117,6 +119,18 @@ When `CODEX_AUDIT_SERVICE_CODEX_ACCOUNT_USAGE=1`, the dashboard reads a
117
119
sanitized Codex rate-limit snapshot through the local authenticated Codex
118
120
app-server; API-key rows remain internal estimates unless a platform usage
119
121
provider is configured separately.
122
+
Set `OPENAI_ADMIN_KEY` to an OpenAI Admin API key to add a sanitized
123
+
GPT/OpenAI organization Usage/Costs snapshot to `/v1/ai/quota`. Optional
124
+
`CODEX_AUDIT_SERVICE_OPENAI_ADMIN_API_KEY_IDS` can limit that snapshot to
125
+
specific API key IDs; never store raw API keys in that filter. When API key
126
+
IDs are configured, both usage and costs are requested with that filter.
127
+
Set `ANTHROPIC_ADMIN_KEY` to an Anthropic Admin API key to add a sanitized
128
+
Claude organization Usage/Cost snapshot. Do not reuse the normal
129
+
`ANTHROPIC_API_KEY`; Anthropic usage/cost reporting requires an Admin API key.
130
+
Optional `CODEX_AUDIT_SERVICE_ANTHROPIC_ADMIN_API_KEY_IDS` and
131
+
`CODEX_AUDIT_SERVICE_ANTHROPIC_ADMIN_WORKSPACE_IDS` filter usage by IDs; costs
132
+
are omitted when those filters are set to avoid mixing filtered usage with an
133
+
unfiltered cost total.
120
134
121
135
If no custom domain is available, `cloudflare/codex-audit-proxy/` contains a minimal Cloudflare Worker that can publish a free `workers.dev` HTTPS entry point while keeping the VPS origin URL in a Cloudflare secret. The production service path is async: submit `POST /v1/codex-audit/jobs`, then poll `GET /v1/codex-audit/jobs/{job_id}`. See `docs/async_service_deployment.md` for the deployment and open-source repository checklist.
Copy file name to clipboardExpand all lines: cloudflare/ai-gateway-dash/src/index.mjs
+2-1Lines changed: 2 additions & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -172,10 +172,11 @@ function healthReasonText(r){const path=r.path||"endpoint";if(r.reason==="error_
172
172
function renderHealth(h){setStatus(h.status||"unknown");setUpdated("health-updated");const target=document.getElementById("health"),endpoints=h.endpoints||[],reasons=h.degradation_reasons||[],total=endpoints.reduce((s,i)=>s+(Number(i.total)||0),0),errors=endpoints.reduce((s,i)=>s+(Number(i.errors)||0),0),p95=Math.max(0,...endpoints.map(i=>Number(i.p95_ms)||0)),details=el("div","");append(details,statRow("接口数量",endpoints.length,"info"),statRow("总请求",fmtNum(total),""),statRow("错误",fmtNum(errors),errors?"err":"ok"),statRow("最高 P95",fmtNum(p95)+" ms",p95>30000?"err":p95>10000?"warn":"ok"));if(reasons.length){reasons.slice(0,3).forEach((reason,i)=>append(details,statRow(i?"降级原因 "+(i+1):"降级原因",healthReasonText(reason),clsStatus(reason.severity||h.status))));if(reasons.length>3)append(details,statRow("更多原因","+"+fmtNum(reasons.length-3),"warn"))}else if(h.last_error&&h.last_error.message){append(details,statRow("最近错误",String(h.last_error.message).slice(0,80),"warn"))}clear(target);append(target,append(el("div","metric-line"),el("div","big ",statusText(h.status||"unknown")),el("span","delta "+clsStatus(h.status||"unknown"),fmtNum(Math.round((h.uptime_seconds||0)/3600))+" h")),details)}
173
173
function quotaTotalRow(label,d){const row=el("div","stat-row"),used=Number(d&&d.total_cost_usd)||0;append(row,el("span","stat-label",label),el("span","stat-value info",fmtUSD(used)));return row}
function quotaHasUsage(d){return Boolean(Number(d&&d.total_cost_usd)||Number(d&&d.calls)||Number(d&&d.tokens_input)||Number(d&&d.tokens_output)||(d&&d.calls_incomplete))}
function appendRepoQuotaRows(list,repos){for(const name of Object.keys(repos)){const d=repos[name]||{},used=Number(d.total_cost_usd)||0,budget=Number(d.daily_budget)||0,p=budget?used/budget*100:0,c=progressClass(p),item=el("div","quota-item"),meta=el("div",""),bar=el("div","bar"),fill=el("div","bar-fill "+c);fill.style.width=Math.max(0,Math.min(p,100))+"%";append(meta,el("div","quota-name",String(name).split("/").pop()||name),el("div","quota-meta",fmtUSD(used)+" / "+fmtUSD(budget)+" repo 日预算"));append(bar,fill);append(item,meta,bar,el("div","bar-label",Math.round(p)+"%"));list.appendChild(item)}}
function renderQuota(q){setUpdated("quota-updated");const target=document.getElementById("quota"),repos=q.repos||{},repoCount=Object.keys(repos).length,summary=q.summary||null,account=summary&&summary.codex_account&&summary.codex_account.status==="available"?summary.codex_account:null,openai=summary&&summary.openai_account&&summary.openai_account.status==="available"?summary.openai_account:null,anthropic=summary&&summary.anthropic_account&&summary.anthropic_account.status==="available"?summary.anthropic_account:null,hasSummaryUsage=summary&&(account||openai||anthropic||quotaHasUsage(summary.combined)||quotaHasUsage(summary.api_key)||quotaHasUsage(summary.codex));clear(target);if(summary&&(repoCount||hasSummaryUsage)){const list=el("div","quota-list"),details=el("div","");appendRepoQuotaRows(list,repos);if(openai)append(details,providerAccountRow("GPT API 账户",openai,"completions"));if(anthropic)append(details,providerAccountRow("Claude API 账户",anthropic,"messages"));if(account)append(details,codexAccountRow(account));append(details,quotaTotalRow("合计估算",summary.combined||{}),quotaUsageRow("API Key 估算",summary.api_key||{}),quotaUsageRow("Codex 估算",summary.codex||{}));if(repoCount)target.appendChild(list);target.appendChild(details);target.appendChild(el("div","subtle","GPT/Claude API 账户行来自各自 Admin Usage/Cost API;Codex 账户行来自本机 Codex rate-limit 快照;合计和 repo 行为内部估算/限流。"));return}if(!repoCount){empty(target,"暂无用量","当前窗口内没有记录到配额消耗。API Key 与 Codex 均无用量记录。");return}const list=el("div","quota-list");appendRepoQuotaRows(list,repos);target.appendChild(list)}
179
180
function renderEffectiveness(e){setUpdated("effectiveness-updated");const target=document.getElementById("effectiveness"),rate=Number(e.improvement_rate)||0;clear(target);append(target,append(el("div","metric-line"),el("div","big blue",fmtPct(rate)),el("span","delta "+(rate>0.7?"ok":rate>0.4?"warn":"info"),"改善率")),append(el("div",""),statRow("已评估",e.evaluated||0,"info"),statRow("改善",e.improved||0,"ok"),statRow("退化",e.degraded||0,"err"),statRow("持平",e.neutral||0,""),statRow("待评估",e.pending||0,"info")))}
180
181
function renderShadow(items){setUpdated("shadow-updated");const target=document.getElementById("shadow"),total=items.reduce((sum,item)=>sum+(Number(item.disagreement_count)||0),0);clear(target);if(!items.length){empty(target,"无活跃分歧","影子审计当前没有待复核分歧。");return}append(target,append(el("div","metric-line"),el("div","big violet",fmtNum(total)),el("span","delta warn","待复核")));for(const d of items.slice(0,5)){const row=el("div","stat-row"),value=el("span","stat-value warn");append(value,badge("warn",(d.disagreement_count||0)+"x"),document.createTextNode(" "+(d.ai_verdict||"")));append(row,el("span","stat-label",d.plugin||d.repo||"shadow"),value);target.appendChild(row)}}
181
182
function renderChanges(items){setUpdated("changes-updated");const target=document.getElementById("changes");clear(target);if(!items.length){empty(target,"窗口内无变更","最近 7 天没有登记的变更记录。 ");return}const table=el("table",""),thead=el("thead",""),tr=el("tr","");for(const h of ["仓库","操作","效果","置信度","时间"])tr.appendChild(el("th","",h));thead.appendChild(tr);const tbody=el("tbody","");for(const c of items){const effect=c.effect||"pending",effectClass=effect==="improved"?"ok":effect==="degraded"?"err":"info",actionClass=c.action==="auto_merge"?"ok":c.action==="auto_pr"?"info":c.action?"violet":"warn",dt=c.created_at?new Date(c.created_at*1000).toLocaleDateString():"—",row=el("tr",""),repoName=String(c.repo||"").split("/").pop()||"—",repoCell=el("td","mono");if(c.external_url){const a=el("a","",repoName);a.href=c.external_url;a.target="_blank";a.rel="noreferrer";a.style.color="#93c5fd";a.style.textDecoration="none";repoCell.appendChild(a)}else repoCell.textContent=repoName;append(row,repoCell,append(el("td",""),badge(actionClass,c.action||"change")),el("td","stat-value "+effectClass,effect),el("td","mono",fmtPct(c.confidence||0)),el("td","mono",dt));tbody.appendChild(row)}append(table,thead,tbody);target.appendChild(table)}
0 commit comments