promptimizer-cli 0.1.26 → 0.1.28

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.
@@ -190,10 +190,8 @@ function banner(session, version) {
190
190
  if (hostCount > 1) out(` ${color(ANSI.dim, `${hostCount} hosts merged · router picks across all`)}`);
191
191
  out(` ${color(ANSI.dim, `baseline ${baseline}`)}`);
192
192
  out();
193
- out(
194
- ` ${color(ANSI.dim, "Type a prompt, or /help /hosts /models /connect /disconnect /clear /quit")}`,
195
- );
196
- out(` ${color(ANSI.dim, "Cache keys on full history — /clear then repeat a prompt to see cache hit")}`);
193
+ out(` ${color(ANSI.dim, "Type a prompt, or /help /hosts /models /connect /disconnect /clear /quit")}`);
194
+ out(` ${color(ANSI.dim, "Repeating the same last message hits prompt cache; /clear resets the chat thread")}`);
197
195
  out();
198
196
  }
199
197
 
@@ -302,8 +300,9 @@ function printMeta(result) {
302
300
  if (meta.provider_id || meta.host) bits.push(String(meta.provider_id || meta.host));
303
301
  if (saved != null) bits.push(`saved ${usd(saved)}`);
304
302
  if (meta.exact_cache_hit) bits.push(color(ANSI.green, "cache hit"));
303
+ else if (meta.prompt_cache_hit) bits.push(color(ANSI.green, "prompt cache"));
305
304
  else if (meta.semantic_cache_hit) {
306
- const mode = meta.semantic_cache_mode === "full" ? "semantic full" : "semantic hybrid";
305
+ const mode = meta.semantic_cache_mode === "full" ? "semantic full" : meta.semantic_cache_mode === "prompt" ? "prompt cache" : "semantic hybrid";
307
306
  const sim =
308
307
  meta.semantic_similarity != null ? ` ${Math.round(Number(meta.semantic_similarity) * 100)}%` : "";
309
308
  bits.push(color(ANSI.green, `${mode}${sim}`));
@@ -330,6 +329,76 @@ async function complete(flags, config, messages) {
330
329
  });
331
330
  }
332
331
 
332
+ /** Stream a completion; writes tokens to stdout as they arrive. Returns { text, result }. */
333
+ async function completeStream(flags, config, messages) {
334
+ const gatewayURL = gateway(flags, config);
335
+ const { apiKey, sessionId } = authFromConfig(flags, config);
336
+ const headers = { "content-type": "application/json" };
337
+ if (apiKey) headers.authorization = `Bearer ${apiKey}`;
338
+ else if (sessionId) {
339
+ headers.authorization = `Bearer ${sessionId}`;
340
+ headers["x-promptimizer-session"] = sessionId;
341
+ }
342
+ const response = await fetch(`${gatewayURL}/v1/chat/completions`, {
343
+ method: "POST",
344
+ headers,
345
+ body: JSON.stringify({ messages, stream: true }),
346
+ });
347
+ if (!response.ok) {
348
+ const data = await response.json().catch(() => ({}));
349
+ const detail =
350
+ typeof data === "object" && data && "detail" in data ? String(data.detail) : response.statusText;
351
+ throw Object.assign(new Error(detail), { status: response.status });
352
+ }
353
+ if (!response.body) {
354
+ const data = await response.json();
355
+ return { text: data.choices?.[0]?.message?.content?.trim() ?? "", result: data };
356
+ }
357
+
358
+ const reader = response.body.getReader();
359
+ const decoder = new TextDecoder();
360
+ let buffer = "";
361
+ let text = "";
362
+ let result = {
363
+ model: undefined,
364
+ usage: undefined,
365
+ promptimizer: undefined,
366
+ choices: [{ message: { role: "assistant", content: "" } }],
367
+ };
368
+
369
+ while (true) {
370
+ const { done, value } = await reader.read();
371
+ if (done) break;
372
+ buffer += decoder.decode(value, { stream: true });
373
+ const parts = buffer.split("\n");
374
+ buffer = parts.pop() ?? "";
375
+ for (const line of parts) {
376
+ const trimmed = line.trim();
377
+ if (!trimmed.startsWith("data:")) continue;
378
+ const data = trimmed.slice(5).trim();
379
+ if (!data) continue;
380
+ if (data === "[DONE]") continue;
381
+ try {
382
+ const parsed = JSON.parse(data);
383
+ if (parsed.error?.message) throw new Error(parsed.error.message);
384
+ const delta = parsed.choices?.[0]?.delta?.content;
385
+ if (delta) {
386
+ text += delta;
387
+ process.stdout.write(delta);
388
+ }
389
+ if (parsed.promptimizer) result.promptimizer = parsed.promptimizer;
390
+ if (parsed.usage) result.usage = parsed.usage;
391
+ if (parsed.model) result.model = parsed.model;
392
+ } catch (err) {
393
+ if (err instanceof Error && err.message && !err.message.includes("JSON")) throw err;
394
+ }
395
+ }
396
+ }
397
+
398
+ result.choices = [{ message: { role: "assistant", content: text } }];
399
+ return { text, result };
400
+ }
401
+
333
402
  async function cmdLogin(flags) {
334
403
  const apiKey = flags.key || flags.k || process.env.PROMPTIMIZER_API_KEY;
335
404
  if (!apiKey) die("Missing --key. Create one at /account.");
@@ -466,10 +535,11 @@ async function cmdChat(flags, positional) {
466
535
  const config = readConfig();
467
536
  const prompt = String(flags.prompt || positional.join(" ")).trim();
468
537
  if (!prompt) die('Usage: promptimizer chat "What is 17 * 24?"');
469
- const result = await complete(flags, config, [{ role: "user", content: prompt }]);
470
- const text = result.choices?.[0]?.message?.content?.trim() ?? "";
471
538
  out();
472
- out(text);
539
+ out(color(ANSI.magenta, "✦"));
540
+ const { text, result } = await completeStream(flags, config, [{ role: "user", content: prompt }]);
541
+ if (!text) process.stdout.write(color(ANSI.dim, "(empty)"));
542
+ process.stdout.write("\n");
473
543
  out();
474
544
  printMeta(result);
475
545
  out();
@@ -674,20 +744,18 @@ async function interactive(flags) {
674
744
  }
675
745
 
676
746
  history.push({ role: "user", content: trimmed });
677
- process.stdout.write(color(ANSI.dim, " … routing\r"));
747
+ out();
748
+ out(color(ANSI.magenta, "✦"));
678
749
  try {
679
- const result = await complete(flags, readConfig(), history);
680
- process.stdout.write(" \r");
681
- const text = result.choices?.[0]?.message?.content?.trim() ?? "";
750
+ const { text, result } = await completeStream(flags, readConfig(), history);
751
+ if (!text) process.stdout.write(color(ANSI.dim, "(empty)"));
752
+ process.stdout.write("\n");
682
753
  history.push({ role: "assistant", content: text });
683
754
  out();
684
- out(color(ANSI.magenta, "✦"));
685
- out(text);
686
- out();
687
755
  printMeta(result);
688
756
  out();
689
757
  } catch (error) {
690
- process.stdout.write(" \r");
758
+ process.stdout.write("\n");
691
759
  history.pop();
692
760
  out(color(ANSI.yellow, ` ${error instanceof Error ? error.message : String(error)}`));
693
761
  out();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "promptimizer-cli",
3
- "version": "0.1.26",
3
+ "version": "0.1.28",
4
4
  "description": "Interactive Promptimizer CLI — Gemini-style REPL for quality-aware routing.",
5
5
  "type": "module",
6
6
  "bin": {