neuralworm commited on
Commit
21e8595
·
1 Parent(s): a345062
app.py CHANGED
@@ -18,31 +18,37 @@ def run_and_display(
18
  prompt_type: str,
19
  seed: int,
20
  num_steps: int,
 
 
21
  progress=gr.Progress(track_tqdm=True)
22
  ):
23
  """
24
- Führt die neue seismische Analyse durch und visualisiert die internen Dynamiken.
25
  """
26
  try:
27
  results = run_seismic_analysis(
28
- model_id, prompt_type, int(seed), int(num_steps), progress
 
 
 
 
 
 
29
  )
30
 
31
  verdict = results.get("verdict", "Analysis complete.")
32
  stats = results.get("stats", {})
33
  deltas = results.get("state_deltas", [])
34
 
35
- # Erstelle einen DataFrame für den Plot
36
  df = pd.DataFrame({
37
  "Internal Step": range(len(deltas)),
38
  "State Change (Delta)": deltas
39
  })
40
 
41
- # Erstelle eine Zusammenfassung der Statistiken
42
  stats_md = f"### Statistical Signature\n"
43
- stats_md += f"- **Mean Delta:** {stats.get('mean_delta', 0):.4f} (Avg. cognitive activity)\n"
44
- stats_md += f"- **Std Dev Delta:** {stats.get('std_delta', 0):.4f} (Volatility of thought)\n"
45
- stats_md += f"- **Max Delta:** {stats.get('max_delta', 0):.4f} (Peak cognitive shift)\n"
46
 
47
  return f"{verdict}\n\n{stats_md}", df, results
48
 
@@ -51,22 +57,23 @@ def run_and_display(
51
  return f"### ❌ Analysis Failed\nAn unexpected error occurred:\n\n```\n{error_str}\n```", pd.DataFrame(), {}
52
 
53
  # --- Gradio App Definition ---
54
- with gr.Blocks(theme=theme, title="Cognitive Seismograph") as demo:
55
- gr.Markdown("# 🧠 Cognitive Seismograph: Visualizing Internal Dynamics")
56
  gr.Markdown(
57
- "**Neues Paradigma:** Wir akzeptieren, dass der 'stille Denkprozess' nicht konvergiert. Stattdessen messen und visualisieren wir die **Signatur der internen Dynamik** ein EKG für den Denkprozess des Modells."
58
  )
59
  with gr.Row(variant='panel'):
60
  with gr.Column(scale=1):
61
- gr.Markdown("### Parameters")
62
  model_id_input = gr.Textbox(value="google/gemma-3-1b-it", label="Model ID")
63
- prompt_type_input = gr.Radio(
64
- choices=list(RESONANCE_PROMPTS.keys()),
65
- value="control_long_prose",
66
- label="Prompt Type (Cognitive Load)"
67
- )
68
  seed_input = gr.Slider(1, 1000, 42, step=1, label="Seed")
69
  num_steps_input = gr.Slider(50, 1000, 300, step=10, label="Number of Internal Steps")
 
 
 
 
 
70
  run_btn = gr.Button("Run Seismic Analysis", variant="primary")
71
 
72
  with gr.Column(scale=2):
@@ -84,15 +91,12 @@ with gr.Blocks(theme=theme, title="Cognitive Seismograph") as demo:
84
 
85
  run_btn.click(
86
  fn=run_and_display,
87
- inputs=[model_id_input, prompt_type_input, seed_input, num_steps_input],
88
  outputs=[verdict_output, plot_output, raw_json_output]
89
  )
90
 
91
  if __name__ == "__main__":
92
- # Die Pre-Flight Checks sind nun entfernt, da das neue Paradigma keine Konvergenz mehr erfordert.
93
  print("="*80)
94
- print("🔬 COGNITIVE SEISMOGRAPH INITIALIZED")
95
  print("="*80)
96
- print("Das experimentelle Paradigma wurde aufgrund der Falsifikation der Konvergenz-Hypothese geändert.")
97
- print("Wir messen nun die Dynamik des nicht-konvergenten Zustands.")
98
  demo.launch(server_name="0.0.0.0", server_port=7860, debug=True)
 
18
  prompt_type: str,
19
  seed: int,
20
  num_steps: int,
21
+ concept_to_inject: str,
22
+ injection_strength: float,
23
  progress=gr.Progress(track_tqdm=True)
24
  ):
25
  """
26
+ Führt die seismische Analyse durch, inklusive der optionalen Konzeptinjektion.
27
  """
28
  try:
29
  results = run_seismic_analysis(
30
+ model_id=model_id,
31
+ prompt_type=prompt_type,
32
+ seed=int(seed),
33
+ num_steps=int(num_steps),
34
+ concept_to_inject=concept_to_inject,
35
+ injection_strength=float(injection_strength),
36
+ progress_callback=progress
37
  )
