langgraph-api 0.2.134__tar.gz → 0.2.137__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 langgraph-api might be problematic. Click here for more details.

Files changed (120) hide show
  1. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/PKG-INFO +1 -1
  2. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/benchmark/Makefile +8 -0
  3. langgraph_api-0.2.137/benchmark/clean.js +87 -0
  4. langgraph_api-0.2.137/benchmark/update-revision.js +148 -0
  5. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/healthcheck.py +1 -0
  6. langgraph_api-0.2.137/langgraph_api/__init__.py +1 -0
  7. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/api/assistants.py +10 -9
  8. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/api/runs.py +10 -6
  9. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/api/store.py +1 -0
  10. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/asyncio.py +10 -21
  11. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/auth/custom.py +8 -5
  12. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/auth/langsmith/backend.py +4 -3
  13. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/auth/langsmith/client.py +7 -13
  14. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/config.py +7 -4
  15. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/cron_scheduler.py +5 -6
  16. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/http.py +3 -7
  17. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/js/remote.py +28 -27
  18. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/js/sse.py +2 -3
  19. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/js/ui.py +6 -5
  20. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/logging.py +9 -0
  21. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/models/run.py +4 -4
  22. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/route.py +2 -1
  23. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/serde.py +1 -2
  24. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/store.py +1 -0
  25. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/stream.py +20 -11
  26. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/worker.py +4 -3
  27. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/pyproject.toml +9 -1
  28. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/uv.lock +6 -6
  29. langgraph_api-0.2.134/langgraph_api/__init__.py +0 -1
  30. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/.gitignore +0 -0
  31. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/LICENSE +0 -0
  32. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/Makefile +0 -0
  33. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/README.md +0 -0
  34. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/benchmark/.gitignore +0 -0
  35. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/benchmark/README.md +0 -0
  36. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/benchmark/burst.js +0 -0
  37. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/benchmark/graphs.js +0 -0
  38. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/benchmark/package.json +0 -0
  39. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/benchmark/ramp.js +0 -0
  40. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/benchmark/weather.js +0 -0
  41. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/constraints.txt +0 -0
  42. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/forbidden.txt +0 -0
  43. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/api/__init__.py +0 -0
  44. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/api/mcp.py +0 -0
  45. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/api/meta.py +0 -0
  46. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/api/openapi.py +0 -0
  47. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/api/threads.py +0 -0
  48. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/api/ui.py +0 -0
  49. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/asgi_transport.py +0 -0
  50. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/auth/__init__.py +0 -0
  51. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/auth/langsmith/__init__.py +0 -0
  52. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/auth/middleware.py +0 -0
  53. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/auth/noop.py +0 -0
  54. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/auth/studio_user.py +0 -0
  55. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/cli.py +0 -0
  56. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/command.py +0 -0
  57. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/errors.py +0 -0
  58. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/executor_entrypoint.py +0 -0
  59. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/feature_flags.py +0 -0
  60. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/graph.py +0 -0
  61. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/http_metrics.py +0 -0
  62. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/js/.gitignore +0 -0
  63. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/js/.prettierrc +0 -0
  64. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/js/__init__.py +0 -0
  65. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/js/base.py +0 -0
  66. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/js/build.mts +0 -0
  67. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/js/client.http.mts +0 -0
  68. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/js/client.mts +0 -0
  69. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/js/errors.py +0 -0
  70. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/js/global.d.ts +0 -0
  71. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/js/package.json +0 -0
  72. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/js/schema.py +0 -0
  73. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/js/src/graph.mts +0 -0
  74. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/js/src/load.hooks.mjs +0 -0
  75. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/js/src/preload.mjs +0 -0
  76. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/js/src/utils/files.mts +0 -0
  77. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/js/src/utils/importMap.mts +0 -0
  78. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/js/src/utils/pythonSchemas.mts +0 -0
  79. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/js/src/utils/serde.mts +0 -0
  80. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/js/traceblock.mts +0 -0
  81. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/js/tsconfig.json +0 -0
  82. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/js/yarn.lock +0 -0
  83. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/metadata.py +0 -0
  84. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/middleware/__init__.py +0 -0
  85. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/middleware/http_logger.py +0 -0
  86. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/middleware/private_network.py +0 -0
  87. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/middleware/request_id.py +0 -0
  88. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/models/__init__.py +0 -0
  89. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/patch.py +0 -0
  90. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/queue_entrypoint.py +0 -0
  91. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/schema.py +0 -0
  92. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/server.py +0 -0
  93. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/sse.py +0 -0
  94. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/state.py +0 -0
  95. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/thread_ttl.py +0 -0
  96. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/traceblock.py +0 -0
  97. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/tunneling/cloudflare.py +0 -0
  98. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/utils/__init__.py +0 -0
  99. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/utils/cache.py +0 -0
  100. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/utils/config.py +0 -0
  101. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/utils/future.py +0 -0
  102. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/utils/headers.py +0 -0
  103. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/utils/uuids.py +0 -0
  104. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/utils.py +0 -0
  105. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/validation.py +0 -0
  106. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_api/webhook.py +0 -0
  107. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_license/__init__.py +0 -0
  108. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_license/validation.py +0 -0
  109. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_runtime/__init__.py +0 -0
  110. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_runtime/checkpoint.py +0 -0
  111. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_runtime/database.py +0 -0
  112. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_runtime/lifespan.py +0 -0
  113. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_runtime/metrics.py +0 -0
  114. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_runtime/ops.py +0 -0
  115. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_runtime/queue.py +0 -0
  116. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_runtime/retry.py +0 -0
  117. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/langgraph_runtime/store.py +0 -0
  118. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/logging.json +0 -0
  119. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/openapi.json +0 -0
  120. {langgraph_api-0.2.134 → langgraph_api-0.2.137}/scripts/create_license.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langgraph-api
