ht32-code-mcp 0.1.3__tar.gz → 0.2.0__tar.gz
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.
- {ht32_code_mcp-0.1.3 → ht32_code_mcp-0.2.0}/DEPLOY.md +3 -3
- {ht32_code_mcp-0.1.3 → ht32_code_mcp-0.2.0}/PKG-INFO +1 -1
- {ht32_code_mcp-0.1.3 → ht32_code_mcp-0.2.0}/pyproject.toml +1 -1
- {ht32_code_mcp-0.1.3 → ht32_code_mcp-0.2.0}/src/ht32_code_mcp/server.py +14 -10
- {ht32_code_mcp-0.1.3 → ht32_code_mcp-0.2.0}/src/ht32_code_mcp/usage.py +56 -15
- {ht32_code_mcp-0.1.3 → ht32_code_mcp-0.2.0}/tests/test_tools.py +32 -10
- {ht32_code_mcp-0.1.3 → ht32_code_mcp-0.2.0}/uv.lock +1 -1
- {ht32_code_mcp-0.1.3 → ht32_code_mcp-0.2.0}/.gitignore +0 -0
- {ht32_code_mcp-0.1.3 → ht32_code_mcp-0.2.0}/.gitkeep +0 -0
- {ht32_code_mcp-0.1.3 → ht32_code_mcp-0.2.0}/README.md +0 -0
- {ht32_code_mcp-0.1.3 → ht32_code_mcp-0.2.0}/e2e_scenario_a.py +0 -0
- {ht32_code_mcp-0.1.3 → ht32_code_mcp-0.2.0}/e2e_scenario_b.py +0 -0
- {ht32_code_mcp-0.1.3 → ht32_code_mcp-0.2.0}/ht32-code-mcp-update.md +0 -0
- {ht32_code_mcp-0.1.3 → ht32_code_mcp-0.2.0}/smoke_stdio.py +0 -0
- {ht32_code_mcp-0.1.3 → ht32_code_mcp-0.2.0}/src/ht32_code_mcp/__init__.py +0 -0
- {ht32_code_mcp-0.1.3 → ht32_code_mcp-0.2.0}/src/ht32_code_mcp/__main__.py +0 -0
- {ht32_code_mcp-0.1.3 → ht32_code_mcp-0.2.0}/src/ht32_code_mcp/exporter.py +0 -0
- {ht32_code_mcp-0.1.3 → ht32_code_mcp-0.2.0}/src/ht32_code_mcp/index.py +0 -0
- {ht32_code_mcp-0.1.3 → ht32_code_mcp-0.2.0}/src/ht32_code_mcp/knowledge.py +0 -0
- {ht32_code_mcp-0.1.3 → ht32_code_mcp-0.2.0}/tests/conftest.py +0 -0
|
@@ -290,9 +290,9 @@ uv lock && uv build && uv publish
|
|
|
290
290
|
|
|
291
291
|
| # | 任务 | 说明 | 状态 |
|
|
292
292
|
|---|------|------|------|
|
|
293
|
-
| 3.1 | 7
|
|
294
|
-
| 3.2 | 工具级测试 | `pytest`
|
|
295
|
-
| 3.3 | PyPI 发布(hatchling) | `ht32-code-mcp` 0.1.
|
|
293
|
+
| 3.1 | MCP 工具实现(7 检索/导出 + 1 反馈) | `.work/mcp-server/src/ht32_code_mcp/`(FastMCP) | ✅ |
|
|
294
|
+
| 3.2 | 工具级测试 | `pytest` 42 用例全绿 | ✅ |
|
|
295
|
+
| 3.3 | PyPI 发布(hatchling) | `ht32-code-mcp` 0.1.3(2026-09-21,含 bundle 通道 + 反馈/用量日志) | ✅ |
|
|
296
296
|
| 3.4 | 云端 git clone + 挂载 | `/opt/ht32-code:/app/code` | ✅ |
|
|
297
297
|
| 3.5 | 1Panel 部署(uvx + supergateway → streamableHttp stateful,镜像 next-uvx) | `https://mcp.s621.work/ht32-code` | ✅ |
|
|
298
298
|
| 3.6 | OpenResty 超时修复(`proxy_read_timeout 3600s`) | 修复 60s 空闲断连;2026-09-21 复核两端空闲 100s 后调用仍 200,未被 1Panel 回退 | ✅ |
|
|
@@ -21,7 +21,7 @@ from .exporter import (
|
|
|
21
21
|
)
|
|
22
22
|
from .index import CodeIndex, normalize_series
|
|
23
23
|
from .knowledge import KnowledgeSearch
|
|
24
|
-
from .usage import UsageMiddleware, record_feedback
|
|
24
|
+
from .usage import SERVICE_CODE, UsageMiddleware, record_feedback, resolve_log_dir
|
|
25
25
|
|
|
26
26
|
# 知识库根目录(ht32-code 仓库根;src/ht32_code_mcp → 上溯 4 级)
|
|
27
27
|
DEFAULT_CODE_ROOT = Path(__file__).resolve().parents[4]
|
|
@@ -43,9 +43,11 @@ BUNDLE_BASE_URL = os.environ.get("BUNDLE_BASE_URL", "").strip()
|
|
|
43
43
|
BUNDLE_TTL = _env_int("BUNDLE_TTL_SECONDS", DEFAULT_BUNDLE_TTL)
|
|
44
44
|
CLOUD_BUNDLE = bool(BUNDLE_DIR and BUNDLE_BASE_URL)
|
|
45
45
|
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
|
|
46
|
+
# 反馈与用量日志:FEEDBACK_ROOT(宿主机挂载的可写卷,与 wiki 同一套约定)
|
|
47
|
+
# 下按服务名分子目录,本服务写 <FEEDBACK_ROOT>/code/。
|
|
48
|
+
# 为空则整体关闭——本地开发默认不落盘。注意:日志含 query 原文,勿提交进仓库。
|
|
49
|
+
FEEDBACK_ROOT = os.environ.get("FEEDBACK_ROOT", "").strip()
|
|
50
|
+
LOG_DIR = resolve_log_dir(FEEDBACK_ROOT, SERVICE_CODE)
|
|
49
51
|
|
|
50
52
|
index = CodeIndex(CODE_ROOT).load()
|
|
51
53
|
knowledge = KnowledgeSearch(CODE_ROOT)
|
|
@@ -83,10 +85,11 @@ _INSTRUCTIONS = """HT32 代码知识库(函数库/例程/工程模板),当
|
|
|
83
85
|
|
|
84
86
|
mcp = FastMCP("ht32-code", instructions=_INSTRUCTIONS)
|
|
85
87
|
|
|
86
|
-
# 用量日志:服务端自动记录所有工具调用(
|
|
88
|
+
# 用量日志:服务端自动记录所有工具调用(FEEDBACK_ROOT 为空则不启用)
|
|
87
89
|
if LOG_DIR:
|
|
88
90
|
mcp.add_middleware(UsageMiddleware(
|
|
89
|
-
LOG_DIR,
|
|
91
|
+
LOG_DIR, SERVICE_CODE,
|
|
92
|
+
mcp_version=_MCP_VERSION, kb_version=index.data.get("code_version", "")))
|
|
90
93
|
|
|
91
94
|
|
|
92
95
|
# ================================================================ MCP Tools
|
|
@@ -118,8 +121,8 @@ def get_code_version() -> str:
|
|
|
118
121
|
("云端 zip 下载:启用(TTL "
|
|
119
122
|
f"{BUNDLE_TTL}s,{BUNDLE_BASE_URL})" if CLOUD_BUNDLE
|
|
120
123
|
else "云端 zip 下载:未配置(本地目录模式,export 需提供 out_dir)"),
|
|
121
|
-
(f"
|
|
122
|
-
else "
|
|
124
|
+
(f"反馈与用量日志:启用({LOG_DIR})" if LOG_DIR
|
|
125
|
+
else "反馈与用量日志:未配置(FEEDBACK_ROOT 为空,不落盘)"),
|
|
123
126
|
"",
|
|
124
127
|
"VERSION 文件内容:",
|
|
125
128
|
version_text,
|
|
@@ -403,6 +406,7 @@ def submit_feedback(
|
|
|
403
406
|
|
|
404
407
|
path = record_feedback(
|
|
405
408
|
LOG_DIR,
|
|
409
|
+
SERVICE_CODE,
|
|
406
410
|
question=question,
|
|
407
411
|
issue_type=issue_type,
|
|
408
412
|
answer=answer,
|
|
@@ -411,10 +415,10 @@ def submit_feedback(
|
|
|
411
415
|
model_id=model_id,
|
|
412
416
|
tools_called=tools_called,
|
|
413
417
|
mcp_version=_MCP_VERSION,
|
|
414
|
-
|
|
418
|
+
kb_version=index.data.get("code_version", ""),
|
|
415
419
|
)
|
|
416
420
|
if path is None:
|
|
417
|
-
return ("错误:反馈未能写入。服务端可能未配置
|
|
421
|
+
return ("错误:反馈未能写入。服务端可能未配置 FEEDBACK_ROOT,"
|
|
418
422
|
"或该目录不可写——请联系管理员。")
|
|
419
423
|
return f"反馈已记录,感谢!问题类型:{issue_type},文件:{path.name}"
|
|
420
424
|
|
|
@@ -1,16 +1,31 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""反馈与用量日志:统一实现(ht32-wiki-mcp / ht32-code-mcp 共用同一份代码)。
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> **本文件在两个仓库存有逐字节相同的副本**(`ht32-wiki` 与 `ht32-code` 各一份)。
|
|
4
|
+
> 修改时**必须同步另一仓库的同名文件**,并同步更新下方的 `_IMPL_VERSION`;
|
|
5
|
+
> 两仓各有一条测试校验该版本号与目录布局,防止只改一边造成漂移。
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
两条采集通道,写入同一目录树:
|
|
8
|
+
|
|
9
|
+
- **tool_call**(`UsageMiddleware`,服务端自动):拦截所有工具调用,
|
|
6
10
|
记录工具名、参数原文、耗时、成败。不依赖 Agent 配合,数据完整。
|
|
7
|
-
- **feedback
|
|
8
|
-
|
|
11
|
+
- **feedback**(`record_feedback`,由各 server 的 `submit_feedback` 工具调用):
|
|
12
|
+
记录 Agent 主动上报的语义性问题,middleware 无法判断这些。
|
|
13
|
+
|
|
14
|
+
目录布局(同根 + 服务子目录):
|
|
9
15
|
|
|
10
|
-
|
|
16
|
+
<FEEDBACK_ROOT>/
|
|
17
|
+
├─ wiki/
|
|
18
|
+
│ ├─ usage_YYYY-MM-DD.jsonl
|
|
19
|
+
│ └─ feedback_YYYY-MM-DD.jsonl
|
|
20
|
+
└─ code/
|
|
21
|
+
├─ usage_YYYY-MM-DD.jsonl
|
|
22
|
+
└─ feedback_YYYY-MM-DD.jsonl
|
|
11
23
|
|
|
12
|
-
|
|
13
|
-
|
|
24
|
+
两个服务挂同一个宿主机目录,各写自己的子目录——清理、备份、查看都是
|
|
25
|
+
单一入口。每条记录另带 `service` 字段作双保险。
|
|
26
|
+
|
|
27
|
+
隐私:参数按原文记录(`query` / `question` 可能含用户项目信息),目录只应
|
|
28
|
+
挂在受控服务器上,不要提交进仓库。
|
|
14
29
|
"""
|
|
15
30
|
from __future__ import annotations
|
|
16
31
|
|
|
@@ -22,8 +37,15 @@ from typing import Any
|
|
|
22
37
|
|
|
23
38
|
from fastmcp.server.middleware import Middleware, MiddlewareContext
|
|
24
39
|
|
|
25
|
-
#
|
|
26
|
-
|
|
40
|
+
# 实现版本号:两仓库副本必须一致(各仓测试会校验,防止只改一边造成漂移)
|
|
41
|
+
_IMPL_VERSION = "1.0.0"
|
|
42
|
+
|
|
43
|
+
# 服务名 → 子目录名。新增服务在此登记。
|
|
44
|
+
SERVICE_WIKI = "wiki"
|
|
45
|
+
SERVICE_CODE = "code"
|
|
46
|
+
|
|
47
|
+
# 不记录的工具参数:分页/长度控制项无分析价值;out_dir 可能含用户本地路径
|
|
48
|
+
ARG_NOISE = frozenset({"limit", "max_length", "out_dir"})
|
|
27
49
|
|
|
28
50
|
USAGE_PREFIX = "usage"
|
|
29
51
|
FEEDBACK_PREFIX = "feedback"
|
|
@@ -33,6 +55,20 @@ def _now() -> str:
|
|
|
33
55
|
return datetime.now(timezone.utc).isoformat()
|
|
34
56
|
|
|
35
57
|
|
|
58
|
+
def resolve_log_dir(feedback_root: str, service: str) -> str:
|
|
59
|
+
"""由反馈根目录 + 服务名算出该服务的日志目录。
|
|
60
|
+
|
|
61
|
+
feedback_root 为空时返回空字符串 = 关闭(本地开发默认不落盘)。
|
|
62
|
+
始终用 '/' 拼接(Windows 亦接受正斜杠),避免同一条配置在不同平台
|
|
63
|
+
产生不同字符串、影响日志路径的可比对性。
|
|
64
|
+
"""
|
|
65
|
+
root = (feedback_root or "").strip().rstrip("/")
|
|
66
|
+
svc = (service or "").strip().strip("/")
|
|
67
|
+
if not root:
|
|
68
|
+
return ""
|
|
69
|
+
return f"{root}/{svc}" if svc else root
|
|
70
|
+
|
|
71
|
+
|
|
36
72
|
def write_record(log_dir: str, prefix: str, record: dict[str, Any]) -> Path | None:
|
|
37
73
|
"""追加一条 JSONL 记录。log_dir 为空或写盘失败时返回 None(绝不因日志中断服务)。"""
|
|
38
74
|
if not log_dir:
|
|
@@ -67,10 +103,12 @@ def _client_info(context: MiddlewareContext) -> dict[str, str]:
|
|
|
67
103
|
class UsageMiddleware(Middleware):
|
|
68
104
|
"""把所有工具调用落成 JSONL(服务端自动采集)。"""
|
|
69
105
|
|
|
70
|
-
def __init__(self, log_dir: str,
|
|
106
|
+
def __init__(self, log_dir: str, service: str,
|
|
107
|
+
mcp_version: str = "", kb_version: str = "") -> None:
|
|
71
108
|
self.log_dir = log_dir
|
|
109
|
+
self.service = service
|
|
72
110
|
self.mcp_version = mcp_version
|
|
73
|
-
self.
|
|
111
|
+
self.kb_version = kb_version
|
|
74
112
|
|
|
75
113
|
async def on_call_tool(self, context: MiddlewareContext, call_next):
|
|
76
114
|
if not self.log_dir:
|
|
@@ -95,13 +133,14 @@ class UsageMiddleware(Middleware):
|
|
|
95
133
|
record = {
|
|
96
134
|
"timestamp": _now(),
|
|
97
135
|
"kind": "tool_call",
|
|
136
|
+
"service": self.service,
|
|
98
137
|
"tool": context.message.name,
|
|
99
138
|
"args": recorded,
|
|
100
139
|
"ok": ok,
|
|
101
140
|
"duration_ms": round((time.perf_counter() - started) * 1000),
|
|
102
141
|
"error": error,
|
|
103
142
|
"mcp_version": self.mcp_version,
|
|
104
|
-
"
|
|
143
|
+
"kb_version": self.kb_version,
|
|
105
144
|
**_client_info(context),
|
|
106
145
|
}
|
|
107
146
|
write_record(self.log_dir, USAGE_PREFIX, record)
|
|
@@ -109,6 +148,7 @@ class UsageMiddleware(Middleware):
|
|
|
109
148
|
|
|
110
149
|
def record_feedback(
|
|
111
150
|
log_dir: str,
|
|
151
|
+
service: str,
|
|
112
152
|
*,
|
|
113
153
|
question: str,
|
|
114
154
|
issue_type: str,
|
|
@@ -118,12 +158,13 @@ def record_feedback(
|
|
|
118
158
|
model_id: str = "",
|
|
119
159
|
tools_called: list[str] | None = None,
|
|
120
160
|
mcp_version: str = "",
|
|
121
|
-
|
|
161
|
+
kb_version: str = "",
|
|
122
162
|
) -> Path | None:
|
|
123
163
|
"""记录一条 Agent 主动提交的反馈。"""
|
|
124
164
|
record = {
|
|
125
165
|
"timestamp": _now(),
|
|
126
166
|
"kind": "feedback",
|
|
167
|
+
"service": service,
|
|
127
168
|
"question": question.strip(),
|
|
128
169
|
"issue_type": issue_type,
|
|
129
170
|
"answer": answer.strip(),
|
|
@@ -133,6 +174,6 @@ def record_feedback(
|
|
|
133
174
|
# 过滤 submit_feedback 自身,只保留查询类工具
|
|
134
175
|
"tools_called": [t for t in (tools_called or []) if t and t != "submit_feedback"],
|
|
135
176
|
"mcp_version": mcp_version,
|
|
136
|
-
"
|
|
177
|
+
"kb_version": kb_version,
|
|
137
178
|
}
|
|
138
179
|
return write_record(log_dir, FEEDBACK_PREFIX, record)
|
|
@@ -304,6 +304,25 @@ def test_version_reports_bundle_mode(monkeypatch):
|
|
|
304
304
|
|
|
305
305
|
|
|
306
306
|
# ---------------------------------------------------------------- 反馈与用量日志
|
|
307
|
+
def test_usage_impl_version_pinned():
|
|
308
|
+
"""两仓库的 usage.py 是逐字节相同副本,实现版本号用于防止只改一边。"""
|
|
309
|
+
from ht32_code_mcp import usage
|
|
310
|
+
assert usage._IMPL_VERSION == "1.0.0"
|
|
311
|
+
assert usage.SERVICE_CODE == "code"
|
|
312
|
+
assert usage.SERVICE_WIKI == "wiki"
|
|
313
|
+
# 噪声参数含 out_dir(code 特有:可能含用户本地路径)
|
|
314
|
+
assert {"limit", "max_length", "out_dir"} <= usage.ARG_NOISE
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def test_resolve_log_dir_layout():
|
|
318
|
+
from ht32_code_mcp.usage import resolve_log_dir
|
|
319
|
+
# 同根 + 服务子目录
|
|
320
|
+
assert resolve_log_dir("/app/feedback", "code") == "/app/feedback/code"
|
|
321
|
+
assert resolve_log_dir("/app/feedback/", "code") == "/app/feedback/code"
|
|
322
|
+
# 根未配置 = 关闭
|
|
323
|
+
assert resolve_log_dir("", "code") == ""
|
|
324
|
+
|
|
325
|
+
|
|
307
326
|
def test_submit_feedback_writes_jsonl(tmp_path, monkeypatch):
|
|
308
327
|
monkeypatch.setattr(srv, "LOG_DIR", str(tmp_path / "logs"))
|
|
309
328
|
out = srv.submit_feedback(
|
|
@@ -319,12 +338,13 @@ def test_submit_feedback_writes_jsonl(tmp_path, monkeypatch):
|
|
|
319
338
|
f = next((tmp_path / "logs").glob("feedback_*.jsonl"))
|
|
320
339
|
rec = json.loads(f.read_text(encoding="utf-8").splitlines()[0])
|
|
321
340
|
assert rec["kind"] == "feedback"
|
|
341
|
+
assert rec["service"] == "code"
|
|
322
342
|
assert rec["question"] == "HT32F52352 怎么配 GPIO 输出?"
|
|
323
343
|
assert rec["issue_type"] == "wrong_api"
|
|
324
344
|
assert rec["model_id"] == "deepseek-v4-flash"
|
|
325
345
|
# submit_feedback 自身被过滤,只留查询类工具
|
|
326
346
|
assert rec["tools_called"] == ["search_code_knowledge", "get_file"]
|
|
327
|
-
assert rec["mcp_version"] and rec["
|
|
347
|
+
assert rec["mcp_version"] and rec["kb_version"]
|
|
328
348
|
|
|
329
349
|
|
|
330
350
|
def test_submit_feedback_empty_question(tmp_path, monkeypatch):
|
|
@@ -336,14 +356,14 @@ def test_submit_feedback_empty_question(tmp_path, monkeypatch):
|
|
|
336
356
|
def test_submit_feedback_without_log_dir(monkeypatch):
|
|
337
357
|
monkeypatch.setattr(srv, "LOG_DIR", "")
|
|
338
358
|
out = srv.submit_feedback(question="x")
|
|
339
|
-
assert "错误" in out and "
|
|
359
|
+
assert "错误" in out and "FEEDBACK_ROOT" in out
|
|
340
360
|
|
|
341
361
|
|
|
342
362
|
def test_version_reports_log_mode(monkeypatch):
|
|
343
363
|
monkeypatch.setattr(srv, "LOG_DIR", "")
|
|
344
|
-
assert "
|
|
345
|
-
monkeypatch.setattr(srv, "LOG_DIR", "/app/
|
|
346
|
-
assert "
|
|
364
|
+
assert "反馈与用量日志:未配置" in srv.get_code_version()
|
|
365
|
+
monkeypatch.setattr(srv, "LOG_DIR", "/app/feedback/code")
|
|
366
|
+
assert "反馈与用量日志:启用(/app/feedback/code)" in srv.get_code_version()
|
|
347
367
|
|
|
348
368
|
|
|
349
369
|
def test_usage_middleware_records_calls(tmp_path):
|
|
@@ -353,8 +373,8 @@ def test_usage_middleware_records_calls(tmp_path):
|
|
|
353
373
|
from fastmcp import Client, FastMCP
|
|
354
374
|
|
|
355
375
|
app = FastMCP("mw-test")
|
|
356
|
-
app.add_middleware(UsageMiddleware(str(tmp_path / "logs"),
|
|
357
|
-
mcp_version="0.1.
|
|
376
|
+
app.add_middleware(UsageMiddleware(str(tmp_path / "logs"), "code",
|
|
377
|
+
mcp_version="0.1.4", kb_version="v0.1.0"))
|
|
358
378
|
|
|
359
379
|
@app.tool()
|
|
360
380
|
def probe(chip: str, limit: int = 5, out_dir: str = "") -> str:
|
|
@@ -378,11 +398,13 @@ def test_usage_middleware_records_calls(tmp_path):
|
|
|
378
398
|
assert len(recs) == 2
|
|
379
399
|
ok_rec, fail_rec = recs
|
|
380
400
|
assert ok_rec["kind"] == "tool_call" and ok_rec["tool"] == "probe"
|
|
401
|
+
assert ok_rec["service"] == "code"
|
|
381
402
|
# limit / out_dir 属噪声被剔除,业务参数保留
|
|
382
403
|
assert ok_rec["args"] == {"chip": "HT32F52352"}
|
|
383
404
|
assert ok_rec["ok"] is True and ok_rec["error"] == ""
|
|
384
405
|
assert ok_rec["duration_ms"] >= 0
|
|
385
|
-
assert ok_rec["mcp_version"] == "0.1.
|
|
406
|
+
assert ok_rec["mcp_version"] == "0.1.4"
|
|
407
|
+
assert ok_rec["kb_version"] == "v0.1.0"
|
|
386
408
|
assert fail_rec["tool"] == "boom" and fail_rec["ok"] is False
|
|
387
409
|
assert "deliberate" in fail_rec["error"]
|
|
388
410
|
|
|
@@ -393,7 +415,7 @@ def test_usage_log_disabled_and_resilient(tmp_path):
|
|
|
393
415
|
|
|
394
416
|
from fastmcp import Client, FastMCP
|
|
395
417
|
|
|
396
|
-
from ht32_code_mcp.usage import
|
|
418
|
+
from ht32_code_mcp.usage import write_record
|
|
397
419
|
|
|
398
420
|
assert write_record("", "usage", {"x": 1}) is None
|
|
399
421
|
|
|
@@ -401,7 +423,7 @@ def test_usage_log_disabled_and_resilient(tmp_path):
|
|
|
401
423
|
blocker = tmp_path / "blocker"
|
|
402
424
|
blocker.write_text("i am a file", encoding="utf-8")
|
|
403
425
|
app = FastMCP("mw-bad")
|
|
404
|
-
app.add_middleware(UsageMiddleware(str(blocker / "logs")))
|
|
426
|
+
app.add_middleware(UsageMiddleware(str(blocker / "logs"), "code"))
|
|
405
427
|
|
|
406
428
|
@app.tool()
|
|
407
429
|
def probe() -> str:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|