Skip to content

resolve()

Submit a human review decision for an escalation. Use this after evaluate() returns status == "escalated" and a human reviewer has assessed the output.


Signature

def resolve(
    *,
    escalation_id: str,
    decision: str,        # "approved" | "rejected" | "corrected"
    corrections: dict | None = None,
    reviewer: str | dict | None = None,
    reason: str | None = None,
) -> ResolutionResult
interface ResolveOptions {
    escalationId: string;
    decision: "approved" | "rejected" | "corrected";
    corrections?: Record<string, unknown>;
    reviewer?: string | { name?: string; email?: string };
    reason?: string;
}

async function resolve(options: ResolveOptions): Promise<ResolutionResult>

Parameters

Parameter Type Default Description
escalation_id str required The escalation to resolve. Obtained from EvaluationDecision.escalation_id.
decision str required The reviewer's decision. Must be one of "approved", "rejected", or "corrected".
corrections dict \| None None The corrected output. Required when decision="corrected". Must match the structure of the original output dict.
reviewer str \| dict \| None None Identity of the human reviewer. Pass a string for backward compatibility, or a dict with name and email for full audit trail visibility.
reason str \| None None Human-readable reason for the decision. Stored for compliance and training purposes.

All parameters are keyword-only (enforced by *).


Returns

ResolutionResult

@dataclass(frozen=True)
class ResolutionResult:
    escalation_id: str
    status: str                          # "approved" | "rejected" | "corrected"
    metrics: list[MetricResult]
    resolved_at: str                     # ISO 8601 timestamp
    resolved_by: str | None = None
    reviewer_name: str | None = None
    reviewer_email: str | None = None
interface ResolutionResult {
    readonly escalationId: string;
    readonly status: string;                 // "approved" | "rejected" | "corrected"
    readonly metrics: MetricResult[];
    readonly resolvedAt: string;             // ISO 8601 timestamp
    readonly resolvedBy?: string;
    readonly reviewerName?: string;
    readonly reviewerEmail?: string;
}
Field Python TypeScript Description
Escalation ID escalation_id escalationId The escalation that was resolved.
Status status status The resolution status (mirrors the decision parameter).
Metrics metrics metrics Per-field quality metric scores computed from the evaluation.
Resolved at resolved_at resolvedAt ISO 8601 timestamp of when the resolution was recorded.
Resolved by resolved_by resolvedBy Reviewer identity string (from reviewer parameter when passed as string).
Reviewer name reviewer_name reviewerName Reviewer's display name (when reviewer is passed as dict).
Reviewer email reviewer_email reviewerEmail Reviewer's email address (when reviewer is passed as dict).

MetricResult

@dataclass(frozen=True)
class MetricResult:
    field: str
    metrics: dict[str, float]
interface MetricResult {
    readonly field: string;
    readonly metrics: Record<string, number>;
}
Field Type Description
field str Output field name (matches a key in the metrics dict from evaluate()).
metrics dict[str, float] Metric name to score mapping. Scores are typically 0.0 to 1.0.

Decision Types

"approved"

The human reviewer confirms the agent's output is correct as-is. No corrections needed. No metrics are computed — the human confirmed the output is correct, so there is no reference to compare against.

result = coalex.resolve(
    escalation_id="esc-001",
    decision="approved",
    reviewer={"name": "Dr. Smith", "email": "dr.smith@hospital.org"},
    reason="Output is clinically accurate and complete.",
)
const result = await resolve({
    escalationId: "esc-001",
    decision: "approved",
    reviewer: { name: "Dr. Smith", email: "dr.smith@hospital.org" },
    reason: "Output is clinically accurate and complete.",
});

Reviewer as string (backward compatible)

You can still pass a plain string for backward compatibility:

reviewer="dr.smith@hospital.org"

However, using the dict form with name and email is recommended -- the dashboard will display the reviewer's name alongside their email in the escalation audit trail.

"rejected"

The human reviewer determines the agent's output is incorrect and should not be served to the user. No corrected version is provided.

result = coalex.resolve(
    escalation_id="esc-002",
    decision="rejected",
    reviewer={"name": "Dr. Jones", "email": "dr.jones@hospital.org"},
    reason="Diagnosis is incorrect. Patient symptoms indicate pneumonia, not bronchitis.",
)
const result = await resolve({
    escalationId: "esc-002",
    decision: "rejected",
    reviewer: { name: "Dr. Jones", email: "dr.jones@hospital.org" },
    reason: "Diagnosis is incorrect. Patient symptoms indicate pneumonia, not bronchitis.",
});

