@oneciel-ai/claude-any 0.1.39 → 0.1.42

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.
package/README.md CHANGED
@@ -48,7 +48,7 @@ arguments through unchanged.
48
48
 
49
49
  Credits: One Ciel LLC
50
50
 
51
- Current version: `0.1.39`
51
+ Current version: `0.1.42`
52
52
 
53
53
  ## Why This Exists
54
54
 
@@ -381,6 +381,21 @@ steps under that larger model's supervision.
381
381
 
382
382
  ## Changelog
383
383
 
384
+ ### 0.1.42
385
+
386
+ - **Live stream progress**: the statusline now updates streamed upstream output
387
+ progress with formatted input/output token estimates and chunk counts.
388
+
389
+ ### 0.1.41
390
+
391
+ - **Statusline formatting**: upstream token counts now use thousands separators
392
+ and a space before `tok`, for example `27,501 tok`.
393
+
394
+ ### 0.1.40
395
+
396
+ - **RPM 0 is preserved**: setting `rate_limit_rpm=0` now stores an explicit
397
+ unlimited mode instead of falling back to the provider default.
398
+
384
399
  ### 0.1.39
385
400
 
386
401
  - **Menu input fixes**: restores terminal line/echo mode before text or number
package/claude_any.py CHANGED
@@ -85,7 +85,7 @@ PROVIDER_LABELS = {
85
85
  "self-hosted-nim": "Self Hosted NIM",
86
86
  }
87
87
  APP_NAME = "Claude Any"
88
- VERSION = "0.1.39"
88
+ VERSION = "0.1.42"
89
89
  CREDITS = "Credits: One Ciel LLC"
90
90
 
91
91
  LOG_LEVELS = {"SILENT": 0, "ERROR": 1, "WARN": 2, "INFO": 3, "DEBUG": 4, "TRACE": 5}
@@ -1339,7 +1339,22 @@ def main():
1339
1339
  tokens = activity.get("tokens")
1340
1340
  rpm_text += f" | upstream {age:.0f}s"
1341
1341
  if tokens:
1342
- rpm_text += f" {tokens}tok"
1342
+ try:
1343
+ rpm_text += f" {int(tokens):,} tok"
1344
+ except Exception:
1345
+ rpm_text += f" {tokens} tok"
1346
+ output_tokens = activity.get("output_tokens")
1347
+ if output_tokens:
1348
+ try:
1349
+ rpm_text += f" -> {int(output_tokens):,} tok"
1350
+ except Exception:
1351
+ rpm_text += f" -> {output_tokens} tok"
1352
+ chunks = activity.get("chunks")
1353
+ if chunks:
1354
+ try:
1355
+ rpm_text += f" ({int(chunks):,} chunks)"
1356
+ except Exception:
1357
+ rpm_text += f" ({chunks} chunks)"
1343
1358
  elif event in ("success", "error"):
1344
1359
  rpm_text += f" | {event} {age:.0f}s"
1345
1360
  print(f"{left} | {color(rpm_text)}")