3
- Version: 0.2.134
3
+ Version: 0.2.137
4
4
  Author-email: Nuno Campos <nuno@langchain.dev>, Will Fu-Hinthorn <will@langchain.dev>
5
5
  License: Elastic-2.0
6
6
  License-File: LICENSE
@@ -1,14 +1,22 @@
1
1
  # Benchmark commands
2
2
  benchmark-burst:
3
+ make benchmark-reset
3
4
  k6 run burst.js
4
5
 
5
6
  benchmark-ramp:
7
+ make benchmark-reset
6
8
  k6 run --out json=raw_data_$(shell date +%Y-%m-%dT%H-%M-%S).json ramp.js
7
9
 
8
10
  benchmark-charts:
9
11
  npm install
10
12
  node graphs.js $(shell ls -t raw_data_*.json | head -1) true
11
13
 
14
+ benchmark-reset:
15
+ node clean.js
16
+
17
+ benchmark-new-revision:
18
+ node update-revision.js
19
+
12
20
  benchmark-clean:
13
21
  rm -f results_*.json summary_*.json raw_data_*.json *_chart_*.png
14
22
 
@@ -0,0 +1,87 @@
1
+ /*
2
+ * Delete all threads and runs from the last benchmark run for consistent tests
3
+ * The default benchmark server has a thread TTL of one hour that should clean things up too so this doesn't run too long.
4
+ */
5
+
6
+ // URL of your LangGraph server
7
+ const BASE_URL = process.env.BASE_URL || 'http://localhost:9123';
8
+ // LangSmith API key only needed with a custom server endpoint
9
+ const LANGSMITH_API_KEY = process.env.LANGSMITH_API_KEY;
10
+
11
+ async function clean() {
12
+ const headers = { 'Content-Type': 'application/json' };
13
+ if (LANGSMITH_API_KEY) {
14
+ headers['x-api-key'] = LANGSMITH_API_KEY;
15
+ }
16
+
17
+ const searchUrl = `${BASE_URL}/threads/search`;
18
+ let totalDeleted = 0;
19
+
20
+ try {
21
+ console.log('Starting thread cleanup...');
22
+
23
+ while (true) {
24
+ try {
25
+ // Get the next page of threads
26
+ console.log('Searching for threads...');
27
+ const searchResponse = await fetch(searchUrl, {
28
+ method: 'POST',
29
+ headers,
30
+ body: JSON.stringify({
31
+ limit: 1000
32
+ })
33
+ });
34
+
35
+ if (!searchResponse.ok) {
36
+ throw new Error(`Search request failed: ${searchResponse.status} ${searchResponse.statusText}`);
37
+ }
38
+
39
+ const threads = await searchResponse.json();
40
+
41
+ // If no threads found, we're done
42
+ if (!threads || threads.length === 0) {
43
+ console.log('No more threads found.');
44
+ break;
45
+ }
46
+
47
+ console.log(`Found ${threads.length} threads to delete`);
48
+
49
+ // Delete each thread
50
+ for (const thread of threads) {
51
+ try {
52
+ const deleteUrl = `${BASE_URL}/threads/${thread.thread_id}`;
53
+ const deleteResponse = await fetch(deleteUrl, {
54
+ method: 'DELETE',
55
+ headers
56
+ });
57
+
58
+ if (!deleteResponse.ok) {
59
+ console.error(`Failed to delete thread ${thread.thread_id}: ${deleteResponse.status} ${deleteResponse.statusText}`);
60
+ } else {
61
+ totalDeleted++;
62
+ }
63
+ } catch (deleteError) {
64
+ console.error(`Error deleting thread ${thread.thread_id}:`, deleteError.message);
65
+ }
66
+ }
67
+
68
+ console.log(`Deleted ${threads.length} threads in this batch`);
69
+
70
+ } catch (batchError) {
71
+ console.error('Error in batch processing:', batchError.message);
72
+ break;
73
+ }
74
+ }
75
+
76
+ console.log(`Cleanup completed. Total threads deleted: ${totalDeleted}`);
77
+
78
+ } catch (error) {
79
+ console.error('Fatal error during cleanup:', error.message);
80
+ process.exit(1);
81
+ }
82
+ }
83
+
84
+ clean().catch(error => {
85
+ console.error('Unhandled error:', error.message);
86
+ process.exit(1);
87
+ });
@@ -0,0 +1,148 @@
1
+ /*
2
+ * Trigger a new revision deployment and wait for it to complete
3
+ * This ensures the benchmark instance is running the latest code
4
+ */
5
+
6
+ // LangSmith API endpoints and credentials
7
+ const DEPLOYMENT_ID = process.env.DEPLOYMENT_ID || 'a23f03ff-6d4d-4efd-8149-bb5a7f3b95cf'; // jdr-benchmark deployment id
8
+ const LANGSMITH_API_KEY = process.env.LANGSMITH_API_KEY;
9
+ const API_BASE = 'https://api.host.langchain.com/v1';
10
+
11
+ // Deployment configuration
12
+ const REVISION_CONFIG = {
13
+ repo_path: "langgraph.json",
14
+ env_vars: [
15
+ {
16
+ name: "N_JOBS_PER_WORKER",
17
+ value: "100",
18
+ value_from: "secret",
19
+ type: "secret"
20
+ }
21
+ ],
22
+ shareable: false,
23
+ container_spec: {
24
+ cpu: null,
25
+ memory_mb: null,
26
+ min_scale: 10,
27
+ max_scale: null
28
+ }
29
+ };
30
+
31
+ // Expected deployment statuses in order
32
+ const EXPECTED_STATUSES = ['CREATED', 'AWAITING_BUILD', 'BUILDING', 'AWAITING_DEPLOY', 'DEPLOYING', 'DEPLOYED', 'QUEUED'];
33
+ const FINAL_STATUS = 'DEPLOYED';
34
+ const POLL_INTERVAL = 10000; // 10 seconds
35
+ const MAX_WAIT_TIME = 30 * 60 * 1000; // 30 minutes
36
+
37
+ async function updateRevision() {
38
+ if (!LANGSMITH_API_KEY) {
39
+ console.error('LANGSMITH_API_KEY environment variable is required');
40
+ process.exit(1);
41
+ }
42
+
43
+ const headers = {
44
+ 'Content-Type': 'application/json',
45
+ 'x-api-key': LANGSMITH_API_KEY
46
+ };
47
+
48
+ const createUrl = `${API_BASE}/projects/${DEPLOYMENT_ID}/revisions`;
49
+ const pollUrl = `${API_BASE}/projects/${DEPLOYMENT_ID}/revisions?limit=1&offset=0`;
50
+
51
+ try {
52
+ console.log('Triggering new revision deployment...');
53
+
54
+ // Step 1: Create new revision
55
+ const createResponse = await fetch(createUrl, {
56
+ method: 'POST',
57
+ headers,
58
+ body: JSON.stringify(REVISION_CONFIG)
59
+ });
60
+
61
+ if (createResponse.status === 409) {
62
+ console.log('⚠️ A new revision is already in progress. Continuing to poll existing deployment...');
63
+ } else if (!createResponse.ok) {
64
+ throw new Error(`Failed to create revision: ${createResponse.status} ${createResponse.statusText}`);
65
+ } else {
66
+ const newRevision = await createResponse.json();
67
+ const revisionId = newRevision.resource.latest_revision.hosted_langserve_revision_id;
68
+ console.log(`✓ New revision created: ${revisionId}`);
69
+ console.log(`Initial status: ${newRevision.status}`);
70
+ }
71
+
72
+ // Step 2: Poll until deployment is complete
73
+ console.log('\nPolling deployment status...');
74
+ const startTime = Date.now();
75
+ let lastStatus = null;
76
+
77
+ while (true) {
78
+ let pollResponse;
79
+ try {
80
+ pollResponse = await fetch(pollUrl, {
81
+ method: 'GET',
82
+ headers
83
+ });
84
+
85
+ if (!pollResponse.ok) {
86
+ throw new Error(`Failed to poll revisions: ${pollResponse.status} ${pollResponse.statusText}`);
87
+ }
88
+ } catch (pollError) {
89
+ console.error('Error calling poll endpoint (will retry):', pollError.message);
90
+ // Wait before next poll and continue
91
+ await new Promise(resolve => setTimeout(resolve, POLL_INTERVAL));
92
+ continue;
93
+ }
94
+
95
+ // Parse response and handle deployment logic (these errors should still be thrown)
96
+ const revisions = await pollResponse.json();
97
+
98
+ if (!revisions || revisions.length === 0) {
99
+ throw new Error('No revisions found');
100
+ }
101
+
102
+ const latestRevision = revisions[0];
103
+ const currentStatus = latestRevision.status;
104
+
105
+ // Log status changes
106
+ if (currentStatus !== lastStatus) {
107
+ const timestamp = new Date().toISOString();
108
+ console.log(`[${timestamp}] Status: ${currentStatus}`);
109
+
110
+ if (latestRevision.status_message) {
111
+ console.log(` Message: ${latestRevision.status_message}`);
112
+ }
113
+
114
+ lastStatus = currentStatus;
115
+ }
116
+
117
+ // Check if deployment is complete
118
+ if (currentStatus === FINAL_STATUS) {
119
+ console.log(`\n✓ Deployment completed successfully!`);
120
+ console.log(`Revision ID: ${latestRevision.id}`);
121
+ console.log(`Total time: ${Math.round((Date.now() - startTime) / 1000)}s`);
122
+ break;
123
+ }
124
+
125
+ // Check for failure statuses
126
+ if (!EXPECTED_STATUSES.includes(currentStatus)) {
127
+ throw new Error(`Deployment failed with status: ${currentStatus}${latestRevision.status_message ? ` - ${latestRevision.status_message}` : ''}`);
128
+ }
129
+
130
+ // Check timeout
131
+ if (Date.now() - startTime > MAX_WAIT_TIME) {
132
+ throw new Error(`Deployment timeout after ${MAX_WAIT_TIME / 60000} minutes. Last status: ${currentStatus}`);
133
+ }
134
+
135
+ // Wait before next poll
136
+ await new Promise(resolve => setTimeout(resolve, POLL_INTERVAL));
137
+ }
138
+
139
+ } catch (error) {
140
+ console.error('Fatal error during revision update:', error.message);
141
+ process.exit(1);
142
+ }
143
+ }
144
+
145
+ updateRevision().catch(error => {
146
+ console.error('Unhandled error:', error.message);
147
+ process.exit(1);
148
+ });
@@ -3,6 +3,7 @@ import os
3
3
  import urllib.request
