Skip to content

Commit 5105b6c

Browse files
authored
feat(telemetry): add client application and additional info from tool (#2514)
1 parent 0e034b4 commit 5105b6c

File tree

9 files changed

+136
-34
lines changed

9 files changed

+136
-34
lines changed

crates/chat-cli/src/cli/chat/mod.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1885,6 +1885,18 @@ impl ChatSession {
18851885
ev.is_accepted = true;
18861886
});
18871887

1888+
// Extract AWS service name and operation name if available
1889+
if let Some(additional_info) = tool.tool.get_additional_info() {
1890+
if let Some(aws_service_name) = additional_info.get("aws_service_name").and_then(|v| v.as_str()) {
1891+
tool_telemetry =
1892+
tool_telemetry.and_modify(|ev| ev.aws_service_name = Some(aws_service_name.to_string()));
1893+
}
1894+
if let Some(aws_operation_name) = additional_info.get("aws_operation_name").and_then(|v| v.as_str()) {
1895+
tool_telemetry =
1896+
tool_telemetry.and_modify(|ev| ev.aws_operation_name = Some(aws_operation_name.to_string()));
1897+
}
1898+
}
1899+
18881900
let invoke_result = tool.tool.invoke(os, &mut self.stdout).await;
18891901

18901902
if self.spinner.is_some() {
@@ -2557,7 +2569,7 @@ impl ChatSession {
25572569
}
25582570
.map(|v| v.to_string());
25592571

2560-
os.telemetry.send_tool_use_suggested(event).ok();
2572+
os.telemetry.send_tool_use_suggested(&os.database, event).await.ok();
25612573
}
25622574
}
25632575

crates/chat-cli/src/cli/chat/tools/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,15 @@ impl Tool {
147147
Tool::Thinking(think) => think.validate(os).await,
148148
}
149149
}
150+
151+
/// Returns additional information about the tool if available
152+
pub fn get_additional_info(&self) -> Option<serde_json::Value> {
153+
match self {
154+
Tool::UseAws(use_aws) => Some(use_aws.get_additional_info()),
155+
// Add other tool types here as they implement get_additional_info()
156+
_ => None,
157+
}
158+
}
150159
}
151160

152161
/// A tool specification to be sent to the model as part of a conversation. Maps to

crates/chat-cli/src/cli/chat/tools/use_aws.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,13 @@ impl UseAws {
178178
Ok(())
179179
}
180180

