Contain provider errors at the LLM adapter

This commit is contained in:
2026-08-09 00:44:38 +00:00
parent cda7a61b47
commit 37b18edf3d
2 changed files with 35 additions and 12 deletions

View File

@@ -142,7 +142,7 @@ func (c *PromptKitClient) CompleteStructured(ctx context.Context, req contracts.
if ctxErr := ctx.Err(); ctxErr != nil {
return contracts.StructuredCompletionResponse{}, ctxErr
}
return contracts.StructuredCompletionResponse{}, fmt.Errorf("prepare PromptKit prompt %q: %w", promptID, redactPromptKitError(err))
return contracts.StructuredCompletionResponse{}, fmt.Errorf("prepare PromptKit prompt %q: %v", promptID, redactPromptKitError(err))
}
defer prepared.Discard()
preparedDetails := prepared.Details()
@@ -169,7 +169,7 @@ func (c *PromptKitClient) CompleteStructured(ctx context.Context, req contracts.
redactPromptKitError(err),
)
}
return contracts.StructuredCompletionResponse{}, fmt.Errorf("run PromptKit prompt %q: %w", promptID, redactPromptKitError(err))
return contracts.StructuredCompletionResponse{}, fmt.Errorf("run PromptKit prompt %q: %v", promptID, redactPromptKitError(err))
}
if result == nil {
return contracts.StructuredCompletionResponse{}, fmt.Errorf("run PromptKit prompt %q: %w: empty result", promptID, contracts.ErrInvalidStructuredOutput)
@@ -427,17 +427,13 @@ func redactPromptKitError(err error) error {
if err == nil {
return nil
}
return redactedProviderError{err: err}
return sanitizedProviderDiagnostic{message: bearerTokenPattern.ReplaceAllString(err.Error(), "Bearer "+secretReplacement)}
}
type redactedProviderError struct {
err error
type sanitizedProviderDiagnostic struct {
message string
}
func (e redactedProviderError) Error() string {
return bearerTokenPattern.ReplaceAllString(e.err.Error(), "Bearer "+secretReplacement)
}
func (e redactedProviderError) Unwrap() error {
return e.err
func (e sanitizedProviderDiagnostic) Error() string {
return e.message
}

View File

@@ -741,6 +741,11 @@ model: local-model
!strings.Contains(err.Error(), promptkit.BackendLocal) {
t.Fatalf("CompleteStructured() without registration error = %v, want preparation failure with local backend context", err)
}
for _, sentinel := range []error{promptkit.ErrProfileLoad, promptkit.ErrInvalidRequest, promptkit.ErrPromptNotFound} {
if errors.Is(err, sentinel) {
t.Fatalf("preparation failure exposes PromptKit sentinel %v: %v", sentinel, err)
}
}
if providerCalls.Load() != 1 {
t.Fatalf("provider calls after missing-registration failure = %d, want 1", providerCalls.Load())
}
@@ -814,7 +819,8 @@ func TestPromptKitClientDecodeFailureReturnsRawResponse(t *testing.T) {
}
func TestPromptKitClientProviderFailureIncludesContextAndRedactsBearerToken(t *testing.T) {
client := newTestPromptKitClient(t, &fakePromptKitLLM{err: errors.New("provider failed with Bearer secret-token")})
providerErr := &credentialBearingProviderError{}
client := newTestPromptKitClient(t, &fakePromptKitLLM{err: providerErr})
var out map[string]any
resp, err := client.CompleteStructured(context.Background(), contracts.StructuredCompletionRequest{
@@ -836,6 +842,21 @@ func TestPromptKitClientProviderFailureIncludesContextAndRedactsBearerToken(t *t
if strings.Contains(err.Error(), "secret-token") || !strings.Contains(err.Error(), "Bearer [REDACTED]") {
t.Fatalf("error = %q, want redacted bearer token", err.Error())
}
for current := err; current != nil; current = errors.Unwrap(current) {
if strings.Contains(current.Error(), "secret-token") {
t.Fatalf("error chain exposes credential: %v", err)
}
}
if errors.Unwrap(err) != nil {
t.Fatalf("provider failure must not expose a wrapped diagnostic: %v", err)
}
var recoveredProviderErr *credentialBearingProviderError
if errors.As(err, &recoveredProviderErr) {
t.Fatalf("provider error escaped the adapter: %v", err)
}
if errors.Is(err, promptkit.ErrLLMGenerate) {
t.Fatalf("provider failure exposes PromptKit generation sentinel: %v", err)
}
if resp.Debug != nil {
t.Fatalf("debug material = %#v, want none for provider failure without result", resp.Debug)
}
@@ -1214,6 +1235,12 @@ type fakePromptKitLLM struct {
maxInFlight int32
}
type credentialBearingProviderError struct{}
func (*credentialBearingProviderError) Error() string {
return "provider failed with Bearer secret-token"
}
func (f *fakePromptKitLLM) Generate(ctx context.Context, req promptkit.GenerateRequest) (*promptkit.GenerateResponse, error) {
f.mu.Lock()
f.last = req