1717import asyncio
1818from contextlib import asynccontextmanager
1919import importlib
20+ import json
2021import logging
2122import os
2223import time
@@ -395,6 +396,9 @@ class AdkWebServer:
395396 managing evaluation set results.
396397 agents_dir: Root directory containing subdirs for agents with those
397398 containing resources (e.g. .env files, eval sets, etc.) for the agents.
399+ extra_plugins: A list of fully qualified names of extra plugins to load.
400+ logo_text: Text to display in the logo of the UI.
401+ logo_image_url: URL of an image to display as logo of the UI.
398402 runners_to_clean: Set of runner names marked for cleanup.
399403 current_app_name_ref: A shared reference to the latest ran app name.
400404 runner_dict: A dict of instantiated runners for each app.
@@ -412,6 +416,8 @@ def __init__(
412416 eval_set_results_manager : EvalSetResultsManager ,
413417 agents_dir : str ,
414418 extra_plugins : Optional [list [str ]] = None ,
419+ logo_text : Optional [str ] = None ,
420+ logo_image_url : Optional [str ] = None ,
415421 ):
416422 self .agent_loader = agent_loader
417423 self .session_service = session_service
@@ -422,6 +428,8 @@ def __init__(
422428 self .eval_set_results_manager = eval_set_results_manager
423429 self .agents_dir = agents_dir
424430 self .extra_plugins = extra_plugins or []
431+ self .logo_text = logo_text
432+ self .logo_image_url = logo_image_url
425433 # Internal propeties we want to allow being modified from callbacks.
426434 self .runners_to_clean : set [str ] = set ()
427435 self .current_app_name_ref : SharedValue [str ] = SharedValue (value = "" )
@@ -506,6 +514,52 @@ def _import_plugin_object(self, qualified_name: str) -> Any:
506514 module = importlib .import_module (module_name )
507515 return getattr (module , obj_name )
508516
517+ def _setup_runtime_config (self , web_assets_dir : str ):
518+ """Sets up the runtime config for the web server."""
519+ # Read existing runtime config file.
520+ runtime_config_path = os .path .join (
521+ web_assets_dir , "assets" , "config" , "runtime-config.json"
522+ )
523+ runtime_config = {}
524+ try :
525+ with open (runtime_config_path , "r" ) as f :
526+ runtime_config = json .load (f )
527+ except FileNotFoundError :
528+ logger .info (
529+ "File not found: %s. A new runtime config file will be created." ,
530+ runtime_config_path ,
531+ )
532+ except json .JSONDecodeError :
533+ logger .warning (
534+ "Failed to decode JSON from %s. The file content will be"
535+ " overwritten." ,
536+ runtime_config_path ,
537+ )
538+
539+ # Set custom logo config.
540+ if self .logo_text or self .logo_image_url :
541+ if not self .logo_text or not self .logo_image_url :
542+ raise ValueError (
543+ "Both --logo-text and --logo-image-url must be defined when using"
544+ " logo config."
545+ )
546+ runtime_config ["logo" ] = {
547+ "text" : self .logo_text ,
548+ "imageUrl" : self .logo_image_url ,
549+ }
550+ elif "logo" in runtime_config :
551+ del runtime_config ["logo" ]
552+
553+ # Write the runtime config file.
554+ try :
555+ os .makedirs (os .path .dirname (runtime_config_path ), exist_ok = True )
556+ with open (runtime_config_path , "w" ) as f :
557+ json .dump (runtime_config , f , indent = 2 )
558+ except IOError as e :
559+ logger .error (
560+ "Failed to write runtime config file %s: %s" , runtime_config_path , e
561+ )
562+
509563 def get_fast_api_app (
510564 self ,
511565 lifespan : Optional [Lifespan [FastAPI ]] = None ,
@@ -570,6 +624,8 @@ async def internal_lifespan(app: FastAPI):
570624 export_lib .SimpleSpanProcessor (memory_exporter ),
571625 ],
572626 )
627+ if web_assets_dir :
628+ self ._setup_runtime_config (web_assets_dir )
573629
574630 # TODO - register_processors to be removed once --otel_to_cloud is no
575631 # longer experimental.
@@ -1413,6 +1469,13 @@ async def process_messages():
14131469 mimetypes .add_type ("application/javascript" , ".js" , True )
14141470 mimetypes .add_type ("text/javascript" , ".js" , True )
14151471
1472+ @app .get ("/dev-ui/config" )
1473+ async def get_ui_config ():
1474+ return {
1475+ "logo_text" : self .logo_text ,
1476+ "logo_image_url" : self .logo_image_url ,
1477+ }
1478+
14161479 @app .get ("/" )
14171480 async def redirect_root_to_dev_ui ():
14181481 return RedirectResponse ("/dev-ui/" )
0 commit comments