vlmparse 0.1.7__py3-none-any.whl → 0.1.9__py3-none-any.whl

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.
Files changed (36) hide show
  1. vlmparse/build_doc.py +20 -19
  2. vlmparse/cli.py +439 -270
  3. vlmparse/clients/chandra.py +176 -60
  4. vlmparse/clients/deepseekocr.py +193 -12
  5. vlmparse/clients/docling.py +0 -1
  6. vlmparse/clients/dotsocr.py +34 -31
  7. vlmparse/clients/glmocr.py +243 -0
  8. vlmparse/clients/granite_docling.py +9 -36
  9. vlmparse/clients/hunyuanocr.py +5 -1
  10. vlmparse/clients/lightonocr.py +23 -1
  11. vlmparse/clients/mineru.py +0 -1
  12. vlmparse/clients/mistral_converter.py +85 -0
  13. vlmparse/clients/nanonetocr.py +5 -1
  14. vlmparse/clients/olmocr.py +6 -2
  15. vlmparse/clients/openai_converter.py +95 -60
  16. vlmparse/clients/paddleocrvl.py +195 -40
  17. vlmparse/converter.py +51 -11
  18. vlmparse/converter_with_server.py +92 -19
  19. vlmparse/registries.py +107 -89
  20. vlmparse/servers/base_server.py +127 -0
  21. vlmparse/servers/docker_compose_deployment.py +489 -0
  22. vlmparse/servers/docker_compose_server.py +39 -0
  23. vlmparse/servers/docker_run_deployment.py +226 -0
  24. vlmparse/servers/docker_server.py +17 -109
  25. vlmparse/servers/model_identity.py +48 -0
  26. vlmparse/servers/server_registry.py +42 -0
  27. vlmparse/servers/utils.py +83 -219
  28. vlmparse/st_viewer/st_viewer.py +1 -1
  29. vlmparse/utils.py +15 -2
  30. {vlmparse-0.1.7.dist-info → vlmparse-0.1.9.dist-info}/METADATA +13 -3
  31. vlmparse-0.1.9.dist-info/RECORD +44 -0
  32. {vlmparse-0.1.7.dist-info → vlmparse-0.1.9.dist-info}/WHEEL +1 -1
  33. vlmparse-0.1.7.dist-info/RECORD +0 -36
  34. {vlmparse-0.1.7.dist-info → vlmparse-0.1.9.dist-info}/entry_points.txt +0 -0
  35. {vlmparse-0.1.7.dist-info → vlmparse-0.1.9.dist-info}/licenses/LICENSE +0 -0
  36. {vlmparse-0.1.7.dist-info → vlmparse-0.1.9.dist-info}/top_level.txt +0 -0
vlmparse/cli.py CHANGED
@@ -1,318 +1,487 @@
1
+ # ruff: noqa: B008
1
2
  from typing import Literal
2
3
 
4
+ import typer
3
5
  from loguru import logger
4
6
 
