Spaces:
Running on Zero
Running on Zero
| """Candidate construction for constrained classification prompts.""" | |
| from __future__ import annotations | |
| from pydantic import BaseModel, ConfigDict, Field | |
| from gcmd_classifier.llm.prompts import PromptCandidate | |
| from gcmd_classifier.models import CanonicalConceptRecord | |
| from gcmd_classifier.vocabulary.index import VocabularyIndex | |
| class TopicCandidate(BaseModel): | |
| """Application candidate for one UUID-bearing GCMD Topic.""" | |
| model_config = ConfigDict(extra="forbid", frozen=True) | |
| candidate_id: str = Field(min_length=1) | |
| topic_uuid: str = Field(min_length=1) | |
| prompt_candidate: PromptCandidate | |
| def record_uuid(self) -> str: | |
| """Authoritative UUID for the candidate's Topic record.""" | |
| return self.topic_uuid | |
| class TermCandidate(BaseModel): | |
| """Application candidate for one direct Term beneath a selected Topic.""" | |
| model_config = ConfigDict(extra="forbid", frozen=True) | |
| candidate_id: str = Field(min_length=1) | |
| topic_uuid: str = Field(min_length=1) | |
| term_uuid: str = Field(min_length=1) | |
| prompt_candidate: PromptCandidate | |
| def record_uuid(self) -> str: | |
| """Authoritative UUID for the candidate's Term record.""" | |
| return self.term_uuid | |
| class VariableCandidate(BaseModel): | |
| """Application candidate for one direct Variable child beneath a selected parent.""" | |
| model_config = ConfigDict(extra="forbid", frozen=True) | |
| candidate_id: str = Field(min_length=1) | |
| parent_uuid: str = Field(min_length=1) | |
| variable_uuid: str = Field(min_length=1) | |
| prompt_candidate: PromptCandidate | |
| def record_uuid(self) -> str: | |
| """Authoritative UUID for the candidate's Variable record.""" | |
| return self.variable_uuid | |
| def build_topic_candidates(index: VocabularyIndex) -> tuple[TopicCandidate, ...]: | |
| """Build deterministic prompt candidates from UUID-bearing Topic records only.""" | |
| candidates: list[TopicCandidate] = [] | |
| for position, topic in enumerate(index.topics(), start=1): | |
| _validate_topic_record(topic) | |
| candidate_id = f"topic_{position:04d}" | |
| candidates.append( | |
| TopicCandidate( | |
| candidate_id=candidate_id, | |
| topic_uuid=topic.UUID, | |
| prompt_candidate=PromptCandidate( | |
| candidate_id=candidate_id, | |
| name=topic.name, | |
| level=topic.level, | |
| definition=topic.definition, | |
| canonical_path=topic.canonical_path, | |
| ), | |
| ) | |
| ) | |
| return tuple(candidates) | |
| def build_term_candidates( | |
| index: VocabularyIndex, | |
| *, | |
| topic_uuid: str, | |
| ) -> tuple[TermCandidate, ...]: | |
| """Build deterministic prompt candidates from direct Term children of one Topic.""" | |
| topic = index.get(topic_uuid) | |
| _validate_topic_record(topic) | |
| candidates: list[TermCandidate] = [] | |
| for position, term in enumerate(index.terms_for_topic(topic_uuid), start=1): | |
| _validate_term_record(term, topic_uuid=topic_uuid, index=index) | |
| candidate_id = f"term_{position:04d}" | |
| candidates.append( | |
| TermCandidate( | |
| candidate_id=candidate_id, | |
| topic_uuid=topic_uuid, | |
| term_uuid=term.UUID, | |
| prompt_candidate=PromptCandidate( | |
| candidate_id=candidate_id, | |
| name=term.name, | |
| level=term.level, | |
| definition=term.definition, | |
| canonical_path=term.canonical_path, | |
| parent_context=topic.canonical_path, | |
| ), | |
| ) | |
| ) | |
| return tuple(candidates) | |
| def build_variable_candidates( | |
| index: VocabularyIndex, | |
| *, | |
| parent_uuid: str, | |
| ) -> tuple[VariableCandidate, ...]: | |
| """Build deterministic prompt candidates from direct Variable children of one parent.""" | |
| parent = index.get(parent_uuid) | |
| _validate_variable_parent_record(parent) | |
| candidates: list[VariableCandidate] = [] | |
| for position, variable in enumerate(index.variables_for_parent(parent_uuid), start=1): | |
| _validate_variable_child_record(variable, parent_uuid=parent_uuid, index=index) | |
| candidate_id = f"variable_{position:04d}" | |
| candidates.append( | |
| VariableCandidate( | |
| candidate_id=candidate_id, | |
| parent_uuid=parent_uuid, | |
| variable_uuid=variable.UUID, | |
| prompt_candidate=PromptCandidate( | |
| candidate_id=candidate_id, | |
| name=variable.name, | |
| level=variable.level, | |
| definition=variable.definition, | |
| canonical_path=variable.canonical_path, | |
| parent_context=parent.canonical_path, | |
| ), | |
| ) | |
| ) | |
| return tuple(candidates) | |
| def validate_variable_candidate_relationship( | |
| candidate: VariableCandidate, | |
| *, | |
| selected_parent_uuid: str, | |
| index: VocabularyIndex, | |
| ) -> None: | |
| """Validate that a Variable candidate is a direct child of the selected parent.""" | |
| if candidate.parent_uuid != selected_parent_uuid: | |
| raise ValueError( | |
| f"Variable candidate {candidate.candidate_id!r} was built for parent " | |
| f"{candidate.parent_uuid!r}, not selected parent {selected_parent_uuid!r}." | |
| ) | |
| variable = index.get(candidate.variable_uuid) | |
| _validate_variable_child_record(variable, parent_uuid=selected_parent_uuid, index=index) | |
| def validate_term_candidate_relationship( | |
| candidate: TermCandidate, | |
| *, | |
| selected_topic_uuid: str, | |
| index: VocabularyIndex, | |
| ) -> None: | |
| """Validate that a Term candidate is a direct child of the selected Topic.""" | |
| if candidate.topic_uuid != selected_topic_uuid: | |
| raise ValueError( | |
| f"Term candidate {candidate.candidate_id!r} was built for Topic " | |
| f"{candidate.topic_uuid!r}, not selected Topic {selected_topic_uuid!r}." | |
| ) | |
| term = index.get(candidate.term_uuid) | |
| _validate_term_record(term, topic_uuid=selected_topic_uuid, index=index) | |
| def _validate_topic_record(record: CanonicalConceptRecord) -> None: | |
| if record.level != "Topic": | |
| raise ValueError(f"Expected Topic record, got {record.level!r} for {record.UUID!r}.") | |
| if not record.UUID: | |
| raise ValueError(f"Topic record {record.name!r} does not have a UUID.") | |
| def _validate_term_record( | |
| record: CanonicalConceptRecord, | |
| *, | |
| topic_uuid: str, | |
| index: VocabularyIndex, | |
| ) -> None: | |
| if record.level != "Term": | |
| raise ValueError(f"Expected Term record, got {record.level!r} for {record.UUID!r}.") | |
| if not record.UUID: | |
| raise ValueError(f"Term record {record.name!r} does not have a UUID.") | |
| if index.parent_of(record.UUID) != topic_uuid: | |
| raise ValueError(f"Term {record.UUID!r} is not a direct child of Topic {topic_uuid!r}.") | |
| def _validate_variable_parent_record(record: CanonicalConceptRecord) -> None: | |
| if record.level not in {"Term", "Variable_Level_1", "Variable_Level_2"}: | |
| raise ValueError( | |
| f"Expected Term or Variable parent, got {record.level!r} for {record.UUID!r}." | |
| ) | |
| if not record.UUID: | |
| raise ValueError(f"Variable parent record {record.name!r} does not have a UUID.") | |
| def _validate_variable_child_record( | |
| record: CanonicalConceptRecord, | |
| *, | |
| parent_uuid: str, | |
| index: VocabularyIndex, | |
| ) -> None: | |
| if record.level not in {"Variable_Level_1", "Variable_Level_2", "Variable_Level_3"}: | |
| raise ValueError(f"Expected Variable record, got {record.level!r} for {record.UUID!r}.") | |
| if not record.UUID: | |
| raise ValueError(f"Variable record {record.name!r} does not have a UUID.") | |
| if index.parent_of(record.UUID) != parent_uuid: | |
| raise ValueError( | |
| f"Variable {record.UUID!r} is not a direct child of parent {parent_uuid!r}." | |
| ) | |