mdify-cli 2.11.6__tar.gz → 2.11.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mdify_cli-2.11.6/mdify_cli.egg-info → mdify_cli-2.11.7}/PKG-INFO +1 -1
- {mdify_cli-2.11.6 → mdify_cli-2.11.7}/mdify/__init__.py +1 -1
- {mdify_cli-2.11.6 → mdify_cli-2.11.7}/mdify/cli.py +86 -2
- {mdify_cli-2.11.6 → mdify_cli-2.11.7}/mdify/container.py +27 -1
- {mdify_cli-2.11.6 → mdify_cli-2.11.7/mdify_cli.egg-info}/PKG-INFO +1 -1
- {mdify_cli-2.11.6 → mdify_cli-2.11.7}/pyproject.toml +1 -1
- {mdify_cli-2.11.6 → mdify_cli-2.11.7}/LICENSE +0 -0
- {mdify_cli-2.11.6 → mdify_cli-2.11.7}/README.md +0 -0
- {mdify_cli-2.11.6 → mdify_cli-2.11.7}/assets/mdify.png +0 -0
- {mdify_cli-2.11.6 → mdify_cli-2.11.7}/mdify/__main__.py +0 -0
- {mdify_cli-2.11.6 → mdify_cli-2.11.7}/mdify/docling_client.py +0 -0
- {mdify_cli-2.11.6 → mdify_cli-2.11.7}/mdify_cli.egg-info/SOURCES.txt +0 -0
- {mdify_cli-2.11.6 → mdify_cli-2.11.7}/mdify_cli.egg-info/dependency_links.txt +0 -0
- {mdify_cli-2.11.6 → mdify_cli-2.11.7}/mdify_cli.egg-info/entry_points.txt +0 -0
- {mdify_cli-2.11.6 → mdify_cli-2.11.7}/mdify_cli.egg-info/requires.txt +0 -0
- {mdify_cli-2.11.6 → mdify_cli-2.11.7}/mdify_cli.egg-info/top_level.txt +0 -0
- {mdify_cli-2.11.6 → mdify_cli-2.11.7}/setup.cfg +0 -0
- {mdify_cli-2.11.6 → mdify_cli-2.11.7}/tests/test_cli.py +0 -0
- {mdify_cli-2.11.6 → mdify_cli-2.11.7}/tests/test_container.py +0 -0
- {mdify_cli-2.11.6 → mdify_cli-2.11.7}/tests/test_docling_client.py +0 -0
|
@@ -752,6 +752,13 @@ Examples:
|
|
|
752
752
|
help="Conversion timeout in seconds (default: 1200, can be set via MDIFY_TIMEOUT env var)",
|
|
753
753
|
)
|
|
754
754
|
|
|
755
|
+
parser.add_argument(
|
|
756
|
+
"--memory",
|
|
757
|
+
type=str,
|
|
758
|
+
default=None,
|
|
759
|
+
help="Container memory limit (e.g., 2g, 512m, 4096m). Default: no limit",
|
|
760
|
+
)
|
|
761
|
+
|
|
755
762
|
# Utility options
|
|
756
763
|
parser.add_argument(
|
|
757
764
|
"--check-update",
|
|
@@ -961,6 +968,7 @@ def main() -> int:
|
|
|
961
968
|
args.port,
|
|
962
969
|
timeout=timeout,
|
|
963
970
|
keep_container=DEBUG,
|
|
971
|
+
memory=args.memory,
|
|
964
972
|
) as container:
|
|
965
973
|
# Convert files
|
|
966
974
|
conversion_start = time.time()
|
|
@@ -1020,6 +1028,60 @@ def main() -> int:
|
|
|
1020
1028
|
f"{progress} {input_file.name} ✗ ({format_duration(elapsed)})"
|
|
1021
1029
|
)
|
|
1022
1030
|
print(f" Error: {error_msg}", file=sys.stderr)
|
|
1031
|
+
|
|
1032
|
+
# Check if it's a connection error and retrieve logs
|
|
1033
|
+
is_connection_error = "Connection refused" in error_msg or "Connection aborted" in error_msg or "RemoteDisconnected" in error_msg
|
|
1034
|
+
if is_connection_error:
|
|
1035
|
+
container_alive = container.is_ready()
|
|
1036
|
+
if container_alive:
|
|
1037
|
+
print(
|
|
1038
|
+
" Connection lost (server may have crashed and restarted)",
|
|
1039
|
+
file=sys.stderr,
|
|
1040
|
+
)
|
|
1041
|
+
else:
|
|
1042
|
+
print(
|
|
1043
|
+
" Container crashed while processing file",
|
|
1044
|
+
file=sys.stderr,
|
|
1045
|
+
)
|
|
1046
|
+
print(
|
|
1047
|
+
" File may be too complex, large, or malformed",
|
|
1048
|
+
file=sys.stderr,
|
|
1049
|
+
)
|
|
1050
|
+
|
|
1051
|
+
# Always show logs for connection errors
|
|
1052
|
+
print(" Retrieving container logs...", file=sys.stderr)
|
|
1053
|
+
logs, log_error = container.get_logs(tail=50)
|
|
1054
|
+
if logs:
|
|
1055
|
+
print(" Container logs (last 50 lines):", file=sys.stderr)
|
|
1056
|
+
for line in logs.strip().split("\n"):
|
|
1057
|
+
if line.strip():
|
|
1058
|
+
print(f" {line}", file=sys.stderr)
|
|
1059
|
+
elif log_error:
|
|
1060
|
+
print(f" Error retrieving logs: {log_error}", file=sys.stderr)
|
|
1061
|
+
else:
|
|
1062
|
+
print(" No logs available (container may have been removed)", file=sys.stderr)
|
|
1063
|
+
|
|
1064
|
+
# Restart container if it crashed
|
|
1065
|
+
if not container_alive:
|
|
1066
|
+
print(" Container crashed - attempting to restart...", file=sys.stderr)
|
|
1067
|
+
try:
|
|
1068
|
+
# Stop and remove the dead container
|
|
1069
|
+
container.stop()
|
|
1070
|
+
container.remove()
|
|
1071
|
+
# Generate new container name to avoid conflicts
|
|
1072
|
+
import uuid
|
|
1073
|
+
container.container_name = f"mdify-serve-{uuid.uuid4().hex[:8]}"
|
|
1074
|
+
# Start a new one
|
|
1075
|
+
container.start(timeout=120)
|
|
1076
|
+
print(" Container restarted successfully", file=sys.stderr)
|
|
1077
|
+
print(" Continuing with next file...", file=sys.stderr)
|
|
1078
|
+
except Exception as restart_error:
|
|
1079
|
+
print(f" Failed to restart container: {restart_error}", file=sys.stderr)
|
|
1080
|
+
if DEBUG:
|
|
1081
|
+
import traceback
|
|
1082
|
+
traceback.print_exc()
|
|
1083
|
+
print(" Stopping remaining conversions", file=sys.stderr)
|
|
1084
|
+
break
|
|
1023
1085
|
except Exception as e:
|
|
1024
1086
|
elapsed = time.time() - start_time
|
|
1025
1087
|
failed_count += 1
|
|
@@ -1031,6 +1093,10 @@ def main() -> int:
|
|
|
1031
1093
|
error_msg = str(e)
|
|
1032
1094
|
is_connection_error = "Connection refused" in error_msg or "Connection aborted" in error_msg or "RemoteDisconnected" in error_msg
|
|
1033
1095
|
|
|
1096
|
+
if DEBUG:
|
|
1097
|
+
print(f" DEBUG: Exception caught: {type(e).__name__}", file=sys.stderr)
|
|
1098
|
+
print(f" DEBUG: is_connection_error={is_connection_error}", file=sys.stderr)
|
|
1099
|
+
|
|
1034
1100
|
if is_connection_error:
|
|
1035
1101
|
container_alive = container.is_ready()
|
|
1036
1102
|
if not args.quiet:
|
|
@@ -1078,9 +1144,27 @@ def main() -> int:
|
|
|
1078
1144
|
if not container_alive:
|
|
1079
1145
|
print(" Stopping remaining conversions", file=sys.stderr)
|
|
1080
1146
|
|
|
1081
|
-
#
|
|
1147
|
+
# Restart container if it crashed
|
|
1082
1148
|
if not container_alive:
|
|
1083
|
-
|
|
1149
|
+
print(" Container crashed - attempting to restart...", file=sys.stderr)
|
|
1150
|
+
try:
|
|
1151
|
+
# Stop and remove the dead container
|
|
1152
|
+
container.stop()
|
|
1153
|
+
container.remove()
|
|
1154
|
+
# Generate new container name to avoid conflicts
|
|
1155
|
+
import uuid
|
|
1156
|
+
container.container_name = f"mdify-serve-{uuid.uuid4().hex[:8]}"
|
|
1157
|
+
# Start a new one
|
|
1158
|
+
container.start(timeout=120)
|
|
1159
|
+
print(" Container restarted successfully", file=sys.stderr)
|
|
1160
|
+
print(" Continuing with next file...", file=sys.stderr)
|
|
1161
|
+
except Exception as restart_error:
|
|
1162
|
+
print(f" Failed to restart container: {restart_error}", file=sys.stderr)
|
|
1163
|
+
if DEBUG:
|
|
1164
|
+
import traceback
|
|
1165
|
+
traceback.print_exc()
|
|
1166
|
+
print(" Stopping remaining conversions", file=sys.stderr)
|
|
1167
|
+
break
|
|
1084
1168
|
else:
|
|
1085
1169
|
# Non-connection error
|
|
1086
1170
|
if not args.quiet:
|
|
@@ -27,6 +27,7 @@ class DoclingContainer:
|
|
|
27
27
|
port: int = 5001,
|
|
28
28
|
timeout: int = 1200,
|
|
29
29
|
keep_container: bool = False,
|
|
30
|
+
memory: Optional[str] = None,
|
|
30
31
|
):
|
|
31
32
|
"""Initialize container manager.
|
|
32
33
|
|
|
@@ -36,12 +37,14 @@ class DoclingContainer:
|
|
|
36
37
|
port: Host port to bind (default: 5001)
|
|
37
38
|
timeout: Conversion timeout in seconds (default: 1200)
|
|
38
39
|
keep_container: If True, do not auto-remove container (preserve logs)
|
|
40
|
+
memory: Memory limit (e.g., "2g", "512m"). None for no limit.
|
|
39
41
|
"""
|
|
40
42
|
self.runtime = runtime
|
|
41
43
|
self.image = image
|
|
42
44
|
self.port = port
|
|
43
45
|
self.timeout = timeout
|
|
44
46
|
self.keep_container = keep_container
|
|
47
|
+
self.memory = memory
|
|
45
48
|
self.container_name = f"mdify-serve-{uuid.uuid4().hex[:8]}"
|
|
46
49
|
self.container_id: Optional[str] = None
|
|
47
50
|
|
|
@@ -110,6 +113,11 @@ class DoclingContainer:
|
|
|
110
113
|
]
|
|
111
114
|
if not self.keep_container:
|
|
112
115
|
cmd.insert(3, "--rm") # Auto-remove on stop
|
|
116
|
+
|
|
117
|
+
# Add memory limit if specified
|
|
118
|
+
if self.memory:
|
|
119
|
+
cmd.insert(3, self.memory)
|
|
120
|
+
cmd.insert(3, "-m")
|
|
113
121
|
|
|
114
122
|
try:
|
|
115
123
|
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
|
@@ -135,6 +143,15 @@ class DoclingContainer:
|
|
|
135
143
|
check=False,
|
|
136
144
|
)
|
|
137
145
|
|
|
146
|
+
def remove(self) -> None:
|
|
147
|
+
"""Remove container. Safe to call multiple times."""
|
|
148
|
+
if self.container_name:
|
|
149
|
+
subprocess.run(
|
|
150
|
+
[self.runtime, "rm", "-f", self.container_name],
|
|
151
|
+
capture_output=True,
|
|
152
|
+
check=False,
|
|
153
|
+
)
|
|
154
|
+
|
|
138
155
|
def get_logs(self, tail: int = 50) -> tuple[str, str]:
|
|
139
156
|
"""Get container logs for debugging.
|
|
140
157
|
|
|
@@ -148,8 +165,17 @@ class DoclingContainer:
|
|
|
148
165
|
return ("", "No container name set")
|
|
149
166
|
|
|
150
167
|
try:
|
|
168
|
+
import os
|
|
169
|
+
runtime_name = os.path.basename(self.runtime)
|
|
170
|
+
|
|
171
|
+
# Apple Container uses -n instead of --tail
|
|
172
|
+
if runtime_name == "container":
|
|
173
|
+
cmd = [self.runtime, "logs", "-n", str(tail), self.container_name]
|
|
174
|
+
else:
|
|
175
|
+
cmd = [self.runtime, "logs", "--tail", str(tail), self.container_name]
|
|
176
|
+
|
|
151
177
|
result = subprocess.run(
|
|
152
|
-
|
|
178
|
+
cmd,
|
|
153
179
|
capture_output=True,
|
|
154
180
|
text=True,
|
|
155
181
|
check=False,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|