agenta 0.2.12__tar.gz → 0.3.0__tar.gz
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.
Potentially problematic release.
This version of agenta might be problematic. Click here for more details.
- {agenta-0.2.12 → agenta-0.3.0}/PKG-INFO +1 -1
- {agenta-0.2.12 → agenta-0.3.0}/agenta/__init__.py +1 -0
- {agenta-0.2.12 → agenta-0.3.0}/agenta/cli/helper.py +8 -5
- {agenta-0.2.12 → agenta-0.3.0}/agenta/cli/main.py +8 -2
- {agenta-0.2.12 → agenta-0.3.0}/agenta/cli/variant_commands.py +49 -39
- {agenta-0.2.12 → agenta-0.3.0}/agenta/client/api_models.py +6 -0
- agenta-0.3.0/agenta/client/client.py +253 -0
- {agenta-0.2.12 → agenta-0.3.0}/pyproject.toml +1 -1
- agenta-0.2.12/agenta/client/client.py +0 -177
- {agenta-0.2.12 → agenta-0.3.0}/README.md +0 -0
- {agenta-0.2.12 → agenta-0.3.0}/agenta/client/Readme.md +0 -0
- {agenta-0.2.12 → agenta-0.3.0}/agenta/client/__init__.py +0 -0
- {agenta-0.2.12 → agenta-0.3.0}/agenta/config.py +0 -0
- {agenta-0.2.12 → agenta-0.3.0}/agenta/config.toml +0 -0
- {agenta-0.2.12 → agenta-0.3.0}/agenta/docker/docker-assets/Dockerfile.template +0 -0
- {agenta-0.2.12 → agenta-0.3.0}/agenta/docker/docker-assets/README.md +0 -0
- {agenta-0.2.12 → agenta-0.3.0}/agenta/docker/docker-assets/entrypoint.sh +0 -0
- {agenta-0.2.12 → agenta-0.3.0}/agenta/docker/docker-assets/main.py +0 -0
- {agenta-0.2.12 → agenta-0.3.0}/agenta/docker/docker_utils.py +0 -0
- {agenta-0.2.12 → agenta-0.3.0}/agenta/sdk/__init__.py +0 -0
- {agenta-0.2.12 → agenta-0.3.0}/agenta/sdk/agenta.py +0 -0
- {agenta-0.2.12 → agenta-0.3.0}/agenta/sdk/context.py +0 -0
- {agenta-0.2.12 → agenta-0.3.0}/agenta/sdk/init.py +0 -0
- {agenta-0.2.12 → agenta-0.3.0}/agenta/sdk/router.py +0 -0
- {agenta-0.2.12 → agenta-0.3.0}/agenta/sdk/types.py +0 -0
- {agenta-0.2.12 → agenta-0.3.0}/agenta/templates/compose_email/README.md +0 -0
- {agenta-0.2.12 → agenta-0.3.0}/agenta/templates/compose_email/app.py +0 -0
- {agenta-0.2.12 → agenta-0.3.0}/agenta/templates/compose_email/env.example +0 -0
- {agenta-0.2.12 → agenta-0.3.0}/agenta/templates/compose_email/requirements.txt +0 -0
- {agenta-0.2.12 → agenta-0.3.0}/agenta/templates/compose_email/template.toml +0 -0
- {agenta-0.2.12 → agenta-0.3.0}/agenta/templates/extract_data_to_json/README.md +0 -0
- {agenta-0.2.12 → agenta-0.3.0}/agenta/templates/extract_data_to_json/app.py +0 -0
- {agenta-0.2.12 → agenta-0.3.0}/agenta/templates/extract_data_to_json/env.example +0 -0
- {agenta-0.2.12 → agenta-0.3.0}/agenta/templates/extract_data_to_json/requirements.txt +0 -0
- {agenta-0.2.12 → agenta-0.3.0}/agenta/templates/extract_data_to_json/template.toml +0 -0
- {agenta-0.2.12 → agenta-0.3.0}/agenta/templates/simple_prompt/README.md +0 -0
- {agenta-0.2.12 → agenta-0.3.0}/agenta/templates/simple_prompt/app.py +0 -0
- {agenta-0.2.12 → agenta-0.3.0}/agenta/templates/simple_prompt/env.example +0 -0
- {agenta-0.2.12 → agenta-0.3.0}/agenta/templates/simple_prompt/requirements.txt +0 -0
- {agenta-0.2.12 → agenta-0.3.0}/agenta/templates/simple_prompt/template.toml +0 -0
|
@@ -7,19 +7,20 @@ from agenta.client.api_models import AppVariant
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def update_variants_from_backend(
|
|
10
|
-
|
|
10
|
+
app_id: str, config: MutableMapping[str, Any], host: str
|
|
11
11
|
) -> MutableMapping[str, Any]:
|
|
12
12
|
"""Reads the list of variants from the backend and updates the config accordingly
|
|
13
13
|
|
|
14
14
|
Arguments:
|
|
15
|
-
|
|
15
|
+
app_id -- the app id
|
|
16
16
|
config -- the config loaded using toml.load
|
|
17
17
|
|
|
18
18
|
Returns:
|
|
19
19
|
a new config object later to be saved using toml.dump(config, config_file.open('w'))
|
|
20
20
|
"""
|
|
21
|
-
variants: List[AppVariant] = client.list_variants(
|
|
21
|
+
variants: List[AppVariant] = client.list_variants(app_id, host)
|
|
22
22
|
config["variants"] = [variant.variant_name for variant in variants]
|
|
23
|
+
config["variant_ids"] = [variant.variant_id for variant in variants]
|
|
23
24
|
return config
|
|
24
25
|
|
|
25
26
|
|
|
@@ -31,10 +32,12 @@ def update_config_from_backend(config_file: Path, host: str):
|
|
|
31
32
|
"""
|
|
32
33
|
assert config_file.exists(), "Config file does not exist!"
|
|
33
34
|
config = toml.load(config_file)
|
|
34
|
-
|
|
35
|
+
app_id = config["app_id"]
|
|
35
36
|
if "variants" not in config:
|
|
36
37
|
config["variants"] = []
|
|
37
|
-
|
|
38
|
+
if "variant_ids" not in config:
|
|
39
|
+
config["variant_ids"] = []
|
|
40
|
+
config = update_variants_from_backend(app_id, config, host)
|
|
38
41
|
toml.dump(config, config_file.open("w"))
|
|
39
42
|
|
|
40
43
|
|
|
@@ -7,6 +7,8 @@ from pathlib import Path
|
|
|
7
7
|
import click
|
|
8
8
|
import questionary
|
|
9
9
|
import toml
|
|
10
|
+
|
|
11
|
+
from agenta.client import client
|
|
10
12
|
from agenta.cli import variant_commands
|
|
11
13
|
|
|
12
14
|
|
|
@@ -29,7 +31,7 @@ def check_latest_version() -> Union[str, None]:
|
|
|
29
31
|
import requests
|
|
30
32
|
|
|
31
33
|
try:
|
|
32
|
-
response = requests.get("https://pypi.org/pypi/agenta/json")
|
|
34
|
+
response = requests.get("https://pypi.org/pypi/agenta/json", timeout=360)
|
|
33
35
|
response.raise_for_status()
|
|
34
36
|
latest_version = response.json()["info"]["version"]
|
|
35
37
|
return latest_version
|
|
@@ -103,7 +105,11 @@ def init(app_name: str):
|
|
|
103
105
|
else "http://" + backend_host
|
|
104
106
|
)
|
|
105
107
|
|
|
106
|
-
|
|
108
|
+
# Get app_id after creating new app in the backend server
|
|
109
|
+
app_id = client.create_new_app(app_name, backend_host)
|
|
110
|
+
|
|
111
|
+
# Set app toml configuration
|
|
112
|
+
config = {"app_name": app_name, "app_id": app_id, "backend_host": backend_host}
|
|
107
113
|
with open("config.toml", "w") as config_file:
|
|
108
114
|
toml.dump(config, config_file)
|
|
109
115
|
|
|
@@ -39,8 +39,11 @@ def add_variant(app_folder: str, file_name: str, host: str) -> str:
|
|
|
39
39
|
app_path = Path(app_folder)
|
|
40
40
|
config_file = app_path / "config.toml"
|
|
41
41
|
config = toml.load(config_file)
|
|
42
|
-
app_name = config["
|
|
43
|
-
|
|
42
|
+
app_name = config["app_name"]
|
|
43
|
+
app_id = config["app_id"]
|
|
44
|
+
base_name = file_name.removesuffix(".py")
|
|
45
|
+
config_name = "default"
|
|
46
|
+
variant_name = f"{base_name}.{config_name}"
|
|
44
47
|
# check files in folder
|
|
45
48
|
app_file = app_path / file_name
|
|
46
49
|
if not app_file.exists():
|
|
@@ -71,7 +74,7 @@ def add_variant(app_folder: str, file_name: str, host: str) -> str:
|
|
|
71
74
|
sys.exit(0)
|
|
72
75
|
|
|
73
76
|
# Validate variant name
|
|
74
|
-
if not re.match("^[a-zA-Z0-9_]+$",
|
|
77
|
+
if not re.match("^[a-zA-Z0-9_]+$", base_name):
|
|
75
78
|
click.echo(
|
|
76
79
|
click.style(
|
|
77
80
|
"Invalid input. Please use only alphanumeric characters without spaces.",
|
|
@@ -82,6 +85,7 @@ def add_variant(app_folder: str, file_name: str, host: str) -> str:
|
|
|
82
85
|
|
|
83
86
|
# update the config file with the variant names from the backend
|
|
84
87
|
overwrite = False
|
|
88
|
+
|
|
85
89
|
if variant_name in config["variants"]:
|
|
86
90
|
overwrite = questionary.confirm(
|
|
87
91
|
"This variant already exists. Do you want to overwrite it?"
|
|
@@ -90,22 +94,22 @@ def add_variant(app_folder: str, file_name: str, host: str) -> str:
|
|
|
90
94
|
click.echo("Operation cancelled.")
|
|
91
95
|
sys.exit(0)
|
|
92
96
|
|
|
93
|
-
if not overwrite:
|
|
94
|
-
config["variants"].append(variant_name)
|
|
95
97
|
try:
|
|
96
98
|
click.echo(
|
|
97
99
|
click.style(
|
|
98
|
-
f"Preparing variant {
|
|
100
|
+
f"Preparing variant {base_name} into a tar file...",
|
|
101
|
+
fg="yellow",
|
|
99
102
|
)
|
|
100
103
|
)
|
|
101
104
|
tar_path = build_tar_docker_container(folder=app_path, file_name=file_name)
|
|
102
105
|
|
|
103
106
|
click.echo(
|
|
104
107
|
click.style(
|
|
105
|
-
f"Building variant {
|
|
108
|
+
f"Building variant {base_name} into a docker image...",
|
|
109
|
+
fg="yellow",
|
|
106
110
|
)
|
|
107
111
|
)
|
|
108
|
-
image: Image = client.send_docker_tar(
|
|
112
|
+
image: Image = client.send_docker_tar(app_id, base_name, tar_path, host)
|
|
109
113
|
# docker_image: DockerImage = build_and_upload_docker_image(
|
|
110
114
|
# folder=app_path, app_name=app_name, variant_name=variant_name)
|
|
111
115
|
except Exception as ex:
|
|
@@ -115,15 +119,18 @@ def add_variant(app_folder: str, file_name: str, host: str) -> str:
|
|
|
115
119
|
if overwrite:
|
|
116
120
|
click.echo(
|
|
117
121
|
click.style(
|
|
118
|
-
f"Updating
|
|
122
|
+
f"Updating {base_name} to server...",
|
|
123
|
+
fg="yellow",
|
|
119
124
|
)
|
|
120
125
|
)
|
|
121
|
-
|
|
126
|
+
variant_id = config["variant_ids"][config["variants"].index(variant_name)]
|
|
127
|
+
client.update_variant_image(variant_id, image, host)
|
|
122
128
|
else:
|
|
123
|
-
click.echo(
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
129
|
+
click.echo(click.style(f"Adding {variant_name} to server...", fg="yellow"))
|
|
130
|
+
response = client.add_variant_to_server(app_id, base_name, image, host)
|
|
131
|
+
variant_id = response["variant_id"]
|
|
132
|
+
config["variants"].append(variant_name)
|
|
133
|
+
config["variant_ids"].append(variant_id)
|
|
127
134
|
except Exception as ex:
|
|
128
135
|
if overwrite:
|
|
129
136
|
click.echo(click.style(f"Error while updating variant: {ex}", fg="red"))
|
|
@@ -151,10 +158,10 @@ def add_variant(app_folder: str, file_name: str, host: str) -> str:
|
|
|
151
158
|
# TODO: Improve this stupid design
|
|
152
159
|
return None
|
|
153
160
|
else:
|
|
154
|
-
return
|
|
161
|
+
return variant_id
|
|
155
162
|
|
|
156
163
|
|
|
157
|
-
def start_variant(
|
|
164
|
+
def start_variant(variant_id: str, app_folder: str, host: str):
|
|
158
165
|
"""
|
|
159
166
|
Starts a container for an existing variant
|
|
160
167
|
Args:
|
|
@@ -164,17 +171,18 @@ def start_variant(variant_name: str, app_folder: str, host: str):
|
|
|
164
171
|
app_folder = Path(app_folder)
|
|
165
172
|
config_file = app_folder / "config.toml"
|
|
166
173
|
config = toml.load(config_file)
|
|
167
|
-
app_name = config["
|
|
174
|
+
app_name = config["app_name"]
|
|
175
|
+
app_id = config["app_id"]
|
|
168
176
|
|
|
169
177
|
if len(config["variants"]) == 0:
|
|
170
178
|
click.echo("No variants found. Please add a variant first.")
|
|
171
179
|
return
|
|
172
180
|
|
|
173
|
-
if
|
|
174
|
-
if
|
|
181
|
+
if variant_id:
|
|
182
|
+
if variant_id not in config["variant_ids"]:
|
|
175
183
|
click.echo(
|
|
176
184
|
click.style(
|
|
177
|
-
f"Variant {
|
|
185
|
+
f"Variant {variant_id} not found in backend. Maybe you removed it in the webUI?",
|
|
178
186
|
fg="red",
|
|
179
187
|
)
|
|
180
188
|
)
|
|
@@ -183,30 +191,30 @@ def start_variant(variant_name: str, app_folder: str, host: str):
|
|
|
183
191
|
variant_name = questionary.select(
|
|
184
192
|
"Please choose a variant", choices=config["variants"]
|
|
185
193
|
).ask()
|
|
194
|
+
variant_id = config["variant_ids"][config["variants"].index(variant_name)]
|
|
186
195
|
|
|
187
|
-
endpoint = client.start_variant(
|
|
196
|
+
endpoint = client.start_variant(variant_id=variant_id, host=host)
|
|
188
197
|
click.echo("\n" + click.style("Congratulations! 🎉", bold=True, fg="green"))
|
|
189
198
|
click.echo(
|
|
190
|
-
click.style(
|
|
191
|
-
+ click.style(
|
|
199
|
+
click.style("Your app has been deployed locally as an API. 🚀", fg="cyan")
|
|
200
|
+
+ click.style(" You can access it here: ", fg="white")
|
|
192
201
|
+ click.style(f"{endpoint}/", bold=True, fg="yellow")
|
|
193
202
|
)
|
|
194
203
|
|
|
195
204
|
click.echo(
|
|
196
|
-
click.style(
|
|
197
|
-
+ click.style(
|
|
205
|
+
click.style("\nRead the API documentation. 📚", fg="cyan")
|
|
206
|
+
+ click.style(" It's available at: ", fg="white")
|
|
198
207
|
+ click.style(f"{endpoint}/docs", bold=True, fg="yellow")
|
|
199
208
|
)
|
|
200
209
|
|
|
201
210
|
webui_host = "http://localhost:3000" if host == "localhost" else host
|
|
202
211
|
click.echo(
|
|
203
212
|
click.style(
|
|
204
|
-
"\nStart experimenting with your app in the playground. 🎮",
|
|
213
|
+
"\nStart experimenting with your app in the playground. 🎮",
|
|
214
|
+
fg="cyan",
|
|
205
215
|
)
|
|
206
216
|
+ click.style(" Go to: ", fg="white")
|
|
207
|
-
+ click.style(
|
|
208
|
-
f"{webui_host}/apps/{app_name}/playground", bold=True, fg="yellow"
|
|
209
|
-
)
|
|
217
|
+
+ click.style(f"{webui_host}/apps/{app_id}/playground", bold=True, fg="yellow")
|
|
210
218
|
+ "\n"
|
|
211
219
|
)
|
|
212
220
|
|
|
@@ -220,7 +228,7 @@ def remove_variant(variant_name: str, app_folder: str, host: str):
|
|
|
220
228
|
"""
|
|
221
229
|
config_file = Path(app_folder) / "config.toml"
|
|
222
230
|
config = toml.load(config_file)
|
|
223
|
-
app_name = config["
|
|
231
|
+
app_name = config["app_name"]
|
|
224
232
|
|
|
225
233
|
if variant_name:
|
|
226
234
|
if variant_name not in config["variants"]:
|
|
@@ -235,8 +243,9 @@ def remove_variant(variant_name: str, app_folder: str, host: str):
|
|
|
235
243
|
variant_name = questionary.select(
|
|
236
244
|
"Please choose a variant", choices=config["variants"]
|
|
237
245
|
).ask()
|
|
246
|
+
variant_id = config["variant_ids"][config["variants"].index(variant_name)]
|
|
238
247
|
try:
|
|
239
|
-
client.remove_variant(
|
|
248
|
+
client.remove_variant(variant_id, host)
|
|
240
249
|
except Exception as ex:
|
|
241
250
|
click.echo(
|
|
242
251
|
click.style(
|
|
@@ -263,8 +272,9 @@ def list_variants(app_folder: str, host: str):
|
|
|
263
272
|
"""
|
|
264
273
|
config_file = Path(app_folder) / "config.toml"
|
|
265
274
|
config = toml.load(config_file)
|
|
266
|
-
|
|
267
|
-
|
|
275
|
+
app_id = config["app_id"]
|
|
276
|
+
app_name = config["app_name"]
|
|
277
|
+
variants: List[AppVariant] = client.list_variants(app_id, host)
|
|
268
278
|
if variants:
|
|
269
279
|
for variant in variants:
|
|
270
280
|
helper.display_app_variant(variant)
|
|
@@ -313,7 +323,9 @@ def remove_variant_cli(variant_name: str, app_folder: str):
|
|
|
313
323
|
"""Remove an existing variant."""
|
|
314
324
|
config_check(app_folder)
|
|
315
325
|
remove_variant(
|
|
316
|
-
variant_name=variant_name,
|
|
326
|
+
variant_name=variant_name,
|
|
327
|
+
app_folder=app_folder,
|
|
328
|
+
host=get_host(app_folder),
|
|
317
329
|
)
|
|
318
330
|
|
|
319
331
|
|
|
@@ -344,17 +356,15 @@ def serve_cli(app_folder: str, file_name: str):
|
|
|
344
356
|
return
|
|
345
357
|
|
|
346
358
|
try:
|
|
347
|
-
|
|
348
|
-
app_folder=app_folder, file_name=file_name, host=host
|
|
349
|
-
)
|
|
359
|
+
variant_id = add_variant(app_folder=app_folder, file_name=file_name, host=host)
|
|
350
360
|
except Exception as e:
|
|
351
361
|
click.echo(click.style("Failed to add variant.", fg="red"))
|
|
352
362
|
click.echo(click.style(f"Error message: {str(e)}", fg="red"))
|
|
353
363
|
return
|
|
354
364
|
|
|
355
|
-
if
|
|
365
|
+
if variant_id:
|
|
356
366
|
try:
|
|
357
|
-
start_variant(
|
|
367
|
+
start_variant(variant_id=variant_id, app_folder=app_folder, host=host)
|
|
358
368
|
except ConnectionError:
|
|
359
369
|
error_msg = "Failed to connect to Agenta backend. Here's how you can solve the issue:\n"
|
|
360
370
|
error_msg += "- First, please ensure that the backend service is running and accessible.\n"
|
|
@@ -3,12 +3,18 @@ from typing import List, Optional, Dict, Any
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class AppVariant(BaseModel):
|
|
6
|
+
app_id: str
|
|
6
7
|
app_name: str
|
|
7
8
|
variant_name: str
|
|
9
|
+
variant_id: str
|
|
8
10
|
parameters: Optional[Dict[str, Any]]
|
|
9
11
|
previous_variant_name: Optional[str]
|
|
10
12
|
|
|
11
13
|
|
|
14
|
+
class Variant(BaseModel):
|
|
15
|
+
variant_id: str
|
|
16
|
+
|
|
17
|
+
|
|
12
18
|
class Image(BaseModel):
|
|
13
19
|
docker_id: str
|
|
14
20
|
tags: str
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import List, Optional, Dict
|
|
4
|
+
|
|
5
|
+
import requests
|
|
6
|
+
from agenta.client.api_models import AppVariant, Image
|
|
7
|
+
from requests.exceptions import RequestException
|
|
8
|
+
|
|
9
|
+
BACKEND_URL_SUFFIX = os.environ["BACKEND_URL_SUFFIX"]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class APIRequestError(Exception):
|
|
13
|
+
"""Exception to be raised when an API request fails."""
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_app_by_name(app_name: str, host: str) -> str:
|
|
17
|
+
"""Get app by its name on the server.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
app_name (str): Name of the app
|
|
21
|
+
host (str): Hostname of the server
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
response = requests.get(
|
|
25
|
+
f"{host}/{BACKEND_URL_SUFFIX}/apps/?app_name={app_name}/",
|
|
26
|
+
timeout=600,
|
|
27
|
+
)
|
|
28
|
+
if response.status_code != 200:
|
|
29
|
+
error_message = response.json()
|
|
30
|
+
raise APIRequestError(
|
|
31
|
+
f"Request to get app failed with status code {response.status_code} and error message: {error_message}."
|
|
32
|
+
)
|
|
33
|
+
return response.json()["app_id"]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def create_new_app(app_name: str, host: str) -> str:
|
|
37
|
+
"""Creates new app on the server.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
app_name (str): Name of the app
|
|
41
|
+
host (str): Hostname of the server
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
response = requests.post(
|
|
45
|
+
f"{host}/{BACKEND_URL_SUFFIX}/apps/",
|
|
46
|
+
json={"app_name": app_name},
|
|
47
|
+
timeout=600,
|
|
48
|
+
)
|
|
49
|
+
if response.status_code != 200:
|
|
50
|
+
error_message = response.json()
|
|
51
|
+
raise APIRequestError(
|
|
52
|
+
f"Request to create new app failed with status code {response.status_code} and error message: {error_message}."
|
|
53
|
+
)
|
|
54
|
+
return response.json()["app_id"]
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def add_variant_to_server(app_id: str, base_name: str, image: Image, host: str) -> Dict:
|
|
58
|
+
"""
|
|
59
|
+
Adds a variant to the server.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
app_id (str): The ID of the app to add the variant to.
|
|
63
|
+
variant_name (str): The name of the variant to add.
|
|
64
|
+
image (Image): The image to use for the variant.
|
|
65
|
+
host (str): The host URL of the server.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
dict: The JSON response from the server.
|
|
69
|
+
Raises:
|
|
70
|
+
APIRequestError: If the request to the server fails.
|
|
71
|
+
"""
|
|
72
|
+
variant_name = f"{base_name.lower()}.default"
|
|
73
|
+
payload = {
|
|
74
|
+
"variant_name": variant_name,
|
|
75
|
+
"base_name": base_name.lower(),
|
|
76
|
+
"config_name": "default",
|
|
77
|
+
"docker_id": image.docker_id,
|
|
78
|
+
"tags": image.tags,
|
|
79
|
+
}
|
|
80
|
+
response = requests.post(
|
|
81
|
+
f"{host}/{BACKEND_URL_SUFFIX}/apps/{app_id}/variant/from-image/",
|
|
82
|
+
json=payload,
|
|
83
|
+
timeout=600,
|
|
84
|
+
)
|
|
85
|
+
if response.status_code != 200:
|
|
86
|
+
error_message = response.json()
|
|
87
|
+
raise APIRequestError(
|
|
88
|
+
f"Request to app_variant endpoint failed with status code {response.status_code} and error message: {error_message}."
|
|
89
|
+
)
|
|
90
|
+
return response.json()
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def start_variant(
|
|
94
|
+
variant_id: str, host: str, env_vars: Optional[Dict[str, str]] = None
|
|
95
|
+
) -> str:
|
|
96
|
+
"""
|
|
97
|
+
Starts or stops a container with the given variant and exposes its endpoint.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
variant_id (str): The ID of the variant.
|
|
101
|
+
host (str): The host URL.
|
|
102
|
+
env_vars (Optional[Dict[str, str]]): Optional environment variables to inject into the container.
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
str: The endpoint of the container.
|
|
106
|
+
|
|
107
|
+
Raises:
|
|
108
|
+
APIRequestError: If the API request fails.
|
|
109
|
+
"""
|
|
110
|
+
payload = {}
|
|
111
|
+
payload["action"] = {"action": "START"}
|
|
112
|
+
if env_vars:
|
|
113
|
+
payload["env_vars"] = {"env_vars": env_vars}
|
|
114
|
+
|
|
115
|
+
try:
|
|
116
|
+
response = requests.put(
|
|
117
|
+
f"{host}/{BACKEND_URL_SUFFIX}/variants/{variant_id}/",
|
|
118
|
+
json=payload,
|
|
119
|
+
timeout=600,
|
|
120
|
+
)
|
|
121
|
+
if response.status_code == 404:
|
|
122
|
+
raise APIRequestError(
|
|
123
|
+
f"404: Variant with ID {variant_id} does not exist on the server."
|
|
124
|
+
)
|
|
125
|
+
elif response.status_code != 200:
|
|
126
|
+
error_message = response.text
|
|
127
|
+
raise APIRequestError(
|
|
128
|
+
f"Request to start variant endpoint failed with status code {response.status_code} and error message: {error_message}."
|
|
129
|
+
)
|
|
130
|
+
return response.json().get("uri", "")
|
|
131
|
+
|
|
132
|
+
except RequestException as e:
|
|
133
|
+
raise APIRequestError(f"An error occurred while making the request: {e}")
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def list_variants(app_id: str, host: str) -> List[AppVariant]:
|
|
137
|
+
"""
|
|
138
|
+
Returns a list of AppVariant objects for a given app_id and host.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
app_id (str): The ID of the app to retrieve variants for.
|
|
142
|
+
host (str): The URL of the host to make the request to.
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
List[AppVariant]: A list of AppVariant objects for the given app_id and host.
|
|
146
|
+
"""
|
|
147
|
+
response = requests.get(
|
|
148
|
+
f"{host}/{BACKEND_URL_SUFFIX}/apps/{app_id}/variants/",
|
|
149
|
+
timeout=600,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
# Check for successful request
|
|
153
|
+
if response.status_code != 200:
|
|
154
|
+
error_message = response.json()
|
|
155
|
+
raise APIRequestError(
|
|
156
|
+
f"Request to apps endpoint failed with status code {response.status_code} and error message: {error_message}."
|
|
157
|
+
)
|
|
158
|
+
app_variants = response.json()
|
|
159
|
+
return [AppVariant(**variant) for variant in app_variants]
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def remove_variant(variant_id: str, host: str):
|
|
163
|
+
"""
|
|
164
|
+
Sends a DELETE request to the Agenta backend to remove a variant with the given ID.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
variant_id (str): The ID of the variant to be removed.
|
|
168
|
+
host (str): The URL of the Agenta backend.
|
|
169
|
+
|
|
170
|
+
Raises:
|
|
171
|
+
APIRequestError: If the request to the remove_variant endpoint fails.
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
None
|
|
175
|
+
"""
|
|
176
|
+
response = requests.delete(
|
|
177
|
+
f"{host}/{BACKEND_URL_SUFFIX}/variants/{variant_id}",
|
|
178
|
+
headers={"Content-Type": "application/json"},
|
|
179
|
+
timeout=600,
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
# Check for successful request
|
|
183
|
+
if response.status_code != 200:
|
|
184
|
+
error_message = response.json()
|
|
185
|
+
raise APIRequestError(
|
|
186
|
+
f"Request to remove_variant endpoint failed with status code {response.status_code} and error message: {error_message}"
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def update_variant_image(variant_id: str, image: Image, host: str):
|
|
191
|
+
"""
|
|
192
|
+
Update the image of a variant with the given ID.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
variant_id (str): The ID of the variant to update.
|
|
196
|
+
image (Image): The new image to set for the variant.
|
|
197
|
+
host (str): The URL of the host to send the request to.
|
|
198
|
+
|
|
199
|
+
Raises:
|
|
200
|
+
APIRequestError: If the request to update the variant fails.
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
None
|
|
204
|
+
"""
|
|
205
|
+
response = requests.put(
|
|
206
|
+
f"{host}/{BACKEND_URL_SUFFIX}/variants/{variant_id}/image/",
|
|
207
|
+
json=image.dict(),
|
|
208
|
+
timeout=600,
|
|
209
|
+
)
|
|
210
|
+
if response.status_code != 200:
|
|
211
|
+
error_message = response.json()
|
|
212
|
+
raise APIRequestError(
|
|
213
|
+
f"Request to update app_variant failed with status code {response.status_code} and error message: {error_message}."
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def send_docker_tar(app_id: str, base_name: str, tar_path: Path, host: str) -> Image:
|
|
218
|
+
"""
|
|
219
|
+
Sends a Docker tar file to the specified host to build an image for the given app ID and variant name.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
app_id (str): The ID of the app.
|
|
223
|
+
base_name (str): The name of the codebase.
|
|
224
|
+
tar_path (Path): The path to the Docker tar file.
|
|
225
|
+
host (str): The URL of the host to send the request to.
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
Image: The built Docker image.
|
|
229
|
+
|
|
230
|
+
Raises:
|
|
231
|
+
Exception: If the response status code is 500, indicating that serving the variant failed.
|
|
232
|
+
"""
|
|
233
|
+
with tar_path.open("rb") as tar_file:
|
|
234
|
+
response = requests.post(
|
|
235
|
+
f"{host}/{BACKEND_URL_SUFFIX}/containers/build_image/?app_id={app_id}&base_name={base_name}",
|
|
236
|
+
files={
|
|
237
|
+
"tar_file": tar_file,
|
|
238
|
+
},
|
|
239
|
+
timeout=1200,
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
if response.status_code == 500:
|
|
243
|
+
response_error = response.json()
|
|
244
|
+
error_msg = "Serving the variant failed.\n"
|
|
245
|
+
error_msg += f"Log: {response_error}\n"
|
|
246
|
+
error_msg += "Here's how you may be able to solve the issue:\n"
|
|
247
|
+
error_msg += "- First, make sure that the requirements.txt file has all the dependencies that you need.\n"
|
|
248
|
+
error_msg += "- Second, check the Docker logs for the backend image to see the error when running the Docker container."
|
|
249
|
+
raise Exception(error_msg)
|
|
250
|
+
|
|
251
|
+
response.raise_for_status()
|
|
252
|
+
image = Image.parse_obj(response.json())
|
|
253
|
+
return image
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
from typing import List
|
|
4
|
-
|
|
5
|
-
import agenta.config
|
|
6
|
-
import requests
|
|
7
|
-
from agenta.client.api_models import AppVariant, Image
|
|
8
|
-
from docker.models.images import Image as DockerImage
|
|
9
|
-
|
|
10
|
-
BACKEND_URL_SUFFIX = os.environ["BACKEND_URL_SUFFIX"]
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class APIRequestError(Exception):
|
|
14
|
-
"""Exception to be raised when an API request fails."""
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def add_variant_to_server(app_name: str, variant_name: str, image: Image, host: str):
|
|
18
|
-
"""Adds a variant to the server.
|
|
19
|
-
|
|
20
|
-
Arguments:
|
|
21
|
-
app_name -- Name of the app
|
|
22
|
-
variant_name -- Name of the variant
|
|
23
|
-
image_name -- Name of the image
|
|
24
|
-
"""
|
|
25
|
-
app_variant: AppVariant = AppVariant(app_name=app_name, variant_name=variant_name)
|
|
26
|
-
response = requests.post(
|
|
27
|
-
f"{host}/{BACKEND_URL_SUFFIX}/app_variant/add/from_image/",
|
|
28
|
-
json={"app_variant": app_variant.dict(), "image": image.dict()},
|
|
29
|
-
timeout=600,
|
|
30
|
-
)
|
|
31
|
-
if response.status_code != 200:
|
|
32
|
-
error_message = response.json()
|
|
33
|
-
raise APIRequestError(
|
|
34
|
-
f"Request to app_variant endpoint failed with status code {response.status_code} and error message: {error_message}."
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def start_variant(app_name: str, variant_name: str, host: str) -> str:
|
|
39
|
-
"""Starts a container with the variant an expose its endpoint
|
|
40
|
-
|
|
41
|
-
Arguments:
|
|
42
|
-
app_name --
|
|
43
|
-
variant_name -- _description_
|
|
44
|
-
|
|
45
|
-
Returns:
|
|
46
|
-
The endpoint of the container
|
|
47
|
-
"""
|
|
48
|
-
response = requests.post(
|
|
49
|
-
f"{host}/{BACKEND_URL_SUFFIX}/app_variant/start/",
|
|
50
|
-
json={"app_variant": {"app_name": app_name, "variant_name": variant_name}},
|
|
51
|
-
timeout=600,
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
if response.status_code != 200:
|
|
55
|
-
error_message = response.json()
|
|
56
|
-
raise APIRequestError(
|
|
57
|
-
f"Request to start variant endpoint failed with status code {response.status_code} and error message: {error_message}."
|
|
58
|
-
)
|
|
59
|
-
return response.json()["uri"]
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
def list_variants(app_name: str, host: str) -> List[AppVariant]:
|
|
63
|
-
"""Lists all the variants registered in the backend for an app
|
|
64
|
-
|
|
65
|
-
Arguments:
|
|
66
|
-
app_name -- the app name to which to return all the variants
|
|
67
|
-
|
|
68
|
-
Returns:
|
|
69
|
-
a list of the variants using the pydantic model
|
|
70
|
-
"""
|
|
71
|
-
response = requests.get(
|
|
72
|
-
f"{host}/{BACKEND_URL_SUFFIX}/app_variant/list_variants/?app_name={app_name}",
|
|
73
|
-
timeout=600,
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
# Check for successful request
|
|
77
|
-
if response.status_code != 200:
|
|
78
|
-
error_message = response.json()
|
|
79
|
-
raise APIRequestError(
|
|
80
|
-
f"Request to list_variants endpoint failed with status code {response.status_code} and error message: {error_message}."
|
|
81
|
-
)
|
|
82
|
-
app_variants = response.json()
|
|
83
|
-
return [AppVariant(**variant) for variant in app_variants]
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
def get_variant_by_name(app_name: str, variant_name: str, host: str) -> AppVariant:
|
|
87
|
-
"""Gets a variant by name
|
|
88
|
-
|
|
89
|
-
Arguments:
|
|
90
|
-
app_name -- the app name
|
|
91
|
-
variant_name -- the variant name
|
|
92
|
-
|
|
93
|
-
Returns:
|
|
94
|
-
the variant using the pydantic model
|
|
95
|
-
"""
|
|
96
|
-
response = requests.get(
|
|
97
|
-
f"{host}/{BACKEND_URL_SUFFIX}/app_variant/get_variant_by_name/?app_name={app_name}&variant_name={variant_name}",
|
|
98
|
-
timeout=600,
|
|
99
|
-
)
|
|
100
|
-
|
|
101
|
-
# Check for successful request
|
|
102
|
-
if response.status_code != 200:
|
|
103
|
-
error_message = response.json()
|
|
104
|
-
raise APIRequestError(
|
|
105
|
-
f"Request to get_variant_by_name endpoint failed with status code {response.status_code} and error message: {error_message}."
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
def remove_variant(app_name: str, variant_name: str, host: str):
|
|
110
|
-
"""Removes a variant from the backend
|
|
111
|
-
|
|
112
|
-
Arguments:
|
|
113
|
-
app_name -- the app name
|
|
114
|
-
variant_name -- the variant name
|
|
115
|
-
"""
|
|
116
|
-
app_variant = AppVariant(app_name=app_name, variant_name=variant_name)
|
|
117
|
-
app_variant_json = app_variant.json()
|
|
118
|
-
response = requests.delete(
|
|
119
|
-
f"{host}/{BACKEND_URL_SUFFIX}/app_variant/remove_variant/",
|
|
120
|
-
data=app_variant_json,
|
|
121
|
-
headers={"Content-Type": "application/json"},
|
|
122
|
-
timeout=600,
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
# Check for successful request
|
|
126
|
-
if response.status_code != 200:
|
|
127
|
-
error_message = response.json()
|
|
128
|
-
raise APIRequestError(
|
|
129
|
-
f"Request to remove_variant endpoint failed with status code {response.status_code} and error message: {error_message}"
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
def update_variant_image(app_name: str, variant_name: str, image: Image, host: str):
|
|
134
|
-
"""Adds a variant to the server.
|
|
135
|
-
|
|
136
|
-
Arguments:
|
|
137
|
-
app_name -- Name of the app
|
|
138
|
-
variant_name -- Name of the variant
|
|
139
|
-
image_name -- Name of the image
|
|
140
|
-
"""
|
|
141
|
-
app_variant: AppVariant = AppVariant(app_name=app_name, variant_name=variant_name)
|
|
142
|
-
response = requests.put(
|
|
143
|
-
f"{host}/{BACKEND_URL_SUFFIX}/app_variant/update_variant_image/",
|
|
144
|
-
json={"app_variant": app_variant.dict(), "image": image.dict()},
|
|
145
|
-
timeout=600,
|
|
146
|
-
)
|
|
147
|
-
if response.status_code != 200:
|
|
148
|
-
error_message = response.json()
|
|
149
|
-
raise APIRequestError(
|
|
150
|
-
f"Request to update app_variant failed with status code {response.status_code} and error message: {error_message}."
|
|
151
|
-
)
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
def send_docker_tar(
|
|
155
|
-
app_name: str, variant_name: str, tar_path: Path, host: str
|
|
156
|
-
) -> Image:
|
|
157
|
-
with tar_path.open("rb") as tar_file:
|
|
158
|
-
response = requests.post(
|
|
159
|
-
f"{host}/{BACKEND_URL_SUFFIX}/containers/build_image/?app_name={app_name}&variant_name={variant_name}",
|
|
160
|
-
files={
|
|
161
|
-
"tar_file": tar_file,
|
|
162
|
-
},
|
|
163
|
-
timeout=1200,
|
|
164
|
-
)
|
|
165
|
-
|
|
166
|
-
if response.status_code == 500:
|
|
167
|
-
response_error = response.json()
|
|
168
|
-
error_msg = "Serving the variant failed.\n"
|
|
169
|
-
error_msg += f"Log: {response_error}\n"
|
|
170
|
-
error_msg += "Here's how you may be able to solve the issue:\n"
|
|
171
|
-
error_msg += "- First, make sure that the requirements.txt file has all the dependencies that you need.\n"
|
|
172
|
-
error_msg += "- Second, check the Docker logs for the backend image to see the error when running the Docker container."
|
|
173
|
-
raise Exception(error_msg)
|
|
174
|
-
|
|
175
|
-
response.raise_for_status()
|
|
176
|
-
image = Image.parse_obj(response.json())
|
|
177
|
-
return image
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|