npcsh 1.0.25__py3-none-any.whl → 1.0.27__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 +105 -105
- npcsh/alicanto.py +88 -88
- npcsh/corca.py +423 -81
- npcsh/guac.py +110 -107
- npcsh/mcp_helpers.py +45 -45
- npcsh/mcp_server.py +16 -17
- npcsh/npc.py +16 -17
- npcsh/npc_team/jinxs/bash_executer.jinx +1 -1
- npcsh/npc_team/jinxs/edit_file.jinx +6 -6
- npcsh/npc_team/jinxs/image_generation.jinx +5 -5
- npcsh/npc_team/jinxs/screen_cap.jinx +2 -2
- npcsh/npcsh.py +5 -2
- npcsh/plonk.py +8 -8
- npcsh/routes.py +110 -90
- npcsh/spool.py +13 -13
- npcsh/wander.py +37 -37
- npcsh/yap.py +72 -72
- {npcsh-1.0.25.data → npcsh-1.0.27.data}/data/npcsh/npc_team/bash_executer.jinx +1 -1
- {npcsh-1.0.25.data → npcsh-1.0.27.data}/data/npcsh/npc_team/edit_file.jinx +6 -6
- {npcsh-1.0.25.data → npcsh-1.0.27.data}/data/npcsh/npc_team/image_generation.jinx +5 -5
- {npcsh-1.0.25.data → npcsh-1.0.27.data}/data/npcsh/npc_team/screen_cap.jinx +2 -2
- {npcsh-1.0.25.dist-info → npcsh-1.0.27.dist-info}/METADATA +12 -6
- npcsh-1.0.27.dist-info/RECORD +73 -0
- npcsh-1.0.25.dist-info/RECORD +0 -73
- {npcsh-1.0.25.data → npcsh-1.0.27.data}/data/npcsh/npc_team/alicanto.npc +0 -0
- {npcsh-1.0.25.data → npcsh-1.0.27.data}/data/npcsh/npc_team/alicanto.png +0 -0
- {npcsh-1.0.25.data → npcsh-1.0.27.data}/data/npcsh/npc_team/corca.npc +0 -0
- {npcsh-1.0.25.data → npcsh-1.0.27.data}/data/npcsh/npc_team/corca.png +0 -0
- {npcsh-1.0.25.data → npcsh-1.0.27.data}/data/npcsh/npc_team/foreman.npc +0 -0
- {npcsh-1.0.25.data → npcsh-1.0.27.data}/data/npcsh/npc_team/frederic.npc +0 -0
- {npcsh-1.0.25.data → npcsh-1.0.27.data}/data/npcsh/npc_team/frederic4.png +0 -0
- {npcsh-1.0.25.data → npcsh-1.0.27.data}/data/npcsh/npc_team/guac.png +0 -0
- {npcsh-1.0.25.data → npcsh-1.0.27.data}/data/npcsh/npc_team/internet_search.jinx +0 -0
- {npcsh-1.0.25.data → npcsh-1.0.27.data}/data/npcsh/npc_team/kadiefa.npc +0 -0
- {npcsh-1.0.25.data → npcsh-1.0.27.data}/data/npcsh/npc_team/kadiefa.png +0 -0
- {npcsh-1.0.25.data → npcsh-1.0.27.data}/data/npcsh/npc_team/npcsh.ctx +0 -0
- {npcsh-1.0.25.data → npcsh-1.0.27.data}/data/npcsh/npc_team/npcsh_sibiji.png +0 -0
- {npcsh-1.0.25.data → npcsh-1.0.27.data}/data/npcsh/npc_team/plonk.npc +0 -0
- {npcsh-1.0.25.data → npcsh-1.0.27.data}/data/npcsh/npc_team/plonk.png +0 -0
- {npcsh-1.0.25.data → npcsh-1.0.27.data}/data/npcsh/npc_team/plonkjr.npc +0 -0
- {npcsh-1.0.25.data → npcsh-1.0.27.data}/data/npcsh/npc_team/plonkjr.png +0 -0
- {npcsh-1.0.25.data → npcsh-1.0.27.data}/data/npcsh/npc_team/python_executor.jinx +0 -0
- {npcsh-1.0.25.data → npcsh-1.0.27.data}/data/npcsh/npc_team/sibiji.npc +0 -0
- {npcsh-1.0.25.data → npcsh-1.0.27.data}/data/npcsh/npc_team/sibiji.png +0 -0
- {npcsh-1.0.25.data → npcsh-1.0.27.data}/data/npcsh/npc_team/spool.png +0 -0
- {npcsh-1.0.25.data → npcsh-1.0.27.data}/data/npcsh/npc_team/yap.png +0 -0
- {npcsh-1.0.25.dist-info → npcsh-1.0.27.dist-info}/WHEEL +0 -0
- {npcsh-1.0.25.dist-info → npcsh-1.0.27.dist-info}/entry_points.txt +0 -0
- {npcsh-1.0.25.dist-info → npcsh-1.0.27.dist-info}/licenses/LICENSE +0 -0
- {npcsh-1.0.25.dist-info → npcsh-1.0.27.dist-info}/top_level.txt +0 -0
npcsh/plonk.py
CHANGED
|
@@ -8,7 +8,7 @@ import matplotlib.pyplot as plt
|
|
|
8
8
|
import matplotlib.patches as patches
|
|
9
9
|
from PIL import Image
|
|
10
10
|
import numpy as np
|
|
11
|
-
import imagehash
|
|
11
|
+
import imagehash
|
|
12
12
|
from npcsh._state import NPCSH_VISION_MODEL, NPCSH_VISION_PROVIDER
|
|
13
13
|
import argparse
|
|
14
14
|
from npcpy.npc_compiler import NPC
|
|
@@ -40,7 +40,7 @@ def format_plonk_summary(synthesized_summary: list) -> str:
|
|
|
40
40
|
def get_image_hash(image_path):
|
|
41
41
|
"""Generate a perceptual hash of the image to detect screen changes intelligently."""
|
|
42
42
|
try:
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
return imagehash.phash(Image.open(image_path))
|
|
45
45
|
except Exception as e:
|
|
46
46
|
print(f"Could not generate image hash: {e}")
|
|
@@ -59,7 +59,7 @@ def add_click_vector_trail(image_path, click_history, output_path):
|
|
|
59
59
|
font_size = max(12, min(width, height) // 80)
|
|
60
60
|
colors = plt.cm.viridis(np.linspace(0.3, 1.0, len(click_history)))
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
if len(click_history) > 1:
|
|
64
64
|
for i in range(len(click_history) - 1):
|
|
65
65
|
x1, y1 = (click_history[i]['x'] * width / 100, click_history[i]['y'] * height / 100)
|
|
@@ -68,7 +68,7 @@ def add_click_vector_trail(image_path, click_history, output_path):
|
|
|
68
68
|
arrowprops=dict(arrowstyle='->,head_width=0.6,head_length=0.8',
|
|
69
69
|
lw=3, color='cyan', alpha=0.9, shrinkA=25, shrinkB=25))
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
|
|
72
72
|
for i, click in enumerate(click_history):
|
|
73
73
|
x_pixel = int(click['x'] * width / 100)
|
|
74
74
|
y_pixel = int(click['y'] * height / 100)
|
|
@@ -79,15 +79,15 @@ def add_click_vector_trail(image_path, click_history, output_path):
|
|
|
79
79
|
facecolor=colors[i], alpha=0.9)
|
|
80
80
|
ax.add_patch(circle)
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
|
|
83
83
|
ax.text(x_pixel, y_pixel, str(i+1),
|
|
84
84
|
fontsize=font_size + 4,
|
|
85
85
|
color='white', weight='bold', ha='center', va='center')
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
|
|
88
88
|
coord_text = f"({click['x']}, {click['y']})"
|
|
89
|
-
ax.text(x_pixel + radius + 5,
|
|
90
|
-
y_pixel,
|
|
89
|
+
ax.text(x_pixel + radius + 5,
|
|
90
|
+
y_pixel,
|
|
91
91
|
coord_text,
|
|
92
92
|
fontsize=font_size,
|
|
93
93
|
color='white',
|
npcsh/routes.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
2
|
|
|
3
3
|
from typing import Callable, Dict, Any, List, Optional, Union
|
|
4
4
|
import functools
|
|
@@ -192,15 +192,15 @@ def safe_get(kwargs, key, default=None):
|
|
|
192
192
|
|
|
193
193
|
@router.route("breathe", "Condense context on a regular cadence")
|
|
194
194
|
def breathe_handler(command: str, **kwargs):
|
|
195
|
-
|
|
195
|
+
|
|
196
196
|
result = breathe(**kwargs)
|
|
197
197
|
if isinstance(result, dict):
|
|
198
198
|
return result
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
|
|
204
204
|
|
|
205
205
|
@router.route("compile", "Compile NPC profiles")
|
|
206
206
|
def compile_handler(command: str, **kwargs):
|
|
@@ -308,7 +308,7 @@ def help_handler(command: str, **kwargs):
|
|
|
308
308
|
parts = shlex.split(command)
|
|
309
309
|
if len(parts) < 2:
|
|
310
310
|
return {"output": get_help_text(), "messages": messages}
|
|
311
|
-
target = parts[1].lstrip('/')
|
|
311
|
+
target = parts[1].lstrip('/')
|
|
312
312
|
output = ""
|
|
313
313
|
|
|
314
314
|
|
|
@@ -330,7 +330,7 @@ def help_handler(command: str, **kwargs):
|
|
|
330
330
|
output += f"- **Associated Jinxs**: {jinx_names}\n"
|
|
331
331
|
return {"output": output, "messages": messages}
|
|
332
332
|
|
|
333
|
-
|
|
333
|
+
|
|
334
334
|
npc = safe_get(kwargs, 'npc')
|
|
335
335
|
jinx_obj = None
|
|
336
336
|
source = ""
|
|
@@ -363,10 +363,10 @@ def init_handler(command: str, **kwargs):
|
|
|
363
363
|
directory = "."
|
|
364
364
|
templates = None
|
|
365
365
|
context = None
|
|
366
|
-
|
|
366
|
+
|
|
367
367
|
if len(parts) > 1 and not parts[1].startswith("-"):
|
|
368
368
|
directory = parts[1]
|
|
369
|
-
|
|
369
|
+
|
|
370
370
|
|
|
371
371
|
initialize_npc_project(
|
|
372
372
|
directory=directory,
|
|
@@ -400,10 +400,10 @@ def ensure_repo():
|
|
|
400
400
|
|
|
401
401
|
def install_dependencies():
|
|
402
402
|
"""Install npm and pip dependencies."""
|
|
403
|
-
|
|
403
|
+
|
|
404
404
|
subprocess.check_call(["npm", "install"], cwd=NPC_STUDIO_DIR)
|
|
405
405
|
|
|
406
|
-
|
|
406
|
+
|
|
407
407
|
req_file = NPC_STUDIO_DIR / "requirements.txt"
|
|
408
408
|
if req_file.exists():
|
|
409
409
|
subprocess.check_call([sys.executable, "-m", "pip", "install", "-r", str(req_file)])
|
|
@@ -415,21 +415,21 @@ def launch_npc_studio(path_to_open: str = None):
|
|
|
415
415
|
ensure_repo()
|
|
416
416
|
install_dependencies()
|
|
417
417
|
|
|
418
|
-
|
|
418
|
+
|
|
419
419
|
backend = subprocess.Popen(
|
|
420
420
|
[sys.executable, "npc_studio_serve.py"],
|
|
421
421
|
cwd=NPC_STUDIO_DIR,
|
|
422
422
|
shell = False
|
|
423
423
|
)
|
|
424
424
|
|
|
425
|
-
|
|
425
|
+
|
|
426
426
|
dev_server = subprocess.Popen(
|
|
427
427
|
["npm", "run", "dev"],
|
|
428
428
|
cwd=NPC_STUDIO_DIR,
|
|
429
429
|
shell=False
|
|
430
430
|
)
|
|
431
431
|
|
|
432
|
-
|
|
432
|
+
|
|
433
433
|
frontend = subprocess.Popen(
|
|
434
434
|
["npm", "start"],
|
|
435
435
|
cwd=NPC_STUDIO_DIR,
|
|
@@ -437,7 +437,7 @@ def launch_npc_studio(path_to_open: str = None):
|
|
|
437
437
|
)
|
|
438
438
|
|
|
439
439
|
return backend, dev_server, frontend
|
|
440
|
-
|
|
440
|
+
|
|
441
441
|
@router.route("npc-studio", "Start npc studio")
|
|
442
442
|
def npc_studio_handler(command: str, **kwargs):
|
|
443
443
|
messages = kwargs.get("messages", [])
|
|
@@ -529,13 +529,13 @@ def plan_handler(command: str, **kwargs):
|
|
|
529
529
|
user_command = " ".join(command.split()[1:])
|
|
530
530
|
if not user_command:
|
|
531
531
|
return {"output": "Usage: /plan <description_of_plan>", "messages": messages}
|
|
532
|
-
|
|
532
|
+
|
|
533
533
|
return execute_plan_command(command=user_command, **kwargs)
|
|
534
534
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
|
|
539
539
|
|
|
540
540
|
@router.route("pti", "Enter Pardon-The-Interruption mode for human-in-the-loop reasoning.")
|
|
541
541
|
def pti_handler(command: str, **kwargs):
|
|
@@ -545,8 +545,8 @@ def pti_handler(command: str, **kwargs):
|
|
|
545
545
|
def plonk_handler(command: str, **kwargs):
|
|
546
546
|
messages = safe_get(kwargs, "messages", [])
|
|
547
547
|
|
|
548
|
-
|
|
549
|
-
|
|
548
|
+
|
|
549
|
+
|
|
550
550
|
positional_args = safe_get(kwargs, 'positional_args', [])
|
|
551
551
|
request_str = " ".join(positional_args)
|
|
552
552
|
|
|
@@ -556,14 +556,14 @@ def plonk_handler(command: str, **kwargs):
|
|
|
556
556
|
try:
|
|
557
557
|
plonk_context = safe_get(kwargs, 'plonk_context')
|
|
558
558
|
|
|
559
|
-
|
|
559
|
+
|
|
560
560
|
summary_data = execute_plonk_command(
|
|
561
561
|
request=request_str,
|
|
562
562
|
model=safe_get(kwargs, 'vmodel', NPCSH_VISION_MODEL),
|
|
563
563
|
provider=safe_get(kwargs, 'vprovider', NPCSH_VISION_PROVIDER),
|
|
564
564
|
npc=safe_get(kwargs, 'npc'),
|
|
565
565
|
plonk_context=plonk_context,
|
|
566
|
-
debug=True
|
|
566
|
+
debug=True
|
|
567
567
|
)
|
|
568
568
|
|
|
569
569
|
if summary_data and isinstance(summary_data, list):
|
|
@@ -586,11 +586,11 @@ def brainblast_handler(command: str, **kwargs):
|
|
|
586
586
|
if not search_query:
|
|
587
587
|
return {"output": "Usage: /brainblast <search_terms>", "messages": messages}
|
|
588
588
|
|
|
589
|
-
|
|
589
|
+
|
|
590
590
|
command_history = kwargs.get('command_history')
|
|
591
591
|
if not command_history:
|
|
592
|
-
|
|
593
|
-
|
|
592
|
+
|
|
593
|
+
|
|
594
594
|
db_path = safe_get(kwargs, "history_db_path", os.path.expanduser('~/npcsh_history.db'))
|
|
595
595
|
try:
|
|
596
596
|
command_history = CommandHistory(db_path)
|
|
@@ -599,11 +599,11 @@ def brainblast_handler(command: str, **kwargs):
|
|
|
599
599
|
return {"output": f"Error connecting to command history: {e}", "messages": messages}
|
|
600
600
|
|
|
601
601
|
try:
|
|
602
|
-
|
|
602
|
+
|
|
603
603
|
if 'messages' in kwargs:
|
|
604
604
|
del kwargs['messages']
|
|
605
605
|
|
|
606
|
-
|
|
606
|
+
|
|
607
607
|
return execute_brainblast_command(
|
|
608
608
|
command=search_query,
|
|
609
609
|
**kwargs)
|
|
@@ -617,17 +617,17 @@ def rag_handler(command: str, **kwargs):
|
|
|
617
617
|
user_command = []
|
|
618
618
|
file_paths = []
|
|
619
619
|
|
|
620
|
-
i = 1
|
|
620
|
+
i = 1
|
|
621
621
|
while i < len(parts):
|
|
622
622
|
if parts[i] == "-f" or parts[i] == "--file":
|
|
623
|
-
|
|
623
|
+
|
|
624
624
|
if i + 1 < len(parts):
|
|
625
625
|
file_paths.append(parts[i + 1])
|
|
626
|
-
i += 2
|
|
626
|
+
i += 2
|
|
627
627
|
else:
|
|
628
628
|
return {"output": "Error: -f/--file flag needs a file path", "messages": messages}
|
|
629
629
|
else:
|
|
630
|
-
|
|
630
|
+
|
|
631
631
|
user_command.append(parts[i])
|
|
632
632
|
i += 1
|
|
633
633
|
|
|
@@ -641,7 +641,7 @@ def rag_handler(command: str, **kwargs):
|
|
|
641
641
|
return {"output": "Usage: /rag [-f file_path] <query>", "messages": kwargs.get('messages', [])}
|
|
642
642
|
|
|
643
643
|
try:
|
|
644
|
-
|
|
644
|
+
|
|
645
645
|
file_contents = []
|
|
646
646
|
for file_path in file_paths:
|
|
647
647
|
try:
|
|
@@ -718,7 +718,7 @@ def sample_handler(command: str, **kwargs):
|
|
|
718
718
|
"npc":kwargs.get("npc"),
|
|
719
719
|
}
|
|
720
720
|
else:
|
|
721
|
-
|
|
721
|
+
|
|
722
722
|
return {"output": str(result), "messages": messages}
|
|
723
723
|
|
|
724
724
|
except Exception as e:
|
|
@@ -728,16 +728,16 @@ def sample_handler(command: str, **kwargs):
|
|
|
728
728
|
def search_handler(command: str, **kwargs):
|
|
729
729
|
"""
|
|
730
730
|
Executes a search command.
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
|
|
735
|
+
|
|
736
|
+
|
|
737
737
|
"""
|
|
738
738
|
messages = safe_get(kwargs, "messages", [])
|
|
739
739
|
|
|
740
|
-
|
|
740
|
+
|
|
741
741
|
positional_args = safe_get(kwargs, 'positional_args', [])
|
|
742
742
|
query = " ".join(positional_args)
|
|
743
743
|
|
|
@@ -764,11 +764,11 @@ def search_handler(command: str, **kwargs):
|
|
|
764
764
|
|
|
765
765
|
@router.route("serve", "Serve an NPC Team")
|
|
766
766
|
def serve_handler(command: str, **kwargs):
|
|
767
|
-
|
|
768
|
-
|
|
767
|
+
|
|
768
|
+
|
|
769
769
|
|
|
770
770
|
port = safe_get(kwargs, "port", 5337)
|
|
771
|
-
|
|
771
|
+
|
|
772
772
|
messages = safe_get(kwargs, "messages", [])
|
|
773
773
|
cors = safe_get(kwargs, "cors", None)
|
|
774
774
|
if cors:
|
|
@@ -821,13 +821,13 @@ def sleep_handler(command: str, **kwargs):
|
|
|
821
821
|
if operations_str and isinstance(operations_str, str):
|
|
822
822
|
operations_config = [op.strip() for op in operations_str.split(',')]
|
|
823
823
|
|
|
824
|
-
|
|
824
|
+
|
|
825
825
|
team_name = team.name if team else "__none__"
|
|
826
826
|
npc_name = npc.name if isinstance(npc, NPC) else "__none__"
|
|
827
827
|
current_path = os.getcwd()
|
|
828
828
|
scope_str = f"Team: '{team_name}', NPC: '{npc_name}', Path: '{current_path}'"
|
|
829
829
|
|
|
830
|
-
|
|
830
|
+
|
|
831
831
|
render_markdown(f"- Checking knowledge graph for scope: {scope_str}")
|
|
832
832
|
|
|
833
833
|
try:
|
|
@@ -840,20 +840,20 @@ def sleep_handler(command: str, **kwargs):
|
|
|
840
840
|
try:
|
|
841
841
|
current_kg = load_kg_from_db(engine, team_name, npc_name, current_path)
|
|
842
842
|
|
|
843
|
-
|
|
843
|
+
|
|
844
844
|
if not current_kg or not current_kg.get('facts'):
|
|
845
845
|
output_msg = f"Knowledge graph for the current scope is empty. Nothing to process.\n"
|
|
846
846
|
output_msg += f" - Scope Checked: {scope_str}\n\n"
|
|
847
847
|
output_msg += "**Hint:** Have a conversation or run some commands first to build up knowledge in this specific context. The KG is unique to each combination of Team, NPC, and directory."
|
|
848
848
|
return {"output": output_msg, "messages": messages}
|
|
849
849
|
|
|
850
|
-
|
|
850
|
+
|
|
851
851
|
original_facts = len(current_kg.get('facts', []))
|
|
852
852
|
original_concepts = len(current_kg.get('concepts', []))
|
|
853
853
|
|
|
854
|
-
|
|
854
|
+
|
|
855
855
|
|
|
856
|
-
|
|
856
|
+
|
|
857
857
|
process_type = "Sleep"
|
|
858
858
|
ops_display = f"with operations: {operations_config}" if operations_config else "with random operations"
|
|
859
859
|
render_markdown(f"- Initiating sleep process {ops_display}")
|
|
@@ -866,7 +866,7 @@ def sleep_handler(command: str, **kwargs):
|
|
|
866
866
|
operations_config=operations_config
|
|
867
867
|
)
|
|
868
868
|
|
|
869
|
-
|
|
869
|
+
|
|
870
870
|
if is_dreaming:
|
|
871
871
|
process_type += " & Dream"
|
|
872
872
|
render_markdown(f"- Initiating dream process on the evolved KG...")
|
|
@@ -877,10 +877,10 @@ def sleep_handler(command: str, **kwargs):
|
|
|
877
877
|
npc=npc
|
|
878
878
|
)
|
|
879
879
|
|
|
880
|
-
|
|
880
|
+
|
|
881
881
|
save_kg_to_db(conn, evolved_kg, team_name, npc_name, current_path)
|
|
882
882
|
|
|
883
|
-
|
|
883
|
+
|
|
884
884
|
new_facts = len(evolved_kg.get('facts', []))
|
|
885
885
|
new_concepts = len(evolved_kg.get('concepts', []))
|
|
886
886
|
|
|
@@ -976,8 +976,9 @@ def vixynt_handler(command: str, **kwargs):
|
|
|
976
976
|
provider = safe_get(kwargs, 'igprovider', NPCSH_IMAGE_GEN_PROVIDER)
|
|
977
977
|
height = safe_get(kwargs, 'height', 1024)
|
|
978
978
|
width = safe_get(kwargs, 'width', 1024)
|
|
979
|
-
|
|
979
|
+
output_file_base = safe_get(kwargs, 'output_file')
|
|
980
980
|
attachments = safe_get(kwargs, 'attachments')
|
|
981
|
+
n_images = safe_get(kwargs, 'n_images', 1)
|
|
981
982
|
if isinstance(attachments, str):
|
|
982
983
|
attachments = attachments.split(',')
|
|
983
984
|
|
|
@@ -986,34 +987,51 @@ def vixynt_handler(command: str, **kwargs):
|
|
|
986
987
|
user_prompt = " ".join(safe_get(kwargs, 'positional_args', []))
|
|
987
988
|
|
|
988
989
|
if not user_prompt:
|
|
989
|
-
return {"output": "Usage: /vixynt <prompt> [--output_file path] [--attachments path]", "messages": messages}
|
|
990
|
+
return {"output": "Usage: /vixynt <prompt> [--output_file path] [--attachments path] [--n_images num]", "messages": messages}
|
|
991
|
+
|
|
990
992
|
try:
|
|
991
|
-
|
|
993
|
+
|
|
994
|
+
images_list = gen_image(
|
|
992
995
|
prompt=user_prompt,
|
|
993
996
|
model=model,
|
|
994
997
|
provider=provider,
|
|
995
998
|
npc=npc,
|
|
996
999
|
height=height,
|
|
997
1000
|
width=width,
|
|
1001
|
+
n_images=n_images,
|
|
998
1002
|
input_images=attachments
|
|
999
1003
|
)
|
|
1000
1004
|
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
os.path.expanduser("~/.npcsh/images/")
|
|
1005
|
-
+ f"image_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png"
|
|
1006
|
-
)
|
|
1007
|
-
else:
|
|
1008
|
-
output_file = os.path.expanduser(output_file)
|
|
1005
|
+
saved_files = []
|
|
1006
|
+
if not isinstance(images_list, list):
|
|
1007
|
+
images_list = [images_list] if images_list is not None else []
|
|
1009
1008
|
|
|
1010
|
-
image
|
|
1011
|
-
|
|
1009
|
+
for i, image in enumerate(images_list):
|
|
1010
|
+
if image is None:
|
|
1011
|
+
continue
|
|
1012
1012
|
|
|
1013
|
-
|
|
1014
|
-
|
|
1013
|
+
if output_file_base is None:
|
|
1014
|
+
os.makedirs(os.path.expanduser("~/.npcsh/images/"), exist_ok=True)
|
|
1015
|
+
current_output_file = (
|
|
1016
|
+
os.path.expanduser("~/.npcsh/images/")
|
|
1017
|
+
+ f"image_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{i}.png"
|
|
1018
|
+
)
|
|
1019
|
+
else:
|
|
1020
|
+
base_name, ext = os.path.splitext(os.path.expanduser(output_file_base))
|
|
1021
|
+
current_output_file = f"{base_name}_{i}{ext}"
|
|
1022
|
+
|
|
1023
|
+
image.save(current_output_file)
|
|
1024
|
+
image.show()
|
|
1025
|
+
saved_files.append(current_output_file)
|
|
1026
|
+
|
|
1027
|
+
if saved_files:
|
|
1028
|
+
if attachments:
|
|
1029
|
+
output = f"Image(s) edited and saved to: {', '.join(saved_files)}"
|
|
1030
|
+
else:
|
|
1031
|
+
output = f"Image(s) generated and saved to: {', '.join(saved_files)}"
|
|
1015
1032
|
else:
|
|
1016
|
-
output = f"
|
|
1033
|
+
output = f"No images {'edited' if attachments else 'generated'}."
|
|
1034
|
+
|
|
1017
1035
|
except Exception as e:
|
|
1018
1036
|
traceback.print_exc()
|
|
1019
1037
|
output = f"Error {'editing' if attachments else 'generating'} image: {e}"
|
|
@@ -1024,37 +1042,39 @@ def vixynt_handler(command: str, **kwargs):
|
|
|
1024
1042
|
"model": model,
|
|
1025
1043
|
"provider": provider
|
|
1026
1044
|
}
|
|
1045
|
+
|
|
1046
|
+
|
|
1027
1047
|
@router.route("wander", "Enter wander mode (experimental)")
|
|
1028
1048
|
def wander_handler(command: str, **kwargs):
|
|
1029
1049
|
messages = safe_get(kwargs, "messages", [])
|
|
1030
1050
|
|
|
1031
|
-
|
|
1051
|
+
|
|
1032
1052
|
try:
|
|
1033
1053
|
parts = shlex.split(command)
|
|
1034
1054
|
problem_parts = []
|
|
1035
1055
|
wander_params = {}
|
|
1036
1056
|
|
|
1037
|
-
i = 1
|
|
1057
|
+
i = 1
|
|
1038
1058
|
while i < len(parts):
|
|
1039
1059
|
part = parts[i]
|
|
1040
1060
|
|
|
1041
1061
|
if '=' in part:
|
|
1042
|
-
|
|
1062
|
+
|
|
1043
1063
|
key, initial_value = part.split('=', 1)
|
|
1044
1064
|
|
|
1045
|
-
|
|
1065
|
+
|
|
1046
1066
|
value_parts = [initial_value]
|
|
1047
1067
|
j = i + 1
|
|
1048
1068
|
while j < len(parts) and '=' not in parts[j]:
|
|
1049
1069
|
value_parts.append(parts[j])
|
|
1050
1070
|
j += 1
|
|
1051
1071
|
|
|
1052
|
-
|
|
1072
|
+
|
|
1053
1073
|
wander_params[key] = " ".join(value_parts)
|
|
1054
|
-
|
|
1074
|
+
|
|
1055
1075
|
i = j
|
|
1056
1076
|
else:
|
|
1057
|
-
|
|
1077
|
+
|
|
1058
1078
|
problem_parts.append(part)
|
|
1059
1079
|
i += 1
|
|
1060
1080
|
|
|
@@ -1066,13 +1086,13 @@ def wander_handler(command: str, **kwargs):
|
|
|
1066
1086
|
return {"output": "Usage: /wander <problem> [key=value...]", "messages": messages}
|
|
1067
1087
|
|
|
1068
1088
|
try:
|
|
1069
|
-
|
|
1089
|
+
|
|
1070
1090
|
mode_args = {
|
|
1071
1091
|
'problem': problem,
|
|
1072
1092
|
'npc': safe_get(kwargs, 'npc'),
|
|
1073
1093
|
'model': safe_get(kwargs, 'model'),
|
|
1074
1094
|
'provider': safe_get(kwargs, 'provider'),
|
|
1075
|
-
|
|
1095
|
+
|
|
1076
1096
|
'environment': wander_params.get('environment'),
|
|
1077
1097
|
'low_temp': float(wander_params.get('low-temp', 0.5)),
|
|
1078
1098
|
'high_temp': float(wander_params.get('high-temp', 1.9)),
|
|
@@ -1111,10 +1131,10 @@ def yap_handler(command: str, **kwargs):
|
|
|
1111
1131
|
def alicanto_handler(command: str, **kwargs):
|
|
1112
1132
|
messages = safe_get(kwargs, "messages", [])
|
|
1113
1133
|
|
|
1114
|
-
|
|
1134
|
+
|
|
1115
1135
|
parts = shlex.split(command)
|
|
1116
1136
|
|
|
1117
|
-
|
|
1137
|
+
|
|
1118
1138
|
query = ""
|
|
1119
1139
|
num_npcs = safe_get(kwargs, 'num_npcs', 5)
|
|
1120
1140
|
depth = safe_get(kwargs, 'depth', 3)
|
|
@@ -1122,11 +1142,11 @@ def alicanto_handler(command: str, **kwargs):
|
|
|
1122
1142
|
creativity_factor = safe_get(kwargs, 'creativity', 0.5)
|
|
1123
1143
|
output_format = safe_get(kwargs, 'format', 'report')
|
|
1124
1144
|
|
|
1125
|
-
|
|
1126
|
-
i = 1
|
|
1145
|
+
|
|
1146
|
+
i = 1
|
|
1127
1147
|
while i < len(parts):
|
|
1128
1148
|
if parts[i].startswith('--'):
|
|
1129
|
-
option = parts[i][2:]
|
|
1149
|
+
option = parts[i][2:]
|
|
1130
1150
|
if option in ['num-npcs', 'npcs']:
|
|
1131
1151
|
if i + 1 < len(parts) and parts[i + 1].isdigit():
|
|
1132
1152
|
num_npcs = int(parts[i + 1])
|
|
@@ -1158,16 +1178,16 @@ def alicanto_handler(command: str, **kwargs):
|
|
|
1158
1178
|
else:
|
|
1159
1179
|
i += 1
|
|
1160
1180
|
else:
|
|
1161
|
-
|
|
1181
|
+
|
|
1162
1182
|
i += 1
|
|
1163
1183
|
else:
|
|
1164
|
-
|
|
1184
|
+
|
|
1165
1185
|
query += parts[i] + " "
|
|
1166
1186
|
i += 1
|
|
1167
1187
|
|
|
1168
1188
|
query = query.strip()
|
|
1169
1189
|
|
|
1170
|
-
|
|
1190
|
+
|
|
1171
1191
|
if 'num_npcs' in kwargs:
|
|
1172
1192
|
try:
|
|
1173
1193
|
num_npcs = int(kwargs['num_npcs'])
|
|
@@ -1210,7 +1230,7 @@ def alicanto_handler(command: str, **kwargs):
|
|
|
1210
1230
|
output_format=output_format
|
|
1211
1231
|
)
|
|
1212
1232
|
|
|
1213
|
-
|
|
1233
|
+
|
|
1214
1234
|
if isinstance(result, dict):
|
|
1215
1235
|
if "integration" in result:
|
|
1216
1236
|
output = result["integration"]
|
npcsh/spool.py
CHANGED
|
@@ -51,10 +51,10 @@ def enter_spool_mode(
|
|
|
51
51
|
**kwargs,
|
|
52
52
|
) -> Dict:
|
|
53
53
|
print_spool_ascii()
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
command_history, state_team, default_npc = setup_shell()
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
spool_state = ShellState(
|
|
59
59
|
npc=npc or default_npc,
|
|
60
60
|
team=team or state_team,
|
|
@@ -65,7 +65,7 @@ def enter_spool_mode(
|
|
|
65
65
|
attachments=None,
|
|
66
66
|
)
|
|
67
67
|
|
|
68
|
-
|
|
68
|
+
|
|
69
69
|
if model:
|
|
70
70
|
spool_state.chat_model = model
|
|
71
71
|
if provider:
|
|
@@ -79,7 +79,7 @@ def enter_spool_mode(
|
|
|
79
79
|
print(f"🧵 Entering spool mode{npc_info}. Type '/sq' to exit spool mode.")
|
|
80
80
|
print("💡 Tip: Press Ctrl+C during streaming to interrupt and continue with a new message.")
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
|
|
83
83
|
loaded_chunks = {}
|
|
84
84
|
if attachments:
|
|
85
85
|
if isinstance(attachments, str):
|
|
@@ -97,14 +97,14 @@ def enter_spool_mode(
|
|
|
97
97
|
except Exception as e:
|
|
98
98
|
print(colored(f"Error loading {file_path}: {str(e)}", "red"))
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
|
|
101
101
|
if not spool_state.messages or spool_state.messages[0].get("role") != "system":
|
|
102
102
|
system_message = get_system_message(spool_state.npc) if spool_state.npc else "You are a helpful assistant."
|
|
103
103
|
spool_state.messages.insert(0, {"role": "system", "content": system_message})
|
|
104
104
|
|
|
105
105
|
while True:
|
|
106
106
|
try:
|
|
107
|
-
|
|
107
|
+
|
|
108
108
|
npc_name = spool_state.npc.name if spool_state.npc else "chat"
|
|
109
109
|
display_model = spool_state.npc.model if spool_state.npc and spool_state.npc.model else spool_state.chat_model
|
|
110
110
|
|
|
@@ -123,7 +123,7 @@ def enter_spool_mode(
|
|
|
123
123
|
spool_state.messages = enter_yap_mode(spool_state.messages, spool_state.npc)
|
|
124
124
|
continue
|
|
125
125
|
|
|
126
|
-
|
|
126
|
+
|
|
127
127
|
if user_input.startswith("/ots"):
|
|
128
128
|
command_parts = user_input.split()
|
|
129
129
|
image_paths = []
|
|
@@ -146,7 +146,7 @@ def enter_spool_mode(
|
|
|
146
146
|
|
|
147
147
|
vision_prompt = input("Prompt for image(s) (or press Enter): ").strip() or "Describe these images."
|
|
148
148
|
|
|
149
|
-
|
|
149
|
+
|
|
150
150
|
response = get_llm_response(
|
|
151
151
|
vision_prompt,
|
|
152
152
|
model=spool_state.vision_model,
|
|
@@ -162,11 +162,11 @@ def enter_spool_mode(
|
|
|
162
162
|
spool_state.messages = response.get('messages', spool_state.messages)
|
|
163
163
|
output = response.get('response')
|
|
164
164
|
|
|
165
|
-
|
|
165
|
+
|
|
166
166
|
process_result(vision_prompt, spool_state, {'output': output}, command_history)
|
|
167
167
|
continue
|
|
168
168
|
|
|
169
|
-
|
|
169
|
+
|
|
170
170
|
current_prompt = user_input
|
|
171
171
|
if loaded_chunks:
|
|
172
172
|
context_content = ""
|
|
@@ -183,7 +183,7 @@ def enter_spool_mode(
|
|
|
183
183
|
if context_content:
|
|
184
184
|
current_prompt += f"\n\n--- Relevant context from loaded files ---\n{context_content}"
|
|
185
185
|
|
|
186
|
-
|
|
186
|
+
|
|
187
187
|
response = get_llm_response(
|
|
188
188
|
current_prompt,
|
|
189
189
|
model=spool_state.npc.model if spool_state.npc and spool_state.npc.model else spool_state.chat_model,
|
|
@@ -197,7 +197,7 @@ def enter_spool_mode(
|
|
|
197
197
|
spool_state.messages = response.get('messages', spool_state.messages)
|
|
198
198
|
output = response.get('response')
|
|
199
199
|
|
|
200
|
-
|
|
200
|
+
|
|
201
201
|
process_result(current_prompt, spool_state, {'output': output}, command_history)
|
|
202
202
|
|
|
203
203
|
except (EOFError,):
|
|
@@ -221,7 +221,7 @@ def main():
|
|
|
221
221
|
|
|
222
222
|
args = parser.parse_args()
|
|
223
223
|
|
|
224
|
-
|
|
224
|
+
|
|
225
225
|
command_history, team, default_npc = setup_shell()
|
|
226
226
|
|
|
227
227
|
npc = None
|