Deliver references to eligible runtime targets
This commit is contained in:
@@ -60,10 +60,11 @@ Reference flags are resolved against selected chunk, extractor, and normalizer
|
|||||||
targets before the run starts. Flat slot names are accepted only when exactly
|
targets before the run starts. Flat slot names are accepted only when exactly
|
||||||
one selected target declares that slot. Bound reference files are read before
|
one selected target declares that slot. Bound reference files are read before
|
||||||
pipeline work starts, validated as UTF-8 text, and recorded as provenance for
|
pipeline work starts, validated as UTF-8 text, and recorded as provenance for
|
||||||
the target that declares the slot. Runtime reference content is currently passed
|
the target that declares the slot. Runtime reference content is passed to the
|
||||||
only to lane extractors. Notarius infers reference media types from file
|
chunker, extractor, or normalizer target that declares the slot. Notarius infers
|
||||||
extensions for provenance and for optional slot checks. Reference content is not
|
reference media types from file extensions for provenance and for optional slot
|
||||||
written to diagnostics, logs, errors, or manifests.
|
checks. Reference content is not written to diagnostics, logs, errors, or
|
||||||
|
manifests.
|
||||||
|
|
||||||
Reference binding precedence is:
|
Reference binding precedence is:
|
||||||
|
|
||||||
|
|||||||
@@ -149,10 +149,10 @@ compatibility bindings, and run-time `--reference` or `--without-reference`
|
|||||||
overrides are applied. Config-relative paths are resolved relative to the config
|
overrides are applied. Config-relative paths are resolved relative to the config
|
||||||
file; CLI reference paths are resolved relative to the current working
|
file; CLI reference paths are resolved relative to the current working
|
||||||
directory. Materialized bound files must be UTF-8 text. Materialized reference
|
directory. Materialized bound files must be UTF-8 text. Materialized reference
|
||||||
provenance is recorded for chunk, extractor, and normalizer targets; runtime
|
provenance is recorded for chunk, extractor, and normalizer targets, and runtime
|
||||||
reference content is currently passed only to lane extractors that declare the
|
reference content is passed to the target that declares the slot. Reference
|
||||||
slot. Reference media types are inferred from file extensions, recorded as
|
media types are inferred from file extensions, recorded as canonical base media
|
||||||
canonical base media types, and checked only when a module declares
|
types, and checked only when a module declares
|
||||||
`AcceptedMediaTypes`; unknown extensions are recorded as
|
`AcceptedMediaTypes`; unknown extensions are recorded as
|
||||||
`application/octet-stream`. Reference content is not written to diagnostics,
|
`application/octet-stream`. Reference content is not written to diagnostics,
|
||||||
logs, errors, or manifests.
|
logs, errors, or manifests.
|
||||||
|
|||||||
@@ -32,14 +32,15 @@ compares the canonical base media type inferred from the file extension,
|
|||||||
case-insensitively and without parameters.
|
case-insensitively and without parameters.
|
||||||
|
|
||||||
The resolver materializes reference content for chunk, extractor, and
|
The resolver materializes reference content for chunk, extractor, and
|
||||||
normalizer targets. Runtime delivery is currently implemented only for lane
|
normalizer targets. Runtime delivery uses `contracts.ChunkRequest.References`,
|
||||||
extractors through `contracts.ExtractionRequest.References`. Reference material
|
`contracts.ExtractionRequest.References`, and
|
||||||
is not source evidence and must not be converted into `SourceRef` values. If a
|
`contracts.NormalizeRequest.References`. Reference material is not source
|
||||||
module prompt uses references, load the prompt bundle with the same declared
|
evidence and must not be converted into `SourceRef` values. If a module prompt
|
||||||
slots and render with `RenderUserSystemWithReferences`. Prompt templates may use
|
uses references, load the prompt bundle with the same declared slots and render
|
||||||
the `reference` function for content and the `hasreference` function for
|
with `RenderUserSystemWithReferences`. Prompt templates may use the `reference`
|
||||||
conditional sections. Prompt metadata hashes remain based on template source,
|
function for content and the `hasreference` function for conditional sections.
|
||||||
not rendered reference bytes.
|
Prompt metadata hashes remain based on template source, not rendered reference
|
||||||
|
bytes.
|
||||||
|
|
||||||
Chunk modules receive the structured LLM client through `contracts.ChunkRequest`
|
Chunk modules receive the structured LLM client through `contracts.ChunkRequest`
|
||||||
when they need model-backed chunking. The pipeline runner validates generic
|
when they need model-backed chunking. The pipeline runner validates generic
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ empty bound files. Media-type acceptance is checked only when a slot declares
|
|||||||
`application/octet-stream`. Reference content is omitted from diagnostics and
|
`application/octet-stream`. Reference content is omitted from diagnostics and
|
||||||
manifests. The CLI writes provenance-only resolved reference diagnostics, and
|
manifests. The CLI writes provenance-only resolved reference diagnostics, and
|
||||||
the run manifest records target-stage reference provenance separately from
|
the run manifest records target-stage reference provenance separately from
|
||||||
source digests. Runtime reference content is currently passed only to the
|
source digests. Runtime reference content is passed to the matching chunker,
|
||||||
matching lane extractor through `ExtractionRequest`.
|
extractor, or normalizer request.
|
||||||
|
|
||||||
Prompt bundles can declare reference slots and use `reference` and
|
Prompt bundles can declare reference slots and use `reference` and
|
||||||
`hasreference` template functions. Bundle loading validates string-literal slot
|
`hasreference` template functions. Bundle loading validates string-literal slot
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ func (r *Runner) Run(ctx context.Context, input RunInput) (RunOutput, error) {
|
|||||||
attachModuleManifestMetadata(&output, "chunker", chunker)
|
attachModuleManifestMetadata(&output, "chunker", chunker)
|
||||||
chunkResult, err := chunker.Chunk(ctx, contracts.ChunkRequest{
|
chunkResult, err := chunker.Chunk(ctx, contracts.ChunkRequest{
|
||||||
Source: doc,
|
Source: doc,
|
||||||
|
References: CloneReferenceSet(input.Pipeline.ChunkReferences.ReferenceSet),
|
||||||
LLMClient: input.LLMClient,
|
LLMClient: input.LLMClient,
|
||||||
LLMProfile: input.Pipeline.Chunk.LLMProfile,
|
LLMProfile: input.Pipeline.Chunk.LLMProfile,
|
||||||
Options: cloneOptions(input.Pipeline.Chunk.Options),
|
Options: cloneOptions(input.Pipeline.Chunk.Options),
|
||||||
@@ -224,6 +225,7 @@ func (r *Runner) runLane(ctx context.Context, input RunInput, doc *source.Source
|
|||||||
Source: doc,
|
Source: doc,
|
||||||
LaneID: lane.ID,
|
LaneID: lane.ID,
|
||||||
Candidates: mergeResult.Candidates,
|
Candidates: mergeResult.Candidates,
|
||||||
|
References: CloneReferenceSet(lane.NormalizeReferences.ReferenceSet),
|
||||||
LLMClient: input.LLMClient,
|
LLMClient: input.LLMClient,
|
||||||
LLMProfile: lane.Normalize.LLMProfile,
|
LLMProfile: lane.Normalize.LLMProfile,
|
||||||
Options: cloneOptions(lane.Normalize.Options),
|
Options: cloneOptions(lane.Normalize.Options),
|
||||||
|
|||||||
@@ -566,24 +566,7 @@ func TestRunPassesModuleBindingConfigToStageRequests(t *testing.T) {
|
|||||||
func TestRunPassesLaneReferencesToExtractorRequests(t *testing.T) {
|
func TestRunPassesLaneReferencesToExtractorRequests(t *testing.T) {
|
||||||
modules := defaultRunnerModules()
|
modules := defaultRunnerModules()
|
||||||
pipeline := resolvedPipeline()
|
pipeline := resolvedPipeline()
|
||||||
pipeline.ArtifactLanes[0].ExtractReferences.ReferenceSet = contracts.ReferenceSet{
|
pipeline.ArtifactLanes[0].ExtractReferences.ReferenceSet = testReferenceSet("roster", "reference text")
|
||||||
Slots: map[string]contracts.ResolvedReferenceSlot{
|
|
||||||
"roster": {
|
|
||||||
Slot: contracts.ReferenceSlot{Name: "roster"},
|
|
||||||
Items: []contracts.ReferenceItem{
|
|
||||||
{
|
|
||||||
SlotName: "roster",
|
|
||||||
MediaType: "text/plain; charset=utf-8",
|
|
||||||
Content: []byte("reference text"),
|
|
||||||
Digest: "sha256:test",
|
|
||||||
Origin: contracts.ReferenceOrigin{Type: "file", URI: "file:///tmp/reference.txt"},
|
|
||||||
SizeBytes: int64(len("reference text")),
|
|
||||||
BindingSource: contracts.ReferenceBindingSourceConfig,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := New(newRunnerRegistries(t, modules)).Run(context.Background(), RunInput{Pipeline: pipeline})
|
_, err := New(newRunnerRegistries(t, modules)).Run(context.Background(), RunInput{Pipeline: pipeline})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -601,6 +584,55 @@ func TestRunPassesLaneReferencesToExtractorRequests(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRunPassesChunkReferencesToChunkerRequest(t *testing.T) {
|
||||||
|
modules := defaultRunnerModules()
|
||||||
|
pipeline := resolvedPipeline()
|
||||||
|
pipeline.ChunkReferences.ReferenceSet = testReferenceSet("scene_guide", "chunk reference text")
|
||||||
|
|
||||||
|
_, err := New(newRunnerRegistries(t, modules)).Run(context.Background(), RunInput{Pipeline: pipeline})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Run() error = %v, want nil", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req := modules.chunker.requests[0]
|
||||||
|
item := req.References.Slots["scene_guide"].Items[0]
|
||||||
|
if string(item.Content) != "chunk reference text" {
|
||||||
|
t.Fatalf("chunk reference content = %q, want chunk reference text", item.Content)
|
||||||
|
}
|
||||||
|
item.Content[0] = 'C'
|
||||||
|
if got := string(pipeline.ChunkReferences.ReferenceSet.Slots["scene_guide"].Items[0].Content); got != "chunk reference text" {
|
||||||
|
t.Fatalf("runner mutated chunk reference set content = %q", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunPassesNormalizeReferencesToNormalizerRequest(t *testing.T) {
|
||||||
|
modules := defaultRunnerModules()
|
||||||
|
pipeline := resolvedPipeline()
|
||||||
|
pipeline.ArtifactLanes[0].NormalizeReferences.ReferenceSet = testReferenceSet("normalization_notes", "normalize reference text")
|
||||||
|
|
||||||
|
_, err := New(newRunnerRegistries(t, modules)).Run(context.Background(), RunInput{Pipeline: pipeline})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Run() error = %v, want nil", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req := modules.normalizers["normalize"].requests[0]
|
||||||
|
item := req.References.Slots["normalization_notes"].Items[0]
|
||||||
|
if string(item.Content) != "normalize reference text" {
|
||||||
|
t.Fatalf("normalize reference content = %q, want normalize reference text", item.Content)
|
||||||
|
}
|
||||||
|
item.Content[0] = 'N'
|
||||||
|
if got := string(pipeline.ArtifactLanes[0].NormalizeReferences.ReferenceSet.Slots["normalization_notes"].Items[0].Content); got != "normalize reference text" {
|
||||||
|
t.Fatalf("runner mutated normalize reference set content = %q", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRunAllowsNilLLMClientWhenModulesDoNotUseIt(t *testing.T) {
|
||||||
|
_, err := New(newRunnerRegistries(t, defaultRunnerModules())).Run(context.Background(), RunInput{Pipeline: resolvedPipeline()})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Run() error = %v, want nil with nil LLM client when modules do not use it", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestRunIncludesInputWarnings(t *testing.T) {
|
func TestRunIncludesInputWarnings(t *testing.T) {
|
||||||
modules := defaultRunnerModules()
|
modules := defaultRunnerModules()
|
||||||
warning := contracts.Warning{Scope: "reference", ReasonCode: "empty_reference", Message: "empty reference"}
|
warning := contracts.Warning{Scope: "reference", ReasonCode: "empty_reference", Message: "empty reference"}
|
||||||
@@ -1235,6 +1267,27 @@ func resolvedPipelineWithValidators(validators ...string) ResolvedPipeline {
|
|||||||
return pipeline
|
return pipeline
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testReferenceSet(slotName string, content string) contracts.ReferenceSet {
|
||||||
|
return contracts.ReferenceSet{
|
||||||
|
Slots: map[string]contracts.ResolvedReferenceSlot{
|
||||||
|
slotName: {
|
||||||
|
Slot: contracts.ReferenceSlot{Name: slotName},
|
||||||
|
Items: []contracts.ReferenceItem{
|
||||||
|
{
|
||||||
|
SlotName: slotName,
|
||||||
|
MediaType: "text/plain; charset=utf-8",
|
||||||
|
Content: []byte(content),
|
||||||
|
Digest: "sha256:test",
|
||||||
|
Origin: contracts.ReferenceOrigin{Type: "file", URI: "file:///tmp/reference.txt"},
|
||||||
|
SizeBytes: int64(len(content)),
|
||||||
|
BindingSource: contracts.ReferenceBindingSourceConfig,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type runnerModules struct {
|
type runnerModules struct {
|
||||||
input *runnerInputAdapter
|
input *runnerInputAdapter
|
||||||
chunker *runnerChunker
|
chunker *runnerChunker
|
||||||
|
|||||||
@@ -43,8 +43,8 @@ func TestWalkingSkeletonFixture(t *testing.T) {
|
|||||||
t.Fatalf("ContentType = %q, want application/json", output.OutputFiles[0].ContentType)
|
t.Fatalf("ContentType = %q, want application/json", output.OutputFiles[0].ContentType)
|
||||||
}
|
}
|
||||||
assertStructuralJSONEqual(t, output.OutputFiles[0].Bytes, expectedBytes)
|
assertStructuralJSONEqual(t, output.OutputFiles[0].Bytes, expectedBytes)
|
||||||
if llmClient.calls != 2 {
|
if llmClient.calls != 3 {
|
||||||
t.Fatalf("LLM calls = %d, want chunk count 2", llmClient.calls)
|
t.Fatalf("LLM calls = %d, want extractor calls plus normalizer call", llmClient.calls)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,6 +345,15 @@ func (normalizer walkingSkeletonNormalizer) ReferenceSlots() []contracts.Referen
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (normalizer walkingSkeletonNormalizer) Normalize(ctx context.Context, req contracts.NormalizeRequest) (contracts.NormalizeResult, error) {
|
func (normalizer walkingSkeletonNormalizer) Normalize(ctx context.Context, req contracts.NormalizeRequest) (contracts.NormalizeResult, error) {
|
||||||
|
var response struct {
|
||||||
|
Call int `json:"call"`
|
||||||
|
}
|
||||||
|
if _, err := req.LLMClient.CompleteStructured(ctx, contracts.StructuredCompletionRequest{
|
||||||
|
StageName: "fake/normalize",
|
||||||
|
ResponseSchemaName: "fake_normalize",
|
||||||
|
}, &response); err != nil {
|
||||||
|
return contracts.NormalizeResult{}, err
|
||||||
|
}
|
||||||
return contracts.NormalizeResult{Candidates: req.Candidates}, nil
|
return contracts.NormalizeResult{Candidates: req.Candidates}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user