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