auth0-ai-langchain 0.2.0__tar.gz → 1.0.0b1__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.

Potentially problematic release.


This version of auth0-ai-langchain might be problematic. Click here for more details.

Files changed (28) hide show
  1. {auth0_ai_langchain-0.2.0 → auth0_ai_langchain-1.0.0b1}/PKG-INFO +126 -25
  2. {auth0_ai_langchain-0.2.0 → auth0_ai_langchain-1.0.0b1}/README.md +119 -18
  3. {auth0_ai_langchain-0.2.0 → auth0_ai_langchain-1.0.0b1}/auth0_ai_langchain/FGARetriever.py +3 -3
  4. auth0_ai_langchain-1.0.0b1/auth0_ai_langchain/auth0_ai.py +112 -0
  5. auth0_ai_langchain-1.0.0b1/auth0_ai_langchain/ciba/__init__.py +3 -0
  6. auth0_ai_langchain-1.0.0b1/auth0_ai_langchain/ciba/ciba_authorizer.py +17 -0
  7. auth0_ai_langchain-1.0.0b1/auth0_ai_langchain/ciba/graph_resumer.py +154 -0
  8. auth0_ai_langchain-1.0.0b1/auth0_ai_langchain/federated_connections/__init__.py +7 -0
  9. auth0_ai_langchain-1.0.0b1/auth0_ai_langchain/federated_connections/federated_connection_authorizer.py +33 -0
  10. auth0_ai_langchain-1.0.0b1/auth0_ai_langchain/fga/__init__.py +4 -0
  11. auth0_ai_langchain-1.0.0b1/auth0_ai_langchain/utils/interrupt.py +30 -0
  12. auth0_ai_langchain-1.0.0b1/auth0_ai_langchain/utils/tool_wrapper.py +34 -0
  13. {auth0_ai_langchain-0.2.0 → auth0_ai_langchain-1.0.0b1}/pyproject.toml +7 -7
  14. auth0_ai_langchain-0.2.0/auth0_ai_langchain/auth0_ai.py +0 -43
  15. auth0_ai_langchain-0.2.0/auth0_ai_langchain/ciba/__init__.py +0 -0
  16. auth0_ai_langchain-0.2.0/auth0_ai_langchain/ciba/ciba_graph/ciba_graph.py +0 -109
  17. auth0_ai_langchain-0.2.0/auth0_ai_langchain/ciba/ciba_graph/initialize_ciba.py +0 -91
  18. auth0_ai_langchain-0.2.0/auth0_ai_langchain/ciba/ciba_graph/initialize_hitl.py +0 -50
  19. auth0_ai_langchain-0.2.0/auth0_ai_langchain/ciba/ciba_graph/types.py +0 -115
  20. auth0_ai_langchain-0.2.0/auth0_ai_langchain/ciba/ciba_graph/utils.py +0 -17
  21. auth0_ai_langchain-0.2.0/auth0_ai_langchain/ciba/ciba_poller_graph.py +0 -105
  22. auth0_ai_langchain-0.2.0/auth0_ai_langchain/ciba/types.py +0 -8
  23. auth0_ai_langchain-0.2.0/auth0_ai_langchain/federated_connections/__init__.py +0 -4
  24. auth0_ai_langchain-0.2.0/auth0_ai_langchain/federated_connections/federated_connection_authorizer.py +0 -52
  25. auth0_ai_langchain-0.2.0/auth0_ai_langchain/fga/fga_authorizer.py +0 -3
  26. auth0_ai_langchain-0.2.0/auth0_ai_langchain/utils/interrupt.py +0 -13
  27. {auth0_ai_langchain-0.2.0 → auth0_ai_langchain-1.0.0b1}/LICENSE +0 -0
  28. {auth0_ai_langchain-0.2.0 → auth0_ai_langchain-1.0.0b1}/auth0_ai_langchain/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: auth0-ai-langchain
3
- Version: 0.2.0
3
+ Version: 1.0.0b1
4
4
  Summary: This package is an SDK for building secure AI-powered applications using Auth0, Okta FGA and LangChain.
5
5
  License: Apache-2.0
6
6
  Author: Auth0
@@ -11,12 +11,12 @@ Classifier: Programming Language :: Python :: 3
11
11
  Classifier: Programming Language :: Python :: 3.11
