agenta 0.20.0a13__py3-none-any.whl → 0.21.0a1__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 +2 -1
- agenta/docker/docker_utils.py +2 -1
- agenta/sdk/__init__.py +2 -0
- agenta/sdk/config_manager.py +205 -0
- agenta/sdk/decorators/llm_entrypoint.py +223 -55
- agenta/sdk/types.py +7 -0
- {agenta-0.20.0a13.dist-info → agenta-0.21.0a1.dist-info}/METADATA +2 -1
- {agenta-0.20.0a13.dist-info → agenta-0.21.0a1.dist-info}/RECORD +10 -9
- {agenta-0.20.0a13.dist-info → agenta-0.21.0a1.dist-info}/WHEEL +0 -0
- {agenta-0.20.0a13.dist-info → agenta-0.21.0a1.dist-info}/entry_points.txt +0 -0
agenta/__init__.py
CHANGED
|
@@ -3,6 +3,7 @@ from .sdk.context import get_contexts, save_context
|
|
|
3
3
|
from .sdk.types import (
|
|
4
4
|
Context,
|
|
5
5
|
DictInput,
|
|
6
|
+
MultipleChoice,
|
|
6
7
|
FloatParam,
|
|
7
8
|
InFile,
|
|
8
9
|
IntParam,
|
|
@@ -22,7 +23,7 @@ from .sdk.agenta_init import Config, AgentaSingleton, init
|
|
|
22
23
|
from .sdk.utils.helper.openai_cost import calculate_token_usage
|
|
23
24
|
from .sdk.client import Agenta
|
|
24
25
|
from .sdk.tracing import callbacks
|
|
25
|
-
|
|
26
|
+
from .sdk.config_manager import ConfigManager
|
|
26
27
|
|
|
27
28
|
config = PreInitObject("agenta.config", Config)
|
|
28
29
|
DEFAULT_AGENTA_SINGLETON_INSTANCE = AgentaSingleton()
|
agenta/docker/docker_utils.py
CHANGED
|
@@ -3,12 +3,13 @@ import shutil
|
|
|
3
3
|
import tarfile
|
|
4
4
|
import tempfile
|
|
5
5
|
from pathlib import Path
|
|
6
|
+
import os
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
logger = logging.getLogger(__name__)
|
|
9
10
|
logger.setLevel(logging.DEBUG)
|
|
10
11
|
|
|
11
|
-
DEBUG = False
|
|
12
|
+
DEBUG = os.environ.get("AGENTA_CLI_DEBUG", False)
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
def create_dockerfile(out_folder: Path) -> Path:
|
agenta/sdk/__init__.py
CHANGED
|
@@ -3,6 +3,7 @@ from .context import get_contexts, save_context
|
|
|
3
3
|
from .types import (
|
|
4
4
|
Context,
|
|
5
5
|
DictInput,
|
|
6
|
+
MultipleChoice,
|
|
6
7
|
FloatParam,
|
|
7
8
|
InFile,
|
|
8
9
|
IntParam,
|
|
@@ -19,6 +20,7 @@ from .decorators.tracing import instrument
|
|
|
19
20
|
from .decorators.llm_entrypoint import entrypoint, app, route
|
|
20
21
|
from .agenta_init import Config, AgentaSingleton, init
|
|
21
22
|
from .utils.helper.openai_cost import calculate_token_usage
|
|
23
|
+
from .config_manager import ConfigManager
|
|
22
24
|
|
|
23
25
|
config = PreInitObject("agenta.config", Config)
|
|
24
26
|
DEFAULT_AGENTA_SINGLETON_INSTANCE = AgentaSingleton()
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Optional, Type, TypeVar
|
|
5
|
+
|
|
6
|
+
import yaml
|
|
7
|
+
from pydantic import BaseModel, ValidationError
|
|
8
|
+
|
|
9
|
+
from agenta.client.backend.client import AgentaApi
|
|
10
|
+
from agenta.sdk.decorators.llm_entrypoint import route_context
|
|
11
|
+
|
|
12
|
+
from . import AgentaSingleton
|
|
13
|
+
|
|
14
|
+
T = TypeVar("T", bound=BaseModel)
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
singleton = AgentaSingleton()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ConfigManager:
|
|
21
|
+
client = None
|
|
22
|
+
|
|
23
|
+
@staticmethod
|
|
24
|
+
def get_from_route(schema: Type[T]) -> T:
|
|
25
|
+
"""
|
|
26
|
+
Retrieves the configuration from the route context and returns a config object.
|
|
27
|
+
|
|
28
|
+
This method checks the route context for configuration information and returns
|
|
29
|
+
an instance of the specified schema based on the available context data.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
schema (Type[T]): A Pydantic model class that defines the structure of the configuration.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
T: An instance of the specified schema populated with the configuration data.
|
|
36
|
+
|
|
37
|
+
Raises:
|
|
38
|
+
ValueError: If conflicting configuration sources are provided or if no valid
|
|
39
|
+
configuration source is found in the context.
|
|
40
|
+
|
|
41
|
+
Note:
|
|
42
|
+
The method prioritizes the inputs in the following way:
|
|
43
|
+
1. 'config' (i.e. when called explicitly from the playground)
|
|
44
|
+
2. 'environment'
|
|
45
|
+
3. 'variant'
|
|
46
|
+
Only one of these should be provided.
|
|
47
|
+
"""
|
|
48
|
+
context = route_context.get()
|
|
49
|
+
if ("config" in context and context["config"]) and (
|
|
50
|
+
("environment" in context and context["environment"])
|
|
51
|
+
or ("variant" in context and context["variant"])
|
|
52
|
+
):
|
|
53
|
+
raise ValueError(
|
|
54
|
+
"Either config, environment or variant must be provided. Not both."
|
|
55
|
+
)
|
|
56
|
+
if "config" in context and context["config"]:
|
|
57
|
+
return schema(**context["config"])
|
|
58
|
+
elif "environment" in context and context["environment"]:
|
|
59
|
+
return ConfigManager.get_from_backend(
|
|
60
|
+
schema, environment=context["environment"]
|
|
61
|
+
)
|
|
62
|
+
elif "variant" in context and context["variant"]:
|
|
63
|
+
return ConfigManager.get_from_backend(schema, variant=context["variant"])
|
|
64
|
+
else:
|
|
65
|
+
raise ValueError("Either config, environment or variant must be provided")
|
|
66
|
+
|
|
67
|
+
@staticmethod
|
|
68
|
+
def get_from_backend(
|
|
69
|
+
schema: Type[T],
|
|
70
|
+
environment: Optional[str] = None,
|
|
71
|
+
version: Optional[str] = None,
|
|
72
|
+
variant: Optional[str] = None,
|
|
73
|
+
) -> T:
|
|
74
|
+
"""
|
|
75
|
+
Pulls the parameters for the app variant from the server and returns a config object.
|
|
76
|
+
|
|
77
|
+
This method retrieves the configuration from the backend server based on the provided
|
|
78
|
+
environment or variant. It then validates and returns the configuration as an instance
|
|
79
|
+
of the specified schema.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
schema (Type[T]): A Pydantic model class that defines the structure of the configuration.
|
|
83
|
+
environment (Optional[str]): The environment name to fetch the configuration for.
|
|
84
|
+
Must be one of "development", "production", or "staging".
|
|
85
|
+
version (Optional[str]): Currently not implemented. Will raise NotImplementedError if provided.
|
|
86
|
+
variant (Optional[str]): The variant name to fetch the configuration for.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
T: An instance of the specified schema populated with the configuration data.
|
|
90
|
+
|
|
91
|
+
Raises:
|
|
92
|
+
ValueError: If neither environment nor variant is provided.
|
|
93
|
+
NotImplementedError: If a specific version is requested (not yet implemented).
|
|
94
|
+
ValidationError: If the retrieved configuration data doesn't match the schema.
|
|
95
|
+
Exception: For any other errors during the process (e.g., API communication issues).
|
|
96
|
+
|
|
97
|
+
Note:
|
|
98
|
+
Either environment or variant must be provided, but not both.
|
|
99
|
+
"""
|
|
100
|
+
if not ConfigManager.client:
|
|
101
|
+
try:
|
|
102
|
+
ConfigManager.client = AgentaApi(
|
|
103
|
+
base_url=singleton.host + "/api",
|
|
104
|
+
api_key=singleton.api_key if singleton.api_key else "",
|
|
105
|
+
)
|
|
106
|
+
except Exception as ex:
|
|
107
|
+
logger.error(
|
|
108
|
+
"Failed to initialize Agenta client with error: %s", str(ex)
|
|
109
|
+
)
|
|
110
|
+
raise
|
|
111
|
+
if not environment and not variant:
|
|
112
|
+
raise ValueError("Either environment or variant must be provided")
|
|
113
|
+
try:
|
|
114
|
+
if environment:
|
|
115
|
+
if version:
|
|
116
|
+
raise NotImplementedError(
|
|
117
|
+
"Getting config for a specific version is not implemented yet."
|
|
118
|
+
)
|
|
119
|
+
else:
|
|
120
|
+
assert environment in [
|
|
121
|
+
"development",
|
|
122
|
+
"production",
|
|
123
|
+
"staging",
|
|
124
|
+
], "Environment must be either development, production or staging"
|
|
125
|
+
config = ConfigManager.client.configs.get_config(
|
|
126
|
+
base_id=singleton.base_id, environment_name=environment
|
|
127
|
+
)
|
|
128
|
+
elif variant:
|
|
129
|
+
config = ConfigManager.client.configs.get_config(
|
|
130
|
+
base_id=singleton.base_id, config_name=variant
|
|
131
|
+
)
|
|
132
|
+
except Exception as ex:
|
|
133
|
+
logger.error(
|
|
134
|
+
"Failed to pull the configuration from the server with error: %s",
|
|
135
|
+
str(ex),
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
try:
|
|
139
|
+
result = schema(**config.parameters)
|
|
140
|
+
except ValidationError as ex:
|
|
141
|
+
logger.error("Failed to validate the configuration with error: %s", str(ex))
|
|
142
|
+
raise
|
|
143
|
+
return result
|
|
144
|
+
|
|
145
|
+
@staticmethod
|
|
146
|
+
def get_from_yaml(filename: str, schema: Type[T]) -> T:
|
|
147
|
+
"""
|
|
148
|
+
Loads configuration from a YAML file and returns a config object.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
filename (str): The name of the YAML file to load.
|
|
152
|
+
schema (Type[T]): A Pydantic model class that defines the structure of the configuration.
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
T: An instance of the specified schema populated with the configuration data.
|
|
156
|
+
|
|
157
|
+
Raises:
|
|
158
|
+
FileNotFoundError: If the specified file doesn't exist.
|
|
159
|
+
ValidationError: If the loaded configuration data doesn't match the schema.
|
|
160
|
+
"""
|
|
161
|
+
file_path = Path(filename)
|
|
162
|
+
if not file_path.exists():
|
|
163
|
+
raise FileNotFoundError(f"Config file not found: {filename}")
|
|
164
|
+
|
|
165
|
+
with open(file_path, "r") as file:
|
|
166
|
+
config_data = yaml.safe_load(file)
|
|
167
|
+
|
|
168
|
+
try:
|
|
169
|
+
return schema(**config_data)
|
|
170
|
+
except ValidationError as ex:
|
|
171
|
+
logger.error(
|
|
172
|
+
f"Failed to validate the configuration from {filename} with error: {str(ex)}"
|
|
173
|
+
)
|
|
174
|
+
raise
|
|
175
|
+
|
|
176
|
+
@staticmethod
|
|
177
|
+
def get_from_json(filename: str, schema: Type[T]) -> T:
|
|
178
|
+
"""
|
|
179
|
+
Loads configuration from a JSON file and returns a config object.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
filename (str): The name of the JSON file to load.
|
|
183
|
+
schema (Type[T]): A Pydantic model class that defines the structure of the configuration.
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
T: An instance of the specified schema populated with the configuration data.
|
|
187
|
+
|
|
188
|
+
Raises:
|
|
189
|
+
FileNotFoundError: If the specified file doesn't exist.
|
|
190
|
+
ValidationError: If the loaded configuration data doesn't match the schema.
|
|
191
|
+
"""
|
|
192
|
+
file_path = Path(filename)
|
|
193
|
+
if not file_path.exists():
|
|
194
|
+
raise FileNotFoundError(f"Config file not found: {filename}")
|
|
195
|
+
|
|
196
|
+
with open(file_path, "r") as file:
|
|
197
|
+
config_data = json.load(file)
|
|
198
|
+
|
|
199
|
+
try:
|
|
200
|
+
return schema(**config_data)
|
|
201
|
+
except ValidationError as ex:
|
|
202
|
+
logger.error(
|
|
203
|
+
f"Failed to validate the configuration from {filename} with error: {str(ex)}"
|
|
204
|
+
)
|
|
205
|
+
raise
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""The code for the Agenta SDK"""
|
|
2
2
|
|
|
3
|
+
from agenta.sdk.utils.debug import debug, DEBUG, SHIFT
|
|
3
4
|
import os
|
|
4
5
|
import sys
|
|
5
6
|
import time
|
|
@@ -30,6 +31,7 @@ from agenta.sdk.types import (
|
|
|
30
31
|
InFile,
|
|
31
32
|
IntParam,
|
|
32
33
|
MultipleChoiceParam,
|
|
34
|
+
MultipleChoice,
|
|
33
35
|
GroupedMultipleChoiceParam,
|
|
34
36
|
TextParam,
|
|
35
37
|
MessagesInput,
|
|
@@ -37,8 +39,16 @@ from agenta.sdk.types import (
|
|
|
37
39
|
BaseResponse,
|
|
38
40
|
BinaryParam,
|
|
39
41
|
)
|
|
42
|
+
import pydantic
|
|
43
|
+
|
|
44
|
+
from pydantic import BaseModel
|
|
45
|
+
from typing import Type
|
|
46
|
+
from annotated_types import Ge, Le, Gt, Lt
|
|
40
47
|
|
|
41
48
|
from pydantic import BaseModel, HttpUrl
|
|
49
|
+
import contextvars
|
|
50
|
+
from contextlib import contextmanager
|
|
51
|
+
|
|
42
52
|
|
|
43
53
|
app = FastAPI()
|
|
44
54
|
|
|
@@ -56,11 +66,31 @@ app.add_middleware(
|
|
|
56
66
|
|
|
57
67
|
app.include_router(router, prefix="")
|
|
58
68
|
|
|
59
|
-
from agenta.sdk.utils.debug import debug, DEBUG, SHIFT
|
|
60
|
-
|
|
61
69
|
|
|
62
70
|
logging.setLevel("DEBUG")
|
|
63
71
|
|
|
72
|
+
route_context = contextvars.ContextVar("route_context", default={})
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@contextmanager
|
|
76
|
+
def route_context_manager(
|
|
77
|
+
config: Optional[Dict[str, Any]] = None,
|
|
78
|
+
environment: Optional[str] = None,
|
|
79
|
+
version: Optional[str] = None,
|
|
80
|
+
variant: Optional[str] = None,
|
|
81
|
+
):
|
|
82
|
+
context = {
|
|
83
|
+
"config": config,
|
|
84
|
+
"environment": environment,
|
|
85
|
+
"version": version,
|
|
86
|
+
"variant": variant,
|
|
87
|
+
}
|
|
88
|
+
token = route_context.set(context)
|
|
89
|
+
try:
|
|
90
|
+
yield
|
|
91
|
+
finally:
|
|
92
|
+
route_context.reset(token)
|
|
93
|
+
|
|
64
94
|
|
|
65
95
|
class PathValidator(BaseModel):
|
|
66
96
|
url: HttpUrl
|
|
@@ -72,16 +102,18 @@ class route(BaseDecorator):
|
|
|
72
102
|
# the @entrypoint decorator, which has certain limitations. By using @route(), we can create new
|
|
73
103
|
# routes without altering the main workflow entrypoint. This helps in modularizing the services
|
|
74
104
|
# and provides flexibility in how we expose different functionalities as APIs.
|
|
75
|
-
def __init__(self, path):
|
|
76
|
-
|
|
77
|
-
path
|
|
78
|
-
|
|
105
|
+
def __init__(self, path, config_schema: BaseModel):
|
|
106
|
+
self.config_schema: BaseModel = config_schema
|
|
107
|
+
if path != "" and path[0] != "/":
|
|
108
|
+
path = "/" + path
|
|
79
109
|
PathValidator(url=f"http://example.com{path}")
|
|
80
110
|
|
|
81
111
|
self.route_path = path
|
|
82
112
|
|
|
83
113
|
def __call__(self, f):
|
|
84
|
-
self.e = entrypoint(
|
|
114
|
+
self.e = entrypoint(
|
|
115
|
+
f, route_path=self.route_path, config_schema=self.config_schema
|
|
116
|
+
)
|
|
85
117
|
|
|
86
118
|
return f
|
|
87
119
|
|
|
@@ -119,15 +151,29 @@ class entrypoint(BaseDecorator):
|
|
|
119
151
|
|
|
120
152
|
routes = list()
|
|
121
153
|
|
|
122
|
-
def __init__(
|
|
154
|
+
def __init__(
|
|
155
|
+
self, func: Callable[..., Any], route_path="", config_schema: BaseModel = None
|
|
156
|
+
):
|
|
123
157
|
logging.info(f"Using Agenta Python SDK version {version('agenta')}")
|
|
124
158
|
|
|
125
159
|
DEFAULT_PATH = "generate"
|
|
126
160
|
PLAYGROUND_PATH = "/playground"
|
|
127
161
|
RUN_PATH = "/run"
|
|
128
|
-
|
|
129
162
|
func_signature = inspect.signature(func)
|
|
130
|
-
|
|
163
|
+
try:
|
|
164
|
+
config = (
|
|
165
|
+
config_schema() if config_schema else None
|
|
166
|
+
) # we initialize the config object to be able to use it
|
|
167
|
+
except pydantic.ValidationError as e:
|
|
168
|
+
raise ValueError(
|
|
169
|
+
f"Error initializing config_schema. Please ensure all required fields have default values: {str(e)}"
|
|
170
|
+
) from e
|
|
171
|
+
except Exception as e:
|
|
172
|
+
raise ValueError(
|
|
173
|
+
f"Unexpected error initializing config_schema: {str(e)}"
|
|
174
|
+
) from e
|
|
175
|
+
|
|
176
|
+
config_params = config.dict() if config else ag.config.all()
|
|
131
177
|
ingestible_files = self.extract_ingestible_files(func_signature)
|
|
132
178
|
|
|
133
179
|
### --- Playground --- #
|
|
@@ -136,36 +182,44 @@ class entrypoint(BaseDecorator):
|
|
|
136
182
|
async def wrapper(*args, **kwargs) -> Any:
|
|
137
183
|
func_params, api_config_params = self.split_kwargs(kwargs, config_params)
|
|
138
184
|
self.ingest_files(func_params, ingestible_files)
|
|
139
|
-
|
|
185
|
+
if not config_schema:
|
|
186
|
+
ag.config.set(**api_config_params)
|
|
140
187
|
|
|
141
188
|
# Set the configuration and environment of the LLM app parent span at run-time
|
|
142
189
|
ag.tracing.update_baggage(
|
|
143
190
|
{"config": config_params, "environment": "playground"}
|
|
144
191
|
)
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
192
|
+
with route_context_manager(config=api_config_params):
|
|
193
|
+
entrypoint_result = await self.execute_function(
|
|
194
|
+
func,
|
|
195
|
+
True, # inline trace: True
|
|
196
|
+
*args,
|
|
197
|
+
params=func_params,
|
|
198
|
+
config_params=config_params,
|
|
199
|
+
)
|
|
153
200
|
|
|
154
201
|
return entrypoint_result
|
|
155
202
|
|
|
156
203
|
self.update_function_signature(
|
|
157
|
-
wrapper,
|
|
204
|
+
wrapper=wrapper,
|
|
205
|
+
func_signature=func_signature,
|
|
206
|
+
config_class=config,
|
|
207
|
+
config_dict=config_params,
|
|
208
|
+
ingestible_files=ingestible_files,
|
|
158
209
|
)
|
|
159
210
|
|
|
160
211
|
#
|
|
161
|
-
if route_path == "":
|
|
212
|
+
if route_path == "" or route_path == "/":
|
|
162
213
|
route = f"/{DEFAULT_PATH}"
|
|
163
214
|
app.post(route, response_model=BaseResponse)(wrapper)
|
|
164
215
|
entrypoint.routes.append(
|
|
165
216
|
{
|
|
166
217
|
"func": func.__name__,
|
|
167
218
|
"endpoint": route,
|
|
168
|
-
"params": {**config_params, **func_signature.parameters}
|
|
219
|
+
"params": {**config_params, **func_signature.parameters}
|
|
220
|
+
if not config
|
|
221
|
+
else func_signature.parameters,
|
|
222
|
+
"config": config,
|
|
169
223
|
}
|
|
170
224
|
)
|
|
171
225
|
|
|
@@ -175,7 +229,10 @@ class entrypoint(BaseDecorator):
|
|
|
175
229
|
{
|
|
176
230
|
"func": func.__name__,
|
|
177
231
|
"endpoint": route,
|
|
178
|
-
"params": {**config_params, **func_signature.parameters}
|
|
232
|
+
"params": {**config_params, **func_signature.parameters}
|
|
233
|
+
if not config
|
|
234
|
+
else func_signature.parameters,
|
|
235
|
+
"config": config,
|
|
179
236
|
}
|
|
180
237
|
)
|
|
181
238
|
### ---------------------------- #
|
|
@@ -187,26 +244,28 @@ class entrypoint(BaseDecorator):
|
|
|
187
244
|
func_params = {
|
|
188
245
|
k: v for k, v in kwargs.items() if k not in ["config", "environment"]
|
|
189
246
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
247
|
+
if not config_schema:
|
|
248
|
+
if "environment" in kwargs and kwargs["environment"] is not None:
|
|
249
|
+
ag.config.pull(environment_name=kwargs["environment"])
|
|
250
|
+
elif "config" in kwargs and kwargs["config"] is not None:
|
|
251
|
+
ag.config.pull(config_name=kwargs["config"])
|
|
252
|
+
else:
|
|
253
|
+
ag.config.pull(config_name="default")
|
|
197
254
|
|
|
198
255
|
# Set the configuration and environment of the LLM app parent span at run-time
|
|
199
256
|
ag.tracing.update_baggage(
|
|
200
257
|
{"config": config_params, "environment": kwargs["environment"]}
|
|
201
258
|
)
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
259
|
+
with route_context_manager(
|
|
260
|
+
variant=kwargs["config"], environment=kwargs["environment"]
|
|
261
|
+
):
|
|
262
|
+
entrypoint_result = await self.execute_function(
|
|
263
|
+
func,
|
|
264
|
+
False, # inline trace: False
|
|
265
|
+
*args,
|
|
266
|
+
params=func_params,
|
|
267
|
+
config_params=config_params,
|
|
268
|
+
)
|
|
210
269
|
|
|
211
270
|
return entrypoint_result
|
|
212
271
|
|
|
@@ -215,8 +274,7 @@ class entrypoint(BaseDecorator):
|
|
|
215
274
|
func_signature,
|
|
216
275
|
ingestible_files,
|
|
217
276
|
)
|
|
218
|
-
|
|
219
|
-
if route_path == "":
|
|
277
|
+
if route_path == "" or route_path == "/":
|
|
220
278
|
route_deployed = f"/{DEFAULT_PATH}_deployed"
|
|
221
279
|
app.post(route_deployed, response_model=BaseResponse)(wrapper_deployed)
|
|
222
280
|
|
|
@@ -231,11 +289,18 @@ class entrypoint(BaseDecorator):
|
|
|
231
289
|
for route in entrypoint.routes:
|
|
232
290
|
self.override_schema(
|
|
233
291
|
openapi_schema=openapi_schema,
|
|
234
|
-
|
|
292
|
+
func_name=route["func"],
|
|
235
293
|
endpoint=route["endpoint"],
|
|
236
294
|
params=route["params"],
|
|
237
295
|
)
|
|
238
|
-
|
|
296
|
+
if route["config"] is not None: # new SDK version
|
|
297
|
+
self.override_config_in_schema(
|
|
298
|
+
openapi_schema=openapi_schema,
|
|
299
|
+
func_name=route["func"],
|
|
300
|
+
endpoint=route["endpoint"],
|
|
301
|
+
config=route["config"],
|
|
302
|
+
)
|
|
303
|
+
|
|
239
304
|
if self.is_main_script(func) and route_path == "":
|
|
240
305
|
self.handle_terminal_run(
|
|
241
306
|
func,
|
|
@@ -393,13 +458,17 @@ class entrypoint(BaseDecorator):
|
|
|
393
458
|
self,
|
|
394
459
|
wrapper: Callable[..., Any],
|
|
395
460
|
func_signature: inspect.Signature,
|
|
396
|
-
|
|
461
|
+
config_class: Type[BaseModel], # TODO: change to our type
|
|
462
|
+
config_dict: Dict[str, Any],
|
|
397
463
|
ingestible_files: Dict[str, inspect.Parameter],
|
|
398
464
|
) -> None:
|
|
399
465
|
"""Update the function signature to include new parameters."""
|
|
400
466
|
|
|
401
467
|
updated_params: List[inspect.Parameter] = []
|
|
402
|
-
|
|
468
|
+
if config_class:
|
|
469
|
+
self.add_config_params_to_parser(updated_params, config_class)
|
|
470
|
+
else:
|
|
471
|
+
self.depracated_add_config_params_to_parser(updated_params, config_dict)
|
|
403
472
|
self.add_func_params_to_parser(updated_params, func_signature, ingestible_files)
|
|
404
473
|
self.update_wrapper_signature(wrapper, updated_params)
|
|
405
474
|
|
|
@@ -419,8 +488,8 @@ class entrypoint(BaseDecorator):
|
|
|
419
488
|
]: # we add the config and environment parameters
|
|
420
489
|
updated_params.append(
|
|
421
490
|
inspect.Parameter(
|
|
422
|
-
param,
|
|
423
|
-
inspect.Parameter.KEYWORD_ONLY,
|
|
491
|
+
name=param,
|
|
492
|
+
kind=inspect.Parameter.KEYWORD_ONLY,
|
|
424
493
|
default=Body(None),
|
|
425
494
|
annotation=str,
|
|
426
495
|
)
|
|
@@ -428,17 +497,32 @@ class entrypoint(BaseDecorator):
|
|
|
428
497
|
self.update_wrapper_signature(wrapper, updated_params)
|
|
429
498
|
|
|
430
499
|
def add_config_params_to_parser(
|
|
431
|
-
self, updated_params: list,
|
|
500
|
+
self, updated_params: list, config_class: Type[BaseModel]
|
|
432
501
|
) -> None:
|
|
433
502
|
"""Add configuration parameters to function signature."""
|
|
434
|
-
for name,
|
|
503
|
+
for name, field in config_class.__fields__.items():
|
|
504
|
+
assert field.default is not None, f"Field {name} has no default value"
|
|
505
|
+
updated_params.append(
|
|
506
|
+
inspect.Parameter(
|
|
507
|
+
name=name,
|
|
508
|
+
kind=inspect.Parameter.KEYWORD_ONLY,
|
|
509
|
+
annotation=field.annotation.__name__,
|
|
510
|
+
default=Body(field.default),
|
|
511
|
+
)
|
|
512
|
+
)
|
|
513
|
+
|
|
514
|
+
def depracated_add_config_params_to_parser(
|
|
515
|
+
self, updated_params: list, config_dict: Dict[str, Any]
|
|
516
|
+
) -> None:
|
|
517
|
+
"""Add configuration parameters to function signature."""
|
|
518
|
+
for name, param in config_dict.items():
|
|
435
519
|
assert (
|
|
436
520
|
len(param.__class__.__bases__) == 1
|
|
437
521
|
), f"Inherited standard type of {param.__class__} needs to be one."
|
|
438
522
|
updated_params.append(
|
|
439
523
|
inspect.Parameter(
|
|
440
|
-
name,
|
|
441
|
-
inspect.Parameter.KEYWORD_ONLY,
|
|
524
|
+
name=name,
|
|
525
|
+
kind=inspect.Parameter.KEYWORD_ONLY,
|
|
442
526
|
default=Body(param),
|
|
443
527
|
annotation=param.__class__.__bases__[
|
|
444
528
|
0
|
|
@@ -546,10 +630,17 @@ class entrypoint(BaseDecorator):
|
|
|
546
630
|
file_path=args_func_params[name],
|
|
547
631
|
)
|
|
548
632
|
|
|
549
|
-
|
|
633
|
+
# Update args_config_params with default values from config_params if not provided in command line arguments
|
|
634
|
+
args_config_params.update(
|
|
635
|
+
{
|
|
636
|
+
key: value
|
|
637
|
+
for key, value in config_params.items()
|
|
638
|
+
if key not in args_config_params
|
|
639
|
+
}
|
|
640
|
+
)
|
|
550
641
|
|
|
551
642
|
# Set the configuration and environment of the LLM app parent span at run-time
|
|
552
|
-
ag.tracing.update_baggage({"config":
|
|
643
|
+
ag.tracing.update_baggage({"config": args_config_params, "environment": "bash"})
|
|
553
644
|
|
|
554
645
|
loop = asyncio.get_event_loop()
|
|
555
646
|
|
|
@@ -561,15 +652,92 @@ class entrypoint(BaseDecorator):
|
|
|
561
652
|
)
|
|
562
653
|
)
|
|
563
654
|
|
|
564
|
-
print(
|
|
655
|
+
print("\n========== Result ==========\n")
|
|
565
656
|
|
|
566
657
|
print("-> data")
|
|
567
658
|
print(json.dumps(result.data, indent=2))
|
|
568
659
|
print("-> trace")
|
|
569
660
|
print(json.dumps(result.trace, indent=2))
|
|
570
661
|
|
|
662
|
+
with open("trace.json", "w") as trace_file:
|
|
663
|
+
json.dump(result.trace, trace_file, indent=4)
|
|
664
|
+
|
|
665
|
+
def override_config_in_schema(
|
|
666
|
+
self,
|
|
667
|
+
openapi_schema: dict,
|
|
668
|
+
func_name: str,
|
|
669
|
+
endpoint: str,
|
|
670
|
+
config: Type[BaseModel],
|
|
671
|
+
):
|
|
672
|
+
endpoint = endpoint[1:].replace("/", "_")
|
|
673
|
+
schema_to_override = openapi_schema["components"]["schemas"][
|
|
674
|
+
f"Body_{func_name}_{endpoint}_post"
|
|
675
|
+
]["properties"]
|
|
676
|
+
# New logic
|
|
677
|
+
for param_name, param_val in config.__fields__.items():
|
|
678
|
+
if param_val.annotation is str:
|
|
679
|
+
if any(
|
|
680
|
+
isinstance(constraint, MultipleChoice)
|
|
681
|
+
for constraint in param_val.metadata
|
|
682
|
+
):
|
|
683
|
+
choices = next(
|
|
684
|
+
constraint.choices
|
|
685
|
+
for constraint in param_val.metadata
|
|
686
|
+
if isinstance(constraint, MultipleChoice)
|
|
687
|
+
)
|
|
688
|
+
if isinstance(choices, dict):
|
|
689
|
+
schema_to_override[param_name]["x-parameter"] = "grouped_choice"
|
|
690
|
+
schema_to_override[param_name]["choices"] = choices
|
|
691
|
+
elif isinstance(choices, list):
|
|
692
|
+
schema_to_override[param_name]["x-parameter"] = "choice"
|
|
693
|
+
schema_to_override[param_name]["enum"] = choices
|
|
694
|
+
else:
|
|
695
|
+
schema_to_override[param_name]["x-parameter"] = "text"
|
|
696
|
+
if param_val.annotation is bool:
|
|
697
|
+
schema_to_override[param_name]["x-parameter"] = "bool"
|
|
698
|
+
if param_val.annotation in (int, float):
|
|
699
|
+
schema_to_override[param_name]["x-parameter"] = (
|
|
700
|
+
"int" if param_val.annotation is int else "float"
|
|
701
|
+
)
|
|
702
|
+
# Check for greater than or equal to constraint
|
|
703
|
+
if any(isinstance(constraint, Ge) for constraint in param_val.metadata):
|
|
704
|
+
min_value = next(
|
|
705
|
+
constraint.ge
|
|
706
|
+
for constraint in param_val.metadata
|
|
707
|
+
if isinstance(constraint, Ge)
|
|
708
|
+
)
|
|
709
|
+
schema_to_override[param_name]["minimum"] = min_value
|
|
710
|
+
# Check for greater than constraint
|
|
711
|
+
elif any(
|
|
712
|
+
isinstance(constraint, Gt) for constraint in param_val.metadata
|
|
713
|
+
):
|
|
714
|
+
min_value = next(
|
|
715
|
+
constraint.gt
|
|
716
|
+
for constraint in param_val.metadata
|
|
717
|
+
if isinstance(constraint, Gt)
|
|
718
|
+
)
|
|
719
|
+
schema_to_override[param_name]["exclusiveMinimum"] = min_value
|
|
720
|
+
# Check for less than or equal to constraint
|
|
721
|
+
if any(isinstance(constraint, Le) for constraint in param_val.metadata):
|
|
722
|
+
max_value = next(
|
|
723
|
+
constraint.le
|
|
724
|
+
for constraint in param_val.metadata
|
|
725
|
+
if isinstance(constraint, Le)
|
|
726
|
+
)
|
|
727
|
+
schema_to_override[param_name]["maximum"] = max_value
|
|
728
|
+
# Check for less than constraint
|
|
729
|
+
elif any(
|
|
730
|
+
isinstance(constraint, Lt) for constraint in param_val.metadata
|
|
731
|
+
):
|
|
732
|
+
max_value = next(
|
|
733
|
+
constraint.lt
|
|
734
|
+
for constraint in param_val.metadata
|
|
735
|
+
if isinstance(constraint, Lt)
|
|
736
|
+
)
|
|
737
|
+
schema_to_override[param_name]["exclusiveMaximum"] = max_value
|
|
738
|
+
|
|
571
739
|
def override_schema(
|
|
572
|
-
self, openapi_schema: dict,
|
|
740
|
+
self, openapi_schema: dict, func_name: str, endpoint: str, params: dict
|
|
573
741
|
):
|
|
574
742
|
"""
|
|
575
743
|
Overrides the default openai schema generated by fastapi with additional information about:
|
|
@@ -641,7 +809,7 @@ class entrypoint(BaseDecorator):
|
|
|
641
809
|
endpoint = endpoint[1:].replace("/", "_")
|
|
642
810
|
|
|
643
811
|
schema_to_override = openapi_schema["components"]["schemas"][
|
|
644
|
-
f"Body_{
|
|
812
|
+
f"Body_{func_name}_{endpoint}_post"
|
|
645
813
|
]["properties"]
|
|
646
814
|
|
|
647
815
|
for param_name, param_val in params.items():
|
agenta/sdk/types.py
CHANGED
|
@@ -2,6 +2,13 @@ import json
|
|
|
2
2
|
from typing import Dict, List, Optional, Any, Union
|
|
3
3
|
|
|
4
4
|
from pydantic import ConfigDict, BaseModel, HttpUrl
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Union
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class MultipleChoice:
|
|
11
|
+
choices: Union[List[str], Dict[str, List[str]]]
|
|
5
12
|
|
|
6
13
|
|
|
7
14
|
class InFile:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: agenta
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.21.0a1
|
|
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
|
|
@@ -27,6 +27,7 @@ Requires-Dist: pydantic (>=2)
|
|
|
27
27
|
Requires-Dist: pymongo (>=4.6.3,<5.0.0)
|
|
28
28
|
Requires-Dist: python-dotenv (>=1.0.0,<2.0.0)
|
|
29
29
|
Requires-Dist: python-multipart (>=0.0.6,<0.0.10)
|
|
30
|
+
Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
|
|
30
31
|
Requires-Dist: questionary (>=1.10,<3.0)
|
|
31
32
|
Requires-Dist: toml (>=0.10.2,<0.11.0)
|
|
32
33
|
Project-URL: Documentation, https://docs.agenta.ai
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
agenta/__init__.py,sha256=
|
|
1
|
+
agenta/__init__.py,sha256=xTGdwyG08dOtbW_jZNMQ7fXwxeZnIIFXdYEnJfSBW8s,959
|
|
2
2
|
agenta/cli/evaluation_commands.py,sha256=fs6492tprPId9p8eGO02Xy-NCBm2RZNJLZWcUxugwd8,474
|
|
3
3
|
agenta/cli/helper.py,sha256=vRxHyeNaltzNIGrfU2vO0H28_rXDzx9QqIZ_S-W6zL4,6212
|
|
4
4
|
agenta/cli/main.py,sha256=Wz0ODhoeKK3Qg_CFUhu6D909szk05tc8ZVBB6H1-w7k,9763
|
|
@@ -125,13 +125,14 @@ agenta/docker/docker-assets/README.md,sha256=XHxwh2ks_ozrtAU7SLbL3J14SB2holG6buo
|
|
|
125
125
|
agenta/docker/docker-assets/entrypoint.sh,sha256=29XK8VQjQsx4hN2j-4JDy-6kQb5y4LCqZEa7PD4eqCQ,74
|
|
126
126
|
agenta/docker/docker-assets/lambda_function.py,sha256=h4UZSSfqwpfsCgERv6frqwm_4JrYu9rLz3I-LxCfeEg,83
|
|
127
127
|
agenta/docker/docker-assets/main.py,sha256=7MI-21n81U7N7A0GxebNi0cmGWtJKcR2sPB6FcH2QfA,251
|
|
128
|
-
agenta/docker/docker_utils.py,sha256=
|
|
129
|
-
agenta/sdk/__init__.py,sha256=
|
|
128
|
+
agenta/docker/docker_utils.py,sha256=kO1q2_IR0fEAo4M-2Pt_v-zC7GxxnkLogjKFhU869Ps,3555
|
|
129
|
+
agenta/sdk/__init__.py,sha256=XyxMUm8-fCH6G6KAFU0iAfiKTGEzNzBtbAfCp9i1Y7o,831
|
|
130
130
|
agenta/sdk/agenta_init.py,sha256=8MfDuypxohd0qRTdtGjX7L17KW-1UGmzNVdiqF15_ak,9790
|
|
131
131
|
agenta/sdk/client.py,sha256=trKyBOYFZRk0v5Eptxvh87yPf50Y9CqY6Qgv4Fy-VH4,2142
|
|
132
|
+
agenta/sdk/config_manager.py,sha256=D4VhCvoDe65IhMYSiNzdmcQSvc32ho9lovxpAPFHIiE,8011
|
|
132
133
|
agenta/sdk/context.py,sha256=q-PxL05-I84puunUAs9LGsffEXcYhDxhQxjuOz2vK90,901
|
|
133
134
|
agenta/sdk/decorators/base.py,sha256=9aNdX5h8a2mFweuhdO-BQPwXGKY9ONPIdLRhSGAGMfY,217
|
|
134
|
-
agenta/sdk/decorators/llm_entrypoint.py,sha256=
|
|
135
|
+
agenta/sdk/decorators/llm_entrypoint.py,sha256=38l7S17HH09gO_t7A8Qwd2m8_Ldk8BFBknK7aKWeelU,35483
|
|
135
136
|
agenta/sdk/decorators/tracing.py,sha256=e0olx2EEdjXY0NqpIoDJSVxCnUmv0woewTUuCJXy2tM,4166
|
|
136
137
|
agenta/sdk/router.py,sha256=0sbajvn5C7t18anH6yNo7-oYxldHnYfwcbmQnIXBePw,269
|
|
137
138
|
agenta/sdk/tracing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -140,7 +141,7 @@ agenta/sdk/tracing/llm_tracing.py,sha256=FfZQcID-8E4G0R34H29SA05yFUNDMEPccb4MqVc
|
|
|
140
141
|
agenta/sdk/tracing/logger.py,sha256=GfH7V-jBHcn7h5dbdrnkDMe_ml3wkXFBeoQiqR4KVRc,474
|
|
141
142
|
agenta/sdk/tracing/tasks_manager.py,sha256=FBSFOWIKBycyA4ShB2ZVMzrzYQ8pWGWWBClFX8nlZFA,3726
|
|
142
143
|
agenta/sdk/tracing/tracing_context.py,sha256=nt3ewa-TK9BRJviGIZYazsAQUiG4daWxjtsbjeaDprs,789
|
|
143
|
-
agenta/sdk/types.py,sha256=
|
|
144
|
+
agenta/sdk/types.py,sha256=1AXUKTURFFgPH5iWeTYcNcg6dxVo1RrD0WuVe9e-NgU,5919
|
|
144
145
|
agenta/sdk/utils/debug.py,sha256=QyuPsSoN0425UD13x_msPxSF_VT6YwHiQunZUibI-jg,2149
|
|
145
146
|
agenta/sdk/utils/globals.py,sha256=JmhJcCOSbwvjQ6GDyUc2_SYR27DZk7YcrRH80ktHHOM,435
|
|
146
147
|
agenta/sdk/utils/helper/openai_cost.py,sha256=1VkgvucDnNZm1pTfcVLz9icWunntp1d7zwMmnviy3Uw,5877
|
|
@@ -160,7 +161,7 @@ agenta/templates/simple_prompt/app.py,sha256=kODgF6lhzsaJPdgL5b21bUki6jkvqjWZzWR
|
|
|
160
161
|
agenta/templates/simple_prompt/env.example,sha256=g9AE5bYcGPpxawXMJ96gh8oenEPCHTabsiOnfQo3c5k,70
|
|
161
162
|
agenta/templates/simple_prompt/requirements.txt,sha256=ywRglRy7pPkw8bljmMEJJ4aOOQKrt9FGKULZ-DGkoBU,23
|
|
162
163
|
agenta/templates/simple_prompt/template.toml,sha256=DQBtRrF4GU8LBEXOZ-GGuINXMQDKGTEG5y37tnvIUIE,60
|
|
163
|
-
agenta-0.
|
|
164
|
-
agenta-0.
|
|
165
|
-
agenta-0.
|
|
166
|
-
agenta-0.
|
|
164
|
+
agenta-0.21.0a1.dist-info/METADATA,sha256=g-T2aQrTVGXMV5YNnX4nf2cKLGdyk6Dq39IBGLk3SU4,26501
|
|
165
|
+
agenta-0.21.0a1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
166
|
+
agenta-0.21.0a1.dist-info/entry_points.txt,sha256=PDiu8_8AsL7ibU9v4iNoOKR1S7F2rdxjlEprjM9QOgo,46
|
|
167
|
+
agenta-0.21.0a1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|