inspect-evals-utils 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- inspect_evals_utils/__init__.py +43 -0
- inspect_evals_utils/cache.py +33 -0
- inspect_evals_utils/code.py +123 -0
- inspect_evals_utils/datasets.py +311 -0
- inspect_evals_utils/huggingface.py +340 -0
- inspect_evals_utils/ids.py +232 -0
- inspect_evals_utils/messages.py +69 -0
- inspect_evals_utils/prompts.py +39 -0
- inspect_evals_utils/py.typed +0 -0
- inspect_evals_utils/readmes/__init__.py +1 -0
- inspect_evals_utils/readmes/cli.py +66 -0
- inspect_evals_utils/readmes/generate.py +670 -0
- inspect_evals_utils/readmes/sections.py +197 -0
- inspect_evals_utils/shuffle.py +23 -0
- inspect_evals_utils-0.1.0.dist-info/METADATA +86 -0
- inspect_evals_utils-0.1.0.dist-info/RECORD +19 -0
- inspect_evals_utils-0.1.0.dist-info/WHEEL +4 -0
- inspect_evals_utils-0.1.0.dist-info/entry_points.txt +3 -0
- inspect_evals_utils-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Runtime helpers for Inspect AI evaluations: dataset loading with retries, sample ids, prompts and README generation."""
|
|
2
|
+
|
|
3
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
4
|
+
|
|
5
|
+
from inspect_evals_utils.cache import INSPECT_EVALS_CACHE_PATH
|
|
6
|
+
from inspect_evals_utils.datasets import load_csv_dataset, load_json_dataset
|
|
7
|
+
from inspect_evals_utils.ids import (
|
|
8
|
+
DuplicateIdError,
|
|
9
|
+
KnownBrokenSampleError,
|
|
10
|
+
create_stable_id,
|
|
11
|
+
drop_known_broken,
|
|
12
|
+
filter_duplicate_ids,
|
|
13
|
+
require_optional_dependency,
|
|
14
|
+
)
|
|
15
|
+
from inspect_evals_utils.messages import message_content_text, reasoning_to_text
|
|
16
|
+
from inspect_evals_utils.prompts import Prompt
|
|
17
|
+
from inspect_evals_utils.shuffle import shuffle_and_seed
|
|
18
|
+
|
|
19
|
+
# The version is set once, in pyproject.toml; this reads it back from the
|
|
20
|
+
# installed package metadata.
|
|
21
|
+
try:
|
|
22
|
+
__version__ = version("inspect-evals-utils")
|
|
23
|
+
except PackageNotFoundError: # a source tree that was never installed
|
|
24
|
+
__version__ = "unknown"
|
|
25
|
+
|
|
26
|
+
# inspect_evals_utils.huggingface is not imported here: it needs the
|
|
27
|
+
# huggingface extra, and the base package must import without it.
|
|
28
|
+
__all__ = [
|
|
29
|
+
"INSPECT_EVALS_CACHE_PATH",
|
|
30
|
+
"DuplicateIdError",
|
|
31
|
+
"KnownBrokenSampleError",
|
|
32
|
+
"Prompt",
|
|
33
|
+
"__version__",
|
|
34
|
+
"create_stable_id",
|
|
35
|
+
"drop_known_broken",
|
|
36
|
+
"filter_duplicate_ids",
|
|
37
|
+
"load_csv_dataset",
|
|
38
|
+
"load_json_dataset",
|
|
39
|
+
"message_content_text",
|
|
40
|
+
"reasoning_to_text",
|
|
41
|
+
"require_optional_dependency",
|
|
42
|
+
"shuffle_and_seed",
|
|
43
|
+
]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""The cache directory shared by inspect_evals and standalone evals.
|
|
2
|
+
|
|
3
|
+
Set INSPECT_EVALS_CACHE_DIR to move it or point it at a pre-populated directory.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
from platformdirs import user_cache_dir
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def cache_path() -> Path:
|
|
13
|
+
"""Resolve the cache directory, rejecting a value that depends on the cwd."""
|
|
14
|
+
configured = os.environ.get("INSPECT_EVALS_CACHE_DIR")
|
|
15
|
+
if not configured:
|
|
16
|
+
return Path(user_cache_dir("inspect_evals"))
|
|
17
|
+
|
|
18
|
+
# A leading ~ survives a .env file and a quoted shell assignment, which is
|
|
19
|
+
# how this variable is usually set, so expand it rather than treating it as
|
|
20
|
+
# a directory name.
|
|
21
|
+
path = Path(configured).expanduser()
|
|
22
|
+
if not path.is_absolute():
|
|
23
|
+
raise ValueError(
|
|
24
|
+
f"INSPECT_EVALS_CACHE_DIR must be absolute or start with ~, but is "
|
|
25
|
+
f"{configured!r}. It is read once when inspect_evals_utils is imported, so "
|
|
26
|
+
f"a relative path would point somewhere different depending on where "
|
|
27
|
+
f"the eval was started from."
|
|
28
|
+
)
|
|
29
|
+
return path
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# Read once at import, because evals derive their own module-level paths from it.
|
|
33
|
+
INSPECT_EVALS_CACHE_PATH = cache_path()
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""Helpers for extracting generated code and JSON from Markdown responses."""
|
|
2
|
+
|
|
3
|
+
import ast
|
|
4
|
+
import re
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
|
|
7
|
+
_LANGUAGE_TAGS = {
|
|
8
|
+
"python": ("python", "python3", "py"),
|
|
9
|
+
"cpp": ("cpp", "c++", "cc", "cxx", "c"),
|
|
10
|
+
"java": ("java",),
|
|
11
|
+
"javascript": ("javascript", "js"),
|
|
12
|
+
"typescript": ("typescript", "ts"),
|
|
13
|
+
"csharp": ("csharp", "cs", "c#"),
|
|
14
|
+
"php": ("php",),
|
|
15
|
+
"shell": ("bash", "sh", "shell"),
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
# A block ends at a bare fence line, just before any other fence line (which a model
|
|
19
|
+
# may write without closing the block first), at a fence glued to the end of the last
|
|
20
|
+
# code line, or at the end of the text when the fence is never closed. The last case is
|
|
21
|
+
# its own group so that an unclosed unlabelled fence, usually a stray closing fence
|
|
22
|
+
# after unfenced code, can be ignored.
|
|
23
|
+
_CLOSE = r"(?:^```[ \t\r]*$|(?=^```)|(?<=\S)```[ \t\r]*$|(\Z))"
|
|
24
|
+
|
|
25
|
+
_FENCE = re.compile(
|
|
26
|
+
r"^```[ \t]*([^\s`]*)[^\n]*\r?\n(.*?)" + _CLOSE,
|
|
27
|
+
re.DOTALL | re.MULTILINE,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
_JSON_FENCE = re.compile(
|
|
32
|
+
r"\A\s*```[ \t]*(?:json)?[ \t]*\r?\n(.*?)\s*```\s*\Z", re.DOTALL | re.IGNORECASE
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _labelled_fence(tags: tuple[str, ...]) -> re.Pattern[str]:
|
|
37
|
+
# A labelled fence may follow prose on the same line, but only when nothing
|
|
38
|
+
# follows the label, so that a fence mentioned in a sentence is not an opener.
|
|
39
|
+
tag = "|".join(re.escape(t) for t in sorted(tags, key=len, reverse=True))
|
|
40
|
+
opener = rf"(?:^```[ \t]*(?:{tag})(?=[\s`])[^\n]*|```[ \t]*(?:{tag})[ \t]*)\r?\n"
|
|
41
|
+
return re.compile(opener + "(.*?)" + _CLOSE, re.DOTALL | re.MULTILINE | re.IGNORECASE)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def extract_code_blocks(completion: str, language: str = "python") -> list[str]:
|
|
45
|
+
"""Return the language-labelled code blocks, or else the unlabelled ones.
|
|
46
|
+
|
|
47
|
+
Blocks are in the order they appear. Unlabelled blocks are returned only
|
|
48
|
+
when no block carries one of the language's labels. Labels are matched
|
|
49
|
+
case-insensitively and both LF and CRLF line endings are accepted. Fences
|
|
50
|
+
labelled for another language are not treated as unlabelled code, and empty
|
|
51
|
+
blocks are skipped. A labelled fence that is never closed runs to the end of
|
|
52
|
+
the text.
|
|
53
|
+
"""
|
|
54
|
+
tags = _LANGUAGE_TAGS.get(language.lower(), (language.lower(),))
|
|
55
|
+
labelled = [
|
|
56
|
+
match.group(1)
|
|
57
|
+
for match in _labelled_fence(tags).finditer(completion)
|
|
58
|
+
if match.group(1).strip()
|
|
59
|
+
]
|
|
60
|
+
if labelled:
|
|
61
|
+
return labelled
|
|
62
|
+
return [
|
|
63
|
+
match.group(2)
|
|
64
|
+
for match in _FENCE.finditer(completion)
|
|
65
|
+
if match.group(1) == "" and match.group(2).strip() and match.group(3) is None
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def extract_code_block(
|
|
70
|
+
completion: str,
|
|
71
|
+
language: str = "python",
|
|
72
|
+
accept: Callable[[str], bool] | None = None,
|
|
73
|
+
) -> str | None:
|
|
74
|
+
"""Return the first block from `extract_code_blocks`, or None if there is none.
|
|
75
|
+
|
|
76
|
+
Language-labelled blocks are preferred over unlabelled blocks even when an
|
|
77
|
+
unlabelled block appears earlier in the response.
|
|
78
|
+
|
|
79
|
+
When `accept` is given, the first block it accepts is returned instead. If
|
|
80
|
+
it accepts none, the first block is returned as without it. Pass
|
|
81
|
+
`defines(name)` to take the block that implements the function the task
|
|
82
|
+
asks for, rather than a usage example or a draft that does not parse.
|
|
83
|
+
"""
|
|
84
|
+
blocks = extract_code_blocks(completion, language)
|
|
85
|
+
|
|
86
|
+
if accept is not None:
|
|
87
|
+
for block in blocks:
|
|
88
|
+
if accept(block):
|
|
89
|
+
return block
|
|
90
|
+
|
|
91
|
+
return blocks[0] if blocks else None
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def strip_json_fence(text: str) -> str:
|
|
95
|
+
"""Return the JSON inside one code fence wrapping the whole text, else the text unchanged.
|
|
96
|
+
|
|
97
|
+
The fence may be labelled `json` or unlabelled. Use it before parsing a
|
|
98
|
+
reply that should be bare JSON, such as one requested with a
|
|
99
|
+
`response_schema`: some providers accept the schema without enforcing it and
|
|
100
|
+
return the JSON in a fence. Text with prose around the fence, or more than
|
|
101
|
+
one fence, is returned unchanged, so it still fails validation.
|
|
102
|
+
"""
|
|
103
|
+
match = _JSON_FENCE.match(text)
|
|
104
|
+
if match is None or re.search(r"^[ \t]*```", match.group(1), re.MULTILINE):
|
|
105
|
+
return text
|
|
106
|
+
return match.group(1)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def defines(name: str) -> Callable[[str], bool]:
|
|
110
|
+
"""Accept Python code that parses and defines `name` at the top level."""
|
|
111
|
+
|
|
112
|
+
def check(code: str) -> bool:
|
|
113
|
+
try:
|
|
114
|
+
tree = ast.parse(code)
|
|
115
|
+
except (SyntaxError, ValueError):
|
|
116
|
+
return False
|
|
117
|
+
return any(
|
|
118
|
+
isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef))
|
|
119
|
+
and node.name == name
|
|
120
|
+
for node in tree.body
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
return check
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import logging
|
|
3
|
+
import os
|
|
4
|
+
from collections.abc import Callable
|
|
5
|
+
from functools import wraps
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any, Concatenate, ParamSpec, cast
|
|
8
|
+
from urllib.parse import urlparse
|
|
9
|
+
|
|
10
|
+
import backoff # type: ignore
|
|
11
|
+
from inspect_ai._util.file import ( # no public API available
|
|
12
|
+
file,
|
|
13
|
+
)
|
|
14
|
+
from inspect_ai.dataset import Dataset, csv_dataset, json_dataset
|
|
15
|
+
|
|
16
|
+
from inspect_evals_utils.cache import INSPECT_EVALS_CACHE_PATH
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
P = ParamSpec("P")
|
|
21
|
+
DatasetLoader = Callable[..., Dataset]
|
|
22
|
+
|
|
23
|
+
SUPPORTED_REMOTE_URL_PROTOCOLS = {"https", "http", "s3", "gs", "gcs", "az", "abfs"}
|
|
24
|
+
LOCAL_PROTOCOL = {"", "c", "d", "e", "file"}
|
|
25
|
+
TRANSIENT_ERRORS = {408, 429, 500, 502, 503, 504}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def is_remote_url(file_path: str) -> bool:
|
|
29
|
+
scheme = urlparse(file_path).scheme
|
|
30
|
+
known_remote = scheme in SUPPORTED_REMOTE_URL_PROTOCOLS
|
|
31
|
+
if known_remote:
|
|
32
|
+
return True
|
|
33
|
+
definitely_local = scheme in LOCAL_PROTOCOL
|
|
34
|
+
if definitely_local:
|
|
35
|
+
return False
|
|
36
|
+
raise ValueError(
|
|
37
|
+
f"Could not parse file path. Found {file_path}"
|
|
38
|
+
"Currently, following protocols are supported:"
|
|
39
|
+
f"{SUPPORTED_REMOTE_URL_PROTOCOLS}"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _get_extension(url: str) -> str:
|
|
44
|
+
"""Get file extension from URL or file path."""
|
|
45
|
+
parsed_url = urlparse(url)
|
|
46
|
+
path = parsed_url.path
|
|
47
|
+
if path and "." in Path(path).name:
|
|
48
|
+
return Path(path).suffix
|
|
49
|
+
|
|
50
|
+
return ""
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def get_cached_fname(url: str, tag: str = "") -> str:
|
|
54
|
+
"""Generate cache filename with optional tag prefix and URL hash.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
url: The URL to hash
|
|
58
|
+
tag: Optional human-readable tag prefix (empty string for no tag)
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Filename in format: {tag}_{hash}.{ext} or {hash}.{ext}
|
|
62
|
+
"""
|
|
63
|
+
prefix = f"{tag}_" if tag else ""
|
|
64
|
+
hash_part = hashlib.sha256(url.encode("utf-8")).hexdigest()[:32]
|
|
65
|
+
extension = _get_extension(url)
|
|
66
|
+
return f"{prefix}{hash_part}{extension}"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _get_cached_path(
|
|
70
|
+
eval_name: str,
|
|
71
|
+
url: str,
|
|
72
|
+
tag: str = "",
|
|
73
|
+
) -> str:
|
|
74
|
+
"""Generate fully qualified cache path for a given URL.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
eval_name: Evaluation name (used for cache directory organization)
|
|
78
|
+
url: The remote URL
|
|
79
|
+
tag: Optional human-readable tag for cache filename (empty string for no tag)
|
|
80
|
+
|
|
81
|
+
"""
|
|
82
|
+
return str(INSPECT_EVALS_CACHE_PATH / eval_name / get_cached_fname(url, tag))
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def is_transient_error(e: Exception) -> bool:
|
|
86
|
+
"""Check if error is transient and should be retried.
|
|
87
|
+
|
|
88
|
+
Handles both HTTP errors from requests and network errors from fsspec.
|
|
89
|
+
"""
|
|
90
|
+
# Network errors that should be retried
|
|
91
|
+
if isinstance(e, (ConnectionError, TimeoutError, OSError)):
|
|
92
|
+
return True
|
|
93
|
+
|
|
94
|
+
# Note: HTTPError import removed as we no longer use requests,
|
|
95
|
+
# but keeping this check for potential future compatibility
|
|
96
|
+
status_code = getattr(getattr(e, "response", None), "status_code", None)
|
|
97
|
+
if status_code is not None:
|
|
98
|
+
return status_code in TRANSIENT_ERRORS
|
|
99
|
+
|
|
100
|
+
return False
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@backoff.on_exception( # type: ignore
|
|
104
|
+
backoff.expo,
|
|
105
|
+
exception=Exception,
|
|
106
|
+
giveup=lambda e: not is_transient_error(e),
|
|
107
|
+
max_tries=5,
|
|
108
|
+
jitter=backoff.full_jitter,
|
|
109
|
+
)
|
|
110
|
+
def _download_remote(
|
|
111
|
+
remote_url: str,
|
|
112
|
+
local_cache_path: str,
|
|
113
|
+
encoding: str = "utf-8",
|
|
114
|
+
fs_options: dict[str, Any] | None = None,
|
|
115
|
+
) -> None:
|
|
116
|
+
"""Downloads from remote_url and puts contents into local_cache_path.
|
|
117
|
+
|
|
118
|
+
Uses fsspec-based file() context manager to support multiple protocols
|
|
119
|
+
(HTTP, S3, GCS, Azure, etc.)
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
remote_url: Remote URL (supports http://, s3://, gs://, az://, etc.)
|
|
123
|
+
local_cache_path: Local path to cache the file
|
|
124
|
+
encoding: Text encoding for the remote file (default: utf-8). The cache
|
|
125
|
+
is always written as UTF-8.
|
|
126
|
+
fs_options: Filesystem options (e.g., S3 credentials, anonymous access)
|
|
127
|
+
"""
|
|
128
|
+
Path(local_cache_path).parent.mkdir(parents=True, exist_ok=True)
|
|
129
|
+
|
|
130
|
+
fs_opts = fs_options or {}
|
|
131
|
+
with (
|
|
132
|
+
file(remote_url, "r", encoding=encoding, fs_options=fs_opts) as f_remote,
|
|
133
|
+
open(local_cache_path, "w", encoding="utf-8") as f_local,
|
|
134
|
+
):
|
|
135
|
+
f_local.write(f_remote.read())
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def cache_dataset_loader() -> Callable[
|
|
139
|
+
[Callable[Concatenate[str, P], Dataset]], Callable[Concatenate[str, P], Dataset]
|
|
140
|
+
]:
|
|
141
|
+
"""Decorator factory that adds caching functionality to dataset loader functions.
|
|
142
|
+
|
|
143
|
+
The cache directory is inferred from the format_extension parameter.
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
A decorator that wraps dataset loaders with caching logic
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
def decorate(
|
|
150
|
+
f: Callable[Concatenate[str, P], Dataset],
|
|
151
|
+
) -> Callable[Concatenate[str, P], Dataset]:
|
|
152
|
+
@wraps(f)
|
|
153
|
+
def wrapper(
|
|
154
|
+
file_path: str,
|
|
155
|
+
eval_name: str,
|
|
156
|
+
*args: P.args,
|
|
157
|
+
**kwargs: P.kwargs,
|
|
158
|
+
) -> Dataset:
|
|
159
|
+
# Extract caching-specific parameters from kwargs
|
|
160
|
+
refresh: bool = bool(kwargs.pop("refresh", False))
|
|
161
|
+
cache_tag: str = str(kwargs.pop("cache_tag", ""))
|
|
162
|
+
encoding: str = kwargs.get("encoding", "utf-8") # type: ignore[assignment]
|
|
163
|
+
fs_options: dict[str, Any] | None = kwargs.get("fs_options") # type: ignore[assignment]
|
|
164
|
+
|
|
165
|
+
if not is_remote_url(file_path):
|
|
166
|
+
logger.info(f"Reading local file {file_path}")
|
|
167
|
+
return f(file_path, *args, **kwargs)
|
|
168
|
+
|
|
169
|
+
# Validate that we can detect extension from remote URL
|
|
170
|
+
extension = _get_extension(file_path)
|
|
171
|
+
if not extension:
|
|
172
|
+
raise ValueError(
|
|
173
|
+
f"Cannot detect file extension from URL: {file_path}. "
|
|
174
|
+
"Please ensure the URL ends with .json, .jsonl, or .csv"
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
cached_path = _get_cached_path(
|
|
178
|
+
eval_name=eval_name,
|
|
179
|
+
url=file_path,
|
|
180
|
+
tag=cache_tag,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
cache_exists = os.path.exists(cached_path)
|
|
184
|
+
cache_is_empty = cache_exists and os.path.getsize(cached_path) == 0
|
|
185
|
+
if refresh or not cache_exists or cache_is_empty:
|
|
186
|
+
logger.info(f"Downloading remote url {file_path} and writing to {cached_path}")
|
|
187
|
+
_download_remote(file_path, cached_path, encoding, fs_options)
|
|
188
|
+
|
|
189
|
+
# `encoding` describes the remote file; the cached copy is always UTF-8.
|
|
190
|
+
kwargs["encoding"] = "utf-8"
|
|
191
|
+
|
|
192
|
+
logger.info(f"Loading cached file from {cached_path}")
|
|
193
|
+
try:
|
|
194
|
+
return f(cached_path, *args, **kwargs)
|
|
195
|
+
except ValueError:
|
|
196
|
+
# This will catch UnicodeDecodeError and JSONDecodeError,
|
|
197
|
+
# as they are both ValueError subclasses.
|
|
198
|
+
# It will NOT catch _csv.Error, which is raised by csv.reader
|
|
199
|
+
# if using a strict dialect and the file is malformed.
|
|
200
|
+
|
|
201
|
+
if refresh:
|
|
202
|
+
raise
|
|
203
|
+
|
|
204
|
+
# Parsing errors from json/csv readers should not poison future
|
|
205
|
+
# runs. Re-download and retry once; if parsing still fails,
|
|
206
|
+
# surface the second ValueError to the caller.
|
|
207
|
+
logger.warning(
|
|
208
|
+
f"Failed to load cached dataset at {cached_path}; "
|
|
209
|
+
"re-downloading and retrying once."
|
|
210
|
+
)
|
|
211
|
+
_download_remote(file_path, cached_path, encoding, fs_options)
|
|
212
|
+
return f(cached_path, *args, **kwargs)
|
|
213
|
+
|
|
214
|
+
return cast(Callable[Concatenate[str, P], Dataset], wrapper)
|
|
215
|
+
|
|
216
|
+
return decorate
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
# Type stub functions for mypy
|
|
220
|
+
def load_json_dataset(
|
|
221
|
+
file_path: str,
|
|
222
|
+
eval_name: str,
|
|
223
|
+
sample_fields: Any = None,
|
|
224
|
+
auto_id: bool = False,
|
|
225
|
+
shuffle: bool = False,
|
|
226
|
+
seed: int | None = None,
|
|
227
|
+
limit: int | bool | None = None,
|
|
228
|
+
epoch: int | None = None,
|
|
229
|
+
name: str = "",
|
|
230
|
+
fs_options: dict[str, Any] | None = None,
|
|
231
|
+
*,
|
|
232
|
+
encoding: str = "utf-8",
|
|
233
|
+
refresh: bool = False,
|
|
234
|
+
cache_tag: str | None = None,
|
|
235
|
+
**reader_kwargs: Any,
|
|
236
|
+
) -> Dataset:
|
|
237
|
+
"""Load a JSON or JSONL dataset with caching support for remote URLs.
|
|
238
|
+
|
|
239
|
+
Args:
|
|
240
|
+
file_path: Local path or remote URL to the JSON/JSONL file (extension auto-detected)
|
|
241
|
+
eval_name: Evaluation name (organizes cache directory)
|
|
242
|
+
sample_fields: Field specification or converter function for samples
|
|
243
|
+
auto_id: Automatically generate sample IDs
|
|
244
|
+
shuffle: Shuffle the dataset
|
|
245
|
+
seed: Random seed for shuffling
|
|
246
|
+
limit: Maximum number of samples to load
|
|
247
|
+
epoch: Epoch number for sampling
|
|
248
|
+
name: Dataset name
|
|
249
|
+
fs_options: Filesystem options for remote URLs
|
|
250
|
+
encoding: Text encoding of the source file. Remote files are cached as UTF-8.
|
|
251
|
+
refresh: Force re-download of cached remote files
|
|
252
|
+
cache_tag: Optional human-readable tag for cache filename
|
|
253
|
+
reader_kwargs: Optional JSON reader options.
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
Dataset object
|
|
257
|
+
"""
|
|
258
|
+
raise NotImplementedError # replaced by the decorated implementation below
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def load_csv_dataset(
|
|
262
|
+
file_path: str,
|
|
263
|
+
eval_name: str,
|
|
264
|
+
sample_fields: Any = None,
|
|
265
|
+
auto_id: bool = False,
|
|
266
|
+
shuffle: bool = False,
|
|
267
|
+
seed: int | None = None,
|
|
268
|
+
limit: int | bool | None = None,
|
|
269
|
+
epoch: int | None = None,
|
|
270
|
+
name: str = "",
|
|
271
|
+
fieldnames: list[str] | None = None,
|
|
272
|
+
dialect: str = "unix",
|
|
273
|
+
delimiter: str | None = None,
|
|
274
|
+
encoding: str = "utf-8",
|
|
275
|
+
fs_options: dict[str, Any] | None = None,
|
|
276
|
+
shuffle_choices: bool | int = False,
|
|
277
|
+
*,
|
|
278
|
+
refresh: bool = False,
|
|
279
|
+
cache_tag: str | None = None,
|
|
280
|
+
) -> Dataset:
|
|
281
|
+
"""Load a CSV dataset with caching support for remote URLs.
|
|
282
|
+
|
|
283
|
+
Args:
|
|
284
|
+
file_path: Local path or remote URL to the CSV file (extension auto-detected)
|
|
285
|
+
eval_name: Evaluation name (organizes cache directory)
|
|
286
|
+
sample_fields: Field specification or converter function for samples
|
|
287
|
+
auto_id: Automatically generate sample IDs
|
|
288
|
+
shuffle: Shuffle the dataset
|
|
289
|
+
seed: Random seed for shuffling
|
|
290
|
+
limit: Maximum number of samples to load
|
|
291
|
+
epoch: Epoch number for sampling
|
|
292
|
+
name: Dataset name
|
|
293
|
+
fieldnames: CSV field names
|
|
294
|
+
dialect: CSV dialect
|
|
295
|
+
delimiter: CSV delimiter character
|
|
296
|
+
encoding: Text encoding of the source file. Remote files are cached as UTF-8.
|
|
297
|
+
fs_options: Filesystem options for remote URLs
|
|
298
|
+
shuffle_choices: Shuffle multiple choice options. An int is used as the
|
|
299
|
+
shuffle seed (as in inspect_ai's ``csv_dataset``); True shuffles unseeded.
|
|
300
|
+
refresh: Force re-download of cached remote files
|
|
301
|
+
cache_tag: Optional human-readable tag for cache filename
|
|
302
|
+
|
|
303
|
+
Returns:
|
|
304
|
+
Dataset object
|
|
305
|
+
"""
|
|
306
|
+
raise NotImplementedError # replaced by the decorated implementation below
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
# Actual decorated implementations
|
|
310
|
+
load_json_dataset = cache_dataset_loader()(json_dataset) # type: ignore[assignment,arg-type] # noqa: F811
|
|
311
|
+
load_csv_dataset = cache_dataset_loader()(csv_dataset) # type: ignore[assignment,arg-type] # noqa: F811
|