12
12
  Classifier: Programming Language :: Python :: 3.12
13
13
  Classifier: Programming Language :: Python :: 3.13
14
- Requires-Dist: auth0-ai (>=0.2.0,<0.3.0)
15
- Requires-Dist: langchain (>=0.3.20,<0.4.0)
16
- Requires-Dist: langchain-core (>=0.3.43,<0.4.0)
17
- Requires-Dist: langgraph (>=0.3.25,<0.4.0)
18
- Requires-Dist: langgraph-sdk (>=0.1.55,<0.2.0)
19
- Requires-Dist: openfga-sdk (>=0.9.0,<0.10.0)
14
+ Requires-Dist: auth0-ai (>=1.0.0b1,<2.0.0)
15
+ Requires-Dist: langchain (>=0.3.25,<0.4.0)
16
+ Requires-Dist: langchain-core (>=0.3.59,<0.4.0)
17
+ Requires-Dist: langgraph (>=0.4.3,<0.5.0)
18
+ Requires-Dist: langgraph-sdk (>=0.1.66,<0.2.0)
19
+ Requires-Dist: openfga-sdk (>=0.9.4,<0.10.0)
20
20
  Project-URL: Homepage, https://auth0.com
21
21
  Description-Content-Type: text/markdown
22
22
 
@@ -34,6 +34,52 @@ Description-Content-Type: text/markdown
34
34
  pip install auth0-ai-langchain
35
35
  ```
36
36
 
37
+ ## Async User Confirmation
38
+
39
+ `Auth0AI` uses CIBA (Client-Initiated Backchannel Authentication) to handle user confirmation asynchronously. This is useful when you need to confirm a user action before proceeding with a tool execution.
40
+
41
+ Full Example of [Async User Confirmation](https://github.com/auth0-lab/auth0-ai-python/tree/main/examples/async-user-confirmation/langchain-examples).
42
+
43
+ 1. Define a tool with the proper authorizer specifying a function to resolve the user id:
44
+
45
+ ```python
46
+ from auth0_ai_langchain.auth0_ai import Auth0AI
47
+ from auth0_ai_langchain.ciba import get_ciba_credentials
48
+ from langchain_core.runnables import ensure_config
49
+ from langchain_core.tools import StructuredTool
50
+
51
+ # If not provided, Auth0 settings will be read from env variables: `AUTH0_DOMAIN`, `AUTH0_CLIENT_ID`, and `AUTH0_CLIENT_SECRET`
52
+ auth0_ai = Auth0AI()
53
+
54
+ with_async_user_confirmation = auth0_ai.with_async_user_confirmation(
55
+ scope="stock:trade",
56
+ audience=os.getenv("AUDIENCE"),
57
+ binding_message=lambda ticker, qty: f"Authorize the purchase of {qty} {ticker}",
58
+ user_id=lambda *_, **__: ensure_config().get("configurable", {}).get("user_id"),
59
+ # Optional:
60
+ # store=InMemoryStore()
61
+ )
62
+
63
+ def tool_function(ticker: str, qty: int) -> str:
64
+ credentials = get_ciba_credentials()
65
+ headers = {
66
+ "Authorization": f"{credentials["token_type"]} {credentials["access_token"]}",
67
+ # ...
68
+ }
69
+ # Call API
70
+
71
+ trade_tool = with_async_user_confirmation(
72
+ StructuredTool(
73
+ name="trade_tool",
74
+ description="Use this function to trade a stock",
75
+ func=trade_tool_function,
76
+ # ...
77
+ )
78
+ )
79
+ ```
80
+
81
+ 2. Handle interruptions properly. For example, if user is not enrolled to MFA, it will throw an interruption. See [Handling Interrupts](#handling-interrupts) section.
82
+
37
83
  ## Authorization for Tools
38
84
 
39
85
  The `FGAAuthorizer` can leverage Okta FGA to authorize tools executions. The `FGAAuthorizer.create` function can be used to create an authorizer that checks permissions before executing the tool.
@@ -43,19 +89,12 @@ Full example of [Authorization for Tools](https://github.com/auth0-lab/auth0-ai-
43
89
  1. Create an instance of FGA Authorizer:
44
90
 
45
91
  ```python
46
- from auth0_ai_langchain.fga.fga_authorizer import FGAAuthorizer, FGAAuthorizerOptions
92
+ from auth0_ai_langchain.fga import FGAAuthorizer
47
93
 