4
4
 
5
5
  prefix = ""
6
+ mount_prefix = None
6
7
  # Override prefix if it's set in the http config
7
8
  if (http := os.environ.get("LANGGRAPH_HTTP")) and (
8
9
  mount_prefix := json.loads(http).get("mount_prefix")
@@ -0,0 +1 @@
1
+ __version__ = "0.2.137"
@@ -82,9 +82,12 @@ def _get_configurable_jsonschema(graph: Pregel) -> dict:
82
82
  json_schema["properties"].pop(key, None)
83
83
  # The type name of the configurable type is not preserved.
84
84
  # We'll add it back to the schema if we can.
85
- if hasattr(graph, "config_type") and graph.config_type is not None:
86
- if hasattr(graph.config_type, "__name__"):
87
- json_schema["title"] = graph.config_type.__name__
85
+ if (
86
+ hasattr(graph, "config_type")
87
+ and graph.config_type is not None
88
+ and hasattr(graph.config_type, "__name__")
89
+ ):
90
+ json_schema["title"] = graph.config_type.__name__
88
91
  return json_schema
89
92
  # If the schema does not have a configurable field, return an empty schema.
90
93
  return {}
@@ -263,18 +266,16 @@ async def get_assistant_graph(
263
266
  drawable_graph = await graph.fetch_graph(xray=xray)
264
267
  json_graph = drawable_graph.to_json()
265
268
  for node in json_graph.get("nodes", []):
266
- if data := node.get("data"):
267
- if isinstance(data, dict):
268
- data.pop("id", None)
269
+ if (data := node.get("data")) and isinstance(data, dict):
270
+ data.pop("id", None)
269
271
  return ApiResponse(json_graph)
270
272
 
271
273
  try:
272
274
  drawable_graph = await graph.aget_graph(xray=xray)
273
275
  json_graph = drawable_graph.to_json()
274
276
  for node in json_graph.get("nodes", []):
275
- if data := node.get("data"):
276
- if isinstance(data, dict):
277
- data.pop("id", None)
277
+ if (data := node.get("data")) and isinstance(data, dict):
278
+ data.pop("id", None)
278
279
  return ApiResponse(json_graph)
279
280
  except NotImplementedError:
280
281
  raise HTTPException(
@@ -212,9 +212,11 @@ async def wait_run(request: ApiRequest):
212
212
  )
213
213
  ) as stream:
214
214
  async for mode, chunk, _ in stream:
215
- if mode == b"values":
216
- vchunk = chunk
217
- elif mode == b"updates" and b"__interrupt__" in chunk:
215
+ if (
216
+ mode == b"values"
217
+ or mode == b"updates"
218
+ and b"__interrupt__" in chunk
219
+ ):
218
220
  vchunk = chunk
219
221
  elif mode == b"error":
220
222
  vchunk = orjson.dumps({"__error__": orjson.Fragment(chunk)})
@@ -295,9 +297,11 @@ async def wait_run_stateless(request: ApiRequest):
295
297
  )
