npcsh 1.1.2__tar.gz → 1.1.3__tar.gz
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-1.1.2 → npcsh-1.1.3}/PKG-INFO +10 -1
- {npcsh-1.1.2 → npcsh-1.1.3}/README.md +9 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/_state.py +0 -29
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/alicanto.py +10 -5
- npcsh-1.1.3/npcsh/build.py +291 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/corca.py +263 -154
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/npc.py +127 -46
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/routes.py +229 -21
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh.egg-info/PKG-INFO +10 -1
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh.egg-info/SOURCES.txt +1 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/setup.py +1 -1
- {npcsh-1.1.2 → npcsh-1.1.3}/LICENSE +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/__init__.py +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/guac.py +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/mcp_helpers.py +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/mcp_server.py +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/npc_team/alicanto.npc +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/npc_team/alicanto.png +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/npc_team/corca.npc +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/npc_team/corca.png +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/npc_team/foreman.npc +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/npc_team/frederic.npc +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/npc_team/frederic4.png +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/npc_team/guac.png +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/npc_team/jinxs/bash_executer.jinx +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/npc_team/jinxs/edit_file.jinx +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/npc_team/jinxs/image_generation.jinx +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/npc_team/jinxs/internet_search.jinx +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/npc_team/jinxs/kg_search.jinx +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/npc_team/jinxs/memory_search.jinx +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/npc_team/jinxs/python_executor.jinx +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/npc_team/jinxs/screen_cap.jinx +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/npc_team/kadiefa.npc +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/npc_team/kadiefa.png +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/npc_team/npcsh.ctx +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/npc_team/npcsh_sibiji.png +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/npc_team/plonk.npc +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/npc_team/plonk.png +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/npc_team/plonkjr.npc +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/npc_team/plonkjr.png +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/npc_team/sibiji.npc +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/npc_team/sibiji.png +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/npc_team/spool.png +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/npc_team/yap.png +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/npcsh.py +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/plonk.py +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/pti.py +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/spool.py +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/wander.py +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh/yap.py +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh.egg-info/dependency_links.txt +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh.egg-info/entry_points.txt +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh.egg-info/requires.txt +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/npcsh.egg-info/top_level.txt +0 -0
- {npcsh-1.1.2 → npcsh-1.1.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: npcsh
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.3
|
|
4
4
|
Summary: npcsh is a command-line toolkit for using AI agents in novel ways.
|
|
5
5
|
Home-page: https://github.com/NPC-Worldwide/npcsh
|
|
6
6
|
Author: Christopher Agostino
|
|
@@ -194,6 +194,14 @@ and you will enter the NPC shell. Additionally, the pip installation includes th
|
|
|
194
194
|
/corca --mcp-server-path /path.to.server.py
|
|
195
195
|
```
|
|
196
196
|
|
|
197
|
+
- **Build an NPC Team**:
|
|
198
|
+
|
|
199
|
+
``` bash
|
|
200
|
+
npc build flask --output ./dist --port 5337
|
|
201
|
+
npc build docker --output ./deploy
|
|
202
|
+
npc build cli --output ./bin
|
|
203
|
+
npc build static --api_url https://api.example.com
|
|
204
|
+
```
|
|
197
205
|
|
|
198
206
|
# NPC Data Layer
|
|
199
207
|
|
|
@@ -217,6 +225,7 @@ Importantly, users can switch easily between the NPCs they are chatting with by
|
|
|
217
225
|
- activated by invoking `/<command> ...` in `npcsh`, macros can be called in bash or through the `npc` CLI. In our examples, we provide both `npcsh` calls as well as bash calls with the `npc` cli where relevant. For converting any `/<command>` in `npcsh` to a bash version, replace the `/` with `npc ` and the macro command will be invoked as a positional argument. Some, like breathe, flush,
|
|
218
226
|
|
|
219
227
|
- `/alicanto` - Conduct deep research with multiple perspectives, identifying gold insights and cliff warnings. Usage: `/alicanto 'query to be researched' --num-npcs <int> --depth <int>`
|
|
228
|
+
- `/build` - Builds the current npc team to an executable format . Usage: `/build <output[flask,docker,cli,static]> --options`
|
|
220
229
|
- `/brainblast` - Execute an advanced chunked search on command history. Usage: `/brainblast 'query' --top_k 10`
|
|
221
230
|
- `/breathe` - Condense context on a regular cadence. Usage: `/breathe -p <provider: NPCSH_CHAT_PROVIDER> -m <model: NPCSH_CHAT_MODEL>`
|
|
222
231
|
- `/compile` - Compile NPC profiles. Usage: `/compile <path_to_npc> `
|
|
@@ -94,6 +94,14 @@ and you will enter the NPC shell. Additionally, the pip installation includes th
|
|
|
94
94
|
/corca --mcp-server-path /path.to.server.py
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
+
- **Build an NPC Team**:
|
|
98
|
+
|
|
99
|
+
``` bash
|
|
100
|
+
npc build flask --output ./dist --port 5337
|
|
101
|
+
npc build docker --output ./deploy
|
|
102
|
+
npc build cli --output ./bin
|
|
103
|
+
npc build static --api_url https://api.example.com
|
|
104
|
+
```
|
|
97
105
|
|
|
98
106
|
# NPC Data Layer
|
|
99
107
|
|
|
@@ -117,6 +125,7 @@ Importantly, users can switch easily between the NPCs they are chatting with by
|
|
|
117
125
|
- activated by invoking `/<command> ...` in `npcsh`, macros can be called in bash or through the `npc` CLI. In our examples, we provide both `npcsh` calls as well as bash calls with the `npc` cli where relevant. For converting any `/<command>` in `npcsh` to a bash version, replace the `/` with `npc ` and the macro command will be invoked as a positional argument. Some, like breathe, flush,
|
|
118
126
|
|
|
119
127
|
- `/alicanto` - Conduct deep research with multiple perspectives, identifying gold insights and cliff warnings. Usage: `/alicanto 'query to be researched' --num-npcs <int> --depth <int>`
|
|
128
|
+
- `/build` - Builds the current npc team to an executable format . Usage: `/build <output[flask,docker,cli,static]> --options`
|
|
120
129
|
- `/brainblast` - Execute an advanced chunked search on command history. Usage: `/brainblast 'query' --top_k 10`
|
|
121
130
|
- `/breathe` - Condense context on a regular cadence. Usage: `/breathe -p <provider: NPCSH_CHAT_PROVIDER> -m <model: NPCSH_CHAT_MODEL>`
|
|
122
131
|
- `/compile` - Compile NPC profiles. Usage: `/compile <path_to_npc> `
|
|
@@ -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)
|
|
@@ -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
|
|
|
@@ -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
|
+
}
|