npcsh 1.1.17__py3-none-any.whl → 1.1.19__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 +122 -91
- npcsh/alicanto.py +2 -2
- npcsh/benchmark/__init__.py +8 -2
- npcsh/benchmark/npcsh_agent.py +87 -22
- npcsh/benchmark/runner.py +85 -43
- npcsh/benchmark/templates/install-npcsh.sh.j2 +35 -0
- npcsh/build.py +2 -4
- npcsh/completion.py +2 -6
- npcsh/config.py +2 -3
- npcsh/conversation_viewer.py +389 -0
- npcsh/corca.py +0 -1
- npcsh/diff_viewer.py +452 -0
- npcsh/execution.py +0 -1
- npcsh/guac.py +0 -1
- npcsh/mcp_helpers.py +2 -3
- npcsh/mcp_server.py +5 -10
- npcsh/npc.py +10 -11
- npcsh/npc_team/jinxs/bin/benchmark.jinx +1 -1
- npcsh/npc_team/jinxs/bin/config_tui.jinx +299 -0
- npcsh/npc_team/jinxs/bin/memories.jinx +316 -0
- npcsh/npc_team/jinxs/bin/setup.jinx +240 -0
- npcsh/npc_team/jinxs/bin/sync.jinx +143 -150
- npcsh/npc_team/jinxs/bin/team_tui.jinx +327 -0
- npcsh/npc_team/jinxs/incognide/add_tab.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/close_pane.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/close_tab.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/confirm.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/focus_pane.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/list_panes.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/navigate.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/notify.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/open_pane.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/read_pane.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/run_terminal.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/send_message.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/split_pane.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/switch_npc.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/switch_tab.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/write_file.jinx +1 -1
- npcsh/npc_team/jinxs/incognide/zen_mode.jinx +1 -1
- npcsh/npc_team/jinxs/lib/core/search/db_search.jinx +321 -17
- npcsh/npc_team/jinxs/lib/core/search/file_search.jinx +312 -67
- npcsh/npc_team/jinxs/lib/core/search/kg_search.jinx +366 -44
- npcsh/npc_team/jinxs/lib/core/search/mem_review.jinx +73 -0
- npcsh/npc_team/jinxs/lib/core/search/mem_search.jinx +328 -20
- npcsh/npc_team/jinxs/lib/core/search/web_search.jinx +242 -10
- npcsh/npc_team/jinxs/lib/core/sleep.jinx +22 -11
- npcsh/npc_team/jinxs/lib/core/sql.jinx +10 -6
- npcsh/npc_team/jinxs/lib/research/paper_search.jinx +387 -76
- npcsh/npc_team/jinxs/lib/research/semantic_scholar.jinx +372 -55
- npcsh/npc_team/jinxs/lib/utils/jinxs.jinx +299 -144
- npcsh/npc_team/jinxs/modes/alicanto.jinx +356 -0
- npcsh/npc_team/jinxs/modes/arxiv.jinx +720 -0
- npcsh/npc_team/jinxs/modes/corca.jinx +430 -0
- npcsh/npc_team/jinxs/modes/guac.jinx +542 -0
- npcsh/npc_team/jinxs/modes/plonk.jinx +379 -0
- npcsh/npc_team/jinxs/modes/pti.jinx +357 -0
- npcsh/npc_team/jinxs/modes/reattach.jinx +291 -0
- npcsh/npc_team/jinxs/modes/spool.jinx +350 -0
- npcsh/npc_team/jinxs/modes/wander.jinx +455 -0
- npcsh/npc_team/jinxs/{bin → modes}/yap.jinx +13 -7
- npcsh/npcsh.py +7 -4
- npcsh/plonk.py +0 -1
- npcsh/pti.py +0 -1
- npcsh/routes.py +1 -3
- npcsh/spool.py +0 -1
- npcsh/ui.py +0 -1
- npcsh/wander.py +0 -1
- npcsh/yap.py +0 -1
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/add_tab.jinx +1 -1
- npcsh-1.1.19.data/data/npcsh/npc_team/alicanto.jinx +356 -0
- npcsh-1.1.19.data/data/npcsh/npc_team/arxiv.jinx +720 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/benchmark.jinx +1 -1
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/close_pane.jinx +1 -1
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/close_tab.jinx +1 -1
- npcsh-1.1.19.data/data/npcsh/npc_team/config_tui.jinx +299 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/confirm.jinx +1 -1
- npcsh-1.1.19.data/data/npcsh/npc_team/corca.jinx +430 -0
- npcsh-1.1.19.data/data/npcsh/npc_team/db_search.jinx +348 -0
- npcsh-1.1.19.data/data/npcsh/npc_team/file_search.jinx +339 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/focus_pane.jinx +1 -1
- npcsh-1.1.19.data/data/npcsh/npc_team/guac.jinx +542 -0
- npcsh-1.1.19.data/data/npcsh/npc_team/jinxs.jinx +331 -0
- npcsh-1.1.19.data/data/npcsh/npc_team/kg_search.jinx +418 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/list_panes.jinx +1 -1
- npcsh-1.1.19.data/data/npcsh/npc_team/mem_review.jinx +73 -0
- npcsh-1.1.19.data/data/npcsh/npc_team/mem_search.jinx +388 -0
- npcsh-1.1.19.data/data/npcsh/npc_team/memories.jinx +316 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/navigate.jinx +1 -1
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/notify.jinx +1 -1
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/open_pane.jinx +1 -1
- npcsh-1.1.19.data/data/npcsh/npc_team/paper_search.jinx +412 -0
- npcsh-1.1.19.data/data/npcsh/npc_team/plonk.jinx +379 -0
- npcsh-1.1.19.data/data/npcsh/npc_team/pti.jinx +357 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/read_pane.jinx +1 -1
- npcsh-1.1.19.data/data/npcsh/npc_team/reattach.jinx +291 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/run_terminal.jinx +1 -1
- npcsh-1.1.19.data/data/npcsh/npc_team/semantic_scholar.jinx +386 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/send_message.jinx +1 -1
- npcsh-1.1.19.data/data/npcsh/npc_team/setup.jinx +240 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/sleep.jinx +22 -11
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/split_pane.jinx +1 -1
- npcsh-1.1.19.data/data/npcsh/npc_team/spool.jinx +350 -0
- npcsh-1.1.19.data/data/npcsh/npc_team/sql.jinx +20 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/switch_npc.jinx +1 -1
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/switch_tab.jinx +1 -1
- npcsh-1.1.19.data/data/npcsh/npc_team/sync.jinx +223 -0
- npcsh-1.1.19.data/data/npcsh/npc_team/team_tui.jinx +327 -0
- npcsh-1.1.19.data/data/npcsh/npc_team/wander.jinx +455 -0
- npcsh-1.1.19.data/data/npcsh/npc_team/web_search.jinx +283 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/write_file.jinx +1 -1
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/yap.jinx +13 -7
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/zen_mode.jinx +1 -1
- {npcsh-1.1.17.dist-info → npcsh-1.1.19.dist-info}/METADATA +110 -14
- npcsh-1.1.19.dist-info/RECORD +244 -0
- {npcsh-1.1.17.dist-info → npcsh-1.1.19.dist-info}/WHEEL +1 -1
- {npcsh-1.1.17.dist-info → npcsh-1.1.19.dist-info}/entry_points.txt +4 -3
- npcsh/npc_team/jinxs/bin/spool.jinx +0 -161
- npcsh/npc_team/jinxs/bin/wander.jinx +0 -242
- npcsh/npc_team/jinxs/lib/research/arxiv.jinx +0 -76
- npcsh-1.1.17.data/data/npcsh/npc_team/arxiv.jinx +0 -76
- npcsh-1.1.17.data/data/npcsh/npc_team/db_search.jinx +0 -44
- npcsh-1.1.17.data/data/npcsh/npc_team/file_search.jinx +0 -94
- npcsh-1.1.17.data/data/npcsh/npc_team/jinxs.jinx +0 -176
- npcsh-1.1.17.data/data/npcsh/npc_team/kg_search.jinx +0 -96
- npcsh-1.1.17.data/data/npcsh/npc_team/mem_search.jinx +0 -80
- npcsh-1.1.17.data/data/npcsh/npc_team/paper_search.jinx +0 -101
- npcsh-1.1.17.data/data/npcsh/npc_team/semantic_scholar.jinx +0 -69
- npcsh-1.1.17.data/data/npcsh/npc_team/spool.jinx +0 -161
- npcsh-1.1.17.data/data/npcsh/npc_team/sql.jinx +0 -16
- npcsh-1.1.17.data/data/npcsh/npc_team/sync.jinx +0 -230
- npcsh-1.1.17.data/data/npcsh/npc_team/wander.jinx +0 -242
- npcsh-1.1.17.data/data/npcsh/npc_team/web_search.jinx +0 -51
- npcsh-1.1.17.dist-info/RECORD +0 -219
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/alicanto.npc +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/alicanto.png +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/browser_action.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/browser_screenshot.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/build.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/chat.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/click.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/close_browser.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/cmd.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/compile.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/compress.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/convene.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/corca.npc +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/corca.png +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/corca_example.png +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/delegate.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/edit_file.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/frederic.npc +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/frederic4.png +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/guac.npc +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/guac.png +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/help.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/incognide.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/init.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/kadiefa.npc +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/kadiefa.png +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/key_press.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/launch_app.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/load_file.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/npcsh.ctx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/npcsh_sibiji.png +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/nql.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/open_browser.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/ots.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/paste.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/plonk.npc +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/plonk.png +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/plonkjr.npc +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/plonkjr.png +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/python.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/roll.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/sample.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/screenshot.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/search.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/serve.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/set.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/sh.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/shh.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/sibiji.npc +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/sibiji.png +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/spool.png +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/switch.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/switches.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/teamviz.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/trigger.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/type_text.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/usage.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/verbose.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/vixynt.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/wait.jinx +0 -0
- {npcsh-1.1.17.data → npcsh-1.1.19.data}/data/npcsh/npc_team/yap.png +0 -0
- {npcsh-1.1.17.dist-info → npcsh-1.1.19.dist-info}/licenses/LICENSE +0 -0
- {npcsh-1.1.17.dist-info → npcsh-1.1.19.dist-info}/top_level.txt +0 -0
npcsh/_state.py
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
# Standard library imports
|
|
2
2
|
import atexit
|
|
3
|
+
import base64
|
|
4
|
+
import os
|
|
3
5
|
from dataclasses import dataclass, field
|
|
4
6
|
from datetime import datetime
|
|
5
7
|
import filecmp
|
|
6
8
|
import inspect
|
|
9
|
+
|
|
7
10
|
import logging
|
|
8
|
-
|
|
11
|
+
|
|
9
12
|
from pathlib import Path
|
|
10
13
|
import platform
|
|
11
14
|
import re
|
|
@@ -16,8 +19,11 @@ import signal
|
|
|
16
19
|
import sqlite3
|
|
17
20
|
import subprocess
|
|
18
21
|
import sys
|
|
22
|
+
import tempfile
|
|
19
23
|
import time
|
|
20
24
|
import textwrap
|
|
25
|
+
import readline
|
|
26
|
+
import json
|
|
21
27
|
from typing import Dict, List, Any, Tuple, Union, Optional, Callable
|
|
22
28
|
import yaml
|
|
23
29
|
|
|
@@ -40,9 +46,9 @@ try:
|
|
|
40
46
|
import pty
|
|
41
47
|
import tty
|
|
42
48
|
import termios
|
|
43
|
-
|
|
49
|
+
|
|
44
50
|
except ImportError:
|
|
45
|
-
|
|
51
|
+
|
|
46
52
|
pty = None
|
|
47
53
|
tty = None
|
|
48
54
|
termios = None
|
|
@@ -53,15 +59,21 @@ try:
|
|
|
53
59
|
except ImportError:
|
|
54
60
|
chromadb = None
|
|
55
61
|
|
|
62
|
+
try:
|
|
63
|
+
import ollama
|
|
64
|
+
except ImportError:
|
|
65
|
+
ollama = None
|
|
66
|
+
|
|
56
67
|
# Third-party imports
|
|
57
|
-
from colorama import
|
|
68
|
+
from colorama import Style
|
|
58
69
|
from litellm import RateLimitError
|
|
70
|
+
import numpy as np
|
|
59
71
|
from termcolor import colored
|
|
60
72
|
|
|
61
73
|
# npcpy imports
|
|
62
74
|
from npcpy.data.load import load_file_contents
|
|
63
75
|
from npcpy.data.web import search_web
|
|
64
|
-
|
|
76
|
+
|
|
65
77
|
from npcpy.llm_funcs import (
|
|
66
78
|
check_llm_command,
|
|
67
79
|
get_llm_response,
|
|
@@ -74,25 +86,25 @@ from npcpy.memory.command_history import (
|
|
|
74
86
|
save_conversation_message,
|
|
75
87
|
load_kg_from_db,
|
|
76
88
|
save_kg_to_db,
|
|
89
|
+
format_memory_context,
|
|
77
90
|
)
|
|
78
91
|
from npcpy.memory.knowledge_graph import kg_evolve_incremental
|
|
79
92
|
from npcpy.memory.search import execute_rag_command, execute_brainblast_command
|
|
80
|
-
from npcpy.npc_compiler import NPC, Team,
|
|
93
|
+
from npcpy.npc_compiler import NPC, Team, build_jinx_tool_catalog
|
|
81
94
|
from npcpy.npc_sysenv import (
|
|
82
95
|
print_and_process_stream_with_markdown,
|
|
83
96
|
render_markdown,
|
|
84
97
|
get_model_and_provider,
|
|
85
98
|
get_locally_available_models,
|
|
86
|
-
|
|
99
|
+
|
|
87
100
|
)
|
|
88
101
|
from npcpy.tools import auto_tools
|
|
102
|
+
from npcpy.gen.embeddings import get_embeddings
|
|
89
103
|
|
|
90
104
|
# Local module imports
|
|
91
105
|
from .config import (
|
|
92
|
-
VERSION,
|
|
93
106
|
DEFAULT_NPC_TEAM_PATH,
|
|
94
107
|
PROJECT_NPC_TEAM_PATH,
|
|
95
|
-
HISTORY_DB_DEFAULT_PATH,
|
|
96
108
|
READLINE_HISTORY_FILE,
|
|
97
109
|
NPCSH_CHAT_MODEL,
|
|
98
110
|
NPCSH_CHAT_PROVIDER,
|
|
@@ -113,6 +125,7 @@ from .config import (
|
|
|
113
125
|
NPCSH_API_URL,
|
|
114
126
|
NPCSH_SEARCH_PROVIDER,
|
|
115
127
|
NPCSH_BUILD_KG,
|
|
128
|
+
NPCSH_EDIT_APPROVAL,
|
|
116
129
|
setup_npcsh_config,
|
|
117
130
|
is_npcsh_initialized,
|
|
118
131
|
set_npcsh_initialized,
|
|
@@ -156,6 +169,9 @@ class ShellState:
|
|
|
156
169
|
video_gen_provider: str = NPCSH_VIDEO_GEN_PROVIDER
|
|
157
170
|
current_mode: str = NPCSH_DEFAULT_MODE
|
|
158
171
|
build_kg: bool = NPCSH_BUILD_KG
|
|
172
|
+
kg_link_facts: bool = False # Link facts to concepts (requires LLM calls)
|
|
173
|
+
kg_link_concepts: bool = False # Link concepts to concepts (requires LLM calls)
|
|
174
|
+
kg_link_facts_facts: bool = False # Link facts to facts (requires LLM calls)
|
|
159
175
|
api_key: Optional[str] = None
|
|
160
176
|
api_url: Optional[str] = NPCSH_API_URL
|
|
161
177
|
current_path: str = field(default_factory=os.getcwd)
|
|
@@ -170,6 +186,10 @@ class ShellState:
|
|
|
170
186
|
session_start_time: float = field(default_factory=lambda: __import__('time').time())
|
|
171
187
|
# Logging level: "silent", "normal", "verbose"
|
|
172
188
|
log_level: str = "normal"
|
|
189
|
+
# Edit approval mode: "off", "interactive", "auto"
|
|
190
|
+
edit_approval: str = NPCSH_EDIT_APPROVAL
|
|
191
|
+
# Pending file edits for approval
|
|
192
|
+
pending_edits: Dict[str, Dict[str, str]] = field(default_factory=dict)
|
|
173
193
|
|
|
174
194
|
def get_model_for_command(self, model_type: str = "chat"):
|
|
175
195
|
if model_type == "chat":
|
|
@@ -256,6 +276,8 @@ CONFIG_KEY_MAP = {
|
|
|
256
276
|
"stream": "NPCSH_STREAM_OUTPUT",
|
|
257
277
|
"apiurl": "NPCSH_API_URL",
|
|
258
278
|
"buildkg": "NPCSH_BUILD_KG",
|
|
279
|
+
"editapproval": "NPCSH_EDIT_APPROVAL",
|
|
280
|
+
"approval": "NPCSH_EDIT_APPROVAL",
|
|
259
281
|
}
|
|
260
282
|
|
|
261
283
|
|
|
@@ -300,9 +322,37 @@ def set_npcsh_config_value(key: str, value: str):
|
|
|
300
322
|
"NPCSH_BUILD_KG": "build_kg",
|
|
301
323
|
"NPCSH_API_URL": "api_url",
|
|
302
324
|
"NPCSH_STREAM_OUTPUT": "stream_output",
|
|
325
|
+
"NPCSH_EDIT_APPROVAL": "edit_approval",
|
|
303
326
|
}
|
|
304
327
|
if env_key in field_map:
|
|
305
328
|
setattr(ShellState, field_map[env_key], parsed_val)
|
|
329
|
+
|
|
330
|
+
# Persist to ~/.npcshrc
|
|
331
|
+
npcshrc_path = os.path.expanduser("~/.npcshrc")
|
|
332
|
+
try:
|
|
333
|
+
existing_lines = []
|
|
334
|
+
if os.path.exists(npcshrc_path):
|
|
335
|
+
with open(npcshrc_path, 'r') as f:
|
|
336
|
+
existing_lines = f.readlines()
|
|
337
|
+
|
|
338
|
+
# Update or add the export line
|
|
339
|
+
export_line = f"export {env_key}=\"{value}\"\n"
|
|
340
|
+
found = False
|
|
341
|
+
for i, line in enumerate(existing_lines):
|
|
342
|
+
if line.strip().startswith(f"export {env_key}="):
|
|
343
|
+
existing_lines[i] = export_line
|
|
344
|
+
found = True
|
|
345
|
+
break
|
|
346
|
+
|
|
347
|
+
if not found:
|
|
348
|
+
existing_lines.append(export_line)
|
|
349
|
+
|
|
350
|
+
with open(npcshrc_path, 'w') as f:
|
|
351
|
+
f.writelines(existing_lines)
|
|
352
|
+
except Exception as e:
|
|
353
|
+
print(f"Warning: Could not persist config to {npcshrc_path}: {e}")
|
|
354
|
+
|
|
355
|
+
|
|
306
356
|
def get_npc_path(npc_name: str, db_path: str) -> str:
|
|
307
357
|
project_npc_team_dir = os.path.abspath("./npc_team")
|
|
308
358
|
project_npc_path = os.path.join(project_npc_team_dir, f"{npc_name}.npc")
|
|
@@ -317,7 +367,7 @@ def get_npc_path(npc_name: str, db_path: str) -> str:
|
|
|
317
367
|
if result:
|
|
318
368
|
return result[0]
|
|
319
369
|
|
|
320
|
-
except Exception
|
|
370
|
+
except Exception:
|
|
321
371
|
try:
|
|
322
372
|
with sqlite3.connect(db_path) as conn:
|
|
323
373
|
cursor = conn.cursor()
|
|
@@ -424,10 +474,10 @@ def initialize_base_npcs_if_needed(db_path: str) -> None:
|
|
|
424
474
|
old_package_jinxs = set()
|
|
425
475
|
if os.path.exists(manifest_path):
|
|
426
476
|
try:
|
|
427
|
-
|
|
477
|
+
|
|
428
478
|
with open(manifest_path, 'r') as f:
|
|
429
479
|
old_package_jinxs = set(json.load(f).get('jinxs', []))
|
|
430
|
-
except:
|
|
480
|
+
except Exception:
|
|
431
481
|
pass
|
|
432
482
|
|
|
433
483
|
# Track current package jinxs
|
|
@@ -480,7 +530,7 @@ def initialize_base_npcs_if_needed(db_path: str) -> None:
|
|
|
480
530
|
|
|
481
531
|
# Save updated manifest
|
|
482
532
|
try:
|
|
483
|
-
|
|
533
|
+
|
|
484
534
|
with open(manifest_path, 'w') as f:
|
|
485
535
|
json.dump({'jinxs': list(current_package_jinxs), 'updated': str(__import__('datetime').datetime.now())}, f, indent=2)
|
|
486
536
|
except Exception as e:
|
|
@@ -561,9 +611,6 @@ def get_relevant_memories(
|
|
|
561
611
|
max_memories: int = 10,
|
|
562
612
|
state: Optional[ShellState] = None
|
|
563
613
|
) -> List[Dict]:
|
|
564
|
-
|
|
565
|
-
engine = command_history.engine
|
|
566
|
-
|
|
567
614
|
all_memories = command_history.get_memories_for_scope(
|
|
568
615
|
npc=npc_name,
|
|
569
616
|
team=team_name,
|
|
@@ -588,7 +635,7 @@ def get_relevant_memories(
|
|
|
588
635
|
|
|
589
636
|
if state and state.embedding_model and state.embedding_provider:
|
|
590
637
|
try:
|
|
591
|
-
|
|
638
|
+
|
|
592
639
|
|
|
593
640
|
search_text = query if query else "recent context"
|
|
594
641
|
query_embedding = get_embeddings(
|
|
@@ -606,7 +653,7 @@ def get_relevant_memories(
|
|
|
606
653
|
state.embedding_provider
|
|
607
654
|
)
|
|
608
655
|
|
|
609
|
-
|
|
656
|
+
|
|
610
657
|
similarities = []
|
|
611
658
|
for mem_emb in memory_embeddings:
|
|
612
659
|
similarity = np.dot(query_embedding, mem_emb) / (
|
|
@@ -816,7 +863,6 @@ BASH_COMMANDS = [
|
|
|
816
863
|
"command",
|
|
817
864
|
"compgen",
|
|
818
865
|
"complete",
|
|
819
|
-
"continue",
|
|
820
866
|
"declare",
|
|
821
867
|
"dirs",
|
|
822
868
|
"disown",
|
|
@@ -1283,7 +1329,7 @@ def get_setting_windows(key, default=None):
|
|
|
1283
1329
|
|
|
1284
1330
|
|
|
1285
1331
|
def setup_readline() -> str:
|
|
1286
|
-
|
|
1332
|
+
|
|
1287
1333
|
if readline is None:
|
|
1288
1334
|
return None
|
|
1289
1335
|
try:
|
|
@@ -1429,7 +1475,7 @@ def make_completer(shell_state: ShellState, router: Any):
|
|
|
1429
1475
|
else:
|
|
1430
1476
|
return None # readline expects None when no more completions
|
|
1431
1477
|
|
|
1432
|
-
except Exception
|
|
1478
|
+
except Exception:
|
|
1433
1479
|
# Using completion_logger for internal debugging, not printing to stdout for user.
|
|
1434
1480
|
# completion_logger.error(f"Exception in completion: {e}", exc_info=True)
|
|
1435
1481
|
return None
|
|
@@ -1589,7 +1635,7 @@ def _input_with_hint_below(prompt: str, state=None, router=None, token_hint: str
|
|
|
1589
1635
|
try:
|
|
1590
1636
|
import termios
|
|
1591
1637
|
import tty
|
|
1592
|
-
|
|
1638
|
+
|
|
1593
1639
|
except ImportError:
|
|
1594
1640
|
return input(prompt)
|
|
1595
1641
|
|
|
@@ -1625,7 +1671,7 @@ def _input_with_hint_below(prompt: str, state=None, router=None, token_hint: str
|
|
|
1625
1671
|
try:
|
|
1626
1672
|
import shutil
|
|
1627
1673
|
term_width = shutil.get_terminal_size().columns
|
|
1628
|
-
except:
|
|
1674
|
+
except json.JSONDecodeError:
|
|
1629
1675
|
term_width = 80
|
|
1630
1676
|
|
|
1631
1677
|
def draw():
|
|
@@ -1638,7 +1684,6 @@ def _input_with_hint_below(prompt: str, state=None, router=None, token_hint: str
|
|
|
1638
1684
|
sys.stdout.write('\r')
|
|
1639
1685
|
# Move up for each wrapped line we're on
|
|
1640
1686
|
cursor_total = prompt_visible_len + pos
|
|
1641
|
-
cursor_line = cursor_total // term_width
|
|
1642
1687
|
# Go up to the first line of input
|
|
1643
1688
|
for _ in range(num_lines - 1):
|
|
1644
1689
|
sys.stdout.write('\033[A')
|
|
@@ -1721,7 +1766,7 @@ def _input_with_hint_below(prompt: str, state=None, router=None, token_hint: str
|
|
|
1721
1766
|
# Check if this looks like binary/image data
|
|
1722
1767
|
# Image signatures: PNG (\x89PNG), JPEG (\xff\xd8\xff), GIF (GIF8), BMP (BM)
|
|
1723
1768
|
# Also check for high ratio of non-printable chars
|
|
1724
|
-
|
|
1769
|
+
|
|
1725
1770
|
if len(paste_buffer) > 4:
|
|
1726
1771
|
# Check for common image magic bytes
|
|
1727
1772
|
if paste_buffer[:4] == '\x89PNG' or paste_buffer[:8] == '\x89PNG\r\n\x1a\n':
|
|
@@ -1742,8 +1787,8 @@ def _input_with_hint_below(prompt: str, state=None, router=None, token_hint: str
|
|
|
1742
1787
|
|
|
1743
1788
|
if is_binary:
|
|
1744
1789
|
# Save image data to temp file
|
|
1745
|
-
|
|
1746
|
-
|
|
1790
|
+
|
|
1791
|
+
|
|
1747
1792
|
try:
|
|
1748
1793
|
# Determine extension from magic bytes
|
|
1749
1794
|
ext = '.bin'
|
|
@@ -1766,14 +1811,14 @@ def _input_with_hint_below(prompt: str, state=None, router=None, token_hint: str
|
|
|
1766
1811
|
with os.fdopen(fd, 'wb') as f:
|
|
1767
1812
|
if paste_buffer.startswith('data:image/'):
|
|
1768
1813
|
# Decode base64 data URL
|
|
1769
|
-
|
|
1814
|
+
|
|
1770
1815
|
_, data = paste_buffer.split(',', 1)
|
|
1771
1816
|
f.write(base64.b64decode(data))
|
|
1772
1817
|
else:
|
|
1773
1818
|
f.write(paste_buffer.encode('latin-1'))
|
|
1774
1819
|
pasted_content = temp_path # Store path to image
|
|
1775
1820
|
placeholder = f"[pasted image: {temp_path}]"
|
|
1776
|
-
except:
|
|
1821
|
+
except Exception:
|
|
1777
1822
|
pasted_content = None
|
|
1778
1823
|
placeholder = "[pasted image: failed to save]"
|
|
1779
1824
|
else:
|
|
@@ -1971,7 +2016,7 @@ def _input_with_hint_below(prompt: str, state=None, router=None, token_hint: str
|
|
|
1971
2016
|
sys.stdout.flush()
|
|
1972
2017
|
else:
|
|
1973
2018
|
pass # No tool call to show
|
|
1974
|
-
except:
|
|
2019
|
+
except Exception:
|
|
1975
2020
|
pass
|
|
1976
2021
|
|
|
1977
2022
|
elif c and ord(c) >= 32: # Printable
|
|
@@ -2000,7 +2045,7 @@ def _get_slash_hints(state, router, prefix='/') -> str:
|
|
|
2000
2045
|
try:
|
|
2001
2046
|
import shutil
|
|
2002
2047
|
term_width = shutil.get_terminal_size().columns
|
|
2003
|
-
except:
|
|
2048
|
+
except Exception:
|
|
2004
2049
|
term_width = 80
|
|
2005
2050
|
|
|
2006
2051
|
# Build hint string that fits in terminal
|
|
@@ -2149,44 +2194,20 @@ def wrap_text(text: str, width: int = 80) -> str:
|
|
|
2149
2194
|
|
|
2150
2195
|
|
|
2151
2196
|
|
|
2152
|
-
def setup_readline() -> str:
|
|
2153
|
-
"""Setup readline with history and completion"""
|
|
2154
|
-
try:
|
|
2155
|
-
readline.read_history_file(READLINE_HISTORY_FILE)
|
|
2156
|
-
readline.set_history_length(1000)
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
readline.parse_and_bind("tab: complete")
|
|
2160
|
-
|
|
2161
|
-
readline.parse_and_bind("set enable-bracketed-paste on")
|
|
2162
|
-
readline.parse_and_bind(r'"\C-r": reverse-search-history')
|
|
2163
|
-
readline.parse_and_bind(r'"\C-e": end-of-line')
|
|
2164
|
-
readline.parse_and_bind(r'"\C-a": beginning-of-line')
|
|
2165
|
-
|
|
2166
|
-
return READLINE_HISTORY_FILE
|
|
2167
|
-
|
|
2168
|
-
except FileNotFoundError:
|
|
2169
|
-
pass
|
|
2170
|
-
except OSError as e:
|
|
2171
|
-
print(f"Warning: Could not read readline history file {READLINE_HISTORY_FILE}: {e}")
|
|
2172
2197
|
|
|
2173
|
-
|
|
2174
|
-
def save_readline_history():
|
|
2175
|
-
try:
|
|
2176
|
-
readline.write_history_file(READLINE_HISTORY_FILE)
|
|
2177
|
-
except OSError as e:
|
|
2178
|
-
print(f"Warning: Could not write readline history file {READLINE_HISTORY_FILE}: {e}")
|
|
2179
2198
|
|
|
2180
2199
|
def store_command_embeddings(command: str, output: Any, state: ShellState):
|
|
2181
2200
|
if not chroma_client or not state.embedding_model or not state.embedding_provider:
|
|
2182
|
-
if not chroma_client:
|
|
2201
|
+
if not chroma_client:
|
|
2202
|
+
print("Warning: ChromaDB client not available for embeddings.", file=sys.stderr)
|
|
2183
2203
|
return
|
|
2184
2204
|
if not command and not output:
|
|
2185
2205
|
return
|
|
2186
2206
|
|
|
2187
2207
|
try:
|
|
2188
2208
|
output_str = str(output) if output else ""
|
|
2189
|
-
if not command and not output_str:
|
|
2209
|
+
if not command and not output_str:
|
|
2210
|
+
return
|
|
2190
2211
|
|
|
2191
2212
|
texts_to_embed = [command, output_str]
|
|
2192
2213
|
|
|
@@ -2358,10 +2379,7 @@ def _ollama_supports_tools(model: str) -> Optional[bool]:
|
|
|
2358
2379
|
Best-effort check for tool-call support on an Ollama model by inspecting its template/metadata.
|
|
2359
2380
|
Mirrors the lightweight check used in the Flask serve path.
|
|
2360
2381
|
"""
|
|
2361
|
-
|
|
2362
|
-
import ollama # Local import to avoid hard dependency when Ollama isn't installed
|
|
2363
|
-
except Exception:
|
|
2364
|
-
return None
|
|
2382
|
+
|
|
2365
2383
|
|
|
2366
2384
|
try:
|
|
2367
2385
|
details = ollama.show(model)
|
|
@@ -2468,7 +2486,7 @@ def wrap_tool_with_display(tool_name: str, tool_func: Callable, state: ShellStat
|
|
|
2468
2486
|
print(colored(f" ⚡ {tool_name}", "cyan") + colored(f" {args_display}", "white", attrs=["dark"]), end="", flush=True)
|
|
2469
2487
|
else:
|
|
2470
2488
|
print(colored(f" ⚡ {tool_name}", "cyan"), end="", flush=True)
|
|
2471
|
-
except:
|
|
2489
|
+
except Exception:
|
|
2472
2490
|
pass
|
|
2473
2491
|
|
|
2474
2492
|
# Execute tool
|
|
@@ -2484,14 +2502,14 @@ def wrap_tool_with_display(tool_name: str, tool_func: Callable, state: ShellStat
|
|
|
2484
2502
|
result_preview = result_preview[:200] + "..."
|
|
2485
2503
|
if result_preview and result_preview not in ('None', '', '{}', '[]'):
|
|
2486
2504
|
print(colored(f" → {result_preview}", "white", attrs=["dark"]), flush=True)
|
|
2487
|
-
except:
|
|
2505
|
+
except Exception:
|
|
2488
2506
|
pass
|
|
2489
2507
|
return result
|
|
2490
2508
|
except Exception as e:
|
|
2491
2509
|
if log_level != "silent":
|
|
2492
2510
|
try:
|
|
2493
2511
|
print(colored(f" ✗ {str(e)[:100]}", "red"), flush=True)
|
|
2494
|
-
except:
|
|
2512
|
+
except Exception as e:
|
|
2495
2513
|
pass
|
|
2496
2514
|
raise
|
|
2497
2515
|
return wrapped
|
|
@@ -2642,10 +2660,10 @@ def should_skip_kg_processing(user_input: str, assistant_output: str) -> bool:
|
|
|
2642
2660
|
|
|
2643
2661
|
return False
|
|
2644
2662
|
|
|
2645
|
-
def execute_slash_command(command: str,
|
|
2646
|
-
stdin_input: Optional[str],
|
|
2647
|
-
state: ShellState,
|
|
2648
|
-
stream: bool,
|
|
2663
|
+
def execute_slash_command(command: str,
|
|
2664
|
+
stdin_input: Optional[str],
|
|
2665
|
+
state: ShellState,
|
|
2666
|
+
stream: bool,
|
|
2649
2667
|
router) -> Tuple[ShellState, Any]:
|
|
2650
2668
|
"""Executes slash commands using the router."""
|
|
2651
2669
|
try:
|
|
@@ -2653,7 +2671,13 @@ def execute_slash_command(command: str,
|
|
|
2653
2671
|
except ValueError:
|
|
2654
2672
|
all_command_parts = command.split()
|
|
2655
2673
|
command_name = all_command_parts[0].lstrip('/')
|
|
2674
|
+
|
|
2675
|
+
# --- QUIT/EXIT HANDLING ---
|
|
2676
|
+
if command_name in ['quit', 'exit', 'q']:
|
|
2656
2677
|
|
|
2678
|
+
print("Goodbye!")
|
|
2679
|
+
sys.exit(0)
|
|
2680
|
+
|
|
2657
2681
|
# --- NPC SWITCHING LOGIC ---
|
|
2658
2682
|
if command_name in ['n', 'npc']:
|
|
2659
2683
|
npc_to_switch_to = all_command_parts[1] if len(all_command_parts) > 1 else None
|
|
@@ -2914,9 +2938,6 @@ def process_pipeline_command(
|
|
|
2914
2938
|
"tools": tools_for_llm,
|
|
2915
2939
|
"tool_map": tool_exec_map,
|
|
2916
2940
|
}
|
|
2917
|
-
# Only add tool_choice for providers that support it (not gemini)
|
|
2918
|
-
is_gemini = (exec_provider and "gemini" in exec_provider.lower()) or \
|
|
2919
|
-
(exec_model and "gemini" in exec_model.lower())
|
|
2920
2941
|
llm_kwargs["tool_choice"] = 'auto'
|
|
2921
2942
|
|
|
2922
2943
|
# Agent loop: keep calling LLM until it stops making tool calls
|
|
@@ -3105,7 +3126,7 @@ def _delegate_to_npc(state: ShellState, npc_name: str, command: str, delegation_
|
|
|
3105
3126
|
MAX_DELEGATION_DEPTH = 1 # Only allow one level of delegation
|
|
3106
3127
|
|
|
3107
3128
|
if delegation_depth > MAX_DELEGATION_DEPTH:
|
|
3108
|
-
return state, {'output':
|
|
3129
|
+
return state, {'output': "⚠ Maximum delegation depth reached."}
|
|
3109
3130
|
|
|
3110
3131
|
if not state.team or not hasattr(state.team, 'npcs') or npc_name not in state.team.npcs:
|
|
3111
3132
|
return state, {'output': f"⚠ NPC '{npc_name}' not found in team"}
|
|
@@ -3311,7 +3332,7 @@ def execute_command(
|
|
|
3311
3332
|
)
|
|
3312
3333
|
)
|
|
3313
3334
|
stdin_for_next = full_stream_output
|
|
3314
|
-
except:
|
|
3335
|
+
except Exception:
|
|
3315
3336
|
if output is not None:
|
|
3316
3337
|
try:
|
|
3317
3338
|
stdin_for_next = str(output)
|
|
@@ -3413,7 +3434,7 @@ def execute_command(
|
|
|
3413
3434
|
def setup_shell() -> Tuple[CommandHistory, Team, Optional[NPC]]:
|
|
3414
3435
|
setup_npcsh_config()
|
|
3415
3436
|
|
|
3416
|
-
db_path =
|
|
3437
|
+
db_path = NPCSH_DB_PATH
|
|
3417
3438
|
db_path = os.path.expanduser(db_path)
|
|
3418
3439
|
os.makedirs(os.path.dirname(db_path), exist_ok=True)
|
|
3419
3440
|
command_history = CommandHistory(db_path)
|
|
@@ -3424,11 +3445,11 @@ def setup_shell() -> Tuple[CommandHistory, Team, Optional[NPC]]:
|
|
|
3424
3445
|
print("NPCSH initialization complete. Restart or source ~/.npcshrc.")
|
|
3425
3446
|
|
|
3426
3447
|
try:
|
|
3427
|
-
|
|
3448
|
+
setup_readline()
|
|
3428
3449
|
atexit.register(save_readline_history)
|
|
3429
3450
|
atexit.register(command_history.close)
|
|
3430
|
-
except:
|
|
3431
|
-
|
|
3451
|
+
except OSError as e:
|
|
3452
|
+
print(f"Warning: Failed to setup readline history: {e}", file=sys.stderr)
|
|
3432
3453
|
|
|
3433
3454
|
project_team_path = os.path.abspath(PROJECT_NPC_TEAM_PATH)
|
|
3434
3455
|
global_team_path = os.path.expanduser(DEFAULT_NPC_TEAM_PATH)
|
|
@@ -3437,7 +3458,7 @@ def setup_shell() -> Tuple[CommandHistory, Team, Optional[NPC]]:
|
|
|
3437
3458
|
default_forenpc_name = None
|
|
3438
3459
|
global_team_path = os.path.expanduser(DEFAULT_NPC_TEAM_PATH)
|
|
3439
3460
|
if not os.path.exists(global_team_path):
|
|
3440
|
-
print(
|
|
3461
|
+
print("Global NPC team directory doesn't exist. Initializing...")
|
|
3441
3462
|
initialize_base_npcs_if_needed(db_path)
|
|
3442
3463
|
if os.path.exists(project_team_path):
|
|
3443
3464
|
team_dir = project_team_path
|
|
@@ -3657,6 +3678,10 @@ def process_result(
|
|
|
3657
3678
|
final_output_str = None
|
|
3658
3679
|
|
|
3659
3680
|
# FIX: Handle dict output properly
|
|
3681
|
+
msg_input_tokens = None
|
|
3682
|
+
msg_output_tokens = None
|
|
3683
|
+
msg_cost = None
|
|
3684
|
+
|
|
3660
3685
|
if isinstance(output, dict):
|
|
3661
3686
|
# Use None-safe check to not skip empty strings
|
|
3662
3687
|
output_content = output.get('output') if 'output' in output else output.get('response')
|
|
@@ -3666,15 +3691,18 @@ def process_result(
|
|
|
3666
3691
|
# Accumulate token usage if available
|
|
3667
3692
|
if 'usage' in output:
|
|
3668
3693
|
usage = output['usage']
|
|
3669
|
-
|
|
3670
|
-
|
|
3694
|
+
msg_input_tokens = usage.get('input_tokens', 0)
|
|
3695
|
+
msg_output_tokens = usage.get('output_tokens', 0)
|
|
3696
|
+
result_state.session_input_tokens += msg_input_tokens
|
|
3697
|
+
result_state.session_output_tokens += msg_output_tokens
|
|
3671
3698
|
# Calculate cost
|
|
3672
3699
|
from npcpy.gen.response import calculate_cost
|
|
3673
|
-
|
|
3700
|
+
msg_cost = calculate_cost(
|
|
3674
3701
|
model_for_stream,
|
|
3675
|
-
|
|
3676
|
-
|
|
3702
|
+
msg_input_tokens,
|
|
3703
|
+
msg_output_tokens
|
|
3677
3704
|
)
|
|
3705
|
+
result_state.session_cost_usd += msg_cost
|
|
3678
3706
|
|
|
3679
3707
|
# If output_content is still a dict, convert to string
|
|
3680
3708
|
if isinstance(output_content, dict):
|
|
@@ -3736,6 +3764,9 @@ def process_result(
|
|
|
3736
3764
|
provider=active_npc.provider,
|
|
3737
3765
|
npc=npc_name,
|
|
3738
3766
|
team=team_name,
|
|
3767
|
+
input_tokens=msg_input_tokens,
|
|
3768
|
+
output_tokens=msg_output_tokens,
|
|
3769
|
+
cost=msg_cost,
|
|
3739
3770
|
)
|
|
3740
3771
|
|
|
3741
3772
|
result_state.turn_count += 1
|
|
@@ -3844,15 +3875,15 @@ def process_result(
|
|
|
3844
3875
|
result_state.current_path
|
|
3845
3876
|
)
|
|
3846
3877
|
evolved_npc_kg, _ = kg_evolve_incremental(
|
|
3847
|
-
existing_kg=npc_kg,
|
|
3878
|
+
existing_kg=npc_kg,
|
|
3848
3879
|
new_facts=approved_facts,
|
|
3849
|
-
model=active_npc.model,
|
|
3850
|
-
provider=active_npc.provider,
|
|
3880
|
+
model=active_npc.model,
|
|
3881
|
+
provider=active_npc.provider,
|
|
3851
3882
|
npc=active_npc,
|
|
3852
3883
|
get_concepts=True,
|
|
3853
|
-
link_concepts_facts=
|
|
3854
|
-
link_concepts_concepts=
|
|
3855
|
-
link_facts_facts=
|
|
3884
|
+
link_concepts_facts=result_state.kg_link_facts,
|
|
3885
|
+
link_concepts_concepts=result_state.kg_link_concepts,
|
|
3886
|
+
link_facts_facts=result_state.kg_link_facts_facts,
|
|
3856
3887
|
)
|
|
3857
3888
|
save_kg_to_db(
|
|
3858
3889
|
engine,
|
npcsh/alicanto.py
CHANGED
|
@@ -4,7 +4,7 @@ alicanto - Deep research mode CLI entry point
|
|
|
4
4
|
This is a thin wrapper that executes the alicanto.jinx through the jinx mechanism.
|
|
5
5
|
"""
|
|
6
6
|
import argparse
|
|
7
|
-
|
|
7
|
+
|
|
8
8
|
import sys
|
|
9
9
|
|
|
10
10
|
from npcsh._state import setup_shell
|
|
@@ -30,7 +30,7 @@ def main():
|
|
|
30
30
|
sys.exit(1)
|
|
31
31
|
|
|
32
32
|
# Setup shell to get team and default NPC
|
|
33
|
-
|
|
33
|
+
_, team, default_npc = setup_shell()
|
|
34
34
|
|
|
35
35
|
if not team or "alicanto" not in team.jinxs_dict:
|
|
36
36
|
print("Error: alicanto jinx not found. Ensure npc_team/jinxs/modes/alicanto.jinx exists.")
|
npcsh/benchmark/__init__.py
CHANGED
|
@@ -16,7 +16,13 @@ Usage:
|
|
|
16
16
|
run_benchmark(model="claude-sonnet-4-20250514", provider="anthropic")
|
|
17
17
|
"""
|
|
18
18
|
|
|
19
|
-
from .npcsh_agent import NpcshAgent
|
|
20
19
|
from .runner import run_benchmark, BenchmarkRunner
|
|
21
20
|
|
|
22
|
-
__all__ = ["
|
|
21
|
+
__all__ = ["run_benchmark", "BenchmarkRunner"]
|
|
22
|
+
|
|
23
|
+
# NpcshAgent requires harbor to be installed - import lazily
|
|
24
|
+
try:
|
|
25
|
+
from .npcsh_agent import NpcshAgent
|
|
26
|
+
__all__.append("NpcshAgent")
|
|
27
|
+
except ImportError:
|
|
28
|
+
NpcshAgent = None # Harbor not installed
|