bugfixes; research subproc; higher sandbox limits

This commit is contained in:
2026-04-16 18:11:26 -04:00
parent f80c943dc3
commit 3153e89d4f
54 changed files with 1947 additions and 498 deletions

View File

@@ -33,7 +33,7 @@ from starlette.routing import Route, Mount
from dexorder import EventPublisher, start_lifecycle_manager, get_lifecycle_manager
from dexorder.api import set_api, API
from dexorder.conda_manager import sync_packages, install_packages, cleanup_extra_packages
from dexorder.conda_manager import sync_packages_async, install_packages_async, cleanup_extra_packages_async
from dexorder.events import EventType, UserEvent, DeliverySpec
from dexorder.impl.charting_api_impl import ChartingAPIImpl
from dexorder.impl.data_api_impl import DataAPIImpl
@@ -893,7 +893,7 @@ def create_mcp_server(config: Config, event_publisher: EventPublisher) -> Server
arguments.get("patch", [])
)
elif name == "python_write":
result = category_manager.write(
result = await category_manager.write(
category=arguments.get("category", ""),
name=arguments.get("name", ""),
description=arguments.get("description", ""),
@@ -920,10 +920,10 @@ def create_mcp_server(config: Config, event_publisher: EventPublisher) -> Server
logging.info(f"python_write '{arguments.get('name')}': no execution result (category={arguments.get('category')})")
if result.get("success"):
_upsert_type(workspace_store, category_manager, arguments.get("category", ""), arguments.get("name", ""))
cleanup_extra_packages(get_data_dir(), _get_env_yml())
await cleanup_extra_packages_async(get_data_dir(), _get_env_yml())
return content
elif name == "python_edit":
result = category_manager.edit(
result = await category_manager.edit(
category=arguments.get("category", ""),
name=arguments.get("name", ""),
code=arguments.get("code"),
@@ -951,7 +951,7 @@ def create_mcp_server(config: Config, event_publisher: EventPublisher) -> Server
logging.info(f"python_edit '{arguments.get('name')}': no execution result")
if result.get("success"):
_upsert_type(workspace_store, category_manager, arguments.get("category", ""), arguments.get("name", ""))
cleanup_extra_packages(get_data_dir(), _get_env_yml())
await cleanup_extra_packages_async(get_data_dir(), _get_env_yml())
return content
elif name == "python_read":
return category_manager.read(
@@ -963,7 +963,7 @@ def create_mcp_server(config: Config, event_publisher: EventPublisher) -> Server
category=arguments.get("category", "")
)
elif name == "python_log":
result = category_manager.git_log(
result = await category_manager.git_log(
category=arguments.get("category"),
name=arguments.get("name"),
limit=int(arguments.get("limit", 20))
@@ -973,7 +973,7 @@ def create_mcp_server(config: Config, event_publisher: EventPublisher) -> Server
lines.append(f"{c['short_hash']} {c['date'][:10]} {c['message']}")
return [TextContent(type="text", text="\n".join(lines))]
elif name == "python_revert":
result = category_manager.git_revert(
result = await category_manager.git_revert(
revision=arguments.get("revision", ""),
category=arguments.get("category", ""),
name=arguments.get("name", "")
@@ -989,13 +989,13 @@ def create_mcp_server(config: Config, event_publisher: EventPublisher) -> Server
_upsert_type(workspace_store, category_manager, arguments.get("category", ""), arguments.get("name", ""))
return [TextContent(type="text", text="\n".join(meta_parts))]
elif name == "python_delete":
result = category_manager.delete(
result = await category_manager.delete(
category=arguments.get("category", ""),
name=arguments.get("name", "")
)
if result.get("success"):
_remove_type(workspace_store, arguments.get("category", ""), arguments.get("name", ""))
cleanup_result = cleanup_extra_packages(get_data_dir(), _get_env_yml())
cleanup_result = await cleanup_extra_packages_async(get_data_dir(), _get_env_yml())
if cleanup_result.get("removed"):
result["packages_removed"] = cleanup_result["removed"]
parts = [f"success: {result['success']}"]
@@ -1004,14 +1004,14 @@ def create_mcp_server(config: Config, event_publisher: EventPublisher) -> Server
parts.append(f"{k}: {result[k]}")
return [TextContent(type="text", text="\n".join(parts))]
elif name == "conda_sync":
return sync_packages(
return await sync_packages_async(
data_dir=get_data_dir(),
environment_yml=_get_env_yml()
)
elif name == "conda_install":
return install_packages(arguments.get("packages", []))
return await install_packages_async(arguments.get("packages", []))
elif name == "execute_research":
result = category_manager.execute_research(name=arguments.get("name", ""))
result = await category_manager.execute_research(name=arguments.get("name", ""))
if "error" in result:
logging.error(f"execute_research '{arguments.get('name')}': {result['error']}")
return [TextContent(type="text", text=f"Error: {result['error']}")]
@@ -1113,6 +1113,8 @@ def create_streamable_http_app(mcp_server: Server) -> Starlette:
@contextlib.asynccontextmanager
async def lifespan(app: Starlette):
from dexorder.event_loop import install_thread_safe_asyncio_run
install_thread_safe_asyncio_run(asyncio.get_running_loop())
async with session_manager.run():
yield
@@ -1156,6 +1158,14 @@ class UserContainer:
# Load configuration
self.config.load()
# Python-level memory guard (RLIMIT_AS soft limit) — DISABLED.
# We assume nodes have ample memory (8Gi limits) and will revisit a
# proper RSS-based cgroup monitor later. The implementation is in
# dexorder/memory_guard.py if we want to re-enable.
# from dexorder.memory_guard import setup_memory_limit
# mem_cfg = self.config.config_data.get("memory", {})
# setup_memory_limit(fraction=float(mem_cfg.get("limit_fraction", 0.85)))
# Initialize data and charting API
data_cfg = self.config.config_data.get("data", {})
iceberg_cfg = data_cfg.get("iceberg", {})