(events: &[EventRow])
| 478 | } |
| 479 | |
| 480 | fn build_hints(events: &[EventRow]) -> Vec<String> { |
| 481 | let mut hints = Vec::new(); |
| 482 | let mut category_tokens: HashMap<String, usize> = HashMap::new(); |
| 483 | let mut errors: HashMap<String, usize> = HashMap::new(); |
| 484 | for event in events { |
| 485 | let tokens = event_tokens(event); |
| 486 | if let Some(category) = &event.category { |
| 487 | *category_tokens.entry(category.clone()).or_default() += tokens; |
| 488 | } |
| 489 | if let Some(error) = &event.error_code { |
| 490 | *errors.entry(error.clone()).or_default() += 1; |
| 491 | } |
| 492 | } |
| 493 | let retrieval_tokens = category_tokens |
| 494 | .get("retrieval") |
| 495 | .copied() |
| 496 | .unwrap_or_default(); |
| 497 | let code_tokens = category_tokens |
| 498 | .get("code_generation") |
| 499 | .copied() |
| 500 | .unwrap_or_default(); |
| 501 | if retrieval_tokens > code_tokens.saturating_mul(2) && retrieval_tokens > 0 { |
| 502 | hints.push("Retrieval is consuming materially more tokens than code generation. Tighten context breadth or lower max tokens for lookup-heavy routes.".to_string()); |
| 503 | } |
| 504 | if errors.values().any(|count| *count >= 2) { |
| 505 | hints.push("The same error repeated in this task. Mark these calls as wasted and change strategy earlier on the next retry.".to_string()); |
| 506 | } |
| 507 | if category_tokens |
| 508 | .get("wasted_calls") |
| 509 | .copied() |
| 510 | .unwrap_or_default() |
| 511 | > 0 |
| 512 | { |
| 513 | hints.push("Failed LLM calls consumed prompt budget. Review provider selection, retry policy, or model fallback order.".to_string()); |
| 514 | } |
| 515 | if hints.is_empty() { |
| 516 | hints.push("No obvious hotspot was detected from the current event stream.".to_string()); |
| 517 | } |
| 518 | hints |
| 519 | } |
| 520 | |
| 521 | fn event_tokens(event: &EventRow) -> usize { |
| 522 | event |
no test coverage detected
searching dependent graphs…