Add prompt input materials and session IDs
This commit is contained in:
@@ -2,8 +2,12 @@ package pipeline
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"mime"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -37,6 +41,7 @@ type RunInput struct {
|
||||
Path string
|
||||
RawInput []byte
|
||||
LLMClient contracts.StructuredLLMClient
|
||||
SessionID string
|
||||
RunID string
|
||||
StartedAt time.Time
|
||||
LLMProfiles []artifacts.LLMProfileManifest
|
||||
@@ -86,6 +91,9 @@ func (r *Runner) Run(ctx context.Context, input RunInput) (RunOutput, error) {
|
||||
if err := source.ValidateDocument(doc); err != nil {
|
||||
return failOutput(output), fmt.Errorf("validate source document: %w", err)
|
||||
}
|
||||
sourceInput := sourceInputMaterial(input.Path, input.RawInput)
|
||||
sessionID := resolvedSessionID(input.SessionID, doc.ID)
|
||||
output.Manifest.Metadata = manifestMetadataWithSessionID(output.Manifest.Metadata, sessionID)
|
||||
output.Manifest.SourceDigests = []string{doc.Digest}
|
||||
|
||||
chunker, err := r.registries.Chunkers.Build(input.Pipeline.Chunk.Module)
|
||||
@@ -94,12 +102,14 @@ func (r *Runner) Run(ctx context.Context, input RunInput) (RunOutput, error) {
|
||||
}
|
||||
attachModuleManifestMetadata(&output, "chunker", chunker)
|
||||
chunkResult, err := chunker.Chunk(ctx, contracts.ChunkRequest{
|
||||
Source: doc,
|
||||
References: CloneReferenceSet(input.Pipeline.ChunkReferences.ReferenceSet),
|
||||
LLMClient: input.LLMClient,
|
||||
LLMProfile: input.Pipeline.Chunk.LLMProfile,
|
||||
Options: cloneOptions(input.Pipeline.Chunk.Options),
|
||||
Metadata: input.Metadata,
|
||||
Source: doc,
|
||||
SourceInput: sourceInput.Clone(),
|
||||
SessionID: sessionID,
|
||||
References: CloneReferenceSet(input.Pipeline.ChunkReferences.ReferenceSet),
|
||||
LLMClient: input.LLMClient,
|
||||
LLMProfile: input.Pipeline.Chunk.LLMProfile,
|
||||
Options: cloneOptions(input.Pipeline.Chunk.Options),
|
||||
Metadata: input.Metadata,
|
||||
})
|
||||
output.Warnings = append(output.Warnings, chunkResult.Warnings...)
|
||||
if err != nil {
|
||||
@@ -115,7 +125,7 @@ func (r *Runner) Run(ctx context.Context, input RunInput) (RunOutput, error) {
|
||||
|
||||
nextCandidateIndex := 0
|
||||
for _, lane := range input.Pipeline.ArtifactLanes {
|
||||
if err := r.runLane(ctx, input, doc, canonicalChunks, lane, &output, &nextCandidateIndex); err != nil {
|
||||
if err := r.runLane(ctx, input, doc, sourceInput, sessionID, canonicalChunks, lane, &output, &nextCandidateIndex); err != nil {
|
||||
return failOutput(output), err
|
||||
}
|
||||
}
|
||||
@@ -154,7 +164,7 @@ func (r *Runner) Run(ctx context.Context, input RunInput) (RunOutput, error) {
|
||||
return output, nil
|
||||
}
|
||||
|
||||
func (r *Runner) runLane(ctx context.Context, input RunInput, doc *source.SourceDocument, chunks []contracts.SourceChunk, lane ResolvedArtifactLane, output *RunOutput, nextCandidateIndex *int) error {
|
||||
func (r *Runner) runLane(ctx context.Context, input RunInput, doc *source.SourceDocument, sourceInput contracts.LLMInputMaterial, sessionID string, chunks []contracts.SourceChunk, lane ResolvedArtifactLane, output *RunOutput, nextCandidateIndex *int) error {
|
||||
extractor, err := r.registries.Extractors.Build(lane.Extract.Module)
|
||||
if err != nil {
|
||||
return fmt.Errorf("build extractor %q for lane %q: %w", lane.Extract.Module, lane.ID, err)
|
||||
@@ -185,13 +195,15 @@ func (r *Runner) runLane(ctx context.Context, input RunInput, doc *source.Source
|
||||
for index := range chunks {
|
||||
chunk := chunks[index]
|
||||
result, err := extractor.Extract(ctx, contracts.ExtractionRequest{
|
||||
Source: doc,
|
||||
Chunk: &chunk,
|
||||
References: CloneReferenceSet(lane.ExtractReferences.ReferenceSet),
|
||||
LLMClient: input.LLMClient,
|
||||
LLMProfile: lane.Extract.LLMProfile,
|
||||
Options: cloneOptions(lane.Extract.Options),
|
||||
Metadata: input.Metadata,
|
||||
Source: doc,
|
||||
Chunk: &chunk,
|
||||
SourceInput: sourceInput.Clone(),
|
||||
SessionID: sessionID,
|
||||
References: CloneReferenceSet(lane.ExtractReferences.ReferenceSet),
|
||||
LLMClient: input.LLMClient,
|
||||
LLMProfile: lane.Extract.LLMProfile,
|
||||
Options: cloneOptions(lane.Extract.Options),
|
||||
Metadata: input.Metadata,
|
||||
})
|
||||
output.Warnings = append(output.Warnings, result.Warnings...)
|
||||
if err != nil {
|
||||
@@ -222,14 +234,16 @@ func (r *Runner) runLane(ctx context.Context, input RunInput, doc *source.Source
|
||||
}
|
||||
|
||||
normalizeResult, err := normalizer.Normalize(ctx, contracts.NormalizeRequest{
|
||||
Source: doc,
|
||||
LaneID: lane.ID,
|
||||
Candidates: mergeResult.Candidates,
|
||||
References: CloneReferenceSet(lane.NormalizeReferences.ReferenceSet),
|
||||
LLMClient: input.LLMClient,
|
||||
LLMProfile: lane.Normalize.LLMProfile,
|
||||
Options: cloneOptions(lane.Normalize.Options),
|
||||
Metadata: input.Metadata,
|
||||
Source: doc,
|
||||
LaneID: lane.ID,
|
||||
Candidates: mergeResult.Candidates,
|
||||
SourceInput: sourceInput.Clone(),
|
||||
SessionID: sessionID,
|
||||
References: CloneReferenceSet(lane.NormalizeReferences.ReferenceSet),
|
||||
LLMClient: input.LLMClient,
|
||||
LLMProfile: lane.Normalize.LLMProfile,
|
||||
Options: cloneOptions(lane.Normalize.Options),
|
||||
Metadata: input.Metadata,
|
||||
})
|
||||
output.Warnings = append(output.Warnings, normalizeResult.Warnings...)
|
||||
if err != nil {
|
||||
@@ -510,6 +524,59 @@ func cloneLLMProfiles(profiles []artifacts.LLMProfileManifest) []artifacts.LLMPr
|
||||
return append([]artifacts.LLMProfileManifest(nil), profiles...)
|
||||
}
|
||||
|
||||
func sourceInputMaterial(inputPath string, content []byte) contracts.LLMInputMaterial {
|
||||
return contracts.NewLLMInputMaterial(
|
||||
"source",
|
||||
sourceInputMediaType(inputPath),
|
||||
content,
|
||||
sourceInputDigest(content),
|
||||
sourceInputOriginURI(inputPath),
|
||||
)
|
||||
}
|
||||
|
||||
func sourceInputMediaType(inputPath string) string {
|
||||
extension := strings.ToLower(filepath.Ext(strings.TrimSpace(inputPath)))
|
||||
if extension == ".json" {
|
||||
return "application/json"
|
||||
}
|
||||
mediaType := mime.TypeByExtension(extension)
|
||||
if strings.TrimSpace(mediaType) == "" {
|
||||
return unknownMediaType
|
||||
}
|
||||
return canonicalMediaType(mediaType)
|
||||
}
|
||||
|
||||
func sourceInputDigest(content []byte) string {
|
||||
sum := sha256.Sum256(content)
|
||||
return "sha256:" + hex.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
func sourceInputOriginURI(inputPath string) string {
|
||||
if strings.TrimSpace(inputPath) == "" {
|
||||
return ""
|
||||
}
|
||||
return fileURI(inputPath)
|
||||
}
|
||||
|
||||
func resolvedSessionID(explicit string, sourceDocumentID string) string {
|
||||
if trimmed := strings.TrimSpace(explicit); trimmed != "" {
|
||||
return trimmed
|
||||
}
|
||||
return strings.TrimSpace(sourceDocumentID)
|
||||
}
|
||||
|
||||
func manifestMetadataWithSessionID(metadata map[string]any, sessionID string) map[string]any {
|
||||
out := cloneMetadata(metadata)
|
||||
if strings.TrimSpace(sessionID) == "" {
|
||||
return out
|
||||
}
|
||||
if out == nil {
|
||||
out = make(map[string]any)
|
||||
}
|
||||
out["session_id"] = sessionID
|
||||
return out
|
||||
}
|
||||
|
||||
func cloneWarnings(warnings []contracts.Warning) []contracts.Warning {
|
||||
if len(warnings) == 0 {
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user