agenta 0.21.0b1__py3-none-any.whl → 0.23.0__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 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,8 @@ 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
27
+ from .sdk import assets as assets
26
28
 
27
29
  config = PreInitObject("agenta.config", Config)
28
30
  DEFAULT_AGENTA_SINGLETON_INSTANCE = AgentaSingleton()
@@ -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()
agenta/sdk/assets.py ADDED
@@ -0,0 +1,84 @@
1
+ supported_llm_models = {
2
+ "Mistral AI": [
3
+ "mistral/mistral-tiny",
4
+ "mistral/mistral-small",
5
+ "mistral/mistral-medium",
6
+ "mistral/mistral-large-latest",
7
+ ],
8
+ "Open AI": [
9
+ "gpt-3.5-turbo-1106",
10
+ "gpt-3.5-turbo",
11
+ "gpt-4",
12
+ "gpt-4o",
13
+ "gpt-4o-mini",
14
+ "gpt-4-1106-preview",
15
+ ],
16
+ "Gemini": ["gemini/gemini-1.5-pro-latest", "gemini/gemini-1.5-flash"],
17
+ "Cohere": [
18
+ "cohere/command-light",
19
+ "cohere/command-r-plus",
20
+ "cohere/command-nightly",
21
+ ],
22
+ "Anthropic": [
23
+ "anthropic/claude-3-5-sonnet-20240620",
24
+ "anthropic/claude-3-opus-20240229",
25
+ "anthropic/claude-3-sonnet-20240229",
26
+ "anthropic/claude-3-haiku-20240307",
27
+ "anthropic/claude-2.1",
28
+ "anthropic/claude-2",
29
+ "anthropic/claude-instant-1.2",
30
+ "anthropic/claude-instant-1",
31
+ ],
32
+ "Anyscale": [
33
+ "anyscale/meta-llama/Llama-2-13b-chat-hf",
34
+ "anyscale/meta-llama/Llama-2-70b-chat-hf",
35
+ ],
36
+ "Perplexity AI": [
37
+ "perplexity/pplx-7b-chat",
38
+ "perplexity/pplx-70b-chat",
39
+ "perplexity/pplx-7b-online",
40
+ "perplexity/pplx-70b-online",
41
+ ],
42
+ "DeepInfra": [
43
+ "deepinfra/meta-llama/Llama-2-70b-chat-hf",
44
+ "deepinfra/meta-llama/Llama-2-13b-chat-hf",
45
+ "deepinfra/codellama/CodeLlama-34b-Instruct-hf",
46
+ "deepinfra/mistralai/Mistral-7B-Instruct-v0.1",
47
+ "deepinfra/jondurbin/airoboros-l2-70b-gpt4-1.4.1",
48
+ ],
49
+ "Together AI": [
50
+ "together_ai/togethercomputer/llama-2-70b-chat",
51
+ "together_ai/togethercomputer/llama-2-70b",
52
+ "together_ai/togethercomputer/LLaMA-2-7B-32K",
53
+ "together_ai/togethercomputer/Llama-2-7B-32K-Instruct",
54
+ "together_ai/togethercomputer/llama-2-7b",
55
+ "together_ai/togethercomputer/alpaca-7b",
56
+ "together_ai/togethercomputer/CodeLlama-34b-Instruct",
57
+ "together_ai/togethercomputer/CodeLlama-34b-Python",
58
+ "together_ai/WizardLM/WizardCoder-Python-34B-V1.0",
59
+ "together_ai/NousResearch/Nous-Hermes-Llama2-13b",
60
+ "together_ai/Austism/chronos-hermes-13b",
61
+ ],
62
+ "Aleph Alpha": [
63
+ "luminous-base",
64
+ "luminous-base-control",
65
+ "luminous-extended-control",
66
+ "luminous-supreme",
67
+ ],
68
+ "OpenRouter": [
69
+ "openrouter/openai/gpt-3.5-turbo",
70
+ "openrouter/openai/gpt-3.5-turbo-16k",
71
+ "openrouter/anthropic/claude-instant-v1",
72
+ "openrouter/google/palm-2-chat-bison",
73
+ "openrouter/google/palm-2-codechat-bison",
74
+ "openrouter/meta-llama/llama-2-13b-chat",
75
+ "openrouter/meta-llama/llama-2-70b-chat",
76
+ ],
77
+ "Groq": [
78
+ "groq/llama3-8b-8192",
79
+ "groq/llama3-70b-8192",
80
+ "groq/llama2-70b-4096",
81
+ "groq/mixtral-8x7b-32768",
82
+ "groq/gemma-7b-it",
83
+ ],
84
+ }
@@ -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
+ AVAILABLE_ENVIRONMENTS = ["development", "production", "staging"]
20
+
21
+
22
+ class ConfigManager:
23
+ client = None
24
+
25
+ @staticmethod
26
+ def get_from_route(schema: Type[T]) -> T:
27
+ """
28
+ Retrieves the configuration from the route context and returns a config object.
29
+
30
+ This method checks the route context for configuration information and returns
31
+ an instance of the specified schema based on the available context data.
32
+
33
+ Args:
34
+ schema (Type[T]): A Pydantic model class that defines the structure of the configuration.
35
+
36
+ Returns:
37
+ T: An instance of the specified schema populated with the configuration data.
38
+
39
+ Raises:
40
+ ValueError: If conflicting configuration sources are provided or if no valid
41
+ configuration source is found in the context.
42
+
43
+ Note:
44
+ The method prioritizes the inputs in the following way:
45
+ 1. 'config' (i.e. when called explicitly from the playground)
46
+ 2. 'environment'
47
+ 3. 'variant'
48
+ Only one of these should be provided.
49
+ """
50
+ context = route_context.get()
51
+ if ("config" in context and context["config"]) and (
52
+ ("environment" in context and context["environment"])
53
+ or ("variant" in context and context["variant"])
54
+ ):
55
+ raise ValueError(
56
+ "Either config, environment or variant must be provided. Not both."
57
+ )
58
+ if "config" in context and context["config"]:
59
+ return schema(**context["config"])
60
+ elif "environment" in context and context["environment"]:
61
+ return ConfigManager.get_from_registry(
62
+ schema, environment=context["environment"]
63
+ )
64
+ elif "variant" in context and context["variant"]:
65
+ return ConfigManager.get_from_registry(schema, variant=context["variant"])
66
+ else:
67
+ raise ValueError("Either config, environment or variant must be provided")
68
+
69
+ @staticmethod
70
+ def get_from_registry(
71
+ schema: Type[T],
72
+ environment: Optional[str] = None,
73
+ version: Optional[str] = None,
74
+ variant: Optional[str] = None,
75
+ ) -> T:
76
+ """
77
+ Pulls the parameters for the app variant from the server and returns a config object.
78
+
79
+ This method retrieves the configuration from the backend server based on the provided
80
+ environment or variant. It then validates and returns the configuration as an instance
81
+ of the specified schema.
82
+
83
+ Args:
84
+ schema (Type[T]): A Pydantic model class that defines the structure of the configuration.
85
+ environment (Optional[str]): The environment name to fetch the configuration for.
86
+ Must be one of "development", "production", or "staging".
87
+ version (Optional[str]): Currently not implemented. Will raise NotImplementedError if provided.
88
+ variant (Optional[str]): The variant name to fetch the configuration for.
89
+
90
+ Returns:
91
+ T: An instance of the specified schema populated with the configuration data.
92
+
93
+ Raises:
94
+ ValueError: If neither environment nor variant is provided.
95
+ NotImplementedError: If a specific version is requested (not yet implemented).
96
+ ValidationError: If the retrieved configuration data doesn't match the schema.
97
+ Exception: For any other errors during the process (e.g., API communication issues).
98
+
99
+ Note:
100
+ Either environment or variant must be provided, but not both.
101
+ """
102
+ if not ConfigManager.client:
103
+ try:
104
+ ConfigManager.client = AgentaApi(
105
+ base_url=singleton.host + "/api",
106
+ api_key=singleton.api_key if singleton.api_key else "",
107
+ )
108
+ except Exception as ex:
109
+ logger.error(
110
+ "Failed to initialize Agenta client with error: %s", str(ex)
111
+ )
112
+ raise
113
+ if not environment and not variant:
114
+ raise ValueError("Either environment or variant must be provided")
115
+ try:
116
+ if environment:
117
+ if version:
118
+ raise NotImplementedError(
119
+ "Getting config for a specific version is not implemented yet."
120
+ )
121
+ else:
122
+ assert (
123
+ environment in AVAILABLE_ENVIRONMENTS
124
+ ), f"Environment must be in {AVAILABLE_ENVIRONMENTS}"
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):
105
+ def __init__(self, path, config_schema: BaseModel):
106
+ self.config_schema: BaseModel = config_schema
76
107
  path = "/" + path.strip("/").strip()
