agenta 0.5.2__tar.gz → 0.5.4__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.

Files changed (43) hide show
  1. {agenta-0.5.2 → agenta-0.5.4}/PKG-INFO +2 -1
  2. agenta-0.5.4/agenta/cli/helper.py +185 -0
  3. agenta-0.5.4/agenta/cli/telemetry.py +48 -0
  4. {agenta-0.5.2 → agenta-0.5.4}/agenta/cli/variant_commands.py +41 -9
  5. {agenta-0.5.2 → agenta-0.5.4}/agenta/client/client.py +27 -0
  6. {agenta-0.5.2 → agenta-0.5.4}/pyproject.toml +2 -1
  7. agenta-0.5.2/agenta/cli/helper.py +0 -118
  8. {agenta-0.5.2 → agenta-0.5.4}/README.md +0 -0
  9. {agenta-0.5.2 → agenta-0.5.4}/agenta/__init__.py +0 -0
  10. {agenta-0.5.2 → agenta-0.5.4}/agenta/cli/main.py +0 -0
  11. {agenta-0.5.2 → agenta-0.5.4}/agenta/client/Readme.md +0 -0
  12. {agenta-0.5.2 → agenta-0.5.4}/agenta/client/__init__.py +0 -0
  13. {agenta-0.5.2 → agenta-0.5.4}/agenta/client/api_models.py +0 -0
  14. {agenta-0.5.2 → agenta-0.5.4}/agenta/config.py +0 -0
  15. {agenta-0.5.2 → agenta-0.5.4}/agenta/config.toml +0 -0
  16. {agenta-0.5.2 → agenta-0.5.4}/agenta/docker/docker-assets/Dockerfile.template +0 -0
  17. {agenta-0.5.2 → agenta-0.5.4}/agenta/docker/docker-assets/README.md +0 -0
  18. {agenta-0.5.2 → agenta-0.5.4}/agenta/docker/docker-assets/entrypoint.sh +0 -0
  19. {agenta-0.5.2 → agenta-0.5.4}/agenta/docker/docker-assets/main.py +0 -0
  20. {agenta-0.5.2 → agenta-0.5.4}/agenta/docker/docker_utils.py +0 -0
  21. {agenta-0.5.2 → agenta-0.5.4}/agenta/sdk/__init__.py +0 -0
  22. {agenta-0.5.2 → agenta-0.5.4}/agenta/sdk/agenta_decorator.py +0 -0
  23. {agenta-0.5.2 → agenta-0.5.4}/agenta/sdk/agenta_init.py +0 -0
  24. {agenta-0.5.2 → agenta-0.5.4}/agenta/sdk/context.py +0 -0
  25. {agenta-0.5.2 → agenta-0.5.4}/agenta/sdk/router.py +0 -0
  26. {agenta-0.5.2 → agenta-0.5.4}/agenta/sdk/types.py +0 -0
  27. {agenta-0.5.2 → agenta-0.5.4}/agenta/sdk/utils/globals.py +0 -0
  28. {agenta-0.5.2 → agenta-0.5.4}/agenta/sdk/utils/preinit.py +0 -0
  29. {agenta-0.5.2 → agenta-0.5.4}/agenta/templates/compose_email/README.md +0 -0
  30. {agenta-0.5.2 → agenta-0.5.4}/agenta/templates/compose_email/app.py +0 -0
  31. {agenta-0.5.2 → agenta-0.5.4}/agenta/templates/compose_email/env.example +0 -0
  32. {agenta-0.5.2 → agenta-0.5.4}/agenta/templates/compose_email/requirements.txt +0 -0
  33. {agenta-0.5.2 → agenta-0.5.4}/agenta/templates/compose_email/template.toml +0 -0
  34. {agenta-0.5.2 → agenta-0.5.4}/agenta/templates/extract_data_to_json/README.md +0 -0
  35. {agenta-0.5.2 → agenta-0.5.4}/agenta/templates/extract_data_to_json/app.py +0 -0
  36. {agenta-0.5.2 → agenta-0.5.4}/agenta/templates/extract_data_to_json/env.example +0 -0
  37. {agenta-0.5.2 → agenta-0.5.4}/agenta/templates/extract_data_to_json/requirements.txt +0 -0
  38. {agenta-0.5.2 → agenta-0.5.4}/agenta/templates/extract_data_to_json/template.toml +0 -0
  39. {agenta-0.5.2 → agenta-0.5.4}/agenta/templates/simple_prompt/README.md +0 -0
  40. {agenta-0.5.2 → agenta-0.5.4}/agenta/templates/simple_prompt/app.py +0 -0
  41. {agenta-0.5.2 → agenta-0.5.4}/agenta/templates/simple_prompt/env.example +0 -0
  42. {agenta-0.5.2 → agenta-0.5.4}/agenta/templates/simple_prompt/requirements.txt +0 -0
  43. {agenta-0.5.2 → agenta-0.5.4}/agenta/templates/simple_prompt/template.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: agenta
