liminal-sdk-python 2024.3.4__tar.gz → 2024.4.0__tar.gz

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.
Files changed (25) hide show
  1. {liminal_sdk_python-2024.3.4 → liminal_sdk_python-2024.4.0}/PKG-INFO +113 -32
  2. {liminal_sdk_python-2024.3.4 → liminal_sdk_python-2024.4.0}/README.md +111 -30
  3. liminal_sdk_python-2024.4.0/liminal/auth/__init__.py +18 -0
  4. liminal_sdk_python-2024.4.0/liminal/auth/microsoft/__init__.py +1 -0
  5. liminal_sdk_python-2024.3.4/liminal/endpoints/auth/__init__.py → liminal_sdk_python-2024.4.0/liminal/auth/microsoft/device_code_flow.py +11 -8
  6. {liminal_sdk_python-2024.3.4/liminal/endpoints/auth → liminal_sdk_python-2024.4.0/liminal/auth/microsoft}/models.py +3 -19
  7. {liminal_sdk_python-2024.3.4 → liminal_sdk_python-2024.4.0}/liminal/client.py +16 -25
  8. {liminal_sdk_python-2024.3.4 → liminal_sdk_python-2024.4.0}/liminal/const.py +2 -0
  9. {liminal_sdk_python-2024.3.4 → liminal_sdk_python-2024.4.0}/liminal/endpoints/llm/__init__.py +6 -1
  10. liminal_sdk_python-2024.4.0/liminal/endpoints/llm/models.py +79 -0
  11. {liminal_sdk_python-2024.3.4 → liminal_sdk_python-2024.4.0}/liminal/endpoints/prompt/__init__.py +66 -21
  12. {liminal_sdk_python-2024.3.4 → liminal_sdk_python-2024.4.0}/liminal/endpoints/prompt/models.py +28 -28
  13. {liminal_sdk_python-2024.3.4 → liminal_sdk_python-2024.4.0}/liminal/endpoints/thread/__init__.py +16 -26
  14. liminal_sdk_python-2024.4.0/liminal/endpoints/thread/models.py +61 -0
  15. {liminal_sdk_python-2024.3.4 → liminal_sdk_python-2024.4.0}/liminal/helpers/model.py +2 -2
  16. liminal_sdk_python-2024.4.0/pyproject.toml +396 -0
  17. liminal_sdk_python-2024.3.4/liminal/endpoints/llm/models.py +0 -46
  18. liminal_sdk_python-2024.3.4/liminal/endpoints/thread/models.py +0 -31
  19. liminal_sdk_python-2024.3.4/pyproject.toml +0 -441
  20. {liminal_sdk_python-2024.3.4 → liminal_sdk_python-2024.4.0}/LICENSE +0 -0
  21. {liminal_sdk_python-2024.3.4 → liminal_sdk_python-2024.4.0}/liminal/__init__.py +0 -0
  22. {liminal_sdk_python-2024.3.4 → liminal_sdk_python-2024.4.0}/liminal/endpoints/__init__.py +0 -0
  23. {liminal_sdk_python-2024.3.4 → liminal_sdk_python-2024.4.0}/liminal/errors.py +0 -0
  24. {liminal_sdk_python-2024.3.4 → liminal_sdk_python-2024.4.0}/liminal/helpers/__init__.py +0 -0
  25. {liminal_sdk_python-2024.3.4 → liminal_sdk_python-2024.4.0}/liminal/helpers/typing.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: liminal-sdk-python
3
- Version: 2024.3.4
3
+ Version: 2024.4.0
4
4
  Summary: The Liminal SDK for Python
5
5
  Home-page: https://github.com/liminal-ai-security/liminal-sdk-python
6
6
  License: Apache-2.0
@@ -17,7 +17,7 @@ Classifier: Programming Language :: Python :: Implementation :: CPython
17
17
  Classifier: Programming Language :: Python :: Implementation :: PyPy
18
18
  Requires-Dist: httpx (==0.27.0)
19
19
  Requires-Dist: mashumaro (==3.12)
20
- Requires-Dist: msal (==1.27.0)
20
+ Requires-Dist: msal (==1.28.0)
21
21
  Project-URL: Bug Tracker, https://github.com/liminal-ai-security/liminal-sdk-python/issues
22
22
  Project-URL: Changelog, https://github.com/liminal-ai-security/liminal-sdk-python/releases
