Refine agent strategy routing and config handling

This commit is contained in:
lky-spec
2026-04-28 19:37:44 +08:00
parent fc6c42ac11
commit 2d45e7ab15
22 changed files with 1321 additions and 968 deletions

View File

@@ -32,10 +32,13 @@ var (
"no such host",
"stream error", // HTTP/2 stream error
"INTERNAL_ERROR", // Server internal error
"status 502", // Bad Gateway
"status 503", // Service Unavailable
"status 520", // Cloudflare origin error
"status 524", // Cloudflare timeout
"status 429", // Rate limit / upstream gateway throttling
"rate_limit_error",
"upstream_empty_output",
"status 502", // Bad Gateway
"status 503", // Service Unavailable
"status 520", // Cloudflare origin error
"status 524", // Cloudflare timeout
}
// TokenUsageCallback is called after each AI request with token usage info

View File

@@ -345,6 +345,11 @@ func TestClient_IsRetryableError(t *testing.T) {
err: errors.New("connection reset by peer"),
expected: true,
},
{
name: "upstream empty output",
err: errors.New(`API returned error (status 429): {"error":{"code":"upstream_empty_output","message":"Upstream model returned empty output.","type":"rate_limit_error"}}`),
expected: true,
},
{
name: "normal error",
err: errors.New("bad request"),

View File

@@ -410,6 +410,47 @@ func TestClient_CallWithRequest_RetrySleepStopsWhenContextCancelled(t *testing.T
}
}
func TestClient_CallWithRequest_RetriesUpstreamEmptyOutput(t *testing.T) {
mockHTTP := NewMockHTTPClient()
attempts := 0
mockHTTP.ResponseFunc = func(req *http.Request) (*http.Response, error) {
attempts++
if attempts == 1 {
body := `{"error":{"code":"upstream_empty_output","message":"Upstream model returned empty output.","type":"rate_limit_error"}}`
return &http.Response{
StatusCode: http.StatusTooManyRequests,
Body: io.NopCloser(strings.NewReader(body)),
Header: make(http.Header),
}, nil
}
return &http.Response{
StatusCode: http.StatusOK,
Body: io.NopCloser(strings.NewReader(`{"choices":[{"message":{"content":"ok after retry"}}]}`)),
Header: make(http.Header),
}, nil
}
client := NewClient(
WithHTTPClient(mockHTTP.ToHTTPClient()),
WithLogger(NewMockLogger()),
WithAPIKey("sk-test-key"),
WithMaxRetries(2),
WithRetryWaitBase(time.Millisecond),
)
request := NewRequestBuilder().WithUserPrompt("Hello").MustBuild()
result, err := client.CallWithRequest(request)
if err != nil {
t.Fatalf("should retry upstream empty output and succeed: %v", err)
}
if result != "ok after retry" {
t.Fatalf("expected retry result, got %q", result)
}
if attempts != 2 {
t.Fatalf("expected 2 attempts, got %d", attempts)
}
}
func TestClient_CallWithRequest_MultiRound(t *testing.T) {
mockHTTP := NewMockHTTPClient()
mockHTTP.SetSuccessResponse("Multi-round response")