npcsh 1.1.20__py3-none-any.whl → 1.1.21__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- npcsh/_state.py +5 -71
- npcsh/diff_viewer.py +3 -3
- npcsh/npc_team/jinxs/lib/core/compress.jinx +373 -85
- npcsh/npc_team/jinxs/lib/core/search/db_search.jinx +17 -6
- npcsh/npc_team/jinxs/lib/core/search/file_search.jinx +17 -6
- npcsh/npc_team/jinxs/lib/core/search/kg_search.jinx +19 -8
- npcsh/npc_team/jinxs/lib/core/search/web_search.jinx +52 -14
- npcsh/npc_team/jinxs/{bin → lib/utils}/benchmark.jinx +2 -2
- npcsh/npc_team/jinxs/{bin → lib/utils}/jinxs.jinx +12 -12
- npcsh/npc_team/jinxs/{bin → lib/utils}/models.jinx +7 -7
- npcsh/npc_team/jinxs/{bin → lib/utils}/setup.jinx +6 -6
- npcsh/npc_team/jinxs/modes/alicanto.jinx +1573 -296
- npcsh/npc_team/jinxs/modes/arxiv.jinx +5 -5
- npcsh/npc_team/jinxs/modes/config_tui.jinx +300 -0
- npcsh/npc_team/jinxs/modes/corca.jinx +3 -3
- npcsh/npc_team/jinxs/modes/git.jinx +795 -0
- {npcsh-1.1.20.data/data/npcsh/npc_team → npcsh/npc_team/jinxs/modes}/kg.jinx +13 -13
- npcsh/npc_team/jinxs/modes/memories.jinx +414 -0
- npcsh/npc_team/jinxs/{bin → modes}/nql.jinx +10 -21
- npcsh/npc_team/jinxs/modes/papers.jinx +578 -0
- npcsh/npc_team/jinxs/modes/plonk.jinx +490 -304
- npcsh/npc_team/jinxs/modes/reattach.jinx +3 -3
- npcsh/npc_team/jinxs/modes/spool.jinx +3 -3
- npcsh/npc_team/jinxs/{bin → modes}/team.jinx +12 -12
- npcsh/npc_team/jinxs/modes/vixynt.jinx +388 -0
- npcsh/npc_team/jinxs/modes/wander.jinx +454 -181
- npcsh/npc_team/jinxs/modes/yap.jinx +10 -3
- npcsh/npcsh.py +112 -47
- npcsh/routes.py +4 -1
- npcsh/salmon_simulation.py +0 -0
- npcsh-1.1.21.data/data/npcsh/npc_team/alicanto.jinx +1633 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/arxiv.jinx +5 -5
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/benchmark.jinx +2 -2
- npcsh-1.1.21.data/data/npcsh/npc_team/compress.jinx +428 -0
- npcsh-1.1.21.data/data/npcsh/npc_team/config_tui.jinx +300 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/corca.jinx +3 -3
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/db_search.jinx +17 -6
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/file_search.jinx +17 -6
- npcsh-1.1.21.data/data/npcsh/npc_team/git.jinx +795 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/jinxs.jinx +12 -12
- {npcsh/npc_team/jinxs/bin → npcsh-1.1.21.data/data/npcsh/npc_team}/kg.jinx +13 -13
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/kg_search.jinx +19 -8
- npcsh-1.1.21.data/data/npcsh/npc_team/memories.jinx +414 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/models.jinx +7 -7
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/nql.jinx +10 -21
- npcsh-1.1.21.data/data/npcsh/npc_team/papers.jinx +578 -0
- npcsh-1.1.21.data/data/npcsh/npc_team/plonk.jinx +565 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/reattach.jinx +3 -3
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/setup.jinx +6 -6
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/spool.jinx +3 -3
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/team.jinx +12 -12
- npcsh-1.1.21.data/data/npcsh/npc_team/vixynt.jinx +388 -0
- npcsh-1.1.21.data/data/npcsh/npc_team/wander.jinx +728 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/web_search.jinx +52 -14
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/yap.jinx +10 -3
- {npcsh-1.1.20.dist-info → npcsh-1.1.21.dist-info}/METADATA +2 -2
- {npcsh-1.1.20.dist-info → npcsh-1.1.21.dist-info}/RECORD +145 -150
- npcsh-1.1.21.dist-info/entry_points.txt +11 -0
- npcsh/npc_team/jinxs/bin/config_tui.jinx +0 -300
- npcsh/npc_team/jinxs/bin/memories.jinx +0 -317
- npcsh/npc_team/jinxs/bin/vixynt.jinx +0 -122
- npcsh/npc_team/jinxs/lib/core/search/mem_review.jinx +0 -73
- npcsh/npc_team/jinxs/lib/core/search/mem_search.jinx +0 -388
- npcsh/npc_team/jinxs/lib/research/paper_search.jinx +0 -412
- npcsh/npc_team/jinxs/lib/research/semantic_scholar.jinx +0 -386
- npcsh/npc_team/plonkjr.npc +0 -23
- npcsh-1.1.20.data/data/npcsh/npc_team/alicanto.jinx +0 -356
- npcsh-1.1.20.data/data/npcsh/npc_team/compress.jinx +0 -140
- npcsh-1.1.20.data/data/npcsh/npc_team/config_tui.jinx +0 -300
- npcsh-1.1.20.data/data/npcsh/npc_team/mem_review.jinx +0 -73
- npcsh-1.1.20.data/data/npcsh/npc_team/mem_search.jinx +0 -388
- npcsh-1.1.20.data/data/npcsh/npc_team/memories.jinx +0 -317
- npcsh-1.1.20.data/data/npcsh/npc_team/paper_search.jinx +0 -412
- npcsh-1.1.20.data/data/npcsh/npc_team/plonk.jinx +0 -379
- npcsh-1.1.20.data/data/npcsh/npc_team/plonkjr.npc +0 -23
- npcsh-1.1.20.data/data/npcsh/npc_team/semantic_scholar.jinx +0 -386
- npcsh-1.1.20.data/data/npcsh/npc_team/vixynt.jinx +0 -122
- npcsh-1.1.20.data/data/npcsh/npc_team/wander.jinx +0 -455
- npcsh-1.1.20.dist-info/entry_points.txt +0 -25
- /npcsh/npc_team/jinxs/lib/{orchestration → core}/convene.jinx +0 -0
- /npcsh/npc_team/jinxs/lib/{orchestration → core}/delegate.jinx +0 -0
- /npcsh/npc_team/jinxs/{bin → lib/core}/sample.jinx +0 -0
- /npcsh/npc_team/jinxs/{bin → lib/utils}/sync.jinx +0 -0
- /npcsh/npc_team/jinxs/{bin → modes}/roll.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/add_tab.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/alicanto.npc +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/alicanto.png +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/browser_action.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/browser_screenshot.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/build.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/chat.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/click.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/close_browser.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/close_pane.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/close_tab.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/cmd.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/compile.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/confirm.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/convene.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/corca.npc +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/corca.png +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/corca_example.png +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/delegate.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/edit_file.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/focus_pane.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/frederic.npc +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/frederic4.png +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/guac.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/guac.npc +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/guac.png +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/help.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/incognide.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/init.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/kadiefa.npc +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/kadiefa.png +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/key_press.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/launch_app.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/list_panes.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/load_file.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/navigate.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/notify.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/npcsh.ctx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/npcsh_sibiji.png +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/open_browser.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/open_pane.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/ots.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/paste.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/plonk.npc +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/plonk.png +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/plonkjr.png +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/pti.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/python.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/read_pane.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/roll.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/run_terminal.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sample.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/screenshot.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/search.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/send_message.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/serve.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/set.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sh.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/shh.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sibiji.npc +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sibiji.png +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sleep.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/split_pane.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/spool.png +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sql.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/switch.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/switch_npc.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/switch_tab.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/switches.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sync.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/teamviz.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/trigger.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/type_text.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/usage.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/verbose.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/wait.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/write_file.jinx +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/yap.png +0 -0
- {npcsh-1.1.20.data → npcsh-1.1.21.data}/data/npcsh/npc_team/zen_mode.jinx +0 -0
- {npcsh-1.1.20.dist-info → npcsh-1.1.21.dist-info}/WHEEL +0 -0
- {npcsh-1.1.20.dist-info → npcsh-1.1.21.dist-info}/licenses/LICENSE +0 -0
- {npcsh-1.1.20.dist-info → npcsh-1.1.21.dist-info}/top_level.txt +0 -0
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
jinx_name: wander
|
|
2
2
|
description: Interactive wandering mode - creative exploration with live TUI dashboard
|
|
3
|
+
interactive: true
|
|
3
4
|
inputs:
|
|
4
5
|
- problem: null
|
|
5
6
|
- environment: null
|
|
6
|
-
- low_temp: 0.
|
|
7
|
-
- high_temp: 1.
|
|
7
|
+
- low_temp: 0.5
|
|
8
|
+
- high_temp: 1.9
|
|
9
|
+
- n_min: 30
|
|
10
|
+
- n_max: 150
|
|
11
|
+
- interruption_likelihood: 0.1
|
|
12
|
+
- sample_rate: 0.5
|
|
13
|
+
- n_high_temp_streams: 5
|
|
14
|
+
- include_events: true
|
|
15
|
+
- num_events: 3
|
|
8
16
|
- model: null
|
|
9
17
|
- provider: null
|
|
10
18
|
|
|
@@ -31,10 +39,17 @@ steps:
|
|
|
31
39
|
|
|
32
40
|
problem = context.get('problem')
|
|
33
41
|
environment = context.get('environment')
|
|
34
|
-
low_temp = float(context.get('low_temp'
|
|
35
|
-
high_temp = float(context.get('high_temp'
|
|
36
|
-
|
|
37
|
-
|
|
42
|
+
low_temp = float(context.get('low_temp') or 0.5)
|
|
43
|
+
high_temp = float(context.get('high_temp') or 1.9)
|
|
44
|
+
n_min = int(context.get('n_min') or 30)
|
|
45
|
+
n_max = int(context.get('n_max') or 150)
|
|
46
|
+
interruption_likelihood = float(context.get('interruption_likelihood') or 0.1)
|
|
47
|
+
sample_rate = float(context.get('sample_rate') or 0.5)
|
|
48
|
+
n_high_temp_streams = int(context.get('n_high_temp_streams') or 5)
|
|
49
|
+
include_events = bool(context.get('include_events', True))
|
|
50
|
+
num_events = int(context.get('num_events') or 3)
|
|
51
|
+
|
|
52
|
+
# Resolve npc
|
|
38
53
|
if isinstance(npc, str) and team:
|
|
39
54
|
npc = team.get(npc) if hasattr(team, 'get') else None
|
|
40
55
|
elif isinstance(npc, str):
|
|
@@ -43,39 +58,85 @@ steps:
|
|
|
43
58
|
model = context.get('model') or (npc.model if npc and hasattr(npc, 'model') else None)
|
|
44
59
|
provider = context.get('provider') or (npc.provider if npc and hasattr(npc, 'provider') else None)
|
|
45
60
|
|
|
61
|
+
# ========== Problem Entry ==========
|
|
46
62
|
if not problem:
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
exit()
|
|
63
|
+
if not sys.stdin.isatty():
|
|
64
|
+
context['output'] = "Wander requires an interactive terminal or a problem argument."
|
|
65
|
+
context['messages'] = messages
|
|
66
|
+
exit()
|
|
67
|
+
print("\033[1;35m WANDER - Creative Exploration \033[0m")
|
|
68
|
+
print("\033[90mEnter a problem or question to explore (or 'q' to quit):\033[0m")
|
|
69
|
+
try:
|
|
70
|
+
problem = input("\033[33m> \033[0m").strip()
|
|
71
|
+
except (EOFError, KeyboardInterrupt):
|
|
72
|
+
problem = ""
|
|
73
|
+
if not problem or problem.lower() == 'q':
|
|
74
|
+
context['output'] = "Wander cancelled."
|
|
75
|
+
context['messages'] = messages
|
|
76
|
+
exit()
|
|
62
77
|
|
|
63
78
|
# ========== State ==========
|
|
64
79
|
class WanderState:
|
|
65
80
|
def __init__(self):
|
|
66
81
|
self.environment = ""
|
|
67
|
-
self.streams = []
|
|
68
|
-
self.starred = []
|
|
69
|
-
self.current_temp =
|
|
70
|
-
self.temp_mode = "focused" # focused, creative, wild
|
|
82
|
+
self.streams = [] # [{temp, insight, words, samples, event, starred, timestamp}]
|
|
83
|
+
self.starred = []
|
|
84
|
+
self.current_temp = high_temp
|
|
71
85
|
self.scroll_offset = 0
|
|
72
|
-
self.current_panel = 0 # 0=main, 1=streams, 2=starred
|
|
86
|
+
self.current_panel = 0 # 0=main, 1=streams, 2=starred, 3=review
|
|
73
87
|
self.status = "Ready"
|
|
74
88
|
self.generating = False
|
|
75
89
|
self.last_output = ""
|
|
90
|
+
self.all_samples = [] # collected word samples across streams
|
|
91
|
+
self.events = [] # [{type, text}]
|
|
92
|
+
self.quit_requested = False
|
|
93
|
+
self.auto_done = False
|
|
94
|
+
# Review mode
|
|
95
|
+
self.review_mode = False
|
|
96
|
+
self.review_items = [] # [{idx, text_preview, selected}]
|
|
97
|
+
self.review_cursor = 0
|
|
98
|
+
self.review_scroll = 0
|
|
99
|
+
self.review_sample_count = 0
|
|
100
|
+
self.editing_count = False
|
|
101
|
+
self.count_buf = ""
|
|
76
102
|
|
|
77
103
|
state = WanderState()
|
|
78
104
|
|
|
105
|
+
# ========== Event Weights ==========
|
|
106
|
+
EVENT_WEIGHTS = {
|
|
107
|
+
'encounter': 0.20,
|
|
108
|
+
'discovery': 0.20,
|
|
109
|
+
'obstacle': 0.15,
|
|
110
|
+
'insight': 0.20,
|
|
111
|
+
'shift': 0.10,
|
|
112
|
+
'memory': 0.15,
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
def weighted_event_type():
|
|
116
|
+
types = list(EVENT_WEIGHTS.keys())
|
|
117
|
+
weights = list(EVENT_WEIGHTS.values())
|
|
118
|
+
return random.choices(types, weights=weights, k=1)[0]
|
|
119
|
+
|
|
120
|
+
# ========== Original Algorithm Helpers ==========
|
|
121
|
+
def truncate_to_word_count(text, n):
|
|
122
|
+
words = text.split()
|
|
123
|
+
if len(words) <= n:
|
|
124
|
+
return text
|
|
125
|
+
return ' '.join(words[:n])
|
|
126
|
+
|
|
127
|
+
def subsample_words(text, k=20):
|
|
128
|
+
words = text.split()
|
|
129
|
+
if len(words) <= k:
|
|
130
|
+
return words
|
|
131
|
+
return random.sample(words, k)
|
|
132
|
+
|
|
133
|
+
def extract_samples(text, rate):
|
|
134
|
+
words = text.split()
|
|
135
|
+
n_sample = max(1, int(len(words) * rate))
|
|
136
|
+
if len(words) <= n_sample:
|
|
137
|
+
return words
|
|
138
|
+
return random.sample(words, n_sample)
|
|
139
|
+
|
|
79
140
|
# ========== TUI Helpers ==========
|
|
80
141
|
def get_size():
|
|
81
142
|
try:
|
|
@@ -94,9 +155,7 @@ steps:
|
|
|
94
155
|
return lines
|
|
95
156
|
|
|
96
157
|
def draw_box(x, y, w, h, title="", color="\033[90m"):
|
|
97
|
-
"""Draw a box with optional title"""
|
|
98
158
|
out = []
|
|
99
|
-
# Top border
|
|
100
159
|
if title:
|
|
101
160
|
title_part = f" {title} "
|
|
102
161
|
border = "─" * ((w - len(title_part) - 2) // 2)
|
|
@@ -104,119 +163,116 @@ steps:
|
|
|
104
163
|
else:
|
|
105
164
|
top = "┌" + "─" * (w - 2) + "┐"
|
|
106
165
|
out.append(f"\033[{y};{x}H{color}{top}\033[0m")
|
|
107
|
-
# Sides
|
|
108
166
|
for i in range(1, h - 1):
|
|
109
167
|
out.append(f"\033[{y+i};{x}H{color}│\033[0m")
|
|
110
168
|
out.append(f"\033[{y+i};{x+w-1}H{color}│\033[0m")
|
|
111
|
-
# Bottom
|
|
112
169
|
out.append(f"\033[{y+h-1};{x}H{color}└{'─' * (w - 2)}┘\033[0m")
|
|
113
170
|
return ''.join(out)
|
|
114
171
|
|
|
115
172
|
def render_screen():
|
|
116
173
|
width, height = get_size()
|
|
117
174
|
out = []
|
|
118
|
-
|
|
119
|
-
# Clear screen
|
|
120
175
|
out.append("\033[2J\033[H")
|
|
121
176
|
|
|
122
177
|
# ===== HEADER =====
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
temp_info = f"
|
|
178
|
+
prob_display = problem[:width-20] + "..." if len(problem) > width-20 else problem
|
|
179
|
+
header = f" WANDER - {prob_display} "
|
|
180
|
+
temp_info = f"\033[35m[T={state.current_temp:.1f}]\033[0m"
|
|
126
181
|
status_color = "\033[33m" if state.generating else "\033[32m"
|
|
127
182
|
status_info = f"{status_color}[{state.status}]\033[0m"
|
|
128
183
|
|
|
129
|
-
out.append(f"\033[1;1H\033[
|
|
184
|
+
out.append(f"\033[1;1H\033[7;1m{header.ljust(width)}\033[0m")
|
|
130
185
|
out.append(f"\033[1;{width-35}H{temp_info} {status_info}")
|
|
131
186
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
187
|
+
if state.review_mode:
|
|
188
|
+
render_review(out, width, height)
|
|
189
|
+
else:
|
|
190
|
+
render_explore(out, width, height)
|
|
191
|
+
|
|
192
|
+
sys.stdout.write(''.join(out))
|
|
193
|
+
sys.stdout.flush()
|
|
194
|
+
|
|
195
|
+
def render_explore(out, width, height):
|
|
135
196
|
left_w = max(25, width // 3)
|
|
136
197
|
right_w = width - left_w - 1
|
|
137
198
|
panel_h = height - 4
|
|
138
199
|
|
|
139
|
-
#
|
|
200
|
+
# Left: Environment
|
|
140
201
|
out.append(draw_box(1, 3, left_w, panel_h // 2, "Environment", "\033[36m"))
|
|
141
|
-
env_lines = wrap_text(state.environment or "
|
|
202
|
+
env_lines = wrap_text(state.environment or "Generating...", left_w - 4)
|
|
142
203
|
for i, line in enumerate(env_lines[:panel_h // 2 - 3]):
|
|
143
204
|
out.append(f"\033[{4+i};3H{line[:left_w-4]}")
|
|
144
205
|
|
|
145
|
-
#
|
|
206
|
+
# Left: Controls
|
|
146
207
|
ctrl_y = 3 + panel_h // 2
|
|
147
208
|
out.append(draw_box(1, ctrl_y, left_w, panel_h // 2, "Controls", "\033[33m"))
|
|
148
209
|
controls = [
|
|
149
210
|
"SPACE - New stream",
|
|
150
|
-
f"t - Temp: {state.
|
|
211
|
+
f"t - Temp: {state.current_temp:.1f}",
|
|
151
212
|
"e - Trigger event",
|
|
152
213
|
"s - Star insight",
|
|
153
|
-
"
|
|
214
|
+
"R - Review & synthesize",
|
|
154
215
|
"Tab - Switch panel",
|
|
155
216
|
"j/k - Scroll",
|
|
156
217
|
"q - Quit",
|
|
157
218
|
"",
|
|
158
219
|
f"Streams: {len(state.streams)}",
|
|
220
|
+
f"Samples: {len(state.all_samples)}",
|
|
159
221
|
f"Starred: {len(state.starred)}",
|
|
160
222
|
]
|
|
161
223
|
for i, ctrl in enumerate(controls[:panel_h // 2 - 3]):
|
|
162
224
|
out.append(f"\033[{ctrl_y+1+i};3H\033[90m{ctrl[:left_w-4]}\033[0m")
|
|
163
225
|
|
|
164
|
-
#
|
|
226
|
+
# Right panel
|
|
165
227
|
panel_titles = ["Output", "Stream History", "Starred Insights"]
|
|
166
|
-
|
|
167
|
-
|
|
228
|
+
panel_idx = min(state.current_panel, 2)
|
|
229
|
+
panel_color = ["\033[37m", "\033[36m", "\033[33m"][panel_idx]
|
|
230
|
+
out.append(draw_box(left_w + 1, 3, right_w, panel_h, panel_titles[panel_idx], panel_color))
|
|
168
231
|
|
|
169
|
-
# Panel content
|
|
170
232
|
content_w = right_w - 4
|
|
171
233
|
content_h = panel_h - 3
|
|
172
234
|
content_lines = []
|
|
173
235
|
|
|
174
|
-
if
|
|
236
|
+
if panel_idx == 0:
|
|
175
237
|
if state.last_output:
|
|
176
238
|
content_lines = wrap_text(state.last_output, content_w)
|
|
177
239
|
else:
|
|
178
|
-
content_lines = ["", "
|
|
179
|
-
" The AI will explore your problem from",
|
|
180
|
-
" different perspectives and temperatures.", "",
|
|
181
|
-
" Star insights you find valuable with 's'.",
|
|
182
|
-
" Request synthesis anytime with 'r'."]
|
|
240
|
+
content_lines = ["", " Waiting for streams..."]
|
|
183
241
|
|
|
184
|
-
elif
|
|
242
|
+
elif panel_idx == 1:
|
|
185
243
|
for i, stream in enumerate(reversed(state.streams)):
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
content_lines.append(header)
|
|
244
|
+
starred_mark = "★ " if stream.get('starred') else " "
|
|
245
|
+
hdr = f"{starred_mark}\033[35mStream {len(state.streams)-i} (T={stream.get('temp', 0):.1f}, {len(stream.get('words', []))}w)\033[0m"
|
|
246
|
+
content_lines.append(hdr)
|
|
190
247
|
preview = stream.get('insight', '')[:100].replace('\n', ' ')
|
|
191
248
|
content_lines.append(f" {preview}...")
|
|
249
|
+
if stream.get('samples'):
|
|
250
|
+
content_lines.append(f" \033[90mSamples: {' '.join(stream['samples'][:8])}...\033[0m")
|
|
192
251
|
content_lines.append("")
|
|
193
252
|
|
|
194
|
-
elif
|
|
253
|
+
elif panel_idx == 2:
|
|
195
254
|
if not state.starred:
|
|
196
|
-
content_lines = ["", " No starred insights yet.", "", " Press 's'
|
|
255
|
+
content_lines = ["", " No starred insights yet.", "", " Press 's' to star current stream."]
|
|
197
256
|
else:
|
|
198
257
|
for i, item in enumerate(state.starred):
|
|
199
|
-
content_lines.append(f"★ {i+1}. [{item.get('
|
|
258
|
+
content_lines.append(f"★ {i+1}. [T={item.get('temp', 0):.1f}]")
|
|
200
259
|
for line in wrap_text(item.get('insight', ''), content_w - 4):
|
|
201
260
|
content_lines.append(f" {line}")
|
|
202
261
|
content_lines.append("")
|
|
203
262
|
|
|
204
|
-
# Apply scroll and render content
|
|
205
263
|
visible = content_lines[state.scroll_offset:state.scroll_offset + content_h]
|
|
206
264
|
for i, line in enumerate(visible):
|
|
207
|
-
# Strip ANSI for length calc but keep for display
|
|
208
265
|
out.append(f"\033[{4+i};{left_w+3}H{line[:content_w]}")
|
|
209
266
|
|
|
210
|
-
# Scroll indicator
|
|
211
267
|
if len(content_lines) > content_h:
|
|
212
268
|
scroll_pct = state.scroll_offset / max(1, len(content_lines) - content_h)
|
|
213
269
|
indicator_pos = int(scroll_pct * (content_h - 1))
|
|
214
270
|
out.append(f"\033[{4+indicator_pos};{width-1}H\033[33m▐\033[0m")
|
|
215
271
|
|
|
216
|
-
#
|
|
272
|
+
# Footer
|
|
217
273
|
panel_tabs = ""
|
|
218
274
|
for i, name in enumerate(["Output", "Streams", "Starred"]):
|
|
219
|
-
if i ==
|
|
275
|
+
if i == panel_idx:
|
|
220
276
|
panel_tabs += f"\033[7m {name} \033[0m "
|
|
221
277
|
else:
|
|
222
278
|
panel_tabs += f"\033[90m {name} \033[0m "
|
|
@@ -224,14 +280,46 @@ steps:
|
|
|
224
280
|
out.append(f"\033[{height-1};1H\033[90m{'─' * width}\033[0m")
|
|
225
281
|
out.append(f"\033[{height};1H{panel_tabs}")
|
|
226
282
|
|
|
227
|
-
|
|
228
|
-
|
|
283
|
+
def render_review(out, width, height):
|
|
284
|
+
panel_h = height - 4
|
|
285
|
+
out.append(draw_box(1, 3, width, panel_h, "Review Insights", "\033[33m"))
|
|
286
|
+
|
|
287
|
+
content_w = width - 6
|
|
288
|
+
content_h = panel_h - 5
|
|
289
|
+
|
|
290
|
+
# Sample count control at top
|
|
291
|
+
if state.editing_count:
|
|
292
|
+
count_line = f" Insights to sample: \033[7m {state.count_buf} \033[0m (Enter to confirm, Esc to cancel)"
|
|
293
|
+
else:
|
|
294
|
+
count_line = f" Insights to sample: \033[1m{state.review_sample_count}\033[0m / {len(state.review_items)} (n to change)"
|
|
295
|
+
out.append(f"\033[4;3H{count_line[:content_w]}")
|
|
296
|
+
out.append(f"\033[5;3H\033[90m{'─' * (content_w)}\033[0m")
|
|
297
|
+
|
|
298
|
+
# Items list
|
|
299
|
+
for i in range(content_h):
|
|
300
|
+
idx = state.review_scroll + i
|
|
301
|
+
row = 6 + i
|
|
302
|
+
out.append(f"\033[{row};3H\033[K")
|
|
303
|
+
if idx >= len(state.review_items):
|
|
304
|
+
continue
|
|
305
|
+
item = state.review_items[idx]
|
|
306
|
+
check = "\033[32m[x]\033[0m" if item['selected'] else "\033[90m[ ]\033[0m"
|
|
307
|
+
cursor = "\033[7m>" if idx == state.review_cursor else " "
|
|
308
|
+
text = item['text_preview'][:content_w - 10]
|
|
309
|
+
if idx == state.review_cursor:
|
|
310
|
+
out.append(f"{cursor} {check} {text}\033[0m")
|
|
311
|
+
else:
|
|
312
|
+
out.append(f" {check} {text}")
|
|
313
|
+
|
|
314
|
+
# Footer
|
|
315
|
+
out.append(f"\033[{height-1};1H\033[90m{'─' * width}\033[0m")
|
|
316
|
+
footer = " j/k:Nav SPACE:Toggle a:All x:None n:Count Enter:Synthesize Esc:Back "
|
|
317
|
+
out.append(f"\033[{height};1H\033[7m{footer.ljust(width)}\033[0m")
|
|
229
318
|
|
|
230
319
|
# ========== Actions ==========
|
|
231
320
|
def generate_environment():
|
|
232
321
|
state.status = "Generating environment..."
|
|
233
322
|
state.generating = True
|
|
234
|
-
render_screen()
|
|
235
323
|
|
|
236
324
|
env_prompt = f"""Create a vivid, metaphorical environment for wandering through while exploring:
|
|
237
325
|
"{problem}"
|
|
@@ -249,82 +337,131 @@ steps:
|
|
|
249
337
|
state.status = "Ready"
|
|
250
338
|
state.generating = False
|
|
251
339
|
|
|
252
|
-
def
|
|
253
|
-
|
|
254
|
-
|
|
340
|
+
def run_one_stream():
|
|
341
|
+
"""Original algorithm: generate with random word-count cutoff, subsample, extract samples."""
|
|
342
|
+
# Alternate temperature
|
|
343
|
+
if len(state.streams) % 2 == 0:
|
|
344
|
+
state.current_temp = low_temp
|
|
345
|
+
is_high = False
|
|
346
|
+
else:
|
|
347
|
+
state.current_temp = high_temp
|
|
348
|
+
is_high = True
|
|
255
349
|
|
|
256
|
-
|
|
350
|
+
temp = state.current_temp
|
|
351
|
+
word_limit = random.randint(n_min, n_max)
|
|
352
|
+
state.status = f"Stream {len(state.streams)+1} (T={temp:.1f}, ~{word_limit}w)..."
|
|
257
353
|
state.generating = True
|
|
258
|
-
render_screen()
|
|
259
354
|
|
|
260
|
-
# Build
|
|
261
|
-
|
|
262
|
-
|
|
355
|
+
# Build prompt
|
|
356
|
+
if is_high:
|
|
357
|
+
# High-temp: use subsample seeds from previous stream
|
|
358
|
+
seeds = []
|
|
359
|
+
if state.streams:
|
|
360
|
+
prev = state.streams[-1]
|
|
361
|
+
seeds = subsample_words(prev.get('insight', ''), k=20)
|
|
362
|
+
seed_text = ' '.join(seeds) if seeds else problem
|
|
363
|
+
|
|
364
|
+
sys_prompt = "Just generate without thinking. Let words and ideas flow freely. Do not filter or organize."
|
|
365
|
+
wander_prompt = f"""Seeds: {seed_text}
|
|
263
366
|
|
|
264
|
-
|
|
367
|
+
Context: {state.environment or 'a conceptual landscape'}
|
|
368
|
+
Problem: "{problem}"
|
|
265
369
|
|
|
266
|
-
|
|
370
|
+
Generate freely from these seeds. Follow any tangent. No structure needed."""
|
|
371
|
+
else:
|
|
372
|
+
# Low-temp: focused exploration
|
|
373
|
+
recent = state.streams[-3:] if state.streams else []
|
|
374
|
+
recent_context = "\n".join([s.get('insight', '')[:200] for s in recent]) if recent else "Starting fresh"
|
|
267
375
|
|
|
268
|
-
|
|
376
|
+
sys_prompt = None
|
|
377
|
+
wander_prompt = f"""You are wandering through: {state.environment or 'a conceptual landscape'}
|
|
269
378
|
|
|
270
|
-
|
|
271
|
-
- Let associations flow freely
|
|
272
|
-
- Notice unexpected connections
|
|
273
|
-
- Follow interesting tangents
|
|
274
|
-
- Share what emerges
|
|
379
|
+
Problem: "{problem}"
|
|
275
380
|
|
|
276
|
-
|
|
381
|
+
Recent thoughts: {recent_context}
|
|
277
382
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
383
|
+
In this exploration:
|
|
384
|
+
- Let associations flow freely
|
|
385
|
+
- Notice unexpected connections
|
|
386
|
+
- Follow interesting tangents
|
|
387
|
+
- Share what emerges
|
|
281
388
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
389
|
+
Respond naturally, 2-4 paragraphs."""
|
|
390
|
+
|
|
391
|
+
try:
|
|
392
|
+
kwargs = dict(model=model, provider=provider, temperature=temp, npc=npc)
|
|
393
|
+
if sys_prompt:
|
|
394
|
+
kwargs['system_prompt'] = sys_prompt
|
|
395
|
+
resp = get_llm_response(wander_prompt, **kwargs)
|
|
396
|
+
insight = str(resp.get('response', ''))
|
|
397
|
+
except Exception as e:
|
|
398
|
+
insight = "Error: " + str(e)
|
|
399
|
+
|
|
400
|
+
# Probabilistic interruption: truncate at random word count
|
|
401
|
+
if random.random() < interruption_likelihood:
|
|
402
|
+
word_limit = random.randint(n_min // 2, n_min)
|
|
403
|
+
insight = truncate_to_word_count(insight, word_limit)
|
|
404
|
+
|
|
405
|
+
# Extract samples
|
|
406
|
+
words = insight.split()
|
|
407
|
+
samples = extract_samples(insight, sample_rate)
|
|
408
|
+
state.all_samples.extend(samples)
|
|
409
|
+
|
|
410
|
+
stream_entry = {
|
|
411
|
+
'temp': temp,
|
|
285
412
|
'insight': insight,
|
|
413
|
+
'words': words,
|
|
414
|
+
'samples': samples,
|
|
286
415
|
'event': None,
|
|
287
416
|
'starred': False,
|
|
288
417
|
'timestamp': datetime.now().isoformat()
|
|
289
418
|
}
|
|
290
|
-
state.streams.append(
|
|
419
|
+
state.streams.append(stream_entry)
|
|
291
420
|
state.last_output = insight
|
|
292
|
-
state.scroll_offset = 0
|
|
293
|
-
state.status = "Ready"
|
|
294
|
-
state.generating = False
|
|
295
421
|
|
|
296
|
-
|
|
297
|
-
if state.
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
state.status = "Event occurring..."
|
|
301
|
-
state.generating = True
|
|
302
|
-
render_screen()
|
|
422
|
+
# After high-temp streams, maybe inject an event
|
|
423
|
+
if is_high and include_events and len(state.events) < num_events:
|
|
424
|
+
if random.random() < 0.3:
|
|
425
|
+
_trigger_event_sync()
|
|
303
426
|
|
|
304
|
-
|
|
305
|
-
|
|
427
|
+
state.scroll_offset = 0
|
|
428
|
+
state.generating = False
|
|
429
|
+
state.status = f"Ready. {len(state.streams)} streams, {len(state.all_samples)} samples"
|
|
306
430
|
|
|
431
|
+
def _trigger_event_sync():
|
|
432
|
+
event_type = weighted_event_type()
|
|
307
433
|
event_prompt = f"""In the environment: {state.environment or 'a conceptual landscape'}
|
|
308
434
|
While exploring "{problem}", a {event_type} occurs.
|
|
309
435
|
|
|
310
436
|
Describe this {event_type} in 2-3 vivid sentences.
|
|
311
437
|
Make it metaphorical and thought-provoking."""
|
|
312
438
|
|
|
313
|
-
|
|
314
|
-
|
|
439
|
+
try:
|
|
440
|
+
resp = get_llm_response(event_prompt, model=model, provider=provider, temperature=1.0, npc=npc)
|
|
441
|
+
event_text = str(resp.get('response', ''))
|
|
442
|
+
except Exception as e:
|
|
443
|
+
event_text = f"A {event_type} occurred but faded before you could grasp it."
|
|
444
|
+
|
|
445
|
+
state.events.append({'type': event_type, 'text': event_text})
|
|
446
|
+
if state.streams:
|
|
447
|
+
state.streams[-1]['event'] = {'type': event_type, 'text': event_text}
|
|
448
|
+
state.last_output = f"[{event_type.upper()}]\n\n{event_text}"
|
|
315
449
|
|
|
316
|
-
|
|
450
|
+
def trigger_event():
|
|
451
|
+
if state.generating:
|
|
452
|
+
return
|
|
453
|
+
state.status = "Event occurring..."
|
|
454
|
+
state.generating = True
|
|
455
|
+
_trigger_event_sync()
|
|
317
456
|
state.scroll_offset = 0
|
|
318
457
|
state.status = "Ready"
|
|
319
458
|
state.generating = False
|
|
320
459
|
|
|
321
460
|
def toggle_temp():
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
state.temp_mode = modes[idx]
|
|
327
|
-
state.current_temp = temps[idx]
|
|
461
|
+
if state.current_temp == high_temp:
|
|
462
|
+
state.current_temp = low_temp
|
|
463
|
+
else:
|
|
464
|
+
state.current_temp = high_temp
|
|
328
465
|
|
|
329
466
|
def star_current():
|
|
330
467
|
if state.streams and not state.streams[-1].get('starred'):
|
|
@@ -332,118 +469,252 @@ steps:
|
|
|
332
469
|
state.starred.append(state.streams[-1])
|
|
333
470
|
state.status = "★ Starred!"
|
|
334
471
|
|
|
335
|
-
def
|
|
336
|
-
|
|
472
|
+
def enter_review():
|
|
473
|
+
"""Enter review mode where user selects insights for synthesis."""
|
|
474
|
+
if not state.streams:
|
|
475
|
+
state.status = "No streams to review"
|
|
476
|
+
return
|
|
477
|
+
state.review_mode = True
|
|
478
|
+
state.review_items = []
|
|
479
|
+
for i, s in enumerate(state.streams):
|
|
480
|
+
preview = s.get('insight', '')[:120].replace('\n', ' ')
|
|
481
|
+
state.review_items.append({
|
|
482
|
+
'idx': i,
|
|
483
|
+
'text_preview': f"[T={s.get('temp', 0):.1f}] {preview}",
|
|
484
|
+
'selected': s.get('starred', False),
|
|
485
|
+
})
|
|
486
|
+
# Default: select starred + random sample
|
|
487
|
+
n_select = max(1, min(len(state.review_items), int(len(state.review_items) * sample_rate)))
|
|
488
|
+
state.review_sample_count = n_select
|
|
489
|
+
# Auto-select starred ones, then fill randomly
|
|
490
|
+
selected_idxs = set()
|
|
491
|
+
for item in state.review_items:
|
|
492
|
+
if state.streams[item['idx']].get('starred'):
|
|
493
|
+
item['selected'] = True
|
|
494
|
+
selected_idxs.add(item['idx'])
|
|
495
|
+
remaining = [item for item in state.review_items if item['idx'] not in selected_idxs]
|
|
496
|
+
n_random = max(0, n_select - len(selected_idxs))
|
|
497
|
+
if remaining and n_random > 0:
|
|
498
|
+
for item in random.sample(remaining, min(n_random, len(remaining))):
|
|
499
|
+
item['selected'] = True
|
|
500
|
+
state.review_cursor = 0
|
|
501
|
+
state.review_scroll = 0
|
|
502
|
+
|
|
503
|
+
def synthesize_from_review():
|
|
504
|
+
"""Synthesize using the selected review items."""
|
|
505
|
+
selected = [state.review_items[i] for i in range(len(state.review_items)) if state.review_items[i]['selected']]
|
|
506
|
+
if not selected:
|
|
507
|
+
state.status = "No insights selected"
|
|
337
508
|
return
|
|
338
509
|
|
|
510
|
+
state.review_mode = False
|
|
339
511
|
state.status = "Synthesizing..."
|
|
340
512
|
state.generating = True
|
|
341
|
-
render_screen()
|
|
342
513
|
|
|
343
|
-
|
|
344
|
-
|
|
514
|
+
selected_insights = []
|
|
515
|
+
for item in selected:
|
|
516
|
+
idx = item['idx']
|
|
517
|
+
selected_insights.append(state.streams[idx].get('insight', ''))
|
|
518
|
+
|
|
519
|
+
# Collect samples from selected streams
|
|
520
|
+
selected_samples = []
|
|
521
|
+
for item in selected:
|
|
522
|
+
idx = item['idx']
|
|
523
|
+
selected_samples.extend(state.streams[idx].get('samples', []))
|
|
345
524
|
|
|
346
|
-
|
|
525
|
+
sample_text = ' '.join(selected_samples[:50]) if selected_samples else 'N/A'
|
|
347
526
|
|
|
348
|
-
|
|
349
|
-
{all_insights}
|
|
527
|
+
synth_prompt = f"""You are synthesizing a wandering exploration of: "{problem}"
|
|
350
528
|
|
|
351
|
-
|
|
352
|
-
{starred_insights}
|
|
529
|
+
Environment traversed: {state.environment or 'a conceptual landscape'}
|
|
353
530
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
2. Most surprising connections
|
|
357
|
-
3. Questions worth pursuing
|
|
358
|
-
4. Potential next steps
|
|
531
|
+
Selected explorations ({len(selected)} of {len(state.streams)} streams):
|
|
532
|
+
{"---".join(selected_insights)}
|
|
359
533
|
|
|
360
|
-
|
|
534
|
+
Word samples extracted during wandering: {sample_text}
|
|
361
535
|
|
|
362
|
-
|
|
363
|
-
|
|
536
|
+
Events encountered: {'; '.join([e['type'] + ': ' + e['text'][:100] for e in state.events]) if state.events else 'None'}
|
|
537
|
+
|
|
538
|
+
From these wanderings, synthesize creative hypotheses:
|
|
539
|
+
1. What unexpected patterns emerge from the word samples?
|
|
540
|
+
2. What creative hypotheses can you form by connecting disparate ideas?
|
|
541
|
+
3. What questions have emerged that weren't visible at the start?
|
|
542
|
+
4. What surprising connections exist between the different temperature explorations?
|
|
543
|
+
|
|
544
|
+
Be bold, creative, and insightful. These hypotheses should feel like discoveries, not summaries."""
|
|
545
|
+
|
|
546
|
+
try:
|
|
547
|
+
resp = get_llm_response(synth_prompt, model=model, provider=provider, temperature=0.4, npc=npc)
|
|
548
|
+
synthesis = str(resp.get('response', ''))
|
|
549
|
+
except Exception as e:
|
|
550
|
+
synthesis = "Error during synthesis: " + str(e)
|
|
364
551
|
|
|
365
552
|
state.last_output = "=== SYNTHESIS ===\n\n" + synthesis
|
|
366
553
|
state.scroll_offset = 0
|
|
554
|
+
state.current_panel = 0
|
|
367
555
|
state.status = "Ready"
|
|
368
556
|
state.generating = False
|
|
369
557
|
|
|
558
|
+
# ========== Auto-run ==========
|
|
559
|
+
def auto_run():
|
|
560
|
+
if not environment:
|
|
561
|
+
generate_environment()
|
|
562
|
+
else:
|
|
563
|
+
state.environment = environment
|
|
564
|
+
|
|
565
|
+
total_streams = n_high_temp_streams * 2 # low + high alternating
|
|
566
|
+
for i in range(total_streams):
|
|
567
|
+
if state.quit_requested:
|
|
568
|
+
break
|
|
569
|
+
run_one_stream()
|
|
570
|
+
|
|
571
|
+
if not state.quit_requested:
|
|
572
|
+
state.auto_done = True
|
|
573
|
+
state.status = f"Done! {len(state.streams)} streams. R=Review, SPACE=more, q=quit"
|
|
574
|
+
|
|
370
575
|
# ========== Main Loop ==========
|
|
371
576
|
fd = sys.stdin.fileno()
|
|
372
577
|
old_settings = termios.tcgetattr(fd)
|
|
578
|
+
import select as _sel
|
|
373
579
|
|
|
374
580
|
try:
|
|
375
581
|
tty.setcbreak(fd)
|
|
376
|
-
sys.stdout.write('\033[?25l')
|
|
582
|
+
sys.stdout.write('\033[?25l')
|
|
377
583
|
sys.stdout.flush()
|
|
378
584
|
|
|
379
|
-
# Generate environment AFTER TUI is set up
|
|
380
|
-
if environment:
|
|
381
|
-
state.environment = environment
|
|
382
|
-
else:
|
|
383
|
-
generate_environment()
|
|
384
|
-
|
|
385
585
|
render_screen()
|
|
386
586
|
|
|
387
|
-
|
|
388
|
-
|
|
587
|
+
auto_thread = threading.Thread(target=auto_run, daemon=True)
|
|
588
|
+
auto_thread.start()
|
|
389
589
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
590
|
+
while True:
|
|
591
|
+
if _sel.select([fd], [], [], 0.3)[0]:
|
|
592
|
+
c = os.read(fd, 1).decode('latin-1')
|
|
593
|
+
|
|
594
|
+
if state.review_mode:
|
|
595
|
+
# Review mode input
|
|
596
|
+
if state.editing_count:
|
|
597
|
+
if c in ('\r', '\n'):
|
|
598
|
+
try:
|
|
599
|
+
val = int(state.count_buf)
|
|
600
|
+
state.review_sample_count = max(0, min(val, len(state.review_items)))
|
|
601
|
+
except:
|
|
602
|
+
pass
|
|
603
|
+
state.editing_count = False
|
|
604
|
+
elif c == '\x1b':
|
|
605
|
+
state.editing_count = False
|
|
606
|
+
elif c == '\x7f' or c == '\x08':
|
|
607
|
+
state.count_buf = state.count_buf[:-1]
|
|
608
|
+
elif c.isdigit():
|
|
609
|
+
state.count_buf += c
|
|
610
|
+
else:
|
|
611
|
+
if c == '\x1b':
|
|
612
|
+
if _sel.select([fd], [], [], 0.05)[0]:
|
|
613
|
+
c2 = os.read(fd, 1).decode('latin-1')
|
|
614
|
+
if c2 == '[':
|
|
615
|
+
c3 = os.read(fd, 1).decode('latin-1')
|
|
616
|
+
if c3 == 'A':
|
|
617
|
+
state.review_cursor = max(0, state.review_cursor - 1)
|
|
618
|
+
elif c3 == 'B':
|
|
619
|
+
state.review_cursor = min(len(state.review_items) - 1, state.review_cursor + 1)
|
|
620
|
+
else:
|
|
621
|
+
state.review_mode = False
|
|
622
|
+
elif c == 'j':
|
|
623
|
+
state.review_cursor = min(len(state.review_items) - 1, state.review_cursor + 1)
|
|
624
|
+
elif c == 'k':
|
|
625
|
+
state.review_cursor = max(0, state.review_cursor - 1)
|
|
626
|
+
elif c == ' ':
|
|
627
|
+
if state.review_items:
|
|
628
|
+
state.review_items[state.review_cursor]['selected'] = not state.review_items[state.review_cursor]['selected']
|
|
629
|
+
elif c == 'a':
|
|
630
|
+
for item in state.review_items:
|
|
631
|
+
item['selected'] = True
|
|
632
|
+
elif c == 'x':
|
|
633
|
+
for item in state.review_items:
|
|
634
|
+
item['selected'] = False
|
|
635
|
+
elif c == 'n':
|
|
636
|
+
state.editing_count = True
|
|
637
|
+
state.count_buf = str(state.review_sample_count)
|
|
638
|
+
elif c in ('\r', '\n'):
|
|
639
|
+
threading.Thread(target=synthesize_from_review, daemon=True).start()
|
|
640
|
+
elif c == 'q':
|
|
641
|
+
state.quit_requested = True
|
|
642
|
+
break
|
|
643
|
+
# Keep cursor in scroll view
|
|
644
|
+
_, h = get_size()
|
|
645
|
+
view_h = h - 9
|
|
646
|
+
if state.review_cursor < state.review_scroll:
|
|
647
|
+
state.review_scroll = state.review_cursor
|
|
648
|
+
elif state.review_cursor >= state.review_scroll + view_h:
|
|
649
|
+
state.review_scroll = state.review_cursor - view_h + 1
|
|
650
|
+
else:
|
|
651
|
+
# Explore mode input
|
|
652
|
+
if c == 'q' or c == '\x03':
|
|
653
|
+
state.quit_requested = True
|
|
654
|
+
break
|
|
655
|
+
elif c == ' ':
|
|
656
|
+
if not state.generating:
|
|
657
|
+
threading.Thread(target=run_one_stream, daemon=True).start()
|
|
658
|
+
elif c == 't':
|
|
659
|
+
toggle_temp()
|
|
660
|
+
elif c == 'e':
|
|
661
|
+
if not state.generating:
|
|
662
|
+
threading.Thread(target=trigger_event, daemon=True).start()
|
|
663
|
+
elif c == 's':
|
|
664
|
+
star_current()
|
|
665
|
+
elif c == 'R' or c == 'r':
|
|
666
|
+
if not state.generating:
|
|
667
|
+
enter_review()
|
|
668
|
+
elif c == '\t':
|
|
418
669
|
state.current_panel = (state.current_panel + 1) % 3
|
|
419
670
|
state.scroll_offset = 0
|
|
420
|
-
elif
|
|
421
|
-
state.
|
|
422
|
-
|
|
671
|
+
elif c == 'j':
|
|
672
|
+
state.scroll_offset += 1
|
|
673
|
+
elif c == 'k':
|
|
674
|
+
state.scroll_offset = max(0, state.scroll_offset - 1)
|
|
675
|
+
elif c == '\x1b':
|
|
676
|
+
if _sel.select([fd], [], [], 0.05)[0]:
|
|
677
|
+
c2 = os.read(fd, 1).decode('latin-1')
|
|
678
|
+
if c2 == '[':
|
|
679
|
+
c3 = os.read(fd, 1).decode('latin-1')
|
|
680
|
+
if c3 == 'A':
|
|
681
|
+
state.scroll_offset = max(0, state.scroll_offset - 1)
|
|
682
|
+
elif c3 == 'B':
|
|
683
|
+
state.scroll_offset += 1
|
|
684
|
+
elif c3 == 'C':
|
|
685
|
+
state.current_panel = (state.current_panel + 1) % 3
|
|
686
|
+
state.scroll_offset = 0
|
|
687
|
+
elif c3 == 'D':
|
|
688
|
+
state.current_panel = (state.current_panel - 1) % 3
|
|
689
|
+
state.scroll_offset = 0
|
|
690
|
+
else:
|
|
691
|
+
state.quit_requested = True
|
|
692
|
+
break
|
|
423
693
|
|
|
424
694
|
render_screen()
|
|
425
695
|
|
|
426
696
|
finally:
|
|
427
697
|
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
|
|
428
|
-
sys.stdout.write('\033[?25h')
|
|
429
|
-
sys.stdout.write('\033[2J\033[H')
|
|
698
|
+
sys.stdout.write('\033[?25h')
|
|
699
|
+
sys.stdout.write('\033[2J\033[H')
|
|
430
700
|
sys.stdout.flush()
|
|
431
701
|
|
|
432
|
-
# Final output
|
|
433
702
|
if state.streams:
|
|
434
703
|
print(colored("=== WANDER SESSION COMPLETE ===\n", "green"))
|
|
435
704
|
print(f"Problem: {problem}")
|
|
436
705
|
print(f"Streams: {len(state.streams)}")
|
|
437
|
-
print(f"
|
|
706
|
+
print(f"Samples: {len(state.all_samples)}")
|
|
707
|
+
print(f"Starred: {len(state.starred)}")
|
|
708
|
+
print(f"Events: {len(state.events)}\n")
|
|
438
709
|
|
|
439
710
|
if state.starred:
|
|
440
711
|
print(colored("Starred Insights:", "yellow"))
|
|
441
712
|
for i, s in enumerate(state.starred):
|
|
442
|
-
print(f"\n{i+1}. [{s.get('
|
|
713
|
+
print(f"\n{i+1}. [T={s.get('temp', 0):.1f}] {s.get('insight', '')[:300]}...")
|
|
443
714
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
715
|
+
if state.last_output and "SYNTHESIS" in state.last_output:
|
|
716
|
+
print(colored("\n--- Final Synthesis ---", "cyan"))
|
|
717
|
+
print(state.last_output)
|
|
447
718
|
|
|
448
719
|
context['output'] = state.last_output
|
|
449
720
|
context['messages'] = messages
|
|
@@ -452,4 +723,6 @@ steps:
|
|
|
452
723
|
'environment': state.environment,
|
|
453
724
|
'streams': state.streams,
|
|
454
725
|
'starred': state.starred,
|
|
726
|
+
'events': state.events,
|
|
727
|
+
'samples': state.all_samples,
|
|
455
728
|
}
|