94
+ # If not provided, FGA settings will be read from env variables: `FGA_STORE_ID`, `FGA_CLIENT_ID`, `FGA_CLIENT_SECRET`, etc.
48
95
  fga = FGAAuthorizer.create()
49
96
  ```
50
97
 
51
- **Note**: Here, you can configure and specify your FGA credentials. By `default`, they are read from environment variables:
52
-
53
- ```sh
54
- FGA_STORE_ID="<fga-store-id>"
55
- FGA_CLIENT_ID="<fga-client-id>"
56
- FGA_CLIENT_SECRET="<fga-client-secret>"
57
- ```
58
-
59
98
  2. Define the FGA query (`build_query`) and, optionally, the `on_unauthorized` handler:
60
99
 
61
100
  ```python
@@ -73,10 +112,10 @@ async def build_fga_query(tool_input):
73
112
  def on_unauthorized(tool_input):
74
113
  return f"The user is not allowed to buy {tool_input["qty"]} shares of {tool_input["ticker"]}."
75
114
 
76
- use_fga = fga(FGAAuthorizerOptions(
115
+ use_fga = fga(
77
116
  build_query=build_fga_query,
78
117
  on_unauthorized=on_unauthorized,
79
- ))
118
+ )
80
119
  ```
81
120
 
82
121
  **Note**: The parameters given to the `build_query` and `on_unauthorized` functions are the same as those provided to the tool function.
@@ -102,7 +141,7 @@ buy_tool = StructuredTool(
102
141
 
103
142
  ## Calling APIs On User's Behalf
104
143
 
105
- The `Auth0AI.with_federated_connection` function exchanges user's refresh token taken from the runnable configuration (`config.configurable._credentials.refresh_token`) for a Federated Connection API token.
144
+ The `Auth0AI.with_federated_connection` function exchanges user's refresh token taken, by default, from the runnable configuration (`config.configurable._credentials.refresh_token`) for a Federated Connection API token.
106
145
 
107
146
  Full Example of [Calling APIs On User's Behalf](https://github.com/auth0-lab/auth0-ai-python/tree/main/examples/calling-apis/langchain-examples).
108
147
 
@@ -110,19 +149,23 @@ Full Example of [Calling APIs On User's Behalf](https://github.com/auth0-lab/aut
110
149
 
111
150
  ```python
112
151
  from auth0_ai_langchain.auth0_ai import Auth0AI
113
- from auth0_ai_langchain.federated_connections import get_access_token_for_connection
152
+ from auth0_ai_langchain.federated_connections import get_credentials_for_connection
114
153
  from langchain_core.tools import StructuredTool
115
154
 
155
+ # If not provided, Auth0 settings will be read from env variables: `AUTH0_DOMAIN`, `AUTH0_CLIENT_ID`, and `AUTH0_CLIENT_SECRET`
116
156
  auth0_ai = Auth0AI()
117
157
 
118
158
  with_google_calendar_access = auth0_ai.with_federated_connection(
119
159
  connection="google-oauth2",
120
- scopes=["https://www.googleapis.com/auth/calendar.freebusy"]
160
+ scopes=["https://www.googleapis.com/auth/calendar.freebusy"],
161
+ # Optional:
162
+ # refresh_token=lambda *_, **__: ensure_config().get("configurable", {}).get("_credentials", {}).get("refresh_token"),
163
+ # store=InMemoryStore(),
121
164
  )
122
165
 
123
166
  def tool_function(date: datetime):
124
- access_token = get_access_token_for_connection()
125
- # Call Google API
167
+ credentials = get_credentials_for_connection()
168
+ # Call Google API using credentials["access_token"]
126
169
 
