|
58 | 58 | " INV[\"TrueFalseInverterScorer<br/>negates one result\"]\n", |
59 | 59 | " CONV[\"create_conversation_scorer()<br/>scores concatenated history\"]\n", |
60 | 60 | " THRESH[\"FloatScaleThresholdScorer<br/>score ≥ threshold\"]\n", |
| 61 | + " FSFALL[\"FloatScaleFallbackScorer<br/>alternative float-scale judge\"]\n", |
| 62 | + " TFFALL[\"TrueFalseFallbackScorer<br/>alternative true/false judge\"]\n", |
61 | 63 | " CONV ~~~ THRESH\n", |
62 | 64 | " end\n", |
63 | 65 | "\n", |
|
70 | 72 | " TF -->|\"1+ via scorers=\"| COMP\n", |
71 | 73 | " TF -->|\"1 via scorer=\"| INV\n", |
72 | 74 | " FS -->|\"1 via scorer=\"| THRESH\n", |
| 75 | + " FS -->|\"2 comparable single-result scorers\"| FSFALL\n", |
| 76 | + " TF -->|\"2 comparable single-result scorers\"| TFFALL\n", |
73 | 77 | " TF -->|\"1 scorer supporting text content\"| CONV\n", |
74 | 78 | " FS -->|\"1 scorer supporting text content\"| CONV\n", |
75 | 79 | "\n", |
76 | 80 | " COMP -. is a .-> TFOUT\n", |
77 | 81 | " INV -. is a .-> TFOUT\n", |
78 | 82 | " THRESH -. is a .-> TFOUT\n", |
| 83 | + " FSFALL -. is a .-> FSOUT\n", |
| 84 | + " TFFALL -. is a .-> TFOUT\n", |
79 | 85 | " CONV -. \"for true/false input\" .-> TFOUT\n", |
80 | 86 | " CONV -. \"for float-scale input\" .-> FSOUT\n", |
81 | 87 | "\n", |
82 | 88 | " classDef input fill:#e8f0fe,stroke:#4285f4,color:#15233a;\n", |
83 | 89 | " classDef wrapper fill:#fff4e5,stroke:#f29900,color:#3d2600;\n", |
84 | 90 | " classDef output fill:#e6f4ea,stroke:#34a853,color:#17351f;\n", |
85 | 91 | " class TF,FS input;\n", |
86 | | - " class COMP,INV,THRESH,CONV wrapper;\n", |
| 92 | + " class COMP,INV,THRESH,CONV,FSFALL,TFFALL wrapper;\n", |
87 | 93 | " class TFOUT,FSOUT output;\n", |
88 | 94 | "```" |
89 | 95 | ] |
|
99 | 105 | "`TrueFalseCompositeScorer` requires at least one `TrueFalseScorer` and combines their\n", |
100 | 106 | "single results with `AND`, `OR`, or `MAJORITY`; `TrueFalseInverterScorer` accepts one\n", |
101 | 107 | "`TrueFalseScorer`. `FloatScaleThresholdScorer` is the cross-kind adapter: it accepts one\n", |
102 | | - "`FloatScaleScorer` and produces a `TrueFalseScorer`. These generic wrappers forward the\n", |
103 | | - "same `Scorable` to their children, so each child must support that evidence kind.\n", |
| 108 | + "`FloatScaleScorer` and produces a `TrueFalseScorer`. `FloatScaleFallbackScorer` accepts two\n", |
| 109 | + "`FloatScaleScorer`s and produces a `FloatScaleScorer`. `TrueFalseFallbackScorer` does the\n", |
| 110 | + "same for two `TrueFalseScorer`s. Each fallback wrapper tries the primary first and calls\n", |
| 111 | + "the fallback only when the primary returns an undetermined score. These wrappers forward the same\n", |
| 112 | + "`Scorable` to their children, so each child must support that evidence kind.\n", |
104 | 113 | "\n", |
105 | 114 | "`create_conversation_scorer()` accepts a true/false or float-scale scorer that supports\n", |
106 | 115 | "text `ContentScorable` evidence. It returns a dynamic wrapper that remains the same scorer\n", |
|
280 | 289 | { |
281 | 290 | "cell_type": "markdown", |
282 | 291 | "id": "11", |
| 292 | + "metadata": {}, |
| 293 | + "source": [ |
| 294 | + "## Routing abstentions to a fallback scorer\n", |
| 295 | + "\n", |
| 296 | + "Both float-scale and true/false scorers can return `ScoreStatus.UNDETERMINED`.\n", |
| 297 | + "A fallback wrapper evaluates the same evidence with a second scorer only when the\n", |
| 298 | + "primary returns that status. A completed `False` or `0.0` is a valid judgment and does\n", |
| 299 | + "not trigger fallback. This lets a fast primary handle most inputs without calling an\n", |
| 300 | + "expensive second judge each time.\n", |
| 301 | + "\n", |
| 302 | + "Both children must belong to the same result family, support the same condition types,\n", |
| 303 | + "and return exactly one score when applicable. The wrapper validates both children\n", |
| 304 | + "before scoring and passes each its supported expectation. If fallback runs, the results\n", |
| 305 | + "must refer to the same evidence and expectation; categories must match when both\n", |
| 306 | + "results supply them. An unreadable judgment can have no category labels. Configure equivalent\n", |
| 307 | + "criteria and, for float-scale scorers, comparable numeric meanings: matching types and\n", |
| 308 | + "categories does not prove that two rubrics measure the same thing.\n", |
| 309 | + "\n", |
| 310 | + "With preconfigured scorers that meet these requirements:\n", |
| 311 | + "\n", |
| 312 | + "```python\n", |
| 313 | + "from pyrit.score import FloatScaleFallbackScorer, TrueFalseFallbackScorer\n", |
| 314 | + "\n", |
| 315 | + "harm_scorer = FloatScaleFallbackScorer(\n", |
| 316 | + " scorer=primary_harm_scorer,\n", |
| 317 | + " fallback_scorer=secondary_harm_scorer,\n", |
| 318 | + ")\n", |
| 319 | + "objective_scorer = TrueFalseFallbackScorer(\n", |
| 320 | + " scorer=primary_objective_scorer,\n", |
| 321 | + " fallback_scorer=secondary_objective_scorer,\n", |
| 322 | + ")\n", |
| 323 | + "```\n", |
| 324 | + "\n", |
| 325 | + "A non-applicable primary (`[]`) returns `[]` without calling the fallback. A non-applicable\n", |
| 326 | + "fallback leaves the primary's undetermined judgment in place. If both abstain, the result\n", |
| 327 | + "remains undetermined. Exceptions propagate; they are not treated as abstentions.\n", |
| 328 | + "\n", |
| 329 | + "Multiple child results are rejected rather than paired by position or discarded.\n", |
| 330 | + "Aggregate them explicitly before using fallback if a single aggregate is meaningful.\n", |
| 331 | + "\n", |
| 332 | + "Only the root score is persisted. The wrapper creates a new score without modifying\n", |
| 333 | + "either child result and retains their observation links. Metadata records `resolved_by`\n", |
| 334 | + "as `\"primary\"` or `\"fallback\"`. Child metadata keys are prefixed with `primary.` and\n", |
| 335 | + "`fallback.`, so duplicate keys and nested fallback details are not overwritten.\n", |
| 336 | + "When fallback is attempted, `primary_rationale` is retained and `fallback_status` records\n", |
| 337 | + "`\"complete\"`, `\"undetermined\"`, or `\"not_applicable\"`. A fallback judgment also supplies\n", |
| 338 | + "`fallback_rationale`; the combined rationale explains both attempts." |
| 339 | + ] |
| 340 | + }, |
| 341 | + { |
| 342 | + "cell_type": "markdown", |
| 343 | + "id": "12", |
283 | 344 | "metadata": { |
284 | 345 | "lines_to_next_cell": 0 |
285 | 346 | }, |
|
299 | 360 | { |
300 | 361 | "cell_type": "code", |
301 | 362 | "execution_count": null, |
302 | | - "id": "12", |
| 363 | + "id": "13", |
303 | 364 | "metadata": {}, |
304 | 365 | "outputs": [ |
305 | 366 | { |
|
341 | 402 | }, |
342 | 403 | { |
343 | 404 | "cell_type": "markdown", |
344 | | - "id": "13", |
| 405 | + "id": "14", |
345 | 406 | "metadata": {}, |
346 | 407 | "source": [ |
347 | 408 | "For a richer, real-world example, wrap a `SelfAskLikertScorer` with the\n", |
|
0 commit comments