ifieryarrows commited on
Commit
89770cf
·
verified ·
1 Parent(s): 88aa535

Sync from GitHub (tests passed)

Browse files
Files changed (1) hide show
  1. app/commentary.py +34 -2
app/commentary.py CHANGED
@@ -138,6 +138,23 @@ def _deterministic_stance_from_inputs(
138
  return "NEUTRAL"
139
 
140
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
  def _build_sentiment_regime_line(sentiment_index: float, sentiment_label: str) -> str:
142
  """One risk line describing the sentiment regime without inventing polarity.
143
 
@@ -861,6 +878,7 @@ DATA INPUTS:
861
  Return valid JSON with exactly two keys: "stance" and "commentary"
862
 
863
  **stance**: Must be one of: BULLISH, BEARISH, NEUTRAL
 
864
  - Derive the stance from the news sentiment regime: Bullish -> BULLISH, Bearish -> BEARISH, Neutral (balanced) -> NEUTRAL. The 5-day TFT-ASRO outlook informs the commentary and synthesis, never the stance.
865
  - Where both models describe the same thing, the TFT-ASRO 5-day view takes priority; the XGBoost next-day view is a diagnostic and must be labelled as such.
866
 
@@ -995,7 +1013,14 @@ Write as a seasoned commodities strategist would for institutional clients—pre
995
  try:
996
  stance, commentary = _parse_commentary_payload(content)
997
  logger.info("AI commentary generated successfully (%s chars)", len(commentary))
998
- return CommentaryGenerationResult(commentary, stance, "llm", actual_model, None, attempts)
 
 
 
 
 
 
 
999
  except Exception as parse_exc:
1000
  logger.warning("Commentary JSON parse failed, attempting repair: %s", parse_exc)
1001
  repaired, repair_data = await _repair_commentary(content)
@@ -1008,7 +1033,14 @@ Write as a seasoned commodities strategist would for institutional clients—pre
1008
  )
1009
  attempts = [*attempts, *(repair_meta.get("attempts") or [])]
1010
  logger.info("AI commentary generated via JSON repair (%s chars)", len(commentary))
1011
- return CommentaryGenerationResult(commentary, stance, "llm_repaired", actual_model, None, attempts)
 
 
 
 
 
 
 
1012
  except Exception as exc:
1013
  logger.warning("Commentary generation failed, using deterministic fallback: %s", exc)
1014
  return CommentaryGenerationResult(
 
138
  return "NEUTRAL"
139
 
140
 
141
+ def _resolve_stance(llm_stance: str, derived_stance: str) -> str:
142
+ """The news regime owns the stance; the model's answer is advisory only.
143
+
144
+ Prompt rules cannot guarantee the model's JSON, and a stance that disagrees
145
+ with the label rendered next to the same sentiment index would put the pet
146
+ in a mood the dashboard contradicts. Divergences are logged so prompt
147
+ regressions stay visible.
148
+ """
149
+ if llm_stance != derived_stance:
150
+ logger.warning(
151
+ "LLM stance %s disagrees with the news-derived %s; publishing the derived stance",
152
+ llm_stance,
153
+ derived_stance,
154
+ )
155
+ return derived_stance
156
+
157
+
158
  def _build_sentiment_regime_line(sentiment_index: float, sentiment_label: str) -> str:
159
  """One risk line describing the sentiment regime without inventing polarity.
160
 
 
878
  Return valid JSON with exactly two keys: "stance" and "commentary"
879
 
880
  **stance**: Must be one of: BULLISH, BEARISH, NEUTRAL
881
+ - Stance for this run is fixed at {deterministic_stance}: the news sentiment regime above decides it, not price direction. Do not contradict it anywhere in the commentary.
882
  - Derive the stance from the news sentiment regime: Bullish -> BULLISH, Bearish -> BEARISH, Neutral (balanced) -> NEUTRAL. The 5-day TFT-ASRO outlook informs the commentary and synthesis, never the stance.
883
  - Where both models describe the same thing, the TFT-ASRO 5-day view takes priority; the XGBoost next-day view is a diagnostic and must be labelled as such.
884
 
 
1013
  try:
1014
  stance, commentary = _parse_commentary_payload(content)
1015
  logger.info("AI commentary generated successfully (%s chars)", len(commentary))
1016
+ return CommentaryGenerationResult(
1017
+ commentary,
1018
+ _resolve_stance(stance, deterministic_stance),
1019
+ "llm",
1020
+ actual_model,
1021
+ None,
1022
+ attempts,
1023
+ )
1024
  except Exception as parse_exc:
1025
  logger.warning("Commentary JSON parse failed, attempting repair: %s", parse_exc)
1026
  repaired, repair_data = await _repair_commentary(content)
 
1033
  )
1034
  attempts = [*attempts, *(repair_meta.get("attempts") or [])]
1035
  logger.info("AI commentary generated via JSON repair (%s chars)", len(commentary))
1036
+ return CommentaryGenerationResult(
1037
+ commentary,
1038
+ _resolve_stance(stance, deterministic_stance),
1039
+ "llm_repaired",
1040
+ actual_model,
1041
+ None,
1042
+ attempts,
1043
+ )
1044
  except Exception as exc:
1045
  logger.warning("Commentary generation failed, using deterministic fallback: %s", exc)
1046
  return CommentaryGenerationResult(