vamp-cli 0.1.0__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.
- vamp_cli/__init__.py +0 -0
- vamp_cli/agent/__init__.py +0 -0
- vamp_cli/agent/context_sync.py +72 -0
- vamp_cli/agent/graph.py +159 -0
- vamp_cli/agent/instructions.py +18 -0
- vamp_cli/agent/nodes.py +375 -0
- vamp_cli/agent/roles.py +8 -0
- vamp_cli/agent/runtime/__init__.py +0 -0
- vamp_cli/agent/runtime/runtime.py +133 -0
- vamp_cli/agent/state.py +55 -0
- vamp_cli/agent/status.py +17 -0
- vamp_cli/config/__init__.py +0 -0
- vamp_cli/config/loader.py +152 -0
- vamp_cli/config/schema.py +82 -0
- vamp_cli/llm/__init__.py +0 -0
- vamp_cli/llm/adapter.py +52 -0
- vamp_cli/llm/adapter_registry.py +26 -0
- vamp_cli/llm/debug_callbacks.py +18 -0
- vamp_cli/llm/router.py +247 -0
- vamp_cli/llm/usage.py +170 -0
- vamp_cli/logging_setup/logging_setup.py +37 -0
- vamp_cli/main.py +1023 -0
- vamp_cli/persistence/__init__.py +29 -0
- vamp_cli/persistence/compaction.py +372 -0
- vamp_cli/persistence/emitter.py +371 -0
- vamp_cli/persistence/event_log.py +303 -0
- vamp_cli/persistence/fork.py +81 -0
- vamp_cli/persistence/loader.py +616 -0
- vamp_cli/persistence/transcript.py +160 -0
- vamp_cli/prompts/prompts.py +182 -0
- vamp_cli/sandbox/__init__.py +0 -0
- vamp_cli/sandbox/base.py +22 -0
- vamp_cli/sandbox/config.py +16 -0
- vamp_cli/sandbox/docker.py +293 -0
- vamp_cli/sandbox/manager.py +31 -0
- vamp_cli/sandbox/result.py +14 -0
- vamp_cli/sandbox/terminal.py +151 -0
- vamp_cli/tools/__init__.py +0 -0
- vamp_cli/tools/base.py +44 -0
- vamp_cli/tools/diagnostic.py +120 -0
- vamp_cli/tools/execute_cmd.py +16 -0
- vamp_cli/tools/files.py +652 -0
- vamp_cli/tools/registry.py +163 -0
- vamp_cli/tools/tools_classes.py +622 -0
- vamp_cli/utils/__init__.py +0 -0
- vamp_cli/utils/error_codes.py +7 -0
- vamp_cli/utils/ignore.py +36 -0
- vamp_cli/utils/sha_256.py +32 -0
- vamp_cli/utils/token_tracker.py +31 -0
- vamp_cli/workspace/__init__.py +0 -0
- vamp_cli/workspace/workspace.py +16 -0
- vamp_cli-0.1.0.dist-info/METADATA +317 -0
- vamp_cli-0.1.0.dist-info/RECORD +57 -0
- vamp_cli-0.1.0.dist-info/WHEEL +5 -0
- vamp_cli-0.1.0.dist-info/entry_points.txt +2 -0
- vamp_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
- vamp_cli-0.1.0.dist-info/top_level.txt +1 -0
vamp_cli/llm/router.py
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import threading
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Any, Sequence
|
|
7
|
+
|
|
8
|
+
from langchain_core.runnables import Runnable
|
|
9
|
+
# from langchain_litellm import ChatLiteLLM
|
|
10
|
+
|
|
11
|
+
from vamp_cli.config.loader import get_api_key
|
|
12
|
+
from vamp_cli.config.schema import AppConfig, ModelCapabilities, ModelConfig
|
|
13
|
+
from vamp_cli.llm.adapter_registry import AdapterRegistry
|
|
14
|
+
from vamp_cli.llm.usage import UsageTracker, UsageSnapshot, _UsageCallback
|
|
15
|
+
from vamp_cli.llm.debug_callbacks import DumpPayloadCallback
|
|
16
|
+
|
|
17
|
+
# log = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# Arguments that ChatLiteLLM accepts directly. Everything else in params is
|
|
21
|
+
# passed through as model_kwargs for LiteLLM/provider-specific parameters.
|
|
22
|
+
_DIRECT_PARAMS = {
|
|
23
|
+
"temperature",
|
|
24
|
+
"max_tokens",
|
|
25
|
+
"top_p",
|
|
26
|
+
"request_timeout",
|
|
27
|
+
"max_retries",
|
|
28
|
+
"streaming",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ModelRouter:
|
|
33
|
+
"""Builds, binds, composes, and caches configured chat model runnables.
|
|
34
|
+
|
|
35
|
+
Public flow:
|
|
36
|
+
|
|
37
|
+
router.get("main", tools=[...])
|
|
38
|
+
|
|
39
|
+
returns one runnable ready for the next layer. Internally the router:
|
|
40
|
+
|
|
41
|
+
1. builds/caches each raw ChatLiteLLM once;
|
|
42
|
+
2. binds the same tools to primary and fallback models;
|
|
43
|
+
3. composes the bound models with LangChain fallbacks;
|
|
44
|
+
4. attaches usage tracking;
|
|
45
|
+
5. caches the assembled runnable for the same model/toolset/context.
|
|
46
|
+
|
|
47
|
+
The provider remains hidden behind ChatLiteLLM/LiteLLM.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
def __init__(
|
|
51
|
+
self,
|
|
52
|
+
config: AppConfig,
|
|
53
|
+
adapters: AdapterRegistry | None = None,
|
|
54
|
+
) -> None:
|
|
55
|
+
self._config = config
|
|
56
|
+
self._adapters = adapters or AdapterRegistry()
|
|
57
|
+
self._model_cache: dict[str, ChatLiteLLM] = {} # type: ignore
|
|
58
|
+
self._runnable_cache: dict[tuple[str, tuple[str, ...], str | None], Runnable[Any, Any]] = {}
|
|
59
|
+
self._usage = UsageTracker()
|
|
60
|
+
self._lock = threading.RLock()
|
|
61
|
+
|
|
62
|
+
def get(
|
|
63
|
+
self,
|
|
64
|
+
model_name: str,
|
|
65
|
+
tools: Sequence[Any] | None = None,
|
|
66
|
+
*,
|
|
67
|
+
usage_context: str | None = None,
|
|
68
|
+
) -> Runnable[Any, Any]:
|
|
69
|
+
"""Return a configured, tool-bound, fallback-enabled runnable.
|
|
70
|
+
|
|
71
|
+
`tools` may be empty. When fallbacks exist, each fallback is bound to the
|
|
72
|
+
same tool set BEFORE the fallback wrapper is created.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
self._model_cfg(model_name) # fail fast on unknown model name
|
|
77
|
+
tool_list = tuple(tools or ())
|
|
78
|
+
tool_key = tuple(self._tool_key(tool) for tool in tool_list)
|
|
79
|
+
cache_key = (model_name, tool_key, usage_context)
|
|
80
|
+
|
|
81
|
+
with self._lock:
|
|
82
|
+
cached = self._runnable_cache.get(cache_key)
|
|
83
|
+
if cached is not None:
|
|
84
|
+
return cached
|
|
85
|
+
|
|
86
|
+
runnable = self._assemble(
|
|
87
|
+
model_name,
|
|
88
|
+
tool_list,
|
|
89
|
+
usage_context=usage_context,
|
|
90
|
+
stack=(),
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
self._runnable_cache[cache_key] = runnable
|
|
94
|
+
return runnable
|
|
95
|
+
|
|
96
|
+
def capabilities(self, model_name: str) -> ModelCapabilities:
|
|
97
|
+
return self._model_cfg(model_name).capabilities
|
|
98
|
+
|
|
99
|
+
def usage(self) -> dict[str, UsageSnapshot]:
|
|
100
|
+
return self._usage.snapshot()
|
|
101
|
+
|
|
102
|
+
def _assemble(
|
|
103
|
+
self,
|
|
104
|
+
model_name: str,
|
|
105
|
+
tools: Sequence[Any],
|
|
106
|
+
*,
|
|
107
|
+
usage_context: str | None,
|
|
108
|
+
stack: tuple[str, ...],
|
|
109
|
+
) -> Runnable[Any, Any]:
|
|
110
|
+
if model_name in stack:
|
|
111
|
+
cycle = " -> ".join((*stack, model_name))
|
|
112
|
+
raise RuntimeError(f"Fallback cycle detected while building: {cycle}")
|
|
113
|
+
|
|
114
|
+
cfg = self._model_cfg(model_name)
|
|
115
|
+
|
|
116
|
+
# 1. get raw ChatLiteLLM
|
|
117
|
+
primary = self._get_base_model(model_name)
|
|
118
|
+
|
|
119
|
+
# IMPORTANT: bind tools before wrapping in with_fallbacks().
|
|
120
|
+
|
|
121
|
+
# 2. Bind tools to primary
|
|
122
|
+
if tools:
|
|
123
|
+
if not cfg.capabilities.supports_tools:
|
|
124
|
+
raise ValueError(
|
|
125
|
+
f"Model '{model_name}' is configured with supports_tools=false "
|
|
126
|
+
"but tools were requested."
|
|
127
|
+
)
|
|
128
|
+
primary_bound = primary.bind_tools(list(tools))
|
|
129
|
+
else:
|
|
130
|
+
primary_bound = primary
|
|
131
|
+
|
|
132
|
+
# Attach usage tracking to each individual model branch. This is
|
|
133
|
+
# deliberately done BEFORE with_fallbacks(), so a fallback call is
|
|
134
|
+
# attributed to the fallback model rather than to the primary model.
|
|
135
|
+
|
|
136
|
+
# 3. Attach usage tracking to the primary
|
|
137
|
+
primary_runnable: Runnable[Any, Any] = primary_bound.with_config(
|
|
138
|
+
callbacks=[
|
|
139
|
+
_UsageCallback(self._usage, model_name, usage_context),
|
|
140
|
+
# DumpPayloadCallback(),
|
|
141
|
+
],
|
|
142
|
+
tags=[f"llm-model:{model_name}"]
|
|
143
|
+
+ ([f"usage:{usage_context}"] if usage_context else []),
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
# 4. Adapt this primary_runnable as per selected model quirks
|
|
147
|
+
adapter = self._adapters.get(cfg.adapter)
|
|
148
|
+
|
|
149
|
+
primary_runnable = adapter.adapt(
|
|
150
|
+
primary_runnable,
|
|
151
|
+
model_name=model_name,
|
|
152
|
+
config=cfg,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
# 5. Build fallbacks recursively
|
|
156
|
+
fallbacks: list[Runnable[Any, Any]] = []
|
|
157
|
+
for fallback_name in cfg.fallbacks:
|
|
158
|
+
fallback = self._assemble(
|
|
159
|
+
fallback_name,
|
|
160
|
+
tools,
|
|
161
|
+
usage_context=usage_context,
|
|
162
|
+
stack=(*stack, model_name),
|
|
163
|
+
)
|
|
164
|
+
fallbacks.append(fallback)
|
|
165
|
+
|
|
166
|
+
# 6. Compose the already-prepared branches
|
|
167
|
+
if fallbacks:
|
|
168
|
+
return primary_runnable.with_fallbacks(fallbacks)
|
|
169
|
+
|
|
170
|
+
return primary_runnable
|
|
171
|
+
|
|
172
|
+
def _get_base_model(self, model_name: str) -> ChatLiteLLM: # type: ignore
|
|
173
|
+
with self._lock:
|
|
174
|
+
cached = self._model_cache.get(model_name)
|
|
175
|
+
if cached is not None:
|
|
176
|
+
return cached
|
|
177
|
+
|
|
178
|
+
model = self._build(model_name)
|
|
179
|
+
self._model_cache[model_name] = model
|
|
180
|
+
return model
|
|
181
|
+
|
|
182
|
+
def _model_cfg(self, model_name: str) -> ModelConfig:
|
|
183
|
+
try:
|
|
184
|
+
return self._config.models[model_name]
|
|
185
|
+
except KeyError:
|
|
186
|
+
raise KeyError(
|
|
187
|
+
f"Unknown model '{model_name}'. Defined models: {sorted(self._config.models)}"
|
|
188
|
+
) from None
|
|
189
|
+
|
|
190
|
+
def _build(self, model_name: str) -> ChatLiteLLM: # type: ignore
|
|
191
|
+
|
|
192
|
+
from langchain_litellm import ChatLiteLLM
|
|
193
|
+
|
|
194
|
+
cfg = self._model_cfg(model_name)
|
|
195
|
+
|
|
196
|
+
direct = {k: v for k, v in cfg.params.items() if k in _DIRECT_PARAMS}
|
|
197
|
+
extra = {k: v for k, v in cfg.params.items() if k not in _DIRECT_PARAMS}
|
|
198
|
+
extra["stream_options"] = {"include_usage": True}
|
|
199
|
+
|
|
200
|
+
kwargs: dict[str, Any] = {
|
|
201
|
+
"model": cfg.model,
|
|
202
|
+
"streaming": True,
|
|
203
|
+
**direct,
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
api_key = get_api_key(cfg)
|
|
207
|
+
if api_key:
|
|
208
|
+
kwargs["api_key"] = api_key
|
|
209
|
+
|
|
210
|
+
if cfg.api_base:
|
|
211
|
+
kwargs["api_base"] = cfg.api_base
|
|
212
|
+
|
|
213
|
+
if extra:
|
|
214
|
+
kwargs["model_kwargs"] = extra
|
|
215
|
+
|
|
216
|
+
# log.info(
|
|
217
|
+
# "Building ChatLiteLLM '%s' -> %s",
|
|
218
|
+
# model_name,
|
|
219
|
+
# cfg.model,
|
|
220
|
+
# )
|
|
221
|
+
|
|
222
|
+
return ChatLiteLLM(**kwargs)
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
@staticmethod
|
|
226
|
+
def _tool_key(tool: Any) -> str:
|
|
227
|
+
# OpenAI-format dict
|
|
228
|
+
if isinstance(tool, dict):
|
|
229
|
+
fn = tool.get("function") or {}
|
|
230
|
+
name = fn.get("name") or tool.get("name")
|
|
231
|
+
if name:
|
|
232
|
+
return str(name)
|
|
233
|
+
return repr(tool)
|
|
234
|
+
|
|
235
|
+
# Tools object / BaseTool / callable
|
|
236
|
+
name = getattr(tool, "name", None)
|
|
237
|
+
if name:
|
|
238
|
+
return str(name)
|
|
239
|
+
|
|
240
|
+
qualname = getattr(tool, "__qualname__", None)
|
|
241
|
+
module = getattr(tool, "__module__", None)
|
|
242
|
+
if qualname:
|
|
243
|
+
return f"{module or ''}:{qualname}"
|
|
244
|
+
|
|
245
|
+
return repr(tool)
|
|
246
|
+
|
|
247
|
+
|
vamp_cli/llm/usage.py
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import threading
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from langchain_core.callbacks import BaseCallbackHandler
|
|
9
|
+
from langchain_core.outputs import LLMResult
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
log = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass(frozen=True)
|
|
16
|
+
class UsageSnapshot:
|
|
17
|
+
calls: int = 0
|
|
18
|
+
input_tokens: int = 0
|
|
19
|
+
output_tokens: int = 0
|
|
20
|
+
total_tokens: int = 0
|
|
21
|
+
cost_usd: float = 0.0
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class UsageTracker:
|
|
25
|
+
"""Thread-safe in-process usage accumulator.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self) -> None:
|
|
29
|
+
self._lock = threading.Lock()
|
|
30
|
+
self._totals: dict[str, UsageSnapshot] = {}
|
|
31
|
+
|
|
32
|
+
def record(
|
|
33
|
+
self,
|
|
34
|
+
model_name: str,
|
|
35
|
+
*,
|
|
36
|
+
input_tokens: int = 0,
|
|
37
|
+
output_tokens: int = 0,
|
|
38
|
+
total_tokens: int = 0,
|
|
39
|
+
cost_usd: float = 0.0,
|
|
40
|
+
) -> None:
|
|
41
|
+
with self._lock:
|
|
42
|
+
previous = self._totals.get(model_name, UsageSnapshot())
|
|
43
|
+
self._totals[model_name] = UsageSnapshot(
|
|
44
|
+
calls=previous.calls + 1,
|
|
45
|
+
input_tokens=previous.input_tokens + input_tokens,
|
|
46
|
+
output_tokens=previous.output_tokens + output_tokens,
|
|
47
|
+
total_tokens=previous.total_tokens + total_tokens,
|
|
48
|
+
cost_usd=previous.cost_usd + cost_usd,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
def snapshot(self) -> dict[str, UsageSnapshot]:
|
|
52
|
+
with self._lock:
|
|
53
|
+
return dict(self._totals)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class _UsageCallback(BaseCallbackHandler):
|
|
57
|
+
def __init__(
|
|
58
|
+
self,
|
|
59
|
+
tracker: UsageTracker,
|
|
60
|
+
model_name: str,
|
|
61
|
+
usage_context: str | None,
|
|
62
|
+
) -> None:
|
|
63
|
+
self._tracker = tracker
|
|
64
|
+
self._model_name = model_name
|
|
65
|
+
self._usage_context = usage_context
|
|
66
|
+
|
|
67
|
+
def on_llm_end(self, response: LLMResult, **_: Any) -> None:
|
|
68
|
+
input_tokens, output_tokens, total_tokens, cost = _extract_usage(response)
|
|
69
|
+
|
|
70
|
+
key = (
|
|
71
|
+
f"{self._usage_context}::{self._model_name}"
|
|
72
|
+
if self._usage_context
|
|
73
|
+
else self._model_name
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
self._tracker.record(
|
|
77
|
+
key,
|
|
78
|
+
input_tokens=input_tokens,
|
|
79
|
+
output_tokens=output_tokens,
|
|
80
|
+
total_tokens=total_tokens,
|
|
81
|
+
cost_usd=cost,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _extract_usage(response: LLMResult) -> tuple[int, int, int, float]:
|
|
86
|
+
"""Best-effort extraction across common LangChain/LiteLLM response shapes."""
|
|
87
|
+
|
|
88
|
+
input_tokens = 0
|
|
89
|
+
output_tokens = 0
|
|
90
|
+
total_tokens = 0
|
|
91
|
+
cost = 0.0
|
|
92
|
+
|
|
93
|
+
llm_output = response.llm_output or {}
|
|
94
|
+
token_usage = (
|
|
95
|
+
llm_output.get("token_usage")
|
|
96
|
+
or llm_output.get("usage")
|
|
97
|
+
or llm_output.get("usage_metadata")
|
|
98
|
+
or {}
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
input_tokens = _as_int(
|
|
102
|
+
token_usage.get("input_tokens")
|
|
103
|
+
or token_usage.get("prompt_tokens")
|
|
104
|
+
or token_usage.get("inputTokenCount")
|
|
105
|
+
)
|
|
106
|
+
output_tokens = _as_int(
|
|
107
|
+
token_usage.get("output_tokens")
|
|
108
|
+
or token_usage.get("completion_tokens")
|
|
109
|
+
or token_usage.get("outputTokenCount")
|
|
110
|
+
)
|
|
111
|
+
total_tokens = _as_int(token_usage.get("total_tokens"))
|
|
112
|
+
cost = _as_float(
|
|
113
|
+
llm_output.get("response_cost")
|
|
114
|
+
or llm_output.get("cost")
|
|
115
|
+
or llm_output.get("total_cost")
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
# Some LangChain integrations place usage on the returned AIMessage.
|
|
119
|
+
for generation_group in response.generations:
|
|
120
|
+
for generation in generation_group:
|
|
121
|
+
message = getattr(generation, "message", None)
|
|
122
|
+
if message is None:
|
|
123
|
+
continue
|
|
124
|
+
|
|
125
|
+
usage = getattr(message, "usage_metadata", None) or {}
|
|
126
|
+
if usage:
|
|
127
|
+
input_tokens = input_tokens or _as_int(usage.get("input_tokens"))
|
|
128
|
+
output_tokens = output_tokens or _as_int(usage.get("output_tokens"))
|
|
129
|
+
total_tokens = total_tokens or _as_int(usage.get("total_tokens"))
|
|
130
|
+
|
|
131
|
+
metadata = getattr(message, "response_metadata", None) or {}
|
|
132
|
+
nested_usage = (
|
|
133
|
+
metadata.get("token_usage")
|
|
134
|
+
or metadata.get("usage")
|
|
135
|
+
or metadata.get("usage_metadata")
|
|
136
|
+
or {}
|
|
137
|
+
)
|
|
138
|
+
input_tokens = input_tokens or _as_int(
|
|
139
|
+
nested_usage.get("input_tokens")
|
|
140
|
+
or nested_usage.get("prompt_tokens")
|
|
141
|
+
)
|
|
142
|
+
output_tokens = output_tokens or _as_int(
|
|
143
|
+
nested_usage.get("output_tokens")
|
|
144
|
+
or nested_usage.get("completion_tokens")
|
|
145
|
+
)
|
|
146
|
+
total_tokens = total_tokens or _as_int(nested_usage.get("total_tokens"))
|
|
147
|
+
cost = cost or _as_float(
|
|
148
|
+
metadata.get("response_cost")
|
|
149
|
+
or metadata.get("cost")
|
|
150
|
+
or metadata.get("total_cost")
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
if total_tokens == 0:
|
|
154
|
+
total_tokens = input_tokens + output_tokens
|
|
155
|
+
|
|
156
|
+
return input_tokens, output_tokens, total_tokens, cost
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _as_int(value: Any) -> int:
|
|
160
|
+
try:
|
|
161
|
+
return int(value or 0)
|
|
162
|
+
except (TypeError, ValueError):
|
|
163
|
+
return 0
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _as_float(value: Any) -> float:
|
|
167
|
+
try:
|
|
168
|
+
return float(value or 0.0)
|
|
169
|
+
except (TypeError, ValueError):
|
|
170
|
+
return 0.0
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import logging, sys
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
TRACE = 5
|
|
5
|
+
|
|
6
|
+
def setup(level=logging.INFO, log_dir: str | None = None):
|
|
7
|
+
root = logging.getLogger()
|
|
8
|
+
for h in root.handlers[:]:
|
|
9
|
+
root.removeHandler(h)
|
|
10
|
+
root.setLevel(level)
|
|
11
|
+
|
|
12
|
+
fmt = logging.Formatter("%(asctime)s %(levelname)-7s %(name)s: %(message)s")
|
|
13
|
+
|
|
14
|
+
sh = logging.StreamHandler(sys.stderr)
|
|
15
|
+
sh.setFormatter(fmt)
|
|
16
|
+
root.addHandler(sh)
|
|
17
|
+
|
|
18
|
+
if not log_dir:
|
|
19
|
+
return
|
|
20
|
+
|
|
21
|
+
p = Path(log_dir)
|
|
22
|
+
p.mkdir(parents=True, exist_ok=True)
|
|
23
|
+
|
|
24
|
+
fh = logging.FileHandler(p / "vamp.log", mode="a")
|
|
25
|
+
fh.setFormatter(fmt)
|
|
26
|
+
root.addHandler(fh)
|
|
27
|
+
|
|
28
|
+
# if level <= logging.DEBUG:
|
|
29
|
+
# wire = logging.FileHandler(p / "wire.log", mode="w")
|
|
30
|
+
# wire.setLevel(TRACE)
|
|
31
|
+
# wire.setFormatter(fmt)
|
|
32
|
+
# for n in ("httpx", "httpcore", "litellm", "LiteLLM"):
|
|
33
|
+
# lg = logging.getLogger(n)
|
|
34
|
+
# lg.handlers.clear()
|
|
35
|
+
# lg.setLevel(TRACE)
|
|
36
|
+
# lg.addHandler(wire)
|
|
37
|
+
# lg.propagate = False
|