"corrected"

The human reviewer provides a corrected version of the output. The corrections parameter is required for this decision type. Metrics are computed by comparing the original output against the provided corrections (output_vs_corrections).

result = coalex.resolve(
    escalation_id="esc-003",
    decision="corrected",
    corrections={
        "diagnosis": "Community-acquired pneumonia",
        "icd_code": "J18.9",
        "recommendation": "Chest X-ray and empiric antibiotics",
    },
    reviewer={"name": "Dr. Jones", "email": "dr.jones@hospital.org"},
    reason="Changed diagnosis from bronchitis to pneumonia based on imaging.",
)
const result = await resolve({
    escalationId: "esc-003",
    decision: "corrected",
    corrections: {
        diagnosis: "Community-acquired pneumonia",
        icd_code: "J18.9",
        recommendation: "Chest X-ray and empiric antibiotics",
    },
    reviewer: { name: "Dr. Jones", email: "dr.jones@hospital.org" },
    reason: "Changed diagnosis from bronchitis to pneumonia based on imaging.",
});

Validation Rules

Rule Error
decision not in {"approved", "rejected", "corrected"} ValueError
decision="corrected" but corrections is None or empty ValueError
register() not called before resolve() RuntimeError

Full Workflow Example

import coalex

# Step 1: Evaluate the agent's output
decision = coalex.evaluate(
    request_id="req-123",
    input={"question": "What medication is recommended?"},
    output={"answer": "Take 500mg of ibuprofen twice daily."},
    metrics={"answer": ["f1", "semantic_similarity", "contains"]},
)

# Step 2: Handle based on status
if decision.status == "auto_approved":
    # Serve the output to the user
    serve_response(output)

elif decision.status == "escalated":
    # Route to human reviewer (your UI, Slack, email, etc.)
    human_decision = await get_human_review(decision.escalation_id)

    # Step 3: Resolve with human decision
    result = coalex.resolve(
        escalation_id=decision.escalation_id,
        decision=human_decision.decision,
        corrections=human_decision.corrections,
        reviewer={"name": human_decision.reviewer_name, "email": human_decision.reviewer_email},
        reason=human_decision.reason,
    )

    print(f"Resolved: {result.status}")
    print(f"Resolved at: {result.resolved_at}")
    print(f"Reviewer: {result.reviewer_name} <{result.reviewer_email}>")

    # Inspect per-field metrics
    for m in result.metrics:
        print(f"  {m.field}: {m.metrics}")
        # e.g., "answer: {'f1': 0.72, 'semantic_similarity': 0.85, 'contains': 0.0}"

elif decision.status == "rejected":
    # Do not serve the output
    return fallback_response()
import { evaluate, resolve } from "@coalex-ai/sdk";

// Step 1: Evaluate the agent's output
const decision = await evaluate({
    requestId: "req-123",
    input: { question: "What medication is recommended?" },
    output: { answer: "Take 500mg of ibuprofen twice daily." },
    metrics: { answer: ["f1", "semantic_similarity", "contains"] },
});

// Step 2: Handle based on status
if (decision.status === "auto_approved") {
    // Serve the output to the user
    serveResponse(output);

} else if (decision.status === "escalated") {
    // Route to human reviewer (your UI, Slack, email, etc.)
    const humanDecision = await getHumanReview(decision.escalationId!);

    // Step 3: Resolve with human decision
    const result = await resolve({
        escalationId: decision.escalationId!,
        decision: humanDecision.decision,
        corrections: humanDecision.corrections,
        reviewer: { name: humanDecision.reviewerName, email: humanDecision.reviewerEmail },
        reason: humanDecision.reason,
    });

    console.log(`Resolved: ${result.status}`);
    console.log(`Resolved at: ${result.resolvedAt}`);
    console.log(`Reviewer: ${result.reviewerName} <${result.reviewerEmail}>`);

    // Inspect per-field metrics
    for (const m of result.metrics) {
        console.log(`  ${m.field}: ${JSON.stringify(m.metrics)}`);
        // e.g., "answer: {"f1":0.72,"semantic_similarity":0.85,"contains":0.0}"
    }

} else if (decision.status === "rejected") {
    // Do not serve the output
    return fallbackResponse();
}

