stub_orchestrator_service.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. from app.schemas.observation import ObservationSummary
  2. from app.schemas.orchestration import (
  3. Hypothesis,
  4. OrchestratorPlan,
  5. PlannedExperiment,
  6. PlannedNode,
  7. StopDecision,
  8. )
  9. class StubOrchestratorService:
  10. def create_initial_plan(self, observation: ObservationSummary) -> OrchestratorPlan:
  11. tags = set(observation.tags)
  12. looks_periodic = "periodic" in tags or observation.duration_ms >= 4500
  13. looks_mixed = "mixed" in tags
  14. hypotheses = [
  15. Hypothesis(
  16. id="periodic-alert-h1",
  17. label="Periodic alert tone",
  18. rationale=(
  19. "Observation metadata and initial capture profile suggest a "
  20. "repeatable alert-like signal rather than a diffuse ambient event."
  21. ),
  22. confidence=0.78 if looks_periodic else 0.56,
  23. related_signal_profiles=["periodic_pulse", "steady_tonal"],
  24. ),
  25. Hypothesis(
  26. id="mechanical-sequence-h2",
  27. label="Mechanical click sequence",
  28. rationale=(
  29. "A secondary possibility is a short repeating mechanical event "
  30. "pattern with less tonal stability."
  31. ),
  32. confidence=0.33 if looks_periodic else 0.48,
  33. related_signal_profiles=["multi_event_sequence", "transient_event"],
  34. ),
  35. ]
  36. if looks_mixed:
  37. hypotheses.append(
  38. Hypothesis(
  39. id="mixed-source-h3",
  40. label="Mixed source contamination",
  41. rationale=(
  42. "Capture tags indicate the possibility of overlapping sources, "
  43. "which could distort classifier confidence."
  44. ),
  45. confidence=0.42,
  46. related_signal_profiles=["mixed_source"],
  47. )
  48. )
  49. experiments = [
  50. PlannedExperiment(
  51. hypothesis_id="periodic-alert-h1",
  52. goal="Validate periodic repeat structure",
  53. preferred_chain_id="periodic_validation_chain",
  54. pipeline=[
  55. PlannedNode(module_id="segment_repeat_based", params={}),
  56. PlannedNode(
  57. module_id="autocorrelation_period_check",
  58. params={"window_ms": 1200},
  59. ),
  60. PlannedNode(
  61. module_id="cepstrum_period_check",
  62. params={"min_interval_ms": 200, "max_interval_ms": 1000},
  63. ),
  64. ],
  65. expected_evidence=[
  66. "repeat_interval_ms",
  67. "repeat_stability",
  68. "periodicity_confidence",
  69. ],
  70. stop_if=["repeat_stability >= 0.85"],
  71. ),
  72. PlannedExperiment(
  73. hypothesis_id="periodic-alert-h1",
  74. goal="Check tonal stability of the leading hypothesis",
  75. preferred_chain_id="harmonic_validation_chain",
  76. pipeline=[
  77. PlannedNode(module_id="spectrogram_extract", params={}),
  78. PlannedNode(
  79. module_id="f0_tracking",
  80. params={"frame_hop_ms": 20},
  81. ),
  82. PlannedNode(
  83. module_id="harmonicity_check",
  84. params={"harmonic_threshold": 0.65},
  85. ),
  86. ],
  87. expected_evidence=[
  88. "f0_track",
  89. "harmonic_ratio",
  90. "tonal_stability",
  91. ],
  92. stop_if=["tonal_stability >= 0.70"],
  93. ),
  94. ]
  95. if looks_mixed:
  96. experiments.append(
  97. PlannedExperiment(
  98. hypothesis_id="mixed-source-h3",
  99. goal="Check whether source separation improves confidence",
  100. preferred_chain_id="mixed_source_chain",
  101. pipeline=[
  102. PlannedNode(module_id="denoise_basic", params={}),
  103. PlannedNode(
  104. module_id="source_separation_basic",
  105. params={"max_sources": 2},
  106. ),
  107. PlannedNode(
  108. module_id="reclassify_with_alt_window",
  109. params={"window_ms": 800},
  110. ),
  111. ],
  112. expected_evidence=[
  113. "mixed_source_probability",
  114. "post_separation_top_k",
  115. ],
  116. stop_if=["mixed_source_probability >= 0.60"],
  117. )
  118. )
  119. return OrchestratorPlan(
  120. hypotheses=hypotheses,
  121. experiments=experiments,
  122. stop_decision=StopDecision(
  123. should_stop=False,
  124. reason="Initial plan created. Probe and validation experiments should run first.",
  125. ),
  126. notes=[
  127. "Stub orchestrator generated a plan from observation metadata and tags.",
  128. ],
  129. )