Files
galaxy/app/src/ai/agent/telemetry.rs
T

114 lines
4.7 KiB
Rust

use galaxyui::{AppContext, SingletonEntity};
use serde::Serialize;
use super::conversation::AIConversationId;
use super::{
AIAgentCitation, AIAgentExchangeId, EntrypointType, PassiveSuggestionTriggerType,
ServerOutputId,
};
use crate::ai::llms::LLMId;
use crate::server::telemetry::AgentModeCitation as CitationForTelemetry;
use crate::CloudModel;
pub trait ForTelemetry {
type Output;
fn for_telemetry(&self, ctx: &AppContext) -> Option<Self::Output>;
}
impl ForTelemetry for AIAgentCitation {
type Output = CitationForTelemetry;
fn for_telemetry(&self, ctx: &AppContext) -> Option<Self::Output> {
match self {
Self::WarpDriveObject { uid } => {
CloudModel::as_ref(ctx).get_by_uid(uid).map(|object| {
CitationForTelemetry::WarpDriveObject {
object_type: object.object_type(),
uid: object.uid(),
}
})
}
Self::WarpDocumentation { path } => {
Some(CitationForTelemetry::WarpDocs { page: path.clone() })
}
Self::WebPage { url } => Some(CitationForTelemetry::WebPage { url: url.clone() }),
Self::AgentMemory {
memory_store_id,
memory_id,
..
} => Some(CitationForTelemetry::AgentMemory {
memory_store_id: memory_store_id.clone(),
memory_id: memory_id.clone(),
}),
}
}
}
impl EntrypointType {
pub fn entrypoint(&self) -> String {
match self {
Self::PromptSuggestion {
is_static,
is_coding,
} => match (is_static, is_coding) {
(true, true) => "PROMPT_SUGGESTION.CODING_STATIC".to_string(),
(true, false) => "PROMPT_SUGGESTION.STATIC".to_string(),
(false, true) => "PROMPT_SUGGESTION.CODING".to_string(),
(false, false) => "PROMPT_SUGGESTION.SIMPLE".to_string(),
},
Self::ZeroStateAgentModePromptSuggestion => {
"ZERO_STATE_AGENT_MODE_PROMPT_SUGGESTION".to_string()
}
Self::InitProjectRules => "INIT_PROJECT_RULES".to_string(),
Self::UserInitiated => "USER_INITIATED".to_string(),
Self::AgentInitiated => "AGENT_INITIATED".to_string(),
Self::TriggerPassiveSuggestion { trigger } => {
let trigger_name = match trigger {
Some(PassiveSuggestionTriggerType::FilesChanged) => "FILES_CHANGED",
Some(PassiveSuggestionTriggerType::CommandRun) => "COMMAND_RUN",
Some(PassiveSuggestionTriggerType::ShellCommandCompleted) => {
"SHELL_COMMAND_COMPLETED"
}
Some(PassiveSuggestionTriggerType::AgentResponseCompleted) => {
"AGENT_RESPONSE_COMPLETED"
}
None => "NONE",
};
format!("TRIGGER_SUGGEST_PROMPT.{trigger_name}")
}
Self::CloneRepository => "CLONE_REPOSITORY".to_string(),
Self::SharedSession => "SHARED_SESSION".to_string(),
Self::ResumeConversation => "RESUME_CONVERSATION".to_string(),
}
}
}
#[derive(Clone, Default, Debug, Serialize)]
pub struct AIIdentifiers {
/// Useful for joining to client-side telemetry.
#[serde(skip_serializing_if = "Option::is_none")]
pub client_conversation_id: Option<AIConversationId>,
/// A stable ID to relate failures for the same underlying request.
#[serde(rename = "exchange_id", skip_serializing_if = "Option::is_none")]
pub client_exchange_id: Option<AIAgentExchangeId>,
/// Unique ID for this output coming from the AI API. Generated by the server. Only passed in
/// the initial response chunk.
#[serde(skip_serializing_if = "Option::is_none")]
pub server_output_id: Option<ServerOutputId>,
/// The conversation ID included in the response chunk.
///
/// Once this is set, it is never updated. That shouldn't be an issue because the conversation
/// ID is only expected to be passed in the initial response chunk.
///
/// Note that this conversation ID is server-scoped; it is _not_ related to the
/// `AIConversationId`, which is entirely a client-side abstraction.
///
/// This is mainly used for server-side logging and analytics.
#[serde(skip_serializing_if = "Option::is_none")]
pub server_conversation_id: Option<String>,
/// The ID of the model actually used to generate the output. This may differ from the requested model.
#[serde(skip_serializing_if = "Option::is_none")]
pub model_id: Option<LLMId>,
}