File size: 1,542 Bytes
910dadd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1bcb9d8
910dadd
 
 
 
 
 
 
 
 
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
"""Task plugin interface.

A task module drops into app/tasks/ and exposes a module-level `TASK = Task(...)`.
The registry auto-discovers it; nav, sampling, running, scoring, and tracing are
generic. Nothing else in the app needs to change.
"""

from dataclasses import dataclass, field
from typing import Any, Callable


@dataclass
class Sample:
    input_id: str
    text: str


@dataclass
class Task:
    id: str
    title: str
    tagline: str
    system_prompt: str
    ui: dict  # frontend hints: {"output": "label"|"config", "input_label": ..., "placeholder": ...}

    sample: Callable[[], Sample]
    lookup_truth: Callable[[str], dict | None]  # input_id -> ground truth (None if unknown)
    parse_output: Callable[[str], Any]  # final assistant text -> parsed result
    score: Callable[[dict, Any], dict[str, float]]  # (truth, parsed) -> auto-scores
    present: Callable[[Any, dict | None], dict]  # (parsed, truth) -> JSON payload for the UI

    tools: list[dict] = field(default_factory=list)  # OpenAI tools format
    execute_tool: Callable[[str, dict], str] | None = None
    order: int = 100  # nav position; lower first
    backend: str | None = None  # named backend (see config.backend); None = default

    def build_messages(self, text: str) -> list[dict]:
        return [
            {"role": "system", "content": self.system_prompt},
            {"role": "user", "content": text},
        ]

    def meta(self) -> dict:
        return {"id": self.id, "title": self.title, "tagline": self.tagline, "ui": self.ui}