arize-ax-cli 0.0.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.
- arize_ax_cli-0.0.0.dist-info/METADATA +472 -0
- arize_ax_cli-0.0.0.dist-info/RECORD +27 -0
- arize_ax_cli-0.0.0.dist-info/WHEEL +4 -0
- arize_ax_cli-0.0.0.dist-info/entry_points.txt +2 -0
- arize_ax_cli-0.0.0.dist-info/licenses/LICENSE +69 -0
- ax/__init__.py +5 -0
- ax/__main__.py +6 -0
- ax/cli.py +79 -0
- ax/commands/__init__.py +1 -0
- ax/commands/cache.py +54 -0
- ax/commands/config.py +359 -0
- ax/commands/datasets.py +388 -0
- ax/commands/projects.py +284 -0
- ax/config/__init__.py +1 -0
- ax/config/input_readers.py +271 -0
- ax/config/manager.py +338 -0
- ax/config/schema.py +240 -0
- ax/config/setup.py +215 -0
- ax/core/__init__.py +1 -0
- ax/core/basemodel_utils.py +113 -0
- ax/core/decorators.py +55 -0
- ax/core/exceptions.py +43 -0
- ax/core/output.py +300 -0
- ax/utils/__init__.py +1 -0
- ax/utils/console.py +233 -0
- ax/utils/file_io.py +197 -0
- ax/version.py +3 -0
ax/commands/cache.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from typing import Annotated
|
|
2
|
+
|
|
3
|
+
import typer
|
|
4
|
+
|
|
5
|
+
from ax.config.manager import ConfigManager
|
|
6
|
+
from ax.core.decorators import handle_errors
|
|
7
|
+
from ax.utils.console import confirm, info, success
|
|
8
|
+
|
|
9
|
+
# Create config subcommand app
|
|
10
|
+
app = typer.Typer(
|
|
11
|
+
name="cache",
|
|
12
|
+
help="Manage Arize cache",
|
|
13
|
+
no_args_is_help=True,
|
|
14
|
+
context_settings={"help_option_names": ["--help", "-h"]},
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@app.command("clear")
|
|
19
|
+
@handle_errors
|
|
20
|
+
def clear_cache(
|
|
21
|
+
profile: Annotated[
|
|
22
|
+
str,
|
|
23
|
+
typer.Option(
|
|
24
|
+
"--profile",
|
|
25
|
+
"-p",
|
|
26
|
+
help="Profile to show (uses active if not specified)",
|
|
27
|
+
),
|
|
28
|
+
] = "",
|
|
29
|
+
verbose: Annotated[
|
|
30
|
+
bool,
|
|
31
|
+
typer.Option(
|
|
32
|
+
"--verbose",
|
|
33
|
+
"-v",
|
|
34
|
+
help="Enable verbose logs",
|
|
35
|
+
),
|
|
36
|
+
] = False,
|
|
37
|
+
) -> None:
|
|
38
|
+
"""Clear the Arize SDK cache directory.
|
|
39
|
+
|
|
40
|
+
Removes all cached data to free up space or force refresh.
|
|
41
|
+
"""
|
|
42
|
+
if not confirm("Clear the Arize SDK cache?", default=False):
|
|
43
|
+
info("Cache not cleared")
|
|
44
|
+
raise typer.Exit()
|
|
45
|
+
|
|
46
|
+
config = ConfigManager.load(profile)
|
|
47
|
+
cache_dir = config.storage.cache_dir
|
|
48
|
+
|
|
49
|
+
if cache_dir.exists() and cache_dir.is_dir():
|
|
50
|
+
import shutil
|
|
51
|
+
|
|
52
|
+
shutil.rmtree(cache_dir)
|
|
53
|
+
cache_dir.mkdir(parents=True, exist_ok=True)
|
|
54
|
+
success("Cache cleared successfully")
|
ax/commands/config.py
ADDED
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
"""Configuration management commands."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from typing import Annotated
|
|
5
|
+
|
|
6
|
+
import typer
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
from rich.table import Table
|
|
9
|
+
|
|
10
|
+
from ax.config.manager import ConfigManager
|
|
11
|
+
from ax.config.schema import (
|
|
12
|
+
AuthConfig,
|
|
13
|
+
Config,
|
|
14
|
+
)
|
|
15
|
+
from ax.config.setup import (
|
|
16
|
+
create_config_from_env_vars,
|
|
17
|
+
create_config_interactively,
|
|
18
|
+
detect_env_vars,
|
|
19
|
+
)
|
|
20
|
+
from ax.core.decorators import handle_errors
|
|
21
|
+
from ax.utils.console import (
|
|
22
|
+
confirm,
|
|
23
|
+
emphasis,
|
|
24
|
+
info,
|
|
25
|
+
mask,
|
|
26
|
+
new_line,
|
|
27
|
+
success,
|
|
28
|
+
text,
|
|
29
|
+
text_bold,
|
|
30
|
+
text_dimmed,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
# Create config subcommand app
|
|
34
|
+
app = typer.Typer(
|
|
35
|
+
name="config",
|
|
36
|
+
help="Manage Arize CLI configuration",
|
|
37
|
+
no_args_is_help=True,
|
|
38
|
+
context_settings={"help_option_names": ["--help", "-h"]},
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
console = Console()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@app.command("init")
|
|
45
|
+
@handle_errors
|
|
46
|
+
def init(
|
|
47
|
+
verbose: Annotated[
|
|
48
|
+
bool,
|
|
49
|
+
typer.Option(
|
|
50
|
+
"--verbose",
|
|
51
|
+
"-v",
|
|
52
|
+
help="Enable verbose logs",
|
|
53
|
+
),
|
|
54
|
+
] = False,
|
|
55
|
+
) -> None:
|
|
56
|
+
"""Initialize Arize CLI configuration interactively.
|
|
57
|
+
|
|
58
|
+
Creates a new configuration profile with API key, defaults, and
|
|
59
|
+
preferences. Detects existing ARIZE_* environment variables and offers
|
|
60
|
+
to create config from them.
|
|
61
|
+
"""
|
|
62
|
+
existing_profiles = ConfigManager.list_profiles()
|
|
63
|
+
|
|
64
|
+
# Profile Selection
|
|
65
|
+
profile = "default"
|
|
66
|
+
if existing_profiles:
|
|
67
|
+
# profiles exist - prompt for name
|
|
68
|
+
emphasis("Create a new configuration profile")
|
|
69
|
+
text(f"existing profiles: {', '.join(existing_profiles)}\n")
|
|
70
|
+
profile = typer.prompt("profile name")
|
|
71
|
+
new_line()
|
|
72
|
+
else:
|
|
73
|
+
# TODO(Kiko): Need a more captivating welcome message
|
|
74
|
+
emphasis("Welcome to Arize CLI!")
|
|
75
|
+
text("No configuration found. Let's set it up!\n")
|
|
76
|
+
|
|
77
|
+
# Check if profile already exists
|
|
78
|
+
if ConfigManager.exists(profile) and not confirm(
|
|
79
|
+
f"Profile '{profile}' already exists. Overwrite?",
|
|
80
|
+
default=False,
|
|
81
|
+
):
|
|
82
|
+
info("Configuration unchanged")
|
|
83
|
+
raise typer.Exit()
|
|
84
|
+
|
|
85
|
+
# Environment Variable Detection
|
|
86
|
+
detected_env_vars = detect_env_vars()
|
|
87
|
+
use_env_vars = False
|
|
88
|
+
if detected_env_vars:
|
|
89
|
+
emphasis("Environment Variable Detection\n")
|
|
90
|
+
for field, env_var in detected_env_vars.items():
|
|
91
|
+
value = os.environ.get(env_var, "")
|
|
92
|
+
# Mask API key for display
|
|
93
|
+
if field == "api_key":
|
|
94
|
+
value = mask(value)
|
|
95
|
+
console.print(f" [green]✓[/green] Detected {env_var} = {value}")
|
|
96
|
+
|
|
97
|
+
console.print()
|
|
98
|
+
use_env_vars = confirm(
|
|
99
|
+
"Create config from detected environment variables?",
|
|
100
|
+
default=True,
|
|
101
|
+
)
|
|
102
|
+
console.print()
|
|
103
|
+
|
|
104
|
+
config = (
|
|
105
|
+
create_config_from_env_vars(profile, detected_env_vars)
|
|
106
|
+
if use_env_vars
|
|
107
|
+
else create_config_interactively(profile)
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Save configuration
|
|
111
|
+
ConfigManager.save(config, profile)
|
|
112
|
+
|
|
113
|
+
# Set as active profile if not default
|
|
114
|
+
if profile != "default":
|
|
115
|
+
ConfigManager.set_active_profile(profile)
|
|
116
|
+
|
|
117
|
+
# Summary
|
|
118
|
+
new_line()
|
|
119
|
+
success(f"Configuration saved to profile '{profile}'")
|
|
120
|
+
new_line()
|
|
121
|
+
text_dimmed("You're ready to go! Try: ax datasets list")
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@app.command("list")
|
|
125
|
+
@handle_errors
|
|
126
|
+
def list_profiles(
|
|
127
|
+
verbose: Annotated[
|
|
128
|
+
bool,
|
|
129
|
+
typer.Option(
|
|
130
|
+
"--verbose",
|
|
131
|
+
"-v",
|
|
132
|
+
help="Enable verbose logs",
|
|
133
|
+
),
|
|
134
|
+
] = False,
|
|
135
|
+
) -> None:
|
|
136
|
+
"""List all available configuration profiles.
|
|
137
|
+
|
|
138
|
+
Shows all profiles with the active profile marked.
|
|
139
|
+
"""
|
|
140
|
+
profiles = ConfigManager.list_profiles()
|
|
141
|
+
|
|
142
|
+
if not profiles:
|
|
143
|
+
info("No profiles found. Run 'ax config init' to create one.")
|
|
144
|
+
raise typer.Exit()
|
|
145
|
+
|
|
146
|
+
active = ConfigManager.get_active_profile()
|
|
147
|
+
|
|
148
|
+
table = Table(show_header=True, header_style="bold cyan")
|
|
149
|
+
table.add_column("Profile")
|
|
150
|
+
table.add_column("Status")
|
|
151
|
+
|
|
152
|
+
for profile in profiles:
|
|
153
|
+
status = "[green]active[/green]" if profile == active else ""
|
|
154
|
+
table.add_row(profile, status)
|
|
155
|
+
|
|
156
|
+
console.print(table)
|
|
157
|
+
new_line()
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@app.command("show")
|
|
161
|
+
@handle_errors
|
|
162
|
+
def show_profile(
|
|
163
|
+
profile: Annotated[
|
|
164
|
+
str,
|
|
165
|
+
typer.Option(
|
|
166
|
+
"--profile",
|
|
167
|
+
"-p",
|
|
168
|
+
help="Profile to show (uses active if not specified)",
|
|
169
|
+
),
|
|
170
|
+
] = "",
|
|
171
|
+
all_sections: Annotated[
|
|
172
|
+
bool,
|
|
173
|
+
typer.Option(
|
|
174
|
+
"--all",
|
|
175
|
+
help="Show all sections including defaults",
|
|
176
|
+
),
|
|
177
|
+
] = False,
|
|
178
|
+
expand_vars: Annotated[
|
|
179
|
+
bool,
|
|
180
|
+
typer.Option(
|
|
181
|
+
"--expand",
|
|
182
|
+
help="Expand environment variable references",
|
|
183
|
+
),
|
|
184
|
+
] = False,
|
|
185
|
+
verbose: Annotated[
|
|
186
|
+
bool,
|
|
187
|
+
typer.Option(
|
|
188
|
+
"--verbose",
|
|
189
|
+
"-v",
|
|
190
|
+
help="Enable verbose logs",
|
|
191
|
+
),
|
|
192
|
+
] = False,
|
|
193
|
+
) -> None:
|
|
194
|
+
"""Show configuration for a profile.
|
|
195
|
+
|
|
196
|
+
By default shows env var references like ${ARIZE_API_KEY}.
|
|
197
|
+
Use --expand to show expanded values.
|
|
198
|
+
Use --all to show all sections including defaults.
|
|
199
|
+
"""
|
|
200
|
+
# Use profile from context if not specified
|
|
201
|
+
config = ConfigManager.load(profile, expand_vars)
|
|
202
|
+
|
|
203
|
+
# Display configuration
|
|
204
|
+
text_bold(f"\nProfile: {profile}")
|
|
205
|
+
if profile == ConfigManager.get_active_profile():
|
|
206
|
+
console.print("[green](active)[/green]")
|
|
207
|
+
new_line()
|
|
208
|
+
|
|
209
|
+
# Determine which sections to show
|
|
210
|
+
default_config = Config(
|
|
211
|
+
auth=AuthConfig(api_key="dummy"),
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
def is_customized(section_name: str) -> bool:
|
|
215
|
+
"""Check if a section has customized (non-default) values."""
|
|
216
|
+
if section_name == "profile" or section_name == "auth":
|
|
217
|
+
return True # Always show these
|
|
218
|
+
config_section = getattr(config, section_name)
|
|
219
|
+
default_section = getattr(default_config, section_name, None)
|
|
220
|
+
if default_section is None:
|
|
221
|
+
return True
|
|
222
|
+
return config_section != default_section
|
|
223
|
+
|
|
224
|
+
# Auth section (always shown)
|
|
225
|
+
emphasis("Authentication:")
|
|
226
|
+
key = config.auth.api_key
|
|
227
|
+
key = key if _is_env_var_ref(key) else mask(key)
|
|
228
|
+
text(f" API Key: {key}")
|
|
229
|
+
|
|
230
|
+
# Routing section
|
|
231
|
+
if all_sections or is_customized("routing"):
|
|
232
|
+
emphasis("\nRouting:")
|
|
233
|
+
if config.routing.region:
|
|
234
|
+
text(f" Region: {config.routing.region}")
|
|
235
|
+
if config.routing.single_host:
|
|
236
|
+
text(f" Single Host: {config.routing.single_host}")
|
|
237
|
+
if config.routing.single_port:
|
|
238
|
+
text(f" Single Port: {config.routing.single_port}")
|
|
239
|
+
if config.routing.base_domain:
|
|
240
|
+
text(f" Base Domain: {config.routing.base_domain}")
|
|
241
|
+
if not (
|
|
242
|
+
config.routing.region
|
|
243
|
+
or config.routing.single_host
|
|
244
|
+
or config.routing.base_domain
|
|
245
|
+
):
|
|
246
|
+
text(f" API Scheme: {config.routing.api_scheme}")
|
|
247
|
+
text(f" API Host: {config.routing.api_host}")
|
|
248
|
+
text(f" OTLP Scheme: {config.routing.otlp_scheme}")
|
|
249
|
+
text(f" OTLP Host: {config.routing.otlp_host}")
|
|
250
|
+
text(f" Flight Scheme: {config.routing.flight_scheme}")
|
|
251
|
+
text(f" Flight Host: {config.routing.flight_host}")
|
|
252
|
+
text(f" Flight Port: {config.routing.flight_port}")
|
|
253
|
+
|
|
254
|
+
# Transport section
|
|
255
|
+
if all_sections or is_customized("transport"):
|
|
256
|
+
emphasis("\nTransport:")
|
|
257
|
+
text(f" Stream Max Workers: {config.transport.stream_max_workers}")
|
|
258
|
+
text(
|
|
259
|
+
f" Stream Max Queue Bound: {config.transport.stream_max_queue_bound}"
|
|
260
|
+
)
|
|
261
|
+
text(
|
|
262
|
+
f" PyArrow Max Chunksize: {config.transport.pyarrow_max_chunksize}"
|
|
263
|
+
)
|
|
264
|
+
text(
|
|
265
|
+
f" Max HTTP Payload Size: {config.transport.max_http_payload_size_mb} MB"
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
# Security section
|
|
269
|
+
if all_sections or is_customized("security"):
|
|
270
|
+
emphasis("\nSecurity:")
|
|
271
|
+
val = config.security.request_verify
|
|
272
|
+
if _is_bool(str(config.security.request_verify)):
|
|
273
|
+
val = bool(config.security.request_verify)
|
|
274
|
+
text(f" Request Verify: {val}")
|
|
275
|
+
|
|
276
|
+
# Storage section
|
|
277
|
+
if all_sections or is_customized("storage"):
|
|
278
|
+
emphasis("\nStorage:")
|
|
279
|
+
text(f" Directory: {config.storage.directory}")
|
|
280
|
+
text(f" Cache: {config.storage.cache_enabled}")
|
|
281
|
+
|
|
282
|
+
# Output section (always shown)
|
|
283
|
+
emphasis("\nOutput:")
|
|
284
|
+
text(f" Format: {config.output.format}")
|
|
285
|
+
|
|
286
|
+
new_line()
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
@app.command("use")
|
|
290
|
+
@handle_errors
|
|
291
|
+
def use_profile(
|
|
292
|
+
profile: Annotated[
|
|
293
|
+
str,
|
|
294
|
+
typer.Argument(help="Profile name to activate"),
|
|
295
|
+
],
|
|
296
|
+
verbose: Annotated[
|
|
297
|
+
bool,
|
|
298
|
+
typer.Option(
|
|
299
|
+
"--verbose",
|
|
300
|
+
"-v",
|
|
301
|
+
help="Enable verbose logs",
|
|
302
|
+
),
|
|
303
|
+
] = False,
|
|
304
|
+
) -> None:
|
|
305
|
+
"""Switch to a different configuration profile.
|
|
306
|
+
|
|
307
|
+
Makes the specified profile active for all future commands.
|
|
308
|
+
"""
|
|
309
|
+
ConfigManager.set_active_profile(profile)
|
|
310
|
+
success(f"Switched to profile '{profile}'")
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
@app.command("delete")
|
|
314
|
+
@handle_errors
|
|
315
|
+
def delete_profile(
|
|
316
|
+
profile: Annotated[
|
|
317
|
+
str,
|
|
318
|
+
typer.Argument(help="Profile name to delete"),
|
|
319
|
+
],
|
|
320
|
+
force: Annotated[
|
|
321
|
+
bool,
|
|
322
|
+
typer.Option(
|
|
323
|
+
"--force",
|
|
324
|
+
"-f",
|
|
325
|
+
help="Skip confirmation",
|
|
326
|
+
),
|
|
327
|
+
] = False,
|
|
328
|
+
verbose: Annotated[
|
|
329
|
+
bool,
|
|
330
|
+
typer.Option(
|
|
331
|
+
"--verbose",
|
|
332
|
+
"-v",
|
|
333
|
+
help="Enable verbose logs",
|
|
334
|
+
),
|
|
335
|
+
] = False,
|
|
336
|
+
) -> None:
|
|
337
|
+
"""Delete a configuration profile.
|
|
338
|
+
|
|
339
|
+
Cannot delete the default profile or currently active profile.
|
|
340
|
+
"""
|
|
341
|
+
if not force and not confirm(
|
|
342
|
+
f"Delete profile '{profile}'?",
|
|
343
|
+
default=False,
|
|
344
|
+
):
|
|
345
|
+
info("Profile not deleted")
|
|
346
|
+
raise typer.Exit()
|
|
347
|
+
|
|
348
|
+
ConfigManager.delete_profile(profile)
|
|
349
|
+
success(f"Deleted profile '{profile}'")
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def _is_bool(val: str) -> bool:
|
|
353
|
+
"""Check if a string represents a boolean value."""
|
|
354
|
+
return val.lower() in ("true", "false")
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def _is_env_var_ref(val: str) -> bool:
|
|
358
|
+
"""Check if a string is an environment variable reference."""
|
|
359
|
+
return val.startswith("${") and val.endswith("}")
|