23
23
  Project-URL: Repository, https://github.com/liminal-ai-security/liminal-sdk-python
@@ -25,6 +25,7 @@ Description-Content-Type: text/markdown
25
25
 
26
26
  # Liminal Python SDK
27
27
 
28
+ [![CI][ci-badge]][ci]
28
29
  [![PyPI][pypi-badge]][pypi]
29
30
  [![Version][version-badge]][version]
30
31
  [![License][license-badge]][license]
@@ -34,7 +35,19 @@ for interacting with the Liminal API.
34
35
 
35
36
  - [Installation](#installation)
36
37
  - [Python Versions](#python-versions)
37
- - [Usage](#usage)
38
+ - [Quickstart](#quickstart)
39
+ - [Initial Authentication](#initial-authentication)
40
+ - [Microsoft Entra ID](#microsoft-entra-id)
41
+ - [Device Code Flow](#device-code-flow)
42
+ - [Ongoing Authentication](#ongoing-authentication)
43
+ - [Manually Interacting with the Refresh Token](#manually-interacting-with-the-refresh-token)
44
+ - [Creating a Liminal Client from a Stored Refresh Token](#creating-a-liminal-client-from-a-stored-refresh-token)
45
+ - [Endpoints](#endpoints)
46
+ - [Getting Model Instances](#getting-model-instances)
47
+ - [Managing Threads](#managing-threads)
48
+ - [Submitting Prompts](#submitting-prompts)
49
+ - [Connection Pooling](#connection-pooling)
50
+ - [Running Examples](#running-examples)
38
51
  - [Contributing](#contributing)
39
52
 
40
53
  # Installation
@@ -59,16 +72,16 @@ API object is easy:
59
72
  import asyncio
60
73
 
61
74
  from liminal import Client
62
- from liminal.endpoints.auth import MicrosoftAuthProvider
75
+ from liminal.auth.microsoft.device_code_flow import DeviceCodeFlowProvider
63
76
 
64
77
 
65
78
  async def main() -> None:
66
79
  """Create the aiohttp session and run the example."""
67
80
  # Create an auth provider to authenticate the user:
68
- microsoft_auth_provider = MicrosoftAuthProvider("<TENANT_ID>", "<CLIENT_ID>")
81
+ auth_provider = DeviceCodeFlowProvider("<TENANT_ID>", "<CLIENT_ID>")
69
82
 
70
83
  # Create the liminal SDK instance:
71
- liminal = Client(microsoft_auth_provider, "<LIMINAL_API_SERVER_URL>")
84
+ liminal = Client(auth_provider, "<LIMINAL_API_SERVER_URL>")
72
85
 
73
86
 
74
87
  asyncio.run(main())
@@ -86,13 +99,15 @@ the following auth providers are supported:
86
99
 
87
100
  ## Microsoft Entra ID
88
101
 
89
- Liminal authenticates with Microsoft Entra ID via an
102
+ ### Device Code Flow
103
+
104
+ This authentication process with Microsoft Entra ID involves an
90
105
  [OAuth 2.0 Device Authorization Grant][oauth-device-auth-grant]. This flow requires you
91
106
  to start your app, retrieve a device code from the logs produced by this SDK, and
92
107
  provide that code to Microsoft via a web browser. Once you complete the login process,
93
108
  the SDK will be authenticated for use with your Liminal instance.
94
109
 
95
- ### Finding your Entra ID Tenant and Client IDs
110
+ To authenticate with this flow, you will need an Entra ID client and tenant ID:
96
111
 
97
112
  - Log into your [Azure portal][azure-portal].
98
113
  - Navigate to `Microsoft Entra ID`.
@@ -101,8 +116,6 @@ the SDK will be authenticated for use with your Liminal instance.
101
116
  - In the `Overview` of the registration, look for the `Application (client) ID` and
102
117
  `Directory (tenant) ID` values.
103
118
 
104
- ### Authenticating Against Entra ID
105
-
106
119
  With a client ID and tenant ID, you can create a Liminal client object and authenticate
107
120
  it:
108
121
 
@@ -110,16 +123,16 @@ it:
110
123
  import asyncio
111
124
 
112
125
  from liminal import Client
113
- from liminal.endpoints.auth import MicrosoftAuthProvider
126
+ from liminal.auth.microsoft.device_code_flow import DeviceCodeFlowProvider
114
127
 
115
128
 
116
129
  async def main() -> None:
117
130
  """Create the aiohttp session and run the example."""
118
131
  # Create an auth provider to authenticate the user:
119
- microsoft_auth_provider = MicrosoftAuthProvider("<TENANT_ID>", "<CLIENT_ID>")
132
+ auth_provider = DeviceCodeFlowProvider("<TENANT_ID>", "<CLIENT_ID>")
120
133
 
121
134
  # Create the liminal SDK instance and authenticate it:
122
- liminal = Client(microsoft_auth_provider, "<LIMINAL_API_SERVER_URL>")
135
+ liminal = Client(auth_provider, "<LIMINAL_API_SERVER_URL>")
123
136
  await liminal.authenticate_from_auth_provider()
124
137
 
125
138
 
@@ -161,13 +174,13 @@ callback at any time.
161
174
  import asyncio
162
175
 
163
176
  from liminal import Client
164
- from liminal.endpoints.auth import MicrosoftAuthProvider
177
+ from liminal.auth.microsoft.device_code_flow import DeviceCodeFlowProvider
165
178
 
166
179
 
167
180
  async def main() -> None:
168
181
  """Create the aiohttp session and run the example."""
169
182
  # Create an auth provider to authenticate the user:
170
- microsoft_auth_provider = MicrosoftAuthProvider("<TENANT_ID>", "<CLIENT_ID>")
183
+ auth_provider = DeviceCodeFlowProvider("<TENANT_ID>", "<CLIENT_ID>")
171
184
 
172
185
  # Create the liminal SDK instance and authenticate it:
173
186
  liminal = Client(microsoft_auth_provider, "<LIMINAL_API_SERVER_URL>")
@@ -189,7 +202,32 @@ async def main() -> None:
189
202
  asyncio.run(main())
190
203
  ```
191
204
 
192
- # Getting Model Instances
205
+ ## Creating a Liminal Client from a Stored Refresh Token
206
+
207
+ Assuming you have a stored refresh token (collected from the callback process shown
208
+ above), it is simple to create a new Limina client using that token:
209
+
210
+ ```python
211
+ import asyncio
212
+
213
+ from liminal import Client
214
+
215
+
216
+ async def main() -> None:
217
+ """Create the aiohttp session and run the example."""
218
+ # Retrieve your stored refresh_token:
219
+ refresh_token = "12345"
220
+
221
+ # Create the client:
222
+ liminal = await client.authenticate_from_refresh_token(refresh_token=refresh_token)
223
+
224
+
225
+ asyncio.run(main())
226
+ ```
227
+
228
+ # Endpoints
229
+
230
+ ## Getting Model Instances
193
231
 
194
232
  Every LLM instance connected in the Liminal admin dashboard is referred to as a "model
195
233
  instance." The SDK provides several methods to interact with model instances:
@@ -198,7 +236,7 @@ instance." The SDK provides several methods to interact with model instances:
198
236
  import asyncio
199
237
 
200
238
  from liminal import Client
201
- from liminal.endpoints.auth import MicrosoftAuthProvider
239
+ from liminal.auth.microsoft.device_code_flow import DeviceCodeFlowProvider
202
240
 
203
241
 
204
242
  async def main() -> None:
@@ -216,7 +254,7 @@ async def main() -> None:
216
254
  asyncio.run(main())
217
255
  ```
218
256
 
219
- # Managing Threads
257
+ ## Managing Threads
220
258
 
221
259
  Threads are conversations with an LLM instance:
222
260
 
@@ -224,7 +262,7 @@ Threads are conversations with an LLM instance:
224
262
  import asyncio
225
263
 
226
264
  from liminal import Client
227
- from liminal.endpoints.auth import MicrosoftAuthProvider
265
+ from liminal.auth.microsoft.device_code_flow import DeviceCodeFlowProvider
228
266
 
229
267
 
230
268
  async def main() -> None:
@@ -245,15 +283,11 @@ async def main() -> None:
245
283
  thread = await liminal.thread.create(model_instance.id, "New Thread")
246
284
  # >>> Thread(...)
247
285
 
248
- # Get the "de-identified" (containing sensitive data) context history for a thread:
249
- thread = await liminal.thread.get_deidentified_context_history(model_instance.id)
250
- # >>> [DeidentifiedToken(...), DeidentifiedToken(...), DeidentifiedToken(...)]
251
-
252
286
 
253
287
  asyncio.run(main())
254
288
  ```
255
289
 
256
- # Submitting Prompts
290
+ ## Submitting Prompts
257
291
 
258
292
  Submitting prompts is easy:
259
293
 
@@ -261,38 +295,49 @@ Submitting prompts is easy:
261
295
  import asyncio
262
296
 
263
297
  from liminal import Client
264
- from liminal.endpoints.auth import MicrosoftAuthProvider
298
+ from liminal.auth.microsoft.device_code_flow import DeviceCodeFlowProvider
265
299
 
266
300
 
267
301
  async def main() -> None:
268
302
  # Assuming you have an authenticated `liminal` object:
269
303
 
270
- # Prompt operations require a thread:
304
+ # Prompt operations require a model instance:
305
+ model_instance = await liminal.llm.get_model_instance(model_instance_name)
306
+
307
+ # Prompt operations optionally take an existing thread:
271
308
  thread = await liminal.thread.get_by_id(123)
272
309
  # >>> Thread(...)
273
310
 
274
311
  # Analayze a prompt for sensitive info:
275
- findings = await liminal.prompt.analyze(thread.id)
312
+ findings = await liminal.prompt.analyze(
313
+ model_instance.id, "Here is a sensitive prompt"
314
+ )
276
315
  # >>> AnalyzeResponse(...)
277
316
 
278
317
  # Cleanse input text by applying the policies defined in the Liminal admin
279
318
  # dashboard. You can optionally provide existing analysis finidings; if not
280
319
  # provided, analyze is # called automatically):
281
320
  cleansed = await liminal.prompt.cleanse(
282
- thread.id, "Here is a sensitive prompt", findings=findings
321
+ model_instance.id,
322
+ "Here is a sensitive prompt",
323
+ findings=findings,
324
+ thread_id=thread.id,
283
325
  )
284
326
  # >>> CleanseResponse(...)
285
327
 
286
328
  # Submit a prompt to an LLM, cleansing it in the process (once again, providing optional
287
329
  # findings):
288
330
  response = await liminal.prompt.submit(
289
- thread.id, "Here is a sensitive prompt", findings=findings
331
+ model_instance.id,
332
+ "Here is a sensitive prompt",
333
+ findings=findings,
334
+ thread_id=thread.id,
290
335
  )
291
- # >>> ProcessResponse(...)
336
+ # >>> SubmitResponse(...)
292
337
 
293
338
  # Rehydrate a response with sensitive data:
294
339
  hydrated = await liminal.prompt.hydrate(
295
- thread.id, "Here is a response to rehdyrate"
340
+ model_instance.id, "Here is a response to rehdyrate", thread_id=thread.id
296
341
  )
297
342
  # >>> HydrateResponse(...)
298
343
 
@@ -300,6 +345,40 @@ async def main() -> None:
300
345
  asyncio.run(main())
301
346
  ```
302
347
 
348
+ # Connection Pooling
349
+
350
+ By default, the library creates a new connection to the Liminal API server with each
351
+ coroutine. If you are calling a large number of coroutines (or merely want to squeeze
352
+ out every second of runtime savings possible), an [`httpx`][httpx] `AsyncClient` can be
353
+ used for connection pooling:
354
+
355
+ ```python
356
+ import asyncio
357
+
358
+ from liminal import Client
359
+ from liminal.auth.microsoft.device_code_flow import DeviceCodeFlowProvider
360
+
361
+
362
+ async def main() -> None:
363
+ # Create an auth provider to authenticate the user:
364
+ microsoft_auth_provider = MicrosoftAuthProvider("<TENANT_ID>", "<CLIENT_ID>")
365
+
366
+ # Create the liminal SDK instance with a shared HTTPX AsyncClient:
367
+ async with httpx.AsyncClient() as client:
368
+ liminal = Client(
369
+ microsoft_auth_provider, "<LIMINAL_API_SERVER_URL>", httpx_client=client
370
+ )
371
+
372
+ # Get to work!
373
+ # ...
374
+
375
+
376
+ asyncio.run(main())
377
+ ```
378
+
379
+ Check out the examples, the tests, and the source files themselves for method
380
+ signatures and more examples.
381
+
303
382
  # Running Examples
304
383
 
305
384
  You can see examples of how to use this SDK via the [`examples`][examples] folder in
@@ -322,7 +401,7 @@ Thanks to all of [our contributors][contributors] so far!
322
401
  2. [Fork the repository][fork].
323
402
  3. (_optional, but highly recommended_) Create a virtual environment: `python3 -m venv .venv`
324
403
  4. (_optional, but highly recommended_) Enter the virtual environment: `source ./.venv/bin/activate`
325
- 5. Install the dev environment: `script/setup`
404
+ 5. Install the dev environment: `./scripts/setup.sh`
326
405
  6. Code your new feature or bug fix on a new branch.
327
406
  7. Write tests that cover your new functionality.
328
407
  8. Run tests and ensure 100% code coverage: `poetry run pytest --cov liminal tests`
@@ -330,10 +409,12 @@ Thanks to all of [our contributors][contributors] so far!
330
409
  10. Submit a pull request!
331
410
 
332
411
  [azure-portal]: https://portal.azure.com
412
+ [ci-badge]: https://img.shields.io/github/actions/workflow/status/liminal-ai-security/liminal-sdk-python/test.yml
333
413
  [ci]: https://github.com/liminal-ai-security/liminal-sdk-python/actions
334
414
  [contributors]: https://github.com/liminal-ai-security/liminal-sdk-python/graphs/contributors
335
415
  [examples]: https://github.com/liminal-ai-security/liminal-sdk-python/tree/development/examples
336
416
  [fork]: https://github.com/liminal-ai-security/liminal-sdk-python/fork
417
+ [httpx]: https://www.python-httpx.org/
337
418
  [issues]: https://github.com/liminal-ai-security/liminal-sdk-python/issues
338
419
  [license-badge]: https://img.shields.io/pypi/l/liminal-sdk-python.svg
339
420
  [license]: https://github.com/liminal-ai-security/liminal-sdk-python/blob/main/LICENSE
@@ -1,5 +1,6 @@
1
1
  # Liminal Python SDK
2
2
 
3
+ [![CI][ci-badge]][ci]
3
4
  [![PyPI][pypi-badge]][pypi]
4
5
  [![Version][version-badge]][version]
5
6
  [![License][license-badge]][license]
@@ -9,7 +10,19 @@ for interacting with the Liminal API.
9
10
 
10
11
  - [Installation](#installation)
11
12
  - [Python Versions](#python-versions)
12
- - [Usage](#usage)
13
+ - [Quickstart](#quickstart)
14
+ - [Initial Authentication](#initial-authentication)
15
+ - [Microsoft Entra ID](#microsoft-entra-id)
16
+ - [Device Code Flow](#device-code-flow)
17
+ - [Ongoing Authentication](#ongoing-authentication)
18
+ - [Manually Interacting with the Refresh Token](#manually-interacting-with-the-refresh-token)
19
+ - [Creating a Liminal Client from a Stored Refresh Token](#creating-a-liminal-client-from-a-stored-refresh-token)
20
+ - [Endpoints](#endpoints)
21
+ - [Getting Model Instances](#getting-model-instances)
22
+ - [Managing Threads](#managing-threads)
23
+ - [Submitting Prompts](#submitting-prompts)
24
+ - [Connection Pooling](#connection-pooling)
25
+ - [Running Examples](#running-examples)
13
26
  - [Contributing](#contributing)
14
27
 
15
28
  # Installation
@@ -34,16 +47,16 @@ API object is easy:
34
47
  import asyncio
35
48
 
36
49
  from liminal import Client
37
- from liminal.endpoints.auth import MicrosoftAuthProvider
50
+ from liminal.auth.microsoft.device_code_flow import DeviceCodeFlowProvider
38
51
 
39
52
 
40
53
  async def main() -> None:
41
54
  """Create the aiohttp session and run the example."""
42
55
  # Create an auth provider to authenticate the user:
43
- microsoft_auth_provider = MicrosoftAuthProvider("<TENANT_ID>", "<CLIENT_ID>")
56
+ auth_provider = DeviceCodeFlowProvider("<TENANT_ID>", "<CLIENT_ID>")
44
57
 
45
58
  # Create the liminal SDK instance:
46
- liminal = Client(microsoft_auth_provider, "<LIMINAL_API_SERVER_URL>")
59
+ liminal = Client(auth_provider, "<LIMINAL_API_SERVER_URL>")
47
60
 
48
61
 
49
62
  asyncio.run(main())
@@ -61,13 +74,15 @@ the following auth providers are supported:
61
74
 
62
75
  ## Microsoft Entra ID
63
76
 
64
- Liminal authenticates with Microsoft Entra ID via an
77
+ ### Device Code Flow
78
+
79
+ This authentication process with Microsoft Entra ID involves an
65
80
  [OAuth 2.0 Device Authorization Grant][oauth-device-auth-grant]. This flow requires you
66
81
  to start your app, retrieve a device code from the logs produced by this SDK, and
67
82
  provide that code to Microsoft via a web browser. Once you complete the login process,
68
83
  the SDK will be authenticated for use with your Liminal instance.
69
84
 
70
- ### Finding your Entra ID Tenant and Client IDs
85
+ To authenticate with this flow, you will need an Entra ID client and tenant ID:
71
86
 
72
87
  - Log into your [Azure portal][azure-portal].
73
88
  - Navigate to `Microsoft Entra ID`.
@@ -76,8 +91,6 @@ the SDK will be authenticated for use with your Liminal instance.
76
91
  - In the `Overview` of the registration, look for the `Application (client) ID` and
77
92
  `Directory (tenant) ID` values.
78
93
 
79
- ### Authenticating Against Entra ID
80
-
81
94
  With a client ID and tenant ID, you can create a Liminal client object and authenticate
82
95
  it:
83
96
 
@@ -85,16 +98,16 @@ it:
85
98
  import asyncio
86
99
 
87
100
  from liminal import Client
88
- from liminal.endpoints.auth import MicrosoftAuthProvider
101
+ from liminal.auth.microsoft.device_code_flow import DeviceCodeFlowProvider
89
102
 
90
103
 
91
104
  async def main() -> None:
92
105
  """Create the aiohttp session and run the example."""
93
106
  # Create an auth provider to authenticate the user:
94
- microsoft_auth_provider = MicrosoftAuthProvider("<TENANT_ID>", "<CLIENT_ID>")
107
+ auth_provider = DeviceCodeFlowProvider("<TENANT_ID>", "<CLIENT_ID>")
95
108
 
96
109
  # Create the liminal SDK instance and authenticate it:
97
- liminal = Client(microsoft_auth_provider, "<LIMINAL_API_SERVER_URL>")
110
+ liminal = Client(auth_provider, "<LIMINAL_API_SERVER_URL>")
98
111
  await liminal.authenticate_from_auth_provider()
99
112
 
100
113
 
@@ -136,13 +149,13 @@ callback at any time.
136
149
  import asyncio
137
150
 
138
151
  from liminal import Client
139
- from liminal.endpoints.auth import MicrosoftAuthProvider
152
+ from liminal.auth.microsoft.device_code_flow import DeviceCodeFlowProvider
140
153
 
141
154
 
142
155
  async def main() -> None:
143
156
  """Create the aiohttp session and run the example."""
144
157
  # Create an auth provider to authenticate the user:
145
- microsoft_auth_provider = MicrosoftAuthProvider("<TENANT_ID>", "<CLIENT_ID>")
158
+ auth_provider = DeviceCodeFlowProvider("<TENANT_ID>", "<CLIENT_ID>")
146
159
 
147
160
  # Create the liminal SDK instance and authenticate it:
148
161
  liminal = Client(microsoft_auth_provider, "<LIMINAL_API_SERVER_URL>")
@@ -164,7 +177,32 @@ async def main() -> None:
164
177
  asyncio.run(main())
165
178
  ```
166
179
 
167
- # Getting Model Instances
180
+ ## Creating a Liminal Client from a Stored Refresh Token
181
+
182
+ Assuming you have a stored refresh token (collected from the callback process shown
183
+ above), it is simple to create a new Limina client using that token:
184
+
185
+ ```python
186
+ import asyncio
187
+
188
+ from liminal import Client
189
+
190
+
191
+ async def main() -> None:
192
+ """Create the aiohttp session and run the example."""
193
+ # Retrieve your stored refresh_token:
194
+ refresh_token = "12345"
195
+
196
+ # Create the client:
197
+ liminal = await client.authenticate_from_refresh_token(refresh_token=refresh_token)
198
+
199
+
200
+ asyncio.run(main())
201
+ ```
202
+
203
+ # Endpoints
204
+
205
+ ## Getting Model Instances
168
206
 
169
207
  Every LLM instance connected in the Liminal admin dashboard is referred to as a "model
170
208
  instance." The SDK provides several methods to interact with model instances:
@@ -173,7 +211,7 @@ instance." The SDK provides several methods to interact with model instances:
173
211
  import asyncio
174
212
 
175
213
  from liminal import Client
176
- from liminal.endpoints.auth import MicrosoftAuthProvider
214
+ from liminal.auth.microsoft.device_code_flow import DeviceCodeFlowProvider
177
215
 
178
216
 
179
217
  async def main() -> None:
@@ -191,7 +229,7 @@ async def main() -> None:
191
229
  asyncio.run(main())
192
230
  ```
193
231
 
194
- # Managing Threads
232
+ ## Managing Threads
195
233
 
196
234
  Threads are conversations with an LLM instance:
197
235
 
@@ -199,7 +237,7 @@ Threads are conversations with an LLM instance:
199
237
  import asyncio
200
238
 
201
239
  from liminal import Client
202
- from liminal.endpoints.auth import MicrosoftAuthProvider
240
+ from liminal.auth.microsoft.device_code_flow import DeviceCodeFlowProvider
203
241
 
204
242
 
205
243
  async def main() -> None:
@@ -220,15 +258,11 @@ async def main() -> None:
220
258
  thread = await liminal.thread.create(model_instance.id, "New Thread")
221
259
  # >>> Thread(...)
222
260
 
223
- # Get the "de-identified" (containing sensitive data) context history for a thread:
224
- thread = await liminal.thread.get_deidentified_context_history(model_instance.id)
225
- # >>> [DeidentifiedToken(...), DeidentifiedToken(...), DeidentifiedToken(...)]
226
-
227
261
 
228
262
  asyncio.run(main())
229
263
  ```
230
264
 
231
- # Submitting Prompts
265
+ ## Submitting Prompts
232
266
 
233
267
  Submitting prompts is easy:
234
268
 
@@ -236,38 +270,49 @@ Submitting prompts is easy:
236
270
  import asyncio
237
271
 
238
272
  from liminal import Client
239
- from liminal.endpoints.auth import MicrosoftAuthProvider
273
+ from liminal.auth.microsoft.device_code_flow import DeviceCodeFlowProvider
240
274
 
241
275
 
242
276
  async def main() -> None:
243
277
  # Assuming you have an authenticated `liminal` object:
244
278
 
245
- # Prompt operations require a thread:
279
+ # Prompt operations require a model instance:
280
+ model_instance = await liminal.llm.get_model_instance(model_instance_name)
281
+
282
+ # Prompt operations optionally take an existing thread:
246
283
  thread = await liminal.thread.get_by_id(123)
247
284
  # >>> Thread(...)
248
285
 
249
286
  # Analayze a prompt for sensitive info:
250
- findings = await liminal.prompt.analyze(thread.id)
287
+ findings = await liminal.prompt.analyze(
288
+ model_instance.id, "Here is a sensitive prompt"
289
+ )
251
290
  # >>> AnalyzeResponse(...)
252
291
 
253
292
  # Cleanse input text by applying the policies defined in the Liminal admin
254
293
  # dashboard. You can optionally provide existing analysis finidings; if not
255
294
  # provided, analyze is # called automatically):
256
295
  cleansed = await liminal.prompt.cleanse(
257
- thread.id, "Here is a sensitive prompt", findings=findings
296
+ model_instance.id,
297
+ "Here is a sensitive prompt",
298
+ findings=findings,
299
+ thread_id=thread.id,
258
300
  )
259
301
  # >>> CleanseResponse(...)
260
302
 
261
303
  # Submit a prompt to an LLM, cleansing it in the process (once again, providing optional
262
304
  # findings):
263
305
  response = await liminal.prompt.submit(
264
- thread.id, "Here is a sensitive prompt", findings=findings
306
+ model_instance.id,
307
+ "Here is a sensitive prompt",
308
+ findings=findings,
309
+ thread_id=thread.id,
265
310
  )
266
- # >>> ProcessResponse(...)
311
+ # >>> SubmitResponse(...)
267
312
 
268
313
  # Rehydrate a response with sensitive data:
269
314
  hydrated = await liminal.prompt.hydrate(
270
- thread.id, "Here is a response to rehdyrate"
315
+ model_instance.id, "Here is a response to rehdyrate", thread_id=thread.id
271
316
  )
272
317
  # >>> HydrateResponse(...)
273
318
 
@@ -275,6 +320,40 @@ async def main() -> None:
275
320
  asyncio.run(main())
276
321
  ```
277
322
 
323
+ # Connection Pooling
324
+
325
+ By default, the library creates a new connection to the Liminal API server with each
326
+ coroutine. If you are calling a large number of coroutines (or merely want to squeeze
327
+ out every second of runtime savings possible), an [`httpx`][httpx] `AsyncClient` can be
328
+ used for connection pooling:
329
+
330
+ ```python
331
+ import asyncio
332
+
333
+ from liminal import Client
334
+ from liminal.auth.microsoft.device_code_flow import DeviceCodeFlowProvider
335
+
336
+
337
+ async def main() -> None:
338
+ # Create an auth provider to authenticate the user:
339
+ microsoft_auth_provider = MicrosoftAuthProvider("<TENANT_ID>", "<CLIENT_ID>")
340
+
341
+ # Create the liminal SDK instance with a shared HTTPX AsyncClient:
342
+ async with httpx.AsyncClient() as client:
343
+ liminal = Client(
344
+ microsoft_auth_provider, "<LIMINAL_API_SERVER_URL>", httpx_client=client
345
+ )
346
+
347
+ # Get to work!
348
+ # ...
349
+
350
+
351
+ asyncio.run(main())
352
+ ```
353
+
354
+ Check out the examples, the tests, and the source files themselves for method
355
+ signatures and more examples.
356
+
278
357
  # Running Examples
279
358
 
280
359
  You can see examples of how to use this SDK via the [`examples`][examples] folder in
@@ -297,7 +376,7 @@ Thanks to all of [our contributors][contributors] so far!
297
376
  2. [Fork the repository][fork].
298
377
  3. (_optional, but highly recommended_) Create a virtual environment: `python3 -m venv .venv`
299
378
  4. (_optional, but highly recommended_) Enter the virtual environment: `source ./.venv/bin/activate`
300
- 5. Install the dev environment: `script/setup`
379
+ 5. Install the dev environment: `./scripts/setup.sh`
301
380
  6. Code your new feature or bug fix on a new branch.
302
381
  7. Write tests that cover your new functionality.
303
382
  8. Run tests and ensure 100% code coverage: `poetry run pytest --cov liminal tests`
@@ -305,10 +384,12 @@ Thanks to all of [our contributors][contributors] so far!
305
384
  10. Submit a pull request!
306
385
 
307
386
  [azure-portal]: https://portal.azure.com
387
+ [ci-badge]: https://img.shields.io/github/actions/workflow/status/liminal-ai-security/liminal-sdk-python/test.yml
308
388
  [ci]: https://github.com/liminal-ai-security/liminal-sdk-python/actions
309
389
  [contributors]: https://github.com/liminal-ai-security/liminal-sdk-python/graphs/contributors
310
390
  [examples]: https://github.com/liminal-ai-security/liminal-sdk-python/tree/development/examples
311
391
  [fork]: https://github.com/liminal-ai-security/liminal-sdk-python/fork
392
+ [httpx]: https://www.python-httpx.org/
312
393
  [issues]: https://github.com/liminal-ai-security/liminal-sdk-python/issues
313
394
  [license-badge]: https://img.shields.io/pypi/l/liminal-sdk-python.svg
314
395
  [license]: https://github.com/liminal-ai-security/liminal-sdk-python/blob/main/LICENSE
@@ -0,0 +1,18 @@
1
+ """Define auth providers."""
2
+
3
+
4
+ class AuthProvider: # pylint: disable=too-few-public-methods
5
+ """Define an auth provider abstract base class."""
6
+
7
+ async def get_access_token(self) -> str:
8
+ """Retrieve an access token from the auth provider.
9
+
10
+ The working principle here is that the auth provider will return an access
11
+ token that can be used to authenticate with the Liminal API server.
12
+
13
+ Returns
14
+ -------
15
+ The access token.
16
+
17
+ """
18
+ raise NotImplementedError
@@ -0,0 +1 @@
1
+ """Define Microsoft providers."""