Skip to main content
valen

Turn a command into a function call

A notes app has a command bar. People type things like "send this to the design team but they shouldn't be able to change anything", and the app should call share_note(permission="view"). Valen chooses the function and each closed-set argument; code holds the function table, decides when to ask a follow-up, and makes the call.

How it works​

The function table in code is the single source of every question. One request carries:

QuestionTypeRead when
function: what does the user want the app to do?choice over the four functionsalways
is_command: could a notes app do what the user asks?predicatealways
<function>.<arg> for every argument of every functionchoiceonly for the chosen function

Asking every function's arguments up front is speculative fan-out: questions are cheap and run together over the same input, so one round trip is enough whichever function wins. A choice always picks an option, so the null case gets its own predicate instead of a none option.

is_commandfunction choiceAction
below 0.3anynot_a_command
0.3 – 0.7uncertainshow_help: list what the app can do
0.7 or aboveuncertainclarify between the top two functions
anytop ≥ 0.8 and gap ≥ 0.2call, or clarify_arguments for an unsure argument

Run it​

Download the bundle once. The recorded answers in caches/function_calling.json let the script run without an API key; --replay off calls the free hosted API instead.

Bash
curl -fLO https://valen-team.github.io/cookbook/valen-cookbook.zip
python -m zipfile -e valen-cookbook.zip .
cd valen-cookbook && python -m pip install -r requirements.txt
python function_calling.py
python function_calling.py --replay off --base-url https://api.valen-team.workers.dev/v1

Code​

function_calling.py
"""Turn a command into a function call: one request picks the function and every closed-set argument. Apache-2.0."""
import json

from common import HERE, choice_status, client, emit, parser, request

# Each function's closed-set arguments. Free text, numbers and dates stay with code.
TOOLS = {
"set_reminder": {"description": "Remind the user about this note later",
"args": {"when": {"later_today": "Later today", "tomorrow": "Tomorrow",
"next_week": "Some time next week"}}},
"share_note": {"description": "Send or share this note with other people",
"args": {"permission": {"view": "They can read but not change it",
"edit": "They can change it"}}},
"change_theme": {"description": "Change how the app looks",
"args": {"theme": {"light": "Light colors", "dark": "Dark colors",
"system": "Follow the device setting"}}},
"export_note": {"description": "Save this note as a file",
"args": {"format": {"pdf": "PDF document", "markdown": "Markdown text file",
"docx": "Word document"}}},
}


def questions():
asked = [{"name": "function", "type": "choice", "instructions": "What does the user want the notes app to do?",
"choices": [{"value": name, "description": tool["description"]} for name, tool in TOOLS.items()]},
# A choice always picks a function, so a separate predicate tells code when none applies.
{"name": "is_command", "type": "predicate",
"instructions": "A notes app could do what the user asks, without outside information such as weather or news."}]
# Speculative: every function's arguments are asked now; code reads only the chosen function's.
for name, tool in TOOLS.items():
for arg, options in tool["args"].items():
asked.append({"name": f"{name}.{arg}", "type": "choice",
"instructions": f"If the user wants to {tool['description'].lower()}, which {arg} do they mean?",
"choices": [{"value": value, "description": text} for value, text in options.items()]})
return asked


def run(predict, command):
answers = request(predict, command, questions())
function = answers["function"]
confidence, certain = choice_status(function)
is_command = answers["is_command"]["probability"]
base = {"command": command, "is_command": is_command, "confidence": confidence}
if is_command < 0.3:
return {**base, "status": "not_a_command"}
if not certain:
if is_command < 0.7:
return {**base, "status": "show_help"}
top = sorted(function["probabilities"], key=lambda x: x["probability"], reverse=True)[:2]
return {**base, "status": "clarify", "options": [x["value"] for x in top]}
kwargs, unsure = {}, []
for arg in TOOLS[function["choice"]]["args"]:
answer = answers[f"{function['choice']}.{arg}"]
kwargs[arg] = answer["choice"]
if not choice_status(answer)[1]:
unsure.append(arg)
return {**base, "status": "clarify_arguments" if unsure else "call",
"call": {"function": function["choice"], "kwargs": kwargs}, "unsure": unsure}


def main():
args = parser(__doc__).parse_args()
commands = json.loads((HERE / "function_calling_data.json").read_text())
with client(args) as predict:
emit([run(predict, command) for command in commands], args)


if __name__ == "__main__":
main()
function_calling_data.json
[
"ping me about this tomorrow morning",
"send this to the design team but they shouldn't be able to change anything",
"my eyes hurt at night, can you make the screen darker",
"I need this as a word file for my manager",
"can you remind me next week? actually make it a pdf instead",
"what's the weather in Shanghai",
"make it look like whatever my phone is set to"
]

Output​

Five commands become calls. "Remind me next week? actually make it a pdf" holds two requests, so the app asks which one. "What's the weather in Shanghai" lands in the middle band and gets the help text.

Recorded output
[
{
"command": "ping me about this tomorrow morning",
"is_command": 0.8666370123638059,
"confidence": 0.8722163963755449,
"status": "call",
"call": {
"function": "set_reminder",
"kwargs": {
"when": "tomorrow"
}
},
"unsure": []
},
{
"command": "send this to the design team but they shouldn't be able to change anything",
"is_command": 0.8469533829065193,
"confidence": 0.9912622488466675,
"status": "call",
"call": {
"function": "share_note",
"kwargs": {
"permission": "view"
}
},
"unsure": []
},
{
"command": "my eyes hurt at night, can you make the screen darker",
"is_command": 0.8734981417655945,
"confidence": 0.8097805124404491,
"status": "call",
"call": {
"function": "change_theme",
"kwargs": {
"theme": "dark"
}
},
"unsure": []
},
{
"command": "I need this as a word file for my manager",
"is_command": 0.9875167965186418,
"confidence": 0.944616775424281,
"status": "call",
"call": {
"function": "export_note",
"kwargs": {
"format": "docx"
}
},
"unsure": []
},
{
"command": "can you remind me next week? actually make it a pdf instead",
"is_command": 0.9193248221227885,
"confidence": 0.5059124954600235,
"status": "clarify",
"options": [
"set_reminder",
"export_note"
]
},
{
"command": "what's the weather in Shanghai",
"is_command": 0.3065500855445862,
"confidence": 0.4502609170856662,
"status": "show_help"
},
{
"command": "make it look like whatever my phone is set to",
"is_command": 0.9843640593466292,
"confidence": 0.9998406975518448,
"status": "call",
"call": {
"function": "change_theme",
"kwargs": {
"theme": "system"
}
},
"unsure": []
}
]

Make it yours​

  • Add a function by adding an entry to TOOLS; questions follow automatically. Free text, numbers and dates stay with code (see pick the right date).
  • Describe each function by what the user wants ("Send or share this note with other people"), not by its API name.
  • The is_command wording names the confusing case directly: requests that need outside information such as weather or news.