296
298
  ) as stream:
297
299
  async for mode, chunk, _ in stream:
298
- if mode == b"values":
299
- vchunk = chunk
300
- elif mode == b"updates" and b"__interrupt__" in chunk:
300
+ if (
301
+ mode == b"values"
302
+ or mode == b"updates"
303
+ and b"__interrupt__" in chunk
304
+ ):
301
305
  vchunk = chunk
302
306
  elif mode == b"error":
303
307
  vchunk = orjson.dumps({"__error__": orjson.Fragment(chunk)})
@@ -134,6 +134,7 @@ async def list_namespaces(request: ApiRequest):
134
134
  payload = await request.json(StoreListNamespacesRequest)
135
135
  prefix = tuple(payload["prefix"]) if payload.get("prefix") else None
136
136
  suffix = tuple(payload["suffix"]) if payload.get("suffix") else None
137
+ err = None
137
138
  if prefix and (err := _validate_namespace(prefix)):
138
139
  return err
139
140
  if suffix and (err := _validate_namespace(suffix)):
@@ -1,7 +1,7 @@
1
1
  import asyncio
2
2
  import concurrent.futures
3
3
  from collections.abc import AsyncIterator, Coroutine
4
- from contextlib import AbstractAsyncContextManager
4
+ from contextlib import AbstractAsyncContextManager, suppress
5
5
  from functools import partial
