coding-proxy 0.2.3a2__py3-none-any.whl → 0.2.3a3__py3-none-any.whl

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.
@@ -56,6 +56,9 @@ _DASHBOARD_HTML = """<!DOCTYPE html>
56
56
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
57
57
  <title>Coding Proxy Dashboard</title>
58
58
  <link rel="icon" type="image/x-icon" href="/favicon.ico" />
59
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
60
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
61
+ <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600&display=swap" rel="stylesheet" />
59
62
  <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.3/dist/chart.umd.min.js"></script>
60
63
  <style>
61
64
  :root {
@@ -63,16 +66,26 @@ _DASHBOARD_HTML = """<!DOCTYPE html>
63
66
  --bg-card: #161b22;
64
67
  --bg-card-hover: #1c2128;
65
68
  --border: #30363d;
69
+ --border-subtle: rgba(48,54,61,.6);
66
70
  --text-primary: #e6edf3;
67
71
  --text-secondary: #8b949e;
72
+ --text-tertiary: #6e7681;
68
73
  --accent-blue: #58a6ff;
69
74
  --accent-green: #3fb950;
70
75
  --accent-yellow: #d29922;
71
76
  --accent-red: #f85149;
72
77
  --accent-purple: #bc8cff;
73
78
  --accent-orange: #ffa657;
74
- --radius: 8px;
75
- --shadow: 0 1px 3px rgba(0,0,0,.4);
79
+ --accent-teal: #39d353;
80
+ --radius: 10px;
81
+ --radius-sm: 6px;
82
+ --shadow: 0 1px 3px rgba(0,0,0,.4), 0 1px 2px rgba(0,0,0,.3);
83
+ --shadow-md: 0 4px 12px rgba(0,0,0,.4), 0 2px 4px rgba(0,0,0,.3);
84
+ --glow-blue: 0 0 0 1px rgba(88,166,255,.15), 0 4px 16px rgba(88,166,255,.06);
85
+ }
86
+ @keyframes fadeInUp {
87
+ from { opacity: 0; transform: translateY(10px); }
88
+ to { opacity: 1; transform: translateY(0); }
76
89
  }
77
90
  *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
78
91
  body {
@@ -85,9 +98,11 @@ _DASHBOARD_HTML = """<!DOCTYPE html>
85
98
  }
86
99
  /* ── 头部 ── */
87
100
  header {
88
- background: var(--bg-card);
101
+ background: rgba(22,27,34,.85);
102
+ backdrop-filter: blur(12px);
103
+ -webkit-backdrop-filter: blur(12px);
89
104
  border-bottom: 1px solid var(--border);
90
- padding: 14px 24px;
105
+ padding: 13px 24px;
91
106
  display: flex;
92
107
  align-items: center;
93
108
  justify-content: space-between;
@@ -97,75 +112,105 @@ _DASHBOARD_HTML = """<!DOCTYPE html>
97
112
  }
98
113
  .header-left { display: flex; align-items: center; gap: 12px; }
99
114
  .logo {
100
- width: 28px; height: 28px;
115
+ width: 30px; height: 30px;
101
116
  background: linear-gradient(135deg, var(--accent-blue), var(--accent-purple));
102
- border-radius: 6px;
117
+ border-radius: 8px;
103
118
  display: flex; align-items: center; justify-content: center;
104
- font-size: 16px; font-weight: bold; color: #fff;
119
+ font-size: 15px; font-weight: 700; color: #fff;
120
+ box-shadow: 0 2px 8px rgba(88,166,255,.3);
105
121
  }
106
- h1 { font-size: 16px; font-weight: 600; color: var(--text-primary); }
122
+ h1 { font-size: 15px; font-weight: 600; color: var(--text-primary); letter-spacing: -.2px; }
107
123
  .header-right { display: flex; align-items: center; gap: 12px; }
108
124
  .badge {
109
125
  font-size: 11px; padding: 2px 8px;
110
126
  border-radius: 12px;
111
- background: rgba(88,166,255,.15);
127
+ background: rgba(88,166,255,.1);
112
128
  color: var(--accent-blue);
113
- border: 1px solid rgba(88,166,255,.25);
129
+ border: 1px solid rgba(88,166,255,.2);
130
+ font-family: 'JetBrains Mono', monospace;
114
131
  }
115
- .refresh-time { font-size: 12px; color: var(--text-secondary); }
132
+ .refresh-time { font-size: 11px; color: var(--text-tertiary); }
116
133
  .btn-refresh {
117
- padding: 5px 12px; border-radius: var(--radius);
118
- background: rgba(48,54,61,.6);
134
+ padding: 5px 12px; border-radius: var(--radius-sm);
135
+ background: rgba(48,54,61,.5);
119
136
  border: 1px solid var(--border);
120
- color: var(--text-primary);
137
+ color: var(--text-secondary);
121
138
  font-size: 12px; cursor: pointer;
122
- transition: background .15s;
139
+ transition: all .2s ease;
140
+ }
141
+ .btn-refresh:hover {
142
+ background: var(--bg-card-hover);
143
+ color: var(--text-primary);
144
+ border-color: rgba(88,166,255,.4);
123
145
  }
124
- .btn-refresh:hover { background: var(--bg-card-hover); }
125
146
  /* ── 主内容 ── */
126
- main { padding: 20px 24px; max-width: 1400px; margin: 0 auto; }
147
+ main { padding: 20px 24px; max-width: 1440px; margin: 0 auto; }
127
148
  /* ── KPI 卡片 ── */
128
149
  .kpi-grid {
129
150
  display: grid;
130
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
131
- gap: 14px;
132
- margin-bottom: 20px;
151
+ grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
152
+ gap: 12px;
153
+ margin-bottom: 18px;
133
154
  }
134
155
  .kpi-card {
135
156
  background: var(--bg-card);
136
157
  border: 1px solid var(--border);
137
158
  border-radius: var(--radius);
138
- padding: 16px 20px;
159
+ padding: 16px 18px 14px;
139
160
  box-shadow: var(--shadow);
140
- transition: background .15s;
161
+ transition: all .2s ease;
162
+ animation: fadeInUp .4s ease both;
163
+ position: relative;
164
+ overflow: hidden;
141
165
  }
