AmmarFahmy
adding all files
105b369
from collections import OrderedDict
from pathlib import Path
from typing import Dict, List, Optional
from phi.cli.console import print_heading, print_info
from phi.cli.settings import phi_cli_settings
from phi.api.schemas.user import UserSchema
from phi.api.schemas.workspace import WorkspaceSchema, WorkspaceDelete
from phi.utils.log import logger
from phi.utils.json_io import read_json_file, write_json_file
from phi.workspace.config import WorkspaceConfig
class PhiCliConfig:
"""The PhiCliConfig class manages user data for the phi cli"""
def __init__(
self,
user: Optional[UserSchema] = None,
active_ws_dir: Optional[str] = None,
ws_config_map: Optional[Dict[str, WorkspaceConfig]] = None,
) -> None:
# Current user, populated after authenticating with the api
# To add a user, use the user setter
self._user: Optional[UserSchema] = user
# Active ws dir - used as the default for `phi` commands
# To add an active workspace, use the active_ws_dir setter
self._active_ws_dir: Optional[str] = active_ws_dir
# Mapping from ws_root_path to ws_config
self.ws_config_map: Dict[str, WorkspaceConfig] = ws_config_map or OrderedDict()
######################################################
## User functions
######################################################
@property
def user(self) -> Optional[UserSchema]:
return self._user
@user.setter
def user(self, user: Optional[UserSchema]) -> None:
"""Sets the user"""
if user is not None:
logger.debug(f"Setting user to: {user.email}")
clear_user_cache = (
self._user is not None # previous user is not None
and self._user.email != "anon" # previous user is not anon
and (user.email != self._user.email or user.id_user != self._user.id_user) # new user is different
)
self._user = user
if clear_user_cache:
self.clear_user_cache()
self.save_config()
def clear_user_cache(self) -> None:
"""Clears the user cache"""
logger.debug("Clearing user cache")
self.ws_config_map.clear()
self._active_ws_dir = None
phi_cli_settings.ai_conversations_path.unlink(missing_ok=True)
logger.info("Workspaces cleared, please setup again using `phi ws setup`")
######################################################
## Workspace functions
######################################################
@property
def active_ws_dir(self) -> Optional[str]:
return self._active_ws_dir
def set_active_ws_dir(self, ws_root_path: Optional[Path]) -> None:
if ws_root_path is not None:
logger.debug(f"Setting active workspace to: {str(ws_root_path)}")
self._active_ws_dir = str(ws_root_path)
self.save_config()
@property
def available_ws(self) -> List[WorkspaceConfig]:
return list(self.ws_config_map.values())
def _add_or_update_ws_config(
self, ws_root_path: Path, ws_schema: Optional[WorkspaceSchema] = None
) -> Optional[WorkspaceConfig]:
"""The main function to create, update or refresh a WorkspaceConfig.
This function does not call self.save_config(). Remember to save_config() after calling this function.
"""
# Validate ws_root_path
if ws_root_path is None or not isinstance(ws_root_path, Path):
raise ValueError(f"Invalid ws_root: {ws_root_path}")
ws_root_str = str(ws_root_path)
######################################################
# Create new ws_config if one does not exist
######################################################
if ws_root_str not in self.ws_config_map:
logger.debug(f"Creating workspace at: {ws_root_str}")
new_workspace_config = WorkspaceConfig(
ws_root_path=ws_root_path,
ws_schema=ws_schema,
)
self.ws_config_map[ws_root_str] = new_workspace_config
logger.debug(f"Workspace created at: {ws_root_str}")
# Return the new_workspace_config
return new_workspace_config
######################################################
# Update ws_config
######################################################
logger.debug(f"Updating workspace at: {ws_root_str}")
# By this point there should be a WorkspaceConfig object for this ws_name
existing_ws_config: Optional[WorkspaceConfig] = self.ws_config_map.get(ws_root_str, None)
if existing_ws_config is None:
logger.error(f"Could not find workspace at: {ws_root_str}, please run `phi ws setup`")
return None
# Update the ws_schema if it's not None and different from the existing one
if ws_schema is not None and existing_ws_config.ws_schema != ws_schema:
existing_ws_config.ws_schema = ws_schema
logger.debug(f"Workspace updated: {ws_root_str}")
# Return the updated_ws_config
return existing_ws_config
######################################################
# END
######################################################
def add_new_ws_to_config(self, ws_root_path: Path) -> Optional[WorkspaceConfig]:
"""Adds a newly created workspace to the PhiCliConfig"""
ws_config = self._add_or_update_ws_config(ws_root_path=ws_root_path)
self.save_config()
return ws_config
def update_ws_config(
self,
ws_root_path: Path,
ws_schema: Optional[WorkspaceSchema] = None,
set_as_active: bool = False,
) -> Optional[WorkspaceConfig]:
"""Updates WorkspaceConfig and returns True if successful"""
ws_config = self._add_or_update_ws_config(
ws_root_path=ws_root_path,
ws_schema=ws_schema,
)
if set_as_active:
self._active_ws_dir = str(ws_root_path)
self.save_config()
return ws_config
def delete_ws(self, ws_root_path: Path) -> None:
"""Handles Deleting a workspace from the PhiCliConfig and api"""
ws_root_str = str(ws_root_path)
print_heading(f"Deleting record for workspace at: {ws_root_str}")
print_info("-*- Note: this does not delete any files on disk, please delete them manually")
ws_config: Optional[WorkspaceConfig] = self.ws_config_map.pop(ws_root_str, None)
if ws_config is None:
logger.warning(f"No record of workspace at {ws_root_str}")
return
# Check if we're deleting the active workspace, if yes, unset the active ws
if self._active_ws_dir is not None and self._active_ws_dir == ws_root_str:
print_info(f"Removing {ws_root_str} as the active workspace")
self._active_ws_dir = None
if self.user is not None and ws_config.ws_schema is not None:
print_info("Deleting workspace from the server")
from phi.api.workspace import delete_workspace_for_user
delete_workspace_for_user(
user=self.user,
workspace=WorkspaceDelete(
id_workspace=ws_config.ws_schema.id_workspace, ws_name=ws_config.ws_schema.ws_name
),
)
self.save_config()
######################################################
## Get Workspace Data
######################################################
def get_ws_config_by_dir_name(self, ws_dir_name: str) -> Optional[WorkspaceConfig]:
ws_root_str: Optional[str] = None
for k, v in self.ws_config_map.items():
if v.ws_root_path.stem == ws_dir_name:
ws_root_str = k
break
if ws_root_str is None or ws_root_str not in self.ws_config_map:
return None
return self.ws_config_map[ws_root_str]
def get_ws_config_by_path(self, ws_root_path: Path) -> Optional[WorkspaceConfig]:
return self.ws_config_map[str(ws_root_path)] if str(ws_root_path) in self.ws_config_map else None
def get_active_ws_config(self) -> Optional[WorkspaceConfig]:
if self.active_ws_dir is not None and self.active_ws_dir in self.ws_config_map:
return self.ws_config_map[self.active_ws_dir]
return None
######################################################
## Save PhiCliConfig
######################################################
def save_config(self):
config_data = {
"user": self.user.model_dump() if self.user else None,
"active_ws_dir": self.active_ws_dir,
"ws_config_map": {k: v.to_dict() for k, v in self.ws_config_map.items()},
}
write_json_file(file_path=phi_cli_settings.config_file_path, data=config_data)
@classmethod
def from_saved_config(cls):
try:
config_data = read_json_file(file_path=phi_cli_settings.config_file_path)
if config_data is None or not isinstance(config_data, dict):
logger.debug("No config found")
return None
user_dict = config_data.get("user")
user_schema = UserSchema.model_validate(user_dict) if user_dict else None
active_ws_dir = config_data.get("active_ws_dir")
# Create a new config
new_config = cls(user_schema, active_ws_dir)
# Add all the workspaces
for k, v in config_data.get("ws_config_map", {}).items():
_ws_config = WorkspaceConfig.from_dict(v)
if _ws_config is not None:
new_config.ws_config_map[k] = _ws_config
return new_config
except Exception as e:
logger.warning(e)
logger.warning("Please setup the workspace using `phi ws setup`")
######################################################
## Print PhiCliConfig
######################################################
def print_to_cli(self, show_all: bool = False):
if self.user:
print_heading(f"User: {self.user.email}\n")
if self.active_ws_dir:
print_heading(f"Active workspace directory: {self.active_ws_dir}\n")
else:
print_info("No active workspace found.")
print_info(
"Please create a workspace using `phi ws create` " "or setup existing workspace using `phi ws setup`"
)
if show_all and len(self.ws_config_map) > 0:
print_heading("Available workspaces:\n")
c = 1
for k, v in self.ws_config_map.items():
print_info(f" {c}. {k}")
if v.ws_schema and v.ws_schema.ws_name:
print_info(f" Name: {v.ws_schema.ws_name}")
c += 1