77
108
  path = "" if path == "/" else path
78
-
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(f, route_path=self.route_path)
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__(self, func: Callable[..., Any], route_path=""):
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
- config_params = ag.config.all()
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,25 +182,30 @@ 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
- ag.config.set(**api_config_params)
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
- entrypoint_result = await self.execute_function(
147
- func,
148
- True, # inline trace: True
149
- *args,
150
- params=func_params,
151
- config_params=config_params,
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, func_signature, config_params, ingestible_files
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
  #
@@ -165,7 +216,10 @@ class entrypoint(BaseDecorator):
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
- if "environment" in kwargs and kwargs["environment"] is not None:
192
- ag.config.pull(environment_name=kwargs["environment"])
193
- elif "config" in kwargs and kwargs["config"] is not None:
194
- ag.config.pull(config_name=kwargs["config"])
195
- else:
196
- ag.config.pull(config_name="default")
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
- entrypoint_result = await self.execute_function(
204
- func,
205
- False, # inline trace: False
206
- *args,
207
- params=func_params,
208
- config_params=config_params,
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,7 +274,6 @@ class entrypoint(BaseDecorator):
215
274
  func_signature,
216
275
  ingestible_files,
217
276
  )
218
-
219
277
  if route_path == "":
220
278
  route_deployed = f"/{DEFAULT_PATH}_deployed"
221
279
  app.post(route_deployed, response_model=BaseResponse)(wrapper_deployed)
@@ -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
- func=route["func"],
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
- config_params: Dict[str, Any],
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
- self.add_config_params_to_parser(updated_params, config_params)
468
+ if config_class:
469
+ self.add_config_params_to_parser(updated_params, config_class)
470
+ else:
471
+ self.deprecated_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, config_params: Dict[str, Any]
500
+ self, updated_params: list, config_class: Type[BaseModel]
432
501
  ) -> None:
