chalk-remote-call-python 1.9.2__tar.gz → 1.10.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.
- {chalk_remote_call_python-1.9.2/chalk_remote_call_python.egg-info → chalk_remote_call_python-1.10.1}/PKG-INFO +6 -3
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/README.md +5 -2
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/chalk-remote-call-rs/Cargo.lock +1 -1
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/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.1}/chalk-remote-call-rs/chalk-remote-call-server/src/self_consumer.rs +194 -31
- chalk_remote_call_python-1.10.1/chalk_remote_call/_version.py +1 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/chalk_remote_call/cli.py +14 -4
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/chalk_remote_call/server.py +8 -7
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1/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.1}/MANIFEST.in +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/chalk-remote-call-rs/Cargo.toml +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/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.1}/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.1}/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.1}/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.1}/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.1}/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.1}/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.1}/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.1}/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.1}/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.1}/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.1}/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.1}/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.1}/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.1}/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.1}/chalk-remote-call-rs/rust-toolchain.toml +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/chalk_remote_call/__init__.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/chalk_remote_call/__main__.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/chalk_remote_call/_gen/__init__.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/chalk_remote_call/_gen/chalk/__init__.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/chalk_remote_call/_gen/chalk/auth/__init__.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/chalk_remote_call/_gen/chalk/auth/v1/__init__.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/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.1}/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.1}/chalk_remote_call/_gen/chalk/common/__init__.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/chalk_remote_call/_gen/chalk/common/v1/__init__.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/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.1}/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.1}/chalk_remote_call/_gen/chalk/runtime/__init__.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/chalk_remote_call/_gen/chalk/runtime/v1/__init__.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/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.1}/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.1}/chalk_remote_call/_gen/chalk/utils/__init__.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/chalk_remote_call/_gen/chalk/utils/v1/__init__.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/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.1}/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.1}/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.1}/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.1}/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.1}/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.1}/chalk_remote_call/_native.pyi +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/chalk_remote_call/arrow_utils.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/chalk_remote_call/errors.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/chalk_remote_call/handler_loader.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/chalk_remote_call/input_transform.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/chalk_remote_call/logging.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/chalk_remote_call/servicer.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/chalk_remote_call/tracing.py +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/chalk_remote_call_python.egg-info/SOURCES.txt +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/chalk_remote_call_python.egg-info/dependency_links.txt +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/chalk_remote_call_python.egg-info/entry_points.txt +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/chalk_remote_call_python.egg-info/requires.txt +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/chalk_remote_call_python.egg-info/top_level.txt +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/pyproject.toml +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/setup.cfg +0 -0
- {chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.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.
|
|
3
|
+
Version: 1.10.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
|
|
@@ -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.1}/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=41458754e6916245a092ddc09562bcaf4dea373e#41458754e6916245a092ddc09562bcaf4dea373e"
|
|
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 = "41458754e6916245a092ddc09562bcaf4dea373e" }
|
|
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,136 @@ 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
|
+
result_ttl: Duration,
|
|
112
|
+
},
|
|
113
|
+
Batch {
|
|
114
|
+
stream_key: String,
|
|
115
|
+
call_id: String,
|
|
116
|
+
row_index: u64,
|
|
117
|
+
},
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
impl ResultDestination {
|
|
121
|
+
fn for_call(queue: &FunctionQueue, work: &QueuedCall, call_uuid: Uuid) -> Self {
|
|
122
|
+
let batch = work.batch_result.as_ref().map(|batch_result| {
|
|
123
|
+
(
|
|
124
|
+
queue.batch_stream_key_for(&batch_result.batch_id),
|
|
125
|
+
work.call_id.clone(),
|
|
126
|
+
batch_result.row_index,
|
|
127
|
+
)
|
|
128
|
+
});
|
|
129
|
+
Self::from_routing(
|
|
130
|
+
queue.stream_key_for(call_uuid),
|
|
131
|
+
Duration::from_secs(work.result_ttl_seconds),
|
|
132
|
+
batch,
|
|
133
|
+
)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
fn from_routing(
|
|
137
|
+
per_call_stream_key: String,
|
|
138
|
+
result_ttl: Duration,
|
|
139
|
+
batch: Option<(String, String, u64)>,
|
|
140
|
+
) -> Self {
|
|
141
|
+
match batch {
|
|
142
|
+
Some((stream_key, call_id, row_index)) => Self::Batch {
|
|
143
|
+
stream_key,
|
|
144
|
+
call_id,
|
|
145
|
+
row_index,
|
|
146
|
+
},
|
|
147
|
+
None => Self::PerCall {
|
|
148
|
+
stream_key: per_call_stream_key,
|
|
149
|
+
result_ttl,
|
|
150
|
+
},
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async fn write_chunk(
|
|
155
|
+
&self,
|
|
156
|
+
conn: &mut ConnectionManager,
|
|
157
|
+
seq: u64,
|
|
158
|
+
data: &[u8],
|
|
159
|
+
) -> Result<(), redis::RedisError> {
|
|
160
|
+
match self {
|
|
161
|
+
Self::PerCall { stream_key, .. } => {
|
|
162
|
+
stream_writer::write_chunk(conn, stream_key, seq, data).await
|
|
163
|
+
}
|
|
164
|
+
Self::Batch {
|
|
165
|
+
stream_key,
|
|
166
|
+
call_id,
|
|
167
|
+
row_index,
|
|
168
|
+
} => {
|
|
169
|
+
stream_writer::BatchStreamWriter::new(stream_key, call_id, *row_index)
|
|
170
|
+
.write_chunk(conn, seq, data)
|
|
171
|
+
.await
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async fn write_end(
|
|
177
|
+
&self,
|
|
178
|
+
conn: &mut ConnectionManager,
|
|
179
|
+
seq: u64,
|
|
180
|
+
) -> Result<(), redis::RedisError> {
|
|
181
|
+
match self {
|
|
182
|
+
Self::PerCall {
|
|
183
|
+
stream_key,
|
|
184
|
+
result_ttl,
|
|
185
|
+
} => stream_writer::write_end_with_ttl(conn, stream_key, seq, *result_ttl).await,
|
|
186
|
+
Self::Batch {
|
|
187
|
+
stream_key,
|
|
188
|
+
call_id,
|
|
189
|
+
row_index,
|
|
190
|
+
} => {
|
|
191
|
+
stream_writer::BatchStreamWriter::new(stream_key, call_id, *row_index)
|
|
192
|
+
.write_end(conn, seq)
|
|
193
|
+
.await
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
async fn write_error(
|
|
199
|
+
&self,
|
|
200
|
+
conn: &mut ConnectionManager,
|
|
201
|
+
seq: u64,
|
|
202
|
+
exc_type: &str,
|
|
203
|
+
message: &str,
|
|
204
|
+
traceback: Option<&str>,
|
|
205
|
+
) -> Result<(), redis::RedisError> {
|
|
206
|
+
match self {
|
|
207
|
+
Self::PerCall {
|
|
208
|
+
stream_key,
|
|
209
|
+
result_ttl,
|
|
210
|
+
} => {
|
|
211
|
+
stream_writer::write_error_with_ttl(
|
|
212
|
+
conn,
|
|
213
|
+
stream_key,
|
|
214
|
+
seq,
|
|
215
|
+
exc_type,
|
|
216
|
+
message,
|
|
217
|
+
traceback,
|
|
218
|
+
*result_ttl,
|
|
219
|
+
)
|
|
220
|
+
.await
|
|
221
|
+
}
|
|
222
|
+
Self::Batch {
|
|
223
|
+
stream_key,
|
|
224
|
+
call_id,
|
|
225
|
+
row_index,
|
|
226
|
+
} => {
|
|
227
|
+
stream_writer::BatchStreamWriter::new(stream_key, call_id, *row_index)
|
|
228
|
+
.write_error(conn, seq, exc_type, message, traceback)
|
|
229
|
+
.await
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
105
235
|
/// Configuration for the self-consumer, read from the environment.
|
|
106
236
|
///
|
|
107
237
|
/// [`from_env`](Self::from_env) returns `None` when `CHALK_FNQ_REDIS_URL` is
|
|
@@ -577,7 +707,7 @@ async fn run_one_call(
|
|
|
577
707
|
{
|
|
578
708
|
Ok(Ok(())) => true,
|
|
579
709
|
Ok(Err(e)) => {
|
|
580
|
-
// The call's result stream may be incomplete; the
|
|
710
|
+
// The call's result stream may be incomplete; the enqueue safety TTL
|
|
581
711
|
// and the meta status are the backstop for a stuck poller.
|
|
582
712
|
error!(call_id = %call_id, error = %e, "failed to execute/write queued call");
|
|
583
713
|
false
|
|
@@ -663,8 +793,8 @@ async fn run_one_coalesced_call(
|
|
|
663
793
|
{
|
|
664
794
|
Ok(()) => true,
|
|
665
795
|
Err(e) => {
|
|
666
|
-
// The call's result stream may be incomplete; the
|
|
667
|
-
// the meta status are the backstop for a stuck poller.
|
|
796
|
+
// The call's result stream may be incomplete; the enqueue safety TTL
|
|
797
|
+
// and the meta status are the backstop for a stuck poller.
|
|
668
798
|
error!(call_id = %call_id, error = %e, "failed to execute/write coalesced queued call");
|
|
669
799
|
false
|
|
670
800
|
}
|
|
@@ -694,7 +824,7 @@ async fn coalesced_execute_and_write(
|
|
|
694
824
|
metrics_pipeline: Option<&Arc<PublishingMetricsPipeline>>,
|
|
695
825
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
696
826
|
let call_uuid = Uuid::parse_str(&work.call_id)?;
|
|
697
|
-
let
|
|
827
|
+
let result_destination = ResultDestination::for_call(queue, &work, call_uuid);
|
|
698
828
|
let ipc_bytes = work.call.feather_bytes()?;
|
|
699
829
|
let function_name = work.call.name.clone();
|
|
700
830
|
let mut metadata = work.metadata.otel_headers.clone();
|
|
@@ -736,8 +866,10 @@ async fn coalesced_execute_and_write(
|
|
|
736
866
|
|
|
737
867
|
match response {
|
|
738
868
|
Some(Ok(resp)) => {
|
|
739
|
-
|
|
740
|
-
|
|
869
|
+
result_destination
|
|
870
|
+
.write_chunk(conn, 1, &resp.feather_stream)
|
|
871
|
+
.await?;
|
|
872
|
+
result_destination.write_end(conn, 2).await?;
|
|
741
873
|
let _ = queue
|
|
742
874
|
.complete_status(
|
|
743
875
|
call_uuid,
|
|
@@ -751,15 +883,9 @@ async fn coalesced_execute_and_write(
|
|
|
751
883
|
Some(Err(status)) => {
|
|
752
884
|
// Parity with the enqueue/poll path: only a message string is
|
|
753
885
|
// available, so exc_type is a fixed placeholder.
|
|
754
|
-
|
|
755
|
-
conn,
|
|
756
|
-
|
|
757
|
-
1,
|
|
758
|
-
"RemoteFunctionError",
|
|
759
|
-
status.message(),
|
|
760
|
-
None,
|
|
761
|
-
)
|
|
762
|
-
.await?;
|
|
886
|
+
result_destination
|
|
887
|
+
.write_error(conn, 1, "RemoteFunctionError", status.message(), None)
|
|
888
|
+
.await?;
|
|
763
889
|
let summary: String = status.message().chars().take(200).collect();
|
|
764
890
|
let _ = queue
|
|
765
891
|
.complete_status(call_uuid, "failed", &summary, Some(processing_trace_id))
|
|
@@ -771,7 +897,8 @@ async fn coalesced_execute_and_write(
|
|
|
771
897
|
// happen (it always sends one Ok/Err per caller), but treat a
|
|
772
898
|
// missing response as a failure rather than leaving the poller hung.
|
|
773
899
|
let msg = "coalesced dispatch produced no response";
|
|
774
|
-
|
|
900
|
+
result_destination
|
|
901
|
+
.write_error(conn, 1, "RemoteFunctionError", msg, None)
|
|
775
902
|
.await?;
|
|
776
903
|
let _ = queue
|
|
777
904
|
.complete_status(call_uuid, "failed", msg, Some(processing_trace_id))
|
|
@@ -810,7 +937,7 @@ async fn execute_and_write(
|
|
|
810
937
|
metrics_pipeline: Option<&Arc<PublishingMetricsPipeline>>,
|
|
811
938
|
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
|
|
812
939
|
let call_uuid = Uuid::parse_str(&work.call_id)?;
|
|
813
|
-
let
|
|
940
|
+
let result_destination = ResultDestination::for_call(queue, &work, call_uuid);
|
|
814
941
|
let ipc_bytes = work.call.feather_bytes()?;
|
|
815
942
|
let function_name = work.call.name.clone();
|
|
816
943
|
let mut metadata = work.metadata.otel_headers.clone();
|
|
@@ -857,7 +984,7 @@ async fn execute_and_write(
|
|
|
857
984
|
biased;
|
|
858
985
|
res = &mut handler_fut => break res,
|
|
859
986
|
_ = tokio::time::timeout(flush_timeout, notify_handle.notified()) => {
|
|
860
|
-
if let Err(e) = flush_new(conn, &
|
|
987
|
+
if let Err(e) = flush_new(conn, &result_destination, &buffer, &mut flushed, &mut seq).await {
|
|
861
988
|
// Non-fatal: keep draining; the cursor didn't advance past the
|
|
862
989
|
// failed chunk, so the next wait retries it without duplicating.
|
|
863
990
|
warn!(call_id = %work.call_id, error = %e, "failed to flush partial results; retrying");
|
|
@@ -883,13 +1010,13 @@ async fn execute_and_write(
|
|
|
883
1010
|
// this is everything). This happens even if the handler panicked mid-execution,
|
|
884
1011
|
// so partial output is always preserved. Parity with the enqueue/poll path: a
|
|
885
1012
|
// generator that yields then raises surfaces its chunks followed by the error.
|
|
886
|
-
if let Err(e) = flush_new(conn, &
|
|
1013
|
+
if let Err(e) = flush_new(conn, &result_destination, &buffer, &mut flushed, &mut seq).await {
|
|
887
1014
|
warn!(call_id = %work.call_id, error = %e, "failed to flush final chunks");
|
|
888
1015
|
}
|
|
889
1016
|
|
|
890
1017
|
match result {
|
|
891
1018
|
Ok(()) => {
|
|
892
|
-
|
|
1019
|
+
result_destination.write_end(conn, seq).await?;
|
|
893
1020
|
let _ = queue
|
|
894
1021
|
.complete_status(
|
|
895
1022
|
call_uuid,
|
|
@@ -901,15 +1028,15 @@ async fn execute_and_write(
|
|
|
901
1028
|
info!(call_id = %work.call_id, chunks = flushed, "queued call completed");
|
|
902
1029
|
}
|
|
903
1030
|
Err(e) => {
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
1031
|
+
result_destination
|
|
1032
|
+
.write_error(
|
|
1033
|
+
conn,
|
|
1034
|
+
seq,
|
|
1035
|
+
"RemoteFunctionError",
|
|
1036
|
+
&e.to_string(),
|
|
1037
|
+
Some(e.details()),
|
|
1038
|
+
)
|
|
1039
|
+
.await?;
|
|
913
1040
|
let summary: String = e.to_string().chars().take(200).collect();
|
|
914
1041
|
let _ = queue
|
|
915
1042
|
.complete_status(call_uuid, "failed", &summary, Some(processing_trace_id))
|
|
@@ -932,7 +1059,7 @@ async fn execute_and_write(
|
|
|
932
1059
|
/// failed chunk, so a retry re-sends only the unwritten tail (no gaps, no dupes).
|
|
933
1060
|
async fn flush_new(
|
|
934
1061
|
conn: &mut ConnectionManager,
|
|
935
|
-
|
|
1062
|
+
result_destination: &ResultDestination,
|
|
936
1063
|
buffer: &Arc<Mutex<ChunkBuffer>>,
|
|
937
1064
|
flushed: &mut usize,
|
|
938
1065
|
seq: &mut u64,
|
|
@@ -947,7 +1074,7 @@ async fn flush_new(
|
|
|
947
1074
|
.collect()
|
|
948
1075
|
};
|
|
949
1076
|
for chunk in &new {
|
|
950
|
-
|
|
1077
|
+
result_destination.write_chunk(conn, *seq, chunk).await?;
|
|
951
1078
|
*seq += 1;
|
|
952
1079
|
*flushed += 1;
|
|
953
1080
|
}
|
|
@@ -1022,6 +1149,41 @@ mod tests {
|
|
|
1022
1149
|
assert_eq!(DEFAULT_FLUSH_TIMEOUT_MS, 100);
|
|
1023
1150
|
}
|
|
1024
1151
|
|
|
1152
|
+
#[test]
|
|
1153
|
+
fn result_destination_preserves_per_call_stream() {
|
|
1154
|
+
assert_eq!(
|
|
1155
|
+
ResultDestination::from_routing(
|
|
1156
|
+
"tenant:gen:call-1:stream".to_string(),
|
|
1157
|
+
Duration::from_secs(60),
|
|
1158
|
+
None,
|
|
1159
|
+
),
|
|
1160
|
+
ResultDestination::PerCall {
|
|
1161
|
+
stream_key: "tenant:gen:call-1:stream".to_string(),
|
|
1162
|
+
result_ttl: Duration::from_secs(60),
|
|
1163
|
+
}
|
|
1164
|
+
);
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
#[test]
|
|
1168
|
+
fn result_destination_routes_batch_call_with_correlation() {
|
|
1169
|
+
assert_eq!(
|
|
1170
|
+
ResultDestination::from_routing(
|
|
1171
|
+
"unused-per-call-stream".to_string(),
|
|
1172
|
+
Duration::from_secs(60),
|
|
1173
|
+
Some((
|
|
1174
|
+
"tenant:batch:batch-1:results".to_string(),
|
|
1175
|
+
"call-1".to_string(),
|
|
1176
|
+
42,
|
|
1177
|
+
)),
|
|
1178
|
+
),
|
|
1179
|
+
ResultDestination::Batch {
|
|
1180
|
+
stream_key: "tenant:batch:batch-1:results".to_string(),
|
|
1181
|
+
call_id: "call-1".to_string(),
|
|
1182
|
+
row_index: 42,
|
|
1183
|
+
}
|
|
1184
|
+
);
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1025
1187
|
#[test]
|
|
1026
1188
|
fn test_config_from_env_returns_none_when_redis_url_unset() {
|
|
1027
1189
|
let _env = lock_env();
|
|
@@ -1043,6 +1205,7 @@ mod tests {
|
|
|
1043
1205
|
std::env::remove_var(ENV_FLUSH_TIMEOUT_MS);
|
|
1044
1206
|
std::env::remove_var(ENV_QUEUE_PROTOCOL);
|
|
1045
1207
|
std::env::remove_var(ENV_STREAM_READ_COUNT);
|
|
1208
|
+
std::env::remove_var(ENV_MAX_CONCURRENT);
|
|
1046
1209
|
|
|
1047
1210
|
let config = SelfConsumerConfig::from_env().expect("config should parse");
|
|
1048
1211
|
assert_eq!(config.sem_ttl, Duration::from_secs(DEFAULT_SEM_TTL_SECS));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.10.1"
|
|
@@ -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.1}/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.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
|
|
@@ -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.1}/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.1}/chalk_remote_call/__init__.py
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/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.1}/chalk_remote_call/_native.pyi
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/chalk_remote_call/arrow_utils.py
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/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.1}/chalk_remote_call/logging.py
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/chalk_remote_call/servicer.py
RENAMED
|
File without changes
|
{chalk_remote_call_python-1.9.2 → chalk_remote_call_python-1.10.1}/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
|