proxytool 2.0.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.
- proxytool/__init__.py +108 -0
- proxytool/__main__.py +14 -0
- proxytool/cli.py +617 -0
- proxytool/conduit.py +67 -0
- proxytool/config.py +488 -0
- proxytool/loopguard.py +92 -0
- proxytool/main.py +558 -0
- proxytool/routing.py +106 -0
- proxytool/settings.py +66 -0
- proxytool/trace.py +81 -0
- proxytool-2.0.1.dist-info/METADATA +552 -0
- proxytool-2.0.1.dist-info/RECORD +14 -0
- proxytool-2.0.1.dist-info/WHEEL +4 -0
- proxytool-2.0.1.dist-info/entry_points.txt +3 -0
proxytool/__init__.py
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""proxytool: FastAPI middleware conduit standing between agent clients and Qwen servers."""
|
|
2
|
+
|
|
3
|
+
import logging as _logging
|
|
4
|
+
from importlib.metadata import PackageNotFoundError
|
|
5
|
+
from importlib.metadata import version as _metadata_version
|
|
6
|
+
|
|
7
|
+
try:
|
|
8
|
+
__version__: str = _metadata_version("proxytool")
|
|
9
|
+
except PackageNotFoundError: # unbuilt dev checkout
|
|
10
|
+
__version__ = "0.0.0.dev0+local"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def configure_logging(*, level: str | None = None, quiet: bool = False) -> None:
|
|
14
|
+
"""Configure app-level logging for proxytool modules.
|
|
15
|
+
|
|
16
|
+
``level`` selects the app logger threshold from the standard tiers
|
|
17
|
+
``debug``, ``info``, ``warning``, or ``error``. When omitted the
|
|
18
|
+
environment variable ``LTP_LOG_LEVEL`` (or its legacy ``LMW_LOG_LEVEL``
|
|
19
|
+
spelling) is used, falling back to ``info``.
|
|
20
|
+
|
|
21
|
+
``quiet`` suppresses non-error app output to stderr by raising the app
|
|
22
|
+
logger threshold to ``error``.
|
|
23
|
+
|
|
24
|
+
This sets up a simple stderr handler so app-level log lines (server
|
|
25
|
+
lifecycle, loop-guard notes, toolpatch-internal debug) are emitted
|
|
26
|
+
consistently. Uvicorn/FastAPI server logging is still configured
|
|
27
|
+
separately by the CLI via ``uvicorn.run(log_level=...)``.
|
|
28
|
+
"""
|
|
29
|
+
import os
|
|
30
|
+
|
|
31
|
+
from toolpatch.logging import LogLevels as _LogLevels
|
|
32
|
+
|
|
33
|
+
_tier_map: dict[str, _LogLevels | None] = {
|
|
34
|
+
"debug": "debug",
|
|
35
|
+
"info": "info",
|
|
36
|
+
"warning": "warning",
|
|
37
|
+
"error": None, # errors-only: hand toolpatch a no-op logger
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if quiet:
|
|
41
|
+
effective: str = "error"
|
|
42
|
+
elif level:
|
|
43
|
+
effective = level
|
|
44
|
+
else:
|
|
45
|
+
effective = os.environ.get("LTP_LOG_LEVEL") or os.environ.get("LMW_LOG_LEVEL") or "info"
|
|
46
|
+
|
|
47
|
+
_level = _resolve_level(effective)
|
|
48
|
+
_app = _logging.getLogger("proxytool")
|
|
49
|
+
_app.setLevel(_level)
|
|
50
|
+
_configure_handler(_app, _level)
|
|
51
|
+
_configure_handler(_logging.getLogger("toolpatch"), _level)
|
|
52
|
+
|
|
53
|
+
# toolpatch uses its own logger names; mirror the same threshold into the
|
|
54
|
+
# shared library so its internal debug lines are visible at the same tier.
|
|
55
|
+
try:
|
|
56
|
+
from toolpatch.logging import configure_logger as _toolpatch_configure
|
|
57
|
+
|
|
58
|
+
_toolpatch_configure("toolpatch", _tier_map.get(effective))
|
|
59
|
+
except Exception:
|
|
60
|
+
# toolpatch logging setup is best-effort; never let it break startup.
|
|
61
|
+
pass
|
|
62
|
+
|
|
63
|
+
# Mirror app threshold into toolpatch's shared logger too (it exposes
|
|
64
|
+
# ``get_logger`` which returns a stdlib logger once configured).
|
|
65
|
+
try:
|
|
66
|
+
from toolpatch import _configure_logging as _toolpatch_logging
|
|
67
|
+
|
|
68
|
+
_toolpatch_logging(level=effective, quiet=False)
|
|
69
|
+
except Exception:
|
|
70
|
+
pass
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _resolve_level(raw: str) -> int:
|
|
74
|
+
mapping = {
|
|
75
|
+
"debug": _logging.DEBUG,
|
|
76
|
+
"info": _logging.INFO,
|
|
77
|
+
"warning": _logging.WARNING,
|
|
78
|
+
"error": _logging.ERROR,
|
|
79
|
+
}
|
|
80
|
+
normalized = raw.strip().lower()
|
|
81
|
+
if normalized not in mapping:
|
|
82
|
+
raise ValueError(
|
|
83
|
+
f"unsupported log level {raw!r}; expected one of {list(mapping)}"
|
|
84
|
+
)
|
|
85
|
+
return mapping[normalized]
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _configure_handler(logger: _logging.Logger, level: int) -> None:
|
|
89
|
+
import sys
|
|
90
|
+
|
|
91
|
+
_ensure_handler(logger, level, stream=sys.stderr)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _ensure_handler(logger: _logging.Logger, level: int, *, stream) -> None:
|
|
95
|
+
import logging as _logging
|
|
96
|
+
|
|
97
|
+
has_target = any(
|
|
98
|
+
isinstance(h, _logging.StreamHandler) and getattr(h, "stream", None) is stream
|
|
99
|
+
for h in logger.handlers
|
|
100
|
+
)
|
|
101
|
+
if has_target:
|
|
102
|
+
return
|
|
103
|
+
handler = _logging.StreamHandler(stream)
|
|
104
|
+
handler.setLevel(level)
|
|
105
|
+
handler.setFormatter(_logging.Formatter(
|
|
106
|
+
"%(levelname)s [%(name)s] %(message)s",
|
|
107
|
+
))
|
|
108
|
+
logger.addHandler(handler)
|
proxytool/__main__.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""`python -m proxytool`: delegate to the ``proxytool`` CLI.
|
|
2
|
+
|
|
3
|
+
Previously this started a dev server unconditionally (ignoring args), which
|
|
4
|
+
made `python -m proxytool --help` launch uvicorn. Now it forwards to the same
|
|
5
|
+
argparse CLI the console script uses; the dev-reload workflow is
|
|
6
|
+
`uv run uvicorn proxytool.main:app --reload` (as documented in the README).
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
from proxytool.cli import main
|
|
12
|
+
|
|
13
|
+
if __name__ == "__main__":
|
|
14
|
+
sys.exit(main())
|