systemprompt_analytics/services/
service.rs1use std::sync::Arc;
7
8use http::HeaderMap;
9
10use systemprompt_models::ContentRouting;
11use systemprompt_traits::ExtractSignals;
12
13use crate::GeoIpReader;
14use crate::repository::{AnalyticsRepositories, CostAnalyticsRepository, SessionRepository};
15use crate::services::{ProfileUsageService, SessionAnalytics, SessionAnalyticsBuilder};
16
17#[derive(Clone)]
18pub struct AnalyticsService {
19 geoip_reader: Option<GeoIpReader>,
20 content_routing: Option<Arc<dyn ContentRouting>>,
21 session_repo: SessionRepository,
22 cost_repo: CostAnalyticsRepository,
23}
24
25impl std::fmt::Debug for AnalyticsService {
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 f.debug_struct("AnalyticsService")
28 .field("geoip_reader", &self.geoip_reader.is_some())
29 .field("content_routing", &self.content_routing.is_some())
30 .field("session_repo", &"SessionRepository")
31 .field("cost_repo", &"CostAnalyticsRepository")
32 .finish()
33 }
34}
35
36impl AnalyticsService {
37 pub fn new(
38 geoip_reader: Option<GeoIpReader>,
39 content_routing: Option<Arc<dyn ContentRouting>>,
40 repositories: &AnalyticsRepositories,
41 ) -> Self {
42 Self {
43 geoip_reader,
44 content_routing,
45 session_repo: repositories.sessions.clone(),
46 cost_repo: repositories.costs.clone(),
47 }
48 }
49
50 pub fn extract_analytics(
51 &self,
52 headers: &HeaderMap,
53 signals: ExtractSignals<'_>,
54 ) -> SessionAnalytics {
55 let mut builder = SessionAnalyticsBuilder::new(headers);
56 if let Some(uri) = signals.uri {
57 builder = builder.with_uri(uri);
58 }
59 if let Some(reader) = self.geoip_reader.as_ref() {
60 builder = builder.with_geoip(reader);
61 }
62 if let Some(content_routing) = self.content_routing.as_deref() {
63 builder = builder.with_content_routing(content_routing);
64 }
65 if let Some(caller_ip) = signals.caller_ip {
66 builder = builder.with_caller_ip(caller_ip);
67 }
68 builder.build()
69 }
70
71
72 pub const fn cost_repo(&self) -> &CostAnalyticsRepository {
73 &self.cost_repo
74 }
75
76 #[must_use]
77 pub fn profile_usage(&self) -> ProfileUsageService {
78 ProfileUsageService::new(self.cost_repo.clone())
79 }
80
81 pub const fn session_repo(&self) -> &SessionRepository {
82 &self.session_repo
83 }
84}