3
3
4
4
import requests
5
5
import structlog
6
- from fastapi import APIRouter , Depends , HTTPException , Response
6
+ from fastapi import APIRouter , Depends , HTTPException , Query , Response
7
7
from fastapi .responses import StreamingResponse
8
8
from fastapi .routing import APIRoute
9
9
from pydantic import BaseModel , ValidationError
10
10
11
+ from codegate .config import API_DEFAULT_PAGE_SIZE , API_MAX_PAGE_SIZE
11
12
import codegate .muxing .models as mux_models
12
13
from codegate import __version__
13
14
from codegate .api import v1_models , v1_processing
14
15
from codegate .db .connection import AlreadyExistsError , DbReader
15
- from codegate .db .models import AlertSeverity , Persona , WorkspaceWithModel
16
+ from codegate .db .models import AlertSeverity , AlertTriggerType , Persona , WorkspaceWithModel
16
17
from codegate .muxing .persona import (
17
18
PersonaDoesNotExistError ,
18
19
PersonaManager ,
@@ -419,7 +420,9 @@ async def get_workspace_alerts(workspace_name: str) -> List[Optional[v1_models.A
419
420
raise HTTPException (status_code = 500 , detail = "Internal server error" )
420
421
421
422
try :
422
- alerts = await dbreader .get_alerts_by_workspace (ws .id , AlertSeverity .CRITICAL .value )
423
+ alerts = await dbreader .get_alerts_by_workspace_or_prompt_id (
424
+ workspace_id = ws .id , trigger_category = AlertSeverity .CRITICAL .value
425
+ )
423
426
prompts_outputs = await dbreader .get_prompts_with_output (ws .id )
424
427
return await v1_processing .parse_get_alert_conversation (alerts , prompts_outputs )
425
428
except Exception :
@@ -443,11 +446,12 @@ async def get_workspace_alerts_summary(workspace_name: str) -> v1_models.AlertSu
443
446
raise HTTPException (status_code = 500 , detail = "Internal server error" )
444
447
445
448
try :
446
- summary = await dbreader .get_alerts_summary_by_workspace ( ws .id )
449
+ summary = await dbreader .get_alerts_summary ( workspace_id = ws .id )
447
450
return v1_models .AlertSummary (
448
- malicious_packages = summary ["codegate_context_retriever_count" ],
449
- pii = summary ["codegate_pii_count" ],
450
- secrets = summary ["codegate_secrets_count" ],
451
+ malicious_packages = summary .total_packages_count ,
452
+ pii = summary .total_pii_count ,
453
+ secrets = summary .total_secrets_count ,
454
+ total_alerts = summary .total_alerts ,
451
455
)
452
456
except Exception :
453
457
logger .exception ("Error while getting alerts summary" )
@@ -459,7 +463,13 @@ async def get_workspace_alerts_summary(workspace_name: str) -> v1_models.AlertSu
459
463
tags = ["Workspaces" ],
460
464
generate_unique_id_function = uniq_name ,
461
465
)
462
- async def get_workspace_messages (workspace_name : str ) -> List [v1_models .Conversation ]:
466
+ async def get_workspace_messages (
467
+ workspace_name : str ,
468
+ page : int = Query (1 , ge = 1 ),
469
+ page_size : int = Query (API_DEFAULT_PAGE_SIZE , ge = 1 , le = API_MAX_PAGE_SIZE ),
470
+ filter_by_ids : Optional [List [str ]] = Query (None ),
471
+ filter_by_alert_trigger_types : Optional [List [AlertTriggerType ]] = Query (None ),
472
+ ) -> v1_models .PaginatedMessagesResponse :
463
473
"""Get messages for a workspace."""
464
474
try :
465
475
ws = await wscrud .get_workspace_by_name (workspace_name )
@@ -469,19 +479,119 @@ async def get_workspace_messages(workspace_name: str) -> List[v1_models.Conversa
469
479
logger .exception ("Error while getting workspace" )
470
480
raise HTTPException (status_code = 500 , detail = "Internal server error" )
471
481
472
- try :
473
- prompts_with_output_alerts_usage = (
474
- await dbreader .get_prompts_with_output_alerts_usage_by_workspace_id (
475
- ws .id , AlertSeverity .CRITICAL .value
476
- )
477
- )
478
- conversations , _ = await v1_processing .parse_messages_in_conversations (
479
- prompts_with_output_alerts_usage
482
+ offset = (page - 1 ) * page_size
483
+ valid_conversations : List [v1_models .ConversationSummary ] = []
484
+ fetched_prompts = 0
485
+
486
+ while len (valid_conversations ) < page_size :
487
+ batch_size = page_size * 2 # Fetch more prompts to compensate for potential skips
488
+
489
+ prompts = await dbreader .get_prompts (
490
+ ws .id ,
491
+ offset + fetched_prompts ,
492
+ batch_size ,
493
+ filter_by_ids ,
494
+ list ([AlertSeverity .CRITICAL .value ]),
495
+ filter_by_alert_trigger_types ,
480
496
)
481
- return conversations
497
+
498
+ if not prompts or len (prompts ) == 0 :
499
+ break
500
+
501
+ # iterate for all prompts to compose the conversation summary
502
+ for prompt in prompts :
503
+ fetched_prompts += 1
504
+ if not prompt .request :
505
+ logger .warning (f"Skipping prompt { prompt .id } . Empty request field" )
506
+ continue
507
+
508
+ messages , _ = await v1_processing .parse_request (prompt .request )
509
+ if not messages or len (messages ) == 0 :
510
+ logger .warning (f"Skipping prompt { prompt .id } . No messages found" )
511
+ continue
512
+
513
+ # message is just the first entry in the request, cleaned properly
514
+ message = v1_processing .parse_question_answer (messages [0 ])
515
+ message_obj = v1_models .ChatMessage (
516
+ message = message , timestamp = prompt .timestamp , message_id = prompt .id
517
+ )
518
+
519
+ # count total alerts for the prompt
520
+ total_alerts_row = await dbreader .get_alerts_summary (prompt_id = prompt .id )
521
+
522
+ # get token usage for the prompt
523
+ prompts_outputs = await dbreader .get_prompts_with_output (prompt_id = prompt .id )
524
+ ws_token_usage = await v1_processing .parse_workspace_token_usage (prompts_outputs )
525
+
526
+ conversation_summary = v1_models .ConversationSummary (
527
+ chat_id = prompt .id ,
528
+ prompt = message_obj ,
529
+ provider = prompt .provider ,
530
+ type = prompt .type ,
531
+ conversation_timestamp = prompt .timestamp ,
532
+ alerts_summary = v1_models .AlertSummary (
533
+ malicious_packages = total_alerts_row .total_packages_count ,
534
+ pii = total_alerts_row .total_pii_count ,
535
+ secrets = total_alerts_row .total_secrets_count ,
536
+ total_alerts = total_alerts_row .total_alerts ,
537
+ ),
538
+ total_alerts = total_alerts_row .total_alerts ,
539
+ token_usage_agg = ws_token_usage ,
540
+ )
541
+
542
+ valid_conversations .append (conversation_summary )
543
+ if len (valid_conversations ) >= page_size :
544
+ break
545
+
546
+ # Fetch total message count
547
+ total_count = await dbreader .get_total_messages_count_by_workspace_id (
548
+ ws .id ,
549
+ filter_by_ids ,
550
+ list ([AlertSeverity .CRITICAL .value ]),
551
+ filter_by_alert_trigger_types ,
552
+ )
553
+
554
+ return v1_models .PaginatedMessagesResponse (
555
+ data = valid_conversations ,
556
+ limit = page_size ,
557
+ offset = offset ,
558
+ total = total_count ,
559
+ )
560
+
561
+
562
+ @v1 .get (
563
+ "/workspaces/{workspace_name}/messages/{prompt_id}" ,
564
+ tags = ["Workspaces" ],
565
+ generate_unique_id_function = uniq_name ,
566
+ )
567
+ async def get_messages_by_prompt_id (
568
+ workspace_name : str ,
569
+ prompt_id : str ,
570
+ ) -> v1_models .Conversation :
571
+ """Get messages for a workspace."""
572
+ try :
573
+ ws = await wscrud .get_workspace_by_name (workspace_name )
574
+ except crud .WorkspaceDoesNotExistError :
575
+ raise HTTPException (status_code = 404 , detail = "Workspace does not exist" )
482
576
except Exception :
483
- logger .exception ("Error while getting messages " )
577
+ logger .exception ("Error while getting workspace " )
484
578
raise HTTPException (status_code = 500 , detail = "Internal server error" )
579
+ prompts_outputs = await dbreader .get_prompts_with_output (
580
+ workspace_id = ws .id , prompt_id = prompt_id
581
+ )
582
+
583
+ # get all alerts for the prompt
584
+ alerts = await dbreader .get_alerts_by_workspace_or_prompt_id (
585
+ workspace_id = ws .id , prompt_id = prompt_id , trigger_category = AlertSeverity .CRITICAL .value
586
+ )
587
+ deduped_alerts = await v1_processing .remove_duplicate_alerts (alerts )
588
+ conversations , _ = await v1_processing .parse_messages_in_conversations (prompts_outputs )
589
+ if not conversations :
590
+ raise HTTPException (status_code = 404 , detail = "Conversation not found" )
591
+
592
+ conversation = conversations [0 ]
593
+ conversation .alerts = deduped_alerts
594
+ return conversation
485
595
486
596
487
597
@v1 .get (
@@ -665,7 +775,7 @@ async def get_workspace_token_usage(workspace_name: str) -> v1_models.TokenUsage
665
775
raise HTTPException (status_code = 500 , detail = "Internal server error" )
666
776
667
777
try :
668
- prompts_outputs = await dbreader .get_prompts_with_output (ws .id )
778
+ prompts_outputs = await dbreader .get_prompts_with_output (workspace_id = ws .id )
669
779
ws_token_usage = await v1_processing .parse_workspace_token_usage (prompts_outputs )
670
780
return ws_token_usage
671
781
except Exception :
0 commit comments