Skip to content

Commit 05af33d

Browse files
committed
link evals to datasets, dataset CLI
1 parent 5808658 commit 05af33d

File tree

18 files changed

+1700
-765
lines changed

18 files changed

+1700
-765
lines changed

src/lmnr/cli/__init__.py

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
from argparse import ArgumentParser, _SubParsersAction
2+
import asyncio
3+
4+
from lmnr.cli.datasets import handle_datasets_command
5+
from lmnr.cli.evals import run_evaluation
6+
from lmnr.cli.rules import add_cursor_rules
7+
from lmnr.sdk.log import get_default_logger
8+
from lmnr.sdk.utils import from_env
9+
10+
LOG = get_default_logger(__name__)
11+
EVAL_DIR = "evals"
12+
DEFAULT_DATASET_PULL_BATCH_SIZE = 100
13+
DEFAULT_DATASET_PUSH_BATCH_SIZE = 100
14+
15+
16+
def setup_eval_parser(subparsers: _SubParsersAction) -> None:
17+
"""Setup the eval subcommand parser."""
18+
parser_eval = subparsers.add_parser(
19+
"eval",
20+
description="Run an evaluation",
21+
help="Run an evaluation",
22+
)
23+
parser_eval.add_argument(
24+
"file",
25+
nargs="*",
26+
help="Files or a file containing the evaluation to run."
27+
+ "If no file name is provided, all evaluation files in the `evals` directory are run as long"
28+
+ "as they match *_eval.py or eval_*.py",
29+
default=[],
30+
)
31+
parser_eval.add_argument(
32+
"--continue-on-error",
33+
action="store_true",
34+
default=False,
35+
help="Continue execution upon errors",
36+
)
37+
parser_eval.add_argument(
38+
"--output-file",
39+
help="Output file to write the results to. Outputs are written in JSON format.",
40+
nargs="?",
41+
)
42+
43+
44+
def setup_add_cursor_rules_parser(subparsers: _SubParsersAction) -> None:
45+
"""Setup the add-cursor-rules subcommand parser."""
46+
subparsers.add_parser(
47+
"add-cursor-rules",
48+
description="Download laminar.mdc file and add it to .cursor/rules",
49+
help="Download laminar.mdc file and add it to .cursor/rules",
50+
)
51+
52+
53+
def setup_laminar_args(parser: ArgumentParser) -> None:
54+
"""Setup the laminar arguments parser."""
55+
parser.add_argument(
56+
"--project-api-key",
57+
help="[Optional] Project API key to use for the command. If no project API key is provided, the project API key will be read from the environment variable LMNR_PROJECT_API_KEY.",
58+
default=from_env("LMNR_PROJECT_API_KEY"),
59+
)
60+
parser.add_argument(
61+
"--base-url",
62+
help="[Optional] Base URL to use for the command. If no base URL is provided, the base URL will be read from the environment variable LMNR_BASE_URL or we default to https://api.lmnr.ai.",
63+
default=from_env("LMNR_BASE_URL") or "https://api.lmnr.ai",
64+
)
65+
parser.add_argument(
66+
"--port",
67+
help="[Optional] Port to use for the command. If no port is provided, the port will be read from the environment variable LMNR_PORT or we default to 443.",
68+
default=from_env("LMNR_PORT") or 443,
69+
)
70+
71+
72+
def setup_datasets_list_parser(subparsers: _SubParsersAction) -> None:
73+
"""Setup the datasets list subcommand parser."""
74+
parser_datasets_list: ArgumentParser = subparsers.add_parser(
75+
"list",
76+
description="List datasets",
77+
help="List datasets",
78+
)
79+
setup_laminar_args(parser_datasets_list)
80+
81+
82+
def setup_datasets_push_parser(subparsers: _SubParsersAction) -> None:
83+
"""Setup the datasets push subcommand parser."""
84+
parser_datasets_push: ArgumentParser = subparsers.add_parser(
85+
"push",
86+
description="Push datapoints to an existing dataset",
87+
help="Push datapoints to an existing dataset",
88+
)
89+
parser_datasets_push.add_argument(
90+
"--name",
91+
"-n",
92+
help="Name of the dataset to push data to. Exactly one of name or id must be provided.",
93+
default=None,
94+
)
95+
parser_datasets_push.add_argument(
96+
"--id",
97+
help="ID of the dataset to push data to. Exactly one of name or id must be provided.",
98+
default=None,
99+
)
100+
parser_datasets_push.add_argument(
101+
"paths",
102+
nargs="*",
103+
help="Paths to the files or directories containing the data to push to the dataset. Supported formats: JSON, CSV, JSONL",
104+
)
105+
parser_datasets_push.add_argument(
106+
"-r",
107+
"--recursive",
108+
action="store_true",
109+
default=False,
110+
help="Recursively read all files in the directories and their subdirectories.",
111+
)
112+
parser_datasets_push.add_argument(
113+
"--batch-size",
114+
type=int,
115+
help=f"Batch size to push data in. If no batch size is provided, data is pushed in batches of {DEFAULT_DATASET_PUSH_BATCH_SIZE}.",
116+
default=DEFAULT_DATASET_PUSH_BATCH_SIZE,
117+
)
118+
setup_laminar_args(parser_datasets_push)
119+
120+
121+
def setup_datasets_pull_parser(subparsers: _SubParsersAction) -> None:
122+
"""Setup the datasets pull subcommand parser."""
123+
parser_datasets_pull: ArgumentParser = subparsers.add_parser(
124+
"pull",
125+
description="Pull data from a dataset",
126+
help="Pull data from a dataset",
127+
)
128+
parser_datasets_pull.add_argument(
129+
"--name",
130+
"-n",
131+
help="Name of the dataset to pull data from",
132+
default=None,
133+
)
134+
parser_datasets_pull.add_argument(
135+
"--id",
136+
help="ID of the dataset to pull data from",
137+
default=None,
138+
)
139+
parser_datasets_pull.add_argument(
140+
"output_path",
141+
help="Path to the file to save the data to. If no path is provided, data is printed to the console in the format specified by --output-format.",
142+
nargs="?",
143+
)
144+
parser_datasets_pull.add_argument(
145+
"--output-format",
146+
default="json",
147+
choices=["json", "csv", "jsonl"],
148+
help="Output format to save the data to. If no format is provided, it is inferred from the file extension.",
149+
)
150+
parser_datasets_pull.add_argument(
151+
"--batch-size",
152+
type=int,
153+
help=f"Batch size to pull data in. If no batch size is provided, data is pulled in batches of {DEFAULT_DATASET_PULL_BATCH_SIZE}.",
154+
default=DEFAULT_DATASET_PULL_BATCH_SIZE,
155+
)
156+
parser_datasets_pull.add_argument(
157+
"--limit",
158+
type=int,
159+
help="Limit the number of data points to pull. If no limit is provided, all data points are pulled.",
160+
)
161+
parser_datasets_pull.add_argument(
162+
"--offset",
163+
type=int,
164+
help="Offset the number of data points to pull. If no offset is provided, data is pulled from the beginning.",
165+
)
166+
setup_laminar_args(parser_datasets_pull)
167+
168+
169+
def setup_datasets_create_parser(subparsers: _SubParsersAction) -> None:
170+
"""Setup the datasets create subcommand parser."""
171+
parser_datasets_create: ArgumentParser = subparsers.add_parser(
172+
"create",
173+
description="Create a dataset from input files and download it in Laminar format",
174+
help="Create a dataset from input files and download it in Laminar format",
175+
)
176+
parser_datasets_create.add_argument(
177+
"name",
178+
help="Name of the dataset to create",
179+
)
180+
parser_datasets_create.add_argument(
181+
"paths",
182+
nargs="+",
183+
help="Paths to the files or directories containing the data to push to the dataset. Supported formats: JSON, CSV, JSONL",
184+
)
185+
parser_datasets_create.add_argument(
186+
"-o",
187+
"--output-file",
188+
required=True,
189+
help="Path to the file to save the pulled data to",
190+
)
191+
parser_datasets_create.add_argument(
192+
"--output-format",
193+
choices=["json", "csv", "jsonl"],
194+
help="Output format to save the data to. If no format is provided, it is inferred from the output file extension.",
195+
)
196+
parser_datasets_create.add_argument(
197+
"-r",
198+
"--recursive",
199+
action="store_true",
200+
default=False,
201+
help="Recursively read all files in the directories and their subdirectories.",
202+
)
203+
parser_datasets_create.add_argument(
204+
"--batch-size",
205+
type=int,
206+
help=f"Batch size to push/pull data in. If no batch size is provided, data is processed in batches of {DEFAULT_DATASET_PUSH_BATCH_SIZE}.",
207+
default=DEFAULT_DATASET_PUSH_BATCH_SIZE,
208+
)
209+
setup_laminar_args(parser_datasets_create)
210+
211+
212+
def setup_datasets_parser(subparsers: _SubParsersAction) -> None:
213+
"""Setup the datasets subcommand parser and its subcommands."""
214+
parser_datasets: ArgumentParser = subparsers.add_parser(
215+
"datasets",
216+
description="Manage datasets",
217+
help="Manage datasets",
218+
)
219+
220+
parser_datasets_subparsers = parser_datasets.add_subparsers(
221+
title="command",
222+
dest="command",
223+
)
224+
225+
# Setup all dataset subcommands
226+
setup_datasets_list_parser(parser_datasets_subparsers)
227+
setup_datasets_push_parser(parser_datasets_subparsers)
228+
setup_datasets_pull_parser(parser_datasets_subparsers)
229+
setup_datasets_create_parser(parser_datasets_subparsers)
230+
231+
232+
def cli() -> None:
233+
"""Main CLI entry point."""
234+
parser = ArgumentParser(
235+
prog="lmnr",
236+
description="CLI for Laminar. Call `lmnr [subcommand] --help` for more information on each subcommand.",
237+
)
238+
239+
subparsers = parser.add_subparsers(title="subcommands", dest="subcommand")
240+
241+
# Setup all subcommand parsers
242+
setup_eval_parser(subparsers)
243+
setup_add_cursor_rules_parser(subparsers)
244+
setup_datasets_parser(subparsers)
245+
246+
# Parse arguments and dispatch to appropriate handler
247+
parsed = parser.parse_args()
248+
249+
if parsed.subcommand == "eval":
250+
asyncio.run(run_evaluation(parsed))
251+
elif parsed.subcommand == "add-cursor-rules":
252+
add_cursor_rules()
253+
elif parsed.subcommand == "datasets":
254+
asyncio.run(handle_datasets_command(parsed))
255+
else:
256+
parser.print_help()

0 commit comments

Comments
 (0)