Skip to content

Commit 1ceff08

Browse files
fix: warmup thread imports only, avoids double model load OOM
On memory-constrained containers, the warmup thread and the first search request could load the model concurrently (~2.4GB for two bfloat16 instances). Reduce warmup to import-only (torch, SentenceTransformer, Encoder) so the first _get_encoder() call is the only model load.
1 parent 438ac81 commit 1ceff08

1 file changed

Lines changed: 35 additions & 29 deletions

File tree

imas_codex/llm/server.py

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,7 +1049,7 @@ def search_dd_paths(
10491049
try:
10501050
tools = _get_imas_tools(semantic_search=True)
10511051
result = _run_async(
1052-
tools.search_dd_paths(
1052+
tools.search_tool.search_dd_paths(
10531053
query=query_text,
10541054
ids_filter=ids_filter,
10551055
max_results=max_results,
@@ -1083,7 +1083,7 @@ def fetch_dd_paths(paths: str, dd_version: int | None = None) -> str:
10831083
try:
10841084
tools = _get_imas_tools()
10851085
result = _run_async(
1086-
tools.fetch_dd_paths(paths=paths, dd_version=dd_version)
1086+
tools.path_tool.fetch_dd_paths(paths=paths, dd_version=dd_version)
10871087
)
10881088
return str(result)
10891089
except Exception as e:
@@ -1109,7 +1109,7 @@ def list_dd_paths(
11091109
try:
11101110
tools = _get_imas_tools()
11111111
result = _run_async(
1112-
tools.list_dd_paths(
1112+
tools.list_tool.list_dd_paths(
11131113
paths=paths,
11141114
leaf_only=leaf_only,
11151115
max_paths=max_paths,
@@ -1133,7 +1133,7 @@ def check_dd_paths(paths: str, dd_version: int | None = None) -> str:
11331133
try:
11341134
tools = _get_imas_tools()
11351135
result = _run_async(
1136-
tools.check_dd_paths(paths=paths, dd_version=dd_version)
1136+
tools.path_tool.check_dd_paths(paths=paths, dd_version=dd_version)
11371137
)
11381138
return str(result)
11391139
except Exception as e:
@@ -1161,7 +1161,7 @@ def get_dd_overview(
11611161
)
11621162
tools = _get_imas_tools()
11631163
result = _run_async(
1164-
tools.get_dd_overview(
1164+
tools.overview_tool.get_dd_overview(
11651165
query=query_text,
11661166
dd_version=dd_version,
11671167
)
@@ -1188,7 +1188,7 @@ def get_dd_path_context(
11881188
try:
11891189
tools = _get_imas_tools()
11901190
result = _run_async(
1191-
tools.get_dd_path_context(
1191+
tools.path_context_tool.get_dd_path_context(
11921192
path=path,
11931193
relationship_types=relationship_types,
11941194
dd_version=dd_version,
@@ -1216,7 +1216,7 @@ def export_imas_ids(
12161216
try:
12171217
tools = _get_imas_tools()
12181218
result = _run_async(
1219-
tools.export_imas_ids(
1219+
tools.structure_tool.export_imas_ids(
12201220
ids_name=ids_name,
12211221
leaf_only=leaf_only,
12221222
dd_version=dd_version,
@@ -1244,7 +1244,7 @@ def export_imas_domain(
12441244
try:
12451245
tools = _get_imas_tools()
12461246
result = _run_async(
1247-
tools.export_imas_domain(
1247+
tools.structure_tool.export_imas_domain(
12481248
domain=domain,
12491249
ids_filter=ids_filter,
12501250
dd_version=dd_version,
@@ -1823,16 +1823,18 @@ def __post_init__(self):
18231823
f"MCP server ready ({mode}) with {tool_count} tools and {len(self._prompts)} prompts"
18241824
)
18251825

1826-
# Pre-warm the embedding model in a background thread so the first
1827-
# search_dd_paths call doesn't pay the 30s+ cold-start penalty.
1828-
# Import Encoder directly to avoid circular import through tools/__init__.
1826+
# Pre-warm heavy imports in a background thread so the first
1827+
# search_dd_paths call doesn't pay the full cold-start penalty.
1828+
# Only imports modules — does NOT load the model, to avoid OOM
1829+
# from two concurrent model instances on memory-constrained containers.
18291830
def _warmup_encoder():
18301831
try:
1831-
from imas_codex.embeddings.encoder import Encoder
1832+
import torch # noqa: F401
1833+
from sentence_transformers import SentenceTransformer # noqa: F401
18321834

1833-
encoder = Encoder()
1834-
encoder.embed_texts(["warmup"])
1835-
logger.info("Encoder warmup complete")
1835+
from imas_codex.embeddings.encoder import Encoder # noqa: F401
1836+
1837+
logger.info("Encoder warmup complete (imports only)")
18361838
except Exception as e:
18371839
logger.warning(
18381840
f"Encoder warmup failed (will retry on first query): {e}"
@@ -2523,7 +2525,7 @@ def search_dd_paths(
25232525
# independent operations sharing the same encoder singleton.
25242526
def _path_search():
25252527
return _run_async(
2526-
tools.search_dd_paths(
2528+
tools.search_tool.search_dd_paths(
25272529
query=query,
25282530
ids_filter=ids_filter,
25292531
max_results=k,
@@ -2594,7 +2596,9 @@ def check_dd_paths(
25942596

25952597
tools = _get_imas_tools()
25962598
result = _run_async(
2597-
tools.check_dd_paths(paths=paths, ids=ids, dd_version=dd_version)
2599+
tools.path_tool.check_dd_paths(
2600+
paths=paths, ids=ids, dd_version=dd_version
2601+
)
25982602
)
25992603
return format_check_report(result)
26002604

@@ -2628,7 +2632,7 @@ def fetch_dd_paths(
26282632
)
26292633
tools = _get_imas_tools()
26302634
result = _run_async(
2631-
tools.fetch_dd_paths(
2635+
tools.path_tool.fetch_dd_paths(
26322636
paths=paths,
26332637
ids=ids,
26342638
dd_version=dd_version,
@@ -2654,7 +2658,7 @@ def fetch_dd_error_fields(
26542658
"""
26552659
tools = _get_imas_tools()
26562660
result = _run_async(
2657-
tools.fetch_dd_error_fields(path=path, dd_version=dd_version)
2661+
tools.path_tool.fetch_dd_error_fields(path=path, dd_version=dd_version)
26582662
)
26592663
return _format_error_fields_report(result)
26602664

@@ -2696,7 +2700,7 @@ def list_dd_paths(
26962700
)
26972701
tools = _get_imas_tools()
26982702
result = _run_async(
2699-
tools.list_dd_paths(
2703+
tools.list_tool.list_dd_paths(
27002704
paths=paths,
27012705
leaf_only=leaf_only,
27022706
max_paths=max_paths,
@@ -2732,7 +2736,7 @@ def get_dd_overview(
27322736
)
27332737
tools = _get_imas_tools()
27342738
result = _run_async(
2735-
tools.get_dd_overview(
2739+
tools.overview_tool.get_dd_overview(
27362740
query=query,
27372741
dd_version=dd_version,
27382742
# include_unit_stats not yet implemented in backend
@@ -2760,7 +2764,9 @@ def get_dd_identifiers(
27602764

27612765
tools = _get_imas_tools()
27622766
result = _run_async(
2763-
tools.get_dd_identifiers(query=query, dd_version=dd_version)
2767+
tools.identifiers_tool.get_dd_identifiers(
2768+
query=query, dd_version=dd_version
2769+
)
27642770
)
27652771
return format_identifiers_report(result)
27662772

@@ -2827,7 +2833,7 @@ def find_related_dd_paths(
28272833

28282834
tools = _get_imas_tools()
28292835
result = _run_async(
2830-
tools.get_dd_path_context(
2836+
tools.path_context_tool.get_dd_path_context(
28312837
path=path,
28322838
relationship_types=relationship_types,
28332839
max_results=max_results,
@@ -2858,7 +2864,7 @@ def export_imas_ids(
28582864

28592865
tools = _get_imas_tools()
28602866
result = _run_async(
2861-
tools.export_imas_ids(
2867+
tools.structure_tool.export_imas_ids(
28622868
ids_name=ids_name,
28632869
leaf_only=leaf_only,
28642870
dd_version=dd_version,
@@ -2886,7 +2892,7 @@ def export_imas_domain(
28862892

28872893
tools = _get_imas_tools()
28882894
result = _run_async(
2889-
tools.export_imas_domain(
2895+
tools.structure_tool.export_imas_domain(
28902896
domain=domain,
28912897
ids_filter=ids_filter,
28922898
dd_version=dd_version,
@@ -2916,7 +2922,7 @@ def get_dd_cocos_fields(
29162922

29172923
tools = _get_imas_tools()
29182924
result = _run_async(
2919-
tools.get_dd_cocos_fields(
2925+
tools.structure_tool.get_cocos_fields(
29202926
transformation_type=transformation_type,
29212927
ids_filter=ids_filter,
29222928
dd_version=dd_version,
@@ -2959,7 +2965,7 @@ def get_dd_version_context(
29592965
"""
29602966
tools = _get_imas_tools()
29612967
result = _run_async(
2962-
tools.get_dd_version_context(
2968+
tools.version_tool.get_dd_version_context(
29632969
paths=paths,
29642970
change_type_filter=change_type_filter,
29652971
ids_filter=ids_filter,
@@ -2978,7 +2984,7 @@ def get_dd_versions() -> str:
29782984
Formatted text report with current version, version count, available version range, and ordered version chain.
29792985
"""
29802986
tools = _get_imas_tools()
2981-
result = _run_async(tools.get_dd_versions())
2987+
result = _run_async(tools.version_tool.get_dd_versions())
29822988
return _format_dd_versions_report(result)
29832989

29842990
@self.mcp.tool()
@@ -3008,7 +3014,7 @@ def get_dd_changelog(
30083014

30093015
tools = _get_imas_tools()
30103016
result = _run_async(
3011-
tools.get_dd_changelog(
3017+
tools.version_tool.get_dd_changelog(
30123018
ids_filter=ids_filter,
30133019
from_version=from_version,
30143020
to_version=to_version,

0 commit comments

Comments
 (0)