181+
pub fn get_additional_info(&self) -> serde_json::Value {
182+
serde_json::json!({
183+
"aws_service_name": self.service_name.clone(),
184+
"aws_operation_name": self.operation_name.clone()
185+
})
186+
}
187+
181188
/// Returns the CLI arguments properly formatted as kebab case if parameters is
182189
/// [Option::Some], otherwise None
183190
fn cli_parameters(&self) -> Option<Vec<(String, String)>> {

crates/chat-cli/src/cli/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,10 @@ impl RootSubcommand {
141141

142142
// Send executed telemetry.
143143
if self.valid_for_telemetry() {
144-
os.telemetry.send_cli_subcommand_executed(&self).ok();
144+
os.telemetry
145+
.send_cli_subcommand_executed(&os.database, &self)
146+
.await
147+
.ok();
145148
}
146149

147150
match self {

crates/chat-cli/src/telemetry/core.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ pub struct Event {
4949
pub created_time: Option<SystemTime>,
5050
pub credential_start_url: Option<String>,
5151
pub sso_region: Option<String>,
52+
pub client_application: Option<String>,
5253
#[serde(flatten)]
5354
pub ty: EventType,
5455
}
@@ -60,6 +61,7 @@ impl Event {
6061
created_time: Some(SystemTime::now()),
6162
credential_start_url: None,
6263
sso_region: None,
64+
client_application: None,
6365
}
6466
}
6567

@@ -71,6 +73,10 @@ impl Event {
7173
self.sso_region = Some(sso_region);
7274
}
7375

76+
pub fn set_client_application(&mut self, client_application: String) {
77+
self.client_application = Some(client_application);
78+
}
79+
7480
pub fn into_metric_datum(self) -> Option<MetricDatum> {
7581
match self.ty {
7682
EventType::UserLoggedIn {} => Some(
@@ -107,6 +113,7 @@ impl Event {
107113
credential_start_url: self.credential_start_url.map(Into::into),
108114
codewhispererterminal_subcommand: Some(subcommand.into()),
109115
codewhispererterminal_in_cloudshell: None,
116+
codewhispererterminal_client_application: self.client_application.map(Into::into),
110117
}
111118
.into_metric_datum(),
112119
),
@@ -209,6 +216,7 @@ impl Event {
209216
.join(",")
210217
.into(),
211218
),
219+
codewhispererterminal_client_application: self.client_application.map(Into::into),
212220
}
213221
.into_metric_datum(),
214222
),
@@ -294,6 +302,8 @@ impl Event {
294302
model,
295303
execution_duration,
296304
turn_duration,
305+
aws_service_name,
306+
aws_operation_name,
297307
} => Some(
298308
CodewhispererterminalToolUseSuggested {
299309
create_time: self.created_time,
@@ -323,6 +333,9 @@ impl Event {
323333
codewhispererterminal_tool_turn_duration_ms: turn_duration
324334
.map(|d| d.as_millis() as i64)
325335
.map(Into::into),
336+
codewhispererterminal_client_application: self.client_application.map(Into::into),
337+
codewhispererterminal_aws_service_name: aws_service_name.map(Into::into),
338+
codewhispererterminal_aws_operation_name: aws_operation_name.map(Into::into),
326339
}
327340
.into_metric_datum(),
328341
),
@@ -343,6 +356,7 @@ impl Event {
343356
codewhispererterminal_tools_per_mcp_server: Some(CodewhispererterminalToolsPerMcpServer(
344357
number_of_tools as i64,
345358
)),
359+
codewhispererterminal_client_application: self.client_application.map(Into::into),
346360
}
347361
.into_metric_datum(),
348362
),
@@ -431,6 +445,7 @@ impl Event {
431445
status_code: status_code.map(|v| v as i64).map(Into::into),
432446
request_id: request_id.map(Into::into),
433447
codewhispererterminal_utterance_id: message_id.map(Into::into),
448+
codewhispererterminal_client_application: self.client_application.map(Into::into),
434449
}
435450
.into_metric_datum(),
436451
),
@@ -562,6 +577,8 @@ pub enum EventType {
562577
model: Option<String>,
563578
execution_duration: Option<Duration>,
564579
turn_duration: Option<Duration>,
580+
aws_service_name: Option<String>,
581+
aws_operation_name: Option<String>,
565582
},
566583
McpServerInit {
567584
conversation_id: String,
@@ -617,6 +634,8 @@ pub struct ToolUseEventBuilder {
617634
pub model: Option<String>,
618635
pub execution_duration: Option<Duration>,
619636
pub turn_duration: Option<Duration>,
637+
pub aws_service_name: Option<String>,
638+
pub aws_operation_name: Option<String>,
620639
}
621640

622641
impl ToolUseEventBuilder {
@@ -639,6 +658,8 @@ impl ToolUseEventBuilder {
639658
model,
640659
execution_duration: None,
641660
turn_duration: None,
661+
aws_service_name: None,
662+
aws_operation_name: None,
642663
}
643664
}
644665

crates/chat-cli/src/telemetry/definitions.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ mod tests {
4343
codewhispererterminal_tool_name: None,
4444
codewhispererterminal_assistant_response_length: Some(20.into()),
4545
codewhispererterminal_chat_message_meta_tags: Some([MessageMetaTag::Compact.to_string()].join(",").into()),
46+
codewhispererterminal_client_application: None,
4647
});
4748

4849
let s = serde_json::to_string_pretty(&metric_datum_init).unwrap();

crates/chat-cli/src/telemetry/mod.rs

Lines changed: 50 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ pub use crate::telemetry::core::{
7171
QProfileSwitchIntent,
7272
TelemetryResult,
7373
};
74+
use crate::util::env_var::Q_CLI_CLIENT_APPLICATION;
7475
use crate::util::system_info::os_version;
7576

7677
#[derive(thiserror::Error, Debug)]
@@ -231,10 +232,17 @@ impl TelemetryThread {
231232
Ok(self.tx.send(Event::new(EventType::UserLoggedIn {}))?)
232233
}
233234

234-
pub fn send_cli_subcommand_executed(&self, subcommand: &RootSubcommand) -> Result<(), TelemetryError> {
235-
Ok(self.tx.send(Event::new(EventType::CliSubcommandExecuted {
235+
pub async fn send_cli_subcommand_executed(
236+
&self,
237+
database: &Database,
238+
subcommand: &RootSubcommand,
239+
) -> Result<(), TelemetryError> {
240+
let mut telemetry_event = Event::new(EventType::CliSubcommandExecuted {
236241
subcommand: subcommand.to_string(),
237-
}))?)
242+
});
243+
set_event_metadata(database, &mut telemetry_event).await;
244+
245+
Ok(self.tx.send(telemetry_event)?)
238246
}
239247

240248
pub async fn send_chat_slash_command_executed(
@@ -246,15 +254,15 @@ impl TelemetryThread {
246254
result: TelemetryResult,
247255
reason: Option<String>,
248256
) -> Result<(), TelemetryError> {
249-
let mut event = Event::new(EventType::ChatSlashCommandExecuted {
257+
let mut telemetry_event = Event::new(EventType::ChatSlashCommandExecuted {
250258
conversation_id,
251259
command,
252260
subcommand,
253261
result,
254262
reason,
255263
});
256-
set_start_url_and_region(database, &mut event).await;
257-
Ok(self.tx.send(event)?)
264+
set_event_metadata(database, &mut telemetry_event).await;
265+
Ok(self.tx.send(telemetry_event)?)
258266
}
259267

260268
#[allow(clippy::too_many_arguments)] // TODO: Should make a parameters struct.
@@ -265,14 +273,14 @@ impl TelemetryThread {
265273
result: TelemetryResult,
266274
data: ChatAddedMessageParams,
267275
) -> Result<(), TelemetryError> {
268-
let mut event = Event::new(EventType::ChatAddedMessage {
276+
let mut telemetry_event = Event::new(EventType::ChatAddedMessage {
269277
conversation_id,
270278
result,
271279
data,
272280
});
273-
set_start_url_and_region(database, &mut event).await;
281+
set_event_metadata(database, &mut telemetry_event).await;
274282

275-
Ok(self.tx.send(event)?)
283+
Ok(self.tx.send(telemetry_event)?)
276284
}
277285

278286
pub async fn send_record_user_turn_completion(
@@ -282,17 +290,21 @@ impl TelemetryThread {
282290
result: TelemetryResult,
283291
args: RecordUserTurnCompletionArgs,
284292
) -> Result<(), TelemetryError> {
285-
let mut event = Event::new(EventType::RecordUserTurnCompletion {
293+
let mut telemetry_event = Event::new(EventType::RecordUserTurnCompletion {
286294
conversation_id,
287295
result,
288296
args,
289297
});
290-
set_start_url_and_region(database, &mut event).await;
291-
Ok(self.tx.send(event)?)
298+
set_event_metadata(database, &mut telemetry_event).await;
299+
Ok(self.tx.send(telemetry_event)?)
292300
}
293301

294-
pub fn send_tool_use_suggested(&self, event: ToolUseEventBuilder) -> Result<(), TelemetryError> {
295-
Ok(self.tx.send(Event::new(EventType::ToolUseSuggested {
302+
pub async fn send_tool_use_suggested(
303+
&self,
304+
database: &Database,
305+
event: ToolUseEventBuilder,
306+
) -> Result<(), TelemetryError> {
307+
let mut telemetry_event = Event::new(EventType::ToolUseSuggested {
296308
conversation_id: event.conversation_id,
297309
utterance_id: event.utterance_id,
298310
user_input_id: event.user_input_id,
@@ -310,7 +322,12 @@ impl TelemetryThread {
310322
model: event.model,
311323
execution_duration: event.execution_duration,
312324
turn_duration: event.turn_duration,
313-
}))?)
325+
aws_service_name: event.aws_service_name,
326+
aws_operation_name: event.aws_operation_name,
327+
});
328+
set_event_metadata(database, &mut telemetry_event).await;
329+
330+
Ok(self.tx.send(telemetry_event)?)
314331
}
315332

316333
pub async fn send_mcp_server_init(
@@ -321,14 +338,15 @@ impl TelemetryThread {
321338
init_failure_reason: Option<String>,
322339
number_of_tools: usize,
323340
) -> Result<(), TelemetryError> {
324-
let mut event = Event::new(crate::telemetry::EventType::McpServerInit {
341+
let mut telemetry_event = Event::new(crate::telemetry::EventType::McpServerInit {
325342
conversation_id,
326343
server_name,
327344
init_failure_reason,
328345
number_of_tools,
329346
});
330-
set_start_url_and_region(database, &mut event).await;
331-
Ok(self.tx.send(event)?)
347+
set_event_metadata(database, &mut telemetry_event).await;
348+
349+
Ok(self.tx.send(telemetry_event)?)
332350
}
333351

334352
pub async fn send_agent_config_init(
@@ -337,9 +355,9 @@ impl TelemetryThread {
337355
conversation_id: String,
338356
args: AgentConfigInitArgs,
339357
) -> Result<(), TelemetryError> {
340-
let mut event = Event::new(crate::telemetry::EventType::AgentConfigInit { conversation_id, args });
341-
set_start_url_and_region(database, &mut event).await;
342-
Ok(self.tx.send(event)?)
358+
let mut telemetry_event = Event::new(crate::telemetry::EventType::AgentConfigInit { conversation_id, args });
359+
set_event_metadata(database, &mut telemetry_event).await;
360+
Ok(self.tx.send(telemetry_event)?)
343361
}
344362

345363
pub fn send_did_select_profile(
@@ -387,7 +405,7 @@ impl TelemetryThread {
387405
request_id: Option<String>,
388406
message_id: Option<String>,
389407
) -> Result<(), TelemetryError> {
390-
let mut event = Event::new(EventType::MessageResponseError {
408+
let mut telemetry_event = Event::new(EventType::MessageResponseError {
391409
result,
392410
reason,
393411
reason_desc,
@@ -397,20 +415,25 @@ impl TelemetryThread {
397415
request_id,
398416
message_id,
399417
});
400-
set_start_url_and_region(database, &mut event).await;
418+
set_event_metadata(database, &mut telemetry_event).await;
401419

402-
Ok(self.tx.send(event)?)
420+
Ok(self.tx.send(telemetry_event)?)
403421
}
404422
}
405423

406-
async fn set_start_url_and_region(database: &Database, event: &mut Event) {
424+
async fn set_event_metadata(database: &Database, event: &mut Event) {
407425
let (start_url, region) = get_start_url_and_region(database).await;
408426
if let Some(start_url) = start_url {
409427
event.set_start_url(start_url);
410428
}
411429
if let Some(region) = region {
412430
event.set_sso_region(region);
413431
}
432+
433+
// Set the client application from environment variable
434+
if let Ok(client_app) = std::env::var(Q_CLI_CLIENT_APPLICATION) {
435+
event.set_client_application(client_app);
436+
}
414437
}
415438

416439
#[derive(Debug)]
@@ -690,7 +713,8 @@ mod test {
690713

691714
thread.send_user_logged_in().ok();
692715
thread
693-
.send_cli_subcommand_executed(&RootSubcommand::Version { changelog: None })
716+
.send_cli_subcommand_executed(&database, &RootSubcommand::Version { changelog: None })
717+
.await
694718
.ok();
695719
thread
696720
.send_chat_added_message(

crates/chat-cli/src/util/consts.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,10 @@ pub mod env_var {
6666
Q_USING_ZSH_AUTOSUGGESTIONS = "Q_USING_ZSH_AUTOSUGGESTIONS",
6767

6868
/// Overrides the path to the bundle metadata released with certain desktop builds.
69-
Q_BUNDLE_METADATA_PATH = "Q_BUNDLE_METADATA_PATH"
69+
Q_BUNDLE_METADATA_PATH = "Q_BUNDLE_METADATA_PATH",
70+
71+
/// Identifier for the client application or service using the chat-cli
72+
Q_CLI_CLIENT_APPLICATION = "Q_CLI_CLIENT_APPLICATION"
7073
}
7174
}
7275

0 commit comments

Comments
 (0)