Download src/agents/slow_path/progress.py from DataEyond/Agentic-Service-Data-Eyond-Catalog: direct link, hf CLI and curl.
- Browser
- Download file 5.24 kB
-
https://huggingface.co/spaces/DataEyond/Agentic-Service-Data-Eyond-Catalog/resolve/main/src/agents/slow_path/progress.py
- Command line
-
hf download hf://spaces/DataEyond/Agentic-Service-Data-Eyond-Catalog/src/agents/slow_path/progress.py
-
curl -L -o progress.py https://huggingface.co/spaces/DataEyond/Agentic-Service-Data-Eyond-Catalog/resolve/main/src/agents/slow_path/progress.py
5.24 kB
| """Bilingual labels for the slow path's SSE `status` events (A22). | |
| The slow path used to emit three stage-level pings for a ~12s turn — "Planning", | |
| "Running N steps", "Composing". Past ~10 seconds what a waiting user needs is not a | |
| faster answer but evidence that something is happening (Miller's attention threshold), | |
| so the TaskRunner now reports each wave as it starts and these labels name it. | |
| Two rules this module exists to enforce: | |
| - **Status text is user-facing, so it follows the reply language.** The rest of a plan | |
| is an internal English artifact (`objective`, `goal_restated`, `success_criteria` — | |
| see the exemplar note in `planner/examples.py`), which is exactly why a task's | |
| `objective` is NOT used as status text: it would show English prose to an Indonesian | |
| user. The activity vocabulary below is a bounded, translated set instead. | |
| - **Unknown tools degrade, never break.** A tool with no entry falls back to the | |
| generic "analysing" label, so the six tools still to land (DEV_PLAN §0.9) produce | |
| sensible status before anyone touches this file. | |
| The wire contract is unchanged: `status` is still `text`, still optional, still safe | |
| for the frontend to ignore (API_CONTRACT §chat). This is more of the same event, not a | |
| new shape. | |
| """ | |
| from __future__ import annotations | |
| from collections.abc import Sequence | |
| from typing import Any | |
| def lang_key(reply_language: str | None) -> str: | |
| """"id" for Indonesian, "en" otherwise — the same test `refusals.py` uses.""" | |
| return "id" if reply_language == "Indonesian" else "en" | |
| # --------------------------------------------------------------------------- # | |
| # Stage pings (coordinator) | |
| # --------------------------------------------------------------------------- # | |
| PLANNING = { | |
| "en": "Planning the analysis…", | |
| "id": "Menyusun rencana analisis…", | |
| } | |
| RUNNING = { | |
| "en": "Running {n} analysis steps…", | |
| "id": "Menjalankan {n} langkah analisis…", | |
| } | |
| COMPOSING = { | |
| "en": "Composing the answer…", | |
| "id": "Menyusun jawaban…", | |
| } | |
| def stage(table: dict[str, str], reply_language: str | None, **fmt: Any) -> str: | |
| """Render one stage ping in the reply language.""" | |
| return table[lang_key(reply_language)].format(**fmt) | |
| # --------------------------------------------------------------------------- # | |
| # Per-wave step labels (TaskRunner) | |
| # --------------------------------------------------------------------------- # | |
| _STEP = { | |
| "en": "Step {pos} of {total} — {activity}", | |
| "id": "Langkah {pos} dari {total} — {activity}", | |
| } | |
| # Bounded, translated vocabulary. Keyed by tool name; anything absent uses _DEFAULT. | |
| _ACTIVITY: dict[str, dict[str, str]] = { | |
| "check_data": {"en": "checking available data", "id": "memeriksa data yang tersedia"}, | |
| "check_knowledge": { | |
| "en": "checking available documents", | |
| "id": "memeriksa dokumen yang tersedia", | |
| }, | |
| "retrieve_data": {"en": "retrieving data", "id": "mengambil data"}, | |
| "retrieve_knowledge": {"en": "searching documents", "id": "mencari di dokumen"}, | |
| "render_chart": {"en": "building the chart", "id": "membuat grafik"}, | |
| "analyze_aggregate": {"en": "aggregating", "id": "menghitung agregat"}, | |
| "analyze_descriptive": {"en": "summarising the data", "id": "meringkas data"}, | |
| "analyze_correlation": {"en": "checking correlation", "id": "memeriksa korelasi"}, | |
| "analyze_trend": {"en": "analysing the trend", "id": "menganalisis tren"}, | |
| "analyze_merge": {"en": "combining the tables", "id": "menggabungkan tabel"}, | |
| "analyze_forecast": {"en": "forecasting", "id": "membuat proyeksi"}, | |
| "analyze_anomaly": {"en": "detecting anomalies", "id": "mendeteksi anomali"}, | |
| } | |
| _DEFAULT = {"en": "analysing", "id": "menganalisis"} | |
| def _task_tool(task: Any) -> str | None: | |
| """The tool that gives a task its purpose — the LAST call in its chain. | |
| A task is an ordered chain (`retrieve_data` → `analyze_trend`); the last call is | |
| what the task is *for*, so it names the step better than the first. | |
| """ | |
| calls = getattr(task, "tool_calls", None) or [] | |
| return getattr(calls[-1], "tool", None) if calls else None | |
| def _activity(tasks: Sequence[Any], lang: str) -> str: | |
| """Label a wave: one tool names itself, two are joined, more stay generic.""" | |
| seen: dict[str, None] = {} | |
| for task in tasks: | |
| tool = _task_tool(task) | |
| if tool: | |
| seen.setdefault(_ACTIVITY.get(tool, _DEFAULT)[lang], None) | |
| labels = list(seen) | |
| if not labels: | |
| return _DEFAULT[lang] | |
| if len(labels) == 1: | |
| return labels[0] | |
| if len(labels) == 2: | |
| return " + ".join(labels) | |
| return _DEFAULT[lang] | |
| def step_label( | |
| tasks: Sequence[Any], completed: int, total: int, reply_language: str | None | |
| ) -> str: | |
| """"Step 2 of 4 — retrieving data", or "Step 2–3 of 4 — …" for a parallel wave. | |
| `completed` is how many tasks finished before this wave, so the positions are | |
| 1-based and contiguous across waves. | |
| """ | |
| lang = lang_key(reply_language) | |
| first, last = completed + 1, completed + len(tasks) | |
| pos = str(first) if first >= last else f"{first}–{last}" | |
| return _STEP[lang].format(pos=pos, total=total, activity=_activity(tasks, lang)) | |