Adopt location registry durable contract

This commit is contained in:
2026-08-05 19:05:42 +00:00
parent 2f61118e78
commit c006b163d5
41 changed files with 196 additions and 196 deletions

View File

@@ -40,7 +40,7 @@ func referenceSlots() []contracts.ReferenceSlot {
Description: "Required normalized location registry used only for location identity grounding, never as occurrence evidence.",
Required: true,
AcceptedMediaTypes: []string{"application/json"},
AcceptedArtifactKinds: []contracts.ArtifactKind{dnd.LocationListKind},
AcceptedArtifactKinds: []contracts.ArtifactKind{dnd.LocationRegistryKind},
MaxBytes: LocationRegistryMaxBytes,
})
sort.Slice(slots, func(left, right int) bool { return slots[left].Name < slots[right].Name })

View File

@@ -111,7 +111,7 @@ func TestExtractRequiresRegistryAndAcceptsEmptyRegistryWithNoOccurrences(t *test
if len(client.requests) != 0 {
t.Fatalf("LLM calls = %d", len(client.requests))
}
empty := dnd.LocationList{Locations: []dnd.Location{}}
empty := dnd.LocationRegistry{Locations: []dnd.Location{}}
references := registryReferences(t, empty)
req := extractionRequest()
req.References = references
@@ -196,7 +196,7 @@ func TestExtractorContractsMetadataAndFailures(t *testing.T) {
slot = candidate
}
}
if !slot.Required || !reflect.DeepEqual(slot.AcceptedArtifactKinds, []contracts.ArtifactKind{dnd.LocationListKind}) || slot.MaxBytes != LocationRegistryMaxBytes {
if !slot.Required || !reflect.DeepEqual(slot.AcceptedArtifactKinds, []contracts.ArtifactKind{dnd.LocationRegistryKind}) || slot.MaxBytes != LocationRegistryMaxBytes {
t.Fatalf("location registry slot = %#v", slot)
}
registry := pipeline.NewExtractorRegistry()
@@ -220,17 +220,17 @@ func TestExtractorContractsMetadataAndFailures(t *testing.T) {
}
}
func locationRegistry(t *testing.T, names ...string) dnd.LocationList {
func locationRegistry(t *testing.T, names ...string) dnd.LocationRegistry {
t.Helper()
locations := make([]dnd.Location, len(names))
for index, name := range names {
refs := []source.SourceRef{{SourceID: "other-session", StartUnitID: index + 1, EndUnitID: index + 1}}
locations[index] = dnd.Location{ID: identity.DeriveID(name, refs), Name: name, SourceRefs: refs}
}
return dnd.LocationList{Locations: locations}
return dnd.LocationRegistry{Locations: locations}
}
func registryReferences(t *testing.T, locations dnd.LocationList) contracts.ReferenceSet {
func registryReferences(t *testing.T, locations dnd.LocationRegistry) contracts.ReferenceSet {
t.Helper()
content, err := locationcodec.New().Encode(locations)
if err != nil {

View File

@@ -47,9 +47,9 @@ func canonicalizeLocation(location *locationResponse, order shared.SourceRefOrde
return order.EarliestValid(refs)
}
func canonicalLocationList(response extractionResponse, sourceID string) dnd.LocationList {
func canonicalLocationRegistry(response extractionResponse, sourceID string) dnd.LocationRegistry {
if response.Locations == nil {
return dnd.LocationList{Locations: nil}
return dnd.LocationRegistry{Locations: nil}
}
locations := make([]dnd.Location, len(response.Locations))
for index, location := range response.Locations {
@@ -60,7 +60,7 @@ func canonicalLocationList(response extractionResponse, sourceID string) dnd.Loc
SourceRefs: refs,
}
}
return dnd.LocationList{Locations: locations}
return dnd.LocationRegistry{Locations: locations}
}
func canonicalSourceRefs(values []locationSourceRefResponse, sourceID string) []source.SourceRef {

View File

@@ -31,7 +31,7 @@ func referenceSlots() []contracts.ReferenceSlot {
return shared.ReferenceSlots(referenceSlotDescriptions)
}
var _ contracts.Extractor[dnd.LocationList] = (*Extractor)(nil)
var _ contracts.Extractor[dnd.LocationRegistry] = (*Extractor)(nil)
var _ contracts.ManifestMetadataProvider = (*Extractor)(nil)
var _ pipeline.CheckpointFingerprintProvider = (*Extractor)(nil)
@@ -95,16 +95,16 @@ func (e *Extractor) CheckpointFingerprints() []pipeline.CheckpointFingerprint {
}
}
func (e *Extractor) Extract(ctx context.Context, req contracts.TypedExtractionRequest) (contracts.TypedExtractionResult[dnd.LocationList], error) {
func (e *Extractor) Extract(ctx context.Context, req contracts.TypedExtractionRequest) (contracts.TypedExtractionResult[dnd.LocationRegistry], error) {
if e == nil {
return contracts.TypedExtractionResult[dnd.LocationList]{}, extractorErrorf("extractor must not be nil")
return contracts.TypedExtractionResult[dnd.LocationRegistry]{}, extractorErrorf("extractor must not be nil")
}
if e.llm == nil {
return contracts.TypedExtractionResult[dnd.LocationList]{}, extractorErrorf("LLM client must not be nil")
return contracts.TypedExtractionResult[dnd.LocationRegistry]{}, extractorErrorf("LLM client must not be nil")
}
sourceInput, err := shared.PrepareChunkExtraction(ctx, req)
if err != nil {
return contracts.TypedExtractionResult[dnd.LocationList]{}, extractorErrorf("%w", err)
return contracts.TypedExtractionResult[dnd.LocationRegistry]{}, extractorErrorf("%w", err)
}
order := shared.NewSourceRefOrder(req.Source)
@@ -114,22 +114,22 @@ func (e *Extractor) Extract(ctx context.Context, req contracts.TypedExtractionRe
ProfileID: req.LLMProfile, SessionID: req.SessionID,
Inputs: shared.PromptInputs(sourceInput, req.References),
}, &response); err != nil {
return contracts.TypedExtractionResult[dnd.LocationList]{}, extractorErrorf("complete structured output: %w", err)
return contracts.TypedExtractionResult[dnd.LocationRegistry]{}, extractorErrorf("complete structured output: %w", err)
}
canonicalizeResponse(&response, order, req.Source.ID)
return contracts.TypedExtractionResult[dnd.LocationList]{Value: canonicalLocationList(response, req.Source.ID)}, nil
return contracts.TypedExtractionResult[dnd.LocationRegistry]{Value: canonicalLocationRegistry(response, req.Source.ID)}, nil
}
func ModuleSpec() pipeline.ModuleSpec {
return pipeline.ModuleSpec{
Key: Key, Stage: pipeline.StageExtract, ExecutionClass: contracts.ExecutionClassLLMBacked,
Requires: append([]string(nil), requiredCapabilities...), Provides: append([]string(nil), providedCapabilities...),
ArtifactKind: dnd.LocationListKind, ReferenceSlots: referenceSlots(),
ArtifactKind: dnd.LocationRegistryKind, ReferenceSlots: referenceSlots(),
}
}
func Register(registry *pipeline.ExtractorRegistry) error {
return pipeline.RegisterExtractorBuilder(registry, ModuleSpec(), validateOptions, func(request pipeline.BuildRequest) (contracts.Extractor[dnd.LocationList], error) {
return pipeline.RegisterExtractorBuilder(registry, ModuleSpec(), validateOptions, func(request pipeline.BuildRequest) (contracts.Extractor[dnd.LocationRegistry], error) {
options, err := DecodeOptions(request.Options)
if err != nil {
return nil, err

View File

@@ -23,7 +23,7 @@ func TestExtractMapsLocationsWithOwnedEvidenceAndDeterministicOrder(t *testing.T
t.Fatalf("Extract() error = %v, want nil", err)
}
refs := []source.SourceRef{{SourceID: "session-locations", StartUnitID: 1, EndUnitID: 1}, {SourceID: "session-locations", StartUnitID: 2, EndUnitID: 2}}
want := dnd.LocationList{Locations: []dnd.Location{
want := dnd.LocationRegistry{Locations: []dnd.Location{
{ID: identity.DeriveID("Old Mill", refs), Name: "Old Mill", SourceRefs: refs},
{ID: identity.DeriveID("The Tavern", []source.SourceRef{{SourceID: "session-locations", StartUnitID: 3, EndUnitID: 3}}), Name: "The Tavern", SourceRefs: []source.SourceRef{{SourceID: "session-locations", StartUnitID: 3, EndUnitID: 3}}},
}}

View File

@@ -18,7 +18,7 @@ func TestModuleRegistrationAndMetadata(t *testing.T) {
if _, err := New(&fakeLocationsLLMClient{}, Options{}, contracts.ReferenceSet{}, contracts.ReferenceSet{}); err == nil || !strings.Contains(err.Error(), "at most one") {
t.Fatalf("New() error = %v, want reference-set rejection", err)
}
want := pipeline.ModuleSpec{Key: Key, Stage: pipeline.StageExtract, ExecutionClass: contracts.ExecutionClassLLMBacked, Requires: []string{"chunks", "source.transcript"}, Provides: []string{"dnd.locations"}, ArtifactKind: dnd.LocationListKind, ReferenceSlots: referenceSlots()}
want := pipeline.ModuleSpec{Key: Key, Stage: pipeline.StageExtract, ExecutionClass: contracts.ExecutionClassLLMBacked, Requires: []string{"chunks", "source.transcript"}, Provides: []string{"dnd.locations"}, ArtifactKind: dnd.LocationRegistryKind, ReferenceSlots: referenceSlots()}
if got := ModuleSpec(); !reflect.DeepEqual(got, want) {
t.Fatalf("ModuleSpec() = %#v, want %#v", got, want)
}