88from contextlib import asynccontextmanager
99from pathlib import Path
1010from queue import Queue
11- from typing import TYPE_CHECKING , Any , Dict , List , Optional
11+ from typing import TYPE_CHECKING , Any , Dict , List , Optional , Literal
1212
1313import psutil
1414import uvicorn
@@ -327,6 +327,8 @@ def __init__(
327327 port : Optional [int ] = 8000 ,
328328 index_file : str = "index.html" ,
329329 elasticsearch_config : Optional [ElasticsearchConfig ] = None ,
330+ backend : Literal ["fireworks" , "elasticsearch" ] = "elasticsearch" ,
331+ fireworks_base_url : Optional [str ] = None ,
330332 debug : bool = False ,
331333 ):
332334 # Enable debug mode if requested
@@ -336,6 +338,10 @@ def __init__(
336338 # Initialize WebSocket manager
337339 self .websocket_manager = WebSocketManager ()
338340
341+ # Backend selection
342+ self .backend : Literal ["fireworks" , "elasticsearch" ] = backend
343+ self .fireworks_base_url = fireworks_base_url
344+
339345 # Initialize Elasticsearch client if config is provided
340346 self .elasticsearch_client : Optional [ElasticsearchClient ] = None
341347 if elasticsearch_config :
@@ -414,6 +420,8 @@ async def status():
414420 # Expose an empty list to satisfy consumers and type checker
415421 "watch_paths" : [],
416422 "elasticsearch_enabled" : self .elasticsearch_client is not None ,
423+ "backend" : self .backend ,
424+ "fireworks_enabled" : self .backend == "fireworks" ,
417425 }
418426
419427 @self .app .get ("/api/logs/{rollout_id}" , response_model = LogsResponse , response_model_exclude_none = True )
@@ -422,7 +430,47 @@ async def get_logs(
422430 level : Optional [str ] = Query (None , description = "Filter by log level (DEBUG, INFO, WARNING, ERROR)" ),
423431 limit : int = Query (100 , description = "Maximum number of log entries to return" ),
424432 ) -> LogsResponse :
425- """Get logs for a specific rollout ID from Elasticsearch."""
433+ """Get logs for a specific rollout ID from the configured backend."""
434+ # Fireworks backend
435+ if self .backend == "fireworks" :
436+ try :
437+ from eval_protocol .adapters .fireworks_tracing import FireworksTracingAdapter
438+
439+ base_url = self .fireworks_base_url or "https://tracing.fireworks.ai"
440+ adapter = FireworksTracingAdapter (base_url = base_url )
441+ # Fetch lightweight log entries filtered by rollout_id tag
442+ tags = [f"rollout_id:{ rollout_id } " ]
443+ entries = adapter .search_logs (tags = tags , limit = limit )
444+ # Map to LogEntry responses
445+ log_entries : List [LogEntry ] = []
446+ for e in entries :
447+ ts = e .get ("timestamp" ) or datetime .utcnow ().isoformat () + "Z"
448+ msg = e .get ("message" ) or "trace"
449+ sev = e .get ("severity" ) or "INFO"
450+ entry = LogEntry (
451+ ** {
452+ "@timestamp" : ts ,
453+ "level" : sev ,
454+ "message" : str (msg ),
455+ "logger_name" : "fireworks" ,
456+ "rollout_id" : rollout_id ,
457+ }
458+ )
459+ log_entries .append (entry )
460+
461+ return LogsResponse (
462+ logs = log_entries ,
463+ total = len (log_entries ),
464+ rollout_id = rollout_id ,
465+ filtered_by_level = level ,
466+ )
467+ except HTTPException :
468+ raise
469+ except Exception as e :
470+ logger .error (f"Error retrieving Fireworks logs for rollout { rollout_id } : { e } " )
471+ raise HTTPException (status_code = 500 , detail = f"Failed to retrieve Fireworks logs: { str (e )} " )
472+
473+ # Elasticsearch backend
426474 if not self .elasticsearch_client :
427475 raise HTTPException (status_code = 503 , detail = "Elasticsearch is not configured for this logs server" )
428476
@@ -574,6 +622,8 @@ def create_app(
574622 port : int = 8000 ,
575623 build_dir : Optional [str ] = None ,
576624 elasticsearch_config : Optional [ElasticsearchConfig ] = None ,
625+ backend : Literal ["fireworks" , "elasticsearch" ] = "elasticsearch" ,
626+ fireworks_base_url : Optional [str ] = None ,
577627 debug : bool = False ,
578628) -> FastAPI :
579629 """
@@ -597,20 +647,36 @@ def create_app(
597647 )
598648
599649 server = LogsServer (
600- host = host , port = port , build_dir = build_dir , elasticsearch_config = elasticsearch_config , debug = debug
650+ host = host ,
651+ port = port ,
652+ build_dir = build_dir ,
653+ elasticsearch_config = elasticsearch_config ,
654+ backend = backend ,
655+ fireworks_base_url = fireworks_base_url ,
656+ debug = debug ,
601657 )
602658 server .start_loops ()
603659 return server .app
604660
605661
606662# For backward compatibility and direct usage
607663def serve_logs (
608- port : Optional [int ] = None , elasticsearch_config : Optional [ElasticsearchConfig ] = None , debug : bool = False
664+ port : Optional [int ] = None ,
665+ elasticsearch_config : Optional [ElasticsearchConfig ] = None ,
666+ debug : bool = False ,
667+ backend : Literal ["fireworks" , "elasticsearch" ] = "elasticsearch" ,
668+ fireworks_base_url : Optional [str ] = None ,
609669):
610670 """
611671 Convenience function to create and run a LogsServer.
612672 """
613- server = LogsServer (port = port , elasticsearch_config = elasticsearch_config , debug = debug )
673+ server = LogsServer (
674+ port = port ,
675+ elasticsearch_config = elasticsearch_config ,
676+ debug = debug ,
677+ backend = backend ,
678+ fireworks_base_url = fireworks_base_url ,
679+ )
614680 server .run ()
615681
616682
0 commit comments