@@ -204,6 +204,140 @@ func (s *SQLiteStore) ProviderDashboardStats(ctx context.Context, ownerUserID st
204204 return & stats , nil
205205}
206206
207+ func (s * SQLiteStore ) ListAll (ctx context.Context , agentID , userID string , limit , offset int ) ([]InvocationRecord , int , error ) {
208+ if limit <= 0 {
209+ limit = 50
210+ }
211+
212+ where := "1=1"
213+ var args []interface {}
214+ if agentID != "" {
215+ where += " AND agent_id = ?"
216+ args = append (args , agentID )
217+ }
218+ if userID != "" {
219+ where += " AND user_id = ?"
220+ args = append (args , userID )
221+ }
222+
223+ var total int
224+ countArgs := make ([]interface {}, len (args ))
225+ copy (countArgs , args )
226+ if err := s .db .QueryRowContext (ctx , "SELECT COUNT(*) FROM invocations WHERE " + where , countArgs ... ).Scan (& total ); err != nil {
227+ return nil , 0 , err
228+ }
229+
230+ args = append (args , limit , offset )
231+ rows , err := s .db .QueryContext (ctx ,
232+ "SELECT id, agent_id, user_id, protocol, status_code, duration_ms, error, ip_address, created_at FROM invocations WHERE " + where + " ORDER BY created_at DESC LIMIT ? OFFSET ?" ,
233+ args ... ,
234+ )
235+ if err != nil {
236+ return nil , 0 , err
237+ }
238+ defer func () { _ = rows .Close () }()
239+
240+ var records []InvocationRecord
241+ for rows .Next () {
242+ var r InvocationRecord
243+ var createdAt string
244+ if err := rows .Scan (& r .ID , & r .AgentID , & r .UserID , & r .Protocol , & r .StatusCode , & r .DurationMs , & r .Error , & r .IPAddress , & createdAt ); err != nil {
245+ return nil , 0 , err
246+ }
247+ r .CreatedAt , _ = time .Parse (time .RFC3339 , createdAt )
248+ records = append (records , r )
249+ }
250+ return records , total , rows .Err ()
251+ }
252+
253+ func (s * SQLiteStore ) GlobalStats (ctx context.Context , since time.Time ) (* AgentInvocationStats , error ) {
254+ var stats AgentInvocationStats
255+ err := s .db .QueryRowContext (ctx ,
256+ `SELECT COUNT(*),
257+ SUM(CASE WHEN status_code >= 200 AND status_code < 400 THEN 1 ELSE 0 END),
258+ SUM(CASE WHEN status_code >= 400 OR error != '' THEN 1 ELSE 0 END),
259+ COALESCE(AVG(duration_ms), 0)
260+ FROM invocations WHERE created_at >= ?` ,
261+ since .UTC ().Format (time .RFC3339 ),
262+ ).Scan (& stats .TotalCalls , & stats .SuccessCalls , & stats .ErrorCalls , & stats .AvgDurationMs )
263+ if err != nil {
264+ return nil , err
265+ }
266+ return & stats , nil
267+ }
268+
269+ func (s * SQLiteStore ) GlobalTimeSeries (ctx context.Context , since time.Time , bucketMinutes int ) ([]TimeSeriesPoint , error ) {
270+ if bucketMinutes <= 0 {
271+ bucketMinutes = 60
272+ }
273+ rows , err := s .db .QueryContext (ctx ,
274+ fmt .Sprintf (`SELECT
275+ strftime('%%Y-%%m-%%dT%%H:%%M:00Z', created_at, 'utc', '-' || (strftime('%%M', created_at) %% %d) || ' minutes') as bucket,
276+ COUNT(*),
277+ SUM(CASE WHEN status_code >= 200 AND status_code < 400 THEN 1 ELSE 0 END),
278+ SUM(CASE WHEN status_code >= 400 OR error != '' THEN 1 ELSE 0 END),
279+ COALESCE(AVG(duration_ms), 0)
280+ FROM invocations WHERE created_at >= ?
281+ GROUP BY bucket ORDER BY bucket` , bucketMinutes ),
282+ since .UTC ().Format (time .RFC3339 ),
283+ )
284+ if err != nil {
285+ return nil , err
286+ }
287+ defer func () { _ = rows .Close () }()
288+
289+ var points []TimeSeriesPoint
290+ for rows .Next () {
291+ var p TimeSeriesPoint
292+ var ts string
293+ if err := rows .Scan (& ts , & p .TotalCalls , & p .SuccessCalls , & p .ErrorCalls , & p .AvgDurationMs ); err != nil {
294+ return nil , err
295+ }
296+ p .Timestamp , _ = time .Parse (time .RFC3339 , ts )
297+ points = append (points , p )
298+ }
299+ return points , rows .Err ()
300+ }
301+
302+ func (s * SQLiteStore ) TopAgents (ctx context.Context , since time.Time , limit int ) ([]AgentCallStats , error ) {
303+ if limit <= 0 {
304+ limit = 10
305+ }
306+ rows , err := s .db .QueryContext (ctx ,
307+ `SELECT i.agent_id, COALESCE(a.name, i.agent_id),
308+ COUNT(*),
309+ SUM(CASE WHEN i.status_code >= 200 AND i.status_code < 400 THEN 1 ELSE 0 END),
310+ SUM(CASE WHEN i.status_code >= 400 OR i.error != '' THEN 1 ELSE 0 END),
311+ COALESCE(AVG(i.duration_ms), 0)
312+ FROM invocations i
313+ LEFT JOIN agents a ON a.id = i.agent_id
314+ WHERE i.created_at >= ?
315+ GROUP BY i.agent_id
316+ ORDER BY COUNT(*) DESC LIMIT ?` ,
317+ since .UTC ().Format (time .RFC3339 ), limit ,
318+ )
319+ if err != nil {
320+ return nil , err
321+ }
322+ defer func () { _ = rows .Close () }()
323+
324+ var stats []AgentCallStats
325+ for rows .Next () {
326+ var s AgentCallStats
327+ if err := rows .Scan (& s .AgentID , & s .AgentName , & s .TotalCalls , & s .SuccessCalls , & s .ErrorCalls , & s .AvgDurationMs ); err != nil {
328+ return nil , err
329+ }
330+ stats = append (stats , s )
331+ }
332+ return stats , rows .Err ()
333+ }
334+
335+ func (s * SQLiteStore ) CountInvocations (ctx context.Context ) (int , error ) {
336+ var count int
337+ err := s .db .QueryRowContext (ctx , "SELECT COUNT(*) FROM invocations" ).Scan (& count )
338+ return count , err
339+ }
340+
207341func (s * SQLiteStore ) Close () error {
208342 return nil
209343}
0 commit comments