Contain provider errors at the LLM adapter
This commit is contained in:
@@ -142,7 +142,7 @@ func (c *PromptKitClient) CompleteStructured(ctx context.Context, req contracts.
|
|||||||
if ctxErr := ctx.Err(); ctxErr != nil {
|
if ctxErr := ctx.Err(); ctxErr != nil {
|
||||||
return contracts.StructuredCompletionResponse{}, ctxErr
|
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()
|
defer prepared.Discard()
|
||||||
preparedDetails := prepared.Details()
|
preparedDetails := prepared.Details()
|
||||||
@@ -169,7 +169,7 @@ func (c *PromptKitClient) CompleteStructured(ctx context.Context, req contracts.
|
|||||||
redactPromptKitError(err),
|
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 {
|
if result == nil {
|
||||||
return contracts.StructuredCompletionResponse{}, fmt.Errorf("run PromptKit prompt %q: %w: empty result", promptID, contracts.ErrInvalidStructuredOutput)
|
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 {
|
if err == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return redactedProviderError{err: err}
|
return sanitizedProviderDiagnostic{message: bearerTokenPattern.ReplaceAllString(err.Error(), "Bearer "+secretReplacement)}
|
||||||
}
|
}
|
||||||
|
|
||||||
type redactedProviderError struct {
|
type sanitizedProviderDiagnostic struct {
|
||||||
err error
|
message string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e redactedProviderError) Error() string {
|
func (e sanitizedProviderDiagnostic) Error() string {
|
||||||
return bearerTokenPattern.ReplaceAllString(e.err.Error(), "Bearer "+secretReplacement)
|
return e.message
|
||||||
}
|
|
||||||
|
|
||||||
func (e redactedProviderError) Unwrap() error {
|
|
||||||
return e.err
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -741,6 +741,11 @@ model: local-model
|
|||||||
!strings.Contains(err.Error(), promptkit.BackendLocal) {
|
!strings.Contains(err.Error(), promptkit.BackendLocal) {
|
||||||
t.Fatalf("CompleteStructured() without registration error = %v, want preparation failure with local backend context", err)
|
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 {
|
if providerCalls.Load() != 1 {
|
||||||
t.Fatalf("provider calls after missing-registration failure = %d, want 1", providerCalls.Load())
|
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) {
|
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
|
var out map[string]any
|
||||||
resp, err := client.CompleteStructured(context.Background(), contracts.StructuredCompletionRequest{
|
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]") {
|
if strings.Contains(err.Error(), "secret-token") || !strings.Contains(err.Error(), "Bearer [REDACTED]") {
|
||||||
t.Fatalf("error = %q, want redacted bearer token", err.Error())
|
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 {
|
if resp.Debug != nil {
|
||||||
t.Fatalf("debug material = %#v, want none for provider failure without result", resp.Debug)
|
t.Fatalf("debug material = %#v, want none for provider failure without result", resp.Debug)
|
||||||
}
|
}
|
||||||
@@ -1214,6 +1235,12 @@ type fakePromptKitLLM struct {
|
|||||||
maxInFlight int32
|
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) {
|
func (f *fakePromptKitLLM) Generate(ctx context.Context, req promptkit.GenerateRequest) (*promptkit.GenerateResponse, error) {
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
f.last = req
|
f.last = req
|
||||||
|
|||||||
Reference in New Issue
Block a user