auth0-ai-langchain 0.1.2__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.1.2 → auth0_ai_langchain-1.0.0b1}/PKG-INFO +127 -27
  2. {auth0_ai_langchain-0.1.2 → auth0_ai_langchain-1.0.0b1}/README.md +120 -20
  3. {auth0_ai_langchain-0.1.2 → 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.1.2 → auth0_ai_langchain-1.0.0b1}/pyproject.toml +8 -9
  14. auth0_ai_langchain-0.1.2/auth0_ai_langchain/auth0_ai.py +0 -43
  15. auth0_ai_langchain-0.1.2/auth0_ai_langchain/ciba/__init__.py +0 -0
  16. auth0_ai_langchain-0.1.2/auth0_ai_langchain/ciba/ciba_graph/ciba_graph.py +0 -109
  17. auth0_ai_langchain-0.1.2/auth0_ai_langchain/ciba/ciba_graph/initialize_ciba.py +0 -91
  18. auth0_ai_langchain-0.1.2/auth0_ai_langchain/ciba/ciba_graph/initialize_hitl.py +0 -50
  19. auth0_ai_langchain-0.1.2/auth0_ai_langchain/ciba/ciba_graph/types.py +0 -115
  20. auth0_ai_langchain-0.1.2/auth0_ai_langchain/ciba/ciba_graph/utils.py +0 -17
  21. auth0_ai_langchain-0.1.2/auth0_ai_langchain/ciba/ciba_poller_graph.py +0 -105
  22. auth0_ai_langchain-0.1.2/auth0_ai_langchain/ciba/types.py +0 -8
  23. auth0_ai_langchain-0.1.2/auth0_ai_langchain/federated_connections/__init__.py +0 -3
  24. auth0_ai_langchain-0.1.2/auth0_ai_langchain/federated_connections/federated_connection_authorizer.py +0 -52
  25. auth0_ai_langchain-0.1.2/auth0_ai_langchain/fga/fga_authorizer.py +0 -3
  26. auth0_ai_langchain-0.1.2/auth0_ai_langchain/utils/interrupt.py +0 -13
  27. {auth0_ai_langchain-0.1.2 → auth0_ai_langchain-1.0.0b1}/LICENSE +0 -0
  28. {auth0_ai_langchain-0.1.2 → 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.1.2
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.1.1,<0.2.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
 
@@ -28,13 +28,58 @@ Description-Content-Type: text/markdown
28
28
 
29
29
  ## Installation
30
30
 
31
- > [!WARNING]
32
- > `auth0-ai-langchain` is currently under development and it is not intended to be used in production, and therefore has no official support.
31
+ > ⚠️ **WARNING**: `auth0-ai-langchain` is currently under development and it is not intended to be used in production, and therefore has no official support.
33
32
 
34
33
  ```bash
35
34
  pip install auth0-ai-langchain
36
35
  ```
37
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
+
38
83
  ## Authorization for Tools
39
84
 
40
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.
@@ -44,19 +89,12 @@ Full example of [Authorization for Tools](https://github.com/auth0-lab/auth0-ai-
44
89
  1. Create an instance of FGA Authorizer:
45
90
 
46
91
  ```python
47
- from auth0_ai_langchain.fga.fga_authorizer import FGAAuthorizer, FGAAuthorizerOptions
92
+ from auth0_ai_langchain.fga import FGAAuthorizer
48
93
 
94
+ # If not provided, FGA settings will be read from env variables: `FGA_STORE_ID`, `FGA_CLIENT_ID`, `FGA_CLIENT_SECRET`, etc.
49
95
  fga = FGAAuthorizer.create()
50
96
  ```
51
97
 
52
- **Note**: Here, you can configure and specify your FGA credentials. By `default`, they are read from environment variables:
53
-
54
- ```sh
55
- FGA_STORE_ID="<fga-store-id>"
56
- FGA_CLIENT_ID="<fga-client-id>"
57
- FGA_CLIENT_SECRET="<fga-client-secret>"
58
- ```
59
-
60
98
  2. Define the FGA query (`build_query`) and, optionally, the `on_unauthorized` handler:
61
99
 
62
100
  ```python
@@ -74,10 +112,10 @@ async def build_fga_query(tool_input):
74
112
  def on_unauthorized(tool_input):
75
113
  return f"The user is not allowed to buy {tool_input["qty"]} shares of {tool_input["ticker"]}."
76
114
 
77
- use_fga = fga(FGAAuthorizerOptions(
115
+ use_fga = fga(
78
116
  build_query=build_fga_query,
79
117
  on_unauthorized=on_unauthorized,
80
- ))
118
+ )
81
119
  ```
82
120
 
83
121
  **Note**: The parameters given to the `build_query` and `on_unauthorized` functions are the same as those provided to the tool function.
@@ -103,7 +141,7 @@ buy_tool = StructuredTool(
103
141
 
104
142
  ## Calling APIs On User's Behalf
105
143
 
106
- 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.
107
145
 
108
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).
109
147
 
@@ -111,19 +149,23 @@ Full Example of [Calling APIs On User's Behalf](https://github.com/auth0-lab/aut
111
149
 
112
150
  ```python
113
151
  from auth0_ai_langchain.auth0_ai import Auth0AI
114
- from auth0_ai_langchain.federated_connections import get_access_token_for_connection
152
+ from auth0_ai_langchain.federated_connections import get_credentials_for_connection
115
153
  from langchain_core.tools import StructuredTool
116
154
 
155
+ # If not provided, Auth0 settings will be read from env variables: `AUTH0_DOMAIN`, `AUTH0_CLIENT_ID`, and `AUTH0_CLIENT_SECRET`
117
156
  auth0_ai = Auth0AI()
118
157
 
119
158
  with_google_calendar_access = auth0_ai.with_federated_connection(
120
159
  connection="google-oauth2",
121
- 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(),
122
164
  )
123
165
 
124
166
  def tool_function(date: datetime):
125
- access_token = get_access_token_for_connection()
126
- # Call Google API
167
+ credentials = get_credentials_for_connection()
168
+ # Call Google API using credentials["access_token"]
127
169
 
128
170
  check_calendar_tool = with_google_calendar_access(
129
171
  StructuredTool(
@@ -155,7 +197,7 @@ workflow = (
155
197
  )
156
198
  ```
157
199
 
158
- 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.
159
201
 
160
202
  ## RAG with FGA
161
203
 
@@ -185,7 +227,8 @@ vector_store = VectorStoreIndex.from_documents(documents)
185
227
  # Create a retriever:
186
228
  base_retriever = vector_store.as_retriever()
187
229
 
188
- # 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.
189
232
  retriever = FGARetriever(
190
233
  base_retriever,
191
234
  build_query=lambda node: ClientCheckRequest(
@@ -207,6 +250,63 @@ response = query_engine.query("What is the forecast for ZEKO?")
207
250
  print(response)
208
251
  ```
209
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
+
210
310
  ---
211
311
 
212
312
  <p align="center">
@@ -6,13 +6,58 @@
6
6
 
7
7
  ## Installation
8
8
 
9
- > [!WARNING]
10
- > `auth0-ai-langchain` is currently under development and it is not intended to be used in production, and therefore has no official support.
9
+ > ⚠️ **WARNING**: `auth0-ai-langchain` is currently under development and it is not intended to be used in production, and therefore has no official support.
11
10
 
12
11
  ```bash
13
12
  pip install auth0-ai-langchain
14
13
  ```
15
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
+
16
61
  ## Authorization for Tools
17
62
 
18
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.
@@ -22,19 +67,12 @@ Full example of [Authorization for Tools](https://github.com/auth0-lab/auth0-ai-
22
67
  1. Create an instance of FGA Authorizer:
23
68
 
24
69
  ```python
25
- from auth0_ai_langchain.fga.fga_authorizer import FGAAuthorizer, FGAAuthorizerOptions
70
+ from auth0_ai_langchain.fga import FGAAuthorizer
26
71
 
72
+ # If not provided, FGA settings will be read from env variables: `FGA_STORE_ID`, `FGA_CLIENT_ID`, `FGA_CLIENT_SECRET`, etc.
27
73
  fga = FGAAuthorizer.create()
28
74
  ```
29
75
 
30
- **Note**: Here, you can configure and specify your FGA credentials. By `default`, they are read from environment variables:
31
-
32
- ```sh
33
- FGA_STORE_ID="<fga-store-id>"
34
- FGA_CLIENT_ID="<fga-client-id>"
35
- FGA_CLIENT_SECRET="<fga-client-secret>"
36
- ```
37
-
38
76
  2. Define the FGA query (`build_query`) and, optionally, the `on_unauthorized` handler:
39
77
 
40
78
  ```python
@@ -52,10 +90,10 @@ async def build_fga_query(tool_input):
52
90
  def on_unauthorized(tool_input):
53
91
  return f"The user is not allowed to buy {tool_input["qty"]} shares of {tool_input["ticker"]}."
54
92
 
55
- use_fga = fga(FGAAuthorizerOptions(
93
+ use_fga = fga(
56
94
  build_query=build_fga_query,
57
95
  on_unauthorized=on_unauthorized,
58
- ))
96
+ )
59
97
  ```
60
98
 
61
99
  **Note**: The parameters given to the `build_query` and `on_unauthorized` functions are the same as those provided to the tool function.
@@ -81,7 +119,7 @@ buy_tool = StructuredTool(
81
119
 
82
120
  ## Calling APIs On User's Behalf
83
121
 
84
- 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.
85
123
 
86
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).
87
125
 
@@ -89,19 +127,23 @@ Full Example of [Calling APIs On User's Behalf](https://github.com/auth0-lab/aut
89
127
 
90
128
  ```python
91
129
  from auth0_ai_langchain.auth0_ai import Auth0AI
92
- from auth0_ai_langchain.federated_connections import get_access_token_for_connection
130
+ from auth0_ai_langchain.federated_connections import get_credentials_for_connection
93
131
  from langchain_core.tools import StructuredTool
94
132
 
133
+ # If not provided, Auth0 settings will be read from env variables: `AUTH0_DOMAIN`, `AUTH0_CLIENT_ID`, and `AUTH0_CLIENT_SECRET`
95
134
  auth0_ai = Auth0AI()
96
135
 
97
136
  with_google_calendar_access = auth0_ai.with_federated_connection(
98
137
  connection="google-oauth2",
99
- 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(),
100
142
  )
101
143
 
102
144
  def tool_function(date: datetime):
103
- access_token = get_access_token_for_connection()
104
- # Call Google API
145
+ credentials = get_credentials_for_connection()
146
+ # Call Google API using credentials["access_token"]
105
147
 
106
148
  check_calendar_tool = with_google_calendar_access(
107
149
  StructuredTool(
@@ -133,7 +175,7 @@ workflow = (
133
175
  )
134
176
  ```
135
177
 
136
- 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.
137
179
 
138
180
  ## RAG with FGA
139
181
 
@@ -163,7 +205,8 @@ vector_store = VectorStoreIndex.from_documents(documents)
163
205
  # Create a retriever:
164
206
  base_retriever = vector_store.as_retriever()
165
207
 
166
- # 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.
167
210
  retriever = FGARetriever(
168
211
  base_retriever,
169
212
  build_query=lambda node: ClientCheckRequest(
@@ -185,6 +228,63 @@ response = query_engine.query("What is the forecast for ZEKO?")
185
228
  print(response)
186
229
  ```
187
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
+
188
288
  ---
189
289
 
190
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