38
 
39
  verdict = results.get("verdict", "Analysis complete.")
40
  stats = results.get("stats", {})
41
  deltas = results.get("state_deltas", [])
42
 
 
43
  df = pd.DataFrame({
44
  "Internal Step": range(len(deltas)),
45
  "State Change (Delta)": deltas
46
  })
47
 
 
48
  stats_md = f"### Statistical Signature\n"
49
+ stats_md += f"- **Mean Delta:** {stats.get('mean_delta', 0):.4f}\n"
50
+ stats_md += f"- **Std Dev Delta:** {stats.get('std_delta', 0):.4f}\n"
51
+ stats_md += f"- **Max Delta:** {stats.get('max_delta', 0):.4f}\n"
52
 
53
  return f"{verdict}\n\n{stats_md}", df, results
54
 
 
57
  return f"### ❌ Analysis Failed\nAn unexpected error occurred:\n\n```\n{error_str}\n```", pd.DataFrame(), {}
58
 
59
  # --- Gradio App Definition ---
60
+ with gr.Blocks(theme=theme, title="Cognitive Seismograph 2.0") as demo:
61
+ gr.Markdown("# 🧠 Cognitive Seismograph 2.0: Modulating Internal Dynamics")
62
  gr.Markdown(
63
+ "**Neues Paradigma:** Wir messen nicht nur die intrinsische Dynamik, sondern versuchen sie aktiv durch **Konzeptinjektionen** zu modulieren. Vergleiche die 'seismische Signatur' mit und ohne Injektion."
64
  )
65
  with gr.Row(variant='panel'):
66
  with gr.Column(scale=1):
67
+ gr.Markdown("### 1. General Parameters")
68
  model_id_input = gr.Textbox(value="google/gemma-3-1b-it", label="Model ID")
69
+ prompt_type_input = gr.Radio(choices=list(RESONANCE_PROMPTS.keys()), value="resonance_prompt", label="Prompt Type")
 
 
 
 
70
  seed_input = gr.Slider(1, 1000, 42, step=1, label="Seed")
71
  num_steps_input = gr.Slider(50, 1000, 300, step=10, label="Number of Internal Steps")
72
+
73
+ gr.Markdown("### 2. Modulation Parameters")
74
+ concept_input = gr.Textbox(label="Concept to Inject", placeholder="e.g., 'solitude' or 'ocean' (leave blank for baseline)")
75
+ strength_input = gr.Slider(0.0, 5.0, 1.0, step=0.1, label="Injection Strength")
76
+
77
  run_btn = gr.Button("Run Seismic Analysis", variant="primary")
78
 
79
  with gr.Column(scale=2):
 
91
 
92
  run_btn.click(
93
  fn=run_and_display,
94
+ inputs=[model_id_input, prompt_type_input, seed_input, num_steps_input, concept_input, strength_input],
95
  outputs=[verdict_output, plot_output, raw_json_output]
96
  )
97
 
98
  if __name__ == "__main__":
 
99
  print("="*80)
100
+ print("🔬 COGNITIVE SEISMOGRAPH 2.0 (MODULATION-ENABLED) INITIALIZED")
101
  print("="*80)
 
 
102
  demo.launch(server_name="0.0.0.0", server_port=7860, debug=True)
cognitive_mapping_probe/__pycache__/concepts.cpython-310.pyc CHANGED
Binary files a/cognitive_mapping_probe/__pycache__/concepts.cpython-310.pyc and b/cognitive_mapping_probe/__pycache__/concepts.cpython-310.pyc differ
 
cognitive_mapping_probe/orchestrator_seismograph.py CHANGED
@@ -4,6 +4,8 @@ from typing import Dict, Any
4
 
5
  from .llm_iface import get_or_load_model
6
  from .resonance_seismograph import run_silent_cogitation_seismic
 
 
7
  from .utils import dbg
8
 