6
6
  from typing import Any, Generic, TypeVar
7
7
 
@@ -26,10 +26,8 @@ def get_event_loop() -> asyncio.AbstractEventLoop:
26
26
 
27
27
 
28
28
  async def sleep_if_not_done(delay: float, done: asyncio.Event) -> None:
29
- try:
29
+ with suppress(TimeoutError):
30
30
  await asyncio.wait_for(done.wait(), delay)
31
- except TimeoutError:
32
- pass
33
31
 
34
32
 
35
33
  class ValueEvent(asyncio.Event):
@@ -95,14 +93,13 @@ PENDING_TASKS = set()
95
93
 
96
94
 
97
95
  def _create_task_done_callback(
98
- ignore_exceptions: tuple[Exception, ...],
96
+ ignore_exceptions: tuple[type[Exception], ...],
99
97
  task: asyncio.Task | asyncio.Future,
100
98
  ) -> None:
101
99
  PENDING_TASKS.discard(task)
102
100
  try:
103
- if exc := task.exception():
104
- if not isinstance(exc, ignore_exceptions):
105
- logger.exception("asyncio.task failed", exc_info=exc)
101
+ if (exc := task.exception()) and not isinstance(exc, ignore_exceptions):
102
+ logger.exception("asyncio.task failed", exc_info=exc)
106
103
  except asyncio.CancelledError:
107
104
  pass
108
105
 
@@ -176,16 +173,13 @@ class SimpleTaskGroup(AbstractAsyncContextManager["SimpleTaskGroup"]):
176
173
  self.taskgroup_name = f" {taskgroup_name} " if taskgroup_name else ""
177
174
 
178
175
  def _create_task_done_callback(
179
- self, ignore_exceptions: tuple[Exception, ...], task: asyncio.Task
176
+ self, ignore_exceptions: tuple[type[Exception], ...], task: asyncio.Task
180
177
  ) -> None:
181
- try:
178
+ with suppress(AttributeError):
182
179
  self.tasks.remove(task)
183
- except AttributeError:
184
- pass
185
180
  try:
186
- if exc := task.exception():
187
- if not isinstance(exc, ignore_exceptions):
188
- logger.exception("asyncio.task failed in task group", exc_info=exc)
181
+ if (exc := task.exception()) and not isinstance(exc, ignore_exceptions):
182
+ logger.exception("asyncio.task failed in task group", exc_info=exc)
189
183
  except asyncio.CancelledError:
190
184
  pass
191
185
 
@@ -286,13 +280,8 @@ class AsyncQueue(Generic[T], asyncio.Queue[T]):
286
280
  await getter
287
281
  except:
288
282
  getter.cancel() # Just in case getter is not done yet.
