1use std::collections::HashMap;
38
39#[derive(Debug, Clone)]
41pub struct ViewDefinition {
42 pub name: &'static str,
44 pub description: &'static str,
46 pub definition: &'static str,
48 pub columns: &'static [&'static str],
50 pub base_tables: &'static [&'static str],
52 pub usage_hints: &'static [&'static str],
54}
55
56pub fn get_predefined_views() -> Vec<ViewDefinition> {
58 vec![
59 ViewDefinition {
63 name: "conversation_view",
64 description: "Conversation history with role, content, and timestamp. \
65 Use for displaying chat transcripts or analyzing dialogue patterns.",
66 definition: "SELECT episode_id, seq, role, content, ts \
67 FROM events \
68 WHERE event_type = 'message' \
69 ORDER BY ts",
70 columns: &["episode_id", "seq", "role", "content", "ts"],
71 base_tables: &["events"],
72 usage_hints: &[
73 "Filter by episode_id to get a single conversation",
74 "Use LAST N to get recent messages",
75 "Filter by role to get only user or assistant messages",
76 ],
77 },
78 ViewDefinition {
82 name: "tool_calls_view",
83 description: "Tool invocations with inputs, outputs, and timing. \
84 Use for debugging tool usage or analyzing patterns.",
85 definition: "SELECT episode_id, seq, tool_name, input, output, \
86 tokens_in, tokens_out, latency_ms, ts \
87 FROM events \
88 WHERE event_type = 'tool_call' \
89 ORDER BY ts",
90 columns: &[
91 "episode_id",
92 "seq",
93 "tool_name",
94 "input",
95 "output",
96 "tokens_in",
97 "tokens_out",
98 "latency_ms",
99 "ts",
100 ],
101 base_tables: &["events"],
102 usage_hints: &[
103 "Filter by tool_name to analyze specific tool usage",
104 "Sum tokens_in + tokens_out for cost analysis",
105 "Use AVG(latency_ms) GROUP BY tool_name for performance",
106 ],
107 },
108 ViewDefinition {
112 name: "error_view",
113 description: "Error events with context and stack traces. \
114 Use for debugging failures or monitoring error rates.",
115 definition: "SELECT episode_id, seq, error_type, message, \
116 stack_trace, ts \
117 FROM events \
118 WHERE event_type = 'error' \
119 ORDER BY ts DESC",
120 columns: &[
121 "episode_id",
122 "seq",
123 "error_type",
124 "message",
125 "stack_trace",
126 "ts",
127 ],
128 base_tables: &["events"],
129 usage_hints: &[
130 "Use LAST N for recent errors",
131 "Group by error_type for error categorization",
132 "Join with episodes for context on when errors occurred",
133 ],
134 },
135 ViewDefinition {
139 name: "episode_summary_view",
140 description: "Episode summaries for quick browsing. \
141 Use for listing recent tasks or finding similar past work.",
142 definition: "SELECT episode_id, episode_type, summary, tags, \
143 ts_start, ts_end \
144 FROM episodes \
145 ORDER BY ts_start DESC",
146 columns: &[
147 "episode_id",
148 "episode_type",
149 "summary",
150 "tags",
151 "ts_start",
152 "ts_end",
153 ],
154 base_tables: &["episodes"],
155 usage_hints: &[
156 "Use memory.search_episodes for semantic search",
157 "Filter by episode_type for specific task types",
158 "Calculate ts_end - ts_start for duration",
159 ],
160 },
161 ViewDefinition {
165 name: "entity_directory_view",
166 description: "Entity directory organized by kind. \
167 Use for finding users, projects, services, or documents.",
168 definition: "SELECT entity_id, kind, name, description, updated_at \
169 FROM entities \
170 ORDER BY kind, name",
171 columns: &["entity_id", "kind", "name", "description", "updated_at"],
172 base_tables: &["entities"],
173 usage_hints: &[
174 "Use memory.search_entities for semantic search",
175 "Filter by kind = 'user' or 'project' etc.",
176 "Use memory.get_entity_facts for full details",
177 ],
178 },
179 ViewDefinition {
183 name: "session_timeline_view",
184 description: "Complete session timeline combining messages and tool calls. \
185 Use for full session replay or context building.",
186 definition: "SELECT episode_id, seq, event_type, role, content, \
187 tool_name, ts \
188 FROM events \
189 WHERE event_type IN ('message', 'tool_call', 'tool_result') \
190 ORDER BY episode_id, seq",
191 columns: &[
192 "episode_id",
193 "seq",
194 "event_type",
195 "role",
196 "content",
197 "tool_name",
198 "ts",
199 ],
200 base_tables: &["events"],
201 usage_hints: &[
202 "Filter by episode_id for a single session",
203 "Use logs.tail for recent events",
204 "Interleaves messages and tool calls chronologically",
205 ],
206 },
207 ViewDefinition {
211 name: "metrics_view",
212 description: "Aggregated metrics per episode. \
213 Use for cost analysis, performance monitoring.",
214 definition: "SELECT episode_id, \
215 COUNT(*) as event_count, \
216 SUM(tokens_in) as total_tokens_in, \
217 SUM(tokens_out) as total_tokens_out, \
218 AVG(latency_ms) as avg_latency_ms, \
219 MIN(ts) as first_event, \
220 MAX(ts) as last_event \
221 FROM events \
222 GROUP BY episode_id",
223 columns: &[
224 "episode_id",
225 "event_count",
226 "total_tokens_in",
227 "total_tokens_out",
228 "avg_latency_ms",
229 "first_event",
230 "last_event",
231 ],
232 base_tables: &["events"],
233 usage_hints: &[
234 "Join with episodes for episode metadata",
235 "Use for cost estimation and budgeting",
236 "Calculate total_tokens_in + total_tokens_out for total usage",
237 ],
238 },
239 ]
240}
241
242pub fn get_view(name: &str) -> Option<ViewDefinition> {
244 get_predefined_views().into_iter().find(|v| v.name == name)
245}
246
247pub fn build_view_map() -> HashMap<&'static str, ViewDefinition> {
249 get_predefined_views()
250 .into_iter()
251 .map(|v| (v.name, v))
252 .collect()
253}
254
255pub mod naming {
261 pub const ID_SUFFIX: &str = "_id";
263
264 pub const TS_PREFIX: &str = "ts_";
266
267 pub const COUNT_SUFFIX: &str = "_count";
269
270 pub const BOOL_IS_PREFIX: &str = "is_";
272 pub const BOOL_HAS_PREFIX: &str = "has_";
273
274 pub const ROLES: &[&str] = &["user", "assistant", "system", "tool", "external"];
276
277 pub const EVENT_TYPES: &[&str] = &[
279 "message",
280 "tool_call",
281 "tool_result",
282 "error",
283 "start",
284 "end",
285 "checkpoint",
286 "observation",
287 ];
288
289 pub const ENTITY_KINDS: &[&str] = &[
291 "user",
292 "project",
293 "document",
294 "service",
295 "agent",
296 "organization",
297 "concept",
298 ];
299
300 pub const EPISODE_TYPES: &[&str] = &[
302 "conversation",
303 "task",
304 "workflow",
305 "debug",
306 "agent_interaction",
307 "batch_job",
308 ];
309}
310
311#[cfg(test)]
316mod tests {
317 use super::*;
318
319 #[test]
320 fn test_get_predefined_views() {
321 let views = get_predefined_views();
322 assert!(views.len() >= 5, "Should have at least 5 predefined views");
323
324 let conv = views.iter().find(|v| v.name == "conversation_view");
326 assert!(conv.is_some());
327
328 let conv = conv.unwrap();
329 assert!(conv.columns.contains(&"role"));
330 assert!(conv.columns.contains(&"content"));
331 }
332
333 #[test]
334 fn test_get_view() {
335 let view = get_view("tool_calls_view");
336 assert!(view.is_some());
337
338 let view = view.unwrap();
339 assert!(view.columns.contains(&"tool_name"));
340 assert!(view.base_tables.contains(&"events"));
341 }
342
343 #[test]
344 fn test_build_view_map() {
345 let map = build_view_map();
346 assert!(map.contains_key("error_view"));
347 assert!(map.contains_key("episode_summary_view"));
348 }
349
350 #[test]
351 fn test_naming_conventions() {
352 use naming::*;
353
354 assert!(ROLES.contains(&"user"));
355 assert!(ROLES.contains(&"assistant"));
356 assert!(EVENT_TYPES.contains(&"tool_call"));
357 assert!(ENTITY_KINDS.contains(&"project"));
358 }
359}