Skip to content

Commit a870a3d

Browse files
tclemCopilot
andcommitted
Round out builder coverage and document the Option<T> escape hatch
After landing per-field builders on the four highest-traffic consumer-facing config structs (MessageOptions, ClientOptions, Tool, SessionConfig/ResumeSessionConfig), this commit closes the long tail. Rust ecosystem research (tokio, reqwest, tonic, axum, AWS SDK) confirms two design points: 1. The dominant pattern for #[non_exhaustive] config builders is the single `with_<field>(impl Into<T>) -> Self` setter that always Some-wraps. AWS SDK's dual `field` + `set_field(Option<T>)` shape is justified by codegen, not by ergonomics — and the `impl Into<Option<T>>` magic that some older crates use has fallen out of fashion (it conflicts with `impl Into<T>` chaining and produces poor type-inference errors). 2. Direct field assignment on a `pub` field is idiomatic Rust for forwarding `Option<T>` values from upstream code. The http::request::Parts / hyper::Body::Builder pattern. We already support it; we just hadn't documented it. This commit therefore: - Adds full builders to the four bare consumer-facing structs: CustomAgentConfig (name + prompt required, six with_* setters), InfiniteSessionConfig (three with_* setters), ProviderConfig (base_url required, six with_* setters), SystemMessageConfig (three with_* setters). - Expands TraceContext with a symmetric new() + with_traceparent pair alongside the existing from_traceparent shorthand. The shorthand is now expressed in terms of the chain (`new().with_traceparent(x)`) for consistency. - Documents the direct-field-assignment escape hatch on SessionConfig and ResumeSessionConfig, with a doc example showing how to mix the fluent chain (for compile-time-known values) with direct mutation (for Option<T> pass-throughs). Per-field `with_<x>_opt(Option<T>)` setters were considered and deliberately rejected: doubling the method count for ~30-field configs would hurt rustdoc discoverability and conflict with the single-setter precedent we just shipped. The mut-let escape hatch covers the same use case without polluting the API surface. Adds 5 unit tests (one per new builder + a TraceContext suite) and extends the existing CHANGELOG "Builder ergonomics" subsection. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent e93ce8e commit a870a3d

3 files changed

Lines changed: 358 additions & 7 deletions

File tree

rust/CHANGELOG.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,25 @@ public surface.
381381
`let mut cfg = ResumeSessionConfig::new(id); cfg.client_name =
382382
Some("...".into()); cfg.streaming = Some(true); ...` (10-15
383383
lines per site) to a single fluent chain.
384+
- Round out builder coverage on the remaining consumer-facing
385+
config structs: `CustomAgentConfig::new(name, prompt)` plus
386+
`with_display_name`, `with_description`, `with_tools`,
387+
`with_mcp_servers`, `with_infer`, `with_skills`;
388+
`InfiniteSessionConfig::new()` plus `with_enabled`,
389+
`with_background_compaction_threshold`,
390+
`with_buffer_exhaustion_threshold`;
391+
`ProviderConfig::new(base_url)` plus `with_provider_type`,
392+
`with_wire_api`, `with_api_key`, `with_bearer_token`,
393+
`with_azure`, `with_headers`; `SystemMessageConfig::new()` plus
394+
`with_mode`, `with_content`, `with_sections`. `TraceContext`
395+
also gains a symmetric `new()` + `with_traceparent` pair
396+
alongside the existing `from_traceparent` shorthand.
397+
- Documented the direct-field-assignment escape hatch on
398+
`SessionConfig` and `ResumeSessionConfig` for callers forwarding
399+
`Option<T>` values from upstream code (matches the
400+
`http::request::Parts` / `hyper::Body::Builder` convention; per-
401+
field `with_*_opt` setters intentionally omitted to keep the
402+
primary API surface small).
384403

385404
### Fixed
386405
- `SessionUi::elicitation` (and the `confirm` / `select` / `input`

rust/src/trace_context.rs

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,26 @@ pub struct TraceContext {
3535
}
3636

3737
impl TraceContext {
38+
/// Construct an empty [`TraceContext`]; both fields default to unset
39+
/// (the SDK skips trace-context injection on the wire).
40+
pub fn new() -> Self {
41+
Self::default()
42+
}
43+
3844
/// Construct a [`TraceContext`] from a `traceparent` header value, with
3945
/// no `tracestate`.
46+
///
47+
/// Equivalent to `TraceContext::new().with_traceparent(value)`; kept
48+
/// for ergonomics in the common single-header case.
4049
pub fn from_traceparent(traceparent: impl Into<String>) -> Self {
41-
Self {
42-
traceparent: Some(traceparent.into()),
43-
tracestate: None,
44-
}
50+
Self::new().with_traceparent(traceparent)
51+
}
52+
53+
/// Set or replace the `traceparent` header value, returning `self` for
54+
/// chaining.
55+
pub fn with_traceparent(mut self, traceparent: impl Into<String>) -> Self {
56+
self.traceparent = Some(traceparent.into());
57+
self
4558
}
4659

4760
/// Set or replace the `tracestate` header value, returning `self` for
@@ -87,3 +100,33 @@ pub(crate) fn inject_trace_context(params: &mut serde_json::Value, ctx: &TraceCo
87100
params["tracestate"] = serde_json::Value::String(ts.clone());
88101
}
89102
}
103+
104+
#[cfg(test)]
105+
mod tests {
106+
use super::TraceContext;
107+
108+
#[test]
109+
fn new_yields_empty_context() {
110+
let ctx = TraceContext::new();
111+
assert!(ctx.is_empty());
112+
assert!(ctx.traceparent.is_none());
113+
assert!(ctx.tracestate.is_none());
114+
}
115+
116+
#[test]
117+
fn builder_composes_traceparent_and_tracestate() {
118+
let ctx = TraceContext::new()
119+
.with_traceparent("00-trace-span-01")
120+
.with_tracestate("vendor=key");
121+
assert_eq!(ctx.traceparent.as_deref(), Some("00-trace-span-01"));
122+
assert_eq!(ctx.tracestate.as_deref(), Some("vendor=key"));
123+
assert!(!ctx.is_empty());
124+
}
125+
126+
#[test]
127+
fn from_traceparent_matches_builder() {
128+
let direct = TraceContext::from_traceparent("00-trace-span-01");
129+
let chained = TraceContext::new().with_traceparent("00-trace-span-01");
130+
assert_eq!(direct, chained);
131+
}
132+
}

0 commit comments

Comments
 (0)