3
- Version: 0.5.2
3
+ Version: 0.5.4
4
4
  Summary: The SDK for agenta is an open-source LLMOps platform.
5
5
  Home-page: https://agenta.ai
6
6
  Keywords: LLMOps,LLM,evaluation,prompt engineering
@@ -19,6 +19,7 @@ Requires-Dist: docker (>=6.1.1,<7.0.0)
19
19
  Requires-Dist: fastapi (>=0.95.1,<0.96.0)
20
20
  Requires-Dist: importlib-metadata (>=6.7.0,<7.0.0)
21
21
  Requires-Dist: ipdb (>=0.13.13,<0.14.0)
22
+ Requires-Dist: posthog (>=3.0.2,<4.0.0)
22
23
  Requires-Dist: python-dotenv (>=1.0.0,<2.0.0)
23
24
  Requires-Dist: python-multipart (>=0.0.6,<0.0.7)
24
25
  Requires-Dist: questionary (>=1.10.0,<2.0.0)
@@ -0,0 +1,185 @@
1
+ import sys
2
+ import toml
3
+ import click
4
+ import questionary
5
+ from pathlib import Path
6
+ from agenta.client import client
7
+ from typing import Any, List, MutableMapping
8
+ from agenta.client.api_models import AppVariant
9
+
10
+
11
+ from typing import Any, Optional
12
+ from pathlib import Path
13
+ import toml
14
+
15
+
16
+ def get_global_config(var_name: str) -> Optional[Any]:
17
+ """
18
+ Get the value of a global configuration variable.
19
+
20
+ Args:
21
+ var_name: the name of the variable to get
22
+
23
+ Returns:
24
+ the value of the variable, or None if it doesn't exist
25
+ """
26
+ agenta_dir = Path.home() / ".agenta"
27
+ if not agenta_dir.exists():
28
+ return None
29
+ agenta_config_file = agenta_dir / "config.toml"
30
+ if not agenta_config_file.exists():
31
+ return None
32
+ global_config = toml.load(agenta_config_file)
33
+ if var_name not in global_config:
34
+ return None
35
+ return global_config[var_name]
36
+
37
+
38
+ def set_global_config(var_name: str, var_value: Any) -> None:
39
+ """
40
+ Set the value of a global configuration variable.
41
+
42
+ Args:
43
+ var_name: the name of the variable to set
44
+ var_value: the value to set the variable to
45
+ """
46
+ agenta_dir = Path.home() / ".agenta"
47
+ if not agenta_dir.exists():
48
+ agenta_dir.mkdir(exist_ok=True)
49
+ agenta_config_file = agenta_dir / "config.toml"
50
+ if not agenta_config_file.exists():
51
+ config = {}
52
+ with agenta_config_file.open("w") as config_file:
53
+ toml.dump(config, config_file)
54
+ global_config = toml.load(agenta_config_file)
55
+ global_config[var_name] = var_value
56
+ with open(agenta_config_file, "w") as config_file:
57
+ toml.dump(global_config, config_file)
58
+
59
+
60
+ def get_api_key() -> str:
61
+ """
62
+ Retrieve or request the API key for accessing the Agenta platform.
63
+
64
+ This function first looks for an existing API key in the global config file.
65
+ If found, it prompts the user to confirm whether they'd like to use that key.
66
+ If not found, it asks the user to input a new key.
67
+
68
+ Returns:
69
+ str: The API key to be used for accessing the Agenta platform.
70
+
71
+ Raises:
72
+ SystemExit: If the user cancels the input by pressing Ctrl+C.
73
+ """
74
+
75
+ api_key = get_global_config("api_key")
76
+ if api_key:
77
+ # API key exists in the config file, ask for confirmation
78
+ confirm_api_key = questionary.confirm(
79
+ f"API Key found: {api_key}\nDo you want to use this API Key?"
80
+ ).ask()
81
+
82
+ if confirm_api_key:
83
+ return api_key
84
+ elif confirm_api_key is None: # User pressed Ctrl+C
85
+ sys.exit(0)
86
+ else:
87
+ api_key = questionary.text(
88
+ "(You can get your API Key here: https://demo.agenta.ai/settings?tab=apiKeys) "
89
+ "Please provide your API key:"
90
+ ).ask()
91
+
92
+ if api_key:
93
+ set_global_config("api_key", api_key)
94
+ elif api_key is None: # User pressed Ctrl+C
95
+ sys.exit(0)
96
+
97
+
98
+ def init_telemetry_config() -> None:
99
+ if (
100
+ get_global_config("telemetry_tracking_enabled") is None
101
+ or get_global_config("telemetry_api_key") is None
102
+ ):
103
+ set_global_config("telemetry_tracking_enabled", True)
104
+ set_global_config(
105
+ "telemetry_api_key", "phc_hmVSxIjTW1REBHXgj2aw4HW9X6CXb6FzerBgP9XenC7"
106
+ )
107
+
108
+
109
+ def update_variants_from_backend(
110
+ app_id: str,
111
+ config: MutableMapping[str, Any],
112
+ host: str,
113
+ api_key: str = None,
114
+ ) -> MutableMapping[str, Any]:
115
+ """Reads the list of variants from the backend and updates the config accordingly
116
+
117
+ Arguments:
118
+ app_id -- the app id
119
+ config -- the config loaded using toml.load
120
+ api_key -- the api key to use for authentication
121
+
122
+ Returns:
123
+ a new config object later to be saved using toml.dump(config, config_file.open('w'))
124
+ """
125
+
126
+ try:
127
+ variants: List[AppVariant] = client.list_variants(app_id, host, api_key)
128
+ except Exception as ex:
129
+ raise ex
130
+
131
+ config["variants"] = [variant.variant_name for variant in variants]
132
+ config["variant_ids"] = [variant.variant_id for variant in variants]
133
+ return config
134
+
135
+
136
+ def update_config_from_backend(config_file: Path, host: str):
137
+ """Updates the config file with new information from the backend
138
+
139
+ Arguments:
140
+ config_file -- the path to the config file
141
+ """
142
+ assert config_file.exists(), "Config file does not exist!"
143
+ config = toml.load(config_file)
144
+ app_id = config["app_id"]
145
+ api_key = config.get("api_key", None)
146
+ if "variants" not in config:
147
+ config["variants"] = []
148
+ if "variant_ids" not in config:
149
+ config["variant_ids"] = []
150
+ config = update_variants_from_backend(app_id, config, host, api_key)
151
+ toml.dump(config, config_file.open("w"))
152
+
153
+
154
+ def display_app_variant(variant: AppVariant):
155
+ """Prints a variant nicely in the terminal"""
156
+ click.echo(
157
+ click.style("App Name: ", bold=True, fg="green")
158
+ + click.style(variant.app_name, fg="green")
159
+ )
160
+ click.echo(
161
+ click.style("Variant Name: ", bold=True, fg="blue")
162
+ + click.style(variant.variant_name, fg="blue")
163
+ )
164
+ click.echo(click.style("Parameters: ", bold=True, fg="cyan"))
165
+ if variant.parameters:
166
+ for param, value in variant.parameters.items():
167
+ click.echo(
168
+ click.style(f" {param}: ", fg="cyan")
169
+ + click.style(str(value), fg="cyan")
170
+ )
171
+ else:
172
+ click.echo(click.style(" Defaults from code", fg="cyan"))
173
+ if variant.previous_variant_name:
174
+ click.echo(
175
+ click.style("Template Variant Name: ", bold=True, fg="magenta")
176
+ + click.style(variant.previous_variant_name, fg="magenta")
177
+ )
178
+ else:
179
+ click.echo(
180
+ click.style("Template Variant Name: ", bold=True, fg="magenta")
181
+ + click.style("None", fg="magenta")
182
+ )
183
+ click.echo(
184
+ click.style("-" * 50, bold=True, fg="white")
185
+ ) # a line for separating each variant
@@ -0,0 +1,48 @@
1
+ # Stdlib Imports
2
+ import toml
3
+ from pathlib import Path
4
+
5
+ # Own Imports
6
+ from agenta.cli import helper
7
+
8
+ # Third party Imports
9
+ from posthog import Posthog
10
+
11
+
12
+ # Load telemetry configuration
13
+ helper.init_telemetry_config()
14
+
15
+
16
+ class EventTracking(Posthog):
17
+ _instance = None
18
+
19
+ def __new__(cls, *args, **kwargs):
20
+ if cls._instance is None:
21
+ cls._instance = super().__new__(cls)
22
+ return cls._instance
23
+
24
+ def __init__(self, api_key: str, host: str) -> None:
25
+ super(Posthog, self).__init__(api_key, host)
26
+
27
+ def capture_event(
28
+ self,
29
+ distinct_id: str,
30
+ event_name: str,
31
+ body: dict,
32
+ ) -> None:
33
+ """
34
+ Captures an event.
35
+
36
+ Args:
37
+ distinct_id (str): A unique identifier for the user or entity associated with the event.
38
+ event_name (str): The name of the event being captured.
39
+ body (dict): Contains the data associated with the event being captured.
40
+ """
41
+
42
+ self.capture(distinct_id, event_name, body)
43
+
44
+
45
+ # Initialize event tracking
46
+ event_track = EventTracking(
47
+ helper.get_global_config("telemetry_api_key"), "https://app.posthog.com"
48
+ )
@@ -1,7 +1,7 @@
1
1
  import re
