chalk-remote-call-python 1.9.1__tar.gz → 1.10.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/PKG-INFO +34 -1
  2. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/README.md +33 -0
  3. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/Cargo.lock +8 -7
  4. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-server/Cargo.toml +5 -4
  5. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-server/src/async_service.rs +5 -5
  6. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-server/src/coalesce.rs +1 -2
  7. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-server/src/metrics.rs +16 -13
  8. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-server/src/python_bridge.rs +38 -3
  9. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-server/src/self_consumer.rs +214 -119
  10. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-server/src/server.rs +1 -2
  11. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-server/src/service.rs +1 -5
  12. chalk_remote_call_python-1.10.0/chalk_remote_call/_version.py +1 -0
  13. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/cli.py +21 -11
  14. chalk_remote_call_python-1.10.0/chalk_remote_call/errors.py +102 -0
  15. chalk_remote_call_python-1.10.0/chalk_remote_call/logging.py +29 -0
  16. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/server.py +8 -7
  17. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/servicer.py +26 -38
  18. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call_python.egg-info/PKG-INFO +34 -1
  19. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call_python.egg-info/SOURCES.txt +2 -0
  20. chalk_remote_call_python-1.9.1/chalk_remote_call/_version.py +0 -1
  21. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/MANIFEST.in +0 -0
  22. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/Cargo.toml +0 -0
  23. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-proto/Cargo.toml +0 -0
  24. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.auth.v1.rs +0 -0
  25. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.common.v1.rs +0 -0
  26. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.runtime.v1.rs +0 -0
  27. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.runtime.v1.tonic.rs +0 -0
  28. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.utils.v1.rs +0 -0
  29. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/descriptor.bin +0 -0
  30. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-proto/src/lib.rs +0 -0
  31. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-server/src/lib.rs +0 -0
  32. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/rust-toolchain.toml +0 -0
  33. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/__init__.py +0 -0
  34. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/__main__.py +0 -0
  35. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/__init__.py +0 -0
  36. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/__init__.py +0 -0
  37. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/auth/__init__.py +0 -0
  38. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/auth/v1/__init__.py +0 -0
  39. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/auth/v1/permissions_pb2.py +0 -0
  40. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/auth/v1/permissions_pb2_grpc.py +0 -0
  41. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/common/__init__.py +0 -0
  42. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/common/v1/__init__.py +0 -0
  43. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/common/v1/chalk_error_pb2.py +0 -0
  44. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/common/v1/chalk_error_pb2_grpc.py +0 -0
  45. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/runtime/__init__.py +0 -0
  46. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/runtime/v1/__init__.py +0 -0
  47. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/runtime/v1/remote_python_call_pb2.py +0 -0
  48. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/runtime/v1/remote_python_call_pb2_grpc.py +0 -0
  49. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/utils/__init__.py +0 -0
  50. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/utils/v1/__init__.py +0 -0
  51. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/utils/v1/encoding_pb2.py +0 -0
  52. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/utils/v1/encoding_pb2_grpc.py +0 -0
  53. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/utils/v1/field_change_pb2.py +0 -0
  54. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/utils/v1/field_change_pb2_grpc.py +0 -0
  55. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/utils/v1/sensitive_pb2.py +0 -0
  56. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/utils/v1/sensitive_pb2_grpc.py +0 -0
  57. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_native.pyi +0 -0
  58. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/arrow_utils.py +0 -0
  59. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/handler_loader.py +0 -0
  60. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/input_transform.py +0 -0
  61. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call/tracing.py +0 -0
  62. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call_python.egg-info/dependency_links.txt +0 -0
  63. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call_python.egg-info/entry_points.txt +0 -0
  64. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call_python.egg-info/requires.txt +0 -0
  65. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/chalk_remote_call_python.egg-info/top_level.txt +0 -0
  66. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/pyproject.toml +0 -0
  67. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/setup.cfg +0 -0
  68. {chalk_remote_call_python-1.9.1 → chalk_remote_call_python-1.10.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chalk-remote-call-python
3
- Version: 1.9.1
3
+ Version: 1.10.0
4
4
  Summary: Chalk remote call Python runtime interface client
5
5
  Author: Chalk AI, Inc.
6
6
  Project-URL: Homepage, https://chalk.ai
@@ -108,6 +108,19 @@ python -m chalk_remote_call --handler my_handler.handler
108
108
  | `--on-shutdown` | | | Dotted path to a shutdown function |
109
109
  | `--log-level` | | `INFO` | `DEBUG`, `INFO`, `WARNING`, or `ERROR` |
110
110
 
111
+ The CLI writes Python logs to stderr as one JSON object per line, with separate
112
+ `timestamp`, `severity`, `logger`, and `message` fields. Exceptions include an
113
+ `exception.stacktrace` field, which the log collector uses for the traceback.
114
+ CLI logging is configured before importing runtime dependencies. Import failures,
115
+ invalid environment configuration, and exceptions escaping startup or the server
116
+ emit a single ERROR record with the full traceback and exit with status 1.
117
+ Calls to `serve()` from Python retain the embedding application's logging configuration.
118
+
119
+ Missing optional StatsD endpoints or a metrics-bus topic do not emit warnings.
120
+ Failures initializing configured metrics sinks still warn. Routine customer
121
+ StatsD configuration messages are emitted at DEBUG by the shared metrics library;
122
+ set `RUST_LOG=info,chalk_metrics::metrics::config=debug` to include them.
123
+
111
124
  ### Environment variables
112
125
 
113
126
  | Variable | Description |
@@ -212,3 +225,23 @@ To check the Rust code independently:
212
225
  ```bash
213
226
  PYO3_PYTHON=python3 cargo check --manifest-path chalk-remote-call-rs/chalk-remote-call-server/Cargo.toml
214
227
  ```
228
+
229
+ ## Remote exception transport
230
+
231
+ Handler failures retain their original exception type, message, traceback,
232
+ exception chains, and exception groups. Runtime dispatch frames are excluded
233
+ from the user traceback; full diagnostics are retained in server logs.
234
+
235
+ All call modes carry a JSON payload with `chalk_remote_error: 1` and an
236
+ `exception` object containing the `ChalkException` fields `kind`, `message`,
237
+ `stacktrace`, and `internal_stacktrace`. Direct calls carry the payload as the
238
+ unescaped gRPC status message; queued calls carry it in the error message. The
239
+ local async service additionally populates `ChalkError.exception`. This permits
240
+ existing queue transports to forward structured errors without a protobuf
241
+ migration. Clients must decode the versioned payload, not parse gRPC debug text
242
+ or classify user errors by their message. Unknown versions remain opaque.
243
+
244
+ Payloads are bounded for gRPC header limits. Truncation is marked explicitly,
245
+ with complete tracebacks kept in server logs. Exception objects and locals are
246
+ never pickled or sent. Chalk SDK-generated handlers identify their dispatch
247
+ frames, and notebook source maps identify the deployed snapshot's lines.
@@ -70,6 +70,19 @@ python -m chalk_remote_call --handler my_handler.handler
70
70
  | `--on-shutdown` | | | Dotted path to a shutdown function |
71
71
  | `--log-level` | | `INFO` | `DEBUG`, `INFO`, `WARNING`, or `ERROR` |
72
72
 
73
+ The CLI writes Python logs to stderr as one JSON object per line, with separate
74
+ `timestamp`, `severity`, `logger`, and `message` fields. Exceptions include an
75
+ `exception.stacktrace` field, which the log collector uses for the traceback.
76
+ CLI logging is configured before importing runtime dependencies. Import failures,
77
+ invalid environment configuration, and exceptions escaping startup or the server
78
+ emit a single ERROR record with the full traceback and exit with status 1.
79
+ Calls to `serve()` from Python retain the embedding application's logging configuration.
80
+
81
+ Missing optional StatsD endpoints or a metrics-bus topic do not emit warnings.
82
+ Failures initializing configured metrics sinks still warn. Routine customer
83
+ StatsD configuration messages are emitted at DEBUG by the shared metrics library;
84
+ set `RUST_LOG=info,chalk_metrics::metrics::config=debug` to include them.
85
+
73
86
  ### Environment variables
74
87
 
75
88
  | Variable | Description |
@@ -174,3 +187,23 @@ To check the Rust code independently:
174
187
  ```bash
175
188
  PYO3_PYTHON=python3 cargo check --manifest-path chalk-remote-call-rs/chalk-remote-call-server/Cargo.toml
176
189
  ```
190
+
191
+ ## Remote exception transport
192
+
193
+ Handler failures retain their original exception type, message, traceback,
194
+ exception chains, and exception groups. Runtime dispatch frames are excluded
195
+ from the user traceback; full diagnostics are retained in server logs.
196
+
197
+ All call modes carry a JSON payload with `chalk_remote_error: 1` and an
198
+ `exception` object containing the `ChalkException` fields `kind`, `message`,
199
+ `stacktrace`, and `internal_stacktrace`. Direct calls carry the payload as the
200
+ unescaped gRPC status message; queued calls carry it in the error message. The
201
+ local async service additionally populates `ChalkError.exception`. This permits
202
+ existing queue transports to forward structured errors without a protobuf
203
+ migration. Clients must decode the versioned payload, not parse gRPC debug text
204
+ or classify user errors by their message. Unknown versions remain opaque.
205
+
206
+ Payloads are bounded for gRPC header limits. Truncation is marked explicitly,
207
+ with complete tracebacks kept in server logs. Exception objects and locals are
208
+ never pickled or sent. Chalk SDK-generated handlers identify their dispatch
209
+ frames, and notebook source maps identify the deployed snapshot's lines.
@@ -484,7 +484,7 @@ dependencies = [
484
484
  [[package]]
485
485
  name = "chalk-core"
486
486
  version = "0.0.0"
487
- source = "git+https://github.com/chalk-ai/chalk-private.git?rev=783e9ce6b2c#783e9ce6b2c17563ab3302e5d11f8a664ed00232"
487
+ source = "git+https://github.com/chalk-ai/chalk-private.git?rev=a0f8932ec10d3b8340af3e98cf63b800d60b4926#a0f8932ec10d3b8340af3e98cf63b800d60b4926"
488
488
  dependencies = [
489
489
  "anyhow",
490
490
  "envconfig",
@@ -499,7 +499,7 @@ dependencies = [
499
499
  [[package]]
500
500
  name = "chalk-protos"
501
501
  version = "0.0.0"
502
- source = "git+https://github.com/chalk-ai/chalk-private.git?rev=783e9ce6b2c#783e9ce6b2c17563ab3302e5d11f8a664ed00232"
502
+ source = "git+https://github.com/chalk-ai/chalk-private.git?rev=a0f8932ec10d3b8340af3e98cf63b800d60b4926#a0f8932ec10d3b8340af3e98cf63b800d60b4926"
503
503
  dependencies = [
504
504
  "prost 0.11.9",
505
505
  "prost-build",
@@ -511,7 +511,7 @@ dependencies = [
511
511
  [[package]]
512
512
  name = "chalk-queue"
513
513
  version = "0.0.0"
514
- source = "git+https://github.com/chalk-ai/chalk-private.git?rev=9c61f6a938d8f885f138217ac64149e29d7a691e#9c61f6a938d8f885f138217ac64149e29d7a691e"
514
+ source = "git+https://github.com/chalk-ai/chalk-private.git?rev=c6eed6bd7bdad1f2a7568ef206e570c719c4c64f#c6eed6bd7bdad1f2a7568ef206e570c719c4c64f"
515
515
  dependencies = [
516
516
  "base64 0.22.1",
517
517
  "redis",
@@ -547,6 +547,7 @@ dependencies = [
547
547
  "prost 0.13.5",
548
548
  "pyo3",
549
549
  "redis",
550
+ "serde_json",
550
551
  "tokio",
551
552
  "tokio-stream",
552
553
  "tonic 0.12.3",
@@ -560,7 +561,7 @@ dependencies = [
560
561
  [[package]]
561
562
  name = "chalk_metric_definitions"
562
563
  version = "0.0.0"
563
- source = "git+https://github.com/chalk-ai/chalk-private.git?rev=783e9ce6b2c#783e9ce6b2c17563ab3302e5d11f8a664ed00232"
564
+ source = "git+https://github.com/chalk-ai/chalk-private.git?rev=a0f8932ec10d3b8340af3e98cf63b800d60b4926#a0f8932ec10d3b8340af3e98cf63b800d60b4926"
564
565
  dependencies = [
565
566
  "chalk-protos",
566
567
  "metrics-generator",
@@ -570,7 +571,7 @@ dependencies = [
570
571
  [[package]]
571
572
  name = "chalk_metrics"
572
573
  version = "0.0.0"
573
- source = "git+https://github.com/chalk-ai/chalk-private.git?rev=783e9ce6b2c#783e9ce6b2c17563ab3302e5d11f8a664ed00232"
574
+ source = "git+https://github.com/chalk-ai/chalk-private.git?rev=a0f8932ec10d3b8340af3e98cf63b800d60b4926#a0f8932ec10d3b8340af3e98cf63b800d60b4926"
574
575
  dependencies = [
575
576
  "async-trait",
576
577
  "chalk-core",
@@ -604,7 +605,7 @@ dependencies = [
604
605
  [[package]]
605
606
  name = "chalk_statsd"
606
607
  version = "0.0.0"
607
- source = "git+https://github.com/chalk-ai/chalk-private.git?rev=783e9ce6b2c#783e9ce6b2c17563ab3302e5d11f8a664ed00232"
608
+ source = "git+https://github.com/chalk-ai/chalk-private.git?rev=a0f8932ec10d3b8340af3e98cf63b800d60b4926#a0f8932ec10d3b8340af3e98cf63b800d60b4926"
608
609
  dependencies = [
609
610
  "chalk-core",
610
611
  "chrono",
@@ -1867,7 +1868,7 @@ dependencies = [
1867
1868
  [[package]]
1868
1869
  name = "metrics-generator"
1869
1870
  version = "0.1.0"
1870
- source = "git+https://github.com/chalk-ai/chalk-private.git?rev=783e9ce6b2c#783e9ce6b2c17563ab3302e5d11f8a664ed00232"
1871
+ source = "git+https://github.com/chalk-ai/chalk-private.git?rev=a0f8932ec10d3b8340af3e98cf63b800d60b4926#a0f8932ec10d3b8340af3e98cf63b800d60b4926"
1871
1872
  dependencies = [
1872
1873
  "codegen",
1873
1874
  "serde",
@@ -21,6 +21,7 @@ arrow = { version = "54", default-features = false, features = ["ipc"] }
21
21
  tracing = "0.1"
22
22
  tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
23
23
  bytes = "1"
24
+ serde_json = "1"
24
25
  uuid = { version = "1", features = ["v4"] }
25
26
  dashmap = "5"
26
27
  futures-util = "0.3"
@@ -34,8 +35,8 @@ futures-util = "0.3"
34
35
  # chalk-private commit, mirroring how chalkcompute_remote_client depends on
35
36
  # chalk_remote_call_client. chalk-queue declares `redis = "1.0.2"`; with no
36
37
  # `[patch.crates-io]` here it builds against vanilla crates.io redis (it only
37
- # uses standard APIs). Same rev across all three so cargo reuses one checkout.
38
+ # uses standard APIs). Metrics and StatsD share a revision so their client types agree.
38
39
  [target.'cfg(target_os = "linux")'.dependencies]
39
- chalk-queue = { git = "https://github.com/chalk-ai/chalk-private.git", rev = "9c61f6a938d8f885f138217ac64149e29d7a691e" }
40
- chalk_metrics = { git = "https://github.com/chalk-ai/chalk-private.git", rev = "783e9ce6b2c" }
41
- chalk_statsd = { git = "https://github.com/chalk-ai/chalk-private.git", rev = "783e9ce6b2c" }
40
+ chalk-queue = { git = "https://github.com/chalk-ai/chalk-private.git", rev = "c6eed6bd7bdad1f2a7568ef206e570c719c4c64f" }
41
+ chalk_metrics = { git = "https://github.com/chalk-ai/chalk-private.git", rev = "a0f8932ec10d3b8340af3e98cf63b800d60b4926" }
42
+ chalk_statsd = { git = "https://github.com/chalk-ai/chalk-private.git", rev = "a0f8932ec10d3b8340af3e98cf63b800d60b4926" }
@@ -39,7 +39,7 @@ struct CallState {
39
39
  /// and `status`/`error` can be updated without blocking emits.
40
40
  chunks: Arc<Mutex<ChunkBuffer>>,
41
41
  /// Populated only when `status == Failed`; surfaced as a `ChalkError`.
42
- error: Option<String>,
42
+ error: Option<ChalkError>,
43
43
  /// When the call reached a terminal status. Used only by the orphan sweeper
44
44
  /// to decide when an un-drained terminal entry is safe to reclaim.
45
45
  completed_at: Option<Instant>,
@@ -189,7 +189,7 @@ impl AsyncRemoteCallService for AsyncRemoteCallServiceImpl {
189
189
  }
190
190
  Err(e) => {
191
191
  s.status = RemoteCallStatus::Failed;
192
- s.error = Some(e.to_string());
192
+ s.error = Some(e.chalk_error());
193
193
  error!(
194
194
  call_id = %id,
195
195
  exception.stacktrace = e.details(),
@@ -246,10 +246,10 @@ impl AsyncRemoteCallService for AsyncRemoteCallServiceImpl {
246
246
  drop(buf);
247
247
 
248
248
  let errors = if status == RemoteCallStatus::Failed {
249
- vec![ChalkError {
250
- message: error.unwrap_or_else(|| "remote call failed".to_string()),
249
+ vec![error.unwrap_or_else(|| ChalkError {
250
+ message: "remote call failed".to_string(),
251
251
  ..Default::default()
252
- }]
252
+ })]
253
253
  } else {
254
254
  Vec::new()
255
255
  };
@@ -193,8 +193,7 @@ impl CoalescingQueue {
193
193
  chalk_metrics::metrics::tags::Mode::Sync,
194
194
  );
195
195
  }
196
- let status =
197
- Status::unknown(format!("Exception raised during handler execution: {e}"));
196
+ let status = Status::unknown(e.to_string());
198
197
  for tx in response_txs {
199
198
  let _ = tx.send(Err(status.clone())).await;
200
199
  }
@@ -1,6 +1,6 @@
1
- //! Metrics emission for the self-consuming shim, mirroring the catalog
2
- //! consumer / fnq-server: build a `chalk_metrics` publishing pipeline at startup
3
- //! and emit per-call dequeue + processing-latency metrics.
1
+ //! Function-call metrics for synchronous gRPC calls and self-consumed queue work.
2
+ //! A shared `chalk_metrics` publishing pipeline emits call counts and
3
+ //! processing latency, independently of whether Redis queue consumption is enabled.
4
4
  //!
5
5
  //! The External Function "Function Calls Dequeued" chart reads TimescaleDB
6
6
  //! `metrics1`, which is fed only by the chalk_metrics PubSub bus (statsd goes to
@@ -15,8 +15,9 @@ use chalk_metrics::metrics::well_known_metrics::WellKnownMetricName;
15
15
  use chalk_metrics::{
16
16
  make_pubsub_publisher, BatchConfig, BusPublisher, MetricsPipeline, PublishingMetricsPipeline,
17
17
  };
18
+ use chalk_statsd::datadog::DatadogError;
18
19
  use chalk_statsd::metrics::MetricsClient;
19
- use tracing::{info, warn};
20
+ use tracing::{debug, warn};
20
21
 
21
22
  // Batch tuning — chalk_function_queue's defaults (METRICS_BATCH_* ), inlined.
22
23
  const BATCH_MAX_MESSAGES: usize = 1000;
@@ -24,26 +25,27 @@ const BATCH_MAX_BYTES: usize = 10 * 1024 * 1024; // 10 MB
24
25
  const BATCH_MAX_LATENCY: Duration = Duration::from_millis(100);
25
26
 
26
27
  /// Build and start the metrics pipeline from the environment, mirroring the
27
- /// fnq-server / catalog-consumer init. Returns `None` only if the pipeline
28
- /// object can't be constructed; the self-consumer then runs without metrics.
28
+ /// fnq-server / catalog-consumer init. Returns `None` if startup fails.
29
29
  ///
30
30
  /// The bus publisher is what reaches the metrics store (and thus the dashboard);
31
- /// statsd alone only reaches Datadog. An empty `METRICS_BUS_TOPIC` therefore
32
- /// means the dashboard won't move — surfaced as a warning, not an error.
31
+ /// StatsD and the bus are optional sinks. Missing configuration is quiet;
32
+ /// failures initializing a configured sink remain warnings.
33
33
  pub async fn init_pipeline() -> Option<Arc<PublishingMetricsPipeline>> {
34
34
  let environment_id = std::env::var("CHALK_ENVIRONMENT_ID").unwrap_or_default();
35
35
 
36
36
  let statsd = Arc::new(
37
37
  MetricsClient::new_from_default_environment().unwrap_or_else(|e| {
38
- warn!(error = %e, "failed to init statsd client; statsd metrics disabled");
38
+ if !matches!(
39
+ e.downcast_ref::<DatadogError>(),
40
+ Some(DatadogError::ConnectionInfoNotFound)
41
+ ) {
42
+ warn!(error = %e, "failed to init statsd client; statsd metrics disabled");
43
+ }
39
44
  MetricsClient::new_null()
40
45
  }),
41
46
  );
42
47
 
43
48
  let publishers: Vec<Arc<dyn BusPublisher>> = build_bus_publisher().await.into_iter().collect();
44
- if publishers.is_empty() {
45
- warn!("no metrics bus publisher configured; function call metrics will not reach the dashboard");
46
- }
47
49
 
48
50
  let metrics_pipeline: Arc<PublishingMetricsPipeline> =
49
51
  Arc::new(PublishingMetricsPipeline::new_with_env_var_defaults(
@@ -54,8 +56,9 @@ pub async fn init_pipeline() -> Option<Arc<PublishingMetricsPipeline>> {
54
56
  ));
55
57
  if let Err(e) = metrics_pipeline.start().await {
56
58
  warn!(error = %e, "failed to start metrics pipeline; metrics disabled");
59
+ return None;
57
60
  }
58
- info!("fnq self-consumer metrics pipeline started");
61
+ debug!("function-call metrics pipeline started");
59
62
  Some(metrics_pipeline)
60
63
  }
61
64
 
@@ -1,6 +1,7 @@
1
1
  use std::collections::HashMap;
2
2
  use std::sync::{Arc, Mutex};
3
3
 
4
+ use chalk_remote_call_proto::chalk::common::v1::{ChalkError, ChalkException};
4
5
  use chalk_remote_call_proto::chalk::runtime::v1::CallFunctionResponse;
5
6
  use pyo3::prelude::*;
6
7
  use pyo3::types::{PyBytes, PyDict, PyList};
@@ -321,28 +322,62 @@ fn invoke_process_fn(
321
322
  #[derive(Debug)]
322
323
  pub struct PythonError {
323
324
  details: String,
325
+ wire_message: String,
324
326
  }
325
327
 
326
328
  impl PythonError {
327
329
  fn new(message: String) -> Self {
328
- Self { details: message }
330
+ Self {
331
+ details: message.clone(),
332
+ wire_message: message,
333
+ }
329
334
  }
330
335
 
331
336
  pub fn details(&self) -> &str {
332
337
  &self.details
333
338
  }
339
+
340
+ pub fn chalk_error(&self) -> ChalkError {
341
+ let exception = serde_json::from_str::<serde_json::Value>(&self.wire_message)
342
+ .ok()
343
+ .and_then(|payload| {
344
+ let exception = payload.get("exception")?;
345
+ Some(ChalkException {
346
+ kind: exception.get("kind")?.as_str()?.to_string(),
347
+ message: exception.get("message")?.as_str()?.to_string(),
348
+ stacktrace: exception.get("stacktrace")?.as_str()?.to_string(),
349
+ internal_stacktrace: exception
350
+ .get("internal_stacktrace")?
351
+ .as_str()?
352
+ .to_string(),
353
+ })
354
+ });
355
+ ChalkError {
356
+ message: self.wire_message.clone(),
357
+ exception,
358
+ ..Default::default()
359
+ }
360
+ }
334
361
  }
335
362
 
336
363
  impl std::fmt::Display for PythonError {
337
364
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
338
- write!(f, "{}", self.details)
365
+ write!(f, "{}", self.wire_message)
339
366
  }
340
367
  }
341
368
 
342
369
  fn into_python_error(py: Python<'_>, err: PyErr) -> PythonError {
343
370
  let summary = err.to_string();
344
371
  let details = format_python_traceback(py, &err).unwrap_or_else(|_| summary.clone());
345
- PythonError { details }
372
+ let wire_message = py
373
+ .import("chalk_remote_call.errors")
374
+ .and_then(|module| module.call_method1("serialize_exception", (err.value(py),)))
375
+ .and_then(|value| value.extract::<String>())
376
+ .unwrap_or_else(|_| details.clone());
377
+ PythonError {
378
+ details,
379
+ wire_message,
380
+ }
346
381
  }
347
382
 
348
383
  fn format_python_traceback(py: Python<'_>, err: &PyErr) -> PyResult<String> {