add nld decision telemetry event#10875
Conversation
|
I'm starting a first review of this pull request. You can view the conversation on Warp. I completed the review and no human review was requested for this pull request. Comment Powered by Oz |
There was a problem hiding this comment.
Overview
This PR adds input-decision source tracking through the NLD classifiers and includes decision metadata plus optionally collected input text on InputBufferSubmitted telemetry.
Concerns
- Locked config updates that go through the compatibility
set_input_configpath can retain a stale priorinput_decision_source, so later submissions can report an unrelated source. - The image attachment path sets
AttachmentForcedAiand then immediately calls the default helper, which overwrites the source back toManualToggle.
Verdict
Found: 0 critical, 2 important, 0 suggestions
Request changes
Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).
Powered by Oz
| self.input_config = new_config; | ||
| if let Some(decision_source) = decision_source { | ||
| self.input_decision_source = Some(decision_source); | ||
| } else if !new_config.is_locked { |
There was a problem hiding this comment.
decision_source is None and the new config is locked, this preserves the previous source. Existing callers that still use set_input_config can leave stale values like ShellPrefix or AttachmentForcedAi, so later submit telemetry can report a source from an unrelated prior transition. Clear the source when no source is provided, or require every locked transition to pass an explicit source.
| InputDecisionSource::AttachmentForcedAi, | ||
| ctx, | ||
| ); | ||
| self.ensure_agent_mode_for_ai_features(true, ctx); |
There was a problem hiding this comment.
AttachmentForcedAi source set just above because the config is already in AI mode and set_input_config_internal_with_source still updates the source for unchanged configs. Remove this duplicate call so image submissions keep the attachment-specific decision source.
|
@szgupta I occasionally saw decision source is Null, but it did not reproduce robustly. Despite our code does imply if no NLD decision applied when is_locked is False the decision source will be Null, it's not clear to me why there's no NLD decision for unlocked sessions. |
|
I'm starting a first review of this pull request. You can view the conversation on Warp. I completed the review and no human review was requested for this pull request. Comment Powered by Oz |
There was a problem hiding this comment.
Overview
This PR adds a decision-source enum for natural language detection, threads that source through input mode state, and expands InputBufferSubmitted telemetry to include gated UGC plus the final decision source. The implementation preserves UGC routing via contains_ugc() and includes targeted tests for manual toggles, shell prefix handling, and attachment-forced AI.
Concerns
- Suggestion: in
crates/input_classifier/src/onnx/mod.rs, thedetect_input_decisionerror path reportsdecision_sourceasNldClassifierwhile falling back tocontext.current_input_type. If inference returns an error, telemetry will look like a successful model decision and mask classifier failures; consider using the heuristic fallback path or adding a distinct source for current-input fallback.
Verdict
Found: 0 critical, 0 important, 1 suggestions
Approve with nits
Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).
Powered by Oz
|
/oz-review |
|
I'm re-reviewing this pull request in response to a review request. You can view the conversation on Warp. I completed the review and no human review was requested for this pull request. Comment Powered by Oz |
There was a problem hiding this comment.
Overview
This PR adds decision-source tracking to input classification and includes that metadata in the InputBufferSubmitted telemetry event, with the submitted input attached only through the existing AI UGC telemetry gate.
Concerns
- No blocking correctness or security concerns found in the annotated diff.
Verdict
Found: 0 critical, 0 important, 0 suggestions
Approve
Comment /oz-review on this pull request to retrigger a review (up to 3 times on the same pull request).
Powered by Oz
| /// The source of the final input decision that is currently reflected in `input_config`. | ||
| input_decision_source: Option<InputDecisionSource>, |
There was a problem hiding this comment.
like the case i got #10875 (comment), although not robustly reproducible;
i'd rather leave it as none (trade coverage for accuracy) if there's some decision path that I am not aware of.
There was a problem hiding this comment.
also for some case if it's set to enable auto detection, then it's okay to be None for NLD to fill in later e.g. in enable_auto_detection
| /// The source of the final input decision that is currently reflected in `input_config`. | ||
| input_decision_source: Option<InputDecisionSource>, |
There was a problem hiding this comment.
"input_decision" is vague - can we be more clear here?
There was a problem hiding this comment.
agreed on this. maybe nld_decision_source is better?
| #[cfg_attr(not(target_family = "wasm"), async_trait)] | ||
| #[cfg_attr(target_family = "wasm", async_trait(?Send))] | ||
| impl InputClassifier for OnnxClassifier { | ||
| async fn detect_input_decision( |
There was a problem hiding this comment.
Does this mean checking the input decision happens separately from actually running NLD? Feel like the detect_input_type API ought to return the type and why that decision was made
There was a problem hiding this comment.
it was mostly a rename. we did append source besides decision. Reverted it back to avoid confusion 557db91
| /// The timestamp of the last time the input mode was switched, if the switch was to AI mode and | ||
| /// it was autodetected. Else, `None`. | ||
| last_ai_autodetection_ts: Option<Instant>, | ||
| /// The source of the final input decision that is currently reflected in `input_config`. |
There was a problem hiding this comment.
if this is tied to some data in InputConfig, why is it not part of InputConfig?
also seems like it might simplify some other code
There was a problem hiding this comment.
@vorporeal it was intended to say the source will be set in set_input_config_internal. Will update the comment to be less confusing; The decision source is dependent on user input text, and will be decided in runtime (e.g if there's ONNX panic, the source will be NLDHeuristicClassifier instead of NLDClassifier..etc), and therefore i think it's not appropriate to put it in input config, which aims to track the mode state (it's logically possible to have different decision source in one mode state)
i also didn't get why put nld_decision_source in inputConfig will simplify this implementation, as we will still need to update everywhere we call set_input_config.
Pls lmk if i oversee some potential shortcut here
| /// it was autodetected. Else, `None`. | ||
| last_ai_autodetection_ts: Option<Instant>, | ||
| /// The source of the final input decision upon submission | ||
| nld_decision_source: Option<NldDecisionSource>, |
There was a problem hiding this comment.
nit: should we call this last_ai_autodetection_decision_source to be consistent with the property above? even better, wrap the two into a mini struct!
| self.set_input_config_internal(current_config.with_input_type(input_type), ctx); | ||
| self.set_input_config_internal( | ||
| current_config.with_input_type(input_type), | ||
| Some(NldDecisionSource::ManualToggle), |
There was a problem hiding this comment.
hrm, how are we sure this is a manual toggle? this API is in a model, not a ui view
| let index = HINT_INDEX.fetch_add(1, Ordering::Relaxed) % AGENT_MODE_HINT_OPTIONS.len(); | ||
| AGENT_MODE_HINT_OPTIONS[index] | ||
| } | ||
| fn submitted_nld_decision_source(input_model: &BlocklistAIInputModel) -> Option<NldDecisionSource> { |
|
|
||
| self.ai_input_model.update(ctx, |ai_input_model, ctx| { | ||
| ai_input_model.set_input_config( | ||
| ai_input_model.set_input_config_with_source( |
There was a problem hiding this comment.
nit: i'd leave this API as set_input_config and make NldDecisionSource required. There shouldn't be a single callsite that can't specify why we chose to change the input type, right?
|
|
||
| /// The source of the final input type decision applied to the user input. | ||
| #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] | ||
| pub enum NldDecisionSource { |
There was a problem hiding this comment.
I think we should decouple the decision source in the input_classifier crate vs the application level decision source. The one coming from this crate should only include the sources that it can actually produce (stuff in the NLD pipeline), whereas the one in the application should be like
enum AiAutodetectionSource {
ManualToggle,
...,
ClassifierSource(ClassifierSource)
}
(naming TBD)
| } | ||
|
|
||
| async fn classify_input( | ||
| async fn classify_input_with_source( |
There was a problem hiding this comment.
nit: let's keep the name as classify_input
| _context: &Context, | ||
| ) -> anyhow::Result<ClassificationResult> { | ||
| context: &Context, | ||
| ) -> anyhow::Result<ClassificationWithSource> { |
There was a problem hiding this comment.
Can we just add the source to the existing ClassificationResult?
Co-Authored-By: Oz <oz-agent@warp.dev>
557db91 to
5c54c6b
Compare
|
|
||
| /// Sources produced by the NLD pipeline. | ||
| #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] | ||
| pub enum NldDecision { |
There was a problem hiding this comment.
nit: InputClassifierDecisionSource
let's avoid "nld" since we don't really use that term in the codebase today
| NldClassifierFallbackHeuristic, | ||
| NldClassifierFallbackCurrentInput, | ||
| Denylist, | ||
| HistoryMatch, |
There was a problem hiding this comment.
The input_classifier crate doesn't check for history
| NldClassifierFallbackCurrentInput, | ||
| Denylist, | ||
| HistoryMatch, | ||
| OneOffWhitelist, |
There was a problem hiding this comment.
Can you break this out into the two allowlists: "ShellCommandAllowlist" vs "NaturalLanguageAllowlist" vs "NaturalLanguageFollowupAllowlist"
| NldClassifier, | ||
| NldClassifierFallbackHeuristic, | ||
| NldClassifierFallbackCurrentInput, | ||
| Denylist, |
There was a problem hiding this comment.
AISettings.autodetection_command_denylist to block auto detection run ; also moved to app level tracking now
|
|
||
| /// Sources produced by the NLD pipeline. | ||
| #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] | ||
| pub enum NldDecision { |
There was a problem hiding this comment.
All of these variants should be more specific and documented please!
| &self, | ||
| input: ParsedTokensSnapshot, | ||
| context: &Context, | ||
| ) -> (InputType, NldDecision) { |
There was a problem hiding this comment.
Instead of a tuple, can you create a new return type (struct) with these two properties?
| /// App level settings from explicit user actions. | ||
| AppLevelOverride(AppLevelOverride), | ||
| /// App level implicit / side-effect transitions from other UI flows. | ||
| AppLevelHeuristic(AppLevelHeuristic), |
There was a problem hiding this comment.
Hrm, I feel like we should collapse these into one. In fact, I would change this type to
enum InputTypeAutoDetectionSource {
InputClassifier(InputClassifierDecisionSource),
HistoryMatch,
... // all the other app types inlined here
}
szgupta
left a comment
There was a problem hiding this comment.
Awesome, thank you for iterating!
|
|
||
| /// The source of the final input type decision applied to the user input. | ||
| #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] | ||
| pub enum InputTypeAutoDetectionSource { |
There was a problem hiding this comment.
nit: this is more like InputTypeDecisionSource - because a lot of these don't go through "auto-detection" (e.g. voice input)
| /// Image / file attachment in progress force-locked AI mode. | ||
| AttachmentForcedAi, |
There was a problem hiding this comment.
nit: I would just call this "AttachedFile"
| /// First token matched the autodetection command denylist. | ||
| Denylist, |
There was a problem hiding this comment.
Can you make this more specific (i.e. which denylist)
| } else { | ||
| AISettings::as_ref(ctx).is_ai_autodetection_enabled(ctx) | ||
| }; | ||
| let initial_decision_source = None; |
There was a problem hiding this comment.
Can you just inline this? no need for variable declaration
| pub fn input_config(&self) -> InputConfig { | ||
| self.input_config | ||
| } | ||
| pub fn last_ai_autodetection_source(&self) -> Option<InputTypeAutoDetectionSource> { |
| /// The source of the final input type decision applied to the user input. | ||
| #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] |
There was a problem hiding this comment.
Can you move this down, past the imports
| TelemetryEvent::AgentExitedShellProcess { .. } => true, | ||
| TelemetryEvent::CreateProjectPromptSubmitted { .. } => false, | ||
| TelemetryEvent::CreateProjectPromptSubmittedContent { .. } => true, | ||
| TelemetryEvent::InputBufferSubmitted { .. } => false, |
There was a problem hiding this comment.
revert this? it was fine before
| return InputType::Shell; | ||
| return InputClassificationResult::new( | ||
| InputType::Shell, | ||
| InputClassifierDecisionSource::ShellHeuristic, |
There was a problem hiding this comment.
Should we break this up into two types rather than use the same one here and in is_likely_shell_command?
| /// The classifier source that produced this classification. | ||
| pub source: InputClassifierDecisionSource, |
There was a problem hiding this comment.
nit: instead of making this pub, define a getter
| #[cfg(test)] | ||
| mod tests { |
Description
Linked Issue
ready-to-specorready-to-implement.Testing
./script/run --features warpui/log_named_telemetry_events
./script/runScreenshots / Videos
ls

ls with file attached

** ls with file attached and ⌘ I**

hello in terminal mode without enabling onnx classifier

** approve after hello, should trigger AgentFollowUp whitelist**

natural language without enabling onnx classifier





hello with enabling onnx classifier, should trigger whitelisting
!hello with enabling onnx classifier, should trigger shell prefix
what's the weather should trigger nldclassifier
/plan should trigger app level heuristics
some video on latest commit
https://www.loom.com/share/480d640e565d4d28a40e1a5d15fc709c
Agent Mode