prompty 0.1.34__tar.gz → 0.1.37__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. {prompty-0.1.34 → prompty-0.1.37}/PKG-INFO +3 -3
  2. {prompty-0.1.34 → prompty-0.1.37}/prompty/azure/processor.py +2 -0
  3. prompty-0.1.37/prompty/azure_beta/__init__.py +11 -0
  4. prompty-0.1.37/prompty/azure_beta/executor.py +281 -0
  5. {prompty-0.1.34 → prompty-0.1.37}/prompty/tracer.py +11 -1
  6. {prompty-0.1.34 → prompty-0.1.37}/pyproject.toml +4 -5
  7. prompty-0.1.37/tests/prompts/structured_output.prompty +24 -0
  8. prompty-0.1.37/tests/prompts/structured_output.prompty.execution.json +68 -0
  9. prompty-0.1.37/tests/prompts/structured_output_schema.json +29 -0
  10. prompty-0.1.37/tests/prompts/sub/sub/__init__.py +0 -0
  11. {prompty-0.1.34 → prompty-0.1.37}/tests/test_common.py +1 -0
  12. {prompty-0.1.34 → prompty-0.1.37}/tests/test_execute.py +10 -0
  13. {prompty-0.1.34 → prompty-0.1.37}/tests/test_tracing.py +46 -0
  14. {prompty-0.1.34 → prompty-0.1.37}/LICENSE +0 -0
  15. {prompty-0.1.34 → prompty-0.1.37}/README.md +0 -0
  16. {prompty-0.1.34 → prompty-0.1.37}/prompty/__init__.py +0 -0
  17. {prompty-0.1.34 → prompty-0.1.37}/prompty/azure/__init__.py +0 -0
  18. {prompty-0.1.34 → prompty-0.1.37}/prompty/azure/executor.py +0 -0
  19. {prompty-0.1.34 → prompty-0.1.37}/prompty/cli.py +0 -0
  20. {prompty-0.1.34 → prompty-0.1.37}/prompty/core.py +0 -0
  21. {prompty-0.1.34 → prompty-0.1.37}/prompty/invoker.py +0 -0
  22. {prompty-0.1.34 → prompty-0.1.37}/prompty/openai/__init__.py +0 -0
  23. {prompty-0.1.34 → prompty-0.1.37}/prompty/openai/executor.py +0 -0
  24. {prompty-0.1.34 → prompty-0.1.37}/prompty/openai/processor.py +0 -0
  25. {prompty-0.1.34 → prompty-0.1.37}/prompty/parsers.py +0 -0
  26. {prompty-0.1.34 → prompty-0.1.37}/prompty/renderers.py +0 -0
  27. {prompty-0.1.34 → prompty-0.1.37}/prompty/serverless/__init__.py +0 -0
  28. {prompty-0.1.34 → prompty-0.1.37}/prompty/serverless/executor.py +0 -0
  29. {prompty-0.1.34 → prompty-0.1.37}/prompty/serverless/processor.py +0 -0
  30. {prompty-0.1.34 → prompty-0.1.37}/prompty/utils.py +0 -0
  31. {prompty-0.1.34/tests/prompts → prompty-0.1.37/tests}/__init__.py +0 -0
  32. {prompty-0.1.34 → prompty-0.1.37}/tests/fake_azure_executor.py +0 -0
  33. {prompty-0.1.34 → prompty-0.1.37}/tests/fake_serverless_executor.py +0 -0
  34. {prompty-0.1.34 → prompty-0.1.37}/tests/generated/1contoso.md +0 -0
  35. {prompty-0.1.34 → prompty-0.1.37}/tests/generated/2contoso.md +0 -0
  36. {prompty-0.1.34 → prompty-0.1.37}/tests/generated/3contoso.md +0 -0
  37. {prompty-0.1.34 → prompty-0.1.37}/tests/generated/4contoso.md +0 -0
  38. {prompty-0.1.34 → prompty-0.1.37}/tests/generated/basic.prompty.md +0 -0
  39. {prompty-0.1.34 → prompty-0.1.37}/tests/generated/camping.jpg +0 -0
  40. {prompty-0.1.34 → prompty-0.1.37}/tests/generated/context.prompty.md +0 -0
  41. {prompty-0.1.34 → prompty-0.1.37}/tests/generated/contoso_multi.md +0 -0
  42. {prompty-0.1.34 → prompty-0.1.37}/tests/generated/faithfulness.prompty.md +0 -0
  43. {prompty-0.1.34 → prompty-0.1.37}/tests/generated/groundedness.prompty.md +0 -0
  44. {prompty-0.1.34 → prompty-0.1.37}/tests/hello_world-goodbye_world-hello_again.embedding.json +0 -0
  45. {prompty-0.1.34 → prompty-0.1.37}/tests/hello_world.embedding.json +0 -0
  46. {prompty-0.1.34/tests/prompts/sub → prompty-0.1.37/tests/prompts}/__init__.py +0 -0
  47. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/basic.prompty +0 -0
  48. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/basic.prompty.execution.json +0 -0
  49. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/basic_json_output.prompty +0 -0
  50. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/camping.jpg +0 -0
  51. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/chat.prompty +0 -0
  52. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/context.json +0 -0
  53. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/context.prompty +0 -0
  54. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/context.prompty.execution.json +0 -0
  55. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/embedding.prompty +0 -0
  56. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/embedding.prompty.execution.json +0 -0
  57. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/evaluation.prompty +0 -0
  58. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/faithfulness.prompty +0 -0
  59. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/faithfulness.prompty.execution.json +0 -0
  60. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/fake.prompty +0 -0
  61. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/funcfile.json +0 -0
  62. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/funcfile.prompty +0 -0
  63. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/functions.prompty +0 -0
  64. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/functions.prompty.execution.json +0 -0
  65. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/groundedness.prompty +0 -0
  66. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/groundedness.prompty.execution.json +0 -0
  67. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/prompty.json +0 -0
  68. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/serverless.prompty +0 -0
  69. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/serverless.prompty.execution.json +0 -0
  70. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/serverless_stream.prompty +0 -0
  71. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/serverless_stream.prompty.execution.json +0 -0
  72. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/streaming.prompty +0 -0
  73. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/streaming.prompty.execution.json +0 -0
  74. {prompty-0.1.34/tests/prompts/sub → prompty-0.1.37/tests/prompts}/sub/__init__.py +0 -0
  75. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/sub/basic.prompty +0 -0
  76. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/sub/sub/basic.prompty +0 -0
  77. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/sub/sub/prompty.json +0 -0
  78. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/sub/sub/test.py +0 -0
  79. {prompty-0.1.34 → prompty-0.1.37}/tests/prompts/test.py +0 -0
  80. {prompty-0.1.34 → prompty-0.1.37}/tests/prompty.json +0 -0
  81. {prompty-0.1.34 → prompty-0.1.37}/tests/test_factory_invoker.py +0 -0
  82. {prompty-0.1.34 → prompty-0.1.37}/tests/test_path_exec.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: prompty
