diff --git a/backend/internal/service/admin_service.go b/backend/internal/service/admin_service.go index 013cb04049..023c6acc1f 100644 --- a/backend/internal/service/admin_service.go +++ b/backend/internal/service/admin_service.go @@ -576,6 +576,14 @@ var proxyQualityTargets = []proxyQualityTarget{ http.StatusOK: {}, }, }, + { + Target: "grok", + URL: "https://api.x.ai/v1/models", + Method: http.MethodGet, + AllowedStatuses: map[int]struct{}{ + http.StatusUnauthorized: {}, + }, + }, } const ( diff --git a/backend/internal/service/admin_service_proxy_quality_test.go b/backend/internal/service/admin_service_proxy_quality_test.go index 023b448fb5..2347c2deba 100644 --- a/backend/internal/service/admin_service_proxy_quality_test.go +++ b/backend/internal/service/admin_service_proxy_quality_test.go @@ -93,3 +93,44 @@ func TestRunProxyQualityTarget_AllowedStatusPassForUnauthorized(t *testing.T) { require.Equal(t, http.StatusUnauthorized, item.HTTPStatus) require.Contains(t, item.Message, "目标可达") } + +func TestProxyQualityTargets_IncludesGrok(t *testing.T) { + var grokTarget *proxyQualityTarget + for i := range proxyQualityTargets { + if proxyQualityTargets[i].Target == "grok" { + grokTarget = &proxyQualityTargets[i] + break + } + } + + require.NotNil(t, grokTarget) + require.Equal(t, "https://api.x.ai/v1/models", grokTarget.URL) + require.Equal(t, http.MethodGet, grokTarget.Method) + require.Contains(t, grokTarget.AllowedStatuses, http.StatusUnauthorized) +} + +func TestRunProxyQualityTarget_GrokUnauthorizedPasses(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + t.Errorf("unexpected method: %s", r.Method) + } + w.WriteHeader(http.StatusUnauthorized) + _, _ = w.Write([]byte(`{"error":"unauthorized"}`)) + })) + defer server.Close() + + target := proxyQualityTarget{ + Target: "grok", + URL: server.URL, + Method: http.MethodGet, + AllowedStatuses: map[int]struct{}{ + http.StatusUnauthorized: {}, + }, + } + + item := runProxyQualityTarget(context.Background(), server.Client(), target) + require.Equal(t, "grok", item.Target) + require.Equal(t, "pass", item.Status) + require.Equal(t, http.StatusUnauthorized, item.HTTPStatus) + require.Contains(t, item.Message, "目标可达") +} diff --git a/frontend/src/views/admin/ProxiesView.vue b/frontend/src/views/admin/ProxiesView.vue index 3cf86b52fa..0572cc254b 100644 --- a/frontend/src/views/admin/ProxiesView.vue +++ b/frontend/src/views/admin/ProxiesView.vue @@ -881,21 +881,21 @@ - - - - + + + + - - + - - +
{{ t('admin.proxies.qualityTableTarget') }}{{ t('admin.proxies.qualityTableStatus') }}HTTP{{ t('admin.proxies.qualityTableLatency') }}{{ t('admin.proxies.qualityTableTarget') }}{{ t('admin.proxies.qualityTableStatus') }}HTTP{{ t('admin.proxies.qualityTableLatency') }} {{ t('admin.proxies.qualityTableMessage') }}
{{ qualityTargetLabel(item.target) }} - {{ qualityStatusLabel(item.status) }} + {{ qualityTargetLabel(item.target) }} + {{ qualityStatusLabel(item.status) }} {{ item.http_status ?? '-' }} + {{ item.http_status ?? '-' }} {{ typeof item.latency_ms === 'number' ? `${item.latency_ms}ms` : '-' }} @@ -1777,6 +1777,8 @@ const qualityTargetLabel = (target: string) => { return 'Anthropic' case 'gemini': return 'Gemini' + case 'grok': + return 'Grok' default: return target }