Code / scripts/ml_training/essay_scoring/features/feature_input.py

scripts/ml_training/essay_scoring/features/feature_input.py

View this file on Codeberg


  1
  """Label-free inputs for ledger-first feature materialization.


  2
  


  3
  Feature extraction needs source text and governed prompt authority, never


  4
  training targets or dataset-specific score columns. This module makes that


  5
  boundary explicit so every producer gives the shared pipeline the same


  6
  materialization inputs.


  7
  """


  8
  


  9
  from __future__ import annotations


  10
  


  11
  from dataclasses import dataclass


  12
  


  13
  from ..datasets.canonical_research_input import (


  14
      CanonicalResearchEssayInputRow,


  15
  )


  16
  from ..datasets.prompt_authority import (


  17
      SemanticPromptAuthority,


  18
      semantic_prompt_authority_for_record,


  19
      validate_semantic_prompt_authority,


  20
  )


  21
  from ..datasets.records import EssayRecord


  22
  


  23
  


  24
  @dataclass(frozen=True)


  25
  class FeaturePipelineInput:


  26
      """One immutable, label-free row accepted by ``FeaturePipeline``.


  27
  


  28
      ``prompt_authority`` carries the governed semantic prompt text and its


  29
      provenance. The mirrored prompt fields make the exact request inputs


  30
      visible without making feature extraction depend on a training record.


  31
      """


  32
  


  33
      record_id: str


  34
      source_text: str


  35
      prompt_label: str


  36
      prompt_text: str


  37
      prompt_authority: SemanticPromptAuthority


  38
  


  39
      def __post_init__(self) -> None:


  40
          """Reject input rows whose visible fields differ from prompt authority."""


  41
  


  42
          if not self.record_id.strip():


  43
              raise ValueError("Feature pipeline input requires a record_id.")


  44
          if not self.source_text.strip():


  45
              raise ValueError("Feature pipeline input requires source_text.")


  46
          validate_semantic_prompt_authority(self.prompt_authority)


  47
          if self.prompt_authority.record_id != self.record_id:


  48
              raise ValueError("Feature pipeline input record_id differs from prompt authority.")


  49
          if self.prompt_authority.prompt_label != self.prompt_label:


  50
              raise ValueError("Feature pipeline input prompt_label differs from prompt authority.")


  51
          if self.prompt_authority.prompt_text != self.prompt_text:


  52
              raise ValueError("Feature pipeline input prompt_text differs from prompt authority.")


  53
  


  54
  


  55
  def feature_pipeline_input_from_essay_record(record: EssayRecord) -> FeaturePipelineInput:


  56
      """Project a current dataset row into the label-free materialization contract."""


  57
  


  58
      prompt_authority = semantic_prompt_authority_for_record(record)


  59
      return FeaturePipelineInput(


  60
          record_id=record.record_id,


  61
          source_text=record.essay,


  62
          prompt_label=prompt_authority.prompt_label,


  63
          prompt_text=prompt_authority.prompt_text,


  64
          prompt_authority=prompt_authority,


  65
      )


  66
  


  67
  


  68
  def feature_pipeline_inputs_from_essay_records(


  69
      records: list[EssayRecord],


  70
  ) -> list[FeaturePipelineInput]:


  71
      """Project current dataset rows before they enter feature materialization."""


  72
  


  73
      return [feature_pipeline_input_from_essay_record(record) for record in records]


  74
  


  75
  


  76
  def feature_pipeline_input_from_canonical_row(


  77
      row: CanonicalResearchEssayInputRow,


  78
  ) -> FeaturePipelineInput:


  79
      """Project one canonical essay row without consulting metadata or score labels."""


  80
  


  81
      authority = SemanticPromptAuthority(


  82
          record_id=row.record_id,


  83
          prompt_label=row.prompt_authority_id,


  84
          prompt_id=row.prompt_authority_id,


  85
          prompt_title=row.prompt_authority_id,


  86
          prompt_text=row.prompt_text,


  87
          prompt_text_sha256=row.prompt_text_sha256,


  88
          authority_kind="canonical_research_input.v1",


  89
          source_id=row.dataset_id,


  90
          source_title=row.dataset_id,


  91
          source_uri="",


  92
          source_page=0,


  93
          source_document_sha256=row.source_artifact_sha256,


  94
          authority_origin="canonical_research_input.v1",


  95
      )


  96
      return feature_pipeline_input_from_canonical_row_with_authority(row=row, authority=authority)


  97
  


  98
  


  99
  def feature_pipeline_input_from_canonical_row_with_authority(


  100
      *,


  101
      row: CanonicalResearchEssayInputRow,


  102
      authority: SemanticPromptAuthority,


  103
  ) -> FeaturePipelineInput:


  104
      """Compose a canonical row only with its exact resolved prompt authority."""


  105
  


  106
      validate_semantic_prompt_authority(authority)


  107
      if authority.record_id != row.record_id:


  108
          raise ValueError("canonical row record_id differs from resolved prompt authority")


  109
      if authority.prompt_id != row.prompt_authority_id:


  110
          raise ValueError("canonical row prompt authority ID differs from resolved authority")


  111
      if authority.prompt_text != row.prompt_text:


  112
          raise ValueError("canonical row prompt text differs from resolved prompt authority")


  113
      if authority.prompt_text_sha256 != row.prompt_text_sha256:


  114
          raise ValueError("canonical row prompt digest differs from resolved prompt authority")


  115
      return FeaturePipelineInput(


  116
          record_id=row.record_id,


  117
          source_text=row.essay_text,


  118
          prompt_label=authority.prompt_label,


  119
          prompt_text=row.prompt_text,


  120
          prompt_authority=authority,


  121
      )