npcsh 0.3.32__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.32.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.32.dist-info → npcsh-1.0.0.dist-info}/licenses/LICENSE +1 -1
- npcsh/audio.py +0 -569
- npcsh/audio_gen.py +0 -1
- npcsh/cli.py +0 -543
- npcsh/command_history.py +0 -566
- npcsh/conversation.py +0 -54
- npcsh/data_models.py +0 -46
- npcsh/dataframes.py +0 -171
- npcsh/embeddings.py +0 -168
- npcsh/helpers.py +0 -646
- npcsh/image.py +0 -298
- npcsh/image_gen.py +0 -79
- npcsh/knowledge_graph.py +0 -1006
- npcsh/llm_funcs.py +0 -2195
- npcsh/load_data.py +0 -83
- npcsh/main.py +0 -5
- npcsh/model_runner.py +0 -189
- npcsh/npc_compiler.py +0 -2879
- npcsh/npc_sysenv.py +0 -388
- 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 -272
- npcsh/search.py +0 -252
- npcsh/serve.py +0 -1467
- npcsh/shell.py +0 -524
- npcsh/shell_helpers.py +0 -3919
- npcsh/stream.py +0 -233
- npcsh/video.py +0 -52
- npcsh/video_gen.py +0 -69
- npcsh-0.3.32.data/data/npcsh/npc_team/bash_executer.tool +0 -32
- npcsh-0.3.32.data/data/npcsh/npc_team/calculator.tool +0 -8
- npcsh-0.3.32.data/data/npcsh/npc_team/celona.npc +0 -0
- npcsh-0.3.32.data/data/npcsh/npc_team/code_executor.tool +0 -16
- npcsh-0.3.32.data/data/npcsh/npc_team/corca.npc +0 -13
- npcsh-0.3.32.data/data/npcsh/npc_team/eriane.npc +0 -4
- npcsh-0.3.32.data/data/npcsh/npc_team/foreman.npc +0 -7
- npcsh-0.3.32.data/data/npcsh/npc_team/generic_search.tool +0 -27
- npcsh-0.3.32.data/data/npcsh/npc_team/image_generation.tool +0 -25
- npcsh-0.3.32.data/data/npcsh/npc_team/lineru.npc +0 -0
- npcsh-0.3.32.data/data/npcsh/npc_team/local_search.tool +0 -149
- npcsh-0.3.32.data/data/npcsh/npc_team/maurawa.npc +0 -0
- npcsh-0.3.32.data/data/npcsh/npc_team/npcsh.ctx +0 -11
- npcsh-0.3.32.data/data/npcsh/npc_team/npcsh_executor.tool +0 -9
- npcsh-0.3.32.data/data/npcsh/npc_team/raone.npc +0 -0
- npcsh-0.3.32.data/data/npcsh/npc_team/screen_cap.tool +0 -27
- npcsh-0.3.32.data/data/npcsh/npc_team/sibiji.npc +0 -4
- npcsh-0.3.32.data/data/npcsh/npc_team/slean.npc +0 -4
- npcsh-0.3.32.data/data/npcsh/npc_team/sql_executor.tool +0 -26
- npcsh-0.3.32.data/data/npcsh/npc_team/test_pipeline.py +0 -181
- npcsh-0.3.32.data/data/npcsh/npc_team/turnic.npc +0 -4
- npcsh-0.3.32.data/data/npcsh/npc_team/welxor.npc +0 -0
- npcsh-0.3.32.dist-info/METADATA +0 -779
- npcsh-0.3.32.dist-info/RECORD +0 -78
- npcsh-0.3.32.dist-info/entry_points.txt +0 -3
- {npcsh-0.3.32.dist-info → npcsh-1.0.0.dist-info}/top_level.txt +0 -0
npcsh/mcp_server.py
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
"""
|
|
3
|
+
Enhanced MCP server that incorporates functionality from npcpy.routes,
|
|
4
|
+
npcpy.llm_funcs, and npcpy.npc_compiler as tools.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import subprocess
|
|
9
|
+
import json
|
|
10
|
+
import asyncio
|
|
11
|
+
|
|
12
|
+
from typing import Optional, Dict, Any, List, Union, Callable
|
|
13
|
+
# MCP imports
|
|
14
|
+
from mcp.server.fastmcp import FastMCP
|
|
15
|
+
import importlib
|
|
16
|
+
# npcpy imports
|
|
17
|
+
from npcpy.gen.response import get_litellm_response
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
import os
|
|
21
|
+
import subprocess
|
|
22
|
+
import json
|
|
23
|
+
import asyncio
|
|
24
|
+
try:
|
|
25
|
+
import inspect
|
|
26
|
+
except:
|
|
27
|
+
pass
|
|
28
|
+
from typing import Optional, Dict, Any, List, Union, Callable, get_type_hints
|
|
29
|
+
# Add these imports to the top of your file
|
|
30
|
+
from functools import wraps
|
|
31
|
+
# Initialize the MCP server
|
|
32
|
+
mcp = FastMCP("npcpy_enhanced")
|
|
33
|
+
|
|
34
|
+
# Define the default workspace
|
|
35
|
+
DEFAULT_WORKSPACE = os.path.join(os.getcwd(), "workspace")
|
|
36
|
+
os.makedirs(DEFAULT_WORKSPACE, exist_ok=True)
|
|
37
|
+
|
|
38
|
+
# ==================== SYSTEM TOOLS ====================
|
|
39
|
+
|
|
40
|
+
@mcp.tool()
|
|
41
|
+
async def run_server_command(command: str) -> str:
|
|
42
|
+
"""
|
|
43
|
+
Run a terminal command in the workspace.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
command: The shell command to run
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
The command output or an error message.
|
|
50
|
+
"""
|
|
51
|
+
try:
|
|
52
|
+
result = subprocess.run(
|
|
53
|
+
command,
|
|
54
|
+
cwd=DEFAULT_WORKSPACE,
|
|
55
|
+
shell=True,
|
|
56
|
+
capture_output=True,
|
|
57
|
+
text=True
|
|
58
|
+
)
|
|
59
|
+
return result.stdout or result.stderr
|
|
60
|
+
except Exception as e:
|
|
61
|
+
return str(e)
|
|
62
|
+
def make_async_wrapper(func: Callable) -> Callable:
|
|
63
|
+
"""Create an async wrapper for sync functions that fixes schema validation issues."""
|
|
64
|
+
|
|
65
|
+
@wraps(func)
|
|
66
|
+
async def async_wrapper(*args, **kwargs):
|
|
67
|
+
# Direct parameter dict case (most common failure)
|
|
68
|
+
if len(args) == 1 and isinstance(args[0], dict):
|
|
69
|
+
params = args[0]
|
|
70
|
+
|
|
71
|
+
# Fix for search_web - add required kwargs parameter
|
|
72
|
+
if "kwargs" not in params:
|
|
73
|
+
# Create a new dict with the kwargs parameter added
|
|
74
|
+
params = {**params, "kwargs": ""}
|
|
75
|
+
|
|
76
|
+
# Call the function with the parameters
|
|
77
|
+
if asyncio.iscoroutinefunction(func):
|
|
78
|
+
return await func(**params)
|
|
79
|
+
else:
|
|
80
|
+
return await asyncio.to_thread(func, **params)
|
|
81
|
+
|
|
82
|
+
# Normal function call or other cases
|
|
83
|
+
if asyncio.iscoroutinefunction(func):
|
|
84
|
+
return await func(*args, **kwargs)
|
|
85
|
+
else:
|
|
86
|
+
return await asyncio.to_thread(func, *args, **kwargs)
|
|
87
|
+
|
|
88
|
+
# Preserve function metadata
|
|
89
|
+
async_wrapper.__name__ = func.__name__
|
|
90
|
+
async_wrapper.__doc__ = func.__doc__
|
|
91
|
+
async_wrapper.__annotations__ = func.__annotations__
|
|
92
|
+
|
|
93
|
+
return async_wrapper
|
|
94
|
+
|
|
95
|
+
# Update your register_module_tools function to use this improved wrapper
|
|
96
|
+
def register_module_tools(module_name: str) -> None:
|
|
97
|
+
"""
|
|
98
|
+
Register all suitable functions from a module as MCP tools with improved argument handling.
|
|
99
|
+
"""
|
|
100
|
+
functions = load_module_functions(module_name)
|
|
101
|
+
for func in functions:
|
|
102
|
+
# Skip functions that don't have docstrings
|
|
103
|
+
if not func.__doc__:
|
|
104
|
+
print(f"Skipping function without docstring: {func.__name__}")
|
|
105
|
+
continue
|
|
106
|
+
|
|
107
|
+
# Create async wrapper with improved argument handling
|
|
108
|
+
async_func = make_async_wrapper(func)
|
|
109
|
+
|
|
110
|
+
# Register as MCP tool
|
|
111
|
+
try:
|
|
112
|
+
mcp.tool()(async_func)
|
|
113
|
+
print(f"Registered tool: {func.__name__}")
|
|
114
|
+
except Exception as e:
|
|
115
|
+
print(f"Failed to register {func.__name__}: {e}")
|
|
116
|
+
def load_module_functions(module_name: str) -> List[Callable]:
|
|
117
|
+
"""
|
|
118
|
+
Dynamically load functions from a module.
|
|
119
|
+
"""
|
|
120
|
+
try:
|
|
121
|
+
module = importlib.import_module(module_name)
|
|
122
|
+
# Get all callables from the module that don't start with underscore
|
|
123
|
+
functions = []
|
|
124
|
+
for name, func in inspect.getmembers(module, callable):
|
|
125
|
+
if not name.startswith('_'):
|
|
126
|
+
# Check if it's a function, not a class
|
|
127
|
+
if inspect.isfunction(func) or inspect.ismethod(func):
|
|
128
|
+
functions.append(func)
|
|
129
|
+
return functions
|
|
130
|
+
except ImportError as e:
|
|
131
|
+
print(f"Warning: Could not import module {module_name}: {e}")
|
|
132
|
+
return []
|
|
133
|
+
|
|
134
|
+
print("Loading tools from npcpy modules...")
|
|
135
|
+
|
|
136
|
+
# Load modules from npcpy.routes
|
|
137
|
+
try:
|
|
138
|
+
from npcpy.routes import routes
|
|
139
|
+
for route_name, route_func in routes.items():
|
|
140
|
+
if callable(route_func):
|
|
141
|
+
async_func = make_async_wrapper(route_func)
|
|
142
|
+
try:
|
|
143
|
+
mcp.tool()(async_func)
|
|
144
|
+
print(f"Registered route: {route_name}")
|
|
145
|
+
except Exception as e:
|
|
146
|
+
print(f"Failed to register route {route_name}: {e}")
|
|
147
|
+
except ImportError as e:
|
|
148
|
+
print(f"Warning: Could not import routes: {e}")
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
# Load npc_compiler functions
|
|
152
|
+
print("Loading functions from npcpy.npc_compiler...")
|
|
153
|
+
try:
|
|
154
|
+
import importlib.util
|
|
155
|
+
if importlib.util.find_spec("npcpy.npc_compiler"):
|
|
156
|
+
register_module_tools("npcpy.npc_compiler")
|
|
157
|
+
except ImportError:
|
|
158
|
+
print("npcpy.npc_compiler not found, skipping...")
|
|
159
|
+
|
|
160
|
+
# Load npc_sysenv functions
|
|
161
|
+
#print("Loading functions from npcpy.npc_sysenv...")
|
|
162
|
+
#register_module_tools("npcpy.npc_sysenv")
|
|
163
|
+
register_module_tools("npcpy.memory.search")
|
|
164
|
+
|
|
165
|
+
register_module_tools("npcpy.work.plan")
|
|
166
|
+
register_module_tools("npcpy.work.trigger")
|
|
167
|
+
register_module_tools("npcpy.work.desktop")
|
|
168
|
+
|
|
169
|
+
#print("Loading functions from npcpy.command_history...")
|
|
170
|
+
#register_module_tools("npcpy.memory.command_history")
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
#print("Loading functions from npcpy.npc_sysenv...")
|
|
174
|
+
#register_module_tools("npcpy.llm_funcs")
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
# ==================== MAIN ENTRY POINT ====================
|
|
178
|
+
|
|
179
|
+
if __name__ == "__main__":
|
|
180
|
+
print(f"Starting enhanced NPCPY MCP server...")
|
|
181
|
+
print(f"Workspace: {DEFAULT_WORKSPACE}")
|
|
182
|
+
|
|
183
|
+
# Run the server
|
|
184
|
+
mcp.run(transport="stdio")
|
npcsh/npc.py
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import sys
|
|
3
|
+
import os
|
|
4
|
+
import sqlite3
|
|
5
|
+
import traceback
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
from npcsh._state import (
|
|
9
|
+
NPCSH_CHAT_MODEL,
|
|
10
|
+
NPCSH_CHAT_PROVIDER,
|
|
11
|
+
NPCSH_API_URL,
|
|
12
|
+
NPCSH_DB_PATH,
|
|
13
|
+
NPCSH_STREAM_OUTPUT,
|
|
14
|
+
)
|
|
15
|
+
from npcpy.npc_sysenv import (
|
|
16
|
+
print_and_process_stream_with_markdown,
|
|
17
|
+
render_markdown,
|
|
18
|
+
)
|
|
19
|
+
from npcpy.npc_compiler import NPC, Team
|
|
20
|
+
from npcpy.routes import router
|
|
21
|
+
from npcpy.llm_funcs import check_llm_command
|
|
22
|
+
|
|
23
|
+
def load_npc_by_name(npc_name: str = "sibiji", db_path: str = NPCSH_DB_PATH) -> Optional[NPC]:
|
|
24
|
+
if not npc_name:
|
|
25
|
+
npc_name = "sibiji"
|
|
26
|
+
|
|
27
|
+
project_npc_path = os.path.abspath(f"./npc_team/{npc_name}.npc")
|
|
28
|
+
global_npc_path = os.path.expanduser(f"~/.npcsh/npc_team/{npc_name}.npc")
|
|
29
|
+
|
|
30
|
+
chosen_path = None
|
|
31
|
+
if os.path.exists(project_npc_path):
|
|
32
|
+
chosen_path = project_npc_path
|
|
33
|
+
elif os.path.exists(global_npc_path):
|
|
34
|
+
chosen_path = global_npc_path
|
|
35
|
+
elif os.path.exists(f"npcs/{npc_name}.npc"):
|
|
36
|
+
chosen_path = f"npcs/{npc_name}.npc"
|
|
37
|
+
|
|
38
|
+
if chosen_path:
|
|
39
|
+
try:
|
|
40
|
+
db_conn = sqlite3.connect(db_path)
|
|
41
|
+
npc = NPC(file=chosen_path, db_conn=db_conn)
|
|
42
|
+
return npc
|
|
43
|
+
except Exception as e:
|
|
44
|
+
print(f"Warning: Failed to load NPC '{npc_name}' from {chosen_path}: {e}", file=sys.stderr)
|
|
45
|
+
return None
|
|
46
|
+
else:
|
|
47
|
+
print(f"Warning: NPC file for '{npc_name}' not found in project or global paths.", file=sys.stderr)
|
|
48
|
+
if npc_name != "sibiji":
|
|
49
|
+
return load_npc_by_name("sibiji", db_path)
|
|
50
|
+
return None
|
|
51
|
+
|
|
52
|
+
def main():
|
|
53
|
+
parser = argparse.ArgumentParser(
|
|
54
|
+
description="NPC Command Line Utilities. Call a command or provide a prompt for the default NPC.",
|
|
55
|
+
usage="npc <command> [command_args...] | <prompt> [--npc NAME] [--model MODEL] [--provider PROV]"
|
|
56
|
+
)
|
|
57
|
+
parser.add_argument(
|
|
58
|
+
"--model", "-m", help="LLM model to use (overrides NPC/defaults)", type=str, default=None
|
|
59
|
+
)
|
|
60
|
+
parser.add_argument(
|
|
61
|
+
"--provider", "-pr", help="LLM provider to use (overrides NPC/defaults)", type=str, default=None
|
|
62
|
+
)
|
|
63
|
+
parser.add_argument(
|
|
64
|
+
"-n", "--npc", help="Name of the NPC to use (default: sibiji)", type=str, default="sibiji"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# No subparsers setup at first - we'll conditionally create them
|
|
68
|
+
|
|
69
|
+
# First, get any arguments without parsing commands
|
|
70
|
+
args, all_args = parser.parse_known_args()
|
|
71
|
+
global_model = args.model
|
|
72
|
+
global_provider = args.provider
|
|
73
|
+
|
|
74
|
+
# Check if the first argument is a known command
|
|
75
|
+
is_valid_command = False
|
|
76
|
+
command_name = None
|
|
77
|
+
if all_args and all_args[0] in router.get_commands():
|
|
78
|
+
is_valid_command = True
|
|
79
|
+
command_name = all_args[0]
|
|
80
|
+
all_args = all_args[1:] # Remove the command from arguments
|
|
81
|
+
|
|
82
|
+
# Only set up subparsers if we have a valid command
|
|
83
|
+
if is_valid_command:
|
|
84
|
+
subparsers = parser.add_subparsers(dest="command", title="Available Commands",
|
|
85
|
+
help="Run 'npc <command> --help' for command-specific help")
|
|
86
|
+
|
|
87
|
+
for cmd_name, help_text in router.help_info.items():
|
|
88
|
+
if router.shell_only.get(cmd_name, False):
|
|
89
|
+
continue
|
|
90
|
+
|
|
91
|
+
cmd_parser = subparsers.add_parser(cmd_name, help=help_text, add_help=False)
|
|
92
|
+
cmd_parser.add_argument('command_args', nargs=argparse.REMAINDER,
|
|
93
|
+
help='Arguments passed directly to the command handler')
|
|
94
|
+
|
|
95
|
+
# Re-parse with command subparsers
|
|
96
|
+
args = parser.parse_args([command_name] + all_args)
|
|
97
|
+
command_args = args.command_args if hasattr(args, 'command_args') else []
|
|
98
|
+
unknown_args = []
|
|
99
|
+
else:
|
|
100
|
+
# Treat all arguments as a prompt
|
|
101
|
+
args.command = None
|
|
102
|
+
command_args = []
|
|
103
|
+
unknown_args = all_args
|
|
104
|
+
|
|
105
|
+
if args.model is None:
|
|
106
|
+
args.model = global_model
|
|
107
|
+
if args.provider is None:
|
|
108
|
+
args.provider = global_provider
|
|
109
|
+
# --- END OF FIX ---
|
|
110
|
+
npc_instance = load_npc_by_name(args.npc, NPCSH_DB_PATH)
|
|
111
|
+
|
|
112
|
+
effective_model = args.model or NPCSH_CHAT_MODEL
|
|
113
|
+
effective_provider = args.provider or NPCSH_CHAT_PROVIDER
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
extras = {}
|
|
118
|
+
|
|
119
|
+
# Process command args if we have a valid command
|
|
120
|
+
if is_valid_command:
|
|
121
|
+
# Parse command args properly
|
|
122
|
+
if command_args:
|
|
123
|
+
i = 0
|
|
124
|
+
while i < len(command_args):
|
|
125
|
+
arg = command_args[i]
|
|
126
|
+
if arg.startswith("--"):
|
|
127
|
+
param = arg[2:] # Remove --
|
|
128
|
+
if "=" in param:
|
|
129
|
+
param_name, param_value = param.split("=", 1)
|
|
130
|
+
extras[param_name] = param_value
|
|
131
|
+
i += 1
|
|
132
|
+
elif i + 1 < len(command_args) and not command_args[i+1].startswith("--"):
|
|
133
|
+
extras[param] = command_args[i+1]
|
|
134
|
+
i += 2
|
|
135
|
+
else:
|
|
136
|
+
extras[param] = True
|
|
137
|
+
i += 1
|
|
138
|
+
else:
|
|
139
|
+
i += 1
|
|
140
|
+
|
|
141
|
+
handler = router.get_route(command_name)
|
|
142
|
+
if not handler:
|
|
143
|
+
print(f"Error: Command '{command_name}' recognized but no handler found.", file=sys.stderr)
|
|
144
|
+
sys.exit(1)
|
|
145
|
+
|
|
146
|
+
full_command_str = command_name
|
|
147
|
+
if command_args:
|
|
148
|
+
full_command_str += " " + " ".join(command_args)
|
|
149
|
+
|
|
150
|
+
handler_kwargs = {
|
|
151
|
+
"model": effective_model,
|
|
152
|
+
"provider": effective_provider,
|
|
153
|
+
"npc": npc_instance,
|
|
154
|
+
"api_url": NPCSH_API_URL,
|
|
155
|
+
"stream": NPCSH_STREAM_OUTPUT,
|
|
156
|
+
"messages": [],
|
|
157
|
+
"team": None,
|
|
158
|
+
"current_path": os.getcwd(),
|
|
159
|
+
**extras
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
try:
|
|
163
|
+
result = handler(command=full_command_str, **handler_kwargs)
|
|
164
|
+
|
|
165
|
+
if isinstance(result, dict):
|
|
166
|
+
output = result.get("output") or result.get("response")
|
|
167
|
+
|
|
168
|
+
if NPCSH_STREAM_OUTPUT and not isinstance(output, str):
|
|
169
|
+
print_and_process_stream_with_markdown(output, effective_model, effective_provider)
|
|
170
|
+
elif output is not None:
|
|
171
|
+
render_markdown(str(output))
|
|
172
|
+
elif result is not None:
|
|
173
|
+
render_markdown(str(result))
|
|
174
|
+
else:
|
|
175
|
+
print(f"Command '{command_name}' executed.")
|
|
176
|
+
|
|
177
|
+
except Exception as e:
|
|
178
|
+
print(f"Error executing command '{command_name}': {e}", file=sys.stderr)
|
|
179
|
+
traceback.print_exc()
|
|
180
|
+
sys.exit(1)
|
|
181
|
+
else:
|
|
182
|
+
# Process as a prompt
|
|
183
|
+
prompt = " ".join(unknown_args)
|
|
184
|
+
|
|
185
|
+
if not prompt:
|
|
186
|
+
# If no prompt and no command, show help
|
|
187
|
+
parser.print_help()
|
|
188
|
+
sys.exit(1)
|
|
189
|
+
|
|
190
|
+
print(f"Processing prompt: '{prompt}' with NPC: '{args.npc}'...")
|
|
191
|
+
try:
|
|
192
|
+
response_data = check_llm_command(
|
|
193
|
+
command=prompt,
|
|
194
|
+
model=effective_model,
|
|
195
|
+
provider=effective_provider,
|
|
196
|
+
npc=npc_instance,
|
|
197
|
+
stream=NPCSH_STREAM_OUTPUT,
|
|
198
|
+
messages=[],
|
|
199
|
+
team=None,
|
|
200
|
+
api_url=NPCSH_API_URL,
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
if isinstance(response_data, dict):
|
|
204
|
+
output = response_data.get("output")
|
|
205
|
+
if NPCSH_STREAM_OUTPUT and hasattr(output, '__iter__') and not isinstance(output, (str, bytes, dict, list)):
|
|
206
|
+
print_and_process_stream_with_markdown(output, effective_model, effective_provider)
|
|
207
|
+
elif output is not None:
|
|
208
|
+
render_markdown(str(output))
|
|
209
|
+
elif response_data is not None:
|
|
210
|
+
render_markdown(str(response_data))
|
|
211
|
+
|
|
212
|
+
except Exception as e:
|
|
213
|
+
print(f"Error processing prompt: {e}", file=sys.stderr)
|
|
214
|
+
traceback.print_exc()
|
|
215
|
+
sys.exit(1)
|
|
216
|
+
|
|
217
|
+
if __name__ == "__main__":
|
|
218
|
+
main()
|