chalk-remote-call-python 1.7.1__tar.gz → 1.7.2__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 (64) hide show
  1. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/PKG-INFO +1 -1
  2. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk-remote-call-rs/chalk-remote-call-server/src/async_service.rs +65 -7
  3. chalk_remote_call_python-1.7.2/chalk_remote_call/_version.py +1 -0
  4. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call_python.egg-info/PKG-INFO +1 -1
  5. chalk_remote_call_python-1.7.1/chalk_remote_call/_version.py +0 -1
  6. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/MANIFEST.in +0 -0
  7. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/README.md +0 -0
  8. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk-remote-call-rs/Cargo.lock +0 -0
  9. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk-remote-call-rs/Cargo.toml +0 -0
  10. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk-remote-call-rs/chalk-remote-call-proto/Cargo.toml +0 -0
  11. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.auth.v1.rs +0 -0
  12. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.common.v1.rs +0 -0
  13. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.runtime.v1.rs +0 -0
  14. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.runtime.v1.tonic.rs +0 -0
  15. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.utils.v1.rs +0 -0
  16. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/descriptor.bin +0 -0
  17. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk-remote-call-rs/chalk-remote-call-proto/src/lib.rs +0 -0
  18. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk-remote-call-rs/chalk-remote-call-server/Cargo.toml +0 -0
  19. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk-remote-call-rs/chalk-remote-call-server/src/coalesce.rs +0 -0
  20. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk-remote-call-rs/chalk-remote-call-server/src/lib.rs +0 -0
  21. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk-remote-call-rs/chalk-remote-call-server/src/python_bridge.rs +0 -0
  22. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk-remote-call-rs/chalk-remote-call-server/src/server.rs +0 -0
  23. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk-remote-call-rs/chalk-remote-call-server/src/service.rs +0 -0
  24. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk-remote-call-rs/rust-toolchain.toml +0 -0
  25. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/__init__.py +0 -0
  26. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/__main__.py +0 -0
  27. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/_gen/__init__.py +0 -0
  28. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/_gen/chalk/__init__.py +0 -0
  29. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/_gen/chalk/auth/__init__.py +0 -0
  30. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/_gen/chalk/auth/v1/__init__.py +0 -0
  31. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/_gen/chalk/auth/v1/permissions_pb2.py +0 -0
  32. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/_gen/chalk/auth/v1/permissions_pb2_grpc.py +0 -0
  33. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/_gen/chalk/common/__init__.py +0 -0
  34. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/_gen/chalk/common/v1/__init__.py +0 -0
  35. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/_gen/chalk/common/v1/chalk_error_pb2.py +0 -0
  36. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/_gen/chalk/common/v1/chalk_error_pb2_grpc.py +0 -0
  37. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/_gen/chalk/runtime/__init__.py +0 -0
  38. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/_gen/chalk/runtime/v1/__init__.py +0 -0
  39. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/_gen/chalk/runtime/v1/remote_python_call_pb2.py +0 -0
  40. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/_gen/chalk/runtime/v1/remote_python_call_pb2_grpc.py +0 -0
  41. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/_gen/chalk/utils/__init__.py +0 -0
  42. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/_gen/chalk/utils/v1/__init__.py +0 -0
  43. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/_gen/chalk/utils/v1/encoding_pb2.py +0 -0
  44. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/_gen/chalk/utils/v1/encoding_pb2_grpc.py +0 -0
  45. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/_gen/chalk/utils/v1/field_change_pb2.py +0 -0
  46. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/_gen/chalk/utils/v1/field_change_pb2_grpc.py +0 -0
  47. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/_gen/chalk/utils/v1/sensitive_pb2.py +0 -0
  48. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/_gen/chalk/utils/v1/sensitive_pb2_grpc.py +0 -0
  49. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/_native.pyi +0 -0
  50. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/arrow_utils.py +0 -0
  51. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/cli.py +0 -0
  52. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/handler_loader.py +0 -0
  53. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/input_transform.py +0 -0
  54. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/server.py +0 -0
  55. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/servicer.py +0 -0
  56. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call/tracing.py +0 -0
  57. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call_python.egg-info/SOURCES.txt +0 -0
  58. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call_python.egg-info/dependency_links.txt +0 -0
  59. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call_python.egg-info/entry_points.txt +0 -0
  60. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call_python.egg-info/requires.txt +0 -0
  61. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/chalk_remote_call_python.egg-info/top_level.txt +0 -0
  62. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/pyproject.toml +0 -0
  63. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/setup.cfg +0 -0
  64. {chalk_remote_call_python-1.7.1 → chalk_remote_call_python-1.7.2}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chalk-remote-call-python
