Skip to content
GitHub

Architecture

Internal design of the lexigram-ai-evaluation package.


The evaluation package sits in the AI subsystem layer, providing benchmarking and testing capabilities for AI model outputs.

flowchart BT
    subgraph Presentation
        UI[lexigram-ui]
        Admin[lexigram-admin]
    end
    subgraph AI Subsystem
        LLM[lexigram-ai-llm]
        RAG[lexigram-ai-rag]
        Agents[lexigram-ai-agents]
        Evaluation[lexigram-ai-evaluation]
    end
    subgraph Infra
        Contracts[lexigram-contracts]
        Vector[lexigram-vector]
        Cache[lexigram-cache]
    end

    Evaluation --> Contracts
    Evaluation --> LLM
    Evaluation --> Vector
    UI --> Evaluation
    Admin --> Evaluation
    LLM --> Contracts
    RAG --> Contracts
    Agents --> Contracts
    Vector --> Contracts
    Cache --> Contracts

Import direction: Arrows point toward the dependency. Evaluation depends on lexigram-contracts for protocols and types, optionally on lexigram-ai-llm for embedding-based evaluation, and on lexigram-vector for vector store-backed comparisons.


classDiagram
    class EvaluationSample {
        +str id
        +str input
        +str reference
        +dict metadata
    }
    class EvaluationDataset {
        +str name
        +list~EvaluationSample~ samples
        +dict metadata
    }
    class EvaluationResult {
        +float score
        +EvaluationScoreType score_type
        +str feedback
        +dict metrics
    }
    class RunReport {
        +str dataset_name
        +str evaluator_name
        +int total_samples
        +int passed_samples
        +float average_score
        +list~EvaluationResult~ results
        +dict metadata
    }
    class EvaluationScoreType {
        <<enum>>
        EXACT_MATCH
        PARTIAL_MATCH
        SEMANTIC_SIMILARITY
        STRING_DISTANCE
        TRAJECTORY_FIDELITY
        CUSTOM
    }

    EvaluationDataset --> EvaluationSample : contains
    RunReport --> EvaluationResult : contains
    EvaluationResult --> EvaluationScoreType : score_type

EvaluationDataset — a named collection of EvaluationSample instances, each with an id, input (prompt/query), reference (expected output), and optional metadata.

EvaluationResult — produced per sample. Contains score (0.0–1.0), score_type (enum mapping the metric family), feedback text, and metrics dict for extensible detail.

RunReport — aggregate result from running one evaluator against one dataset. Includes the average score, pass/fail count against a threshold, and per-sample results.

Enum ValueMeaning
EXACT_MATCHBinary — output must match reference exactly
PARTIAL_MATCHPartial keyword/concept overlap
SEMANTIC_SIMILARITYEmbedding cosine similarity
STRING_DISTANCELevenshtein, Jaccard, or cosine string distance
TRAJECTORY_FIDELITYAgent step-by-step trajectory match
CUSTOMUser-defined scoring strategy

sequenceDiagram
    participant Client as Client Code
    participant H as EvaluationHarness
    participant E as EvaluatorProtocol
    participant M as Metrics

    Client->>H: run(dataset, evaluator)
    H->>H: Log run start
    loop Each sample in dataset
        H->>E: evaluate(input, output, reference)
        E->>E: Compute score
        E-->>H: Ok(EvaluationResult) | Err
        H->>H: Check pass_threshold
        H->>M: record score, passed/failed
    end
    H->>H: Compute aggregate stats
    H-->>Client: Ok(RunReport) | Err
  1. Dataset loaded — from JSON, CSV, or in-memory list of EvaluationSample
  2. Per-sample evaluation — harness iterates samples, calling evaluator.evaluate(input, output, reference) for each
  3. Threshold check — each result compared against pass_threshold
  4. Aggregation — scores averaged, pass/fail counted, RunReport built
  5. ReturnOk(RunReport) with full detail, or Err on infrastructure failure

EvaluatorScore TypeDescription
CriteriaEvaluatorEXACT_MATCHRule-based evaluation — exact match, contains, contains_all, regex. Accepts a list of criteria dicts. Falls back to exact string comparison when no criteria given.
QAEvaluatorPARTIAL_MATCHKeyword-overlap evaluation. Tokenizes output and reference, removes stopwords, computes intersection ratio.
StringDistanceEvaluatorSTRING_DISTANCEString similarity via Levenshtein distance or Jaccard similarity on word sets.
EmbeddingDistanceEvaluatorSEMANTIC_SIMILARITYCosine similarity between output and reference embeddings. Requires an EmbeddingClientProtocol in the container.
TrajectoryEvaluatorTRAJECTORY_FIDELITYAgent trajectory fidelity. Parses JSON trajectories, compares step actions and final state key-values.

All built-in evaluators share the same evaluate(input, output, reference) signature and return Result[EvaluationResult, Exception]. They inherit from BaseEvaluator which provides the _create_result() helper.

# di/provider.py — five named singletons
container.singleton(EvaluatorProtocol, CriteriaEvaluator(), name="criteria")
container.singleton(EvaluatorProtocol, QAEvaluator(), name="qa")
container.singleton(EvaluatorProtocol, StringDistanceEvaluator(), name="string_distance")
container.singleton(EvaluatorProtocol, EmbeddingDistanceEvaluator(), name="embedding_distance")

