48 lines
1.7 KiB
Python
48 lines
1.7 KiB
Python
"""Keep internal namespaced tools compatible with providers' 64-character names."""
|
|
import hashlib
|
|
import re
|
|
from functools import wraps
|
|
|
|
from app.contracts import MessageRole, ModelRequest
|
|
|
|
|
|
def prepare_tool_names(request: ModelRequest) -> tuple[ModelRequest, dict[str, str]]:
|
|
names = {tool.name for tool in request.tools}
|
|
for message in request.messages:
|
|
names.update(call.name for call in message.tool_calls)
|
|
if message.role == MessageRole.tool and message.name:
|
|
names.add(message.name)
|
|
mapping = {name: name for name in names if re.fullmatch(r"[A-Za-z0-9_-]{1,64}", name)}
|
|
used = set(mapping)
|
|
for name in sorted(names - mapping.keys()):
|
|
salt = 0
|
|
while True:
|
|
alias = "tool_" + hashlib.sha256(f"{name}:{salt}".encode()).hexdigest()[:56]
|
|
if alias not in used:
|
|
break
|
|
salt += 1
|
|
mapping[name] = alias
|
|
used.add(alias)
|
|
if all(name == alias for name, alias in mapping.items()):
|
|
return request, {}
|
|
wire = request.model_copy(deep=True)
|
|
for tool in wire.tools:
|
|
tool.name = mapping[tool.name]
|
|
for message in wire.messages:
|
|
for call in message.tool_calls:
|
|
call.name = mapping[call.name]
|
|
if message.role == MessageRole.tool and message.name:
|
|
message.name = mapping[message.name]
|
|
return wire, {alias: name for name, alias in mapping.items()}
|
|
|
|
|
|
def mapped_tool_names(complete):
|
|
@wraps(complete)
|
|
async def wrapped(self, request: ModelRequest):
|
|
wire, originals = prepare_tool_names(request)
|
|
turn = await complete(self, wire)
|
|
for call in turn.tool_calls:
|
|
call.name = originals.get(call.name, call.name)
|
|
return turn
|
|
return wrapped
|