promptimizer-cli 0.1.50 → 0.1.51

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.
@@ -153,6 +153,74 @@ function printAnswer(text) {
153
153
  process.stdout.write(`${rendered}\n`);
154
154
  }
155
155
 
156
+ function sleep(ms) {
157
+ return new Promise((resolve) => setTimeout(resolve, ms));
158
+ }
159
+
160
+ /** Split so ANSI escapes stay intact while typewriting. */
161
+ function ansiChunks(text) {
162
+ const parts = [];
163
+ const re = /\x1b\[[0-9;]*m|[^\x1b]+/g;
164
+ let match;
165
+ while ((match = re.exec(String(text)))) parts.push(match[0]);
166
+ return parts;
167
+ }
168
+
169
+ /**
170
+ * Stream clean markdown to the terminal. Cache hits use a very high CPS so
171
+ * the replay feels snappy instead of dumping raw markdown source.
172
+ */
173
+ async function streamAnswer(text, opts = {}) {
174
+ if (!text) {
175
+ out(color(ANSI.dim, "(empty)"));
176
+ return;
177
+ }
178
+ if (!ANSI_OK) {
179
+ process.stdout.write(`${text}\n`);
180
+ return;
181
+ }
182
+ const rendered = renderMarkdown(text);
183
+ const cacheHit = Boolean(opts.cacheHit);
184
+ // ~chars per second for the visible body (escapes are free)
185
+ const cps = cacheHit ? 14_000 : opts.fast ? 9_000 : 5_500;
186
+ const chunkSize = Math.max(8, Math.floor(cps / 80));
187
+ let pending = "";
188
+ let visible = 0;
189
+
190
+ for (const part of ansiChunks(rendered)) {
191
+ if (part.startsWith("\x1b")) {
192
+ process.stdout.write(part);
193
+ continue;
194
+ }
195
+ for (let i = 0; i < part.length; ) {
196
+ const slice = part.slice(i, i + chunkSize);
197
+ pending += slice;
198
+ visible += slice.length;
199
+ i += slice.length;
200
+ if (pending.length >= chunkSize || slice.includes("\n")) {
201
+ process.stdout.write(pending);
202
+ pending = "";
203
+ await sleep(Math.max(4, Math.round((1000 * chunkSize) / cps)));
204
+ }
205
+ }
206
+ }
207
+ if (pending) process.stdout.write(pending);
208
+ process.stdout.write("\n");
209
+ // silence unused when tiny answers finish in one tick
210
+ void visible;
211
+ }
212
+
213
+ function isCacheHit(meta) {
214
+ if (!meta || typeof meta !== "object") return false;
215
+ return Boolean(
216
+ meta.exact_cache_hit ||
217
+ meta.prompt_cache_hit ||
218
+ meta.semantic_cache_hit ||
219
+ meta.prefix_cache_hit ||
220
+ meta.cache_hit,
221
+ );
222
+ }
223
+
156
224
  const COMMANDS = [
157
225
  ["", "Start interactive session (Gemini-style REPL)"],
158
226
  ["login", "Save a Promptimizer API key"],
@@ -462,7 +530,7 @@ async function complete(flags, config, messages) {
462
530
  });
463
531
  }
464
532
 
465
- /** Stream a completion; buffers tokens then returns { text, result }. */
533
+ /** Stream a completion; buffers tokens (no raw dump), returns { text, result }. */
466
534
  async function completeStream(flags, config, messages) {
467
535
  const gatewayURL = gateway(flags, config);
468
536
  const { apiKey, sessionId } = authFromConfig(flags, config);
@@ -500,6 +568,7 @@ async function completeStream(flags, config, messages) {
500
568
  };
501
569
  let spinTimer = null;
502
570
  let spinFrame = 0;
571
+ const started = Date.now();
503
572
  const frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
504
573
  if (ANSI_OK) {
505
574
  spinTimer = setInterval(() => {
@@ -542,7 +611,7 @@ async function completeStream(flags, config, messages) {
542
611
  }
543
612
 
544
613
  result.choices = [{ message: { role: "assistant", content: text } }];
545
- return { text, result };
614
+ return { text, result, elapsedMs: Date.now() - started };
546
615
  }
547
616
 
548
617
  async function cmdLogin(flags) {
@@ -684,8 +753,14 @@ async function cmdChat(flags, positional) {
684
753
  if (!prompt) die('Usage: promptimizer chat "What is 17 * 24?"');
685
754
  out();
686
755
  out(color(ANSI.magenta, "✦"));
687
- const { text, result } = await completeStream(flags, config, [{ role: "user", content: prompt }]);
688
- printAnswer(text);
756
+ const { text, result, elapsedMs } = await completeStream(flags, config, [
757
+ { role: "user", content: prompt },
758
+ ]);
759
+ const meta = result.promptimizer ?? {};
760
+ await streamAnswer(text, {
761
+ cacheHit: isCacheHit(meta),
762
+ fast: (elapsedMs ?? 0) < 500,
763
+ });
689
764
  out();
690
765
  printMeta(result);
691
766
  out();
@@ -893,8 +968,12 @@ async function interactive(flags) {
893
968
  out();
894
969
  out(color(ANSI.magenta, "✦"));
895
970
  try {
896
- const { text, result } = await completeStream(flags, readConfig(), history);
897
- printAnswer(text);
971
+ const { text, result, elapsedMs } = await completeStream(flags, readConfig(), history);
972
+ const meta = result.promptimizer ?? {};
973
+ await streamAnswer(text, {
974
+ cacheHit: isCacheHit(meta),
975
+ fast: (elapsedMs ?? 0) < 500,
976
+ });
898
977
  history.push({ role: "assistant", content: text });
899
978
  out();
900
979
  printMeta(result);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "promptimizer-cli",
3
- "version": "0.1.50",
3
+ "version": "0.1.51",
4
4
  "description": "Interactive Promptimizer CLI — Gemini-style REPL for quality-aware routing.",
5
5
  "type": "module",
6
6
  "bin": {