@@ -2207,8 +2222,10 @@ def router_rate_limit_recent(timestamps: Any, now: float, window: float, *, incl
2207
2222
 
2208
2223
  def router_rate_limit_usage(provider: str, pcfg: dict[str, Any], model: str | None = None) -> tuple[int, int | None]:
2209
2224
  rpm = router_rate_limit_effective_rpm(provider, pcfg, model)
2210
- if rpm is None:
2211
- return 0, None
2225
+ if rpm is None:
2226
+ return 0, None
2227
+ if rpm == 0:
2228
+ return 0, 0
2212
2229
  key = router_rate_limit_key(provider, pcfg, model)
2213
2230
  now = time.time()
2214
2231
  try:
@@ -4697,6 +4714,8 @@ def stream_openai_chat_to_anthropic_sse(
4697
4714
  source_body: dict[str, Any] | None = None,
4698
4715
  start_index: int = 0,
4699
4716
  word_chunking: bool = False,
4717
+ input_tokens: int | None = None,
4718
+ input_bytes: int | None = None,
4700
4719
  ) -> None:
4701
4720
  next_content_index = start_index
4702
4721
  text_started = False
@@ -4709,6 +4728,8 @@ def stream_openai_chat_to_anthropic_sse(
4709
4728
  tool_fragments: dict[int, dict[str, Any]] = {}
4710
4729
  output_tokens = 0
4711
4730
  finish_reason = "stop"
4731
+ chunks_seen = 0
4732
+ last_activity_update = 0.0
4712
4733
 
4713
4734
  def emit(event_name: str, payload: dict[str, Any]) -> None:
4714
4735
  handler.wfile.write(f"event: {event_name}\ndata: {json.dumps(payload, ensure_ascii=False)}\n\n".encode())
@@ -4736,8 +4757,27 @@ def stream_openai_chat_to_anthropic_sse(
4736
4757
  {"type": "content_block_delta", "index": idx, "delta": {"type": "text_delta", "text": text}},
4737
4758
  )
4738
4759
 
4760
+ def update_stream_activity(force: bool = False) -> None:
4761
+ nonlocal last_activity_update
4762
+ now = time.time()
4763
+ if not force and now - last_activity_update < 0.5:
4764
+ return
4765
+ last_activity_update = now
4766
+ estimated_output = output_tokens or max(0, len(text_so_far) // 4)
4767
+ write_router_activity(
4768
+ "request",
4769
+ provider,
4770
+ model,
4771
+ tokens=input_tokens,
4772
+ bytes=input_bytes,
4773
+ output_tokens=estimated_output,
4774
+ chunks=chunks_seen,
4775
+ stream=True,
4776
+ )
4777
+
4739
4778
  try:
4740
4779
  for raw_line in resp:
4780
+ chunks_seen += 1
4741
4781
  line = raw_line.decode("utf-8", errors="ignore").strip()
4742
4782
  if not line or line.startswith(":"):
4743
4783
  continue
@@ -4789,6 +4829,7 @@ def stream_openai_chat_to_anthropic_sse(
4789
4829
  emit_text_delta(to_flush)
4790
4830
  else:
4791
4831
  emit_text_delta(text_chunk)
4832
+ update_stream_activity()
4792
4833
  for call in delta.get("tool_calls") or []:
4793
4834
  if not isinstance(call, dict):
4794
4835
  continue
@@ -4804,6 +4845,8 @@ def stream_openai_chat_to_anthropic_sse(
4804
4845
  slot["name"] += str(fn.get("name"))
4805
4846
  if fn.get("arguments"):
4806
4847
  slot["arguments"] += str(fn.get("arguments"))
4848
+ update_stream_activity()
4849
+ update_stream_activity(force=True)
4807
4850
  if word_chunking and text_buffer:
4808
4851
  to_flush, text_buffer = _split_word_buffer(text_buffer, force=True)
4809
4852
  emit_text_delta(to_flush)
@@ -5108,6 +5151,8 @@ def forward_openai_compatible_chat(handler: BaseHTTPRequestHandler, provider: st
5108
5151
  model,
5109
5152
  emit_retry_notice,
5110
5153
  )
5154
+ req_tokens = estimate_tokens(req_body)
5155
+ req_bytes = len(json.dumps(req_body, ensure_ascii=False).encode("utf-8"))
5111
5156
  stream_openai_chat_to_anthropic_sse(
5112
5157
  handler,
5113
5158
  resp,
@@ -5116,8 +5161,10 @@ def forward_openai_compatible_chat(handler: BaseHTTPRequestHandler, provider: st
5116
5161
  source_body=body,
5117
5162
  start_index=index,
5118
5163
  word_chunking=bool(pcfg.get("stream_word_chunking", False)),
5164
+ input_tokens=req_tokens,
5165
+ input_bytes=req_bytes,
5119
5166
  )
5120
- write_router_activity("success", provider, model, tokens=estimate_tokens(req_body), bytes=len(json.dumps(req_body, ensure_ascii=False).encode("utf-8")), stream=True)
5167
+ write_router_activity("success", provider, model, tokens=req_tokens, bytes=req_bytes, stream=True)
5121
5168
  except RuntimeError as exc:
5122
5169
  msg = str(exc)
5123
5170
  write_anthropic_stream_blocks(handler, [{"type": "text", "text": f"Upstream error: {msg}"}], index)
@@ -6663,11 +6710,11 @@ def apply_provider_option(provider: str, pcfg: dict[str, Any], token: str) -> No
6663
6710
  raise SystemExit("timeout must be a positive integer; values above 10000 are treated as milliseconds")
6664
6711
  pcfg["request_timeout_ms"] = fixed if key.endswith("_ms") or fixed > 10000 else fixed * 1000
6665
6712
  return
6666
- if key in ("rate_limit", "rate_limit_rpm", "rpm"):
6667
- fixed = positive_int(value)
6668
- if value in (0, "0", False, None):
6669
- pcfg.pop("rate_limit_rpm", None)
6670
- return
6713
+ if key in ("rate_limit", "rate_limit_rpm", "rpm"):
6714
+ fixed = positive_int(value)
6715
+ if value in (0, "0", False, None):
6716
+ pcfg["rate_limit_rpm"] = 0
6717
+ return
6671
6718
  if not fixed:
6672
6719
  raise SystemExit("rate_limit_rpm must be a positive integer, or 0/unset to disable")
6673
6720
  pcfg["rate_limit_rpm"] = fixed
package/docs/README.ja.md CHANGED
@@ -47,7 +47,7 @@ vLLM、NVIDIA hosted、self-hosted NIM を選択し、通常の Claude Code 引
47
47
 
48
48
  Credits: One Ciel LLC
49
49
 
50
- 現在のバージョン: `0.1.39`
50
+ 現在のバージョン: `0.1.42`
51
51
 
52
52
  ## 作られた理由
53
53
 
@@ -351,6 +351,21 @@ Windows/Linux 管理、クリーンアップスクリプト、定期的なセキ
351
351
 
352
352
  ## 変更履歴
353
353
 
354
+ ### 0.1.42
355
+
356
+ - **ライブストリーム進捗**: statusline が upstream streaming の出力進捗を
357
+ 入力/出力 token 推定値と chunk 数で継続更新します。
358
+
359
+ ### 0.1.41
360
+
361
+ - **Statusline 表示改善**: upstream token 数に桁区切りと `tok` 前の空白を入れ、
362
+ `27,501 tok` のように表示します。
363
+
364
+ ### 0.1.40
365
+
366
+ - **RPM 0 を保持**: `rate_limit_rpm=0` の設定が provider 既定値に戻らず、
367
+ 明示的な無制限モードとして保存されます。
368
+
354
369
  ### 0.1.39
355
370
 
356
371
  - **メニュー入力修正**: テキスト/数字プロンプトの前に terminal line/echo mode を
package/docs/README.ko.md CHANGED
@@ -47,7 +47,7 @@ NVIDIA hosted, self-hosted NIM을 선택하고, Claude Code의 일반 인자는
47
47
 
48
48
  Credits: One Ciel LLC
49
49
 
50
- 현재 버전: `0.1.39`
50
+ 현재 버전: `0.1.42`
51
51
 
52
52
  ## 왜 만들었나
53
53
 
@@ -351,6 +351,21 @@ Windows 이벤트 로그 리뷰, 바이러스/랜섬웨어 침입 시도 정리,
351
351
 
352
352
  ## 변경 이력
353
353
 
354
+ ### 0.1.42
355
+
356
+ - **실시간 스트림 진행 표시**: statusline이 upstream streaming 출력 진행을
357
+ 입력/출력 token 추정치와 chunk 수로 계속 갱신합니다.
358
+
359
+ ### 0.1.41
360
+
361
+ - **Statusline 표시 개선**: upstream token 수에 천 단위 구분자와 `tok` 앞 공백을
362
+ 넣어 `27,501 tok`처럼 표시합니다.
363
+
364
+ ### 0.1.40
365
+
366
+ - **RPM 0 유지**: `rate_limit_rpm=0` 설정이 provider 기본값으로 되돌아가지 않고
367
+ 명시적인 무제한 모드로 저장됩니다.
368
+
354
369
  ### 0.1.39
355
370
 
356
371
  - **메뉴 입력 수정**: 텍스트/숫자 프롬프트 전에 터미널 line/echo 모드를 복구하여
package/docs/README.zh.md CHANGED
@@ -47,7 +47,7 @@ NIM,并把普通 Claude Code 参数原样传递。
47
47
 
48
48
  Credits: One Ciel LLC
49
49
 
50
- 当前版本: `0.1.39`
50
+ 当前版本: `0.1.42`
51
51
 
52
52
  ## 为什么存在
53
53
 
@@ -337,6 +337,21 @@ Hermes 格式模型或部分较旧的 Qwen tool template。
337
337
 
338
338
  ## 更新日志
339
339
 
340
+ ### 0.1.42
341
+
342
+ - **实时流式进度**:statusline 会持续更新 upstream streaming 输出进度,
343
+ 显示输入/输出 token 估算值和 chunk 数。
344
+
345
+ ### 0.1.41
346
+
347
+ - **Statusline 格式优化**:upstream token 数现在带千位分隔符,并在 `tok` 前加入空格,
348
+ 例如 `27,501 tok`。
349
+
350
+ ### 0.1.40
351
+
352
+ - **保留 RPM 0**:`rate_limit_rpm=0` 现在会保存为明确的无限制模式,
353
+ 不会回退到 provider 默认值。
354
+
340
355
  ### 0.1.39
341
356
 
342
357
  - **菜单输入修复**:在文本/数字提示前恢复 terminal line/echo mode,
package/docs/manual.md CHANGED
@@ -10,7 +10,7 @@ Code starts, while passing normal Claude Code arguments through unchanged.
10
10
 
11
11
  Credits: One Ciel LLC
12
12
 
13
- Current version: `0.1.39`
13
+ Current version: `0.1.42`
14
14
 
15
15
  ## Install
16
16
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oneciel-ai/claude-any",
3
- "version": "0.1.39",
3
+ "version": "0.1.42",
4
4
  "description": "Claude Code provider selector for Anthropic, Ollama, Ollama Cloud, vLLM, NVIDIA hosted, and self-hosted NIM.",
5
5
  "license": "MIT",
6
6
  "author": "One Ciel LLC",