| 131 | } |
| 132 | |
| 133 | pub fn infer_usage_events( |
| 134 | tool_names: Option<&str>, |
| 135 | metadata_json: Option<&str>, |
| 136 | text: Option<&str>, |
| 137 | ) -> Vec<UsageEvent> { |
| 138 | let metadata = metadata_json.and_then(|raw| serde_json::from_str::<Value>(raw).ok()); |
| 139 | let command_hint = metadata.as_ref().and_then(command_from_metadata); |
| 140 | |
| 141 | let mut events = BTreeSet::new(); |
| 142 | let tools = tool_names |
| 143 | .into_iter() |
| 144 | .flat_map(split_tool_names) |
| 145 | .collect::<Vec<_>>(); |
| 146 | for tool in &tools { |
| 147 | insert_tool_event(&mut events, tool, command_hint.as_deref()); |
| 148 | } |
| 149 | |
| 150 | if let Some(value) = metadata.as_ref() { |
| 151 | let mut skills = explicit_skills_from_metadata(value); |
| 152 | if tools.iter().any(|tool| is_skill_view_tool(tool)) { |
| 153 | collect_skill_view_metadata(value, &mut skills); |
| 154 | } |
| 155 | for skill in skills { |
| 156 | insert_skill_event(&mut events, &skill); |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | for skill in skills_from_text(text.unwrap_or_default()) { |
| 161 | insert_skill_event(&mut events, &skill); |
| 162 | } |
| 163 | |
| 164 | events.into_iter().collect() |
| 165 | } |
| 166 | |
| 167 | pub fn underused_tool_family_signals<'a>( |
| 168 | observations: impl IntoIterator<Item = ToolUsageObservation<'a>>, |