Simplify diagnostic report ownership
This commit is contained in:
@@ -77,7 +77,8 @@ not retain headers, credentials, raw request or response bodies, or a redirect
|
|||||||
trace. These are observations at Promptkit's RoundTripper boundary, not proof
|
trace. These are observations at Promptkit's RoundTripper boundary, not proof
|
||||||
that bytes reached a peer or that a custom transport did no hidden dispatch.
|
that bytes reached a peer or that a custom transport did no hidden dispatch.
|
||||||
Reachability and named authentication probes use the same configured built-in
|
Reachability and named authentication probes use the same configured built-in
|
||||||
client but discard their response bodies after local recognition. The
|
client. The OpenRouter adapter owns endpoint derivation, bounded account-response
|
||||||
|
recognition, and body closure; the smoke coordinator interprets its evidence. The
|
||||||
[integration contract](../integrations/openai-compatible-chat.md#explicit-smoke-diagnostics)
|
[integration contract](../integrations/openai-compatible-chat.md#explicit-smoke-diagnostics)
|
||||||
owns their observable HTTP semantics.
|
owns their observable HTTP semantics.
|
||||||
|
|
||||||
|
|||||||
@@ -602,6 +602,12 @@ and public smoke tests pass on Go 1.27.3.
|
|||||||
unchanged tri-state facts, neutral outcomes, no ordinary observer events, target
|
unchanged tri-state facts, neutral outcomes, no ordinary observer events, target
|
||||||
redaction, and independent provider maps. Update owning internal documentation.
|
redaction, and independent provider maps. Update owning internal documentation.
|
||||||
|
|
||||||
|
Completion: diagnostic targets detach provider values directly, and unused
|
||||||
|
request/report cloning helpers and their dedicated tests have been removed.
|
||||||
|
Authentication routing was completed in the preceding unit. Focused domain,
|
||||||
|
adapter, smoke, and facade tests pass on Go 1.27.3; internal adapter ownership
|
||||||
|
documentation is updated.
|
||||||
|
|
||||||
## Stage 23: Extract Final-Configuration Record Validators
|
## Stage 23: Extract Final-Configuration Record Validators
|
||||||
|
|
||||||
**Catalog:** 22, validation part. **Files:** configuration validation and tests.
|
**Catalog:** 22, validation part. **Files:** configuration validation and tests.
|
||||||
|
|||||||
@@ -154,105 +154,6 @@ func CloneBackendSmokeTestConfig(config *BackendSmokeTestConfig) *BackendSmokeTe
|
|||||||
return &cloned
|
return &cloned
|
||||||
}
|
}
|
||||||
|
|
||||||
// CloneSmokeTestRequest returns a copy that owns its optional settings.
|
|
||||||
func CloneSmokeTestRequest(request SmokeTestRequest) (SmokeTestRequest, error) {
|
|
||||||
cloned := request
|
|
||||||
cloned.TransientRetries = cloneInt(request.TransientRetries)
|
|
||||||
if request.Execution == nil {
|
|
||||||
return cloned, nil
|
|
||||||
}
|
|
||||||
execution := *request.Execution
|
|
||||||
execution.Temperature = cloneFloat64(request.Execution.Temperature)
|
|
||||||
execution.MaxTokens = cloneInt(request.Execution.MaxTokens)
|
|
||||||
execution.TopP = cloneFloat64(request.Execution.TopP)
|
|
||||||
execution.TimeoutSeconds = cloneInt(request.Execution.TimeoutSeconds)
|
|
||||||
execution.ReasoningEffort = cloneString(request.Execution.ReasoningEffort)
|
|
||||||
var err error
|
|
||||||
execution.ExtraParams, err = jsonvalue.CopyMap(request.Execution.ExtraParams)
|
|
||||||
if err != nil {
|
|
||||||
return SmokeTestRequest{}, err
|
|
||||||
}
|
|
||||||
cloned.Execution = &execution
|
|
||||||
return cloned, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CloneSmokeTestReport returns a copy with independent optional facts and maps.
|
|
||||||
func CloneSmokeTestReport(report SmokeTestReport) (SmokeTestReport, error) {
|
|
||||||
cloned := report
|
|
||||||
cloned.Applicable = cloneBool(report.Applicable)
|
|
||||||
cloned.Facts = cloneSmokeTestFacts(report.Facts)
|
|
||||||
cloned.Timing = cloneSmokeTestTiming(report.Timing)
|
|
||||||
var err error
|
|
||||||
cloned.Target.EffectiveSettings.ExtraParams, err = jsonvalue.CopyMap(report.Target.EffectiveSettings.ExtraParams)
|
|
||||||
if err != nil {
|
|
||||||
return SmokeTestReport{}, err
|
|
||||||
}
|
|
||||||
if report.Attempts != nil {
|
|
||||||
cloned.Attempts = make([]SmokeTestAttempt, len(report.Attempts))
|
|
||||||
for index, attempt := range report.Attempts {
|
|
||||||
cloned.Attempts[index] = attempt
|
|
||||||
cloned.Attempts[index].HTTPStatus = cloneInt(attempt.HTTPStatus)
|
|
||||||
cloned.Attempts[index].Facts = cloneSmokeTestFacts(attempt.Facts)
|
|
||||||
cloned.Attempts[index].Timing = cloneSmokeTestTiming(attempt.Timing)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cloned, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func cloneSmokeTestFacts(facts SmokeTestFacts) SmokeTestFacts {
|
|
||||||
facts.CredentialsSupplied = cloneBool(facts.CredentialsSupplied)
|
|
||||||
facts.EndpointReachable = cloneBool(facts.EndpointReachable)
|
|
||||||
facts.CredentialsAccepted = cloneBool(facts.CredentialsAccepted)
|
|
||||||
facts.ResponseReceived = cloneBool(facts.ResponseReceived)
|
|
||||||
facts.GenerationCompleted = cloneBool(facts.GenerationCompleted)
|
|
||||||
facts.TextReturned = cloneBool(facts.TextReturned)
|
|
||||||
facts.ExpectedTextMatched = cloneBool(facts.ExpectedTextMatched)
|
|
||||||
facts.StructuredOutputRequested = cloneBool(facts.StructuredOutputRequested)
|
|
||||||
facts.ValidJSON = cloneBool(facts.ValidJSON)
|
|
||||||
facts.SchemaMatched = cloneBool(facts.SchemaMatched)
|
|
||||||
return facts
|
|
||||||
}
|
|
||||||
|
|
||||||
func cloneSmokeTestTiming(timing SmokeTestTiming) SmokeTestTiming {
|
|
||||||
timing.CapacityWait = cloneDuration(timing.CapacityWait)
|
|
||||||
timing.RequestDuration = cloneDuration(timing.RequestDuration)
|
|
||||||
timing.RetryDelay = cloneDuration(timing.RetryDelay)
|
|
||||||
timing.EvaluationDuration = cloneDuration(timing.EvaluationDuration)
|
|
||||||
return timing
|
|
||||||
}
|
|
||||||
|
|
||||||
func cloneBool(value *bool) *bool {
|
|
||||||
if value == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
cloned := *value
|
|
||||||
return &cloned
|
|
||||||
}
|
|
||||||
|
|
||||||
func cloneDuration(value *time.Duration) *time.Duration {
|
|
||||||
if value == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
cloned := *value
|
|
||||||
return &cloned
|
|
||||||
}
|
|
||||||
|
|
||||||
func cloneFloat64(value *float64) *float64 {
|
|
||||||
if value == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
cloned := *value
|
|
||||||
return &cloned
|
|
||||||
}
|
|
||||||
|
|
||||||
func cloneInt(value *int) *int {
|
|
||||||
if value == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
cloned := *value
|
|
||||||
return &cloned
|
|
||||||
}
|
|
||||||
|
|
||||||
func cloneString(value *string) *string {
|
func cloneString(value *string) *string {
|
||||||
if value == nil {
|
if value == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
package domain
|
package domain
|
||||||
|
|
||||||
import (
|
import "testing"
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCloneBackendCopiesDiagnosticMetadataAndProviderValues(t *testing.T) {
|
func TestCloneBackendCopiesDiagnosticMetadataAndProviderValues(t *testing.T) {
|
||||||
profile := "profile"
|
profile := "profile"
|
||||||
@@ -40,73 +37,3 @@ func TestCloneBackendCopiesDiagnosticMetadataAndProviderValues(t *testing.T) {
|
|||||||
t.Fatalf("source provider parameter changed through clone: %v", got)
|
t.Fatalf("source provider parameter changed through clone: %v", got)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCloneSmokeTestRequestOwnsOptionalSettings(t *testing.T) {
|
|
||||||
temperature := 0.25
|
|
||||||
retries := 2
|
|
||||||
reasoning := "low"
|
|
||||||
request := SmokeTestRequest{
|
|
||||||
APIKey: "secret",
|
|
||||||
TransientRetries: &retries,
|
|
||||||
Execution: &ExecutionTargetOverride{
|
|
||||||
Temperature: &temperature,
|
|
||||||
ReasoningEffort: &reasoning,
|
|
||||||
ExtraParams: map[string]any{"nested": map[string]any{"value": "original"}},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
cloned, err := CloneSmokeTestRequest(request)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
*cloned.TransientRetries = 3
|
|
||||||
*cloned.Execution.Temperature = 0.5
|
|
||||||
*cloned.Execution.ReasoningEffort = "high"
|
|
||||||
cloned.Execution.ExtraParams["nested"].(map[string]any)["value"] = "changed"
|
|
||||||
|
|
||||||
if retries != 2 || temperature != 0.25 || reasoning != "low" {
|
|
||||||
t.Fatal("cloned request shared optional scalar pointers")
|
|
||||||
}
|
|
||||||
if got := request.Execution.ExtraParams["nested"].(map[string]any)["value"]; got != "original" {
|
|
||||||
t.Fatalf("source provider parameter changed through clone: %v", got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCloneSmokeTestReportOwnsMutableValues(t *testing.T) {
|
|
||||||
applicable := true
|
|
||||||
reachable := true
|
|
||||||
status := 200
|
|
||||||
wait := 3 * time.Millisecond
|
|
||||||
report := SmokeTestReport{
|
|
||||||
Applicable: &applicable,
|
|
||||||
Target: SmokeTestTarget{
|
|
||||||
EffectiveSettings: ExecutionTarget{
|
|
||||||
ExtraParams: map[string]any{"nested": map[string]any{"value": "original"}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Facts: SmokeTestFacts{EndpointReachable: &reachable},
|
|
||||||
Attempts: []SmokeTestAttempt{{
|
|
||||||
HTTPStatus: &status,
|
|
||||||
Facts: SmokeTestFacts{EndpointReachable: &reachable},
|
|
||||||
Timing: SmokeTestTiming{CapacityWait: &wait},
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
|
|
||||||
cloned, err := CloneSmokeTestReport(report)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
*cloned.Applicable = false
|
|
||||||
*cloned.Facts.EndpointReachable = false
|
|
||||||
*cloned.Attempts[0].HTTPStatus = 503
|
|
||||||
*cloned.Attempts[0].Facts.EndpointReachable = false
|
|
||||||
*cloned.Attempts[0].Timing.CapacityWait = time.Second
|
|
||||||
cloned.Target.EffectiveSettings.ExtraParams["nested"].(map[string]any)["value"] = "changed"
|
|
||||||
|
|
||||||
if !applicable || !reachable || status != 200 || wait != 3*time.Millisecond {
|
|
||||||
t.Fatal("cloned report shared optional scalar pointers")
|
|
||||||
}
|
|
||||||
if got := report.Target.EffectiveSettings.ExtraParams["nested"].(map[string]any)["value"]; got != "original" {
|
|
||||||
t.Fatalf("source provider parameter changed through clone: %v", got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"go.maximumdirect.net/promptkit/internal/domain"
|
"go.maximumdirect.net/promptkit/internal/domain"
|
||||||
"go.maximumdirect.net/promptkit/internal/generation"
|
"go.maximumdirect.net/promptkit/internal/generation"
|
||||||
|
"go.maximumdirect.net/promptkit/internal/jsonvalue"
|
||||||
"go.maximumdirect.net/promptkit/internal/llm"
|
"go.maximumdirect.net/promptkit/internal/llm"
|
||||||
"go.maximumdirect.net/promptkit/internal/observation"
|
"go.maximumdirect.net/promptkit/internal/observation"
|
||||||
)
|
)
|
||||||
@@ -438,11 +439,11 @@ func httpExecutionFacts(evidence llm.HTTPExecutionEvidence) domain.SmokeTestFact
|
|||||||
}
|
}
|
||||||
|
|
||||||
func smokeReportTarget(target domain.SmokeTestTarget) (domain.SmokeTestTarget, error) {
|
func smokeReportTarget(target domain.SmokeTestTarget) (domain.SmokeTestTarget, error) {
|
||||||
cloned, err := domain.CloneSmokeTestReport(domain.SmokeTestReport{Target: target})
|
params, err := jsonvalue.CopyMap(target.EffectiveSettings.ExtraParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return domain.SmokeTestTarget{}, fmt.Errorf("clone smoke-test target settings: %w", err)
|
return domain.SmokeTestTarget{}, fmt.Errorf("clone smoke-test target settings: %w", err)
|
||||||
}
|
}
|
||||||
target = cloned.Target
|
target.EffectiveSettings.ExtraParams = params
|
||||||
target.EffectiveSettings.APIKey = ""
|
target.EffectiveSettings.APIKey = ""
|
||||||
target.Endpoint, err = endpointWithoutUserInfo(target.Endpoint)
|
target.Endpoint, err = endpointWithoutUserInfo(target.Endpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -393,3 +393,27 @@ func (b cancelOnCloseSmokeBody) Close() error {
|
|||||||
b.cancel()
|
b.cancel()
|
||||||
return b.ReadCloser.Close()
|
return b.ReadCloser.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSmokeReportTargetOwnsProviderValuesAndRedactsCredentials(t *testing.T) {
|
||||||
|
target := domain.SmokeTestTarget{
|
||||||
|
Endpoint: "https://user:password@example.invalid/key",
|
||||||
|
EffectiveSettings: domain.ExecutionTarget{
|
||||||
|
Endpoint: "https://user:password@example.invalid/v1",
|
||||||
|
APIKey: "secret",
|
||||||
|
ExtraParams: map[string]any{"nested": map[string]any{"value": "original"}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
got, err := smokeReportTarget(target)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
got.EffectiveSettings.ExtraParams["nested"].(map[string]any)["value"] = "changed"
|
||||||
|
if target.EffectiveSettings.ExtraParams["nested"].(map[string]any)["value"] != "original" ||
|
||||||
|
got.EffectiveSettings.APIKey != "" || got.Endpoint != "https://example.invalid/key" || got.EffectiveSettings.Endpoint != "https://example.invalid/v1" {
|
||||||
|
t.Fatalf("report target not detached or redacted: %+v", got)
|
||||||
|
}
|
||||||
|
target.EffectiveSettings.ExtraParams = map[string]any{"invalid": func() {}}
|
||||||
|
if _, err := smokeReportTarget(target); err == nil {
|
||||||
|
t.Fatal("invalid provider values were accepted")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user