Accept ConfigKit integers in module options
This commit is contained in:
@@ -7,6 +7,8 @@ import (
|
||||
|
||||
configkit "gitea.maximumdirect.net/eric/ConfigKit"
|
||||
"gitea.maximumdirect.net/eric/notarius/internal/framework/pipeline"
|
||||
units "gitea.maximumdirect.net/eric/notarius/internal/modules/generic/chunk/units"
|
||||
jsonoutput "gitea.maximumdirect.net/eric/notarius/internal/modules/generic/output/json"
|
||||
)
|
||||
|
||||
func TestLoadedConfigConvertsDocumentsAndResolvesRuntimePipeline(t *testing.T) {
|
||||
@@ -91,6 +93,40 @@ pipelines:
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadedConfigPreservesConfigKitIntegerOptionsForModules(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
loaded, err := loadConfigSource(configkit.Bytes("document.yml", []byte(`version: 5
|
||||
pipelines:
|
||||
session:
|
||||
input: {module: input}
|
||||
chunk:
|
||||
module: generic
|
||||
options:
|
||||
max_units: 3
|
||||
overlap_units: 1
|
||||
output:
|
||||
module: json
|
||||
options:
|
||||
evidence_context:
|
||||
enabled: true
|
||||
lanes: [score]
|
||||
window_units: 2
|
||||
`)), LoadOptions{})
|
||||
if err != nil {
|
||||
t.Fatalf("loadConfigSource() error = %v", err)
|
||||
}
|
||||
profile := loaded.Config.Pipelines["session"]
|
||||
chunkOptions, err := units.DecodeOptions(profile.Chunk.Options)
|
||||
if err != nil || chunkOptions.MaxUnits != 3 || chunkOptions.OverlapUnits != 1 {
|
||||
t.Fatalf("chunk options = %#v, %v", chunkOptions, err)
|
||||
}
|
||||
outputOptions, err := jsonoutput.DecodeOptions(profile.Output.Options)
|
||||
if err != nil || !outputOptions.EvidenceContext.Enabled || outputOptions.EvidenceContext.WindowUnits != 2 {
|
||||
t.Fatalf("output options = %#v, %v", outputOptions, err)
|
||||
}
|
||||
}
|
||||
|
||||
func pipelineProfileWithChangedOption(profile pipeline.PipelineProfile) pipeline.PipelineProfile {
|
||||
profile = clonePipelineProfile(profile)
|
||||
nested := profile.Artifacts["score"].Extract.Options["nested"].(map[string]any)
|
||||
|
||||
@@ -69,3 +69,22 @@ func RejectUnknownOptions(options map[string]any, allowed ...string) error {
|
||||
}
|
||||
return fmt.Errorf("unknown options %q", unknown)
|
||||
}
|
||||
|
||||
// IntOption accepts the two integer representations admitted at the module
|
||||
// boundary: native int values from programmatic bindings and int64 values from
|
||||
// ConfigKit dynamic option maps. Callers remain responsible for their own
|
||||
// domain constraints.
|
||||
func IntOption(value any) (int, bool) {
|
||||
switch typed := value.(type) {
|
||||
case int:
|
||||
return typed, true
|
||||
case int64:
|
||||
converted := int(typed)
|
||||
if int64(converted) != typed {
|
||||
return 0, false
|
||||
}
|
||||
return converted, true
|
||||
default:
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ func nonNegativeIntOption(name string, value any) (int, error) {
|
||||
}
|
||||
|
||||
func intOption(name string, value any) (int, error) {
|
||||
got, ok := value.(int)
|
||||
got, ok := pipeline.IntOption(value)
|
||||
if !ok {
|
||||
return 0, chunkerErrorf("%s must be an integer", name)
|
||||
}
|
||||
|
||||
@@ -121,9 +121,19 @@ func TestPlanRejectsInvalidOptions(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeOptionsRejectsNonNativeIntegers(t *testing.T) {
|
||||
func TestDecodeOptionsAcceptsConfigKitIntegers(t *testing.T) {
|
||||
options, err := DecodeOptions(map[string]any{"max_units": int64(2), "overlap_units": int64(1)})
|
||||
if err != nil {
|
||||
t.Fatalf("DecodeOptions() error = %v", err)
|
||||
}
|
||||
if options.MaxUnits != 2 || options.OverlapUnits != 1 {
|
||||
t.Fatalf("DecodeOptions() = %#v", options)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeOptionsRejectsUnsupportedIntegerRepresentations(t *testing.T) {
|
||||
for _, value := range []any{
|
||||
int8(2), int16(2), int32(2), int64(2),
|
||||
int8(2), int16(2), int32(2),
|
||||
uint(2), uint8(2), uint16(2), uint32(2), uint64(2),
|
||||
float32(2), float64(2), json.Number("2"),
|
||||
} {
|
||||
|
||||
@@ -182,7 +182,7 @@ func decodeEvidenceContextPolicy(value any) (pipeline.EvidenceContextPolicy, err
|
||||
}
|
||||
windowUnits := 3
|
||||
if rawWindow, ok := object["window_units"]; ok {
|
||||
value, ok := rawWindow.(int)
|
||||
value, ok := pipeline.IntOption(rawWindow)
|
||||
if !ok {
|
||||
return pipeline.EvidenceContextPolicy{}, encoderErrorf("evidence_context option %q must be an integer", "window_units")
|
||||
}
|
||||
|
||||
@@ -87,6 +87,7 @@ func TestDecodeEvidenceContextOptions(t *testing.T) {
|
||||
{name: "disabled", options: map[string]any{"evidence_context": map[string]any{"enabled": false}}},
|
||||
{name: "enabled default window", options: map[string]any{"evidence_context": map[string]any{"enabled": true, "lanes": []any{"npcs"}}}, want: pipeline.EvidenceContextPolicy{Enabled: true, WindowUnits: 3, LaneIDs: []string{"npcs"}}},
|
||||
{name: "explicit zero window and normalized lanes", options: map[string]any{"evidence_context": map[string]any{"enabled": true, "lanes": []any{" spells ", "npcs"}, "window_units": 0}}, want: pipeline.EvidenceContextPolicy{Enabled: true, WindowUnits: 0, LaneIDs: []string{"npcs", "spells"}}},
|
||||
{name: "ConfigKit window integer", options: map[string]any{"evidence_context": map[string]any{"enabled": true, "lanes": []any{"npcs"}, "window_units": int64(2)}}, want: pipeline.EvidenceContextPolicy{Enabled: true, WindowUnits: 2, LaneIDs: []string{"npcs"}}},
|
||||
{name: "duplicate lanes", options: map[string]any{"evidence_context": map[string]any{"enabled": true, "lanes": []any{"npcs", " npcs "}}}, wantErr: "duplicated"},
|
||||
{name: "unknown nested option", options: map[string]any{"evidence_context": map[string]any{"enabled": false, "extra": true}}, wantErr: "unknown option"},
|
||||
{name: "disabled nested fields", options: map[string]any{"evidence_context": map[string]any{"enabled": false, "lanes": []any{"npcs"}}}, wantErr: "not allowed"},
|
||||
|
||||
Reference in New Issue
Block a user