433
502
  """Add configuration parameters to function signature."""
434
- for name, param in config_params.items():
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 deprecated_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
- ag.config.set(**args_config_params)
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": ag.config.all(), "environment": "bash"})
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(f"\n========== Result ==========\n")
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, func: str, endpoint: str, params: 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_{func}_{endpoint}_post"
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:
File without changes
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: agenta
3
- Version: 0.21.0b1
3
+ Version: 0.23.0
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=nL_Fogmx_64xS--WjhE4gE_0h5LIJDbDGKpjZoNJ9oI,894
1
+ agenta/__init__.py,sha256=MCeFBzYtrVNdhElvl1fBjypsqhgEaAMKy6ZR-B3YmJk,993
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,15 @@ 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=5uHMCzXkCvIsDdEiwbnnn97KkzsFbBvyMwogCsv_Z5U,3509
129
- agenta/sdk/__init__.py,sha256=ewYNjm6AHlqkIrPfX2D_pXZMwShOdhEUcWXb7xGA2bk,769
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
+ agenta/sdk/assets.py,sha256=Zv4i8MVUSB3jMODQon1mzJtYxuntmrCNjLGk8f-2fls,2856
131
132
  agenta/sdk/client.py,sha256=trKyBOYFZRk0v5Eptxvh87yPf50Y9CqY6Qgv4Fy-VH4,2142
133
+ agenta/sdk/config_manager.py,sha256=n3UTpIhiB1J6FIQ5IvlucfGVpxoIIemnmdI68p34Yvw,8003
132
134
  agenta/sdk/context.py,sha256=q-PxL05-I84puunUAs9LGsffEXcYhDxhQxjuOz2vK90,901
