futurehouse-client 0.0.2__py3-none-any.whl → 0.0.4__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.
- futurehouse_client/__init__.py +4 -4
- futurehouse_client/clients/__init__.py +4 -4
- futurehouse_client/clients/rest_client.py +51 -47
- futurehouse_client/models/__init__.py +4 -4
- futurehouse_client/models/app.py +5 -5
- {futurehouse_client-0.0.2.dist-info → futurehouse_client-0.0.4.dist-info}/METADATA +5 -5
- futurehouse_client-0.0.4.dist-info/RECORD +14 -0
- futurehouse_client-0.0.2.dist-info/RECORD +0 -14
- {futurehouse_client-0.0.2.dist-info → futurehouse_client-0.0.4.dist-info}/WHEEL +0 -0
- {futurehouse_client-0.0.2.dist-info → futurehouse_client-0.0.4.dist-info}/top_level.txt +0 -0
futurehouse_client/__init__.py
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
from .clients.job_client import JobClient, JobNames
|
2
|
-
from .clients.rest_client import
|
2
|
+
from .clients.rest_client import PQATaskResponse, TaskResponse, TaskResponseVerbose
|
3
3
|
from .clients.rest_client import RestClient as FutureHouseClient
|
4
4
|
|
5
5
|
__all__ = [
|
6
6
|
"FutureHouseClient",
|
7
7
|
"JobClient",
|
8
8
|
"JobNames",
|
9
|
-
"
|
10
|
-
"
|
11
|
-
"
|
9
|
+
"PQATaskResponse",
|
10
|
+
"TaskResponse",
|
11
|
+
"TaskResponseVerbose",
|
12
12
|
]
|
@@ -1,12 +1,12 @@
|
|
1
1
|
from .job_client import JobClient, JobNames
|
2
|
-
from .rest_client import
|
2
|
+
from .rest_client import PQATaskResponse, TaskResponse, TaskResponseVerbose
|
3
3
|
from .rest_client import RestClient as FutureHouseClient
|
4
4
|
|
5
5
|
__all__ = [
|
6
6
|
"FutureHouseClient",
|
7
7
|
"JobClient",
|
8
8
|
"JobNames",
|
9
|
-
"
|
10
|
-
"
|
11
|
-
"
|
9
|
+
"PQATaskResponse",
|
10
|
+
"TaskResponse",
|
11
|
+
"TaskResponseVerbose",
|
12
12
|
]
|
@@ -29,10 +29,9 @@ from futurehouse_client.clients import JobNames
|
|
29
29
|
from futurehouse_client.models.app import (
|
30
30
|
APIKeyPayload,
|
31
31
|
AuthType,
|
32
|
-
|
33
|
-
CrowDeploymentConfig,
|
34
|
-
JobRequest,
|
32
|
+
JobDeploymentConfig,
|
35
33
|
Stage,
|
34
|
+
TaskRequest,
|
36
35
|
)
|
37
36
|
from futurehouse_client.utils.module_utils import (
|
38
37
|
OrganizationSelector,
|
@@ -41,7 +40,7 @@ from futurehouse_client.utils.module_utils import (
|
|
41
40
|
|
42
41
|
logger = logging.getLogger(__name__)
|
43
42
|
|
44
|
-
|
43
|
+
TaskRequest.model_rebuild()
|
45
44
|
|
46
45
|
FILE_UPLOAD_IGNORE_PARTS = {
|
47
46
|
".ruff_cache",
|
@@ -57,6 +56,10 @@ class RestClientError(Exception):
|
|
57
56
|
"""Base exception for REST client errors."""
|
58
57
|
|
59
58
|
|
59
|
+
class TaskFetchError(RestClientError):
|
60
|
+
"""Raised when there's an error fetching a task."""
|
61
|
+
|
62
|
+
|
60
63
|
class JobFetchError(RestClientError):
|
61
64
|
"""Raised when there's an error fetching a job."""
|
62
65
|
|
@@ -73,13 +76,13 @@ class InvalidTaskDescriptionError(Exception):
|
|
73
76
|
JWT_TOKEN_CACHE_EXPIRY: int = 300 # seconds
|
74
77
|
|
75
78
|
|
76
|
-
class
|
77
|
-
"""Base class for
|
79
|
+
class TaskResponse(BaseModel):
|
80
|
+
"""Base class for task responses. This holds attributes shared over all futurehouse jobs."""
|
78
81
|
|
79
82
|
model_config = ConfigDict(extra="ignore")
|
80
83
|
|
81
84
|
status: str
|
82
|
-
|
85
|
+
query: str
|
83
86
|
user: str
|
84
87
|
created_at: datetime
|
85
88
|
job_name: str
|
@@ -88,7 +91,7 @@ class JobResponse(BaseModel):
|
|
88
91
|
build_owner: str | None = None
|
89
92
|
environment_name: str | None = None
|
90
93
|
agent_name: str | None = None
|
91
|
-
|
94
|
+
task_id: UUID | None = None
|
92
95
|
|
93
96
|
@model_validator(mode="before")
|
94
97
|
@classmethod
|
@@ -96,13 +99,13 @@ class JobResponse(BaseModel):
|
|
96
99
|
# Extract fields from environment frame state
|
97
100
|
if not isinstance(data, dict):
|
98
101
|
return data
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
+
# TODO: We probably want to remove these two once we define the final names.
|
103
|
+
data["job_name"] = data.get("crow")
|
104
|
+
data["query"] = data.get("task")
|
102
105
|
if not (env_frame := data.get("environment_frame", {})):
|
103
106
|
return data
|
104
107
|
state = env_frame.get("state", {}).get("state", {})
|
105
|
-
data["
|
108
|
+
data["task_id"] = cast(UUID, state.get("id")) if state.get("id") else None
|
106
109
|
if not (metadata := data.get("metadata", {})):
|
107
110
|
return data
|
108
111
|
data["environment_name"] = metadata.get("environment_name")
|
@@ -110,7 +113,7 @@ class JobResponse(BaseModel):
|
|
110
113
|
return data
|
111
114
|
|
112
115
|
|
113
|
-
class
|
116
|
+
class PQATaskResponse(TaskResponse):
|
114
117
|
model_config = ConfigDict(extra="ignore")
|
115
118
|
|
116
119
|
answer: str | None = None
|
@@ -143,15 +146,15 @@ class PQAJobResponse(JobResponse):
|
|
143
146
|
|
144
147
|
return data
|
145
148
|
|
146
|
-
def clean_verbose(self) -> "
|
149
|
+
def clean_verbose(self) -> "TaskResponse":
|
147
150
|
"""Clean the verbose response from the server."""
|
148
151
|
self.request = None
|
149
152
|
self.response = None
|
150
153
|
return self
|
151
154
|
|
152
155
|
|
153
|
-
class
|
154
|
-
"""Class for responses to include all the fields of a
|
156
|
+
class TaskResponseVerbose(TaskResponse):
|
157
|
+
"""Class for responses to include all the fields of a task response."""
|
155
158
|
|
156
159
|
model_config = ConfigDict(extra="allow")
|
157
160
|
|
@@ -170,7 +173,7 @@ class RestClient:
|
|
170
173
|
|
171
174
|
def __init__(
|
172
175
|
self,
|
173
|
-
stage: Stage = Stage.
|
176
|
+
stage: Stage = Stage.PROD,
|
174
177
|
service_uri: str | None = None,
|
175
178
|
organization: str | None = None,
|
176
179
|
auth_type: AuthType = AuthType.API_KEY,
|
@@ -330,66 +333,66 @@ class RestClient:
|
|
330
333
|
path: Path that was searched for files
|
331
334
|
|
332
335
|
Raises:
|
333
|
-
|
336
|
+
TaskFetchError: If no files were found
|
334
337
|
|
335
338
|
"""
|
336
339
|
if not files:
|
337
|
-
raise
|
340
|
+
raise TaskFetchError(f"No files found in {path}")
|
338
341
|
|
339
342
|
@retry(
|
340
343
|
stop=stop_after_attempt(MAX_RETRY_ATTEMPTS),
|
341
344
|
wait=wait_exponential(multiplier=RETRY_MULTIPLIER, max=MAX_RETRY_WAIT),
|
342
345
|
retry=retry_if_exception_type(Timeout),
|
343
346
|
)
|
344
|
-
def
|
345
|
-
self,
|
346
|
-
) -> "
|
347
|
-
"""Get details for a specific
|
347
|
+
def get_task(
|
348
|
+
self, task_id: str | None = None, history: bool = False, verbose: bool = False
|
349
|
+
) -> "TaskResponse":
|
350
|
+
"""Get details for a specific task."""
|
348
351
|
try:
|
349
|
-
|
352
|
+
task_id = task_id or self.trajectory_id
|
350
353
|
response = self.client.get(
|
351
|
-
f"/v0.1/trajectories/{
|
354
|
+
f"/v0.1/trajectories/{task_id}",
|
352
355
|
params={"history": history},
|
353
356
|
)
|
354
357
|
response.raise_for_status()
|
355
|
-
verbose_response =
|
358
|
+
verbose_response = TaskResponseVerbose(**response.json())
|
356
359
|
if verbose:
|
357
360
|
return verbose_response
|
358
361
|
if any(
|
359
362
|
JobNames.from_string(job_name) in verbose_response.job_name
|
360
363
|
for job_name in ["crow", "falcon", "owl", "dummy"]
|
361
364
|
):
|
362
|
-
return
|
363
|
-
return
|
365
|
+
return PQATaskResponse(**response.json())
|
366
|
+
return TaskResponse(**response.json())
|
364
367
|
except ValueError as e:
|
365
|
-
raise ValueError("Invalid
|
368
|
+
raise ValueError("Invalid task ID format. Must be a valid UUID.") from e
|
366
369
|
except Exception as e:
|
367
|
-
raise
|
370
|
+
raise TaskFetchError(f"Error getting task: {e!s}") from e
|
368
371
|
|
369
372
|
@retry(
|
370
373
|
stop=stop_after_attempt(MAX_RETRY_ATTEMPTS),
|
371
374
|
wait=wait_exponential(multiplier=RETRY_MULTIPLIER, max=MAX_RETRY_WAIT),
|
372
375
|
retry=retry_if_exception_type(Timeout),
|
373
376
|
)
|
374
|
-
def
|
375
|
-
"""Create a new futurehouse
|
376
|
-
if isinstance(
|
377
|
-
|
378
|
-
|
379
|
-
if isinstance(
|
380
|
-
|
381
|
-
|
377
|
+
def create_task(self, task_data: TaskRequest | dict[str, Any]):
|
378
|
+
"""Create a new futurehouse task."""
|
379
|
+
if isinstance(task_data, dict):
|
380
|
+
task_data = TaskRequest.model_validate(task_data)
|
381
|
+
|
382
|
+
if isinstance(task_data.name, JobNames):
|
383
|
+
task_data.name = task_data.name.from_stage(
|
384
|
+
task_data.name.name,
|
382
385
|
self.stage,
|
383
386
|
)
|
384
387
|
|
385
388
|
try:
|
386
389
|
response = self.client.post(
|
387
|
-
"/v0.1/crows", json=
|
390
|
+
"/v0.1/crows", json=task_data.model_dump(mode="json")
|
388
391
|
)
|
389
392
|
response.raise_for_status()
|
390
393
|
self.trajectory_id = response.json()["trajectory_id"]
|
391
394
|
except Exception as e:
|
392
|
-
raise
|
395
|
+
raise TaskFetchError(f"Error creating task: {e!s}") from e
|
393
396
|
return self.trajectory_id
|
394
397
|
|
395
398
|
@retry(
|
@@ -405,17 +408,16 @@ class RestClient:
|
|
405
408
|
return response.json()
|
406
409
|
|
407
410
|
# TODO: Refactor later so we don't have to ignore PLR0915
|
408
|
-
# TODO: Rename this to create_job. I'm not doing it now to avoid breaking the infrastructure.
|
409
411
|
@retry(
|
410
412
|
stop=stop_after_attempt(MAX_RETRY_ATTEMPTS),
|
411
413
|
wait=wait_exponential(multiplier=RETRY_MULTIPLIER, max=MAX_RETRY_WAIT),
|
412
414
|
retry=retry_if_exception_type(Timeout),
|
413
415
|
)
|
414
|
-
def
|
415
|
-
"""Creates a
|
416
|
+
def create_job(self, config: JobDeploymentConfig) -> dict[str, Any]: # noqa: PLR0915
|
417
|
+
"""Creates a futurehouse job deployment from the environment and environment files.
|
416
418
|
|
417
419
|
Args:
|
418
|
-
config: Configuration object containing all necessary parameters for
|
420
|
+
config: Configuration object containing all necessary parameters for job deployment.
|
419
421
|
|
420
422
|
Returns:
|
421
423
|
A response object containing metadata of the build.
|
@@ -434,7 +436,7 @@ class RestClient:
|
|
434
436
|
raise InvalidTaskDescriptionError(
|
435
437
|
"Task description cannot be None or empty. Ensure your from_task environment function has a valid docstring."
|
436
438
|
" If you are deploying with your Environment as a dependency, "
|
437
|
-
"you must add a `task_description` to your `
|
439
|
+
"you must add a `task_description` to your `JobDeploymentConfig`.",
|
438
440
|
)
|
439
441
|
selected_org = OrganizationSelector.select_organization(self.organizations)
|
440
442
|
if selected_org is None:
|
@@ -583,9 +585,11 @@ class RestClient:
|
|
583
585
|
except HTTPStatusError as e:
|
584
586
|
error_detail = response.json()
|
585
587
|
error_message = error_detail.get("detail", str(e))
|
586
|
-
raise
|
588
|
+
raise JobCreationError(
|
589
|
+
f"Server validation error: {error_message}"
|
590
|
+
) from e
|
587
591
|
except Exception as e:
|
588
|
-
raise
|
592
|
+
raise JobCreationError(f"Error generating docker image: {e!s}") from e
|
589
593
|
return build_context
|
590
594
|
|
591
595
|
|
@@ -1,21 +1,21 @@
|
|
1
1
|
from .app import (
|
2
2
|
AuthType,
|
3
|
-
CrowDeploymentConfig,
|
4
3
|
DockerContainerConfiguration,
|
5
4
|
FramePath,
|
6
|
-
|
5
|
+
JobDeploymentConfig,
|
7
6
|
RuntimeConfig,
|
8
7
|
Stage,
|
9
8
|
Step,
|
9
|
+
TaskRequest,
|
10
10
|
)
|
11
11
|
|
12
12
|
__all__ = [
|
13
13
|
"AuthType",
|
14
|
-
"CrowDeploymentConfig",
|
15
14
|
"DockerContainerConfiguration",
|
16
15
|
"FramePath",
|
17
|
-
"
|
16
|
+
"JobDeploymentConfig",
|
18
17
|
"RuntimeConfig",
|
19
18
|
"Stage",
|
20
19
|
"Step",
|
20
|
+
"TaskRequest",
|
21
21
|
]
|
futurehouse_client/models/app.py
CHANGED
@@ -342,7 +342,7 @@ class DockerContainerConfiguration(BaseModel):
|
|
342
342
|
return self
|
343
343
|
|
344
344
|
|
345
|
-
class
|
345
|
+
class JobDeploymentConfig(BaseModel):
|
346
346
|
model_config = ConfigDict(
|
347
347
|
extra="forbid",
|
348
348
|
arbitrary_types_allowed=True, # Allows for agent: Agent | str
|
@@ -581,7 +581,7 @@ class RuntimeConfig(BaseModel):
|
|
581
581
|
default=None,
|
582
582
|
description=(
|
583
583
|
"Agent configuration to use for this job. If None, it will default to the "
|
584
|
-
"agent selected during Crow deployment in the
|
584
|
+
"agent selected during Crow deployment in the JobDeploymentConfig object."
|
585
585
|
),
|
586
586
|
)
|
587
587
|
continued_job_id: UUID | None = Field(
|
@@ -607,10 +607,10 @@ class RuntimeConfig(BaseModel):
|
|
607
607
|
return value
|
608
608
|
|
609
609
|
|
610
|
-
class
|
611
|
-
|
610
|
+
class TaskRequest(BaseModel):
|
611
|
+
task_id: UUID | None = Field(
|
612
612
|
default=None,
|
613
|
-
description="Optional
|
613
|
+
description="Optional task identifier",
|
614
614
|
alias="id",
|
615
615
|
)
|
616
616
|
name: "str | JobNames" = Field(
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: futurehouse-client
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.4
|
4
4
|
Summary: A client for interacting with endpoints of the FutureHouse service.
|
5
5
|
Author-email: FutureHouse technical staff <hello@futurehouse.org>
|
6
6
|
Classifier: Operating System :: OS Independent
|
@@ -70,7 +70,7 @@ from aviary.core import DummyEnv
|
|
70
70
|
import ldp
|
71
71
|
|
72
72
|
client = CrowClient(
|
73
|
-
stage=Stage.
|
73
|
+
stage=Stage.PROD,
|
74
74
|
auth_type=AuthType.API_KEY,
|
75
75
|
api_key="your_api_key",
|
76
76
|
)
|
@@ -110,7 +110,7 @@ from crow_client import CrowClient
|
|
110
110
|
from crow_client.models import Stage, AuthType
|
111
111
|
|
112
112
|
client = CrowClient(
|
113
|
-
stage=Stage.
|
113
|
+
stage=Stage.PROD,
|
114
114
|
organization="your_organization",
|
115
115
|
auth_type=AuthType.API_KEY,
|
116
116
|
api_key="your_api_key",
|
@@ -150,7 +150,7 @@ from crow_client import CrowClient, JobNames
|
|
150
150
|
from crow_client.models import AuthType, Stage
|
151
151
|
|
152
152
|
client = CrowClient(
|
153
|
-
stage=Stage.
|
153
|
+
stage=Stage.PROD,
|
154
154
|
auth_type=AuthType.API_KEY,
|
155
155
|
api_key="your_api_key",
|
156
156
|
)
|
@@ -186,7 +186,7 @@ from crow_client import CrowClient, JobNames
|
|
186
186
|
from crow_client.models import AuthType, Stage
|
187
187
|
|
188
188
|
client = CrowClient(
|
189
|
-
stage=Stage.
|
189
|
+
stage=Stage.PROD,
|
190
190
|
auth_type=AuthType.API_KEY,
|
191
191
|
api_key="your_api_key",
|
192
192
|
)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
futurehouse_client/__init__.py,sha256=ddxO7JE97c6bt7LjNglZZ2Ql8bYCGI9laSFeh9MP6VU,344
|
2
|
+
futurehouse_client/clients/__init__.py,sha256=tFWqwIAY5PvwfOVsCje4imjTpf6xXNRMh_UHIKVI1_0,320
|
3
|
+
futurehouse_client/clients/job_client.py,sha256=RNgdSJVI1vjQSypdfswHX0Gvv_XnKG4bZjklf3WdSuk,8828
|
4
|
+
futurehouse_client/clients/rest_client.py,sha256=GtHDTGHdz0f6xzBWaufqh3f76Life66df6abrJrSKvA,25116
|
5
|
+
futurehouse_client/models/__init__.py,sha256=ta3jFLM_LsDz1rKDmx8rja8sT7WtSKoFvMgLF0yFpvA,342
|
6
|
+
futurehouse_client/models/app.py,sha256=G8_-I3aQnRIyxFJT3snTSWsPcZZ2nEvYkRaE5sVdeys,22299
|
7
|
+
futurehouse_client/models/client.py,sha256=n4HD0KStKLm6Ek9nL9ylP-bkK10yzAaD1uIDF83Qp_A,1828
|
8
|
+
futurehouse_client/models/rest.py,sha256=W-wNFTN7HALYFFphw-RQYRMm6_TSa1cl4T-mZ1msk90,393
|
9
|
+
futurehouse_client/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
|
+
futurehouse_client/utils/module_utils.py,sha256=aFyd-X-pDARXz9GWpn8SSViUVYdSbuy9vSkrzcVIaGI,4955
|
11
|
+
futurehouse_client-0.0.4.dist-info/METADATA,sha256=mb918ca4LdaKsctNjrzswLkfq-NAfD0s6CmjWqyvo3I,8898
|
12
|
+
futurehouse_client-0.0.4.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
13
|
+
futurehouse_client-0.0.4.dist-info/top_level.txt,sha256=TRuLUCt_qBnggdFHCX4O_BoCu1j2X43lKfIZC-ElwWY,19
|
14
|
+
futurehouse_client-0.0.4.dist-info/RECORD,,
|
@@ -1,14 +0,0 @@
|
|
1
|
-
futurehouse_client/__init__.py,sha256=QhDraw1J-DNc2kEq8E3iMIMokbllTHuVEOXgUCIT8qc,338
|
2
|
-
futurehouse_client/clients/__init__.py,sha256=JpqWhXNRo4-xe9-SA_9QI5Z5P3xlJ-b4STGaljQpj3k,314
|
3
|
-
futurehouse_client/clients/job_client.py,sha256=RNgdSJVI1vjQSypdfswHX0Gvv_XnKG4bZjklf3WdSuk,8828
|
4
|
-
futurehouse_client/clients/rest_client.py,sha256=Qu6Ui-ot_pAJG-55Ll_IjOWLOkFELDaTObLkay7vxEY,25101
|
5
|
-
futurehouse_client/models/__init__.py,sha256=VBKYb_2kmxtDwp9LvTL7BEFyioekggQsrQg5I_dhZoI,342
|
6
|
-
futurehouse_client/models/app.py,sha256=_MO31xvKJaAztWz4KrV13ty4POg-RuWrGgqmvS9w2bc,22298
|
7
|
-
futurehouse_client/models/client.py,sha256=n4HD0KStKLm6Ek9nL9ylP-bkK10yzAaD1uIDF83Qp_A,1828
|
8
|
-
futurehouse_client/models/rest.py,sha256=W-wNFTN7HALYFFphw-RQYRMm6_TSa1cl4T-mZ1msk90,393
|
9
|
-
futurehouse_client/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
|
-
futurehouse_client/utils/module_utils.py,sha256=aFyd-X-pDARXz9GWpn8SSViUVYdSbuy9vSkrzcVIaGI,4955
|
11
|
-
futurehouse_client-0.0.2.dist-info/METADATA,sha256=lOwcSqbXH6Y1MDN22bW4j7VlsLItc0aG3lHaysdA-TU,8894
|
12
|
-
futurehouse_client-0.0.2.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
13
|
-
futurehouse_client-0.0.2.dist-info/top_level.txt,sha256=TRuLUCt_qBnggdFHCX4O_BoCu1j2X43lKfIZC-ElwWY,19
|
14
|
-
futurehouse_client-0.0.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|