3
- Version: 0.1.34
3
+ Version: 0.1.37
4
4
  Summary: Prompty is a new asset class and format for LLM prompts that aims to provide observability, understandability, and portability for developers. It includes spec, tooling, and a runtime. This Prompty runtime supports Python
5
5
  Author-Email: Seth Juarez <seth.juarez@microsoft.com>
6
6
  License: MIT
@@ -13,9 +13,9 @@ Requires-Dist: click>=8.1.7
13
13
  Requires-Dist: aiofiles>=24.1.0
14
14
  Provides-Extra: azure
15
15
  Requires-Dist: azure-identity>=1.17.1; extra == "azure"
16
- Requires-Dist: openai>=1.35.10; extra == "azure"
16
+ Requires-Dist: openai>=1.43.0; extra == "azure"
17
17
  Provides-Extra: openai
18
- Requires-Dist: openai>=1.35.10; extra == "openai"
18
+ Requires-Dist: openai>=1.43.0; extra == "openai"
19
19
  Provides-Extra: serverless
20
20
  Requires-Dist: azure-identity>=1.17.1; extra == "serverless"
21
21
  Requires-Dist: azure-ai-inference>=1.0.0b3; extra == "serverless"
@@ -9,6 +9,8 @@ from openai.types.create_embedding_response import CreateEmbeddingResponse
9
9
 
10
10
  @InvokerFactory.register_processor("azure")
11
11
  @InvokerFactory.register_processor("azure_openai")
12
+ @InvokerFactory.register_processor("azure_beta")
13
+ @InvokerFactory.register_processor("azure_openai_beta")
12
14
  class AzureOpenAIProcessor(Invoker):
13
15
  """Azure OpenAI Processor"""
14
16
 
