protocol-proxy 2.8.2 → 2.9.0

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.
package/public/index.html CHANGED
@@ -24,6 +24,11 @@
24
24
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"></rect><rect x="14" y="3" width="7" height="7"></rect><rect x="14" y="14" width="7" height="7"></rect><rect x="3" y="14" width="7" height="7"></rect></svg>
25
25
  <span>总览</span>
26
26
  </a>
27
+ <a href="#" class="nav-item" data-page="assistant">
28
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path></svg>
29
+ <span>智控助手</span>
30
+ <span class="nav-badge" style="background:var(--accent);color:#fff">AI</span>
31
+ </a>
27
32
  <a href="#" class="nav-item" data-page="proxies">
28
33
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="12 2 2 7 12 12 22 7 12 2"></polygon><polyline points="2 17 12 22 22 17"></polyline><polyline points="2 12 12 17 22 12"></polyline></svg>
29
34
  <span>代理管理</span>
@@ -313,6 +318,73 @@
313
318
  </div>
314
319
  </div>
315
320
 
321
+ <!-- ==================== Assistant Page ==================== -->
322
+ <div class="page" id="page-assistant">
323
+ <div class="assistant-layout">
324
+ <div class="assistant-toolbar">
325
+ <div class="assistant-model-select">
326
+ <label>后端代理</label>
327
+ <select id="assistant-proxy-select">
328
+ <option value="">选择后端代理...</option>
329
+ </select>
330
+ </div>
331
+ <div class="assistant-model-select">
332
+ <label>供应商</label>
333
+ <select id="assistant-provider-select" onchange="onAssistantProviderChange(this.value)">
334
+ <option value="">自动(跟随代理)</option>
335
+ </select>
336
+ </div>
337
+ <div class="assistant-model-select">
338
+ <label>模型</label>
339
+ <select id="assistant-model-select">
340
+ <option value="">自动(跟随代理)</option>
341
+ </select>
342
+ </div>
343
+ <div class="assistant-model-select">
344
+ <label>会话</label>
345
+ <div class="conversation-dropdown" id="conversation-dropdown">
346
+ <button class="conversation-dropdown-trigger" id="conversation-dropdown-trigger" onclick="toggleConversationDropdown()">新会话</button>
347
+ <div class="conversation-dropdown-menu" id="conversation-dropdown-menu"></div>
348
+ </div>
349
+ </div>
350
+ <div class="toolbar-actions">
351
+ <button class="btn btn-sm" onclick="clearAssistantChat()">新会话</button>
352
+ </div>
353
+ </div>
354
+ <div class="assistant-chat" id="assistant-chat">
355
+ <div class="assistant-welcome">
356
+ <div class="assistant-welcome-icon">
357
+ <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path></svg>
358
+ </div>
359
+ <h3>智控助手</h3>
360
+ <p>我是你的 Protocol Proxy 智能助手,可以帮你:</p>
361
+ <ul>
362
+ <li>查询代理和供应商运行状态</li>
363
+ <li>分析日志,定位异常原因</li>
364
+ <li>解读配置并给出优化建议</li>
365
+ <li>自然语言排障与问答</li>
366
+ </ul>
367
+ <p class="assistant-hint">请先选择一个运行中的代理作为对话后端</p>
368
+ </div>
369
+ </div>
370
+ <div class="assistant-context-bar" id="assistant-context-bar" style="display:none">
371
+ <div class="context-bar-track">
372
+ <div class="context-bar-fill" id="context-bar-fill" style="width:0%"></div>
373
+ </div>
374
+ <div class="context-bar-info">
375
+ <span id="context-bar-tokens">0</span> / <span id="context-bar-max">200K</span> tokens
376
+ (<span id="context-bar-percent">0</span>%)
377
+ · <span id="context-bar-messages">0</span> 条消息
378
+ </div>
379
+ <button class="btn btn-sm context-compress-btn" id="context-compress-btn" onclick="compressAssistantContext()" style="display:none">压缩</button>
380
+ </div>
381
+ <div class="assistant-input-area">
382
+ <textarea id="assistant-input" placeholder="输入问题,例如:帮我看看哪个供应商最慢,最近有什么异常?" rows="1"></textarea>
383
+ <button class="btn btn-primary" id="assistant-send-btn" onclick="sendAssistantMessage()" disabled>发送</button>
384
+ </div>
385
+ </div>
386
+ </div>
387
+
316
388
  <!-- ==================== Settings Page ==================== -->
