89 lines
2.5 KiB
Go
89 lines
2.5 KiB
Go
package usecase
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
|
|
"gitea.maximumdirect.net/eric/promptkit/internal/domain"
|
|
"gitea.maximumdirect.net/eric/promptkit/internal/generation"
|
|
"gitea.maximumdirect.net/eric/promptkit/internal/llm"
|
|
)
|
|
|
|
type smokeHTTPGetter interface {
|
|
GetWithEvidence(context.Context, string, int) (*http.Response, llm.HTTPExecutionEvidence, error)
|
|
GetWithAuthenticationEvidence(context.Context, string, int, domain.ExecutionTarget) (*http.Response, llm.HTTPExecutionEvidence, error)
|
|
}
|
|
|
|
type reachabilityProbe struct {
|
|
client smokeHTTPGetter
|
|
}
|
|
|
|
type reachabilityProbeOutcome struct {
|
|
supported bool
|
|
reason domain.SmokeTestReason
|
|
initialEndpoint string
|
|
reachable *bool
|
|
evidence llm.HTTPExecutionEvidence
|
|
failure domain.ExecutionFailureKind
|
|
}
|
|
|
|
func (p reachabilityProbe) run(
|
|
ctx context.Context,
|
|
endpoint string,
|
|
timeoutSeconds int,
|
|
) (reachabilityProbeOutcome, error) {
|
|
if p.client == nil {
|
|
return reachabilityProbeOutcome{
|
|
reason: domain.SmokeTestReasonHTTPDiagnosticsUnavailable,
|
|
}, nil
|
|
}
|
|
|
|
endpoint, err := endpointWithoutUserInfo(endpoint)
|
|
if err != nil {
|
|
return reachabilityProbeOutcome{}, fmt.Errorf("prepare reachability endpoint: %w", err)
|
|
}
|
|
result := reachabilityProbeOutcome{supported: true, initialEndpoint: endpoint}
|
|
response, evidence, requestErr := p.client.GetWithEvidence(ctx, endpoint, timeoutSeconds)
|
|
if response != nil && response.Body != nil {
|
|
_ = response.Body.Close()
|
|
}
|
|
result.evidence = evidence
|
|
if evidence.ResponseReceived || response != nil {
|
|
result.reachable = boolPointer(true)
|
|
return result, nil
|
|
}
|
|
if evidence.DispatchInvoked {
|
|
result.reachable = boolPointer(false)
|
|
}
|
|
if requestErr != nil {
|
|
result.failure = httpProbeFailure(ctx, requestErr)
|
|
if !evidence.DispatchInvoked {
|
|
return result, fmt.Errorf("execute reachability request: %w", requestErr)
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// Failed I/O can lose context error identity. Check the caller at that boundary,
|
|
// without reclassifying a probe that already established its result.
|
|
func httpProbeFailure(ctx context.Context, err error) domain.ExecutionFailureKind {
|
|
if contextErr := ctx.Err(); contextErr != nil {
|
|
return generation.ClassifyFailure(contextErr)
|
|
}
|
|
return generation.ClassifyFailure(errors.Join(llm.ErrTransport, err))
|
|
}
|
|
|
|
func endpointWithoutUserInfo(endpoint string) (string, error) {
|
|
parsed, err := url.Parse(endpoint)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
parsed.User = nil
|
|
return parsed.String(), nil
|
|
}
|
|
|
|
func boolPointer(value bool) *bool { return &value }
|