Skip to content

Jev Score

Rasmus Wulff Jensen edited this page Sep 22, 2026 · 3 revisions

Jev Score

Use Score when the answer lies on an ordered spectrum you can describe in steps: issue severity, urgency, or degree of customer frustration. Score can land between levels. For unrelated categories, use Choice; for a yes/no proposition, use Noul.

In JevDotNet, an enum defines the rubric, [JevScoreQuestion<TEnum>] defines the question, and the result property determines whether you receive just a number or the full JevScore<TEnum> record.

Define the rubric

using System.ComponentModel;
using JevDotNet.Models;

public enum IssueSeverity
{
    [Description("Cosmetic problem; functionality still works")]
    Cosmetic = 0,

    [Description("Feature is broken or degraded, but a workaround exists")]
    Degraded = 1,

    [Description("Blocking issue with no usable workaround")]
    Blocking = 2
}

The enum must have 2–10 members. JevDotNet orders the members by their underlying numeric values, then sends their [Description] values as an ordered array of criteria. Without a description, it sends the member name. Use distinct numeric enum values and descriptions that explain what each level means. Explicit 0, 1, 2 assignments make the order easy to see.

The API level numbers are positions in that ordered array, starting at zero. They are not the enum's underlying numeric values. For example, an enum with values 10, 20, 30 still defines API levels 0, 1, 2. The simple Score property receives the numeric position on this scale, which may be fractional; it does not receive an enum member.

JevDotNet currently sends string descriptions, even though the underlying API also supports structured level descriptions.

Add a Score property

public sealed class IssueAssessment
{
    [JevScoreQuestion<IssueSeverity>("How severe is the reported issue?")]
    public double? Severity { get; set; }
}

IssueSeverity defines the ordered levels. Severity is the question ID in the request and answer ID in the response. The question text is sent as instructions. The property must be public and writable, and the question text must not be blank.

Property type What you get
double, double?, decimal, or decimal? Only the numeric score.
JevScore<IssueSeverity> or JevScore<IssueSeverity>? Score, confidence, per-level probabilities, and the returned legend.

IssueSeverity itself is not a valid Score property type. The enum inside JevScore<T> must match the attribute's enum exactly. A nullable property is convenient before evaluation; a successful answer still supplies a value.

Read a numeric score

This complete example needs the NuGet package and a TypeSafe AI key in JEV_API_KEY:

using System;
using System.ComponentModel;
using JevDotNet;
using JevDotNet.Models;

string apiKey = Environment.GetEnvironmentVariable("JEV_API_KEY")
    ?? throw new InvalidOperationException("Set JEV_API_KEY first.");

JevClient client = new(apiKey);
JevResponse<IssueAssessment> response = await client.EvaluateAsync<IssueAssessment>(
    "The export button does nothing, but CSV export still works.");

double? score = response.Result.Severity;
Console.WriteLine($"Severity: {score}");

public sealed class IssueAssessment
{
    [JevScoreQuestion<IssueSeverity>("How severe is the reported issue?")]
    public double? Severity { get; set; }
}

public enum IssueSeverity
{
    [Description("Cosmetic problem; functionality still works")]
    Cosmetic = 0,
    [Description("Feature is broken or degraded, but a workaround exists")]
    Degraded = 1,
    [Description("Blocking issue with no usable workaround")]
    Blocking = 2
}

With three levels, the score lies from 0 to 2. For example, a score of 1.75 is a position between Degraded (1) and Blocking (2). It is not an enum value or a percentage. A decimal? property is also supported if your application prefers decimal arithmetic; the detailed JevScore<T>.Score field is a double.

Keep the full Score answer

Change the property to JevScore<IssueSeverity>? to inspect how the model weighed the levels:

public sealed class DetailedIssueAssessment
{
    [JevScoreQuestion<IssueSeverity>("How severe is the reported issue?")]
    public JevScore<IssueSeverity>? Severity { get; set; }
}

Then evaluate it and read the detailed answer:

JevResponse<DetailedIssueAssessment> detailedResponse =
    await client.EvaluateAsync<DetailedIssueAssessment>(
        "The export button does nothing, but CSV export still works.");

JevScore<IssueSeverity> answer = detailedResponse.Result.Severity
    ?? throw new InvalidOperationException("Severity answer was missing.");

double score = answer.Score;
double confidence = answer.Confidence;
double blockingProbability = answer.Probabilities[IssueSeverity.Blocking];
string blockingDescription = answer.Legend[IssueSeverity.Blocking];

In this follow-on snippet, client and IssueSeverity are defined as in the preceding example. Probabilities is an IReadOnlyDictionary<IssueSeverity, double> for the levels. Legend is an IReadOnlyDictionary<IssueSeverity, string> containing the descriptions returned by the API. Confidence is a separate 0–1 summary of how concentrated the level distribution is. It is not the probability of a particular level and does not guarantee the assessment is correct.

The API score is a probability-weighted average of the zero-based level positions. For example, probabilities 0.00, 0.25, and 0.75 produce 0×0.00 + 1×0.25 + 2×0.75 = 1.75. Different distributions can have the same average, so use Probabilities and Confidence when the distinction matters.

What JevDotNet sends and reads

The Severity property above produces a Score question shaped like this (the surrounding request also contains model and state):

{
  "questions": {
    "Severity": {
      "type": "score",
      "instructions": "How severe is the reported issue?",
      "criteria": [
        "Cosmetic problem; functionality still works",
        "Feature is broken or degraded, but a workaround exists",
        "Blocking issue with no usable workaround"
      ]
    }
  }
}

An illustrative response entry for that question is:

{
  "Severity": {
    "type": "score",
    "score": 1.75,
    "confidence": 0.60,
    "probabilities": { "0": 0.00, "1": 0.25, "2": 0.75 },
    "legend": {
      "0": "Cosmetic problem; functionality still works",
      "1": "Feature is broken or degraded, but a workaround exists",
      "2": "Blocking issue with no usable workaround"
    }
  }
}

The answer's score is mapped directly to double or decimal, or to JevScore<IssueSeverity>.Score. For a detailed property, JevDotNet maps zero-based response keys in probabilities and legend back to enum members in numeric order. An out-of-range level key or a non-string legend value causes a JsonException.

Common mistakes

  • Using an enum as the result property: the enum defines the scale; the answer is numeric or JevScore<T>.
  • Assuming enum numbers are returned: even 10, 20, 30 enum values yield API positions 0, 1, 2.
  • Ordering by source declaration alone: JevDotNet sorts by underlying numeric value, so assign distinct values deliberately.
  • Writing vague levels: describe concrete situations such as “workaround exists” rather than only “medium.”
  • Reading 1.75 as 75%: it is a location on the chosen scale. Use Noul for the probability of a yes/no statement.

Build the full return object · Evaluate text and use results · TypeSafe Score reference

Clone this wiki locally