317
389
  <div class="page" id="page-settings">
318
390
  <div class="settings-grid">
@@ -367,6 +439,37 @@
367
439
  </div>
368
440
  </div>
369
441
  </div>
442
+ <div class="panel">
443
+ <div class="panel-header">
444
+ <h3>智控助手</h3>
445
+ </div>
446
+ <div class="settings-body">
447
+ <div class="setting-item">
448
+ <div class="setting-info">
449
+ <div class="setting-name">模型上下文大小</div>
450
+ <div class="setting-desc">设置所用模型的上下文窗口大小(Token),影响压缩触发阈值</div>
451
+ </div>
452
+ <input type="number" id="settings-max-context" value="200000" min="10000" step="10000"
453
+ style="width:120px" onchange="updateMaxContext(this.value)">
454
+ </div>
455
+ <div class="setting-item">
456
+ <div class="setting-info">
457
+ <div class="setting-name">最大工具调用轮次</div>
458
+ <div class="setting-desc">单次对话中模型可连续调用工具的最大轮数,达到上限后模型会自动总结回复</div>
459
+ </div>
460
+ <input type="number" id="settings-max-rounds" value="10" min="1" max="100"
461
+ style="width:120px" onchange="updateMaxRounds(this.value)">
462
+ </div>
463
+ <div class="setting-item">
464
+ <div class="setting-info">
465
+ <div class="setting-name">最大会话数</div>
466
+ <div class="setting-desc">保存的历史会话数量上限,超出时自动删除最早的会话,0 表示不限制</div>
467
+ </div>
468
+ <input type="number" id="settings-max-conversations" value="0" min="0"
469
+ style="width:120px" onchange="updateMaxConversations(this.value)">
470
+ </div>
471
+ </div>
472
+ </div>
370
473
  </div>
371
474
  </div>
372
475
  </main>
package/public/style.css CHANGED
@@ -1784,3 +1784,517 @@ body {
1784
1784
  background: var(--accent-subtle);
1785
1785
  color: var(--accent);
1786
1786
  }
