Преглед на файлове

Updated flow for decision model

Efren Yevale Varela преди 1 месец
родител
ревизия
0599b3edd5
променени са 8 файла, в които са добавени 53 реда и са изтрити 16 реда
  1. 3 2
      config.example.toml
  2. 6 3
      docs/CONFIGURATION.md
  3. 3 2
      docs/TRIAGE.md
  4. 2 0
      src/stt_daemon/assistant/laya_lane.py
  5. 3 3
      src/stt_daemon/assistant/triage.py
  6. 6 2
      src/stt_daemon/config.py
  7. 6 3
      tests/test_config.py
  8. 24 1
      tests/test_laya_lane.py

+ 3 - 2
config.example.toml

@@ -94,8 +94,9 @@ triage_system_prompt     = "You extract structured signals from a spoken convers
 [triage]
 enabled              = false
 questions_file       = "questions.json"
-window               = 8
-state_char_budget    = 1200
+window               = 500
+state_char_budget    = 3000
+predict_max_tokens   = 1024
 confidence_field     = "answer_confidence"
 escalate_threshold   = 0.6
 choice_min_confidence = 0.85

+ 6 - 3
docs/CONFIGURATION.md

@@ -118,8 +118,9 @@ triage_system_prompt     = "You extract structured signals from a spoken convers
 [triage]                                    # Display-only signal panel (Laya lane; no socket, no extra process)
 enabled              = false                # Master gate: false = no lane, no panel, behavior identical to today
 questions_file       = "questions.json"     # Laya questions JSON in the cwd; missing/invalid -> no_questions
-window               = 8                    # Recent transcribed utterances condensed for each classification
-state_char_budget    = 1200                 # Char cap for the condensed state sent to Laya
+window               = 500                    # Utterances available per classification; the char budget is the real limiter
+state_char_budget    = 3000                 # Char cap (~750 tokens) for the condensed state sent to Laya
+predict_max_tokens   = 1024                 # Max tokens sent to Laya; the multilingual checkpoint supports 1024; state shares this budget with the ~192-token question head (~830-token state room)
 confidence_field     = "answer_confidence"  # Gating confidence: "answer_confidence" (calibrated max(p)) | "confidence"
 escalate_threshold   = 0.6                  # noul answers at or above this p escalate to extraction
 choice_min_confidence = 0.85                # choice answers below this confidence escalate
@@ -150,7 +151,9 @@ in-process lane, then, when signals escalate, asks the `[llm]` lane for a struct
 extraction (`extract_enabled`). `device = ""` means auto: it maps to `None`, so Laya picks the
 default device for the multilingual checkpoint. `preload = ["multilingual"]` is the fixed default
 and `default_checkpoint = "multilingual"`; the multilingual checkpoint is pinned for every call
-(there is no `.en` English-only variant). `confidence_field` selects which of Laya's two
+(there is no `.en` English-only variant). `predict_max_tokens` is the Laya token budget (the
+multilingual checkpoint supports 1024); the condensed state shares it with the ~192-token
+question head, leaving ~830 tokens of room for state at the default. `confidence_field` selects which of Laya's two
 confidence values gates escalation, with `answer_confidence` (the calibrated `max(p)`) as the
 default. With `enabled = false` (the default) the lane is never built and the system behaves
 exactly as before.

+ 3 - 2
docs/TRIAGE.md

@@ -207,8 +207,9 @@ file.
 |---|---|---|
 | `enabled` | `false` | Master gate. `false` means no lane, no panel, and behavior identical to before. |
 | `questions_file` | `"questions.json"` | Laya questions JSON in the working directory. Missing or invalid means `no_questions`. |
-| `window` | `8` | Number of recent transcribed utterances condensed for each classification. |
-| `state_char_budget` | `1200` | Character cap for the condensed state sent to Laya. |
+| `window` | `500` | Recent transcribed utterances available per classification; the char budget is the real limiter. |
+| `state_char_budget` | `3000` | Character cap (~750 tokens) for the condensed state sent to Laya. |
+| `predict_max_tokens` | `1024` | Max tokens sent to Laya; the multilingual checkpoint supports 1024; state shares this budget with the ~192-token question head (~830-token state room). |
 | `confidence_field` | `"answer_confidence"` | Which Laya confidence gates escalation: `answer_confidence` (calibrated `max(p)`) or `confidence` (normalized entropy). |
 | `escalate_threshold` | `0.6` | A `noul` answer at or above this `p` escalates to extraction. |
 | `choice_min_confidence` | `0.85` | A `choice` answer below this confidence escalates. |

+ 2 - 0
src/stt_daemon/assistant/laya_lane.py

@@ -174,6 +174,7 @@ class LayaLane:
                 _WARMUP_STATE,
                 _WARMUP_QUESTIONS,
                 model=self._cfg.default_checkpoint,
+                max_len=getattr(self._cfg, "predict_max_tokens", 1024),
             )
         except ImportError as e:
             logger.warning("LayaLane: laya not installed: %s", e)
@@ -211,6 +212,7 @@ class LayaLane:
                     questions,
                     model=self._cfg.default_checkpoint,
                     lang=lang,
+                    max_len=getattr(self._cfg, "predict_max_tokens", 1024),
                 )
             except Exception as e:  # noqa: BLE001 - one bad predict gets None payload
                 logger.error("LayaLane predict failed: %s", e)

+ 3 - 3
src/stt_daemon/assistant/triage.py

