prompty 0.1.9__py3-none-any.whl → 0.1.33__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.
- prompty/__init__.py +312 -117
- prompty/azure/__init__.py +10 -0
- prompty/azure/executor.py +218 -0
- prompty/azure/processor.py +142 -0
- prompty/cli.py +74 -28
- prompty/core.py +138 -221
- prompty/invoker.py +297 -0
- prompty/openai/__init__.py +10 -0
- prompty/openai/executor.py +114 -0
- prompty/{processors.py → openai/processor.py} +25 -15
- prompty/parsers.py +18 -1
- prompty/renderers.py +19 -2
- prompty/serverless/__init__.py +8 -0
- prompty/serverless/executor.py +153 -0
- prompty/serverless/processor.py +78 -0
- prompty/tracer.py +162 -22
- prompty/utils.py +105 -0
- prompty-0.1.33.dist-info/METADATA +218 -0
- prompty-0.1.33.dist-info/RECORD +22 -0
- {prompty-0.1.9.dist-info → prompty-0.1.33.dist-info}/WHEEL +1 -1
- prompty-0.1.33.dist-info/entry_points.txt +5 -0
- prompty/executors.py +0 -94
- prompty-0.1.9.dist-info/METADATA +0 -136
- prompty-0.1.9.dist-info/RECORD +0 -12
- {prompty-0.1.9.dist-info → prompty-0.1.33.dist-info}/licenses/LICENSE +0 -0
prompty/core.py
CHANGED
@@ -1,14 +1,19 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import os
|
4
|
-
import re
|
5
|
-
import yaml
|
6
|
-
import json
|
7
|
-
import abc
|
8
4
|
from pathlib import Path
|
9
|
-
|
5
|
+
|
6
|
+
from .tracer import Tracer, to_dict
|
10
7
|
from pydantic import BaseModel, Field, FilePath
|
11
|
-
from typing import Iterator, List, Literal, Dict, Callable, Set
|
8
|
+
from typing import AsyncIterator, Iterator, List, Literal, Dict, Callable, Set, Tuple
|
9
|
+
|
10
|
+
from .utils import load_json, load_json_async
|
11
|
+
|
12
|
+
|
13
|
+
class ToolCall(BaseModel):
|
14
|
+
id: str
|
15
|
+
name: str
|
16
|
+
arguments: str
|
12
17
|
|
13
18
|
|
14
19
|
class PropertySettings(BaseModel):
|
@@ -188,33 +193,74 @@ class Prompty(BaseModel):
|
|
188
193
|
d[k] = v
|
189
194
|
return d
|
190
195
|
|
196
|
+
@staticmethod
|
197
|
+
def hoist_base_prompty(top: Prompty, base: Prompty) -> Prompty:
|
198
|
+
top.name = base.name if top.name == "" else top.name
|
199
|
+
top.description = base.description if top.description == "" else top.description
|
200
|
+
top.authors = list(set(base.authors + top.authors))
|
201
|
+
top.tags = list(set(base.tags + top.tags))
|
202
|
+
top.version = base.version if top.version == "" else top.version
|
203
|
+
|
204
|
+
top.model.api = base.model.api if top.model.api == "" else top.model.api
|
205
|
+
top.model.configuration = param_hoisting(
|
206
|
+
top.model.configuration, base.model.configuration
|
207
|
+
)
|
208
|
+
top.model.parameters = param_hoisting(
|
209
|
+
top.model.parameters, base.model.parameters
|
210
|
+
)
|
211
|
+
top.model.response = param_hoisting(top.model.response, base.model.response)
|
212
|
+
|
213
|
+
top.sample = param_hoisting(top.sample, base.sample)
|
214
|
+
|
215
|
+
top.basePrompty = base
|
216
|
+
|
217
|
+
return top
|
218
|
+
|
191
219
|
@staticmethod
|
192
220
|
def _process_file(file: str, parent: Path) -> any:
|
193
221
|
file = Path(parent / Path(file)).resolve().absolute()
|
194
222
|
if file.exists():
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
223
|
+
items = load_json(file)
|
224
|
+
if isinstance(items, list):
|
225
|
+
return [Prompty.normalize(value, parent) for value in items]
|
226
|
+
elif isinstance(items, dict):
|
227
|
+
return {
|
228
|
+
key: Prompty.normalize(value, parent)
|
229
|
+
for key, value in items.items()
|
230
|
+
}
|
231
|
+
else:
|
232
|
+
return items
|
233
|
+
else:
|
234
|
+
raise FileNotFoundError(f"File {file} not found")
|
235
|
+
|
236
|
+
@staticmethod
|
237
|
+
async def _process_file_async(file: str, parent: Path) -> any:
|
238
|
+
file = Path(parent / Path(file)).resolve().absolute()
|
239
|
+
if file.exists():
|
240
|
+
items = await load_json_async(file)
|
241
|
+
if isinstance(items, list):
|
242
|
+
return [Prompty.normalize(value, parent) for value in items]
|
243
|
+
elif isinstance(items, dict):
|
244
|
+
return {
|
245
|
+
key: Prompty.normalize(value, parent)
|
246
|
+
for key, value in items.items()
|
247
|
+
}
|
248
|
+
else:
|
249
|
+
return items
|
206
250
|
else:
|
207
251
|
raise FileNotFoundError(f"File {file} not found")
|
208
252
|
|
209
253
|
@staticmethod
|
210
|
-
def _process_env(variable: str, env_error=True) -> any:
|
254
|
+
def _process_env(variable: str, env_error=True, default: str = None) -> any:
|
211
255
|
if variable in os.environ.keys():
|
212
256
|
return os.environ[variable]
|
213
257
|
else:
|
258
|
+
if default:
|
259
|
+
return default
|
214
260
|
if env_error:
|
215
261
|
raise ValueError(f"Variable {variable} not found in environment")
|
216
|
-
|
217
|
-
|
262
|
+
|
263
|
+
return ""
|
218
264
|
|
219
265
|
@staticmethod
|
220
266
|
def normalize(attribute: any, parent: Path, env_error=True) -> any:
|
@@ -224,30 +270,15 @@ class Prompty(BaseModel):
|
|
224
270
|
# check if env or file
|
225
271
|
variable = attribute[2:-1].split(":")
|
226
272
|
if variable[0] == "env" and len(variable) > 1:
|
227
|
-
return Prompty._process_env(
|
273
|
+
return Prompty._process_env(
|
274
|
+
variable[1],
|
275
|
+
env_error,
|
276
|
+
variable[2] if len(variable) > 2 else None,
|
277
|
+
)
|
228
278
|
elif variable[0] == "file" and len(variable) > 1:
|
229
279
|
return Prompty._process_file(variable[1], parent)
|
230
280
|
else:
|
231
|
-
|
232
|
-
v = Prompty._process_env(variable[0], False)
|
233
|
-
if len(v) == 0:
|
234
|
-
if len(variable) > 1:
|
235
|
-
return variable[1]
|
236
|
-
else:
|
237
|
-
if env_error:
|
238
|
-
raise ValueError(
|
239
|
-
f"Variable {variable[0]} not found in environment"
|
240
|
-
)
|
241
|
-
else:
|
242
|
-
return v
|
243
|
-
else:
|
244
|
-
return v
|
245
|
-
elif (
|
246
|
-
attribute.startswith("file:")
|
247
|
-
and Path(parent / attribute.split(":")[1]).exists()
|
248
|
-
):
|
249
|
-
# old way of doing things for back compatibility
|
250
|
-
return Prompty._process_file(attribute.split(":")[1], parent)
|
281
|
+
raise ValueError(f"Invalid attribute format ({attribute})")
|
251
282
|
else:
|
252
283
|
return attribute
|
253
284
|
elif isinstance(attribute, list):
|
@@ -260,6 +291,35 @@ class Prompty(BaseModel):
|
|
260
291
|
else:
|
261
292
|
return attribute
|
262
293
|
|
294
|
+
@staticmethod
|
295
|
+
async def normalize_async(attribute: any, parent: Path, env_error=True) -> any:
|
296
|
+
if isinstance(attribute, str):
|
297
|
+
attribute = attribute.strip()
|
298
|
+
if attribute.startswith("${") and attribute.endswith("}"):
|
299
|
+
# check if env or file
|
300
|
+
variable = attribute[2:-1].split(":")
|
301
|
+
if variable[0] == "env" and len(variable) > 1:
|
302
|
+
return Prompty._process_env(
|
303
|
+
variable[1],
|
304
|
+
env_error,
|
305
|
+
variable[2] if len(variable) > 2 else None,
|
306
|
+
)
|
307
|
+
elif variable[0] == "file" and len(variable) > 1:
|
308
|
+
return await Prompty._process_file_async(variable[1], parent)
|
309
|
+
else:
|
310
|
+
raise ValueError(f"Invalid attribute format ({attribute})")
|
311
|
+
else:
|
312
|
+
return attribute
|
313
|
+
elif isinstance(attribute, list):
|
314
|
+
return [await Prompty.normalize_async(value, parent) for value in attribute]
|
315
|
+
elif isinstance(attribute, dict):
|
316
|
+
return {
|
317
|
+
key: await Prompty.normalize_async(value, parent)
|
318
|
+
for key, value in attribute.items()
|
319
|
+
}
|
320
|
+
else:
|
321
|
+
return attribute
|
322
|
+
|
263
323
|
|
264
324
|
def param_hoisting(
|
265
325
|
top: Dict[str, any], bottom: Dict[str, any], top_key: str = None
|
@@ -274,183 +334,6 @@ def param_hoisting(
|
|
274
334
|
return new_dict
|
275
335
|
|
276
336
|
|
277
|
-
class Invoker(abc.ABC):
|
278
|
-
"""Abstract class for Invoker
|
279
|
-
|
280
|
-
Attributes
|
281
|
-
----------
|
282
|
-
prompty : Prompty
|
283
|
-
The prompty object
|
284
|
-
name : str
|
285
|
-
The name of the invoker
|
286
|
-
|
287
|
-
"""
|
288
|
-
|
289
|
-
def __init__(self, prompty: Prompty) -> None:
|
290
|
-
self.prompty = prompty
|
291
|
-
self.name = self.__class__.__name__
|
292
|
-
|
293
|
-
@abc.abstractmethod
|
294
|
-
def invoke(self, data: any) -> any:
|
295
|
-
"""Abstract method to invoke the invoker
|
296
|
-
|
297
|
-
Parameters
|
298
|
-
----------
|
299
|
-
data : any
|
300
|
-
The data to be invoked
|
301
|
-
|
302
|
-
Returns
|
303
|
-
-------
|
304
|
-
any
|
305
|
-
The invoked
|
306
|
-
"""
|
307
|
-
pass
|
308
|
-
|
309
|
-
@trace
|
310
|
-
def __call__(self, data: any) -> any:
|
311
|
-
"""Method to call the invoker
|
312
|
-
|
313
|
-
Parameters
|
314
|
-
----------
|
315
|
-
data : any
|
316
|
-
The data to be invoked
|
317
|
-
|
318
|
-
Returns
|
319
|
-
-------
|
320
|
-
any
|
321
|
-
The invoked
|
322
|
-
"""
|
323
|
-
return self.invoke(data)
|
324
|
-
|
325
|
-
|
326
|
-
class InvokerFactory:
|
327
|
-
"""Factory class for Invoker"""
|
328
|
-
|
329
|
-
_renderers: Dict[str, Invoker] = {}
|
330
|
-
_parsers: Dict[str, Invoker] = {}
|
331
|
-
_executors: Dict[str, Invoker] = {}
|
332
|
-
_processors: Dict[str, Invoker] = {}
|
333
|
-
|
334
|
-
@classmethod
|
335
|
-
def register_renderer(cls, name: str) -> Callable:
|
336
|
-
def inner_wrapper(wrapped_class: Invoker) -> Callable:
|
337
|
-
cls._renderers[name] = wrapped_class
|
338
|
-
return wrapped_class
|
339
|
-
|
340
|
-
return inner_wrapper
|
341
|
-
|
342
|
-
@classmethod
|
343
|
-
def register_parser(cls, name: str) -> Callable:
|
344
|
-
def inner_wrapper(wrapped_class: Invoker) -> Callable:
|
345
|
-
cls._parsers[name] = wrapped_class
|
346
|
-
return wrapped_class
|
347
|
-
|
348
|
-
return inner_wrapper
|
349
|
-
|
350
|
-
@classmethod
|
351
|
-
def register_executor(cls, name: str) -> Callable:
|
352
|
-
def inner_wrapper(wrapped_class: Invoker) -> Callable:
|
353
|
-
cls._executors[name] = wrapped_class
|
354
|
-
return wrapped_class
|
355
|
-
|
356
|
-
return inner_wrapper
|
357
|
-
|
358
|
-
@classmethod
|
359
|
-
def register_processor(cls, name: str) -> Callable:
|
360
|
-
def inner_wrapper(wrapped_class: Invoker) -> Callable:
|
361
|
-
cls._processors[name] = wrapped_class
|
362
|
-
return wrapped_class
|
363
|
-
|
364
|
-
return inner_wrapper
|
365
|
-
|
366
|
-
@classmethod
|
367
|
-
def create_renderer(cls, name: str, prompty: Prompty) -> Invoker:
|
368
|
-
if name not in cls._renderers:
|
369
|
-
raise ValueError(f"Renderer {name} not found")
|
370
|
-
return cls._renderers[name](prompty)
|
371
|
-
|
372
|
-
@classmethod
|
373
|
-
def create_parser(cls, name: str, prompty: Prompty) -> Invoker:
|
374
|
-
if name not in cls._parsers:
|
375
|
-
raise ValueError(f"Parser {name} not found")
|
376
|
-
return cls._parsers[name](prompty)
|
377
|
-
|
378
|
-
@classmethod
|
379
|
-
def create_executor(cls, name: str, prompty: Prompty) -> Invoker:
|
380
|
-
if name not in cls._executors:
|
381
|
-
raise ValueError(f"Executor {name} not found")
|
382
|
-
return cls._executors[name](prompty)
|
383
|
-
|
384
|
-
@classmethod
|
385
|
-
def create_processor(cls, name: str, prompty: Prompty) -> Invoker:
|
386
|
-
if name not in cls._processors:
|
387
|
-
raise ValueError(f"Processor {name} not found")
|
388
|
-
return cls._processors[name](prompty)
|
389
|
-
|
390
|
-
|
391
|
-
@InvokerFactory.register_renderer("NOOP")
|
392
|
-
@InvokerFactory.register_parser("NOOP")
|
393
|
-
@InvokerFactory.register_executor("NOOP")
|
394
|
-
@InvokerFactory.register_processor("NOOP")
|
395
|
-
@InvokerFactory.register_parser("prompty.embedding")
|
396
|
-
@InvokerFactory.register_parser("prompty.image")
|
397
|
-
@InvokerFactory.register_parser("prompty.completion")
|
398
|
-
class NoOp(Invoker):
|
399
|
-
def invoke(self, data: any) -> any:
|
400
|
-
return data
|
401
|
-
|
402
|
-
|
403
|
-
class Frontmatter:
|
404
|
-
"""Frontmatter class to extract frontmatter from string."""
|
405
|
-
|
406
|
-
_yaml_delim = r"(?:---|\+\+\+)"
|
407
|
-
_yaml = r"(.*?)"
|
408
|
-
_content = r"\s*(.+)$"
|
409
|
-
_re_pattern = r"^\s*" + _yaml_delim + _yaml + _yaml_delim + _content
|
410
|
-
_regex = re.compile(_re_pattern, re.S | re.M)
|
411
|
-
|
412
|
-
@classmethod
|
413
|
-
def read_file(cls, path):
|
414
|
-
"""Returns dict with separated frontmatter from file.
|
415
|
-
|
416
|
-
Parameters
|
417
|
-
----------
|
418
|
-
path : str
|
419
|
-
The path to the file
|
420
|
-
"""
|
421
|
-
with open(path, encoding="utf-8") as file:
|
422
|
-
file_contents = file.read()
|
423
|
-
return cls.read(file_contents)
|
424
|
-
|
425
|
-
@classmethod
|
426
|
-
def read(cls, string):
|
427
|
-
"""Returns dict with separated frontmatter from string.
|
428
|
-
|
429
|
-
Parameters
|
430
|
-
----------
|
431
|
-
string : str
|
432
|
-
The string to extract frontmatter from
|
433
|
-
|
434
|
-
|
435
|
-
Returns
|
436
|
-
-------
|
437
|
-
dict
|
438
|
-
The separated frontmatter
|
439
|
-
"""
|
440
|
-
fmatter = ""
|
441
|
-
body = ""
|
442
|
-
result = cls._regex.search(string)
|
443
|
-
|
444
|
-
if result:
|
445
|
-
fmatter = result.group(1)
|
446
|
-
body = result.group(2)
|
447
|
-
return {
|
448
|
-
"attributes": yaml.load(fmatter, Loader=yaml.FullLoader),
|
449
|
-
"body": body,
|
450
|
-
"frontmatter": fmatter,
|
451
|
-
}
|
452
|
-
|
453
|
-
|
454
337
|
class PromptyStream(Iterator):
|
455
338
|
"""PromptyStream class to iterate over LLM stream.
|
456
339
|
Necessary for Prompty to handle streaming data when tracing."""
|
@@ -474,8 +357,42 @@ class PromptyStream(Iterator):
|
|
474
357
|
except StopIteration:
|
475
358
|
# StopIteration is raised
|
476
359
|
# contents are exhausted
|
477
|
-
if len(self.items) > 0:
|
478
|
-
with Tracer.start(
|
479
|
-
trace("
|
360
|
+
if len(self.items) > 0:
|
361
|
+
with Tracer.start("PromptyStream") as trace:
|
362
|
+
trace("signature", f"{self.name}.PromptyStream")
|
363
|
+
trace("inputs", "None")
|
364
|
+
trace("result", [to_dict(s) for s in self.items])
|
480
365
|
|
481
366
|
raise StopIteration
|
367
|
+
|
368
|
+
|
369
|
+
class AsyncPromptyStream(AsyncIterator):
|
370
|
+
"""AsyncPromptyStream class to iterate over LLM stream.
|
371
|
+
Necessary for Prompty to handle streaming data when tracing."""
|
372
|
+
|
373
|
+
def __init__(self, name: str, iterator: AsyncIterator):
|
374
|
+
self.name = name
|
375
|
+
self.iterator = iterator
|
376
|
+
self.items: List[any] = []
|
377
|
+
self.__name__ = "AsyncPromptyStream"
|
378
|
+
|
379
|
+
def __aiter__(self):
|
380
|
+
return self
|
381
|
+
|
382
|
+
async def __anext__(self):
|
383
|
+
try:
|
384
|
+
# enumerate but add to list
|
385
|
+
o = await self.iterator.__anext__()
|
386
|
+
self.items.append(o)
|
387
|
+
return o
|
388
|
+
|
389
|
+
except StopAsyncIteration:
|
390
|
+
# StopIteration is raised
|
391
|
+
# contents are exhausted
|
392
|
+
if len(self.items) > 0:
|
393
|
+
with Tracer.start("AsyncPromptyStream") as trace:
|
394
|
+
trace("signature", f"{self.name}.AsyncPromptyStream")
|
395
|
+
trace("inputs", "None")
|
396
|
+
trace("result", [to_dict(s) for s in self.items])
|
397
|
+
|
398
|
+
raise StopAsyncIteration
|
prompty/invoker.py
ADDED
@@ -0,0 +1,297 @@
|
|
1
|
+
import abc
|
2
|
+
from .tracer import trace
|
3
|
+
from .core import Prompty
|
4
|
+
from typing import Callable, Dict, Literal
|
5
|
+
|
6
|
+
|
7
|
+
class Invoker(abc.ABC):
|
8
|
+
"""Abstract class for Invoker
|
9
|
+
|
10
|
+
Attributes
|
11
|
+
----------
|
12
|
+
prompty : Prompty
|
13
|
+
The prompty object
|
14
|
+
name : str
|
15
|
+
The name of the invoker
|
16
|
+
|
17
|
+
"""
|
18
|
+
|
19
|
+
def __init__(self, prompty: Prompty) -> None:
|
20
|
+
self.prompty = prompty
|
21
|
+
self.name = self.__class__.__name__
|
22
|
+
|
23
|
+
@abc.abstractmethod
|
24
|
+
def invoke(self, data: any) -> any:
|
25
|
+
"""Abstract method to invoke the invoker
|
26
|
+
|
27
|
+
Parameters
|
28
|
+
----------
|
29
|
+
data : any
|
30
|
+
The data to be invoked
|
31
|
+
|
32
|
+
Returns
|
33
|
+
-------
|
34
|
+
any
|
35
|
+
The invoked
|
36
|
+
"""
|
37
|
+
pass
|
38
|
+
|
39
|
+
@abc.abstractmethod
|
40
|
+
async def invoke_async(self, data: any) -> any:
|
41
|
+
"""Abstract method to invoke the invoker asynchronously
|
42
|
+
|
43
|
+
Parameters
|
44
|
+
----------
|
45
|
+
data : any
|
46
|
+
The data to be invoked
|
47
|
+
|
48
|
+
Returns
|
49
|
+
-------
|
50
|
+
any
|
51
|
+
The invoked
|
52
|
+
"""
|
53
|
+
pass
|
54
|
+
|
55
|
+
@trace
|
56
|
+
def run(self, data: any) -> any:
|
57
|
+
"""Method to run the invoker
|
58
|
+
|
59
|
+
Parameters
|
60
|
+
----------
|
61
|
+
data : any
|
62
|
+
The data to be invoked
|
63
|
+
|
64
|
+
Returns
|
65
|
+
-------
|
66
|
+
any
|
67
|
+
The invoked
|
68
|
+
"""
|
69
|
+
return self.invoke(data)
|
70
|
+
|
71
|
+
@trace
|
72
|
+
async def run_async(self, data: any) -> any:
|
73
|
+
"""Method to run the invoker asynchronously
|
74
|
+
|
75
|
+
Parameters
|
76
|
+
----------
|
77
|
+
data : any
|
78
|
+
The data to be invoked
|
79
|
+
|
80
|
+
Returns
|
81
|
+
-------
|
82
|
+
any
|
83
|
+
The invoked
|
84
|
+
"""
|
85
|
+
return await self.invoke_async(data)
|
86
|
+
|
87
|
+
|
88
|
+
class InvokerFactory:
|
89
|
+
"""Factory class for Invoker"""
|
90
|
+
|
91
|
+
_renderers: Dict[str, Invoker] = {}
|
92
|
+
_parsers: Dict[str, Invoker] = {}
|
93
|
+
_executors: Dict[str, Invoker] = {}
|
94
|
+
_processors: Dict[str, Invoker] = {}
|
95
|
+
|
96
|
+
@classmethod
|
97
|
+
def add_renderer(cls, name: str, invoker: Invoker) -> None:
|
98
|
+
cls._renderers[name] = invoker
|
99
|
+
|
100
|
+
@classmethod
|
101
|
+
def add_parser(cls, name: str, invoker: Invoker) -> None:
|
102
|
+
cls._parsers[name] = invoker
|
103
|
+
|
104
|
+
@classmethod
|
105
|
+
def add_executor(cls, name: str, invoker: Invoker) -> None:
|
106
|
+
cls._executors[name] = invoker
|
107
|
+
|
108
|
+
@classmethod
|
109
|
+
def add_processor(cls, name: str, invoker: Invoker) -> None:
|
110
|
+
cls._processors[name] = invoker
|
111
|
+
|
112
|
+
@classmethod
|
113
|
+
def register_renderer(cls, name: str) -> Callable:
|
114
|
+
def inner_wrapper(wrapped_class: Invoker) -> Callable:
|
115
|
+
cls._renderers[name] = wrapped_class
|
116
|
+
return wrapped_class
|
117
|
+
|
118
|
+
return inner_wrapper
|
119
|
+
|
120
|
+
@classmethod
|
121
|
+
def register_parser(cls, name: str) -> Callable:
|
122
|
+
def inner_wrapper(wrapped_class: Invoker) -> Callable:
|
123
|
+
cls._parsers[name] = wrapped_class
|
124
|
+
return wrapped_class
|
125
|
+
|
126
|
+
return inner_wrapper
|
127
|
+
|
128
|
+
@classmethod
|
129
|
+
def register_executor(cls, name: str) -> Callable:
|
130
|
+
def inner_wrapper(wrapped_class: Invoker) -> Callable:
|
131
|
+
cls._executors[name] = wrapped_class
|
132
|
+
return wrapped_class
|
133
|
+
|
134
|
+
return inner_wrapper
|
135
|
+
|
136
|
+
@classmethod
|
137
|
+
def register_processor(cls, name: str) -> Callable:
|
138
|
+
def inner_wrapper(wrapped_class: Invoker) -> Callable:
|
139
|
+
cls._processors[name] = wrapped_class
|
140
|
+
return wrapped_class
|
141
|
+
|
142
|
+
return inner_wrapper
|
143
|
+
|
144
|
+
@classmethod
|
145
|
+
def _get_name(
|
146
|
+
cls,
|
147
|
+
type: Literal["renderer", "parser", "executor", "processor"],
|
148
|
+
prompty: Prompty,
|
149
|
+
) -> str:
|
150
|
+
if type == "renderer":
|
151
|
+
return prompty.template.type
|
152
|
+
elif type == "parser":
|
153
|
+
return f"{prompty.template.parser}.{prompty.model.api}"
|
154
|
+
elif type == "executor":
|
155
|
+
return prompty.model.configuration["type"]
|
156
|
+
elif type == "processor":
|
157
|
+
return prompty.model.configuration["type"]
|
158
|
+
else:
|
159
|
+
raise ValueError(f"Type {type} not found")
|
160
|
+
|
161
|
+
@classmethod
|
162
|
+
def _get_invoker(
|
163
|
+
cls,
|
164
|
+
type: Literal["renderer", "parser", "executor", "processor"],
|
165
|
+
prompty: Prompty,
|
166
|
+
) -> Invoker:
|
167
|
+
if type == "renderer":
|
168
|
+
name = prompty.template.type
|
169
|
+
if name not in cls._renderers:
|
170
|
+
raise ValueError(f"Renderer {name} not found")
|
171
|
+
|
172
|
+
return cls._renderers[name](prompty)
|
173
|
+
|
174
|
+
elif type == "parser":
|
175
|
+
name = f"{prompty.template.parser}.{prompty.model.api}"
|
176
|
+
if name not in cls._parsers:
|
177
|
+
raise ValueError(f"Parser {name} not found")
|
178
|
+
|
179
|
+
return cls._parsers[name](prompty)
|
180
|
+
|
181
|
+
elif type == "executor":
|
182
|
+
name = prompty.model.configuration["type"]
|
183
|
+
if name not in cls._executors:
|
184
|
+
raise ValueError(f"Executor {name} not found")
|
185
|
+
|
186
|
+
return cls._executors[name](prompty)
|
187
|
+
|
188
|
+
elif type == "processor":
|
189
|
+
name = prompty.model.configuration["type"]
|
190
|
+
if name not in cls._processors:
|
191
|
+
raise ValueError(f"Processor {name} not found")
|
192
|
+
|
193
|
+
return cls._processors[name](prompty)
|
194
|
+
|
195
|
+
else:
|
196
|
+
raise ValueError(f"Type {type} not found")
|
197
|
+
|
198
|
+
@classmethod
|
199
|
+
def run(
|
200
|
+
cls,
|
201
|
+
type: Literal["renderer", "parser", "executor", "processor"],
|
202
|
+
prompty: Prompty,
|
203
|
+
data: any,
|
204
|
+
default: any = None,
|
205
|
+
):
|
206
|
+
name = cls._get_name(type, prompty)
|
207
|
+
if name.startswith("NOOP") and default != None:
|
208
|
+
return default
|
209
|
+
elif name.startswith("NOOP"):
|
210
|
+
return data
|
211
|
+
|
212
|
+
invoker = cls._get_invoker(type, prompty)
|
213
|
+
value = invoker.run(data)
|
214
|
+
return value
|
215
|
+
|
216
|
+
@classmethod
|
217
|
+
async def run_async(
|
218
|
+
cls,
|
219
|
+
type: Literal["renderer", "parser", "executor", "processor"],
|
220
|
+
prompty: Prompty,
|
221
|
+
data: any,
|
222
|
+
default: any = None,
|
223
|
+
):
|
224
|
+
name = cls._get_name(type, prompty)
|
225
|
+
if name.startswith("NOOP") and default != None:
|
226
|
+
return default
|
227
|
+
elif name.startswith("NOOP"):
|
228
|
+
return data
|
229
|
+
invoker = cls._get_invoker(type, prompty)
|
230
|
+
value = await invoker.run_async(data)
|
231
|
+
return value
|
232
|
+
|
233
|
+
@classmethod
|
234
|
+
def run_renderer(cls, prompty: Prompty, data: any, default: any = None) -> any:
|
235
|
+
return cls.run("renderer", prompty, data, default)
|
236
|
+
|
237
|
+
@classmethod
|
238
|
+
async def run_renderer_async(
|
239
|
+
cls, prompty: Prompty, data: any, default: any = None
|
240
|
+
) -> any:
|
241
|
+
return await cls.run_async("renderer", prompty, data, default)
|
242
|
+
|
243
|
+
@classmethod
|
244
|
+
def run_parser(cls, prompty: Prompty, data: any, default: any = None) -> any:
|
245
|
+
return cls.run("parser", prompty, data, default)
|
246
|
+
|
247
|
+
@classmethod
|
248
|
+
async def run_parser_async(
|
249
|
+
cls, prompty: Prompty, data: any, default: any = None
|
250
|
+
) -> any:
|
251
|
+
return await cls.run_async("parser", prompty, data, default)
|
252
|
+
|
253
|
+
@classmethod
|
254
|
+
def run_executor(cls, prompty: Prompty, data: any, default: any = None) -> any:
|
255
|
+
return cls.run("executor", prompty, data, default)
|
256
|
+
|
257
|
+
@classmethod
|
258
|
+
async def run_executor_async(
|
259
|
+
cls, prompty: Prompty, data: any, default: any = None
|
260
|
+
) -> any:
|
261
|
+
return await cls.run_async("executor", prompty, data, default)
|
262
|
+
|
263
|
+
@classmethod
|
264
|
+
def run_processor(cls, prompty: Prompty, data: any, default: any = None) -> any:
|
265
|
+
return cls.run("processor", prompty, data, default)
|
266
|
+
|
267
|
+
@classmethod
|
268
|
+
async def run_processor_async(
|
269
|
+
cls, prompty: Prompty, data: any, default: any = None
|
270
|
+
) -> any:
|
271
|
+
return await cls.run_async("processor", prompty, data, default)
|
272
|
+
|
273
|
+
|
274
|
+
class InvokerException(Exception):
|
275
|
+
"""Exception class for Invoker"""
|
276
|
+
|
277
|
+
def __init__(self, message: str, type: str) -> None:
|
278
|
+
super().__init__(message)
|
279
|
+
self.type = type
|
280
|
+
|
281
|
+
def __str__(self) -> str:
|
282
|
+
return f"{super().__str__()}. Make sure to pip install any necessary package extras (i.e. could be something like `pip install prompty[{self.type}]`) for {self.type} as well as import the appropriate invokers (i.e. could be something like `import prompty.{self.type}`)."
|
283
|
+
|
284
|
+
|
285
|
+
@InvokerFactory.register_renderer("NOOP")
|
286
|
+
@InvokerFactory.register_parser("NOOP")
|
287
|
+
@InvokerFactory.register_executor("NOOP")
|
288
|
+
@InvokerFactory.register_processor("NOOP")
|
289
|
+
@InvokerFactory.register_parser("prompty.embedding")
|
290
|
+
@InvokerFactory.register_parser("prompty.image")
|
291
|
+
@InvokerFactory.register_parser("prompty.completion")
|
292
|
+
class NoOp(Invoker):
|
293
|
+
def invoke(self, data: any) -> any:
|
294
|
+
return data
|
295
|
+
|
296
|
+
async def invoke_async(self, data: str) -> str:
|
297
|
+
return self.invoke(data)
|