1787
+
1788
+ /* ---------- Assistant Page ---------- */
1789
+ .assistant-layout {
1790
+ display: flex;
1791
+ flex-direction: column;
1792
+ height: calc(100vh - var(--topbar-height) - 48px);
1793
+ gap: 16px;
1794
+ }
1795
+
1796
+ .assistant-toolbar {
1797
+ display: flex;
1798
+ align-items: center;
1799
+ justify-content: space-between;
1800
+ gap: 12px;
1801
+ flex-shrink: 0;
1802
+ }
1803
+
1804
+ .assistant-model-select {
1805
+ display: flex;
1806
+ align-items: center;
1807
+ gap: 8px;
1808
+ }
1809
+
1810
+ .assistant-model-select label {
1811
+ font-size: 12px;
1812
+ color: var(--text-muted);
1813
+ font-weight: 600;
1814
+ text-transform: uppercase;
1815
+ letter-spacing: 0.04em;
1816
+ }
1817
+
1818
+ .assistant-model-select select {
1819
+ height: 32px;
1820
+ padding: 0 10px;
1821
+ background: var(--bg-elevated);
1822
+ border: 1px solid var(--border-default);
1823
+ border-radius: var(--radius-md);
1824
+ color: var(--text-primary);
1825
+ font-size: 13px;
1826
+ font-family: var(--font-body);
1827
+ outline: none;
1828
+ cursor: pointer;
1829
+ }
1830
+
1831
+ .conversation-dropdown {
1832
+ position: relative;
1833
+ }
1834
+ .conversation-dropdown-trigger {
1835
+ height: 32px;
1836
+ padding: 0 10px;
1837
+ background: var(--bg-elevated);
1838
+ border: 1px solid var(--border-default);
1839
+ border-radius: var(--radius-md);
1840
+ color: var(--text-primary);
1841
+ font-size: 13px;
1842
+ font-family: var(--font-body);
1843
+ cursor: pointer;
1844
+ text-align: left;
1845
+ min-width: 180px;
1846
+ max-width: 260px;
1847
+ overflow: hidden;
1848
+ text-overflow: ellipsis;
1849
+ white-space: nowrap;
1850
+ }
1851
+ .conversation-dropdown-menu {
1852
+ display: none;
1853
+ position: absolute;
1854
+ top: 100%;
1855
+ left: 0;
1856
+ z-index: 100;
1857
+ min-width: 280px;
1858
+ max-height: 320px;
1859
+ overflow-y: auto;
1860
+ background: var(--bg-elevated);
1861
+ border: 1px solid var(--border-default);
1862
+ border-radius: var(--radius-md);
1863
+ box-shadow: 0 4px 12px rgba(0,0,0,0.15);
1864
+ margin-top: 4px;
1865
+ }
1866
+ .conversation-dropdown-menu.open {
1867
+ display: block;
1868
+ }
1869
+ .conversation-dropdown-item {
1870
+ display: flex;
1871
+ align-items: center;
1872
+ justify-content: space-between;
1873
+ padding: 8px 10px;
1874
+ cursor: pointer;
1875
+ font-size: 13px;
1876
+ gap: 8px;
1877
+ }
1878
+ .conversation-dropdown-item:hover {
1879
+ background: var(--bg-hover);
1880
+ }
1881
+ .conversation-dropdown-item.active {
1882
+ background: var(--bg-selected);
1883
+ }
1884
+ .conversation-dropdown-item-label {
1885
+ flex: 1;
1886
+ overflow: hidden;
1887
+ text-overflow: ellipsis;
1888
+ white-space: nowrap;
1889
+ color: var(--text-primary);
1890
+ }
1891
+ .conversation-dropdown-item-delete {
1892
+ flex-shrink: 0;
1893
+ width: 20px;
1894
+ height: 20px;
1895
+ display: flex;
1896
+ align-items: center;
1897
+ justify-content: center;
1898
+ border: none;
1899
+ background: transparent;
1900
+ color: var(--text-muted);
1901
+ cursor: pointer;
1902
+ border-radius: 4px;
1903
+ font-size: 14px;
1904
+ line-height: 1;
1905
+ }
1906
+ .conversation-dropdown-item-delete:hover {
1907
+ background: var(--error);
1908
+ color: #fff;
1909
+ }
1910
+
1911
+ .assistant-chat {
1912
+ flex: 1;
1913
+ overflow-y: auto;
1914
+ display: flex;
1915
+ flex-direction: column;
1916
+ gap: 12px;
1917
+ padding: 16px;
1918
+ background: var(--bg-elevated);
1919
+ border: 1px solid var(--border-default);
1920
+ border-radius: var(--radius-lg);
1921
+ min-height: 0;
1922
+ }
1923
+
1924
+ .assistant-message {
1925
+ max-width: 85%;
1926
+ padding: 12px 16px;
1927
+ border-radius: var(--radius-lg);
1928
+ font-size: 14px;
1929
+ line-height: 1.6;
1930
+ word-break: break-word;
1931
+ animation: msg-in 200ms ease;
1932
+ }
1933
+
1934
+ @keyframes msg-in {
1935
+ from { opacity: 0; transform: translateY(4px); }
1936
+ to { opacity: 1; transform: translateY(0); }
1937
+ }
1938
+
1939
+ .assistant-message.user {
1940
+ align-self: flex-end;
1941
+ background: var(--accent);
1942
+ color: #fff;
1943
+ }
1944
+
1945
+ .assistant-message.assistant {
1946
+ align-self: flex-start;
1947
+ background: var(--bg-surface);
1948
+ color: var(--text-secondary);
1949
+ border: 1px solid var(--border-default);
1950
+ }
1951
+
1952
+ .assistant-message.assistant pre {
1953
+ background: var(--bg-base);
1954
+ padding: 12px;
1955
+ border-radius: var(--radius-md);
1956
+ overflow-x: auto;
1957
+ font-family: var(--font-mono);
1958
+ font-size: 12px;
1959
+ margin: 8px 0;
1960
+ border: 1px solid var(--border-subtle);
1961
+ }
1962
+
1963
+ .assistant-message.assistant code {
1964
+ font-family: var(--font-mono);
1965
+ font-size: 12px;
1966
+ background: var(--bg-base);
1967
+ padding: 2px 6px;
1968
+ border-radius: var(--radius-sm);
1969
+ color: var(--text-primary);
1970
+ }
1971
+
1972
+ .assistant-message.assistant p {
1973
+ margin-bottom: 8px;
1974
+ }
1975
+
1976
+ .assistant-message.assistant p:last-child {
1977
+ margin-bottom: 0;
1978
+ }
1979
+
1980
+ .think-block {
1981
+ margin: 8px 0;
1982
+ border: 1px solid var(--border-subtle);
1983
+ border-radius: var(--radius-md);
1984
+ background: var(--bg-base);
1985
+ font-size: 12px;
1986
+ }
1987
+ .think-block summary {
1988
+ padding: 6px 10px;
1989
+ cursor: pointer;
1990
+ color: var(--text-muted);
1991
+ user-select: none;
1992
+ }
1993
+ .think-block summary:hover {
1994
+ color: var(--text-secondary);
1995
+ }
1996
+ .think-content {
1997
+ padding: 8px 10px;
1998
+ border-top: 1px solid var(--border-subtle);
1999
+ color: var(--text-secondary);
2000
+ white-space: pre-wrap;
2001
+ line-height: 1.5;
2002
+ max-height: 300px;
2003
+ overflow-y: auto;
2004
+ }
2005
+
2006
+ .assistant-message.assistant ul,
2007
+ .assistant-message.assistant ol {
2008
+ margin: 8px 0;
2009
+ padding-left: 20px;
2010
+ }
2011
+
2012
+ .assistant-message.assistant li {
2013
+ margin-bottom: 4px;
2014
+ }
2015
+
2016
+ .assistant-message.assistant strong {
2017
+ color: var(--text-primary);
2018
+ font-weight: 600;
2019
+ }
2020
+
2021
+ .assistant-welcome {
2022
+ text-align: center;
2023
+ padding: 40px 20px;
2024
+ color: var(--text-muted);
2025
+ margin: auto;
2026
+ }
2027
+
2028
+ .assistant-welcome-icon {
2029
+ margin-bottom: 16px;
2030
+ color: var(--accent);
2031
+ opacity: 0.7;
2032
+ }
2033
+
2034
+ .assistant-welcome h3 {
2035
+ color: var(--text-primary);
2036
+ font-size: 20px;
2037
+ font-weight: 700;
2038
+ margin-bottom: 12px;
2039
+ font-family: var(--font-display);
2040
+ }
2041
+
2042
+ .assistant-welcome ul {
2043
+ text-align: left;
2044
+ display: inline-block;
2045
+ margin: 12px 0;
2046
+ padding-left: 20px;
2047
+ }
2048
+
2049
+ .assistant-welcome li {
2050
+ margin-bottom: 6px;
2051
+ }
2052
+
2053
+ .assistant-hint {
2054
+ font-size: 12px;
2055
+ color: var(--text-faint);
2056
+ margin-top: 16px;
2057
+ }
2058
+
2059
+ .compression-summary {
2060
+ margin: 12px 24px;
2061
+ padding: 8px 12px;
2062
+ background: var(--bg-elevated);
2063
+ border: 1px solid var(--border-subtle);
2064
+ border-radius: 8px;
2065
+ font-size: 13px;
2066
+ color: var(--text-secondary);
2067
+ }
2068
+ .compression-summary summary {
2069
+ cursor: pointer;
2070
+ font-weight: 500;
2071
+ color: var(--text-primary);
2072
+ }
2073
+ .compression-summary-content {
2074
+ margin-top: 8px;
2075
+ white-space: pre-wrap;
2076
+ line-height: 1.5;
2077
+ }
2078
+
2079
+ /* 上下文占用栏 */
2080
+ .assistant-context-bar {
2081
+ display: flex;
2082
+ align-items: center;
2083
+ gap: 10px;
2084
+ padding: 6px 12px;
2085
+ flex-shrink: 0;
2086
+ border-top: 1px solid var(--border-subtle);
2087
+ }
2088
+
2089
+ .context-bar-track {
2090
+ flex: 1;
2091
+ height: 4px;
2092
+ background: var(--bg-base);
2093
+ border-radius: 2px;
2094
+ overflow: hidden;
2095
+ min-width: 60px;
2096
+ }
2097
+
2098
+ .context-bar-fill {
2099
+ height: 100%;
2100
+ border-radius: 2px;
2101
+ transition: width 0.3s ease, background 0.3s ease;
2102
+ background: var(--success);
2103
+ }
2104
+
2105
+ .context-bar-fill.mid {
2106
+ background: var(--warning);
2107
+ }
2108
+
2109
+ .context-bar-fill.high {
2110
+ background: var(--error);
2111
+ }
2112
+
2113
+ .context-bar-info {
2114
+ font-size: 11px;
2115
+ color: var(--text-muted);
2116
+ font-family: var(--font-mono);
2117
+ white-space: nowrap;
2118
+ }
2119
+
2120
+ .context-compress-btn {
2121
+ white-space: nowrap;
2122
+ color: var(--warning) !important;
2123
+ border-color: var(--warning) !important;
2124
+ }
2125
+
2126
+ .assistant-input-area {
2127
+ display: flex;
2128
+ gap: 8px;
2129
+ align-items: flex-end;
2130
+ flex-shrink: 0;
2131
+ }
2132
+
2133
+ .assistant-input-area textarea {
2134
+ flex: 1;
2135
+ min-height: 44px;
2136
+ max-height: 120px;
2137
+ padding: 10px 14px;
2138
+ background: var(--bg-elevated);
2139
+ border: 1px solid var(--border-default);
2140
+ border-radius: var(--radius-md);
2141
+ color: var(--text-primary);
2142
+ font-family: var(--font-body);
2143
+ font-size: 14px;
2144
+ outline: none;
2145
+ resize: none;
2146
+ line-height: 1.5;
2147
+ transition: all var(--transition-micro);
2148
+ }
2149
+
2150
+ .assistant-input-area textarea:focus {
2151
+ border-color: var(--accent);
2152
+ box-shadow: 0 0 0 3px var(--accent-subtle);
2153
+ }
2154
+
2155
+ .assistant-thinking {
2156
+ align-self: flex-start;
2157
+ display: flex;
2158
+ align-items: center;
2159
+ gap: 6px;
2160
+ padding: 10px 14px;
2161
+ background: var(--bg-surface);
2162
+ border-radius: var(--radius-lg);
2163
+ color: var(--text-muted);
2164
+ font-size: 13px;
2165
+ border: 1px solid var(--border-default);
2166
+ }
2167
+
2168
+ .assistant-message.thinking {
2169
+ display: flex;
2170
+ gap: 5px;
2171
+ align-items: center;
2172
+ padding: 14px 18px;
2173
+ }
2174
+
2175
+ .assistant-dot {
2176
+ width: 6px;
2177
+ height: 6px;
2178
+ border-radius: 50%;
2179
+ background: var(--text-muted);
2180
+ animation: assistant-bounce 1.4s infinite ease-in-out both;
2181
+ }
2182
+
2183
+ .assistant-dot:nth-child(1) { animation-delay: -0.32s; }
2184
+ .assistant-dot:nth-child(2) { animation-delay: -0.16s; }
2185
+
2186
+ @keyframes assistant-bounce {
2187
+ 0%, 80%, 100% { transform: scale(0); }
2188
+ 40% { transform: scale(1); }
2189
+ }
2190
+
2191
+ /* 工具调用块 */
2192
+ .assistant-message.tool-calls {
2193
+ align-self: flex-start;
2194
+ max-width: 85%;
2195
+ background: var(--bg-surface);
2196
+ border: 1px solid var(--accent-border);
2197
+ border-left: 3px solid var(--accent);
2198
+ border-radius: var(--radius-md);
2199
+ padding: 10px 14px;
2200
+ font-size: 13px;
2201
+ color: var(--text-muted);
2202
+ }
2203
+
2204
+ .tool-calls-header {
2205
+ font-size: 11px;
2206
+ font-weight: 600;
2207
+ text-transform: uppercase;
2208
+ letter-spacing: 0.05em;
2209
+ color: var(--accent);
2210
+ margin-bottom: 6px;
2211
+ }
2212
+
2213
+ .tool-call-item {
2214
+ display: flex;
2215
+ align-items: center;
2216
+ gap: 8px;
2217
+ padding: 3px 0;
2218
+ }
2219
+
2220
+ .tool-call-name {
2221
+ font-family: var(--font-mono);
2222
+ font-size: 12px;
2223
+ color: var(--accent);
2224
+ font-weight: 500;
2225
+ }
2226
+
2227
+ .tool-call-args {
2228
+ font-family: var(--font-mono);
2229
+ font-size: 11px;
2230
+ color: var(--text-faint);
2231
+ max-width: 300px;
2232
+ overflow: hidden;
2233
+ text-overflow: ellipsis;
2234
+ white-space: nowrap;
2235
+ }
2236
+
2237
+ /* 工具结果块 */
2238
+ .assistant-message.tool-result {
2239
+ align-self: flex-start;
2240
+ max-width: 85%;
2241
+ background: var(--bg-elevated);
2242
+ border: 1px solid var(--border-subtle);
2243
+ border-left: 3px solid var(--success);
2244
+ border-radius: var(--radius-md);
2245
+ padding: 8px 12px;
2246
+ font-size: 12px;
2247
+ color: var(--text-faint);
2248
+ }
2249
+
2250
+ .assistant-message.tool-result.tool-error {
2251
+ border-left-color: var(--error);
2252
+ }
2253
+
2254
+ .assistant-message.tool-result.tool-error .tool-result-name {
2255
+ color: var(--error);
2256
+ }
2257
+
2258
+ .tool-result-header {
2259
+ display: flex;
2260
+ align-items: center;
2261
+ justify-content: space-between;
2262
+ cursor: pointer;
2263
+ user-select: none;
2264
+ }
2265
+
2266
+ .tool-result-name {
2267
+ font-family: var(--font-mono);
2268
+ font-size: 12px;
2269
+ color: var(--text-secondary);
2270
+ font-weight: 500;
2271
+ }
2272
+
2273
+ .tool-result-toggle {
2274
+ font-size: 11px;
2275
+ color: var(--text-muted);
2276
+ }
2277
+
2278
+ .tool-result-body {
2279
+ display: none;
2280
+ margin-top: 8px;
2281
+ padding: 8px;
2282
+ background: var(--bg-base);
2283
+ border-radius: var(--radius-sm);
2284
+ border: 1px solid var(--border-subtle);
2285
+ max-height: 200px;
2286
+ overflow-y: auto;
2287
+ }
2288
+
2289
+ .tool-result-body.expanded {
2290
+ display: block;
2291
+ }
2292
+
2293
+ .tool-result-body pre {
2294
+ margin: 0;
2295
+ font-family: var(--font-mono);
2296
+ font-size: 11px;
2297
+ white-space: pre-wrap;
2298
+ word-break: break-all;
2299
+ color: var(--text-secondary);
2300
+ }