mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-05-17 13:14:44 +08:00
refactor: stabilize runtime and unify config
This commit is contained in:
117
pkg/providers/execution.go
Normal file
117
pkg/providers/execution.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package providers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func newProviderExecutionError(code, message, stage string, retryable bool, source string) *ProviderExecutionError {
|
||||
return &ProviderExecutionError{
|
||||
Code: code,
|
||||
Message: message,
|
||||
Stage: stage,
|
||||
Retryable: retryable,
|
||||
Source: source,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *HTTPProvider) executeJSONAttempts(ctx context.Context, endpoint string, payload interface{}, mutate func(*http.Request, authAttempt), classify func(int, []byte) (oauthFailureReason, bool)) (ProviderExecutionResult, error) {
|
||||
jsonData, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return ProviderExecutionResult{Error: newProviderExecutionError("marshal_failed", err.Error(), "marshal", false, p.providerName)}, fmt.Errorf("failed to marshal request: %w", err)
|
||||
}
|
||||
attempts, err := p.authAttempts(ctx)
|
||||
if err != nil {
|
||||
return ProviderExecutionResult{Error: newProviderExecutionError("auth_unavailable", err.Error(), "auth", false, p.providerName)}, err
|
||||
}
|
||||
var last ProviderExecutionResult
|
||||
for _, attempt := range attempts {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewReader(jsonData))
|
||||
if err != nil {
|
||||
return ProviderExecutionResult{Error: newProviderExecutionError("request_build_failed", err.Error(), "request", false, p.providerName)}, fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Accept", "application/json")
|
||||
applyAttemptAuth(req, attempt)
|
||||
applyAttemptProviderHeaders(req, attempt, p, false)
|
||||
if mutate != nil {
|
||||
mutate(req, attempt)
|
||||
}
|
||||
body, status, ctype, err := p.doJSONAttempt(req, attempt)
|
||||
if err != nil {
|
||||
return ProviderExecutionResult{
|
||||
StatusCode: status,
|
||||
ContentType: ctype,
|
||||
AttemptKind: attempt.kind,
|
||||
Error: newProviderExecutionError("request_failed", err.Error(), "request", false, p.providerName),
|
||||
}, err
|
||||
}
|
||||
reason, retry := classify(status, body)
|
||||
last = ProviderExecutionResult{
|
||||
Body: body,
|
||||
StatusCode: status,
|
||||
ContentType: ctype,
|
||||
AttemptKind: attempt.kind,
|
||||
Retryable: retry,
|
||||
Failure: reason,
|
||||
}
|
||||
if !retry {
|
||||
p.markAttemptSuccess(attempt)
|
||||
return last, nil
|
||||
}
|
||||
applyAttemptFailure(p, attempt, reason, nil)
|
||||
}
|
||||
return last, nil
|
||||
}
|
||||
|
||||
func (p *HTTPProvider) executeStreamAttempts(ctx context.Context, endpoint string, payload interface{}, mutate func(*http.Request, authAttempt), onEvent func(string)) (ProviderExecutionResult, error) {
|
||||
jsonData, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return ProviderExecutionResult{Error: newProviderExecutionError("marshal_failed", err.Error(), "marshal", false, p.providerName)}, fmt.Errorf("failed to marshal request: %w", err)
|
||||
}
|
||||
attempts, err := p.authAttempts(ctx)
|
||||
if err != nil {
|
||||
return ProviderExecutionResult{Error: newProviderExecutionError("auth_unavailable", err.Error(), "auth", false, p.providerName)}, err
|
||||
}
|
||||
var last ProviderExecutionResult
|
||||
for _, attempt := range attempts {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint, bytes.NewReader(jsonData))
|
||||
if err != nil {
|
||||
return ProviderExecutionResult{Error: newProviderExecutionError("request_build_failed", err.Error(), "request", false, p.providerName)}, fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Accept", "text/event-stream")
|
||||
applyAttemptAuth(req, attempt)
|
||||
applyAttemptProviderHeaders(req, attempt, p, true)
|
||||
if mutate != nil {
|
||||
mutate(req, attempt)
|
||||
}
|
||||
body, status, ctype, quotaHit, err := p.doStreamAttempt(req, attempt, onEvent)
|
||||
if err != nil {
|
||||
return ProviderExecutionResult{
|
||||
StatusCode: status,
|
||||
ContentType: ctype,
|
||||
AttemptKind: attempt.kind,
|
||||
Error: newProviderExecutionError("stream_failed", err.Error(), "request", false, p.providerName),
|
||||
}, err
|
||||
}
|
||||
reason, _ := classifyOAuthFailure(status, body)
|
||||
last = ProviderExecutionResult{
|
||||
Body: body,
|
||||
StatusCode: status,
|
||||
ContentType: ctype,
|
||||
AttemptKind: attempt.kind,
|
||||
Retryable: quotaHit,
|
||||
Failure: reason,
|
||||
}
|
||||
if !quotaHit {
|
||||
p.markAttemptSuccess(attempt)
|
||||
return last, nil
|
||||
}
|
||||
applyAttemptFailure(p, attempt, reason, nil)
|
||||
}
|
||||
return last, nil
|
||||
}
|
||||
Reference in New Issue
Block a user