chalk-remote-call-python 1.9.2__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.
- {chalk_remote_call_python-1.9.2/chalk_remote_call_python.egg-info → chalk_remote_call_python-1.10.0}/PKG-INFO +6 -3
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/README.md +5 -2
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/Cargo.lock +1 -1
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-server/Cargo.toml +1 -1
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-server/src/self_consumer.rs +161 -28
- chalk_remote_call_python-1.10.0/chalk_remote_call/_version.py +1 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/cli.py +14 -4
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/server.py +8 -7
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0/chalk_remote_call_python.egg-info}/PKG-INFO +6 -3
- chalk_remote_call_python-1.9.2/chalk_remote_call/_version.py +0 -1
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/MANIFEST.in +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/Cargo.toml +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-proto/Cargo.toml +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.auth.v1.rs +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.common.v1.rs +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.runtime.v1.rs +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.runtime.v1.tonic.rs +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/chalk.utils.v1.rs +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-proto/src/gen/descriptor.bin +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-proto/src/lib.rs +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-server/src/async_service.rs +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-server/src/coalesce.rs +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-server/src/lib.rs +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-server/src/metrics.rs +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-server/src/python_bridge.rs +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-server/src/server.rs +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/chalk-remote-call-server/src/service.rs +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/rust-toolchain.toml +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/__init__.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/__main__.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/__init__.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/__init__.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/auth/__init__.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/auth/v1/__init__.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/auth/v1/permissions_pb2.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/auth/v1/permissions_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/common/__init__.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/common/v1/__init__.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/common/v1/chalk_error_pb2.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/common/v1/chalk_error_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/runtime/__init__.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/runtime/v1/__init__.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/runtime/v1/remote_python_call_pb2.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/runtime/v1/remote_python_call_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/utils/__init__.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/utils/v1/__init__.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/utils/v1/encoding_pb2.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/utils/v1/encoding_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/utils/v1/field_change_pb2.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/utils/v1/field_change_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/utils/v1/sensitive_pb2.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_gen/chalk/utils/v1/sensitive_pb2_grpc.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_native.pyi +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/arrow_utils.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/errors.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/handler_loader.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/input_transform.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/logging.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/servicer.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/tracing.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call_python.egg-info/SOURCES.txt +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call_python.egg-info/dependency_links.txt +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call_python.egg-info/entry_points.txt +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call_python.egg-info/requires.txt +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call_python.egg-info/top_level.txt +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/pyproject.toml +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/setup.cfg +0 -0
- {chalk_remote_call_python-1.9.2 → 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.
|
|
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
|
|
@@ -110,8 +110,11 @@ python -m chalk_remote_call --handler my_handler.handler
|
|
|
110
110
|
|
|
111
111
|
The CLI writes Python logs to stderr as one JSON object per line, with separate
|
|
112
112
|
`timestamp`, `severity`, `logger`, and `message` fields. Exceptions include an
|
|
113
|
-
`exception.stacktrace` field
|
|
114
|
-
|
|
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.
|
|
115
118
|
|
|
116
119
|
Missing optional StatsD endpoints or a metrics-bus topic do not emit warnings.
|
|
117
120
|
Failures initializing configured metrics sinks still warn. Routine customer
|
|
@@ -72,8 +72,11 @@ python -m chalk_remote_call --handler my_handler.handler
|
|
|
72
72
|
|
|
73
73
|
The CLI writes Python logs to stderr as one JSON object per line, with separate
|
|
74
74
|
`timestamp`, `severity`, `logger`, and `message` fields. Exceptions include an
|
|
75
|
-
`exception.stacktrace` field
|
|
76
|
-
|
|
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.
|
|
77
80
|
|
|
78
81
|
Missing optional StatsD endpoints or a metrics-bus topic do not emit warnings.
|
|
79
82
|
Failures initializing configured metrics sinks still warn. Routine customer
|
{chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/Cargo.lock
RENAMED
|
@@ -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=
|
|
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",
|
|
@@ -37,6 +37,6 @@ futures-util = "0.3"
|
|
|
37
37
|
# `[patch.crates-io]` here it builds against vanilla crates.io redis (it only
|
|
38
38
|
# uses standard APIs). Metrics and StatsD share a revision so their client types agree.
|
|
39
39
|
[target.'cfg(target_os = "linux")'.dependencies]
|
|
40
|
-
chalk-queue = { git = "https://github.com/chalk-ai/chalk-private.git", rev = "
|
|
40
|
+
chalk-queue = { git = "https://github.com/chalk-ai/chalk-private.git", rev = "c6eed6bd7bdad1f2a7568ef206e570c719c4c64f" }
|
|
41
41
|
chalk_metrics = { git = "https://github.com/chalk-ai/chalk-private.git", rev = "a0f8932ec10d3b8340af3e98cf63b800d60b4926" }
|
|
42
42
|
chalk_statsd = { git = "https://github.com/chalk-ai/chalk-private.git", rev = "a0f8932ec10d3b8340af3e98cf63b800d60b4926" }
|
|
@@ -102,6 +102,112 @@ const STREAM_RECLAIM_INTERVAL: Duration = Duration::from_secs(10);
|
|
|
102
102
|
/// inbound network peer on this path.
|
|
103
103
|
const PEER: &str = "fnq-self-consumer";
|
|
104
104
|
|
|
105
|
+
/// Selects where a queued call publishes its result without allowing a shared
|
|
106
|
+
/// batch stream to lose the call identity needed by the collector.
|
|
107
|
+
#[derive(Debug, PartialEq, Eq)]
|
|
108
|
+
enum ResultDestination {
|
|
109
|
+
PerCall {
|
|
110
|
+
stream_key: String,
|
|
111
|
+
},
|
|
112
|
+
Batch {
|
|
113
|
+
stream_key: String,
|
|
114
|
+
call_id: String,
|
|
115
|
+
row_index: u64,
|
|
116
|
+
},
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
impl ResultDestination {
|
|
120
|
+
fn for_call(queue: &FunctionQueue, work: &QueuedCall, call_uuid: Uuid) -> Self {
|
|
121
|
+
let batch = work.batch_result.as_ref().map(|batch_result| {
|
|
122
|
+
(
|
|
123
|
+
queue.batch_stream_key_for(&batch_result.batch_id),
|
|
124
|
+
work.call_id.clone(),
|
|
125
|
+
batch_result.row_index,
|
|
126
|
+
)
|
|
127
|
+
});
|
|
128
|
+
Self::from_routing(queue.stream_key_for(call_uuid), batch)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
fn from_routing(per_call_stream_key: String, batch: Option<(String, String, u64)>) -> Self {
|
|
132
|
+
match batch {
|
|
133
|
+
Some((stream_key, call_id, row_index)) => Self::Batch {
|
|
134
|
+
stream_key,
|
|
135
|
+
call_id,
|
|
136
|
+
row_index,
|
|
137
|
+
},
|
|
138
|
+
None => Self::PerCall {
|
|
139
|
+
stream_key: per_call_stream_key,
|
|
140
|
+
},
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async fn write_chunk(
|
|
145
|
+
&self,
|
|
146
|
+
conn: &mut ConnectionManager,
|
|
147
|
+
seq: u64,
|
|
148
|
+
data: &[u8],
|
|
149
|
+
) -> Result<(), redis::RedisError> {
|
|
150
|
+
match self {
|
|
151
|
+
Self::PerCall { stream_key } => {
|
|
152
|
+
stream_writer::write_chunk(conn, stream_key, seq, data).await
|
|
153
|
+
}
|
|
154
|
+
Self::Batch {
|
|
155
|
+
stream_key,
|
|
156
|
+
call_id,
|
|
157
|
+
row_index,
|
|
158
|
+
} => {
|
|
159
|
+
stream_writer::BatchStreamWriter::new(stream_key, call_id, *row_index)
|
|
160
|
+
.write_chunk(conn, seq, data)
|
|
161
|
+
.await
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async fn write_end(
|
|
167
|
+
&self,
|
|
168
|
+
conn: &mut ConnectionManager,
|
|
169
|
+
seq: u64,
|
|
170
|
+
) -> Result<(), redis::RedisError> {
|
|
171
|
+
match self {
|
|
172
|
+
Self::PerCall { stream_key } => stream_writer::write_end(conn, stream_key, seq).await,
|
|
173
|
+
Self::Batch {
|
|
174
|
+
stream_key,
|
|
175
|
+
call_id,
|
|
176
|
+
row_index,
|
|
177
|
+
} => {
|
|
178
|
+
stream_writer::BatchStreamWriter::new(stream_key, call_id, *row_index)
|
|
179
|
+
.write_end(conn, seq)
|
|
180
|
+
.await
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async fn write_error(
|
|
186
|
+
&self,
|
|
187
|
+
conn: &mut ConnectionManager,
|
|
188
|
+
seq: u64,
|
|
189
|
+
exc_type: &str,
|
|
190
|
+
message: &str,
|
|
191
|
+
traceback: Option<&str>,
|
|
192
|
+
) -> Result<(), redis::RedisError> {
|
|
193
|
+
match self {
|
|
194
|
+
Self::PerCall { stream_key } => {
|
|
195
|
+
stream_writer::write_error(conn, stream_key, seq, exc_type, message, traceback)
|
|
196
|
+
.await
|
|
197
|
+
}
|
|
198
|
+
Self::Batch {
|
|
199
|
+
stream_key,
|
|
200
|
+
call_id,
|
|
201
|
+
row_index,
|
|
202
|
+
} => {
|
|
203
|
+
stream_writer::BatchStreamWriter::new(stream_key, call_id, *row_index)
|
|
204
|
+
.write_error(conn, seq, exc_type, message, traceback)
|
|
205
|
+
.await
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
105
211
|
/// Configuration for the self-consumer, read from the environment.
|
|
106
212
|
///
|
|
107
213
|
/// [`from_env`](Self::from_env) returns `None` when `CHALK_FNQ_REDIS_URL` is
|
|
@@ -694,7 +800,7 @@ async fn coalesced_execute_and_write(
|
|
|
694
800
|
metrics_pipeline: Option<&Arc<PublishingMetricsPipeline>>,
|
|
695
801
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
696
802
|
let call_uuid = Uuid::parse_str(&work.call_id)?;
|
|
697
|
-
let
|
|
803
|
+
let result_destination = ResultDestination::for_call(queue, &work, call_uuid);
|
|
698
804
|
let ipc_bytes = work.call.feather_bytes()?;
|
|
699
805
|
let function_name = work.call.name.clone();
|
|
700
806
|
let mut metadata = work.metadata.otel_headers.clone();
|
|
@@ -736,8 +842,10 @@ async fn coalesced_execute_and_write(
|
|
|
736
842
|
|
|
737
843
|
match response {
|
|
738
844
|
Some(Ok(resp)) => {
|
|
739
|
-
|
|
740
|
-
|
|
845
|
+
result_destination
|
|
846
|
+
.write_chunk(conn, 1, &resp.feather_stream)
|
|
847
|
+
.await?;
|
|
848
|
+
result_destination.write_end(conn, 2).await?;
|
|
741
849
|
let _ = queue
|
|
742
850
|
.complete_status(
|
|
743
851
|
call_uuid,
|
|
@@ -751,15 +859,9 @@ async fn coalesced_execute_and_write(
|
|
|
751
859
|
Some(Err(status)) => {
|
|
752
860
|
// Parity with the enqueue/poll path: only a message string is
|
|
753
861
|
// available, so exc_type is a fixed placeholder.
|
|
754
|
-
|
|
755
|
-
conn,
|
|
756
|
-
|
|
757
|
-
1,
|
|
758
|
-
"RemoteFunctionError",
|
|
759
|
-
status.message(),
|
|
760
|
-
None,
|
|
761
|
-
)
|
|
762
|
-
.await?;
|
|
862
|
+
result_destination
|
|
863
|
+
.write_error(conn, 1, "RemoteFunctionError", status.message(), None)
|
|
864
|
+
.await?;
|
|
763
865
|
let summary: String = status.message().chars().take(200).collect();
|
|
764
866
|
let _ = queue
|
|
765
867
|
.complete_status(call_uuid, "failed", &summary, Some(processing_trace_id))
|
|
@@ -771,7 +873,8 @@ async fn coalesced_execute_and_write(
|
|
|
771
873
|
// happen (it always sends one Ok/Err per caller), but treat a
|
|
772
874
|
// missing response as a failure rather than leaving the poller hung.
|
|
773
875
|
let msg = "coalesced dispatch produced no response";
|
|
774
|
-
|
|
876
|
+
result_destination
|
|
877
|
+
.write_error(conn, 1, "RemoteFunctionError", msg, None)
|
|
775
878
|
.await?;
|
|
776
879
|
let _ = queue
|
|
777
880
|
.complete_status(call_uuid, "failed", msg, Some(processing_trace_id))
|
|
@@ -810,7 +913,7 @@ async fn execute_and_write(
|
|
|
810
913
|
metrics_pipeline: Option<&Arc<PublishingMetricsPipeline>>,
|
|
811
914
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
812
915
|
let call_uuid = Uuid::parse_str(&work.call_id)?;
|
|
813
|
-
let
|
|
916
|
+
let result_destination = ResultDestination::for_call(queue, &work, call_uuid);
|
|
814
917
|
let ipc_bytes = work.call.feather_bytes()?;
|
|
815
918
|
let function_name = work.call.name.clone();
|
|
816
919
|
let mut metadata = work.metadata.otel_headers.clone();
|
|
@@ -857,7 +960,7 @@ async fn execute_and_write(
|
|
|
857
960
|
biased;
|
|
858
961
|
res = &mut handler_fut => break res,
|
|
859
962
|
_ = tokio::time::timeout(flush_timeout, notify_handle.notified()) => {
|
|
860
|
-
if let Err(e) = flush_new(conn, &
|
|
963
|
+
if let Err(e) = flush_new(conn, &result_destination, &buffer, &mut flushed, &mut seq).await {
|
|
861
964
|
// Non-fatal: keep draining; the cursor didn't advance past the
|
|
862
965
|
// failed chunk, so the next wait retries it without duplicating.
|
|
863
966
|
warn!(call_id = %work.call_id, error = %e, "failed to flush partial results; retrying");
|
|
@@ -883,13 +986,13 @@ async fn execute_and_write(
|
|
|
883
986
|
// this is everything). This happens even if the handler panicked mid-execution,
|
|
884
987
|
// so partial output is always preserved. Parity with the enqueue/poll path: a
|
|
885
988
|
// generator that yields then raises surfaces its chunks followed by the error.
|
|
886
|
-
if let Err(e) = flush_new(conn, &
|
|
989
|
+
if let Err(e) = flush_new(conn, &result_destination, &buffer, &mut flushed, &mut seq).await {
|
|
887
990
|
warn!(call_id = %work.call_id, error = %e, "failed to flush final chunks");
|
|
888
991
|
}
|
|
889
992
|
|
|
890
993
|
match result {
|
|
891
994
|
Ok(()) => {
|
|
892
|
-
|
|
995
|
+
result_destination.write_end(conn, seq).await?;
|
|
893
996
|
let _ = queue
|
|
894
997
|
.complete_status(
|
|
895
998
|
call_uuid,
|
|
@@ -901,15 +1004,15 @@ async fn execute_and_write(
|
|
|
901
1004
|
info!(call_id = %work.call_id, chunks = flushed, "queued call completed");
|
|
902
1005
|
}
|
|
903
1006
|
Err(e) => {
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
1007
|
+
result_destination
|
|
1008
|
+
.write_error(
|
|
1009
|
+
conn,
|
|
1010
|
+
seq,
|
|
1011
|
+
"RemoteFunctionError",
|
|
1012
|
+
&e.to_string(),
|
|
1013
|
+
Some(e.details()),
|
|
1014
|
+
)
|
|
1015
|
+
.await?;
|
|
913
1016
|
let summary: String = e.to_string().chars().take(200).collect();
|
|
914
1017
|
let _ = queue
|
|
915
1018
|
.complete_status(call_uuid, "failed", &summary, Some(processing_trace_id))
|
|
@@ -932,7 +1035,7 @@ async fn execute_and_write(
|
|
|
932
1035
|
/// failed chunk, so a retry re-sends only the unwritten tail (no gaps, no dupes).
|
|
933
1036
|
async fn flush_new(
|
|
934
1037
|
conn: &mut ConnectionManager,
|
|
935
|
-
|
|
1038
|
+
result_destination: &ResultDestination,
|
|
936
1039
|
buffer: &Arc<Mutex<ChunkBuffer>>,
|
|
937
1040
|
flushed: &mut usize,
|
|
938
1041
|
seq: &mut u64,
|
|
@@ -947,7 +1050,7 @@ async fn flush_new(
|
|
|
947
1050
|
.collect()
|
|
948
1051
|
};
|
|
949
1052
|
for chunk in &new {
|
|
950
|
-
|
|
1053
|
+
result_destination.write_chunk(conn, *seq, chunk).await?;
|
|
951
1054
|
*seq += 1;
|
|
952
1055
|
*flushed += 1;
|
|
953
1056
|
}
|
|
@@ -1022,6 +1125,35 @@ mod tests {
|
|
|
1022
1125
|
assert_eq!(DEFAULT_FLUSH_TIMEOUT_MS, 100);
|
|
1023
1126
|
}
|
|
1024
1127
|
|
|
1128
|
+
#[test]
|
|
1129
|
+
fn result_destination_preserves_per_call_stream() {
|
|
1130
|
+
assert_eq!(
|
|
1131
|
+
ResultDestination::from_routing("tenant:gen:call-1:stream".to_string(), None),
|
|
1132
|
+
ResultDestination::PerCall {
|
|
1133
|
+
stream_key: "tenant:gen:call-1:stream".to_string(),
|
|
1134
|
+
}
|
|
1135
|
+
);
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
#[test]
|
|
1139
|
+
fn result_destination_routes_batch_call_with_correlation() {
|
|
1140
|
+
assert_eq!(
|
|
1141
|
+
ResultDestination::from_routing(
|
|
1142
|
+
"unused-per-call-stream".to_string(),
|
|
1143
|
+
Some((
|
|
1144
|
+
"tenant:batch:batch-1:results".to_string(),
|
|
1145
|
+
"call-1".to_string(),
|
|
1146
|
+
42,
|
|
1147
|
+
)),
|
|
1148
|
+
),
|
|
1149
|
+
ResultDestination::Batch {
|
|
1150
|
+
stream_key: "tenant:batch:batch-1:results".to_string(),
|
|
1151
|
+
call_id: "call-1".to_string(),
|
|
1152
|
+
row_index: 42,
|
|
1153
|
+
}
|
|
1154
|
+
);
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1025
1157
|
#[test]
|
|
1026
1158
|
fn test_config_from_env_returns_none_when_redis_url_unset() {
|
|
1027
1159
|
let _env = lock_env();
|
|
@@ -1043,6 +1175,7 @@ mod tests {
|
|
|
1043
1175
|
std::env::remove_var(ENV_FLUSH_TIMEOUT_MS);
|
|
1044
1176
|
std::env::remove_var(ENV_QUEUE_PROTOCOL);
|
|
1045
1177
|
std::env::remove_var(ENV_STREAM_READ_COUNT);
|
|
1178
|
+
std::env::remove_var(ENV_MAX_CONCURRENT);
|
|
1046
1179
|
|
|
1047
1180
|
let config = SelfConsumerConfig::from_env().expect("config should parse");
|
|
1048
1181
|
assert_eq!(config.sem_ttl, Duration::from_secs(DEFAULT_SEM_TTL_SECS));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.10.0"
|
|
@@ -6,7 +6,6 @@ import os
|
|
|
6
6
|
import sys
|
|
7
7
|
|
|
8
8
|
from chalk_remote_call.handler_loader import load_function, load_handler
|
|
9
|
-
from chalk_remote_call.input_transform import parse_input_args
|
|
10
9
|
from chalk_remote_call.logging import JsonFormatter
|
|
11
10
|
from chalk_remote_call.server import serve
|
|
12
11
|
|
|
@@ -23,6 +22,17 @@ def _env_int(name: str) -> int | None:
|
|
|
23
22
|
|
|
24
23
|
|
|
25
24
|
def main() -> None:
|
|
25
|
+
log_handler = logging.StreamHandler(sys.stderr)
|
|
26
|
+
log_handler.setFormatter(JsonFormatter())
|
|
27
|
+
logging.basicConfig(level=logging.INFO, handlers=[log_handler], force=True)
|
|
28
|
+
try:
|
|
29
|
+
_run()
|
|
30
|
+
except Exception:
|
|
31
|
+
logging.getLogger(__name__).exception("Remote-call server failed")
|
|
32
|
+
sys.exit(1)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _run() -> None:
|
|
26
36
|
parser = argparse.ArgumentParser(
|
|
27
37
|
prog="chalk-remote-call",
|
|
28
38
|
description="Start a gRPC server implementing the Chalk RemoteCallService.",
|
|
@@ -100,9 +110,9 @@ def main() -> None:
|
|
|
100
110
|
)
|
|
101
111
|
args = parser.parse_args()
|
|
102
112
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
113
|
+
logging.getLogger().setLevel(args.log_level)
|
|
114
|
+
|
|
115
|
+
from chalk_remote_call.input_transform import parse_input_args
|
|
106
116
|
|
|
107
117
|
# Load handler
|
|
108
118
|
try:
|
{chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/server.py
RENAMED
|
@@ -5,13 +5,6 @@ import os
|
|
|
5
5
|
from collections.abc import Callable
|
|
6
6
|
from typing import Any, Literal
|
|
7
7
|
|
|
8
|
-
from chalk_remote_call._native import start_server
|
|
9
|
-
from chalk_remote_call.servicer import (
|
|
10
|
-
process_batches,
|
|
11
|
-
process_batches_coalesced,
|
|
12
|
-
process_batches_coalesced_combined,
|
|
13
|
-
)
|
|
14
|
-
|
|
15
8
|
logger = logging.getLogger(__name__)
|
|
16
9
|
|
|
17
10
|
_BATCHING_MODE_ENV = "CHALK_REMOTE_CALL_BATCHING_MODE"
|
|
@@ -75,6 +68,14 @@ def serve(
|
|
|
75
68
|
CHALK_REMOTE_CALL_BATCHING_MODE env var when set; otherwise the
|
|
76
69
|
env var (or "per_caller") wins.
|
|
77
70
|
"""
|
|
71
|
+
# Keep package imports lightweight so the CLI can configure logging before dependency failures.
|
|
72
|
+
from chalk_remote_call._native import start_server
|
|
73
|
+
from chalk_remote_call.servicer import (
|
|
74
|
+
process_batches,
|
|
75
|
+
process_batches_coalesced,
|
|
76
|
+
process_batches_coalesced_combined,
|
|
77
|
+
)
|
|
78
|
+
|
|
78
79
|
if on_startup is not None:
|
|
79
80
|
logger.info("Running startup hook...")
|
|
80
81
|
on_startup()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: chalk-remote-call-python
|
|
3
|
-
Version: 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
|
|
@@ -110,8 +110,11 @@ python -m chalk_remote_call --handler my_handler.handler
|
|
|
110
110
|
|
|
111
111
|
The CLI writes Python logs to stderr as one JSON object per line, with separate
|
|
112
112
|
`timestamp`, `severity`, `logger`, and `message` fields. Exceptions include an
|
|
113
|
-
`exception.stacktrace` field
|
|
114
|
-
|
|
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.
|
|
115
118
|
|
|
116
119
|
Missing optional StatsD endpoints or a metrics-bus topic do not emit warnings.
|
|
117
120
|
Failures initializing configured metrics sinks still warn. Routine customer
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "1.9.2"
|
|
File without changes
|
{chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk-remote-call-rs/Cargo.toml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/__init__.py
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/__main__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/_native.pyi
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/arrow_utils.py
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/errors.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/logging.py
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/servicer.py
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.0}/chalk_remote_call/tracing.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|