@@ -0,0 +1,11 @@
1
+ # __init__.py
2
+ from prompty.invoker import InvokerException
3
+
4
+ try:
5
+ from .executor import AzureOpenAIBetaExecutor
6
+ # Reuse the common Azure OpenAI Processor
7
+ from ..azure.processor import AzureOpenAIProcessor
8
+ except ImportError:
9
+ raise InvokerException(
10
+ "Error registering AzureOpenAIBetaExecutor and AzureOpenAIProcessor", "azure_beta"
11
+ )
@@ -0,0 +1,281 @@
1
+ import azure.identity
2
+ import importlib.metadata
3
+ from typing import AsyncIterator, Iterator
4
+ from openai import AzureOpenAI, AsyncAzureOpenAI
5
+
6
+ from prompty.tracer import Tracer
7
+ from ..core import AsyncPromptyStream, Prompty, PromptyStream
8
+ from ..invoker import Invoker, InvokerFactory
9
+ import re
10
+ from datetime import datetime
11
+
12
+ def extract_date(data: str) -> datetime:
13
+ """Extract date from a string
14
+
15
+ Parameters
16
+ ----------
17
+ data : str
18
+ The string containing the date
19
+
20
+ Returns
21
+ -------
22
+ datetime
23
+ The extracted date as a datetime object
24
+ """
25
+
26
+ # 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')
28
+ match = date_pattern.search(data)
29
+ if match:
30
+ date_str = match.group(0)
31
+ # Validate the date format
32
+ try:
33
+ return datetime.strptime(date_str, '%Y-%m-%d')
34
+ except ValueError:
35
+ pass
36
+ return None
37
+
38
+ def is_structured_output_available(api_version: str) -> bool:
39
+ """Check if the structured output API is available for the given API version
40
+
41
+ Parameters
42
+ ----------
43
+ api_version : datetime
44
+ The API version
45
+
46
+ Returns
47
+ -------
48
+ bool
49
+ True if the structured output API is available, False otherwise
50
+ """
51
+
52
+ # Define the threshold date
53
+ threshold_api_version_date = datetime(2024, 8, 1)
54
+
55
+ api_version_date = extract_date(api_version)
56
+
57
+ # Check if the API version are on or after the threshold date
58
+ if api_version_date >= threshold_api_version_date:
59
+ return True
60
+ return False
61
+
62
+ VERSION = importlib.metadata.version("prompty")
63
+
64
+
65
+ @InvokerFactory.register_executor("azure_beta")
66
+ @InvokerFactory.register_executor("azure_openai_beta")
67
+ class AzureOpenAIBetaExecutor(Invoker):
68
+ """Azure OpenAI Beta Executor"""
69
+
70
+ def __init__(self, prompty: Prompty) -> None:
71
+ super().__init__(prompty)
72
+ self.kwargs = {
73
+ key: value
74
+ for key, value in self.prompty.model.configuration.items()
75
+ if key != "type"
76
+ }
77
+
78
+ # no key, use default credentials
79
+ if "api_key" not in self.kwargs:
80
+ # managed identity if client id
81
+ if "client_id" in self.kwargs:
82
+ default_credential = azure.identity.ManagedIdentityCredential(
83
+ client_id=self.kwargs.pop("client_id"),
84
+ )
85
+ # default credential
86
+ else:
87
+ default_credential = azure.identity.DefaultAzureCredential(
88
+ exclude_shared_token_cache_credential=True
89
+ )
90
+
91
+ self.kwargs["azure_ad_token_provider"] = (
92
+ azure.identity.get_bearer_token_provider(
93
+ default_credential, "https://cognitiveservices.azure.com/.default"
94
+ )
95
+ )
96
+
97
+ self.api = self.prompty.model.api
98
+ self.api_version = self.prompty.model.configuration["api_version"]
99
+ self.deployment = self.prompty.model.configuration["azure_deployment"]
100
+ self.parameters = self.prompty.model.parameters
101
+
102
+ def invoke(self, data: any) -> any:
103
+ """Invoke the Azure OpenAI API
104
+
105
+ Parameters
106
+ ----------
107
+ data : any
108
+ The data to send to the Azure OpenAI API
109
+
110
+ Returns
111
+ -------
112
+ any
113
+ The response from the Azure OpenAI API
114
+ """
115
+
116
+ with Tracer.start("AzureOpenAI") as trace:
117
+ trace("type", "LLM")
118
+ trace("signature", "AzureOpenAI.ctor")
119
+ trace("description", "Azure OpenAI Constructor")
120
+ trace("inputs", self.kwargs)
121
+ client = AzureOpenAI(
122
+ default_headers={
123
+ "User-Agent": f"prompty/{VERSION}",
124
+ "x-ms-useragent": f"prompty/{VERSION}",
125
+ },
126
+ **self.kwargs,
127
+ )
128
+ trace("result", client)
129
+
130
+ with Tracer.start("create") as trace:
131
+ trace("type", "LLM")
132
+ trace("description", "Azure OpenAI Client")
133
+
134
+ if self.api == "chat":
135
+ # 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
137
+ choose_beta = is_structured_output_available(self.api_version)
138
+ if choose_beta:
139
+ trace("signature", "AzureOpenAI.beta.chat.completions.parse")
140
+ else:
141
+ trace("signature", "AzureOpenAI.chat.completions.create")
142
+
143
+ args = {
144
+ "model": self.deployment,
145
+ "messages": data if isinstance(data, list) else [data],
146
+ **self.parameters,
147
+ }
148
+ trace("inputs", args)
149
+ if choose_beta:
150
+ response = client.beta.chat.completions.parse(**args)
151
+ else:
152
+ response = client.chat.completions.create(**args)
153
+ trace("result", response)
154
+
155
+ elif self.api == "completion":
156
+ trace("signature", "AzureOpenAI.completions.create")
157
+ args = {
158
+ "prompt": data,
159
+ "model": self.deployment,
160
+ **self.parameters,
161
+ }
162
+ trace("inputs", args)
163
+ response = client.completions.create(**args)
164
+ trace("result", response)
165
+
166
+ elif self.api == "embedding":
167
+ trace("signature", "AzureOpenAI.embeddings.create")
168
+ args = {
169
+ "input": data if isinstance(data, list) else [data],
170
+ "model": self.deployment,
171
+ **self.parameters,
172
+ }
173
+ trace("inputs", args)
174
+ response = client.embeddings.create(**args)
175
+ trace("result", response)
176
+
177
+ elif self.api == "image":
178
+ trace("signature", "AzureOpenAI.images.generate")
179
+ args = {
180
+ "prompt": data,
181
+ "model": self.deployment,
182
+ **self.parameters,
183
+ }
184
+ trace("inputs", args)
185
+ response = client.images.generate.create(**args)
186
+ trace("result", response)
187
+
188
+ # stream response
189
+ if isinstance(response, Iterator):
190
+ if self.api == "chat":
191
+ # TODO: handle the case where there might be no usage in the stream
192
+ return PromptyStream("AzureOpenAIBetaExecutor", response)
193
+ else:
194
+ return PromptyStream("AzureOpenAIBetaExecutor", response)
195
+ else:
196
+ return response
197
+
198
+ async def invoke_async(self, data: str) -> str:
199
+ """Invoke the Prompty Chat Parser (Async)
200
+
201
+ Parameters
202
+ ----------
203
+ data : str
204
+ The data to parse
205
+
206
+ Returns
207
+ -------
208
+ str
209
+ The parsed data
210
+ """
211
+ with Tracer.start("AzureOpenAIAsync") as trace:
212
+ trace("type", "LLM")
213
+ trace("signature", "AzureOpenAIAsync.ctor")
214
+ trace("description", "Async Azure OpenAI Constructor")
215
+ trace("inputs", self.kwargs)
216
+ client = AsyncAzureOpenAI(
217
+ default_headers={
218
+ "User-Agent": f"prompty/{VERSION}",
219
+ "x-ms-useragent": f"prompty/{VERSION}",
220
+ },
221
+ **self.kwargs,
222
+ )
223
+ trace("result", client)
224
+
225
+ with Tracer.start("create") as trace:
226
+ trace("type", "LLM")
227
+ trace("description", "Azure OpenAI Client")
228
+
229
+ if self.api == "chat":
230
+ trace("signature", "AzureOpenAIAsync.chat.completions.create")
231
+ args = {
232
+ "model": self.deployment,
233
+ "messages": data if isinstance(data, list) else [data],
234
+ **self.parameters,
235
+ }
236
+ trace("inputs", args)
237
+ response = await client.chat.completions.create(**args)
238
+ trace("result", response)
239
+
240
+ elif self.api == "completion":
241
+ trace("signature", "AzureOpenAIAsync.completions.create")
242
+ args = {
243
+ "prompt": data,
244
+ "model": self.deployment,
245
+ **self.parameters,
246
+ }
247
+ trace("inputs", args)
248
+ response = await client.completions.create(**args)
249
+ trace("result", response)
250
+
251
+ elif self.api == "embedding":
252
+ trace("signature", "AzureOpenAIAsync.embeddings.create")
253
+ args = {
254
+ "input": data if isinstance(data, list) else [data],
255
+ "model": self.deployment,
256
+ **self.parameters,
257
+ }
258
+ trace("inputs", args)
259
+ response = await client.embeddings.create(**args)
260
+ trace("result", response)
261
+
262
+ elif self.api == "image":
263
+ trace("signature", "AzureOpenAIAsync.images.generate")
264
+ args = {
265
+ "prompt": data,
266
+ "model": self.deployment,
267
+ **self.parameters,
268
+ }
269
+ trace("inputs", args)
270
+ response = await client.images.generate.create(**args)
271
+ trace("result", response)
272
+
273
+ # stream response
274
+ if isinstance(response, AsyncIterator):
275
+ if self.api == "chat":
276
+ # TODO: handle the case where there might be no usage in the stream
277
+ return AsyncPromptyStream("AzureOpenAIBetaExecutorAsync", response)
278
+ else:
279
+ return AsyncPromptyStream("AzureOpenAIBetaExecutorAsync", response)
280
+ else:
281
+ return response
@@ -28,6 +28,10 @@ def sanitize(key: str, value: Any) -> Any:
28
28
  class Tracer:
