File size: 7,110 Bytes
2e818da
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
"""Canonical observation and evaluation contracts for RAG telemetry.

Every later observability task (OTel boundary, RAG instrumentation, benchmark
harness, Control Room wiring, SigNoz dashboards, the final write-up) imports
these shapes instead of redefining its own. The goal is the one stated in the
plan preamble: "Every value has one name and meaning across local
observations, spans, metrics, Control Room, SigNoz, and the final report."

This module defines shapes only -> it emits nothing. No span, metric, log, or
file write happens here.
"""

from __future__ import annotations

from typing import Any, Literal

from pydantic import BaseModel, ConfigDict, Field

# --- Telemetry mode -------------------------------------------------

# "disabled": no OTel emission at all.
# "local": emit locally (local_store JSONL / Control Room) without exporting
#          to a remote collector.
# "full": emit locally AND export via OTLP to SigNoz.
TelemetryMode = Literal["disabled", "local", "full"]


# --- Retrieval outcome -------------------------------------------------

# Deliberately distinct from a plain empty list: "success_empty" means the
# retrieval pipeline ran correctly and genuinely found nothing, while
# "error_fallback" means the pipeline itself failed and the caller fell back
# to an empty/degraded result. Collapsing these into one signal was the
# whole reason retrieval failures used to be invisible before instrumentation.
RetrievalStatus = Literal["success", "success_empty", "error_fallback"]


class RetrievalOutcome(BaseModel):
    """The result of one retrieval attempt, kept distinct from a plain list.

    Product code and instrumentation both consume this: product code gets a
    real status even in fallback paths, and observability code gets a
    ready-made `retrieval_status` / `empty_result` / `retrieval_error` triple
    per the canonical "Retrieval-behavior measures" contract.
    """

    model_config = ConfigDict(extra="forbid")

    status: RetrievalStatus
    candidates: list[dict[str, Any]] = Field(default_factory=list)
    empty_result: bool = False
    retrieval_error: bool = False
    error_type: str | None = None

    @classmethod
    def success(cls, candidates: list[dict[str, Any]]) -> "RetrievalOutcome":
        return cls(
            status="success" if candidates else "success_empty",
            candidates=candidates,
            empty_result=not candidates,
        )

    @classmethod
    def failure(cls, error_type: str) -> "RetrievalOutcome":
        return cls(status="error_fallback", retrieval_error=True, error_type=error_type)


# --- Metric-safe attribute filtering -------------------------------------------------

# Exactly the "Metric-safe dimensions" list from the plan preamble. Never add
# run/query/trace/project/document/operation IDs here -> those stay trace and
# log attributes only, per the global constraint that plain project IDs are
# allowed as trace/log attributes but must never become metric dimensions.
METRIC_ATTRIBUTE_KEYS: frozenset[str] = frozenset(
    {
        "pipeline.version",
        "query.category",
        "cache.state",
        "consumer",
        "operation",
        "subsystem",
        "status",
        "service.name",
        "evaluation_run",
        # Single bounded operator-controlled boolean. This lets canonical
        # product cohorts exclude controlled diagnostic signals without using
        # a high-cardinality run or request identifier.
        "diagnostic.smoke",
    }
)


def metric_attributes(attributes: dict[str, Any]) -> dict[str, Any]:
    """Narrow an arbitrary attribute dict down to metric-safe dimensions.

    Keeps only keys in `METRIC_ATTRIBUTE_KEYS` whose value is a plain scalar
    (str/int/float/bool). Non-scalar values (lists, dicts, None) are dropped
    even for an otherwise-safe key, since a metric dimension must be a single
    bounded value, not a structure that can blow up cardinality on its own.
    Does not mutate the input.
    """
    return {
        key: value
        for key, value in attributes.items()
        if key in METRIC_ATTRIBUTE_KEYS and isinstance(value, (str, int, float, bool))
    }


# --- Experiment identity -------------------------------------------------


class ExperimentIdentity(BaseModel):
    """The canonical experiment-identity fields from the plan preamble.

    Ordinary product traffic leaves these unset; labelled evaluation runs
    (the benchmark harness in a later task) populate them so every trace,
    metric, log, and local record for one run can be joined back together.
    Field names are snake_case; the dotted preamble names they mirror are
    noted alongside each field.
    """

    model_config = ConfigDict(extra="forbid")

    experiment_id: str | None = None  # experiment.id
    run_id: str | None = None  # experiment.run_id
    pipeline_version: str | None = None  # pipeline.version
    query_set_version: str | None = None  # query_set.version
    corpus_version: str | None = None  # corpus.version
    query_id: str | None = None  # query.id
    query_category: str | None = None  # query.category
    repetition: int | None = None  # repetition
    cache_state: str | None = None  # cache.state
    telemetry_mode: TelemetryMode | None = None  # telemetry.mode
    cold_start: bool | None = None
    git_commit: str | None = None  # git.commit
    embedding_model: str | None = None  # embedding.model
    cerebras_model: str | None = None  # cerebras.model
    chroma_version: str | None = None  # chroma.version
    cognee_version: str | None = None  # cognee.version


# --- Operation observation -------------------------------------------------


class OperationObservation(BaseModel):
    """Compact, per-operation local observation.

    This is the shape the (not-yet-built) single operation helper produces
    for Control Room and experiment evidence: "latest stage durations,
    counts, statuses, and trace ID" per the preamble's signal-responsibility
    table. It carries no prompts, full responses, PDF text, chunks,
    annotation contents, filesystem paths, or raw document IDs -> only
    durations, counts, statuses, and identity.

    `status` is intentionally a plain string rather than a narrow Literal:
    it must accommodate both ordinary per-operation statuses (e.g. "success",
    "error") and the canonical missing-data sentinels (`not_instrumented`,
    `not_applicable`, `not_observed`, `degraded`) -> a numeric duration must
    stay `None` under those sentinels, never silently become 0.
    """

    model_config = ConfigDict(extra="forbid")

    operation: str
    status: str
    subsystem: str | None = None
    consumer: str | None = None
    duration_ms: float | None = None
    stage_durations_ms: dict[str, float] = Field(default_factory=dict)
    counts: dict[str, int | float] = Field(default_factory=dict)
    trace_id: str | None = None
    experiment: ExperimentIdentity | None = None
    retrieval: RetrievalOutcome | None = None
    evaluation_run: bool = False
    attributes: dict[str, Any] = Field(default_factory=dict)