110 lines
4.2 KiB
Python
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
|