289
- try:
290
- # Clean self._getters from canceled getters.
283
+ with suppress(ValueError):
291
284
  self._getters.remove(getter)
292
- except ValueError:
293
- # The getter could be removed from self._getters by a
294
- # previous put_nowait call.
295
- pass
296
285
  if not self.empty() and not getter.cancelled():
297
286
  # We were woken up by put_nowait(), but can't take
298
287
  # the call. Wake up the next in line.
@@ -239,14 +239,11 @@ def _get_custom_auth_middleware(
239
239
 
240
240
  @functools.lru_cache(maxsize=1)
241
241
  def _get_auth_instance(path: str | None = None) -> Auth | Literal["js"] | None:
242
- if path is not None:
243
- auth_instance = _load_auth_obj(path)
244
- else:
245
- auth_instance = None
242
+ auth_instance = _load_auth_obj(path) if path is not None else None
246
243
 
247
244
  if auth_instance == "js":
248
245
  return auth_instance
249
-
246
+ deps = None
250
247
  if auth_instance is not None and (
251
248
  deps := _get_dependencies(auth_instance._authenticate_handler)
252
249
  ):
@@ -525,6 +522,12 @@ class ProxyUser(BaseUser):
525
522
  """Proxy any other attributes to the underlying user object."""
526
523
  return getattr(self._user, name)
527
524
 
525
+ def __iter__(self):
526
+ return iter(self._user)
527
+
528
+ def __len__(self):
529
+ return len(self._user)
530
+
528
531
  def __str__(self) -> str:
529
532
  return f"{self._user}"
530
533
 
@@ -86,9 +86,10 @@ class LangsmithAuthBackend(AuthenticationBackend):
86
86
  # If tenant id verification is disabled, the bearer token requests
87
87
  # are not required to match the tenant id. Api key requests are
88
88
  # always required to match the tenant id.
89
- if LANGSMITH_AUTH_VERIFY_TENANT_ID or conn.headers.get("x-api-key"):
90
- if auth_dict["tenant_id"] != LANGSMITH_TENANT_ID:
91
- raise AuthenticationError("Invalid tenant ID")
89
+ if (
90
+ LANGSMITH_AUTH_VERIFY_TENANT_ID or conn.headers.get("x-api-key")
91
+ ) and auth_dict["tenant_id"] != LANGSMITH_TENANT_ID:
92
+ raise AuthenticationError("Invalid tenant ID")
92
93
 
93
94
  credentials = AuthCredentials(["authenticated"])
94
95
  user = StudioUser(auth_dict.get("user_id"), is_authenticated=True)
@@ -1,5 +1,5 @@
1
1
  from collections.abc import AsyncGenerator
2
- from contextlib import asynccontextmanager
2
+ from contextlib import asynccontextmanager, suppress
3
3
  from typing import Any
4
4
 
5
5
  import httpx
@@ -17,11 +17,10 @@ _client: "JsonHttpClient"
17
17
  def is_retriable_error(exception: BaseException) -> bool:
18
18
  if isinstance(exception, httpx.TransportError):
19
19
  return True
20
- if isinstance(exception, httpx.HTTPStatusError):
21
- if exception.response.status_code > 499:
22
- return True
23
-
24
- return False
20
+ return (
21
+ isinstance(exception, httpx.HTTPStatusError)
22
+ and exception.response.status_code > 499
23
+ )
25
24
 
26
25
 
27
26
  retry_httpx = retry(
@@ -106,10 +105,8 @@ def create_client() -> JsonHttpClient:
106
105
  async def close_auth_client() -> None:
107
106
  """Close the auth http client."""
108
107
  global _client
109
- try:
108
+ with suppress(NameError):
110
109
  await _client.client.aclose()
111
- except NameError:
112
- pass
113
110
 
114
111
 
115
112
  async def initialize_auth_client() -> None:
@@ -132,10 +129,7 @@ async def auth_client() -> AsyncGenerator[JsonHttpClient, None]:
132
129
  await client.client.aclose()
133
130
  else:
134
131
  try:
135
- if not _client.client.is_closed:
136
- found = True
137
- else:
138
- found = False
132
+ found = bool(not _client.client.is_closed)
139
133
  except NameError:
140
134
  found = False
141
135
  if found:
@@ -242,9 +242,12 @@ CORS_CONFIG: CorsConfig | None = env("CORS_CONFIG", cast=_parse_json, default=No
242
242
  }
243
243
  }
244
244
  """
245
- if CORS_CONFIG is not None and CORS_ALLOW_ORIGINS != "*":
246
- if CORS_CONFIG.get("allow_origins") is None:
247
- CORS_CONFIG["allow_origins"] = CORS_ALLOW_ORIGINS
245
+ if (
246
+ CORS_CONFIG is not None
247
+ and CORS_ALLOW_ORIGINS != "*"
248
+ and CORS_CONFIG.get("allow_origins") is None
249
+ ):
250
+ CORS_CONFIG["allow_origins"] = CORS_ALLOW_ORIGINS
248
251
 
249
252
  # queue
250
253
 
@@ -371,7 +374,7 @@ API_VARIANT = env("LANGSMITH_LANGGRAPH_API_VARIANT", cast=str, default="")
371
374
  # UI
372
375
  UI_USE_BUNDLER = env("LANGGRAPH_UI_BUNDLER", cast=bool, default=False)
373
376
  IS_QUEUE_ENTRYPOINT = False
374
-
377
+ ref_sha = None
375
378
  if not os.getenv("LANGCHAIN_REVISION_ID") and (
376
379
  ref_sha := os.getenv("LANGSMITH_LANGGRAPH_GIT_REF_SHA")
377
380
  ):
@@ -46,12 +46,11 @@ async def cron_scheduler():
46
46
  cron["cron_id"],
47
47
  )
48
48
  )
49
- except Exception as e:
50
- logger.error(
49
+ except Exception:
50
+ logger.exception(
51
51
  "Error scheduling cron run cron_id={}".format(
52
52
  cron["cron_id"]
53
- ),
54
- exc_info=e,
53
+ )
55
54
  )
56
55
  next_run_date = await run_in_executor(
57
56
  None, next_cron_date, cron["schedule"], cron["now"]
@@ -63,6 +62,6 @@ async def cron_scheduler():
63
62
  await asyncio.sleep(SLEEP_TIME)
64
63
  except asyncio.CancelledError:
65
64
  raise
66
- except Exception as e:
67
- logger.error("Error in cron_scheduler", exc_info=e)
65
+ except Exception:
66
+ logger.exception("Error in cron_scheduler")
68
67
  await asyncio.sleep(SLEEP_TIME + random())
@@ -1,4 +1,5 @@
1
1
  import asyncio
2
+ import contextlib
2
3
 
3
4
  import httpx
4
5
  from tenacity import (
@@ -65,10 +66,8 @@ class JsonHttpClient:
65
66
  res.raise_for_status()
66
67
  finally:
67
68
  # We don't need the response body, so we close the response
68
- try:
69
+ with contextlib.suppress(UnboundLocalError):
69
70
  await res.aclose()
70
- except UnboundLocalError:
71
- pass
72
71
 
73
72
 
74
73
  _http_client: JsonHttpClient
@@ -173,10 +172,7 @@ async def http_request(
173
172
 
174
173
  content = None
175
174
  if body is not None:
176
- if isinstance(body, str):
177
- content = body.encode("utf-8")
178
- else:
179
- content = body
175
+ content = body.encode("utf-8") if isinstance(body, str) else body
180
176
  elif json is not None:
181
177
  content = json_dumpb(json)
182
178