3
- Version: 1.7.1
3
+ Version: 1.7.2
4
4
  Summary: Chalk remote call Python runtime interface client
5
5
  Author: Chalk AI, Inc.
6
6
  Project-URL: Homepage, https://chalk.ai
@@ -1,6 +1,7 @@
1
1
  use std::collections::HashMap;
2
2
  use std::sync::atomic::{AtomicU64, Ordering};
3
3
  use std::sync::{Arc, Mutex};
4
+ use std::time::{Duration, Instant};
4
5
 
5
6
  use chalk_remote_call_proto::chalk::common::v1::ChalkError;
6
7
  use chalk_remote_call_proto::chalk::runtime::v1::async_remote_call_service_server::AsyncRemoteCallService;
@@ -14,6 +15,14 @@ use tracing::{debug, error, info, instrument};
14
15
  use crate::python_bridge::{ChunkBuffer, PythonHandler};
15
16
  use crate::service::extract_metadata;
16
17
 
18
+ /// How often the orphan sweeper runs.
19
+ const SWEEP_INTERVAL: Duration = Duration::from_secs(30);
20
+ /// How long a *terminal* entry is retained before the sweeper reclaims it. Only
21
+ /// matters for orphans — calls the consumer drained are evicted immediately on
22
+ /// their terminal poll. Comfortably larger than the consumer's poll cadence so
23
+ /// the sweeper never races a live consumer that is about to read a result.
24
+ const RETENTION: Duration = Duration::from_mins(30);
25
+
17
26
  /// In-memory execution record for one enqueued call.
18
27
  ///
19
28
  /// Lives only on the sg pod that accepted the `EnqueueRemoteCall`, and is lost
@@ -31,6 +40,9 @@ struct CallState {
31
40
  chunks: ChunkBuffer,
32
41
  /// Populated only when `status == Failed`; surfaced as a `ChalkError`.
33
42
  error: Option<String>,
43
+ /// When the call reached a terminal status. Used only by the orphan sweeper
44
+ /// to decide when an un-drained terminal entry is safe to reclaim.
45
+ completed_at: Option<Instant>,
34
46
  }
35
47
 
36
48
  impl CallState {
@@ -39,12 +51,20 @@ impl CallState {
39
51
  status: RemoteCallStatus::Pending,
40
52
  chunks: Arc::new(Mutex::new(Vec::new())),
41
53
  error: None,
54
+ completed_at: None,
42
55
  }
43
56
  }
44
57
  }
45
58
 
46
59
  type Registry = Arc<Mutex<HashMap<String, CallState>>>;
47
60
 
61
+ fn is_terminal(status: RemoteCallStatus) -> bool {
62
+ matches!(
63
+ status,
64
+ RemoteCallStatus::Completed | RemoteCallStatus::Failed
65
+ )
66
+ }
67
+
48
68
  /// Async (enqueue + poll) executor for the scaling-group hop.
49
69
  ///
50
70
  /// `EnqueueRemoteCall` runs the handler in a detached background task that
@@ -53,6 +73,11 @@ type Registry = Arc<Mutex<HashMap<String, CallState>>>;
53
73
  /// function execution from any single connection's lifetime, so the path is
54
74
  /// immune to gateway request/stream timeouts — every RPC here is sub-second
55
75
  /// regardless of how long the function runs.
