agenta 0.15.0__py3-none-any.whl → 0.15.0a0__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.
Potentially problematic release.
This version of agenta might be problematic. Click here for more details.
- agenta/__init__.py +3 -6
- agenta/client/backend/client.py +6 -8
- agenta/client/backend/types/create_span.py +1 -1
- agenta/docker/docker-assets/Dockerfile.cloud.template +1 -1
- agenta/sdk/__init__.py +4 -6
- agenta/sdk/agenta_decorator.py +570 -0
- agenta/sdk/agenta_init.py +108 -120
- agenta/sdk/tracing/decorators.py +41 -0
- agenta/sdk/tracing/llm_tracing.py +103 -122
- agenta/sdk/tracing/logger.py +1 -1
- agenta/sdk/tracing/tasks_manager.py +3 -1
- agenta/sdk/types.py +67 -86
- agenta/sdk/utils/globals.py +1 -3
- {agenta-0.15.0.dist-info → agenta-0.15.0a0.dist-info}/METADATA +3 -3
- {agenta-0.15.0.dist-info → agenta-0.15.0a0.dist-info}/RECORD +17 -18
- agenta/sdk/decorators/base.py +0 -10
- agenta/sdk/decorators/llm_entrypoint.py +0 -499
- agenta/sdk/decorators/tracing.py +0 -98
- {agenta-0.15.0.dist-info → agenta-0.15.0a0.dist-info}/WHEEL +0 -0
- {agenta-0.15.0.dist-info → agenta-0.15.0a0.dist-info}/entry_points.txt +0 -0
agenta/sdk/agenta_init.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import logging
|
|
3
|
-
import
|
|
4
|
-
|
|
3
|
+
from typing import Any, Optional
|
|
4
|
+
|
|
5
|
+
from .utils.globals import set_global
|
|
5
6
|
|
|
6
|
-
from agenta.sdk.utils.globals import set_global
|
|
7
7
|
from agenta.client.backend.client import AgentaApi
|
|
8
8
|
from agenta.sdk.tracing.llm_tracing import Tracing
|
|
9
9
|
from agenta.client.exceptions import APIRequestError
|
|
@@ -13,97 +13,119 @@ logger = logging.getLogger(__name__)
|
|
|
13
13
|
logger.setLevel(logging.DEBUG)
|
|
14
14
|
|
|
15
15
|
|
|
16
|
+
BACKEND_URL_SUFFIX = os.environ.get("BACKEND_URL_SUFFIX", "api")
|
|
17
|
+
CLIENT_API_KEY = os.environ.get("AGENTA_API_KEY")
|
|
18
|
+
CLIENT_HOST = os.environ.get("AGENTA_HOST", "http://localhost")
|
|
19
|
+
|
|
20
|
+
# initialize the client with the backend url and api key
|
|
21
|
+
backend_url = f"{CLIENT_HOST}/{BACKEND_URL_SUFFIX}"
|
|
22
|
+
client = AgentaApi(
|
|
23
|
+
base_url=backend_url,
|
|
24
|
+
api_key=CLIENT_API_KEY if CLIENT_API_KEY else "",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
16
28
|
class AgentaSingleton:
|
|
17
29
|
"""Singleton class to save all the "global variables" for the sdk."""
|
|
18
30
|
|
|
19
31
|
_instance = None
|
|
20
32
|
setup = None
|
|
21
33
|
config = None
|
|
22
|
-
tracing: Optional[Tracing] = None
|
|
23
34
|
|
|
24
35
|
def __new__(cls):
|
|
25
36
|
if not cls._instance:
|
|
26
37
|
cls._instance = super(AgentaSingleton, cls).__new__(cls)
|
|
27
38
|
return cls._instance
|
|
28
39
|
|
|
29
|
-
@property
|
|
30
|
-
def client(self):
|
|
31
|
-
"""API Backend client.
|
|
32
|
-
|
|
33
|
-
Returns:
|
|
34
|
-
AgentaAPI: instance of agenta api backend
|
|
35
|
-
"""
|
|
36
|
-
|
|
37
|
-
return AgentaApi(base_url=self.host + "/api", api_key=self.api_key)
|
|
38
|
-
|
|
39
40
|
def init(
|
|
40
41
|
self,
|
|
42
|
+
app_name: Optional[str] = None,
|
|
43
|
+
base_name: Optional[str] = None,
|
|
44
|
+
api_key: Optional[str] = None,
|
|
45
|
+
base_id: Optional[str] = None,
|
|
41
46
|
app_id: Optional[str] = None,
|
|
42
47
|
host: Optional[str] = None,
|
|
43
|
-
|
|
44
|
-
config_fname: Optional[str] = None,
|
|
48
|
+
**kwargs: Any,
|
|
45
49
|
) -> None:
|
|
46
50
|
"""Main function to initialize the singleton.
|
|
47
51
|
|
|
48
|
-
Initializes the singleton with the given `
|
|
49
|
-
|
|
50
|
-
2. Value from the configuration file specified by `config_fname`.
|
|
51
|
-
3. Environment variables.
|
|
52
|
-
|
|
53
|
-
Examples:
|
|
54
|
-
ag.init(app_id="xxxx", api_key="xxx")
|
|
55
|
-
ag.init(config_fname="config.toml")
|
|
56
|
-
ag.init() #assuming env vars are set
|
|
52
|
+
Initializes the singleton with the given `app_name`, `base_name`, and `host`. If any of these arguments are not provided,
|
|
53
|
+
the function will look for them in environment variables.
|
|
57
54
|
|
|
58
55
|
Args:
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
56
|
+
app_name (Optional[str]): Name of the Agenta application. Defaults to None. If not provided, will look for "AGENTA_APP_NAME" in environment variables.
|
|
57
|
+
base_name (Optional[str]): Base name for the Agenta setup. Defaults to None. If not provided, will look for "AGENTA_BASE_NAME" in environment variables.
|
|
58
|
+
host (Optional[str]): Host name of the backend server. Defaults to None. If not provided, will look for "AGENTA_HOST" in environment variables.
|
|
59
|
+
kwargs (Any): Additional keyword arguments.
|
|
63
60
|
|
|
64
61
|
Raises:
|
|
65
|
-
ValueError: If `
|
|
62
|
+
ValueError: If `app_name`, `base_name`, or `host` are not specified either as arguments or in the environment variables.
|
|
63
|
+
"""
|
|
64
|
+
if app_name is None:
|
|
65
|
+
app_name = os.environ.get("AGENTA_APP_NAME")
|
|
66
|
+
if base_name is None:
|
|
67
|
+
base_name = os.environ.get("AGENTA_BASE_NAME")
|
|
68
|
+
if api_key is None:
|
|
69
|
+
api_key = os.environ.get("AGENTA_API_KEY")
|
|
70
|
+
if base_id is None:
|
|
71
|
+
base_id = os.environ.get("AGENTA_BASE_ID")
|
|
72
|
+
if host is None:
|
|
73
|
+
host = os.environ.get("AGENTA_HOST", "http://localhost")
|
|
74
|
+
|
|
75
|
+
if base_id is None:
|
|
76
|
+
if app_name is None or base_name is None:
|
|
77
|
+
print(
|
|
78
|
+
f"Warning: Your configuration will not be saved permanently since app_name and base_name are not provided."
|
|
79
|
+
)
|
|
80
|
+
else:
|
|
81
|
+
try:
|
|
82
|
+
app_id = self.get_app(app_name)
|
|
83
|
+
base_id = self.get_app_base(app_id, base_name)
|
|
84
|
+
except Exception as ex:
|
|
85
|
+
raise APIRequestError(
|
|
86
|
+
f"Failed to get base id and/or app_id from the server with error: {ex}"
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
self.base_id = base_id
|
|
90
|
+
self.host = host
|
|
91
|
+
self.app_id = os.environ.get("AGENTA_APP_ID") if app_id is None else app_id
|
|
92
|
+
self.variant_id = os.environ.get("AGENTA_VARIANT_ID")
|
|
93
|
+
self.variant_name = os.environ.get("AGENTA_VARIANT_NAME")
|
|
94
|
+
self.api_key = api_key
|
|
95
|
+
self.config = Config(base_id=base_id, host=host)
|
|
96
|
+
|
|
97
|
+
def get_app(self, app_name: str) -> str:
|
|
98
|
+
apps = client.apps.list_apps(app_name=app_name)
|
|
99
|
+
if len(apps) == 0:
|
|
100
|
+
raise APIRequestError(f"App with name {app_name} not found")
|
|
101
|
+
|
|
102
|
+
app_id = apps[0].app_id
|
|
103
|
+
return app_id
|
|
104
|
+
|
|
105
|
+
def get_app_base(self, app_id: str, base_name: str) -> str:
|
|
106
|
+
bases = client.bases.list_bases(app_id=app_id, base_name=base_name)
|
|
107
|
+
if len(bases) == 0:
|
|
108
|
+
raise APIRequestError(f"No base was found for the app {app_id}")
|
|
109
|
+
return bases[0].base_id
|
|
110
|
+
|
|
111
|
+
def get_current_config(self):
|
|
112
|
+
"""
|
|
113
|
+
Retrieves the current active configuration
|
|
66
114
|
"""
|
|
67
|
-
config = {}
|
|
68
|
-
if config_fname:
|
|
69
|
-
config = toml.load(config_fname)
|
|
70
|
-
|
|
71
|
-
self.app_id = app_id or config.get("app_id") or os.environ.get("AGENTA_APP_ID")
|
|
72
|
-
self.host = (
|
|
73
|
-
host
|
|
74
|
-
or config.get("backend_host")
|
|
75
|
-
or os.environ.get("AGENTA_HOST", "https://cloud.agenta.ai")
|
|
76
|
-
)
|
|
77
|
-
self.api_key = (
|
|
78
|
-
api_key or config.get("api_key") or os.environ.get("AGENTA_API_KEY")
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
if not self.app_id:
|
|
82
|
-
raise ValueError(
|
|
83
|
-
"App ID must be specified. You can provide it in one of the following ways:\n"
|
|
84
|
-
"1. As an argument when calling ag.init(app_id='your_app_id').\n"
|
|
85
|
-
"2. In the configuration file specified by config_fname.\n"
|
|
86
|
-
"3. As an environment variable 'AGENTA_APP_ID'."
|
|
87
|
-
)
|
|
88
|
-
self.base_id = os.environ.get("AGENTA_BASE_ID")
|
|
89
|
-
if self.base_id is None:
|
|
90
|
-
print(
|
|
91
|
-
"Warning: Your configuration will not be saved permanently since base_id is not provided."
|
|
92
|
-
)
|
|
93
115
|
|
|
94
|
-
|
|
116
|
+
if self._config_data is None:
|
|
117
|
+
raise RuntimeError("AgentaSingleton has not been initialized")
|
|
118
|
+
return self._config_data
|
|
95
119
|
|
|
96
120
|
|
|
97
121
|
class Config:
|
|
98
|
-
def __init__(self, base_id
|
|
122
|
+
def __init__(self, base_id, host):
|
|
99
123
|
self.base_id = base_id
|
|
100
124
|
self.host = host
|
|
101
|
-
|
|
102
125
|
if base_id is None or host is None:
|
|
103
126
|
self.persist = False
|
|
104
127
|
else:
|
|
105
128
|
self.persist = True
|
|
106
|
-
self.client = AgentaApi(base_url=self.host + "/api", api_key=api_key)
|
|
107
129
|
|
|
108
130
|
def register_default(self, overwrite=False, **kwargs):
|
|
109
131
|
"""alias for default"""
|
|
@@ -122,7 +144,7 @@ class Config:
|
|
|
122
144
|
self.push(config_name="default", overwrite=overwrite, **kwargs)
|
|
123
145
|
except Exception as ex:
|
|
124
146
|
logger.warning(
|
|
125
|
-
"Unable to push the default configuration to the server.
|
|
147
|
+
"Unable to push the default configuration to the server." + str(ex)
|
|
126
148
|
)
|
|
127
149
|
|
|
128
150
|
def push(self, config_name: str, overwrite=True, **kwargs):
|
|
@@ -135,7 +157,7 @@ class Config:
|
|
|
135
157
|
if not self.persist:
|
|
136
158
|
return
|
|
137
159
|
try:
|
|
138
|
-
|
|
160
|
+
client.configs.save_config(
|
|
139
161
|
base_id=self.base_id,
|
|
140
162
|
config_name=config_name,
|
|
141
163
|
parameters=kwargs,
|
|
@@ -143,40 +165,38 @@ class Config:
|
|
|
143
165
|
)
|
|
144
166
|
except Exception as ex:
|
|
145
167
|
logger.warning(
|
|
146
|
-
"Failed to push the configuration to the server with error:
|
|
168
|
+
"Failed to push the configuration to the server with error: " + str(ex)
|
|
147
169
|
)
|
|
148
170
|
|
|
149
|
-
def pull(
|
|
150
|
-
self, config_name: str = "default", environment_name: Optional[str] = None
|
|
151
|
-
):
|
|
171
|
+
def pull(self, config_name: str = "default", environment_name: str = None):
|
|
152
172
|
"""Pulls the parameters for the app variant from the server and sets them to the config"""
|
|
153
173
|
if not self.persist and (
|
|
154
174
|
config_name != "default" or environment_name is not None
|
|
155
175
|
):
|
|
156
|
-
raise
|
|
176
|
+
raise Exception(
|
|
157
177
|
"Cannot pull the configuration from the server since the app_name and base_name are not provided."
|
|
158
178
|
)
|
|
159
179
|
if self.persist:
|
|
160
180
|
try:
|
|
161
181
|
if environment_name:
|
|
162
|
-
config =
|
|
182
|
+
config = client.configs.get_config(
|
|
163
183
|
base_id=self.base_id, environment_name=environment_name
|
|
164
184
|
)
|
|
165
185
|
|
|
166
186
|
else:
|
|
167
|
-
config =
|
|
187
|
+
config = client.configs.get_config(
|
|
168
188
|
base_id=self.base_id,
|
|
169
189
|
config_name=config_name,
|
|
170
190
|
)
|
|
171
191
|
except Exception as ex:
|
|
172
192
|
logger.warning(
|
|
173
|
-
"Failed to pull the configuration from the server with error:
|
|
174
|
-
str(ex)
|
|
193
|
+
"Failed to pull the configuration from the server with error: "
|
|
194
|
+
+ str(ex)
|
|
175
195
|
)
|
|
176
196
|
try:
|
|
177
197
|
self.set(**{"current_version": config.current_version, **config.parameters})
|
|
178
198
|
except Exception as ex:
|
|
179
|
-
logger.warning("Failed to set the configuration with error:
|
|
199
|
+
logger.warning("Failed to set the configuration with error: " + str(ex))
|
|
180
200
|
|
|
181
201
|
def all(self):
|
|
182
202
|
"""Returns all the parameters for the app variant"""
|
|
@@ -184,15 +204,7 @@ class Config:
|
|
|
184
204
|
k: v
|
|
185
205
|
for k, v in self.__dict__.items()
|
|
186
206
|
if k
|
|
187
|
-
not in [
|
|
188
|
-
"app_name",
|
|
189
|
-
"base_name",
|
|
190
|
-
"host",
|
|
191
|
-
"base_id",
|
|
192
|
-
"api_key",
|
|
193
|
-
"persist",
|
|
194
|
-
"client",
|
|
195
|
-
]
|
|
207
|
+
not in ["app_name", "base_name", "host", "base_id", "api_key", "persist"]
|
|
196
208
|
}
|
|
197
209
|
|
|
198
210
|
# function to set the parameters for the app variant
|
|
@@ -205,52 +217,28 @@ class Config:
|
|
|
205
217
|
for key, value in kwargs.items():
|
|
206
218
|
setattr(self, key, value)
|
|
207
219
|
|
|
208
|
-
def dump(self):
|
|
209
|
-
"""Returns all the information about the current version in the configuration.
|
|
210
|
-
|
|
211
|
-
Raises:
|
|
212
|
-
NotImplementedError: _description_
|
|
213
|
-
"""
|
|
214
|
-
|
|
215
|
-
raise NotImplementedError()
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
def init(
|
|
219
|
-
app_id: Optional[str] = None,
|
|
220
|
-
host: Optional[str] = None,
|
|
221
|
-
api_key: Optional[str] = None,
|
|
222
|
-
config_fname: Optional[str] = None,
|
|
223
|
-
max_workers: Optional[int] = None,
|
|
224
|
-
):
|
|
225
|
-
"""Main function to initialize the agenta sdk.
|
|
226
|
-
|
|
227
|
-
Initializes agenta with the given `app_id`, `host`, and `api_key`. The order of precedence for these variables is:
|
|
228
|
-
1. Explicit argument provided in the function call.
|
|
229
|
-
2. Value from the configuration file specified by `config_fname`.
|
|
230
|
-
3. Environment variables.
|
|
231
|
-
|
|
232
|
-
- `app_id` is a required parameter (to be specified in one of the above ways)
|
|
233
|
-
- `host` is optional and defaults to "https://cloud.agenta.ai"
|
|
234
|
-
- `api_key` is optional and defaults to "". It is required only when using cloud or enterprise version of agenta.
|
|
235
220
|
|
|
221
|
+
def init(app_name=None, base_name=None, **kwargs):
|
|
222
|
+
"""Main function to be called by the user to initialize the sdk.
|
|
236
223
|
|
|
237
224
|
Args:
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
api_key (Optional[str]): API Key to use with the host of the backend server. Defaults to None. If not provided, will look for "api_key" in the config file, then "AGENTA_API_KEY" in environment variables.
|
|
241
|
-
config_fname (Optional[str]): Path to the configuration file. Defaults to None.
|
|
242
|
-
|
|
243
|
-
Raises:
|
|
244
|
-
ValueError: If `app_id` is not specified either as an argument, in the config file, or in the environment variables.
|
|
225
|
+
app_name: _description_. Defaults to None.
|
|
226
|
+
base_name: _description_. Defaults to None.
|
|
245
227
|
"""
|
|
246
|
-
|
|
247
228
|
singleton = AgentaSingleton()
|
|
229
|
+
singleton.init(app_name=app_name, base_name=base_name, **kwargs)
|
|
230
|
+
set_global(setup=singleton.setup, config=singleton.config)
|
|
231
|
+
|
|
248
232
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
233
|
+
def llm_tracing(max_workers: Optional[int] = None) -> Tracing:
|
|
234
|
+
"""Function to start llm tracing."""
|
|
235
|
+
|
|
236
|
+
singleton = AgentaSingleton()
|
|
237
|
+
return Tracing(
|
|
238
|
+
base_url=singleton.host,
|
|
252
239
|
app_id=singleton.app_id, # type: ignore
|
|
240
|
+
variant_id=singleton.variant_id, # type: ignore
|
|
241
|
+
variant_name=singleton.variant_name,
|
|
253
242
|
api_key=singleton.api_key,
|
|
254
243
|
max_workers=max_workers,
|
|
255
244
|
)
|
|
256
|
-
set_global(setup=singleton.setup, config=singleton.config, tracing=tracing)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Stdlib Imports
|
|
2
|
+
import inspect
|
|
3
|
+
from functools import wraps
|
|
4
|
+
|
|
5
|
+
# Own Imports
|
|
6
|
+
import agenta as ag
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def span(type: str):
|
|
10
|
+
"""Decorator to automatically start and end spans."""
|
|
11
|
+
|
|
12
|
+
tracing = ag.llm_tracing()
|
|
13
|
+
|
|
14
|
+
def decorator(func):
|
|
15
|
+
@wraps(func)
|
|
16
|
+
async def wrapper(*args, **kwargs):
|
|
17
|
+
result = None
|
|
18
|
+
span = tracing.start_span(
|
|
19
|
+
name=func.__name__,
|
|
20
|
+
input=kwargs,
|
|
21
|
+
spankind=type,
|
|
22
|
+
)
|
|
23
|
+
try:
|
|
24
|
+
is_coroutine_function = inspect.iscoroutinefunction(func)
|
|
25
|
+
if is_coroutine_function:
|
|
26
|
+
result = await func(*args, **kwargs)
|
|
27
|
+
else:
|
|
28
|
+
result = func(*args, **kwargs)
|
|
29
|
+
tracing.update_span_status(span=span, value="OK")
|
|
30
|
+
except Exception as e:
|
|
31
|
+
result = str(e)
|
|
32
|
+
tracing.update_span_status(span=span, value="ERROR")
|
|
33
|
+
finally:
|
|
34
|
+
if not isinstance(result, dict):
|
|
35
|
+
result = {"message": result}
|
|
36
|
+
tracing.end_span(outputs=result, span=span)
|
|
37
|
+
return result
|
|
38
|
+
|
|
39
|
+
return wrapper
|
|
40
|
+
|
|
41
|
+
return decorator
|