forked from github/copilot-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstartup_timings.rs
More file actions
105 lines (99 loc) · 4.78 KB
/
Copy pathstartup_timings.rs
File metadata and controls
105 lines (99 loc) · 4.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
//! Per-phase timing breakdown for [`Client::start`](crate::Client::start).
//!
//! `Client::start` performs several sequential phases between "spawn the CLI"
//! and "client is ready to create sessions": resolving (and possibly
//! extracting) the CLI binary, spawning the subprocess, waiting for the TCP
//! port announcement, the `connect` protocol handshake, and the optional
//! `sessionFs.setProvider` / `llmInference.setProvider` registration RPCs.
//!
//! Each phase is already measured internally with an [`Instant`] and logged at
//! `debug`. [`StartupTimings`] aggregates those durations into a single value
//! so a host can attribute total startup latency ("time to first token"
//! groundwork) to a specific phase — e.g. separating "process exec cost" from
//! "handshake/negotiation cost" — instead of reconstructing it from scattered
//! log lines.
//!
//! Retrieve it after start via
//! [`Client::startup_timings`](crate::Client::startup_timings).
//!
//! [`Instant`]: std::time::Instant
use std::time::Duration;
/// Millisecond breakdown of the phases of [`Client::start`](crate::Client::start).
///
/// Optional fields represent phases that do not run for every configuration:
/// `program_resolve_ms` is `None` when the caller supplies an explicit CLI path
/// (no resolution/extraction), `port_wait_ms` is `Some` only for the TCP
/// transport, and `session_fs_ms` / `llm_handler_ms` are `Some` only when the
/// corresponding option is configured. `process_spawn_ms` is `None` for
/// transports that do not spawn a subprocess (external server, in-process FFI
/// runtime). `transport_setup_ms`, `handshake_ms`, and `total_ms` are always
/// populated for a value returned by
/// [`Client::startup_timings`](crate::Client::startup_timings).
///
/// Durations are whole milliseconds, matching the existing `elapsed_ms`
/// tracing fields.
#[derive(Debug, Clone, Default, PartialEq, Eq)]
#[non_exhaustive]
pub struct StartupTimings {
/// Time spent in `resolve::copilot_binary_with_extract_dir` locating (and,
/// for a bundled CLI, extracting) the copilot binary. `None` when the
/// caller passes an explicit [`CliProgram::Path`](crate::CliProgram::Path).
pub program_resolve_ms: Option<u64>,
/// Time spent spawning the CLI subprocess (`command.spawn()`). `None` for
/// the external-server and in-process transports, which do not spawn a
/// child.
pub process_spawn_ms: Option<u64>,
/// Time spent waiting for the TCP server to announce its listening port on
/// stdout. `Some` only for the TCP transport.
pub port_wait_ms: Option<u64>,
/// Total transport setup time. This includes spawning and connecting to a
/// subprocess, connecting to an external server, or starting the in-process
/// FFI runtime. `process_spawn_ms` and `port_wait_ms` provide nested detail
/// for spawned transports.
pub transport_setup_ms: u64,
/// Time spent on the `connect` protocol handshake in
/// [`Client::verify_protocol_version`](crate::Client::verify_protocol_version),
/// including the fallback to the legacy `ping` RPC.
pub handshake_ms: u64,
/// Time spent registering the filesystem provider via
/// `sessionFs.setProvider`. `Some` only when
/// [`ClientOptions::session_fs`](crate::ClientOptions::session_fs) is set.
pub session_fs_ms: Option<u64>,
/// Time spent registering the LLM inference provider via
/// `llmInference.setProvider`. `Some` only when
/// [`ClientOptions::request_handler`](crate::ClientOptions::request_handler)
/// is set.
pub llm_handler_ms: Option<u64>,
/// Total wall-clock time for [`Client::start`](crate::Client::start), from
/// entry to the client being ready. Always present.
pub total_ms: u64,
}
impl StartupTimings {
/// Whole milliseconds of `duration`, saturating at [`u64::MAX`].
pub(crate) fn millis(duration: Duration) -> u64 {
u64::try_from(duration.as_millis()).unwrap_or(u64::MAX)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn millis_truncates_to_whole_milliseconds() {
assert_eq!(StartupTimings::millis(Duration::from_micros(1_999)), 1);
assert_eq!(StartupTimings::millis(Duration::from_millis(250)), 250);
assert_eq!(StartupTimings::millis(Duration::ZERO), 0);
}
#[test]
fn default_leaves_every_phase_unset() {
let timings = StartupTimings::default();
assert_eq!(timings, StartupTimings::default());
assert!(timings.program_resolve_ms.is_none());
assert!(timings.process_spawn_ms.is_none());
assert!(timings.port_wait_ms.is_none());
assert_eq!(timings.transport_setup_ms, 0);
assert_eq!(timings.handshake_ms, 0);
assert!(timings.session_fs_ms.is_none());
assert!(timings.llm_handler_ms.is_none());
assert_eq!(timings.total_ms, 0);
}
}