Skip to content

Commit

Permalink
[AIC-py][editor] bug fix: catch aiconfigruntime load exc (#575)
Browse files Browse the repository at this point in the history
[AIC-py][editor] bug fix: catch aiconfigruntime load exc

---
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with
[ReviewStack](https://reviewstack.dev/lastmile-ai/aiconfig/pull/575).
* #580
* #579
* #576
* __->__ #575
  • Loading branch information
jonathanlastmileai authored Dec 22, 2023
2 parents cda5c4f + fca0578 commit 97eeae2
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 30 deletions.
60 changes: 44 additions & 16 deletions python/src/aiconfig/editor/server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ def _run_with_path(path: str) -> HttpPostResponse:
return result.to_flask_format()


def _get_validated_request_path(raw_path: str) -> Result[str, str]:
def _get_validated_path(raw_path: str) -> Result[str, str]:
if not raw_path:
return Err("No path provided")
resolved = _resolve_path(raw_path)
Expand All @@ -176,11 +176,14 @@ def _get_validated_request_path(raw_path: str) -> Result[str, str]:
return Ok(resolved)


def _http_response_with_path(path_fn: Callable[[str], HttpPostResponse]) -> HttpPostResponse:
def _validated_request_path() -> Result[str, str]:
request_json = request.get_json()
path = request_json.get("path", None)
return _get_validated_path(path)


validated_path = _get_validated_request_path(path)
def _http_response_with_path(path_fn: Callable[[str], HttpPostResponse]) -> HttpPostResponse:
validated_path = _validated_request_path()
match validated_path:
case Ok(path):
return path_fn(path)
Expand All @@ -190,9 +193,9 @@ def _http_response_with_path(path_fn: Callable[[str], HttpPostResponse]) -> Http

@app.route("/api/load", methods=["POST"])
def load():
state = _get_server_state(app)

def _run_with_path(path: str) -> HttpPostResponse:
LOGGER.info(f"Loading AIConfig from {path}")
state = _get_server_state(app)
try:
state.aiconfig = AIConfigRuntime.load(path) # type: ignore
return HttpPostResponse(message="Done")
Expand Down Expand Up @@ -270,34 +273,59 @@ def run_backend_server(edit_config: EditServerConfig) -> Result[str, str]:
LOGGER.info(f"Starting server on http://localhost:{edit_config.server_port}")

app.server_state = ServerState() # type: ignore
_init_server_state(app, edit_config)

debug = edit_config.server_mode in [ServerMode.DEBUG_BACKEND, ServerMode.DEBUG_SERVERS]
LOGGER.info(f"Running in {edit_config.server_mode} mode")
app.run(port=edit_config.server_port, debug=debug, use_reloader=True)
return Ok("Done")
res_server_state_init = _init_server_state(app, edit_config)
match res_server_state_init:
case Ok(_):
LOGGER.info("Initialized server state")
debug = edit_config.server_mode in [ServerMode.DEBUG_BACKEND, ServerMode.DEBUG_SERVERS]
LOGGER.info(f"Running in {edit_config.server_mode} mode")
app.run(port=edit_config.server_port, debug=debug, use_reloader=True)
return Ok("Done")
case Err(e):
LOGGER.error(f"Failed to initialize server state: {e}")
return Err(f"Failed to initialize server state: {e}")


def _load_user_parser_module_if_exists(parsers_module_path: str) -> None:
res = _get_validated_request_path(parsers_module_path).and_then(_load_user_parser_module)
res = _get_validated_path(parsers_module_path).and_then(_load_user_parser_module)
match res:
case Ok(_):
LOGGER.info(f"Loaded parsers module from {parsers_module_path}")
case Err(e):
LOGGER.warning(f"Failed to load parsers module: {e}")


def _init_server_state(app: Flask, edit_config: EditServerConfig) -> None:
def _safe_load_from_disk(aiconfig_path: str) -> Result[AIConfigRuntime, str]:
validated_path = _get_validated_path(aiconfig_path)

def _load(path: str) -> Result[AIConfigRuntime, str]:
try:
aiconfig = AIConfigRuntime.load(path) # type: ignore
return Ok(aiconfig)
except Exception as e:
return core_utils.ErrWithTraceback(e)

return validated_path.and_then(_load)


def _init_server_state(app: Flask, edit_config: EditServerConfig) -> Result[None, str]:
LOGGER.info("Initializing server state")
_load_user_parser_module_if_exists(edit_config.parsers_module_path)
state = _get_server_state(app)

assert state.aiconfig is None
if edit_config.aiconfig_path:
LOGGER.info(f"Loading AIConfig from {edit_config.aiconfig_path}")
aiconfig_runtime = AIConfigRuntime.load(edit_config.aiconfig_path) # type: ignore
state.aiconfig = aiconfig_runtime
LOGGER.info(f"Loaded AIConfig from {edit_config.aiconfig_path}")
aiconfig_runtime = _safe_load_from_disk(edit_config.aiconfig_path)
LOGGER.debug(f"{aiconfig_runtime.is_ok()=}")
match aiconfig_runtime:
case Ok(aiconfig_runtime_):
state.aiconfig = aiconfig_runtime_
LOGGER.info(f"Loaded AIConfig from {edit_config.aiconfig_path}")
return Ok(None)
case Err(e):
LOGGER.error(f"Failed to load AIConfig from {edit_config.aiconfig_path}: {e}")
return Err(f"Failed to load AIConfig from {edit_config.aiconfig_path}: {e}")
else:
aiconfig_runtime = AIConfigRuntime.create() # type: ignore
state.aiconfig = aiconfig_runtime
Expand Down
39 changes: 25 additions & 14 deletions python/src/aiconfig/scripts/aiconfig_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import sys

import lastmile_utils.lib.core.api as core_utils
import result
from aiconfig.editor.server.server import EditServerConfig, ServerMode, run_backend_server
from result import Err, Ok, Result

Expand All @@ -29,6 +30,7 @@ async def main(argv: list[str]) -> int:


def run_subcommand(argv: list[str]) -> Result[str, str]:
LOGGER.info("Running subcommand")
subparser_record_types = {"edit": EditServerConfig}
main_parser = core_utils.argparsify(AIConfigCLIConfig, subparser_record_types=subparser_record_types)

Expand All @@ -39,9 +41,17 @@ def run_subcommand(argv: list[str]) -> Result[str, str]:
LOGGER.info(f"Running subcommand: {subparser_name}")

if subparser_name == "edit":
LOGGER.debug("Running edit subcommand")
res_edit_config = core_utils.parse_args(main_parser, argv[1:], EditServerConfig)
LOGGER.debug(f"{res_edit_config.is_ok()=}")
res_servers = res_edit_config.and_then(_run_editor_servers)
return res_servers
out: Result[str, str] = result.do(
#
Ok(",".join(res_servers_ok))
#
for res_servers_ok in res_servers
)
return out
else:
return Err(f"Unknown subparser: {subparser_name}")

Expand All @@ -53,27 +63,28 @@ def _sigint(procs: list[subprocess.Popen[bytes]]) -> Result[str, str]:
return Ok("Sent SIGINT to frontend servers.")


def _run_editor_servers(edit_config: EditServerConfig) -> Result[str, str]:
def _run_editor_servers(edit_config: EditServerConfig) -> Result[list[str], str]:
LOGGER.info("Running editor servers")
frontend_procs = _run_frontend_server_background() if edit_config.server_mode in [ServerMode.DEBUG_SERVERS] else Ok([])
match frontend_procs:
case Ok(_):
pass
case Err(e):
return Err(e)

results: list[Result[str, str]] = []
backend_res = run_backend_server(edit_config)
match backend_res:
case Ok(msg):
LOGGER.info("Backend server res: Ok:\n%s", msg)
out = Ok(msg)
case Ok(_):
pass
case Err(e):
LOGGER.critical("Backend server err: %s", e)
out = Err(e)
return Err(e)

sigint_res = frontend_procs.and_then(_sigint)
match sigint_res:
case Ok(msg):
LOGGER.info("SIGINT res: Ok:\n%s", msg)
case Err(e):
LOGGER.critical("SIGINT err: %s", e)
results.append(backend_res)

return out
sigint_res = frontend_procs.and_then(_sigint)
results.append(sigint_res)
return core_utils.result_reduce_list_all_ok(results)


def _process_cli_config(cli_config: AIConfigCLIConfig) -> Result[bool, str]:
Expand Down

0 comments on commit 97eeae2

Please sign in to comment.