chalk-remote-call-python 1.5.1__tar.gz → 1.6.1__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.5.1 → chalk_remote_call_python-1.6.1}/PKG-INFO +1 -1
  2. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk-remote-call-rs/chalk-remote-call-server/src/python_bridge.rs +11 -4
  3. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk-remote-call-rs/chalk-remote-call-server/src/service.rs +6 -1
  4. chalk_remote_call_python-1.6.1/chalk_remote_call/_version.py +1 -0
  5. chalk_remote_call_python-1.6.1/chalk_remote_call/tracing.py +329 -0
  6. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call_python.egg-info/PKG-INFO +1 -1
  7. chalk_remote_call_python-1.5.1/chalk_remote_call/_version.py +0 -1
  8. chalk_remote_call_python-1.5.1/chalk_remote_call/tracing.py +0 -150
  9. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/MANIFEST.in +0 -0
  10. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/README.md +0 -0
  11. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk-remote-call-rs/Cargo.lock +0 -0
  12. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk-remote-call-rs/Cargo.toml +0 -0
  13. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk-remote-call-rs/chalk-remote-call-proto/Cargo.toml +0 -0
  14. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.auth.v1.rs +0 -0
  15. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.common.v1.rs +0 -0
  16. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.runtime.v1.rs +0 -0
  17. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.runtime.v1.tonic.rs +0 -0
  18. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.utils.v1.rs +0 -0
  19. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/descriptor.bin +0 -0
  20. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk-remote-call-rs/chalk-remote-call-proto/src/lib.rs +0 -0
  21. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk-remote-call-rs/chalk-remote-call-server/Cargo.toml +0 -0
  22. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk-remote-call-rs/chalk-remote-call-server/src/coalesce.rs +0 -0
  23. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk-remote-call-rs/chalk-remote-call-server/src/lib.rs +0 -0
  24. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk-remote-call-rs/chalk-remote-call-server/src/server.rs +0 -0
  25. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk-remote-call-rs/rust-toolchain.toml +0 -0
  26. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call/__init__.py +0 -0
  27. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call/__main__.py +0 -0
  28. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call/_gen/__init__.py +0 -0
  29. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call/_gen/chalk/__init__.py +0 -0
  30. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call/_gen/chalk/auth/__init__.py +0 -0
  31. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call/_gen/chalk/auth/v1/__init__.py +0 -0
  32. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call/_gen/chalk/auth/v1/permissions_pb2.py +0 -0
  33. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call/_gen/chalk/auth/v1/permissions_pb2_grpc.py +0 -0
  34. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call/_gen/chalk/common/__init__.py +0 -0
  35. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call/_gen/chalk/common/v1/__init__.py +0 -0
  36. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call/_gen/chalk/common/v1/chalk_error_pb2.py +0 -0
  37. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call/_gen/chalk/common/v1/chalk_error_pb2_grpc.py +0 -0
  38. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call/_gen/chalk/runtime/__init__.py +0 -0
  39. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call/_gen/chalk/runtime/v1/__init__.py +0 -0
  40. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call/_gen/chalk/runtime/v1/remote_python_call_pb2.py +0 -0
  41. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call/_gen/chalk/runtime/v1/remote_python_call_pb2_grpc.py +0 -0
  42. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call/_gen/chalk/utils/__init__.py +0 -0
  43. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call/_gen/chalk/utils/v1/__init__.py +0 -0
  44. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call/_gen/chalk/utils/v1/encoding_pb2.py +0 -0
  45. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call/_gen/chalk/utils/v1/encoding_pb2_grpc.py +0 -0
  46. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call/_gen/chalk/utils/v1/field_change_pb2.py +0 -0
  47. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call/_gen/chalk/utils/v1/field_change_pb2_grpc.py +0 -0
  48. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call/_gen/chalk/utils/v1/sensitive_pb2.py +0 -0
  49. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call/_gen/chalk/utils/v1/sensitive_pb2_grpc.py +0 -0
  50. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call/_native.pyi +0 -0
  51. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call/arrow_utils.py +0 -0
  52. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call/cli.py +0 -0
  53. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call/handler_loader.py +0 -0
  54. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call/input_transform.py +0 -0
  55. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call/server.py +0 -0
  56. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call/servicer.py +0 -0
  57. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call_python.egg-info/SOURCES.txt +0 -0
  58. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call_python.egg-info/dependency_links.txt +0 -0
  59. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call_python.egg-info/entry_points.txt +0 -0
  60. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call_python.egg-info/requires.txt +0 -0
  61. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/chalk_remote_call_python.egg-info/top_level.txt +0 -0
  62. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/pyproject.toml +0 -0
  63. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/setup.cfg +0 -0
  64. {chalk_remote_call_python-1.5.1 → chalk_remote_call_python-1.6.1}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chalk-remote-call-python
