promptlayer 1.0.35__py3-none-any.whl → 1.0.78__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.
- promptlayer/__init__.py +37 -2
- promptlayer/exceptions.py +119 -0
- promptlayer/groups/__init__.py +9 -5
- promptlayer/groups/groups.py +4 -6
- promptlayer/promptlayer.py +237 -104
- promptlayer/promptlayer_base.py +31 -40
- promptlayer/promptlayer_mixins.py +216 -65
- promptlayer/span_exporter.py +19 -24
- promptlayer/streaming/__init__.py +64 -0
- promptlayer/streaming/blueprint_builder.py +382 -0
- promptlayer/streaming/response_handlers.py +960 -0
- promptlayer/streaming/stream_processor.py +106 -0
- promptlayer/templates.py +14 -12
- promptlayer/track/__init__.py +32 -20
- promptlayer/track/track.py +47 -30
- promptlayer/types/prompt_template.py +33 -1
- promptlayer/utils.py +1256 -967
- {promptlayer-1.0.35.dist-info → promptlayer-1.0.78.dist-info}/METADATA +16 -12
- promptlayer-1.0.78.dist-info/RECORD +23 -0
- {promptlayer-1.0.35.dist-info → promptlayer-1.0.78.dist-info}/WHEEL +1 -1
- promptlayer-1.0.35.dist-info/RECORD +0 -18
- {promptlayer-1.0.35.dist-info → promptlayer-1.0.78.dist-info/licenses}/LICENSE +0 -0
promptlayer/promptlayer.py
CHANGED
|
@@ -1,31 +1,64 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
2
4
|
import os
|
|
3
5
|
from typing import Any, Dict, List, Literal, Optional, Union
|
|
4
6
|
|
|
5
7
|
import nest_asyncio
|
|
6
8
|
|
|
9
|
+
from promptlayer import exceptions as _exceptions
|
|
7
10
|
from promptlayer.groups import AsyncGroupManager, GroupManager
|
|
8
11
|
from promptlayer.promptlayer_base import PromptLayerBase
|
|
9
12
|
from promptlayer.promptlayer_mixins import PromptLayerMixin
|
|
13
|
+
from promptlayer.streaming import astream_response, stream_response
|
|
10
14
|
from promptlayer.templates import AsyncTemplateManager, TemplateManager
|
|
11
15
|
from promptlayer.track import AsyncTrackManager, TrackManager
|
|
12
16
|
from promptlayer.types.prompt_template import PromptTemplate
|
|
13
17
|
from promptlayer.utils import (
|
|
18
|
+
RERAISE_ORIGINAL_EXCEPTION,
|
|
19
|
+
_get_workflow_workflow_id_or_name,
|
|
14
20
|
arun_workflow_request,
|
|
15
|
-
astream_response,
|
|
16
21
|
atrack_request,
|
|
17
22
|
autil_log_request,
|
|
18
|
-
stream_response,
|
|
19
23
|
track_request,
|
|
20
24
|
util_log_request,
|
|
21
25
|
)
|
|
22
26
|
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_base_url(base_url: Union[str, None]):
|
|
31
|
+
return base_url or os.environ.get("PROMPTLAYER_BASE_URL", "https://api.promptlayer.com")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def is_workflow_results_dict(obj: Any) -> bool:
|
|
35
|
+
if not isinstance(obj, dict):
|
|
36
|
+
return False
|
|
37
|
+
|
|
38
|
+
required_keys = {
|
|
39
|
+
"status",
|
|
40
|
+
"value",
|
|
41
|
+
"error_message",
|
|
42
|
+
"raw_error_message",
|
|
43
|
+
"is_output_node",
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
for val in obj.values():
|
|
47
|
+
if not isinstance(val, dict):
|
|
48
|
+
return False
|
|
49
|
+
if not required_keys.issubset(val.keys()):
|
|
50
|
+
return False
|
|
51
|
+
|
|
52
|
+
return True
|
|
53
|
+
|
|
23
54
|
|
|
24
55
|
class PromptLayer(PromptLayerMixin):
|
|
25
56
|
def __init__(
|
|
26
57
|
self,
|
|
27
|
-
api_key: str = None,
|
|
58
|
+
api_key: Union[str, None] = None,
|
|
28
59
|
enable_tracing: bool = False,
|
|
60
|
+
base_url: Union[str, None] = None,
|
|
61
|
+
throw_on_error: bool = True,
|
|
29
62
|
):
|
|
30
63
|
if api_key is None:
|
|
31
64
|
api_key = os.environ.get("PROMPTLAYER_API_KEY")
|
|
@@ -36,13 +69,15 @@ class PromptLayer(PromptLayerMixin):
|
|
|
36
69
|
"Please set the PROMPTLAYER_API_KEY environment variable or pass the api_key parameter."
|
|
37
70
|
)
|
|
38
71
|
|
|
72
|
+
self.base_url = get_base_url(base_url)
|
|
39
73
|
self.api_key = api_key
|
|
40
|
-
self.
|
|
41
|
-
self.
|
|
74
|
+
self.throw_on_error = throw_on_error
|
|
75
|
+
self.templates = TemplateManager(api_key, self.base_url, self.throw_on_error)
|
|
76
|
+
self.group = GroupManager(api_key, self.base_url, self.throw_on_error)
|
|
42
77
|
self.tracer_provider, self.tracer = self._initialize_tracer(
|
|
43
|
-
api_key, enable_tracing
|
|
78
|
+
api_key, self.base_url, self.throw_on_error, enable_tracing
|
|
44
79
|
)
|
|
45
|
-
self.track = TrackManager(api_key)
|
|
80
|
+
self.track = TrackManager(api_key, self.base_url, self.throw_on_error)
|
|
46
81
|
|
|
47
82
|
def __getattr__(
|
|
48
83
|
self,
|
|
@@ -51,24 +86,20 @@ class PromptLayer(PromptLayerMixin):
|
|
|
51
86
|
if name == "openai":
|
|
52
87
|
import openai as openai_module
|
|
53
88
|
|
|
54
|
-
|
|
55
|
-
openai_module,
|
|
56
|
-
function_name="openai",
|
|
57
|
-
api_key=self.api_key,
|
|
58
|
-
tracer=self.tracer,
|
|
89
|
+
return PromptLayerBase(
|
|
90
|
+
self.api_key, self.base_url, openai_module, function_name="openai", tracer=self.tracer
|
|
59
91
|
)
|
|
60
|
-
return openai
|
|
61
92
|
elif name == "anthropic":
|
|
62
93
|
import anthropic as anthropic_module
|
|
63
94
|
|
|
64
|
-
|
|
95
|
+
return PromptLayerBase(
|
|
96
|
+
self.api_key,
|
|
97
|
+
self.base_url,
|
|
65
98
|
anthropic_module,
|
|
66
99
|
function_name="anthropic",
|
|
67
100
|
provider_type="anthropic",
|
|
68
|
-
api_key=self.api_key,
|
|
69
101
|
tracer=self.tracer,
|
|
70
102
|
)
|
|
71
|
-
return anthropic
|
|
72
103
|
else:
|
|
73
104
|
raise AttributeError(f"module {__name__} has no attribute {name}")
|
|
74
105
|
|
|
@@ -80,6 +111,7 @@ class PromptLayer(PromptLayerMixin):
|
|
|
80
111
|
input_variables,
|
|
81
112
|
group_id,
|
|
82
113
|
pl_run_span_id: Union[str, None] = None,
|
|
114
|
+
request_start_time: Union[float, None] = None,
|
|
83
115
|
):
|
|
84
116
|
def _track_request(**body):
|
|
85
117
|
track_request_kwargs = self._prepare_track_request_kwargs(
|
|
@@ -89,9 +121,10 @@ class PromptLayer(PromptLayerMixin):
|
|
|
89
121
|
input_variables,
|
|
90
122
|
group_id,
|
|
91
123
|
pl_run_span_id,
|
|
124
|
+
request_start_time=request_start_time,
|
|
92
125
|
**body,
|
|
93
126
|
)
|
|
94
|
-
return track_request(**track_request_kwargs)
|
|
127
|
+
return track_request(self.base_url, self.throw_on_error, **track_request_kwargs)
|
|
95
128
|
|
|
96
129
|
return _track_request
|
|
97
130
|
|
|
@@ -108,50 +141,82 @@ class PromptLayer(PromptLayerMixin):
|
|
|
108
141
|
group_id: Union[int, None] = None,
|
|
109
142
|
stream: bool = False,
|
|
110
143
|
pl_run_span_id: Union[str, None] = None,
|
|
144
|
+
provider: Union[str, None] = None,
|
|
145
|
+
model: Union[str, None] = None,
|
|
111
146
|
) -> Dict[str, Any]:
|
|
147
|
+
import datetime
|
|
148
|
+
|
|
112
149
|
get_prompt_template_params = self._prepare_get_prompt_template_params(
|
|
113
150
|
prompt_version=prompt_version,
|
|
114
151
|
prompt_release_label=prompt_release_label,
|
|
115
152
|
input_variables=input_variables,
|
|
116
153
|
metadata=metadata,
|
|
154
|
+
provider=provider,
|
|
155
|
+
model=model,
|
|
156
|
+
model_parameter_overrides=model_parameter_overrides,
|
|
117
157
|
)
|
|
118
158
|
prompt_blueprint = self.templates.get(prompt_name, get_prompt_template_params)
|
|
159
|
+
if not prompt_blueprint:
|
|
160
|
+
raise _exceptions.PromptLayerNotFoundError(
|
|
161
|
+
f"Prompt template '{prompt_name}' not found.",
|
|
162
|
+
response=None,
|
|
163
|
+
body=None,
|
|
164
|
+
)
|
|
119
165
|
prompt_blueprint_model = self._validate_and_extract_model_from_prompt_blueprint(
|
|
120
166
|
prompt_blueprint=prompt_blueprint, prompt_name=prompt_name
|
|
121
167
|
)
|
|
122
|
-
|
|
168
|
+
llm_data = self._prepare_llm_data(
|
|
123
169
|
prompt_blueprint=prompt_blueprint,
|
|
124
170
|
prompt_template=prompt_blueprint["prompt_template"],
|
|
125
171
|
prompt_blueprint_model=prompt_blueprint_model,
|
|
126
|
-
model_parameter_overrides=model_parameter_overrides,
|
|
127
172
|
stream=stream,
|
|
128
173
|
)
|
|
129
174
|
|
|
130
|
-
|
|
131
|
-
|
|
175
|
+
# Capture start time before making the LLM request
|
|
176
|
+
request_start_time = datetime.datetime.now(datetime.timezone.utc).timestamp()
|
|
177
|
+
|
|
178
|
+
# response is just whatever the LLM call returns
|
|
179
|
+
# streaming=False > Pydantic model instance
|
|
180
|
+
# streaming=True > generator that yields ChatCompletionChunk pieces as they arrive
|
|
181
|
+
response = llm_data["request_function"](
|
|
182
|
+
prompt_blueprint=llm_data["prompt_blueprint"],
|
|
183
|
+
client_kwargs=llm_data["client_kwargs"],
|
|
184
|
+
function_kwargs=llm_data["function_kwargs"],
|
|
132
185
|
)
|
|
133
186
|
|
|
187
|
+
# Capture end time after the LLM request completes
|
|
188
|
+
request_end_time = datetime.datetime.now(datetime.timezone.utc).timestamp()
|
|
189
|
+
|
|
134
190
|
if stream:
|
|
135
191
|
return stream_response(
|
|
136
|
-
response,
|
|
137
|
-
self._create_track_request_callable(
|
|
138
|
-
request_params=
|
|
192
|
+
generator=response,
|
|
193
|
+
after_stream=self._create_track_request_callable(
|
|
194
|
+
request_params=llm_data,
|
|
139
195
|
tags=tags,
|
|
140
196
|
input_variables=input_variables,
|
|
141
197
|
group_id=group_id,
|
|
142
198
|
pl_run_span_id=pl_run_span_id,
|
|
199
|
+
request_start_time=request_start_time,
|
|
143
200
|
),
|
|
144
|
-
|
|
201
|
+
map_results=llm_data["stream_function"],
|
|
202
|
+
metadata=llm_data["prompt_blueprint"]["metadata"],
|
|
145
203
|
)
|
|
146
204
|
|
|
205
|
+
if isinstance(response, dict):
|
|
206
|
+
request_response = response
|
|
207
|
+
else:
|
|
208
|
+
request_response = response.model_dump(mode="json")
|
|
209
|
+
|
|
147
210
|
request_log = self._track_request_log(
|
|
148
|
-
|
|
211
|
+
llm_data,
|
|
149
212
|
tags,
|
|
150
213
|
input_variables,
|
|
151
214
|
group_id,
|
|
152
215
|
pl_run_span_id,
|
|
153
216
|
metadata=metadata,
|
|
154
|
-
request_response=
|
|
217
|
+
request_response=request_response,
|
|
218
|
+
request_start_time=request_start_time,
|
|
219
|
+
request_end_time=request_end_time,
|
|
155
220
|
)
|
|
156
221
|
|
|
157
222
|
return {
|
|
@@ -180,7 +245,7 @@ class PromptLayer(PromptLayerMixin):
|
|
|
180
245
|
metadata=metadata,
|
|
181
246
|
**body,
|
|
182
247
|
)
|
|
183
|
-
return track_request(**track_request_kwargs)
|
|
248
|
+
return track_request(self.base_url, self.throw_on_error, **track_request_kwargs)
|
|
184
249
|
|
|
185
250
|
def run(
|
|
186
251
|
self,
|
|
@@ -193,17 +258,21 @@ class PromptLayer(PromptLayerMixin):
|
|
|
193
258
|
metadata: Union[Dict[str, str], None] = None,
|
|
194
259
|
group_id: Union[int, None] = None,
|
|
195
260
|
stream: bool = False,
|
|
261
|
+
provider: Union[str, None] = None,
|
|
262
|
+
model: Union[str, None] = None,
|
|
196
263
|
) -> Dict[str, Any]:
|
|
197
264
|
_run_internal_kwargs = {
|
|
198
265
|
"prompt_name": prompt_name,
|
|
199
266
|
"prompt_version": prompt_version,
|
|
200
267
|
"prompt_release_label": prompt_release_label,
|
|
201
|
-
"input_variables": input_variables,
|
|
268
|
+
"input_variables": input_variables or {},
|
|
202
269
|
"model_parameter_overrides": model_parameter_overrides,
|
|
203
270
|
"tags": tags,
|
|
204
271
|
"metadata": metadata,
|
|
205
272
|
"group_id": group_id,
|
|
206
273
|
"stream": stream,
|
|
274
|
+
"provider": provider,
|
|
275
|
+
"model": model,
|
|
207
276
|
}
|
|
208
277
|
|
|
209
278
|
if self.tracer:
|
|
@@ -211,9 +280,7 @@ class PromptLayer(PromptLayerMixin):
|
|
|
211
280
|
span.set_attribute("prompt_name", prompt_name)
|
|
212
281
|
span.set_attribute("function_input", str(_run_internal_kwargs))
|
|
213
282
|
pl_run_span_id = hex(span.context.span_id)[2:].zfill(16)
|
|
214
|
-
result = self._run_internal(
|
|
215
|
-
**_run_internal_kwargs, pl_run_span_id=pl_run_span_id
|
|
216
|
-
)
|
|
283
|
+
result = self._run_internal(**_run_internal_kwargs, pl_run_span_id=pl_run_span_id)
|
|
217
284
|
span.set_attribute("function_output", str(result))
|
|
218
285
|
return result
|
|
219
286
|
else:
|
|
@@ -221,51 +288,63 @@ class PromptLayer(PromptLayerMixin):
|
|
|
221
288
|
|
|
222
289
|
def run_workflow(
|
|
223
290
|
self,
|
|
224
|
-
|
|
291
|
+
workflow_id_or_name: Optional[Union[int, str]] = None,
|
|
225
292
|
input_variables: Optional[Dict[str, Any]] = None,
|
|
226
293
|
metadata: Optional[Dict[str, str]] = None,
|
|
227
294
|
workflow_label_name: Optional[str] = None,
|
|
228
|
-
workflow_version: Optional[
|
|
229
|
-
int
|
|
230
|
-
] = None, # This is the version number, not the version ID
|
|
295
|
+
workflow_version: Optional[int] = None,
|
|
231
296
|
return_all_outputs: Optional[bool] = False,
|
|
232
|
-
|
|
297
|
+
# `workflow_name` deprecated, kept for backward compatibility only.
|
|
298
|
+
# Allows `workflow_name` to be passed both as keyword and positional argument
|
|
299
|
+
# (virtually identical to `workflow_id_or_name`)
|
|
300
|
+
workflow_name: Optional[str] = None,
|
|
301
|
+
) -> Union[Dict[str, Any], Any]:
|
|
233
302
|
try:
|
|
234
303
|
try:
|
|
235
|
-
# Check if we're inside a running event loop
|
|
236
|
-
loop = asyncio.get_running_loop()
|
|
304
|
+
loop = asyncio.get_running_loop() # Check if we're inside a running event loop
|
|
237
305
|
except RuntimeError:
|
|
238
306
|
loop = None
|
|
239
307
|
|
|
240
308
|
if loop and loop.is_running():
|
|
241
309
|
nest_asyncio.apply()
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
310
|
+
|
|
311
|
+
results = asyncio.run(
|
|
312
|
+
arun_workflow_request(
|
|
313
|
+
api_key=self.api_key,
|
|
314
|
+
base_url=self.base_url,
|
|
315
|
+
throw_on_error=self.throw_on_error,
|
|
316
|
+
workflow_id_or_name=_get_workflow_workflow_id_or_name(workflow_id_or_name, workflow_name),
|
|
317
|
+
input_variables=input_variables or {},
|
|
318
|
+
metadata=metadata,
|
|
319
|
+
workflow_label_name=workflow_label_name,
|
|
320
|
+
workflow_version_number=workflow_version,
|
|
321
|
+
return_all_outputs=return_all_outputs,
|
|
253
322
|
)
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
workflow_label_name=workflow_label_name,
|
|
262
|
-
workflow_version_number=workflow_version,
|
|
263
|
-
api_key=self.api_key,
|
|
264
|
-
return_all_outputs=return_all_outputs,
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
if not return_all_outputs and is_workflow_results_dict(results):
|
|
326
|
+
output_nodes = [node_data for node_data in results.values() if node_data.get("is_output_node")]
|
|
327
|
+
if not output_nodes:
|
|
328
|
+
raise _exceptions.PromptLayerNotFoundError(
|
|
329
|
+
f"Output nodes not found: {json.dumps(results, indent=4)}", response=None, body=results
|
|
265
330
|
)
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
331
|
+
|
|
332
|
+
if not any(node.get("status") == "SUCCESS" for node in output_nodes):
|
|
333
|
+
raise _exceptions.PromptLayerAPIError(
|
|
334
|
+
f"None of the output nodes have succeeded: {json.dumps(results, indent=4)}",
|
|
335
|
+
response=None,
|
|
336
|
+
body=results,
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
return results
|
|
340
|
+
except Exception as ex:
|
|
341
|
+
logger.exception("Error running workflow")
|
|
342
|
+
if RERAISE_ORIGINAL_EXCEPTION:
|
|
343
|
+
raise
|
|
344
|
+
else:
|
|
345
|
+
raise _exceptions.PromptLayerAPIError(
|
|
346
|
+
f"Error running workflow: {str(ex)}", response=None, body=None
|
|
347
|
+
) from ex
|
|
269
348
|
|
|
270
349
|
def log_request(
|
|
271
350
|
self,
|
|
@@ -276,6 +355,8 @@ class PromptLayer(PromptLayerMixin):
|
|
|
276
355
|
output: PromptTemplate,
|
|
277
356
|
request_start_time: float,
|
|
278
357
|
request_end_time: float,
|
|
358
|
+
# TODO(dmu) MEDIUM: Avoid using mutable defaults
|
|
359
|
+
# TODO(dmu) MEDIUM: Deprecate and remove this wrapper function?
|
|
279
360
|
parameters: Dict[str, Any] = {},
|
|
280
361
|
tags: List[str] = [],
|
|
281
362
|
metadata: Dict[str, str] = {},
|
|
@@ -287,9 +368,13 @@ class PromptLayer(PromptLayerMixin):
|
|
|
287
368
|
price: float = 0.0,
|
|
288
369
|
function_name: str = "",
|
|
289
370
|
score: int = 0,
|
|
371
|
+
prompt_id: Union[int, None] = None,
|
|
372
|
+
score_name: Union[str, None] = None,
|
|
290
373
|
):
|
|
291
374
|
return util_log_request(
|
|
292
375
|
self.api_key,
|
|
376
|
+
self.base_url,
|
|
377
|
+
throw_on_error=self.throw_on_error,
|
|
293
378
|
provider=provider,
|
|
294
379
|
model=model,
|
|
295
380
|
input=input,
|
|
@@ -307,14 +392,18 @@ class PromptLayer(PromptLayerMixin):
|
|
|
307
392
|
price=price,
|
|
308
393
|
function_name=function_name,
|
|
309
394
|
score=score,
|
|
395
|
+
prompt_id=prompt_id,
|
|
396
|
+
score_name=score_name,
|
|
310
397
|
)
|
|
311
398
|
|
|
312
399
|
|
|
313
400
|
class AsyncPromptLayer(PromptLayerMixin):
|
|
314
401
|
def __init__(
|
|
315
402
|
self,
|
|
316
|
-
api_key: str = None,
|
|
403
|
+
api_key: Union[str, None] = None,
|
|
317
404
|
enable_tracing: bool = False,
|
|
405
|
+
base_url: Union[str, None] = None,
|
|
406
|
+
throw_on_error: bool = True,
|
|
318
407
|
):
|
|
319
408
|
if api_key is None:
|
|
320
409
|
api_key = os.environ.get("PROMPTLAYER_API_KEY")
|
|
@@ -325,34 +414,34 @@ class AsyncPromptLayer(PromptLayerMixin):
|
|
|
325
414
|
"Please set the PROMPTLAYER_API_KEY environment variable or pass the api_key parameter."
|
|
326
415
|
)
|
|
327
416
|
|
|
417
|
+
self.base_url = get_base_url(base_url)
|
|
328
418
|
self.api_key = api_key
|
|
329
|
-
self.
|
|
330
|
-
self.
|
|
419
|
+
self.throw_on_error = throw_on_error
|
|
420
|
+
self.templates = AsyncTemplateManager(api_key, self.base_url, self.throw_on_error)
|
|
421
|
+
self.group = AsyncGroupManager(api_key, self.base_url, self.throw_on_error)
|
|
331
422
|
self.tracer_provider, self.tracer = self._initialize_tracer(
|
|
332
|
-
api_key, enable_tracing
|
|
423
|
+
api_key, self.base_url, self.throw_on_error, enable_tracing
|
|
333
424
|
)
|
|
334
|
-
self.track = AsyncTrackManager(api_key)
|
|
425
|
+
self.track = AsyncTrackManager(api_key, self.base_url, self.throw_on_error)
|
|
335
426
|
|
|
336
|
-
def __getattr__(
|
|
337
|
-
self, name: Union[Literal["openai"], Literal["anthropic"], Literal["prompts"]]
|
|
338
|
-
):
|
|
427
|
+
def __getattr__(self, name: Union[Literal["openai"], Literal["anthropic"], Literal["prompts"]]):
|
|
339
428
|
if name == "openai":
|
|
340
429
|
import openai as openai_module
|
|
341
430
|
|
|
342
431
|
openai = PromptLayerBase(
|
|
343
|
-
openai_module,
|
|
344
|
-
function_name="openai",
|
|
345
|
-
api_key=self.api_key,
|
|
432
|
+
self.api_key, self.base_url, openai_module, function_name="openai", tracer=self.tracer
|
|
346
433
|
)
|
|
347
434
|
return openai
|
|
348
435
|
elif name == "anthropic":
|
|
349
436
|
import anthropic as anthropic_module
|
|
350
437
|
|
|
351
438
|
anthropic = PromptLayerBase(
|
|
439
|
+
self.api_key,
|
|
440
|
+
self.base_url,
|
|
352
441
|
anthropic_module,
|
|
353
442
|
function_name="anthropic",
|
|
354
443
|
provider_type="anthropic",
|
|
355
|
-
|
|
444
|
+
tracer=self.tracer,
|
|
356
445
|
)
|
|
357
446
|
return anthropic
|
|
358
447
|
else:
|
|
@@ -360,28 +449,37 @@ class AsyncPromptLayer(PromptLayerMixin):
|
|
|
360
449
|
|
|
361
450
|
async def run_workflow(
|
|
362
451
|
self,
|
|
363
|
-
|
|
452
|
+
workflow_id_or_name: Optional[Union[int, str]] = None,
|
|
364
453
|
input_variables: Optional[Dict[str, Any]] = None,
|
|
365
454
|
metadata: Optional[Dict[str, str]] = None,
|
|
366
455
|
workflow_label_name: Optional[str] = None,
|
|
367
|
-
workflow_version: Optional[
|
|
368
|
-
int
|
|
369
|
-
] = None, # This is the version number, not the version ID
|
|
456
|
+
workflow_version: Optional[int] = None, # This is the version number, not the version ID
|
|
370
457
|
return_all_outputs: Optional[bool] = False,
|
|
371
|
-
|
|
458
|
+
# `workflow_name` deprecated, kept for backward compatibility only.
|
|
459
|
+
# Allows `workflow_name` to be passed both as keyword and positional argument
|
|
460
|
+
# (virtually identical to `workflow_id_or_name`)
|
|
461
|
+
workflow_name: Optional[str] = None,
|
|
462
|
+
) -> Union[Dict[str, Any], Any]:
|
|
372
463
|
try:
|
|
373
|
-
|
|
374
|
-
|
|
464
|
+
return await arun_workflow_request(
|
|
465
|
+
api_key=self.api_key,
|
|
466
|
+
base_url=self.base_url,
|
|
467
|
+
throw_on_error=self.throw_on_error,
|
|
468
|
+
workflow_id_or_name=_get_workflow_workflow_id_or_name(workflow_id_or_name, workflow_name),
|
|
375
469
|
input_variables=input_variables or {},
|
|
376
470
|
metadata=metadata,
|
|
377
471
|
workflow_label_name=workflow_label_name,
|
|
378
472
|
workflow_version_number=workflow_version,
|
|
379
|
-
api_key=self.api_key,
|
|
380
473
|
return_all_outputs=return_all_outputs,
|
|
381
474
|
)
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
475
|
+
except Exception as ex:
|
|
476
|
+
logger.exception("Error running workflow")
|
|
477
|
+
if RERAISE_ORIGINAL_EXCEPTION:
|
|
478
|
+
raise
|
|
479
|
+
else:
|
|
480
|
+
raise _exceptions.PromptLayerAPIError(
|
|
481
|
+
f"Error running workflow: {str(ex)}", response=None, body=None
|
|
482
|
+
) from ex
|
|
385
483
|
|
|
386
484
|
async def run(
|
|
387
485
|
self,
|
|
@@ -394,6 +492,8 @@ class AsyncPromptLayer(PromptLayerMixin):
|
|
|
394
492
|
metadata: Union[Dict[str, str], None] = None,
|
|
395
493
|
group_id: Union[int, None] = None,
|
|
396
494
|
stream: bool = False,
|
|
495
|
+
provider: Union[str, None] = None,
|
|
496
|
+
model: Union[str, None] = None,
|
|
397
497
|
) -> Dict[str, Any]:
|
|
398
498
|
_run_internal_kwargs = {
|
|
399
499
|
"prompt_name": prompt_name,
|
|
@@ -405,6 +505,8 @@ class AsyncPromptLayer(PromptLayerMixin):
|
|
|
405
505
|
"metadata": metadata,
|
|
406
506
|
"group_id": group_id,
|
|
407
507
|
"stream": stream,
|
|
508
|
+
"provider": provider,
|
|
509
|
+
"model": model,
|
|
408
510
|
}
|
|
409
511
|
|
|
410
512
|
if self.tracer:
|
|
@@ -412,9 +514,7 @@ class AsyncPromptLayer(PromptLayerMixin):
|
|
|
412
514
|
span.set_attribute("prompt_name", prompt_name)
|
|
413
515
|
span.set_attribute("function_input", str(_run_internal_kwargs))
|
|
414
516
|
pl_run_span_id = hex(span.context.span_id)[2:].zfill(16)
|
|
415
|
-
result = await self._run_internal(
|
|
416
|
-
**_run_internal_kwargs, pl_run_span_id=pl_run_span_id
|
|
417
|
-
)
|
|
517
|
+
result = await self._run_internal(**_run_internal_kwargs, pl_run_span_id=pl_run_span_id)
|
|
418
518
|
span.set_attribute("function_output", str(result))
|
|
419
519
|
return result
|
|
420
520
|
else:
|
|
@@ -440,9 +540,12 @@ class AsyncPromptLayer(PromptLayerMixin):
|
|
|
440
540
|
price: float = 0.0,
|
|
441
541
|
function_name: str = "",
|
|
442
542
|
score: int = 0,
|
|
543
|
+
prompt_id: Union[int, None] = None,
|
|
443
544
|
):
|
|
444
545
|
return await autil_log_request(
|
|
445
546
|
self.api_key,
|
|
547
|
+
self.base_url,
|
|
548
|
+
throw_on_error=self.throw_on_error,
|
|
446
549
|
provider=provider,
|
|
447
550
|
model=model,
|
|
448
551
|
input=input,
|
|
@@ -460,6 +563,7 @@ class AsyncPromptLayer(PromptLayerMixin):
|
|
|
460
563
|
price=price,
|
|
461
564
|
function_name=function_name,
|
|
462
565
|
score=score,
|
|
566
|
+
prompt_id=prompt_id,
|
|
463
567
|
)
|
|
464
568
|
|
|
465
569
|
async def _create_track_request_callable(
|
|
@@ -470,6 +574,7 @@ class AsyncPromptLayer(PromptLayerMixin):
|
|
|
470
574
|
input_variables,
|
|
471
575
|
group_id,
|
|
472
576
|
pl_run_span_id: Union[str, None] = None,
|
|
577
|
+
request_start_time: Union[float, None] = None,
|
|
473
578
|
):
|
|
474
579
|
async def _track_request(**body):
|
|
475
580
|
track_request_kwargs = self._prepare_track_request_kwargs(
|
|
@@ -479,9 +584,10 @@ class AsyncPromptLayer(PromptLayerMixin):
|
|
|
479
584
|
input_variables,
|
|
480
585
|
group_id,
|
|
481
586
|
pl_run_span_id,
|
|
587
|
+
request_start_time=request_start_time,
|
|
482
588
|
**body,
|
|
483
589
|
)
|
|
484
|
-
return await atrack_request(**track_request_kwargs)
|
|
590
|
+
return await atrack_request(self.base_url, self.throw_on_error, **track_request_kwargs)
|
|
485
591
|
|
|
486
592
|
return _track_request
|
|
487
593
|
|
|
@@ -505,7 +611,7 @@ class AsyncPromptLayer(PromptLayerMixin):
|
|
|
505
611
|
metadata=metadata,
|
|
506
612
|
**body,
|
|
507
613
|
)
|
|
508
|
-
return await atrack_request(**track_request_kwargs)
|
|
614
|
+
return await atrack_request(self.base_url, self.throw_on_error, **track_request_kwargs)
|
|
509
615
|
|
|
510
616
|
async def _run_internal(
|
|
511
617
|
self,
|
|
@@ -520,54 +626,81 @@ class AsyncPromptLayer(PromptLayerMixin):
|
|
|
520
626
|
group_id: Union[int, None] = None,
|
|
521
627
|
stream: bool = False,
|
|
522
628
|
pl_run_span_id: Union[str, None] = None,
|
|
629
|
+
provider: Union[str, None] = None,
|
|
630
|
+
model: Union[str, None] = None,
|
|
523
631
|
) -> Dict[str, Any]:
|
|
632
|
+
import datetime
|
|
633
|
+
|
|
524
634
|
get_prompt_template_params = self._prepare_get_prompt_template_params(
|
|
525
635
|
prompt_version=prompt_version,
|
|
526
636
|
prompt_release_label=prompt_release_label,
|
|
527
637
|
input_variables=input_variables,
|
|
528
638
|
metadata=metadata,
|
|
639
|
+
provider=provider,
|
|
640
|
+
model=model,
|
|
641
|
+
model_parameter_overrides=model_parameter_overrides,
|
|
529
642
|
)
|
|
530
|
-
prompt_blueprint = await self.templates.get(
|
|
531
|
-
|
|
532
|
-
|
|
643
|
+
prompt_blueprint = await self.templates.get(prompt_name, get_prompt_template_params)
|
|
644
|
+
if not prompt_blueprint:
|
|
645
|
+
raise _exceptions.PromptLayerNotFoundError(
|
|
646
|
+
f"Prompt template '{prompt_name}' not found.",
|
|
647
|
+
response=None,
|
|
648
|
+
body=None,
|
|
649
|
+
)
|
|
533
650
|
prompt_blueprint_model = self._validate_and_extract_model_from_prompt_blueprint(
|
|
534
651
|
prompt_blueprint=prompt_blueprint, prompt_name=prompt_name
|
|
535
652
|
)
|
|
536
|
-
|
|
653
|
+
llm_data = self._prepare_llm_data(
|
|
537
654
|
prompt_blueprint=prompt_blueprint,
|
|
538
655
|
prompt_template=prompt_blueprint["prompt_template"],
|
|
539
656
|
prompt_blueprint_model=prompt_blueprint_model,
|
|
540
|
-
model_parameter_overrides=model_parameter_overrides,
|
|
541
657
|
stream=stream,
|
|
542
658
|
is_async=True,
|
|
543
659
|
)
|
|
544
660
|
|
|
545
|
-
|
|
546
|
-
|
|
661
|
+
# Capture start time before making the LLM request
|
|
662
|
+
request_start_time = datetime.datetime.now(datetime.timezone.utc).timestamp()
|
|
663
|
+
|
|
664
|
+
response = await llm_data["request_function"](
|
|
665
|
+
prompt_blueprint=llm_data["prompt_blueprint"],
|
|
666
|
+
client_kwargs=llm_data["client_kwargs"],
|
|
667
|
+
function_kwargs=llm_data["function_kwargs"],
|
|
547
668
|
)
|
|
548
669
|
|
|
670
|
+
# Capture end time after the LLM request completes
|
|
671
|
+
request_end_time = datetime.datetime.now(datetime.timezone.utc).timestamp()
|
|
672
|
+
|
|
673
|
+
if hasattr(response, "model_dump"):
|
|
674
|
+
request_response = response.model_dump(mode="json")
|
|
675
|
+
else:
|
|
676
|
+
request_response = response
|
|
677
|
+
|
|
549
678
|
if stream:
|
|
550
679
|
track_request_callable = await self._create_track_request_callable(
|
|
551
|
-
request_params=
|
|
680
|
+
request_params=llm_data,
|
|
552
681
|
tags=tags,
|
|
553
682
|
input_variables=input_variables,
|
|
554
683
|
group_id=group_id,
|
|
555
684
|
pl_run_span_id=pl_run_span_id,
|
|
685
|
+
request_start_time=request_start_time,
|
|
556
686
|
)
|
|
557
687
|
return astream_response(
|
|
558
|
-
|
|
688
|
+
request_response,
|
|
559
689
|
track_request_callable,
|
|
560
|
-
|
|
690
|
+
llm_data["stream_function"],
|
|
691
|
+
llm_data["prompt_blueprint"]["metadata"],
|
|
561
692
|
)
|
|
562
693
|
|
|
563
694
|
request_log = await self._track_request_log(
|
|
564
|
-
|
|
695
|
+
llm_data,
|
|
565
696
|
tags,
|
|
566
697
|
input_variables,
|
|
567
698
|
group_id,
|
|
568
699
|
pl_run_span_id,
|
|
569
700
|
metadata=metadata,
|
|
570
|
-
request_response=
|
|
701
|
+
request_response=request_response,
|
|
702
|
+
request_start_time=request_start_time,
|
|
703
|
+
request_end_time=request_end_time,
|
|
571
704
|
)
|
|
572
705
|
|
|
573
706
|
return {
|