npcsh 1.1.2__py3-none-any.whl → 1.1.3__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 +0 -29
- npcsh/alicanto.py +10 -5
- npcsh/build.py +291 -0
- npcsh/corca.py +263 -154
- npcsh/npc.py +127 -46
- npcsh/routes.py +229 -21
- {npcsh-1.1.2.dist-info → npcsh-1.1.3.dist-info}/METADATA +10 -1
- {npcsh-1.1.2.dist-info → npcsh-1.1.3.dist-info}/RECORD +40 -39
- {npcsh-1.1.2.data → npcsh-1.1.3.data}/data/npcsh/npc_team/alicanto.npc +0 -0
- {npcsh-1.1.2.data → npcsh-1.1.3.data}/data/npcsh/npc_team/alicanto.png +0 -0
- {npcsh-1.1.2.data → npcsh-1.1.3.data}/data/npcsh/npc_team/bash_executer.jinx +0 -0
- {npcsh-1.1.2.data → npcsh-1.1.3.data}/data/npcsh/npc_team/corca.npc +0 -0
- {npcsh-1.1.2.data → npcsh-1.1.3.data}/data/npcsh/npc_team/corca.png +0 -0
- {npcsh-1.1.2.data → npcsh-1.1.3.data}/data/npcsh/npc_team/edit_file.jinx +0 -0
- {npcsh-1.1.2.data → npcsh-1.1.3.data}/data/npcsh/npc_team/foreman.npc +0 -0
- {npcsh-1.1.2.data → npcsh-1.1.3.data}/data/npcsh/npc_team/frederic.npc +0 -0
- {npcsh-1.1.2.data → npcsh-1.1.3.data}/data/npcsh/npc_team/frederic4.png +0 -0
- {npcsh-1.1.2.data → npcsh-1.1.3.data}/data/npcsh/npc_team/guac.png +0 -0
- {npcsh-1.1.2.data → npcsh-1.1.3.data}/data/npcsh/npc_team/image_generation.jinx +0 -0
- {npcsh-1.1.2.data → npcsh-1.1.3.data}/data/npcsh/npc_team/internet_search.jinx +0 -0
- {npcsh-1.1.2.data → npcsh-1.1.3.data}/data/npcsh/npc_team/kadiefa.npc +0 -0
- {npcsh-1.1.2.data → npcsh-1.1.3.data}/data/npcsh/npc_team/kadiefa.png +0 -0
- {npcsh-1.1.2.data → npcsh-1.1.3.data}/data/npcsh/npc_team/kg_search.jinx +0 -0
- {npcsh-1.1.2.data → npcsh-1.1.3.data}/data/npcsh/npc_team/memory_search.jinx +0 -0
- {npcsh-1.1.2.data → npcsh-1.1.3.data}/data/npcsh/npc_team/npcsh.ctx +0 -0
- {npcsh-1.1.2.data → npcsh-1.1.3.data}/data/npcsh/npc_team/npcsh_sibiji.png +0 -0
- {npcsh-1.1.2.data → npcsh-1.1.3.data}/data/npcsh/npc_team/plonk.npc +0 -0
- {npcsh-1.1.2.data → npcsh-1.1.3.data}/data/npcsh/npc_team/plonk.png +0 -0
- {npcsh-1.1.2.data → npcsh-1.1.3.data}/data/npcsh/npc_team/plonkjr.npc +0 -0
- {npcsh-1.1.2.data → npcsh-1.1.3.data}/data/npcsh/npc_team/plonkjr.png +0 -0
- {npcsh-1.1.2.data → npcsh-1.1.3.data}/data/npcsh/npc_team/python_executor.jinx +0 -0
- {npcsh-1.1.2.data → npcsh-1.1.3.data}/data/npcsh/npc_team/screen_cap.jinx +0 -0
- {npcsh-1.1.2.data → npcsh-1.1.3.data}/data/npcsh/npc_team/sibiji.npc +0 -0
- {npcsh-1.1.2.data → npcsh-1.1.3.data}/data/npcsh/npc_team/sibiji.png +0 -0
- {npcsh-1.1.2.data → npcsh-1.1.3.data}/data/npcsh/npc_team/spool.png +0 -0
- {npcsh-1.1.2.data → npcsh-1.1.3.data}/data/npcsh/npc_team/yap.png +0 -0
- {npcsh-1.1.2.dist-info → npcsh-1.1.3.dist-info}/WHEEL +0 -0
- {npcsh-1.1.2.dist-info → npcsh-1.1.3.dist-info}/entry_points.txt +0 -0
- {npcsh-1.1.2.dist-info → npcsh-1.1.3.dist-info}/licenses/LICENSE +0 -0
- {npcsh-1.1.2.dist-info → npcsh-1.1.3.dist-info}/top_level.txt +0 -0
npcsh/_state.py
CHANGED
|
@@ -645,7 +645,6 @@ BASH_COMMANDS = [
|
|
|
645
645
|
"fg",
|
|
646
646
|
"getopts",
|
|
647
647
|
"hash",
|
|
648
|
-
"help",
|
|
649
648
|
"history",
|
|
650
649
|
"if",
|
|
651
650
|
"jobs",
|
|
@@ -2206,34 +2205,6 @@ def execute_command(
|
|
|
2206
2205
|
npc_name = state.npc.name if isinstance(state.npc, NPC) else "__none__"
|
|
2207
2206
|
team_name = state.team.name if state.team else "__none__"
|
|
2208
2207
|
|
|
2209
|
-
if command_history:
|
|
2210
|
-
relevant_memories = get_relevant_memories(
|
|
2211
|
-
command_history=command_history,
|
|
2212
|
-
npc_name=npc_name,
|
|
2213
|
-
team_name=team_name,
|
|
2214
|
-
path=state.current_path,
|
|
2215
|
-
query=command,
|
|
2216
|
-
max_memories=5,
|
|
2217
|
-
state=state
|
|
2218
|
-
)
|
|
2219
|
-
print('Memory jogged...')
|
|
2220
|
-
print(relevant_memories)
|
|
2221
|
-
|
|
2222
|
-
if relevant_memories:
|
|
2223
|
-
memory_context = "\n".join([
|
|
2224
|
-
f"- {m.get('final_memory', '')}"
|
|
2225
|
-
for m in relevant_memories
|
|
2226
|
-
])
|
|
2227
|
-
memory_msg = {
|
|
2228
|
-
"role": "system",
|
|
2229
|
-
"content": f"Relevant memories:\n{memory_context}"
|
|
2230
|
-
}
|
|
2231
|
-
if not state.messages or \
|
|
2232
|
-
state.messages[0].get("role") != "system":
|
|
2233
|
-
state.messages.insert(0, memory_msg)
|
|
2234
|
-
else:
|
|
2235
|
-
state.messages[0]["content"] += \
|
|
2236
|
-
f"\n\n{memory_msg['content']}"
|
|
2237
2208
|
|
|
2238
2209
|
original_command_for_embedding = command
|
|
2239
2210
|
commands = split_by_pipes(command)
|
npcsh/alicanto.py
CHANGED
|
@@ -12,6 +12,16 @@ from dataclasses import dataclass, asdict, field
|
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
from concurrent.futures import ThreadPoolExecutor
|
|
14
14
|
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
from datasets import load_dataset
|
|
18
|
+
except:
|
|
19
|
+
load_dataset = None
|
|
20
|
+
from sklearn.feature_extraction.text import TfidfVectorizer
|
|
21
|
+
from sklearn.metrics.pairwise import cosine_similarity
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
15
25
|
from npcpy.tools import auto_tools
|
|
16
26
|
from npcpy.llm_funcs import get_llm_response
|
|
17
27
|
from npcpy.data.web import search_web
|
|
@@ -88,11 +98,6 @@ def list_files(directory: str = ".") -> List[str]:
|
|
|
88
98
|
return os.listdir(directory)
|
|
89
99
|
|
|
90
100
|
|
|
91
|
-
|
|
92
|
-
from datasets import load_dataset
|
|
93
|
-
from sklearn.feature_extraction.text import TfidfVectorizer
|
|
94
|
-
from sklearn.metrics.pairwise import cosine_similarity
|
|
95
|
-
|
|
96
101
|
DATASET_CACHE = None
|
|
97
102
|
SEARCH_INDEX = None
|
|
98
103
|
|
npcsh/build.py
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import shutil
|
|
3
|
+
import textwrap
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def build_flask_server(config, **kwargs):
|
|
8
|
+
output_dir = Path(config['output_dir'])
|
|
9
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
10
|
+
|
|
11
|
+
server_script = output_dir / 'npc_server.py'
|
|
12
|
+
|
|
13
|
+
server_code = textwrap.dedent(f'''
|
|
14
|
+
import os
|
|
15
|
+
from npcpy.serve import start_flask_server
|
|
16
|
+
from npcpy.npc_compiler import Team
|
|
17
|
+
from sqlalchemy import create_engine
|
|
18
|
+
|
|
19
|
+
if __name__ == "__main__":
|
|
20
|
+
team_path = os.path.join(
|
|
21
|
+
os.path.dirname(__file__),
|
|
22
|
+
"npc_team"
|
|
23
|
+
)
|
|
24
|
+
db_path = os.path.expanduser("~/npcsh_history.db")
|
|
25
|
+
|
|
26
|
+
db_conn = create_engine(f'sqlite:///{{db_path}}')
|
|
27
|
+
team = Team(team_path=team_path, db_conn=db_conn)
|
|
28
|
+
|
|
29
|
+
start_flask_server(
|
|
30
|
+
port={config['port']},
|
|
31
|
+
cors_origins={config.get('cors_origins')},
|
|
32
|
+
teams={{"main": team}},
|
|
33
|
+
npcs=team.npcs,
|
|
34
|
+
db_path=db_path,
|
|
35
|
+
user_npc_directory=os.path.expanduser(
|
|
36
|
+
"~/.npcsh/npc_team"
|
|
37
|
+
)
|
|
38
|
+
)
|
|
39
|
+
''')
|
|
40
|
+
|
|
41
|
+
server_script.write_text(server_code)
|
|
42
|
+
|
|
43
|
+
shutil.copytree(
|
|
44
|
+
config['team_path'],
|
|
45
|
+
output_dir / 'npc_team',
|
|
46
|
+
dirs_exist_ok=True
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
requirements = output_dir / 'requirements.txt'
|
|
50
|
+
requirements.write_text(
|
|
51
|
+
'npcsh\n'
|
|
52
|
+
'flask\n'
|
|
53
|
+
'flask-cors\n'
|
|
54
|
+
'sqlalchemy\n'
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
readme = output_dir / 'README.md'
|
|
58
|
+
readme.write_text(textwrap.dedent(f'''
|
|
59
|
+
# NPC Team Server
|
|
60
|
+
|
|
61
|
+
Run: python npc_server.py
|
|
62
|
+
|
|
63
|
+
Server will be available at http://localhost:{config['port']}
|
|
64
|
+
|
|
65
|
+
For pyinstaller standalone:
|
|
66
|
+
pyinstaller --onefile npc_server.py
|
|
67
|
+
'''))
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
"output": f"Flask server built in {output_dir}",
|
|
71
|
+
"messages": kwargs.get('messages', [])
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def build_docker_compose(config, **kwargs):
|
|
76
|
+
output_dir = Path(config['output_dir'])
|
|
77
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
78
|
+
|
|
79
|
+
shutil.copytree(
|
|
80
|
+
config['team_path'],
|
|
81
|
+
output_dir / 'npc_team',
|
|
82
|
+
dirs_exist_ok=True
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
dockerfile = output_dir / 'Dockerfile'
|
|
86
|
+
dockerfile.write_text(textwrap.dedent('''
|
|
87
|
+
FROM python:3.11-slim
|
|
88
|
+
|
|
89
|
+
WORKDIR /app
|
|
90
|
+
|
|
91
|
+
COPY requirements.txt .
|
|
92
|
+
RUN pip install --no-cache-dir -r requirements.txt
|
|
93
|
+
|
|
94
|
+
COPY npc_team ./npc_team
|
|
95
|
+
COPY npc_server.py .
|
|
96
|
+
|
|
97
|
+
EXPOSE 5337
|
|
98
|
+
|
|
99
|
+
CMD ["python", "npc_server.py"]
|
|
100
|
+
'''))
|
|
101
|
+
|
|
102
|
+
compose = output_dir / 'docker-compose.yml'
|
|
103
|
+
compose.write_text(textwrap.dedent(f'''
|
|
104
|
+
version: '3.8'
|
|
105
|
+
|
|
106
|
+
services:
|
|
107
|
+
npc-server:
|
|
108
|
+
build: .
|
|
109
|
+
ports:
|
|
110
|
+
- "{config['port']}:{config['port']}"
|
|
111
|
+
volumes:
|
|
112
|
+
- npc-data:/root/.npcsh
|
|
113
|
+
environment:
|
|
114
|
+
- NPCSH_DB_PATH=/root/.npcsh/npcsh_history.db
|
|
115
|
+
|
|
116
|
+
volumes:
|
|
117
|
+
npc-data:
|
|
118
|
+
'''))
|
|
119
|
+
|
|
120
|
+
build_flask_server(config, **kwargs)
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
"output": f"Docker compose built in {output_dir}. Run: docker-compose up",
|
|
124
|
+
"messages": kwargs.get('messages', [])
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def build_cli_executable(config, **kwargs):
|
|
129
|
+
output_dir = Path(config['output_dir'])
|
|
130
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
131
|
+
|
|
132
|
+
cli_script = output_dir / 'npc_cli.py'
|
|
133
|
+
|
|
134
|
+
cli_code = textwrap.dedent('''
|
|
135
|
+
import sys
|
|
136
|
+
from npcsh._state import setup_shell, execute_command, initial_state
|
|
137
|
+
from npcsh.routes import router
|
|
138
|
+
|
|
139
|
+
def main():
|
|
140
|
+
if len(sys.argv) < 2:
|
|
141
|
+
print("Usage: npc_cli <command>")
|
|
142
|
+
sys.exit(1)
|
|
143
|
+
|
|
144
|
+
command = " ".join(sys.argv[1:])
|
|
145
|
+
|
|
146
|
+
command_history, team, npc = setup_shell()
|
|
147
|
+
initial_state.npc = npc
|
|
148
|
+
initial_state.team = team
|
|
149
|
+
|
|
150
|
+
state, result = execute_command(
|
|
151
|
+
command,
|
|
152
|
+
initial_state,
|
|
153
|
+
router=router
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
output = result.get('output') if isinstance(result, dict) else result
|
|
157
|
+
print(output)
|
|
158
|
+
|
|
159
|
+
if __name__ == "__main__":
|
|
160
|
+
main()
|
|
161
|
+
''')
|
|
162
|
+
|
|
163
|
+
cli_script.write_text(cli_code)
|
|
164
|
+
|
|
165
|
+
shutil.copytree(
|
|
166
|
+
config['team_path'],
|
|
167
|
+
output_dir / 'npc_team',
|
|
168
|
+
dirs_exist_ok=True
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
spec_file = output_dir / 'npc_cli.spec'
|
|
172
|
+
spec_file.write_text(textwrap.dedent('''
|
|
173
|
+
a = Analysis(
|
|
174
|
+
['npc_cli.py'],
|
|
175
|
+
pathex=[],
|
|
176
|
+
binaries=[],
|
|
177
|
+
datas=[('npc_team', 'npc_team')],
|
|
178
|
+
hiddenimports=[],
|
|
179
|
+
hookspath=[],
|
|
180
|
+
hooksconfig={},
|
|
181
|
+
runtime_hooks=[],
|
|
182
|
+
excludes=[],
|
|
183
|
+
win_no_prefer_redirects=False,
|
|
184
|
+
win_private_assemblies=False,
|
|
185
|
+
cipher=None,
|
|
186
|
+
noarchive=False,
|
|
187
|
+
)
|
|
188
|
+
pyz = PYZ(a.pure, a.zipped_data, cipher=None)
|
|
189
|
+
|
|
190
|
+
exe = EXE(
|
|
191
|
+
pyz,
|
|
192
|
+
a.scripts,
|
|
193
|
+
a.binaries,
|
|
194
|
+
a.zipfiles,
|
|
195
|
+
a.datas,
|
|
196
|
+
[],
|
|
197
|
+
name='npc',
|
|
198
|
+
debug=False,
|
|
199
|
+
bootloader_ignore_signals=False,
|
|
200
|
+
strip=False,
|
|
201
|
+
upx=True,
|
|
202
|
+
upx_exclude=[],
|
|
203
|
+
runtime_tmpdir=None,
|
|
204
|
+
console=True,
|
|
205
|
+
)
|
|
206
|
+
'''))
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
"output": (
|
|
210
|
+
f"CLI executable built in {output_dir}. "
|
|
211
|
+
f"Run: pyinstaller npc_cli.spec"
|
|
212
|
+
),
|
|
213
|
+
"messages": kwargs.get('messages', [])
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def build_static_site(config, **kwargs):
|
|
218
|
+
output_dir = Path(config['output_dir'])
|
|
219
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
220
|
+
|
|
221
|
+
html = output_dir / 'index.html'
|
|
222
|
+
html.write_text(textwrap.dedent(f'''
|
|
223
|
+
<!DOCTYPE html>
|
|
224
|
+
<html>
|
|
225
|
+
<head>
|
|
226
|
+
<title>NPC Team Interface</title>
|
|
227
|
+
<style>
|
|
228
|
+
body {{
|
|
229
|
+
font-family: monospace;
|
|
230
|
+
max-width: 800px;
|
|
231
|
+
margin: 50px auto;
|
|
232
|
+
}}
|
|
233
|
+
#output {{
|
|
234
|
+
white-space: pre-wrap;
|
|
235
|
+
background: #f5f5f5;
|
|
236
|
+
padding: 20px;
|
|
237
|
+
min-height: 300px;
|
|
238
|
+
}}
|
|
239
|
+
</style>
|
|
240
|
+
</head>
|
|
241
|
+
<body>
|
|
242
|
+
<h1>NPC Team</h1>
|
|
243
|
+
<input id="input" type="text"
|
|
244
|
+
placeholder="Enter command..."
|
|
245
|
+
style="width: 100%; padding: 10px;">
|
|
246
|
+
<div id="output"></div>
|
|
247
|
+
|
|
248
|
+
<script>
|
|
249
|
+
const API_URL = '{config.get("api_url", "http://localhost:5337")}';
|
|
250
|
+
|
|
251
|
+
document.getElementById('input').addEventListener('keypress',
|
|
252
|
+
async (e) => {{
|
|
253
|
+
if (e.key === 'Enter') {{
|
|
254
|
+
const cmd = e.target.value;
|
|
255
|
+
e.target.value = '';
|
|
256
|
+
|
|
257
|
+
const resp = await fetch(`${{API_URL}}/api/stream`, {{
|
|
258
|
+
method: 'POST',
|
|
259
|
+
headers: {{'Content-Type': 'application/json'}},
|
|
260
|
+
body: JSON.stringify({{
|
|
261
|
+
commandstr: cmd,
|
|
262
|
+
conversationId: 'web-session',
|
|
263
|
+
model: 'llama3.2',
|
|
264
|
+
provider: 'ollama'
|
|
265
|
+
}})
|
|
266
|
+
}});
|
|
267
|
+
|
|
268
|
+
const reader = resp.body.getReader();
|
|
269
|
+
const decoder = new TextDecoder();
|
|
270
|
+
|
|
271
|
+
while (true) {{
|
|
272
|
+
const {{done, value}} = await reader.read();
|
|
273
|
+
if (done) break;
|
|
274
|
+
|
|
275
|
+
const text = decoder.decode(value);
|
|
276
|
+
document.getElementById('output').textContent += text;
|
|
277
|
+
}}
|
|
278
|
+
}}
|
|
279
|
+
}});
|
|
280
|
+
</script>
|
|
281
|
+
</body>
|
|
282
|
+
</html>
|
|
283
|
+
'''))
|
|
284
|
+
|
|
285
|
+
return {
|
|
286
|
+
"output": (
|
|
287
|
+
f"Static site built in {output_dir}. "
|
|
288
|
+
f"Serve with: python -m http.server 8000"
|
|
289
|
+
),
|
|
290
|
+
"messages": kwargs.get('messages', [])
|
|
291
|
+
}
|