File size: 2,242 Bytes
af83196
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
"""Shared utilities for context builders."""

from pathlib import Path
from typing import Any, Optional


class TemplateManager:
    """Loads .txt templates from one or more directories.

    Directories are processed in order; later directories override
    templates with the same name from earlier ones.
    """

    def __init__(self, *directories: Optional[str]):
        """
        Initializes the TemplateManager with the given directories.
        If there are multiple directories, the templates from the later directories will override
        the templates from the earlier directories.
        """
        self.templates: dict[str, str] = {}
        for d in directories:
            if d:
                path = Path(d)
                if path.exists():
                    self._load_from_directory(path)

    def _load_from_directory(self, directory: Path) -> None:
        for txt_file in directory.glob("*.txt"):
            with open(txt_file, "r") as f:
                self.templates[txt_file.stem] = f.read()

    def get_template(self, name: str) -> str:
        if name not in self.templates:
            raise ValueError(f"Template '{name}' not found")
        return self.templates[name]


def prog_attr(program: Any, key: str, default: Any = "") -> Any:
    """Read an attribute from a Program object or a plain dict."""
    if hasattr(program, key):
        return getattr(program, key)
    if isinstance(program, dict):
        return program.get(key, default)
    return default


def format_artifacts(program: Any, heading: str = "##", max_len: int = 2000) -> str:
    """Format evaluator artifacts (e.g. feedback) into markdown sections."""
    artifacts = prog_attr(program, "artifacts", None)
    if not artifacts:
        return ""
    sections = []
    for key, value in artifacts.items():
        if value is None:
            continue
        text = str(value)
        if len(text) > max_len:
            text = text[:max_len] + "\n... (truncated)"
        if key == "feedback":
            sections.append(f"{heading} Evaluator Feedback\n{text}")
        else:
            sections.append(f"{heading} {key}\n{text}")
    if not sections:
        return ""
    return "\n" + "\n\n".join(sections) + "\n"