127
170
  check_calendar_tool = with_google_calendar_access(
128
171
  StructuredTool(
@@ -154,7 +197,7 @@ workflow = (
154
197
  )
155
198
  ```
156
199
 
157
- 3. Handle interruptions properly. If the tool does not have access to user's Google Calendar, it will throw an interruption.
200
+ 3. Handle interruptions properly. For example, if the tool does not have access to user's Google Calendar, it will throw an interruption. See [Handling Interrupts](#handling-interrupts) section.
158
201
 
159
202
  ## RAG with FGA
160
203
 
@@ -184,7 +227,8 @@ vector_store = VectorStoreIndex.from_documents(documents)
184
227
  # Create a retriever:
185
228
  base_retriever = vector_store.as_retriever()
186
229
 
187
- # Create the FGA retriever wrapper:
230
+ # Create the FGA retriever wrapper.
231
+ # If not provided, FGA settings will be read from env variables: `FGA_STORE_ID`, `FGA_CLIENT_ID`, `FGA_CLIENT_SECRET`, etc.
188
232
  retriever = FGARetriever(
189
233
  base_retriever,
190
234
  build_query=lambda node: ClientCheckRequest(
@@ -206,6 +250,63 @@ response = query_engine.query("What is the forecast for ZEKO?")
206
250
  print(response)
207
251
  ```
208
252
 
253
+ ## Handling Interrupts
254
+
255
+ `Auth0AI` uses interrupts extensively and will never block a graph. Whenever an authorizer requires user interaction, the graph throws a `GraphInterrupt` exception with data that allows the client to resume the flow.
256
+
257
+ It is important to disable error handling in your tools node as follows:
258
+
259
+ ```python
260
+ .add_node(
261
+ "tools",
262
+ ToolNode(
263
+ [
264
+ # your authorizer-wrapped tools
265
+ ],
266
+ # Error handler should be disabled in order to trigger interruptions from within tools.
267
+ handle_tool_errors=False
268
+ )
269
+ )
270
+ ```
271
+
272
+ From the client side of the graph you get the interrupts:
273
+
274
+ ```python
275
+ from auth0_ai_langchain.utils.interrupt import get_auth0_interrupts
276
+
277
+ # Get the langgraph thread:
278
+ thread = await client.threads.get(thread_id)
279
+
280
+ # Filter the auth0 interrupts:
281
+ auth0_interrupts = get_auth0_interrupts(thread)
282
+ ```
283
+
284
+ Then you can resume the thread by doing this:
285
+
286
+ ```python
287
+ await client.runs.wait(thread_id, assistant_id)
288
+ ```
289
+
290
+ For the specific case of **CIBA (Client-Initiated Backchannel Authorization)** you might attach a `GraphResumer` instance that watches for interrupted threads in the `"Authorization Pending"` state and attempts to resume them automatically, respecting Auth0's polling interval.
291
+
292
+ ```python
293
+ import os
294
+ from auth0_ai_langchain.ciba import GraphResumer
295
+ from langgraph_sdk import get_client
296
+
297
+ resumer = GraphResumer(
298
+ lang_graph=get_client(url=os.getenv("LANGGRAPH_API_URL")),
299
+ # optionally, you can filter by a specific graph:
300
+ filters={"graph_id": "conditional-trade"},
301
+ )
302
+
303
+ resumer \
304
+ .on_resume(lambda thread: print(f"Attempting to resume thread {thread['thread_id']} from interruption {thread['interruption_id']}")) \
305
+ .on_error(lambda err: print(f"Error in GraphResumer: {str(err)}"))
306
+
307
+ resumer.start()
308
+ ```
309
+
209
310
  ---
210
311
 
211
312
  <p align="center">
@@ -12,6 +12,52 @@
12
12
  pip install auth0-ai-langchain
13
13
  ```
14
14
 
15
+ ## Async User Confirmation
16
+
17
+ `Auth0AI` uses CIBA (Client-Initiated Backchannel Authentication) to handle user confirmation asynchronously. This is useful when you need to confirm a user action before proceeding with a tool execution.
18
+
19
+ Full Example of [Async User Confirmation](https://github.com/auth0-lab/auth0-ai-python/tree/main/examples/async-user-confirmation/langchain-examples).
20
+
21
+ 1. Define a tool with the proper authorizer specifying a function to resolve the user id:
22
+
23
+ ```python
24
+ from auth0_ai_langchain.auth0_ai import Auth0AI
25
+ from auth0_ai_langchain.ciba import get_ciba_credentials
26
+ from langchain_core.runnables import ensure_config
27
+ from langchain_core.tools import StructuredTool
28
+
29
+ # If not provided, Auth0 settings will be read from env variables: `AUTH0_DOMAIN`, `AUTH0_CLIENT_ID`, and `AUTH0_CLIENT_SECRET`
30
+ auth0_ai = Auth0AI()
31
+
32
+ with_async_user_confirmation = auth0_ai.with_async_user_confirmation(
33
+ scope="stock:trade",
34
+ audience=os.getenv("AUDIENCE"),
35
+ binding_message=lambda ticker, qty: f"Authorize the purchase of {qty} {ticker}",
36
+ user_id=lambda *_, **__: ensure_config().get("configurable", {}).get("user_id"),
37
+ # Optional:
38
+ # store=InMemoryStore()
39
+ )
40
+
41
+ def tool_function(ticker: str, qty: int) -> str:
42
+ credentials = get_ciba_credentials()
43
+ headers = {
44
+ "Authorization": f"{credentials["token_type"]} {credentials["access_token"]}",
45
+ # ...
46
+ }
47
+ # Call API
48
+
49
+ trade_tool = with_async_user_confirmation(
50
+ StructuredTool(
51
+ name="trade_tool",
52
+ description="Use this function to trade a stock",
53
+ func=trade_tool_function,
54
+ # ...
55
+ )
56
+ )
57
+ ```
58
+
59
+ 2. Handle interruptions properly. For example, if user is not enrolled to MFA, it will throw an interruption. See [Handling Interrupts](#handling-interrupts) section.
60
+
15
61
  ## Authorization for Tools
16
62
 
17
63
  The `FGAAuthorizer` can leverage Okta FGA to authorize tools executions. The `FGAAuthorizer.create` function can be used to create an authorizer that checks permissions before executing the tool.
@@ -21,19 +67,12 @@ Full example of [Authorization for Tools](https://github.com/auth0-lab/auth0-ai-
21
67
  1. Create an instance of FGA Authorizer:
22
68
 
23
69
  ```python
24
- from auth0_ai_langchain.fga.fga_authorizer import FGAAuthorizer, FGAAuthorizerOptions
70
+ from auth0_ai_langchain.fga import FGAAuthorizer
25
71
 
72
+ # If not provided, FGA settings will be read from env variables: `FGA_STORE_ID`, `FGA_CLIENT_ID`, `FGA_CLIENT_SECRET`, etc.
26
73
  fga = FGAAuthorizer.create()
27
74
  ```
28
75
 
29
- **Note**: Here, you can configure and specify your FGA credentials. By `default`, they are read from environment variables:
30
-
31
- ```sh
32
- FGA_STORE_ID="<fga-store-id>"
33
- FGA_CLIENT_ID="<fga-client-id>"
34
- FGA_CLIENT_SECRET="<fga-client-secret>"
35
- ```
36
-
37
76
  2. Define the FGA query (`build_query`) and, optionally, the `on_unauthorized` handler:
38
77
 
39
78
  ```python
@@ -51,10 +90,10 @@ async def build_fga_query(tool_input):
51
90
  def on_unauthorized(tool_input):
52
91
  return f"The user is not allowed to buy {tool_input["qty"]} shares of {tool_input["ticker"]}."
53
92
 
54
- use_fga = fga(FGAAuthorizerOptions(
93
+ use_fga = fga(
55
94
  build_query=build_fga_query,
56
95
  on_unauthorized=on_unauthorized,
57
- ))
96
+ )
58
97
  ```
59
98
 
60
99
  **Note**: The parameters given to the `build_query` and `on_unauthorized` functions are the same as those provided to the tool function.
@@ -80,7 +119,7 @@ buy_tool = StructuredTool(
80
119
 
81
120
  ## Calling APIs On User's Behalf
82
121
 
83
- The `Auth0AI.with_federated_connection` function exchanges user's refresh token taken from the runnable configuration (`config.configurable._credentials.refresh_token`) for a Federated Connection API token.
122
+ The `Auth0AI.with_federated_connection` function exchanges user's refresh token taken, by default, from the runnable configuration (`config.configurable._credentials.refresh_token`) for a Federated Connection API token.
84
123
 
85
124
  Full Example of [Calling APIs On User's Behalf](https://github.com/auth0-lab/auth0-ai-python/tree/main/examples/calling-apis/langchain-examples).
86
125
 
@@ -88,19 +127,23 @@ Full Example of [Calling APIs On User's Behalf](https://github.com/auth0-lab/aut
88
127
 
89
128
  ```python
90
129
  from auth0_ai_langchain.auth0_ai import Auth0AI
91
- from auth0_ai_langchain.federated_connections import get_access_token_for_connection
130
+ from auth0_ai_langchain.federated_connections import get_credentials_for_connection
92
131
  from langchain_core.tools import StructuredTool
93
132
 
133
+ # If not provided, Auth0 settings will be read from env variables: `AUTH0_DOMAIN`, `AUTH0_CLIENT_ID`, and `AUTH0_CLIENT_SECRET`
94
134
  auth0_ai = Auth0AI()
95
135
 
96
136
  with_google_calendar_access = auth0_ai.with_federated_connection(
97
137
  connection="google-oauth2",
98
- scopes=["https://www.googleapis.com/auth/calendar.freebusy"]
138
+ scopes=["https://www.googleapis.com/auth/calendar.freebusy"],
139
+ # Optional:
140
+ # refresh_token=lambda *_, **__: ensure_config().get("configurable", {}).get("_credentials", {}).get("refresh_token"),
141
+ # store=InMemoryStore(),
99
142
  )
100
143
 
101
144
  def tool_function(date: datetime):
102
- access_token = get_access_token_for_connection()
103
- # Call Google API
145
+ credentials = get_credentials_for_connection()
146
+ # Call Google API using credentials["access_token"]
104
147
 
105
148
  check_calendar_tool = with_google_calendar_access(
106
149
  StructuredTool(
@@ -132,7 +175,7 @@ workflow = (
132
175
  )
133
176
  ```
134
177
 
135
- 3. Handle interruptions properly. If the tool does not have access to user's Google Calendar, it will throw an interruption.
178
+ 3. Handle interruptions properly. For example, if the tool does not have access to user's Google Calendar, it will throw an interruption. See [Handling Interrupts](#handling-interrupts) section.
136
179
 
137
180
  ## RAG with FGA
138
181
 
@@ -162,7 +205,8 @@ vector_store = VectorStoreIndex.from_documents(documents)
162
205
  # Create a retriever:
163
206
  base_retriever = vector_store.as_retriever()
164
207
 
165
- # Create the FGA retriever wrapper:
208
+ # Create the FGA retriever wrapper.
209
+ # If not provided, FGA settings will be read from env variables: `FGA_STORE_ID`, `FGA_CLIENT_ID`, `FGA_CLIENT_SECRET`, etc.
166
210
  retriever = FGARetriever(
167
211
  base_retriever,
168
212
  build_query=lambda node: ClientCheckRequest(
@@ -184,6 +228,63 @@ response = query_engine.query("What is the forecast for ZEKO?")
184
228
  print(response)
185
229
  ```
186
230
 
231
+ ## Handling Interrupts
232
+
233
+ `Auth0AI` uses interrupts extensively and will never block a graph. Whenever an authorizer requires user interaction, the graph throws a `GraphInterrupt` exception with data that allows the client to resume the flow.
234
+
235
+ It is important to disable error handling in your tools node as follows:
236
+
237
+ ```python
238
+ .add_node(
239
+ "tools",
240
+ ToolNode(
241
+ [
242
+ # your authorizer-wrapped tools
243
+ ],
244
+ # Error handler should be disabled in order to trigger interruptions from within tools.
245
+ handle_tool_errors=False
246
+ )
247
+ )
248
+ ```
249
+
250
+ From the client side of the graph you get the interrupts:
251
+
252
+ ```python
253
+ from auth0_ai_langchain.utils.interrupt import get_auth0_interrupts
254
+
255
+ # Get the langgraph thread:
256
+ thread = await client.threads.get(thread_id)
257
+
258
+ # Filter the auth0 interrupts:
259
+ auth0_interrupts = get_auth0_interrupts(thread)
260
+ ```
261
+
262
+ Then you can resume the thread by doing this:
263
+
264
+ ```python
265
+ await client.runs.wait(thread_id, assistant_id)
266
+ ```
267
+
268
+ For the specific case of **CIBA (Client-Initiated Backchannel Authorization)** you might attach a `GraphResumer` instance that watches for interrupted threads in the `"Authorization Pending"` state and attempts to resume them automatically, respecting Auth0's polling interval.
269
+
270
+ ```python
271
+ import os
272
+ from auth0_ai_langchain.ciba import GraphResumer
273
+ from langgraph_sdk import get_client
274
+
275
+ resumer = GraphResumer(
276
+ lang_graph=get_client(url=os.getenv("LANGGRAPH_API_URL")),
277
+ # optionally, you can filter by a specific graph:
278
+ filters={"graph_id": "conditional-trade"},
279
+ )
280
+
281
+ resumer \
282
+ .on_resume(lambda thread: print(f"Attempting to resume thread {thread['thread_id']} from interruption {thread['interruption_id']}")) \
283
+ .on_error(lambda err: print(f"Error in GraphResumer: {str(err)}"))
284
+
285
+ resumer.start()
286
+ ```
287
+
187
288
  ---
188
289
 
189
290
  <p align="center">
@@ -32,7 +32,7 @@ class FGARetriever(BaseRetriever):
32
32
  Args:
33
33
  retriever (BaseRetriever): The retriever used to fetch documents.
34
34
  build_query (Callable[[Document], ClientBatchCheckItem]): Function to convert documents into FGA queries.
35
- fga_configuration (Optional[ClientConfiguration]): Configuration for the OpenFGA client. If not provided, defaults to environment variables.
35
+ fga_configuration (ClientConfiguration, optional): Configuration for the OpenFGA client. If not provided, defaults to environment variables.
36
36
  """
37
37
  super().__init__()
38
38
  self._retriever = retriever
@@ -95,7 +95,7 @@ class FGARetriever(BaseRetriever):
95
95
 
96
96
  Args:
97
97
  query (str): The query for retrieving documents.
98
- run_manager (Optional[object]): Optional manager for tracking runs.
98
+ run_manager (object, optional): Optional manager for tracking runs.
99
99
 
100
100
  Returns:
101
101
  List[Document]: Filtered and relevant documents.
@@ -148,7 +148,7 @@ class FGARetriever(BaseRetriever):
148
148
 
149
149
  Args:
150
150
  query (str): The query for retrieving documents.
151
- run_manager (Optional[object]): Optional manager for tracking runs.
151
+ run_manager (object, optional): Optional manager for tracking runs.
152
152
 
153
153
  Returns:
154
154
  List[Document]: Filtered and relevant documents.
@@ -0,0 +1,112 @@
1
+ from typing import Callable, Optional
2
+ from langchain_core.tools import BaseTool
3
+ from auth0_ai.authorizers.ciba import CIBAAuthorizerParams
4
+ from auth0_ai.authorizers.federated_connection_authorizer import FederatedConnectionAuthorizerParams
5
+ from auth0_ai.authorizers.types import Auth0ClientParams
6
+ from auth0_ai_langchain.ciba.ciba_authorizer import CIBAAuthorizer
7
+ from auth0_ai_langchain.federated_connections.federated_connection_authorizer import FederatedConnectionAuthorizer
8
+
9
+
10
+ class Auth0AI:
11
+ """Provides decorators to secure LangChain tools using Auth0 authorization flows.
12
+ """
13
+
14
+ def __init__(self, auth0: Optional[Auth0ClientParams] = None):
15
+ """Initializes the Auth0AI instance.
16
+
17
+ Args:
18
+ auth0 (Optional[Auth0ClientParams]): Parameters for the Auth0 client.
19
+ If not provided, values will be automatically read from environment
20
+ variables: `AUTH0_DOMAIN`, `AUTH0_CLIENT_ID`, and `AUTH0_CLIENT_SECRET`.
21
+ """
22
+ self.auth0 = auth0
23
+
24
+ def with_async_user_confirmation(self, **params: CIBAAuthorizerParams) -> Callable[[BaseTool], BaseTool]:
25
+ """Protects a tool with the CIBA (Client-Initiated Backchannel Authentication) flow.
26
+
27
+ Requires user confirmation via a second device (e.g., phone)
28
+ before allowing the tool to execute.
29
+
30
+ Args:
31
+ **params: Parameters defined in `CIBAAuthorizerParams`.
32
+
33
+ Returns:
34
+ Callable[[BaseTool], BaseTool]: A decorator to wrap a LangChain tool.
35
+
36
+ Example:
37
+ ```python
38
+ import os
39
+ from auth0_ai_langchain.auth0_ai import Auth0AI
40
+ from auth0_ai_langchain.ciba import get_ciba_credentials
41
+ from langchain_core.runnables import ensure_config
42
+ from langchain_core.tools import StructuredTool
43
+
44
+ auth0_ai = Auth0AI()
45
+
46
+ with_async_user_confirmation = auth0_ai.with_async_user_confirmation(
47
+ scope="stock:trade",
48
+ audience=os.getenv("AUDIENCE"),
49
+ binding_message=lambda ticker, qty: f"Authorize the purchase of {qty} {ticker}",
50
+ user_id=lambda *_, **__: ensure_config().get("configurable", {}).get("user_id")
51
+ )
52
+
53
+ def tool_function(ticker: str, qty: int) -> str:
54
+ credentials = get_ciba_credentials()
55
+ headers = {
56
+ "Authorization": f"{credentials['token_type']} {credentials['access_token']}",
57
+ # ...
58
+ }
59
+ # Call API
60
+
61
+ trade_tool = with_async_user_confirmation(
62
+ StructuredTool(
63
+ name="trade_tool",
64
+ description="Use this function to trade a stock",
65
+ func=tool_function,
66
+ )
67
+ )
68
+ ```
69
+ """
70
+ authorizer = CIBAAuthorizer(CIBAAuthorizerParams(**params), self.auth0)
71
+ return authorizer.authorizer()
72
+
73
+ def with_federated_connection(self, **params: FederatedConnectionAuthorizerParams) -> Callable[[BaseTool], BaseTool]:
74
+ """Enables a tool to obtain an access token from a federated identity provider (e.g., Google, Azure AD).
75
+
76
+ The token can then be used within the tool to call third-party APIs on behalf of the user.
77
+
78
+ Args:
79
+ **params: Parameters defined in `FederatedConnectionAuthorizerParams`.
80
+
81
+ Returns:
82
+ Callable[[BaseTool], BaseTool]: A decorator to wrap a LangChain tool.
83
+
84
+ Example:
85
+ ```python
86
+ from auth0_ai_langchain.auth0_ai import Auth0AI
87
+ from auth0_ai_langchain.federated_connections import get_credentials_for_connection
88
+ from langchain_core.tools import StructuredTool
89
+ from datetime import datetime
90
+
91
+ auth0_ai = Auth0AI()
92
+
93
+ with_google_calendar_access = auth0_ai.with_federated_connection(
94
+ connection="google-oauth2",
95
+ scopes=["https://www.googleapis.com/auth/calendar.freebusy"]
96
+ )
97
+
98
+ def tool_function(date: datetime):
99
+ credentials = get_credentials_for_connection()
100
+ # Call Google API using credentials["access_token"]
101
+
102
+ check_calendar_tool = with_google_calendar_access(
103
+ StructuredTool(
104
+ name="check_user_calendar",
105
+ description="Use this function to check if the user is available on a certain date and time",
106
+ func=tool_function,
107
+ )
108
+ )
109
+ ```
110
+ """
111
+ authorizer = FederatedConnectionAuthorizer(FederatedConnectionAuthorizerParams(**params), self.auth0)
112
+ return authorizer.authorizer()
@@ -0,0 +1,3 @@
1
+ from auth0_ai.authorizers.ciba.ciba_authorizer_base import get_ciba_credentials as get_ciba_credentials
2
+ from auth0_ai_langchain.ciba.ciba_authorizer import CIBAAuthorizer as CIBAAuthorizer
3
+ from auth0_ai_langchain.ciba.graph_resumer import GraphResumer as GraphResumer
@@ -0,0 +1,17 @@
1
+ from abc import ABC
2
+ from typing import Union
3
+ from auth0_ai.authorizers.ciba import CIBAAuthorizerBase
4
+ from auth0_ai.interrupts.ciba_interrupts import AuthorizationPendingInterrupt, AuthorizationPollingInterrupt
5
+ from auth0_ai_langchain.utils.interrupt import to_graph_interrupt
6
+ from auth0_ai_langchain.utils.tool_wrapper import tool_wrapper
7
+ from langchain_core.tools import BaseTool
8
+
9
+ class CIBAAuthorizer(CIBAAuthorizerBase, ABC):
10
+ def _handle_authorization_interrupts(self, err: Union[AuthorizationPendingInterrupt, AuthorizationPollingInterrupt]) -> None:
11
+ raise to_graph_interrupt(err)
12
+
13
+ def authorizer(self):
14
+ def wrap_tool(tool: BaseTool) -> BaseTool:
15
+ return tool_wrapper(tool, self.protect)
16
+
17
+ return wrap_tool