142
- .kpi-card:hover { background: var(--bg-card-hover); }
143
- .kpi-label { font-size: 12px; color: var(--text-secondary); margin-bottom: 6px; }
144
- .kpi-value { font-size: 26px; font-weight: 700; line-height: 1.2; }
145
- .kpi-sub { font-size: 12px; color: var(--text-secondary); margin-top: 4px; }
146
- .kpi-delta { font-size: 12px; margin-top: 4px; }
147
- .kpi-delta.up { color: var(--accent-green); }
148
- .kpi-delta.down { color: var(--accent-red); }
166
+ .kpi-card::before {
167
+ content: '';
168
+ position: absolute;
169
+ top: 0; left: 0; right: 0;
170
+ height: 2px;
171
+ border-radius: var(--radius) var(--radius) 0 0;
172
+ }
173
+ .kpi-card:nth-child(1)::before { background: var(--accent-blue); }
174
+ .kpi-card:nth-child(2)::before { background: var(--accent-purple); }
175
+ .kpi-card:nth-child(3)::before { background: var(--accent-green); }
176
+ .kpi-card:nth-child(4)::before { background: var(--accent-yellow); }
177
+ .kpi-card:nth-child(5)::before { background: var(--accent-red); }
178
+ .kpi-card:nth-child(6)::before { background: var(--accent-orange); }
179
+ .kpi-card:hover {
180
+ background: var(--bg-card-hover);
181
+ box-shadow: var(--glow-blue);
182
+ transform: translateY(-1px);
183
+ }
184
+ .kpi-header { display: flex; align-items: center; gap: 6px; margin-bottom: 8px; }
185
+ .kpi-icon { font-size: 13px; opacity: .8; }
186
+ .kpi-label { font-size: 11px; color: var(--text-secondary); font-weight: 500; letter-spacing: .2px; }
187
+ .kpi-value {
188
+ font-size: 24px; font-weight: 700; line-height: 1.2;
189
+ font-family: 'JetBrains Mono', monospace;
190
+ letter-spacing: -0.5px;
191
+ }
192
+ .kpi-sub { font-size: 11px; color: var(--text-tertiary); margin-top: 5px; }
149
193
  .color-blue { color: var(--accent-blue); }
150
194
  .color-green { color: var(--accent-green); }
151
195
  .color-yellow { color: var(--accent-yellow); }
152
196
  .color-red { color: var(--accent-red); }
153
197
  .color-purple { color: var(--accent-purple); }
198
+ .color-orange { color: var(--accent-orange); }
154
199
  /* ── 图表网格 ── */
155
200
  .charts-grid {
156
201
  display: grid;
157
202
  grid-template-columns: 1fr 2fr;
158
- gap: 14px;
159
- margin-bottom: 14px;
203
+ gap: 12px;
204
+ margin-bottom: 12px;
160
205
  }
