Files

110 lines
4.2 KiB
Python

"""Summarize actual wire tool constraints without persisting schemas or prompts."""
import hashlib
import json
import re
from artemis.llm.argument_fingerprint import MAX_TRACKED_TOOLS
MAX_REQUEST_BYTES = 4 * 1024 * 1024
def safe_label(value):
return value if isinstance(value, str) and re.fullmatch(r"[\w.:-]{1,128}", value) else None
def strict_state(tool: dict) -> str:
if "strict" not in tool:
return "omitted"
if tool["strict"] is True:
return "true"
if tool["strict"] is False:
return "false"
return "invalid"
def schema_summary(schema: dict) -> dict:
"""Count syntactic schema nodes, not expanded $refs or arbitrary examples."""
result = {"objects": 0, "optional_properties": 0, "open_objects": 0, "nodes": 0}
pending = [schema]
while pending and result["nodes"] < 1024:
node = pending.pop()
result["nodes"] += 1
properties = node.get("properties")
if node.get("type") == "object" or isinstance(properties, dict):
result["objects"] += 1
required = node.get("required")
required = required if isinstance(required, list) else []
if isinstance(properties, dict):
result["optional_properties"] += sum(k not in required for k in properties)
if node.get("additionalProperties") is not False:
result["open_objects"] += 1
children = []
for key in ("properties", "$defs", "definitions"):
if isinstance(node.get(key), dict):
children.extend(node[key].values())
for key in ("anyOf", "oneOf", "allOf", "prefixItems"):
if isinstance(node.get(key), list):
children.extend(node[key])
children.extend(node.get(k) for k in ("items", "additionalProperties", "not"))
remaining = max(0, 1024 - result["nodes"] - len(pending))
eligible = [v for v in children if isinstance(v, dict)]
if len(eligible) > remaining:
result["truncated"] = True
pending.extend(eligible[:remaining])
result["truncated"] = bool(pending) or result.get("truncated", False)
try:
result["sha256"] = hashlib.sha256(
json.dumps(schema, sort_keys=True, separators=(",", ":"), ensure_ascii=True).encode()
).hexdigest()
except (RecursionError, TypeError, ValueError):
result["sha256"] = None
result["truncated"] = True
return result
def tool_constraints(tools) -> dict:
records = []
pending = tools[:MAX_TRACKED_TOOLS] if isinstance(tools, list) else []
truncated = isinstance(tools, list) and len(tools) > MAX_TRACKED_TOOLS
visited = 0
while pending and visited < MAX_TRACKED_TOOLS:
tool = pending.pop(0)
visited += 1
if not isinstance(tool, dict):
continue
if tool.get("type") == "namespace":
nested = tool.get("tools")
if isinstance(nested, list):
remaining = MAX_TRACKED_TOOLS - len(pending) - visited
truncated = truncated or len(nested) > remaining
pending.extend(nested[:remaining])
continue
function = tool.get("function", tool)
if not isinstance(function, dict):
continue
record = {
"type": safe_label(tool.get("type")),
"name": safe_label(function.get("name")),
"strict": strict_state(function),
}
if isinstance(function.get("parameters"), dict):
record["schema"] = schema_summary(function["parameters"])
records.append(record)
return {"tools": records, "truncated": truncated or bool(pending), "observation": "available"}
def request_constraints(body: bytes) -> dict:
if len(body) > MAX_REQUEST_BYTES:
return {"observation": "body_too_large"}
try:
payload = json.loads(body)
except (ValueError, UnicodeError, RecursionError):
return {"observation": "invalid_json"}
if not isinstance(payload, dict):
return {"observation": "not_an_object"}
result = tool_constraints(payload.get("tools"))
limit = payload.get("max_output_tokens")
result["max_output_tokens"] = limit if type(limit) is int and limit > 0 else None
return result