greentic_runner_host/engine/
host.rs

1use super::error::GResult;
2use async_trait::async_trait;
3use greentic_types::TenantCtx;
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6use std::collections::HashMap;
7use std::hash::{Hash, Hasher};
8use std::sync::Arc;
9use std::time::{Duration, SystemTime};
10
11#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, Hash)]
12pub struct SpanContext {
13    pub trace_id: Option<String>,
14    pub span_id: Option<String>,
15}
16
17#[async_trait]
18pub trait SecretsHost: Send + Sync {
19    async fn get(&self, name: &str) -> GResult<String>;
20}
21
22#[async_trait]
23pub trait TelemetryHost: Send + Sync {
24    async fn emit(&self, span: &SpanContext, fields: &[(&str, &str)]) -> GResult<()>;
25}
26
27#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
28pub struct SessionKey {
29    pub tenant_key: String,
30    pub flow_id: String,
31    pub session_hint: Option<String>,
32}
33
34impl SessionKey {
35    pub fn new(tenant: &TenantCtx, flow_id: &str, session_hint: Option<String>) -> Self {
36        let tenant_key = format!("{}::{}", tenant.env.as_str(), tenant.tenant.as_str());
37        Self {
38            tenant_key,
39            flow_id: flow_id.to_string(),
40            session_hint,
41        }
42    }
43
44    pub fn stable_session_id(&self) -> Option<String> {
45        self.session_hint.clone()
46    }
47}
48
49#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)]
50pub struct SessionCursor {
51    pub position: usize,
52    pub outbox_seq: u64,
53}
54
55#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
56pub struct SessionOutboxEntry {
57    pub seq: u64,
58    pub hash: String,
59    pub response: Value,
60}
61
62#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
63pub struct WaitState {
64    pub reason: String,
65    pub recorded_at: SystemTime,
66}
67
68#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
69pub struct SessionSnapshot {
70    pub key: SessionKey,
71    pub session_id: String,
72    pub revision: u64,
73    pub cursor: SessionCursor,
74    pub state: Value,
75    pub outbox: HashMap<OutboxKey, SessionOutboxEntry>,
76    pub waiting: Option<WaitState>,
77    pub last_outcome: Option<Value>,
78    pub ttl: Duration,
79}
80
81impl SessionSnapshot {
82    pub fn new(key: SessionKey, session_id: String) -> Self {
83        Self {
84            key,
85            session_id,
86            revision: 0,
87            cursor: SessionCursor::default(),
88            state: Value::Object(Default::default()),
89            outbox: HashMap::new(),
90            waiting: None,
91            last_outcome: None,
92            ttl: Duration::from_secs(3600),
93        }
94    }
95}
96
97#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
98pub struct OutboxKey {
99    seq: u64,
100    hash: String,
101}
102
103impl OutboxKey {
104    pub fn new(seq: u64, hash: String) -> Self {
105        Self { seq, hash }
106    }
107}
108
109impl Hash for OutboxKey {
110    fn hash<H: Hasher>(&self, state: &mut H) {
111        self.seq.hash(state);
112        self.hash.hash(state);
113    }
114}
115
116#[async_trait]
117pub trait SessionHost: Send + Sync {
118    async fn get(&self, key: &SessionKey) -> GResult<Option<SessionSnapshot>>;
119    async fn put(&self, snapshot: SessionSnapshot) -> GResult<()>;
120    async fn update_cas(&self, snapshot: SessionSnapshot, expected_revision: u64) -> GResult<bool>;
121    async fn delete(&self, key: &SessionKey) -> GResult<()>;
122    async fn touch(&self, key: &SessionKey, ttl: Duration) -> GResult<()>;
123}
124
125#[async_trait]
126pub trait StateHost: Send + Sync {
127    async fn get_json(&self, key: &SessionKey) -> GResult<Option<Value>>;
128    async fn set_json(&self, key: &SessionKey, value: Value) -> GResult<()>;
129    async fn del(&self, key: &SessionKey) -> GResult<()>;
130    async fn del_prefix(&self, key_prefix: &str) -> GResult<()>;
131}
132
133pub struct HostBundle {
134    pub secrets: Arc<dyn SecretsHost>,
135    pub telemetry: Arc<dyn TelemetryHost>,
136    pub session: Arc<dyn SessionHost>,
137    pub state: Arc<dyn StateHost>,
138}
139
140impl HostBundle {
141    pub fn new(
142        secrets: Arc<dyn SecretsHost>,
143        telemetry: Arc<dyn TelemetryHost>,
144        session: Arc<dyn SessionHost>,
145        state: Arc<dyn StateHost>,
146    ) -> Self {
147        Self {
148            secrets,
149            telemetry,
150            session,
151            state,
152        }
153    }
154}