133
135
  agenta/sdk/decorators/base.py,sha256=9aNdX5h8a2mFweuhdO-BQPwXGKY9ONPIdLRhSGAGMfY,217
134
- agenta/sdk/decorators/llm_entrypoint.py,sha256=9GFT6dpVWY2_1Ckh1Y_N0xSXwosoSjtNtqMEM7u55uY,28224
136
+ agenta/sdk/decorators/llm_entrypoint.py,sha256=dEhdwC9mvqIriOCqAA3qPlhvaa8kFNAwnJaNjbTO-MQ,35457
135
137
  agenta/sdk/decorators/tracing.py,sha256=e0olx2EEdjXY0NqpIoDJSVxCnUmv0woewTUuCJXy2tM,4166
136
138
  agenta/sdk/router.py,sha256=0sbajvn5C7t18anH6yNo7-oYxldHnYfwcbmQnIXBePw,269
137
139
  agenta/sdk/tracing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -140,7 +142,8 @@ agenta/sdk/tracing/llm_tracing.py,sha256=FfZQcID-8E4G0R34H29SA05yFUNDMEPccb4MqVc
140
142
  agenta/sdk/tracing/logger.py,sha256=GfH7V-jBHcn7h5dbdrnkDMe_ml3wkXFBeoQiqR4KVRc,474
141
143
  agenta/sdk/tracing/tasks_manager.py,sha256=FBSFOWIKBycyA4ShB2ZVMzrzYQ8pWGWWBClFX8nlZFA,3726
142
144
  agenta/sdk/tracing/tracing_context.py,sha256=nt3ewa-TK9BRJviGIZYazsAQUiG4daWxjtsbjeaDprs,789
143
- agenta/sdk/types.py,sha256=DSsKtoYh1ka_ketfkN10F0v8Oe2O4BB0WfnTkdIEyxE,5773
145
+ agenta/sdk/types.py,sha256=1AXUKTURFFgPH5iWeTYcNcg6dxVo1RrD0WuVe9e-NgU,5919
146
+ agenta/sdk/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
144
147
  agenta/sdk/utils/debug.py,sha256=QyuPsSoN0425UD13x_msPxSF_VT6YwHiQunZUibI-jg,2149
145
148
  agenta/sdk/utils/globals.py,sha256=JmhJcCOSbwvjQ6GDyUc2_SYR27DZk7YcrRH80ktHHOM,435
146
149
  agenta/sdk/utils/helper/openai_cost.py,sha256=1VkgvucDnNZm1pTfcVLz9icWunntp1d7zwMmnviy3Uw,5877
@@ -160,7 +163,7 @@ agenta/templates/simple_prompt/app.py,sha256=kODgF6lhzsaJPdgL5b21bUki6jkvqjWZzWR
160
163
  agenta/templates/simple_prompt/env.example,sha256=g9AE5bYcGPpxawXMJ96gh8oenEPCHTabsiOnfQo3c5k,70
161
164
  agenta/templates/simple_prompt/requirements.txt,sha256=ywRglRy7pPkw8bljmMEJJ4aOOQKrt9FGKULZ-DGkoBU,23
162
165
  agenta/templates/simple_prompt/template.toml,sha256=DQBtRrF4GU8LBEXOZ-GGuINXMQDKGTEG5y37tnvIUIE,60
163
- agenta-0.21.0b1.dist-info/METADATA,sha256=mDj2JjVAkAq5pfoBxzxy8K-5d4jBNwj0hwKclm_1MGo,26462
164
- agenta-0.21.0b1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
165
- agenta-0.21.0b1.dist-info/entry_points.txt,sha256=PDiu8_8AsL7ibU9v4iNoOKR1S7F2rdxjlEprjM9QOgo,46
166
- agenta-0.21.0b1.dist-info/RECORD,,
166
+ agenta-0.23.0.dist-info/METADATA,sha256=VEhH467aTqh-DeppazDWuc9REpfvqU5aXpaxl6wXFnY,26499
167
+ agenta-0.23.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
168
+ agenta-0.23.0.dist-info/entry_points.txt,sha256=PDiu8_8AsL7ibU9v4iNoOKR1S7F2rdxjlEprjM9QOgo,46
169
+ agenta-0.23.0.dist-info/RECORD,,