feat: release v1.0.1 CADWorld 网站与 LeKiwi 智能抓放
web-platform-ci / Standalone decision service (no cloud credentials) (push) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (push) Has been cancelled
web-platform-ci / Playwright E2E (push) Has been cancelled
lekiwi-compatibility / cpu-compatibility (push) Has been cancelled
web-platform-ci / Standalone decision service (no cloud credentials) (pull_request) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (pull_request) Has been cancelled
web-platform-ci / Playwright E2E (pull_request) Has been cancelled
lekiwi-compatibility / cpu-compatibility (pull_request) Has been cancelled
web-platform-ci / Standalone decision service (no cloud credentials) (push) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (push) Has been cancelled
web-platform-ci / Playwright E2E (push) Has been cancelled
lekiwi-compatibility / cpu-compatibility (push) Has been cancelled
web-platform-ci / Standalone decision service (no cloud credentials) (pull_request) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (pull_request) Has been cancelled
web-platform-ci / Playwright E2E (pull_request) Has been cancelled
lekiwi-compatibility / cpu-compatibility (pull_request) Has been cancelled
集成同源 BYOK 会话隔离、精简模型设置、官方订阅入口和 HTTPS 发布运维;保留本地训练/调参与控制能力。同步 npm 版本及 CHANGELOG,记录公网真实 API 验收仍待用户凭据。
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
"""Offline native capability gate: no advertised tools AND injected calls rejected.
|
||||
|
||||
Runs only against a private loopback fake Responses service with synthetic output.
|
||||
No account login, remote inference, inherited credentials or agent delegation occurs.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import hmac
|
||||
import json
|
||||
|
||||
from aiohttp import web
|
||||
|
||||
from ..protocol import DecisionError
|
||||
from .codex import CodexAccount
|
||||
|
||||
|
||||
async def verify_no_tools(directory, model, evidence=None):
|
||||
completed = asyncio.get_running_loop().create_future()
|
||||
count = 0
|
||||
first_tools = None
|
||||
client = None
|
||||
names = ["apply_patch", "shell_command", "exec_command", "view_image"]
|
||||
|
||||
async def receive(request):
|
||||
nonlocal count, first_tools
|
||||
if not client or not hmac.compare_digest(
|
||||
request.headers.get("Authorization", ""), "Bearer " + client.probe_token
|
||||
):
|
||||
return web.Response(status=403)
|
||||
body = await request.json()
|
||||
count += 1
|
||||
if count == 1:
|
||||
first_tools = body.get("tools", [])
|
||||
items = []
|
||||
for i, name in enumerate(names):
|
||||
item = {
|
||||
"id": f"tool_{i}",
|
||||
"call_id": f"gate_{i}",
|
||||
"name": name,
|
||||
"status": "completed",
|
||||
}
|
||||
if name == "apply_patch":
|
||||
item.update(
|
||||
type="custom_tool_call",
|
||||
input=(
|
||||
"*** Begin Patch\n*** Add File: MUST_NOT_WRITE\n+test\n*** End Patch"
|
||||
),
|
||||
)
|
||||
else:
|
||||
item.update(
|
||||
type="function_call",
|
||||
arguments=json.dumps(
|
||||
{
|
||||
"command": "touch MUST_NOT_WRITE",
|
||||
"cmd": "touch MUST_NOT_WRITE",
|
||||
"path": str(client.cwd / "nonexistent-image.png"),
|
||||
}
|
||||
),
|
||||
)
|
||||
items.append(item)
|
||||
events = [
|
||||
{
|
||||
"type": "response.created",
|
||||
"response": {"id": "gate_response", "status": "in_progress"},
|
||||
}
|
||||
]
|
||||
events.extend(
|
||||
{"type": "response.output_item.done", "output_index": i, "item": item}
|
||||
for i, item in enumerate(items)
|
||||
)
|
||||
events.append(
|
||||
{
|
||||
"type": "response.completed",
|
||||
"response": {
|
||||
"id": "gate_response",
|
||||
"status": "completed",
|
||||
"output": items,
|
||||
"usage": {"input_tokens": 1, "output_tokens": 1, "total_tokens": 2},
|
||||
},
|
||||
}
|
||||
)
|
||||
return web.Response(
|
||||
content_type="text/event-stream",
|
||||
text="".join(
|
||||
"event: " + event["type"] + "\ndata: " + json.dumps(event) + "\n\n"
|
||||
for event in events
|
||||
),
|
||||
)
|
||||
feedback = {
|
||||
item.get("call_id"): item.get("output")
|
||||
for item in body.get("input", [])
|
||||
if item.get("type") in ("function_call_output", "custom_tool_call_output")
|
||||
}
|
||||
passed = (
|
||||
first_tools == []
|
||||
and body.get("tools", []) == []
|
||||
and all(
|
||||
isinstance(feedback.get(f"gate_{i}"), str)
|
||||
and "unsupported" in feedback[f"gate_{i}"].lower()
|
||||
and name in feedback[f"gate_{i}"]
|
||||
for i, name in enumerate(names)
|
||||
)
|
||||
and not (client.cwd / "MUST_NOT_WRITE").exists()
|
||||
)
|
||||
if evidence is not None:
|
||||
evidence.update(model=model, tools=first_tools, feedback=feedback, passed=passed)
|
||||
if not completed.done():
|
||||
completed.set_result(passed)
|
||||
return web.Response(status=400, text="offline gate finished")
|
||||
|
||||
app = web.Application(client_max_size=262144)
|
||||
app.router.add_post("/v1/responses", receive)
|
||||
runner = web.AppRunner(app, access_log=None)
|
||||
await runner.setup()
|
||||
site = web.TCPSite(runner, "127.0.0.1", 0)
|
||||
await site.start()
|
||||
port = site._server.sockets[0].getsockname()[1]
|
||||
client = CodexAccount(directory, probe_url=f"http://127.0.0.1:{port}/v1")
|
||||
try:
|
||||
async with asyncio.timeout(25):
|
||||
await client.start()
|
||||
thread = await client.rpc(
|
||||
"thread/start",
|
||||
{
|
||||
"cwd": str(client.cwd),
|
||||
"ephemeral": True,
|
||||
"approvalPolicy": "never",
|
||||
"sandbox": "read-only",
|
||||
"model": model,
|
||||
"modelProvider": "offline_probe",
|
||||
"baseInstructions": "Return JSON only. Do not call tools.",
|
||||
},
|
||||
)
|
||||
await client.rpc(
|
||||
"turn/start",
|
||||
{
|
||||
"threadId": thread["thread"]["id"],
|
||||
"input": [{"type": "text", "text": 'Return {"ok":true}'}],
|
||||
"outputSchema": {
|
||||
"type": "object",
|
||||
"additionalProperties": False,
|
||||
"required": ["ok"],
|
||||
"properties": {"ok": {"type": "boolean"}},
|
||||
},
|
||||
},
|
||||
)
|
||||
if not await completed:
|
||||
raise DecisionError("codex_tool_gate_failed", 409)
|
||||
except TimeoutError:
|
||||
raise DecisionError("codex_tool_gate_timeout", 504) from None
|
||||
finally:
|
||||
await client.close()
|
||||
await runner.cleanup()
|
||||
Reference in New Issue
Block a user