Notes

  • Resolution data is stored permanently for compliance and audit purposes (EU AI Act, ISO 42001).
  • Corrections submitted with decision="corrected" feed back into the evaluation pipeline and can be used for fine-tuning or policy updates.
  • The reviewer field is optional but strongly recommended for audit trails in regulated industries.
  • The resolved_at timestamp is set server-side by the Coalex platform, not by the client.
  • Network errors from the Coalex API raise httpx.HTTPStatusError.

API Reference

coalex.resolve

SDK resolve() -- submit human review for an escalation.

Classes

MetricResult dataclass

Quality metrics for a single output field.

Source code in coalex/resolve.py
@dataclasses.dataclass(frozen=True)
class MetricResult:
    """Quality metrics for a single output field."""

    field: str
    metrics: dict[str, float]

ResolutionResult dataclass

Result of a resolve() call.

Source code in coalex/resolve.py
@dataclasses.dataclass(frozen=True)
class ResolutionResult:
    """Result of a resolve() call."""

    escalation_id: str
    status: str  # "approved" | "rejected" | "corrected"
    metrics: list[MetricResult]
    resolved_at: str
    resolved_by: str | None = None
    reviewer_name: str | None = None
    reviewer_email: str | None = None

Functions

resolve

resolve(
    *,
    escalation_id: str,
    decision: str,
    corrections: dict | None = None,
    reviewer: str | dict | None = None,
    reason: str | None = None,
    _config: CoalexConfig | None = None,
    _api_key: str | None = None,
) -> ResolutionResult

Submit human review for an escalation. BYO HITL or Coalex dashboard.

Parameters:

Name Type Description Default
escalation_id str

The escalation to resolve.

required
decision str

One of "approved", "rejected", "corrected".

required
corrections dict | None

Required when decision="corrected". The corrected output.

None
reviewer str | dict | None

Optional reviewer identity. Pass a string (backwards compat) or a dict with name and/or email keys.

None
reason str | None

Optional human-readable reason.

None
_config CoalexConfig | None

Override config (testing only).

None
_api_key str | None

Override API key (testing only).

None
Source code in coalex/resolve.py
def resolve(
    *,
    escalation_id: str,
    decision: str,
    corrections: dict | None = None,
    reviewer: str | dict | None = None,
    reason: str | None = None,
    _config: CoalexConfig | None = None,
    _api_key: str | None = None,
) -> ResolutionResult:
    """Submit human review for an escalation. BYO HITL or Coalex dashboard.

    Args:
        escalation_id: The escalation to resolve.
        decision: One of "approved", "rejected", "corrected".
        corrections: Required when decision="corrected". The corrected output.
        reviewer: Optional reviewer identity. Pass a string (backwards compat)
            or a dict with ``name`` and/or ``email`` keys.
        reason: Optional human-readable reason.
        _config: Override config (testing only).
        _api_key: Override API key (testing only).
    """
    import coalex as _sdk

    cfg = _config or _sdk._config
    api_key = _api_key or _sdk._api_key
    if cfg is None:
        raise RuntimeError("coalex.register() must be called before resolve()")

    if decision not in VALID_DECISIONS:
        raise ValueError(f"decision must be one of {VALID_DECISIONS}, got '{decision}'")

    if decision == "corrected" and not corrections:
        raise ValueError("corrections required when decision is 'corrected'")

    payload: dict = {"decision": decision}
    if corrections is not None:
        payload["corrections"] = corrections
    if isinstance(reviewer, dict):
        if reviewer.get("name"):
            payload["reviewer_name"] = reviewer["name"]
        if reviewer.get("email"):
            payload["reviewer_email"] = reviewer["email"]
    elif isinstance(reviewer, str):
        payload["reviewer"] = reviewer
        payload["reviewer_name"] = reviewer
    if reason is not None:
        payload["reason"] = reason

    with httpx.Client(timeout=30.0) as client:
        resp = client.post(
            f"{cfg.endpoint}/api/v1/escalations/{escalation_id}/resolve",
            json=payload,
            headers={"Authorization": f"Bearer {api_key}"},
        )
        resp.raise_for_status()

    data = resp.json()
    return ResolutionResult(
        escalation_id=data["escalation_id"],
        status=data["status"],
        metrics=[MetricResult(field=m["field"], metrics=m["metrics"]) for m in data.get("metrics", [])],
        resolved_at=data["resolved_at"],
        resolved_by=data.get("resolved_by"),
        reviewer_name=data.get("reviewer_name"),
        reviewer_email=data.get("reviewer_email"),
    )