lightningrod-ai 0.1.6__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.
- lightningrod/__init__.py +66 -0
- lightningrod/_display.py +204 -0
- lightningrod/_errors.py +67 -0
- lightningrod/_generated/__init__.py +8 -0
- lightningrod/_generated/api/__init__.py +1 -0
- lightningrod/_generated/api/datasets/__init__.py +1 -0
- lightningrod/_generated/api/datasets/create_dataset_datasets_post.py +133 -0
- lightningrod/_generated/api/datasets/get_dataset_datasets_dataset_id_get.py +168 -0
- lightningrod/_generated/api/datasets/get_dataset_samples_datasets_dataset_id_samples_get.py +209 -0
- lightningrod/_generated/api/datasets/upload_samples_datasets_dataset_id_samples_post.py +190 -0
- lightningrod/_generated/api/file_sets/__init__.py +1 -0
- lightningrod/_generated/api/file_sets/add_file_to_set_filesets_file_set_id_files_post.py +190 -0
- lightningrod/_generated/api/file_sets/create_file_set_filesets_post.py +174 -0
- lightningrod/_generated/api/file_sets/get_file_set_filesets_file_set_id_get.py +168 -0
- lightningrod/_generated/api/file_sets/list_file_sets_filesets_get.py +173 -0
- lightningrod/_generated/api/file_sets/list_files_in_set_filesets_file_set_id_files_get.py +209 -0
- lightningrod/_generated/api/files/__init__.py +1 -0
- lightningrod/_generated/api/files/create_file_upload_files_post.py +174 -0
- lightningrod/_generated/api/open_ai_compatible/__init__.py +1 -0
- lightningrod/_generated/api/open_ai_compatible/chat_completions_openai_chat_completions_post.py +174 -0
- lightningrod/_generated/api/organizations/__init__.py +1 -0
- lightningrod/_generated/api/organizations/get_balance_organizations_balance_get.py +131 -0
- lightningrod/_generated/api/samples/__init__.py +1 -0
- lightningrod/_generated/api/samples/validate_sample_samples_validate_post.py +174 -0
- lightningrod/_generated/api/transform_jobs/__init__.py +1 -0
- lightningrod/_generated/api/transform_jobs/cost_estimation_transform_jobs_cost_estimation_post.py +174 -0
- lightningrod/_generated/api/transform_jobs/create_transform_job_transform_jobs_post.py +174 -0
- lightningrod/_generated/api/transform_jobs/get_transform_job_metrics_transform_jobs_job_id_metrics_get.py +172 -0
- lightningrod/_generated/api/transform_jobs/get_transform_job_transform_jobs_job_id_get.py +168 -0
- lightningrod/_generated/client.py +268 -0
- lightningrod/_generated/errors.py +16 -0
- lightningrod/_generated/models/__init__.py +147 -0
- lightningrod/_generated/models/answer_type.py +129 -0
- lightningrod/_generated/models/answer_type_enum.py +11 -0
- lightningrod/_generated/models/balance_response.py +61 -0
- lightningrod/_generated/models/chat_completion_request.py +216 -0
- lightningrod/_generated/models/chat_completion_response.py +146 -0
- lightningrod/_generated/models/chat_message.py +69 -0
- lightningrod/_generated/models/choice.py +97 -0
- lightningrod/_generated/models/create_dataset_response.py +61 -0
- lightningrod/_generated/models/create_file_set_file_request.py +101 -0
- lightningrod/_generated/models/create_file_set_file_request_metadata_type_0.py +46 -0
- lightningrod/_generated/models/create_file_set_request.py +83 -0
- lightningrod/_generated/models/create_file_upload_request.py +91 -0
- lightningrod/_generated/models/create_file_upload_response.py +165 -0
- lightningrod/_generated/models/create_file_upload_response_metadata_type_0.py +46 -0
- lightningrod/_generated/models/create_transform_job_request.py +312 -0
- lightningrod/_generated/models/dataset_metadata.py +69 -0
- lightningrod/_generated/models/estimate_cost_request.py +243 -0
- lightningrod/_generated/models/estimate_cost_response.py +117 -0
- lightningrod/_generated/models/event_usage_summary.py +80 -0
- lightningrod/_generated/models/file_set.py +128 -0
- lightningrod/_generated/models/file_set_file.py +203 -0
- lightningrod/_generated/models/file_set_file_metadata_type_0.py +57 -0
- lightningrod/_generated/models/file_set_query_seed_generator.py +136 -0
- lightningrod/_generated/models/file_set_seed_generator.py +126 -0
- lightningrod/_generated/models/filter_criteria.py +83 -0
- lightningrod/_generated/models/forward_looking_question.py +130 -0
- lightningrod/_generated/models/forward_looking_question_generator.py +217 -0
- lightningrod/_generated/models/gdelt_seed_generator.py +103 -0
- lightningrod/_generated/models/http_validation_error.py +79 -0
- lightningrod/_generated/models/job_usage.py +185 -0
- lightningrod/_generated/models/job_usage_by_step_type_0.py +59 -0
- lightningrod/_generated/models/label.py +143 -0
- lightningrod/_generated/models/list_file_set_files_response.py +113 -0
- lightningrod/_generated/models/list_file_sets_response.py +75 -0
- lightningrod/_generated/models/llm_model_usage_summary.py +98 -0
- lightningrod/_generated/models/mock_transform_config.py +243 -0
- lightningrod/_generated/models/mock_transform_config_metadata_additions.py +46 -0
- lightningrod/_generated/models/model_config.py +316 -0
- lightningrod/_generated/models/model_source_type.py +16 -0
- lightningrod/_generated/models/news_context.py +82 -0
- lightningrod/_generated/models/news_context_generator.py +127 -0
- lightningrod/_generated/models/news_seed_generator.py +220 -0
- lightningrod/_generated/models/paginated_samples_response.py +113 -0
- lightningrod/_generated/models/pipeline_metrics_response.py +99 -0
- lightningrod/_generated/models/question.py +74 -0
- lightningrod/_generated/models/question_and_label_generator.py +217 -0
- lightningrod/_generated/models/question_generator.py +217 -0
- lightningrod/_generated/models/question_pipeline.py +417 -0
- lightningrod/_generated/models/question_renderer.py +123 -0
- lightningrod/_generated/models/rag_context.py +82 -0
- lightningrod/_generated/models/response_message.py +69 -0
- lightningrod/_generated/models/rollout.py +130 -0
- lightningrod/_generated/models/rollout_generator.py +139 -0
- lightningrod/_generated/models/rollout_parsed_output_type_0.py +46 -0
- lightningrod/_generated/models/sample.py +323 -0
- lightningrod/_generated/models/sample_meta.py +46 -0
- lightningrod/_generated/models/seed.py +135 -0
- lightningrod/_generated/models/step_cost_breakdown.py +109 -0
- lightningrod/_generated/models/transform_job.py +268 -0
- lightningrod/_generated/models/transform_job_status.py +11 -0
- lightningrod/_generated/models/transform_step_metrics_response.py +131 -0
- lightningrod/_generated/models/transform_type.py +25 -0
- lightningrod/_generated/models/upload_samples_request.py +75 -0
- lightningrod/_generated/models/upload_samples_response.py +69 -0
- lightningrod/_generated/models/usage.py +77 -0
- lightningrod/_generated/models/usage_summary.py +102 -0
- lightningrod/_generated/models/usage_summary_events.py +59 -0
- lightningrod/_generated/models/usage_summary_llm_by_model.py +59 -0
- lightningrod/_generated/models/validate_sample_response.py +69 -0
- lightningrod/_generated/models/validation_error.py +90 -0
- lightningrod/_generated/models/web_search_labeler.py +120 -0
- lightningrod/_generated/py.typed +1 -0
- lightningrod/_generated/types.py +54 -0
- lightningrod/client.py +48 -0
- lightningrod/datasets/__init__.py +4 -0
- lightningrod/datasets/client.py +174 -0
- lightningrod/datasets/dataset.py +255 -0
- lightningrod/files/__init__.py +0 -0
- lightningrod/files/client.py +58 -0
- lightningrod/filesets/__init__.py +0 -0
- lightningrod/filesets/client.py +106 -0
- lightningrod/organization/__init__.py +0 -0
- lightningrod/organization/client.py +17 -0
- lightningrod/py.typed +0 -0
- lightningrod/transforms/__init__.py +0 -0
- lightningrod/transforms/client.py +154 -0
- lightningrod_ai-0.1.6.dist-info/METADATA +122 -0
- lightningrod_ai-0.1.6.dist-info/RECORD +123 -0
- lightningrod_ai-0.1.6.dist-info/WHEEL +5 -0
- lightningrod_ai-0.1.6.dist-info/licenses/LICENSE +23 -0
- lightningrod_ai-0.1.6.dist-info/top_level.txt +1 -0
lightningrod/__init__.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Lightning Rod Python SDK
|
|
3
|
+
|
|
4
|
+
AI-powered forecasting dataset generation platform.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from lightningrod.client import LightningRod
|
|
8
|
+
from lightningrod.datasets.dataset import Dataset
|
|
9
|
+
from lightningrod._generated.models import (
|
|
10
|
+
AnswerType,
|
|
11
|
+
AnswerTypeEnum,
|
|
12
|
+
TransformJob,
|
|
13
|
+
TransformJobStatus,
|
|
14
|
+
NewsSeedGenerator,
|
|
15
|
+
GdeltSeedGenerator,
|
|
16
|
+
NewsContextGenerator,
|
|
17
|
+
QuestionGenerator,
|
|
18
|
+
QuestionAndLabelGenerator,
|
|
19
|
+
ForwardLookingQuestionGenerator,
|
|
20
|
+
QuestionPipeline,
|
|
21
|
+
QuestionRenderer,
|
|
22
|
+
WebSearchLabeler,
|
|
23
|
+
FilterCriteria,
|
|
24
|
+
Sample,
|
|
25
|
+
SampleMeta,
|
|
26
|
+
Seed,
|
|
27
|
+
# TODO(filesets): Enable when filesets are publicly supported
|
|
28
|
+
# FileSetSeedGenerator,
|
|
29
|
+
# FileSetQuerySeedGenerator,
|
|
30
|
+
# CreateFileSetRequest,
|
|
31
|
+
# CreateFileSetFileRequest,
|
|
32
|
+
# CreateFileUploadResponse,
|
|
33
|
+
# FileSetFile,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
__version__ = "0.1.6"
|
|
37
|
+
__all__ = [
|
|
38
|
+
"AnswerType",
|
|
39
|
+
"AnswerTypeEnum",
|
|
40
|
+
"AnswerTypes",
|
|
41
|
+
"AsyncDataset",
|
|
42
|
+
"Dataset",
|
|
43
|
+
# TODO(filesets): Enable when filesets are publicly supported
|
|
44
|
+
# "FileSetSeedGenerator",
|
|
45
|
+
# "FileSetQuerySeedGenerator",
|
|
46
|
+
# "CreateFileSetRequest",
|
|
47
|
+
# "CreateFileSetFileRequest",
|
|
48
|
+
# "CreateFileUploadResponse",
|
|
49
|
+
# "FileSetFile",
|
|
50
|
+
"FilterCriteria",
|
|
51
|
+
"ForwardLookingQuestionGenerator",
|
|
52
|
+
"GdeltSeedGenerator",
|
|
53
|
+
"NewsContextGenerator",
|
|
54
|
+
"NewsSeedGenerator",
|
|
55
|
+
"QuestionAndLabelGenerator",
|
|
56
|
+
"QuestionGenerator",
|
|
57
|
+
"QuestionPipeline",
|
|
58
|
+
"QuestionRenderer",
|
|
59
|
+
"Sample",
|
|
60
|
+
"SampleMeta",
|
|
61
|
+
"Seed",
|
|
62
|
+
"TransformJob",
|
|
63
|
+
"TransformJobStatus",
|
|
64
|
+
"WebSearchLabeler",
|
|
65
|
+
"LightningRod",
|
|
66
|
+
]
|
lightningrod/_display.py
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Optional
|
|
4
|
+
|
|
5
|
+
from rich.console import Console, Group, RenderableType
|
|
6
|
+
from rich.panel import Panel
|
|
7
|
+
from rich.table import Table
|
|
8
|
+
from rich.text import Text
|
|
9
|
+
|
|
10
|
+
from lightningrod._generated.types import Unset
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _is_set(value: Any) -> bool:
|
|
14
|
+
return not isinstance(value, Unset) and value is not None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _safe_markup(text: Optional[str]) -> Text:
|
|
18
|
+
"""Parse text as rich markup, falling back to plain text if parsing fails."""
|
|
19
|
+
if text is None:
|
|
20
|
+
return Text("")
|
|
21
|
+
try:
|
|
22
|
+
return Text.from_markup(text)
|
|
23
|
+
except Exception:
|
|
24
|
+
return Text(text)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _format_duration(seconds: float) -> str:
|
|
28
|
+
if seconds < 60:
|
|
29
|
+
return f"{seconds:.0f}s"
|
|
30
|
+
minutes = int(seconds // 60)
|
|
31
|
+
secs = int(seconds % 60)
|
|
32
|
+
return f"{minutes}m {secs}s"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _build_cost_lines(job: Any) -> list[RenderableType]:
|
|
36
|
+
"""Build cost info lines from job.usage. Returns empty list if no data."""
|
|
37
|
+
if not _is_set(job.usage):
|
|
38
|
+
return []
|
|
39
|
+
usage = job.usage
|
|
40
|
+
lines: list[RenderableType] = []
|
|
41
|
+
|
|
42
|
+
# Total cost
|
|
43
|
+
if _is_set(usage.current_cost_dollars):
|
|
44
|
+
lines.append(_safe_markup(f" [bold]Total cost:[/bold] [bright_green]${usage.current_cost_dollars:.2f}[/bright_green]"))
|
|
45
|
+
if _is_set(usage.max_cost_dollars):
|
|
46
|
+
lines.append(_safe_markup(f" [bold]Budget:[/bold] ${usage.max_cost_dollars:.2f}"))
|
|
47
|
+
if _is_set(usage.estimated_cost_dollars):
|
|
48
|
+
lines.append(_safe_markup(f" [bold]Estimated:[/bold] ${usage.estimated_cost_dollars:.2f}"))
|
|
49
|
+
|
|
50
|
+
return lines
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def build_live_display(
|
|
54
|
+
metrics: Any = None,
|
|
55
|
+
job: Any = None,
|
|
56
|
+
) -> RenderableType:
|
|
57
|
+
"""Build the live display renderable for the polling loop."""
|
|
58
|
+
renderables: list[RenderableType] = []
|
|
59
|
+
|
|
60
|
+
renderables.append(_safe_markup("[bold bright_blue]>> Pipeline Running[/bold bright_blue]"))
|
|
61
|
+
renderables.append(Text(""))
|
|
62
|
+
|
|
63
|
+
# Cost summary from job.usage
|
|
64
|
+
if job is not None:
|
|
65
|
+
cost_lines = _build_cost_lines(job)
|
|
66
|
+
if cost_lines:
|
|
67
|
+
renderables.extend(cost_lines)
|
|
68
|
+
renderables.append(Text(""))
|
|
69
|
+
|
|
70
|
+
if metrics is None:
|
|
71
|
+
renderables.append(Text("Waiting for metrics...", style="dim italic"))
|
|
72
|
+
return Panel(
|
|
73
|
+
Group(*renderables),
|
|
74
|
+
border_style="bright_blue",
|
|
75
|
+
padding=(1, 2),
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Per-step table
|
|
79
|
+
table = Table(show_header=True, header_style="bold cyan", expand=True)
|
|
80
|
+
table.add_column("Step", style="bold", no_wrap=True)
|
|
81
|
+
table.add_column("Progress", width=20)
|
|
82
|
+
table.add_column("In", justify="right")
|
|
83
|
+
table.add_column("Out", justify="right")
|
|
84
|
+
table.add_column("Rejected", justify="right")
|
|
85
|
+
table.add_column("Errors", justify="right")
|
|
86
|
+
table.add_column("Duration", justify="right")
|
|
87
|
+
|
|
88
|
+
for step in sorted(metrics.steps, key=lambda s: s.step_index):
|
|
89
|
+
if step.progress >= 1.0:
|
|
90
|
+
status = Text("Complete", style="bold bright_green")
|
|
91
|
+
elif step.progress > 0:
|
|
92
|
+
status = Text("In progress", style="bold bright_yellow")
|
|
93
|
+
else:
|
|
94
|
+
status = Text("Pending", style="dim")
|
|
95
|
+
|
|
96
|
+
rejected_style = "bright_red" if step.rejected_count > 0 else "dim"
|
|
97
|
+
error_style = "bold bright_red" if step.error_count > 0 else "dim"
|
|
98
|
+
|
|
99
|
+
table.add_row(
|
|
100
|
+
step.transform_name,
|
|
101
|
+
status,
|
|
102
|
+
str(step.input_rows),
|
|
103
|
+
str(step.output_rows),
|
|
104
|
+
Text(str(step.rejected_count), style=rejected_style),
|
|
105
|
+
Text(str(step.error_count), style=error_style),
|
|
106
|
+
_format_duration(step.duration_seconds),
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
renderables.append(table)
|
|
110
|
+
|
|
111
|
+
return Panel(
|
|
112
|
+
Group(*renderables),
|
|
113
|
+
border_style="bright_blue",
|
|
114
|
+
padding=(1, 2),
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _is_notebook() -> bool:
|
|
119
|
+
"""Check if we're running inside a Jupyter notebook."""
|
|
120
|
+
try:
|
|
121
|
+
from IPython import get_ipython
|
|
122
|
+
shell = get_ipython()
|
|
123
|
+
return shell is not None and shell.__class__.__name__ == "ZMQInteractiveShell"
|
|
124
|
+
except ImportError:
|
|
125
|
+
return False
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def run_live_display(
|
|
129
|
+
poll_callback: Any,
|
|
130
|
+
poll_interval: float = 15,
|
|
131
|
+
warning_message: Optional[str] = None,
|
|
132
|
+
) -> None:
|
|
133
|
+
"""Run a live-updating display that polls for metrics.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
poll_callback: A callable that returns (metrics, job, is_running) each cycle.
|
|
137
|
+
- metrics: PipelineMetricsResponse or None
|
|
138
|
+
- job: TransformJob with current status/usage
|
|
139
|
+
- is_running: bool, False to stop the loop
|
|
140
|
+
poll_interval: Seconds between polls.
|
|
141
|
+
warning_message: Optional warning to persist above the live display.
|
|
142
|
+
"""
|
|
143
|
+
import time
|
|
144
|
+
console = Console()
|
|
145
|
+
|
|
146
|
+
if _is_notebook():
|
|
147
|
+
from IPython.display import clear_output
|
|
148
|
+
metrics, job, is_running = poll_callback()
|
|
149
|
+
while is_running:
|
|
150
|
+
clear_output(wait=True)
|
|
151
|
+
if warning_message:
|
|
152
|
+
display_warning(warning_message)
|
|
153
|
+
console.print(build_live_display(metrics=metrics, job=job))
|
|
154
|
+
time.sleep(poll_interval)
|
|
155
|
+
metrics, job, is_running = poll_callback()
|
|
156
|
+
else:
|
|
157
|
+
from rich.live import Live
|
|
158
|
+
with Live(
|
|
159
|
+
build_live_display(metrics=None, job=None),
|
|
160
|
+
console=console,
|
|
161
|
+
refresh_per_second=1,
|
|
162
|
+
transient=True,
|
|
163
|
+
) as live:
|
|
164
|
+
metrics, job, is_running = poll_callback()
|
|
165
|
+
while is_running:
|
|
166
|
+
live.update(build_live_display(metrics=metrics, job=job))
|
|
167
|
+
time.sleep(poll_interval)
|
|
168
|
+
metrics, job, is_running = poll_callback()
|
|
169
|
+
# Final update
|
|
170
|
+
live.update(build_live_display(metrics=metrics, job=job))
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def display_error(message: str, title: str = "Error", job: Any = None) -> None:
|
|
174
|
+
console = Console()
|
|
175
|
+
renderables: list[RenderableType] = []
|
|
176
|
+
|
|
177
|
+
renderables.append(_safe_markup(f"[bold bright_red]>> {title}[/bold bright_red]"))
|
|
178
|
+
renderables.append(Text(""))
|
|
179
|
+
renderables.append(_safe_markup(f"[bold]{message}[/bold]"))
|
|
180
|
+
|
|
181
|
+
if job is not None:
|
|
182
|
+
cost_lines = _build_cost_lines(job)
|
|
183
|
+
if cost_lines:
|
|
184
|
+
renderables.append(Text(""))
|
|
185
|
+
renderables.extend(cost_lines)
|
|
186
|
+
|
|
187
|
+
console.print(Panel(Group(*renderables), border_style="bright_red", padding=(1, 2)))
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def display_warning(message: str, title: str = "Warning", job: Any = None) -> None:
|
|
191
|
+
console = Console()
|
|
192
|
+
renderables: list[RenderableType] = []
|
|
193
|
+
|
|
194
|
+
renderables.append(_safe_markup(f"[bold yellow]>> {title}[/bold yellow]"))
|
|
195
|
+
renderables.append(Text(""))
|
|
196
|
+
renderables.append(_safe_markup(message))
|
|
197
|
+
|
|
198
|
+
if job is not None:
|
|
199
|
+
cost_lines = _build_cost_lines(job)
|
|
200
|
+
if cost_lines:
|
|
201
|
+
renderables.append(Text(""))
|
|
202
|
+
renderables.extend(cost_lines)
|
|
203
|
+
|
|
204
|
+
console.print(Panel(Group(*renderables), border_style="yellow", padding=(1, 2)))
|
lightningrod/_errors.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from http import HTTPStatus
|
|
3
|
+
from typing import Any, TypeVar
|
|
4
|
+
|
|
5
|
+
from lightningrod._display import display_error
|
|
6
|
+
from lightningrod._generated.models import HTTPValidationError
|
|
7
|
+
from lightningrod._generated.types import Response
|
|
8
|
+
|
|
9
|
+
T = TypeVar("T")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def extract_error_message(response: Response[Any], operation: str) -> str:
|
|
13
|
+
"""
|
|
14
|
+
Extract a detailed error message from a Response object.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
response: The Response object from sync_detailed
|
|
18
|
+
operation: Description of the operation that failed (e.g., "create dataset")
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
A detailed error message string
|
|
22
|
+
"""
|
|
23
|
+
if isinstance(response.parsed, HTTPValidationError):
|
|
24
|
+
return f"Failed to {operation}: {response.parsed.detail}"
|
|
25
|
+
|
|
26
|
+
if response.parsed is None:
|
|
27
|
+
status_code = response.status_code.value if isinstance(response.status_code, HTTPStatus) else response.status_code
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
error_data = json.loads(response.content.decode('utf-8'))
|
|
31
|
+
if isinstance(error_data, dict) and 'detail' in error_data:
|
|
32
|
+
detail = error_data['detail']
|
|
33
|
+
return f"Failed to {operation}: {detail} (HTTP {status_code})"
|
|
34
|
+
elif isinstance(error_data, dict):
|
|
35
|
+
return f"Failed to {operation}: {json.dumps(error_data)} (HTTP {status_code})"
|
|
36
|
+
except (json.JSONDecodeError, UnicodeDecodeError):
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
content_preview = response.content.decode('utf-8', errors='ignore')[:500]
|
|
40
|
+
if content_preview:
|
|
41
|
+
return f"Failed to {operation}: HTTP {status_code} - {content_preview}"
|
|
42
|
+
else:
|
|
43
|
+
return f"Failed to {operation}: HTTP {status_code} (no response body)"
|
|
44
|
+
|
|
45
|
+
return f"Failed to {operation}: unexpected response format"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def handle_response_error(response: Response[T], operation: str) -> T:
|
|
49
|
+
"""
|
|
50
|
+
Validate a Response object and return the parsed response, raising an exception if there's an error.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
response: The Response object from sync_detailed
|
|
54
|
+
operation: Description of the operation that failed (e.g., "create dataset")
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
The parsed response object
|
|
58
|
+
|
|
59
|
+
Raises:
|
|
60
|
+
Exception: If the response indicates an error (parsed is None or HTTPValidationError)
|
|
61
|
+
"""
|
|
62
|
+
if response.parsed is None or isinstance(response.parsed, HTTPValidationError):
|
|
63
|
+
error_msg = extract_error_message(response, operation)
|
|
64
|
+
display_error(error_msg, title=f"API Error: {operation}")
|
|
65
|
+
raise Exception(error_msg)
|
|
66
|
+
|
|
67
|
+
return response.parsed
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Contains methods for accessing the API"""
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Contains endpoint functions for accessing the API"""
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
from http import HTTPStatus
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
import httpx
|
|
5
|
+
|
|
6
|
+
from ... import errors
|
|
7
|
+
from ...client import AuthenticatedClient, Client
|
|
8
|
+
from ...models.create_dataset_response import CreateDatasetResponse
|
|
9
|
+
from ...types import Response
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _get_kwargs() -> dict[str, Any]:
|
|
13
|
+
_kwargs: dict[str, Any] = {
|
|
14
|
+
"method": "post",
|
|
15
|
+
"url": "/datasets",
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return _kwargs
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _parse_response(*, client: AuthenticatedClient | Client, response: httpx.Response) -> CreateDatasetResponse | None:
|
|
22
|
+
if response.status_code == 201:
|
|
23
|
+
response_201 = CreateDatasetResponse.from_dict(response.json())
|
|
24
|
+
|
|
25
|
+
return response_201
|
|
26
|
+
|
|
27
|
+
if client.raise_on_unexpected_status:
|
|
28
|
+
raise errors.UnexpectedStatus(response.status_code, response.content)
|
|
29
|
+
else:
|
|
30
|
+
return None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _build_response(
|
|
34
|
+
*, client: AuthenticatedClient | Client, response: httpx.Response
|
|
35
|
+
) -> Response[CreateDatasetResponse]:
|
|
36
|
+
return Response(
|
|
37
|
+
status_code=HTTPStatus(response.status_code),
|
|
38
|
+
content=response.content,
|
|
39
|
+
headers=response.headers,
|
|
40
|
+
parsed=_parse_response(client=client, response=response),
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def sync_detailed(
|
|
45
|
+
*,
|
|
46
|
+
client: AuthenticatedClient,
|
|
47
|
+
) -> Response[CreateDatasetResponse]:
|
|
48
|
+
"""Create Dataset
|
|
49
|
+
|
|
50
|
+
Create a new empty dataset
|
|
51
|
+
|
|
52
|
+
Raises:
|
|
53
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
54
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
Response[CreateDatasetResponse]
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
kwargs = _get_kwargs()
|
|
61
|
+
|
|
62
|
+
response = client.get_httpx_client().request(
|
|
63
|
+
**kwargs,
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
return _build_response(client=client, response=response)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def sync(
|
|
70
|
+
*,
|
|
71
|
+
client: AuthenticatedClient,
|
|
72
|
+
) -> CreateDatasetResponse | None:
|
|
73
|
+
"""Create Dataset
|
|
74
|
+
|
|
75
|
+
Create a new empty dataset
|
|
76
|
+
|
|
77
|
+
Raises:
|
|
78
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
79
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
CreateDatasetResponse
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
return sync_detailed(
|
|
86
|
+
client=client,
|
|
87
|
+
).parsed
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
async def asyncio_detailed(
|
|
91
|
+
*,
|
|
92
|
+
client: AuthenticatedClient,
|
|
93
|
+
) -> Response[CreateDatasetResponse]:
|
|
94
|
+
"""Create Dataset
|
|
95
|
+
|
|
96
|
+
Create a new empty dataset
|
|
97
|
+
|
|
98
|
+
Raises:
|
|
99
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
100
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
Response[CreateDatasetResponse]
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
kwargs = _get_kwargs()
|
|
107
|
+
|
|
108
|
+
response = await client.get_async_httpx_client().request(**kwargs)
|
|
109
|
+
|
|
110
|
+
return _build_response(client=client, response=response)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
async def asyncio(
|
|
114
|
+
*,
|
|
115
|
+
client: AuthenticatedClient,
|
|
116
|
+
) -> CreateDatasetResponse | None:
|
|
117
|
+
"""Create Dataset
|
|
118
|
+
|
|
119
|
+
Create a new empty dataset
|
|
120
|
+
|
|
121
|
+
Raises:
|
|
122
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
123
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
CreateDatasetResponse
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
return (
|
|
130
|
+
await asyncio_detailed(
|
|
131
|
+
client=client,
|
|
132
|
+
)
|
|
133
|
+
).parsed
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
from http import HTTPStatus
|
|
2
|
+
from typing import Any
|
|
3
|
+
from urllib.parse import quote
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
from ... import errors
|
|
8
|
+
from ...client import AuthenticatedClient, Client
|
|
9
|
+
from ...models.dataset_metadata import DatasetMetadata
|
|
10
|
+
from ...models.http_validation_error import HTTPValidationError
|
|
11
|
+
from ...types import Response
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _get_kwargs(
|
|
15
|
+
dataset_id: str,
|
|
16
|
+
) -> dict[str, Any]:
|
|
17
|
+
_kwargs: dict[str, Any] = {
|
|
18
|
+
"method": "get",
|
|
19
|
+
"url": "/datasets/{dataset_id}".format(
|
|
20
|
+
dataset_id=quote(str(dataset_id), safe=""),
|
|
21
|
+
),
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return _kwargs
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _parse_response(
|
|
28
|
+
*, client: AuthenticatedClient | Client, response: httpx.Response
|
|
29
|
+
) -> DatasetMetadata | HTTPValidationError | None:
|
|
30
|
+
if response.status_code == 200:
|
|
31
|
+
response_200 = DatasetMetadata.from_dict(response.json())
|
|
32
|
+
|
|
33
|
+
return response_200
|
|
34
|
+
|
|
35
|
+
if response.status_code == 422:
|
|
36
|
+
response_422 = HTTPValidationError.from_dict(response.json())
|
|
37
|
+
|
|
38
|
+
return response_422
|
|
39
|
+
|
|
40
|
+
if client.raise_on_unexpected_status:
|
|
41
|
+
raise errors.UnexpectedStatus(response.status_code, response.content)
|
|
42
|
+
else:
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _build_response(
|
|
47
|
+
*, client: AuthenticatedClient | Client, response: httpx.Response
|
|
48
|
+
) -> Response[DatasetMetadata | HTTPValidationError]:
|
|
49
|
+
return Response(
|
|
50
|
+
status_code=HTTPStatus(response.status_code),
|
|
51
|
+
content=response.content,
|
|
52
|
+
headers=response.headers,
|
|
53
|
+
parsed=_parse_response(client=client, response=response),
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def sync_detailed(
|
|
58
|
+
dataset_id: str,
|
|
59
|
+
*,
|
|
60
|
+
client: AuthenticatedClient,
|
|
61
|
+
) -> Response[DatasetMetadata | HTTPValidationError]:
|
|
62
|
+
"""Get Dataset
|
|
63
|
+
|
|
64
|
+
Get dataset metadata including ID and row count
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
dataset_id (str):
|
|
68
|
+
|
|
69
|
+
Raises:
|
|
70
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
71
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
Response[DatasetMetadata | HTTPValidationError]
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
kwargs = _get_kwargs(
|
|
78
|
+
dataset_id=dataset_id,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
response = client.get_httpx_client().request(
|
|
82
|
+
**kwargs,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
return _build_response(client=client, response=response)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def sync(
|
|
89
|
+
dataset_id: str,
|
|
90
|
+
*,
|
|
91
|
+
client: AuthenticatedClient,
|
|
92
|
+
) -> DatasetMetadata | HTTPValidationError | None:
|
|
93
|
+
"""Get Dataset
|
|
94
|
+
|
|
95
|
+
Get dataset metadata including ID and row count
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
dataset_id (str):
|
|
99
|
+
|
|
100
|
+
Raises:
|
|
101
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
102
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
DatasetMetadata | HTTPValidationError
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
return sync_detailed(
|
|
109
|
+
dataset_id=dataset_id,
|
|
110
|
+
client=client,
|
|
111
|
+
).parsed
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
async def asyncio_detailed(
|
|
115
|
+
dataset_id: str,
|
|
116
|
+
*,
|
|
117
|
+
client: AuthenticatedClient,
|
|
118
|
+
) -> Response[DatasetMetadata | HTTPValidationError]:
|
|
119
|
+
"""Get Dataset
|
|
120
|
+
|
|
121
|
+
Get dataset metadata including ID and row count
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
dataset_id (str):
|
|
125
|
+
|
|
126
|
+
Raises:
|
|
127
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
128
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
Response[DatasetMetadata | HTTPValidationError]
|
|
132
|
+
"""
|
|
133
|
+
|
|
134
|
+
kwargs = _get_kwargs(
|
|
135
|
+
dataset_id=dataset_id,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
response = await client.get_async_httpx_client().request(**kwargs)
|
|
139
|
+
|
|
140
|
+
return _build_response(client=client, response=response)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
async def asyncio(
|
|
144
|
+
dataset_id: str,
|
|
145
|
+
*,
|
|
146
|
+
client: AuthenticatedClient,
|
|
147
|
+
) -> DatasetMetadata | HTTPValidationError | None:
|
|
148
|
+
"""Get Dataset
|
|
149
|
+
|
|
150
|
+
Get dataset metadata including ID and row count
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
dataset_id (str):
|
|
154
|
+
|
|
155
|
+
Raises:
|
|
156
|
+
errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True.
|
|
157
|
+
httpx.TimeoutException: If the request takes longer than Client.timeout.
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
DatasetMetadata | HTTPValidationError
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
return (
|
|
164
|
+
await asyncio_detailed(
|
|
165
|
+
dataset_id=dataset_id,
|
|
166
|
+
client=client,
|
|
167
|
+
)
|
|
168
|
+
).parsed
|