3
- Version: 1.5.1
3
+ Version: 1.6.1
4
4
  Summary: Chalk remote call Python runtime interface client
5
5
  Author: Chalk AI, Inc.
6
6
  Project-URL: Homepage, https://chalk.ai
@@ -15,11 +15,12 @@ pub struct PythonHandler {
15
15
  arg_names: Option<Vec<String>>,
16
16
  }
17
17
 
18
- /// One caller's input: request bytes + gRPC metadata + peer address.
18
+ /// One caller's input: request bytes + function name + gRPC metadata + peer address.
19
19
  /// Used identically in both the single-request and coalesced paths; the
20
20
  /// coalesced path just has N of them.
21
21
  pub struct CallerInput {
22
22
  pub ipc_bytes: Vec<u8>,
23
+ pub function_name: String,
23
24
  pub metadata: HashMap<String, String>,
24
25
  pub peer: String,
25
26
  }
@@ -40,6 +41,7 @@ impl PythonHandler {
40
41
  pub async fn call(
41
42
  &self,
42
43
  ipc_bytes: Vec<u8>,
44
+ function_name: String,
43
45
  metadata: HashMap<String, String>,
44
46
  peer: String,
45
47
  ) -> Result<Vec<Vec<u8>>, PythonError> {
@@ -50,7 +52,9 @@ impl PythonHandler {
50
52
  tokio::task::spawn_blocking(move || {
51
53
  Python::attach(|py| -> Result<Vec<Vec<u8>>, PythonError> {
52
54
  let py_bytes = PyBytes::new(py, &ipc_bytes).into_any().unbind();
53
- let py_ctx = build_context(py, &metadata, &peer)?.into_any().unbind();
55
+ let py_ctx = build_context(py, &function_name, &metadata, &peer)?
56
+ .into_any()
57
+ .unbind();
54
58
  let py_arg_names = build_arg_names(py, arg_names.as_deref())?;
55
59
  invoke_process_fn(py, &process_fn, &handler, py_bytes, py_arg_names, py_ctx)
56
60
  })
@@ -79,7 +83,7 @@ impl PythonHandler {
79
83
  .append(PyBytes::new(py, &c.ipc_bytes))
80
84
  .map_err(|e| into_python_error(py, e))?;
81
85
  py_contexts
82
- .append(build_context(py, &c.metadata, &c.peer)?)
86
+ .append(build_context(py, &c.function_name, &c.metadata, &c.peer)?)
83
87
  .map_err(|e| into_python_error(py, e))?;
84
88
  }
85
89
  let py_arg_names = build_arg_names(py, arg_names.as_deref())?;
@@ -100,9 +104,10 @@ impl PythonHandler {
100
104
 
101
105
  // ---- Shared helpers -------------------------------------------------------
102
106
 
103
- /// Build a `{"peer": str, "metadata": dict}` context dict for one caller.
107
+ /// Build a `{"function_name": str, "peer": str, "metadata": dict}` context dict for one caller.
104
108
  fn build_context<'py>(
105
109
  py: Python<'py>,
110
+ function_name: &str,
106
111
  metadata: &HashMap<String, String>,
107
112
  peer: &str,
108
113
  ) -> Result<Bound<'py, PyDict>, PythonError> {
@@ -113,6 +118,8 @@ fn build_context<'py>(
113
118
  .map_err(|e| into_python_error(py, e))?;
114
119
  }
115
120
  let ctx = PyDict::new(py);
121
+ ctx.set_item("function_name", function_name)
122
+ .map_err(|e| into_python_error(py, e))?;
116
123
  ctx.set_item("peer", peer)
117
124
  .map_err(|e| into_python_error(py, e))?;
118
125
  ctx.set_item("metadata", meta_dict)
@@ -40,7 +40,11 @@ impl RemoteCallService for RemoteCallServiceImpl {
40
40
 
41
41
  // Accumulate all feather_stream bytes from the request stream
42
42
  let mut all_bytes: Vec<u8> = Vec::new();
43
+ let mut function_name = String::new();
43
44
  while let Some(req) = stream.message().await? {
45
+ if function_name.is_empty() && !req.name.is_empty() {
46
+ function_name = req.name.clone();
47
+ }
44
48
  all_bytes.extend_from_slice(&req.feather_stream);
45
49
  if all_bytes.len() > MAX_REQUEST_SIZE {
46
50
  return Err(Status::resource_exhausted(format!(
@@ -74,6 +78,7 @@ impl RemoteCallService for RemoteCallServiceImpl {
74
78
  .submit(BufferedCall {
75
79
  input: CallerInput {
76
80
  ipc_bytes: all_bytes,
81
+ function_name,
77
82
  metadata,
78
83
  peer,
79
84
  },
@@ -86,7 +91,7 @@ impl RemoteCallService for RemoteCallServiceImpl {
86
91
  // Single-request path.
87
92
  let handler = self.python_handler.clone();
88
93
  tokio::spawn(async move {
89
- match handler.call(all_bytes, metadata, peer).await {
94
+ match handler.call(all_bytes, function_name, metadata, peer).await {
90
95
  Ok(response_chunks) => {
91
96
  for chunk in response_chunks {
92
97
  let msg = CallFunctionResponse {
@@ -0,0 +1 @@
1
+ __version__ = "1.6.1"
@@ -0,0 +1,329 @@
1
+ from __future__ import annotations
2
+
3
+ import atexit
4
+ import contextlib
5
+ import importlib
6
+ import os
7
+ from collections.abc import Iterator, Mapping, Sequence
8
+ from typing import Any, cast
9
+
10
+ _TRACER_NAME = "chalk_remote_call"
11
+ _TRACE_CONTEXT_METADATA_KEYS = ("traceparent", "tracestate", "baggage")
12
+ _REMOTE_FUNCTION_TRACE_POLICY_ENV_VAR = "CHALK_REMOTE_FUNCTION_TRACE_POLICY"
13
+ _REMOTE_FUNCTION_TRACE_SAMPLE_RATE_ENV_VAR = "CHALK_REMOTE_FUNCTION_TRACE_SAMPLE_RATE"
14
+ _TRACE_POLICY_PARENT_BASED_ALWAYS_OFF = "parentbased_always_off"
15
+ _TRACE_POLICY_PARENT_BASED_TRACE_ID_RATIO = "parentbased_traceidratio"
16
+ _TRACE_POLICY_ALWAYS_OFF = "always_off"
17
+ _TRACE_POLICIES = frozenset(
18
+ {
19
+ _TRACE_POLICY_PARENT_BASED_ALWAYS_OFF,
20
+ _TRACE_POLICY_PARENT_BASED_TRACE_ID_RATIO,
21
+ _TRACE_POLICY_ALWAYS_OFF,
22
+ }
23
+ )
24
+ _missing_otel_modules = object()
25
+ _missing_always_off_tracer = object()
26
+ _otel_modules: tuple[Any, Any, Any] | object | None = None
27
+ _always_off_tracer: Any | object | None = None
28
+ _runtime_tracing_configured = False
29
+ _runtime_tracer_provider: Any | None = None
30
+
31
+
32
+ def _raw_metadata(context_metadata: Any) -> Mapping[str, Any] | None:
33
+ if not isinstance(context_metadata, Mapping):
34
+ return None
35
+
36
+ raw = context_metadata.get("metadata", context_metadata)
37
+ if isinstance(raw, Mapping):
38
+ return raw
39
+ return None
40
+
41
+
42
+ def _collect_trace_metadata(
43
+ context_metadata: Any,
44
+ ) -> tuple[Mapping[str, Any] | None, Mapping[str, Any] | None]:
45
+ if isinstance(context_metadata, Mapping):
46
+ first = _raw_metadata(context_metadata)
47
+ if first is not None and first.get("traceparent"):
48
+ return first, first
49
+ return first, None
50
+
51
+ first: Mapping[str, Any] | None = None
52
+ if isinstance(context_metadata, Sequence) and not isinstance(context_metadata, str | bytes):
53
+ for item in context_metadata:
54
+ metadata = _raw_metadata(item)
55
+ if metadata is None:
56
+ continue
57
+
58
+ if first is None:
59
+ first = metadata
60
+ if metadata.get("traceparent"):
61
+ return first, metadata
62
+
63
+ return first, None
64
+
65
+
66
+ def _trace_policy() -> str | None:
67
+ raw_policy = os.environ.get(_REMOTE_FUNCTION_TRACE_POLICY_ENV_VAR)
68
+ if raw_policy is None:
69
+ return None
70
+
71
+ policy = raw_policy.strip().lower()
72
+ if policy in _TRACE_POLICIES:
73
+ return policy
74
+ return None
75
+
76
+
77
+ def _trace_sample_rate() -> float | None:
78
+ raw_rate = os.environ.get(_REMOTE_FUNCTION_TRACE_SAMPLE_RATE_ENV_VAR)
79
+ if raw_rate is None:
80
+ return None
81
+
82
+ try:
83
+ rate = float(raw_rate)
84
+ except ValueError:
85
+ return None
86
+
87
+ if rate < 0.0 or rate > 1.0:
88
+ return None
89
+ return rate
90
+
91
+
92
+ def _otel_exporter_configured() -> bool:
93
+ return bool(os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT") or os.environ.get("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT"))
94
+
95
+
96
+ def _configure_runtime_tracing(sample_rate: float) -> None:
97
+ global _runtime_tracing_configured, _runtime_tracer_provider
98
+ if _runtime_tracing_configured:
99
+ return
100
+
101
+ try:
102
+ resources = importlib.import_module("opentelemetry.sdk.resources")
103
+ sdk_trace = importlib.import_module("opentelemetry.sdk.trace")
104
+ sdk_sampling = importlib.import_module("opentelemetry.sdk.trace.sampling")
105
+ trace_api = importlib.import_module("opentelemetry.trace")
106
+ except Exception:
107
+ _runtime_tracing_configured = True
108
+ return
109
+
110
+ try:
111
+ provider = sdk_trace.TracerProvider(
112
+ resource=resources.Resource.create({"service.name": os.environ.get("CHALK_SERVICE") or _TRACER_NAME}),
113
+ sampler=sdk_sampling.ParentBased(sdk_sampling.TraceIdRatioBased(sample_rate)),
114
+ )
115
+
116
+ if _otel_exporter_configured():
117
+ try:
118
+ otlp_exporter = importlib.import_module("opentelemetry.exporter.otlp.proto.grpc.trace_exporter")
119
+ sdk_export = importlib.import_module("opentelemetry.sdk.trace.export")
120
+ provider.add_span_processor(sdk_export.BatchSpanProcessor(otlp_exporter.OTLPSpanExporter()))
121
+ except Exception:
122
+ pass
123
+
124
+ trace_api.set_tracer_provider(provider)
125
+ _runtime_tracer_provider = provider
126
+ atexit.register(provider.shutdown)
127
+ except Exception:
128
+ pass
129
+ _runtime_tracing_configured = True
130
+
131
+
132
+ def _sample_rate_for_inherited_parent(policy: str) -> float | None:
133
+ if policy == _TRACE_POLICY_PARENT_BASED_ALWAYS_OFF:
134
+ return 0.0
135
+ if policy == _TRACE_POLICY_PARENT_BASED_TRACE_ID_RATIO:
136
+ return _trace_sample_rate()
137
+ return None
138
+
139
+
140
+ def _span_metadata(
141
+ first_metadata: Mapping[str, Any] | None,
142
+ traceparent_metadata: Mapping[str, Any] | None,
143
+ policy: str,
144
+ ) -> tuple[Mapping[str, Any] | None, Any | None, Any]:
145
+ if traceparent_metadata is not None:
146
+ carrier = _trace_carrier(traceparent_metadata)
147
+ if not carrier:
148
+ return None, None, None
149
+
150
+ otel_modules = _get_otel_modules()
151
+ if otel_modules is None:
152
+ return None, None, None
153
+ _, propagate, trace = otel_modules
154
+
155
+ parent_context = propagate.extract(carrier)
156
+ parent_span_context = trace.get_current_span(parent_context).get_span_context()
157
+ if not parent_span_context.is_valid:
158
+ return None, None, None
159
+
160
+ sample_rate = _sample_rate_for_inherited_parent(policy)
161
+ if sample_rate is None:
162
+ return None, None, None
163
+ _configure_runtime_tracing(sample_rate)
164
+ return traceparent_metadata, parent_context, otel_modules
165
+
166
+ if policy != _TRACE_POLICY_PARENT_BASED_TRACE_ID_RATIO:
167
+ return None, None, None
168
+
169
+ sample_rate = _trace_sample_rate()
170
+ if sample_rate is None:
171
+ return None, None, None
172
+
173
+ _configure_runtime_tracing(sample_rate)
174
+ otel_modules = _get_otel_modules()
175
+ if otel_modules is None:
176
+ return None, None, None
177
+ return first_metadata, None, otel_modules
178
+
179
+
180
+ def _always_off_span_metadata(
181
+ first_metadata: Mapping[str, Any] | None,
182
+ traceparent_metadata: Mapping[str, Any] | None,
183
+ ) -> tuple[Mapping[str, Any] | None, Any | None, Any | None, Any | None]:
184
+ otel_modules = _get_otel_modules()
185
+ if otel_modules is None:
186
+ return None, None, None, None
187
+ _, propagate, trace = otel_modules
188
+
189
+ parent_context = None
190
+ if traceparent_metadata is not None:
191
+ carrier = _trace_carrier(traceparent_metadata)
192
+ if carrier:
193
+ extracted_context = propagate.extract(carrier)
194
+ parent_span_context = trace.get_current_span(extracted_context).get_span_context()
195
+ if parent_span_context.is_valid:
196
+ parent_context = extracted_context
197
+
198
+ tracer = _get_always_off_tracer()
199
+ if tracer is None:
200
+ return None, None, None, None
201
+ return traceparent_metadata or first_metadata, parent_context, otel_modules, tracer
202
+
203
+
204
+ def _trace_carrier(metadata: Mapping[str, Any]) -> dict[str, str]:
205
+ return {
206
+ str(key).lower(): str(value)
207
+ for key, value in metadata.items()
208
+ if str(key).lower() in _TRACE_CONTEXT_METADATA_KEYS and value
209
+ }
210
+
211
+
212
+ def _function_name(context_metadata: Any) -> str:
213
+ if isinstance(context_metadata, Mapping):
214
+ return str(context_metadata.get("function_name") or "") or "unknown"
215
+
216
+ if isinstance(context_metadata, Sequence) and not isinstance(context_metadata, str | bytes):
217
+ for item in context_metadata:
218
+ if isinstance(item, Mapping):
219
+ function_name = str(item.get("function_name") or "")
220
+ if function_name:
221
+ return function_name
222
+
223
+ return "unknown"
224
+
225
+
226
+ def _get_tracer(trace_api: Any) -> Any:
227
+ return trace_api.get_tracer(_TRACER_NAME)
228
+
229
+
230
+ def _get_otel_modules() -> tuple[Any, Any, Any] | None:
231
+ global _otel_modules
232
+ if _otel_modules is None:
233
+ try:
234
+ _otel_modules = (
235
+ importlib.import_module("opentelemetry.context"),
236
+ importlib.import_module("opentelemetry.propagate"),
237
+ importlib.import_module("opentelemetry.trace"),
238
+ )
239
+ except ImportError:
240
+ _otel_modules = _missing_otel_modules
241
+
242
+ if _otel_modules is _missing_otel_modules:
243
+ return None
244
+ return cast(tuple[Any, Any, Any], _otel_modules)
245
+
246
+
247
+ def _get_always_off_tracer() -> Any | None:
248
+ global _always_off_tracer
249
+ if _always_off_tracer is None:
250
+ try:
251
+ sdk_trace = importlib.import_module("opentelemetry.sdk.trace")
252
+ sdk_sampling = importlib.import_module("opentelemetry.sdk.trace.sampling")
253
+ except ImportError:
254
+ _always_off_tracer = _missing_always_off_tracer
255
+ else:
256
+ # Keep always-off local to this invocation instead of replacing the
257
+ # process-wide tracer provider.
258
+ provider = sdk_trace.TracerProvider(sampler=sdk_sampling.ALWAYS_OFF)
259
+ _always_off_tracer = provider.get_tracer(_TRACER_NAME)
260
+
261
+ if _always_off_tracer is _missing_always_off_tracer:
262
+ return None
263
+ return _always_off_tracer
264
+
265
+
266
+ @contextlib.contextmanager
267
+ def remote_function_invocation_span(
268
+ context_metadata: Any,
269
+ coalesced_count: int | None = None,
270
+ ) -> Iterator[None]:
271
+ policy = _trace_policy()
272
+ if policy is None:
273
+ yield
274
+ return
275
+
276
+ first_metadata, traceparent_metadata = _collect_trace_metadata(context_metadata)
277
+ if policy == _TRACE_POLICY_PARENT_BASED_ALWAYS_OFF and traceparent_metadata is None:
278
+ yield
279
+ return
280
+
281
+ if policy == _TRACE_POLICY_ALWAYS_OFF:
282
+ _metadata, parent_context, otel_modules, tracer = _always_off_span_metadata(
283
+ first_metadata,
284
+ traceparent_metadata,
285
+ )
286
+ else:
287
+ _metadata, parent_context, otel_modules = _span_metadata(
288
+ first_metadata,
289
+ traceparent_metadata,
290
+ policy,
291
+ )
292
+ tracer = _get_tracer(otel_modules[2]) if otel_modules is not None else None
293
+
294
+ if otel_modules is None:
295
+ yield
296
+ return
297
+ if tracer is None:
298
+ yield
299
+ return
300
+ otel_context, _, trace = otel_modules
301
+
302
+ SpanKind = trace.SpanKind
303
+ Status = trace.Status
304
+ StatusCode = trace.StatusCode
305
+ function_name = _function_name(context_metadata)
306
+ span_kwargs: dict[str, Any] = {"kind": SpanKind.SERVER}
307
+ if parent_context is not None:
308
+ span_kwargs["context"] = parent_context
309
+
310
+ with tracer.start_as_current_span("chalkcompute.remote_function.invoke", **span_kwargs) as span:
311
+ span.set_attribute("chalk.remote_function.name", function_name)
312
+ if coalesced_count is not None:
313
+ span.set_attribute(
314
+ "chalk.remote_function.coalesced_count",
315
+ coalesced_count,
316
+ )
317
+ if parent_context is not None and not span.get_span_context().is_valid:
318
+ token = otel_context.attach(parent_context)
319
+ try:
320
+ yield
321
+ finally:
322
+ otel_context.detach(token)
323
+ return
324
+ try:
325
+ yield
326
+ except Exception as exc:
327
+ span.record_exception(exc)
328
+ span.set_status(Status(StatusCode.ERROR, str(exc)))
329
+ raise
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chalk-remote-call-python
3
- Version: 1.5.1
3
+ Version: 1.6.1
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.5.1"
@@ -1,150 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import contextlib
4
- import importlib
5
- import os
6
- from collections.abc import Callable, Iterator, Mapping, Sequence
7
- from typing import Any, cast
8
-
9
- _TRACER_NAME = "chalk_remote_call"
10
- _TRACE_CONTEXT_METADATA_KEYS = ("traceparent", "tracestate", "baggage")
11
- _REMOTE_FUNCTION_NAME_METADATA = "x-chalk-function-name"
12
- _REMOTE_FUNCTION_TRACING_ENV_VAR = "CHALK_ENABLE_REMOTE_FUNCTION_TRACING"
13
- _missing_sdk_tracer = object()
14
- _missing_otel_modules = object()
15
- _sdk_get_tracer: Callable[[], Any] | object | None = None
16
- _otel_modules: tuple[Any, Any, Any] | object | None = None
17
-
18
-
19
- def _raw_metadata(context_metadata: Any) -> Mapping[str, Any] | None:
20
- if not isinstance(context_metadata, Mapping):
21
- return None
22
-
23
- raw = context_metadata.get("metadata", context_metadata)
24
- if isinstance(raw, Mapping):
25
- return raw
26
- return None
27
-
28
-
29
- def _first_context_with_traceparent(context_metadata: Any) -> Mapping[str, Any] | None:
30
- if isinstance(context_metadata, Sequence) and not isinstance(context_metadata, str | bytes):
31
- for item in context_metadata:
32
- metadata = _raw_metadata(item)
33
- if metadata is not None and metadata.get("traceparent"):
34
- return metadata
35
- return None
36
-
37
- metadata = _raw_metadata(context_metadata)
38
- if metadata is not None and metadata.get("traceparent"):
39
- return metadata
40
- return None
41
-
42
-
43
- def _trace_carrier(metadata: Mapping[str, Any]) -> dict[str, str]:
44
- return {
45
- str(key).lower(): str(value)
46
- for key, value in metadata.items()
47
- if str(key).lower() in _TRACE_CONTEXT_METADATA_KEYS and value
48
- }
49
-
50
-
51
- def _function_name(metadata: Mapping[str, Any]) -> str:
52
- return str(metadata.get(_REMOTE_FUNCTION_NAME_METADATA) or "") or "unknown"
53
-
54
-
55
- def _get_tracer(trace_api: Any) -> Any:
56
- global _sdk_get_tracer
57
- if _sdk_get_tracer is None:
58
- try:
59
- sdk_tracing = importlib.import_module("chalkcompute._tracing")
60
- except ImportError:
61
- _sdk_get_tracer = _missing_sdk_tracer
62
- else:
63
- _sdk_get_tracer = sdk_tracing.get_tracer
64
-
65
- sdk_get_tracer = _sdk_get_tracer
66
- if callable(sdk_get_tracer):
67
- try:
68
- return sdk_get_tracer()
69
- except ImportError:
70
- return trace_api.get_tracer(_TRACER_NAME)
71
- return trace_api.get_tracer(_TRACER_NAME)
72
-
73
-
74
- def _get_otel_modules() -> tuple[Any, Any, Any] | None:
75
- global _otel_modules
76
- if _otel_modules is None:
77
- try:
78
- _otel_modules = (
79
- importlib.import_module("opentelemetry.context"),
80
- importlib.import_module("opentelemetry.propagate"),
81
- importlib.import_module("opentelemetry.trace"),
82
- )
83
- except ImportError:
84
- _otel_modules = _missing_otel_modules
85
-
86
- if _otel_modules is _missing_otel_modules:
87
- return None
88
- return cast(tuple[Any, Any, Any], _otel_modules)
89
-
90
-
91
- @contextlib.contextmanager
92
- def remote_function_invocation_span(
93
- context_metadata: Any,
94
- coalesced_count: int | None = None,
95
- ) -> Iterator[None]:
96
- if os.environ.get(_REMOTE_FUNCTION_TRACING_ENV_VAR, "") != "1":
97
- yield
98
- return
99
-
100
- metadata = _first_context_with_traceparent(context_metadata)
101
- if metadata is None:
102
- yield
103
- return
104
-
105
- carrier = _trace_carrier(metadata)
106
- if not carrier:
107
- yield
108
- return
109
-
110
- otel_modules = _get_otel_modules()
111
- if otel_modules is None:
112
- yield
113
- return
114
- otel_context, propagate, trace = otel_modules
115
-
116
- parent_context = propagate.extract(carrier)
117
- parent_span_context = trace.get_current_span(parent_context).get_span_context()
118
- if not parent_span_context.is_valid:
119
- yield
120
- return
121
-
122
- SpanKind = trace.SpanKind
123
- Status = trace.Status
124
- StatusCode = trace.StatusCode
125
- function_name = _function_name(metadata)
126
- tracer = _get_tracer(trace)
127
- with tracer.start_as_current_span(
128
- "chalkcompute.remote_function.invoke",
129
- context=parent_context,
130
- kind=SpanKind.SERVER,
131
- ) as span:
132
- span.set_attribute("chalk.remote_function.name", function_name)
133
- if coalesced_count is not None:
134
- span.set_attribute(
135
- "chalk.remote_function.coalesced_count",
136
- coalesced_count,
137
- )
138
- if not span.get_span_context().is_valid:
139
- token = otel_context.attach(parent_context)
140
- try:
141
- yield
142
- finally:
143
- otel_context.detach(token)
144
- return
145
- try:
146
- yield
147
- except Exception as exc:
148
- span.record_exception(exc)
149
- span.set_status(Status(StatusCode.ERROR, str(exc)))
150
- raise