29
29
  _tracers: Dict[str, Callable[[str], Iterator[Callable[[str, Any], None]]]] = {}
30
30
 
31
+ SIGNATURE = "signature"
32
+ INPUTS = "inputs"
33
+ RESULT = "result"
34
+
31
35
  @classmethod
32
36
  def add(
33
37
  cls, name: str, tracer: Callable[[str], Iterator[Callable[[str, Any], None]]]
@@ -40,11 +44,17 @@ class Tracer:
40
44
 
41
45
  @classmethod
42
46
  @contextlib.contextmanager
43
- def start(cls, name: str) -> Iterator[Callable[[str, Any], None]]:
47
+ def start(cls, name: str, attributes: Dict[str, Any] = None) -> Iterator[Callable[[str, Any], None]]:
44
48
  with contextlib.ExitStack() as stack:
45
49
  traces = [
46
50
  stack.enter_context(tracer(name)) for tracer in cls._tracers.values()
47
51
  ]
52
+
53
+ if attributes:
54
+ for trace in traces:
55
+ for key, value in attributes.items():
56
+ trace(key, value)
57
+
48
58
  yield lambda key, value: [
49
59
  # normalize and sanitize any trace values
50
60
  trace(key, sanitize(key, to_dict(value)))
@@ -15,7 +15,7 @@ dependencies = [
15
15
  "click>=8.1.7",
16
16
  "aiofiles>=24.1.0",
17
17
  ]
18
- version = "0.1.34"
18
+ version = "0.1.37"
19
19
 
20
20
  [project.license]
21
21
  text = "MIT"
@@ -23,10 +23,10 @@ text = "MIT"
23
23
  [project.optional-dependencies]
24
24
  azure = [
25
25
  "azure-identity>=1.17.1",
26
- "openai>=1.35.10",
26
+ "openai>=1.43.0",
27
27
  ]
28
28
  openai = [
29
- "openai>=1.35.10",
29
+ "openai>=1.43.0",
30
30
  ]
31
31
  serverless = [
32
32
  "azure-identity>=1.17.1",
@@ -42,11 +42,10 @@ distribution = true
42
42
  [tool.pdm.dev-dependencies]
43
43
  dev = [
44
44
  "pytest>=8.2.2",
45
- "openai>=1.35.10",
45
+ "openai>=1.43.0",
46
46
  "azure-ai-inference>=1.0.0b3",
47
47
  "pytest-asyncio>=0.24.0",
48
48
  "azure-identity>=1.17.1",
49
- "openai>=1.35.10",
50
49
  ]
51
50
 
52
51
  [tool.pdm.version]
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: Structured Output Prompt
3
+ description: A prompt that uses the GPT-4o chat API to answer questions in a structured format.
4
+ authors:
5
+ - vgiraud
6
+ model:
7
+ api: chat
8
+ configuration:
9
+ # Minimal model version required for structured output
10
+ azure_deployment: gpt-4o-2024-08-06
11
+ # Minimal API version required for structured output
12
+ api_version: 2024-08-01-preview
13
+ # OpenAI beta API required for structured output
14
+ type: azure_openai_beta
15
+ parameters:
16
+ response_format: ${file:structured_output_schema.json}
17
+ sample:
18
+ statement: Alice and Bob are going to a science fair on Friday.
19
+ ---
20
+ system:
21
+ Extract the event information.
22
+
23
+ user:
24
+ {{statement}}
@@ -0,0 +1,68 @@
1
+ {
2
+ "choices": [
3
+ {
4
+ "content_filter_results": {
5
+ "hate": {
6
+ "filtered": false,
7
+ "severity": "safe"
8
+ },
9
+ "self_harm": {
10
+ "filtered": false,
11
+ "severity": "safe"
12
+ },
13
+ "sexual": {
14
+ "filtered": false,
15
+ "severity": "safe"
16
+ },
17
+ "violence": {
18
+ "filtered": false,
19
+ "severity": "safe"
20
+ }
21
+ },
22
+ "finish_reason": "stop",
23
+ "index": 0,
24
+ "logprobs": null,
25
+ "message": {
26
+ "content": "{\"name\":\"Science Fair\",\"date\":\"Friday\",\"participants\":[\"Alice\",\"Bob\"]}",
27
+ "role": "assistant"
28
+ }
29
+ }
30
+ ],
31
+ "created": 1728500899,
32
+ "id": "chatcmpl-AGWKhgl9bSoQffPMuWjT6JojCKVVs",
33
+ "model": "gpt-4o-2024-08-06",
34
+ "object": "chat.completion",
35
+ "prompt_filter_results": [
36
+ {
37
+ "prompt_index": 0,
38
+ "content_filter_results": {
39
+ "hate": {
40
+ "filtered": false,
41
+ "severity": "safe"
42
+ },
43
+ "jailbreak": {
44
+ "filtered": false,
45
+ "detected": false
46
+ },
47
+ "self_harm": {
48
+ "filtered": false,
49
+ "severity": "safe"
50
+ },
51
+ "sexual": {
52
+ "filtered": false,
53
+ "severity": "safe"
54
+ },
55
+ "violence": {
56
+ "filtered": false,
57
+ "severity": "safe"
58
+ }
59
+ }
60
+ }
61
+ ],
62
+ "system_fingerprint": "fp_67802d9a6d",
63
+ "usage": {
64
+ "completion_tokens": 17,
65
+ "prompt_tokens": 74,
66
+ "total_tokens": 91
67
+ }
68
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "type": "json_schema",
3
+ "json_schema": {
4
+ "name": "calendar_event",
5
+ "schema": {
6
+ "type": "object",
7
+ "properties": {
8
+ "name": {
9
+ "type": "string"
10
+ },
11
+ "date": {
12
+ "type": "string",
13
+ "format": "date-time"
14
+ },
15
+ "participants": {
16
+ "type": "array",
17
+ "items": {
18
+ "type": "string"
19
+ }
20
+ }
21
+ },
22
+ "required": [
23
+ "name",
24
+ "date",
25
+ "participants"
26
+ ]
27
+ }
28
+ }
29
+ }
File without changes
@@ -15,6 +15,7 @@ import prompty
15
15
  "prompts/faithfulness.prompty",
16
16
  "prompts/funcfile.prompty",
17
17
  "prompts/functions.prompty",
18
+ "prompts/structured_output.prompty",
18
19
  "prompts/groundedness.prompty",
19
20
  "prompts/sub/basic.prompty",
20
21
  "prompts/sub/sub/basic.prompty",
@@ -13,12 +13,17 @@ from dotenv import load_dotenv
13
13
  load_dotenv()
14
14
 
15
15
 
16
+
16
17
  @pytest.fixture(scope="module", autouse=True)
17
18
  def fake_azure_executor():
18
19
  InvokerFactory.add_executor("azure", FakeAzureExecutor)
19
20
  InvokerFactory.add_executor("azure_openai", FakeAzureExecutor)
21
+ InvokerFactory.add_executor("azure_beta", FakeAzureExecutor)
22
+ InvokerFactory.add_executor("azure_openai_beta", FakeAzureExecutor)
20
23
  InvokerFactory.add_processor("azure", AzureOpenAIProcessor)
21
24
  InvokerFactory.add_processor("azure_openai", AzureOpenAIProcessor)
25
+ InvokerFactory.add_executor("azure_beta", AzureOpenAIProcessor)
26
+ InvokerFactory.add_executor("azure_openai_beta", AzureOpenAIProcessor)
22
27
  InvokerFactory.add_executor("serverless", FakeServerlessExecutor)
23
28
  InvokerFactory.add_processor("serverless", ServerlessProcessor)
24
29
 
@@ -217,6 +222,11 @@ def test_function_calling():
217
222
  )
218
223
  print(result)
219
224
 
225
+ def test_structured_output():
226
+ result = prompty.execute(
227
+ "prompts/structured_output.prompty",
228
+ )
229
+ print(result)
220
230
 
221
231
  @pytest.mark.asyncio
222
232
  async def test_function_calling_async():
@@ -1,19 +1,27 @@
1
1
  from typing import AsyncIterator
2
2
  import pytest
3
3
  import prompty
4
+ from prompty.serverless.processor import ServerlessProcessor
4
5
  from prompty.tracer import trace, Tracer, console_tracer, PromptyTracer
5
6
 
6
7
  from prompty.invoker import InvokerFactory
7
8
  from tests.fake_azure_executor import FakeAzureExecutor
8
9
  from prompty.azure import AzureOpenAIProcessor
10
+ from tests.fake_serverless_executor import FakeServerlessExecutor
9
11
 
10
12
 
11
13
  @pytest.fixture(scope="module", autouse=True)
12
14
  def setup_module():
13
15
  InvokerFactory.add_executor("azure", FakeAzureExecutor)
14
16
  InvokerFactory.add_executor("azure_openai", FakeAzureExecutor)
17
+ InvokerFactory.add_executor("azure_beta", FakeAzureExecutor)
18
+ InvokerFactory.add_executor("azure_openai_beta", FakeAzureExecutor)
15
19
  InvokerFactory.add_processor("azure", AzureOpenAIProcessor)
16
20
  InvokerFactory.add_processor("azure_openai", AzureOpenAIProcessor)
21
+ InvokerFactory.add_executor("azure_beta", AzureOpenAIProcessor)
22
+ InvokerFactory.add_executor("azure_openai_beta", AzureOpenAIProcessor)
23
+ InvokerFactory.add_executor("serverless", FakeServerlessExecutor)
24
+ InvokerFactory.add_processor("serverless", ServerlessProcessor)
17
25
 
18
26
  Tracer.add("console", console_tracer)
19
27
  json_tracer = PromptyTracer()
@@ -238,6 +246,32 @@ async def test_function_calling_async():
238
246
  print(result)
239
247
 
240
248
 
249
+ @trace
250
+ def test_structured_output():
251
+ result = prompty.execute(
252
+ "prompts/structured_output.prompty",
253
+ )
254
+ print(result)
255
+
256
+
257
+ @pytest.mark.asyncio
258
+ @trace
259
+ async def test_structured_output_async():
260
+ result = await prompty.execute_async(
261
+ "prompts/structured_output.prompty",
262
+ )
263
+ print(result)
264
+
265
+
266
+ @pytest.mark.asyncio
267
+ @trace
268
+ async def test_function_calling_async():
269
+ result = await prompty.execute_async(
270
+ "prompts/functions.prompty",
271
+ )
272
+ print(result)
273
+
274
+
241
275
  # need to add trace attribute to
242
276
  # materialize stream into the function
243
277
  # trace decorator
@@ -262,3 +296,15 @@ async def test_streaming_async():
262
296
  if isinstance(result, AsyncIterator):
263
297
  async for item in result:
264
298
  print(item)
299
+
300
+ @trace
301
+ def test_tracing_attributes():
302
+ with Tracer.start("Test1", {Tracer.SIGNATURE: "test1", "two": 2}) as trace:
303
+ trace(Tracer.INPUTS, 3)
304
+ trace(Tracer.INPUTS, 4)
305
+ with Tracer.start("Test2", {"signature": "5", "six": 6}) as trace:
306
+ trace("inputs", 7)
307
+ trace(Tracer.RESULT, 8)
308
+ with Tracer.start("Test3", {"signature": "9", "ten": 10}) as trace:
309
+ trace("inputs", 11)
310
+ trace(Tracer.RESULT, 12)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes