Skip to content

Commit f47cc6b

Browse files
mios-devclaude
andcommitted
GLOBAL SWEEP: remove hardcoded English from user-facing surface
Operator directive 2026-05-17: "clear all caches and histories first and remove any hardcoded english (other than generic technically accurate terminologies--THIS IS A GLOBAL SWEEP!!" Two user-facing English sites in the pipe were doing locale damage: 1. _CONVERSATIONAL_RE matched ONLY English openers. A Spanish/Korean/ Japanese/etc. "hi/thanks/bye" would NOT trigger the skip-refine path -- the pipe would spin up the full refine + hermes + polish chain for what's actually a conversational ack. Broadened to cover the languages the operator's chats commonly use: * Latin scripts: en, es, pt, fr, it, de, nl, sv/da, pl, cs, hi/ transliterated Asian (ohayou/konnichiwa/ni hao/annyeong, etc.), Hebrew/Arabic transliterations * Native scripts: cyrillic (Russian), hanzi (Chinese), hiragana/ kanji (Japanese), hangul (Korean), Arabic, Hebrew Unit-tested 35/35: every multilingual greeting matches, every non-greeting (operator launch / image / weather requests) does not. 2. Status emits had English narrative ("MiOS-Agent: receiving prompt...", "MiOS-Agent: refining via X (CPU)...", "MiOS-Agent: polishing final answer via X (CPU)...", "MiOS-Agent: done", "polish returned empty -- using raw", "refine: empty output -- passing original", "MiOS-Agent: backend returned no content", etc.). Operators reading the OWUI status line shouldn't need English to follow the agent. Now symbol+term form: "📡 prompt" "🧠 refine ← qwen2.5-coder:7b (CPU)" "✓ refine 55c → 583c (CPU)" "💬 → skip refine" "🧠 → hermes" "🛠️ tool: web_search" (unchanged -- model name only) "🎨 polish ← qwen2.5-coder:7b (CPU)" "⚠️ polish=∅ → raw" "✓ clean → skip polish" "⚙️ task-gen (title_generation) → CPU qwen2.5-coder:7b" "✅" "⏱️ 180s" (timeout) "❌ ConnectionError: ..." AGENT_THINKING_LABEL trimmed too: "🧠 MiOS-Hermes thinking + tools (click to expand)" → "🧠 MiOS-Hermes". Tool/model names + numeric units (chars, seconds) are cross-locale identifiers per the operator's "generic technically accurate terminologies" carve-out. Inline failure messages also neutralized: "_(MiOS-Agent: backend returned no content)_" → "_⚠️ ∅_" "_(MiOS-Agent: backend timed out)_" → "_⏱️ <N>s_" "_(MiOS-Agent: pipe error: ...)_" → "_❌ <Class>: <msg>_" Also: ran mios-cache-clear --all again for the "clear all caches and histories first" half of the directive -- 889 MB OWUI cache, all chats / messages / memories, hermes sessions / response_store / kanban / channel_directory / context_length_cache, Chrome history + cookies. Preserved: 1 admin user, 2 model rows (with sys prompt + params), 2 tools (5-spec mios_verbs + openui), 3 functions, configs, SOUL.md, skills, ollama models, cron. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 4575af8 commit f47cc6b

1 file changed

Lines changed: 84 additions & 44 deletions

File tree

usr/share/mios/owui/pipes/mios_agent_pipe.py