76
+ ///
77
+ /// Entries are reclaimed two ways: the terminal poll that delivers a result
78
+ /// evicts its own entry (the consumer drains a call exactly once, then persists
79
+ /// to Redis and stops polling it), and a background sweeper reclaims orphans —
80
+ /// calls that finished but whose consumer died before the terminal poll.
56
81
  pub struct AsyncRemoteCallServiceImpl {
57
82
  python_handler: Arc<PythonHandler>,
58
83
  registry: Registry,
@@ -66,9 +91,13 @@ pub struct AsyncRemoteCallServiceImpl {
66
91
 
67
92
  impl AsyncRemoteCallServiceImpl {
68
93
  pub fn new(python_handler: Arc<PythonHandler>, batching_enabled: bool) -> Self {
94
+ let registry: Registry = Arc::new(Mutex::new(HashMap::new()));
95
+ // Runs inside the server's tokio runtime (the service is constructed
96
+ // within `run_server`); reclaims orphaned terminal entries.
97
+ spawn_sweeper(registry.clone());
69
98
  Self {
70
99
  python_handler,
71
- registry: Arc::new(Mutex::new(HashMap::new())),
100
+ registry,
72
101
  next_id: AtomicU64::new(1),
73
102
  batching_enabled,
74
103
  }
@@ -138,6 +167,7 @@ impl AsyncRemoteCallService for AsyncRemoteCallServiceImpl {
138
167
 
139
168
  let mut reg = registry.lock().expect("registry mutex poisoned");
140
169
  if let Some(s) = reg.get_mut(&id) {
170
+ s.completed_at = Some(Instant::now());
141
171
  match result {
142
172
  Ok(()) => {
143
173
  s.status = RemoteCallStatus::Completed;
@@ -170,16 +200,23 @@ impl AsyncRemoteCallService for AsyncRemoteCallServiceImpl {
170
200
  .map_err(|_| Status::invalid_argument(format!("invalid cursor: {}", req.cursor)))?
171
201
  };
172
202
 
173
- // Read status/error and grab the chunk-buffer handle under the map lock,
174
- // then release it before locking the buffer. `status` is set to a
175
- // terminal value only after the handler returns (all chunks already
176
- // appended), so seeing `Completed` guarantees the buffer is complete.
203
+ // Read status/error and grab the chunk-buffer handle under the map lock.
204
+ // `status` reaches a terminal value only after the handler returns (all
205
+ // chunks already appended), so seeing `Completed`/`Failed` guarantees
206
+ // the buffer is complete — and this poll delivers it in full. Since the
207
+ // consumer drains a call to terminal exactly once (then persists to
208
+ // Redis and never re-polls this call_id), we evict the entry here. The
209
+ // cloned `chunks` Arc keeps the buffer alive for the read below.
177
210
  let (status, error, chunks) = {
178
- let reg = self.registry.lock().expect("registry mutex poisoned");
211
+ let mut reg = self.registry.lock().expect("registry mutex poisoned");
179
212
  let state = reg
180
213
  .get(&req.call_id)
181
214
  .ok_or_else(|| Status::not_found(format!("unknown call_id: {}", req.call_id)))?;
182
- (state.status, state.error.clone(), state.chunks.clone())
215
+ let snapshot = (state.status, state.error.clone(), state.chunks.clone());
216
+ if is_terminal(snapshot.0) {
217
+ reg.remove(&req.call_id);
218
+ }
219
+ snapshot
183
220
  };
184
221
 
185
222
  let buf = chunks.lock().expect("chunk buffer poisoned");
@@ -236,3 +273,24 @@ fn set_status(registry: &Registry, call_id: &str, status: RemoteCallStatus) {
236
273
  }
237
274
  }
238
275
  }
276
+
277
+ /// Background task: periodically reclaim *orphaned* terminal entries — calls
278
+ /// that finished but whose consumer died before issuing the terminal poll that
279
+ /// would normally evict them. Entries the consumer drains are removed on their
280
+ /// terminal poll and never reach the sweeper; non-terminal entries (still
281
+ /// running) are never reclaimed here.
282
+ fn spawn_sweeper(registry: Registry) {
283
+ tokio::spawn(async move {
284
+ let mut ticker = tokio::time::interval(SWEEP_INTERVAL);
285
+ ticker.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
286
+ loop {
287
+ ticker.tick().await;
288
+ let Ok(mut reg) = registry.lock() else {
289
+ continue;
290
+ };
291
+ reg.retain(|_, s| {
292
+ !(is_terminal(s.status) && s.completed_at.is_some_and(|t| t.elapsed() > RETENTION))
293
+ });
294
+ }
295
+ });
296
+ }
@@ -0,0 +1 @@
1
+ __version__ = "1.7.2"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chalk-remote-call-python
3
- Version: 1.7.1
3
+ Version: 1.7.2
4
4
  Summary: Chalk remote call Python runtime interface client
5
5
  Author: Chalk AI, Inc.
6
6
  Project-URL: Homepage, https://chalk.ai
@@ -1 +0,0 @@
1
- __version__ = "1.7.1"