@miller-tech/uap 1.86.0 → 1.87.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
- package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-312.pyc +0 -0
- package/tools/agents/scripts/confidence_escalation.py +86 -4
- package/tools/agents/tests/test_confidence_escalation.py +34 -6
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
@@ -98,15 +98,97 @@ def extract_text(anthropic_resp: dict) -> str:
|
|
|
98
98
|
return "".join(parts)
|
|
99
99
|
|
|
100
100
|
|
|
101
|
-
# ---- #2 recipe selection
|
|
101
|
+
# ---- #2 recipe selection (reactor-aligned signals) ------------------------
|
|
102
|
+
# Faithful port of src/utils/query-complexity.ts (the SAME difficulty signal the
|
|
103
|
+
# reactor's capability-router / auto-optimizer use), plus a task-shape signal.
|
|
104
|
+
# The serving layer extracts its own signals (vLLM-SR philosophy) so recipe
|
|
105
|
+
# choice is task-shaped, not prompt-length guesswork. Optional override:
|
|
106
|
+
# the harness may stamp x-uap-task-signal style hints into the request later.
|
|
107
|
+
_TECH_PATTERNS = [
|
|
108
|
+
re.compile(r"debug|fix|error|exception|bug", re.I),
|
|
109
|
+
re.compile(r"implement|refactor|optimize|build", re.I),
|
|
110
|
+
re.compile(r"configure|setup|install|deploy", re.I),
|
|
111
|
+
re.compile(r"security|vulnerability|cve|auth", re.I),
|
|
112
|
+
re.compile(r"performance|memory|cpu|latency", re.I),
|
|
113
|
+
re.compile(r"database|query|migration|schema", re.I),
|
|
114
|
+
re.compile(r"test|coverage|mock|spec", re.I),
|
|
115
|
+
]
|
|
116
|
+
_FILE_RE = re.compile(r"[\w./\\-]+\.(?:ts|tsx|js|jsx|mjs|cjs|py|json|yaml|sh|sql)", re.I)
|
|
117
|
+
_MULTISTEP = re.compile(r"and then|after that|followed by|step \d|first.*then|also|additionally", re.I)
|
|
118
|
+
_WHYHOW = re.compile(r"^(why|how|what caused|explain)", re.I)
|
|
119
|
+
_ACTIONS = re.compile(r"\b(fix|implement|configure|debug|create|update|delete|add|remove)\b", re.I)
|
|
120
|
+
_REASONING = re.compile(
|
|
121
|
+
r"\b(prove|derive|theorem|reason|analy[sz]e|compare|evaluate|calculate|which of|true or false)\b"
|
|
122
|
+
r"|\b[A-D]\)\s", re.I,
|
|
123
|
+
)
|
|
124
|
+
_CODE = re.compile(r"```|\b(function|class|api|endpoint|module|compile|script)\b", re.I)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def complexity_score(text: str) -> float:
|
|
128
|
+
"""Port of query-complexity.ts measureQueryComplexity."""
|
|
129
|
+
t = text or ""
|
|
130
|
+
score = 0.0
|
|
131
|
+
wc = len(t.split())
|
|
132
|
+
if wc > 30:
|
|
133
|
+
score += 1.5
|
|
134
|
+
elif wc > 12:
|
|
135
|
+
score += 0.75
|
|
136
|
+
elif wc > 6:
|
|
137
|
+
score += 0.25
|
|
138
|
+
for pat in _TECH_PATTERNS:
|
|
139
|
+
if pat.search(t):
|
|
140
|
+
score += 0.4
|
|
141
|
+
files = _FILE_RE.findall(t)
|
|
142
|
+
score += len(files) * 0.3
|
|
143
|
+
if _MULTISTEP.search(t):
|
|
144
|
+
score += 1.0
|
|
145
|
+
if _WHYHOW.search(t):
|
|
146
|
+
score += 0.5
|
|
147
|
+
actions = _ACTIONS.findall(t)
|
|
148
|
+
if len(actions) > 1:
|
|
149
|
+
score += len(actions) * 0.3
|
|
150
|
+
return score
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def query_complexity(text: str) -> str:
|
|
154
|
+
score = complexity_score(text)
|
|
155
|
+
if score >= 2:
|
|
156
|
+
return "complex"
|
|
157
|
+
if score >= 1:
|
|
158
|
+
return "moderate"
|
|
159
|
+
return "simple"
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def task_shape(text: str) -> str:
|
|
163
|
+
t = text or ""
|
|
164
|
+
if _REASONING.search(t):
|
|
165
|
+
return "reasoning"
|
|
166
|
+
if _CODE.search(t) or _FILE_RE.search(t) or any(p.search(t) for p in _TECH_PATTERNS[:3]):
|
|
167
|
+
return "code"
|
|
168
|
+
if t.strip().endswith("?") and len(t.split()) <= 14:
|
|
169
|
+
return "qa"
|
|
170
|
+
return "general"
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def task_signals(anthropic_body: dict) -> dict:
|
|
174
|
+
text = latest_user_text(anthropic_body)
|
|
175
|
+
return {"complexity": query_complexity(text), "shape": task_shape(text)}
|
|
176
|
+
|
|
177
|
+
|
|
102
178
|
def select_recipe(anthropic_body: dict, settings: Settings, has_tools: bool) -> str:
|
|
103
179
|
if not settings.enabled or has_tools:
|
|
104
180
|
return "single"
|
|
105
181
|
if settings.recipe != "auto":
|
|
106
182
|
return settings.recipe
|
|
107
|
-
#
|
|
108
|
-
|
|
109
|
-
if
|
|
183
|
+
# Task-shaped auto-selection (the article's lesson: best loop is task-shaped).
|
|
184
|
+
sig = task_signals(anthropic_body)
|
|
185
|
+
if settings.backend_configured() and (
|
|
186
|
+
sig["complexity"] == "complex" or sig["shape"] == "reasoning"
|
|
187
|
+
):
|
|
188
|
+
# High reasoning variance / disagreement-prone -> breadth + judge.
|
|
189
|
+
return "fusion"
|
|
190
|
+
# Long-prompt fallback trigger (tunable) keeps the old escape hatch.
|
|
191
|
+
if settings.backend_configured() and len(latest_user_text(anthropic_body)) >= settings.auto_fusion_chars:
|
|
110
192
|
return "fusion"
|
|
111
193
|
return "confidence"
|
|
112
194
|
|
|
@@ -52,17 +52,45 @@ class HelpersTest(unittest.TestCase):
|
|
|
52
52
|
self.assertEqual([x["temperature"] for x in v], [0.4, 0.6])
|
|
53
53
|
|
|
54
54
|
|
|
55
|
-
class
|
|
55
|
+
class SignalsTest(unittest.TestCase): # reactor-aligned signals
|
|
56
|
+
def test_complexity_port(self):
|
|
57
|
+
# faithful to query-complexity.ts: "and then" multi-step + tech + file + 2 actions
|
|
58
|
+
self.assertEqual(ce.query_complexity("fix the bug in src/a.ts and then update the tests"), "complex")
|
|
59
|
+
self.assertEqual(ce.query_complexity("add a button"), "simple")
|
|
60
|
+
self.assertEqual(ce.query_complexity("implement a redis rate limiter"), "simple")
|
|
61
|
+
self.assertIn(ce.query_complexity("implement a redis rate limiter with tests and config"), ("moderate", "complex"))
|
|
62
|
+
|
|
63
|
+
def test_task_shape(self):
|
|
64
|
+
self.assertEqual(ce.task_shape("prove that x is even"), "reasoning")
|
|
65
|
+
self.assertEqual(ce.task_shape("Which is right? A) x B) y"), "reasoning")
|
|
66
|
+
self.assertEqual(ce.task_shape("refactor the function in app.ts"), "code")
|
|
67
|
+
self.assertEqual(ce.task_shape("what is the capital of france?"), "qa")
|
|
68
|
+
self.assertEqual(ce.task_shape("tell me a story"), "general")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class SelectorTest(unittest.TestCase): # #2 task-shaped
|
|
56
72
|
def test_disabled_or_tools_single(self):
|
|
57
73
|
self.assertEqual(ce.select_recipe(body(), S(enabled=False), False), "single")
|
|
58
74
|
self.assertEqual(ce.select_recipe(body(), S(), True), "single")
|
|
59
75
|
|
|
60
|
-
def
|
|
61
|
-
self.assertEqual(ce.select_recipe(body(), S(recipe="fusion"), False), "fusion")
|
|
76
|
+
def test_explicit_recipe_overrides_signals(self):
|
|
77
|
+
self.assertEqual(ce.select_recipe(body("add a button"), S(recipe="fusion"), False), "fusion")
|
|
78
|
+
self.assertEqual(ce.select_recipe(body("prove x is even"), S(recipe="confidence"), False), "confidence")
|
|
62
79
|
|
|
63
|
-
def
|
|
64
|
-
self.assertEqual(
|
|
65
|
-
|
|
80
|
+
def test_complex_task_routes_to_fusion(self):
|
|
81
|
+
self.assertEqual(
|
|
82
|
+
ce.select_recipe(body("fix the bug in src/a.ts and then update the tests"), S(recipe="auto"), False),
|
|
83
|
+
"fusion",
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
def test_reasoning_task_routes_to_fusion(self):
|
|
87
|
+
self.assertEqual(ce.select_recipe(body("prove that the sum of two evens is even"), S(recipe="auto"), False), "fusion")
|
|
88
|
+
|
|
89
|
+
def test_simple_task_routes_to_confidence(self):
|
|
90
|
+
self.assertEqual(ce.select_recipe(body("add a button"), S(recipe="auto"), False), "confidence")
|
|
91
|
+
|
|
92
|
+
def test_no_backend_never_fusion(self):
|
|
93
|
+
self.assertEqual(ce.select_recipe(body("fix the bug in a.ts and then update tests"), S(recipe="auto", endpoint=""), False), "confidence")
|
|
66
94
|
|
|
67
95
|
|
|
68
96
|
class ApplyConfidenceTest(unittest.TestCase): # #1 / #5
|