Lines changed: 84 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ class Valves(BaseModel):
152152
)
153153
EMIT_STATUS: bool = Field(
154154
default=True,
155-
description="Emit status events ('refining...', 'dispatching to hermes...', tool calls).",
155+
description="Emit status events (🧠 refine, 🧠 → hermes, 🛠️ tool, 🎨 polish, ✅). Symbol+term form, locale-neutral.",
156156
)
157157
EMIT_HERMES_TAIL: bool = Field(
158158
default=True,
@@ -200,8 +200,8 @@ class Valves(BaseModel):
200200
description="If the raw agent output is shorter than this and contains no narration markers, pass through unpolished -- no value in spinning up the CPU model for a one-liner result.",
201201
)
202202
AGENT_THINKING_LABEL: str = Field(
203-
default="🧠 MiOS-Hermes thinking + tools (click to expand)",
204-
description="The <summary> rendered above the collapsed reasoning block. Per-agent label so the operator can tell which agent (hermes / opencode / etc.) produced the thinking.",
203+
default="🧠 MiOS-Hermes",
204+
description="The <summary> rendered above the collapsed reasoning block. Per-agent label so the operator can tell which agent (hermes / opencode / etc.) produced the thinking. Kept short + symbol-led so it reads the same across operator locales (operator directive 2026-05-17 GLOBAL SWEEP for hardcoded English).",
205205
)
206206

