coderouter-cli 2.11.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.
@@ -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
- out: list[tuple[int, int, str]] = []
544
- n = len(text)
545
- i = 0
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
- out: list[tuple[int, int, str]] = []
592
- n = len(text)
593
- i = 0
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 _find_balanced_json_objects(cleaned):
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 _find_candidate_object_spans(cleaned):
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):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coderouter-cli
3
- Version: 2.11.0
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
@@ -1,16 +1,16 @@
1
1
  coderouter/__init__.py,sha256=ghdjPrLtnRzY8fyQ4CJZI1UJKADyNTLtA3G7se8H7Ns,696
2
2
  coderouter/__main__.py,sha256=-LCgxJnvgUV240HjQKv7ly-mn2NuKHpC4nCpvTHjeSU,130
3
- coderouter/cli.py,sha256=rEo-G9fyIBXnRWmVCHxWBlfzII2CWMcFvhRJW5h2oBU,34650
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=x0OqWUMGPzYaX2poWSH2UA90sEr4Z69-gVYLXYQFCFU,85483
7
- coderouter/doctor_apply.py,sha256=r_J6xbu5-HivofPNriw4_vjNYs_VRs7GsGTS0oMEX10,24209
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=KeE00CfbYRa4gSTrzuwC6zVuHi20IzLC4nUQa98BEXI,10387
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=_5R7dA8tHEhwLn9VkXccLEXPJREfpHu90nYd6-cm8Xs,28180
13
+ coderouter/launcher_devices.py,sha256=Jd2hVJMsQZ0x6YFc_2ROvEc2L10b6dh77Y-gvP_Po8M,29039
14
14
  coderouter/launcher_speculative.py,sha256=h0RTGsD1Y56sJVywDJwybrT75_Ta3GF1Jz_B1d42aPg,12931
15
15
  coderouter/launcher_swap.py,sha256=tMW5ZElDdf9T1VQGsUkvfLiSCdj_oXN_UtkV7DE8ySY,24175
16
16
  coderouter/logging.py,sha256=5rfi4_R4aoeeUYDc642dgs61wA0aBX7wvubyB-k3Uyo,58074
@@ -19,15 +19,15 @@ coderouter/token_estimation.py,sha256=iz22vZEEW2P7uKLB2pYvPNpIbZGbgXRO5MtfkS_-9S
19
19
  coderouter/token_estimation_accurate.py,sha256=GTfzrBVnvAGjeVzmzAeUdOYZvWZKLAxcxPpFiJGlzjk,4609
20
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=CT9Hitun-c3z83YHT2zZXgwZpY3_t6eRnOfAmNud2aw,23610
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=FUEe8m4Tnmj_aul0vSctD8vKvNW-oLRoMRbTpSKqSmc,4077
30
- coderouter/config/schemas.py,sha256=nPwaG4uIdhqoOs74nay4fPTNvEHxpsx7RhqFTZzBX-E,127468
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=vlepNw_GjvmpHZHTkMS5JM7XKxHaRxzjj_GfopRa1M0,13489
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=CGD_Uq8jz1qwFPQD-HXfHdB7-nD1rvPaHkpXzmTzB1w,133287
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=DDH3kvnWqXOD28ENWi7kgpaBQOJo6_LRIj1a43ZWXP0,155655
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=uEScq-icLn6ZXFWaxsKROv7feaq3zD0ZogjbTJ3Y7gQ,12578
72
- coderouter/translation/convert.py,sha256=VV4kpu4dvLmGBmJfQnCLY5ryy_AnQcV2NuwjqRtaR8Q,52633
73
- coderouter/translation/tool_repair.py,sha256=DGOKArM53yfVbIfiAveoul1vGq9NEKmjagf43Qh4IkM,57630
74
- coderouter_cli-2.11.0.dist-info/METADATA,sha256=KIJ1syHgU0_eFQlBpZvaasysCEzQE5LLsITNQYP9x3o,16897
75
- coderouter_cli-2.11.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
76
- coderouter_cli-2.11.0.dist-info/entry_points.txt,sha256=-dnLfD1YZ2WjH2zSdNCvlO65wYltM9bsHt9Fhg3yGss,51
77
- coderouter_cli-2.11.0.dist-info/licenses/LICENSE,sha256=wkEzoR86jFw33jvfOHjULqmkGEfxTFMgMaJnpR8mPRw,1065
78
- coderouter_cli-2.11.0.dist-info/RECORD,,
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,,