Predict tier from text structure using v1's learned classifier.
| 45 | |
| 46 | |
| 47 | class StructuralSignal: |
| 48 | """Predict tier from text structure using v1's learned classifier.""" |
| 49 | |
| 50 | def predict(self, row: dict[str, Any]) -> TierVote: |
| 51 | messages = row.get("messages", []) |
| 52 | text = _extract_last_user_message(messages) |
| 53 | if not text.strip(): |
| 54 | return TierVote(tier_id=None, confidence=0.0) |
| 55 | |
| 56 | # Signal B is intended to classify the current user ask. Claude Code |
| 57 | # injects a large system prompt and tool scaffold into every request; |
| 58 | # including all of that text makes trivial prompts like "hello" look |
| 59 | # complex. Short system constraints still matter, e.g. JSON output. |
| 60 | system_prompt = _compact_system_prompt_for_classification(messages) |
| 61 | result = classify(text, system_prompt=system_prompt) |
| 62 | tier_id = _map_v1_to_v2_tier_id(result.tier, result.complexity) |
| 63 | confidence = max(0.0, min(1.0, result.confidence)) |
| 64 | |
| 65 | # Dampen confidence for very short prompts — structural features |
| 66 | # are unreliable when there's little text to analyze. |
| 67 | # Use word count for Latin scripts, character count for CJK. |
| 68 | length_text = text if not system_prompt else f"{system_prompt}\n\n{text}" |
| 69 | word_count = len(length_text.split()) |
| 70 | if word_count <= 2 and len(length_text) > 15: |
| 71 | # CJK or no-space script: use character length instead |
| 72 | word_count = len(length_text) // 3 # rough CJK word estimate |
| 73 | if word_count <= 8: |
| 74 | confidence = min(confidence, 0.50) |
| 75 | elif word_count <= 15: |
| 76 | confidence = min(confidence, 0.70) |
| 77 | if system_prompt and tier_id is not None and tier_id >= 1: |
| 78 | confidence = max(confidence, 0.70) |
| 79 | |
| 80 | if tier_id is None: |
| 81 | return TierVote(tier_id=None, confidence=confidence) |
| 82 | |
| 83 | return TierVote(tier_id=tier_id, confidence=confidence) |
no outgoing calls