161
- .charts-grid-3 {
206
+ .charts-grid-2 {
162
207
  display: grid;
163
208
  grid-template-columns: 1fr 2fr;
164
- gap: 14px;
165
- margin-bottom: 14px;
209
+ gap: 12px;
210
+ margin-bottom: 12px;
166
211
  }
167
- @media (max-width: 900px) {
168
- .charts-grid, .charts-grid-3 { grid-template-columns: 1fr; }
212
+ @media (max-width: 960px) {
213
+ .charts-grid, .charts-grid-2 { grid-template-columns: 1fr; }
169
214
  }
170
215
  .card {
171
216
  background: var(--bg-card);
@@ -173,101 +218,131 @@ _DASHBOARD_HTML = """<!DOCTYPE html>
173
218
  border-radius: var(--radius);
174
219
  padding: 16px 20px;
175
220
  box-shadow: var(--shadow);
221
+ transition: box-shadow .2s ease;
222
+ animation: fadeInUp .4s ease both;
176
223
  }
224
+ .card:hover { box-shadow: var(--shadow-md); }
177
225
  .card-title {
178
- font-size: 13px; font-weight: 600;
179
- color: var(--text-secondary);
226
+ font-size: 11px; font-weight: 600;
227
+ color: var(--text-tertiary);
180
228
  text-transform: uppercase;
181
- letter-spacing: .5px;
229
+ letter-spacing: .8px;
182
230
  margin-bottom: 14px;
183
231
  display: flex; align-items: center; justify-content: space-between;
184
232
  }
185
233
  .chart-wrap { position: relative; height: 220px; }
186
234
  .chart-wrap-lg { position: relative; height: 240px; }
235
+ .chart-wrap-xl { position: relative; height: 260px; }
187
236
  /* ── 供应商状态 ── */
188
- .vendor-list { display: flex; flex-direction: column; gap: 10px; }
237
+ .vendor-list { display: flex; flex-direction: column; gap: 8px; }
189
238
  .vendor-item {
190
239
  display: flex; align-items: center; justify-content: space-between;
191
240
  padding: 10px 12px;
192
- background: rgba(255,255,255,.03);
193
- border: 1px solid var(--border);
194
- border-radius: 6px;
241
+ background: rgba(255,255,255,.02);
242
+ border: 1px solid var(--border-subtle);
243
+ border-radius: var(--radius-sm);
244
+ transition: background .15s;
245
+ }
246
+ .vendor-item:hover { background: rgba(255,255,255,.04); }
247
+ .vendor-info { display: flex; align-items: center; gap: 10px; }
248
+ .vendor-avatar {
249
+ width: 28px; height: 28px; border-radius: 50%;
250
+ background: linear-gradient(135deg, var(--accent-blue), var(--accent-purple));
251
+ display: flex; align-items: center; justify-content: center;
252
+ font-size: 11px; font-weight: 700; color: #fff;
253
+ flex-shrink: 0;
195
254
  }
196
- .vendor-name { font-weight: 600; font-size: 13px; min-width: 80px; }
197
- .vendor-badges { display: flex; gap: 6px; flex-wrap: wrap; }
255
+ .vendor-name { font-weight: 600; font-size: 12px; }
256
+ .vendor-badges { display: flex; gap: 5px; flex-wrap: wrap; align-items: center; }
198
257
  .status-badge {
199
- font-size: 11px; padding: 2px 7px;
258
+ font-size: 10px; padding: 2px 7px;
200
259
  border-radius: 10px;
201
260
  font-weight: 500;
202
261
  }
203
- .sb-ok { background: rgba(63,185,80,.15); color: var(--accent-green); border: 1px solid rgba(63,185,80,.25); }
204
- .sb-warn { background: rgba(210,153,34,.15); color: var(--accent-yellow); border: 1px solid rgba(210,153,34,.25); }
205
- .sb-err { background: rgba(248,81,73,.15); color: var(--accent-red); border: 1px solid rgba(248,81,73,.25); }
206
- .sb-info { background: rgba(88,166,255,.15); color: var(--accent-blue); border: 1px solid rgba(88,166,255,.25); }
207
- .quota-bar-wrap { flex: 1; margin: 0 12px; max-width: 120px; }
262
+ .sb-ok { background: rgba(63,185,80,.12); color: var(--accent-green); border: 1px solid rgba(63,185,80,.2); }
263
+ .sb-warn { background: rgba(210,153,34,.12); color: var(--accent-yellow); border: 1px solid rgba(210,153,34,.2); }
264
+ .sb-err { background: rgba(248,81,73,.12); color: var(--accent-red); border: 1px solid rgba(248,81,73,.2); }
265
+ .sb-info { background: rgba(88,166,255,.12); color: var(--accent-blue); border: 1px solid rgba(88,166,255,.2); }
266
+ .quota-bar-wrap { flex: 1; margin: 0 10px; max-width: 100px; }
208
267
  .quota-bar-bg {
209
- height: 5px; border-radius: 3px;
210
- background: rgba(255,255,255,.08);
268
+ height: 4px; border-radius: 2px;
269
+ background: rgba(255,255,255,.06);
211
270
  overflow: hidden;
212
271
  }
213
- .quota-bar-fill { height: 100%; border-radius: 3px; transition: width .4s; }
214
- .quota-pct { font-size: 11px; color: var(--text-secondary); margin-top: 2px; text-align: right; }
272
+ .quota-bar-fill {
273
+ height: 100%; border-radius: 2px;
274
+ transition: width .6s cubic-bezier(.4,0,.2,1);
275
+ }
215
276
  /* ── 故障转移表 ── */
216
277
  .ft-table-wrap { overflow-x: auto; }
217
278
  table { width: 100%; border-collapse: collapse; }
279
+ thead tr { position: sticky; top: 0; background: var(--bg-card); z-index: 1; }
218
280
  th {
219
- text-align: left; font-size: 12px; color: var(--text-secondary);
220
- font-weight: 500; padding: 6px 10px;
281
+ text-align: left; font-size: 11px; color: var(--text-tertiary);
282
+ font-weight: 600; padding: 6px 10px;
221
283
  border-bottom: 1px solid var(--border);
284
+ letter-spacing: .4px; text-transform: uppercase;
222
285
  }
223
- td { padding: 8px 10px; font-size: 13px; border-bottom: 1px solid rgba(48,54,61,.5); }
286
+ td { padding: 8px 10px; font-size: 13px; border-bottom: 1px solid var(--border-subtle); }
224
287
  tr:last-child td { border-bottom: none; }
225
288
  tr:hover td { background: rgba(255,255,255,.02); }
226
289
  .tag-vendor {
227
290
  display: inline-block;
228
- font-size: 11px; padding: 1px 7px;
291
+ font-size: 11px; padding: 2px 8px;
229
292
  border-radius: 10px;
230
- background: rgba(188,140,255,.15);
293
+ background: rgba(188,140,255,.1);
231
294
  color: var(--accent-purple);
232
- border: 1px solid rgba(188,140,255,.25);
295
+ border: 1px solid rgba(188,140,255,.2);
296
+ font-weight: 500;
233
297
  }
234
- .arrow { color: var(--text-secondary); margin: 0 4px; }
298
+ .arrow { color: var(--text-tertiary); margin: 0 4px; }
235
299
  /* ── 时间区间选择栏 ── */
236
300
  .time-range-bar {
237
301
  display: flex; align-items: center; gap: 8px;
238
- margin-bottom: 16px; flex-wrap: wrap;
302
+ margin-bottom: 18px; flex-wrap: wrap;
303
+ padding: 10px 14px;
304
+ background: rgba(22,27,34,.6);
305
+ border: 1px solid var(--border-subtle);
306
+ border-radius: var(--radius);
307
+ backdrop-filter: blur(8px);
239
308
  }
240
- .time-range-label { font-size: 13px; color: var(--text-secondary); }
309
+ .time-range-label { font-size: 12px; color: var(--text-tertiary); font-weight: 500; }
241
310
  .range-btn {
242
311
  padding: 4px 14px; border-radius: 14px;
243
- background: rgba(48,54,61,.6);
244
- border: 1px solid var(--border);
312
+ background: transparent;
313
+ border: 1px solid transparent;
245
314
  color: var(--text-secondary);
246
315
  font-size: 12px; cursor: pointer;
247
- transition: background .15s, color .15s, border-color .15s;
316
+ transition: all .2s ease;
248
317
  }
249
- .range-btn:hover { background: var(--bg-card-hover); color: var(--text-primary); }
318
+ .range-btn:hover { background: rgba(255,255,255,.05); color: var(--text-primary); }
250
319
  .range-btn.active {
251
- background: rgba(88,166,255,.15);
252
- border-color: rgba(88,166,255,.5);
320
+ background: rgba(88,166,255,.12);
321
+ border-color: rgba(88,166,255,.35);
253
322
  color: var(--accent-blue);
323
+ font-weight: 500;
254
324
  }
255
325
  .range-custom { display: none; align-items: center; gap: 6px; }
256
326
  .range-custom.visible { display: flex; }
257
327
  .range-date {
258
- padding: 3px 8px; border-radius: var(--radius);
259
- background: var(--bg-card); border: 1px solid var(--border);
328
+ padding: 3px 10px; border-radius: var(--radius-sm);
329
+ background: rgba(48,54,61,.4); border: 1px solid var(--border);
260
330
  color: var(--text-primary); font-size: 12px;
261
331
  color-scheme: dark;
332
+ transition: border-color .2s;
262
333
  }
263
- .range-sep { font-size: 12px; color: var(--text-secondary); }
334
+ .range-date:focus { outline: none; border-color: rgba(88,166,255,.5); }
335
+ .range-sep { font-size: 12px; color: var(--text-tertiary); }
264
336
  /* ── 空态 ── */
265
337
  .empty {
266
338
  text-align: center; padding: 32px;
267
- color: var(--text-secondary); font-size: 13px;
339
+ color: var(--text-tertiary); font-size: 13px;
268
340
  }
341
+ .empty-icon { font-size: 28px; margin-bottom: 8px; opacity: .5; }
269
342
  /* ── 加载态 ── */
270
343
  .loading { opacity: .4; pointer-events: none; }
344
+ /* ── 图表标签截断 ── */
345
+ .chart-legend-note { font-size: 11px; color: var(--text-tertiary); margin-top: 4px; text-align: center; }
271
346
  </style>
272
347
  </head>
273
348
  <body>
@@ -286,28 +361,52 @@ _DASHBOARD_HTML = """<!DOCTYPE html>
286
361
  <main>
287
362
  <!-- 时间区间选择器 -->
288
363
  <div class="time-range-bar">
289
- <span class="time-range-label">数据时间区间:</span>
290
- <button class="range-btn active" onclick="setTimeRange(7, this)">最近一周</button>
291
- <button class="range-btn" onclick="setTimeRange(30, this)">最近一月</button>
364
+ <span class="time-range-label">时间区间</span>
365
+ <button class="range-btn active" onclick="setTimeRange(7, this)">近 7 天</button>
366
+ <button class="range-btn" onclick="setTimeRange(30, this)">近 30 天</button>
292
367
  <button class="range-btn" onclick="setTimeRange(0, this)">自选区间</button>
293
368
  <div class="range-custom" id="range-custom">
294
369
  <input type="date" id="range-start" class="range-date" onchange="applyCustomRange()" />
295
- <span class="range-sep">–</span>
370
+ <span class="range-sep">→</span>
296
371
  <input type="date" id="range-end" class="range-date" onchange="applyCustomRange()" />
297
372
  </div>
298
373
  </div>
299
374
 
300
375
  <!-- KPI 卡片 -->
301
376
  <div class="kpi-grid" id="kpi-grid">
302
- <div class="kpi-card"><div class="kpi-label">今日请求数</div><div class="kpi-value color-blue" id="kpi-req-today">–</div><div class="kpi-sub" id="kpi-req-week">本周 –</div></div>
303
- <div class="kpi-card"><div class="kpi-label">今日 Token 总量</div><div class="kpi-value color-purple" id="kpi-tok-today">–</div><div class="kpi-sub" id="kpi-tok-week">本周 –</div></div>
304
- <div class="kpi-card"><div class="kpi-label">今日输出 Token</div><div class="kpi-value color-green" id="kpi-out-today">–</div><div class="kpi-sub" id="kpi-out-week">本周 –</div></div>
305
- <div class="kpi-card"><div class="kpi-label">今日费用估算</div><div class="kpi-value color-yellow" id="kpi-cost-today">–</div><div class="kpi-sub" id="kpi-cost-week">本周 –</div></div>
306
- <div class="kpi-card"><div class="kpi-label">故障转移(今日)</div><div class="kpi-value color-red" id="kpi-fo-today">–</div><div class="kpi-sub" id="kpi-fo-week">本周 –</div></div>
307
- <div class="kpi-card"><div class="kpi-label">平均延迟(今日)</div><div class="kpi-value" id="kpi-lat-today">–</div><div class="kpi-sub" id="kpi-lat-week">本周 –</div></div>
377
+ <div class="kpi-card">
378
+ <div class="kpi-header"><span class="kpi-icon">📊</span><span class="kpi-label">今日请求数</span></div>
379
+ <div class="kpi-value color-blue" id="kpi-req-today">–</div>
380
+ <div class="kpi-sub" id="kpi-req-week">本周 –</div>
381
+ </div>
382
+ <div class="kpi-card">
383
+ <div class="kpi-header"><span class="kpi-icon">🔢</span><span class="kpi-label">今日 Token 总量</span></div>
384
+ <div class="kpi-value color-purple" id="kpi-tok-today">–</div>
385
+ <div class="kpi-sub" id="kpi-tok-week">本周 –</div>
386
+ </div>
387
+ <div class="kpi-card">
388
+ <div class="kpi-header"><span class="kpi-icon">💬</span><span class="kpi-label">今日输出 Token</span></div>
389
+ <div class="kpi-value color-green" id="kpi-out-today">–</div>
390
+ <div class="kpi-sub" id="kpi-out-week">本周 –</div>
391
+ </div>
392
+ <div class="kpi-card">
393
+ <div class="kpi-header"><span class="kpi-icon">💰</span><span class="kpi-label">今日费用估算</span></div>
394
+ <div class="kpi-value color-yellow" id="kpi-cost-today">–</div>
395
+ <div class="kpi-sub" id="kpi-cost-week">本周 –</div>
396
+ </div>
397
+ <div class="kpi-card">
398
+ <div class="kpi-header"><span class="kpi-icon">🔄</span><span class="kpi-label">故障转移(今日)</span></div>
399
+ <div class="kpi-value color-red" id="kpi-fo-today">–</div>
400
+ <div class="kpi-sub" id="kpi-fo-week">本周 –</div>
401
+ </div>
402
+ <div class="kpi-card">
403
+ <div class="kpi-header"><span class="kpi-icon">⚡</span><span class="kpi-label">平均延迟(今日)</span></div>
404
+ <div class="kpi-value color-orange" id="kpi-lat-today">–</div>
405
+ <div class="kpi-sub" id="kpi-lat-week">本周 –</div>
406
+ </div>
308
407
  </div>
309
408
 
310
- <!-- 供应商状态 + 趋势折线图 -->
409
+ <!-- 供应商状态 + 请求量趋势折线图 -->
311
410
  <div class="charts-grid">
312
411
  <div class="card">
313
412
  <div class="card-title">供应商状态</div>
@@ -323,8 +422,8 @@ _DASHBOARD_HTML = """<!DOCTYPE html>
323
422
  </div>
324
423
  </div>
325
424
 
326
- <!-- 供应商分布 + Token 量趋势 -->
327
- <div class="charts-grid-3">
425
+ <!-- 供应商分布 + Token 量趋势(按 vendor) -->
426
+ <div class="charts-grid-2">
328
427
  <div class="card">
329
428
  <div class="card-title" id="title-vendor-dist">供应商请求分布(近 7 天)</div>
330
429
  <div class="chart-wrap">
@@ -332,13 +431,21 @@ _DASHBOARD_HTML = """<!DOCTYPE html>
332
431
  </div>
333
432
  </div>
334
433
  <div class="card">
335
- <div class="card-title" id="title-token-timeline">近 7 天 Token 量趋势</div>
434
+ <div class="card-title" id="title-token-timeline">近 7 天 Token 量趋势(按供应商)</div>
336
435
  <div class="chart-wrap-lg">
337
436
  <canvas id="chart-token-timeline"></canvas>
338
437
  </div>
339
438
  </div>
340
439
  </div>
341
440
 
441
+ <!-- Token 用量(按 Vendor / 模型)堆叠图 -->
442
+ <div class="card" style="margin-bottom:12px">
443
+ <div class="card-title" id="title-model-token-timeline">近 7 天 Token 用量(按 Vendor / 模型)</div>
444
+ <div class="chart-wrap-xl">
445
+ <canvas id="chart-model-token-timeline"></canvas>
446
+ </div>
447
+ </div>
448
+
342
449
  <!-- 故障转移明细表 -->
343
450
  <div class="card">
344
451
  <div class="card-title">故障转移明细</div>
@@ -364,13 +471,9 @@ _DASHBOARD_HTML = """<!DOCTYPE html>
364
471
  const VENDOR_COLORS = [
365
472
  '#58a6ff','#bc8cff','#3fb950','#ffa657','#f85149',
366
473
  '#79c0ff','#d2a8ff','#56d364','#ffb77c','#ff7b72',
474
+ '#39d353','#e3b341','#a5d6ff','#f0a9eb','#7ee787',
475
+ '#ffa198','#cae8ff','#dbedff','#b6e3ff','#54aeff',
367
476
  ];
368
- const TOKEN_COLORS = {
369
- input: '#58a6ff',
370
- output: '#3fb950',
371
- cache_creation: '#d29922',
372
- cache_read: '#bc8cff',
373
- };
374
477
 
375
478
  // ── 工具函数 ──────────────────────────────────────────────
376
479
  function fmtTokens(n) {
@@ -385,20 +488,36 @@ function now() {
385
488
  return new Date().toLocaleTimeString('zh-CN', {hour:'2-digit',minute:'2-digit',second:'2-digit'});
386
489
  }
387
490
 
491
+ // ── 渐变填充工具 ──────────────────────────────────────────
492
+ function makeGradient(ctx, color) {
493
+ const h = ctx.canvas.height;
494
+ const grad = ctx.createLinearGradient(0, 0, 0, h);
495
+ grad.addColorStop(0, color + '44');
496
+ grad.addColorStop(1, color + '04');
497
+ return grad;
498
+ }
499
+
388
500
  // ── Chart.js 全局默认 ─────────────────────────────────────
389
501
  Chart.defaults.color = '#8b949e';
390
- Chart.defaults.borderColor = '#30363d';
502
+ Chart.defaults.borderColor = 'rgba(255,255,255,.04)';
391
503
  Chart.defaults.font.family = '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif';
392
- Chart.defaults.font.size = 12;
504
+ Chart.defaults.font.size = 11;
505
+
506
+ const COMMON_SCALE_X = { grid: { display: false }, ticks: { maxTicksLimit: 10 } };
507
+ const COMMON_SCALE_Y = { grid: { color: 'rgba(255,255,255,.04)' }, beginAtZero: true };
508
+ const COMMON_LEGEND = { position: 'bottom', labels: { boxWidth: 8, padding: 14, usePointStyle: true, pointStyleWidth: 8, font: { size: 11 } } };
509
+ const COMMON_LINE_DATASET = { tension: .35, pointRadius: 0, pointHoverRadius: 5, borderWidth: 2 };
393
510
 
394
511
  // ── 图表实例 ──────────────────────────────────────────────
395
512
  let chartTimeline = null;
396
513
  let chartVendorDist = null;
397
514
  let chartTokenTimeline = null;
515
+ let chartModelTokenTimeline = null;
398
516
 
399
517
  function destroyCharts() {
400
- [chartTimeline, chartVendorDist, chartTokenTimeline].forEach(c => c && c.destroy());
401
- chartTimeline = chartVendorDist = chartTokenTimeline = null;
518
+ [chartTimeline, chartVendorDist, chartTokenTimeline, chartModelTokenTimeline]
519
+ .forEach(c => c && c.destroy());
520
+ chartTimeline = chartVendorDist = chartTokenTimeline = chartModelTokenTimeline = null;
402
521
  }
403
522
 
404
523
  // ── 数据拉取 ──────────────────────────────────────────────
@@ -410,7 +529,7 @@ async function fetchJSON(url) {
410
529
 
411
530
  // ── KPI 更新 ──────────────────────────────────────────────
412
531
  function updateKPI(summary) {
413
- const t = summary.today, w = summary.week, m = summary.month;
532
+ const t = summary.today, w = summary.week;
414
533
 
415
534
  document.getElementById('kpi-req-today').textContent = fmtNum(t.requests);
416
535
  document.getElementById('kpi-req-week').textContent = '本周 ' + fmtNum(w.requests);
@@ -464,7 +583,7 @@ function updateVendorStatus(status) {
464
583
  const tiers = status.tiers || [];
465
584
  const list = document.getElementById('vendor-list');
466
585
  if (!tiers.length) {
467
- list.innerHTML = '<div class="empty">无供应商数据</div>';
586
+ list.innerHTML = '<div class="empty"><div class="empty-icon">🔌</div>无供应商数据</div>';
468
587
  return;
469
588
  }
470
589
  list.innerHTML = tiers.map(tier => {
@@ -475,25 +594,28 @@ function updateVendorStatus(status) {
475
594
  const cbLabel = cbStateLabel(cb.state);
476
595
  const pct = qg.usage_percent != null ? Math.round(qg.usage_percent) : null;
477
596
  const wpct = wqg.usage_percent != null ? Math.round(wqg.usage_percent) : null;
597
+ const initial = (tier.name || '?').charAt(0).toUpperCase();
478
598
 
479
599
  let quotaHTML = '';
480
600
  if (pct != null) {
481
601
  quotaHTML += `
482
- <span class="status-badge ${quotaClass(pct)}">日配额 ${pct}%</span>
602
+ <span class="status-badge ${quotaClass(pct)}">日配 ${pct}%</span>
483
603
  <div class="quota-bar-wrap">
484
604
  <div class="quota-bar-bg"><div class="quota-bar-fill" style="width:${Math.min(pct,100)}%;background:${quotaBarColor(pct)}"></div></div>
485
605
  </div>`;
486
606
  }
487
607
  if (wpct != null) {
488
- quotaHTML += `<span class="status-badge ${quotaClass(wpct)}">周配额 ${wpct}%</span>`;
608
+ quotaHTML += `<span class="status-badge ${quotaClass(wpct)}">周配 ${wpct}%</span>`;
489
609
  }
490
610
 
491
611
  const rlInfo = tier.rate_limit || {};
492
- const rlHtml = rlInfo.limited
493
- ? `<span class="status-badge sb-warn">限速中</span>` : '';
612
+ const rlHtml = rlInfo.limited ? `<span class="status-badge sb-warn">限速中</span>` : '';
494
613
 
495
614
  return `<div class="vendor-item">
496
- <span class="vendor-name">${tier.name}</span>
615
+ <div class="vendor-info">
616
+ <div class="vendor-avatar">${initial}</div>
617
+ <span class="vendor-name">${tier.name}</span>
618
+ </div>
497
619
  <div class="vendor-badges">
498
620
  <span class="status-badge ${cbClass}">${cbLabel}${cb.failure_count ? ' ×'+cb.failure_count : ''}</span>
499
621
  ${quotaHTML}
@@ -503,10 +625,9 @@ function updateVendorStatus(status) {
503
625
  }).join('');
504
626
  }
505
627
 
506
- // ── 时序折线图 ────────────────────────────────────────────
628
+ // ── 时序折线图(请求量,按 vendor)────────────────────────
507
629
  function buildTimeline(rows) {
508
- // vendor 分组,按 date 汇总
509
- const vendorDateMap = {}; // vendor → {date → count}
630
+ const vendorDateMap = {};
510
631
  const allDates = new Set();
511
632
  for (const r of rows) {
512
633
  const v = r.vendor, d = r.date;
@@ -518,29 +639,30 @@ function buildTimeline(rows) {
518
639
  const dates = [...allDates].sort();
519
640
  const vendors = Object.keys(vendorDateMap).sort();
520
641
 
521
- const datasets = vendors.map((v, i) => ({
522
- label: v,
523
- data: dates.map(d => vendorDateMap[v][d] || 0),
524
- borderColor: VENDOR_COLORS[i % VENDOR_COLORS.length],
525
- backgroundColor: VENDOR_COLORS[i % VENDOR_COLORS.length] + '22',
526
- fill: true,
527
- tension: .3,
528
- pointRadius: 3,
529
- pointHoverRadius: 5,
530
- }));
531
-
532
642
  if (chartTimeline) chartTimeline.destroy();
533
643
  const ctx = document.getElementById('chart-timeline').getContext('2d');
644
+ const datasets = vendors.map((v, i) => {
645
+ const color = VENDOR_COLORS[i % VENDOR_COLORS.length];
646
+ return {
647
+ ...COMMON_LINE_DATASET,
648
+ label: v,
649
+ data: dates.map(d => vendorDateMap[v][d] || 0),
650
+ borderColor: color,
651
+ backgroundColor: makeGradient(ctx, color),
652
+ fill: true,
653
+ };
654
+ });
655
+
534
656
  chartTimeline = new Chart(ctx, {
535
657
  type: 'line',
536
658
  data: { labels: dates, datasets },
537
659
  options: {
538
660
  responsive: true, maintainAspectRatio: false,
539
661
  interaction: { mode: 'index', intersect: false },
540
- plugins: { legend: { position: 'bottom', labels: { boxWidth: 10, padding: 12 } } },
662
+ plugins: { legend: COMMON_LEGEND },
541
663
  scales: {
542
- x: { grid: { color: '#30363d' } },
543
- y: { grid: { color: '#30363d' }, beginAtZero: true, ticks: { precision: 0 } },
664
+ x: COMMON_SCALE_X,
665
+ y: { ...COMMON_SCALE_Y, ticks: { precision: 0 } },
544
666
  },
545
667
  },
546
668
  });
@@ -560,7 +682,7 @@ function buildVendorDist(rows) {
560
682
  if (chartVendorDist) chartVendorDist.destroy();
561
683
  const ctx = document.getElementById('chart-vendor-dist').getContext('2d');
562
684
  if (!labels.length) {
563
- ctx.canvas.parentElement.innerHTML = '<div class="empty">暂无数据</div>';
685
+ ctx.canvas.parentElement.innerHTML = '<div class="empty"><div class="empty-icon">📭</div>暂无数据</div>';
564
686
  return;
565
687
  }
566
688
  chartVendorDist = new Chart(ctx, {
@@ -571,23 +693,22 @@ function buildVendorDist(rows) {
571
693
  data,
572
694
  backgroundColor: labels.map((_,i) => VENDOR_COLORS[i % VENDOR_COLORS.length]),
573
695
  borderWidth: 0,
574
- hoverOffset: 6,
696
+ hoverOffset: 8,
575
697
  }],
576
698
  },
577
699
  options: {
578
700
  responsive: true, maintainAspectRatio: false,
579
701
  plugins: {
580
- legend: { position: 'bottom', labels: { boxWidth: 10, padding: 10 } },
581
- tooltip: { callbacks: { label: ctx => ` ${ctx.label}: ${ctx.raw} 次` } },
702
+ legend: COMMON_LEGEND,
703
+ tooltip: { callbacks: { label: c => ` ${c.label}: ${c.raw.toLocaleString()} 次` } },
582
704
  },
583
705
  },
584
706
  });
585
707
  }
586
708
 
587
- // ── Token 量趋势折线图 ────────────────────────────────────
709
+ // ── Token 量趋势折线图(按 vendor)───────────────────────
588
710
  function buildTokenTimeline(rows) {
589
- // vendor 分组,按 date 汇总 token 总量
590
- const vendorDateMap = {}; // vendor → {date → total_tokens}
711
+ const vendorDateMap = {};
591
712
  const allDates = new Set();
592
713
  for (const r of rows) {
593
714
  const v = r.vendor, d = r.date;
@@ -604,20 +725,21 @@ function buildTokenTimeline(rows) {
604
725
  if (chartTokenTimeline) chartTokenTimeline.destroy();
605
726
  const ctx = document.getElementById('chart-token-timeline').getContext('2d');
606
727
  if (!dates.length) {
607
- ctx.canvas.parentElement.innerHTML = '<div class="empty">暂无数据</div>';
728
+ ctx.canvas.parentElement.innerHTML = '<div class="empty"><div class="empty-icon">📭</div>暂无数据</div>';
608
729
  return;
609
730
  }
610
731
 
611
- const datasets = vendors.map((v, i) => ({
612
- label: v,
613
- data: dates.map(d => vendorDateMap[v][d] || 0),
614
- borderColor: VENDOR_COLORS[i % VENDOR_COLORS.length],
615
- backgroundColor: VENDOR_COLORS[i % VENDOR_COLORS.length] + '22',
616
- fill: true,
617
- tension: .3,
618
- pointRadius: 3,
619
- pointHoverRadius: 5,
620
- }));
732
+ const datasets = vendors.map((v, i) => {
733
+ const color = VENDOR_COLORS[i % VENDOR_COLORS.length];
734
+ return {
735
+ ...COMMON_LINE_DATASET,
736
+ label: v,
737
+ data: dates.map(d => vendorDateMap[v][d] || 0),
738
+ borderColor: color,
739
+ backgroundColor: makeGradient(ctx, color),
740
+ fill: true,
741
+ };
742
+ });
621
743
 
622
744
  chartTokenTimeline = new Chart(ctx, {
623
745
  type: 'line',
@@ -625,11 +747,94 @@ function buildTokenTimeline(rows) {
625
747
  options: {
626
748
  responsive: true, maintainAspectRatio: false,
627
749
  interaction: { mode: 'index', intersect: false },
628
- plugins: { legend: { position: 'bottom', labels: { boxWidth: 10, padding: 12 } } },
750
+ plugins: {
751
+ legend: COMMON_LEGEND,
752
+ tooltip: { callbacks: { label: c => ` ${c.dataset.label}: ${fmtTokens(c.raw)}` } },
753
+ },
754
+ scales: {
755
+ x: COMMON_SCALE_X,
756
+ y: { ...COMMON_SCALE_Y, ticks: { callback: v => fmtTokens(v) } },
757
+ },
758
+ },
759
+ });
760
+ }
761
+
762
+ // ── Token 用量趋势(按 Vendor / 模型,堆叠面积图)────────
763
+ function buildModelTokenTimeline(rows) {
764
+ const modelDateMap = {};
765
+ const allDates = new Set();
766
+ for (const r of rows) {
767
+ const key = (r.vendor || '?') + ' / ' + (r.model_served || '?');
768
+ const d = r.date;
769
+ if (!d) continue;
770
+ if (!modelDateMap[key]) modelDateMap[key] = {};
771
+ const total = (r.total_input || 0) + (r.total_output || 0)
772
+ + (r.total_cache_creation || 0) + (r.total_cache_read || 0);
773
+ modelDateMap[key][d] = (modelDateMap[key][d] || 0) + total;
774
+ allDates.add(d);
775
+ }
776
+ const dates = [...allDates].sort();
777
+ // 按总量降序排列 key
778
+ const keys = Object.keys(modelDateMap).sort((a, b) => {
779
+ const sumA = Object.values(modelDateMap[a]).reduce((s, v) => s + v, 0);
780
+ const sumB = Object.values(modelDateMap[b]).reduce((s, v) => s + v, 0);
781
+ return sumB - sumA;
782
+ });
783
+
784
+ if (chartModelTokenTimeline) chartModelTokenTimeline.destroy();
785
+ const canvasEl = document.getElementById('chart-model-token-timeline');
786
+ if (!canvasEl) return;
787
+ const ctx = canvasEl.getContext('2d');
788
+ if (!dates.length || !keys.length) {
789
+ ctx.canvas.parentElement.innerHTML = '<div class="empty"><div class="empty-icon">📭</div>暂无数据</div>';
790
+ return;
791
+ }
792
+
793
+ const datasets = keys.map((key, i) => {
794
+ const color = VENDOR_COLORS[i % VENDOR_COLORS.length];
795
+ return {
796
+ ...COMMON_LINE_DATASET,
797
+ label: key,
798
+ data: dates.map(d => modelDateMap[key][d] || 0),
799
+ borderColor: color,
800
+ backgroundColor: color + '30',
801
+ fill: true,
802
+ };
803
+ });
804
+
805
+ chartModelTokenTimeline = new Chart(ctx, {
806
+ type: 'line',
807
+ data: { labels: dates, datasets },
808
+ options: {
809
+ responsive: true, maintainAspectRatio: false,
810
+ interaction: { mode: 'index', intersect: false },
811
+ plugins: {
812
+ legend: {
813
+ position: keys.length > 8 ? 'right' : 'bottom',
814
+ labels: {
815
+ ...COMMON_LEGEND.labels,
816
+ generateLabels: chart => Chart.defaults.plugins.legend.labels.generateLabels(chart).map(item => {
817
+ const maxLen = 32;
818
+ if (item.text.length > maxLen) item.text = item.text.slice(0, maxLen) + '…';
819
+ return item;
820
+ }),
821
+ },
822
+ },
823
+ tooltip: {
824
+ callbacks: {
825
+ label: c => ` ${c.dataset.label}: ${fmtTokens(c.raw)}`,
826
+ footer: items => {
827
+ const total = items.reduce((s, i) => s + (i.raw || 0), 0);
828
+ return total > 0 ? '合计: ' + fmtTokens(total) : '';
829
+ },
830
+ },
831
+ },
832
+ },
629
833
  scales: {
630
- x: { grid: { color: '#30363d' } },
834
+ x: COMMON_SCALE_X,
631
835
  y: {
632
- grid: { color: '#30363d' }, beginAtZero: true,
836
+ ...COMMON_SCALE_Y,
837
+ stacked: true,
633
838
  ticks: { callback: v => fmtTokens(v) },
634
839
  },
635
840
  },
@@ -641,14 +846,14 @@ function buildTokenTimeline(rows) {
641
846
  function updateFtTable(failoverStats) {
642
847
  const tbody = document.getElementById('ft-tbody');
643
848
  if (!failoverStats || !failoverStats.length) {
644
- tbody.innerHTML = '<tr><td colspan="3" class="empty">暂无故障转移记录</td></tr>';
849
+ tbody.innerHTML = '<tr><td colspan="3" class="empty"><div class="empty-icon">✅</div>暂无故障转移记录</td></tr>';
645
850
  return;
646
851
  }
647
852
  tbody.innerHTML = failoverStats.map(r => `
648
853
  <tr>
649
854
  <td><span class="tag-vendor">${r.failover_from || 'unknown'}</span></td>
650
855
  <td><span class="tag-vendor">${r.vendor || ''}</span></td>
651
- <td>${fmtNum(r.count)}</td>
856
+ <td><span style="font-family:'JetBrains Mono',monospace">${fmtNum(r.count)}</span></td>
652
857
  </tr>`).join('');
653
858
  }
654
859
 
@@ -662,7 +867,6 @@ function setTimeRange(days, btn) {
662
867
  const customEl = document.getElementById('range-custom');
663
868
  if (days === 0) {
664
869
  customEl.classList.add('visible');
665
- // 初始化日期:默认今天往前 7 天
666
870
  const today = new Date();
667
871
  const weekAgo = new Date(today);
668
872
  weekAgo.setDate(weekAgo.getDate() - 6);
@@ -686,20 +890,16 @@ function applyCustomRange() {
686
890
  refresh();
687
891
  }
688
892
 
689
- function rangeLabel() {
690
- if (currentDays <= 7) return '近 7 天';
691
- if (currentDays <= 30) return '近 30 天';
692
- return '近 ' + currentDays + ' 天';
693
- }
694
-
695
893
  function updateChartTitles(days) {
696
894
  const label = days <= 7 ? '近 7 天' : (days <= 30 ? '近 30 天' : '近 ' + days + ' 天');
697
895
  const tl = document.getElementById('title-timeline');
698
896
  const tt = document.getElementById('title-token-timeline');
699
897
  const vd = document.getElementById('title-vendor-dist');
898
+ const mt = document.getElementById('title-model-token-timeline');
700
899
  if (tl) tl.textContent = label + ' 请求量趋势';
701
- if (tt) tt.textContent = label + ' Token 量趋势';
900
+ if (tt) tt.textContent = label + ' Token 量趋势(按供应商)';
702
901
  if (vd) vd.textContent = '供应商请求分布(' + label + ')';
902
+ if (mt) mt.textContent = label + ' Token 用量(按 Vendor / 模型)';
703
903
  }
704
904
 
705
905
  // ── 主刷新逻辑 ────────────────────────────────────────────
@@ -716,7 +916,6 @@ async function refresh() {
716
916
  fetchJSON('/api/status'),
717
917
  ]);
718
918
 
719
- // 版本号
720
919
  if (summary.version) {
721
920
  document.getElementById('version-badge').textContent = 'v' + summary.version;
722
921
  }
@@ -729,6 +928,7 @@ async function refresh() {
729
928
  buildTimeline(rows);
730
929
  buildVendorDist(rows);
731
930
  buildTokenTimeline(rows);
931
+ buildModelTokenTimeline(rows);
732
932
 
733
933
  updateFtTable(summary.failover_stats || []);
734
934
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coding-proxy
3
- Version: 0.2.3a2
3
+ Version: 0.2.3a3
4
4
  Summary: A High-Availability, Transparent, and Smart Multi-Vendor Proxy for Claude Code. Support Claude Plans, GitHub Copilot, Google Antigravity, ZAI/GLM, MiniMax, Qwen, Xiaomi, Kimi, Doubao...
5
5
  Project-URL: Source Code, https://github.com/ThreeFish-AI/coding-proxy
6
6
  Project-URL: User Guide, https://github.com/ThreeFish-AI/coding-proxy/blob/master/docs/user-guide.md
@@ -56,7 +56,7 @@ coding/proxy/routing/usage_parser.py,sha256=j4G0ArFduQ2D4Yeuad94DVlwdc-JvSh7SJLV
56
56
  coding/proxy/routing/usage_recorder.py,sha256=pObOrX2yIITTiyojl1fJcqO0yWWpbP4KqsJvFdmlt04,6273
57
57
  coding/proxy/server/__init__.py,sha256=KeH7mEu36v9v27m3VgeSxSeFz9sLTJrxS_EVATJ7Vks,20
58
58
  coding/proxy/server/app.py,sha256=kRGgb772dZu8200LPnn7Nt0IU5oagcbBf_Vc5ynxxzE,5599
59
- coding/proxy/server/dashboard.py,sha256=r2_4-7Jn4hpYhfU7G-3W8FGOgTaRKWQj3PsN8SFajcE,34859
59
+ coding/proxy/server/dashboard.py,sha256=GD1s_k7B-fYaKv5CuDJ73o5XfKQ-eoCB3XXqruph49I,42627
60
60
  coding/proxy/server/factory.py,sha256=w8VFvxoogw9K9sO8MlT6bIP7xM7mR6sCohrZle9y_Gg,9985
61
61
  coding/proxy/server/request_normalizer.py,sha256=XUqpZP42_DJmsoX9aMFVk7oLWDeG3UgXdmCv0A76FN4,12334
62
62
  coding/proxy/server/responses.py,sha256=i0ugnLRNOdRYGHEWxkwsxR35ChmdMQsSaD8AjRluTn4,2167
@@ -80,8 +80,8 @@ coding/proxy/vendors/native_anthropic.py,sha256=SxtM71PDci0gqLqiwCrFnT410SnSoD7F
80
80
  coding/proxy/vendors/token_manager.py,sha256=s10t4Com0jNnKGkPyJ_HpG5SjHrCEJvfArEOAaPKA_k,4189
81
81
  coding/proxy/vendors/xiaomi.py,sha256=E-GcmJBZh7GOtDFonxZmlf0hKRhrlrXzL0IxHFRYcRo,860
82
82
  coding/proxy/vendors/zhipu.py,sha256=3j_rqNFu1CX-B5ugtrL6Y1OeWSy9yiqsVa9Bi1ssaAA,1062
83
- coding_proxy-0.2.3a2.dist-info/METADATA,sha256=YINKvqEA_wFWpR0rK9CHjf96doxPiIDI9WMeS_URVSY,10819
84
- coding_proxy-0.2.3a2.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
85
- coding_proxy-0.2.3a2.dist-info/entry_points.txt,sha256=moIVzt5ho0Wk9B47LOo2SEAbhzuDDHWi-EfM30U0XBg,54
86
- coding_proxy-0.2.3a2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
87
- coding_proxy-0.2.3a2.dist-info/RECORD,,
83
+ coding_proxy-0.2.3a3.dist-info/METADATA,sha256=qEW8vHGSB9gQZhF4OMgK68ZRq2h1wDw0MtUvt8YmvYY,10819
84
+ coding_proxy-0.2.3a3.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
85
+ coding_proxy-0.2.3a3.dist-info/entry_points.txt,sha256=moIVzt5ho0Wk9B47LOo2SEAbhzuDDHWi-EfM30U0XBg,54
86
+ coding_proxy-0.2.3a3.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
87
+ coding_proxy-0.2.3a3.dist-info/RECORD,,