2
2
  import sys
3
- from pathlib import Path
4
3
  from typing import List
4
+ from pathlib import Path
5
5
 
6
6
  from requests.exceptions import ConnectionError
7
7
 
@@ -10,12 +10,9 @@ import questionary
10
10
  import toml
11
11
  from agenta.cli import helper
12
12
  from agenta.client import client
13
+ from agenta.cli.telemetry import event_track
13
14
  from agenta.client.api_models import AppVariant, Image
14
- from agenta.docker.docker_utils import (
15
- build_and_upload_docker_image,
16
- build_tar_docker_container,
17
- )
18
- from docker.models.images import Image as DockerImage
15
+ from agenta.docker.docker_utils import build_tar_docker_container
19
16
 
20
17
 
21
18
  @click.group()
@@ -44,12 +41,15 @@ def add_variant(
44
41
  app_path = Path(app_folder)
45
42
  config_file = app_path / "config.toml"
46
43
  config = toml.load(config_file)
44
+
47
45
  app_name = config["app_name"]
48
46
  app_id = config["app_id"]
49
47
  api_key = config.get("api_key", None)
50
- base_name = file_name.removesuffix(".py")
48
+
51
49
  config_name = "default"
50
+ base_name = file_name.removesuffix(".py")
52
51
  variant_name = f"{base_name}.{config_name}"
52
+
53
53
  # check files in folder
54
54
  app_file = app_path / file_name
55
55
  if not app_file.exists():
@@ -104,7 +104,8 @@ def add_variant(
104
104
  try:
105
105
  click.echo(
106
106
  click.style(
107
- f"Preparing code base {base_name} into a tar file...", fg="bright_black"
107
+ f"Preparing code base {base_name} into a tar file...",
108
+ fg="bright_black",
108
109
  )
109
110
  )
110
111
  tar_path = build_tar_docker_container(folder=app_path, file_name=file_name)
@@ -149,7 +150,25 @@ def add_variant(
149
150
  else:
150
151
  click.echo(click.style(f"Error while adding variant: {ex}", fg="red"))
151
152
  return None
153
+
154
+ agenta_dir = Path.home() / ".agenta"
155
+ global_toml_file = toml.load(agenta_dir / "config.toml")
156
+ tracking_enabled: bool = global_toml_file["telemetry_tracking_enabled"]
152
157
  if overwrite:
158
+ # Track a deployment event
159
+ if tracking_enabled:
160
+ user_id = client.retrieve_user_id(host, api_key)
161
+ event_track.capture_event(
162
+ user_id,
163
+ "app_deployment",
164
+ body={
165
+ "app_id": app_id,
166
+ "deployed_by": user_id,
167
+ "environment": "CLI",
168
+ "version": "cloud" if api_key else "oss",
169
+ },
170
+ )
171
+
153
172
  click.echo(
154
173
  click.style(
155
174
  f"Variant {variant_name} for App {app_name} updated successfully 🎉",
@@ -158,6 +177,20 @@ def add_variant(
158
177
  )
159
178
  )
160
179
  else:
180
+ # Track a deployment event
181
+ if tracking_enabled:
182
+ user_id = client.retrieve_user_id(host, api_key)
183
+ event_track.capture_event(
184
+ user_id,
185
+ "app_deployment",
186
+ body={
187
+ "app_id": app_id,
188
+ "deployed_by": user_id,
189
+ "environment": "CLI",
190
+ "version": "cloud" if api_key else "oss",
191
+ },
192
+ )
193
+
161
194
  click.echo(
162
195
  click.style(
163
196
  f"Variant {variant_name} for App {app_name} added successfully to Agenta!",
@@ -184,7 +217,6 @@ def start_variant(variant_id: str, app_folder: str, host: str):
184
217
  app_folder = Path(app_folder)
185
218
  config_file = app_folder / "config.toml"
186
219
  config = toml.load(config_file)
187
- app_name = config["app_name"]
188
220
  api_key = config.get("api_key", None)
189
221
  app_id = config["app_id"]
190
222
 
@@ -463,3 +463,30 @@ def validate_api_key(api_key: str, host: str) -> bool:
463
463
  return True
464
464
  except RequestException as e:
465
465
  raise APIRequestError(f"An error occurred while making the request: {e}")
466
+
467
+
468
+ def retrieve_user_id(host: str, api_key: Optional[str] = None) -> str:
469
+ """Retrieve user ID from the server.
470
+
471
+ Args:
472
+ host (str): The URL of the Agenta backend
473
+ api_key (str): The API key to validate with.
474
+
475
+ Returns:
476
+ str: the user ID
477
+ """
478
+
479
+ try:
480
+ response = requests.get(
481
+ f"{host}/{BACKEND_URL_SUFFIX}/profile/",
482
+ headers={"Authorization": api_key} if api_key is not None else None,
483
+ timeout=600,
484
+ )
485
+ if response.status_code != 200:
486
+ error_message = response.json().get("detail", "Unknown error")
487
+ raise APIRequestError(
488
+ f"Request to fetch_user_profile endpoint failed with status code {response.status_code}. Error message: {error_message}"
489
+ )
490
+ return response.json()["id"]
491
+ except RequestException as e:
492
+ raise APIRequestError(f"Request failed: {str(e)}")
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "agenta"
3
- version = "0.5.2"
3
+ version = "0.5.4"
4
4
  description = "The SDK for agenta is an open-source LLMOps platform."
5
5
  readme = "README.md"
6
6
  authors = ["Mahmoud Mabrouk <mahmoud@agenta.ai>"]
@@ -26,6 +26,7 @@ ipdb = "^0.13.13"
26
26
  python-dotenv = "^1.0.0"
27
27
  python-multipart = "^0.0.6"
28
28
  importlib-metadata = "^6.7.0"
29
+ posthog = "^3.0.2"
29
30
 
30
31
  [tool.poetry.dev-dependencies]
31
32
  pytest = "^6.2"
@@ -1,118 +0,0 @@
1
- import sys
2
- import toml
3
- import click
4
- import questionary
5
- from pathlib import Path
6
- from agenta.client import client
7
- from typing import Any, List, MutableMapping
8
- from agenta.client.api_models import AppVariant
9
-
10
-
11
- def get_api_key():
12
- agenta_dir = Path.home() / ".agenta"
13
- agenta_dir.mkdir(exist_ok=True)
14
- credentials_file = agenta_dir / "config.toml"
15
-
16
- if credentials_file.exists():
17
- config = toml.load(credentials_file)
18
- api_key = config.get("api_key", None)
19
-
20
- if api_key:
21
- # API key exists in the config file, ask for confirmation
22
- confirm_api_key = questionary.confirm(
23
- f"API Key found: {api_key}\nDo you want to use this API Key?"
24
- ).ask()
25
-
26
- if confirm_api_key:
27
- return api_key
28
- elif confirm_api_key is None: # User pressed Ctrl+C
29
- sys.exit(0)
30
-
31
- api_key = questionary.text(
32
- "(You can get your API Key here: https://demo.agenta.ai/settings?tab=apiKeys) Please provide your API key:"
33
- ).ask()
34
-
35
- if api_key:
36
- config = {"api_key": api_key}
37
- with open(credentials_file, "w") as config_file:
38
- toml.dump(config, config_file)
39
-
40
- return api_key
41
- elif api_key is None: # User pressed Ctrl+C
42
- sys.exit(0)
43
-
44
-
45
- def update_variants_from_backend(
46
- app_id: str, config: MutableMapping[str, Any], host: str, api_key: str = None
47
- ) -> MutableMapping[str, Any]:
48
- """Reads the list of variants from the backend and updates the config accordingly
49
-
50
- Arguments:
51
- app_id -- the app id
52
- config -- the config loaded using toml.load
53
- api_key -- the api key to use for authentication
54
-
55
- Returns:
56
- a new config object later to be saved using toml.dump(config, config_file.open('w'))
57
- """
58
-
59
- try:
60
- variants: List[AppVariant] = client.list_variants(app_id, host, api_key)
61
- except Exception as ex:
62
- raise ex
63
-
64
- config["variants"] = [variant.variant_name for variant in variants]
65
- config["variant_ids"] = [variant.variant_id for variant in variants]
66
- return config
67
-
68
-
69
- def update_config_from_backend(config_file: Path, host: str):
70
- """Updates the config file with new information from the backend
71
-
72
- Arguments:
73
- config_file -- the path to the config file
74
- """
75
- assert config_file.exists(), "Config file does not exist!"
76
- config = toml.load(config_file)
77
- app_id = config["app_id"]
78
- api_key = config.get("api_key", None)
79
- if "variants" not in config:
80
- config["variants"] = []
81
- if "variant_ids" not in config:
82
- config["variant_ids"] = []
83
- config = update_variants_from_backend(app_id, config, host, api_key)
84
- toml.dump(config, config_file.open("w"))
85
-
86
-
87
- def display_app_variant(variant: AppVariant):
88
- """Prints a variant nicely in the terminal"""
89
- click.echo(
90
- click.style("App Name: ", bold=True, fg="green")
91
- + click.style(variant.app_name, fg="green")
92
- )
93
- click.echo(
94
- click.style("Variant Name: ", bold=True, fg="blue")
95
- + click.style(variant.variant_name, fg="blue")
96
- )
97
- click.echo(click.style("Parameters: ", bold=True, fg="cyan"))
98
- if variant.parameters:
99
- for param, value in variant.parameters.items():
100
- click.echo(
101
- click.style(f" {param}: ", fg="cyan")
102
- + click.style(str(value), fg="cyan")
103
- )
104
- else:
105
- click.echo(click.style(" Defaults from code", fg="cyan"))
106
- if variant.previous_variant_name:
107
- click.echo(
108
- click.style("Template Variant Name: ", bold=True, fg="magenta")
109
- + click.style(variant.previous_variant_name, fg="magenta")
110
- )
111
- else:
112
- click.echo(
113
- click.style("Template Variant Name: ", bold=True, fg="magenta")
114
- + click.style("None", fg="magenta")
115
- )
116
- click.echo(
117
- click.style("-" * 50, bold=True, fg="white")
118
- ) # a line for separating each variant
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