Validate generated text catalog before collection
This commit is contained in:
@@ -16,7 +16,7 @@ The `~` prefix is part of each OpenRouter rolling-alias model ID. The embedded p
|
|||||||
|
|
||||||
## Selection And Active Execution
|
## Selection And Active Execution
|
||||||
|
|
||||||
Before weather collection, Weatherreporter validates the exact prompt version, output contract, and selected profile. A nonblank `promptkit.profile` selects one profile ID for every report in the command; otherwise the prompt's declared default selects it. Promptkit resolves the selected definition in this order:
|
Before weather collection, Weatherreporter validates the report's exact generated-text report/schema/template catalog binding, prompt version, output contract, and selected profile. A nonblank `promptkit.profile` selects one profile ID for every report in the command; otherwise the prompt's declared default selects it. Promptkit resolves the selected definition in this order:
|
||||||
|
|
||||||
1. explicit in-memory profiles used by an embedding consumer or test;
|
1. explicit in-memory profiles used by an embedding consumer or test;
|
||||||
2. the configured `profile_file` or `profile_dir`;
|
2. the configured `profile_file` or `profile_dir`;
|
||||||
@@ -37,8 +37,9 @@ filesystem path causes the requested execution to fail.
|
|||||||
|
|
||||||
## Comparison Execution
|
## Comparison Execution
|
||||||
|
|
||||||
For `compare`, Weatherreporter validates one exact prompt and every explicitly
|
For `compare`, Weatherreporter validates the report's generated-text catalog
|
||||||
selected profile before weather collection. It prepares one deterministic YAML
|
binding, one exact prompt, and every explicitly selected profile before weather
|
||||||
|
collection. It prepares one deterministic YAML
|
||||||
data package, retains immutable copies of the report inputs, and executes every
|
data package, retains immutable copies of the report inputs, and executes every
|
||||||
profile against the same exact data-package bytes. Each profile remains an
|
profile against the same exact data-package bytes. Each profile remains an
|
||||||
independent Promptkit execution: one provider or validation failure does not
|
independent Promptkit execution: one provider or validation failure does not
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ is owned by the [CLI reference](../cli.md) and [operations guide](../operations.
|
|||||||
|
|
||||||
## Single-Report Flow
|
## Single-Report Flow
|
||||||
|
|
||||||
`GenerateDetailed` resolves the requested report and output destination before initializing an optional explicit debug writer. An explicit output file wins; otherwise the configured output directory is used, falling back to the captured working directory. It validates the exact Promptkit prompt and selected profile before collecting weather data. The resolved profile, backend, and model are carried in the active result.
|
`GenerateDetailed` resolves the requested report and output destination before initializing an optional explicit debug writer. An explicit output file wins; otherwise the configured output directory is used, falling back to the captured working directory. It validates the report's generated-text catalog binding, exact Promptkit prompt, and selected profile before collecting weather data. The resolved profile, backend, and model are carried in the active result.
|
||||||
|
|
||||||
The workflow builds facts, a module snapshot, briefing metadata, and the YAML prompt package in memory. It executes Promptkit, validates the returned generated text, builds a render context, and renders Markdown. `fileutil` atomically writes the completed Markdown to the selected output path. Only after that write succeeds does single-report notification run.
|
The workflow builds facts, a module snapshot, briefing metadata, and the YAML prompt package in memory. It executes Promptkit, validates the returned generated text, builds a render context, and renders Markdown. `fileutil` atomically writes the completed Markdown to the selected output path. Only after that write succeeds does single-report notification run.
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ Failures return an active partial result with safe identity, profile, warning, v
|
|||||||
|
|
||||||
## Batches
|
## Batches
|
||||||
|
|
||||||
`RunBatchDetailed` selects an explicit output directory first, otherwise the configured directory and then the captured working directory. It does this before creating at most one explicit debug writer or validating prompt and profile candidates for the selected batch. It collects once, calculates the data-dependent plan, then validates and retains the final output path for every planned report before invoking the same generation core sequentially.
|
`RunBatchDetailed` selects an explicit output directory first, otherwise the configured directory and then the captured working directory. It does this before creating at most one explicit debug writer or validating generated-text catalog, prompt, and profile candidates for the selected batch. It collects once, calculates the data-dependent plan, then validates and retains the final output path for every planned report before invoking the same generation core sequentially.
|
||||||
|
|
||||||
Each item has an independent result. A failed item does not stop later items; successful items retain their published output paths. Per-report notification is suppressed during a batch. Batch notification runs only after every planned report has published successfully. It is skipped when any item failed. Batch result counters count report items only; a batch notification failure is represented by the top-level notification result and still produces a failed batch outcome.
|
Each item has an independent result. A failed item does not stop later items; successful items retain their published output paths. Per-report notification is suppressed during a batch. Batch notification runs only after every planned report has published successfully. It is skipped when any item failed. Batch result counters count report items only; a batch notification failure is represented by the top-level notification result and still produces a failed batch outcome.
|
||||||
|
|
||||||
@@ -23,8 +23,9 @@ Each item has an independent result. A failed item does not stop later items; su
|
|||||||
|
|
||||||
`CompareDetailed` validates ordered explicit profile IDs, resolves the report,
|
`CompareDetailed` validates ordered explicit profile IDs, resolves the report,
|
||||||
and preflights the exact bundle destination before initializing optional prompt
|
and preflights the exact bundle destination before initializing optional prompt
|
||||||
debugging, prompt inspection, or collection. It then inspects the one prompt
|
debugging, prompt inspection, or collection. It then validates the report's
|
||||||
and every selected profile, collects once, and delegates shared report
|
generated-text catalog binding, inspects the one prompt and every selected
|
||||||
|
profile, collects once, and delegates shared report
|
||||||
construction to the prepared-report flow. It does not accept a notifier.
|
construction to the prepared-report flow. It does not accept a notifier.
|
||||||
|
|
||||||
Once the destination is resolved, the partial result retains its absolute
|
Once the destination is resolved, the partial result retains its absolute
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
# Prepared Report Internals
|
# Prepared Report Internals
|
||||||
|
|
||||||
`internal/app` builds a `preparedReport` after collection and before profile
|
`internal/app` validates the report's generated-text catalog binding during
|
||||||
execution. This is the immutable boundary shared by ordinary report generation
|
prompt inspection, before collection, and carries the resulting handler into
|
||||||
and profile comparison; it is not a durable artifact.
|
`preparedReport` construction after collection. This is the immutable boundary
|
||||||
|
shared by ordinary report generation and profile comparison; it is not a
|
||||||
|
durable artifact.
|
||||||
|
|
||||||
Preparation first establishes one `PreparedIdentity` for the report run, report
|
Preparation first establishes one `PreparedIdentity` for the report run, report
|
||||||
and prompt IDs, variant, generation time, units, timezone, valid period,
|
and prompt IDs, variant, generation time, units, timezone, valid period,
|
||||||
@@ -21,5 +23,7 @@ bytes, and only then assembles the resulting logical bundle. The prompt-input
|
|||||||
shape is owned by [prompt-input internals](prompt-input.md); profile execution
|
shape is owned by [prompt-input internals](prompt-input.md); profile execution
|
||||||
semantics are owned by [Promptkit integration](../integrations/promptkit.md).
|
semantics are owned by [Promptkit integration](../integrations/promptkit.md).
|
||||||
|
|
||||||
Preparation failure has no publication side effects. Tests for this boundary
|
Catalog incompatibility stops prompt inspection before weather collection or
|
||||||
cover mutation isolation, byte equality, and reuse by both execution paths.
|
model work. Preparation failure has no publication side effects. Tests for this
|
||||||
|
boundary cover catalog-preflight ordering, mutation isolation, byte equality,
|
||||||
|
and reuse by both execution paths.
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ func compareDetailed(ctx context.Context, req ComparisonRequest, publish compari
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
prepared, err := prepareReport(prepareReportRequest{Config: req.Config, Resolved: resolved, Collection: *collection})
|
prepared, err := prepareReport(prepareReportRequest{Config: req.Config, Resolved: resolved, Collection: *collection, handler: inspection.handler})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return result, fmt.Errorf("prepare comparison report: %w", err)
|
return result, fmt.Errorf("prepare comparison report: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ type prepareReportRequest struct {
|
|||||||
Config config.Config
|
Config config.Config
|
||||||
Resolved report.Resolved
|
Resolved report.Resolved
|
||||||
Collection collect.Result
|
Collection collect.Result
|
||||||
|
handler generatedtext.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
type preparationError struct {
|
type preparationError struct {
|
||||||
@@ -69,11 +70,6 @@ func prepareReport(req prepareReportRequest) (preparedReport, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return preparedReport{}, &preparationError{operation: "marshal data package", err: err}
|
return preparedReport{}, &preparationError{operation: "marshal data package", err: err}
|
||||||
}
|
}
|
||||||
handler, err := generatedtext.LookupDefinition(req.Resolved.Definition)
|
|
||||||
if err != nil {
|
|
||||||
return preparedReport{}, &preparationError{operation: "lookup generated text catalog", err: err}
|
|
||||||
}
|
|
||||||
|
|
||||||
clonedDerived, err := clonePreparedValue(reportFacts.Derived)
|
clonedDerived, err := clonePreparedValue(reportFacts.Derived)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return preparedReport{}, &preparationError{operation: "copy prepared derived facts", err: err}
|
return preparedReport{}, &preparationError{operation: "copy prepared derived facts", err: err}
|
||||||
@@ -93,7 +89,7 @@ func prepareReport(req prepareReportRequest) (preparedReport, error) {
|
|||||||
identity: clonedIdentity,
|
identity: clonedIdentity,
|
||||||
sourceWarnings: append([]weatherdata.SourceWarning(nil), clonedIdentity.SourceWarnings...),
|
sourceWarnings: append([]weatherdata.SourceWarning(nil), clonedIdentity.SourceWarnings...),
|
||||||
dataPackage: append([]byte(nil), serializedDataPackage...),
|
dataPackage: append([]byte(nil), serializedDataPackage...),
|
||||||
handler: handler,
|
handler: req.handler,
|
||||||
}
|
}
|
||||||
return prepared, nil
|
return prepared, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ import (
|
|||||||
|
|
||||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/briefing"
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/briefing"
|
||||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/collect"
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/collect"
|
||||||
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/generatedtext"
|
||||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/module"
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/module"
|
||||||
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/report"
|
||||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/weatherdata"
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/weatherdata"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,7 +24,7 @@ func TestPrepareReportBuildsImmutableDeterministicInputs(t *testing.T) {
|
|||||||
t.Fatalf("ResolveGenerate() error = %v", err)
|
t.Fatalf("ResolveGenerate() error = %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
request := prepareReportRequest{Config: cfg, Resolved: resolved, Collection: collect.Result{Bundle: &bundle}}
|
request := prepareReportRequest{Config: cfg, Resolved: resolved, Collection: collect.Result{Bundle: &bundle}, handler: preparedHandler(t, resolved)}
|
||||||
prepared, err := prepareReport(request)
|
prepared, err := prepareReport(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("prepareReport() error = %v", err)
|
t.Fatalf("prepareReport() error = %v", err)
|
||||||
@@ -73,7 +75,7 @@ func TestPrepareReportProjectsPreparedIdentity(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("ResolveGenerate() error = %v", err)
|
t.Fatalf("ResolveGenerate() error = %v", err)
|
||||||
}
|
}
|
||||||
prepared, err := prepareReport(prepareReportRequest{Config: cfg, Resolved: resolved, Collection: collect.Result{Bundle: &bundle}})
|
prepared, err := prepareReport(prepareReportRequest{Config: cfg, Resolved: resolved, Collection: collect.Result{Bundle: &bundle}, handler: preparedHandler(t, resolved)})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("prepareReport() error = %v", err)
|
t.Fatalf("prepareReport() error = %v", err)
|
||||||
}
|
}
|
||||||
@@ -108,3 +110,12 @@ func TestPrepareReportProjectsPreparedIdentity(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func preparedHandler(t *testing.T, resolved report.Resolved) generatedtext.Handler {
|
||||||
|
t.Helper()
|
||||||
|
handler, err := generatedtext.LookupDefinition(resolved.Definition)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("LookupDefinition() error = %v", err)
|
||||||
|
}
|
||||||
|
return handler
|
||||||
|
}
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ func preparedDailyProfile(t *testing.T) (preparedReport, PromptInspectionResult)
|
|||||||
t.Fatalf("ResolveGenerate() error = %v", err)
|
t.Fatalf("ResolveGenerate() error = %v", err)
|
||||||
}
|
}
|
||||||
bundle := generationBundle(t)
|
bundle := generationBundle(t)
|
||||||
prepared, err := prepareReport(prepareReportRequest{Config: cfg, Resolved: resolved, Collection: collect.Result{Bundle: &bundle}})
|
prepared, err := prepareReport(prepareReportRequest{Config: cfg, Resolved: resolved, Collection: collect.Result{Bundle: &bundle}, handler: preparedHandler(t, resolved)})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("prepareReport() error = %v", err)
|
t.Fatalf("prepareReport() error = %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ func generatePromptReport(ctx context.Context, req promptReportRequest) (*Report
|
|||||||
if result == nil {
|
if result == nil {
|
||||||
result = initialReportResult(req.GenerateRequest, req.Resolved, req.Inspection)
|
result = initialReportResult(req.GenerateRequest, req.Resolved, req.Inspection)
|
||||||
}
|
}
|
||||||
prepared, err := prepareReport(prepareReportRequest{Config: req.Config, Resolved: req.Resolved, Collection: req.Collection})
|
prepared, err := prepareReport(prepareReportRequest{Config: req.Config, Resolved: req.Resolved, Collection: req.Collection, handler: req.Inspection.handler})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return result, generatedPreparationError(req.Resolved, result.RunID, err)
|
return result, generatedPreparationError(req.Resolved, result.RunID, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/comparison"
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/comparison"
|
||||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/config"
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/config"
|
||||||
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/generatedtext"
|
||||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/promptexec"
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/promptexec"
|
||||||
"gitea.maximumdirect.net/eric/weatherreporter/internal/report"
|
"gitea.maximumdirect.net/eric/weatherreporter/internal/report"
|
||||||
)
|
)
|
||||||
@@ -29,6 +30,7 @@ type PromptInspectionResult struct {
|
|||||||
ProfileID string
|
ProfileID string
|
||||||
BackendID string
|
BackendID string
|
||||||
ModelName string
|
ModelName string
|
||||||
|
handler generatedtext.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
// PromptExecutionsInspectionRequest validates all prompt/profile combinations
|
// PromptExecutionsInspectionRequest validates all prompt/profile combinations
|
||||||
@@ -56,6 +58,7 @@ type ComparisonInspectionResult struct {
|
|||||||
PromptVersion string
|
PromptVersion string
|
||||||
PromptHash string
|
PromptHash string
|
||||||
Profiles []ComparisonProfileInspection
|
Profiles []ComparisonProfileInspection
|
||||||
|
handler generatedtext.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
// ComparisonProfileInspection contains one requested profile's safe effective
|
// ComparisonProfileInspection contains one requested profile's safe effective
|
||||||
@@ -91,6 +94,10 @@ func InspectPromptExecutions(ctx context.Context, req PromptExecutionsInspection
|
|||||||
profiles := map[string]promptexec.ProfileInspection{}
|
profiles := map[string]promptexec.ProfileInspection{}
|
||||||
for _, resolved := range req.Resolved {
|
for _, resolved := range req.Resolved {
|
||||||
definition := resolved.Definition
|
definition := resolved.Definition
|
||||||
|
handler, err := generatedtext.LookupDefinition(definition)
|
||||||
|
if err != nil {
|
||||||
|
return nil, promptexec.NewError(promptexec.InvalidConfiguration, "report generated-text catalog is incompatible", err)
|
||||||
|
}
|
||||||
inspection, err := inspectPromptContract(ctx, req.Executor, definition)
|
inspection, err := inspectPromptContract(ctx, req.Executor, definition)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -113,6 +120,7 @@ func InspectPromptExecutions(ctx context.Context, req PromptExecutionsInspection
|
|||||||
results[definition.ID] = PromptInspectionResult{
|
results[definition.ID] = PromptInspectionResult{
|
||||||
PromptID: inspection.PromptID, PromptVersion: inspection.PromptVersion, PromptHash: inspection.PromptHash,
|
PromptID: inspection.PromptID, PromptVersion: inspection.PromptVersion, PromptHash: inspection.PromptHash,
|
||||||
ProfileID: profile.ProfileID, BackendID: profile.BackendID, ModelName: profile.ModelName,
|
ProfileID: profile.ProfileID, BackendID: profile.BackendID, ModelName: profile.ModelName,
|
||||||
|
handler: handler,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return results, nil
|
return results, nil
|
||||||
@@ -130,6 +138,10 @@ func InspectComparisonExecution(ctx context.Context, req ComparisonInspectionReq
|
|||||||
return ComparisonInspectionResult{}, promptexec.NewError(promptexec.InvalidConfiguration, "prompt executor is required", nil)
|
return ComparisonInspectionResult{}, promptexec.NewError(promptexec.InvalidConfiguration, "prompt executor is required", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handler, err := generatedtext.LookupDefinition(req.Resolved.Definition)
|
||||||
|
if err != nil {
|
||||||
|
return ComparisonInspectionResult{}, comparisonInspectionError("comparison generated-text catalog inspection failed", promptexec.NewError(promptexec.InvalidConfiguration, "report generated-text catalog is incompatible", err))
|
||||||
|
}
|
||||||
inspection, err := inspectPromptContract(ctx, req.Executor, req.Resolved.Definition)
|
inspection, err := inspectPromptContract(ctx, req.Executor, req.Resolved.Definition)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ComparisonInspectionResult{}, comparisonInspectionError("comparison prompt inspection failed", err)
|
return ComparisonInspectionResult{}, comparisonInspectionError("comparison prompt inspection failed", err)
|
||||||
@@ -139,6 +151,7 @@ func InspectComparisonExecution(ctx context.Context, req ComparisonInspectionReq
|
|||||||
PromptVersion: inspection.PromptVersion,
|
PromptVersion: inspection.PromptVersion,
|
||||||
PromptHash: inspection.PromptHash,
|
PromptHash: inspection.PromptHash,
|
||||||
Profiles: make([]ComparisonProfileInspection, 0, len(req.ProfileIDs)),
|
Profiles: make([]ComparisonProfileInspection, 0, len(req.ProfileIDs)),
|
||||||
|
handler: handler,
|
||||||
}
|
}
|
||||||
for _, profileID := range req.ProfileIDs {
|
for _, profileID := range req.ProfileIDs {
|
||||||
profile, err := inspectPromptProfile(ctx, req.Executor, profileID, req.LookupEnv)
|
profile, err := inspectPromptProfile(ctx, req.Executor, profileID, req.LookupEnv)
|
||||||
|
|||||||
@@ -110,9 +110,7 @@ func TestInspectPromptExecutionReturnsSafeInspectionError(t *testing.T) {
|
|||||||
|
|
||||||
func TestInspectPromptExecutionsReusesEffectiveProfile(t *testing.T) {
|
func TestInspectPromptExecutionsReusesEffectiveProfile(t *testing.T) {
|
||||||
first := inspectionResolved(t)
|
first := inspectionResolved(t)
|
||||||
second := first
|
second := inspectionResolvedFor(t, report.Today)
|
||||||
second.Definition.ID = report.Today
|
|
||||||
second.Definition.PromptID = "weather.today"
|
|
||||||
executor := &inspectionExecutor{
|
executor := &inspectionExecutor{
|
||||||
prompt: validPromptInspection(first.Definition),
|
prompt: validPromptInspection(first.Definition),
|
||||||
profiles: map[string]promptexec.ProfileInspection{
|
profiles: map[string]promptexec.ProfileInspection{
|
||||||
@@ -132,6 +130,67 @@ func TestInspectPromptExecutionsReusesEffectiveProfile(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPromptInspectionRejectsIncompatibleGeneratedTextCatalogBeforeExecutorWork(t *testing.T) {
|
||||||
|
base := inspectionResolved(t)
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
resolved report.Resolved
|
||||||
|
inspect func(context.Context, report.Resolved, *inspectionExecutor) error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "single report unknown template",
|
||||||
|
resolved: func() report.Resolved {
|
||||||
|
resolved := base
|
||||||
|
resolved.Definition.TemplateID = "unknown"
|
||||||
|
return resolved
|
||||||
|
}(),
|
||||||
|
inspect: func(ctx context.Context, resolved report.Resolved, executor *inspectionExecutor) error {
|
||||||
|
_, err := InspectPromptExecution(ctx, PromptInspectionRequest{Resolved: resolved, Executor: executor})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "batch known pair for another report",
|
||||||
|
resolved: func() report.Resolved {
|
||||||
|
resolved := base
|
||||||
|
resolved.Definition.GeneratedTextSchemaID = "today"
|
||||||
|
resolved.Definition.TemplateID = "today"
|
||||||
|
return resolved
|
||||||
|
}(),
|
||||||
|
inspect: func(ctx context.Context, resolved report.Resolved, executor *inspectionExecutor) error {
|
||||||
|
_, err := InspectPromptExecutions(ctx, PromptExecutionsInspectionRequest{Resolved: []report.Resolved{resolved}, Executor: executor})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "comparison known pair for another report",
|
||||||
|
resolved: func() report.Resolved {
|
||||||
|
resolved := base
|
||||||
|
resolved.Definition.GeneratedTextSchemaID = "today"
|
||||||
|
resolved.Definition.TemplateID = "today"
|
||||||
|
return resolved
|
||||||
|
}(),
|
||||||
|
inspect: func(ctx context.Context, resolved report.Resolved, executor *inspectionExecutor) error {
|
||||||
|
_, err := InspectComparisonExecution(ctx, ComparisonInspectionRequest{Resolved: resolved, ProfileIDs: []string{"weather-light", "weather-deep"}, Executor: executor})
|
||||||
|
return err
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
executor := &inspectionExecutor{}
|
||||||
|
err := test.inspect(context.Background(), test.resolved, executor)
|
||||||
|
if err == nil || promptexec.CategoryOf(err) != promptexec.InvalidConfiguration {
|
||||||
|
t.Fatalf("inspection error/category = %v/%q, want invalid configuration", err, promptexec.CategoryOf(err))
|
||||||
|
}
|
||||||
|
if len(executor.promptRequests) != 0 || len(executor.profileRequests) != 0 || executor.executeRequests != 0 {
|
||||||
|
t.Fatalf("incompatible catalog performed executor work: prompts %#v profiles %#v executions %d", executor.promptRequests, executor.profileRequests, executor.executeRequests)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestInspectComparisonExecutionPreservesOrderedExplicitProfiles(t *testing.T) {
|
func TestInspectComparisonExecutionPreservesOrderedExplicitProfiles(t *testing.T) {
|
||||||
resolved := inspectionResolved(t)
|
resolved := inspectionResolved(t)
|
||||||
executor := &inspectionExecutor{
|
executor := &inspectionExecutor{
|
||||||
@@ -265,12 +324,16 @@ func (e *inspectionExecutor) Execute(context.Context, promptexec.ExecuteRequest,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func inspectionResolved(t *testing.T) report.Resolved {
|
func inspectionResolved(t *testing.T) report.Resolved {
|
||||||
|
return inspectionResolvedFor(t, report.Daily)
|
||||||
|
}
|
||||||
|
|
||||||
|
func inspectionResolvedFor(t *testing.T, id report.ID) report.Resolved {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
resolved, err := report.DefaultRegistry().Resolve(report.Daily, report.ResolveRequest{
|
request := report.ResolveRequest{Now: time.Date(2026, 5, 29, 12, 0, 0, 0, time.UTC), Location: time.UTC}
|
||||||
Now: time.Date(2026, 5, 29, 12, 0, 0, 0, time.UTC),
|
if id == report.Daily {
|
||||||
Date: time.Date(2026, 5, 29, 0, 0, 0, 0, time.UTC),
|
request.Date = time.Date(2026, 5, 29, 0, 0, 0, 0, time.UTC)
|
||||||
Location: time.UTC,
|
}
|
||||||
})
|
resolved, err := report.DefaultRegistry().Resolve(id, request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Resolve() error = %v", err)
|
t.Fatalf("Resolve() error = %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user