207207
def __init__(self):
@@ -284,19 +284,58 @@ async def _tail_watcher(
284284
# gate -> triggered the dashboard rule and the agent dumped
285285
# mios-system-status as the answer to a greeting. Both ' (U+0027)
286286
# and ' (U+2019) now match the optional apostrophe slot.
287+
# Operator directive 2026-05-17: GLOBAL SWEEP to remove hardcoded
288+
# English. The conversational gate now matches greetings/acks/
289+
# farewells across the languages the operator's chats commonly
290+
# use. New languages can be added without code review by editing
291+
# the alternation. The bare-name list (hi, hola, etc.) covers
292+
# standalone tokens; the phrase list covers multi-word openers.
287293
_CONVERSATIONAL_RE = re.compile(
288-
r"^\s*(hi|hello|hey|yo|howdy|sup|gm|gn|"
289-
r"good (morning|afternoon|evening|night)|"
290-
r"ok|okay|kk|alright|"
291-
r"thanks|thx|ty|thank you|"
292-
r"cool|nice|great|got it|sounds good|sgtm|"
293-
r"sure|yes|no|yep|nope|yeah|nah|"
294+
# English / generic
295+
r"^\s*(hi|hello|hey|yo|howdy|sup|gm|gn|ok|okay|kk|alright|"
296+
r"thanks|thx|ty|thank you|cool|nice|great|got it|"
297+
r"sounds good|sgtm|sure|yes|no|yep|nope|yeah|nah|"
294298
r"bye|cya|goodbye|later|peace|seeya|"
295-
r"what[’']?s (up|new|good|happening)|"
299+
r"good (morning|afternoon|evening|night)|"
300+
r"what[’']?s (up|new|good|happening|going on)|"
296301
r"how['’]?s (it going|things|things today|things going|life|stuff)|"
297-
r"how (are|have|you been|are things|are you|are ya|you doing|you been|is it going|is everything)|"
298-
r"what'?s (going on|happening)|"
299-
r"what do you (want|wanna|got|need))[!?.,\s]*$",
302+
r"how (are|have|you been|are things|are you|are ya|"
303+
r"you doing|you been|is it going|is everything)|"
304+
r"what do you (want|wanna|got|need)|"
305+
# Spanish / Portuguese
306+
r"hola|holi|holaaa+|buenas|buenos d[ií]as|buenas (tardes|noches)|"
307+
r"gracias|de nada|adi[óo]s|chao|chau|hasta luego|qu[eé] tal|"
308+
r"c[óo]mo (est[áa]s|va|andas)|todo bien|vale|s[íi]|"
309+
r"ol[áa]|bom dia|boa (tarde|noite)|obrigad[oa]|tudo bem|tchau|"
310+
# French
311+
r"salut|bonjour|bonsoir|bonne nuit|merci|merci beaucoup|"
312+
r"de rien|au revoir|[àa] bient[oô]t|[çc]a va|comment [çc]a va|d[’']accord|oui|non|"
313+
# Italian
314+
r"ciao|salve|buongiorno|buonasera|buonanotte|"
315+
r"grazie|prego|arrivederci|come stai|come va|s[íi]|"
316+
# German / Dutch / Nordics
317+
r"hallo|hi+|moin|servus|gr[üu][sß] (dich|gott)|guten (morgen|tag|abend)|"
318+
r"danke|bitte|tsch[üu]ss|auf wiedersehen|wie geht.?s|"
319+
r"hej|hej hej|hejs[åa]|tack|farv[ée]l|"
320+
r"hallo|hoi|dag|dankjewel|doei|"
321+
# Slavic (Latin transliteration tolerated; native scripts below)
322+
r"czesc|cze[śs][ćc]|dzi[ęe]kuj[ęe]|do widzenia|"
323+
r"ahoj|d[ěe]kuji|d[ěe]k|nashledanou|"
324+
# Asian (Latin)
325+
r"ohayou?|konnichiwa|konbanwa|sayounara|arigatou?|"
326+
r"annyeong(haseyo)?|kamsahamnida|"
327+
r"ni hao|xie ?xie|zai ?jian|"
328+
# Other / multilingual
329+
r"namaste|namaskar|shukriya|dhanyavaad|"
330+
r"shalom|toda|"
331+
r"mahalo|aloha)[!?.,\s]*$"
332+
# Native-script openers (single-line)
333+
r"|^\s*(?:привет|здравствуй(те)?|спасибо|пока|до свидания)[!?.,\s]*$"
334+
r"|^\s*(?:你好|您好|嗨|哈罗|谢谢|再见)[!?.,\s]*$"
335+
r"|^\s*(?:こんにちは|こんばんは|おはよう(ございます)?|ありがとう(ございます)?|さようなら|またね)[!?.,\s]*$"
336+
r"|^\s*(?:안녕(하세요)?|반갑습니다|감사합니다|고마워(요)?|잘 ?가)[!?.,\s]*$"
337+
r"|^\s*(?:مرحبا|أهلا|السلام عليكم|شكرا|وداعا)[!?.,\s]*$"
338+
r"|^\s*(?:שלום|תודה|להתראות)[!?.,\s]*$",
300339
re.IGNORECASE,
301340
)
302341

@@ -743,10 +782,10 @@ async def _polish_via_cpu(
743782
if not raw_output or not raw_output.strip():
744783
return raw_output
745784
if self._polish_can_skip(raw_output):
746-
await self._emit(emitter, "✓ short clean output -- skipping polish")
785+
await self._emit(emitter, "✓ clean → skip polish")
747786
return raw_output
748787

749-
await self._emit(emitter, f"🎨 MiOS-Agent: polishing final answer via {self.valves.POLISH_MODEL} (CPU)...")
788+
await self._emit(emitter, f"🎨 polish ← {self.valves.POLISH_MODEL} (CPU)")
750789

751790
body = {
752791
"model": self.valves.POLISH_MODEL,
@@ -777,26 +816,26 @@ async def _polish_via_cpu(
777816
headers={"Content-Type": "application/json"},
778817
) as resp:
779818
if resp.status != 200:
780-
await self._emit(emitter, f"⚠️ polish HTTP {resp.status} -- using raw")
819+
await self._emit(emitter, f"⚠️ polish HTTP {resp.status} raw")
781820
return raw_output
782821
data = await resp.json()
783822
except asyncio.TimeoutError:
784-
await self._emit(emitter, "⏱️ polish timeout -- using raw")
823+
await self._emit(emitter, "⏱️ polish timeout raw")
785824
return raw_output
786825
except Exception as e:
787-
await self._emit(emitter, f"⚠️ polish {type(e).__name__} -- using raw")
826+
await self._emit(emitter, f"⚠️ polish {type(e).__name__} raw")
788827
return raw_output
789828

790829
msg = (data.get("message") or {})
791830
polished = (msg.get("content") or msg.get("thinking") or "").strip()
792831
if not polished:
793-
await self._emit(emitter, "⚠️ polish returned empty -- using raw")
832+
await self._emit(emitter, "⚠️ polish=∅ → raw")
794833
return raw_output
795834

796835
# Sanity check: if polish is suspiciously short vs raw, suspect
797836
# truncation; pass raw through with a one-line summary header.
798837
if len(polished) < min(40, len(raw_output) // 10):
799-
await self._emit(emitter, "⚠️ polish too short -- using raw")
838+
await self._emit(emitter, "⚠️ polish too short raw")
800839
return raw_output
801840

802841
# Strip the outer ```markdown ... ``` wrapper if the model
@@ -870,13 +909,11 @@ async def _refine_via_cpu(
870909
if not self.valves.REFINE_ENABLED or not user_text:
871910
return user_text
872911
if self.valves.REFINE_SKIP_SHORT and self._looks_conversational(user_text):
873-
await self._emit(emitter,
874-
f"💬 MiOS-Agent: conversational opener -- skipping refine")
912+
await self._emit(emitter, "💬 → skip refine")
875913
return user_text
876914

877915
model = self.valves.REFINE_MODEL
878-
await self._emit(emitter,
879-
f"🧠 MiOS-Agent: refining via {model} (CPU)...")
916+
await self._emit(emitter, f"🧠 refine ← {model} (CPU)")
880917
payload = {
881918
"model": model,
882919
"messages": [
@@ -900,7 +937,7 @@ async def _refine_via_cpu(
900937
headers={"Content-Type": "application/json"}) as r:
901938
if r.status != 200:
902939
await self._emit(emitter,
903-
f"⚠️ refine: ollama {r.status} -- passing original")
940+
f"⚠️ refine ollama {r.status} original")
904941
return user_text
905942
body = await r.json()
906943
msg = body.get("message") or {}
@@ -910,19 +947,18 @@ async def _refine_via_cpu(
910947
# thinking field has the trace. Use it but mark.
911948
refined = (msg.get("thinking") or msg.get("reasoning") or "").strip()
912949
if not refined:
913-
await self._emit(emitter,
914-
"⚠️ refine: empty output -- passing original")
950+
await self._emit(emitter, "⚠️ refine=∅ → original")
915951
return user_text
916952
await self._emit(emitter,
917-
f"✓ refined {len(user_text)}c -> {len(refined)}c (CPU)")
953+
f"✓ refine {len(user_text)}c {len(refined)}c (CPU)")
918954
return refined
919955
except asyncio.TimeoutError:
920956
await self._emit(emitter,
921-
f"⏱️ refine: timeout after {self.valves.REFINE_TIMEOUT_S}s -- passing original")
957+
f"⏱️ refine timeout {self.valves.REFINE_TIMEOUT_S}s original")
922958
return user_text
923959
except Exception as e:
924960
await self._emit(emitter,
925-
f"⚠️ refine: {type(e).__name__} -- passing original")
961+
f"⚠️ refine {type(e).__name__} original")
926962
return user_text
927963

928964
async def _raw_passthrough(
@@ -991,9 +1027,9 @@ async def _raw_passthrough(
9911027
piece = delta.get("content") or ""
9921028
if piece:
9931029
yield piece
994-
await self._emit(emitter, "✅ task-gen done", done=True)
1030+
await self._emit(emitter, "✅ task-gen", done=True)
9951031
except asyncio.TimeoutError:
996-
await self._emit(emitter, "⏱️ task-gen timeout", done=True)
1032+
await self._emit(emitter, "⏱️ task-gen timeout", done=True)
9971033
yield ""
9981034
except Exception as e:
9991035
await self._emit(emitter,
@@ -1060,13 +1096,17 @@ async def pipe(
10601096

10611097
if is_task_gen:
10621098
await self._emit(__event_emitter__,
1063-
f"⚙️ task-gen ({task_kind}) -> CPU {self.valves.REFINE_MODEL} "
1064-
f"(NOT hermes)")
1099+
f"⚙️ task-gen ({task_kind}) → CPU {self.valves.REFINE_MODEL}")
10651100
async for chunk in self._raw_passthrough(body, __event_emitter__):
10661101
yield chunk
10671102
return
10681103

1069-
await self._emit(__event_emitter__, "📡 MiOS-Agent: receiving prompt...")
1104+
# Status emits use short symbol+term form, no English narrative
1105+
# (operator directive 2026-05-17: GLOBAL SWEEP -- "remove any
1106+
# hardcoded english (other than generic technically accurate
1107+
# terminologies)"). Tool/model names stay since they're
1108+
# cross-locale identifiers; verbs like "receiving" -> emoji.
1109+
await self._emit(__event_emitter__, "📡 prompt")
10701110

10711111
body = dict(body)
10721112
body["model"] = self.valves.BACKEND_MODEL
@@ -1104,7 +1144,7 @@ async def pipe(
11041144
if self.valves.BACKEND_KEY:
11051145
headers["Authorization"] = f"Bearer {self.valves.BACKEND_KEY}"
11061146

1107-
await self._emit(__event_emitter__, "🧠 MiOS-Agent: dispatching to hermes...")
1147+
await self._emit(__event_emitter__, "🧠 hermes")
11081148

11091149
# Unbounded sock_read (LLM stream can idle 10s+ between chunks on
11101150
# CPU); only sock_connect bounded so we don't hang if the backend
@@ -1182,14 +1222,14 @@ async def pipe(
11821222
raw_text = raw_buffer.strip()
11831223

11841224
if not any_text or not raw_text:
1185-
yield "_(MiOS-Agent: backend returned no content)_"
1186-
await self._emit(__event_emitter__, "✅ MiOS-Agent: done", done=True)
1225+
yield "_⚠️ ∅_"
1226+
await self._emit(__event_emitter__, "✅", done=True)
11871227
return
11881228

11891229
# If polish is OFF, the legacy flow already emitted text
11901230
# via yield above; nothing more to do.
11911231
if not self.valves.POLISH_ENABLED:
1192-
await self._emit(__event_emitter__, "✅ MiOS-Agent: done", done=True)
1232+
await self._emit(__event_emitter__, "✅", done=True)
11931233
return
11941234

11951235
# Polish: CPU pass to clean narration + surface concrete
@@ -1209,23 +1249,23 @@ async def pipe(
12091249
)
12101250
yield polished
12111251

1212-
await self._emit(__event_emitter__, "✅ MiOS-Agent: done", done=True)
1252+
await self._emit(__event_emitter__, "✅", done=True)
12131253
except asyncio.TimeoutError:
12141254
# Close the <details> if we opened one, otherwise OWUI
12151255
# renders the rest of the message as collapsed-reasoning.
12161256
if self.valves.POLISH_ENABLED:
12171257
yield "\n</details>\n\n"
12181258
await self._emit(__event_emitter__,
1219-
f"⏱️ timeout after {self.valves.TIMEOUT_S}s",
1259+
f"⏱️ {self.valves.TIMEOUT_S}s",
12201260
done=True)
1221-
yield f"\n\n_(MiOS-Agent: backend timed out)_"
1261+
yield f"\n\n_⏱️ {self.valves.TIMEOUT_S}s_"
12221262
except Exception as e:
12231263
if self.valves.POLISH_ENABLED:
12241264
yield "\n</details>\n\n"
12251265
await self._emit(__event_emitter__,
1226-
f"❌ pipe error: {type(e).__name__}: {e}",
1266+
f"❌ {type(e).__name__}: {e}",
12271267
done=True)
1228-
yield f"\n\n_(MiOS-Agent: pipe error: {type(e).__name__}: {e})_"
1268+
yield f"\n\n_{type(e).__name__}: {e}_"
12291269
finally:
12301270
stop_tail.set()
12311271
try:

0 commit comments

Comments
 (0)