@@ -27,7 +27,7 @@ Public API (consumed by the assistant service layer):
         def refresh(self, state: str, reason: str = "") -> None
 
 ``cfg`` is duck-typed (attribute access only, never imported): it provides
-``questions_file``, ``state_char_budget`` (default 1200),
+``questions_file``, ``state_char_budget`` (default 3000),
 ``confidence_field`` (default ``"answer_confidence"``),
 ``escalate_threshold`` (default 0.6) and ``choice_min_confidence``
 (default 0.85).
@@ -138,9 +138,9 @@ class TriageAnalyzer:
 
     def _budget(self) -> int:
         try:
-            return int(getattr(self._cfg, "state_char_budget", 1200))
+            return int(getattr(self._cfg, "state_char_budget", 3000))
         except (TypeError, ValueError):
-            return 1200
+            return 3000
 
     def _confidence_field(self) -> str:
         return getattr(self._cfg, "confidence_field", "answer_confidence")

+ 6 - 2
src/stt_daemon/config.py

@@ -161,8 +161,9 @@ class TriageConfig:
     """Laya triage-panel configuration (in-process lane, multilingual pinned)."""
     enabled: bool = False
     questions_file: str = "questions.json"
-    window: int = 8
-    state_char_budget: int = 1200
+    window: int = 500
+    state_char_budget: int = 3000
+    predict_max_tokens: int = 1024
     confidence_field: str = "answer_confidence"
     escalate_threshold: float = 0.6
     choice_min_confidence: float = 0.85
@@ -453,6 +454,9 @@ def load_config(path: str | None = None) -> Config:
                 state_char_budget=triage_data.get(
                     "state_char_budget", cfg.triage.state_char_budget
                 ),
+                predict_max_tokens=triage_data.get(
+                    "predict_max_tokens", cfg.triage.predict_max_tokens
+                ),
                 confidence_field=triage_data.get(
                     "confidence_field", cfg.triage.confidence_field
                 ),

+ 6 - 3
tests/test_config.py

@@ -524,8 +524,9 @@ def test_triage_defaults():
 
     assert cfg.triage.enabled is False
     assert cfg.triage.questions_file == "questions.json"
-    assert cfg.triage.window == 8
-    assert cfg.triage.state_char_budget == 1200
+    assert cfg.triage.window == 500
+    assert cfg.triage.state_char_budget == 3000
+    assert cfg.triage.predict_max_tokens == 1024
     assert cfg.triage.confidence_field == "answer_confidence"
     assert cfg.triage.escalate_threshold == 0.6
     assert cfg.triage.choice_min_confidence == 0.85
@@ -545,6 +546,7 @@ enabled = true
 questions_file = "custom_questions.json"
 window = 4
 state_char_budget = 800
+predict_max_tokens = 768
 confidence_field = "confidence"
 escalate_threshold = 0.7
 choice_min_confidence = 0.9
@@ -565,6 +567,7 @@ default_checkpoint = "english"
     assert cfg.triage.questions_file == "custom_questions.json"
     assert cfg.triage.window == 4
     assert cfg.triage.state_char_budget == 800
+    assert cfg.triage.predict_max_tokens == 768
     assert cfg.triage.confidence_field == "confidence"
     assert cfg.triage.escalate_threshold == 0.7
     assert cfg.triage.choice_min_confidence == 0.9
@@ -592,7 +595,7 @@ window = 3
 
     assert "triag" in caplog.text
     assert cfg.triage.enabled is False
-    assert cfg.triage.window == 8
+    assert cfg.triage.window == 500
 
 
 def test_triage_config_has_no_struck_keys():

+ 24 - 1
tests/test_laya_lane.py

@@ -22,6 +22,7 @@ def make_cfg(**overrides):
         "device": "",
         "preload": ["multilingual"],
         "default_checkpoint": "multilingual",
+        "predict_max_tokens": 1024,
     }
     cfg.update(overrides)
     return SimpleNamespace(**cfg)
@@ -50,7 +51,8 @@ class FakeRouter:
         self.max_concurrent = max(self.max_concurrent, self.current)
         try:
             self.predict_log.append(
-                {"state": state, "questions": questions, "model": model, "lang": lang}
+                {"state": state, "questions": questions, "model": model, "lang": lang,
+                 "kwargs": kwargs}
             )
             if "_warmup" in questions:
                 # loader warmup: never gated, returns immediately
@@ -492,6 +494,27 @@ def test_repeated_submit_drop_stop_leaves_no_thread_alive():
     assert lingering == []
 
 
+def test_predict_passes_max_len_from_cfg():
+    calls = {"count": 0}
+    factory = make_factory(calls)
+    lane = LayaLane(make_cfg(predict_max_tokens=768), agent_factory=factory)
+    lane.start()
+    try:
+        assert wait_until(lane.ready)
+        router = factory.routers[0]
+        # loader warmup carries the budget too
+        assert router.predict_log[0]["kwargs"].get("max_len") == 768
+        router.predict_log.clear()
+        assert lane.submit("hello world", {"q": 1}, "es", 1) is True
+        assert wait_until(lambda: len(router.predict_log) >= 1, timeout=5.0)
+        call = router.predict_log[0]
+        assert call["kwargs"].get("max_len") == 768
+        assert call["model"] == "multilingual"
+        assert call["lang"] == "es"
+    finally:
+        lane.stop()
+
+
 # -- todo 5: multilingual pin + lang + resource measurement ------------
 
 def test_multilingual_pin_lang_and_resources():