Spaces:
Sleeping
Sleeping
Download src/routing_agent/classifier.py from SebAustin/amd-routing-agent-demo: direct link, hf CLI and curl.
- Browser
- Download file 5.89 kB
-
https://huggingface.co/spaces/SebAustin/amd-routing-agent-demo/resolve/main/src/routing_agent/classifier.py
- Command line
-
hf download hf://spaces/SebAustin/amd-routing-agent-demo/src/routing_agent/classifier.py
-
curl -L -o classifier.py https://huggingface.co/spaces/SebAustin/amd-routing-agent-demo/resolve/main/src/routing_agent/classifier.py
5.89 kB
| """Zero-token task classification via ordered regex/heuristic rules. | |
| Purely local; never calls a model. Conservative by design: when no rule | |
| confidently matches, the task falls back to `TaskType.GENERAL` rather than | |
| guessing, since Tier-0/Tier-1 routing decisions downstream key off this type. | |
| """ | |
| from __future__ import annotations | |
| import re | |
| from enum import StrEnum | |
| class TaskType(StrEnum): | |
| ARITHMETIC = "arithmetic" | |
| DATE_MATH = "date_math" | |
| STRING_OP = "string_op" | |
| UNIT_CONVERSION = "unit_conversion" | |
| EXTRACTION = "extraction" | |
| CLASSIFICATION = "classification" | |
| MULTIPLE_CHOICE = "multiple_choice" | |
| SHORT_QA = "short_qa" | |
| CODE = "code" | |
| SUMMARIZATION = "summarization" | |
| GENERAL = "general" | |
| _ARITHMETIC_EXPR_RE = re.compile(r"^[\s0-9+\-*/%().^]+$") | |
| _ARITHMETIC_WORDED_RE = re.compile( | |
| r"\b(sum of|product of|difference|quotient|square root|percent\b|percentage|" | |
| r"multiplied by|divided by|\bplus\b|\bminus\b|\d+ times\b|" | |
| r"add \d|subtract \d|compute \d|multiply \d|" | |
| r"squared|cubed|raised to the power|to the power of|" | |
| r"marked up|discounted|\d+% tax|has a \d+% tax|markup|" | |
| r"how much (do you have|does it cost)|total cost|" | |
| r"how many .* (are needed|do you need)|" | |
| r"rounded to \d+ decimal places?)", | |
| re.IGNORECASE, | |
| ) | |
| _ARITHMETIC_KEYWORD_RE = re.compile(r"what\s+is\s+[\d.\s+\-*/%()]+[+\-*/%]", re.IGNORECASE) | |
| _PERCENT_OF_RE = re.compile(r"\d+(\.\d+)?\s*%\s*of\s+\d+(\.\d+)?", re.IGNORECASE) | |
| _DATE_MATH_RE = re.compile( | |
| r"\b(days? (after|before|from)|weekday of|day of the week|" | |
| r"how many days (are there|between|until|since|from)|" | |
| r"is (the year|\d{4}) .*leap year|leap year|" | |
| r"how many days (are )?in\b|date (is|was))\b", | |
| re.IGNORECASE, | |
| ) | |
| _DATE_TOKEN_RE = re.compile( | |
| r"\b(january|february|march|april|may|june|july|august|september|" | |
| r"october|november|december|\d{4}-\d{2}-\d{2}|\d{1,2}/\d{1,2}/\d{2,4})\b", | |
| re.IGNORECASE, | |
| ) | |
| _STRING_OP_RE = re.compile( | |
| r"\b(reverse|uppercase|lowercase|upper[- ]?case|lower[- ]?case|title case|" | |
| r"how many (letters|characters|vowels|words)|count the (occurrences|letters)|" | |
| r"how many times does the letter|" | |
| r"first \d+ characters|last \d+ characters|sort the letters|" | |
| r"(\d+(st|nd|rd|th)|first|last) character of|palindrome)\b", | |
| re.IGNORECASE, | |
| ) | |
| _UNIT_CONVERSION_RE = re.compile( | |
| r"(\bconvert\b.*\b(km|kilometers?|miles?|meters?|feet|ft|inches?|cm|" | |
| r"celsius|fahrenheit|kelvin|kg|kilograms?|pounds?|lbs?|grams?|" | |
| r"kb|mb|gb|tb|bytes?)\b|" | |
| r"how many (mb|kb|gb|tb|bytes?|meters?|feet|inches?|km|miles?|kg|grams?|pounds?) " | |
| r"(are|is) (in|there in))", | |
| re.IGNORECASE, | |
| ) | |
| _EXTRACTION_RE = re.compile( | |
| r"\b(extract|find the (email|url|phone|number|date)s?|" | |
| r"list all (email|url|phone|number|date)s?)\b", | |
| re.IGNORECASE, | |
| ) | |
| _MULTIPLE_CHOICE_PAREN_RE = re.compile(r"\([a-dA-D]\)", re.MULTILINE) | |
| _MULTIPLE_CHOICE_LINE_RE = re.compile(r"^[a-dA-D][).]\s", re.MULTILINE) | |
| # Inline "A) foo B) bar" option markers (no line anchor needed) — requires at | |
| # least two to avoid false positives on prose containing a single "B)". | |
| _MULTIPLE_CHOICE_INLINE_RE = re.compile(r"\b[A-D]\)\s*\S") | |
| _MULTIPLE_CHOICE_HINT_RE = re.compile( | |
| r"\bchoose the correct\b|\bselect one\b|\bwhich of the following\b", re.IGNORECASE | |
| ) | |
| _CLASSIFICATION_RE = re.compile( | |
| r"\b(classify|categorize|label|identify the language|" | |
| r"is (this|the following)\b.{0,20}\b(positive|negative|spam|ham)|sentiment)\b", | |
| re.IGNORECASE, | |
| ) | |
| _CODE_RE = re.compile( | |
| r"\b(write a function|write code|implement|def |function\s*\(|" | |
| r"in (python|javascript|java|c\+\+|go|rust)\b|fix the bug|debug this|" | |
| r"what does this (code|python|javascript|java)?\s*(code )?(print|output|return)|" | |
| r"what (is|will be) the output of|what does this snippet)\b", | |
| re.IGNORECASE, | |
| ) | |
| _SUMMARIZATION_RE = re.compile( | |
| r"\b(summarize|summary of|tl;?dr|in (one|a few) sentences?, summarize)\b", | |
| re.IGNORECASE, | |
| ) | |
| _SHORT_QA_HINT_RE = re.compile(r"^(who|what|when|where|which|how many|how much)\b", re.IGNORECASE) | |
| def classify(prompt: str) -> TaskType: | |
| """Classify a task prompt into a `TaskType` using ordered heuristics. | |
| Rules are checked most-specific-first so that, e.g., a date-math question | |
| phrased as a "what" question is caught by DATE_MATH before falling | |
| through to the generic SHORT_QA bucket. Returns GENERAL when no rule | |
| matches confidently. | |
| """ | |
| text = prompt.strip() | |
| if not text: | |
| return TaskType.GENERAL | |
| stripped_expr = text.rstrip("? .") | |
| if _ARITHMETIC_EXPR_RE.match(stripped_expr) and any(c.isdigit() for c in text): | |
| return TaskType.ARITHMETIC | |
| if _PERCENT_OF_RE.search(text): | |
| return TaskType.ARITHMETIC | |
| if _ARITHMETIC_KEYWORD_RE.search(text) or _ARITHMETIC_WORDED_RE.search(text): | |
| return TaskType.ARITHMETIC | |
| if _DATE_MATH_RE.search(text) and _DATE_TOKEN_RE.search(text): | |
| return TaskType.DATE_MATH | |
| if _DATE_MATH_RE.search(text): | |
| return TaskType.DATE_MATH | |
| if _UNIT_CONVERSION_RE.search(text): | |
| return TaskType.UNIT_CONVERSION | |
| if _STRING_OP_RE.search(text): | |
| return TaskType.STRING_OP | |
| if _EXTRACTION_RE.search(text): | |
| return TaskType.EXTRACTION | |
| if _CODE_RE.search(text): | |
| return TaskType.CODE | |
| if _SUMMARIZATION_RE.search(text): | |
| return TaskType.SUMMARIZATION | |
| if ( | |
| _MULTIPLE_CHOICE_PAREN_RE.search(text) | |
| or _MULTIPLE_CHOICE_LINE_RE.search(text) | |
| or len(_MULTIPLE_CHOICE_INLINE_RE.findall(text)) >= 2 | |
| or _MULTIPLE_CHOICE_HINT_RE.search(text) | |
| ): | |
| return TaskType.MULTIPLE_CHOICE | |
| if _CLASSIFICATION_RE.search(text): | |
| return TaskType.CLASSIFICATION | |
| if _SHORT_QA_HINT_RE.match(text) and len(text) < 200: | |
| return TaskType.SHORT_QA | |
| return TaskType.GENERAL | |