licos-dev-cli 0.2.0__tar.gz → 0.2.1__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.
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: licos-dev-cli
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: LICOS Dev CLI - generate files and call model capabilities
5
5
  Requires-Python: >=3.10
6
6
  Requires-Dist: click>=8.1
7
- Requires-Dist: licos-dev-sdk>=0.2.0
7
+ Requires-Dist: licos-dev-sdk>=0.2.1
@@ -1,19 +1,19 @@
1
- [build-system]
2
- requires = ["hatchling"]
3
- build-backend = "hatchling.build"
4
-
5
- [project]
6
- name = "licos-dev-cli"
7
- version = "0.2.0"
8
- description = "LICOS Dev CLI - generate files and call model capabilities"
9
- requires-python = ">=3.10"
10
- dependencies = [
11
- "licos-dev-sdk>=0.2.0",
12
- "click>=8.1",
13
- ]
14
-
15
- [project.scripts]
16
- licos-dev = "licos_dev_cli.main:cli"
17
-
18
- [tool.hatch.build.targets.wheel]
19
- packages = ["src/licos_dev_cli"]
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "licos-dev-cli"
7
+ version = "0.2.1"
8
+ description = "LICOS Dev CLI - generate files and call model capabilities"
9
+ requires-python = ">=3.10"
10
+ dependencies = [
11
+ "licos-dev-sdk>=0.2.1",
12
+ "click>=8.1",
13
+ ]
14
+
15
+ [project.scripts]
16
+ licos-dev = "licos_dev_cli.main:cli"
17
+
18
+ [tool.hatch.build.targets.wheel]
19
+ packages = ["src/licos_dev_cli"]
@@ -1,522 +1,522 @@
1
- """CLI entry point - wraps licos-dev-sdk functions as click commands."""
2
-
3
- from __future__ import annotations
4
-
5
- import sys
6
- import json
7
- import click
8
-
9
- # ── Helpers ──────────────────────────────────────────────────────────────────
10
-
11
- def _read_input(input_path: str | None, content: str | None) -> str:
12
- """Read content from --input file, --content arg, or stdin."""
13
- if input_path:
14
- with open(input_path, "r", encoding="utf-8") as f:
15
- return f.read()
16
- if content:
17
- return content
18
- if not sys.stdin.isatty():
19
- return sys.stdin.read()
20
- raise click.UsageError("Provide --input, --content, or pipe via stdin.")
21
-
22
-
23
- def _read_json(input_path: str | None, content: str | None) -> object:
24
- """Read and parse JSON from input."""
25
- raw = _read_input(input_path, content)
26
- return json.loads(raw)
27
-
28
-
29
- def _json_option(value: str | None, name: str) -> dict | list | None:
30
- if not value:
31
- return None
32
- try:
33
- return json.loads(value)
34
- except json.JSONDecodeError as exc:
35
- raise click.BadParameter(f"{name} must be valid JSON") from exc
36
-
37
-
38
- def _echo_json(value: object) -> None:
39
- if hasattr(value, "to_dict"):
40
- value = value.to_dict()
41
- click.echo(json.dumps(value, ensure_ascii=False, indent=2))
42
-
43
-
44
- # ── CLI Group ────────────────────────────────────────────────────────────────
45
-
46
- @click.group()
47
- def cli():
48
- """LICOS Dev - file generation and model CLI for AI agents."""
49
- pass
50
-
51
-
52
- # ── PDF ──────────────────────────────────────────────────────────────────────
53
-
54
- @cli.command()
55
- @click.option("-i", "--input", "input_path", help="Input file (Markdown or HTML)")
56
- @click.option("-c", "--content", help="Direct content")
57
- @click.option("-f", "--filename", required=True, help="Output filename (no extension)")
58
- @click.option("-o", "--output-dir", default=None, help="Output directory")
59
- @click.option("--format", "content_type", default="markdown", help="Content type: markdown|html")
60
- @click.option("--page-size", default="A4", help="Page size: A4|LETTER")
61
- def pdf(input_path, content, filename, output_dir, content_type, page_size):
62
- """Generate PDF from Markdown or HTML."""
63
- from licos_dev_sdk import create_pdf
64
- text = _read_input(input_path, content)
65
- path = create_pdf(text, filename, content_type=content_type, output_dir=output_dir, page_size=page_size)
66
- click.echo(path)
67
-
68
-
69
- # ── DOCX ─────────────────────────────────────────────────────────────────────
70
-
71
- @cli.command()
72
- @click.option("-i", "--input", "input_path")
73
- @click.option("-c", "--content")
74
- @click.option("-f", "--filename", required=True)
75
- @click.option("-o", "--output-dir", default=None)
76
- @click.option("--format", "content_type", default="markdown")
77
- @click.option("--font", default="Arial")
78
- @click.option("--font-size", default=11, type=int)
79
- def docx(input_path, content, filename, output_dir, content_type, font, font_size):
80
- """Generate DOCX from Markdown or HTML."""
81
- from licos_dev_sdk import create_docx
82
- text = _read_input(input_path, content)
83
- path = create_docx(text, filename, content_type=content_type, output_dir=output_dir, font_name=font, font_size=font_size)
84
- click.echo(path)
85
-
86
-
87
- # ── XLSX ─────────────────────────────────────────────────────────────────────
88
-
89
- @cli.command()
90
- @click.option("-i", "--input", "input_path")
91
- @click.option("-c", "--content", help="JSON data")
92
- @click.option("-f", "--filename", required=True)
93
- @click.option("-o", "--output-dir", default=None)
94
- @click.option("--sheet-name", default="Sheet1")
95
- @click.option("--header-color", default="4472C4")
96
- def xlsx(input_path, content, filename, output_dir, sheet_name, header_color):
97
- """Generate XLSX from JSON data."""
98
- from licos_dev_sdk import create_xlsx
99
- data = _read_json(input_path, content)
100
- path = create_xlsx(data, filename, output_dir=output_dir, sheet_name=sheet_name, header_color=header_color)
101
- click.echo(path)
102
-
103
-
104
- # ── CSV ──────────────────────────────────────────────────────────────────────
105
-
106
- @cli.command()
107
- @click.option("-i", "--input", "input_path")
108
- @click.option("-c", "--content", help="JSON data")
109
- @click.option("-f", "--filename", required=True)
110
- @click.option("-o", "--output-dir", default=None)
111
- def csv(input_path, content, filename, output_dir):
112
- """Generate CSV from JSON data."""
113
- from licos_dev_sdk import create_csv
114
- data = _read_json(input_path, content)
115
- path = create_csv(data, filename, output_dir=output_dir)
116
- click.echo(path)
117
-
118
-
119
- # ── PPTX ─────────────────────────────────────────────────────────────────────
120
-
121
- @cli.command()
122
- @click.option("-i", "--input", "input_path")
123
- @click.option("-c", "--content")
124
- @click.option("-f", "--filename", required=True)
125
- @click.option("-o", "--output-dir", default=None)
126
- @click.option("--format", "content_type", default="markdown")
127
- def pptx(input_path, content, filename, output_dir, content_type):
128
- """Generate PPTX from Markdown."""
129
- from licos_dev_sdk import create_pptx
130
- text = _read_input(input_path, content)
131
- path = create_pptx(text, filename, content_type=content_type, output_dir=output_dir)
132
- click.echo(path)
133
-
134
-
135
- # ── Chart ────────────────────────────────────────────────────────────────────
136
-
137
- @cli.command()
138
- @click.option("-i", "--input", "input_path")
139
- @click.option("-c", "--content", help="JSON chart data")
140
- @click.option("-f", "--filename", required=True)
141
- @click.option("-o", "--output-dir", default=None)
142
- @click.option("--type", "chart_type", required=True, help="bar|line|pie|scatter|heatmap")
143
- @click.option("--format", "fmt", default="png", help="png|svg")
144
- @click.option("--title", default="")
145
- @click.option("--width", default=800, type=int)
146
- @click.option("--height", default=600, type=int)
147
- def chart(input_path, content, filename, output_dir, chart_type, fmt, title, width, height):
148
- """Generate chart image from JSON data."""
149
- from licos_dev_sdk import create_chart
150
- data = _read_json(input_path, content)
151
- path = create_chart(chart_type, data, filename, output_dir=output_dir, format=fmt, title=title, width=width, height=height)
152
- click.echo(path)
153
-
154
-
155
- # ── Diagram ──────────────────────────────────────────────────────────────────
156
-
157
- @cli.command()
158
- @click.option("-i", "--input", "input_path")
159
- @click.option("-c", "--content", help="DOT source")
160
- @click.option("-f", "--filename", required=True)
161
- @click.option("-o", "--output-dir", default=None)
162
- @click.option("--format", "fmt", default="png", help="png|svg|pdf")
163
- def diagram(input_path, content, filename, output_dir, fmt):
164
- """Generate diagram from Graphviz DOT source."""
165
- from licos_dev_sdk import create_diagram
166
- text = _read_input(input_path, content)
167
- path = create_diagram(text, filename, output_dir=output_dir, format=fmt)
168
- click.echo(path)
169
-
170
-
171
- # ── QR Code ──────────────────────────────────────────────────────────────────
172
-
173
- @cli.command()
174
- @click.option("-d", "--data", required=True, help="Data to encode")
175
- @click.option("-f", "--filename", required=True)
176
- @click.option("-o", "--output-dir", default=None)
177
- @click.option("--size", default=300, type=int)
178
- def qrcode(data, filename, output_dir, size):
179
- """Generate QR code PNG image."""
180
- from licos_dev_sdk import create_qrcode
181
- path = create_qrcode(data, filename, output_dir=output_dir, size=size)
182
- click.echo(path)
183
-
184
-
185
- # ── Barcode ──────────────────────────────────────────────────────────────────
186
-
187
- @cli.command()
188
- @click.option("-d", "--data", required=True, help="Data to encode")
189
- @click.option("-f", "--filename", required=True)
190
- @click.option("-o", "--output-dir", default=None)
191
- @click.option("--type", "barcode_type", default="code128", help="code128|ean13|ean8|isbn13|upc")
192
- def barcode(data, filename, output_dir, barcode_type):
193
- """Generate barcode PNG image."""
194
- from licos_dev_sdk import create_barcode
195
- path = create_barcode(data, filename, output_dir=output_dir, barcode_type=barcode_type)
196
- click.echo(path)
197
-
198
-
199
- # ── ZIP ──────────────────────────────────────────────────────────────────────
200
-
201
- @cli.command("zip")
202
- @click.option("-p", "--paths", required=True, help="Comma-separated file/dir paths")
203
- @click.option("-f", "--filename", required=True)
204
- @click.option("-o", "--output-dir", default=None)
205
- def zip_cmd(paths, filename, output_dir):
206
- """Create ZIP archive."""
207
- from licos_dev_sdk import create_zip
208
- source_paths = [p.strip() for p in paths.split(",")]
209
- path = create_zip(source_paths, filename, output_dir=output_dir)
210
- click.echo(path)
211
-
212
-
213
- # ── TAR.GZ ───────────────────────────────────────────────────────────────────
214
-
215
- @cli.command("tar")
216
- @click.option("-p", "--paths", required=True, help="Comma-separated file/dir paths")
217
- @click.option("-f", "--filename", required=True)
218
- @click.option("-o", "--output-dir", default=None)
219
- def tar_cmd(paths, filename, output_dir):
220
- """Create TAR.GZ archive."""
221
- from licos_dev_sdk import create_tar_gz
222
- source_paths = [p.strip() for p in paths.split(",")]
223
- path = create_tar_gz(source_paths, filename, output_dir=output_dir)
224
- click.echo(path)
225
-
226
-
227
- # ── JSON ─────────────────────────────────────────────────────────────────────
228
-
229
- @cli.command("json")
230
- @click.option("-i", "--input", "input_path")
231
- @click.option("-c", "--content", help="JSON or YAML content")
232
- @click.option("-f", "--filename", required=True)
233
- @click.option("-o", "--output-dir", default=None)
234
- def json_cmd(input_path, content, filename, output_dir):
235
- """Generate JSON file (accepts JSON or YAML input)."""
236
- from licos_dev_sdk import create_json
237
- raw = _read_input(input_path, content)
238
- try:
239
- data = json.loads(raw)
240
- except json.JSONDecodeError:
241
- import yaml
242
- data = yaml.safe_load(raw)
243
- path = create_json(data, filename, output_dir=output_dir)
244
- click.echo(path)
245
-
246
-
247
- # ── YAML ─────────────────────────────────────────────────────────────────────
248
-
249
- @cli.command("yaml")
250
- @click.option("-i", "--input", "input_path")
251
- @click.option("-c", "--content", help="JSON or YAML content")
252
- @click.option("-f", "--filename", required=True)
253
- @click.option("-o", "--output-dir", default=None)
254
- def yaml_cmd(input_path, content, filename, output_dir):
255
- """Generate YAML file (accepts JSON or YAML input)."""
256
- from licos_dev_sdk import create_yaml
257
- import yaml as yaml_lib
258
- raw = _read_input(input_path, content)
259
- try:
260
- data = json.loads(raw)
261
- except json.JSONDecodeError:
262
- data = yaml_lib.safe_load(raw)
263
- path = create_yaml(data, filename, output_dir=output_dir)
264
- click.echo(path)
265
-
266
-
267
- # ── XML ──────────────────────────────────────────────────────────────────────
268
-
269
- @cli.command("xml")
270
- @click.option("-i", "--input", "input_path")
271
- @click.option("-c", "--content", help="JSON content")
272
- @click.option("-f", "--filename", required=True)
273
- @click.option("-o", "--output-dir", default=None)
274
- @click.option("--root-tag", default="root")
275
- def xml_cmd(input_path, content, filename, output_dir, root_tag):
276
- """Generate XML file from JSON data."""
277
- from licos_dev_sdk import create_xml
278
- data = _read_json(input_path, content)
279
- path = create_xml(data, filename, output_dir=output_dir, root_tag=root_tag)
280
- click.echo(path)
281
-
282
-
283
- # ── HTML ─────────────────────────────────────────────────────────────────────
284
-
285
- @cli.command("html")
286
- @click.option("-i", "--input", "input_path")
287
- @click.option("-c", "--content")
288
- @click.option("-f", "--filename", required=True)
289
- @click.option("-o", "--output-dir", default=None)
290
- @click.option("--format", "content_type", default="markdown", help="markdown|html")
291
- def html_cmd(input_path, content, filename, output_dir, content_type):
292
- """Generate HTML file from Markdown or raw HTML."""
293
- from licos_dev_sdk import create_html
294
- text = _read_input(input_path, content)
295
- path = create_html(text, filename, output_dir=output_dir, content_type=content_type)
296
- click.echo(path)
297
-
298
-
299
- # ── Model Catalog ────────────────────────────────────────────────────────────
300
-
301
- @cli.command("model-catalog")
302
- @click.option("--refresh", is_flag=True, help="Bypass local catalog cache")
303
- def model_catalog(refresh):
304
- """Print platform model capability catalog."""
305
- from licos_dev_sdk import fetch_model_catalogs
306
-
307
- _echo_json(fetch_model_catalogs(refresh=refresh))
308
-
309
-
310
- # ── LLM ──────────────────────────────────────────────────────────────────────
311
-
312
- @cli.group("llm")
313
- def llm_group():
314
- """Call chat/text model capabilities."""
315
- pass
316
-
317
-
318
- @llm_group.command("invoke")
319
- @click.option("-p", "--prompt", help="User prompt. If omitted, --messages or stdin is used.")
320
- @click.option("--messages", help="OpenAI-style messages JSON array")
321
- @click.option("--model", default=None)
322
- @click.option("--temperature", default=None, type=float)
323
- @click.option("--max-completion-tokens", default=None, type=int)
324
- @click.option("--extra", default=None, help="Extra request fields as JSON object")
325
- def llm_invoke(prompt, messages, model, temperature, max_completion_tokens, extra):
326
- """Run a non-streaming chat completion."""
327
- from licos_dev_sdk import LLMClient
328
-
329
- if messages:
330
- message_payload = _json_option(messages, "--messages")
331
- else:
332
- message_payload = prompt if prompt is not None else _read_input(None, None)
333
- extra_payload = _json_option(extra, "--extra") or {}
334
- if not isinstance(extra_payload, dict):
335
- raise click.BadParameter("--extra must be a JSON object")
336
- result = LLMClient().invoke(
337
- message_payload,
338
- model=model,
339
- temperature=temperature,
340
- max_completion_tokens=max_completion_tokens,
341
- **extra_payload,
342
- )
343
- _echo_json(result)
344
-
345
-
346
- @llm_group.command("stream")
347
- @click.option("-p", "--prompt", help="User prompt. If omitted, --messages or stdin is used.")
348
- @click.option("--messages", help="OpenAI-style messages JSON array")
349
- @click.option("--model", default=None)
350
- @click.option("--temperature", default=None, type=float)
351
- @click.option("--max-completion-tokens", default=None, type=int)
352
- @click.option("--extra", default=None, help="Extra request fields as JSON object")
353
- def llm_stream(prompt, messages, model, temperature, max_completion_tokens, extra):
354
- """Run a streaming chat completion and print text chunks."""
355
- from licos_dev_sdk import LLMClient
356
-
357
- if messages:
358
- message_payload = _json_option(messages, "--messages")
359
- else:
360
- message_payload = prompt if prompt is not None else _read_input(None, None)
361
- extra_payload = _json_option(extra, "--extra") or {}
362
- if not isinstance(extra_payload, dict):
363
- raise click.BadParameter("--extra must be a JSON object")
364
- for chunk in LLMClient().stream(
365
- message_payload,
366
- model=model,
367
- temperature=temperature,
368
- max_completion_tokens=max_completion_tokens,
369
- **extra_payload,
370
- ):
371
- click.echo(chunk, nl=False)
372
-
373
-
374
- # ── Vision ───────────────────────────────────────────────────────────────────
375
-
376
- @cli.group("vision")
377
- def vision_group():
378
- """Call chat vision model capabilities."""
379
- pass
380
-
381
-
382
- @vision_group.command("understand")
383
- @click.option("--image-url", multiple=True, help="Image URL. Can be repeated.")
384
- @click.option("-p", "--prompt", default="Describe this image.")
385
- @click.option("--model", default=None)
386
- @click.option("--raw-request", default=None, help="Raw JSON request body")
387
- def vision_understand(image_url, prompt, model, raw_request):
388
- """Understand one or more images with the catalog vision model."""
389
- from licos_dev_sdk import VisionClient
390
-
391
- raw_payload = _json_option(raw_request, "--raw-request")
392
- if raw_payload is not None and not isinstance(raw_payload, dict):
393
- raise click.BadParameter("--raw-request must be a JSON object")
394
- if raw_payload is None and not image_url:
395
- raise click.UsageError("Provide --image-url or --raw-request.")
396
- result = VisionClient().understand(
397
- image_urls=list(image_url),
398
- prompt=prompt,
399
- model=model,
400
- raw_request=raw_payload,
401
- )
402
- _echo_json(result)
403
-
404
-
405
- # ── Image Generation ─────────────────────────────────────────────────────────
406
-
407
- @cli.group("image-generation")
408
- def image_generation_group():
409
- """Call image generation model capabilities."""
410
- pass
411
-
412
-
413
- @image_generation_group.command("generate")
414
- @click.option("-p", "--prompt")
415
- @click.option("--negative-prompt", default=None)
416
- @click.option("--count", default=1, type=int)
417
- @click.option("--size", default=None)
418
- @click.option("--model", default=None)
419
- @click.option("--parameters", default=None, help="Parameters JSON object")
420
- @click.option("--raw-request", default=None, help="Raw JSON request body")
421
- @click.option("--no-wait", is_flag=True, help="Return submit response without polling async tasks")
422
- def image_generate(prompt, negative_prompt, count, size, model, parameters, raw_request, no_wait):
423
- """Generate images from a text prompt."""
424
- from licos_dev_sdk import ImageGenerationClient
425
-
426
- parameter_payload = _json_option(parameters, "--parameters") or {}
427
- raw_payload = _json_option(raw_request, "--raw-request")
428
- if not isinstance(parameter_payload, dict):
429
- raise click.BadParameter("--parameters must be a JSON object")
430
- if raw_payload is not None and not isinstance(raw_payload, dict):
431
- raise click.BadParameter("--raw-request must be a JSON object")
432
- if raw_payload is None and not prompt:
433
- raise click.UsageError("Provide --prompt or --raw-request.")
434
- result = ImageGenerationClient().generate(
435
- prompt or "",
436
- negative_prompt=negative_prompt,
437
- count=count,
438
- size=size,
439
- model=model,
440
- parameters=parameter_payload,
441
- raw_request=raw_payload,
442
- wait=not no_wait,
443
- )
444
- _echo_json(result)
445
-
446
-
447
- # ── Video Generation ─────────────────────────────────────────────────────────
448
-
449
- @cli.group("video-generation")
450
- def video_generation_group():
451
- """Call video generation model capabilities."""
452
- pass
453
-
454
-
455
- @video_generation_group.command("generate")
456
- @click.option("-p", "--prompt")
457
- @click.option("--image-url", default=None)
458
- @click.option("--model", default=None)
459
- @click.option("--parameters", default=None, help="Parameters JSON object")
460
- @click.option("--raw-request", default=None, help="Raw JSON request body")
461
- @click.option("--no-wait", is_flag=True, help="Return submit response without polling async tasks")
462
- def video_generate(prompt, image_url, model, parameters, raw_request, no_wait):
463
- """Generate a video from a text prompt and optional first-frame image."""
464
- from licos_dev_sdk import VideoGenerationClient
465
-
466
- parameter_payload = _json_option(parameters, "--parameters") or {}
467
- raw_payload = _json_option(raw_request, "--raw-request")
468
- if not isinstance(parameter_payload, dict):
469
- raise click.BadParameter("--parameters must be a JSON object")
470
- if raw_payload is not None and not isinstance(raw_payload, dict):
471
- raise click.BadParameter("--raw-request must be a JSON object")
472
- if raw_payload is None and not prompt:
473
- raise click.UsageError("Provide --prompt or --raw-request.")
474
- result = VideoGenerationClient().generate(
475
- prompt or "",
476
- image_url=image_url,
477
- model=model,
478
- parameters=parameter_payload,
479
- raw_request=raw_payload,
480
- wait=not no_wait,
481
- )
482
- _echo_json(result)
483
-
484
-
485
- # ── Audio / ASR ──────────────────────────────────────────────────────────────
486
-
487
- @cli.group("audio")
488
- def audio_group():
489
- """Call audio model capabilities."""
490
- pass
491
-
492
-
493
- @audio_group.command("recognize")
494
- @click.option("--audio-url", multiple=True, help="Audio URL. Can be repeated.")
495
- @click.option("--model", default=None)
496
- @click.option("--parameters", default=None, help="Parameters JSON object")
497
- @click.option("--raw-request", default=None, help="Raw JSON request body")
498
- @click.option("--no-wait", is_flag=True, help="Return submit response without polling async tasks")
499
- def audio_recognize(audio_url, model, parameters, raw_request, no_wait):
500
- """Recognize speech from one or more audio URLs."""
501
- from licos_dev_sdk import SpeechRecognitionClient
502
-
503
- parameter_payload = _json_option(parameters, "--parameters") or {}
504
- raw_payload = _json_option(raw_request, "--raw-request")
505
- if not isinstance(parameter_payload, dict):
506
- raise click.BadParameter("--parameters must be a JSON object")
507
- if raw_payload is not None and not isinstance(raw_payload, dict):
508
- raise click.BadParameter("--raw-request must be a JSON object")
509
- if raw_payload is None and not audio_url:
510
- raise click.UsageError("Provide --audio-url or --raw-request.")
511
- result = SpeechRecognitionClient().recognize(
512
- audio_urls=list(audio_url),
513
- model=model,
514
- parameters=parameter_payload,
515
- raw_request=raw_payload,
516
- wait=not no_wait,
517
- )
518
- _echo_json(result)
519
-
520
-
521
- if __name__ == "__main__":
522
- cli()
1
+ """CLI entry point - wraps licos-dev-sdk functions as click commands."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ import json
7
+ import click
8
+
9
+ # ── Helpers ──────────────────────────────────────────────────────────────────
10
+
11
+ def _read_input(input_path: str | None, content: str | None) -> str:
12
+ """Read content from --input file, --content arg, or stdin."""
13
+ if input_path:
14
+ with open(input_path, "r", encoding="utf-8") as f:
15
+ return f.read()
16
+ if content:
17
+ return content
18
+ if not sys.stdin.isatty():
19
+ return sys.stdin.read()
20
+ raise click.UsageError("Provide --input, --content, or pipe via stdin.")
21
+
22
+
23
+ def _read_json(input_path: str | None, content: str | None) -> object:
24
+ """Read and parse JSON from input."""
25
+ raw = _read_input(input_path, content)
26
+ return json.loads(raw)
27
+
28
+
29
+ def _json_option(value: str | None, name: str) -> dict | list | None:
30
+ if not value:
31
+ return None
32
+ try:
33
+ return json.loads(value)
34
+ except json.JSONDecodeError as exc:
35
+ raise click.BadParameter(f"{name} must be valid JSON") from exc
36
+
37
+
38
+ def _echo_json(value: object) -> None:
39
+ if hasattr(value, "to_dict"):
40
+ value = value.to_dict()
41
+ click.echo(json.dumps(value, ensure_ascii=False, indent=2))
42
+
43
+
44
+ # ── CLI Group ────────────────────────────────────────────────────────────────
45
+
46
+ @click.group()
47
+ def cli():
48
+ """LICOS Dev - file generation and model CLI for AI agents."""
49
+ pass
50
+
51
+
52
+ # ── PDF ──────────────────────────────────────────────────────────────────────
53
+
54
+ @cli.command()
55
+ @click.option("-i", "--input", "input_path", help="Input file (Markdown or HTML)")
56
+ @click.option("-c", "--content", help="Direct content")
57
+ @click.option("-f", "--filename", required=True, help="Output filename (no extension)")
58
+ @click.option("-o", "--output-dir", default=None, help="Output directory")
59
+ @click.option("--format", "content_type", default="markdown", help="Content type: markdown|html")
60
+ @click.option("--page-size", default="A4", help="Page size: A4|LETTER")
61
+ def pdf(input_path, content, filename, output_dir, content_type, page_size):
62
+ """Generate PDF from Markdown or HTML."""
63
+ from licos_dev_sdk import create_pdf
64
+ text = _read_input(input_path, content)
65
+ path = create_pdf(text, filename, content_type=content_type, output_dir=output_dir, page_size=page_size)
66
+ click.echo(path)
67
+
68
+
69
+ # ── DOCX ─────────────────────────────────────────────────────────────────────
70
+
71
+ @cli.command()
72
+ @click.option("-i", "--input", "input_path")
73
+ @click.option("-c", "--content")
74
+ @click.option("-f", "--filename", required=True)
75
+ @click.option("-o", "--output-dir", default=None)
76
+ @click.option("--format", "content_type", default="markdown")
77
+ @click.option("--font", default="Arial")
78
+ @click.option("--font-size", default=11, type=int)
79
+ def docx(input_path, content, filename, output_dir, content_type, font, font_size):
80
+ """Generate DOCX from Markdown or HTML."""
81
+ from licos_dev_sdk import create_docx
82
+ text = _read_input(input_path, content)
83
+ path = create_docx(text, filename, content_type=content_type, output_dir=output_dir, font_name=font, font_size=font_size)
84
+ click.echo(path)
85
+
86
+
87
+ # ── XLSX ─────────────────────────────────────────────────────────────────────
88
+
89
+ @cli.command()
90
+ @click.option("-i", "--input", "input_path")
91
+ @click.option("-c", "--content", help="JSON data")
92
+ @click.option("-f", "--filename", required=True)
93
+ @click.option("-o", "--output-dir", default=None)
94
+ @click.option("--sheet-name", default="Sheet1")
95
+ @click.option("--header-color", default="4472C4")
96
+ def xlsx(input_path, content, filename, output_dir, sheet_name, header_color):
97
+ """Generate XLSX from JSON data."""
98
+ from licos_dev_sdk import create_xlsx
99
+ data = _read_json(input_path, content)
100
+ path = create_xlsx(data, filename, output_dir=output_dir, sheet_name=sheet_name, header_color=header_color)
101
+ click.echo(path)
102
+
103
+
104
+ # ── CSV ──────────────────────────────────────────────────────────────────────
105
+
106
+ @cli.command()
107
+ @click.option("-i", "--input", "input_path")
108
+ @click.option("-c", "--content", help="JSON data")
109
+ @click.option("-f", "--filename", required=True)
110
+ @click.option("-o", "--output-dir", default=None)
111
+ def csv(input_path, content, filename, output_dir):
112
+ """Generate CSV from JSON data."""
113
+ from licos_dev_sdk import create_csv
114
+ data = _read_json(input_path, content)
115
+ path = create_csv(data, filename, output_dir=output_dir)
116
+ click.echo(path)
117
+
118
+
119
+ # ── PPTX ─────────────────────────────────────────────────────────────────────
120
+
121
+ @cli.command()
122
+ @click.option("-i", "--input", "input_path")
123
+ @click.option("-c", "--content")
124
+ @click.option("-f", "--filename", required=True)
125
+ @click.option("-o", "--output-dir", default=None)
126
+ @click.option("--format", "content_type", default="markdown")
127
+ def pptx(input_path, content, filename, output_dir, content_type):
128
+ """Generate PPTX from Markdown."""
129
+ from licos_dev_sdk import create_pptx
130
+ text = _read_input(input_path, content)
131
+ path = create_pptx(text, filename, content_type=content_type, output_dir=output_dir)
132
+ click.echo(path)
133
+
134
+
135
+ # ── Chart ────────────────────────────────────────────────────────────────────
136
+
137
+ @cli.command()
138
+ @click.option("-i", "--input", "input_path")
139
+ @click.option("-c", "--content", help="JSON chart data")
140
+ @click.option("-f", "--filename", required=True)
141
+ @click.option("-o", "--output-dir", default=None)
142
+ @click.option("--type", "chart_type", required=True, help="bar|line|pie|scatter|heatmap")
143
+ @click.option("--format", "fmt", default="png", help="png|svg")
144
+ @click.option("--title", default="")
145
+ @click.option("--width", default=800, type=int)
146
+ @click.option("--height", default=600, type=int)
147
+ def chart(input_path, content, filename, output_dir, chart_type, fmt, title, width, height):
148
+ """Generate chart image from JSON data."""
149
+ from licos_dev_sdk import create_chart
150
+ data = _read_json(input_path, content)
151
+ path = create_chart(chart_type, data, filename, output_dir=output_dir, format=fmt, title=title, width=width, height=height)
152
+ click.echo(path)
153
+
154
+
155
+ # ── Diagram ──────────────────────────────────────────────────────────────────
156
+
157
+ @cli.command()
158
+ @click.option("-i", "--input", "input_path")
159
+ @click.option("-c", "--content", help="DOT source")
160
+ @click.option("-f", "--filename", required=True)
161
+ @click.option("-o", "--output-dir", default=None)
162
+ @click.option("--format", "fmt", default="png", help="png|svg|pdf")
163
+ def diagram(input_path, content, filename, output_dir, fmt):
164
+ """Generate diagram from Graphviz DOT source."""
165
+ from licos_dev_sdk import create_diagram
166
+ text = _read_input(input_path, content)
167
+ path = create_diagram(text, filename, output_dir=output_dir, format=fmt)
168
+ click.echo(path)
169
+
170
+
171
+ # ── QR Code ──────────────────────────────────────────────────────────────────
172
+
173
+ @cli.command()
174
+ @click.option("-d", "--data", required=True, help="Data to encode")
175
+ @click.option("-f", "--filename", required=True)
176
+ @click.option("-o", "--output-dir", default=None)
177
+ @click.option("--size", default=300, type=int)
178
+ def qrcode(data, filename, output_dir, size):
179
+ """Generate QR code PNG image."""
180
+ from licos_dev_sdk import create_qrcode
181
+ path = create_qrcode(data, filename, output_dir=output_dir, size=size)
182
+ click.echo(path)
183
+
184
+
185
+ # ── Barcode ──────────────────────────────────────────────────────────────────
186
+
187
+ @cli.command()
188
+ @click.option("-d", "--data", required=True, help="Data to encode")
189
+ @click.option("-f", "--filename", required=True)
190
+ @click.option("-o", "--output-dir", default=None)
191
+ @click.option("--type", "barcode_type", default="code128", help="code128|ean13|ean8|isbn13|upc")
192
+ def barcode(data, filename, output_dir, barcode_type):
193
+ """Generate barcode PNG image."""
194
+ from licos_dev_sdk import create_barcode
195
+ path = create_barcode(data, filename, output_dir=output_dir, barcode_type=barcode_type)
196
+ click.echo(path)
197
+
198
+
199
+ # ── ZIP ──────────────────────────────────────────────────────────────────────
200
+
201
+ @cli.command("zip")
202
+ @click.option("-p", "--paths", required=True, help="Comma-separated file/dir paths")
203
+ @click.option("-f", "--filename", required=True)
204
+ @click.option("-o", "--output-dir", default=None)
205
+ def zip_cmd(paths, filename, output_dir):
206
+ """Create ZIP archive."""
207
+ from licos_dev_sdk import create_zip
208
+ source_paths = [p.strip() for p in paths.split(",")]
209
+ path = create_zip(source_paths, filename, output_dir=output_dir)
210
+ click.echo(path)
211
+
212
+
213
+ # ── TAR.GZ ───────────────────────────────────────────────────────────────────
214
+
215
+ @cli.command("tar")
216
+ @click.option("-p", "--paths", required=True, help="Comma-separated file/dir paths")
217
+ @click.option("-f", "--filename", required=True)
218
+ @click.option("-o", "--output-dir", default=None)
219
+ def tar_cmd(paths, filename, output_dir):
220
+ """Create TAR.GZ archive."""
221
+ from licos_dev_sdk import create_tar_gz
222
+ source_paths = [p.strip() for p in paths.split(",")]
223
+ path = create_tar_gz(source_paths, filename, output_dir=output_dir)
224
+ click.echo(path)
225
+
226
+
227
+ # ── JSON ─────────────────────────────────────────────────────────────────────
228
+
229
+ @cli.command("json")
230
+ @click.option("-i", "--input", "input_path")
231
+ @click.option("-c", "--content", help="JSON or YAML content")
232
+ @click.option("-f", "--filename", required=True)
233
+ @click.option("-o", "--output-dir", default=None)
234
+ def json_cmd(input_path, content, filename, output_dir):
235
+ """Generate JSON file (accepts JSON or YAML input)."""
236
+ from licos_dev_sdk import create_json
237
+ raw = _read_input(input_path, content)
238
+ try:
239
+ data = json.loads(raw)
240
+ except json.JSONDecodeError:
241
+ import yaml
242
+ data = yaml.safe_load(raw)
243
+ path = create_json(data, filename, output_dir=output_dir)
244
+ click.echo(path)
245
+
246
+
247
+ # ── YAML ─────────────────────────────────────────────────────────────────────
248
+
249
+ @cli.command("yaml")
250
+ @click.option("-i", "--input", "input_path")
251
+ @click.option("-c", "--content", help="JSON or YAML content")
252
+ @click.option("-f", "--filename", required=True)
253
+ @click.option("-o", "--output-dir", default=None)
254
+ def yaml_cmd(input_path, content, filename, output_dir):
255
+ """Generate YAML file (accepts JSON or YAML input)."""
256
+ from licos_dev_sdk import create_yaml
257
+ import yaml as yaml_lib
258
+ raw = _read_input(input_path, content)
259
+ try:
260
+ data = json.loads(raw)
261
+ except json.JSONDecodeError:
262
+ data = yaml_lib.safe_load(raw)
263
+ path = create_yaml(data, filename, output_dir=output_dir)
264
+ click.echo(path)
265
+
266
+
267
+ # ── XML ──────────────────────────────────────────────────────────────────────
268
+
269
+ @cli.command("xml")
270
+ @click.option("-i", "--input", "input_path")
271
+ @click.option("-c", "--content", help="JSON content")
272
+ @click.option("-f", "--filename", required=True)
273
+ @click.option("-o", "--output-dir", default=None)
274
+ @click.option("--root-tag", default="root")
275
+ def xml_cmd(input_path, content, filename, output_dir, root_tag):
276
+ """Generate XML file from JSON data."""
277
+ from licos_dev_sdk import create_xml
278
+ data = _read_json(input_path, content)
279
+ path = create_xml(data, filename, output_dir=output_dir, root_tag=root_tag)
280
+ click.echo(path)
281
+
282
+
283
+ # ── HTML ─────────────────────────────────────────────────────────────────────
284
+
285
+ @cli.command("html")
286
+ @click.option("-i", "--input", "input_path")
287
+ @click.option("-c", "--content")
288
+ @click.option("-f", "--filename", required=True)
289
+ @click.option("-o", "--output-dir", default=None)
290
+ @click.option("--format", "content_type", default="markdown", help="markdown|html")
291
+ def html_cmd(input_path, content, filename, output_dir, content_type):
292
+ """Generate HTML file from Markdown or raw HTML."""
293
+ from licos_dev_sdk import create_html
294
+ text = _read_input(input_path, content)
295
+ path = create_html(text, filename, output_dir=output_dir, content_type=content_type)
296
+ click.echo(path)
297
+
298
+
299
+ # ── Model Catalog ────────────────────────────────────────────────────────────
300
+
301
+ @cli.command("model-catalog")
302
+ @click.option("--refresh", is_flag=True, help="Bypass local catalog cache")
303
+ def model_catalog(refresh):
304
+ """Print platform model capability catalog."""
305
+ from licos_dev_sdk import fetch_model_catalogs
306
+
307
+ _echo_json(fetch_model_catalogs(refresh=refresh))
308
+
309
+
310
+ # ── LLM ──────────────────────────────────────────────────────────────────────
311
+
312
+ @cli.group("llm")
313
+ def llm_group():
314
+ """Call chat/text model capabilities."""
315
+ pass
316
+
317
+
318
+ @llm_group.command("invoke")
319
+ @click.option("-p", "--prompt", help="User prompt. If omitted, --messages or stdin is used.")
320
+ @click.option("--messages", help="OpenAI-style messages JSON array")
321
+ @click.option("--model", default=None)
322
+ @click.option("--temperature", default=None, type=float)
323
+ @click.option("--max-completion-tokens", default=None, type=int)
324
+ @click.option("--extra", default=None, help="Extra request fields as JSON object")
325
+ def llm_invoke(prompt, messages, model, temperature, max_completion_tokens, extra):
326
+ """Run a non-streaming chat completion."""
327
+ from licos_dev_sdk import LLMClient
328
+
329
+ if messages:
330
+ message_payload = _json_option(messages, "--messages")
331
+ else:
332
+ message_payload = prompt if prompt is not None else _read_input(None, None)
333
+ extra_payload = _json_option(extra, "--extra") or {}
334
+ if not isinstance(extra_payload, dict):
335
+ raise click.BadParameter("--extra must be a JSON object")
336
+ result = LLMClient().invoke(
337
+ message_payload,
338
+ model=model,
339
+ temperature=temperature,
340
+ max_completion_tokens=max_completion_tokens,
341
+ **extra_payload,
342
+ )
343
+ _echo_json(result)
344
+
345
+
346
+ @llm_group.command("stream")
347
+ @click.option("-p", "--prompt", help="User prompt. If omitted, --messages or stdin is used.")
348
+ @click.option("--messages", help="OpenAI-style messages JSON array")
349
+ @click.option("--model", default=None)
350
+ @click.option("--temperature", default=None, type=float)
351
+ @click.option("--max-completion-tokens", default=None, type=int)
352
+ @click.option("--extra", default=None, help="Extra request fields as JSON object")
353
+ def llm_stream(prompt, messages, model, temperature, max_completion_tokens, extra):
354
+ """Run a streaming chat completion and print text chunks."""
355
+ from licos_dev_sdk import LLMClient
356
+
357
+ if messages:
358
+ message_payload = _json_option(messages, "--messages")
359
+ else:
360
+ message_payload = prompt if prompt is not None else _read_input(None, None)
361
+ extra_payload = _json_option(extra, "--extra") or {}
362
+ if not isinstance(extra_payload, dict):
363
+ raise click.BadParameter("--extra must be a JSON object")
364
+ for chunk in LLMClient().stream(
365
+ message_payload,
366
+ model=model,
367
+ temperature=temperature,
368
+ max_completion_tokens=max_completion_tokens,
369
+ **extra_payload,
370
+ ):
371
+ click.echo(chunk, nl=False)
372
+
373
+
374
+ # ── Vision ───────────────────────────────────────────────────────────────────
375
+
376
+ @cli.group("vision")
377
+ def vision_group():
378
+ """Call chat vision model capabilities."""
379
+ pass
380
+
381
+
382
+ @vision_group.command("understand")
383
+ @click.option("--image-url", multiple=True, help="Image URL. Can be repeated.")
384
+ @click.option("-p", "--prompt", default="Describe this image.")
385
+ @click.option("--model", default=None)
386
+ @click.option("--raw-request", default=None, help="Raw JSON request body")
387
+ def vision_understand(image_url, prompt, model, raw_request):
388
+ """Understand one or more images with the catalog vision model."""
389
+ from licos_dev_sdk import VisionClient
390
+
391
+ raw_payload = _json_option(raw_request, "--raw-request")
392
+ if raw_payload is not None and not isinstance(raw_payload, dict):
393
+ raise click.BadParameter("--raw-request must be a JSON object")
394
+ if raw_payload is None and not image_url:
395
+ raise click.UsageError("Provide --image-url or --raw-request.")
396
+ result = VisionClient().understand(
397
+ image_urls=list(image_url),
398
+ prompt=prompt,
399
+ model=model,
400
+ raw_request=raw_payload,
401
+ )
402
+ _echo_json(result)
403
+
404
+
405
+ # ── Image Generation ─────────────────────────────────────────────────────────
406
+
407
+ @cli.group("image-generation")
408
+ def image_generation_group():
409
+ """Call image generation model capabilities."""
410
+ pass
411
+
412
+
413
+ @image_generation_group.command("generate")
414
+ @click.option("-p", "--prompt")
415
+ @click.option("--negative-prompt", default=None)
416
+ @click.option("--count", default=1, type=int)
417
+ @click.option("--size", default=None)
418
+ @click.option("--model", default=None)
419
+ @click.option("--parameters", default=None, help="Parameters JSON object")
420
+ @click.option("--raw-request", default=None, help="Raw JSON request body")
421
+ @click.option("--no-wait", is_flag=True, help="Return submit response without polling async tasks")
422
+ def image_generate(prompt, negative_prompt, count, size, model, parameters, raw_request, no_wait):
423
+ """Generate images from a text prompt."""
424
+ from licos_dev_sdk import ImageGenerationClient
425
+
426
+ parameter_payload = _json_option(parameters, "--parameters") or {}
427
+ raw_payload = _json_option(raw_request, "--raw-request")
428
+ if not isinstance(parameter_payload, dict):
429
+ raise click.BadParameter("--parameters must be a JSON object")
430
+ if raw_payload is not None and not isinstance(raw_payload, dict):
431
+ raise click.BadParameter("--raw-request must be a JSON object")
432
+ if raw_payload is None and not prompt:
433
+ raise click.UsageError("Provide --prompt or --raw-request.")
434
+ result = ImageGenerationClient().generate(
435
+ prompt or "",
436
+ negative_prompt=negative_prompt,
437
+ count=count,
438
+ size=size,
439
+ model=model,
440
+ parameters=parameter_payload,
441
+ raw_request=raw_payload,
442
+ wait=not no_wait,
443
+ )
444
+ _echo_json(result)
445
+
446
+
447
+ # ── Video Generation ─────────────────────────────────────────────────────────
448
+
449
+ @cli.group("video-generation")
450
+ def video_generation_group():
451
+ """Call video generation model capabilities."""
452
+ pass
453
+
454
+
455
+ @video_generation_group.command("generate")
456
+ @click.option("-p", "--prompt")
457
+ @click.option("--image-url", default=None)
458
+ @click.option("--model", default=None)
459
+ @click.option("--parameters", default=None, help="Parameters JSON object")
460
+ @click.option("--raw-request", default=None, help="Raw JSON request body")
461
+ @click.option("--no-wait", is_flag=True, help="Return submit response without polling async tasks")
462
+ def video_generate(prompt, image_url, model, parameters, raw_request, no_wait):
463
+ """Generate a video from a text prompt and optional first-frame image."""
464
+ from licos_dev_sdk import VideoGenerationClient
465
+
466
+ parameter_payload = _json_option(parameters, "--parameters") or {}
467
+ raw_payload = _json_option(raw_request, "--raw-request")
468
+ if not isinstance(parameter_payload, dict):
469
+ raise click.BadParameter("--parameters must be a JSON object")
470
+ if raw_payload is not None and not isinstance(raw_payload, dict):
471
+ raise click.BadParameter("--raw-request must be a JSON object")
472
+ if raw_payload is None and not prompt:
473
+ raise click.UsageError("Provide --prompt or --raw-request.")
474
+ result = VideoGenerationClient().generate(
475
+ prompt or "",
476
+ image_url=image_url,
477
+ model=model,
478
+ parameters=parameter_payload,
479
+ raw_request=raw_payload,
480
+ wait=not no_wait,
481
+ )
482
+ _echo_json(result)
483
+
484
+
485
+ # ── Audio / ASR ──────────────────────────────────────────────────────────────
486
+
487
+ @cli.group("audio")
488
+ def audio_group():
489
+ """Call audio model capabilities."""
490
+ pass
491
+
492
+
493
+ @audio_group.command("recognize")
494
+ @click.option("--audio-url", multiple=True, help="Audio URL. Can be repeated.")
495
+ @click.option("--model", default=None)
496
+ @click.option("--parameters", default=None, help="Parameters JSON object")
497
+ @click.option("--raw-request", default=None, help="Raw JSON request body")
498
+ @click.option("--no-wait", is_flag=True, help="Return submit response without polling async tasks")
499
+ def audio_recognize(audio_url, model, parameters, raw_request, no_wait):
500
+ """Recognize speech from one or more audio URLs."""
501
+ from licos_dev_sdk import SpeechRecognitionClient
502
+
503
+ parameter_payload = _json_option(parameters, "--parameters") or {}
504
+ raw_payload = _json_option(raw_request, "--raw-request")
505
+ if not isinstance(parameter_payload, dict):
506
+ raise click.BadParameter("--parameters must be a JSON object")
507
+ if raw_payload is not None and not isinstance(raw_payload, dict):
508
+ raise click.BadParameter("--raw-request must be a JSON object")
509
+ if raw_payload is None and not audio_url:
510
+ raise click.UsageError("Provide --audio-url or --raw-request.")
511
+ result = SpeechRecognitionClient().recognize(
512
+ audio_urls=list(audio_url),
513
+ model=model,
514
+ parameters=parameter_payload,
515
+ raw_request=raw_payload,
516
+ wait=not no_wait,
517
+ )
518
+ _echo_json(result)
519
+
520
+
521
+ if __name__ == "__main__":
522
+ cli()
File without changes