venice-cli 0.14.0__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.
@@ -0,0 +1,59 @@
1
+ """`venice contact-sheet` -- montage a set of images into a review grid.
2
+
3
+ Pure local post-processing (no Venice API call): tiles a batch of generated
4
+ images -- variant rolls, a whole set -- into one sheet via ImageMagick `montage`
5
+ or `ffmpeg` (auto-detected). Optional per-cell filename labels. Makes a large
6
+ batch reviewable at a glance.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ from pathlib import Path
11
+
12
+ from .. import image_montage
13
+
14
+
15
+ def register(subparsers) -> None:
16
+ p = subparsers.add_parser(
17
+ "contact-sheet",
18
+ help="Montage images into a review grid (needs ImageMagick montage or ffmpeg).",
19
+ description=(
20
+ "Tiles a directory/glob of images into a single contact sheet -- no "
21
+ "Venice API call. Uses ImageMagick `montage` when present, else "
22
+ "ffmpeg's tile filter (auto-detected). With --label, each cell is "
23
+ "captioned with its filename."
24
+ ),
25
+ )
26
+ p.add_argument("inputs", nargs="+", metavar="DIR_OR_GLOB",
27
+ help="A directory, glob, or list of image files.")
28
+ p.add_argument("--output", "-o", type=Path, default=None,
29
+ help="Output image path (default: ./contact-sheet.png).")
30
+ p.add_argument("--cols", type=int, default=4, metavar="N",
31
+ help="Number of columns in the grid (default 4).")
32
+ p.add_argument("--cell", default="256x320", metavar="WxH",
33
+ help="Cell (thumbnail) size, WxH (default 256x320).")
34
+ p.add_argument("--label", action="store_true",
35
+ help="Caption each cell with its filename.")
36
+ p.add_argument("--background", default="white", metavar="COLOR",
37
+ help="Background/pad color (default white).")
38
+ p.add_argument("--padding", type=int, default=4, metavar="PX",
39
+ help="Gap between cells in pixels (default 4).")
40
+ p.add_argument("--engine", choices=("auto", "montage", "ffmpeg"), default="auto",
41
+ help="Which tool to use (default auto: montage, else ffmpeg).")
42
+ p.add_argument("--dry-run", action="store_true",
43
+ help="Print the montage/ffmpeg command without running it.")
44
+ p.set_defaults(handler=_run)
45
+
46
+
47
+ def _run(args) -> int:
48
+ out = args.output or image_montage.default_output()
49
+ return image_montage.contact_sheet(
50
+ args.inputs,
51
+ out,
52
+ cols=args.cols,
53
+ cell=args.cell,
54
+ label=args.label,
55
+ background=args.background,
56
+ padding=args.padding,
57
+ engine=args.engine,
58
+ dry_run=args.dry_run,
59
+ )
@@ -0,0 +1,157 @@
1
+ """`venice embed` -- one-shot /embeddings.
2
+
3
+ Built on the official `openai` SDK (Venice is OpenAI-compatible; the SDK is
4
+ lazy-imported so the rest of the stdlib-only CLI works without it). Mirrors the
5
+ shape of `venice chat`: the free `/models?type=embedding` catalog GET (via the
6
+ lean urllib client) validates `--model` and resolves a default before the paid
7
+ embeddings call.
8
+
9
+ Output: by default one embedding is printed per line as a JSON array
10
+ (newline-delimited JSON -- pipes cleanly to `jq`). `--json` dumps the full raw
11
+ response object (model, data, usage) instead.
12
+ """
13
+ from __future__ import annotations
14
+
15
+ import json
16
+ import sys
17
+
18
+ from .. import auth
19
+ from ..client import build_client_from_auth
20
+ from . import _models, _openai
21
+
22
+
23
+ def register(subparsers) -> None:
24
+ p = subparsers.add_parser(
25
+ "embed",
26
+ help="Create embeddings (/embeddings).",
27
+ description=(
28
+ "Turn text into embedding vectors with a Venice embedding model. "
29
+ "Reads the text from the argument, from stdin when it is '-' or "
30
+ "piped, or one input per non-empty line with --from-file (batch). "
31
+ "By default prints one vector per line as a JSON array."
32
+ ),
33
+ )
34
+ p.add_argument(
35
+ "text",
36
+ nargs="?",
37
+ help="Input text. Use '-' (or pipe stdin) to read from stdin.",
38
+ )
39
+ p.add_argument(
40
+ "--from-file",
41
+ dest="from_file",
42
+ default=None,
43
+ metavar="PATH",
44
+ help="Batch: embed one input per non-empty line of PATH.",
45
+ )
46
+ p.add_argument(
47
+ "--model",
48
+ "-m",
49
+ default=None,
50
+ help="Embedding model id (default: the catalog's 'default'-trait model).",
51
+ )
52
+ p.add_argument(
53
+ "--dimensions",
54
+ type=int,
55
+ default=None,
56
+ help="Truncate output vectors to this many dimensions (if supported).",
57
+ )
58
+ p.add_argument(
59
+ "--encoding-format",
60
+ choices=("float", "base64"),
61
+ default=None,
62
+ dest="encoding_format",
63
+ help="Vector encoding to request (default: float).",
64
+ )
65
+ p.add_argument(
66
+ "--json",
67
+ action="store_true",
68
+ help="Print the raw response object (model, data, usage).",
69
+ )
70
+ p.set_defaults(handler=_run)
71
+
72
+
73
+ def _resolve_inputs(args) -> tuple:
74
+ """Return (inputs, exit_code). inputs is a str (single) or list[str] (batch).
75
+
76
+ exit_code is None on success. Rejects giving both a positional/stdin text
77
+ and --from-file.
78
+ """
79
+ if args.from_file is not None:
80
+ if args.text:
81
+ print(
82
+ "embed: provide either text or --from-file, not both",
83
+ file=sys.stderr,
84
+ )
85
+ return None, 2
86
+ try:
87
+ with open(args.from_file, "r", encoding="utf-8") as fh:
88
+ lines = [ln.strip() for ln in fh]
89
+ except OSError as e:
90
+ print(f"embed: cannot read {args.from_file}: {e}", file=sys.stderr)
91
+ return None, 2
92
+ inputs = [ln for ln in lines if ln]
93
+ if not inputs:
94
+ print(f"embed: no non-empty lines in {args.from_file}", file=sys.stderr)
95
+ return None, 2
96
+ return inputs, None
97
+
98
+ text = args.text
99
+ if text == "-" or (text is None and not sys.stdin.isatty()):
100
+ text = sys.stdin.read().strip() or None
101
+ if not text:
102
+ print(
103
+ "embed: no input (pass text, pipe stdin, or use --from-file)",
104
+ file=sys.stderr,
105
+ )
106
+ return None, 2
107
+ return text, None
108
+
109
+
110
+ def _build_kwargs(args, model: str, inputs) -> dict:
111
+ kwargs: dict = {"model": model, "input": inputs}
112
+ if args.dimensions is not None:
113
+ kwargs["dimensions"] = args.dimensions
114
+ if args.encoding_format is not None:
115
+ kwargs["encoding_format"] = args.encoding_format
116
+ return kwargs
117
+
118
+
119
+ def _run(args) -> int:
120
+ inputs, rc = _resolve_inputs(args)
121
+ if rc is not None:
122
+ return rc
123
+
124
+ openai = _openai.import_openai("embed")
125
+ if openai is None:
126
+ return 2
127
+
128
+ try:
129
+ client = build_client_from_auth()
130
+ except auth.AuthError as e:
131
+ print(str(e), file=sys.stderr)
132
+ return 2
133
+
134
+ models = _models.catalog(client, "embedding")
135
+ model, rc = _models.resolve_model(
136
+ args.model, models, label="embed", noun="embedding model"
137
+ )
138
+ if rc is not None:
139
+ return rc
140
+
141
+ oai = _openai.build_openai(openai, client)
142
+ kwargs = _build_kwargs(args, model, inputs)
143
+
144
+ try:
145
+ resp = oai.embeddings.create(**kwargs)
146
+ except openai.OpenAIError as e:
147
+ return _openai.status_to_exit(openai, e, "embed")
148
+
149
+ if args.json:
150
+ json.dump(resp.model_dump(), sys.stdout, indent=2, default=str)
151
+ sys.stdout.write("\n")
152
+ return 0
153
+
154
+ for item in sorted(resp.data, key=lambda d: d.index):
155
+ json.dump(item.embedding, sys.stdout)
156
+ sys.stdout.write("\n")
157
+ return 0