Evaluators are registered as named singletons on the EvaluatorProtocol contract. Consumers resolve by name via the container.


sequenceDiagram
    actor User as Client Code
    participant M as EvaluationModule
    participant P as EvaluationProvider
    participant C as Container

    User->>M: configure(config)
    M->>M: Create DynamicModule
    M-->>User: DynamicModule
    User->>C: Create container with module
    C->>C: Freeze container

    rect rgb(200, 240, 200)
        Note over C,P: register phase
        C->>P: register(registrar)
        P->>C: singleton(EvaluationConfig)
        P->>C: singleton(EvaluatorProtocol × 5)
        P->>C: singleton(EvaluationHarness)
        C-->>P: ok
    end

    rect rgb(200, 240, 240)
        Note over C,P: boot phase
        C->>P: boot(resolver)
        P->>P: Log "evaluation_provider_booted"
        C-->>P: ok
    end

    User->>C: resolve(EvaluatorProtocol, name="criteria")
    C-->>User: CriteriaEvaluator instance
    User->>C: resolve(EvaluationHarness)
    C-->>User: EvaluationHarness instance
PhaseInputActions
register()ContainerRegistrarProtocolBind EvaluationConfig, 5 named EvaluatorProtocol singletons, EvaluationHarness
boot()BootContainerProtocolLog completion
shutdown()noneNo-op (evaluators are stateless)
from lexigram.ai.evaluation import EvaluationModule
from lexigram.ai.evaluation.config import EvaluationConfig
module = EvaluationModule.configure(
config=EvaluationConfig(
default_threshold=0.9,
embedding_model="text-embedding-3-large",
)
)

EvaluationModule.configure() accepts an optional EvaluationConfig. The stub() classmethod produces a module suitable for testing with safe defaults.


All contracts live in lexigram.contracts.ai.evaluation:

SymbolKindDescription
EvaluatorProtocolProtocolevaluate(input, output, reference) -> Result[EvaluationResult, Exception]
EvaluationHarnessProtocolProtocolrun(dataset, evaluator) -> Result[RunReport, Exception]
EvaluationResultFrozen dataclassscore, score_type, feedback, metrics
EvaluationSampleFrozen dataclassid, input, reference, metadata
EvaluationDatasetFrozen dataclassname, samples, metadata
RunReportFrozen dataclassdataset_name, evaluator_name, total_samples, passed_samples, average_score, results, metadata
EvaluationScoreTypeEnumEXACT_MATCH, PARTIAL_MATCH, SEMANTIC_SIMILARITY, STRING_DISTANCE, TRAJECTORY_FIDELITY, CUSTOM
EmbeddingClientProtocolProtocol (from lexigram.contracts.ai.llm)Used optionally by EmbeddingDistanceEvaluator
EvaluationErrorExceptionBase exception for evaluation errors

EvaluationConfig extends BaseConfig and is populated from environment variables or constructor kwargs:

FieldDefaultEnv Variable
enabledTrueLEX_AI_EVALUATION__ENABLED
default_threshold0.8LEX_AI_EVALUATION__DEFAULT_THRESHOLD
embedding_model"text-embedding-3-small"LEX_AI_EVALUATION__EMBEDDING_MODEL
include_metadataTrueLEX_AI_EVALUATION__INCLUDE_METADATA
max_samplesNoneLEX_AI_EVALUATION__MAX_SAMPLES
max_retries3LEX_AI_EVALUATION__MAX_RETRIES
timeout_seconds30LEX_AI_EVALUATION__TIMEOUT_SECONDS

LexigramError
└── AIError (contracts)
└── EvaluationError (contracts)
├── EvaluationConfigError
├── EvaluatorNotFoundError
├── DatasetError
└── HarnessError

Leaf exceptions live in the extension package. EvaluationError lives in contracts as the base callers catch at the domain boundary.


PointMechanism
Custom evaluatorImplement EvaluatorProtocol, register as named singleton
Custom metricReturn extra values in EvaluationResult.metrics dict
Custom dataset sourceBuild EvaluationDataset from any source (JSON, CSV, DB, API)
Custom scoring strategySubclass BaseEvaluator, override evaluate()
Evaluator combinationWrap multiple evaluators in a composite implementing EvaluatorProtocol
Threshold policyOverride pass_threshold on EvaluationHarness
Async dataset loadingImplement custom loader that returns EvaluationDataset
from lexigram.contracts.ai.evaluation import (
EvaluationResult,
EvaluationScoreType,
EvaluatorProtocol,
)
from lexigram.result import Ok, Result
class LengthEvaluator(EvaluatorProtocol):
@property
def name(self) -> str:
return "length"
async def evaluate(
self, input: str, output: str, reference: str
) -> Result[EvaluationResult, Exception]:
ratio = min(len(output), len(reference)) / max(len(output), len(reference), 1)
return Ok(EvaluationResult(
score=ratio,
score_type=EvaluationScoreType.CUSTOM,
feedback=f"Length ratio: {ratio:.2f}",
metrics={"output_length": len(output), "reference_length": len(reference)},
))

Register in the container: container.singleton(EvaluatorProtocol, LengthEvaluator(), name="length").