Clarify semantic reconciliation candidate numbers
This commit is contained in:
@@ -70,18 +70,26 @@ const (
|
||||
LimitExceeded
|
||||
)
|
||||
|
||||
// CandidateMapping relates one model-visible request-local ID to the
|
||||
// CandidateMapping relates one model-visible request-local number to the
|
||||
// corresponding zero-based position in the caller's candidate slice.
|
||||
type CandidateMapping struct {
|
||||
CandidateID int
|
||||
CandidateNumber int
|
||||
CandidatePosition int
|
||||
}
|
||||
|
||||
// CandidateNumberRange is the exact inclusive request-local number range
|
||||
// presented to the model. Ready preparations always use a one-based range.
|
||||
type CandidateNumberRange struct {
|
||||
First int `json:"first"`
|
||||
Last int `json:"last"`
|
||||
}
|
||||
|
||||
// Preparation owns the visible candidate mapping and prompt materials.
|
||||
type Preparation struct {
|
||||
disposition Disposition
|
||||
mappings []CandidateMapping
|
||||
materials contracts.LLMInputSet
|
||||
disposition Disposition
|
||||
candidateNumberRange CandidateNumberRange
|
||||
mappings []CandidateMapping
|
||||
materials contracts.LLMInputSet
|
||||
}
|
||||
|
||||
// Disposition returns the preparation outcome.
|
||||
@@ -94,6 +102,12 @@ func (preparation Preparation) CandidateMappings() []CandidateMapping {
|
||||
return append([]CandidateMapping(nil), preparation.mappings...)
|
||||
}
|
||||
|
||||
// CandidateNumberRange returns the exact request-local range assigned during
|
||||
// preparation. A zero value means that no eligible candidates were present.
|
||||
func (preparation Preparation) CandidateNumberRange() CandidateNumberRange {
|
||||
return preparation.candidateNumberRange
|
||||
}
|
||||
|
||||
// Materials returns independently owned candidate and transcript materials.
|
||||
// It is empty unless Disposition returns Ready.
|
||||
func (preparation Preparation) Materials() contracts.LLMInputSet {
|
||||
@@ -106,13 +120,14 @@ type sourceRange struct {
|
||||
}
|
||||
|
||||
type visibleCandidate struct {
|
||||
CandidateID int `json:"candidate_id"`
|
||||
Label string `json:"label"`
|
||||
SourceRefs []sourceRange `json:"source_refs"`
|
||||
CandidateNumber int `json:"candidate_number"`
|
||||
Label string `json:"label"`
|
||||
SourceRefs []sourceRange `json:"source_refs"`
|
||||
}
|
||||
|
||||
type candidateInput struct {
|
||||
Candidates []visibleCandidate `json:"candidates"`
|
||||
CandidateNumberRange CandidateNumberRange `json:"candidate_number_range"`
|
||||
Candidates []visibleCandidate `json:"candidates"`
|
||||
}
|
||||
|
||||
type transcriptInput struct {
|
||||
@@ -168,17 +183,20 @@ func Prepare(document *source.SourceDocument, candidates []Candidate, limits Lim
|
||||
disposition: InsufficientCandidates,
|
||||
mappings: make([]CandidateMapping, len(prepared)),
|
||||
}
|
||||
if len(prepared) > 0 {
|
||||
result.candidateNumberRange = CandidateNumberRange{First: 1, Last: len(prepared)}
|
||||
}
|
||||
views := make([]visibleCandidate, len(prepared))
|
||||
for index, candidate := range prepared {
|
||||
candidateID := index + 1
|
||||
candidateNumber := index + 1
|
||||
result.mappings[index] = CandidateMapping{
|
||||
CandidateID: candidateID,
|
||||
CandidateNumber: candidateNumber,
|
||||
CandidatePosition: candidate.position,
|
||||
}
|
||||
views[index] = visibleCandidate{
|
||||
CandidateID: candidateID,
|
||||
Label: candidates[candidate.position].Label,
|
||||
SourceRefs: cloneSourceRanges(candidate.references),
|
||||
CandidateNumber: candidateNumber,
|
||||
Label: candidates[candidate.position].Label,
|
||||
SourceRefs: cloneSourceRanges(candidate.references),
|
||||
}
|
||||
}
|
||||
if len(prepared) < 2 {
|
||||
@@ -189,7 +207,7 @@ func Prepare(document *source.SourceDocument, candidates []Candidate, limits Lim
|
||||
return result, nil
|
||||
}
|
||||
|
||||
candidateContent, withinLimit, err := marshalCandidateInput(views, limits.MaximumMaterialBytes)
|
||||
candidateContent, withinLimit, err := marshalCandidateInput(result.candidateNumberRange, views, limits.MaximumMaterialBytes)
|
||||
if err != nil {
|
||||
return Preparation{}, fmt.Errorf("prepare semantic reconciliation: encode candidate material: %w", err)
|
||||
}
|
||||
@@ -302,10 +320,14 @@ func coalesceIntervals(intervals []sourceInterval) []sourceInterval {
|
||||
return coalesced
|
||||
}
|
||||
|
||||
func marshalCandidateInput(candidates []visibleCandidate, maximumBytes int) ([]byte, bool, error) {
|
||||
func marshalCandidateInput(numberRange CandidateNumberRange, candidates []visibleCandidate, maximumBytes int) ([]byte, bool, error) {
|
||||
content := make([]byte, 0, min(maximumBytes, 4096))
|
||||
encodedRange, err := json.Marshal(numberRange)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
var withinLimit bool
|
||||
content, withinLimit = appendWithinLimit(content, maximumBytes, []byte(`{"candidates":[`))
|
||||
content, withinLimit = appendWithinLimit(content, maximumBytes, []byte(`{"candidate_number_range":`), encodedRange, []byte(`,"candidates":[`))
|
||||
if !withinLimit {
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user