pub const SEMCONV_VERSION: &str = "1.42.0";
pub const CAPTURE_CONTENT_ENV: &str = "OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT";
pub const RATEL_SEARCH: &str = "ratel.search";
pub const EXECUTE_TOOL: &str = "execute_tool";
pub const RATEL_SKILL_LOAD: &str = "ratel.skill.load";
pub const RATEL_UPSTREAM_REGISTER: &str = "ratel.upstream.register";
pub const RATEL_AUTH_FLOW: &str = "ratel.auth.flow";
pub const RATEL_SEARCH_RESULTS: &str = "ratel.search.results";
pub const GEN_AI_INFERENCE_DETAILS: &str = "gen_ai.client.inference.operation.details";
pub const RATEL_ORIGIN: &str = "ratel.origin";
pub const RATEL_SEARCH_TARGET: &str = "ratel.search.target";
pub const RATEL_SEARCH_TOP_K: &str = "ratel.search.top_k";
pub const RATEL_SEARCH_HIT_COUNT: &str = "ratel.search.hit_count";
pub const RATEL_SEARCH_QUERY: &str = "ratel.search.query";
pub const RATEL_TOOL_ARGS_SIZE_BYTES: &str = "ratel.tool.args_size_bytes";
pub const RATEL_UPSTREAM_SERVER: &str = "ratel.upstream.server";
pub const RATEL_UPSTREAM_TRANSPORT: &str = "ratel.upstream.transport";
pub const RATEL_UPSTREAM_TOOL_COUNT: &str = "ratel.upstream.tool_count";
pub const RATEL_SKILL_ID: &str = "ratel.skill.id";
pub const RATEL_AUTH_OUTCOME: &str = "ratel.auth.outcome";
pub const GEN_AI_OPERATION_NAME: &str = "gen_ai.operation.name";
pub const GEN_AI_TOOL_NAME: &str = "gen_ai.tool.name";
pub const GEN_AI_TOOL_CALL_ID: &str = "gen_ai.tool.call.id";
pub const GEN_AI_TOOL_CALL_ARGUMENTS: &str = "gen_ai.tool.call.arguments";
pub const GEN_AI_TOOL_CALL_RESULT: &str = "gen_ai.tool.call.result";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Origin {
Direct,
Agent,
}
impl Origin {
pub fn as_str(self) -> &'static str {
match self {
Origin::Direct => "direct",
Origin::Agent => "agent",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SearchTarget {
Tool,
Skill,
}
impl SearchTarget {
pub fn as_str(self) -> &'static str {
match self {
SearchTarget::Tool => "tool",
SearchTarget::Skill => "skill",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AuthOutcome {
Ok,
Refreshed,
NeedsAuth,
Failed,
}
impl AuthOutcome {
pub fn as_str(self) -> &'static str {
match self {
AuthOutcome::Ok => "ok",
AuthOutcome::Refreshed => "refreshed",
AuthOutcome::NeedsAuth => "needs_auth",
AuthOutcome::Failed => "failed",
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn origin_maps_to_wire_strings() {
assert_eq!(Origin::Direct.as_str(), "direct");
assert_eq!(Origin::Agent.as_str(), "agent");
}
#[test]
fn search_target_maps_to_wire_strings() {
assert_eq!(SearchTarget::Tool.as_str(), "tool");
assert_eq!(SearchTarget::Skill.as_str(), "skill");
}
#[test]
fn auth_outcome_maps_to_wire_strings() {
assert_eq!(AuthOutcome::Ok.as_str(), "ok");
assert_eq!(AuthOutcome::Refreshed.as_str(), "refreshed");
assert_eq!(AuthOutcome::NeedsAuth.as_str(), "needs_auth");
assert_eq!(AuthOutcome::Failed.as_str(), "failed");
}
#[test]
fn ratel_attribute_keys_match_the_pin() {
assert_eq!(RATEL_ORIGIN, "ratel.origin");
assert_eq!(RATEL_SEARCH_TARGET, "ratel.search.target");
assert_eq!(RATEL_SEARCH_TOP_K, "ratel.search.top_k");
assert_eq!(RATEL_SEARCH_HIT_COUNT, "ratel.search.hit_count");
assert_eq!(RATEL_SEARCH_QUERY, "ratel.search.query");
assert_eq!(RATEL_TOOL_ARGS_SIZE_BYTES, "ratel.tool.args_size_bytes");
assert_eq!(RATEL_UPSTREAM_SERVER, "ratel.upstream.server");
assert_eq!(RATEL_UPSTREAM_TRANSPORT, "ratel.upstream.transport");
assert_eq!(RATEL_UPSTREAM_TOOL_COUNT, "ratel.upstream.tool_count");
assert_eq!(RATEL_SKILL_ID, "ratel.skill.id");
assert_eq!(RATEL_AUTH_OUTCOME, "ratel.auth.outcome");
}
#[test]
fn semconv_pin_is_explicit() {
assert_eq!(SEMCONV_VERSION, "1.42.0");
}
#[test]
fn content_capture_gate_is_the_ecosystem_env_var() {
assert_eq!(
CAPTURE_CONTENT_ENV,
"OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT"
);
}
#[test]
fn span_event_names_match_the_pin() {
assert_eq!(RATEL_SEARCH_RESULTS, "ratel.search.results");
assert_eq!(
GEN_AI_INFERENCE_DETAILS,
"gen_ai.client.inference.operation.details"
);
}
#[test]
fn span_names_match_the_pin() {
assert_eq!(RATEL_SEARCH, "ratel.search");
assert_eq!(RATEL_SKILL_LOAD, "ratel.skill.load");
assert_eq!(RATEL_UPSTREAM_REGISTER, "ratel.upstream.register");
assert_eq!(RATEL_AUTH_FLOW, "ratel.auth.flow");
}
#[test]
fn tool_invocation_is_the_gen_ai_execute_tool_operation_not_ratel_invoke() {
assert_eq!(EXECUTE_TOOL, "execute_tool");
assert_ne!(EXECUTE_TOOL, "ratel.invoke");
}
#[test]
fn gen_ai_interop_keys_match_the_pin() {
assert_eq!(GEN_AI_OPERATION_NAME, "gen_ai.operation.name");
assert_eq!(GEN_AI_TOOL_NAME, "gen_ai.tool.name");
assert_eq!(GEN_AI_TOOL_CALL_ID, "gen_ai.tool.call.id");
assert_eq!(GEN_AI_TOOL_CALL_ARGUMENTS, "gen_ai.tool.call.arguments");
assert_eq!(GEN_AI_TOOL_CALL_RESULT, "gen_ai.tool.call.result");
for key in [
GEN_AI_OPERATION_NAME,
GEN_AI_TOOL_NAME,
GEN_AI_TOOL_CALL_ID,
GEN_AI_TOOL_CALL_ARGUMENTS,
GEN_AI_TOOL_CALL_RESULT,
] {
assert!(key.starts_with("gen_ai."), "{key} is not under gen_ai.*");
assert!(
!key.starts_with("ratel."),
"{key} must not be renamed into ratel.*"
);
}
}
#[test]
fn every_ratel_attribute_key_is_namespaced() {
for key in [
RATEL_ORIGIN,
RATEL_SEARCH_TARGET,
RATEL_SEARCH_TOP_K,
RATEL_SEARCH_HIT_COUNT,
RATEL_SEARCH_QUERY,
RATEL_TOOL_ARGS_SIZE_BYTES,
RATEL_UPSTREAM_SERVER,
RATEL_UPSTREAM_TRANSPORT,
RATEL_UPSTREAM_TOOL_COUNT,
RATEL_SKILL_ID,
RATEL_AUTH_OUTCOME,
] {
assert!(key.starts_with("ratel."), "{key} is not under ratel.*");
}
}
#[test]
fn attribute_keys_are_unique() {
let keys = [
RATEL_ORIGIN,
RATEL_SEARCH_TARGET,
RATEL_SEARCH_TOP_K,
RATEL_SEARCH_HIT_COUNT,
RATEL_SEARCH_QUERY,
RATEL_TOOL_ARGS_SIZE_BYTES,
RATEL_UPSTREAM_SERVER,
RATEL_UPSTREAM_TRANSPORT,
RATEL_UPSTREAM_TOOL_COUNT,
RATEL_SKILL_ID,
RATEL_AUTH_OUTCOME,
GEN_AI_OPERATION_NAME,
GEN_AI_TOOL_NAME,
GEN_AI_TOOL_CALL_ID,
GEN_AI_TOOL_CALL_ARGUMENTS,
GEN_AI_TOOL_CALL_RESULT,
];
let unique: std::collections::HashSet<&str> = keys.iter().copied().collect();
assert_eq!(unique.len(), keys.len(), "duplicate attribute key");
}
#[test]
fn span_names_are_unique() {
let names = [
RATEL_SEARCH,
EXECUTE_TOOL,
RATEL_SKILL_LOAD,
RATEL_UPSTREAM_REGISTER,
RATEL_AUTH_FLOW,
];
let unique: std::collections::HashSet<&str> = names.iter().copied().collect();
assert_eq!(unique.len(), names.len(), "duplicate span name");
}
#[test]
fn event_names_are_unique() {
let names = [RATEL_SEARCH_RESULTS, GEN_AI_INFERENCE_DETAILS];
let unique: std::collections::HashSet<&str> = names.iter().copied().collect();
assert_eq!(unique.len(), names.len(), "duplicate event name");
}
}