npcsh 0.3.31__py3-none-any.whl → 1.0.0__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 +942 -0
- npcsh/alicanto.py +1074 -0
- npcsh/guac.py +785 -0
- npcsh/mcp_helpers.py +357 -0
- npcsh/mcp_npcsh.py +822 -0
- npcsh/mcp_server.py +184 -0
- npcsh/npc.py +218 -0
- npcsh/npcsh.py +1161 -0
- npcsh/plonk.py +387 -269
- npcsh/pti.py +234 -0
- npcsh/routes.py +958 -0
- npcsh/spool.py +315 -0
- npcsh/wander.py +550 -0
- npcsh/yap.py +573 -0
- npcsh-1.0.0.dist-info/METADATA +596 -0
- npcsh-1.0.0.dist-info/RECORD +21 -0
- {npcsh-0.3.31.dist-info → npcsh-1.0.0.dist-info}/WHEEL +1 -1
- npcsh-1.0.0.dist-info/entry_points.txt +9 -0
- {npcsh-0.3.31.dist-info → npcsh-1.0.0.dist-info}/licenses/LICENSE +1 -1
- npcsh/audio.py +0 -210
- npcsh/cli.py +0 -545
- npcsh/command_history.py +0 -566
- npcsh/conversation.py +0 -291
- npcsh/data_models.py +0 -46
- npcsh/dataframes.py +0 -163
- npcsh/embeddings.py +0 -168
- npcsh/helpers.py +0 -641
- npcsh/image.py +0 -298
- npcsh/image_gen.py +0 -79
- npcsh/knowledge_graph.py +0 -1006
- npcsh/llm_funcs.py +0 -2027
- npcsh/load_data.py +0 -83
- npcsh/main.py +0 -5
- npcsh/model_runner.py +0 -189
- npcsh/npc_compiler.py +0 -2870
- npcsh/npc_sysenv.py +0 -383
- npcsh/npc_team/assembly_lines/test_pipeline.py +0 -181
- npcsh/npc_team/corca.npc +0 -13
- npcsh/npc_team/foreman.npc +0 -7
- npcsh/npc_team/npcsh.ctx +0 -11
- npcsh/npc_team/sibiji.npc +0 -4
- npcsh/npc_team/templates/analytics/celona.npc +0 -0
- npcsh/npc_team/templates/hr_support/raone.npc +0 -0
- npcsh/npc_team/templates/humanities/eriane.npc +0 -4
- npcsh/npc_team/templates/it_support/lineru.npc +0 -0
- npcsh/npc_team/templates/marketing/slean.npc +0 -4
- npcsh/npc_team/templates/philosophy/maurawa.npc +0 -0
- npcsh/npc_team/templates/sales/turnic.npc +0 -4
- npcsh/npc_team/templates/software/welxor.npc +0 -0
- npcsh/npc_team/tools/bash_executer.tool +0 -32
- npcsh/npc_team/tools/calculator.tool +0 -8
- npcsh/npc_team/tools/code_executor.tool +0 -16
- npcsh/npc_team/tools/generic_search.tool +0 -27
- npcsh/npc_team/tools/image_generation.tool +0 -25
- npcsh/npc_team/tools/local_search.tool +0 -149
- npcsh/npc_team/tools/npcsh_executor.tool +0 -9
- npcsh/npc_team/tools/screen_cap.tool +0 -27
- npcsh/npc_team/tools/sql_executor.tool +0 -26
- npcsh/response.py +0 -623
- npcsh/search.py +0 -248
- npcsh/serve.py +0 -1460
- npcsh/shell.py +0 -538
- npcsh/shell_helpers.py +0 -3529
- npcsh/stream.py +0 -700
- npcsh/video.py +0 -49
- npcsh-0.3.31.data/data/npcsh/npc_team/bash_executer.tool +0 -32
- npcsh-0.3.31.data/data/npcsh/npc_team/calculator.tool +0 -8
- npcsh-0.3.31.data/data/npcsh/npc_team/celona.npc +0 -0
- npcsh-0.3.31.data/data/npcsh/npc_team/code_executor.tool +0 -16
- npcsh-0.3.31.data/data/npcsh/npc_team/corca.npc +0 -13
- npcsh-0.3.31.data/data/npcsh/npc_team/eriane.npc +0 -4
- npcsh-0.3.31.data/data/npcsh/npc_team/foreman.npc +0 -7
- npcsh-0.3.31.data/data/npcsh/npc_team/generic_search.tool +0 -27
- npcsh-0.3.31.data/data/npcsh/npc_team/image_generation.tool +0 -25
- npcsh-0.3.31.data/data/npcsh/npc_team/lineru.npc +0 -0
- npcsh-0.3.31.data/data/npcsh/npc_team/local_search.tool +0 -149
- npcsh-0.3.31.data/data/npcsh/npc_team/maurawa.npc +0 -0
- npcsh-0.3.31.data/data/npcsh/npc_team/npcsh.ctx +0 -11
- npcsh-0.3.31.data/data/npcsh/npc_team/npcsh_executor.tool +0 -9
- npcsh-0.3.31.data/data/npcsh/npc_team/raone.npc +0 -0
- npcsh-0.3.31.data/data/npcsh/npc_team/screen_cap.tool +0 -27
- npcsh-0.3.31.data/data/npcsh/npc_team/sibiji.npc +0 -4
- npcsh-0.3.31.data/data/npcsh/npc_team/slean.npc +0 -4
- npcsh-0.3.31.data/data/npcsh/npc_team/sql_executor.tool +0 -26
- npcsh-0.3.31.data/data/npcsh/npc_team/test_pipeline.py +0 -181
- npcsh-0.3.31.data/data/npcsh/npc_team/turnic.npc +0 -4
- npcsh-0.3.31.data/data/npcsh/npc_team/welxor.npc +0 -0
- npcsh-0.3.31.dist-info/METADATA +0 -1853
- npcsh-0.3.31.dist-info/RECORD +0 -76
- npcsh-0.3.31.dist-info/entry_points.txt +0 -3
- {npcsh-0.3.31.dist-info → npcsh-1.0.0.dist-info}/top_level.txt +0 -0
npcsh/shell.py
DELETED
|
@@ -1,538 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import sys
|
|
3
|
-
import readline
|
|
4
|
-
import atexit
|
|
5
|
-
import re
|
|
6
|
-
import pty
|
|
7
|
-
import select
|
|
8
|
-
import termios
|
|
9
|
-
import tty
|
|
10
|
-
import shlex
|
|
11
|
-
import json
|
|
12
|
-
|
|
13
|
-
from datetime import datetime
|
|
14
|
-
from inspect import isgenerator
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
# Third-party imports
|
|
18
|
-
import pandas as pd
|
|
19
|
-
import sqlite3
|
|
20
|
-
import numpy as np
|
|
21
|
-
from termcolor import colored
|
|
22
|
-
from dotenv import load_dotenv
|
|
23
|
-
import subprocess
|
|
24
|
-
from typing import Dict, Any, List, Optional
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
try:
|
|
28
|
-
from sentence_transformers import SentenceTransformer
|
|
29
|
-
except:
|
|
30
|
-
print("Could not load the sentence-transformers package.")
|
|
31
|
-
# Local imports
|
|
32
|
-
|
|
33
|
-
from .npc_sysenv import (
|
|
34
|
-
get_system_message,
|
|
35
|
-
lookup_provider,
|
|
36
|
-
NPCSH_STREAM_OUTPUT,
|
|
37
|
-
NPCSH_CHAT_MODEL,
|
|
38
|
-
NPCSH_CHAT_PROVIDER,
|
|
39
|
-
NPCSH_API_URL,
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
from .command_history import (
|
|
43
|
-
CommandHistory,
|
|
44
|
-
start_new_conversation,
|
|
45
|
-
save_conversation_message,
|
|
46
|
-
save_attachment_to_message,
|
|
47
|
-
)
|
|
48
|
-
from .llm_funcs import (
|
|
49
|
-
execute_llm_command,
|
|
50
|
-
execute_llm_question,
|
|
51
|
-
generate_image,
|
|
52
|
-
check_llm_command,
|
|
53
|
-
get_conversation,
|
|
54
|
-
get_system_message,
|
|
55
|
-
)
|
|
56
|
-
from .search import rag_search, search_web
|
|
57
|
-
from .helpers import (
|
|
58
|
-
load_all_files,
|
|
59
|
-
setup_npcsh_config,
|
|
60
|
-
is_npcsh_initialized,
|
|
61
|
-
initialize_base_npcs_if_needed,
|
|
62
|
-
)
|
|
63
|
-
from .shell_helpers import (
|
|
64
|
-
complete, # For command completion
|
|
65
|
-
readline_safe_prompt,
|
|
66
|
-
get_multiline_input,
|
|
67
|
-
setup_readline,
|
|
68
|
-
execute_command,
|
|
69
|
-
render_markdown,
|
|
70
|
-
render_code_block,
|
|
71
|
-
orange, # For colored prompt
|
|
72
|
-
)
|
|
73
|
-
from .npc_compiler import (
|
|
74
|
-
NPCCompiler,
|
|
75
|
-
load_tools_from_directory,
|
|
76
|
-
NPC,
|
|
77
|
-
initialize_npc_project,
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
import argparse
|
|
81
|
-
from .serve import (
|
|
82
|
-
start_flask_server,
|
|
83
|
-
)
|
|
84
|
-
import importlib.metadata # Python 3.8+
|
|
85
|
-
|
|
86
|
-
# Fetch the version from the package metadata
|
|
87
|
-
try:
|
|
88
|
-
VERSION = importlib.metadata.version(
|
|
89
|
-
"npcsh"
|
|
90
|
-
) # Replace "npcsh" with your package name
|
|
91
|
-
except importlib.metadata.PackageNotFoundError:
|
|
92
|
-
VERSION = "unknown" # Fallback if the package is not installed
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
def main() -> None:
|
|
96
|
-
"""
|
|
97
|
-
Main function for the npcsh shell and server.
|
|
98
|
-
Starts either the Flask server or the interactive shell based on the argument provided.
|
|
99
|
-
"""
|
|
100
|
-
# Set up argument parsing to handle 'serve' and regular commands
|
|
101
|
-
|
|
102
|
-
check_old_par_name = os.environ.get("NPCSH_MODEL", None)
|
|
103
|
-
if check_old_par_name is not None:
|
|
104
|
-
# raise a deprecation warning
|
|
105
|
-
print(
|
|
106
|
-
"""Deprecation Warning: NPCSH_MODEL and NPCSH_PROVIDER were deprecated in v0.3.5 in favor of NPCSH_CHAT_MODEL and NPCSH_CHAT_PROVIDER instead.\
|
|
107
|
-
Please update your environment variables to use the new names.
|
|
108
|
-
"""
|
|
109
|
-
)
|
|
110
|
-
|
|
111
|
-
parser = argparse.ArgumentParser(description="npcsh CLI")
|
|
112
|
-
parser.add_argument(
|
|
113
|
-
"-v",
|
|
114
|
-
"--version",
|
|
115
|
-
action="version",
|
|
116
|
-
version=f"npcsh version {VERSION}", # Use the dynamically fetched version
|
|
117
|
-
)
|
|
118
|
-
args = parser.parse_args()
|
|
119
|
-
|
|
120
|
-
setup_npcsh_config()
|
|
121
|
-
if "NPCSH_DB_PATH" in os.environ:
|
|
122
|
-
db_path = os.path.expanduser(os.environ["NPCSH_DB_PATH"])
|
|
123
|
-
else:
|
|
124
|
-
db_path = os.path.expanduser("~/npcsh_history.db")
|
|
125
|
-
|
|
126
|
-
command_history = CommandHistory(db_path)
|
|
127
|
-
valid_commands = [
|
|
128
|
-
"/compile",
|
|
129
|
-
"/com",
|
|
130
|
-
"/whisper",
|
|
131
|
-
"/notes",
|
|
132
|
-
"/data",
|
|
133
|
-
"/cmd",
|
|
134
|
-
"/command",
|
|
135
|
-
"/set",
|
|
136
|
-
"/sample",
|
|
137
|
-
"/spool",
|
|
138
|
-
"/sp",
|
|
139
|
-
"/help",
|
|
140
|
-
"/exit",
|
|
141
|
-
"/quit",
|
|
142
|
-
]
|
|
143
|
-
|
|
144
|
-
readline.set_completer_delims(" \t\n")
|
|
145
|
-
readline.set_completer(complete)
|
|
146
|
-
if sys.platform == "darwin":
|
|
147
|
-
readline.parse_and_bind("bind ^I rl_complete")
|
|
148
|
-
else:
|
|
149
|
-
readline.parse_and_bind("tab: complete")
|
|
150
|
-
|
|
151
|
-
# check if ./npc_team exists
|
|
152
|
-
if os.path.exists("./npc_team"):
|
|
153
|
-
|
|
154
|
-
npc_directory = os.path.abspath("./npc_team/")
|
|
155
|
-
else:
|
|
156
|
-
npc_directory = os.path.expanduser("~/.npcsh/npc_team/")
|
|
157
|
-
|
|
158
|
-
npc_compiler = NPCCompiler(npc_directory, db_path)
|
|
159
|
-
|
|
160
|
-
os.makedirs(npc_directory, exist_ok=True)
|
|
161
|
-
|
|
162
|
-
# Compile all NPCs in the user's npc_team directory
|
|
163
|
-
for filename in os.listdir(npc_directory):
|
|
164
|
-
if filename.endswith(".npc"):
|
|
165
|
-
npc_file_path = os.path.join(npc_directory, filename)
|
|
166
|
-
npc_compiler.compile(npc_file_path)
|
|
167
|
-
|
|
168
|
-
# Compile NPCs from project-specific npc_team directory
|
|
169
|
-
if os.path.exists(npc_directory):
|
|
170
|
-
for filename in os.listdir(npc_directory):
|
|
171
|
-
if filename.endswith(".npc"):
|
|
172
|
-
npc_file_path = os.path.join(npc_directory, filename)
|
|
173
|
-
npc_compiler.compile(npc_file_path)
|
|
174
|
-
|
|
175
|
-
if not is_npcsh_initialized():
|
|
176
|
-
print("Initializing NPCSH...")
|
|
177
|
-
initialize_base_npcs_if_needed(db_path)
|
|
178
|
-
print(
|
|
179
|
-
"NPCSH initialization complete. Please restart your terminal or run 'source ~/.npcshrc' for the changes to take effect."
|
|
180
|
-
)
|
|
181
|
-
|
|
182
|
-
history_file = setup_readline()
|
|
183
|
-
atexit.register(readline.write_history_file, history_file)
|
|
184
|
-
atexit.register(command_history.close)
|
|
185
|
-
# make npcsh into ascii art
|
|
186
|
-
from colorama import init
|
|
187
|
-
|
|
188
|
-
init() # Initialize colorama for ANSI code support
|
|
189
|
-
if sys.stdin.isatty():
|
|
190
|
-
|
|
191
|
-
print(
|
|
192
|
-
"""
|
|
193
|
-
Welcome to \033[1;94mnpc\033[0m\033[1;38;5;202msh\033[0m!
|
|
194
|
-
\033[1;94m \033[0m\033[1;38;5;202m \\\\
|
|
195
|
-
\033[1;94m _ __ _ __ ___ \033[0m\033[1;38;5;202m ___ | |___ \\\\
|
|
196
|
-
\033[1;94m| '_ \ | '_ \ / __|\033[0m\033[1;38;5;202m/ __/ | |_ _| \\\\
|
|
197
|
-
\033[1;94m| | | || |_) |( |__ \033[0m\033[1;38;5;202m\_ \ | | | | //
|
|
198
|
-
\033[1;94m|_| |_|| .__/ \___|\033[0m\033[1;38;5;202m|___/ |_| |_| //
|
|
199
|
-
\033[1;94m| | \033[0m\033[1;38;5;202m //
|
|
200
|
-
\033[1;94m| |
|
|
201
|
-
\033[1;94m|_|
|
|
202
|
-
|
|
203
|
-
Begin by asking a question, issuing a bash command, or typing '/help' for more information.
|
|
204
|
-
"""
|
|
205
|
-
)
|
|
206
|
-
|
|
207
|
-
current_npc = None
|
|
208
|
-
messages = None
|
|
209
|
-
current_conversation_id = start_new_conversation()
|
|
210
|
-
|
|
211
|
-
# --- Minimal Piped Input Handling ---
|
|
212
|
-
if not sys.stdin.isatty():
|
|
213
|
-
for line in sys.stdin:
|
|
214
|
-
user_input = line.strip()
|
|
215
|
-
if not user_input:
|
|
216
|
-
continue # Skip empty lines
|
|
217
|
-
if user_input.lower() in ["exit", "quit"]:
|
|
218
|
-
print("Goodbye!")
|
|
219
|
-
sys.exit(0)
|
|
220
|
-
result = execute_command(
|
|
221
|
-
user_input,
|
|
222
|
-
db_path,
|
|
223
|
-
npc_compiler,
|
|
224
|
-
current_npc,
|
|
225
|
-
model=NPCSH_CHAT_MODEL,
|
|
226
|
-
provider=NPCSH_CHAT_PROVIDER,
|
|
227
|
-
messages=messages,
|
|
228
|
-
conversation_id=current_conversation_id,
|
|
229
|
-
stream=NPCSH_STREAM_OUTPUT,
|
|
230
|
-
api_url=NPCSH_API_URL,
|
|
231
|
-
)
|
|
232
|
-
messages = result.get("messages", messages)
|
|
233
|
-
if "current_npc" in result:
|
|
234
|
-
current_npc = result["current_npc"]
|
|
235
|
-
output = result.get("output")
|
|
236
|
-
conversation_id = result.get("conversation_id")
|
|
237
|
-
model = result.get("model")
|
|
238
|
-
provider = result.get("provider")
|
|
239
|
-
npc = result.get("npc")
|
|
240
|
-
messages = result.get("messages")
|
|
241
|
-
current_path = result.get("current_path")
|
|
242
|
-
attachments = result.get("attachments")
|
|
243
|
-
npc_name = (
|
|
244
|
-
npc.name
|
|
245
|
-
if isinstance(npc, NPC)
|
|
246
|
-
else npc if isinstance(npc, str) else None
|
|
247
|
-
)
|
|
248
|
-
|
|
249
|
-
save_conversation_message(
|
|
250
|
-
command_history,
|
|
251
|
-
conversation_id,
|
|
252
|
-
"user",
|
|
253
|
-
user_input,
|
|
254
|
-
wd=current_path,
|
|
255
|
-
model=model,
|
|
256
|
-
provider=provider,
|
|
257
|
-
npc=npc_name,
|
|
258
|
-
attachments=attachments,
|
|
259
|
-
)
|
|
260
|
-
if NPCSH_STREAM_OUTPUT and (
|
|
261
|
-
isgenerator(output)
|
|
262
|
-
or (hasattr(output, "__iter__") and hasattr(output, "__next__"))
|
|
263
|
-
):
|
|
264
|
-
str_output = ""
|
|
265
|
-
in_code = False
|
|
266
|
-
code_buffer = ""
|
|
267
|
-
text_buffer = ""
|
|
268
|
-
|
|
269
|
-
for chunk in output:
|
|
270
|
-
# Get chunk content based on provider
|
|
271
|
-
if provider == "anthropic":
|
|
272
|
-
chunk_content = (
|
|
273
|
-
chunk.delta.text
|
|
274
|
-
if chunk.type == "content_block_delta"
|
|
275
|
-
else None
|
|
276
|
-
)
|
|
277
|
-
elif provider in ["openai", "deepseek", "openai-like"]:
|
|
278
|
-
chunk_content = "".join(
|
|
279
|
-
c.delta.content for c in chunk.choices if c.delta.content
|
|
280
|
-
)
|
|
281
|
-
elif provider == "ollama":
|
|
282
|
-
chunk_content = chunk["message"]["content"]
|
|
283
|
-
else:
|
|
284
|
-
continue
|
|
285
|
-
|
|
286
|
-
if not chunk_content:
|
|
287
|
-
continue
|
|
288
|
-
|
|
289
|
-
str_output += chunk_content
|
|
290
|
-
|
|
291
|
-
# Process chunk
|
|
292
|
-
if "```" in chunk_content:
|
|
293
|
-
parts = chunk_content.split("```")
|
|
294
|
-
|
|
295
|
-
for i, part in enumerate(parts):
|
|
296
|
-
if i == 0: # First part (before any backticks)
|
|
297
|
-
if in_code:
|
|
298
|
-
code_buffer += part
|
|
299
|
-
else:
|
|
300
|
-
print(part, end="")
|
|
301
|
-
elif i % 2 == 1: # Inside a code block
|
|
302
|
-
if not in_code: # Starting a new code block
|
|
303
|
-
in_code = True
|
|
304
|
-
code_buffer = part
|
|
305
|
-
else: # Shouldn't happen but handle anyway
|
|
306
|
-
code_buffer += part
|
|
307
|
-
else: # Outside a code block
|
|
308
|
-
if in_code: # Just finished a code block
|
|
309
|
-
in_code = False
|
|
310
|
-
# Render the code block
|
|
311
|
-
render_code_block(code_buffer)
|
|
312
|
-
code_buffer = ""
|
|
313
|
-
print(part, end="")
|
|
314
|
-
else:
|
|
315
|
-
if in_code:
|
|
316
|
-
code_buffer += chunk_content
|
|
317
|
-
else:
|
|
318
|
-
print(chunk_content, end="")
|
|
319
|
-
|
|
320
|
-
# Handle any remaining code buffer
|
|
321
|
-
if in_code and code_buffer:
|
|
322
|
-
render_code_block(code_buffer)
|
|
323
|
-
|
|
324
|
-
if str_output:
|
|
325
|
-
output = str_output
|
|
326
|
-
save_conversation_message(
|
|
327
|
-
command_history,
|
|
328
|
-
conversation_id,
|
|
329
|
-
"assistant",
|
|
330
|
-
output,
|
|
331
|
-
wd=current_path,
|
|
332
|
-
model=model,
|
|
333
|
-
provider=provider,
|
|
334
|
-
npc=npc_name,
|
|
335
|
-
)
|
|
336
|
-
sys.exit(0)
|
|
337
|
-
|
|
338
|
-
while True:
|
|
339
|
-
try:
|
|
340
|
-
if current_npc:
|
|
341
|
-
prompt = f"{colored(os.getcwd(), 'blue')}:{orange(current_npc.name)}> "
|
|
342
|
-
else:
|
|
343
|
-
prompt = f"{colored(os.getcwd(), 'blue')}:\033[1;94mnpc\033[0m\033[1;38;5;202msh\033[0m!> "
|
|
344
|
-
|
|
345
|
-
prompt = readline_safe_prompt(prompt)
|
|
346
|
-
user_input = get_multiline_input(prompt).strip()
|
|
347
|
-
|
|
348
|
-
if user_input.lower() in ["exit", "quit"]:
|
|
349
|
-
if current_npc:
|
|
350
|
-
print(f"Exiting {current_npc.name} mode.")
|
|
351
|
-
current_npc = None
|
|
352
|
-
continue
|
|
353
|
-
else:
|
|
354
|
-
print("Goodbye!")
|
|
355
|
-
break
|
|
356
|
-
# print(current_npc, "current npc fore command execution")
|
|
357
|
-
# Execute the command and capture the result
|
|
358
|
-
result = execute_command(
|
|
359
|
-
user_input,
|
|
360
|
-
db_path,
|
|
361
|
-
npc_compiler,
|
|
362
|
-
current_npc=current_npc,
|
|
363
|
-
model=NPCSH_CHAT_MODEL,
|
|
364
|
-
provider=NPCSH_CHAT_PROVIDER,
|
|
365
|
-
messages=messages,
|
|
366
|
-
conversation_id=current_conversation_id,
|
|
367
|
-
stream=NPCSH_STREAM_OUTPUT,
|
|
368
|
-
api_url=NPCSH_API_URL,
|
|
369
|
-
)
|
|
370
|
-
|
|
371
|
-
messages = result.get("messages", messages)
|
|
372
|
-
|
|
373
|
-
# need to adjust the output for the messages to all have
|
|
374
|
-
# model, provider, npc, timestamp, role, content
|
|
375
|
-
# also messages
|
|
376
|
-
|
|
377
|
-
if "current_npc" in result:
|
|
378
|
-
|
|
379
|
-
current_npc = result["current_npc"]
|
|
380
|
-
output = result.get("output")
|
|
381
|
-
|
|
382
|
-
conversation_id = result.get("conversation_id")
|
|
383
|
-
model = result.get("model")
|
|
384
|
-
provider = result.get("provider")
|
|
385
|
-
|
|
386
|
-
messages = result.get("messages")
|
|
387
|
-
current_path = result.get("current_path")
|
|
388
|
-
attachments = result.get("attachments")
|
|
389
|
-
|
|
390
|
-
if current_npc is not None:
|
|
391
|
-
if isinstance(current_npc, NPC):
|
|
392
|
-
npc_name = current_npc.name
|
|
393
|
-
elif isinstance(current_npc, str):
|
|
394
|
-
npc_name = current_npc
|
|
395
|
-
else:
|
|
396
|
-
npc_name = None
|
|
397
|
-
message_id = save_conversation_message(
|
|
398
|
-
command_history,
|
|
399
|
-
conversation_id,
|
|
400
|
-
"user",
|
|
401
|
-
user_input,
|
|
402
|
-
wd=current_path,
|
|
403
|
-
model=model,
|
|
404
|
-
provider=provider,
|
|
405
|
-
npc=npc_name,
|
|
406
|
-
attachments=attachments,
|
|
407
|
-
)
|
|
408
|
-
|
|
409
|
-
str_output = ""
|
|
410
|
-
try:
|
|
411
|
-
if NPCSH_STREAM_OUTPUT and hasattr(output, "__iter__"):
|
|
412
|
-
|
|
413
|
-
buffer = ""
|
|
414
|
-
in_code = False
|
|
415
|
-
code_buffer = ""
|
|
416
|
-
|
|
417
|
-
for chunk in output:
|
|
418
|
-
|
|
419
|
-
if provider == "anthropic":
|
|
420
|
-
chunk_content = (
|
|
421
|
-
chunk.delta.text
|
|
422
|
-
if chunk.type == "content_block_delta"
|
|
423
|
-
else None
|
|
424
|
-
)
|
|
425
|
-
elif provider in ["openai", "deepseek", "openai-like"]:
|
|
426
|
-
chunk_content = "".join(
|
|
427
|
-
c.delta.content
|
|
428
|
-
for c in chunk.choices
|
|
429
|
-
if c.delta.content
|
|
430
|
-
)
|
|
431
|
-
elif provider == "ollama":
|
|
432
|
-
chunk_content = chunk["message"]["content"]
|
|
433
|
-
else:
|
|
434
|
-
continue
|
|
435
|
-
|
|
436
|
-
if not chunk_content:
|
|
437
|
-
continue
|
|
438
|
-
|
|
439
|
-
str_output += chunk_content
|
|
440
|
-
# print(str_output, "str_output")
|
|
441
|
-
# Process the content character by character
|
|
442
|
-
for char in chunk_content:
|
|
443
|
-
buffer += char
|
|
444
|
-
|
|
445
|
-
# Check for triple backticks
|
|
446
|
-
if buffer.endswith("```"):
|
|
447
|
-
if not in_code:
|
|
448
|
-
# Start of code block
|
|
449
|
-
in_code = True
|
|
450
|
-
# Print everything before the backticks
|
|
451
|
-
print(buffer[:-3], end="")
|
|
452
|
-
buffer = ""
|
|
453
|
-
code_buffer = ""
|
|
454
|
-
else:
|
|
455
|
-
# End of code block
|
|
456
|
-
in_code = False
|
|
457
|
-
# Remove the backticks from the end of the buffer
|
|
458
|
-
buffer = buffer[:-3]
|
|
459
|
-
# Add buffer to code content and render
|
|
460
|
-
code_buffer += buffer
|
|
461
|
-
|
|
462
|
-
# Check for and strip language tag
|
|
463
|
-
if (
|
|
464
|
-
"\n" in code_buffer
|
|
465
|
-
and code_buffer.index("\n") < 15
|
|
466
|
-
):
|
|
467
|
-
first_line, rest = code_buffer.split("\n", 1)
|
|
468
|
-
if (
|
|
469
|
-
first_line.strip()
|
|
470
|
-
and not "```" in first_line
|
|
471
|
-
):
|
|
472
|
-
code_buffer = rest
|
|
473
|
-
|
|
474
|
-
# Render the code block
|
|
475
|
-
render_code_block(code_buffer)
|
|
476
|
-
|
|
477
|
-
# Reset buffers
|
|
478
|
-
buffer = ""
|
|
479
|
-
code_buffer = ""
|
|
480
|
-
elif in_code:
|
|
481
|
-
# Just add to code buffer
|
|
482
|
-
code_buffer += char
|
|
483
|
-
if len(buffer) >= 3: # Keep buffer small while in code
|
|
484
|
-
buffer = buffer[-3:]
|
|
485
|
-
else:
|
|
486
|
-
# Regular text - print if buffer gets too large
|
|
487
|
-
if len(buffer) > 100:
|
|
488
|
-
print(buffer[:-3], end="")
|
|
489
|
-
buffer = buffer[
|
|
490
|
-
-3:
|
|
491
|
-
] # Keep last 3 chars to check for backticks
|
|
492
|
-
|
|
493
|
-
# Handle any remaining content
|
|
494
|
-
if in_code:
|
|
495
|
-
render_code_block(code_buffer)
|
|
496
|
-
else:
|
|
497
|
-
print(buffer, end="")
|
|
498
|
-
|
|
499
|
-
if str_output:
|
|
500
|
-
output = str_output
|
|
501
|
-
except:
|
|
502
|
-
output = None
|
|
503
|
-
|
|
504
|
-
print("\n")
|
|
505
|
-
|
|
506
|
-
if isinstance(output, str):
|
|
507
|
-
save_conversation_message(
|
|
508
|
-
command_history,
|
|
509
|
-
conversation_id,
|
|
510
|
-
"assistant",
|
|
511
|
-
output,
|
|
512
|
-
wd=current_path,
|
|
513
|
-
model=model,
|
|
514
|
-
provider=provider,
|
|
515
|
-
npc=npc_name,
|
|
516
|
-
)
|
|
517
|
-
|
|
518
|
-
# if there are attachments in most recent user sent message, save them
|
|
519
|
-
# save_attachment_to_message(command_history, message_id, # file_path, attachment_name, attachment_type)
|
|
520
|
-
|
|
521
|
-
if (
|
|
522
|
-
result["output"] is not None
|
|
523
|
-
and not user_input.startswith("/")
|
|
524
|
-
and not isinstance(result, dict)
|
|
525
|
-
):
|
|
526
|
-
print("final", result)
|
|
527
|
-
|
|
528
|
-
except (KeyboardInterrupt, EOFError):
|
|
529
|
-
if current_npc:
|
|
530
|
-
print(f"\nExiting {current_npc.name} mode.")
|
|
531
|
-
current_npc = None
|
|
532
|
-
else:
|
|
533
|
-
print("\nGoodbye!")
|
|
534
|
-
break
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
if __name__ == "__main__":
|
|
538
|
-
main()
|