5
-
6
- class DParseCLI:
7
- """Parsing of pdf to text using VLMs: typ in vlmparse to get the command lists, then `vlmparse <command> --help` to get help on a specific command."""
8
-
9
- def serve(
10
- self,
11
- model: str,
12
- port: int | None = None,
13
- gpus: str | None = None,
14
- vllm_args: list[str] | None = None,
15
- forget_predefined_vllm_args: bool = False,
16
- ):
17
- """Deploy a VLLM server in a Docker container.
18
-
19
- Args:
20
- model: Model name
21
- port: VLLM server port (default: 8056)
22
- gpus: Comma-separated GPU device IDs (e.g., "0" or "0,1,2"). If not specified, all GPUs will be used.
23
- vllm_args: Additional keyword arguments to pass to the VLLM server.
24
- forget_predefined_vllm_args: If True, the predefined VLLM kwargs from the docker config will be replaced by vllm_args otherwise the predefined kwargs will be updated with vllm_args with a risk of collision of argument names.
25
- """
26
-
27
- from vlmparse.converter_with_server import start_server
28
-
29
- base_url, container, _, _ = start_server(
30
- model=model,
31
- gpus=gpus,
32
- port=port,
33
- with_vllm_server=True,
34
- vllm_args=vllm_args,
35
- forget_predefined_vllm_args=forget_predefined_vllm_args,
36
- auto_stop=False,
7
+ app = typer.Typer(help="Parse PDF documents with VLMs.", pretty_exceptions_enable=False)
8
+
9
+
10
+ @app.command("serve")
11
+ def serve(
12
+ model: str = typer.Argument(..., help="Model name"),
13
+ port: int | None = typer.Option(None, help="VLLM server port (default: 8056)"),
14
+ gpus: str | None = typer.Option(
15
+ None,
16
+ help='Comma-separated GPU device IDs (e.g., "0" or "0,1,2"). If not specified, all GPUs will be used.',
17
+ ),
18
+ server: Literal["registry", "hf"] = typer.Option(
19
+ "registry", help="Server type for the model. 'registry' (default) or 'hf'."
20
+ ),
21
+ vllm_args: list[str] | None = typer.Option(
22
+ None,
23
+ "--vllm-args",
24
+ help="Additional keyword arguments to pass to the VLLM server.",
25
+ ),
26
+ forget_predefined_vllm_args: bool = typer.Option(
27
+ False,
28
+ help=(
29
+ "If True, the predefined VLLM kwargs from the docker config will be replaced by vllm_args. "
30
+ "Otherwise they will be updated with vllm_args (may overwrite keys)."
31
+ ),
32
+ ),
33
+ ):
34
+ """Deploy a VLLM server in a Docker container.
35
+
36
+ Args:
37
+ model: Model name
38
+ port: VLLM server port (default: 8056)
39
+ gpus: Comma-separated GPU device IDs (e.g., "0" or "0,1,2"). If not specified, GPU 0 will be used.
40
+ server: Server type for the model. 'registry' (default) or 'hf'.
41
+ vllm_args: Additional keyword arguments to pass to the VLLM server.
42
+ forget_predefined_vllm_args: If True, the predefined VLLM kwargs from the docker config will be replaced by vllm_args otherwise the predefined kwargs will be updated with vllm_args with a risk of collision of argument names.
43
+ """
44
+
45
+ from vlmparse.converter_with_server import start_server
46
+
47
+ base_url, container, _, _ = start_server(
48
+ model=model,
49
+ gpus=gpus,
50
+ port=port,
51
+ server=server,
52
+ vllm_args=vllm_args,
53
+ forget_predefined_vllm_args=forget_predefined_vllm_args,
54
+ auto_stop=False,
55
+ )
56
+
57
+ logger.info(f"✓ VLLM server ready at {base_url}")
58
+ if container is not None:
59
+ logger.info(f"✓ Container ID: {container.id}")
60
+ logger.info(f"✓ Container name: {container.name}")
61
+
62
+
63
+ @app.command("convert")
64
+ def convert(
65
+ inputs: str = typer.Argument(..., help="List of folders to process"),
66
+ out_folder: str = typer.Option(".", help="Output folder for parsed documents"),
67
+ model: str | None = typer.Option(
68
+ None, help="Model name. If not specified, inferred from the URI."
69
+ ),
70
+ uri: str | None = typer.Option(
71
+ None,
72
+ help=(
73
+ "URI of the server. If not specified and the pipe is vllm, "
74
+ "a local server will be deployed."
75
+ ),
76
+ ),
77
+ gpus: str | None = typer.Option(
78
+ None,
79
+ help='Comma-separated GPU device IDs (e.g., "0" or "0,1,2"). If not specified, GPU 0 will be used.',
80
+ ),
81
+ mode: Literal["document", "md", "md_page"] = typer.Option(
82
+ "document",
83
+ help=(
84
+ "Output mode - document (save as JSON zip), md (save as markdown file), "
85
+ "md_page (save as folder of markdown pages)"
86
+ ),
87
+ ),
88
+ conversion_mode: Literal[
89
+ "ocr",
90
+ "ocr_layout",
91
+ "table",
92
+ "image_description",
93
+ "formula",
94
+ "chart",
95
+ ] = typer.Option(
96
+ "ocr",
97
+ help=(
98
+ "Conversion mode - ocr (plain), ocr_layout (OCR with layout), table (table-centric), "
99
+ "image_description (describe the image), formula (formula extraction), chart (chart recognition)"
100
+ ),
101
+ ),
102
+ server: Literal["registry", "hf", "google", "openai"] = typer.Option(
103
+ "registry",
104
+ help="Server type for the model. Defaults to 'registry'.",
105
+ ),
106
+ with_vllm_server: bool = typer.Option(
107
+ False,
108
+ help=(
109
+ "Deprecated. Use --server hf instead. "
110
+ "If True, a local VLLM server will be deployed if the model is not found in the registry. "
111
+ "Note that if the model is in the registry and uri is None, the server will be deployed."
112
+ ),
113
+ ),
114
+ concurrency: int = typer.Option(10, help="Number of parallel requests"),
115
+ dpi: int | None = typer.Option(None, help="DPI to use for the conversion"),
116
+ debug: bool = typer.Option(False, help="Run in debug mode"),
117
+ _return_documents: bool = typer.Option(False, hidden=True),
118
+ ):
119
+ """Parse PDF documents and save results.
120
+
121
+ Args:
122
+ inputs: List of folders to process
123
+ out_folder: Output folder for parsed documents
124
+ pipe: Converter type ("vllm", "openai", or "lightonocr", default: "vllm")
125
+ model: Model name. If not specified, the model will be inferred from the URI.
126
+ uri: URI of the server, if not specified and the pipe is vllm, a local server will be deployed
127
+ gpus: Comma-separated GPU device IDs (e.g., "0" or "0,1,2"). If not specified, all GPUs will be used.
128
+ mode: Output mode - "document" (save as JSON zip), "md" (save as markdown file), "md_page" (save as folder of markdown pages)
129
+ conversion_mode: Conversion mode - "ocr" (plain), "ocr_layout" (OCR with layout), "table" (table-centric), "image_description" (describe the image), "formula" (formula extraction), "chart" (chart recognition)
130
+ server: Server type for the model. Defaults to 'registry'.
131
+ with_vllm_server: Deprecated. Use --server hf instead. If True, a local VLLM server will be deployed if the model is not found in the registry. Note that if the model is in the registry and the uri is None, the server will be anyway deployed.
132
+ dpi: DPI to use for the conversion. If not specified, the default DPI will be used.
133
+ debug: If True, run in debug mode (single-threaded, no concurrency)
134
+ """
135
+ from vlmparse.converter_with_server import ConverterWithServer
136
+
137
+ if with_vllm_server and server == "registry":
138
+ server = "hf"
139
+
140
+ with ConverterWithServer(
141
+ model=model,
142
+ uri=uri,
143
+ gpus=gpus,
144
+ server=server,
145
+ concurrency=concurrency,
146
+ return_documents=_return_documents,
147
+ ) as converter_with_server:
148
+ return converter_with_server.parse(
149
+ inputs=inputs,
150
+ out_folder=out_folder,
151
+ mode=mode,
152
+ conversion_mode=conversion_mode,
153
+ dpi=dpi,
154
+ debug=debug,
37
155
  )
38
156
 
39
- logger.info(f"✓ VLLM server ready at {base_url}")
40
- if container is not None:
41
- logger.info(f"✓ Container ID: {container.id}")
42
- logger.info(f"✓ Container name: {container.name}")
43
-
44
- def convert(
45
- self,
46
- inputs: str | list[str],
47
- out_folder: str = ".",
48
- model: str | None = None,
49
- uri: str | None = None,
50
- gpus: str | None = None,
51
- mode: Literal["document", "md", "md_page"] = "document",
52
- with_vllm_server: bool = False,
53
- concurrency: int = 10,
54
- dpi: int | None = None,
55
- debug: bool = False,
56
- ):
57
- """Parse PDF documents and save results.
58
-
59
- Args:
60
- inputs: List of folders to process
61
- out_folder: Output folder for parsed documents
62
- pipe: Converter type ("vllm", "openai", or "lightonocr", default: "vllm")
63
- model: Model name. If not specified, the model will be inferred from the URI.
64
- uri: URI of the server, if not specified and the pipe is vllm, a local server will be deployed
65
- gpus: Comma-separated GPU device IDs (e.g., "0" or "0,1,2"). If not specified, all GPUs will be used.
66
- mode: Output mode - "document" (save as JSON zip), "md" (save as markdown file), "md_page" (save as folder of markdown pages)
67
- with_vllm_server: If True, a local VLLM server will be deployed if the model is not found in the registry. Note that if the model is in the registry and the uri is None, the server will be anyway deployed.
68
- dpi: DPI to use for the conversion. If not specified, the default DPI will be used.
69
- debug: If True, run in debug mode (single-threaded, no concurrency)
70
- """
71
- from vlmparse.converter_with_server import ConverterWithServer
72
-
73
- with ConverterWithServer(
74
- model=model,
75
- uri=uri,
76
- gpus=gpus,
77
- with_vllm_server=with_vllm_server,
78
- concurrency=concurrency,
79
- ) as converter_with_server:
80
- return converter_with_server.parse(
81
- inputs=inputs, out_folder=out_folder, mode=mode, dpi=dpi, debug=debug
82
- )
83
157
 
84
- def list(self):
85
- """List all containers whose name begins with vlmparse."""
86
- import docker
158
+ @app.command("list")
159
+ def containers():
160
+ """List all containers whose name begins with vlmparse."""
161
+ import docker
87
162
 
88
- try:
89
- client = docker.from_env()
90
- containers = client.containers.list()
163
+ from vlmparse.servers.utils import _get_container_labels
91
164
 
92
- if not containers:
93
- logger.info("No running containers found")
94
- return
165
+ try:
166
+ client = docker.from_env()
167
+ all_containers = client.containers.list(all=True)
95
168
 
96
- # Filter for containers whose name begins with "vlmparse"
97
- vlmparse_containers = [
98
- container
99
- for container in containers
100
- if container.name.startswith("vlmparse")
101
- ]
169
+ if not all_containers:
170
+ logger.info("No containers found")
171
+ return
102
172
 
103
- if not vlmparse_containers:
104
- logger.info("No vlmparse containers found")
105
- return
173
+ # Group containers by compose project or as standalone
174
+ projects = {} # project_name -> list of containers
106
175
 
107
- # Prepare table data
108
- table_data = []
109
- for container in vlmparse_containers:
110
- # Extract port mappings
111
- ports = []
176
+ for container in all_containers:
177
+ labels = _get_container_labels(container)
178
+ project = labels.get("com.docker.compose.project") or labels.get(
179
+ "vlmparse_compose_project"
180
+ )
181
+
182
+ # Include if name starts with vlmparse OR if it's in a vlmparse compose project
183
+ if container.name.startswith("vlmparse"):
184
+ if project:
185
+ projects.setdefault(project, []).append(container)
186
+ else:
187
+ projects[container.name] = [container]
188
+ elif project and project.startswith("vlmparse"):
189
+ projects.setdefault(project, []).append(container)
190
+
191
+ if not projects:
192
+ logger.info("No vlmparse containers found")
193
+ return
194
+
195
+ # Prepare table data - one row per project/standalone container
196
+ table_data = []
197
+ for project_name, project_containers in projects.items():
198
+ # Find the main container with vlmparse labels
199
+ main_container = project_containers[0]
200
+ for c in project_containers:
201
+ labels = _get_container_labels(c)
202
+ if labels.get("vlmparse_uri"):
203
+ main_container = c
204
+ break
205
+
206
+ labels = _get_container_labels(main_container)
207
+
208
+ # Extract port mappings from all containers in the project
209
+ ports = []
210
+ for container in project_containers:
112
211
  if container.ports:
113
212
  for _, host_bindings in container.ports.items():
114
213
  if host_bindings:
115
214
  for binding in host_bindings:
116
215
  ports.append(f"{binding['HostPort']}")
117
216
 
118
- port_str = ", ".join(set(ports)) if ports else "N/A"
119
- uri = container.labels.get("vlmparse_uri", "N/A")
120
- gpu = container.labels.get("vlmparse_gpus", "N/A")
121
-
122
- table_data.append(
123
- [
124
- container.name,
125
- container.status,
126
- port_str,
127
- gpu,
128
- uri,
129
- ]
130
- )
217
+ port_str = ", ".join(sorted(set(ports))) if ports else "N/A"
218
+ uri = labels.get("vlmparse_uri", "N/A")
219
+ gpu = labels.get("vlmparse_gpus", "N/A")
131
220
 
132
- # Display as table
133
- from tabulate import tabulate
221
+ # Get all statuses
222
+ statuses = list(set(c.status for c in project_containers))
223
+ status_str = (
224
+ statuses[0] if len(statuses) == 1 else f"mixed ({', '.join(statuses)})"
225
+ )
226
+
227
+ # Name: show project name if compose, otherwise container name
228
+ is_compose = labels.get("com.docker.compose.project") or labels.get(
229
+ "vlmparse_compose_project"
230
+ )
231
+ name = project_name if is_compose else main_container.name
134
232
 
135
- headers = ["Name", "Status", "Port(s)", "GPU", "URI"]
136
- table = tabulate(table_data, headers=headers, tablefmt="grid")
233
+ table_data.append([name, status_str, port_str, gpu, uri])
137
234
 
138
- logger.info(f"\nFound {len(vlmparse_containers)} vlmparse container(s):\n")
139
- print(table)
235
+ # Display as table
236
+ from tabulate import tabulate
140
237
 
141
- except docker.errors.DockerException as e:
142
- logger.error(f"Failed to connect to Docker: {e}")
143
- logger.error(
144
- "Make sure Docker is running and you have the necessary permissions"
145
- )
238
+ headers = ["Name", "Status", "Port(s)", "GPU", "URI"]
239
+ table = tabulate(table_data, headers=headers, tablefmt="grid")
146
240
 
147
- def stop(self, container: str | None = None):
148
- """Stop a Docker container by its ID or name.
241
+ total = sum(len(containers) for containers in projects.values())
242
+ logger.info(
243
+ f"\nFound {len(projects)} vlmparse deployment(s) ({total} container(s)):\n"
244
+ )
245
+ print(table)
149
246
 
150
- Args:
151
- container: Container ID or name to stop. If not specified, automatically stops the container if only one vlmparse container is running.
152
- """
153
- import docker
247
+ except docker.errors.DockerException as e:
248
+ logger.error(f"Failed to connect to Docker: {e}")
249
+ logger.error(
250
+ "Make sure Docker is running and you have the necessary permissions"
251
+ )
154
252
 
155
- try:
156
- client = docker.from_env()
157
-
158
- # If no container specified, try to auto-select
159
- if container is None:
160
- containers = client.containers.list()
161
- vlmparse_containers = [
162
- c for c in containers if c.name.startswith("vlmparse")
163
- ]
164
-
165
- if len(vlmparse_containers) == 0:
166
- logger.error("No vlmparse containers found")
167
- return
168
- elif len(vlmparse_containers) > 1:
169
- logger.error(
170
- f"Multiple vlmparse containers found ({len(vlmparse_containers)}). "
171
- "Please specify a container ID or name:"
172
- )
173
- for c in vlmparse_containers:
174
- logger.info(f" - {c.name} ({c.short_id})")
175
- return
176
- else:
177
- target_container = vlmparse_containers[0]
253
+
254
+ @app.command("stop")
255
+ def stop(
256
+ container: str | None = typer.Argument(None, help="Container ID or name to stop"),
257
+ ):
258
+ """Stop a Docker container by its ID or name.
259
+
260
+ If the selected container belongs to a Docker Compose project, the whole
261
+ compose stack is stopped and removed.
262
+
263
+ Args:
264
+ container: Container ID or name to stop. If not specified, automatically stops the container if only one vlmparse container is running.
265
+ """
266
+ import docker
267
+
268
+ from vlmparse.servers.utils import _stop_compose_stack_for_container
269
+
270
+ try:
271
+ client = docker.from_env()
272
+
273
+ # If no container specified, try to auto-select
274
+ if container is None:
275
+ from vlmparse.servers.utils import _get_container_labels
276
+
277
+ all_containers = client.containers.list()
278
+
279
+ # Group containers by compose project or as standalone
280
+ projects = {} # project_name -> list of containers
281
+ for c in all_containers:
282
+ labels = _get_container_labels(c)
283
+ project = labels.get("com.docker.compose.project") or labels.get(
284
+ "vlmparse_compose_project"
285
+ )
286
+
287
+ # Include if name starts with vlmparse OR if it's in a vlmparse compose project
288
+ if c.name.startswith("vlmparse"):
289
+ if project:
290
+ projects.setdefault(project, []).append(c)
291
+ else:
292
+ projects[c.name] = [c]
293
+ elif project and project.startswith("vlmparse"):
294
+ projects.setdefault(project, []).append(c)
295
+
296
+ if len(projects) == 0:
297
+ logger.error("No vlmparse containers found")
298
+ return
299
+ elif len(projects) > 1:
300
+ logger.error(
301
+ f"Multiple vlmparse deployments found ({len(projects)}). "
302
+ "Please specify a container ID or name:"
303
+ )
304
+ for project_name, containers in projects.items():
305
+ if len(containers) > 1:
306
+ logger.info(
307
+ f" - {project_name} ({len(containers)} containers)"
308
+ )
309
+ else:
310
+ logger.info(
311
+ f" - {containers[0].name} ({containers[0].short_id})"
312
+ )
313
+ return
178
314
  else:
179
- # Try to get the specified container
180
- try:
181
- target_container = client.containers.get(container)
182
- except docker.errors.NotFound:
183
- logger.error(f"Container not found: {container}")
184
- return
185
-
186
- # Stop the container
187
- logger.info(
188
- f"Stopping container: {target_container.name} ({target_container.short_id})"
189
- )
315
+ # Only one project/deployment, pick any container from it
316
+ target_container = list(projects.values())[0][0]
317
+ else:
318
+ # Try to get the specified container
319
+ try:
320
+ target_container = client.containers.get(container)
321
+ except docker.errors.NotFound:
322
+ logger.error(f"Container not found: {container}")
323
+ return
324
+
325
+ # If the container is part of a docker-compose stack, bring the whole stack down.
326
+ if _stop_compose_stack_for_container(target_container):
327
+ return
328
+
329
+ # Stop + remove the container
330
+ logger.info(
331
+ f"Stopping container: {target_container.name} ({target_container.short_id})"
332
+ )
333
+ try:
190
334
  target_container.stop()
191
- logger.info("✓ Container stopped successfully")
335
+ except Exception:
336
+ pass
337
+ try:
338
+ target_container.remove(force=True)
339
+ except Exception:
340
+ pass
341
+ logger.info("✓ Container stopped and removed successfully")
342
+
343
+ except docker.errors.DockerException as e:
344
+ logger.error(f"Failed to connect to Docker: {e}")
345
+ logger.error(
346
+ "Make sure Docker is running and you have the necessary permissions"
347
+ )
192
348
 
193
- except docker.errors.DockerException as e:
194
- logger.error(f"Failed to connect to Docker: {e}")
195
- logger.error(
196
- "Make sure Docker is running and you have the necessary permissions"
197
- )
198
349
 
199
- def log(self, container: str | None = None, follow: bool = True, tail: int = 500):
200
- """Show logs from a Docker container.
350
+ @app.command("log")
351
+ def log(
352
+ container: str | None = typer.Argument(
353
+ None, help="Container ID or name. If not specified, auto-selects."
354
+ ),
355
+ follow: bool = typer.Option(True, help="Follow log output"),
356
+ tail: int = typer.Option(500, help="Number of lines to show from the end"),
357
+ ):
358
+ """Show logs from a Docker container.
359
+
360
+ Args:
361
+ container: Container ID or name. If not specified, automatically selects the container if only one vlmparse container is running.
362
+ follow: If True, follow log output (stream logs in real-time)
363
+ tail: Number of lines to show from the end of the logs
364
+ """
365
+ import docker
366
+
367
+ try:
368
+ client = docker.from_env()
369
+
370
+ # If no container specified, try to auto-select
371
+ if container is None:
372
+ from vlmparse.servers.utils import _get_container_labels
373
+
374
+ all_containers = client.containers.list()
375
+ vlmparse_containers = []
376
+
377
+ for c in all_containers:
378
+ labels = _get_container_labels(c)
379
+ project = labels.get("com.docker.compose.project") or labels.get(
380
+ "vlmparse_compose_project"
381
+ )
201
382
 
202
- Args:
203
- container: Container ID or name. If not specified, automatically selects the container if only one vlmparse container is running.
204
- follow: If True, follow log output (stream logs in real-time)
205
- tail: Number of lines to show from the end of the logs
206
- """
207
- import docker
383
+ # Include if name starts with vlmparse OR if it's in a vlmparse compose project
384
+ if c.name.startswith("vlmparse") or (
385
+ project and project.startswith("vlmparse")
386
+ ):
387
+ vlmparse_containers.append(c)
208
388
 
209
- try:
210
- client = docker.from_env()
211
-
212
- # If no container specified, try to auto-select
213
- if container is None:
214
- containers = client.containers.list()
215
- vlmparse_containers = [
216
- c for c in containers if c.name.startswith("vlmparse")
217
- ]
218
-
219
- if len(vlmparse_containers) == 0:
220
- logger.error("No vlmparse containers found")
221
- return
222
- elif len(vlmparse_containers) > 1:
223
- logger.error(
224
- f"Multiple vlmparse containers found ({len(vlmparse_containers)}). "
225
- "Please specify a container ID or name:"
226
- )
227
- for c in vlmparse_containers:
228
- logger.info(f" - {c.name} ({c.short_id})")
229
- return
230
- else:
231
- target_container = vlmparse_containers[0]
232
- logger.info(
233
- f"Showing logs for: {target_container.name} ({target_container.short_id})"
234
- )
235
- else:
236
- # Try to get the specified container
237
- try:
238
- target_container = client.containers.get(container)
239
- except docker.errors.NotFound:
240
- logger.error(f"Container not found: {container}")
241
- return
242
-
243
- # Get and display logs
244
- if follow:
245
- logger.info("Following logs (press Ctrl+C to stop)...")
246
- try:
247
- for log_line in target_container.logs(
248
- stream=True, follow=True, tail=tail
249
- ):
250
- print(log_line.decode("utf-8", errors="replace"), end="")
251
- except KeyboardInterrupt:
252
- logger.info("\nStopped following logs")
389
+ if len(vlmparse_containers) == 0:
390
+ logger.error("No vlmparse containers found")
391
+ return
392
+ elif len(vlmparse_containers) > 1:
393
+ logger.error(
394
+ f"Multiple vlmparse containers found ({len(vlmparse_containers)}). "
395
+ "Please specify a container ID or name:"
396
+ )
397
+ for c in vlmparse_containers:
398
+ logger.info(f" - {c.name} ({c.short_id})")
399
+ return
253
400
  else:
254
- logs = target_container.logs().decode("utf-8", errors="replace")
255
- print(logs)
401
+ target_container = vlmparse_containers[0]
402
+ logger.info(
403
+ f"Showing logs for: {target_container.name} ({target_container.short_id})"
404
+ )
405
+ else:
406
+ # Try to get the specified container
407
+ try:
408
+ target_container = client.containers.get(container)
409
+ except docker.errors.NotFound:
410
+ logger.error(f"Container not found: {container}")
411
+ return
256
412
 
257
- except docker.errors.DockerException as e:
258
- logger.error(f"Failed to connect to Docker: {e}")
259
- logger.error(
260
- "Make sure Docker is running and you have the necessary permissions"
261
- )
413
+ # Get and display logs
414
+ if follow:
415
+ logger.info("Following logs (press Ctrl+C to stop)...")
416
+ try:
417
+ for log_line in target_container.logs(
418
+ stream=True, follow=True, tail=tail
419
+ ):
420
+ print(log_line.decode("utf-8", errors="replace"), end="")
421
+ except KeyboardInterrupt:
422
+ logger.info("\nStopped following logs")
423
+ else:
424
+ logs = target_container.logs().decode("utf-8", errors="replace")
425
+ print(logs)
262
426
 
263
- def list_register(self):
264
- """List all model keys registered in client and server registries."""
265
- from vlmparse.registries import (
266
- converter_config_registry,
267
- docker_config_registry,
427
+ except docker.errors.DockerException as e:
428
+ logger.error(f"Failed to connect to Docker: {e}")
429
+ logger.error(
430
+ "Make sure Docker is running and you have the necessary permissions"
268
431
  )
269
432
 
270
- client_models = sorted(converter_config_registry.list_models())
271
- server_models = sorted(docker_config_registry.list_models())
272
433
 
273
- print("\nClient Models Registry:")
274
- for model in client_models:
275
- print(f" - {model}")
434
+ @app.command("list-register")
435
+ def list_register():
436
+ """List all model keys registered in client and server registries."""
437
+ from vlmparse.registries import converter_config_registry, docker_config_registry
276
438
 
277
- print("\nServer Models Registry:")
278
- for model in server_models:
279
- print(f" - {model}")
439
+ client_models = sorted(converter_config_registry.list_models())
440
+ server_models = sorted(docker_config_registry.list_models())
280
441
 
281
- def view(self, folder):
282
- import subprocess
283
- import sys
442
+ print("\nClient Models Registry:")
443
+ for model in client_models:
444
+ print(f" - {model}")
284
445
 
285
- from streamlit import runtime
446
+ print("\nServer Models Registry:")
447
+ for model in server_models:
448
+ print(f" - {model}")
286
449
 
287
- from vlmparse.st_viewer.st_viewer import __file__ as st_viewer_file
288
- from vlmparse.st_viewer.st_viewer import run_streamlit
289
450
 
290
- if runtime.exists():
291
- run_streamlit(folder)
292
- else:
293
- try:
294
- subprocess.run(
295
- [
296
- sys.executable,
297
- "-m",
298
- "streamlit",
299
- "run",
300
- st_viewer_file,
301
- "--",
302
- folder,
303
- ],
304
- check=True,
305
- )
306
- except KeyboardInterrupt:
307
- print("\nStreamlit app terminated by user.")
308
- except subprocess.CalledProcessError as e:
309
- print(f"Error while running Streamlit: {e}")
451
+ @app.command("view")
452
+ def view(folder: str = typer.Argument(..., help="Folder to visualize with Streamlit")):
453
+ import subprocess
454
+ import sys
310
455
 
456
+ from streamlit import runtime
311
457
 
312
- def main():
313
- import fire
458
+ from vlmparse.st_viewer.st_viewer import __file__ as st_viewer_file
459
+ from vlmparse.st_viewer.st_viewer import run_streamlit
460
+
461
+ if runtime.exists():
462
+ run_streamlit(folder)
463
+ else:
464
+ try:
465
+ subprocess.run(
466
+ [
467
+ sys.executable,
468
+ "-m",
469
+ "streamlit",
470
+ "run",
471
+ st_viewer_file,
472
+ "--",
473
+ folder,
474
+ ],
475
+ check=True,
476
+ )
477
+ except KeyboardInterrupt:
478
+ print("\nStreamlit app terminated by user.")
479
+ except subprocess.CalledProcessError as e:
480
+ print(f"Error while running Streamlit: {e}")
314
481
 
315
- fire.Fire(DParseCLI)
482
+
483
+ def main():
484
+ app()
316
485
 
317
486
 
318
487
  if __name__ == "__main__":