mirror of
https://github.com/lllllllama/RigorPilot-Skills.git
synced 2026-09-14 13:43:27 +08:00
134 lines
6.5 KiB
Python
134 lines
6.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Exercise real HTTP serialization and fail-closed provider error handling locally."""
|
|
import json
|
|
import http.client
|
|
import os
|
|
import socket
|
|
import ssl
|
|
import sys
|
|
import threading
|
|
import urllib.error
|
|
from unittest import mock
|
|
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "shared/scripts"))
|
|
from agent_provider import AnthropicProvider, ProviderError
|
|
|
|
|
|
def main():
|
|
requests = []
|
|
mode = ["ok"]
|
|
class Handler(BaseHTTPRequestHandler):
|
|
def log_message(self, *_):
|
|
pass
|
|
|
|
def do_POST(self):
|
|
requests.append(json.loads(self.rfile.read(int(self.headers["content-length"]))))
|
|
assert self.headers["x-api-key"] == "test-credential"
|
|
if mode[0] == "redirect":
|
|
self.send_response(302)
|
|
self.send_header("Location", "http://127.0.0.1:1/not-allowed")
|
|
self.end_headers()
|
|
return
|
|
self.send_response(200 if mode[0] in {"ok", "non_object", "invalid_json"} else 502 if mode[0] == "gateway" else 401)
|
|
if mode[0] == "invalid_json":
|
|
self.end_headers()
|
|
self.wfile.write(b"test-credential-not-json")
|
|
return
|
|
if mode[0] == "non_object":
|
|
# A gateway can return valid JSON of the wrong shape with HTTP 200.
|
|
self.end_headers()
|
|
self.wfile.write(b"[]")
|
|
return
|
|
self.end_headers()
|
|
self.wfile.write(json.dumps({"content": [{"type": "tool_use", "id": "one", "name": "list_files", "input": {"reason": "inspect"}}],
|
|
"usage": {"input_tokens": 8, "output_tokens": 9}, "private_echo": "test-credential"}).encode())
|
|
server = ThreadingHTTPServer(("127.0.0.1", 0), Handler)
|
|
thread = threading.Thread(target=server.serve_forever, daemon=True)
|
|
thread.start()
|
|
previous = os.environ.get("RIGORPILOT_PROVIDER_TEST_KEY")
|
|
os.environ["RIGORPILOT_PROVIDER_TEST_KEY"] = "test-credential"
|
|
try:
|
|
profile = {"model": "local-test", "credential_env": "RIGORPILOT_PROVIDER_TEST_KEY",
|
|
"endpoint": f"http://127.0.0.1:{server.server_port}",
|
|
"parameters": {"temperature": 1.0, "stop_sequences": ["STOP"]}}
|
|
client = AnthropicProvider(profile)
|
|
response = client.complete([{"role": "user", "content": "test"}], "system", [], 32, 5)
|
|
assert response["usage"]["output_tokens"] == 9
|
|
assert requests[0]["max_tokens"] == 32 and requests[0]["model"] == "local-test"
|
|
assert requests[0]["temperature"] == 1.0 and requests[0]["stop_sequences"] == ["STOP"]
|
|
for failure in ["unauthorized", "redirect", "gateway"]:
|
|
mode[0] = failure
|
|
try:
|
|
client.complete([], "system", [], 32, 5)
|
|
except ProviderError as exc:
|
|
assert "test-credential" not in str(exc)
|
|
expected_status = {"unauthorized": 401, "redirect": 302, "gateway": 502}[failure]
|
|
assert exc.diagnostic == {"code": "redirect_refused" if failure == "redirect" else "http_error",
|
|
"http_status": expected_status}
|
|
assert "test-credential" not in json.dumps(exc.diagnostic)
|
|
else:
|
|
raise AssertionError("provider did not stop on error/redirect")
|
|
assert len(requests) == 4, "provider silently retried"
|
|
for parameters in ({"max_tokens": 999999}, {"thinking": {"type": "adaptive"}},
|
|
{"temperature": True}, {"top_p": float("nan")}, {"temperature": -1},
|
|
{"temperature": 1, "top_p": 1}, {"stop_sequences": "STOP"}):
|
|
try:
|
|
AnthropicProvider({**profile, "parameters": parameters})
|
|
except ProviderError:
|
|
pass
|
|
else:
|
|
raise AssertionError("unsupported/invalid parameters were silently accepted")
|
|
assert len(requests) == 4, "invalid configuration sent an HTTP request"
|
|
mode[0] = "ok"
|
|
AnthropicProvider({**profile, "parameters": {"top_p": 1}}).complete([], "system", [], 32, 5)
|
|
assert requests[-1]["top_p"] == 1 and "temperature" not in requests[-1]
|
|
mode[0] = "non_object"
|
|
try:
|
|
client.complete([], "system", [], 32, 5)
|
|
except ProviderError as exc:
|
|
assert exc.diagnostic == {"code": "invalid_response"}
|
|
else:
|
|
raise AssertionError("non-object response escaped fail-closed handling")
|
|
mode[0] = "invalid_json"
|
|
try:
|
|
client.complete([], "system", [], 32, 5)
|
|
except ProviderError as exc:
|
|
assert exc.diagnostic == {"code": "invalid_json"}
|
|
assert "test-credential" not in str(exc)
|
|
else:
|
|
raise AssertionError("invalid JSON must fail without echoing its body")
|
|
cases = [
|
|
(urllib.error.URLError(socket.gaierror(-2, "test-credential")), {"code": "dns_error", "errno": -2}),
|
|
(urllib.error.URLError(ssl.SSLCertVerificationError(1, "test-credential")), {"code": "tls_error"}),
|
|
(urllib.error.URLError(TimeoutError("test-credential")), {"code": "timeout"}),
|
|
(ConnectionRefusedError(10061, "test-credential"), {"code": "connection_error", "errno": 10061}),
|
|
(http.client.IncompleteRead(b"test-credential"), {"code": "invalid_response"}),
|
|
]
|
|
for failure, expected in cases:
|
|
with mock.patch("agent_provider.urllib.request.build_opener") as opener:
|
|
opener.return_value.open.side_effect = failure
|
|
try:
|
|
client.complete([], "system", [], 32, 5)
|
|
except ProviderError as exc:
|
|
assert exc.diagnostic == expected
|
|
assert "test-credential" not in str(exc) + json.dumps(exc.diagnostic)
|
|
else:
|
|
raise AssertionError("transport failure lost its public classification")
|
|
assert opener.return_value.open.call_count == 1
|
|
finally:
|
|
server.shutdown()
|
|
server.server_close()
|
|
thread.join(timeout=5)
|
|
if previous is None:
|
|
os.environ.pop("RIGORPILOT_PROVIDER_TEST_KEY", None)
|
|
else:
|
|
os.environ["RIGORPILOT_PROVIDER_TEST_KEY"] = previous
|
|
print("ok: True; HTTP tool messages, usage, auth errors and redirects verified")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|