coderouter-cli 2.10.0__py3-none-any.whl → 2.11.1__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.
- coderouter/adapters/anthropic_native.py +23 -5
- coderouter/cli.py +49 -0
- coderouter/config/loader.py +65 -0
- coderouter/config/schemas.py +228 -12
- coderouter/doctor.py +98 -19
- coderouter/doctor_apply.py +72 -3
- coderouter/gguf_introspect.py +132 -25
- coderouter/guards/drift_detection.py +13 -1
- coderouter/ingress/launcher_routes.py +289 -22
- coderouter/launcher_devices.py +200 -4
- coderouter/launcher_speculative.py +5 -1
- coderouter/launcher_swap.py +11 -2
- coderouter/routing/fallback.py +14 -2
- coderouter/translation/anthropic.py +14 -1
- coderouter/translation/convert.py +7 -0
- coderouter/translation/tool_repair.py +193 -76
- coderouter/vscode_init.py +4 -2
- {coderouter_cli-2.10.0.dist-info → coderouter_cli-2.11.1.dist-info}/METADATA +2 -1
- {coderouter_cli-2.10.0.dist-info → coderouter_cli-2.11.1.dist-info}/RECORD +22 -22
- {coderouter_cli-2.10.0.dist-info → coderouter_cli-2.11.1.dist-info}/WHEEL +0 -0
- {coderouter_cli-2.10.0.dist-info → coderouter_cli-2.11.1.dist-info}/entry_points.txt +0 -0
- {coderouter_cli-2.10.0.dist-info → coderouter_cli-2.11.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -90,12 +90,28 @@ from __future__ import annotations
|
|
|
90
90
|
|
|
91
91
|
import contextlib
|
|
92
92
|
import json
|
|
93
|
+
import logging
|
|
93
94
|
import re
|
|
94
95
|
import uuid
|
|
96
|
+
from array import array
|
|
95
97
|
from typing import Any
|
|
96
98
|
|
|
97
99
|
__all__ = ["deduplicate_tool_calls", "repair_tool_calls_in_text"]
|
|
98
100
|
|
|
101
|
+
# Plain stdlib logger (same idiom as coderouter/translation/anthropic.py).
|
|
102
|
+
# This module is deliberately import-free of the rest of the package:
|
|
103
|
+
# benchmarks/tool-repair/run_offline.py loads it straight from its source
|
|
104
|
+
# path, with no `coderouter` package on sys.path.
|
|
105
|
+
logger = logging.getLogger(__name__)
|
|
106
|
+
|
|
107
|
+
# Hard ceiling on the text handed to the bare-JSON brace scanners. The
|
|
108
|
+
# scanners themselves are linear, but every candidate they emit still costs a
|
|
109
|
+
# JSON parse attempt plus a prose-cue check, and an assistant message this
|
|
110
|
+
# large is never a tool call anyway. Above the limit the bare-JSON step is
|
|
111
|
+
# skipped outright; the fenced / XML / R4a / R4c paths are regex-driven and
|
|
112
|
+
# linear, so they keep running.
|
|
113
|
+
_MAX_BARE_SCAN_CHARS = 262_144
|
|
114
|
+
|
|
99
115
|
|
|
100
116
|
# ------------------------------------------------------------------
|
|
101
117
|
# R2: shape detection + normalisation (with key aliases)
|
|
@@ -528,6 +544,139 @@ def _in_spans(pos: int, spans: list[tuple[int, int]]) -> bool:
|
|
|
528
544
|
return any(start <= pos < end for start, end in spans)
|
|
529
545
|
|
|
530
546
|
|
|
547
|
+
# ------------------------------------------------------------------
|
|
548
|
+
# Brace scanning (bare-JSON candidate discovery)
|
|
549
|
+
#
|
|
550
|
+
# Both scanners below answer the same question for every ``{`` in the text:
|
|
551
|
+
# "does a *fresh* scan started right here reach a balanced close?". The
|
|
552
|
+
# obvious implementation re-scans to end-of-text once per ``{``, which is
|
|
553
|
+
# O(k*n) for k unclosed braces — a 48 KB assistant message full of stray
|
|
554
|
+
# braces took ~20 s, and because ``to_anthropic_response`` is called from the
|
|
555
|
+
# request path that stalled the whole server.
|
|
556
|
+
#
|
|
557
|
+
# Instead we precompute, in ONE right-to-left sweep, a table that answers
|
|
558
|
+
# "starting at offset j outside any string, where is the first ``}`` that is
|
|
559
|
+
# still at brace depth 0?". The closing brace of a ``{`` at offset q is then
|
|
560
|
+
# simply ``table[q + 1]``, so the driver below is a plain left-to-right walk
|
|
561
|
+
# and the whole scan is linear in the length of the text.
|
|
562
|
+
#
|
|
563
|
+
# The table is defined by a right-to-left recurrence over the scanner's state
|
|
564
|
+
# machine (outside a string / inside a string / previous char was a
|
|
565
|
+
# backslash). Only the "outside a string" row needs random access — the ``{``
|
|
566
|
+
# case has to jump over the nested object it just resolved — so the
|
|
567
|
+
# in-string rows are carried as rolling scalars instead of arrays.
|
|
568
|
+
# ------------------------------------------------------------------
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
def _scan_object_spans(text: str, close_table: array[int]) -> list[tuple[int, int, str]]:
|
|
572
|
+
"""Walk ``text`` left to right emitting balanced ``{...}`` spans.
|
|
573
|
+
|
|
574
|
+
``close_table`` comes from :func:`_strict_close_table` or
|
|
575
|
+
:func:`_lenient_close_table` and supplies, for each offset, the index of
|
|
576
|
+
the first depth-0 ``}`` at or after it (``-1`` when there is none). The
|
|
577
|
+
driver is shared; the flavour lives entirely in the table.
|
|
578
|
+
|
|
579
|
+
Returns (start, end_exclusive, substring) triples. See the two callers'
|
|
580
|
+
docstrings for the (deliberate, load-bearing) return-value contract.
|
|
581
|
+
"""
|
|
582
|
+
out: list[tuple[int, int, str]] = []
|
|
583
|
+
n = len(text)
|
|
584
|
+
i = 0
|
|
585
|
+
while i < n:
|
|
586
|
+
start = text.find("{", i)
|
|
587
|
+
if start < 0:
|
|
588
|
+
break
|
|
589
|
+
close = close_table[start + 1]
|
|
590
|
+
if close < 0:
|
|
591
|
+
# Never closes — drop this `{` and resume at the next one. The
|
|
592
|
+
# restart deliberately begins from a clean "outside a string"
|
|
593
|
+
# state (see the contract note in the callers).
|
|
594
|
+
i = start + 1
|
|
595
|
+
continue
|
|
596
|
+
out.append((start, close + 1, text[start : close + 1]))
|
|
597
|
+
i = close + 1
|
|
598
|
+
return out
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
def _strict_close_table(text: str) -> array[int]:
|
|
602
|
+
"""Depth-0 close-brace lookup table with *strict* JSON string rules.
|
|
603
|
+
|
|
604
|
+
``table[j]`` is the offset of the first ``}`` that a scanner starting at
|
|
605
|
+
``j`` — outside any string literal, at brace depth 0 — would reach while
|
|
606
|
+
still at depth 0, or ``-1`` if it runs off the end. Only ``"`` opens a
|
|
607
|
+
string; ``\\`` escapes the next character inside one.
|
|
608
|
+
|
|
609
|
+
Built in a single right-to-left sweep, so building plus scanning is
|
|
610
|
+
O(len(text)) regardless of how many unbalanced braces the text contains.
|
|
611
|
+
"""
|
|
612
|
+
n = len(text)
|
|
613
|
+
outside = array("i", [-1]) * (n + 1)
|
|
614
|
+
in_str = -1 # answer when the scan *enters* offset j+1 inside a "..."
|
|
615
|
+
escaped = -1 # ... inside a "..." with the previous char a backslash
|
|
616
|
+
for j in range(n - 1, -1, -1):
|
|
617
|
+
c = text[j]
|
|
618
|
+
nxt_outside = outside[j + 1]
|
|
619
|
+
nxt_in_str = in_str
|
|
620
|
+
nxt_escaped = escaped
|
|
621
|
+
# Escaped char: consumed verbatim, then we are back inside the string.
|
|
622
|
+
escaped = nxt_in_str
|
|
623
|
+
# Inside a string literal.
|
|
624
|
+
if c == "\\":
|
|
625
|
+
in_str = nxt_escaped
|
|
626
|
+
elif c == '"':
|
|
627
|
+
in_str = nxt_outside
|
|
628
|
+
else:
|
|
629
|
+
in_str = nxt_in_str
|
|
630
|
+
# Outside any string literal.
|
|
631
|
+
if c == '"':
|
|
632
|
+
outside[j] = nxt_in_str
|
|
633
|
+
elif c == "{":
|
|
634
|
+
inner = nxt_outside # close of the nested object opening at j
|
|
635
|
+
outside[j] = -1 if inner < 0 else outside[inner + 1]
|
|
636
|
+
elif c == "}":
|
|
637
|
+
outside[j] = j
|
|
638
|
+
else:
|
|
639
|
+
outside[j] = nxt_outside
|
|
640
|
+
return outside
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
def _lenient_close_table(text: str) -> array[int]:
|
|
644
|
+
"""Depth-0 close-brace lookup table tolerating single-quoted strings.
|
|
645
|
+
|
|
646
|
+
Same contract as :func:`_strict_close_table`, but BOTH ``'`` and ``"``
|
|
647
|
+
open a string literal (and only the matching quote closes it), which is
|
|
648
|
+
what the lenient pass needs to survive Python-repr / single-quoted
|
|
649
|
+
malformed objects. Five scanner states instead of three; again only the
|
|
650
|
+
"outside a string" row needs to be materialised as an array.
|
|
651
|
+
"""
|
|
652
|
+
n = len(text)
|
|
653
|
+
outside = array("i", [-1]) * (n + 1)
|
|
654
|
+
in_sq = in_dq = esc_sq = esc_dq = -1
|
|
655
|
+
for j in range(n - 1, -1, -1):
|
|
656
|
+
c = text[j]
|
|
657
|
+
nxt_outside = outside[j + 1]
|
|
658
|
+
nxt_in_sq, nxt_in_dq = in_sq, in_dq
|
|
659
|
+
nxt_esc_sq, nxt_esc_dq = esc_sq, esc_dq
|
|
660
|
+
esc_sq, esc_dq = nxt_in_sq, nxt_in_dq
|
|
661
|
+
if c == "\\":
|
|
662
|
+
in_sq, in_dq = nxt_esc_sq, nxt_esc_dq
|
|
663
|
+
else:
|
|
664
|
+
in_sq = nxt_outside if c == "'" else nxt_in_sq
|
|
665
|
+
in_dq = nxt_outside if c == '"' else nxt_in_dq
|
|
666
|
+
if c == "'":
|
|
667
|
+
outside[j] = nxt_in_sq
|
|
668
|
+
elif c == '"':
|
|
669
|
+
outside[j] = nxt_in_dq
|
|
670
|
+
elif c == "{":
|
|
671
|
+
inner = nxt_outside
|
|
672
|
+
outside[j] = -1 if inner < 0 else outside[inner + 1]
|
|
673
|
+
elif c == "}":
|
|
674
|
+
outside[j] = j
|
|
675
|
+
else:
|
|
676
|
+
outside[j] = nxt_outside
|
|
677
|
+
return outside
|
|
678
|
+
|
|
679
|
+
|
|
531
680
|
def _find_balanced_json_objects(text: str) -> list[tuple[int, int, str]]:
|
|
532
681
|
"""Find top-level `{...}` JSON substrings by a brace-counter scan.
|
|
533
682
|
|
|
@@ -539,45 +688,27 @@ def _find_balanced_json_objects(text: str) -> list[tuple[int, int, str]]:
|
|
|
539
688
|
brace balancing. Single-quoted / unquoted malformed objects are found by
|
|
540
689
|
:func:`_find_candidate_object_spans` instead, which brace-balances without
|
|
541
690
|
assuming JSON string syntax.
|
|
691
|
+
|
|
692
|
+
Return-value contract (load-bearing — downstream recall depends on all
|
|
693
|
+
three; do NOT "simplify" this into a single global stack pass):
|
|
694
|
+
|
|
695
|
+
1. **Top-level objects only.** Once a ``{`` closes, the scan resumes after
|
|
696
|
+
its ``}``, so objects nested inside a *closed* object are not returned.
|
|
697
|
+
``'{"a": {"b": 1}}'`` yields the outer object alone.
|
|
698
|
+
2. **Nested objects surface when the outer never closes.** An unbalanced
|
|
699
|
+
``{`` is dropped and the scan restarts at the next ``{``, so
|
|
700
|
+
``'{"a": {"b":1}'`` yields ``[(6, 13, '{"b":1}')]`` — the inner
|
|
701
|
+
object is still repairable even though the outer one is broken.
|
|
702
|
+
3. **Every restart begins with a fresh string state.** The scan between
|
|
703
|
+
candidates does not track quotes, so a stray/unbalanced quote before a
|
|
704
|
+
``{`` cannot swallow it. This is what keeps prose apostrophes
|
|
705
|
+
(``"I'll call the tool now."``) and quoted braces (``'{ "z{}" '`` ->
|
|
706
|
+
``[(4, 6, '{}')]``) from hiding a real call. A single global pass that
|
|
707
|
+
carried string state across candidates loses both.
|
|
542
708
|
"""
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
while i < n:
|
|
547
|
-
if text[i] != "{":
|
|
548
|
-
i += 1
|
|
549
|
-
continue
|
|
550
|
-
# Scan forward to find a balanced close.
|
|
551
|
-
depth = 0
|
|
552
|
-
j = i
|
|
553
|
-
in_str = False
|
|
554
|
-
escape = False
|
|
555
|
-
while j < n:
|
|
556
|
-
c = text[j]
|
|
557
|
-
if escape:
|
|
558
|
-
escape = False
|
|
559
|
-
elif in_str:
|
|
560
|
-
if c == "\\":
|
|
561
|
-
escape = True
|
|
562
|
-
elif c == '"':
|
|
563
|
-
in_str = False
|
|
564
|
-
else:
|
|
565
|
-
if c == '"':
|
|
566
|
-
in_str = True
|
|
567
|
-
elif c == "{":
|
|
568
|
-
depth += 1
|
|
569
|
-
elif c == "}":
|
|
570
|
-
depth -= 1
|
|
571
|
-
if depth == 0:
|
|
572
|
-
out.append((i, j + 1, text[i : j + 1]))
|
|
573
|
-
i = j + 1
|
|
574
|
-
break
|
|
575
|
-
j += 1
|
|
576
|
-
else:
|
|
577
|
-
# Ran off the end without closing — skip this `{` and move on.
|
|
578
|
-
i += 1
|
|
579
|
-
continue
|
|
580
|
-
return out
|
|
709
|
+
if "{" not in text:
|
|
710
|
+
return []
|
|
711
|
+
return _scan_object_spans(text, _strict_close_table(text))
|
|
581
712
|
|
|
582
713
|
|
|
583
714
|
def _find_candidate_object_spans(text: str) -> list[tuple[int, int, str]]:
|
|
@@ -587,43 +718,17 @@ def _find_candidate_object_spans(text: str) -> list[tuple[int, int, str]]:
|
|
|
587
718
|
respecting BOTH ``'`` and ``"`` string delimiters, so it can locate
|
|
588
719
|
Python-repr / single-quoted malformed objects for the lenient parser.
|
|
589
720
|
Returns (start, end_exclusive, substring).
|
|
721
|
+
|
|
722
|
+
The same three-point return-value contract documented on
|
|
723
|
+
:func:`_find_balanced_json_objects` applies here: top-level objects only,
|
|
724
|
+
nested objects surface when the outer never closes, and every restart
|
|
725
|
+
begins from a fresh string state. Point 3 matters most on this flavour —
|
|
726
|
+
``'`` opens a string here, so carrying state across candidates would let
|
|
727
|
+
an ordinary prose apostrophe swallow every following object.
|
|
590
728
|
"""
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
while i < n:
|
|
595
|
-
if text[i] != "{":
|
|
596
|
-
i += 1
|
|
597
|
-
continue
|
|
598
|
-
depth = 0
|
|
599
|
-
j = i
|
|
600
|
-
quote: str | None = None
|
|
601
|
-
escape = False
|
|
602
|
-
while j < n:
|
|
603
|
-
c = text[j]
|
|
604
|
-
if escape:
|
|
605
|
-
escape = False
|
|
606
|
-
elif quote is not None:
|
|
607
|
-
if c == "\\":
|
|
608
|
-
escape = True
|
|
609
|
-
elif c == quote:
|
|
610
|
-
quote = None
|
|
611
|
-
else:
|
|
612
|
-
if c == '"' or c == "'":
|
|
613
|
-
quote = c
|
|
614
|
-
elif c == "{":
|
|
615
|
-
depth += 1
|
|
616
|
-
elif c == "}":
|
|
617
|
-
depth -= 1
|
|
618
|
-
if depth == 0:
|
|
619
|
-
out.append((i, j + 1, text[i : j + 1]))
|
|
620
|
-
i = j + 1
|
|
621
|
-
break
|
|
622
|
-
j += 1
|
|
623
|
-
else:
|
|
624
|
-
i += 1
|
|
625
|
-
continue
|
|
626
|
-
return out
|
|
729
|
+
if "{" not in text:
|
|
730
|
+
return []
|
|
731
|
+
return _scan_object_spans(text, _lenient_close_table(text))
|
|
627
732
|
|
|
628
733
|
|
|
629
734
|
# ------------------------------------------------------------------
|
|
@@ -1405,12 +1510,24 @@ def repair_tool_calls_in_text(
|
|
|
1405
1510
|
# Skip anything inside a protected code fence or introduced by a prose
|
|
1406
1511
|
# example cue. Walk back-to-front so slicing removals don't shift the
|
|
1407
1512
|
# indices of earlier matches.
|
|
1513
|
+
# Oversized inputs skip this step entirely (see _MAX_BARE_SCAN_CHARS):
|
|
1514
|
+
# the per-candidate JSON parsing / cue checking is the expensive part
|
|
1515
|
+
# and a message that large is not a tool call. Steps 1/2/2b/2c above
|
|
1516
|
+
# are regex-driven and linear, so they are NOT skipped.
|
|
1408
1517
|
protected = _protected_code_spans(cleaned)
|
|
1409
1518
|
spans_to_remove: list[tuple[int, int]] = []
|
|
1410
1519
|
repaired_from_bare: list[dict[str, Any]] = []
|
|
1411
1520
|
|
|
1521
|
+
strict_spans: list[tuple[int, int, str]] = []
|
|
1522
|
+
lenient_spans: list[tuple[int, int, str]] = []
|
|
1523
|
+
if len(cleaned) > _MAX_BARE_SCAN_CHARS:
|
|
1524
|
+
logger.warning("tool-repair-input-too-large", extra={"chars": len(cleaned)})
|
|
1525
|
+
else:
|
|
1526
|
+
strict_spans = _find_balanced_json_objects(cleaned)
|
|
1527
|
+
lenient_spans = _find_candidate_object_spans(cleaned)
|
|
1528
|
+
|
|
1412
1529
|
# First pass: strict-JSON balanced objects.
|
|
1413
|
-
for start, end, substr in
|
|
1530
|
+
for start, end, substr in strict_spans:
|
|
1414
1531
|
if _in_spans(start, protected):
|
|
1415
1532
|
continue
|
|
1416
1533
|
if _preceded_by_prose_cue(cleaned, start):
|
|
@@ -1436,7 +1553,7 @@ def repair_tool_calls_in_text(
|
|
|
1436
1553
|
|
|
1437
1554
|
# Second pass: lenient objects (single-quoted / unquoted / trailing comma /
|
|
1438
1555
|
# double-brace) that strict parsing missed. Skip spans already claimed.
|
|
1439
|
-
for start, end, substr in
|
|
1556
|
+
for start, end, substr in lenient_spans:
|
|
1440
1557
|
if _in_spans(start, spans_to_remove):
|
|
1441
1558
|
continue
|
|
1442
1559
|
if _in_spans(start, protected):
|
coderouter/vscode_init.py
CHANGED
|
@@ -57,8 +57,10 @@ DEFAULT_PORT = 8088
|
|
|
57
57
|
# Bogus token — CodeRouter's chat ingress does not validate ANTHROPIC_AUTH_TOKEN
|
|
58
58
|
# (real API keys are managed on the ``providers.yaml`` side). Using a
|
|
59
59
|
# fixed sentinel here means we never accidentally embed the operator's
|
|
60
|
-
# real token into a workspace file.
|
|
61
|
-
|
|
60
|
+
# real token into a workspace file. (Not annotated with `noqa: S105`
|
|
61
|
+
# because this project doesn't enable that ruff rule — RUF100 would
|
|
62
|
+
# then flag the unused directive.)
|
|
63
|
+
DEFAULT_TOKEN = "dummy"
|
|
62
64
|
|
|
63
65
|
# VSCode reads terminal env from a separate key per host OS. Writing all
|
|
64
66
|
# three means the same ``.vscode/`` folder works whether the developer
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: coderouter-cli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.11.1
|
|
4
4
|
Summary: Local-first, free-first, fallback-built-in LLM router. Claude Code / OpenAI compatible.
|
|
5
5
|
Project-URL: Homepage, https://github.com/zephel01/CodeRouter
|
|
6
6
|
Project-URL: Repository, https://github.com/zephel01/CodeRouter
|
|
@@ -224,6 +224,7 @@ providers:
|
|
|
224
224
|
| **ログビューア** | 各プロセスの stdout/stderr をブラウザ内でリアルタイム確認 |
|
|
225
225
|
| **provider 自動同期** (v2.7.4) | 起動したバックエンドを provider として自動登録(`launcher-llamacpp-8085` 等)。providers.yaml 無編集で `X-CodeRouter-Profile: launcher` からルーティング可能。メモリ内のみ・serve と同寿命 |
|
|
226
226
|
| **モデル名パススルー** (v2.7.4) | `model: ""` の provider は `/v1/models` が上流のロード中モデル ID(gguf 名)をそのまま返す。gguf を差し替えても config 編集不要 — 外部ベンチからモデルを識別できる |
|
|
227
|
+
| **特化ビルドの切り替え** (v2.11.0) | `llama.cpp-cuda` / `-vulkan` / `-rocm` を `backends` に登録すると、起動ごとにどのビルドの `llama-server` を使うか選べる。ビルド別にデバイス検出・option_profiles・ベンチスイープが独立(書かなければ従来どおり) |
|
|
227
228
|
|
|
228
229
|
```yaml
|
|
229
230
|
# providers.yaml に追記するだけで有効になる
|
|
@@ -1,33 +1,33 @@
|
|
|
1
1
|
coderouter/__init__.py,sha256=ghdjPrLtnRzY8fyQ4CJZI1UJKADyNTLtA3G7se8H7Ns,696
|
|
2
2
|
coderouter/__main__.py,sha256=-LCgxJnvgUV240HjQKv7ly-mn2NuKHpC4nCpvTHjeSU,130
|
|
3
|
-
coderouter/cli.py,sha256=
|
|
3
|
+
coderouter/cli.py,sha256=2g-0HILLM16mNnkFUOR6lWXy5f6Y0LipCjo_YH4--j0,36896
|
|
4
4
|
coderouter/cli_stats.py,sha256=CCjzc1G4hTRHZ2gG1XhxhDpUkJnnl3NXbcbp1T18jpg,29894
|
|
5
5
|
coderouter/cost.py,sha256=32h6uzb4nxh2eA5d2Hn3kD9yJbtis6CFDAbeIy5KRkM,7431
|
|
6
|
-
coderouter/doctor.py,sha256=
|
|
7
|
-
coderouter/doctor_apply.py,sha256=
|
|
6
|
+
coderouter/doctor.py,sha256=wmPunlvqm2s7x3VGLjexqHPLD2oejwE3Mu04V8y-xn4,89869
|
|
7
|
+
coderouter/doctor_apply.py,sha256=UqRvzRqENHWqsL3OCsuSotNdxCMw5rP2LnCWMBLmZow,28033
|
|
8
8
|
coderouter/env_security.py,sha256=FEBZnXfJ0xE39kmMMn39zk0W_DRRnmcB_REmP9f4xWo,14796
|
|
9
9
|
coderouter/errors.py,sha256=Xmq67lheyw8iv3Ox39jh2c4tvNI5RcUR4QkoxVDN6l4,1130
|
|
10
|
-
coderouter/gguf_introspect.py,sha256=
|
|
10
|
+
coderouter/gguf_introspect.py,sha256=Scu0JjUfLHj7T7TL0HQIm1AZcGYrXsjkaMMSN9HkTro,16121
|
|
11
11
|
coderouter/hardware.py,sha256=gn3_9qbVcGRR81yKMn1lJE_8-YDRau0LxIH_M-f7pxE,8356
|
|
12
12
|
coderouter/language_tax.py,sha256=LTbE3tIfoJuV2O3T0NixRKhzq_dEOTUuPEerJv2q9uk,9360
|
|
13
|
-
coderouter/launcher_devices.py,sha256=
|
|
14
|
-
coderouter/launcher_speculative.py,sha256=
|
|
15
|
-
coderouter/launcher_swap.py,sha256=
|
|
13
|
+
coderouter/launcher_devices.py,sha256=Jd2hVJMsQZ0x6YFc_2ROvEc2L10b6dh77Y-gvP_Po8M,29039
|
|
14
|
+
coderouter/launcher_speculative.py,sha256=h0RTGsD1Y56sJVywDJwybrT75_Ta3GF1Jz_B1d42aPg,12931
|
|
15
|
+
coderouter/launcher_swap.py,sha256=tMW5ZElDdf9T1VQGsUkvfLiSCdj_oXN_UtkV7DE8ySY,24175
|
|
16
16
|
coderouter/logging.py,sha256=5rfi4_R4aoeeUYDc642dgs61wA0aBX7wvubyB-k3Uyo,58074
|
|
17
17
|
coderouter/output_filters.py,sha256=0ry_rPiS_kC-FnHgaNVP6v7e6Al2djxzu9vBzZ8kEkE,25314
|
|
18
18
|
coderouter/token_estimation.py,sha256=iz22vZEEW2P7uKLB2pYvPNpIbZGbgXRO5MtfkS_-9Sk,7531
|
|
19
19
|
coderouter/token_estimation_accurate.py,sha256=GTfzrBVnvAGjeVzmzAeUdOYZvWZKLAxcxPpFiJGlzjk,4609
|
|
20
|
-
coderouter/vscode_init.py,sha256=
|
|
20
|
+
coderouter/vscode_init.py,sha256=ATQ1FzeQRAXfBBNcPwpEHRP4l4Yjn4hYgE-TjkcVvck,20034
|
|
21
21
|
coderouter/adapters/__init__.py,sha256=7dIDSZ-FE_0iSqLSDc_lK1idRdLTKcM2hP9tCJipgPI,463
|
|
22
|
-
coderouter/adapters/anthropic_native.py,sha256
|
|
22
|
+
coderouter/adapters/anthropic_native.py,sha256=-FGTQFlFL5Z2IZms4NUGPcNkIN-fVvQrCY8e8nxW17s,24777
|
|
23
23
|
coderouter/adapters/base.py,sha256=ykKMaiIVVGr23oFFXilK2iP_YhI8-CtsEiJyFXaC9bE,10396
|
|
24
24
|
coderouter/adapters/openai_compat.py,sha256=wcrd_UpV7N6ISpUyKvdFPGU-YjCrX5oMqGpjAFEeWfg,20356
|
|
25
25
|
coderouter/adapters/registry.py,sha256=XNc1wmFv_oZlqKS0rA693TRDdbq1sZ-OIcofXOVOtq0,3680
|
|
26
26
|
coderouter/config/__init__.py,sha256=FODEn74fN-qZnt4INPSHswqhOlEgpL6-_onxsitSx8g,274
|
|
27
27
|
coderouter/config/capability_registry.py,sha256=RZgzHF54dXy0CCDocRCz2ee21qXGbbgTSKTkRI9iLL4,16936
|
|
28
28
|
coderouter/config/env_file.py,sha256=CoMK27fuAXm-NtoLzXb8yN2E-wDFjHQuFwiIlmgTBQw,10356
|
|
29
|
-
coderouter/config/loader.py,sha256=
|
|
30
|
-
coderouter/config/schemas.py,sha256=
|
|
29
|
+
coderouter/config/loader.py,sha256=jUoHAbEyjK6Y0HDqhjXHO4tfncMPuzsjfWJokrPb2x8,6876
|
|
30
|
+
coderouter/config/schemas.py,sha256=8MZMW3Iask-ZvkWV2b77UYszqMgJ4ETc6gEU80Ekx9g,131685
|
|
31
31
|
coderouter/data/__init__.py,sha256=uNyfD9jaCvTWsBAWtaw1Fr25OSxzv3psGMfBjT1z0Cc,328
|
|
32
32
|
coderouter/data/model-capabilities.yaml,sha256=S9jt6SC6-3s2-icZ_n-a14iEMnc2yB1C2R6q-N_tZWQ,19309
|
|
33
33
|
coderouter/guards/__init__.py,sha256=5qliYBqygvVPneej7nx0uSjxDKsz7t8VzvrDgVBJlvU,1170
|
|
@@ -36,7 +36,7 @@ coderouter/guards/backend_health.py,sha256=GLUVQ5BhU6i8Cxz_FrgIMs5AZ-YSKAW8veFWg
|
|
|
36
36
|
coderouter/guards/context_budget.py,sha256=6u08JqBdRQkDz9NIQ-aISXo3w3L804oYFg027s04IwY,17202
|
|
37
37
|
coderouter/guards/continuous_probe.py,sha256=WIfS-apVMWXGv7bPBxxkJssePa95I4fT0mi4PNqK5iE,12181
|
|
38
38
|
coderouter/guards/drift_actions.py,sha256=A6pY5CR480Ct5rCVyjlBvjPFVc93eu_r5qcUpK9mWKc,3602
|
|
39
|
-
coderouter/guards/drift_detection.py,sha256=
|
|
39
|
+
coderouter/guards/drift_detection.py,sha256=lW45EZDGRleDUTstw_CHlfUiwLUrlDHX8g7ncuOYNEw,13962
|
|
40
40
|
coderouter/guards/memory_budget.py,sha256=_bRtusk4AwrU781wVXW32OFU7zD2FXxOwTb7yGqPJqA,8278
|
|
41
41
|
coderouter/guards/memory_pressure.py,sha256=9Xm-b7rv9Td5kPNmAFoL3sNmaOR5zT8NLdn6erWcuyY,8719
|
|
42
42
|
coderouter/guards/self_healing.py,sha256=Qene73S1kqMJn8HYzHO-WT8hhWxKbo2v_JtXtEgpCp8,14434
|
|
@@ -45,7 +45,7 @@ coderouter/ingress/__init__.py,sha256=WQsCH2CGJCAhy0mS6GSEdeYZRkkQu2OHDsP4CJWTLu
|
|
|
45
45
|
coderouter/ingress/anthropic_routes.py,sha256=1ThXzx8VgxRV7UCi3NF5ey7kbr0ADPKTXyQuYS5gK1w,27063
|
|
46
46
|
coderouter/ingress/app.py,sha256=6e_qNlYZIyjLKhY20t0ZNL-x5Fv8R8oaIH_HCdxa9Ic,20921
|
|
47
47
|
coderouter/ingress/dashboard_routes.py,sha256=1SqNNrVTfQiZcx5n2MvjAzWirittX0e61tCt-MKp4Ag,25617
|
|
48
|
-
coderouter/ingress/launcher_routes.py,sha256=
|
|
48
|
+
coderouter/ingress/launcher_routes.py,sha256=g-0Mee1vpY10b82jI51-2fTeR8hxutMCaLYBcicwM_k,133287
|
|
49
49
|
coderouter/ingress/metrics_routes.py,sha256=M22dwOGn24P05Ge4W3c7d7mYytSGWjIR-pPSPOAiHJY,3965
|
|
50
50
|
coderouter/ingress/openai_routes.py,sha256=TrugsQNJfNPgKKLzfT1aXzrs4emWYGE4XebEBuaZoWk,12505
|
|
51
51
|
coderouter/metrics/__init__.py,sha256=7Es351DPS7yLM0yVF_F0eesmiD83n7Zzhie44chht38,1465
|
|
@@ -60,7 +60,7 @@ coderouter/routing/adaptive.py,sha256=G2o377twGSjbUh65wiIFx6klnpFGjsD_nI3oDvcBwh
|
|
|
60
60
|
coderouter/routing/auto_router.py,sha256=y4v0c8u5F9f98Vmhx1vRcKPiOgAvpzbFqr6TIh058h0,13341
|
|
61
61
|
coderouter/routing/budget.py,sha256=PblmVKJGs_BwNa9uDHAA8hmZ4XIVKv38mHAeU0V3OMs,8451
|
|
62
62
|
coderouter/routing/capability.py,sha256=rRQhzTgQYFTFDNYcSLKvq3xjdw6B1w7T-3jS7PTMI14,27864
|
|
63
|
-
coderouter/routing/fallback.py,sha256=
|
|
63
|
+
coderouter/routing/fallback.py,sha256=fqwjz5hTNsQDEf45nYcVTwGJwz2mFxQ5ZmZwdvFSKsI,156394
|
|
64
64
|
coderouter/state/__init__.py,sha256=XoGcPmmBQSiZWML2S0juSveQ78xfhtdeCliNnVyzu7E,1088
|
|
65
65
|
coderouter/state/audit_log.py,sha256=n7vuDsTfd5iuNUJhlRQPakJ2tMSsT9EfgPCzs1GEaac,10941
|
|
66
66
|
coderouter/state/replay.py,sha256=Z_YHKroTKZdrL8qObFxcoLOAQWWXZvXFdLfxzvBhEJg,11230
|
|
@@ -68,11 +68,11 @@ coderouter/state/request_log.py,sha256=mNxQJ9IDGpUtT5rZbCo_-CTyhxmvig7C2Gfvdn9rc
|
|
|
68
68
|
coderouter/state/store.py,sha256=h-rsMJq8GILsOfCP94nI40cuHaj4Vqycsm9UNN77REI,7445
|
|
69
69
|
coderouter/state/suggest_rules.py,sha256=FvdhEvao5NvdKp9zs8AkcoFKHY4yqqXY2HekvSjpDFA,16670
|
|
70
70
|
coderouter/translation/__init__.py,sha256=PYXN7XVEwpG1uC8RLy6fvnGbzEZhhrEuUapH8IYOtG8,1788
|
|
71
|
-
coderouter/translation/anthropic.py,sha256=
|
|
72
|
-
coderouter/translation/convert.py,sha256=
|
|
73
|
-
coderouter/translation/tool_repair.py,sha256=
|
|
74
|
-
coderouter_cli-2.
|
|
75
|
-
coderouter_cli-2.
|
|
76
|
-
coderouter_cli-2.
|
|
77
|
-
coderouter_cli-2.
|
|
78
|
-
coderouter_cli-2.
|
|
71
|
+
coderouter/translation/anthropic.py,sha256=ZQQGS9itN_3o7IKD68lKr5gR_Kem32Jsd7OcGjJzv8Q,13468
|
|
72
|
+
coderouter/translation/convert.py,sha256=VrstDAMsUpcQ1VgMTn_-ETp1OjGX8v0ZGhCkcd4icyU,53037
|
|
73
|
+
coderouter/translation/tool_repair.py,sha256=1p8rmdyyscGX2g0DDOYitMnLv-nKfR1SLuLaRB4z7GM,64244
|
|
74
|
+
coderouter_cli-2.11.1.dist-info/METADATA,sha256=s5AYjjcq3k-9rNEZ4cNAf5arPT4FkS8bMuqVlNpBOc0,16897
|
|
75
|
+
coderouter_cli-2.11.1.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
76
|
+
coderouter_cli-2.11.1.dist-info/entry_points.txt,sha256=-dnLfD1YZ2WjH2zSdNCvlO65wYltM9bsHt9Fhg3yGss,51
|
|
77
|
+
coderouter_cli-2.11.1.dist-info/licenses/LICENSE,sha256=wkEzoR86jFw33jvfOHjULqmkGEfxTFMgMaJnpR8mPRw,1065
|
|
78
|
+
coderouter_cli-2.11.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|