prompty 0.1.40__py3-none-any.whl → 0.1.44__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 CHANGED
@@ -1,8 +1,8 @@
1
1
  import traceback
2
+ import typing
2
3
  from pathlib import Path
3
- from typing import Dict, List, Union
4
- from .tracer import trace
5
- from .invoker import InvokerFactory, NoOp
4
+ from typing import Union
5
+
6
6
  from .core import (
7
7
  ModelSettings,
8
8
  Prompty,
@@ -10,23 +10,27 @@ from .core import (
10
10
  TemplateSettings,
11
11
  param_hoisting,
12
12
  )
13
+ from .invoker import InvokerFactory
14
+ from .parsers import PromptyChatParser
15
+ from .renderers import Jinja2Renderer
16
+ from .tracer import trace
13
17
  from .utils import (
14
18
  load_global_config,
15
19
  load_global_config_async,
16
- load_prompty_async,
17
20
  load_prompty,
21
+ load_prompty_async,
18
22
  )
19
23
 
20
- from .renderers import *
21
- from .parsers import *
24
+ InvokerFactory.add_renderer("jinja2", Jinja2Renderer)
25
+ InvokerFactory.add_parser("prompty.chat", PromptyChatParser)
22
26
 
23
27
 
24
28
  @trace(description="Create a headless prompty object for programmatic use.")
25
29
  def headless(
26
30
  api: str,
27
- content: str | List[str] | dict,
28
- configuration: Dict[str, any] = {},
29
- parameters: Dict[str, any] = {},
31
+ content: Union[str, list[str], dict],
32
+ configuration: dict[str, typing.Any] = {},
33
+ parameters: dict[str, typing.Any] = {},
30
34
  connection: str = "default",
31
35
  ) -> Prompty:
32
36
  """Create a headless prompty object for programmatic use.
@@ -81,9 +85,9 @@ def headless(
81
85
  @trace(description="Create a headless prompty object for programmatic use.")
82
86
  async def headless_async(
83
87
  api: str,
84
- content: str | List[str] | dict,
85
- configuration: Dict[str, any] = {},
86
- parameters: Dict[str, any] = {},
88
+ content: Union[str, list[str], dict],
89
+ configuration: dict[str, typing.Any] = {},
90
+ parameters: dict[str, typing.Any] = {},
87
91
  connection: str = "default",
88
92
  ) -> Prompty:
89
93
  """Create a headless prompty object for programmatic use.
@@ -188,17 +192,17 @@ def _load_raw_prompty(attributes: dict, content: str, p: Path, global_config: di
188
192
  else:
189
193
  outputs = {}
190
194
 
191
- p = Prompty(
192
- **attributes,
195
+ prompty = Prompty(
193
196
  model=model,
194
197
  inputs=inputs,
195
198
  outputs=outputs,
196
199
  template=template,
197
200
  content=content,
198
201
  file=p,
202
+ **attributes
199
203
  )
200
204
 
201
- return p
205
+ return prompty
202
206
 
203
207
 
204
208
  @trace(description="Load a prompty file.")
@@ -311,7 +315,7 @@ async def load_async(prompty_file: str, configuration: str = "default") -> Promp
311
315
  @trace(description="Prepare the inputs for the prompt.")
312
316
  def prepare(
313
317
  prompt: Prompty,
314
- inputs: Dict[str, any] = {},
318
+ inputs: dict[str, typing.Any] = {},
315
319
  ):
316
320
  """Prepare the inputs for the prompt.
317
321
 
@@ -345,7 +349,7 @@ def prepare(
345
349
  @trace(description="Prepare the inputs for the prompt.")
346
350
  async def prepare_async(
347
351
  prompt: Prompty,
348
- inputs: Dict[str, any] = {},
352
+ inputs: dict[str, typing.Any] = {},
349
353
  ):
350
354
  """Prepare the inputs for the prompt.
351
355
 
@@ -379,9 +383,9 @@ async def prepare_async(
379
383
  @trace(description="Run the prepared Prompty content against the model.")
380
384
  def run(
381
385
  prompt: Prompty,
382
- content: dict | list | str,
383
- configuration: Dict[str, any] = {},
384
- parameters: Dict[str, any] = {},
386
+ content: Union[dict, list, str],
387
+ configuration: dict[str, typing.Any] = {},
388
+ parameters: dict[str, typing.Any] = {},
385
389
  raw: bool = False,
386
390
  ):
387
391
  """Run the prepared Prompty content.
@@ -431,9 +435,9 @@ def run(
431
435
  @trace(description="Run the prepared Prompty content against the model.")
432
436
  async def run_async(
433
437
  prompt: Prompty,
434
- content: dict | list | str,
435
- configuration: Dict[str, any] = {},
436
- parameters: Dict[str, any] = {},
438
+ content: Union[dict, list, str],
439
+ configuration: dict[str, typing.Any] = {},
440
+ parameters: dict[str, typing.Any] = {},
437
441
  raw: bool = False,
438
442
  ):
439
443
  """Run the prepared Prompty content.
@@ -483,9 +487,9 @@ async def run_async(
483
487
  @trace(description="Execute a prompty")
484
488
  def execute(
485
489
  prompt: Union[str, Prompty],
486
- configuration: Dict[str, any] = {},
487
- parameters: Dict[str, any] = {},
488
- inputs: Dict[str, any] = {},
490
+ configuration: dict[str, typing.Any] = {},
491
+ parameters: dict[str, typing.Any] = {},
492
+ inputs: dict[str, typing.Any] = {},
489
493
  raw: bool = False,
490
494
  config_name: str = "default",
491
495
  ):
@@ -517,7 +521,7 @@ def execute(
517
521
  >>> inputs = {"name": "John Doe"}
518
522
  >>> result = prompty.execute("prompts/basic.prompty", inputs=inputs)
519
523
  """
520
- if isinstance(prompt, str):
524
+ if isinstance(prompt, (str, Path)):
521
525
  path = Path(prompt)
522
526
  if not path.is_absolute():
523
527
  # get caller's path (take into account trace frame)
@@ -537,9 +541,9 @@ def execute(
537
541
  @trace(description="Execute a prompty")
538
542
  async def execute_async(
539
543
  prompt: Union[str, Prompty],
540
- configuration: Dict[str, any] = {},
541
- parameters: Dict[str, any] = {},
542
- inputs: Dict[str, any] = {},
544
+ configuration: dict[str, typing.Any] = {},
545
+ parameters: dict[str, typing.Any] = {},
546
+ inputs: dict[str, typing.Any] = {},
543
547
  raw: bool = False,
544
548
  config_name: str = "default",
545
549
  ):
@@ -571,7 +575,7 @@ async def execute_async(
571
575
  >>> inputs = {"name": "John Doe"}
572
576
  >>> result = await prompty.execute_async("prompts/basic.prompty", inputs=inputs)
573
577
  """
574
- if isinstance(prompt, str):
578
+ if isinstance(prompt, (str, Path)):
575
579
  path = Path(prompt)
576
580
  if not path.is_absolute():
577
581
  # get caller's path (take into account trace frame)
prompty/azure/__init__.py CHANGED
@@ -2,8 +2,8 @@
2
2
  from prompty.invoker import InvokerException
3
3
 
4
4
  try:
5
- from .executor import AzureOpenAIExecutor
6
- from .processor import AzureOpenAIProcessor
5
+ from .executor import AzureOpenAIExecutor # noqa
6
+ from .processor import AzureOpenAIProcessor # noqa
7
7
  except ImportError:
8
8
  raise InvokerException(
9
9
  "Error registering AzureOpenAIExecutor and AzureOpenAIProcessor", "azure"
prompty/azure/executor.py CHANGED
@@ -1,12 +1,14 @@
1
- import json
2
- import azure.identity
3
1
  import importlib.metadata
4
- from typing import AsyncIterator, Iterator
5
- from openai import APIResponse, AzureOpenAI, AsyncAzureOpenAI
2
+ import typing
3
+ from collections.abc import AsyncIterator, Iterator
6
4
 
7
- from prompty.tracer import Tracer, sanitize
8
- from ..core import AsyncPromptyStream, Prompty, PromptyStream
5
+ import azure.identity
6
+ from openai import APIResponse, AsyncAzureOpenAI, AzureOpenAI
9
7
  from openai.types.chat.chat_completion import ChatCompletion
8
+
9
+ from prompty.tracer import Tracer
10
+
11
+ from ..core import AsyncPromptyStream, Prompty, PromptyStream
10
12
  from ..invoker import Invoker, InvokerFactory
11
13
 
12
14
  VERSION = importlib.metadata.version("prompty")
@@ -29,7 +31,10 @@ class AzureOpenAIExecutor(Invoker):
29
31
  if "api_key" not in self.kwargs:
30
32
  # managed identity if client id
31
33
  if "client_id" in self.kwargs:
32
- default_credential = azure.identity.ManagedIdentityCredential(
34
+ default_credential: typing.Union[
35
+ azure.identity.ManagedIdentityCredential,
36
+ azure.identity.DefaultAzureCredential,
37
+ ] = azure.identity.ManagedIdentityCredential(
33
38
  client_id=self.kwargs.pop("client_id"),
34
39
  )
35
40
  # default credential
@@ -48,7 +53,7 @@ class AzureOpenAIExecutor(Invoker):
48
53
  self.deployment = self.prompty.model.configuration["azure_deployment"]
49
54
  self.parameters = self.prompty.model.parameters
50
55
 
51
- def invoke(self, data: any) -> any:
56
+ def invoke(self, data: typing.Any) -> typing.Union[str, PromptyStream]:
52
57
  """Invoke the Azure OpenAI API
53
58
 
54
59
  Parameters
@@ -89,12 +94,11 @@ class AzureOpenAIExecutor(Invoker):
89
94
  }
90
95
  trace("inputs", args)
91
96
 
92
- if "stream" in args and args["stream"] == True:
97
+ if "stream" in args and args["stream"]:
93
98
  response = client.chat.completions.create(**args)
94
99
  else:
95
- raw: APIResponse = client.chat.completions.with_raw_response.create(
96
- **args
97
- )
100
+ raw = client.chat.completions.with_raw_response.create(**args)
101
+
98
102
  response = ChatCompletion.model_validate_json(raw.text)
99
103
 
100
104
  for k, v in raw.headers.raw:
@@ -135,7 +139,7 @@ class AzureOpenAIExecutor(Invoker):
135
139
  **self.parameters,
136
140
  }
137
141
  trace("inputs", args)
138
- response = client.images.generate.create(**args)
142
+ response = client.images.generate(**args)
139
143
  trace("result", response)
140
144
 
141
145
  # stream response
@@ -148,7 +152,7 @@ class AzureOpenAIExecutor(Invoker):
148
152
  else:
149
153
  return response
150
154
 
151
- async def invoke_async(self, data: str) -> str:
155
+ async def invoke_async(self, data: str) -> typing.Union[str, AsyncPromptyStream]:
152
156
  """Invoke the Prompty Chat Parser (Async)
153
157
 
154
158
  Parameters
@@ -188,13 +192,13 @@ class AzureOpenAIExecutor(Invoker):
188
192
  }
189
193
  trace("inputs", args)
190
194
 
191
- if "stream" in args and args["stream"] == True:
195
+ if "stream" in args and args["stream"]:
192
196
  response = await client.chat.completions.create(**args)
193
197
  else:
194
- raw: APIResponse = await client.chat.completions.with_raw_response.create(
195
- **args
198
+ raw: APIResponse = (
199
+ await client.chat.completions.with_raw_response.create(**args)
196
200
  )
197
- response = ChatCompletion.model_validate_json(raw.text)
201
+ response = ChatCompletion.model_validate_json(raw.text())
198
202
  for k, v in raw.headers.raw:
199
203
  trace(k.decode("utf-8"), v.decode("utf-8"))
200
204
 
@@ -234,7 +238,7 @@ class AzureOpenAIExecutor(Invoker):
234
238
  **self.parameters,
235
239
  }
236
240
  trace("inputs", args)
237
- response = await client.images.generate.create(**args)
241
+ response = await client.images.generate(**args)
238
242
  trace("result", response)
239
243
 
240
244
  # stream response
@@ -1,10 +1,13 @@
1
- from typing import AsyncIterator, Iterator
1
+ import typing
2
+ from collections.abc import AsyncIterator, Iterator
3
+
4
+ from openai.types.chat.chat_completion import ChatCompletion
2
5
  from openai.types.completion import Completion
6
+ from openai.types.create_embedding_response import CreateEmbeddingResponse
3
7
  from openai.types.images_response import ImagesResponse
4
- from openai.types.chat.chat_completion import ChatCompletion
8
+
5
9
  from ..core import AsyncPromptyStream, Prompty, PromptyStream, ToolCall
6
10
  from ..invoker import Invoker, InvokerFactory
7
- from openai.types.create_embedding_response import CreateEmbeddingResponse
8
11
 
9
12
 
10
13
  @InvokerFactory.register_processor("azure")
@@ -17,7 +20,15 @@ class AzureOpenAIProcessor(Invoker):
17
20
  def __init__(self, prompty: Prompty) -> None:
18
21
  super().__init__(prompty)
19
22
 
20
- def invoke(self, data: any) -> any:
23
+ def invoke(self, data: typing.Any) -> typing.Union[
24
+ str,
25
+ list[typing.Union[str, None]],
26
+ list[ToolCall],
27
+ list[float],
28
+ list[list[float]],
29
+ PromptyStream,
30
+ None,
31
+ ]:
21
32
  """Invoke the OpenAI/Azure API
22
33
 
23
34
  Parameters
@@ -71,7 +82,7 @@ class AzureOpenAIProcessor(Invoker):
71
82
  for chunk in data:
72
83
  if (
73
84
  len(chunk.choices) == 1
74
- and chunk.choices[0].delta.content != None
85
+ and chunk.choices[0].delta.content is not None
75
86
  ):
76
87
  content = chunk.choices[0].delta.content
77
88
  yield content
@@ -80,7 +91,7 @@ class AzureOpenAIProcessor(Invoker):
80
91
  else:
81
92
  return data
82
93
 
83
- async def invoke_async(self, data: str) -> str:
94
+ async def invoke_async(self, data: str) -> typing.Union[str, AsyncPromptyStream]:
84
95
  """Invoke the Prompty Chat Parser (Async)
85
96
 
86
97
  Parameters
@@ -134,7 +145,7 @@ class AzureOpenAIProcessor(Invoker):
134
145
  async for chunk in data:
135
146
  if (
136
147
  len(chunk.choices) == 1
137
- and chunk.choices[0].delta.content != None
148
+ and chunk.choices[0].delta.content is not None
138
149
  ):
139
150
  content = chunk.choices[0].delta.content
140
151
  yield content
@@ -2,9 +2,9 @@
2
2
  from prompty.invoker import InvokerException
3
3
 
4
4
  try:
5
- from .executor import AzureOpenAIBetaExecutor
6
5
  # Reuse the common Azure OpenAI Processor
7
- from ..azure.processor import AzureOpenAIProcessor
6
+ from ..azure.processor import AzureOpenAIProcessor # noqa
7
+ from .executor import AzureOpenAIBetaExecutor # noqa
8
8
  except ImportError:
9
9
  raise InvokerException(
10
10
  "Error registering AzureOpenAIBetaExecutor and AzureOpenAIProcessor", "azure_beta"
@@ -1,15 +1,19 @@
1
- import azure.identity
2
1
  import importlib.metadata
3
- from typing import AsyncIterator, Iterator
4
- from openai import AzureOpenAI, AsyncAzureOpenAI
2
+ import re
3
+ import typing
4
+ from collections.abc import AsyncIterator, Iterator
5
+ from datetime import datetime
6
+
7
+ import azure.identity
8
+ from openai import AsyncAzureOpenAI, AzureOpenAI
5
9
 
6
10
  from prompty.tracer import Tracer
11
+
7
12
  from ..core import AsyncPromptyStream, Prompty, PromptyStream
8
13
  from ..invoker import Invoker, InvokerFactory
9
- import re
10
- from datetime import datetime
11
14
 
12
- def extract_date(data: str) -> datetime:
15
+
16
+ def extract_date(data: str) -> typing.Union[datetime, None]:
13
17
  """Extract date from a string
14
18
 
15
19
  Parameters
@@ -24,17 +28,18 @@ def extract_date(data: str) -> datetime:
24
28
  """
25
29
 
26
30
  # Regular expression to find dates in the format YYYY-MM-DD
27
- date_pattern = re.compile(r'\b\d{4}-\d{2}-\d{2}\b')
31
+ date_pattern = re.compile(r"\b\d{4}-\d{2}-\d{2}\b")
28
32
  match = date_pattern.search(data)
29
33
  if match:
30
34
  date_str = match.group(0)
31
35
  # Validate the date format
32
36
  try:
33
- return datetime.strptime(date_str, '%Y-%m-%d')
37
+ return datetime.strptime(date_str, "%Y-%m-%d")
34
38
  except ValueError:
35
39
  pass
36
40
  return None
37
41
 
42
+
38
43
  def is_structured_output_available(api_version: str) -> bool:
39
44
  """Check if the structured output API is available for the given API version
40
45
 
@@ -55,10 +60,11 @@ def is_structured_output_available(api_version: str) -> bool:
55
60
  api_version_date = extract_date(api_version)
56
61
 
57
62
  # Check if the API version are on or after the threshold date
58
- if api_version_date >= threshold_api_version_date:
63
+ if api_version_date is not None and api_version_date >= threshold_api_version_date:
59
64
  return True
60
65
  return False
61
66
 
67
+
62
68
  VERSION = importlib.metadata.version("prompty")
63
69
 
64
70
 
@@ -79,7 +85,10 @@ class AzureOpenAIBetaExecutor(Invoker):
79
85
  if "api_key" not in self.kwargs:
80
86
  # managed identity if client id
81
87
  if "client_id" in self.kwargs:
82
- default_credential = azure.identity.ManagedIdentityCredential(
88
+ default_credential: typing.Union[
89
+ azure.identity.ManagedIdentityCredential,
90
+ azure.identity.DefaultAzureCredential,
91
+ ] = azure.identity.ManagedIdentityCredential(
83
92
  client_id=self.kwargs.pop("client_id"),
84
93
  )
85
94
  # default credential
@@ -99,7 +108,7 @@ class AzureOpenAIBetaExecutor(Invoker):
99
108
  self.deployment = self.prompty.model.configuration["azure_deployment"]
100
109
  self.parameters = self.prompty.model.parameters
101
110
 
102
- def invoke(self, data: any) -> any:
111
+ def invoke(self, data: typing.Any) -> typing.Any:
103
112
  """Invoke the Azure OpenAI API
104
113
 
105
114
  Parameters
@@ -133,13 +142,13 @@ class AzureOpenAIBetaExecutor(Invoker):
133
142
 
134
143
  if self.api == "chat":
135
144
  # We can only verify the API version as the model and its version are not part of prompty configuration
136
- # Should be gpt-4o and 2024-08-06 or later
145
+ # Should be gpt-4o and 2024-08-06 or later
137
146
  choose_beta = is_structured_output_available(self.api_version)
138
147
  if choose_beta:
139
148
  trace("signature", "AzureOpenAI.beta.chat.completions.parse")
140
149
  else:
141
150
  trace("signature", "AzureOpenAI.chat.completions.create")
142
-
151
+
143
152
  args = {
144
153
  "model": self.deployment,
145
154
  "messages": data if isinstance(data, list) else [data],
@@ -147,7 +156,7 @@ class AzureOpenAIBetaExecutor(Invoker):
147
156
  }
148
157
  trace("inputs", args)
149
158
  if choose_beta:
150
- response = client.beta.chat.completions.parse(**args)
159
+ response: typing.Any = client.beta.chat.completions.parse(**args)
151
160
  else:
152
161
  response = client.chat.completions.create(**args)
153
162
  trace("result", response)
@@ -182,7 +191,7 @@ class AzureOpenAIBetaExecutor(Invoker):
182
191
  **self.parameters,
183
192
  }
184
193
  trace("inputs", args)
185
- response = client.images.generate.create(**args)
194
+ response = client.images.generate(**args)
186
195
  trace("result", response)
187
196
 
188
197
  # stream response
@@ -195,7 +204,7 @@ class AzureOpenAIBetaExecutor(Invoker):
195
204
  else:
196
205
  return response
197
206
 
198
- async def invoke_async(self, data: str) -> str:
207
+ async def invoke_async(self, data: str) -> typing.Union[str, AsyncPromptyStream]:
199
208
  """Invoke the Prompty Chat Parser (Async)
200
209
 
201
210
  Parameters
@@ -267,7 +276,7 @@ class AzureOpenAIBetaExecutor(Invoker):
267
276
  **self.parameters,
268
277
  }
269
278
  trace("inputs", args)
270
- response = await client.images.generate.create(**args)
279
+ response = await client.images.generate(**args)
271
280
  trace("result", response)
272
281
 
273
282
  # stream response
prompty/cli.py CHANGED
@@ -1,15 +1,15 @@
1
- import os
2
- import json
3
- import click
4
1
  import importlib
5
- from typing import Any, Dict, Optional
6
-
2
+ import json
3
+ import os
7
4
  from pathlib import Path
5
+ from typing import Any, Optional
6
+
7
+ import click
8
+ from dotenv import load_dotenv
8
9
  from pydantic import BaseModel
9
10
 
10
11
  import prompty
11
- from prompty.tracer import trace, PromptyTracer, console_tracer, Tracer
12
- from dotenv import load_dotenv
12
+ from prompty.tracer import PromptyTracer, Tracer, console_tracer, trace
13
13
 
14
14
 
15
15
  def normalize_path(p, create_dir=False) -> Path:
@@ -47,9 +47,9 @@ def chat_mode(prompt_path: str):
47
47
  W = "\033[0m" # white (normal)
48
48
  R = "\033[31m" # red
49
49
  G = "\033[32m" # green
50
- O = "\033[33m" # orange
50
+ #O = "\033[33m" # orange
51
51
  B = "\033[34m" # blue
52
- P = "\033[35m" # purple
52
+ #P = "\033[35m" # purple
53
53
  print(f"Executing {str(prompt_path)} in chat mode...")
54
54
  p = prompty.load(str(prompt_path))
55
55
  if "chat_history" not in p.sample:
@@ -81,7 +81,7 @@ def chat_mode(prompt_path: str):
81
81
 
82
82
 
83
83
  @trace
84
- def execute(prompt_path: str, inputs: Optional[Dict[str, Any]] = None, raw=False):
84
+ def execute(prompt_path: str, inputs: Optional[dict[str, Any]] = None, raw=False):
85
85
  p = prompty.load(prompt_path)
86
86
 
87
87
  inputs = inputs or {}