Solving tips
- A tool call's identity is (name, args) together — never the name alone, since one tool gets called with many different args.
- args is a dict, so it isn't hashable; build a canonical string key with json.dumps(args, sort_keys=True) so key order doesn't matter.
- Cache on first execution and reuse: duplicates should hit the cache, not the tool.
When a model requests several tool calls at once (a “parallel” turn), the list often contains identical calls — the same tool with the same arguments. Running each one separately wastes time and money and, for a non-idempotent tool, can be outright wrong. Your job is the dispatch layer that runs each unique call exactly once and lets duplicates reuse the cached result.
The problem
A call is a dict {"name": str, "args": dict}. Two calls are the same when they have the same name and the same args — where {"a": 1, "b": 2} and {"b": 2, "a": 1} count as the same args, since key order is not meaningful. You cannot use the args dict directly as a cache key because dicts are unhashable, so you build a canonical key from it.
Task
Complete run_parallel_calls(tools, calls):
- For each call, build a canonical key from its name and args, for example
f"{name}({json.dumps(args, sort_keys=True)})".sort_keys=Truemakes the key independent of args ordering. - Keep a
cachedict from key -> result. The first time you see a key, look the tool up intools, call it withargs, and store the result under that key. On a repeated key, do not call the tool again. - Return the
cachedict: one entry per unique call, mapping the canonical key to its result. - If a call names a tool that isn’t in
tools, store an error string (e.g.f"error: unknown tool '{name}'") as that call’s result instead of crashing.
Example
calls_log = []
def search(args):
calls_log.append(args["q"])
return f"results for {args['q']}"
tools = {"search": search}
calls = [
{"name": "search", "args": {"q": "cats"}},
{"name": "search", "args": {"q": "dogs"}},
{"name": "search", "args": {"q": "cats"}}, # duplicate of the first call
]
run_parallel_calls(tools, calls)
# -> {
# 'search({"q": "cats"})': 'results for cats',
# 'search({"q": "dogs"})': 'results for dogs',
# }
# calls_log == ["cats", "dogs"] # "cats" ran once, not twice
Constraints
- Each unique
(name, args)call must invoke its tool at most once. - Treat
argsdicts that differ only in key order as identical. - Assume
argsvalues are JSON-serializable. - Do not call any real LLM or network —
toolsare plain callables provided to you.