Construct universal modules with decoded options

This commit is contained in:
2026-07-17 06:30:56 +00:00
parent ce3a07512f
commit b949e9bbc0
22 changed files with 290 additions and 158 deletions

View File

@@ -55,6 +55,7 @@ func newPreparedRunner(t *testing.T, registries Registries) preparedRunnerHarnes
func (h preparedRunnerHarness) Run(ctx context.Context, input RunInput) (RunOutput, error) {
h.t.Helper()
input.llmClient = WithDebugLLMRecording(input.llmClient, input.Debug)
prepared, err := Prepare(input.pipeline, h.registries, ModuleDependencies{LLM: input.llmClient})
if err != nil {
return RunOutput{}, err
@@ -541,7 +542,7 @@ func TestRunPreservesChunkMetadataDuringCanonicalization(t *testing.T) {
}
}
func TestRunExecutesChunksAndPassesChunkAndLLMClient(t *testing.T) {
func TestRunExecutesChunksAndUsesConstructedLLMClient(t *testing.T) {
modules := defaultRunnerModules()
llmClient := fakeLLMClient{}
@@ -558,8 +559,8 @@ func TestRunExecutesChunksAndPassesChunkAndLLMClient(t *testing.T) {
if !reflect.DeepEqual(extractor.seenChunkIDs, []string{"chunk-0", "chunk-1"}) {
t.Fatalf("seen chunks = %#v, want both chunks", extractor.seenChunkIDs)
}
if len(modules.chunker.requests) != 1 || modules.chunker.requests[0].LLMClient == nil {
t.Fatalf("chunker LLM client = %#v, want client on chunk request", modules.chunker.requests)
if len(modules.chunker.requests) != 1 || modules.chunker.llmClient == nil {
t.Fatalf("chunker LLM client = %#v, want injected client", modules.chunker.llmClient)
}
if len(extractor.seenLLMClients) != 2 || extractor.seenLLMClients[0] == nil || extractor.seenLLMClients[1] == nil {
t.Fatalf("seen LLM clients = %#v, want client for each chunk", extractor.seenLLMClients)
@@ -721,14 +722,14 @@ func TestRunPassesModuleBindingConfigToStageRequests(t *testing.T) {
if got := modules.input.requests[0].LLMProfile; got != "input-profile" {
t.Fatalf("input LLMProfile = %q, want input-profile", got)
}
if got := modules.input.requests[0].Options["input_option"]; got != "input-value" {
t.Fatalf("input Options = %#v, want input option", modules.input.requests[0].Options)
if got := modules.input.buildOptions["input_option"]; got != "input-value" {
t.Fatalf("input construction options = %#v, want input option", modules.input.buildOptions)
}
if got := modules.chunker.requests[0].LLMProfile; got != "chunk-profile" {
t.Fatalf("chunk LLMProfile = %q, want chunk-profile", got)
}
if got := modules.chunker.requests[0].Options["chunk_option"]; got != "chunk-value" {
t.Fatalf("chunk Options = %#v, want chunk option", modules.chunker.requests[0].Options)
if got := modules.chunker.buildOptions["chunk_option"]; got != "chunk-value" {
t.Fatalf("chunk construction options = %#v, want chunk option", modules.chunker.buildOptions)
}
if got := modules.extractors["extract-alpha"].requests[0].LLMProfile; got != "extract-profile" {
t.Fatalf("extract LLMProfile = %q, want extract-profile", got)
@@ -751,8 +752,8 @@ func TestRunPassesModuleBindingConfigToStageRequests(t *testing.T) {
if got := modules.output.requests[0].LLMProfile; got != "output-profile" {
t.Fatalf("output LLMProfile = %q, want output-profile", got)
}
if got := modules.output.requests[0].Options["output_option"]; got != "output-value" {
t.Fatalf("output Options = %#v, want output option", modules.output.requests[0].Options)
if got := modules.output.buildOptions["output_option"]; got != "output-value" {
t.Fatalf("output construction options = %#v, want output option", modules.output.buildOptions)
}
}
@@ -2034,18 +2035,22 @@ func newRunnerRegistries(t *testing.T, modules *runnerModules) Registries {
ValidatorChains: NewValidatorChainRegistry(),
Outputs: NewOutputEncoderRegistry(),
}
if err := registries.Inputs.Register("input", func() (contracts.InputAdapter, error) {
allowOptions := func(map[string]any) error { return nil }
if err := registries.Inputs.RegisterBuilderWithSpec(defaultModuleSpec("input", StageInput), allowOptions, func(request BuildRequest) (contracts.InputAdapter, error) {
if modules.inputBuildErr != nil {
return nil, modules.inputBuildErr
}
modules.input.buildOptions = cloneOptions(request.Options)
return modules.input, nil
}); err != nil {
t.Fatalf("register input: %v", err)
}
if err := registries.Chunkers.Register("chunk", func() (contracts.Chunker, error) {
if err := registries.Chunkers.RegisterBuilderWithSpec(defaultModuleSpec("chunk", StageChunk), allowOptions, func(request BuildRequest) (contracts.Chunker, error) {
if modules.chunkerBuildErr != nil {
return nil, modules.chunkerBuildErr
}
modules.chunker.buildOptions = cloneOptions(request.Options)
modules.chunker.llmClient = request.Dependencies.LLM
return modules.chunker, nil
}); err != nil {
t.Fatalf("register chunker: %v", err)
@@ -2075,7 +2080,10 @@ func newRunnerRegistries(t *testing.T, modules *runnerModules) Registries {
t.Fatalf("register validator %q: %v", key, err)
}
}
if err := registries.Outputs.Register("output", func() (contracts.OutputEncoder, error) { return modules.output, nil }); err != nil {
if err := registries.Outputs.RegisterBuilderWithSpec(defaultModuleSpec("output", StageOutput), allowOptions, func(request BuildRequest) (contracts.OutputEncoder, error) {
modules.output.buildOptions = cloneOptions(request.Options)
return modules.output, nil
}); err != nil {
t.Fatalf("register output: %v", err)
}
return registries
@@ -2087,6 +2095,7 @@ type runnerInputAdapter struct {
err error
manifestMetadata map[string]any
requests []contracts.ParseRequest
buildOptions map[string]any
}
func (adapter *runnerInputAdapter) Key() string {
@@ -2113,6 +2122,8 @@ type runnerChunker struct {
llmPromptID string
manifestMetadata map[string]any
requests []contracts.ChunkRequest
buildOptions map[string]any
llmClient contracts.StructuredLLMClient
}
func (chunker *runnerChunker) Key() string {
@@ -2133,13 +2144,13 @@ func (chunker *runnerChunker) Chunk(ctx context.Context, req contracts.ChunkRequ
}
return contracts.ChunkResult{}, err
}
if chunker.callLLM && req.LLMClient != nil {
if chunker.callLLM && chunker.llmClient != nil {
promptID := strings.TrimSpace(chunker.llmPromptID)
if promptID == "" {
promptID = "runner.chunk"
}
var out map[string]any
if _, err := req.LLMClient.CompleteStructured(ctx, contracts.StructuredCompletionRequest{
if _, err := chunker.llmClient.CompleteStructured(ctx, contracts.StructuredCompletionRequest{
StageName: chunker.key,
PromptID: promptID,
ProfileID: req.LLMProfile,
@@ -2402,6 +2413,7 @@ type runnerOutputEncoder struct {
err error
manifestMetadata map[string]any
requests []contracts.OutputRequest
buildOptions map[string]any
}
func (encoder *runnerOutputEncoder) Key() string {