9
  def run_seismic_analysis(
@@ -11,28 +13,37 @@ def run_seismic_analysis(
11
  prompt_type: str,
12
  seed: int,
13
  num_steps: int,
 
 
14
  progress_callback
15
  ) -> Dict[str, Any]:
16
  """
17
- Orchestriert das neue "Cognitive Seismograph"-Experiment.
18
- Führt den Loop aus, sammelt die `state_deltas` und berechnet statistische Metriken.
19
  """
20
  progress_callback(0.1, desc="Loading model...")
21
  llm = get_or_load_model(model_id, seed)
22
 
 
 
 
 
 
 
23
  progress_callback(0.3, desc=f"Running seismic cogitation for '{prompt_type}'...")
24
 
25
- # Der Resonanz-Loop gibt nun die volle Zeitreihe der Deltas zurück
26
  state_deltas = run_silent_cogitation_seismic(
27
- llm,
28
  prompt_type=prompt_type,
29
  num_steps=num_steps,
30
- temperature=0.1, # Eine niedrige, aber nicht-deterministische Temperatur
 
 
 
31
  )
32
 
33
  progress_callback(0.9, desc="Analyzing dynamics...")
34
 
35
- # Statistische Analyse der Zeitreihe
36
  if state_deltas:
37
  deltas_np = np.array(state_deltas)
38
  stats = {
@@ -42,6 +53,8 @@ def run_seismic_analysis(
42
  "min_delta": float(np.min(deltas_np)),
43
  }
44
  verdict = f"### ✅ Seismic Analysis Complete\nDie interne Dynamik für '{prompt_type}' wurde über {len(deltas_np)} Schritte aufgezeichnet."
 
 
45
  else:
46
  stats = {}
47
  verdict = "### ⚠️ Analysis Warning\nKeine Zustandsänderungen aufgezeichnet."
@@ -52,8 +65,7 @@ def run_seismic_analysis(
52
  "state_deltas": state_deltas
53
  }
54
 
55
- dbg("--- Seismic Analysis Results ---")
56
- dbg(results)
57
 
58
  del llm
59
  if torch.cuda.is_available():
 
4
 
5
  from .llm_iface import get_or_load_model
6
  from .resonance_seismograph import run_silent_cogitation_seismic
7
+ # WIEDERHERGESTELLTER IMPORT
8
+ from .concepts import get_concept_vector
9
  from .utils import dbg
10
 
11
  def run_seismic_analysis(
 
13
  prompt_type: str,
14
  seed: int,
15
  num_steps: int,
16
+ concept_to_inject: str,
17
+ injection_strength: float,
18
  progress_callback
19
  ) -> Dict[str, Any]:
20
  """
21
+ Orchestriert das "Cognitive Seismograph"-Experiment, jetzt mit optionaler
22
+ Konzeptinjektion zur Modulation der Dynamik.
23
  """
24
  progress_callback(0.1, desc="Loading model...")
25
  llm = get_or_load_model(model_id, seed)
26
 
27
+ # Lade den Konzeptvektor, falls ein Konzept angegeben wurde
28
+ injection_vector = None
29
+ if concept_to_inject and concept_to_inject.strip():
30
+ progress_callback(0.2, desc=f"Extracting vector for '{concept_to_inject}'...")
31
+ injection_vector = get_concept_vector(llm, concept_to_inject.strip())
32
+
33
  progress_callback(0.3, desc=f"Running seismic cogitation for '{prompt_type}'...")
34
 
 
35
  state_deltas = run_silent_cogitation_seismic(
36
+ llm=llm,
37
  prompt_type=prompt_type,
38
  num_steps=num_steps,
39
+ temperature=0.1,
40
+ # Übergebe die neuen Parameter an den Resonanz-Loop
41
+ injection_vector=injection_vector,
42
+ injection_strength=injection_strength
43
  )
44
 
45
  progress_callback(0.9, desc="Analyzing dynamics...")
46
 
 
47
  if state_deltas:
48
  deltas_np = np.array(state_deltas)
49
  stats = {
 
53
  "min_delta": float(np.min(deltas_np)),
54
  }
55
  verdict = f"### ✅ Seismic Analysis Complete\nDie interne Dynamik für '{prompt_type}' wurde über {len(deltas_np)} Schritte aufgezeichnet."
56
+ if injection_vector is not None:
57
+ verdict += f"\nModuliert mit dem Konzept **'{concept_to_inject}'** bei Stärke **{injection_strength:.2f}**."
58
  else:
59
  stats = {}
60
  verdict = "### ⚠️ Analysis Warning\nKeine Zustandsänderungen aufgezeichnet."
 
65
  "state_deltas": state_deltas
66
  }
67
 
68
+ dbg("--- Seismic Analysis Results ---", results)
 
69
 
70
  del llm
71
  if torch.cuda.is_available():
cognitive_mapping_probe/resonance_seismograph.py CHANGED
@@ -12,10 +12,13 @@ def run_silent_cogitation_seismic(
12
  prompt_type: str,
13
  num_steps: int,
14
  temperature: float,
 
 
 
15
  ) -> List[float]:
16
  """
17
- NEUE VERSION: Führt den 'silent thought' Prozess aus und gibt die gesamte
18
- Zeitreihe der `state_delta`-Werte zurück, anstatt auf Konvergenz zu prüfen.
19
  """
20
  prompt = RESONANCE_PROMPTS[prompt_type]
21
  inputs = llm.tokenizer(prompt, return_tensors="pt").to(llm.model.device)
@@ -28,19 +31,44 @@ def run_silent_cogitation_seismic(
28
  previous_hidden_state = hidden_state_2d.clone()
29
  state_deltas = []
30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  for i in tqdm(range(num_steps), desc=f"Recording Dynamics (Temp {temperature:.2f})", leave=False, bar_format="{l_bar}{bar:10}{r_bar}"):
32
  next_token_logits = llm.model.lm_head(hidden_state_2d)
33
 
34
- # Wir verwenden immer stochastisches Sampling, um die Dynamik zu erfassen
35
  probabilities = torch.nn.functional.softmax(next_token_logits / temperature, dim=-1)
36
  next_token_id = torch.multinomial(probabilities, num_samples=1)
37
 
38
- outputs = llm.model(
39
- input_ids=next_token_id,
40
- past_key_values=kv_cache,
41
- output_hidden_states=True,
42
- use_cache=True,
43
- )
 
 
 
 
 
 
 
 
 
 
 
44
 
45
  hidden_state_2d = outputs.hidden_states[-1][:, -1, :]
46
  kv_cache = outputs.past_key_values
 
12
  prompt_type: str,
13
  num_steps: int,
14
  temperature: float,
15
+ injection_vector: Optional[torch.Tensor] = None,
16
+ injection_strength: float = 0.0,
17
+ injection_layer: Optional[int] = None,
18
  ) -> List[float]:
19
  """
20
+ ERWEITERTE VERSION: Führt den 'silent thought' Prozess aus und ermöglicht
21
+ die Injektion von Konzeptvektoren zur Modulation der Dynamik.
22
  """
23
  prompt = RESONANCE_PROMPTS[prompt_type]
24
  inputs = llm.tokenizer(prompt, return_tensors="pt").to(llm.model.device)
 
31
  previous_hidden_state = hidden_state_2d.clone()
32
  state_deltas = []
33
 
34
+ # Bereite den Hook für die Injektion vor
35
+ hook_handle = None
36
+ if injection_vector is not None and injection_strength > 0:
37
+ injection_vector = injection_vector.to(device=llm.model.device, dtype=llm.model.dtype)
38
+ if injection_layer is None:
39
+ injection_layer = llm.config.num_hidden_layers // 2
40
+
41
+ dbg(f"Injection enabled: Layer {injection_layer}, Strength {injection_strength:.2f}")
42
+
43
+ def injection_hook(module, layer_input):
44
+ # Der Hook operiert auf dem Input, der bereits 3D ist [batch, seq_len, hidden_dim]
45
+ injection_3d = injection_vector.unsqueeze(0).unsqueeze(0)
46
+ modified_hidden_states = layer_input[0] + (injection_3d * injection_strength)
47
+ return (modified_hidden_states,) + layer_input[1:]
48
+
49
  for i in tqdm(range(num_steps), desc=f"Recording Dynamics (Temp {temperature:.2f})", leave=False, bar_format="{l_bar}{bar:10}{r_bar}"):
50
  next_token_logits = llm.model.lm_head(hidden_state_2d)
51
 
 
52
  probabilities = torch.nn.functional.softmax(next_token_logits / temperature, dim=-1)
53
  next_token_id = torch.multinomial(probabilities, num_samples=1)
54
 
55
+ try:
56
+ # Aktiviere den Hook vor dem forward-Pass
57
+ if injection_vector is not None and injection_strength > 0:
58
+ target_layer = llm.model.model.layers[injection_layer]
59
+ hook_handle = target_layer.register_forward_pre_hook(injection_hook)
60
+
61
+ outputs = llm.model(
62
+ input_ids=next_token_id,
63
+ past_key_values=kv_cache,
64
+ output_hidden_states=True,
65
+ use_cache=True,
66
+ )
67
+ finally:
68
+ # Deaktiviere den Hook sofort nach dem Pass
69
+ if hook_handle:
70
+ hook_handle.remove()
71
+ hook_handle = None
72
 
73
  hidden_state_2d = outputs.hidden_states[-1][:, -1, :]
74
  kv_cache = outputs.past_key_values
tests/conftest.py CHANGED
@@ -16,38 +16,25 @@ def mock_llm_config():
16
  def mock_llm(mocker, mock_llm_config):
17
  """
18
  Erstellt einen schnellen "Mock-LLM" für Unit-Tests.
19
- ERWEITERT: Patcht nun alle relevanten Stellen, an denen das LLM geladen wird,
20
- um in allen Testdateien zu funktionieren.
21
  """
22
  mock_tokenizer = mocker.MagicMock()
23
  mock_tokenizer.eos_token_id = 1
24
 
25
  def mock_model_forward(*args, **kwargs):
26
  batch_size = 1
27
- if 'input_ids' in kwargs:
28
- seq_len = kwargs['input_ids'].shape[1]
29
- elif 'past_key_values' in kwargs:
30
- seq_len = kwargs['past_key_values'][0][0].shape[-2] + 1
31
- else:
32
- seq_len = 1
33
 
34
  mock_outputs = {
35
- "hidden_states": tuple(
36
- [torch.randn(batch_size, seq_len, mock_llm_config.hidden_size) for _ in range(mock_llm_config.num_hidden_layers + 1)]
37
- ),
38
- "past_key_values": tuple(
39
- [
40
- (torch.randn(batch_size, mock_llm_config.num_attention_heads, seq_len, 16),
41
- torch.randn(batch_size, mock_llm_config.num_attention_heads, seq_len, 16))
42
- for _ in range(mock_llm_config.num_hidden_layers)
43
- ]
44
- ),
45
  "logits": torch.randn(batch_size, seq_len, 32000)
46
  }
47
  return SimpleNamespace(**mock_outputs)
48
 
49
  llm_instance = LLM.__new__(LLM)
50
-
51
  llm_instance.model = mock_model_forward
52
  llm_instance.model.config = mock_llm_config
53
  llm_instance.model.device = 'cpu'
@@ -61,10 +48,8 @@ def mock_llm(mocker, mock_llm_config):
61
  llm_instance.seed = 42
62
  llm_instance.set_all_seeds = mocker.MagicMock()
63
 
64
- # ERWEITERUNG: Stelle sicher, dass `get_or_load_model` an allen Orten gepatcht wird.
65
- mocker.patch('cognitive_mapping_probe.llm_iface.get_or_load_model', return_value=llm_instance)
66
  mocker.patch('cognitive_mapping_probe.orchestrator_seismograph.get_or_load_model', return_value=llm_instance)
67
- # Hinzufügen von Patches für die resonance-Datei, falls sie direkt importiert wird
68
- mocker.patch('cognitive_mapping_probe.resonance_seismograph.LLM', return_value=llm_instance, create=True)
69
 
70
  return llm_instance
 
16
  def mock_llm(mocker, mock_llm_config):
17
  """
18
  Erstellt einen schnellen "Mock-LLM" für Unit-Tests.
19
+ ERWEITERT: Patcht nun auch die `concepts`-Abhängigkeit.
 
20
  """
21
  mock_tokenizer = mocker.MagicMock()
22
  mock_tokenizer.eos_token_id = 1
23
 
24
  def mock_model_forward(*args, **kwargs):
25
  batch_size = 1
26
+ if 'input_ids' in kwargs: seq_len = kwargs['input_ids'].shape[1]
27
+ elif 'past_key_values' in kwargs: seq_len = kwargs['past_key_values'][0][0].shape[-2] + 1
28
+ else: seq_len = 1
 
 
 
29
 
30
  mock_outputs = {
31
+ "hidden_states": tuple([torch.randn(batch_size, seq_len, mock_llm_config.hidden_size) for _ in range(mock_llm_config.num_hidden_layers + 1)]),
32
+ "past_key_values": tuple([(torch.randn(batch_size, mock_llm_config.num_attention_heads, seq_len, 16), torch.randn(batch_size, mock_llm_config.num_attention_heads, seq_len, 16)) for _ in range(mock_llm_config.num_hidden_layers)]),
 
 
 
 
 
 
 
 
33
  "logits": torch.randn(batch_size, seq_len, 32000)
34
  }
35
  return SimpleNamespace(**mock_outputs)
36
 
37
  llm_instance = LLM.__new__(LLM)
 
38
  llm_instance.model = mock_model_forward
39
  llm_instance.model.config = mock_llm_config
40
  llm_instance.model.device = 'cpu'
 
48
  llm_instance.seed = 42
49
  llm_instance.set_all_seeds = mocker.MagicMock()
50
 
 
 
51
  mocker.patch('cognitive_mapping_probe.orchestrator_seismograph.get_or_load_model', return_value=llm_instance)
52
+ # Patch für die wiederhergestellte `concepts`-Funktion
53
+ mocker.patch('cognitive_mapping_probe.orchestrator_seismograph.get_concept_vector', return_value=torch.randn(mock_llm_config.hidden_size))
54
 
55
  return llm_instance
tests/test_app_logic.py CHANGED
@@ -13,42 +13,27 @@ def test_run_and_display_logic(mocker):
13
  # 1. Definiere die Schein-Ausgabe, die `run_seismic_analysis` zurückgeben soll
14
  mock_results = {
15
  "verdict": "Mock Verdict",
16
- "stats": {
17
- "mean_delta": 0.5,
18
- "std_delta": 0.1,
19
- "max_delta": 1.0,
20
- },
21
  "state_deltas": [0.4, 0.5, 0.6]
22
  }
23
  mocker.patch('app.run_seismic_analysis', return_value=mock_results)
24
 
25
- # Mocke den Gradio Progress-Callback
26
  mock_progress = mocker.MagicMock()
27
 
28
- # 2. Rufe die zu testende Funktion auf
29
  verdict_md, plot_df, raw_json = run_and_display(
30
  model_id="mock_model",
31
  prompt_type="mock_prompt",
32
  seed=42,
33
  num_steps=3,
 
 
34
  progress=mock_progress
35
  )
36
 
37
- # 3. Validiere die Ausgaben mit granularen Assertions
38
-
39
- # ASSERT 1: Die Markdown-Ausgabe muss die korrekten Statistiken enthalten
40
  assert "Mock Verdict" in verdict_md
41
- assert "Mean Delta:" in verdict_md
42
  assert "0.5000" in verdict_md
43
- assert "Std Dev Delta:" in verdict_md
44
- assert "0.1000" in verdict_md
45
-
46
- # ASSERT 2: Der Pandas DataFrame für den Plot muss korrekt erstellt werden
47
  assert isinstance(plot_df, pd.DataFrame)
48
- assert "Internal Step" in plot_df.columns
49
- assert "State Change (Delta)" in plot_df.columns
50
  assert len(plot_df) == 3
51
- assert plot_df["State Change (Delta)"].tolist() == [0.4, 0.5, 0.6]
52
-
53
- # ASSERT 3: Die Raw-JSON-Ausgabe muss die Originaldaten enthalten
54
  assert raw_json == mock_results
 
13
  # 1. Definiere die Schein-Ausgabe, die `run_seismic_analysis` zurückgeben soll
14
  mock_results = {
15
  "verdict": "Mock Verdict",
16
+ "stats": { "mean_delta": 0.5, "std_delta": 0.1, "max_delta": 1.0, },
 
 
 
 
17
  "state_deltas": [0.4, 0.5, 0.6]
18
  }
19
  mocker.patch('app.run_seismic_analysis', return_value=mock_results)
20
 
 
21
  mock_progress = mocker.MagicMock()
22
 
23
+ # 2. Rufe die zu testende Funktion mit den KORRIGIERTEN Argumenten auf
24
  verdict_md, plot_df, raw_json = run_and_display(
25
  model_id="mock_model",
26
  prompt_type="mock_prompt",
27
  seed=42,
28
  num_steps=3,
29
+ concept_to_inject="", # Fehlendes Argument hinzugefügt
30
+ injection_strength=0.0, # Fehlendes Argument hinzugefügt
31
  progress=mock_progress
32
  )
33
 
34
+ # 3. Validiere die Ausgaben
 
 
35
  assert "Mock Verdict" in verdict_md
 
36
  assert "0.5000" in verdict_md
 
 
 
 
37
  assert isinstance(plot_df, pd.DataFrame)
 
 
38
  assert len(plot_df) == 3
 
 
 
39
  assert raw_json == mock_results
tests/test_dynamics.py DELETED
@@ -1,60 +0,0 @@
1
- import torch
2
- import numpy as np
3
- import pytest
4
- from types import SimpleNamespace
5
-
6
- from cognitive_mapping_probe.resonance_seismograph import run_silent_cogitation_seismic
7
- from cognitive_mapping_probe.orchestrator_seismograph import run_seismic_analysis
8
-
9
- def test_run_silent_cogitation_seismic_output(mock_llm):
10
- """
11
- Testet die Kernfunktion `run_silent_cogitation_seismic`.
12
- ASSERT: Gibt eine Liste von Floats zurück, deren Länge der Anzahl der Schritte entspricht.
13
- """
14
- num_steps = 10
15
- state_deltas = run_silent_cogitation_seismic(
16
- llm=mock_llm,
17
- prompt_type="control_long_prose",
18
- num_steps=num_steps,
19
- temperature=0.7
20
- )
21
-
22
- assert isinstance(state_deltas, list)
23
- assert len(state_deltas) == num_steps
24
- assert all(isinstance(delta, float) for delta in state_deltas)
25
-
26
- def test_seismic_analysis_orchestrator(mocker, mock_llm):
27
- """
28
- Testet den `run_seismic_analysis` Orchestrator.
29
- Wir mocken die darunterliegende `run_silent_cogitation_seismic`, um das Verhalten
30
- des Orchestrators isoliert zu prüfen.
31
- ASSERT: Berechnet korrekte Statistiken und gibt die erwartete Datenstruktur zurück.
32
- """
33
- mock_deltas = [1.0, 2.0, 3.0, 4.0, 5.0]
34
- mocker.patch('cognitive_mapping_probe.orchestrator_seismograph.run_silent_cogitation_seismic', return_value=mock_deltas)
35
-
36
- # Mocke den Gradio Progress-Callback
37
- mock_progress = mocker.MagicMock()
38
-
39
- results = run_seismic_analysis(
40
- model_id="mock_model",
41
- prompt_type="test_prompt",
42
- seed=42,
43
- num_steps=5,
44
- progress_callback=mock_progress
45
- )
46
-
47
- # ASSERT: Die Ergebnisse haben die korrekte Struktur und den korrekten Inhalt
48
- assert "verdict" in results
49
- assert "stats" in results
50
- assert "state_deltas" in results
51
-
52
- stats = results["stats"]
53
- assert stats["mean_delta"] == pytest.approx(np.mean(mock_deltas))
54
- assert stats["std_delta"] == pytest.approx(np.std(mock_deltas))
55
- assert stats["max_delta"] == pytest.approx(max(mock_deltas))
56
-
57
- assert results["state_deltas"] == mock_deltas
58
-
59
- # ASSERT: Der Progress-Callback wurde aufgerufen
60
- assert mock_progress.call_count > 0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
tests/test_integration.py CHANGED
@@ -9,17 +9,15 @@ def test_end_to_end_with_mock_llm(mock_llm, mocker):
9
  """
10
  Ein End-to-End-Integrationstest, der den gesamten Datenfluss von der App
11
  über den Orchestrator bis zum (gemockten) LLM validiert.
12
-
13
- Dieser Test ersetzt die Notwendigkeit für `pre_flight_checks.py`, indem er
14
- die gesamte Kette in einer kontrollierten Testumgebung ausführt.
15
  """
16
- # 1. Führe den Orchestrator mit dem `mock_llm` aus.
17
- # Dies ist ein echter Aufruf, keine gemockte Funktion.
18
  results = run_seismic_analysis(
19
  model_id="mock_model",
20
  prompt_type="control_long_prose",
21
  seed=42,
22
  num_steps=5,
 
 
23
  progress_callback=mocker.MagicMock()
24
  )
25
 
@@ -31,12 +29,14 @@ def test_end_to_end_with_mock_llm(mock_llm, mocker):
31
  # 2. Mocke nun den Orchestrator, um die App-Logik mit seinen Ergebnissen zu füttern
32
  mocker.patch('app.run_seismic_analysis', return_value=results)
33
 
34
- # 3. Führe die App-Logik aus
35
  _, plot_df, _ = run_and_display(
36
  model_id="mock_model",
37
  prompt_type="control_long_prose",
38
  seed=42,
39
  num_steps=5,
 
 
40
  progress=mocker.MagicMock()
41
  )
42
 
 
9
  """
10
  Ein End-to-End-Integrationstest, der den gesamten Datenfluss von der App
11
  über den Orchestrator bis zum (gemockten) LLM validiert.
 
 
 
12
  """
13
+ # 1. Führe den Orchestrator mit dem `mock_llm` und den KORRIGIERTEN Argumenten aus.
 
14
  results = run_seismic_analysis(
15
  model_id="mock_model",
16
  prompt_type="control_long_prose",
17
  seed=42,
18
  num_steps=5,
19
+ concept_to_inject="test_concept", # Argument hinzugefügt
20
+ injection_strength=1.0, # Argument hinzugefügt
21
  progress_callback=mocker.MagicMock()
22
  )
23
 
 
29
  # 2. Mocke nun den Orchestrator, um die App-Logik mit seinen Ergebnissen zu füttern
30
  mocker.patch('app.run_seismic_analysis', return_value=results)
31
 
32
+ # 3. Führe die App-Logik mit den KORRIGIERTEN Argumenten aus
33
  _, plot_df, _ = run_and_display(
34
  model_id="mock_model",
35
  prompt_type="control_long_prose",
36
  seed=42,
37
  num_steps=5,
38
+ concept_to_inject="test_concept", # Argument hinzugefügt
39
+ injection_strength=1.0, # Argument hinzugefügt
40
  progress=mocker.MagicMock()
41
  )
42
 
tests/test_orchestration.py CHANGED
@@ -1,43 +1,67 @@
1
  import numpy as np
2
  import pytest
 
3
  from types import SimpleNamespace
4
 
5
  from cognitive_mapping_probe.orchestrator_seismograph import run_seismic_analysis
6
 
7
- def test_seismic_analysis_orchestrator(mocker, mock_llm):
8
  """
9
- Testet den `run_seismic_analysis` Orchestrator.
10
- Wir mocken die darunterliegende `run_silent_cogitation_seismic`, um das Verhalten
11
- des Orchestrators isoliert zu prüfen.
12
- ASSERT: Berechnet korrekte Statistiken und gibt die erwartete Datenstruktur zurück.
13
  """
14
- # Definiere das erwartete Verhalten der gemockten Funktion
15
- mock_deltas = [1.0, 2.0, 3.0, 4.0, 5.0]
16
- mocker.patch('cognitive_mapping_probe.orchestrator_seismograph.run_silent_cogitation_seismic', return_value=mock_deltas)
17
 
18
- # Mocke den Gradio Progress-Callback
19
  mock_progress = mocker.MagicMock()
20
 
21
- # Führe die zu testende Funktion aus
22
  results = run_seismic_analysis(
23
  model_id="mock_model",
24
  prompt_type="test_prompt",
25
  seed=42,
26
- num_steps=5,
 
 
27
  progress_callback=mock_progress
28
  )
29
 
30
- # ASSERT: Die Ergebnisse haben die korrekte Struktur und den korrekten Inhalt
31
- assert "verdict" in results
32
- assert "stats" in results
33
- assert "state_deltas" in results
34
 
35
- stats = results["stats"]
36
- assert stats["mean_delta"] == pytest.approx(np.mean(mock_deltas))
37
- assert stats["std_delta"] == pytest.approx(np.std(mock_deltas))
38
- assert stats["max_delta"] == pytest.approx(max(mock_deltas))
39
 
40
- assert results["state_deltas"] == mock_deltas
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
- # ASSERT: Der Progress-Callback wurde aufgerufen
43
- assert mock_progress.call_count > 0
 
1
  import numpy as np
2
  import pytest
3
+ import torch
4
  from types import SimpleNamespace
5
 
6
  from cognitive_mapping_probe.orchestrator_seismograph import run_seismic_analysis
7
 
8
+ def test_seismic_analysis_orchestrator_no_injection(mocker, mock_llm):
9
  """
10
+ Testet den Orchestrator im Baseline-Modus (ohne Injektion).
 
 
 
11
  """
12
+ mock_deltas = [1.0, 2.0, 3.0]
13
+ mock_run_seismic = mocker.patch('cognitive_mapping_probe.orchestrator_seismograph.run_silent_cogitation_seismic', return_value=mock_deltas)
 
14
 
 
15
  mock_progress = mocker.MagicMock()
16
 
 
17
  results = run_seismic_analysis(
18
  model_id="mock_model",
19
  prompt_type="test_prompt",
20
  seed=42,
21
+ num_steps=3,
22
+ concept_to_inject="", # Kein Konzept
23
+ injection_strength=0.0,
24
  progress_callback=mock_progress
25
  )
26
 
27
+ # ASSERT: `run_silent_cogitation_seismic` wurde mit `injection_vector=None` aufgerufen
28
+ mock_run_seismic.assert_called_once()
29
+ call_args, call_kwargs = mock_run_seismic.call_args
30
+ assert call_kwargs['injection_vector'] is None
31
 
32
+ # ASSERT: Die Statistiken sind korrekt
33
+ assert results["stats"]["mean_delta"] == pytest.approx(2.0)
 
 
34
 
35
+ def test_seismic_analysis_orchestrator_with_injection(mocker, mock_llm):
36
+ """
37
+ Testet den Orchestrator mit aktivierter Konzeptinjektion.
38
+ """
39
+ mock_deltas = [5.0, 6.0, 7.0]
40
+ mock_run_seismic = mocker.patch('cognitive_mapping_probe.orchestrator_seismograph.run_silent_cogitation_seismic', return_value=mock_deltas)
41
+
42
+ # Der `mock_llm` Fixture patcht bereits `get_concept_vector`
43
+ mock_get_concept_vector = mocker.patch('cognitive_mapping_probe.orchestrator_seismograph.get_concept_vector')
44
+
45
+ mock_progress = mocker.MagicMock()
46
+
47
+ results = run_seismic_analysis(
48
+ model_id="mock_model",
49
+ prompt_type="test_prompt",
50
+ seed=42,
51
+ num_steps=3,
52
+ concept_to_inject="test_concept", # Konzept wird übergeben
53
+ injection_strength=1.5,
54
+ progress_callback=mock_progress
55
+ )
56
+
57
+ # ASSERT: `get_concept_vector` wurde aufgerufen
58
+ mock_get_concept_vector.assert_called_once_with(mocker.ANY, "test_concept")
59
+
60
+ # ASSERT: `run_silent_cogitation_seismic` wurde mit einem Vektor und Stärke aufgerufen
61
+ mock_run_seismic.assert_called_once()
62
+ call_args, call_kwargs = mock_run_seismic.call_args
63
+ assert call_kwargs['injection_vector'] is not None
64
+ assert call_kwargs['injection_strength'] == 1.5
65
 
66
+ # ASSERT: Die Statistiken sind korrekt
67
+ assert results["stats"]["mean_delta"] == pytest.approx(6.0)