agenta 0.14.14a0__py3-none-any.whl → 0.15.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 +6 -3
- agenta/client/backend/client.py +8 -6
- agenta/client/backend/types/create_span.py +1 -1
- agenta/docker/docker-assets/Dockerfile.cloud.template +1 -1
- agenta/sdk/__init__.py +6 -4
- agenta/sdk/agenta_init.py +120 -108
- agenta/sdk/decorators/base.py +10 -0
- agenta/sdk/decorators/llm_entrypoint.py +499 -0
- agenta/sdk/decorators/tracing.py +98 -0
- agenta/sdk/tracing/llm_tracing.py +122 -103
- agenta/sdk/tracing/logger.py +1 -1
- agenta/sdk/tracing/tasks_manager.py +1 -3
- agenta/sdk/types.py +86 -67
- agenta/sdk/utils/globals.py +3 -1
- {agenta-0.14.14a0.dist-info → agenta-0.15.0.dist-info}/METADATA +2 -2
- {agenta-0.14.14a0.dist-info → agenta-0.15.0.dist-info}/RECORD +18 -17
- agenta/sdk/agenta_decorator.py +0 -570
- agenta/sdk/tracing/decorators.py +0 -41
- {agenta-0.14.14a0.dist-info → agenta-0.15.0.dist-info}/WHEEL +0 -0
- {agenta-0.14.14a0.dist-info → agenta-0.15.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,59 +1,79 @@
|
|
|
1
|
-
|
|
1
|
+
import os
|
|
2
|
+
from threading import Lock
|
|
2
3
|
from datetime import datetime, timezone
|
|
3
4
|
from typing import Optional, Dict, Any, List, Union
|
|
4
5
|
|
|
5
|
-
# Own Imports
|
|
6
6
|
from agenta.sdk.tracing.logger import llm_logger
|
|
7
7
|
from agenta.sdk.tracing.tasks_manager import TaskQueue
|
|
8
8
|
from agenta.client.backend.client import AsyncAgentaApi
|
|
9
9
|
from agenta.client.backend.client import AsyncObservabilityClient
|
|
10
10
|
from agenta.client.backend.types.create_span import CreateSpan, SpanKind, SpanStatusCode
|
|
11
11
|
|
|
12
|
-
# Third Party Imports
|
|
13
12
|
from bson.objectid import ObjectId
|
|
14
13
|
|
|
14
|
+
VARIANT_TRACKING_FEATURE_FLAG = False
|
|
15
15
|
|
|
16
|
-
class Tracing(object):
|
|
17
|
-
"""Agenta llm tracing object.
|
|
18
16
|
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
class SingletonMeta(type):
|
|
18
|
+
"""
|
|
19
|
+
Thread-safe implementation of Singleton.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
_instances = {} # type: ignore
|
|
23
|
+
|
|
24
|
+
# We need the lock mechanism to synchronize threads \
|
|
25
|
+
# during the initial access to the Singleton object.
|
|
26
|
+
_lock: Lock = Lock()
|
|
27
|
+
|
|
28
|
+
def __call__(cls, *args, **kwargs):
|
|
29
|
+
"""
|
|
30
|
+
Ensures that changes to the `__init__` arguments do not affect the
|
|
31
|
+
returned instance.
|
|
32
|
+
|
|
33
|
+
Uses a lock to make this method thread-safe. If an instance of the class
|
|
34
|
+
does not already exist, it creates one. Otherwise, it returns the
|
|
35
|
+
existing instance.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
with cls._lock:
|
|
39
|
+
if cls not in cls._instances:
|
|
40
|
+
instance = super().__call__(*args, **kwargs)
|
|
41
|
+
cls._instances[cls] = instance
|
|
42
|
+
return cls._instances[cls]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class Tracing(metaclass=SingletonMeta):
|
|
46
|
+
"""The `Tracing` class is an agent for LLM tracing with specific initialization arguments.
|
|
47
|
+
|
|
48
|
+
__init__ args:
|
|
49
|
+
host (str): The URL of the backend host
|
|
21
50
|
api_key (str): The API Key of the backend host
|
|
22
51
|
tasks_manager (TaskQueue): The tasks manager dedicated to handling asynchronous tasks
|
|
23
52
|
llm_logger (Logger): The logger associated with the LLM tracing
|
|
24
53
|
max_workers (int): The maximum number of workers to run tracing
|
|
25
54
|
"""
|
|
26
55
|
|
|
27
|
-
_instance = None
|
|
28
|
-
|
|
29
|
-
def __new__(cls, *args, **kwargs):
|
|
30
|
-
if not cls._instance:
|
|
31
|
-
cls._instance = super().__new__(cls)
|
|
32
|
-
return cls._instance
|
|
33
|
-
|
|
34
56
|
def __init__(
|
|
35
57
|
self,
|
|
36
|
-
|
|
58
|
+
host: str,
|
|
37
59
|
app_id: str,
|
|
38
|
-
variant_id: str,
|
|
39
|
-
variant_name: Optional[str] = None,
|
|
40
60
|
api_key: Optional[str] = None,
|
|
41
61
|
max_workers: Optional[int] = None,
|
|
42
62
|
):
|
|
43
|
-
self.
|
|
63
|
+
self.host = host + "/api"
|
|
44
64
|
self.api_key = api_key if api_key is not None else ""
|
|
45
65
|
self.llm_logger = llm_logger
|
|
46
66
|
self.app_id = app_id
|
|
47
|
-
self.variant_id = variant_id
|
|
48
|
-
self.variant_name = variant_name
|
|
49
67
|
self.tasks_manager = TaskQueue(
|
|
50
68
|
max_workers if max_workers else 4, logger=llm_logger
|
|
51
69
|
)
|
|
52
|
-
self.active_span =
|
|
53
|
-
self.
|
|
54
|
-
self.
|
|
55
|
-
self.recorded_spans: List[CreateSpan] = []
|
|
70
|
+
self.active_span: Optional[CreateSpan] = None
|
|
71
|
+
self.active_trace_id: Optional[str] = None
|
|
72
|
+
self.pending_spans: List[CreateSpan] = []
|
|
56
73
|
self.tags: List[str] = []
|
|
74
|
+
self.trace_config_cache: Dict[
|
|
75
|
+
str, Any
|
|
76
|
+
] = {} # used to save the trace configuration before starting the first span
|
|
57
77
|
self.span_dict: Dict[str, CreateSpan] = {} # type: ignore
|
|
58
78
|
|
|
59
79
|
@property
|
|
@@ -65,131 +85,130 @@ class Tracing(object):
|
|
|
65
85
|
"""
|
|
66
86
|
|
|
67
87
|
return AsyncAgentaApi(
|
|
68
|
-
base_url=self.
|
|
88
|
+
base_url=self.host, api_key=self.api_key, timeout=120 # type: ignore
|
|
69
89
|
).observability
|
|
70
90
|
|
|
71
91
|
def set_span_attribute(
|
|
72
|
-
self, parent_key: Optional[str] = None, attributes: Dict[str, Any] = {}
|
|
73
|
-
):
|
|
74
|
-
span = self.span_dict[self.active_span.id] # type: ignore
|
|
75
|
-
for key, value in attributes.items():
|
|
76
|
-
self.set_attribute(span.attributes, key, value, parent_key) # type: ignore
|
|
77
|
-
|
|
78
|
-
def set_attribute(
|
|
79
92
|
self,
|
|
80
|
-
|
|
81
|
-
key: str,
|
|
82
|
-
value: Any,
|
|
83
|
-
parent_key: Optional[str] = None,
|
|
93
|
+
attributes: Dict[str, Any] = {},
|
|
84
94
|
):
|
|
85
|
-
if
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
95
|
+
if (
|
|
96
|
+
self.active_span is None
|
|
97
|
+
): # This is the case where entrypoint wants to save the trace information but the parent span has not been initialized yet
|
|
98
|
+
for key, value in attributes.items():
|
|
99
|
+
self.trace_config_cache[key] = value
|
|
90
100
|
else:
|
|
91
|
-
|
|
101
|
+
for key, value in attributes.items():
|
|
102
|
+
self.active_span.attributes[key] = value
|
|
92
103
|
|
|
93
104
|
def set_trace_tags(self, tags: List[str]):
|
|
94
105
|
self.tags.extend(tags)
|
|
95
106
|
|
|
96
|
-
def start_parent_span(
|
|
97
|
-
self, name: str, inputs: Dict[str, Any], config: Dict[str, Any], **kwargs
|
|
98
|
-
):
|
|
99
|
-
trace_id = self._create_trace_id()
|
|
100
|
-
span_id = self._create_span_id()
|
|
101
|
-
self.llm_logger.info("Recording parent span...")
|
|
102
|
-
span = CreateSpan(
|
|
103
|
-
id=span_id,
|
|
104
|
-
app_id=self.app_id,
|
|
105
|
-
variant_id=self.variant_id,
|
|
106
|
-
variant_name=self.variant_name,
|
|
107
|
-
inputs=inputs,
|
|
108
|
-
name=name,
|
|
109
|
-
config=config,
|
|
110
|
-
environment=kwargs.get("environment"),
|
|
111
|
-
spankind=SpanKind.WORKFLOW.value,
|
|
112
|
-
status=SpanStatusCode.UNSET.value,
|
|
113
|
-
start_time=datetime.now(timezone.utc),
|
|
114
|
-
)
|
|
115
|
-
self.active_trace = span
|
|
116
|
-
self.recording_trace_id = trace_id
|
|
117
|
-
self.parent_span_id = span.id
|
|
118
|
-
self.llm_logger.info(
|
|
119
|
-
f"Recorded active_trace and setting parent_span_id: {span.id}"
|
|
120
|
-
)
|
|
121
|
-
|
|
122
107
|
def start_span(
|
|
123
108
|
self,
|
|
124
109
|
name: str,
|
|
125
110
|
spankind: str,
|
|
126
111
|
input: Dict[str, Any],
|
|
127
|
-
config: Dict[str, Any] =
|
|
112
|
+
config: Optional[Dict[str, Any]] = None,
|
|
113
|
+
**kwargs,
|
|
128
114
|
) -> CreateSpan:
|
|
129
115
|
span_id = self._create_span_id()
|
|
130
|
-
self.llm_logger.info(
|
|
116
|
+
self.llm_logger.info(
|
|
117
|
+
f"Recording {'parent' if spankind == 'workflow' else spankind} span..."
|
|
118
|
+
)
|
|
131
119
|
span = CreateSpan(
|
|
132
120
|
id=span_id,
|
|
133
121
|
inputs=input,
|
|
134
122
|
name=name,
|
|
135
123
|
app_id=self.app_id,
|
|
136
|
-
variant_id=self.variant_id,
|
|
137
|
-
variant_name=self.variant_name,
|
|
138
124
|
config=config,
|
|
139
|
-
environment=self.active_trace.environment,
|
|
140
|
-
parent_span_id=self.parent_span_id,
|
|
141
125
|
spankind=spankind.upper(),
|
|
142
126
|
attributes={},
|
|
143
127
|
status=SpanStatusCode.UNSET.value,
|
|
144
128
|
start_time=datetime.now(timezone.utc),
|
|
129
|
+
outputs=None,
|
|
130
|
+
tags=None,
|
|
131
|
+
user=None,
|
|
132
|
+
end_time=None,
|
|
133
|
+
tokens=None,
|
|
134
|
+
cost=None,
|
|
135
|
+
token_consumption=None,
|
|
136
|
+
parent_span_id=None,
|
|
145
137
|
)
|
|
146
138
|
|
|
147
|
-
self.
|
|
139
|
+
if self.active_trace_id is None: # This is a parent span
|
|
140
|
+
self.active_trace_id = self._create_trace_id()
|
|
141
|
+
span.environment = (
|
|
142
|
+
self.trace_config_cache.get("environment")
|
|
143
|
+
if self.trace_config_cache is not None
|
|
144
|
+
else os.environ.get("environment", "unset")
|
|
145
|
+
)
|
|
146
|
+
span.config = (
|
|
147
|
+
self.trace_config_cache.get("config")
|
|
148
|
+
if not config and self.trace_config_cache is not None
|
|
149
|
+
else None
|
|
150
|
+
)
|
|
151
|
+
if VARIANT_TRACKING_FEATURE_FLAG:
|
|
152
|
+
# TODO: we should get the variant_id and variant_name (and environment) from the config object
|
|
153
|
+
span.variant_id = config.variant_id
|
|
154
|
+
span.variant_name = (config.variant_name,)
|
|
155
|
+
|
|
156
|
+
else:
|
|
157
|
+
span.parent_span_id = self.active_span.id
|
|
148
158
|
self.span_dict[span.id] = span
|
|
149
|
-
self.
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
)
|
|
159
|
+
self.active_span = span
|
|
160
|
+
|
|
161
|
+
self.llm_logger.info(f"Recorded span and setting parent_span_id: {span.id}")
|
|
153
162
|
return span
|
|
154
163
|
|
|
155
164
|
def update_span_status(self, span: CreateSpan, value: str):
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
)
|
|
165
|
+
span.status = value
|
|
166
|
+
|
|
167
|
+
def end_span(self, outputs: Dict[str, Any]):
|
|
168
|
+
"""
|
|
169
|
+
Ends the active span, if it is a parent span, ends the trace too.
|
|
170
|
+
"""
|
|
171
|
+
if self.active_span is None:
|
|
172
|
+
raise ValueError("There is no active span to end.")
|
|
173
|
+
self.active_span.end_time = datetime.now(timezone.utc)
|
|
174
|
+
self.active_span.outputs = [outputs.get("message", "")]
|
|
175
|
+
self.active_span.cost = outputs.get("cost", None)
|
|
176
|
+
self.active_span.tokens = outputs.get("usage", None)
|
|
167
177
|
|
|
168
178
|
# Push span to list of recorded spans
|
|
169
|
-
self.
|
|
179
|
+
self.pending_spans.append(self.active_span)
|
|
170
180
|
self.llm_logger.info(
|
|
171
|
-
f"Pushed {
|
|
181
|
+
f"Pushed {self.active_span.spankind} span {self.active_span.id} to recorded spans."
|
|
172
182
|
)
|
|
183
|
+
if self.active_span.parent_span_id is None:
|
|
184
|
+
self.end_trace(parent_span=self.active_span)
|
|
185
|
+
else:
|
|
186
|
+
self.active_span = self.span_dict[self.active_span.parent_span_id]
|
|
173
187
|
|
|
174
|
-
def
|
|
175
|
-
self.end_span(outputs=outputs, span=span, **kwargs)
|
|
188
|
+
def end_trace(self, parent_span: CreateSpan):
|
|
176
189
|
if self.api_key == "":
|
|
177
190
|
return
|
|
178
191
|
|
|
179
|
-
|
|
180
|
-
|
|
192
|
+
if not self.active_trace_id:
|
|
193
|
+
raise RuntimeError("No active trace to end.")
|
|
194
|
+
|
|
195
|
+
self.llm_logger.info("Preparing to send recorded spans for processing.")
|
|
196
|
+
self.llm_logger.info(f"Recorded spans => {len(self.pending_spans)}")
|
|
181
197
|
self.tasks_manager.add_task(
|
|
182
|
-
self.
|
|
198
|
+
self.active_trace_id,
|
|
183
199
|
"trace",
|
|
184
200
|
self.client.create_traces(
|
|
185
|
-
trace=self.
|
|
201
|
+
trace=self.active_trace_id, spans=self.pending_spans # type: ignore
|
|
186
202
|
),
|
|
187
203
|
self.client,
|
|
188
204
|
)
|
|
189
205
|
self.llm_logger.info(
|
|
190
|
-
f"Tracing for {
|
|
206
|
+
f"Tracing for {parent_span.id} recorded successfully and sent for processing."
|
|
191
207
|
)
|
|
192
|
-
self.
|
|
208
|
+
self._clear_pending_spans()
|
|
209
|
+
self.active_trace_id = None
|
|
210
|
+
self.active_span = None
|
|
211
|
+
self.trace_config_cache.clear()
|
|
193
212
|
|
|
194
213
|
def _create_trace_id(self) -> str:
|
|
195
214
|
"""Creates a unique mongo id for the trace object.
|
|
@@ -209,12 +228,12 @@ class Tracing(object):
|
|
|
209
228
|
|
|
210
229
|
return str(ObjectId())
|
|
211
230
|
|
|
212
|
-
def
|
|
231
|
+
def _clear_pending_spans(self) -> None:
|
|
213
232
|
"""
|
|
214
233
|
Clear the list of recorded spans to prepare for next batch processing.
|
|
215
234
|
"""
|
|
216
235
|
|
|
217
|
-
self.
|
|
236
|
+
self.pending_spans = []
|
|
218
237
|
self.llm_logger.info(
|
|
219
|
-
f"Cleared all recorded spans from batch: {self.
|
|
238
|
+
f"Cleared all recorded spans from batch: {self.pending_spans}"
|
|
220
239
|
)
|
agenta/sdk/tracing/logger.py
CHANGED
|
@@ -106,9 +106,7 @@ class TaskQueue(object):
|
|
|
106
106
|
future.result()
|
|
107
107
|
except Exception as exc:
|
|
108
108
|
self._logger.error(f"Error running task: {str(exc)}")
|
|
109
|
-
self._logger.error(
|
|
110
|
-
f"Recording trace {task.coroutine_type} status to ERROR."
|
|
111
|
-
)
|
|
109
|
+
self._logger.error(f"Recording {task.coroutine_type} status to ERROR.")
|
|
112
110
|
break
|
|
113
111
|
finally:
|
|
114
112
|
self.tasks.task_done()
|
agenta/sdk/types.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
|
-
from typing import Dict, List, Optional
|
|
2
|
+
from typing import Any, Dict, List, Optional
|
|
3
3
|
|
|
4
|
-
from pydantic import
|
|
4
|
+
from pydantic import BaseModel, Extra, HttpUrl, Field
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class InFile:
|
|
@@ -24,75 +24,87 @@ class FuncResponse(BaseModel):
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
class DictInput(dict):
|
|
27
|
-
def __new__(cls, default_keys
|
|
27
|
+
def __new__(cls, default_keys=None):
|
|
28
28
|
instance = super().__new__(cls, default_keys)
|
|
29
29
|
if default_keys is None:
|
|
30
30
|
default_keys = []
|
|
31
|
-
instance.data = [key for key in default_keys]
|
|
31
|
+
instance.data = [key for key in default_keys]
|
|
32
32
|
return instance
|
|
33
33
|
|
|
34
34
|
@classmethod
|
|
35
|
-
def
|
|
36
|
-
|
|
35
|
+
def __modify_schema__(cls, field_schema):
|
|
36
|
+
field_schema.update({"x-parameter": "dict"})
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
class TextParam(str):
|
|
40
40
|
@classmethod
|
|
41
|
-
def
|
|
42
|
-
|
|
41
|
+
def __modify_schema__(cls, field_schema):
|
|
42
|
+
field_schema.update({"x-parameter": "text"})
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
class BinaryParam(int):
|
|
46
46
|
def __new__(cls, value: bool = False):
|
|
47
47
|
instance = super().__new__(cls, int(value))
|
|
48
|
-
instance.default = value
|
|
48
|
+
instance.default = value
|
|
49
49
|
return instance
|
|
50
50
|
|
|
51
51
|
@classmethod
|
|
52
|
-
def
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
def __modify_schema__(cls, field_schema):
|
|
53
|
+
field_schema.update(
|
|
54
|
+
{
|
|
55
|
+
"x-parameter": "bool",
|
|
56
|
+
"type": "boolean",
|
|
57
|
+
}
|
|
58
|
+
)
|
|
57
59
|
|
|
58
60
|
|
|
59
61
|
class IntParam(int):
|
|
60
62
|
def __new__(cls, default: int = 6, minval: float = 1, maxval: float = 10):
|
|
61
63
|
instance = super().__new__(cls, default)
|
|
62
|
-
instance.minval = minval
|
|
63
|
-
instance.maxval = maxval
|
|
64
|
+
instance.minval = minval
|
|
65
|
+
instance.maxval = maxval
|
|
64
66
|
return instance
|
|
65
67
|
|
|
66
68
|
@classmethod
|
|
67
|
-
def
|
|
68
|
-
|
|
69
|
+
def __modify_schema__(cls, field_schema):
|
|
70
|
+
field_schema.update(
|
|
71
|
+
{
|
|
72
|
+
"x-parameter": "int",
|
|
73
|
+
"type": "integer",
|
|
74
|
+
"minimum": 1,
|
|
75
|
+
"maximum": 10,
|
|
76
|
+
}
|
|
77
|
+
)
|
|
69
78
|
|
|
70
79
|
|
|
71
80
|
class FloatParam(float):
|
|
72
81
|
def __new__(cls, default: float = 0.5, minval: float = 0.0, maxval: float = 1.0):
|
|
73
82
|
instance = super().__new__(cls, default)
|
|
74
|
-
instance.
|
|
75
|
-
instance.
|
|
76
|
-
instance.maxval = maxval # type: ignore
|
|
83
|
+
instance.minval = minval
|
|
84
|
+
instance.maxval = maxval
|
|
77
85
|
return instance
|
|
78
86
|
|
|
79
87
|
@classmethod
|
|
80
|
-
def
|
|
81
|
-
|
|
88
|
+
def __modify_schema__(cls, field_schema):
|
|
89
|
+
field_schema.update(
|
|
90
|
+
{
|
|
91
|
+
"x-parameter": "float",
|
|
92
|
+
"type": "number",
|
|
93
|
+
"minimum": 0.0,
|
|
94
|
+
"maximum": 1.0,
|
|
95
|
+
}
|
|
96
|
+
)
|
|
82
97
|
|
|
83
98
|
|
|
84
99
|
class MultipleChoiceParam(str):
|
|
85
|
-
def __new__(
|
|
86
|
-
|
|
87
|
-
):
|
|
88
|
-
if default is not None and type(default) is list:
|
|
100
|
+
def __new__(cls, default: str = None, choices: List[str] = None):
|
|
101
|
+
if type(default) is list:
|
|
89
102
|
raise ValueError(
|
|
90
103
|
"The order of the parameters for MultipleChoiceParam is wrong! It's MultipleChoiceParam(default, choices) and not the opposite"
|
|
91
104
|
)
|
|
92
|
-
|
|
93
|
-
if not default and choices is not None:
|
|
105
|
+
if default is None and choices:
|
|
94
106
|
# if a default value is not provided,
|
|
95
|
-
#
|
|
107
|
+
# uset the first value in the choices list
|
|
96
108
|
default = choices[0]
|
|
97
109
|
|
|
98
110
|
if default is None and not choices:
|
|
@@ -100,25 +112,28 @@ class MultipleChoiceParam(str):
|
|
|
100
112
|
raise ValueError("You must provide either a default value or choices")
|
|
101
113
|
|
|
102
114
|
instance = super().__new__(cls, default)
|
|
103
|
-
instance.choices = choices
|
|
104
|
-
instance.default = default
|
|
115
|
+
instance.choices = choices
|
|
116
|
+
instance.default = default
|
|
105
117
|
return instance
|
|
106
118
|
|
|
107
119
|
@classmethod
|
|
108
|
-
def
|
|
109
|
-
|
|
120
|
+
def __modify_schema__(cls, field_schema: dict[str, Any]):
|
|
121
|
+
field_schema.update(
|
|
122
|
+
{
|
|
123
|
+
"x-parameter": "choice",
|
|
124
|
+
"type": "string",
|
|
125
|
+
"enum": [],
|
|
126
|
+
}
|
|
127
|
+
)
|
|
110
128
|
|
|
111
129
|
|
|
112
130
|
class GroupedMultipleChoiceParam(str):
|
|
113
|
-
def __new__(
|
|
114
|
-
cls,
|
|
115
|
-
default: Optional[str] = None,
|
|
116
|
-
choices: Optional[Dict[str, List[str]]] = None,
|
|
117
|
-
):
|
|
131
|
+
def __new__(cls, default: str = None, choices: Dict[str, List[str]] = None):
|
|
118
132
|
if choices is None:
|
|
119
133
|
choices = {}
|
|
120
|
-
|
|
121
|
-
|
|
134
|
+
if default and not any(
|
|
135
|
+
default in choice_list for choice_list in choices.values()
|
|
136
|
+
):
|
|
122
137
|
if not choices:
|
|
123
138
|
print(
|
|
124
139
|
f"Warning: Default value {default} provided but choices are empty."
|
|
@@ -129,23 +144,31 @@ class GroupedMultipleChoiceParam(str):
|
|
|
129
144
|
)
|
|
130
145
|
|
|
131
146
|
if not default:
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
default = default_selected_choice[0]
|
|
147
|
+
for choices in choices.values():
|
|
148
|
+
if choices:
|
|
149
|
+
default = choices[0]
|
|
150
|
+
break
|
|
137
151
|
|
|
138
152
|
instance = super().__new__(cls, default)
|
|
139
|
-
instance.choices = choices
|
|
140
|
-
instance.default = default
|
|
153
|
+
instance.choices = choices
|
|
154
|
+
instance.default = default
|
|
141
155
|
return instance
|
|
142
156
|
|
|
143
157
|
@classmethod
|
|
144
|
-
def
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
158
|
+
def __modify_schema__(cls, field_schema: dict[str, Any], **kwargs):
|
|
159
|
+
choices = kwargs.get("choices", {})
|
|
160
|
+
field_schema.update(
|
|
161
|
+
{
|
|
162
|
+
"x-parameter": "grouped_choice",
|
|
163
|
+
"type": "string",
|
|
164
|
+
"choices": choices,
|
|
165
|
+
}
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class Message(BaseModel):
|
|
170
|
+
role: str
|
|
171
|
+
content: str
|
|
149
172
|
|
|
150
173
|
|
|
151
174
|
class MessagesInput(list):
|
|
@@ -160,32 +183,28 @@ class MessagesInput(list):
|
|
|
160
183
|
|
|
161
184
|
"""
|
|
162
185
|
|
|
163
|
-
def __new__(cls, messages: List[Dict[str, str]] =
|
|
164
|
-
instance = super().__new__(cls)
|
|
165
|
-
instance.default = messages
|
|
186
|
+
def __new__(cls, messages: List[Dict[str, str]] = None):
|
|
187
|
+
instance = super().__new__(cls, messages)
|
|
188
|
+
instance.default = messages
|
|
166
189
|
return instance
|
|
167
190
|
|
|
168
191
|
@classmethod
|
|
169
|
-
def
|
|
170
|
-
|
|
192
|
+
def __modify_schema__(cls, field_schema: dict[str, Any]):
|
|
193
|
+
field_schema.update({"x-parameter": "messages", "type": "array"})
|
|
171
194
|
|
|
172
195
|
|
|
173
196
|
class FileInputURL(HttpUrl):
|
|
174
|
-
def __new__(cls, url: str):
|
|
175
|
-
instance = super().__new__(cls, url)
|
|
176
|
-
instance.default = url # type: ignore
|
|
177
|
-
return instance
|
|
178
|
-
|
|
179
197
|
@classmethod
|
|
180
|
-
def
|
|
181
|
-
|
|
198
|
+
def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None:
|
|
199
|
+
field_schema.update({"x-parameter": "file_url", "type": "string"})
|
|
182
200
|
|
|
183
201
|
|
|
184
202
|
class Context(BaseModel):
|
|
185
|
-
|
|
203
|
+
class Config:
|
|
204
|
+
extra = Extra.allow
|
|
186
205
|
|
|
187
206
|
def to_json(self):
|
|
188
|
-
return self.
|
|
207
|
+
return self.json()
|
|
189
208
|
|
|
190
209
|
@classmethod
|
|
191
210
|
def from_json(cls, json_str: str):
|
agenta/sdk/utils/globals.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import agenta
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
def set_global(setup=None, config=None):
|
|
4
|
+
def set_global(setup=None, config=None, tracing=None):
|
|
5
5
|
"""Allows usage of agenta.config and agenta.setup in the user's code.
|
|
6
6
|
|
|
7
7
|
Args:
|
|
@@ -12,3 +12,5 @@ def set_global(setup=None, config=None):
|
|
|
12
12
|
agenta.setup = setup
|
|
13
13
|
if config is not None:
|
|
14
14
|
agenta.config = config
|
|
15
|
+
if tracing is not None:
|
|
16
|
+
agenta.tracing = tracing
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: agenta
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.15.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
|
|
@@ -23,7 +23,7 @@ Requires-Dist: httpx (>=0.24,<0.28)
|
|
|
23
23
|
Requires-Dist: importlib-metadata (>=6.7,<8.0)
|
|
24
24
|
Requires-Dist: ipdb (>=0.13)
|
|
25
25
|
Requires-Dist: posthog (>=3.1.0,<4.0.0)
|
|
26
|
-
Requires-Dist: pydantic (
|
|
26
|
+
Requires-Dist: pydantic (==1.10.13)
|
|
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)
|