Spaces:
Running on Zero
Running on Zero
File size: 6,230 Bytes
51da1ee cbe9a94 51da1ee cbe9a94 a4c4ce7 cbe9a94 51da1ee cbe9a94 acda592 cbe9a94 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 | """Synthetic command-intent dataset for the router specialist (seeded, no download).
python -m oev.convert_commands
"""
import json
import os
import random
INTENTS = ["music", "search", "note", "calculator", "app", "unknown"]
INSTRUCTIONS_INTENT = ("What does the user want the computer to do? "
"unknown covers partial commands, bare triggers and chat.")
APPS = ["spotify", "chrome", "notepad", "calculator", "vs code", "discord",
"steam", "explorer", "settings", "terminal"]
SONGS = ["blinding lights", "bohemian rhapsody", "take five", "gimme shelter",
"fly me to the moon", "dream on", "landslide", "redbone",
"electric feel", "ain't no mountain high enough"]
ARTISTS = ["the weeknd", "queen", "dave brubeck", "nina simone", "daft punk",
"fleetwood mac", "tame impala", "miles davis", "chaka khan"]
QUERIES = ["rust async programming", "latest nvidia drivers", "weather tomorrow",
"best noise cancelling headphones", "python deque vs list",
"how to fold a fitted sheet", "distance to the moon",
"cheap flights to lisbon", "markdown table syntax", "is pluto a planet"]
TASKS = ["finish the benchmark writeup", "call the dentist", "water the plants",
"back up the laptop", "renew the domain", "ship the release notes"]
ITEMS = ["oat milk", "printer paper", "a new mouse", "birthday candles", "coffee beans"]
DAYS = ["saturday", "monday", "friday", "tomorrow"]
PEOPLE = ["sam", "priya", "marco", "lena"]
CHAT = ["hello", "hey there", "good morning", "how are you", "thanks a lot",
"what time is it", "tell me a joke", "never mind", "actually stop"]
MUSIC_T = ["play {song} on spotify", "open spotify and play {song}",
"put on {artist}", "play some {artist} songs", "queue up {song}",
"play {song} by {artist}", "start {artist} radio"]
SEARCH_T = ["search the web for {query}", "search youtube for {query}",
"google {query}", "look up {query}", "find {query} on the web"]
NOTE_T = ["write in my notes that I need to {task}", "take a note: {task}",
"remind me to {task}", "note that I have to {task}"]
CALC_T = ["calculate {a} times {b}", "what is {a} plus {b}", "what is {a} minus {b}",
"calculate {a} divided by {b}", "{a} times {b}"]
APP_T = ["open {app}", "launch {app}", "start {app}", "switch to {app}",
"open up {app}"]
def _fill(templates, rng, **pools):
t = rng.choice(templates)
return t.format(**{k: rng.choice(v) for k, v in pools.items()})
def _full_command(rng):
kind = rng.choice(["music", "search", "note", "calculator", "app"])
if kind == "music":
return _fill(MUSIC_T, rng, song=SONGS, artist=ARTISTS), "music"
if kind == "search":
return _fill(SEARCH_T, rng, query=QUERIES), "search"
if kind == "note":
task = rng.choice(TASKS) + (" " + rng.choice(["today", "this week", "by friday"])
if rng.random() < 0.4 else "")
return rng.choice(NOTE_T).format(task=task), "note"
if kind == "calculator":
a, b = rng.randint(2, 999), rng.randint(2, 99)
return _fill(CALC_T, rng, a=[a], b=[b]), "calculator"
return _fill(APP_T, rng, app=APPS), "app"
def _partial(full, rng):
words = full.split()
cut = rng.randint(1, max(1, len(words) - 2))
return " ".join(words[:cut])
def _case(i, state, intent, complete, prefix):
return {
"id": f"{prefix}-{i:06d}",
"domain": "commands",
"state": state,
"questions": [
{"name": "intent", "type": "choice", "instructions": INSTRUCTIONS_INTENT,
"options": INTENTS, "answer": intent},
{"name": "complete", "type": "noul", "options": ["no", "yes"],
"instructions": "Is this a finished command, or is the user still speaking?",
"answer": "yes" if complete else "no"},
],
}
def generate(n, seed, prefix):
# balanced: equal intent quotas; the full/partial/trigger/chat mix inside
# each class teaches the finished-vs-still-speaking split. repeats are
# re-randomized rather than dropped so no class is starved by dedupe.
rng = random.Random(seed)
# one slot per case, cycling intent classes so every class gets n/len(INTENTS)
slots = [INTENTS[i % len(INTENTS)] for i in range(n)]
rng.shuffle(slots)
out = []
for state_slot in slots:
r = rng.random()
if state_slot == "unknown":
# unknown = partials, bare triggers, chat (never full commands)
if r < 0.55:
full, _ = _full_command(rng)
state = _partial(full, rng)
elif r < 0.8:
state = rng.choice(["open", "play", "search", "write", "calculate",
"can you", "hey", "um", "launch"])
else:
state = rng.choice(CHAT)
out.append(_case(len(out), state, "unknown", rng.random() < 0.3, prefix))
continue
# a real intent: 60% full command, 25% partial, 15% bare trigger of that intent
full, _ = _full_command(rng)
# force the template family to match the slot
for _ in range(50):
full, kind = _full_command(rng)
if kind == state_slot:
break
if r < 0.6:
out.append(_case(len(out), full, state_slot, True, prefix))
elif r < 0.85:
out.append(_case(len(out), _partial(full, rng), state_slot, False, prefix))
else:
trigger = full.split()[0]
out.append(_case(len(out), trigger, state_slot, False, prefix))
return out
def main(out_dir="data/commands"):
os.makedirs(out_dir, exist_ok=True)
sizes = {"train": 8000, "valid": 1000, "test": 1000}
seeds = {"train": 11, "valid": 22, "test": 33}
for split, n in sizes.items():
path = os.path.join(out_dir, f"{split}.jsonl")
with open(path, "w", encoding="utf-8") as f:
f.writelines(json.dumps(c) + "\n" for c in generate(n, seeds[split], split[:2]))
print(f"wrote {n} cases to {path} (intents: {', '.join(INTENTS)})")
if __name__ == "__main__":
main()
|