diff --git a/app/tools/executor.py b/app/tools/executor.py index c7c1ca9..0b22b0f 100644 --- a/app/tools/executor.py +++ b/app/tools/executor.py @@ -32,6 +32,9 @@ _SESSION_MAX_COOKIES = 50 _SESSION_MAX_HEADERS = 30 +# HTTP 响应体读取硬上限(字节):防异常超大响应塞爆内存;超限截断响应并提示。 +# _read_limited_response 曾引用此名却无定义,会走到截断分支时抛 NameError。 +_HTTP_MAX_BYTES = int(os.environ.get("WORKER_HTTP_MAX_BYTES", str(1024 * 1024))) # 单目标工作目录落地日志体积上限(字节)。24x7 防撞盘:超限后停止写新日志文件, # 仍把截断输出回传给 LLM,不影响挖掘,只是不再落地完整证据。 _WORKDIR_MAX_BYTES = int(os.environ.get("WORKER_WORKDIR_MAX_BYTES", str(50 * 1024 * 1024))) diff --git a/tests/test_executor_http_limit.py b/tests/test_executor_http_limit.py new file mode 100644 index 0000000..fcca8bb --- /dev/null +++ b/tests/test_executor_http_limit.py @@ -0,0 +1,33 @@ +"""覆盖 _HTTP_MAX_BYTES 常量及响应截断逻辑。 + +历史背景:_read_limited_response 曾引用 _HTTP_MAX_BYTES 但全库无定义,走到截断分支会抛 NameError。 +本用例断言该常量已定义,并验证大响应被截断、小响应原样放行。 +""" +import unittest + +import httpx + +from app.tools.executor import ToolExecutor, _HTTP_MAX_BYTES + + +class TestHttpResponseLimit(unittest.TestCase): + def test_limit_constant_defined_and_positive(self): + self.assertIsInstance(_HTTP_MAX_BYTES, int) + self.assertGreater(_HTTP_MAX_BYTES, 0) + + def test_large_response_truncated(self): + big = b"x" * (_HTTP_MAX_BYTES * 2) + resp = httpx.Response(200, content=big, headers={"Content-Type": "text/plain"}) + body, truncated = ToolExecutor._read_limited_response(resp) + self.assertTrue(truncated) + self.assertLessEqual(len(body), _HTTP_MAX_BYTES + 200) + + def test_small_response_passes_through(self): + resp = httpx.Response(200, content=b"hello", headers={"Content-Type": "text/plain"}) + body, truncated = ToolExecutor._read_limited_response(resp) + self.assertFalse(truncated) + self.assertIn("hello", body) + + +if __name__ == "__main__": + unittest.main() \ No newline at end of file